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 C INCLUDE: include 'fattrib.h' include 'filparts.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 bValidDriveName C iOSCheckDirectory *(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 save iType, iDir, cDir iFirst = abs(jFirst) iFirst = mod(iFirst,100) bError = iFirst/10 .eq. 0 iFirst = mod(iFirst,10) if (iFirst .eq. 1) then if (iGetLogical(cSearchIn,cSearch) .eq. 0) cSearch = cSearchIn ! First try translation as logical L = iSetFileSpec(cSearch) L = iGetFileSpec(0,FIL__DEVICE,cDir)! Get device (includes ':') if (L .gt. 1) then ! Device present I = iGetLogical(cDir(:L-1),cDir)! Translate device if (I .ne. 0) cSearch = cDir(:I)//cSearch(L+1:) end if bStatus = bValidPath(cSearch,cDir,cName) ! Split cSearch in cDir and cName 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 '//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 = FA_NORMAL if (jFirst .lt. 0) iType = FA_DIREC 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. FA_DIREC .and. iFirst .eq. 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 = ' ' cFound = cDir(:iDir)//cName else if (iFirst .eq. 1) then ! File not found if (bError) call Say('iSearch','W','error','searching for '//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 (For internal use by iSearch only) C Search for specified file(s) by making a 'dir' or 'ls' call C CALLING SEQUENCE: logical function bOSFind(iNewSearch,cBufIn,iType,cName) C INCLUDE: include 'dirspec.h' include 'filparts.h' include 'fattrib.h' include 'str2str_inc.h' C CALLS: C itrim, Str2Str, Str2StrSet, iGetLogical, iGetLun, iFreeLun, iOSDeleteFile C iOSSpawnCmd, iScratchLun C PROCEDURE: C Specify full path including drive,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; pphick@ucsd.edu) C- integer iNewSearch character cBufIn*(*) integer iType character cName*(*) character cBufName*(FIL__LENGTH) integer Str2Str integer Str2StrSet 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 i = iGetLogical(cTemp,cFile) if (i .eq. 0) then ! cTemp not defined cFile = 'C:' ! Always exists, hopefully i = itrim(cFile) i = i+Str2Str(cTrail,cFile(i+1:)) end if cFile(i+1:) = cOpSys//'FIND.TXT' ! Add file name end if if (iNewSearch .eq. 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 !----- ! Files in a directory are collected in a dummy file by making a system ! call with the command DIR/B/A:D (for directories) or DIR/B/A:-D (for ! normal files). NOTE: DIR/B/A:D does not pick up directories . (current) ! and .. (parent). iBuf = 0 if (iType .ne. FA_DIREC) iBuf = iBuf+Str2Str('-',cBufName(iBuf+1:)) iBuf = iBuf+Str2Str('d',cBufName(iBuf+1:))+1! Add a space iBuf = iBuf+Str2Str(cBufIn,cBufName(iBuf+1:))! Append file spec if (itrim(cBufIn) .eq. 2) iBuf = iBuf+Str2Str('\',cBufName(iBuf+1:)) ! Drive only (C:); add trailing \ i = iOSSpawnCmd('dir/b/a:'//cBufName(:iBuf)//' > '//cFile,0) ! Redirect into dummy file !------- ! 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 (iType .eq. FA_DIREC) then i = itrim(cName) if (cName(i:i) .ne. cTrail) cName(i+1:) = cTrail end if else bOSFind = .FALSE. cName = ' ' end if iStr = Str2StrSet(iStr) return end C+ C NAME: C bValidPath C PURPOSE: C (For internal use by iSearch only) C Build a valid fully-qualified file name for Unix or DOS C CALLING SEQUENCE: logical function bValidPath(cFile,cDir,cName) C INPUTS: C cFile character*(*) string to be tested for validity C OUTPUTS: C CALLS: C bValidFileName, bValidFragment, bValidDriveName C itrim, uppercase, iOSGetDirectory, iSetFileSpec, iGetFileSpec C INCLUDE: include 'dirspec.h' include 'filparts.h' C RESTRICTIONS: C DOS: 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*(*) character cDef*80 logical bValidDriveName logical bValidFragment logical bValidFileName bValidPath = .FALSE. nFile = itrim(cFile) cDir = ' ' cName = ' ' cDef = ' ' !------ ! 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 = index(cFile,':') if (iDir .ne. 0) then ! Leftmost colon if (iDir .ne. 2) return ! Colon in 1st pos if (.not. bValidDriveName(cFile(:2))) return cDir = cFile(:iDir) ! Copy drive letter call uppercase(cDir) ! Drive # (1=A,..) iptr = iptr+2 ! Pos following colon in cFile else ! No drive specified (incl. nFile=0) if (iOSGetDirectory(cDef) .eq. 0) return iDir = iSetFileSpec(cDef) iDir = iGetFileSpec(0,FIL__DEVICE,cDir) ! Current drive end if !----- ! cDir(:iDir) now contains a valid drive (e.g. C:). iptr is the next char ! in cFile to be processed. if (iptr .gt. nFile) then ! cFile empty or drive spec only if (cDef .eq. ' ') then if (iOSGetDirectory(cDef) .eq. 0) return L = iSetFileSpec(cDef) end if ! Include trailing backslash L = iGetFileSpec(FIL__DIRECTORY,FIL__DIRECTORY,cDir(iDir+1:)) if (L .eq. 0) return iDir = iDir+L ! No backslash in 1st pos else if (cFile(iptr:iptr) .ne. cTrail) then if (cDef .eq. ' ') then if (iOSGetDirectory(cDef) .eq. 0) return L = iSetFileSpec(cDef) end if L = iGetFileSpec(FIL__DIRECTORY,FIL__DIRECTORY,cDir(iDir+1:)) if (L .eq. 0) return iDir = iDir+L else ! Backslash in 1st pos of cFile iDir = iDir+1 cDir(iDir:) = cTrail iptr = iptr+1 ! Skip past backslash end if !----- ! 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 (For internal use by iSearch only) C Build a valid fully-qualified file name for Unix or DOS C CALLING SEQUENCE: logical function bValidFileName(iType,cFile) 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 CALLS: C uppercase C SEE ALSO: C bValidPath, bValidFragment, bValidDriveName C INCLUDE: include 'dirspec.h' C RESTRICTIONS: C DOS: Extended chars are permitted in principle. This is not implemented here. 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 = 200) !251, ! 8, parameter (nExtMax = 255-nFileMax-1) parameter (nNoName = 16) parameter (nValid = 26+26+10+18) character cNoName(nNoName)*(nFileMax) & /'CLOCK$','CON','AUX','COM1','COM2','COM3','COM4', & 'LPT1','LPT2','LPT3','LPT4','NUL','PRN','USER','LINE','ERR'/ character cValid*(nValid) ! Includes wildcard chars (!*?) & /'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_^$~!#%&-{}()*?@''`'/ character cFileName*(nFileMax) character cExtName *(nExtMax) bValidFileName = .FALSE. nFileMin = iType ! 0 for file name, 1 for dir name 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 ! Check for reserved names do I=1,nNoName if (cFileName .eq. cNoName(I)) return end do N = len(cValid) ! @'` invalid in directory name if (iType .eq. 1) N = index(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 (For internal use by iSearch only) C Build a valid fully-qualified file name for Unix or DOS C CALLING SEQUENCE: logical function bValidFragment(iType,isub,cFileSub,iBuf,cBuf) C INPUTS: C iType integer 0 = test for file name C 1 = test for directory name C OUTPUTS: C Result logical .FALSE. = invalid file/directory name C .TRUE. = valid file/directory name C cBuf character*(*) parsed version of input spec (including C drive, directory and file name) C CALLS: C LocLast, bValidFileName C SEE ALSO: C bValidPath, bValidDriveName C INCLUDE: include 'dirspec.h' C RESTRICTIONS: C PROCEDURE: 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 C+ C NAME: C bValidDriveName C PURPOSE: C (For internal use by iSearch only) C Build a valid fully-qualified file name for Unix or DOS C CALLING SEQUENCE: logical function bValidDriveName(cDrive) C INPUTS: C (none) C OUTPUTS: C cDrive character*(*) 2-char drive spec ('C:') (DOS-only) C CALLS: C iOSCheckDirectory C SEE ALSO: C bValidPath, bValidFileName, bValidFragment C INCLUDE: include 'dirspec.h' C PROCEDURE: C bValidDriveName: C A:,..,Z:,a:,..,z: are valid drive names C MODIFICATION HISTORY: C DEC-1995, Paul Hick (UCSD/CASS; pphick@ucsd.edu) C- character cDrive*(*) character cptr bValidDriveName = .FALSE. if (len(cDrive) .ne. 2 ) return if (cDrive(2:2) .ne. ':') return cptr = cDrive(:1) bValidDriveName = (lle('A',cptr) .and. lle(cptr,'Z')) .or. & (lle('a',cptr) .and. lle(cptr,'z')) bValidDriveName = iOSCheckDirectory(cDrive//cTrail) .eq. 1 return end