program LibraryUtility; { written 10/09/84 by Steve Freeman This program was written to function as Gary Novosielski's LU. As such it will function as a utility to manipulate library members under any operating system which will support TURBO Pascal. Minor rewrites may be necessary for other versions of Pascal. This program is placed into the Public Domain by the author and, as a Public Domain program, may NOT be used for commercial purposes. } const ProgramVersion = '1.00'; BufferSize = 127; { maximum size of data buffer - 1 } EntriesPerBuffer = 4; { (BufferSize+1)/32 } maxent = 128; { maximum dir entries this program will take } type TimeType = integer; FileNameType = array[1..11] of char; LibFileType = file; EntryType = record status: byte; name: array[1..8] of char; ext: array[1..3] of char; index: integer; length: integer; CRC: integer; CreationDate: integer; LastChangeDate: integer; CreationTime: TimeType; LastChangeTime: TimeType; PadCount: byte; filler: array[27..31] of byte; end; EntryPtr = ^EntryType; hexstr = string[4]; maxstr = string[255]; filename = string[12]; var buffer: array[0..BufferSize] of byte; library, file2: file; SizeFile: file of byte; DirectoryChanged: boolean; LibName, fname: filename; LibSize, NumEntries: integer; LibEntry: EntryType; Dir: array[0..maxent] of EntryPtr; active, unused, deleted: integer; {.cp7} procedure WaitKey; var c: char; begin write(^M^J,'Press any key to continue...'); repeat until keypressed; read(kbd,c); end; {.cp13} function Confirm: boolean; var c: char; begin write('Confirm operation (Y/N): '); repeat read(kbd,c); c := upcase(c); until (c in ['Y','N']); writeln(c); if c = 'Y' then Confirm := true else Confirm := false end; {.cp9} function CommandLine: maxstr; var len, i: integer; str: maxstr; begin str := ''; len := mem[cseg:$80]; if len>1 then for i:=2 to len do str := str + chr(mem[cseg:$80+i]); CommandLine := str; end; {.cp13} function hex(num: integer): hexstr; var i, j: integer; h: string[16]; str: hexstr; begin str := '0000'; h := '0123456789ABCDEF'; j := num; for i:=4 downto 1 do begin str[i] := h[(j and 15)+1]; j := j shr 4; end; hex := str; end; {.cp14} procedure MakeName(f: filename; var name: FileNameType); var dotpos, endname, i: integer; begin for i:=1 to 11 do name[i] := ' '; dotpos := pos('.',f); if dotpos > 0 then endname := dotpos-1 else endname := length(f); for i:=1 to length(f) do f[i] := upcase(f[i]); if dotpos > 0 then for i:=1 to 3 do if f[dotpos+i]<>' ' then name[8+i] := f[dotpos+i]; for i:=1 to endname do name[i] := f[i]; end; {.cp8} procedure PutName(f: filename; n: integer); var i: integer; name: FileNameType; begin MakeName(f,name); for i:=1 to 8 do Dir[n]^.name[i] := name[i]; for i:=1 to 3 do Dir[n]^.ext[i] := name[i+8]; end; {.cp29} function FindMember(f: filename): integer; var member, dotpos, endname, i, k: integer; lookup: FileNameType; found: boolean; function NamesMatch(entry: integer): boolean; var match: boolean; begin NamesMatch := true; with Dir[entry]^ do begin if (status <> 0) and (status <> $FE) then NamesMatch := false; for k:=1 to 8 do if name[k]<>lookup[k] then NamesMatch := false; for k:=1 to 3 do if ext[k]<>lookup[8+k] then NamesMatch := false; end; end; begin MakeName(f,lookup); found := false; i := 1; while not(found) and (i0 then f:=copy(f,1,i-1); f := f + '.LBR'; Parse := f; end; {.cp13} procedure WriteDirectoryToDisk(var lib: LibFileType); var member, i: integer; begin reset(lib); member := 0; while member < NumEntries do begin for i:=0 to EntriesPerBuffer-1 do move(Dir[member+i]^,buffer[32*i],32); blockwrite(lib,buffer,1); member := member + 4 end; DirectoryChanged := false end; {.cp6} procedure ZeroEntry(n: integer); begin fillchar(Dir[n]^,32,chr(0)); {clear the record} fillchar(Dir[n]^.name[1],11,' '); {clear file name} Dir[n]^.status := -1; {mark unused} end; {.cp38} procedure SortDir; var i, j: integer; function larger(a, b: integer): boolean; var ok, x: integer; c1, c2: char; begin ok := 0; x := 1; if (Dir[a]^.status <> 0) and (Dir[b]^.status <> 0) then ok := 2; if (Dir[a]^.status <> 0) and (ok = 0) then ok := 1; if (Dir[b]^.status <> 0) and (ok = 0) then ok := 2; while (x < 12) and (ok=0) do begin c1 := Dir[a]^.name[x]; c2 := Dir[b]^.name[x]; if c1 > c2 then ok := 1; if c1 < c2 then ok := 2; x := x + 1 end; if ok=1 then larger := true else larger := false end; procedure swap(x, y: integer); var temp: EntryPtr; begin temp := Dir[x]; Dir[x] := Dir[y]; Dir[y] := temp end; begin for i:=1 to NumEntries-1 do if Dir[i]^.status <> 0 then ZeroEntry(i); for i:=1 to NumEntries-2 do begin for j:=i+1 to NumEntries-1 do if larger(i,j) then swap(i,j); end; end; {.cp22} procedure CreateDirectory; var i: integer; begin rewrite(library); clrscr; writeln('Creating a new library. Name = ',LibName); write('How many entries? '); readln(i); NumEntries := i + 1; {add 1 for Directory entry} i := NumEntries MOD 4; if i<>0 then NumEntries := NumEntries + (4 - i); for i:=0 to NumEntries-1 do begin new(Dir[i]); ZeroEntry(i); end; Dir[0]^.status := 0; {directory entry is always used} Dir[0]^.length := NumEntries DIV 4; active := 1; unused := NumEntries - 1; deleted := 0; WriteDirectoryToDisk(library); end; {.cp26} procedure GetDirectory; var i, offset: integer; begin offset := 0; DirectoryChanged := false; LibSize := (1 + filesize(library)) DIV 8; {in kilobytes} blockread(library,buffer,1); new(Dir[0]); {make space for directory header} move(buffer[0],Dir[0]^,32); {move header entry} NumEntries := (128 * Dir[0]^.length) DIV 32; for i:=1 to NumEntries-1 do begin if (i MOD EntriesPerBuffer) = 0 then begin {read next block} blockread(library,buffer,1); offset := offset + EntriesPerBuffer; end; new(Dir[i]); move(buffer[32*(i-offset)],Dir[i]^,32); end; active := 1; unused := 0; deleted := 0; for i:=1 to NumEntries-1 do if Dir[i]^.status=0 then active := active + 1 else if Dir[i]^.status=$FE then deleted := deleted + 1 else unused := unused + 1; end; {.cp8} procedure OpenLibrary; begin assign(library,LibName); {$I-} reset(library) {$I+}; if IOresult=0 then GetDirectory else CreateDirectory; end; {.cp23} procedure Directory; var i, j: integer; begin clrscr; writeln('Library ',LibName,' is ',LibSize,'K',^M^J); writeln(' name index length CRC'); writeln('------------------------------------'); for i:=1 to NumEntries-1 do with Dir[i]^ do begin if status<>$FF then begin for j:=1 to 8 do write(name[j]); write('.'); for j:=1 to 3 do write(ext[j]); write(' ',index:8,length:8,' ',hex(CRC)); if status=$FE then write(' deleted'); writeln; end; end; writeln(^M^J,active,' active, ',unused,' unused, ',deleted,' deleted: ',active+unused+deleted,' total entries.'); WaitKey; end; {.pa} procedure Extract; var fname2: filename; i, blocknum, bytenum: integer; begin clrscr; write('Enter filename to extract: '); readln(fname2); if length(fname2)>0 then begin i := FindMember(fname2); if i>0 then begin assign(file2,fname2); rewrite(file2); with Dir[i]^ do begin seek(library,index); blocknum := 1; bytenum := 0; while blocknum <= length do begin blockread(library,buffer,1); if blocknum<=length then blockwrite(file2,buffer,1) else begin close(file2); {save disk info} assign(SizeFile,fname2); reset(SizeFile); seek(SizeFile,filesize(SizeFile)); while bytenum < ((128 - PadCount) MOD 128) do begin write(SizeFile,buffer[bytenum]); bytenum := bytenum + 1 end; close(SizeFile); reset(file2); {for later close} end; blocknum := blocknum + 1 end; end; close(file2); end else writeln('member was not found!!'); end; WaitKey; end; {.cp27} procedure Delete; var fname2: filename; i: integer; ok: boolean; begin clrscr; write('Enter member to delete: '); readln(fname2); if length(fname2)>0 then begin i := FindMember(fname2); if i>0 then begin ok := Confirm; write('Member ',fname2); if ok then begin Dir[i]^.status := $FE; deleted := deleted + 1; active := active - 1; writeln(' was deleted.'); DirectoryChanged := true; end else writeln(' was NOT deleted.') end else writeln(fname2,' does not exist.'); WaitKey; end; end; {.cp21} procedure Undelete; var fname2: filename; i: integer; ok: boolean; begin clrscr; write('Enter member to undelete: '); readln(fname2); if length(fname2)>0 then begin i := FindMember(fname2); if i>0 then begin Dir[i]^.status := 0; deleted := deleted - 1; active := active + 1; writeln(fname2,' was undeleted.'); DirectoryChanged := true; end else writeln(fname2,' does not exist.'); WaitKey; end; end; {.pa} procedure Add; var fname2: filename; EntryLength, EntryIndex, SizeOfFile, number, i: integer; begin number := 0; i := 1; while (number = 0) and (i < NumEntries) do begin if (Dir[i]^.status=$FF) and (number=0) then number := i else i := i + 1; end; clrscr; if number > 0 then begin write('Enter member to add: '); readln(fname2); if length(fname2)>0 then begin if FindMember(fname2) = 0 then begin assign(SizeFile,fname2); {$I-} reset(SizeFile) {$I+}; if IOresult=0 then begin SizeOfFile := filesize(SizeFile); close(SizeFile); assign(file2,fname2); reset(file2); EntryIndex := filesize(library); EntryLength := filesize(file2); seek(library,EntryIndex); while not(eof(file2)) do begin blockread(file2,buffer,1); blockwrite(library,buffer,1) end; close(file2); fillchar(Dir[number]^,32,chr(0)); {status:=0} Dir[number]^.index := EntryIndex; Dir[number]^.length := EntryLength; Dir[number]^.PadCount := (128 - (SizeOfFile MOD 128)) and $7F; PutName(fname2,number); unused := unused - 1; active := active + 1; write('Member ',fname2,' was added.'); DirectoryChanged := true; end else writeln('File ',fname2,' was not found.'); end else writeln(fname2,' is already a member.'); end; end else writeln('There are no available places to put this entry.'); WaitKey; end; {.pa} procedure Reorganize; var i, j: integer; begin SortDir; assign(file2,'WORK-$$$.LBR'); reset(library); rewrite(file2); WriteDirectoryToDisk(file2); for i:=1 to NumEntries-1 do with Dir[i]^ do begin if (status = 0) and (length > 0) then begin writeln('Copying: ',name,'.',ext,' ',filepos(file2)); seek(library,index); index := filepos(file2); for j:=1 to length do begin blockread (library,buffer,1); blockwrite(file2, buffer,1) end end end; WriteDirectoryToDisk(file2); close(file2); close(library); erase(library); rename(file2,LibName); reset(library); end; {.cp8} procedure HelpCmdLine; begin clrscr; writeln(^M^J,'You must enter a file name:'); writeln(^M^J,'LU [.LBR]'); writeln(^M^J,'NOTE: the .LBR suffix is optional.'); WaitKey; end; {.cp14} procedure Help; begin clrscr; writeln('Library Utility Commands:',^M^J); writeln('Add - add a new member, can''t be duplicate'); writeln('Directory - gives the listing of this library''s directory'); writeln('Extract - copy a member out to its own file'); writeln('Kill - delete a member from the library'); writeln('Undelete - reverses the effects of a delete'); writeln('Reorganize- compresses blank space in library'); writeln('eXit - terminate this program'); writeln('Help - gives this screen'); WaitKey; end; {.pa} procedure Menu; var selection: char; begin OpenLibrary; repeat clrscr; gotoxy(30,2); write('Library Utility Menu'); gotoxy(35,3); write('version ',ProgramVersion); gotoxy(40-length(LibName) DIV 2,5); write(LibName); gotoxy(10,07); write('D - directory'); gotoxy(10,08); write('E - extract member'); gotoxy(10,09); write('A - add member'); gotoxy(10,10); write('K - delete member'); gotoxy(10,11); write('U - undelete member'); gotoxy(10,12); write('R - reorganize library'); gotoxy(10,13); write('X - exit'); gotoxy(10,14); write('? - help'); gotoxy(20,20); write('choose one: '); repeat repeat until keypressed; read(kbd,selection); selection := upcase(selection); until (selection in ['A','D','E','K','R','U','X','?']); writeln(selection); case selection of 'A': Add; 'D': Directory; 'E': Extract; '?': Help; 'K': Delete; 'R': Reorganize; 'U': Undelete; end; until selection='X'; if DirectoryChanged then WriteDirectoryToDisk(library); close(library); end; {.cp8} begin {Main} LibName := Parse(CommandLine); if length(CommandLine) = 0 then begin write('Enter name of library file: '); Read(fname); Libname := Parse(fname); Menu; end else Menu; end.  .