MODULE D; (* * REPERTOIRE * Release 1.6 * By Charles Bradford and Cole Brecheen * (c) Copyright 1985-1990 PMI * Green Bay Wisconsin * All rights reserved * (414) 468-6040 * * $Header: D:/logfiles/demos/d.mov 1.2 03 Dec 1990 07:01:24 coleb $ * *) (* Directory Management With D.MOD D.MOD is a sample director manager. Its purpose is to illustrate the use of Repertoire's Drectory module. See section 7.10. It makes more convenient the four most frequently performed operations in the PC/MS-DOS operating system: listing, deleting, renaming, and copying files. It lists directories using both the left and right sides of the screen; it allows prompted file erasure; it allows prompted renaming and directory- shifting of files; and it allows prompted file copying and moving with safety checks to prevent accidental erasures. Listing Directories. To list the entire current directory of the default drive using both sides of the screen, enter "d" and press . If the screen fills, the program suspends the listing until you press a key. Unlike the MS-DOS DIR /W (wide) command, which lists only file names, D provides complete information about each file. To list the entire current directory of a drive other than the default drive (say, the A: drive), type "d a" and press . Notice that D does not require a trailing colon to specify a drive letter--it assumes that any single-character program parameter is a drive letter. To list all files with extensions of .ltr in the subdirectory \letters, enter "d \letters\*.ltr" or "d letters\*.ltr". If \letters is not on the default drive, enter "d a:letters\*.ltr" or "d a:\letters\*.ltr". To list all the files in the \letters subdirectory, enter "d letters\". The trailing slash tells D to list a subdirectory instead of a file in the current directory named letters. D expands the power of the asterisk as an expansion character. For example, to list all files beginning with the letter A, regardless of extension, you need only enter "a*" as D's command-line parameter. Entering "art*xt" would list all files beginning with 'art' and ending (in the extension part of the name) with 'xt'. It would list ARTICLE.TXT, for example, or ARTWORK.EXT. Multiple asterisks are allowed in a single command. Erasing Files. Suppose you want to erase all documents in the \letters subdirectory on drive B: that are more than a year old. Using D, you can enter "d b:\letters\/e" and press . The trailing /e tells D to invoke its file-erasing function. D will display a full directory entry for the first file in the \letters subdirectory, followed by the prompt "Erase?" If the date indicates that the file is more than a year old, you can press Y to erase it. Press N to leave the file on the disk. D will continue to display directory entries and ask for permission to erase them until it can find no more matching files or until you press Q, for quit. Copying Files. D allows you to decide both whether to copy each matching file and whether to destroy any files of the same name on the destination disk. Suppose, for example, you want to make sure that two disks each have the same 30-file set of implementation modules for a large program you're developing. For each of the 30 files, you have to determine which disk has either the larger or the more recent version of the file. On the DOS command line, enter "d a:*imp/c". The trailing /c tells D that it is to COPY matching files. The program will ask, "Copy to what drive:\path?" Enter "b" and press . No trailing colon is required. You can copy to the current drive by merely pressing . For each of the 30 implementation modules on drive A:, D will display complete directory information and ask, "Copy?" Press Y to copy the file, N to skip it, and Q to terminate the program. If you press Y, D will determine whether the destination disk contains a file of the same name. If it does, D will display complete directory information for that file and ask, "Destroy?" You can look at the date and, if the file on the destination disk is less recent than the file on the source disk, press Y. If you press N, D will prompt you to "Enter new name, or to skip." To copy the file to the destination disk under a different name, type in the new name and press . D will again determine whether the destination disk contains a file of the same name. If it doesn't, D will copy the file. If you only press when prompted for a new name, D will not copy the file on the source disk, and will go to the next matching file. After you've answered yes or no to D's prompts on each of the 30 .MOD files on drive A:, reverse the process by entering "d b:*.MOD/c" and entering "a" when asked "Copy to what drive:\path?" Each time D asks whether you want to destroy a file on drive A:, press N if the file is identical. At the end of the process you'll have the most recent 30-file set of implementation modules on each disk. Renaming Files. D allows you to rename files and move them between directories without recopying them using much the same syntax that you employ with its other functions. For example, to move selected files with .LTR extensions from the root directory of your hard disk to the \LTRS subdirectory, enter: C>d *ltr \ltrs\*ltr/r D will respond as follows: F1.LTR 50950 2-02-86 8:04p |Rename \ltrs\F1.ltr? no F3.LTR 23557 2-02-86 1:31p |Rename \ltrs\F3.ltr? yes F2.LTR 34821 2-02-86 2:59p |Rename \ltrs\F2.ltr? no C>_ The trailing /r is the command that tells D to rename the files that match its first command-line parameter. D renames the files in accordance with the same rules used in the DOS RENAME operation, with the exception that D does not require separate asterisks on each side of the period. Moving Files A trailing /m modifies both the renaming and the copying operations. A trailing /m causes D first to determine whether the two file specifications are on different drives. If they are, D copies files to the destination drive just as it does when you use the /c command, but it also erases from the source drive any files that are successfully copied. If the files are on the same drive, a trailing /m causes D to erase any conflicting file names from the destination directory; the rename operation normally aborts if a file of the same name is in the destination directory. Batch Mode A trailing /b causes any yes/no prompt automatically to be answered Yes. *) IMPORT BigSets; IMPORT Drectory; IMPORT EnvironUtils; IMPORT ErrorManager; IMPORT ErrStrgs; IMPORT KbdInput; IMPORT LowLevel; IMPORT M2Strings; IMPORT Numbers; IMPORT HandleIO; IMPORT FAPI; IMPORT PosUtils; IMPORT StrConv; IMPORT StrEdit; IMPORT StringIO; IMPORT SYSTEM; IMPORT InitCompilerMods; CONST blank = ' '; null = ''; VAR SecondStr, DirMask: ARRAY [0..79] OF CHAR; FileInfo: Drectory.FileInfoRec; BatchMode, exiting, moving, renaming, copying, deleting, OnTheLeft : BOOLEAN; SavedMessage : CARDINAL; TheDrive : CHAR; BytesPerSector, SectorsPerCluster, ClustersOnDisk: CARDINAL; KindOfDisk : Drectory.DiskTypes; ClusterSize : LONGINT; PROCEDURE yes() : BOOLEAN; VAR choice : CARDINAL; BEGIN (*yes*) IF BatchMode THEN StringIO.WriteStr(StringIO.outp, 'yes'); RETURN TRUE; END; choice := KbdInput.KeyHit( KbdInput.YorN ); IF KbdInput.CAPkey(choice) = ORD('Y') THEN StringIO.WriteStr(StringIO.outp, 'yes'); RETURN TRUE; ELSE StringIO.WriteStr(StringIO.outp, 'no'); IF KbdInput.CAPkey(choice) = ORD('Q') THEN exiting := TRUE; END; RETURN FALSE; END; END yes; PROCEDURE IsDriveLetter( TheStr: ARRAY OF CHAR ): BOOLEAN; BEGIN RETURN (M2Strings.Length(TheStr) = 1) AND ( (CAP(TheStr[0]) >= 'A') AND (CAP(TheStr[0]) <= 'Z')); END IsDriveLetter; PROCEDURE Punctuate( VAR TheStr: ARRAY OF CHAR ); VAR index, PlaceCount: CARDINAL; BEGIN index := M2Strings.Length( TheStr ); IF index > 0 THEN DEC( index ); PlaceCount := 2; REPEAT IF PlaceCount > 0 THEN IF index > 0 THEN DEC( index ); END; DEC( PlaceCount ); ELSE PlaceCount := 2; M2Strings.Insert( ',', TheStr, index ); DEC( index ); END; UNTIL (index = 0) OR (NOT PosUtils.IsNumericChar(TheStr[index])); END; END Punctuate; PROCEDURE DifferentDrives( str1, str2: ARRAY OF CHAR ): BOOLEAN; VAR DriveChar1, DriveChar2: CHAR; BEGIN IF IsDriveLetter(str1) OR (str1[1] = ':') THEN DriveChar1 := CAP(str1[0]); ELSE DriveChar1 := CAP(Drectory.GetDefaultDrive()); END; IF IsDriveLetter(str2) OR (str2[1] = ':') THEN DriveChar2 := CAP(str2[0]); ELSE DriveChar2 := CAP(Drectory.GetDefaultDrive()); END; RETURN DriveChar1 # DriveChar2; END DifferentDrives; PROCEDURE MakeInfoStr( FileInfo: Drectory.FileInfoRec; VAR TheStr: ARRAY OF CHAR ); VAR dumstr: ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr( FileInfo.name, TheStr ); WHILE M2Strings.Length(TheStr) < 12 DO StrEdit.Append( TheStr, ' ' ); END; IF Drectory.SubdirectoryFile IN FileInfo.attribute THEN StrEdit.Append( TheStr, ' ' ); ELSE StrConv.LongIntegerToStr( FileInfo.size, 0, dumstr ); IF FileInfo.size < 1000000 THEN (* No room for commas if the string is longer than 6 chars. *) Punctuate( dumstr ); END; StrEdit.RightJustify( dumstr, 8 ); StrEdit.Append( TheStr, dumstr ); END; StrConv.DateToStr( FileInfo.month, FileInfo.day, FileInfo.year, dumstr ); StrEdit.Append( TheStr, ' ' ); StrEdit.Append( TheStr, dumstr ); StrConv.TimeToStr( FileInfo.hour, FileInfo.minute, FileInfo.second, FileInfo.second, dumstr ); M2Strings.Delete( dumstr, M2Strings.Length(dumstr) - 7, 6 ); StrEdit.Append( TheStr, ' ' ); StrEdit.Append( TheStr, dumstr ); END MakeInfoStr; PROCEDURE SayNotFound(); VAR BytesFree : LONGINT; label, BytesFreeStr : ARRAY [0..79] OF CHAR; BEGIN ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, Drectory.FreeDiskSpace(TheDrive,BytesFree) ); StrConv.LongIntegerToStr(BytesFree, 1, BytesFreeStr); Punctuate( BytesFreeStr ); Drectory.VolumeLabel(TheDrive, label); StringIO.WriteStr(StringIO.outp, 'File not found on '); StringIO.WriteStr(StringIO.outp, label); StringIO.WriteStr(StringIO.outp, '; '); StringIO.WriteStr(StringIO.outp, BytesFreeStr); StringIO.WriteEol(StringIO.outp, ' bytes free.'); END SayNotFound; PROCEDURE WriteBarAndPrompt( FileInfoStr, ActionProposed, NewName: ARRAY OF CHAR ); VAR tmpstr: ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr( FileInfoStr, tmpstr ); StrEdit.Append( tmpstr, blank ); StrEdit.Append( tmpstr, CHR(186) ); StrEdit.Append( tmpstr, blank ); StrEdit.Append( tmpstr, ActionProposed ); IF M2Strings.Length(NewName) > 0 THEN StrEdit.Append( tmpstr, blank ); StrEdit.Append( tmpstr, NewName ); END; StrEdit.Append( tmpstr, '? ' ); StringIO.WriteStr( StringIO.outp, tmpstr ); END WriteBarAndPrompt; PROCEDURE ExpandAsterisk(VAR TheMask : ARRAY OF CHAR); VAR base, spot, indx : CARDINAL; BEGIN (*ExpandAsterisk*) indx := M2Strings.Pos('*',TheMask); WHILE indx<=HIGH(TheMask) DO TheMask[indx] := '?'; base := indx; WHILE (base > 0) AND NOT (M2Strings.Pos( TheMask[base-1], ':\/' ) <= 2) DO DEC(base); END; spot := PosUtils.Positn( '.', TheMask, base ); IF (spot>HIGH(TheMask)) THEN (*There's no period.*) StrEdit.InsertTimes('?', TheMask, indx, 12 - (M2Strings.Length(TheMask) - base) ); TheMask[ base + 8 ] := '.'; ELSIF (spot>indx) THEN (*Asterisk was in the filename part, not extension part.*) StrEdit.InsertTimes('?', TheMask, indx, 8 - (spot - base) ); ELSE (*Asterisk was in the extension part.*) StrEdit.InsertTimes('?', TheMask, indx, 4 - (M2Strings.Length(TheMask) - spot) ); END; indx := M2Strings.Pos('*',TheMask); END; END ExpandAsterisk; PROCEDURE PrepareDirMask(VAR TheStr : ARRAY OF CHAR); VAR spot : CARDINAL; BEGIN TheDrive := 'z'; (*for default*) deleting := FALSE; copying := FALSE; renaming := FALSE; moving := FALSE; StrEdit.CrunchBlanks(TheStr); IF (M2Strings.Length(TheStr)>=2) THEN IF TheStr[M2Strings.Length(TheStr)-2]='/' THEN IF CAP( TheStr[M2Strings.Length(TheStr)-1] ) = 'B' THEN M2Strings.Delete(TheStr, M2Strings.Length(TheStr)-2, 2); StrEdit.CutTrailingChars(blank, TheStr); BatchMode := TRUE; ELSE BatchMode := FALSE; END; deleting := CAP( TheStr[M2Strings.Length(TheStr)-1] ) = 'E'; copying := CAP( TheStr[M2Strings.Length(TheStr)-1] ) = 'C'; renaming := CAP( TheStr[M2Strings.Length(TheStr)-1] ) = 'R'; moving := CAP( TheStr[M2Strings.Length(TheStr)-1] ) = 'M'; M2Strings.Delete(TheStr, M2Strings.Length(TheStr)-2, 2); StrEdit.CutTrailingChars(blank, TheStr); END; END; spot := M2Strings.Pos(blank,TheStr); IF spot<=HIGH(TheStr) THEN M2Strings.Copy(TheStr, spot+1, M2Strings.Length(TheStr)-1-spot, SecondStr); M2Strings.Delete(TheStr, spot, M2Strings.Length(SecondStr)+1); ELSE SecondStr := null; END; IF M2Strings.Length(TheStr) = 0 THEN StrEdit.AssignStr( '*.*',TheStr); ExpandAsterisk( TheStr ); RETURN; END; IF M2Strings.Pos( '..', TheStr ) = ( HIGH(TheStr) - 1 ) THEN StrEdit.Append(TheStr, '\*.*'); END; IF ((TheStr[M2Strings.Length(TheStr)-1]='\') OR (TheStr[M2Strings.Length(TheStr)-1]='/')) THEN StrEdit.Append(TheStr, '*.*'); ELSIF IsDriveLetter(TheStr) THEN StrEdit.Append(TheStr, ':*.*'); END; IF TheStr[1]=':' THEN TheDrive := TheStr[0]; IF M2Strings.Length(TheStr)=2 THEN StrEdit.Append(TheStr, '*.*'); END; END; ExpandAsterisk( TheStr ); END PrepareDirMask; PROCEDURE SplitPathName(VAR PathName, TheMask : ARRAY OF CHAR); VAR TmpStr : ARRAY [0..79] OF CHAR; spot : CARDINAL; BEGIN (*SplitPathName*) IF IsDriveLetter(TheMask) THEN StrEdit.AssignStr( TheMask, PathName ); StrEdit.Append( PathName, ':' ); StrEdit.AssignStr( null, TheMask ); RETURN; END; StrEdit.AssignStr( null, PathName); spot := 0; WHILE spot+1<=M2Strings.Length(TheMask) DO IF ((TheMask[spot]='\') OR (TheMask[spot]='/' ) OR (TheMask[spot]=':')) THEN M2Strings.Copy(TheMask, 0, spot+1, TmpStr); StrEdit.Append(PathName, TmpStr); M2Strings.Delete(TheMask, 0, 1+spot); spot := 0; ELSE INC(spot); END; END; END SplitPathName; PROCEDURE EraseOneFile( PathName, FileName: ARRAY OF CHAR ): BOOLEAN; VAR Fname : ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr( FileName, Fname ); M2Strings.Insert(PathName, Fname, 0); IF Drectory.DeleteFile(Fname) = StringIO.NoError THEN RETURN TRUE; ELSE RETURN FALSE; END; END EraseOneFile; PROCEDURE EraseFiles(); VAR PathName, Fname, TmpStr : ARRAY [0..79] OF CHAR; DirHandle: CARDINAL; BEGIN (*EraseFiles*) DirHandle := 0FFFFH; SavedMessage := Drectory.FindFirstFile( DirMask, DirHandle, FileInfo ); IF SavedMessage # StringIO.NoError THEN SayNotFound(); RETURN; END; SplitPathName(PathName, DirMask); REPEAT MakeInfoStr( FileInfo, TmpStr ); IF NOT (Drectory.SubdirectoryFile IN FileInfo.attribute) THEN WriteBarAndPrompt( TmpStr, 'Erase', '' ); IF yes() THEN IF NOT EraseOneFile( PathName, FileInfo.name ) THEN END; END; StringIO.WriteEol(StringIO.outp, null); END; SavedMessage := Drectory.FindNextFile( DirHandle, FileInfo ); UNTIL (SavedMessage#StringIO.NoError) OR exiting; IF 0 # FAPI.DOSFINDCLOSE( DirHandle ) THEN END; END EraseFiles; PROCEDURE CopyFiles(); VAR PathName1, PathName2 : ARRAY [0..79] OF CHAR; OldHandle, DirHandle : CARDINAL; OldHandleOpen : BOOLEAN; SavedMessage : CARDINAL; SavedFileMode : BITSET; TmpStr1, NewFullName, OldFullName : ARRAY [0..79] OF CHAR; PROCEDURE CheckDest( VAR PathName, FileName: ARRAY OF CHAR ): BOOLEAN; VAR TmpStr, NewFileName: ARRAY [0..79] OF CHAR; FinishedChecking : BOOLEAN; TmpFileInfo: Drectory.FileInfoRec; DirHandle: CARDINAL; BEGIN DirHandle := 0FFFFH; REPEAT FinishedChecking := TRUE; M2Strings.Concat(PathName, FileName, NewFileName); SavedMessage := Drectory.FindFirstFile(NewFileName, DirHandle, TmpFileInfo); IF NOT ((SavedMessage = StringIO.FileNotFound) OR (SavedMessage= StringIO.NoMoreFiles)) THEN IF SavedMessage # StringIO.NoError THEN ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, SavedMessage ); ELSE MakeInfoStr( TmpFileInfo, TmpStr ); StringIO.WriteEol(StringIO.outp, null); WriteBarAndPrompt( TmpStr, 'Destroy', '' ); IF NOT yes() THEN StringIO.WriteEol(StringIO.outp, null); StringIO.WriteStr(StringIO.outp, 'Enter new name, or to skip: '); StringIO.ReadStr(StringIO.inp, FileName); IF M2Strings.Length(FileName)=0 THEN IF 0 # FAPI.DOSFINDCLOSE( DirHandle ) THEN END; RETURN FALSE; ELSE SplitPathName( PathName, FileName ); FinishedChecking := FALSE; END; END; END; END; UNTIL FinishedChecking OR exiting; IF 0 # FAPI.DOSFINDCLOSE( DirHandle ) THEN END; RETURN TRUE; END CheckDest; BEGIN (*CopyFiles*) DirHandle := 0FFFFH; IF Drectory.FindFirstFile( DirMask, DirHandle, FileInfo ) # StringIO.NoError THEN SayNotFound(); RETURN; END; IF M2Strings.Length(SecondStr)=0 THEN StringIO.WriteStr(StringIO.outp, ' Copy to what drive:\path? '); StringIO.ReadStr(StringIO.inp, SecondStr ); END; SplitPathName( PathName2, SecondStr ); SplitPathName( PathName1, DirMask ); REPEAT MakeInfoStr( FileInfo, TmpStr1 ); IF NOT (Drectory.SubdirectoryFile IN FileInfo.attribute) THEN WriteBarAndPrompt( TmpStr1, 'Copy', '' ); IF yes() THEN M2Strings.Concat(PathName1, FileInfo.name, OldFullName); SavedFileMode := HandleIO.DefaultFileMode; HandleIO.DefaultFileMode := BITSET{ 5 }; (* Read-only access; deny no other access *) ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, HandleIO.OpenFile(OldHandle, OldFullName) ); OldHandleOpen := TRUE; HandleIO.DefaultFileMode := SavedFileMode; IF M2Strings.Length(SecondStr) = 0 THEN StrEdit.AssignStr( FileInfo.name, TmpStr1 ); ELSE StrEdit.AssignStr( SecondStr, TmpStr1 ); END; IF CheckDest( PathName2, TmpStr1 ) THEN M2Strings.Concat(PathName2, TmpStr1, NewFullName); ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, Drectory.CopyFile(OldHandle, NewFullName)); IF moving THEN ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, HandleIO.CloseHandle(OldHandle)); OldHandleOpen := FALSE; IF NOT EraseOneFile( PathName1, FileInfo.name ) THEN HALT; END; END; END; IF OldHandleOpen THEN ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, HandleIO.CloseHandle(OldHandle)); OldHandleOpen := FALSE; END; END; StringIO.WriteEol(StringIO.outp, null); END; SavedMessage := Drectory.FindNextFile( DirHandle, FileInfo ); UNTIL (SavedMessage # StringIO.NoError) OR exiting; IF 0 # FAPI.DOSFINDCLOSE( DirHandle ) THEN END; END CopyFiles; PROCEDURE RenameFiles(); VAR mask2, path2, path1 : ARRAY [0..79] OF CHAR; PROCEDURE GetNewName(VAR InfoStr, OldName, NewName : ARRAY OF CHAR); VAR TmpOld : ARRAY [0..79] OF CHAR; ndx : CARDINAL; BEGIN (*GetNewName*) StrEdit.AssignStr( null, NewName); M2Strings.Copy(InfoStr, 0, M2Strings.Pos(blank,InfoStr), OldName); StrEdit.AssignStr( OldName, TmpOld); ndx := M2Strings.Pos('.',TmpOld); IF ndx>HIGH(TmpOld) THEN StrEdit.Append(TmpOld, '*'); ExpandAsterisk(TmpOld); ELSIF (ndx<8) THEN M2Strings.Insert('*', TmpOld, ndx); ExpandAsterisk(TmpOld); END; ndx := 0; REPEAT IF mask2[ndx]='?' THEN IF TmpOld[ndx]#'?' THEN StrEdit.Append(NewName, TmpOld[ndx]); END; ELSE StrEdit.Append(NewName, mask2[ndx]); END; INC(ndx); UNTIL (ndx>=M2Strings.Length(mask2)); END GetNewName; VAR TmpStr1, TmpStr2, OldName, InfoStr, NewName : ARRAY [0..79] OF CHAR; DirHandle: CARDINAL; BEGIN (*RenameFiles*) DirHandle := 0FFFFH; IF Drectory.FindFirstFile(DirMask, DirHandle, FileInfo) # StringIO.NoError THEN SayNotFound(); RETURN; END; IF M2Strings.Length(SecondStr)=0 THEN StringIO.WriteStr(StringIO.outp, ' New name: '); StringIO.ReadStr(StringIO.inp, mask2); ELSE StrEdit.AssignStr( SecondStr, mask2); END; IF M2Strings.Length(mask2)=0 THEN StrEdit.AssignStr( '\*.*', mask2); END; IF DifferentDrives( DirMask, mask2 ) THEN copying := TRUE; renaming := FALSE; RETURN; END; SplitPathName(path2, mask2); SplitPathName(path1, DirMask); IF M2Strings.Length(path2)=0 THEN StrEdit.AssignStr( path1, path2); END; ExpandAsterisk(mask2); REPEAT IF NOT (Drectory.SubdirectoryFile IN FileInfo.attribute) THEN MakeInfoStr( FileInfo, InfoStr ); GetNewName(InfoStr, OldName, NewName); StrEdit.AssignStr( path2, TmpStr1); StrEdit.Append(TmpStr1, NewName); WriteBarAndPrompt( InfoStr, 'Rename', TmpStr1 ); IF yes() THEN M2Strings.Concat(path1, OldName, TmpStr1); M2Strings.Concat(path2, NewName, TmpStr2); IF moving THEN IF Drectory.RenameFile(TmpStr1,TmpStr2) # StringIO.NoError THEN IF NOT EraseOneFile( path2, NewName ) THEN HALT; ELSE ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, Drectory.RenameFile(TmpStr1,TmpStr2)); END; END; ELSE ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, Drectory.RenameFile(TmpStr1,TmpStr2)); END; END; StringIO.WriteEol(StringIO.outp, null); ELSE StringIO.WriteStr(StringIO.outp, "Can't rename '"); StringIO.WriteStr(StringIO.outp, FileInfo.name); StringIO.WriteEol(StringIO.outp, "' "); END; UNTIL (Drectory.FindNextFile(DirHandle, FileInfo) # StringIO.NoError) OR exiting; IF 0 # FAPI.DOSFINDCLOSE( DirHandle ) THEN END; END RenameFiles; PROCEDURE UpToClusterSize(VAR TheRealSize : LONGINT); VAR clusters, NewSize : LONGINT; BEGIN clusters := TheRealSize DIV ClusterSize; INC(clusters); NewSize := ClusterSize * clusters; IF (TheRealSize + ClusterSize) # NewSize THEN TheRealSize := NewSize; END; END UpToClusterSize; PROCEDURE ListFiles(); VAR choice : CARDINAL; DirHandle, resultcode, linecnt : CARDINAL; VolLabel, ListedBytes, BytesFreeStr : ARRAY [0..16] OF CHAR; TmpStr, ThePath : ARRAY [0..79] OF CHAR; TmpNum, ByteCnt, BytesFree : LONGINT; dummy : BOOLEAN; BEGIN (*ListFiles*) ByteCnt := Numbers.Lc( 0 ); OnTheLeft := TRUE; Drectory.VolumeLabel(TheDrive, VolLabel); ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, Drectory.GetCurrentDir(TheDrive,ThePath)); DirHandle := 0FFFFH; SavedMessage := Drectory.FindFirstFile(DirMask, DirHandle, FileInfo); IF SavedMessage # StringIO.NoError THEN SayNotFound(); RETURN; END; ErrStrgs.WriteErrToHandle( StringIO.ErrorOutp, Drectory.FreeDiskSpace(TheDrive,BytesFree)); linecnt := 1; REPEAT TmpNum := FileInfo.size; UpToClusterSize(TmpNum); ByteCnt := ByteCnt + TmpNum; MakeInfoStr( FileInfo, TmpStr ); IF OnTheLeft THEN StringIO.WriteStr(StringIO.outp, TmpStr); ELSE M2Strings.Insert(' ', TmpStr, 0); TmpStr[1] := CHR(186); StringIO.WriteStr(StringIO.outp, TmpStr); INC(linecnt); IF linecnt=26 THEN choice := KbdInput.KeyHit( KbdInput.AnyKeyNum ); IF KbdInput.CAPkey(choice) = ORD('Q') THEN ErrorManager.StraightToDOS(''); END; StringIO.WriteStr(StringIO.outp, '_'); linecnt := 1; ELSE StringIO.WriteEol(StringIO.outp, null); END; END; OnTheLeft := NOT (OnTheLeft); SavedMessage := Drectory.FindNextFile( DirHandle, FileInfo ); UNTIL SavedMessage # StringIO.NoError; IF NOT OnTheLeft THEN INC(linecnt); IF linecnt>=25 THEN choice := KbdInput.KeyHit( KbdInput.AnyKeyNum ); IF KbdInput.CAPkey(choice) = ORD('Q') THEN ErrorManager.StraightToDOS(''); END; END; StringIO.WriteEol(StringIO.outp, null); END; StrConv.LongIntegerToStr(ByteCnt, 1, ListedBytes); Punctuate( ListedBytes ); StrConv.LongIntegerToStr(BytesFree, 1, BytesFreeStr); Punctuate( BytesFreeStr ); M2Strings.Insert(';', ThePath, 0); M2Strings.Insert(VolLabel, ThePath, 0); M2Strings.Insert(' ', ThePath, 0); StrEdit.Append(ThePath, '; '); StrEdit.Append(ThePath, BytesFreeStr); StrEdit.Append(ThePath, ' free. Files above occupy '); StrEdit.Append(ThePath, ListedBytes); StrEdit.Append(ThePath, '.'); StringIO.WriteStr(StringIO.outp, ThePath); IF linecnt<=23 THEN StringIO.WriteEol(StringIO.outp, null); END; IF 0 # FAPI.DOSFINDCLOSE( DirHandle ) THEN END; END ListFiles; BEGIN (*D*) exiting := FALSE; BigSets.AppendSet( KbdInput.YorN, "{'q', 'Q'}" ); EnvironUtils.GetProgParams(DirMask); PrepareDirMask(DirMask); Drectory.GetDiskInfo( TheDrive, BytesPerSector, SectorsPerCluster, ClustersOnDisk, KindOfDisk ); ClusterSize := Numbers.Lc( BytesPerSector * SectorsPerCluster ); IF deleting THEN EraseFiles(); ELSIF moving OR renaming THEN RenameFiles(); IF copying THEN (* We do this because we may have discovered differing drive names. *) CopyFiles(); END; ELSIF copying THEN CopyFiles(); ELSE ListFiles(); END; END D.