[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Which like command for IDL?
Sorry for superseding my post, but I had left a rant in about
routine_info/resolve_routine that I really didn't want to send,
since I'd discovered some information about those two routines that
made my rant a bit too splenetic, if you know what I mean.
Anyway, I do have some problems with those two routines which I'll
indicate below.
davidf@dfanning.com (David Fanning) writes:
> David Fanning (davidf@dfanning.com) writes:
>
> > It is a moot point anyway, in this case, since the program
> > uses some of the neat new SWITCH, BREAK, etc. stuff that
> > comes in IDL 5.4, and will not compile in earlier versions.
>
> Interestingly, the FILE_WHICH program supplied in IDL 5.4
> calls a built-in, but undocumented, program STRTOK, which
> appears to separate the path subdirectories based on
> a delimiter supplied to the function. I'll leave it
> to the expert sleuths in the group to tell us what it
> *really* does. :-)
>
<snip>
If it's like the C function of the same name, it 'tokenizes' the
string using any delimiter which appears in a particular set, which
is input to the function. It's like repeated calls to strsplit with
different delimiters.
So, *IIRC* you could say 'stuff=strtok(path,':/\') and it would
split the string up regardless of whether you were on a Windows of
Unix machine. (I forget what the delimiter is for Vaxen)
By the way, here's my entry into the (pre 5.4) field. It works by
trying it as a system routine first, then it looks in the output
from help,/source for an *exact* match of the input name (stopping
at the first, see my <rant> below), then an object (if it has a ::
in it) then procedure, a function and, if all these fail, it appends
a '__define' on the input name and tries that, just in case someone
just passed the name of the object it.
It will even work if the object method is defined in it's own file,
provided one follows the obj__method.pro naming convention.
It has a *whole* slew (well, two actually) of GOTOs which I couldn't
find a way to get rid of, mostly because
resolve_routine/routine_info need to know whether the thing being
resolved/asked-about is a procedure or a function beforhand.
<rant>
After I rewrote this routine to be a bit smarter I came to a better
understanding of the problems associated with
resolve_routine/routine_info. But I still think that the proper way
to do this sort of thing is to ere on the side of accomodating the
user and let them resolve necessary ambiguities rather then
requiring them to do it *before* the call. (of course, in order to
follow my own advice, I'll have to rewrite my `which.pro', which I
am going to do in my copious free time!) If the user askes for
information about two routines with the same name, one a function
and one a procedure, I think routine_info should return information
about *both* along with some way to tell which is which and let the
user decide which he/she wants. Similarly, I wonder why routine_info
doesn't resolve the routine(s) itself, instead of requiring it be
done by the user before hand. If there is ambiguity, *resolve both*
and default to the previous lemma.
If anyone can tell me why this wouldn't be a better way to do it,
please do so but I don't see any *real* reason to do it except that
it's harder to write the code. (and that's only a quasi-real reason ;->)
</rant>
William Daffer
;+
; NAME: Which
; $Id: which.pro,v 1.2 2001/01/05 21:03:04 vapuser Exp $
; PURPOSE: Like the Unix 'which' program. Tells you which source file
; a given routine is in.
;
; AUTHOR: William Daffer
;
; CATEGORY: Utility
;
; CALLING SEQUENCE: which,'routine'
;
; INPUTS: routine: An IDL procedure/function
;
; OPTIONAL INPUTS: None
;
; KEYWORD PARAMETERS: None
;
; OUTPUTS: Prints one line with the following info
;
; "routine: System routine" if it's a system routine. -- or --
; "routine: path" if it finds the routine -- or --
; "routine: Doesn't exist" if the previous two fail.
;
;
; OPTIONAL OUTPUTS: none
;
; COMMON BLOCKS: none
;
; SIDE EFFECTS: The routine is compiled along with any possible
; routines contained in the object definition, if this
; circumstance applies.
;
; RESTRICTIONS:
;
; PROCEDURE: Look in the system routines for this name, if not there,
; look in the output from help,/source, if it isn't there,
; try various calls to resolve_routine and routine_info.
; If `routine' has a '::' in it (e.g. foo::bar), `which'
; will resolve will be foo__define and see if bar is a
; method defined in that file, otherwise it will assume
; that the routine is defined in the file `foo__bar.'
;
; If these no '::' and `routine' doesn't resolve either as
; a procedure or a function, `which' will attempt to
; revolve 'routine__define' and see if someone just passed
; an object name in.
;
;
;
; EXAMPLE:
;
; IDL> which,'foo'
; foo: /path/to/foo.pro
;
; IDL> which,'foo::init'
; foo::init: /path/to/foo__define.pro
;
; if init is defined in foo__define.pro
;
; -- or --
;
; IDL> which,'foo::init'
; foo::init: /path/to/foo__init.pro
;
; if init is defined in foo__init.pro
;
; IDL> which,'contour'
; contour: SYSTEM ROUTINE!
;
; IDL> which,'foobar'
; foobar: DOESN'T EXIST!
;
; MODIFICATION HISTORY:
;
; $Log: which.pro,v $
; Revision 1.2 2001/01/05 21:03:04 vapuser
; Reworked completely
;
; Revision 1.1 1999/10/06 21:54:32 vapuser
; Initial revision
;
;
;Copyright (c) 1999, William Daffer
;-
PRO which, procname
usg = "Usage: which,`procname' (with `procname' a nonempty STRING)"
IF n_params() LT 1 OR n_elements(procname) EQ 0 THEN BEGIN
Message,USG,/cont
return
ENDIF
IF size(procname,/type) NE 7 THEN BEGIN
Message,usg,/cont
return
ENDIF
tproc = strupcase(strtrim( procname,2))
IF strlen(tproc) EQ 0 THEN BEGIN
Message,usg,/cont
return
ENDIF
savequiet = !quiet
!quiet = 1
system_routines = routine_info(/system)
catch,/cancel
errcnt = -1
is_func = 0
is_obj = 0
;; Look in the SYSTEM routines first
pos = strpos( system_routines, tproc)
x = where(pos NE -1,nx)
IF nx NE 0 THEN BEGIN
found = 0
ii = 0
REPEAT BEGIN
;; check for possible false positives!
tmp = strcompress(system_routines[x[ii]])
tmp = strsplit(tmp,' ',/extract)
test = tmp[0]
IF test EQ tproc THEN found = 1
ii = ii+1
ENDREP UNTIL found OR ii GE nx
IF found THEN BEGIN
outmsg = procname + ': SYSTEM ROUTINE!'
print,outmsg
!quiet = savequiet
return
ENDIF
ENDIF
;; Then in the already compiled routines
help,/source,out=out
out = strupcase(out)
pos = strpos(out,tproc)
x = where(pos NE -1, nx )
found = 0
ii = 0
IF nx NE 0 THEN BEGIN
REPEAT BEGIN
;; check for false positives!
tmp = strcompress(out[x[ii]])
tmp = strsplit(tmp,' ',/extract)
test = tmp[0]
IF test EQ tproc THEN found = 1
ii = ii+1
ENDREP UNTIL found OR ii GE nx
IF found THEN BEGIN
catch, error
IF error NE 0 THEN BEGIN
catch,/cancel
is_func = 1
ENDIF
info = routine_info(tproc,/source,FUNC=is_func)
outmsg = info.path
ENDIF
ENDIF
;; And finally, try to compile it!
errcnt = -1
is_func = 0
is_obj = 0
IF NOT found THEN BEGIN
IF strpos(procname,'::') NE -1 THEN BEGIN
;; Damn! object reference!
tmp = strsplit(tproc,':',/extract)
procs_to_resolve = [tmp[0] + "__DEFINE", procname]
message,/reset
errcnt2 = -1
is_func2 = 0
catch, error1
IF error1 NE 0 THEN BEGIN
errcnt2 = errcnt2 + 1
CASE errcnt2 OF
0: BEGIN
is_func2 = 1
message,/reset
END
1: GOTO, own_file
ENDCASE
ENDIF
IF errcnt2 LT 0 THEN $
resolve_routine,procs_to_resolve[0] ; the __define routine, always a proc
info = routine_info(procname,/source,func=is_func2)
;; If we've made it this far, it's defined in the
;; tmp[0]__define file, so go to the end
outmsg = info.path
GOTO, endit
OWN_FILE:
errcnt2 = -1
is_func2 = 0
catch,error2
IF error2 NE 0 THEN BEGIN
error2 = 0
errcnt2 = errcnt2 + 1
CASE errcnt2 OF
0: BEGIN
is_func2 = 1
message,/reset
END
1: BEGIN
print, procname + ": DOESN'T EXIST!"
return
END
ENDCASE
ENDIF
resolve_routine,procs_to_resolve[1],is_func=is_func2 ;
info = routine_info(procname,/source,func=is_func2)
outmsg = info.path
ENDIF ELSE BEGIN
;; Doesn't have a "::" in it. May still be an object name, though!
catch, error
IF error NE 0 THEN BEGIN
errcnt = errcnt+1
CASE errcnt OF
0: BEGIN
; won't compile as a procedure,
; try as funtion
is_func = 1
message,/reset
END
1: BEGIN
is_obj = 1
is_func = 0
tproc = tproc + "__DEFINE"
message,/reset
;resolve_routine, tproc[jj]
END
ELSE : BEGIN
;; can't resolve it as either procedure
;; function or object.
;; Must not exist!
!quiet = savequiet
print, procname + ": DOESN'T EXIST!"
return
END
ENDCASE
ENDIF
resolve_routine, tproc, is_func= is_func
info = routine_info(tproc,/source,FUNC=is_func)
IF !error_state.code NE 0 THEN BEGIN
!quiet = savequiet
outmsg = procname + ": DOESN'T EXIST!"
print, outmsg
return
ENDIF
outmsg = info.path
ENDELSE
ENDIF
ENDIT:
outmsg = procname + ': ' + outmsg
print, outmsg
!quiet = savequiet
return
END
--
William Daffer: 818-354-0161: William.Daffer@jpl.nasa.gov