C+ C NAME: C iSearch C PURPOSE: C Search for file based on file name specification C CALLING SEQUENCE: function iSearch(jFirst,cSearchIn,cFound) C INPUTS: C jFirst integer =1 on 1st call (set up for subsequent searches) C =0 on subsequent calls C Add 10 to iFirst to suppress the error message C when no file is found C Add 100 if you want to test for parsing errors only C A positive value searches for files; C a negative value searches for directories (see SIDE EFFECTS) C cSearch character*(*) File specification to search for C OUTPUTS: C cFound character*(*) File specification of located file C = ' ' if no file is found C iSearch integer 0 : file not found C 1 : file found C 2 : error parsing cSearch (probably typo) C CALLS: C itrim, bOSFind, bValidPath, iOSCheckDirectory, iGetLogical, iSetFileSpec C iGetFileSpec, Say, LocFirstLen, cHideLogical C INCLUDE: include 'filparts.h' include 'dirspec.h' C SIDE EFFECTS: C > iSetFileSpec is used to store the file for retrieval with iGetFileSpec C by the caller. C > In general iFirst must be set to a negative value when searching for C directories on DOS or Unix. However, when searching for a single directory C the sign of iFirst is not used if the trailing (back)slash is included. C PROCEDURE: C Scans a directory for files based on a specified search string C (wild cards permitted) and returns successive matching file C specifications. When no match is found, the returned specification is blank. C If the input is to be checked for parsing, a valid cFound is returned C even if it does not exist. C C When searching for directories using a wild card specification C (no trailing slash will be present) then the names of directories C are returned without a trailing slash. C C When searching for a single directory with no wildcard present C (i.e. checking whether a directory exists) then trailing slash C is only present if was included also in the input search string. C DEPENDENCY TREE: C itrim C iGetLogical C itrim C LogModFile C itrim C bGetLun C iGetLun C iFreeLun C iOSRenameFile *(system call) C iOSDeleteFile *(system call) C iSetFileSpec C itrim C uppercase C LocFirst C LocLast C ParseRepair C LocFirst C Str2Str C Str2StrSet C iGetFileSpec C Str2Str C Str2StrSet C bValidPath C uppercase C itrim C iSetFileSpec C iGetFileSpec C iOSGetDirectory *(system call) C bValidFileName C uppercase C bValidFragment C LocLast C bValidFileName C uppercase C iOSCheckDirectory *(system call) C bOSFind C itrim C Str2Str C Str2StrSet C iGetLun C iFreeLun C iGetLogical C itrim C LogModFile C itrim C bGetLun C iGetLun C iFreeLun C iOSRenameFile *(system call) C iOSDeleteFile *(system call) C iOSSpawnCmd *(system call) C Say C uppercase C itrim C LocFirstLen C LocFirst C iGetLun C iFilePath C Str2Str C itrim C Str2StrSet C iGetLogical C itrim C LogModFile C itrim C bGetLun C iGetLun C iFreeLun C iOSRenameFile *(system call) C iOSDeleteFile *(system call) C iGetSymbol C itrim C LogModFile C itrim C bGetLun C iGetLun C iFreeLun C iOSRenameFile *(system call) C iOSDeleteFile *(system call) C ExitCmd *(system call) C MODIFICATION HISTORY: C DEC-1995, Paul Hick (UCSD/CASS; pphick@ucsd.edu) C- integer jFirst character cSearchIn*(*) character cFound*(*) character cSearch *(FIL__LENGTH) character cDir *(FIL__LENGTH) character cName *(FIL__LENGTH) character cNameTmp*(FIL__LENGTH) logical bValidPath logical bOSFind logical bStatus logical bError character cHideLogical*(FIL__LENGTH) save iType, iDir, cDir iFirst = abs(jFirst) iFirst = mod(iFirst,100) bError = iFirst/10 .eq. 0 iFirst = mod(iFirst,10) if (iFirst .ge. 1) then if (iGetLogical(cSearchIn,cSearch) .eq. 0) cSearch = cSearchIn !------ ! Kinda kludgy, but will have to do. L1 = 0 if (cSearch(:iEnvi) .eq. cEnvi) then ! $ present in 1st pos L1 = 2 ! Skip past $ sign L = LocFirstLen(cDivide(:iDivide),cSearch) end if if (L1 .ne. 0) then if (iGetLogical(cSearch(L1:L-1),cDir) .ne. 0) then if (L .lt. len(cSearch)) cDir(itrim(cDir)+1:) = cSearch(L+1:) cSearch = cDir end if end if bStatus = bValidPath(cSearch,cDir,cName) ! Split cSearch in cDir and cName !iDir = itrim(cDir) !if (bStatus) bStatus = bOSFind(1,cDir(:iDir-iTrail),1,cNameTmp) if (bStatus) then !------- ! Check whether cDir exists. A parsing error should result if it does not. bStatus = iOSCheckDirectory(cDir) .eq. 1 if (bStatus) then !------- ! The following cSearch is a valid file or directory name. If it ! is an existing directory then absorb the file name cName in the ! directory cDir. This allows an early exit with iSearch=1 below ! because now cName = ' '. cSearch = cDir(:itrim(cDir))//cName if (iOSCheckDirectory(cSearch) .eq. 1) then cDir = cSearch ! Will include trailing backslash cName = ' ' end if end if !------- ! At this point we know that cDir//cName is a valid file name, ! even if a parse error results because the directory does not ! exist. Store the full name with iSetFileSpec. I = iSetFileSpec(cDir(:itrim(cDir))//cName) end if if (.not. bStatus) then cDir = cSearch if (bError) call Say('iSearch','W','error','parsing '//cHideLogical(cDir)) iSearch = 2 cFound = ' ' return end if if (cName .eq. ' ') then ! Input ended with trailing slash iSearch = 1 cFound = cDir ! Includes trailing slash return end if iType = 0 ! Search for non-directories if (jFirst .lt. 0) iType = 1! Search for directories iDir = itrim(cDir) end if !------- ! Unix: Value of cSearch does not matter ! Win : Value of cSearch only matters if iFirst=1 cNameTmp = cName bStatus = bOSFind(iFirst,cSearch,iType,cName) if (iType .eq. 1 .and. iFirst .ge. 1) then if (cName .eq. '.' ) bStatus = bOSFind(0,cSearch,iType,cName) if (cName .eq. '..') bStatus = bOSFind(0,cSearch,iType,cName) end if if (bStatus) then iSearch = 1 cFound = cDir(:iDir)//cName else if (iFirst .ge. 1) then ! File not found if (bError) call Say('iSearch','W','error','searching for '//cHideLogical(cDir(:iDir)//cNameTmp)) iSearch = 0 cFound = cDir(:iDir)//cNameTmp else iSearch = 0 cFound = ' ' end if I = iSetFileSpec(cFound) return end C+ C NAME: C bOSFind C PURPOSE: C CALLING SEQUENCE: logical function bOSFind(iNewSearch,cBufIn,iType,cName) C INCLUDE: include 'dirspec.h' include 'filparts.h' include 'str2str_inc.h' C CALLS: C Str2StrSet, iGetLogical, itrim, iGetLun, iFreeLun, iOSDeleteFile, LocLast C LocFirst, iOSSpawnCmd, Str2Str, iScratchLun C PROCEDURE: C Specify full path including directory and file name. C Wildcards only permitted in the file name part. (use output of bValidPath) C In DOS to test for the root directory set e.g. cBufIn=A: (no trailing \). C ??? What really happens when cBufIn=A: ??. C The file name is returned in cName. C C Note that the scratch file used to as a destination for the ouput of the C dir command remains open, and hence that the associated logical unit C remains assigned. C C Somewhere there is a version of bOSFind for DOS which uses the DOS C interrupt function instead of the SYSTEM function. This version was written to C for use with Windows programs. C MODIFICATION HISTORY: C DEC-1995, Paul Hick (UCSD/CASS) C JUN-2004, Paul Hick (UCSD/CASS; pphick@ucsd.edu) C Added imperfect patch to allow iSearch to work partially with C directories with lots of files in. C- integer iNewSearch character cBufIn*(*) integer iType character cName*(*) character cBufName*(FIL__LENGTH) character cBufDir*132 character cgrepv(0:1)*2 /'-v',' '/ integer Str2Str integer Str2StrSet logical bOSFindClose logical bFirstCall /.TRUE./ integer iU /FIL__NOUNIT/ character cFile*(FIL__LENGTH) save bFirstCall, iU, cFile iStr = Str2StrSet(STR__TRIM) if (bFirstCall) then ! First call: bFirstCall = .FALSE. ! Construct dummy file name cFile = ' ' call getenv('TUB',cFile) if (cFile .eq. ' ') call getenv('HOME',cFile)! Always exists, hopefully cFile(itrim(cFile)+1:) = cTrail(:iTrail)//cOpSys//'find?.tmp' i = iUniqueName(cFile,cFile) end if if (iNewSearch .ge. 1) then ! Set up new search if (iU .ne. FIL__NOUNIT) then iU = iFreeLun(-iU) ! Close and delete else i = iOSDeleteFile(cFile)! Safety belt, the file shouldn't exist end if iBufIn = itrim(cBufIn) if (iBufIn .eq. 0) then ! The root directory always exists bOSFind = .TRUE. cName = ' ' return end if !----- ! Files in a directory are collected in a dummy file by making a system ! call constituting of a cd and an ls command. The cd moves into the ! directory proper directory. ls -d -F collects all entries (-d lists ! directory names rather than directory content; -F lists directories with ! a trailing backslash.). Errors from the ls command are redirected to the ! null device (2> /dev/null). The grep command is used to collect directories ! (grep /) or non-directories (grep -v /). The output of the grep ! command is redirected into the dummy file cOpSys//'FIND.TXT'. iLst = LocLast('/',cBufin) !----- ! The command 'cd' without arguments, returns to the $HOME directory. To avoid ! this, the current working directory '.' is explicitly used if iLst=0. cBufDir = '.' if (iLst .ne. 0) cBufDir = cBufIn(:iLst)! The directory (including last /) iBufDir = itrim(cBufDir) cBufName = cBufIn(iLst+1:iBufIn) iBufName = iBufIn-iLst !------- ! The 2> /dev/null option has not been tested yet on Sun Unix. It works in Linux ! Option -F on Linux marks executable files with a *; -p does not. ! Files on a FAT partition are all executable or none are executable (depending on ! whether the exec option is used on the mount command). I want to use the exec ! option to be able to execute something from a FAT partition, but then also all ! data files are marked as executable, and will be marked with * if -F is used. ! The * then becomes part of the returned file name, effectively returning the ! file name of a non-existing file. -p works around this problem. !i = iOSSpawnCmd('cd '//cBufDir(:iBufDir)//' ; ls -d -F '//cBufName(:iBufName)// !i = iOSSpawnCmd('bash -c "cd '//cBufDir(:iBufDir)//' ; ls -d -F '//cBufName(:iBufName)//'"'// if (iNewSearch .eq. 1) then if (iBufName .ge. 3 .and. cBufName .eq. '*.*') then ! This should work in directories with lots of files in it. i = iOSSpawnCmd('cd '//cBufDir(:iBufDir)//' ; ls -p '// & ' 2> /dev/null | grep '//cgrepv(iType)//' / > '//cFile,0)! Create new dummy file else i = iOSSpawnCmd('cd '//cBufDir(:iBufDir)//' ; ls -d -p '//cBufName(:iBufName)// & ' 2> /dev/null | grep '//cgrepv(iType)//' / > '//cFile,0)! Create new dummy file end if else i = iOSSpawnCmd('cd '//cBufDir(:iBufDir)//' ; ls -p'// & ' 2> /dev/null | grep '//cgrepv(iType)//' / > '//cFile,0)! Create new dummy file end if !------- ! Note that the program is allowed to crash here on an open error. iU = iGetLun(cFile) if (iU .ne. FIL__NOUNIT) then open (iU,file=cFile,status='UNKNOWN')! Open dummy file i = iScratchLun(iU) ! Mark as scratch file end if end if if (iU .ne. FIL__NOUNIT) then read (iU,'(A)',iostat=i) cName bOSFind = i .eq. 0 if (.not. bOSFind) then iU = iFreeLun(-iU) ! Close and delete cName = ' ' end if if (iNewSearch .ge. 1) then if (LocFirst(cSingle,cBufName) .eq. 0 .and. LocFirst('*',cBufName) .eq. 0) iU = iFreeLun(-iU) end if if (iType .eq. 1) cName(itrim(cName):) = ' ' cName = cName(LocLast('/',cName)+1:) else bOSFind = .FALSE. cName = ' ' end if iStr = Str2StrSet(iStr) return C+ C NAME: C bOSFindClose C- entry bOSFindClose() iU = iFreeLun(-iU) bOSFindClose = iU .eq. FIL__NOUNIT return end C+ C NAME: C bValidPath C PURPOSE: C Build a valid fully-qualified file name for Unix or DOS C CALLING SEQUENCE: logical function bValidPath(cFile,cDir,cName) C INPUTS: C iType integer 0 = test for file name C 1 = test for directory name C cFile character*(*) string to be tested for validity C OUTPUTS: C Result logical .FALSE. = invalid file/directory name C .TRUE. = valid file/directory name C cDrive character*(*) 2-char drive spec ('C:') (DOS-only) C cBuf character*(*) parsed version of input spec (including C drive, directory and file name) C INCLUDE: include 'dirspec.h' C CALLS: C itrim, iOSCheckDirectory, iSetFileSpec, iGetFileSpec, uppercase C RESTRICTIONS: C DOS: C Extended chars are permitted in principle. This is not implemented here. C PROCEDURE: C Tests a full spec consisting of drive, directory and filename. C C Valid full directory names are e.g. (the C: is optional): C C: ???? excluded C C:\ C C:\sub1 C C:\sub1\ ???? excluded C i.e. any string that, after terminating it with \ if necessary, C can be combined with a file name to produce a valid path to the file C name. C Note that this definition makes the interpretation of C: ambiguous: C C:file.ext is a file in the current directory on the C: drive. C But if first the terminating backslash is appended the meaning changes: C C:\file.ext is a file in the root directory on the C: drive. C C A valid full filename is tested by validating the part preceding the last C backslash as a valid directory name, and validating the remaining part C as a file name. Valid full filenames are: (the C: is optional) C C:file.ext C C:\file.ext C C:\sub\file.ext C MODIFICATION HISTORY: C DEC-1995, Paul Hick (UCSD/CASS; pphick@ucsd.edu) C- character cFile*(*) character cDir *(*) character cName*(*) logical bValidFragment logical bValidFileName integer getcwd bValidPath = .FALSE. nFile = itrim(cFile) !------ ! First fill in all the implicit parts in the directory name ! iptr is the next char in cFile to be processed. iptr = 1 ! First pos in cFile iDir = 0 ! Length of pathname if (iptr .gt. nFile) then ! cFile empty if (getcwd(cDir(iDir+1:)) .ne. 0) return ! No slash in 1st pos else if (cFile(iptr:iptr+1) .eq. '~'//cTrail) then call getenv('HOME',cDir(iDir+1:)) if (itrim(cDir) .eq. 0) return iptr = iptr+2 else if (cFile(iptr:iptr) .ne. cTrail) then if (getcwd(cDir(iDir+1:)) .ne. 0) return else ! Slash in 1st pos of cFile cDir = ' ' iptr = iptr+1 ! Skip past slash end if iDir = itrim(cDir)+1 cDir(iDir:) = cTrail !----- ! cDir(:iDir) now contains a valid directory with trailing (back)slash. ! (e.g. \ or \dos\exe\ on Unix and C:\ or C:\dos\exe\ on DOS). ! Now extract pieces bracketed by (back)slashes from cFile(iptr:). ! These must be valid directory names. L = 0 if (iptr .le. nFile) L = index(cFile(iptr:nFile),cDivide) do while (L .ne. 0) ! Subdirectory fragment found isub = L-1 ! Subdirectory length if (isub .ne. 0) then if (.not. bValidFragment(1,isub,cFile(iptr:iptr+isub-1),iDir,cDir)) return end if iptr = iptr+L ! Position after backslash L = 0 if (iptr .le. nFile) L = index(cFile(iptr:),cDivide) end do cDir(iDir+1:) = ' ' !----- ! cDir(:iDir) contains a valid directory name with a trailing '\','/'. ! If cFile(:nFile) has not been completely processed yet, the remaining ! part cFile(iptr:nFile) does not contain a '\','/'. bValidPath = .TRUE. cName = ' ' if (nFile-iptr+1 .gt. 0) then bValidPath = bValidFileName(0,cFile(iptr:nFile)) if (bValidPath) cName = cFile(iptr:nFile) end if !if (iDir .gt. 3) cDir(iDir:) = ' ' ! Clear trailing '\','/' return end C+ C NAME: C bValidFileName C PURPOSE: C CALLING SEQUENCE: logical function bValidFileName(iType,cFile) C INCLUDE: include 'dirspec.h' C PROCEDURE: C Based on DOS5.0 User's Guide and Reference. C For file names: Chapter 4, p. 69 C For directory names : Chapter 5, p. 102 C C The difference in testing for file and directory names are: C 1.) The 3 characters @ ' ` are not permitted in directories C 2.) Directory names are at least 1 char long; file names C can have zero length. C 3.) File name and extension can't have zero-length at the same time C MODIFICATION HISTORY: C DEC-1995, Paul Hick (UCSD/CASS; pphick@ucsd.edu) C- integer iType character cFile*(*) !------- ! WIN-NT: Long filename can be 255 chars ! DOS : File name is 12 chars under DOS (8.3 convention) parameter (nFileMax = 128) parameter (nFileMin = 0) parameter (nExtMax = 128) parameter (nValid = 26+26+10+6) !88) character cValid*(nValid) ! Includes wildcard chars (*?) & /'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.,*?'/ character cFileName*(nFileMax) character cExtName *(nExtMax) bValidFileName = .FALSE. cFileName = ' ' cExtName = ' ' iptr = index(cFile,'.') ! Locate first . for extension if (iptr .ne. 0) then ! Dot located iFile = iptr-1 ! # chars preceeding dot iExt = len(cFile)-iptr ! Could be zero else ! No dot iFile = len(cFile) ! Total length of string iExt = 0 ! No extension end if ! Invalid length if (iFile .lt. nFileMin .or. iFile .gt. nFileMax .or. & iExt .gt. nExtMax .or. iFile+iExt .eq. 0) return if (iFile .ne. 0) then ! Split off file name cFileName = cFile(:iFile) if (bOS__NotCaseSensitive) call uppercase(cFileName)! Uppercase end if if (iExt .ne. 0) then ! Split of extension name cExtName = cFile(iptr+1:) if (bOS__NotCaseSensitive) call uppercase(cExtName) ! Uppercase end if N = len(cValid) do I=1,iFile ! Test for invalid characters if (index(cValid(:N),cFileName(I:I)) .eq. 0) return end do do I=1,iExt if (index(cValid(:N),cExtName(I:I)) .eq. 0) return end do bValidFileName = .TRUE. ! Valid name return end C+ C NAME: C bValidFragment C PURPOSE: C CALLING SEQUENCE: logical function bValidFragment(iType,isub,cFileSub,iBuf,cBuf) C INCLUDE: include 'dirspec.h' C MODIFICATION HISTORY: C DEC-1995, Paul Hick (UCSD/CASS; pphick@ucsd.edu) C- integer iType integer isub character cFileSub*(*) integer iBuf character cBuf*(*) logical bValidFileName bValidFragment = .FALSE. if (isub .eq. 1) then ! Check for . (current dir) if (iType .eq. 1 .and. cFileSub(:isub) .eq. '.' ) isub = 0 else if (isub .eq. 2) then ! Check for .. (parent) if (iType .eq. 1 .and. cFileSub(:isub) .eq. '..') then ! Backtrack to previous '/','\' iBuf = LocLast(cDivide,cBuf(:iBuf-1)) if (iBuf .eq. 0) return ! No parent isub = 0 end if end if if (isub .ne. 0) then if (.not. bValidFileName(iType,cFileSub(:isub))) return cBuf(iBuf+1:iBuf+isub) = cFileSub(:isub) if (iType .eq. 1) then isub = isub+1 ! Add trailing '/','\' cBuf(iBuf+isub:iBuf+isub) = cTrail end if iBuf = iBuf+isub end if bValidFragment = .TRUE. return end