$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 *============================================================================ */ listFilesModule: do; $if not noID declare IDString (*) byte data ( '@(#)$Id: dls.p86 931 2024-03-02 00:50:35Z rmgillmore $', 0 ); $endif declare programVersion (*) byte data ( 'v2.2', 0 ), copyrightString (*) byte data ( '(c)Copyright 2020-2022, 2024, the ACME Software Deli', 0 ); $include (..\lib\comnDefs.ext) $include (..\lib\sysCalls.ext) $include (..\lib\DOSFind.ext) $include (..\lib\curDir.ext) $include (..\lib\fileIO.ext) $include (..\lib\string.ext) $include (..\lib\date.ext) $include (..\lib\cmdArgs.ext) $include (..\lib\videoIO.ext) $include (..\lib\diskIO.ext) $include (..\lib\ptrMath.ext) $include (..\lib\dirFunct.ext) $include (..\lib\videoIO.ext) $include (..\lib\volInfo.ext) $include (..\lib\changExt.ext) $if LOGGING $include (..\lib\logger.ext) $endif declare FILESIZE_LENGTH literally '13', /* 8.3 plus the space */ NUMBER_HEADER_LINES literally '7'; declare help boolean, pageReporting boolean, nameMask ( MAX_PATH ) char, noSwitch1 ( MAX_PATH ) char, recurse boolean, archive boolean, directory boolean, hidden boolean, $if LOGGING verbose boolean, $endif searchAttribute word; declare numberFilesReported integer, numberColumnsPerPage integer, numberLinesPerPage integer, totalNumberFiles integer, totalNumberDirectories integer, recordSearchNumber integer, totalFileSize dword; declare argDefinitions (*) commandOption_t data ( EXISTS, @help, @( '?', 0 ), @( 'help', 0 ), @( '(optional) displays this information', 0 ), EXISTS, @pageReporting, @( 'p', 0 ), @( 'page', 0 ), @( '(optional) print one page of the file list, then wait for ', 'the user to press a key to continue. ''X'' and ''Q'', ', '<ESC> and their lowercase equivalents, will discontinue the ', 'reporting of files and exit the program. Without this option, ', 'files are listed without pausing until all are listed', 0 ), EXISTS, @recurse, @( 'r', 0 ), @( 'recurse', 0 ), @( '(optional) look for directory entries that match the filespec in ', 'sub-directories, also', 0 ), EXISTS, @hidden, @( 'hid', 0 ), @( 'hidden', 0 ), @( '(optional) include those files which are hidden', 0 ), $if LOGGING EXISTS, @verbose, @( 'v', 0 ), @( 'verbose', 0 ), @( '(optional) add what may be useful information to a log file', 0 ), $endif NEXT_ARG, @nameMask, @( 'f', 0 ), @( 'fileMask', 0 ), @( '(switch optional) specifies the filename mask (following the OS rules)', 0 ), NO_SWITCH, @noSwitch1, NULL, NULL, NULL ); declare commandOptionDef commandOptionDefinition_t; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ shiftStringRight: procedure ( stringInPtr ) public; declare stringInPtr pointer, stringIn based stringInPtr (*) char; /* * shift the entire string to the right one character */ declare lastIndex integer, index integer; lastIndex = signed( strlen( stringInPtr ) ); stringIn( lastIndex + 1 ) = NUL; index = lastIndex; do while ( index >= 0 ); stringIn( index + 1 ) = stringIn( index ); index = index - 1; end; end shiftStringRight; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ fillWithCommas: procedure ( localStringPtr ) public; declare localStringPtr pointer, localString based localStringPtr (*) char; declare NUMBER_TEMP_STRINGS literally '7', DIGITS_PER_THOUSAND literally '3', SEGMENT_LENGTH literally '4'; /* DIGITS_PER_THOUSAND + 1 */ declare numberIterations integer, stringIndex integer; numberIterations = signed( strlen( localStringPtr ) ) / DIGITS_PER_THOUSAND; if ( numberIterations > 0 ) then do; stringIndex = signed( strlen( localStringPtr ) ) - ( SEGMENT_LENGTH - 1 ); end; adjustString: do; declare counter integer; counter = 0; do while ( ( counter < numberIterations ) and ( stringIndex > 0 ) ); /* * shift the string one character to the right at the current * location, then insert the comma */ call shiftStringRight( @localString( stringIndex ) ); localString( stringIndex ) = ','; counter = counter + 1; stringIndex = stringIndex - ( SEGMENT_LENGTH - 1 ); end; end adjustString; end fillWithCommas; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ reportFileSize: procedure ( fileSize ) public; declare fileSize dword; /* * 4,294,967,295 is the worst case size report */ declare fileSizeString ( FILESIZE_LENGTH ) char, tempFileSizeString ( FILESIZE_LENGTH ) char; /* * initialize the temp string * translate the size to ASCII * insert commas into the size */ call memset( @tempFileSizeString, 0, FILESIZE_LENGTH ); call sprintf( @tempFileSizeString, @( '%ld', 0 ), fileSize ); call fillWithCommas( @tempFileSizeString ); /* * now that we have the file size in ASCII, we can determine how * many leading spaces are needed */ call memset( @fileSizeString, 0, FILESIZE_LENGTH ); call memset( @fileSizeString, ' ', FILESIZE_LENGTH - strlen( @tempFileSizeString ) ); call strcat( @fileSizeString, @tempFileSizeString ); /* * now, report the file size */ call printf( @fileSizeString ); end reportFileSize; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ findCurrentDirectory: procedure ( inputPathPtr ) public; declare inputPathPtr pointer, slashDelimPtr pointer, index word, localDirName ( MAX_PATH ) char, localPathPtr pointer, localPath based localPathPtr (*) char, chdirReturned word; if ( 0 = strlen( inputPathPtr ) ) then do; /* * there is no input path specification, so we will change to the current * directory since that is all we can assume. However, there is really * no need to do any work because we are already there */ end; else do; /* * the directory path MUST use the backslash, so change them * so that everything actually works */ call strcpy( @localDirName, inputPathPtr ); index = 0; do while ( localDirName( index ) <> NUL ); if ( localDirName( index ) = '/' ) then do; localDirName( index ) = '\'; end; index = index + 1; end; /* * now, we need to get to the current directory, too. However, since * the name specified could be a relative directory specification, * we need to change to that directory */ slashDelimPtr = normalizePtr( strrchr( @localDirName, '\' ) ); if ( slashDelimPtr > NULL ) then do; /* * the last path delimiter is a slash */ localPathPtr = slashDelimPtr; localPath( 0 ) = NUL; end; /* * change to the path specified */ chdirReturned = chdir( @localDirName ); end; end findCurrentDirectory; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ reportVolume: procedure public; declare findDataRecord findDataRecordType, returnCode integer, currentDrive char, currentDirectoryString ( MAX_PATH ) char, nameMask (*) char data ( ' :\*.*', 0 ), volumeNumber dword, chdirReturned word, status word, volumeName ( diskInfoLabelLength ) char; returnCode = 0; /* * Volume in drive C is Mike-HP * Volume Serial Number is 7486-2C5D * Directory of C:\Users\Owner\Documents\work\Plm86 */ /* * if the file listing is being done for a directory which is not on the * default drive, we need to change to the specified drive, get its volume * name, and return to the default drive */ currentDrive = getCurrentDisk; call getcwd( @currentDirectoryString ); if ( returnCode = 0 ) then do; call getVolumeName( toupper( currentDirectoryString( 0 ) ) - 'A', @volumeName ); call printf( @( 'The volume in Drive %c is %s\n', 0 ), getCurrentDisk, @volumeName ); call findClose( @findDataRecord ); volumeInfoReport: do; declare volumeInfo volumeInfoType, serialNumber dword, serialNumberString ( 15 ) char; serialNumber = getDriveSerialNumber( getCurrentDisk ); if ( serialNumber <> 0 ) then do; call sprintf( @serialNumberString, @( '%X', 0 ), high( serialNumber ) ); serialNumberString( 4 ) = '-'; call sprintf( @serialNumberString( 5 ), @( '%X', 0 ), low( serialNumber ) ); serialNumberString( 9 ) = NUL; end; else do; call strcpy( @serialNumberString, @( 'N/A', 0 ) ); end; call printf( @( 'Volume Serial Number is %s\n', 0 ), @serialNumberString ); end volumeInfoReport; directoryNameReport: do; declare localDirName ( MAX_PATH ) char; call findCurrentDirectory( @nameMask ); call getcwd( @localDirName ); call printf( @( 'Directory of %s\n', 0 ), @localDirName ); end directoryNameReport; call printf( @( '\n', 0 ) ); /* * get back to where we started */ returnCode = signed( setCurrentDisk( currentDrive ) ); chdirReturned = chdir( @currentDirectoryString ); end; end reportVolume; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ $if LOGGING reportRecord: procedure ( searchNumber, dataRecordPtr ) public; declare searchNumber integer, dataRecordPtr pointer, dataRecord based dataRecordPtr findDataRecordType; call addToLog( DEBUG, __FILE__, __LINE__, @( '# %d dos: "%s" full: "%s"', 0 ), searchNumber, @dataRecord.dosName, @dataRecord.fullName ); end reportRecord; $endif /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ reportInLine: procedure ( fileAttr, fileSize, fileDate, dosFileNamePtr, fullFileNamePtr ) public; declare fileAttr dword, fileSize dword, fileDate dword, dosFileNamePtr pointer, fullFileNamePtr pointer; declare fileNameLength word, fileName ( MAX_PATH ) char, returned word, fileTime time_t, timeStruct tmStruct, tmStructPtr pointer, timeDateString ( 40 ) char, LONGEST_NAME literally '12', spacesString ( LONGEST_NAME ) char data ( ' ' ), dirString (*) char data ( '<DIR>', 0 ); if ( ( fileAttr and DIRECTORY_BIT ) <> 0 ) then do; returned = fwrite( stdout, @spacesString, ( FILESIZE_LENGTH - strlen( @dirString ) ) ); returned = fwrite( stdout, @dirString, strlen( @dirString ) ); totalNumberDirectories = totalNumberDirectories + 1; end; else do; call reportFileSize( fileSize ); totalNumberFiles = totalNumberFiles + 1; totalFileSize = totalFileSize + fileSize; end; tmStructPtr = @timeStruct; call localtime( fileDate, @tmStructPtr ); call strftime( @tmStructPtr, @( '%m/%d/%y %I:%M%p', 0 ), @timeDateString ); /* * remove the 'm' from the end of the date string (we want * 'a' or 'p', not 'am' or 'pm'), then report */ timeDateString( strlen( @timeDateString ) - 1 ) = 0; call printf( @( ' %s ', 0 ), @timeDateString ); /* * If we have a "long" name, use it; otherwise, use the * DOS name */ if ( 0 < strlen( fullFileNamePtr ) ) then do; call strcpy( @fileName, fullFileNamePtr ); end; else do; call strcpy( @fileName, dosFileNamePtr ); end; call printf( @( '%s', 0 ), @fileName ); fileNameLength = strlen( @fileName ); if ( fileNameLength < LONGEST_NAME ) then do; returned = fwrite( stdout, @spacesString, ( LONGEST_NAME - fileNameLength ) ); end; call printf( @( '\n', 0 ) ); end reportInLine; summaryReport: procedure; declare tempNumberString ( FILESIZE_LENGTH ) char, tempNumberString2 ( FILESIZE_LENGTH ) char; if ( totalNumberFiles = 1 ) then do; call memset( @tempNumberString, 0, FILESIZE_LENGTH ); call sprintf( @tempNumberString, @( '%ld', 0 ), totalFileSize ); call fillWithCommas( @tempNumberString ); call printf( @( '%s bytes in 1 file', 0 ), @tempNumberString ); end; else if ( totalNumberFiles > 1 ) then do; call memset( @tempNumberString, 0, FILESIZE_LENGTH ); call sprintf( @tempNumberString, @( '%ld', 0 ), totalFileSize ); call fillWithCommas( @tempNumberString ); call memset( @tempNumberString2, 0, FILESIZE_LENGTH ); call sprintf( @tempNumberString2, @( '%d', 0 ), totalNumberFiles ); call fillWithCommas( @tempNumberString2 ); call printf( @( '%s bytes in %s files', 0 ), @tempNumberString, @tempNumberString2 ); end; if ( totalNumberDirectories = 0 ) then do; /* * no directories, do nothing */ end; else if ( totalNumberDirectories = 1 ) then do; call printf( @( ' (1 directory)', 0 ) ); end; else if ( totalNumberDirectories > 1 ) then do; /* * initialize the temp string * translate the size to ASCII * insert commas into the size */ call memset( @tempNumberString, 0, FILESIZE_LENGTH ); call sprintf( @tempNumberString, @( '%d', 0 ), totalNumberDirectories ); call fillWithCommas( @tempNumberString ); call printf( @( ' (%s directories)', 0 ), @tempNumberString ); end; end summaryReport; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: * * Description: * * Returns: * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ /* * <DIR> 10/28/20 5:12p .svn * <DIR> 10/29/20 4:02p SCRIPTS * 91,299,318 2/19/20 11:35p 16BitTools.zip * 46 10/15/19 8:14p 8dot3Path.cmd * 20,916 10/28/20 5:16p acmeDir.exe * 67,891 10/28/20 5:16p acmeDir.lst */ reportFiles: procedure integer public; declare returnCode integer, searchStarted boolean, findDataRecord findDataRecordType; searchStarted = False; totalNumberFiles = 0; totalNumberDirectories = 0; recordSearchNumber = 0; totalFileSize = 0; call screenSize( @numberColumnsPerPage, @numberLinesPerPage ); returnCode = 0; do while ( 0 = returnCode ); if ( not searchStarted ) then do; /* * search attributes */ if ( archive ) then findDataRecord.fileAttr = findDataRecord.fileAttr or ARCHIVE_BIT; if ( directory ) then findDataRecord.fileAttr = findDataRecord.fileAttr or DIRECTORY_BIT; if ( hidden ) then findDataRecord.fileAttr = findDataRecord.fileAttr or HIDDEN_BIT; /* * filename mask (for what are we searching) */ if ( ( strlen( @noSwitch1 ) > 0 ) and ( strlen ( @nameMask ) = 0 ) ) then call strcpy( @nameMask, @noSwitch1 ); else if ( strlen( @nameMask ) = 0 ) then call strcpy( @nameMask, @( '*.*', 0 ) ); if ( pageReporting ) then do; /* * if we are to report only one screen at a time, we need * to know how many lines there are on the page. The * size is reported as # columns and # rows. Since we don't * need the # of columns, we will ignore them */ numberFilesReported = NUMBER_HEADER_LINES; end; else do; numberFilesReported = 0; numberLinesPerPage = 32767; end; call reportVolume; returnCode = signed( findFirst( @nameMask, @findDataRecord ) ); recordSearchNumber = recordSearchNumber + 1; searchStarted = True; end; else do; returnCode = signed( findNext( @findDataRecord ) ); recordSearchNumber = recordSearchNumber + 1; end; reportBlock: do; if ( 0 = returnCode ) then do; call reportInLine( findDataRecord.fileAttr, findDataRecord.fileSize( 1 ), findDataRecord.modifiedTime( 0 ), @findDataRecord.dosName, @findDataRecord.fullName ); returnCode = 0; numberFilesReported = numberFilesReported + 1; if ( pageReporting ) then do; pageReportingBlock: do; declare keyCode integer; if ( numberFilesReported > ( numberLinesPerPage - 2 ) ) then do; numberFilesReported = 0; call printf( @( '%c%c More %c%c \r', 0 ), LINE_SINGLE_HORIZONTAL, LINE_SINGLE_HORIZONTAL, LINE_SINGLE_HORIZONTAL, LINE_SINGLE_HORIZONTAL ); keyCode = ci; if ( ( keyCode = 'X' ) or ( keyCode = 'x' ) or ( keyCode = 'Q' ) or ( keyCode = 'q' ) ) then do; returnCode = 1; end; end; /* are we to quit reporting? */ end pageReportingBlock; end; /* are we pageReporting? */ end; /* if there is a file to report */ end reportBlock; end; /* while returnCode is zero */ call findClose( @findDataRecord ); call summaryReport; return ( returnCode ); end reportFiles; /* * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * Name: main() * * Description: * Program Entry Point * * Returns: * integer exit code * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ main: procedure ( numArgs, argStrPtrs, envPtr ) integer public; declare numArgs word, argStrPtrs pointer, argStrs based argStrPtrs (*) pointer, envPtr pointer; declare returnCode integer; returnCode = 0; /* * we want to get all directories in the specified directory */ directory = True; call greetUser( argStrs( 0 ), @programVersion, @copyrightString ); /* * in C or C++, these definitions may be done in a const statement, but * in PL/M-86 data statements, there are restrictions */ commandOptionDef.numberOptions = signed( length( argDefinitions ) ); commandOptionDef.commandOptions = @argDefinitions; commandOptionDef.delimiterChar = '-'; returnCode = getCmdOptions( signed( numArgs ), argStrPtrs, @commandOptionDef ); if ( returnCode = 0 ) then do; if ( help ) then do; call reportCmdOptions( argStrs( 0 ), @commandOptionDef ); end; else do; $if LOGGING if ( verbose ) then do; declare logFileName ( 260 ) char; call strcpy( @logFileName, argStrs( 0 ) ); call changeExtension( @logFileName, @( 'log', 0 ) ); call openLogFile( @logFileName ); call changeLogLevel( DEBUG ); end; $endif returnCode = reportFiles; end; end; return ( returnCode ); end main; end listFilesModule;