procedure filesys; const mostfiles = 40; soh = 1; eot = 4; ack = 6; nak = $15; can = $18; C = $43; drivecap = 191; {Kbyte capacity of files drive} ksize = 1; {minimum increment of file size in Kbytes} type filerec = record title: name; submit: integer; date: name; size: integer; accesses: integer; ASCII: boolean; section: byte; public: boolean; end; channel = array[0..127] of byte; var filefile: file of filerec; filetab: array[0..mostfiles] of filerec; filebuff: array [0..16] of channel; datafile: file; chksum: byte; CRC: integer; crcmode: boolean; enddir: integer; comch: char; procedure xmit(x:byte); begin xmitchar(chr(x)); end; function inbyte: byte; var temp: char; begin repeat until inready or not cts; if keypressed then read(kbd, temp) else temp := recvchar; inbyte := ord(temp); end; procedure calcCRC(data:byte); var carry: boolean; i: byte; begin chksum := lo(chksum + data); for i := 0 to 7 do begin carry := (crc and $8000) <> 0; crc := crc shl 1; if (data and $80) <> 0 then crc := crc or $0001; if carry then crc := crc xor $1021; data := lo(data shl 1); end; end; procedure sendcalc(ch : byte); begin xmit(ch); calcCRC(ch); end; procedure acknak(var inch: byte; time: integer); var loop, loopend: integer; begin loopend := 100 * time; loop := 0; inch := 0; repeat delay(10); if inready then inch := inbyte; loop :=loop + 1; until (inch in [ack, nak, can, C]) or (loop >= loopend) or not cts; end; function acknakout(ch : byte): boolean; var times, loops: integer; begin times := 0; repeat loops := 0; xmit(ch); while (loops < 10) and not timedin do loops := loops + 1; times := times + 1; until inready or (times > 9) or not cts; acknakout := inready and cts; end; procedure download(var successful: boolean); var inch, loop: byte; blocknum, period, tries: integer; done: boolean; temp: line; begin reset(datafile); str(filesize(datafile):4, temp); lineout('Ready for XMODEM transfer:'); lineout('File open:' + temp + ' records;'); lineout('To cancel: type CTL-X until you return to command prompt.'); blockread(datafile, filebuff[0], 1); done := false; tries := 0; blocknum := 1; crcmode := false; repeat acknak(inch, 60); if inch = 0 then inch := can; if inch = C then begin crcmode := true; writeln('CRC mode requested'); end; if inch = ack then begin if eof(datafile) then done := true else begin write(cr + 'Sent #', blocknum:4); blockread(datafile, filebuff[0], 1); blocknum := blocknum + 1; tries := 0; end; end else tries := tries + 1; if (inch <> can) and cts and not done then begin xmit(soh); xmit(lo(blocknum)); xmit(255-lo(blocknum)); chksum := 0; crc := 0; for loop := 0 to 127 do sendcalc(filebuff[0][loop]); calcCRC(0); calcCRC(0); if crcmode then begin xmit(hi(crc)); xmit(lo(crc)); end else xmit(chksum); end; if tries = 5 then crcmode := not crcmode; until (inch = can) or done or (tries= 10) or not cts; successful := done; tries := 0; if successful and cts then repeat xmit(eot); acknak(inch, 10); tries := tries + 1; until (inch=ack) or (tries > 10) or not cts; if cts and (inch <> can) and not successful then xmit(can); close(datafile); end; function recchar(var error: boolean): byte; var temp: byte; begin temp := 0; if not cts then error := true; if not error then begin if not timedin then error := true else begin temp := inbyte; calcCRC(temp); recchar := temp; end; end; end; procedure clearline; var junk: byte; begin while timedin do junk := inbyte; end; {$I-} procedure upload(var successful: boolean); var blocknum, tries, byteloc : integer; comp, locblock, crc2 : integer; fatal, error, done : boolean; opening, inch, locrc : byte; hicrc, csum2, mode : byte; begin lineout('Beginning XMODEM protocol upload:'); lineout('To cancel: type CTRL-X until you return to command prompt.'); tries := 0; done := false; opening := 0; locblock := 1; rewrite(datafile); fatal := ioresult > 0; if crcmode then mode := C else mode := nak; if cts and not fatal then fatal := not acknakout(mode); while cts and not (done or fatal) do begin tries := tries + 1; error := false; opening := recchar(error); if opening = can then fatal := true; if opening = eot then done := true; if (opening <> eot) and (opening <> soh) and not fatal then error := true; if cts and not (error or fatal or done) then begin blocknum := recchar(error); comp := recchar(error); if lo(comp + blocknum + opening) <> 0 then error := true; byteloc := 0; crc := 0; chksum := 0; while (byteloc < 128) and not (error or fatal) do begin filebuff[0][byteloc] := recchar(error); byteloc := byteloc + 1; end; if cts and not (error or fatal) then begin calcCRC(0); calcCRC(0); crc2 := crc; csum2 := chksum; hicrc := recchar(error); if crcmode then begin locrc := recchar(error); if (lo(crc2) <> locrc) or (hi(crc2) <> hicrc) then error := true; end else if csum2 <> hicrc then error := true; if (lo(locblock) <> blocknum) and (lo(locblock) <> lo(blocknum+1)) and not error then fatal := true; if (lo(locblock) = blocknum) and not (error or fatal) then begin blockwrite(datafile, filebuff[0], 1); write(cr + ' Received #', blocknum:4); if IOresult <> 0 then fatal := true; tries := 0; locblock := locblock + 1; end; end; end; if not (fatal or error) then flush else clearline; if done or not (error or fatal) then fatal := not acknakout(ack); if error and not fatal then begin fatal := not acknakout(nak); if tries > 6 then crcmode := not crcmode; end; end; if fatal then xmit(can); if done then xmit(ack); close(datafile); successful := (IOresult = 0) and done and not fatal; if not successful then erase(datafile); end; procedure storebuff(var buffernum: byte; var paused, aborted: boolean); var loop: byte; begin loop := 0; while (loop < buffernum) and not aborted do begin blockwrite(datafile, filebuff[loop], 1); if IOresult > 0 then aborted := true; loop := loop + 1; end; if buffernum in [1..16] then filebuff[0] := filebuff[buffernum]; buffernum := 0; repeat xmit(17) until timedin; paused := false; end; procedure textcap(var successful: boolean); var buffernum, where, loop : byte; cc, cz, paused : boolean; withecho, done, aborted : boolean; temp : byte; begin withecho := (getcap('Do you want your text echoed (Y/N) ? ') = 'Y'); lineout('Beginning text capture: two CTRL-Cs abort, two CTRL-Zs end.'); cc := false; cz := false; done := false; paused := false; buffernum := 0; where := 0; rewrite(datafile); aborted := (IOresult > 0); while cts and not (done or aborted) do begin if paused then if not timedin then storebuff(buffernum, paused, aborted); temp := inbyte; if not cts then aborted := true; if withecho and outready then xmit(temp); if temp = 3 then begin if cc then aborted := true else cc := true; end else cc := false; if temp = 26 then begin if cz then done := true else cz := true; end else cz := false; filebuff[buffernum][where] := temp; where := where + 1; if where > 127 then begin where := 0; buffernum := buffernum + 1; end; if buffernum > 14 then begin xmit(19); paused := true; end; if buffernum > 16 then aborted := true; end; if done and cts and not aborted then begin buffernum := buffernum + 1; storebuff(buffernum, paused, aborted); end; close(datafile); if aborted and (IOresult = 0) then erase(datafile); successful := done and (IOresult=0) and not aborted; end; {$I+} function exists(filename: name): boolean; var found: boolean; begin assign(datafile, filename); {$I-} reset(datafile) {$I+}; found := (IOresult = 0); if found then close(datafile); exists := found; end; function alpha(filename: name): boolean; var strpos: integer; okay: boolean; dots: byte; begin dots := 0; alpha := true; if length(filename) > 0 then for strpos := 1 to length(filename) do begin if filename[strpos] = '.' then dots := dots + 1; if not (filename[strpos] in ['.', '-', '_', '0'..'9', 'A'..'Z']) then alpha := false; end; if dots > 1 then alpha := false; end; function getlegal: name; var filename: name; dotpos: integer; begin repeat filename := allcaps(getinput('Enter name of file ? ', 12, echo)); dotpos := pos('.', filename); until ((dotpos < 10) and (dotpos <> 1) and (not((dotpos = 0) and (length(filename) > 8))) and (not((dotpos > 0) and (length(filename) > dotpos + 3))) and alpha(filename)) or (filename = ''); getlegal := filename; end; function dirpos(filename: name): integer; var loopvar: integer; begin dirpos := 0; loopvar := 0; repeat loopvar := loopvar + 1; until (filetab[loopvar].title = filename) or (loopvar >= enddir); if filetab[loopvar].title = filename then dirpos := loopvar; end; function getsect: byte; var temp: integer; begin if sectsin then repeat temp := getint(numsects, 0, 'Which section (0 for all, ? for list) ? '); if temp = -1 then listsections else getsect := temp; until (temp <> -1) or not cts else getsect := 1; end; procedure addfile(filename: name; sectnum: byte; xmodem: boolean); begin with filetab[enddir + 1] do begin title := filename; submit := usernum; if clockin then date := timeon; assign(datafile, filedrive + filename); reset(datafile); size := filesize(datafile); close(datafile); accesses := 0; ASCII := not xmodem; section := sectnum; public := false; end; end; procedure newfile(xmodem: boolean); var filename: name; successful: boolean; sectnum: byte; begin clearsc; if enddir >= mostfiles then lineout('No file space available.') else begin stringout('Upload: '); filename := getlegal; if filename <> '' then begin if exists(filedrive + filename) then lineout('File name in use.') else begin repeat sectnum := getsect until (sectnum <> 0) or not cts; assign(datafile, filedrive + filename); if cts then begin if xmodem then upload(successful) else textcap(successful); if successful then addfile(filename, sectnum, xmodem); clearline; if successful then enddir := enddir + 1 else lineout('Fatal transfer error or disk full...'); end; end; end; end; end; function legaltab(prompt: line): integer; var filename: name; tabloc: integer; begin tabloc := 0; clearsc; stringout(prompt); filename := getlegal; if filename <> '' then begin tabloc := dirpos(filename); if tabloc <> 0 then if not (filetab[tabloc].public or (access > reg)) then tabloc := 0; if tabloc <> 0 then assign(datafile, filedrive + filename) else if filename <> '' then lineout('No such file available.'); end; legaltab := tabloc; end; procedure transmitfile; var successful: boolean; tabloc: integer; begin tabloc := legaltab('Download: '); if tabloc > 0 then begin download(successful); if successful then with filetab[tabloc] do accesses := accesses + 1 else lineout('Transfer failed.'); end; end; procedure textdump; var tabloc : integer; libname: longname; begin tabloc := legaltab('ASCII text dump: '); lineout(space); if tabloc > 0 then with filetab[tabloc] do begin libname := title; if copy(title, pos('.', title), 4) = '.LBR' then begin lineout(title + ' is a library file: please select a member: '); libname := getlegal; if libname = '' then libname := 'DIR'; libname := copy(title, 1, length(title)-4) + '/' + libname; end; typefile(filedrive + libname, false); if not cancelled then accesses := accesses + 1; end; end; procedure showspace; var loop, howbig, howmuch, sectmin : integer; temp : line; begin sectmin := ksize shl 3; howmuch := drivecap; if enddir > 0 then for loop := 1 to enddir do with filetab[loop] do begin howbig := (size + sectmin - 1) div sectmin; howmuch := howmuch - howbig; end; str(howmuch:4, temp); if cts then lineout(cr + lf + temp + 'K space remaining.'); end; procedure dir(sectnum: byte); var loop, spaces : byte; howbig, sectmin : integer; any : boolean; temp : line; begin any := false; sectmin := ksize shl 3; lineout(space); if sectsin then lineout('Section ' + sect[sectnum] + ':'); if enddir > 0 then for loop := 1 to enddir do with filetab[loop] do begin howbig := (size + sectmin - 1) div sectmin; if cts and (public or (access = sysop) or (submit = usernum)) and (sectnum = section) then begin str(howbig:4, temp); for spaces := length(title) to 13 do temp := ' ' + temp; stringout(title + temp + 'K'); if clockin then stringout(' ' + date); if not public then stringout(' * Private *'); lineout(space); if (access = sysop) or (submit = usernum) then begin str(accesses:4, temp); lineout('Accesses: ' + temp + ' From: ' + getname(submit)); end; any := true; end; end; if cts and not any then lineout('No files found.'); end; procedure directory; var sectnum : byte; begin stringout('Directory: '); sectnum := getsect; if sectnum > 0 then dir(sectnum) else for sectnum := 1 to numsects do dir(sectnum); showspace; end; procedure ldir; var tabloc : integer; begin tabloc := legaltab('Library directory: '); lineout(space); if tabloc > 0 then typefile(filedrive + filetab[tabloc].title + '/DIR', false); end; procedure killfile; var loop, tabloc: integer; begin tabloc := legaltab('Delete: '); if tabloc > 0 then begin erase(datafile); if enddir > tabloc then for loop := tabloc + 1 to enddir do filetab[loop - 1] := filetab[loop]; enddir := enddir - 1; end; end; procedure installfile; var filename : name; sectnum : byte; begin if enddir < mostfiles then begin filename := getlegal; if filename <> '' then begin if exists(filedrive+filename) and (dirpos(filename) = 0) then begin repeat sectnum := getsect until (sectnum <> 0) or not cts; addfile(filename, sectnum, true); enddir := enddir + 1; lineout('File installed.'); end; end; end; end; function newname(tabloc: integer): name; var filename: name; begin newname := filetab[tabloc].title; stringout('New name? '); filename := getlegal; if (filename <> '') then begin if not exists(filedrive + filename) then begin assign(datafile, filedrive + filetab[tabloc].title); rename(datafile, filename); newname := filename; stringout('File renamed.'); end else lineout('Name in use - cannot rename.'); end; end; procedure editheader; var tabloc: integer; filename: name; innum: integer; sectstring: name; begin tabloc := legaltab('Edit: '); if tabloc > 0 then with filetab[tabloc] do begin repeat str(section:3, sectstring); lineout(space); lineout('1- Name : ' + title); lineout('2- From : ' + getname(submit)); lineout('3- Section : ' + sectstring); lineout('4- Public? : ' + yn[public]); lineout(space); innum := getint(4, 0, 'Number of parameter to change? '); case innum of 1: title := newname(tabloc); 2: submit := getid('Name of submitter? '); 3: repeat section := getsect until (section <> 0) or not cts; 4: public := not public; end; until (innum = 0) or not cts; assign(datafile, filedrive + title); reset(datafile); size := filesize(datafile); close(datafile); end else lineout('File not in directory.'); end; procedure initfile; var loopvar: integer; temp: name; begin lineout('Initializing file system...'); loopvar := 0; assign(filefile, 'FILES.BBS'); {$I-} reset(filefile) {$I+}; if IOresult = 0 then begin while not eof(filefile) do begin loopvar := loopvar + 1; read(filefile, filetab[loopvar]); end; close(filefile); end; enddir := loopvar; filesopen := true; end; procedure closefile; var loopvar: integer; begin rewrite(filefile); if enddir > 0 then for loopvar := 1 to enddir do write(filefile, filetab[loopvar]); close(filefile); filesopen := false; end; begin clearsc; initfile; if not expert then outfile(filemenu); repeat lineout(space); comch := getcap('Files command (or ? for menu) ? '); case comch of 'D' : directory; 'S' : transmitfile; 'T' : textdump; 'H' : outfile(filehelp); 'G' : disconnect; '?' : outfile(filemenu); 'L' : ldir; 'U' : if access>newuser then begin crcmode := true; newfile(true); end; 'C' : if access>newuser then begin crcmode := false; newfile(true); end; 'V' : if access>newuser then newfile(false); 'K' : if access = sysop then killfile; 'I' : if access = sysop then installfile; 'E' : if access = sysop then editheader; end; until (comch = 'Q') or not cts; if cts then lineout('Closing file system...'); closefile; end;