PROGRAM unix; /* Copyright (c) 1979 David A. Butterfield and Daniel Weise */ /* Modified 1982 by Alan E. Frisbie */ /* This program is (reputedly) able to read unix file structured disks under RT11. Once the compiler and rt11 weirdnesses are removed it migt become a utility to read unix disks on anything. Due to its vintage, it probably is suited to unix filestructures circa V7 Unix. */ /* N.b. The name "unix" is a trademark of AT&T (I think). Nevertheless, there has been large worldwide circulation of source code for various versions of unix V7 and earlier and later. Unix is (or at the stage of V7 was) not copyrighted but was treated as a trade secret. I believe the status of a trade secret that the whole world knows is that the information is public domain. Hence I believe Unix V7 is now public domain material. It remains a perfectly lousy OS for all that and should be used only by those who really LIKE to fix corrupted filestructures up. */ CONST dirbit = 40000B; spbit = 20000B; large = 10000B; TYPE char13array = ARRAY [0..13] OF char; inode = RECORD flags: integer; nlinks: char; uid: char; gid: char; sizehi: char; sizelo: integer; blknos: ARRAY [0..7] OF integer; actime: ARRAY [0..1] OF integer; modtime: ARRAY [0..1] OF integer END; direntry = RECORD inodeno: integer; filename: ARRAY [0..13] OF char END; funny = RECORD CASE integer OF 1: (inoderec: ARRAY [0..15] OF inode); 2: (dirblockrec: ARRAY [0..31] OF direntry); 3: (charrec: ARRAY [0..511] OF char); 4: (ints: ARRAY [0..255] OF integer) END; VAR dev: text; cihave: integer; /* INODE NUMBER OF CURRENT DIRECTORY */ icurd: inode; /* CONTAINS THE INODE OF THE CURRENT DIRECTORY */ decnt: integer; /* NUMBER OF ENTRIES IN THE CURRENT DIRECTORY */ datablock: funny; inodeblock: ARRAY [0..15] OF inode; inhave: integer; /* PHYSICAL BLOCK IN INODEBLOCK */ dirblock: ARRAY [0..31] OF direntry; dihave: integer; /* PHYSICAL BLOCK IN DIRBLOCK */ indblock: ARRAY [0..255] OF integer; ibhave: integer; /* PHYSICAL BLOCK IN INDBLOCK */ dindblock: ARRAY [0..255] OF integer; dibhave: integer; /* PHYSICAL BLOCK IN DINDBLOCK */ inbuff: ARRAY [1..80] OF char; /* THIS IS THE INPUT LINE */ logname: ARRAY [1..8] OF char; logpass: ARRAY [1..8] OF char; loggedin: boolean; notdone: boolean; option: char; arg: integer; foo: char; nl: ARRAY [0..1] OF char; PROCEDURE seek(VAR f: text; VAR x: funny; blockno: integer); EXTERNAL; FUNCTION getblkno(rblock: integer; sourceinode: inode): integer; VAR lindblockno, ldindblockno, physblock: integer; BEGIN IF (sourceinode.flags& large) = 0 THEN IF rblock < 8 THEN physblock := sourceinode.blknos[rblock] ELSE BEGIN writeln('ATTEMPT TO REFERENCE #', rblock, 'OF A SMALL FILE.'); rblock := rblock DIV 0 END ELSE BEGIN /* LARGE OR HUGE FILE */ lindblockno := rblock DIV 256; IF lindblockno < 7 THEN BEGIN IF sourceinode.blknos[lindblockno] <> ibhave THEN BEGIN seek(dev, datablock, sourceinode.blknos[lindblockno]); indblock := datablock.ints; ibhave := sourceinode.blknos[lindblockno] END; physblock := indblock[rblock MOD 256] END ELSE BEGIN /* HUGE FILE WERE HERE IFF LINDBLOCKNO >= 7 */ IF sourceinode.blknos[7] <> ibhave THEN BEGIN seek(dev, datablock, sourceinode.blknos[7]); indblock := datablock.ints; ibhave := sourceinode.blknos[7] END; lindblockno := lindblockno - 7; IF indblock[lindblockno] <> dibhave THEN BEGIN seek(dev, datablock, indblock[lindblockno]); dindblock := datablock.ints; dibhave := indblock[lindblockno] END; physblock := dindblock[rblock MOD 256] END END; getblkno := physblock END; FUNCTION geti(inodeno: integer): integer; VAR block: integer; BEGIN inodeno := inodeno - 1; geti := 17B& inodeno; block := inodeno DIV 16 + 2; IF block <> inhave THEN BEGIN seek(dev, datablock, block); inodeblock := datablock.inoderec; inhave := block END END; FUNCTION getd(dirnumber: integer): integer; VAR block: integer; BEGIN getd := 37B& dirnumber; block := getblkno(dirnumber DIV 32, icurd); IF block <> dihave THEN BEGIN seek(dev, datablock, block); dihave := block; dirblock := datablock.dirblockrec END END; PROCEDURE getfile(inodeno: integer); VAR hi: char; finptr, lo, size, block, i, j: integer; answer: ARRAY [1..14] OF char; insertcrs: boolean; BEGIN write('Should I add carriage returns? '); readln(answer); insertcrs := (answer[1] = 'y') OR (answer[1] = 'Y'); finptr := geti(inodeno); hi := inodeblock[finptr].sizehi; lo := inodeblock[finptr].sizelo; /* following assembly language computes size by subtracting 1 from bytecount (to 32 bits), then shifting to divide by 512 to get # blocks. */ /*$C MOVB HI(6),%0 BIC #^O177400,%0 MOV LO(6),%1 SUB #1,%1 SBC %0 ASHC #-9.,%0 MOV %1,SIZE(6) */ FOR i := 0 TO size DO BEGIN block := getblkno(i, inodeblock[finptr]); IF block <> 0 THEN BEGIN seek(dev, datablock, block); IF insertcrs THEN FOR j := 0 TO 511 DO IF datablock.charrec[j] = chr(12B) THEN write(chr(15B), chr(12B)) ELSE write(datablock.charrec[j]) ELSE write(datablock.charrec) END ELSE writeln(nl, 'BLOCK ', i, ' IS EMPTY') END END; PROCEDURE movefile(inodeno: integer); VAR output: text; filename: ARRAY [1..14] OF char; hi: char; finptr, lo, size, block, i, j: integer; insertcrs: boolean; BEGIN write('Write to file: '); readln(filename); rewrite(output, filename); write('Should I add carriage returns? '); readln(filename); insertcrs := (filename[1] = 'y') OR (filename[1] = 'Y'); finptr := geti(inodeno); hi := inodeblock[finptr].sizehi; lo := inodeblock[finptr].sizelo; /* following assembly language computes size by subtracting 1 from bytecount (to 32 bits), then shifting to divide by 512 to get # blocks. */ /*$C MOVB HI(6),%0 BIC #^O177400,%0 MOV LO(6),%1 SUB #1,%1 SBC %0 ASHC #-9.,%0 MOV %1,SIZE(6) */ FOR i := 0 TO size DO BEGIN block := getblkno(i, inodeblock[finptr]); seek(dev, datablock, block); IF insertcrs THEN FOR j := 0 TO 511 DO IF datablock.charrec[j] = chr(12B) THEN write(output, chr(15B), chr(12B)) ELSE write(output, datablock.charrec[j]) ELSE write(output, datablock.charrec) END; close(output) END; PROCEDURE writef(VAR name: char13array); VAR i: integer; BEGIN FOR i := 0 TO 13 DO BEGIN IF ord(name[i]) = 0 THEN write(' ') ELSE write(name[i]) END END; PROCEDURE getcurd(ciwant: integer); VAR hi: char; finptr, lo: integer; BEGIN IF ciwant <> cihave THEN BEGIN finptr := geti(ciwant); IF (inodeblock[finptr].flags& dirbit) = 0 THEN writeln('Not a directory') ELSE BEGIN cihave := ciwant; icurd := inodeblock[finptr] END END; hi := icurd.sizehi; lo := icurd.sizelo; /* Take (# bytes / 16) as decnt value */ /*$C MOVB HI(6),%0 MOV LO(6),%1 ASHC #-4,%0 MOV %1,DECNT(5) ; ASSUME NO MORE THAN 32767 ENTRIES */ END; PROCEDURE dirlist; VAR hi: char; lo, finode, finptr, dirptr, dircnt, size: integer; BEGIN writeln(' inode filename type blocks'); FOR dircnt := 0 TO decnt - 1 DO BEGIN dirptr := getd(dircnt); finode := dirblock[dirptr].inodeno; IF finode <> 0 THEN BEGIN write(finode: 6, ' '); writef(dirblock[dirptr].filename); write(' '); finptr := geti(finode); IF (inodeblock[finptr].flags AND dirbit) <> 0 THEN write('d') ELSE write(' '); IF (inodeblock[finptr].flags AND spbit) <> 0 THEN write('s') ELSE write(' '); hi := inodeblock[finptr].sizehi; lo := inodeblock[finptr].sizelo; /* following assembly language computes size by subtracting 1 from bytecount (to 32 bits), then shifting to divide by 512 to get # blocks. */ /*$C MOVB HI(6),%0 BIC #^O177400,%0 MOV LO(6),%1 SUB #1,%1 SBC %0 ASHC #-9.,%0 MOV %1,SIZE(6) */ writeln(size + 1: 4) END END END; BEGIN /* sets special (no-echo?) mode on terminal */ /*$C BIS #^O40000,@#^O44 */ nl[0] := chr(15B); nl[1] := chr(12B); writeln(chr(14B), 'Program to read Unix disks.'); write('Unix disk drive: '); readln(logname); IF ord(logname[1]) > 96 THEN logname[1] := chr(ord(logname[1]) - 32); IF ord(logname[2]) > 96 THEN logname[2] := chr(ord(logname[2]) - 32); notdone := true; cihave := 0; inhave := 0; dihave := 0; ibhave := 0; dibhave := 0; /* RESET(DEV,'DX1:'); */ reset(dev, logname); getcurd(1); WHILE notdone DO BEGIN write('% '); read(option); CASE option OF '?': /* Type instructions */ BEGIN readln(foo); writeln('q = quit', nl, 'd = l = directory listing', nl, 'c = change directory', nl, 't = type file', nl, 'm = move to RT11 file', nl, ' "c", "t", & "m" are followed by the inode number', nl, ' of the directory or file.', nl) END; 'q', 'Q': /* Quit to monitor */ BEGIN readln(foo); /* Get rid of trailing CR */ notdone := false; END; 'd', 'D', 'l', 'L': /* List Directory */ BEGIN readln(foo); dirlist END; 'c', 'C': /* Change working directory */ BEGIN readln(arg); getcurd(arg) END; 't', 'T': /* Type contents of Unix file */ BEGIN readln(arg); getfile(arg) END; 'm', 'M': /* Move Unix file to RT-11 file */ BEGIN readln(arg); movefile(arg) END; ' ': /* Catch null lines */ BEGIN writeln('Illegal command -- type ? for help') END; ELSE BEGIN readln(foo); writeln('Illegal command -- type ? for help') END END END; close(dev) END.