-- Transport mechanism: Maintain: small procedures

-- [Juniper]<Grapevine>Maintain>MaintainDetails.mesa

-- Andrew Birrell  13-Jan-82 15:47:42
-- Philip Karlton  15-May-81 16:05:16

DIRECTORY
Ascii,
BodyDefs	USING[ Connect, maxRNameLength, Password, RName ],
GlassDefs	USING[ Handle ],
MaintainPrivate	USING[ Command, Del, Handle ],
NameInfoDefs	USING[ CheckStamp, StampInfo ],
ProtocolDefs	USING[ MakeKey, RNameType ],
PupDefs		USING[ GetPupAddress, PupAddress, PupNameTrouble ],
String		USING[ AppendString, UpperCase ],
Storage		USING[ Prune ];

MaintainDetails: PROGRAM
IMPORTS MaintainPrivate, NameInfoDefs, ProtocolDefs, PupDefs, String,
        Storage
EXPORTS MaintainPrivate =

BEGIN

OPEN MaintainPrivate;

CopyName: PUBLIC PROC[from, to: BodyDefs.RName] =
   { to.length ←0; String.AppendString[to, from] };

CantCheckName: PUBLIC SIGNAL[bad: BodyDefs.RName] = CODE;
BadName:       PUBLIC SIGNAL[bad: BodyDefs.RName] = CODE;
BadConnect:    PUBLIC SIGNAL[bad: BodyDefs.Connect] = CODE;

VerifyOnOff: PUBLIC PROC[handle: MaintainPrivate.Handle] =
   BEGIN
   handle.glass.WriteString[IF handle.verifying THEN " ← off"L ELSE " ← on"L];
   IF Confirm[handle.glass] THEN handle.verifying ← NOT handle.verifying;
   END;

CheckMailName: PUBLIC PROC[name: BodyDefs.RName] =
   { CheckRealName[name ! BadName => {CheckForeignName[name];CONTINUE} ] };

CheckRealName: PUBLIC PROC[name: BodyDefs.RName] =
   BEGIN
   info: NameInfoDefs.StampInfo = NameInfoDefs.CheckStamp[name];
   SELECT info FROM
     allDown => SIGNAL CantCheckName[name];
     notFound => SIGNAL BadName[name];
   ENDCASE => NULL;
   END;

CheckForeignName: PUBLIC PROC[name: BodyDefs.RName] =
   BEGIN
   foreign: BodyDefs.RName = [BodyDefs.maxRNameLength];
   MakeRNameHaveNA["Foreign"L, name, foreign];
   CheckRealName[foreign !
                 CantCheckName => { SIGNAL CantCheckName[name]; RESUME };
                 BadName => { SIGNAL BadName[name]; RESUME } ];
   END;

CheckConnect: PUBLIC PROC[connect: BodyDefs.Connect] =
   BEGIN
   addr: PupDefs.PupAddress;
   PupDefs.GetPupAddress[@addr, connect ! PupDefs.PupNameTrouble =>
      SELECT code FROM
        errorFromServer => { SIGNAL BadConnect[connect]; CONTINUE };
        noRoute => CONTINUE;
      ENDCASE => { SIGNAL CantCheckName[connect]; CONTINUE } ];
   END;

MakeRNameHaveNA: PROC[nname, sname, destination: BodyDefs.RName] =
   BEGIN
   -- nname is of the form "NA" or "NA.something"
   -- sname is of form "something.SN" or "SN"
   -- assumes that SN and NA do not contain '.
   -- constructs "SN.NA", truncating NA if needed
   SNstart: CARDINAL;
   sep: CHARACTER = '.;
   destination.length ← 0;
   SNstart ← sname.length;
   DO IF SNstart = 0 OR sname[SNstart-1] = sep THEN EXIT;
      SNstart ← SNstart - 1;
   ENDLOOP;
   FOR index: CARDINAL IN [SNstart..sname.length)
   DO IF destination.length = destination.maxlength THEN ERROR;
      destination[destination.length] ← sname[index];
      destination.length ← destination.length + 1;
   ENDLOOP;
   IF destination.length = destination.maxlength THEN RETURN;
   destination[destination.length] ← sep;
   destination.length ← destination.length + 1;
   FOR index: CARDINAL IN [0..nname.length)
   WHILE nname[index] # sep
   DO IF destination.length = destination.maxlength THEN EXIT;
      destination[destination.length] ← nname[index];
      destination.length ← destination.length + 1;
   ENDLOOP;
   END;

ReadWord: PUBLIC PROC[str: GlassDefs.Handle,
                      prompt, dest: STRING] =
   { IF str.ReadString[prompt, dest, word] = Ascii.DEL THEN ERROR Del[] };

ReadPassword: PUBLIC PROC[str: GlassDefs.Handle,
                          text, pwd: STRING]
                  RETURNS[key: BodyDefs.Password] =
   BEGIN
   IF str.ReadString[text, pwd, word] = Ascii.DEL THEN ERROR Del[];
   key ← ProtocolDefs.MakeKey[pwd]
   END;

Confirm: PUBLIC PROC[str: GlassDefs.Handle] RETURNS[ ok: BOOLEAN ] =
   BEGIN
   OPEN str;
   WriteString[" [Confirm] "L];
   DO SELECT ReadChar[] FROM
         Ascii.CR, 'Y, 'y => { WriteString["Yes"L]; ok ← TRUE; EXIT };
         'N, 'n => { WriteString["No"L]; ok ← FALSE; EXIT };
         Ascii.DEL => ERROR Del[];
      ENDCASE => WriteChar['?];
   ENDLOOP;
   END;

TypeType: PUBLIC PROC[str: GlassDefs.Handle,
                      type: ProtocolDefs.RNameType] =
   BEGIN
   OPEN str;
   WriteString[", type = "L];
   WriteString[SELECT type FROM
          group =>      "group"L,
          individual => "individual"L,
          notFound =>   "not found"L,
          dead =>       "deleted"L,
        ENDCASE => "???"L];
   END;

Scan: PROC[handle: MaintainPrivate.Handle,
           list: DESCRIPTOR FOR ARRAY OF MaintainPrivate.Command,
           prompt: STRING] =
   BEGIN
   OPEN handle.glass;
   inline: STRING ← [256];
   inchar: CHARACTER;
   i, nMatches, prevMatch, maxPrefixLength, commonPrefixLength: CARDINAL;

   Match: PROCEDURE [i: CARDINAL] RETURNS [BOOLEAN] =
      BEGIN
      j: CARDINAL;
      FOR j IN [0..inline.length)
      DO IF String.UpperCase [list[i].name[j]]~=
             String.UpperCase [inline[j]]
          THEN RETURN[FALSE];
      ENDLOOP;
      RETURN[TRUE]
      END;

   [] ← Storage.Prune[];

   inline.length ← 0;
   WriteString[prompt];

   DO inchar ← ReadChar[];
      SELECT inchar FROM
         '? =>
           BEGIN
           WriteString["?   Commands are:"]; WriteChar[Ascii.CR];
           FOR i IN [0..LENGTH[list])
           DO WriteString[list[i].name];
              IF i+1 # LENGTH[list] THEN WriteString[",  "L];
              ENDLOOP;
           WriteChar[Ascii.CR]; WriteString[prompt]; WriteString[inline];
           END;
         Ascii.DEL =>
           ERROR MaintainPrivate.Del[];
         Ascii.BS, Ascii.ControlA =>
           IF inline.length~=0 THEN
              BEGIN
              inline.length ← inline.length-1;
              WriteChar[("\\"L)[0] -- because \ is escape char in cedar--];
              END;
         ENDCASE =>
           BEGIN
           IF (inchar = Ascii.SP OR inchar = Ascii.CR
               OR inchar = Ascii.ESC) AND inline.length = 0
           THEN { WriteChar[Ascii.CR]; WriteString[prompt]; LOOP };
           inline[inline.length] ← inchar;
           inline.length ← inline.length+1;
           nMatches ← 0;
           FOR i IN [0..LENGTH[list]) DO
              IF Match[i] THEN
                 BEGIN
                 IF nMatches > 0 THEN
                    BEGIN
                    maxPrefixLength ← MIN[list[i].name.length, commonPrefixLength];
                    FOR commonPrefixLength ← inline.length, commonPrefixLength+1 DO
                       IF list[i].name[commonPrefixLength]~=
                          list[prevMatch].name[commonPrefixLength] OR
                          commonPrefixLength=maxPrefixLength THEN EXIT
                       ENDLOOP;
                    END
                 ELSE
                    commonPrefixLength ← list[i].name.length;
                 nMatches ← nMatches+1;
                 prevMatch ← i
                 END
              ENDLOOP;
           IF nMatches=0 THEN
              BEGIN
              WriteChar[inchar];
              DO WriteString["?"];
                 IF ReadChar[] = Ascii.DEL THEN ERROR MaintainPrivate.Del[];
              ENDLOOP
              END
           ELSE
              BEGIN
              WriteChar[list[prevMatch].name[inline.length-1]];
              FOR i IN [inline.length..commonPrefixLength) DO
                 WriteChar[inline[i]←list[prevMatch].name[i]]
                 ENDLOOP;
              inline.length ← commonPrefixLength;
              END;

           IF nMatches=1 THEN EXIT;
           END;
      ENDLOOP;

   list[prevMatch].work[handle]

   END;


Parse: PUBLIC PROC[handle: MaintainPrivate.Handle,
                   list: DESCRIPTOR FOR ARRAY OF MaintainPrivate.Command,
                   prompt: STRING] =
   { DO handle.glass.WriteChar[Ascii.CR]; Scan[handle, list, prompt];
     ENDLOOP };

END.