pro which,file,path,findall=findall,dironly=dironly,verbose=verbose,$ all=all,outfile=outfile,search=search,quiet=quiet, _extra=e ;+ ;procedure which ; apes the csh command `which` in the IDL environment, and ; finds the path to the specified file by looking through ; all the directories specified in the !PATH variable. ; ; NOTE: it is possible to use ROUTINE_INFO to get the path to a ; compiled subroutine directly: ; help,routine_info(routine,/source,/function),/structure ; but clearly you need to know the exact name of the routine ; (can't use wildcards) as well as whether what you need is ; a procedure or a function beforehand. also, they have to ; be compiled procedures, not, for example, command files or ; scripts or datafiles lurking under the horizon. and what ; if there are duplicates in different directories? ; btw, FILEPATH is completely uselss for this purpose. ; ;syntax ; which,file,path,/findall,/dironly,verbose=verbose,$ ; /all,outfile=outfile,search=search,quiet=quiet ; ;parameters ; file [INPUT; required] name(s) of file to search for ; * may include shell wildcards, regexps, etc. ; (but remember to prepend the escape character "\") ; * may also be an array. ; path [OUTPUT] full path to the requested file(s) ; * there is no reason to expect that the array sizes of ; FILE and PATH will match. ; ;keywords ; findall [INPUT] if set, continues to search even if ; the file has been found ; * if not set, and FILE is scalar, stops searching ; as soon as at least one copy of file has been found ; * set automatically if FILE is an array or contains ; special characters '*', '?', '[', ']', '\' ; - in this case, set explicitly to 0 to quit at first ; match. ; dironly [INPUT] if set, returns only the name(s) of the ; directory(ies) containing the specified file(s) ; verbose [INPUT] if set, spits out informational messages ; all [INPUT] added for compatibility with SSW version, ; same as FINDALL ; outfile [OUTPUT] added for compatibility with SSW version, ; same as PATH ; search [INPUT] added for compatibility with SSW version, ; automatically sets FINDALL and tacks on '*'s to FILE ; quiet [INPUT] added for compatibility with SSW version, ; same as setting VERBOSE=0 ; _extra [JUNK] here only to prevent crashing the program ; ;restrictions ; FOR UNIX ONLY (spawns find) ; ;history ; vinay kashyap (FebMM) ; quits if not UNIX (VK; FebMMI) ; added keywords ALL, OUTFILE, and SEARCH for SSW-IDL compatibility ; (VK; May03) ;- ; usage ok='ok' & np=n_params() & nf=n_elements(file) & path='' szf=size(file) & nszf=n_elements(szf) if np eq 0 then ok='Missing filename' else $ if nf eq 0 then ok='Input filename undefined' else $ if szf(nszf-2) ne 7 then ok='Filename not character string' else $ if !version.OS_FAMILY ne 'unix' then ok='spawns find; UNIX only' if ok ne 'ok' then begin print,'Usage: which,file,path,/findall,/dironly,verbose=verbose,$' print,' /all,outfile=outfile,search=search' print,' search through !PATH to find file' print,' see also: whereis(), routine_info(), findfile()' if np ne 0 then message,ok,/info return endif ; recast inputs ff=[file(*)] if keyword_set(all) then findall=1 if keyword_set(search) and n_elements(findall) eq 0 then begin findall=1 & ff='*'+ff+'*' endif v=0 & if keyword_set(verbose) then v=fix(verbose(0)) > 1 if keyword_set(quiet) then v=0 ;OVERRIDE if n_elements(findall) eq 0 then begin ;(FINDALL is not specified getall=0 ;skip out after first match if nf gt 1 then getall=1 else begin ok='ok' if strpos(ff(0),'*',0) ge 0 then ok='*' if strpos(ff(0),'?',0) ge 0 then ok='?' if strpos(ff(0),'[',0) ge 0 then ok='[' if strpos(ff(0),']',0) ge 0 then ok=']' if strpos(ff(0),'(',0) ge 0 then ok='(' if strpos(ff(0),')',0) ge 0 then ok=')' if strpos(ff(0),'\',0) ge 0 then ok='\' if ok ne 'ok' then getall=1 ;regex wild-cards in filename endelse endif else begin ;)(is set getall=1 if findall(0) eq 0 then getall=0 ;explicitly set to 0 endelse ;FINDALL?) if v ge 10 then message,'FINDALL='+strtrim(getall,2),/info ; define the output path='' ; expand !path dirs=expand_path(!path,/array) & ndirs=n_elements(dirs) for i=0L,nf-1L do begin ;{for each file if v ge 1 then kilroy,dot=strtrim(ff(i),2)+': ' go_on=1 & k=0L while go_on do begin ;{search through all directories if v ge 5 then kilroy,dot=dirs(k) & if v ge 1 then kilroy cmd='find '+dirs(k)+' -name '+strtrim(ff(i),2) if keyword_set(dironly) then cmd=cmd+' -exec dirname {} \;' else $ cmd=cmd+' -print' spawn,cmd,cc if keyword_set(cc) then begin if keyword_set(path) then path=[path,cc] else path=cc if v ge 2 then print,cc endif ; stopping rules k=k+1L if k eq ndirs then begin if v gt 2 then message,'done looking through !PATH',/info go_on=0 endif if getall eq 0 then begin if keyword_set(path) then go_on=0 endif endwhile ;GO_ON} endfor ;I=0,NF-1} if np eq 1 then begin if keyword_set(path) then print,path else print,file+': Not found' endif if keyword_set(path) then outfile=path else outfile='' return end