$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 *============================================================================ */ dosFindModule: do; $if not noID declare IDString (*) byte data ( '@(#)dosFind.p86 $Author: rmgillmore $ $Date:: 2025-05-04 19:35:39#$:', 0 ); $endif $set (DOSFindSource) $include (..\lib\sysCalls.ext) $include (..\lib\dosFind.ext) $include (..\lib\fileIO.ext) $include (..\lib\dirFunct.ext) $include (..\lib\string.ext) $include (..\lib\date.ext) declare DOS_DATE_TIME literally '1'; declare dtaPtr pointer, dta based dtaPtr dta_t, longNamesSupported boolean, longNamesChecked boolean; dwordToQword: procedure ( dwordValue, qwordPtr ) reentrant; declare dwordValue dword, qwordPtr pointer, qwordValue based qwordPtr qword; qwordValue( 0 ) = dwordValue; qwordValue( 1 ) = 0; end dwordToQword; swapDwords: procedure ( qwordPtr ) reentrant; declare qwordPtr pointer, qwordValue based qwordPtr qword, tempDword dword; tempDword = qwordValue( 0 ); qwordValue( 0 ) = qwordValue( 1 ); qwordValue( 1 ) = tempDword; end swapDwords; DTAtoFindDataRecord: procedure ( findDataRecordPtr, dtaRecordPtr ) reentrant; declare findDataRecordPtr pointer, findDataRecord based findDataRecordPtr findDataRecordType, dtaRecordPtr pointer, dta based dtaRecordPtr dta_t; declare dateTimeStamp time_t; call memcpy( @findDataRecord.dtaData, @dta, size( dta ) ); findDataRecord.fileAttr = double( dta.fileFoundAttribute ); dateTimeStamp = dos2time_t( dta.timeStamp, dta.dateStamp ); call dwordToQword( dateTimeStamp, @findDataRecord.creationTime ); call dwordToQword( dateTimeStamp, @findDataRecord.accessTime ); call dwordToQword( dateTimeStamp, @findDataRecord.modifiedTime ); call strcpy( @findDataRecord.dosName, @dta.fileName ); findDataRecord.fullName( 0 ) = NUL; findDataRecord.fileSize( 1 ) = dta.fileSize; end DTAtoFindDataRecord; /* * ====================================================================================== * findFirst(), findNext() and findClose() are to be used together. * * NOTE: A call to findFirst() should always be followed by either a findNext() * and findClose() or just a findClose() * ====================================================================================== */ /* This is NOT reentrant because it creates invalid nested procedures */ findFirst: procedure ( fileNamePtr, findDataRecordPtr ) word public; declare fileNamePtr pointer, findDataRecordPtr pointer, findDataRecord based findDataRecordPtr findDataRecordType; declare returnCode word, cpuRegisters; findFirstLong: procedure; wordRegs.Ax = 714eh; if ( 0 = findDataRecord.fileAttr ) then do; findDataRecord.fileAttr = ARCHIVE_BIT or DIRECTORY_BIT or VOLUME_BIT or SYSTEM_BIT or HIDDEN_BIT or READONLY_BIT; end; wordRegs.Cx = findDataRecord.fileAttr; wordRegs.Si = DOS_DATE_TIME; /* use DOS date/time values */ wordRegs.Ds = selector$of( fileNamePtr ); wordRegs.Dx = offset$of( fileNamePtr ); wordRegs.Es = selector$of( findDataRecordPtr ); wordRegs.Di = offset$of( findDataRecordPtr ); call int86( DOS_CALL, @wordRegs ); if ( carryClear( @wordRegs ) ) then do; findDataRecord.findHandle = wordRegs.Ax; if ( 0 = strlen( @findDataRecord.dosName ) ) then do; call strcpy( @findDataRecord.dosName, @findDataRecord.fullName ); call strupr( @findDataRecord.dosName ); end; end; else do; returnCode, errno = wordRegs.Ax; end; end findFirstLong; findFirstShort: procedure; /* * on this system, long filenames are not supported, so we must * use the older DOS findfirst/findnext records */ /* * get the address of the Disk Transfer Area (DTA) */ dtaPtr = getDTA; if ( 0 = findDataRecord.fileAttr ) then do; findDataRecord.fileAttr = ARCHIVE_BIT or DIRECTORY_BIT or VOLUME_BIT or SYSTEM_BIT or HIDDEN_BIT or READONLY_BIT; end; /* * get the first file and its characteristics */ byteRegs.Ah = 4eh; wordRegs.Ds = selector$of( fileNamePtr ); wordRegs.Dx = offset$of( fileNamePtr ); wordRegs.Cx = findDataRecord.fileAttr; call int86( DOS_CALL, @wordRegs ); if ( not carryClear( @wordRegs ) ) then do; /* * copy the full name so the caller can use it, then * close the "find handle" (which was returned in Ax) */ returnCode, errno = wordRegs.Ax; end; else do; /* * copy all of the information we retrieved from the current * DTA to the caller's records */ call DTAtoFindDataRecord( @findDataRecord, @dta ); returnCode, errno = 0; end; end findFirstShort; if ( not longNamesChecked ) then do; longNamesChecked = True; longNamesSupported = lfnSupported; end; returnCode, errno = 0; if ( longNamesSupported and ( findDataRecord.fileAttr <> VOLUME_BIT ) ) then do; call findFirstLong; end; else do; call findFirstShort; end; return ( returnCode ); end findFirst; /* This is NOT reentrant because it creates invalid nested procedures */ findNext: procedure ( findDataRecordPtr ) word public; declare findDataRecordPtr pointer, findDataRecord based findDataRecordPtr findDataRecordType; declare returnCode word, cpuRegisters; findNextLong: procedure; wordRegs.Ax = 0714fh; wordRegs.Bx = findDataRecord.findHandle; wordRegs.Si = DOS_DATE_TIME; wordRegs.Es = selector$of( findDataRecordPtr ); wordRegs.Di = offset$of( findDataRecordPtr ); call int86( DOS_CALL, @wordRegs ); if ( carryClear( @wordRegs ) ) then do; if ( 0 = strlen( @findDataRecord.dosName ) ) then do; call strcpy( @findDataRecord.dosName, @findDataRecord.fullName ); call strupr( @findDataRecord.dosName ); end; end; else do; returnCode, errno = wordRegs.Ax; end; end findNextLong; findNextShort: procedure; /* * on this system, long filenames are not supported, so we must * use the older DOS findfirst/findnext records */ dtaPtr = getDTA; /* * restore the data from the findFirst or the previous findNext */ call memcpy( @dta, @findDataRecord.dtaData, size( dta ) ); /* * get the next file and its characteristics */ byteRegs.Ah = 4fh; call int86( DOS_CALL, @wordRegs ); if ( not carryClear( @wordRegs ) ) then do; /* * copy the full name so the caller can use it, then * close the "find handle" (which was returned in Ax) */ returnCode, errno = wordRegs.Ax; end; else do; /* * copy all of the information we retrieved from the current * DTA to the caller's records */ call DTAtoFindDataRecord( @findDataRecord, @dta ); returnCode, errno = 0; end; end findNextShort; returnCode, errno = 0; if ( longNamesSupported ) then do; call findNextLong; end; else do; call findNextShort; end; return ( returnCode ); end findNext; findClose: procedure ( findDataRecordPtr ) reentrant public; declare findDataRecordPtr pointer, findDataRecord based findDataRecordPtr findDataRecordType; declare cpuRegisters; if ( longNamesSupported ) then do; wordRegs.Ax = 71a1h; wordRegs.Bx = findDataRecord.findHandle; call int86( DOS_CALL, @wordRegs ); end; else do; /* * with no support for long filenames, there is no handle to close, therefore * no further work is necessary */ end; end findClose; end dosFindModule;