$include (..\lib\compStch.ext) /* *============================================================================ * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE * * Permission to use for any purpose, modify, copy, and make enhancements * and derivative works of the software is granted if attribution is given to * R.M. Gillmore, dba the ACME Software Deli, as the author * * While the ACME Software Deli does not work for money, there is nonetheless * a significant amount of work involved. The ACME Software Deli maintains the * rights to all code written, though it may be used and distributed as long as * the following conditions are maintained. * * 1. The copyright statement at the top of each code block is maintained in * your distribution. * 2. You do not identify yourself as the ACME Software Deli * 3. Any changes made to the software are sent to the ACME Software Deli *============================================================================ */ opendirModule: do; $if not noID declare IDString (*) byte data ( '@(#)pOpenDir.p86 $Author: rmgillmore $ $Date:: 2025-05-04 19:35:39#$:', 0 ); $endif $include (..\lib\comnDefs.ext) $set (opendirImplementation=1) $include (..\lib\posixDir.ext) $include (..\lib\mem.ext) $include (..\lib\sysCalls.ext) $include (..\lib\string.ext) $include (..\lib\dosFind.ext) $include (..\lib\fileIO.ext) $include (..\lib\dirFunct.ext) $include (..\lib\curDir.ext) $include (..\lib\errCode.ext) $include (..\lib\ptrMath.ext) $set (LOGGING=1) $if LOGGING $include (..\lib\logger.ext) $include (..\lib\debugHlp.ext) $endif /* * ====================================================================================== * Requirements for the opendir() function * 1. Accept a path name * a. the path name may have several forms: * directory name (with or without drive specification) * combined directory name and file name * file name * b. the file name specification _may_ have wild card characters * * 2. Return a HANDLE to the directory record * a. if the HANDLE is NULL, "errno" will contain the code to help * troubleshoot * * Design notes: * * 1. The HANDLE is a pointer to a structure containing the following * information * a. the fully-qualified directory name * b. the number of files in the directory * c. the file records * It should be noted that the list of files is created when the * directory is opened. If the directory is changed by another * process, the opened directory contents will not change - see * rewinddir() and reopendir() * * 2. Since we store the _fully-qualified_ name, but a fully-qualified name * is not required of the user, we will need to get the fully-qualified name * before doing anything else * ====================================================================================== */ opendir: procedure ( pathString ) dirHandlePtr_t reentrant public; declare pathString pointer; declare resultsBoolean boolean, tempSize word, ignoredReturn word, lastDelimPtr pointer, pathChar based lastDelimPtr char, fileSpecLength word, dirNamePtr pointer, fileSpec ( MAX_PATH ) char, errorCode word; declare directoryHandlePtr dirHandlePtr_t, dirHandle based directoryHandlePtr dirHandle_t; /* * if the path string is not a directory with a valid file mask (*.* is * assumed if only a directory is specified), it is not valid. We indicate * it is not valid with a NULL pointer returned, and errno set to the cause * of the failure */ directoryHandlePtr = NULL; errorCode = EXIT_SUCCESS; $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'pathString : "%s"', 0 ), pathString ); call addToLog( DEBUG, __FILE__, __LINE__, @( 'hasWildCards: %t isDir: %t', 0 ), hasWildCards( pathString ), isDir( pathString ) ); $endif if ( ( not hasWildCards( pathString ) ) and isDir( pathString ) ) then do; /* * simplest case; what the caller has presented is a directory. */ dirNamePtr = fullyQualifiedDirName( pathString ); /* * since there was no file spec, assume *.* */ call strcpy( @fileSpec, @( '*.*', 0 ) ); $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'dirName : "%s" fileSpec : %s', 0 ), dirNamePtr, @fileSpec ); $endif end; /* is dir with no file spec */ else do; /* * it is not a directory, so see if there is a file specification * appended to a directory */ lastDelimPtr = strrchr( pathString, PATH_DELIM ); if ( lastDelimPtr = NULL ) then do; /* * we are to assume that the caller wants to use the current * directory */ dirNamePtr = fullyQualifiedDirName( @( '.', 0 ) ); if hasWildCards( pathString ) then do; /* * there is a file specification. we will capture the last * token (the string portion after the last path delimiter) */ fileSpecLength = strlen( pathString ); call strcpy( @fileSpec, pathString ); $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'dirName : "%s" fileSpec : %s', 0 ), dirNamePtr, @fileSpec ); $endif end; /* pathString is a file specification */ end; /* assuming current directory */ else do; /* * there is a path delimiter ... perform VERY carefully ... * * we will change the last delimiter to a NUL so * we can check the first token as a directory */ pathChar = NUL; lastDelimPtr = incPtr( lastDelimPtr, size( fileSpec( 0 ) ), 1 ); if ( isDir( pathString ) ) then do; /* * that which is in front of the last token is a directory */ dirNamePtr = fullyQualifiedDirName( pathString ); end; else do; /* * invalid path specified */ errorCode = DOS_PATH_NOT_FOUND; end; /* * restore the delimiter, then skip past it (read second token) */ lastDelimPtr = decPtr( lastDelimPtr, size( fileSpec( 0 ) ), 1 ); pathChar = PATH_DELIM; lastDelimPtr = incPtr( lastDelimPtr, size( fileSpec( 0 ) ), 1 ); if ( errorCode = EXIT_SUCCESS ) then do; /* * examine what we have to see if we have a file name * specification with wild cards */ if ( hasWildCards( lastDelimPtr ) ) then do; /* * there is a file specification. we will capture the * last token (the string portion after the last path * delimiter) */ fileSpecLength = strlen( lastDelimPtr ); call strcpy( @fileSpec, lastDelimPtr ); $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'dirName : "%s" fileSpec : %s', 0 ), dirNamePtr, @fileSpec ); $endif end; /* store the file spec */ else do; /* * the path specification is not valid */ errorCode = DOS_PATH_NOT_FOUND; end; end; /* if the first token was a valid directory */ end; /* there is a path delimiter */ end; /* not a simple directory specified */ /* * allocate the directoryHandlePtr, then assign the directory name, and * finally allocate file specification space, and copy the file name token */ $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'dirName : "%s" fileSpec : %s, errorCode: %d', 0 ), dirNamePtr, @fileSpec, errorCode ); $endif if ( errorCode = 0 ) then do; directoryHandlePtr = malloc( size( dirHandle ) ); if ( directoryHandlePtr <> NULL ) then do; dirHandle.directoryName = dirNamePtr; dirHandle.fileSearchSpecString = malloc( strlen( @fileSpec ) + 1 ); if ( dirHandle.fileSearchSpecString <> NULL ) then do; call strcpy( dirHandle.fileSearchSpecString, @fileSpec ); dirHandle.statusCode, errorCode = EXIT_SUCCESS; /* * prepare for readdir() function calls */ dirHandle.searchHandle = -1; dirHandle.readComplete = False; end; /* file search string mem allocated */ else do; declare freeReturned integer; /* * there was insufficient memory, so de-allocate what we have, * and indicate the failure */ errorCode = INSUFFICIENT_MEMORY; freeReturned = free( dirNamePtr ); freeReturned = free( directoryHandlePtr ); directoryHandlePtr = NULL; end; end; else do; errorCode = INSUFFICIENT_MEMORY; end; end; $if LOGGING call addToLog( DEBUG, __FILE__, __LINE__, @( 'dirName : "%s" fileSpec : %s', 0 ), dirHandle.directoryName, dirHandle.fileSearchSpecString ); $endif /* * indicate to caller the results of this function */ errno = errorCode; return ( directoryHandlePtr ); end opendir; /* * ====================================================================================== * Name: fullyQualifiedDirName * * Design Notes: * 1. Assumes that the pathString received is a directory * 2. This function is reentrant to reduce heap size requirements * * Warning: * The pointer returned must be free()'d by the caller or a memory leak * will result * ====================================================================================== */ fullyQualifiedDirName: procedure ( pathString ) pointer reentrant public; declare pathString pointer, fullDirNamePtr pointer, tempCWD ( MAX_PATH ) char, ignoredReturn word; fullDirNamePtr = malloc( MAX_PATH + 1 ); if ( NULL <> fullDirNamePtr ) then do; /* * fully qualify the path specified, and return to the caller. we must be * careful to store our current location, and then return to it when we * get the full name of the specified path */ call getcwd( @tempCWD ); ignoredReturn = chdir( pathString ); call getcwd( fullDirNamePtr ); ignoredReturn = chdir( @tempCWD ); end; return ( fullDirNamePtr ); end fullyQualifiedDirName; /* * ====================================================================================== * Name: hasWildCards * * Design Notes: * 1. Look for the DOS wild-card characters. * 2. If any of the characters exists in the string, we return True, otherwise * we return False * ====================================================================================== */ hasWildCards: procedure ( stringIn ) boolean reentrant; declare stringIn pointer, returnCode boolean; returnCode = ( ( strchr( stringIn, '*' ) <> NULL ) or ( strchr( stringIn, '?' ) <> NULL ) ); return ( returnCode ); end hasWildCards; end openDirModule;