{-------------------------------------------------------------}
{               routines for phase 1 -- input                 }
{-------------------------------------------------------------}

procedure indent;
    { indent the cursor for the current nesting level }
    var i : 0..9;
    begin
        for i := 1 to indlevel do write('. . ')
    end;

function readnc : nchar;
    { get one byte from the keyboard, bypassing the
      usual pascal procedures and going straight to CP/M }
    var c : integer;
    begin
        repeat
            c := bdos(6,-1)
        until c<>0;
        if c>31 then write(chr(c));
        readnc := c;
    end;

procedure getterm(tree: pnode; var a:str; var cont: boolean);
    { get a term from the user, with control keys used thus:
        cr : end the term.
        lf : end the term, begin a subterm of it.
        esc: try to complete the term with the next (first)
             matching term from the present tree-context.
        del: cancel esc-completion, return to original entry.  }
    var
        c       : nchar;
        j, oj   : strindex;
        k       : strlength;
        x,ua    : str;
        quit    : boolean;
        tw      : treewalk;
        p       : pnode;

    procedure backup;
        { backup the screen and the "a" string to the original
            term that was entered. }
        var qj  : strindex;
        begin
            for qj := j downto (oj+1) do
              write(chr(asciibs),chr(asciiblank),chr(asciibs));
            j := oj;
            a.val[j] := nullch
        end;

    procedure startscan;
        { set up for an alphabetical scan over all terms that
          are an initial match to user entry thus far.  Setscan
          does most of the work. }
        begin
            stucase(a,ua); { for stepscan's benefit }
            p := setscan(tree,tw,a);
            if p<>nil then { phony node only if a.len=0 }
                if p^.skip then p := treestep(tw);
            if p<>nil then begin { this node has to be equal }
                stget(p^.iref,x);
                k := x.len+1
            end
            else k := 0
        end;

    procedure stepscan;
        { find the next match to the original string, leaving
          its value in x, or k=0 if there is none.  }
        begin
            k := 0;
            p := treestep(tw);
            if p<>nil then
                if p^.skip then p := treestep(tw);
            if p<>nil then
                if equal=sxcomp(ua,p^.uref) then begin
                    stget(p^.iref,x);
                    k := x.len+1
                end
        end;

    begin { the main Get Term procedure }
        indent; write('term: ');
        j := 1; oj := j;        { no data in the a-string      }
        k := 0;                 { no esc-scan working          }
        quit := false;          { not finished yet (hardly!)   }
        repeat
            a.val[j] := nullch; { keep "a" a finished string   }
            a.len := j-1;       { ..at all times               }
            c := readnc;
            case c of

            asciibs :           { destructive backspace        }
                if j>1 then begin
               write(chr(asciibs),chr(asciiblank),chr(asciibs));
                    j := j-1;
                    oj := j;    { the current scan is accepted }
                    k := 0;     { ..and no scan is underway    }
                end;

            asciicr :           { normal completion            }
                begin
                    writeln;
                    quit := true
                end;

            asciilf :           { complete, move on to subterm }
                begin
                    writeln;
                    quit := true
                end;

            asciiesc :          { automatic scan for match     }
                begin
                    backup;     { wipe rejected match if any   }
                    if k=0 then startscan else stepscan;
                    if k=0 then { no (further) match found     }
                        write(chr(asciibel))
                    else        { next (first?) match found    }
                        while j<k do begin
                            a.val[j] := x.val[j];
                            write(chr(a.val[j]));
                            j := j+1
                        end
                end;

            asciidel :          { cancel search for match      }
                begin
                    backup;
                    k := 0      { no active scan               }
                end;

            else                { ordinary (?) character       }
                if (c<asciiblank) or (j=strmax) then
                    write(chr(asciibel))
                else begin
                    a.val[j] := c;
                    j := j+1;
                    oj := j;     { the current scan has been   }
                    k := 0       { ..accepted and is over      }
                end
            end {case}
        until quit;
        cont := c=asciilf
    end;

procedure getpage(var i: integer);
    { read a page number into an integer.  If page numbers
      are not simple integers, eg "3-17" and the like, this
      routine would have to build a string. }
    begin
        indent;
        write('page: ');
        readln(i)
    end;

procedure makepage(var p:ppage; i:integer);
    { make a page record and install its address }
    begin
        new(p);
        p^.next := nil;
        p^.num  := i
    end;

procedure addpage(np: pnode; pg: integer);
    { add a page number to the chain off a node.  This is
      a classic case of an algorithm that requires a 2-exit
      loop; the scan of the chain has to stop when a higher
      page number is found OR when the end of the chain is
      reached.  It could be done with Repeat or While, but
      it actually looks cleaner with Goto. }
    label 99,101,102,103;
    var p1, p2, p3: ppage;
    begin
        p1 := np^.phead;
        if p1=nil then makepage(np^.phead,pg)
        else  { some pages already noted, search chain }
            if pg<p1^.num then begin
                makepage(p2,pg); { this page less than all }
                p2^.next := p1;
                np^.phead := p2
            end
            else begin { this page goes somewhere in chain }
            99: p2 := p1^.next;
                if p2=nil then goto 101;
                if pg<p2^.num then goto 102;
                p1 := p2;
                goto 99;
            101: { p1^ last number in chain, pg is => it }
                begin
                    if pg>p1^.num then
                        makepage(p1^.next,pg);
                    goto 103
                end;
            102: {p1^.num <= pg <p2^.num; pg goes between }
                begin
                    if pg>p1^.num then begin
                        makepage(p3,pg);
                        p3^.next := p2;
                        p1^.next := p3
                    end
                end;
            103: ;
            end
    end;

procedure load(var atree:pnode);
    { input control: load terms into a tree from the keyboard.
      the code is recursive; if the user wants to do a subterm
      this routine calls itself to load the sub-tree of the
      superior term's node.  A page number of zero is a disaster
      when we reload the saved tree, so one is converted to -1.}
    var aterm : str;
        anode : pnode;
        apage : integer;
        cont  : boolean;
    begin
        repeat
            getterm(atree,aterm,cont);
            if aterm.len>0 then begin
                anode := insert(atree,aterm);
                if not cont then begin
                    getpage(apage);
                    if apage=0 then apage := 32767;
                    addpage(anode,apage)
                end
                else begin { user hit lf, wants to recurse }
                    if anode^.subt=nil then
                        startree(anode^.subt);
                    indlevel := indlevel+1;
                    load(anode^.subt);
                    indlevel := indlevel-1
                end
            end;
        until (aterm.len=0) or (indlevel>0)
    end;
         indl