(* Copyright 2025 Sebastian Lederer. See the file LICENSE.md for details *) program recover; const PageMargin = 4; var filename:string; volid:integer; (* we use some stuff internal to stdlib.pas *) procedure getdirslot(volumeid:integer;slotNo:integer;var result:DirectorySlot;var error:integer); external; procedure openfile(volid:integer; slotno:integer; var dirslot:DirectorySlot; var aFile:File; mode:filemode); external; function openvolume:integer; var volid:integer; begin openvolume := -1; volid := findvolume(DefaultVolume); if volid < 1 then writeln('Volume ', DefaultVolume, ' not found.') else openvolume := volid; end; procedure waitForKey; var c:char; begin writeln; writeln('-- press any key to continue --'); c := conin; end; procedure copyfile(volid:integer;slotno:integer;oldname,newname:string); var srcfile,destfile:file; dirslot:DirectorySlot; error:integer; ch:char; count:integer; begin (* to open the deleted source file, we emulate parts of the open procedure from stdlib *) getdirslot(volid, slotno, dirslot, error); if not (SlotDeleted in dirslot.flags) or (dirslot.name <> oldname) then writeln('Invalid slot ', slotno) else begin openfile(volid, slotno, dirslot, srcfile, ModeReadOnly); if error <> 0 then writeln('Error opening file from slot ', slotno, ': ', ErrorStr(error)) else begin open(destfile, newname, ModeCreate); if IOResult(destfile) = IOFileExists then begin write('File ', newname, ' already exists, overwrite? [y/n] '); readln(ch); if ch in ['Y', 'y'] then open(destfile, newname, ModeOverwrite); end; if IOResult(destfile) <> 0 then writeln('Error opening ', newname, ': ', ErrorStr(IOResult(destfile))) else begin (* taken from shell.pas copyFile *) write('Recovering from slot ', slotno, ' to ', newname, '...'); count := 0; while not eof(srcfile) do begin read(srcfile,ch); write(destfile,ch); count := count + 1; if (count and 8191) = 0 then write('.'); end; writeln; close(destfile); end; close(srcfile); end; end; end; procedure recoverfile(volid:integer;wantedname:string); var dirs:DirectorySlot; i:integer; lastSlot:integer; error:integer; screenW,screenH:integer; count:integer; datestr:string; ftime:DateTime; wantedslot:integer; newname:string; foundsomething:boolean; begin writeln('Files available for recovery:'); foundsomething := false; newname := ''; GetTermSize(screenW, screenH); count := PageMargin; lastSlot := volumeTable[volid].part.dirSize - 1; openvolumeid(volid, error); (* ignoring theoretically possible out-of-heap-space error *) for i := 0 to lastSlot do begin getdirslot(volid, i, dirs, error); with dirs do begin if (SlotFirst in flags) or (SlotDeleted in flags) then if name = wantedname then begin ftime := GetDateTime(dirs.modTime); datestr := DateStr(ftime) + ' ' + TimeStr(ftime, true); write('slot ', i:4, name:34, sizeBytes:8, datestr:21, ' '); if SlotFirst in flags then write('Cur'); if SlotExtent in flags then write('Extent'); if SlotReserved in flags then write('Resvd'); if SlotDeleted in flags then write('Del'); if SlotFree in flags then write('Free'); if SlotEndScan in flags then write('End'); writeln; foundsomething := true; count := count + 1; if count >= screenH then begin count := PageMargin; waitForKey; end; if SlotEndScan in flags then break; end; end; end; if foundsomething then begin write('Slot no to recover> '); readln(wantedslot); if (wantedslot < 1) or (wantedslot >= volumeTable[volid].part.dirSize) then writeln('Invalid slot number.') else begin write('New filename> '); readln(newname); end; if length(newname) > 0 then copyfile(volid, wantedslot, wantedname, newname); end; closevolumeid(volid); end; begin if ParamCount > 0 then filename := ParamStr(1) else begin write('Filename to recover> '); readln(filename); end; volid := openvolume; if volid > 0 then recoverfile(volid, filename); end.