PROGRAM UNIX;
/* Copyright (c) 1979 David A. Butterfield and Daniel Weise */

CONST
    QUIT = 'q';
    DIR = 'd';
    LIST = 'l';
    HUH = '?';
    CHDIR = 'c';
    TYPEIT = 't';
    MOVE = 'm';

    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;
    /*$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;
    /*$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;
    /*$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
    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;
            /*$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
    /*$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
    HUH:    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') END;
    QUIT,'Q':
            Begin
            Readln(Foo);	/* Get rid of trailing CR */
            NOTDONE := FALSE;
            End;

    DIR,'D',LIST,'L':
                BEGIN
                READLN(FOO);
                DIRLIST END;

    CHDIR,'C':
                BEGIN
                READLN(ARG);
                GETCURD(ARG) END;

    TYPEIT,'T':
                BEGIN
                READLN(ARG);
                GETFILE(ARG) END;

    MOVE,'M':
                BEGIN
                READLN(ARG);
                MOVEFILE(ARG) END;

    ELSE        BEGIN
                READLN(FOO);
                WRITELN('Illegal command -- type ? for help') END END END;

    CLOSE(DEV) END.
