home *** CD-ROM | disk | FTP | other *** search
/ Programmer's ROM - The Computer Language Library / programmersrom.iso / ada / misc / cli.src < prev    next >
Encoding:
Text File  |  1988-05-03  |  29.3 KB  |  1,056 lines

  1. --::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  2. --chpt1.txt
  3. --::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  4. with TEXT_IO;
  5. package CHAPTER_1 is
  6. --  From Kernighan and Plauger's "Software Tools in Pascal" Addison-Wesley
  7.  
  8.   use TEXT_IO;
  9.   
  10.  
  11.   TAB                   : constant CHARACTER := ASCII.HT;
  12.   LF                    : constant CHARACTER := ASCII.LF;
  13.   NEWLINE               : constant CHARACTER := ASCII.CR;
  14.   ENDFILE               : constant CHARACTER := ASCII.FS;
  15.   BLANK                 : constant CHARACTER := ' ';
  16.  
  17.   MAXLINE               : constant INTEGER := 250;
  18.   TABSPACE              : constant INTEGER := 4;
  19.  
  20.   type TABTYPE is array(INTEGER range 1..MAXLINE) of BOOLEAN;
  21.   TABSTOPS : TABTYPE;
  22.  
  23.   procedure GETC(C : out CHARACTER);
  24.   procedure COPY;
  25.   procedure CHARCOUNT;
  26.   procedure LINECOUNT;
  27.   procedure WORDCOUNT;
  28.   function TABPOS(COL : INTEGER; TABSTOPS : TABTYPE) return BOOLEAN;
  29.   procedure SETTABS(TABSTOPS : out TABTYPE);
  30.   procedure DETAB;
  31.  
  32. end CHAPTER_1;
  33.  
  34.  
  35. with TEXT_IO;
  36. package body CHAPTER_1 is
  37. --  Comments mostly point out differences from Pascal version
  38.  
  39. --  package INTEGER_IO is new TEXT_IO.INTEGER_IO(INTEGER);
  40.   use TEXT_IO;
  41.   use INTEGER_IO;
  42.   
  43.   procedure GETC(C : out CHARACTER) is
  44.   --  Different from the SWT getc which is a function with side effect
  45.   --  This can read a ASCII.FS from the keyboard for a file terminator
  46.   --  Or use an END_ERROR off a file or tape
  47.   begin
  48.     GET(C);
  49.     if C = ENDFILE  then
  50.       raise END_ERROR;
  51.     end if;
  52.   end GETC;
  53.   
  54.   procedure COPY is
  55.   --  1.1  copy input characters to output
  56.   --  This has the major modification to the style of Software Tools
  57.   --  That style may have been required by limitations of other languages
  58.   --  Rather than the unusual getc function with side effects
  59.   --  we will use a more conventional Ada construct
  60.   --  End of file will be handled as in Ada rather than as a special character
  61.   --  so type CHARACTER can be used
  62.   --  When an explicit End of file character is needed ASCII.FS will be used
  63.  
  64.     C : CHARACTER;
  65.  
  66.   begin
  67.     loop
  68.       GETC(C);
  69.       PUT(C);          --  May have to turn off local echo to make sense
  70.     end loop;
  71.   exception
  72.   when END_ERROR  =>
  73.     null;              --      When system EOF finish and exit
  74.   when others  =>
  75.     null;
  76.   end COPY;
  77.  
  78.   
  79.   procedure CHARCOUNT is
  80.   --  1.2  count characters in standard input
  81.  
  82.     C : CHARACTER;
  83.     NC : INTEGER := 0;                 --  Can initialize here in Ada
  84.  
  85.   begin
  86.     loop
  87.       GETC(C);
  88.       NC := NC + 1;
  89.     end loop;
  90.     NEW_LINE;
  91.     PUT("NUMBER OF CHARACTERS = ");
  92.     PUT(NC);                          --  Ada PUT distinguishes type
  93.     NEW_LINE;                         --  Ada has explicit procedure 
  94.   exception
  95.   when END_ERROR  =>
  96.     NEW_LINE;
  97.     PUT("NUMBER OF CHARACTERS = ");
  98.     PUT(NC);
  99.     NEW_LINE;
  100.   end CHARCOUNT;
  101.  
  102.   
  103.   procedure LINECOUNT is
  104.   --  1.3  count lines in standard input
  105.  
  106.     C : CHARACTER;
  107.     NL : INTEGER := 0;
  108.  
  109.   begin
  110.     loop
  111.       GETC(C);
  112.       if C = NEWLINE  then    --  Looks for explicit end of line 
  113.         NL := NL + 1;
  114.       end if;
  115.     end loop;
  116.     NEW_LINE;
  117.     PUT("NUMBER OF LINES = ");
  118.     PUT(NL);
  119.     NEW_LINE;
  120.   exception
  121.   when END_ERROR  =>
  122.     NEW_LINE;
  123.     PUT("NUMBER OF LINES = ");
  124.     PUT(NL);                  --  An unterminated fragment is not counted
  125.     NEW_LINE;
  126.   end LINECOUNT;
  127.  
  128.   
  129.   procedure WORDCOUNT is
  130.   --  1.4  count lines in standard input
  131.  
  132.     C : CHARACTER;
  133.     WC : INTEGER := 0;
  134.     INWORD : BOOLEAN := FALSE;       -- BOOLEAN rather than integer
  135.  
  136.   begin
  137.     loop
  138.       GETC(C);
  139.       if C = BLANK  or  C = NEWLINE  or  C = TAB or C = LF  then
  140.         INWORD := FALSE;        --  We also worry about line feed
  141.       else
  142.         if INWORD = FALSE  then
  143.           INWORD := TRUE;
  144.           WC := WC + 1;
  145.         end if;
  146.       end if;
  147.     end loop;
  148.     NEW_LINE;
  149.     PUT("NUMBER OF WORDS = ");
  150.     PUT(WC);
  151.     NEW_LINE;
  152.   exception
  153.   when END_ERROR  =>
  154.     NEW_LINE;
  155.     PUT("NUMBER OF WORDS = ");
  156.     PUT(WC);
  157.     NEW_LINE;
  158.   end WORDCOUNT;
  159.  
  160.   procedure SETTABS(TABSTOPS : out TABTYPE) is
  161.   --  1.5  set initial tab stops
  162.   begin
  163.     for I in 1..MAXLINE  loop
  164.       if I mod TABSPACE = 1  then
  165.         TABSTOPS(I) := TRUE;
  166.       else
  167.         TABSTOPS(I) := FALSE;
  168.       end if;
  169.     end loop;
  170.   end SETTABS;
  171.  
  172.   function TABPOS(COL : INTEGER; TABSTOPS : TABTYPE) return BOOLEAN is
  173.   --  1.5  return true if col is a tab stop
  174.   begin
  175.     if COL > MAXLINE  then
  176.       return TRUE;
  177.     else
  178.       return TABSTOPS(COL);
  179.     end if;
  180.   end TABPOS;
  181.   
  182.   procedure DETAB is
  183.   --  1.5  convert tabs to equivalent number of blanks
  184.     C : CHARACTER;
  185.     COL : INTEGER := 1;
  186.  
  187.   begin
  188.     SETTABS(TABSTOPS);
  189.     loop
  190.       GETC(C);
  191.       if C = TAB  then
  192.         loop
  193.           PUT(BLANK);
  194.           COL := COL + 1;
  195.           exit when TABPOS(COL, TABSTOPS) = TRUE;
  196.         end loop;
  197.       elsif C = NEWLINE  then
  198.         PUT(NEWLINE);
  199.         COL := 1;
  200.       else
  201.        PUT(C);
  202.         if C /= LF  then
  203.          COL := COL + 1;
  204.         end if;
  205.       end if;
  206.     end loop;
  207.     PUT(ENDFILE);      --  If that is how we got here
  208.   exception
  209.   when END_ERROR  =>
  210.     null;              --  If we got out by sensing EOF
  211.   end DETAB;
  212.  
  213. begin
  214.   SET_INPUT(STANDARD_INPUT);
  215. end CHAPTER_1;
  216. --::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  217. --chpt2.txt
  218. --::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  219. with CHAPTER_1;         use CHAPTER_1;
  220. package CHAPTER_2 is
  221.  
  222.   BACKSPACE : constant CHARACTER := ASCII.BS;
  223.   WARNING   :   constant CHARACTER := ASCII.TILDE;
  224.   ENDLINE   : constant CHARACTER := NEWLINE;
  225.   ENDSTR    : constant CHARACTER := ASCII.GS;             --  CTL-]
  226.   ESCAPE    : constant CHARACTER := '@';
  227.   DASH      : constant CHARACTER := '-';
  228.   NEGATE    : constant CHARACTER := '^';
  229.  
  230.   MAXSTR    : constant INTEGER := MAXLINE;
  231.   MAXSET    : constant INTEGER := MAXSTR;
  232.   MAXARG    : constant INTEGER := 12;
  233.  
  234.   ARGUMENTS : array(1..MAXARG) of STRING(1..MAXSTR);
  235.   NUMBER_OF_ARGUMENTS : INTEGER := 0;
  236.  
  237.   GETARG_OK  : BOOLEAN := FALSE;
  238.   ADDSTR_OK  : BOOLEAN := FALSE;
  239.   MAKESET_OK : BOOLEAN := FALSE;
  240.  
  241.   ESCAPED_CHAR : CHARACTER;
  242.  
  243.   ERROR_ERROR    : exception;
  244.   GETARG_ERROR   : exception;
  245.   ADDSTR_ERROR   : exception;
  246.   MAKESET_ERROR  : exception;
  247.   TRANSLIT_ERROR : exception;
  248.  
  249.   procedure ERROR(ERROR_MESSAGE : STRING);
  250.   procedure ENTAB;
  251.   function MAX(X, Y : INTEGER) return INTEGER;
  252.   procedure OVERSTRIKE;
  253.   function MIN(X, Y : INTEGER) return INTEGER;
  254.   procedure COMPRESS;
  255.   function ISUPPER(C : CHARACTER) return BOOLEAN;
  256.   procedure EXPAND;
  257.   procedure CONVERT_ESCAPED(S : in STRING;
  258.                             I : in out INTEGER;
  259.                  ESCAPED_CHAR : out CHARACTER);
  260.   procedure GETARG(N : in INTEGER;
  261.               ARGSTR : out STRING;
  262.               MAX_OF : in INTEGER);
  263.   function LENGTH(S : STRING) return INTEGER;
  264.   procedure ADDSTR(C : in CHARACTER;
  265.               OUTSET : in out STRING;
  266.                    J : in out INTEGER;
  267.               MAX_OF : in INTEGER);
  268.   procedure DODASH(DELIM : in CHARACTER;
  269.                      SRC : in STRING;
  270.                        I : in out INTEGER;
  271.                     DEST : in out STRING;
  272.                        J : in out INTEGER;
  273.                   MAX_OF : in INTEGER);
  274.   procedure TRANSLIT;
  275.  
  276. end CHAPTER_2;
  277.  
  278.  
  279. with TEXT_IO; 
  280. with CHAPTER_1; use CHAPTER_1;
  281. package body CHAPTER_2 is
  282. --  package INTEGER_IO is new TEXT_IO.INTEGER_IO(INTEGER);
  283.   use TEXT_IO;
  284.   use INTEGER_IO;
  285.  
  286.   procedure ERROR(ERROR_MESSAGE : STRING) is
  287.   begin
  288.     PUT(ERROR_MESSAGE);
  289.     raise ERROR_ERROR;
  290.   end ERROR;
  291.  
  292.   procedure ENTAB is
  293.   --  2.1  replace blanks by tabs and blanks
  294.     type TABTYPE is array (INTEGER range 1..MAXLINE) of BOOLEAN;
  295.     C : CHARACTER;
  296.     COL, NEWCOL : INTEGER := 1;
  297.   begin
  298.     SETTABS(TABSTOPS);
  299.     loop
  300.       NEWCOL := COL;
  301.       loop
  302.         GETC(C);
  303.         exit when C /= BLANK;
  304.         NEWCOL := NEWCOL + 1;
  305.         if TABPOS(NEWCOL, TABSTOPS)  then
  306.           PUT(TAB);
  307.           COL := NEWCOL;
  308.         end if;
  309.       end loop;
  310.       while COL < NEWCOL  loop
  311.         PUT(BLANK);
  312.         COL := COL + 1;
  313.       end loop;
  314.       if C /= ENDFILE  then
  315.         PUT(C);
  316.         if C = NEWLINE  then
  317.           COL := 1;
  318.         else
  319.           COL := COL + 1;
  320.         end if;
  321.       end if;
  322.       exit when C = ENDFILE;
  323.     end loop;
  324.   exception
  325.     when END_ERROR  =>
  326.     null;              --      When system EOF finish and exit
  327.   when others  =>
  328.     null;
  329.   end ENTAB;
  330.  
  331.   function MAX(X, Y : INTEGER) return INTEGER is
  332.   --  2.2  compute maximum of two integers
  333.   begin
  334.     if X > Y  then
  335.       return X;
  336.     else
  337.       return Y;
  338.     end if;
  339.   end MAX;
  340.  
  341.   procedure OVERSTRIKE is
  342.   --  2.2  convert into multiple lines
  343.     SKIP   : constant CHARACTER := BLANK;
  344.     NOSKIP : constant CHARACTER := '+';
  345.  
  346.     C : CHARACTER;
  347.     COL, NEWCOL : INTEGER := 1;
  348.     I : INTEGER;
  349.  
  350.   begin
  351.     loop
  352.       NEWCOL := COL;
  353.       loop                            --  eat backspace
  354.         GETC(C);
  355.         exit when C /= BACKSPACE;
  356.         NEWCOL := MAX(NEWCOL - 1, 1);
  357.       end loop;
  358.       if NEWCOL < COL  then
  359.         NEW_LINE;
  360.         PUT(NOSKIP);                  --  start overstrike line
  361.         for I in 1..NEWCOL-1  loop
  362.           PUT(BLANK);
  363.         end loop;
  364.         COL := NEWCOL;
  365.       elsif COL = 1  and  C /= ENDFILE  then
  366.         PUT(SKIP);                    --  normal line
  367.       end if;
  368.       if C /= ENDFILE  then
  369.         PUT(C);
  370.         if C = NEWLINE  then
  371.           COL := 1;
  372.         else
  373.           COL := COL + 1;
  374.         end if;
  375.       end if;
  376.       exit when C = ENDFILE;
  377.     end loop;
  378.   exception
  379.     when END_ERROR  =>
  380.     null;              --      When system EOF finish and exit
  381.   when others  =>
  382.     null;
  383.   end OVERSTRIKE;
  384.  
  385.  
  386.   function MIN(X, Y : INTEGER) return INTEGER is
  387.   --  2.3  compute minimum of two integers
  388.   begin
  389.     if X < Y  then
  390.       return X;
  391.     else
  392.       return Y;
  393.     end if;
  394.   end MIN;
  395.  
  396.   procedure PUTREP(N : in INTEGER; C : in CHARACTER) is
  397.   --  2.3  put out representation of run of n 'C's
  398.     MAXREP : constant INTEGER := 26;         --  assumimg 'A'..'Z'
  399.     THRESH : constant INTEGER := 4;
  400.     M : INTEGER;
  401.   begin
  402.     M := N;
  403.     while M >= THRESH  or  (C = WARNING and M > 0)  loop
  404.       PUT(WARNING);
  405.       PUT(CHARACTER'VAL(MIN(N, MAXREP) - 1 + CHARACTER'POS('A')));
  406.       PUT(C);
  407.       M := M - MAXREP;
  408.     end loop;
  409.     for I in REVERSE 1..M  loop
  410.       PUT(C);
  411.     end loop;
  412.   end PUTREP;
  413.  
  414.   procedure COMPRESS is
  415.   --  2.3  compress standard input
  416.     C, LASTC : CHARACTER;
  417.     N : INTEGER := 1;
  418.   begin
  419.     GETC(LASTC);
  420.     while LASTC /= ENDFILE  loop
  421.       GETC(C);
  422.       if C = ENDFILE  then
  423.         if N > 1  or LASTC = WARNING  then
  424.           PUTREP(N, LASTC);
  425.           N := 1;
  426.         else
  427.           PUT(LASTC);
  428.         end if;
  429.       elsif C = LASTC  then
  430.         N := N + 1;
  431.       elsif N > 1  or LASTC = WARNING  then
  432.         PUTREP(N, LASTC);
  433.         N := 1;
  434.       else
  435.         PUT(LASTC);
  436.       end if;
  437.       LASTC := C;
  438.     end loop;
  439.   exception
  440.     when END_ERROR  =>
  441.       null;              --      When system EOF finish and exit
  442.       if N > 1  or LASTC = WARNING  then
  443.         PUTREP(N, LASTC);
  444.         N := 1;
  445.       else
  446.         PUT(LASTC);
  447.       end if;
  448.     when others  =>
  449.       null;
  450.   end COMPRESS;
  451.  
  452.   function ISUPPER(C : CHARACTER) return BOOLEAN is
  453.   --  2.4  true if C is upper case letter
  454.   begin
  455.     return C in 'A'..'Z';
  456.   end ISUPPER;
  457.  
  458.   procedure EXPAND is
  459.   --  2.4  uncompress standard input
  460.     C : CHARACTER;
  461.     N : INTEGER;
  462.   begin
  463.     loop
  464.       GETC(C);
  465.       exit when C = ENDFILE;
  466.       if C /= WARNING  then
  467.         PUT(C);
  468.       else
  469.         GET(C);
  470.         if ISUPPER(C)  then
  471.           N := CHARACTER'POS(C) - CHARACTER'POS('A') + 1;
  472.           GET(C);
  473.           if C /= ENDFILE  then
  474.             for I in reverse 1..N  loop
  475.               PUT(C);
  476.             end loop;
  477.           else
  478.             PUT(WARNING);
  479.             PUT(CHARACTER'VAL(N - 1 + CHARACTER'POS('A')));
  480.           end if;
  481.         else
  482.           PUT(WARNING);
  483.           if C /= ENDFILE  then
  484.             PUT(C);
  485.           end if;
  486.         end if;
  487.       end if;
  488.     end loop;
  489.   exception
  490.     when END_ERROR  =>
  491.     null;              --      When system EOF finish and exit
  492.     PUT(ENDFILE);                --  I think it needs this one
  493.   when others  =>
  494.     null;
  495.   end EXPAND;
  496.  
  497.   procedure GETARG(N : in INTEGER;
  498.               ARGSTR : out STRING;
  499.               MAX_OF : in INTEGER) is
  500.   begin
  501.     if N > NUMBER_OF_ARGUMENTS  then
  502.       --GETARG_OK := FALSE;      --########################################
  503.       ARGSTR(1) := ENDSTR;
  504.     else
  505.       --GETARG_OK := TRUE;
  506.       ARGSTR := ARGUMENTS(N);
  507.     end if;
  508.     return;
  509.   end GETARG;
  510.  
  511.   function LENGTH(S : STRING) return INTEGER is
  512.   --  2.5  compute length of string
  513.   --  A bit different from the Pascal in initialization and return lines
  514.     N : INTEGER := 0;
  515.   begin
  516.     for I in 1..MAXSTR  loop
  517.       exit when S(I) = ENDSTR;
  518.       N := N + 1;
  519.     end loop;
  520.     return N;
  521.   end LENGTH;
  522.  
  523.   procedure ECHO is
  524.   --  2.5  echo command line arguments to output
  525.     I : INTEGER := 1;
  526.     ARGSTR : STRING(1..MAXSTR);
  527.   begin
  528.     loop
  529.       GETARG(I, ARGSTR, MAXSTR);
  530.       if I > 1  then
  531.         PUT(BLANK);
  532.       end if;
  533.       for J in 1..LENGTH(ARGSTR)  loop  
  534.         PUT(ARGSTR(J));
  535.       end loop;
  536.       I := I + 1;
  537.     end loop;
  538.   exception
  539.     when GETARG_ERROR  =>
  540.       if  I > 1  then
  541.         NEW_LINE;
  542.       end if;
  543.   end ECHO;
  544.  
  545.     procedure CONVERT_ESCAPED(S : in STRING;
  546.                             I : in out INTEGER;
  547.                  ESCAPED_CHAR : out CHARACTER) is
  548. --  2.6  map S(I) into escaped character, increment I
  549.   --  this procedure is substituted for the equivalent SOFTWARE TOOLS function
  550.   --  since Ada will not abide side effect of an in out parameter in function
  551.   --  further it prepares for a wide range of escaped characters
  552.     --ESCAPED : array(CHARACTER range 'A'..'z') of CHARACTER :=
  553.          --('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  554.       --NEWLINE, 'O', 'P', 'Q', 'R', 'S', TAB, 'U', 'V', 'W', 'X', 'Y', 'Z',
  555.           --'[', '\', ']', '^', '_', '|',
  556.           --'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  557.       --NEWLINE, 'o', 'p', 'q', 'r', 's', TAB, 'u', 'v', 'w', 'x', 'y', 'z');
  558.   --  Put in to simulate the character array that is not yet implemented
  559.   function ESCAPED(C : in CHARACTER) return CHARACTER is
  560.     Q : CHARACTER;
  561.   begin
  562.     Q := C;
  563.     if (C = 'N') or (C = 'n')  then
  564.       return NEWLINE;
  565.     elsif (C = 'T') or (C = 't')  then
  566.       return TAB;
  567.     else
  568.       return Q;
  569.     end if;
  570.   end ESCAPED;
  571.   
  572.   begin
  573.     if S(I) /= ESCAPE  then
  574.      ESCAPED_CHAR := S(I);
  575.     elsif S(I+1) = ENDSTR  then              --  ESCAPE not special at end
  576.      ESCAPED_CHAR := ESCAPE;
  577.     else
  578.       I := I + 1;
  579.      ESCAPED_CHAR := ESCAPED(S(I));
  580.     end if;
  581.   end CONVERT_ESCAPED;
  582.  
  583.   function ISALPHANUM(C : CHARACTER) return BOOLEAN is
  584.   --  2.6 if C is letter or digit
  585.   begin
  586.     return (C in 'A'..'Z') or (C in 'a'..'z') or (C in '0'..'9');
  587.   end ISALPHANUM;
  588.  
  589.   procedure ADDSTR(C : in CHARACTER;
  590.               OUTSET : in out STRING;
  591.                    J : in out INTEGER;
  592.               MAX_OF : in INTEGER) is
  593.   --  2.6  put C in OUTSET(J) if it fits, increment J
  594.   --  substituted a procedure for the SOFTWARE TOOLS function
  595.   begin
  596.     if J > MAX_OF  then
  597.       --ADDSTR_OK := FALSE;  --####################################
  598.       return;
  599.     else
  600.       OUTSET(J) := C;
  601.       J := J + 1;
  602.       --ADDSTR_OK := TRUE;   --#####################################
  603.     end if;
  604.   end ADDSTR;
  605.  
  606.   procedure DODASH(DELIM : in CHARACTER;
  607.                      SRC : in STRING;
  608.                        I : in out INTEGER;
  609.                     DEST : in out STRING;
  610.                        J : in out INTEGER;
  611.                   MAX_OF : in INTEGER) is
  612.   --  2.6  expand set at SRC(I) into DEST(J), stop at DELIM
  613.     K : CHARACTER;
  614.   begin
  615.     while (SRC(I) /= DELIM) and (SRC(I) /= ENDSTR)  loop
  616.       if SRC(I) = ESCAPE  then
  617.         CONVERT_ESCAPED(SRC, I, ESCAPED_CHAR);
  618.         ADDSTR(ESCAPED_CHAR, DEST, J, MAXSET);
  619.       elsif SRC(I) /= DASH  then
  620.         ADDSTR(SRC(I), DEST, J, MAXSET);
  621.       elsif J <= 1  or  SRC(I+1) = ENDSTR  then
  622.         ADDSTR(DASH, DEST, J, MAXSET);       --  literal -
  623.       elsif ISALPHANUM(SRC(I-1)) and ISALPHANUM(SRC(I+1)) and
  624.                      SRC(I-1) <= SRC(I+1)  then
  625.         for K in CHARACTER'VAL(CHARACTER'POS(SRC(I-1)) + 1)..SRC(I+1)  loop
  626.           ADDSTR(K, DEST, J, MAXSET);
  627.         end loop;
  628.         I := I + 1;
  629.       else
  630.         ADDSTR(DASH, DEST, J, MAXSET);
  631.       end if;
  632.       I := I + 1;
  633.     end loop;
  634.   end DODASH;
  635.  
  636.   procedure MAKESET(INSET : in STRING;
  637.                         K : in out INTEGER;
  638.                    OUTSET : in out STRING;
  639.                    MAX_OF : in INTEGER) is
  640.   --  2.6  make set from INSET(K) in OUTSET
  641.   --  procedure rather than function
  642.     J : INTEGER := 1;
  643.   begin
  644.     DODASH(ENDSTR, INSET, K, OUTSET, J, MAXSET);
  645.     ADDSTR(ENDSTR, OUTSET, J, MAXSET);
  646.   exception
  647.     when ADDSTR_ERROR  =>
  648.       raise MAKESET_ERROR;
  649.   end MAKESET;
  650.  
  651.   function INDEX(S : STRING; C : CHARACTER) return INTEGER is
  652.   --  2.6  find position of  character C in string S
  653.     I : INTEGER := 1;
  654.   begin
  655.     while S(I) /= C  and  S(I) /= ENDSTR  loop
  656.       I := I + 1;
  657.     end loop;
  658.     if S(I) = ENDSTR  then
  659.       return 0;
  660.     else
  661.       return I;
  662.     end if;
  663.   end INDEX;
  664.   
  665.   function XINDEX(INSET : STRING; C : CHARACTER;
  666.                   ALLBUT : BOOLEAN; LASTTO : INTEGER) return INTEGER is
  667.   --  2.6  conditionally invert value from index
  668.   begin
  669.     if C = ENDFILE  then
  670.       return 0;
  671.     elsif not ALLBUT  then
  672.       return INDEX(INSET, C);
  673.     elsif INDEX(INSET, C) > 0  then
  674.       return 0;
  675.     else
  676.       return LASTTO + 1;
  677.     end if;
  678.   end XINDEX;
  679.  
  680.   procedure TRANSLIT is
  681.   --  2.6  map characters
  682.     ARGSTR, FROMSET, TOSET : STRING(1..MAXSTR);
  683.     C : CHARACTER;
  684.     I, K, LASTTO : INTEGER;
  685.     ALLBUT, SQUASH : BOOLEAN;
  686.   begin
  687.     
  688.     GETARG_FROMSET:
  689.       begin
  690.         GETARG(1, ARGSTR, MAXSTR);
  691.       exception
  692.         when GETARG_ERROR  =>
  693.           ERROR("usage: translit from to");
  694.           raise TRANSLIT_ERROR;
  695.       end GETARG_FROMSET;
  696.     
  697.     ALLBUT := (ARGSTR(1) = NEGATE);
  698.     if ALLBUT  then
  699.       I := 2;
  700.     else
  701.       I := 1;
  702.     end if;
  703.     
  704.     MAKE_FROMSET:
  705.       begin
  706.         MAKESET(ARGSTR, I, FROMSET, MAXSTR);
  707.       exception
  708.         when MAKESET_ERROR  =>
  709.           ERROR("translit: from set too large");
  710.           raise TRANSLIT_ERROR;
  711.     end MAKE_FROMSET;
  712.  
  713.     GETARG_TOSET:
  714.       begin
  715.         GETARG(2, ARGSTR, MAXSTR);
  716.           K := 1;                          --  Dummy to MAKESET 1
  717.     
  718.           MAKE_TOSET:
  719.             begin
  720.               MAKESET(ARGSTR, K, TOSET, MAXSTR);
  721.             exception
  722.               when MAKESET_ERROR  =>
  723.                 ERROR("translit: to set too large");
  724.                 raise TRANSLIT_ERROR;
  725.             end MAKE_TOSET;
  726.             
  727.           if  LENGTH(FROMSET) < LENGTH(TOSET)  then
  728.             ERROR("translit: from shorter than to");
  729.             raise TRANSLIT_ERROR;
  730.           end if;
  731.       exception
  732.         when GETARG_ERROR  =>
  733.         TOSET(1) := ENDSTR;
  734.       end GETARG_TOSET;
  735.     
  736.  
  737.     LASTTO := LENGTH(TOSET);
  738.     SQUASH := (LENGTH(FROMSET) > LASTTO) or ALLBUT;
  739.     loop
  740.       GETC(C);
  741.       I := XINDEX(FROMSET, C, ALLBUT, LASTTO);
  742.       if SQUASH and (I >= LASTTO) and (LASTTO > 0)  then
  743.         PUT(TOSET(LASTTO));
  744.         loop
  745.           GETC(C);
  746.           I := XINDEX(FROMSET, C, ALLBUT, LASTTO);
  747.           exit when I < LASTTO;
  748.         end loop;
  749.       end if;
  750.       if C /= ENDFILE  then
  751.         if (I > 0) and (LASTTO > 0)  then                     --  translate
  752.           PUT(TOSET(I));
  753.         elsif I = 0  then                                     --  copy
  754.           PUT(C);
  755.         end if;
  756.       end if;
  757.       exit when C = ENDFILE;
  758.     end loop;
  759.   exception
  760.   when TRANSLIT_ERROR  =>
  761.     PUT("TRANSLIT ERROR");
  762.     return;
  763.   when END_ERROR  =>
  764.     return;
  765.   end TRANSLIT;
  766.  
  767. begin
  768.   null;
  769. end CHAPTER_2;
  770.  
  771. --::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  772. --cli2.txt
  773. --::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  774. with TEXT_IO; use TEXT_IO;
  775. with CHAPTER_1; use CHAPTER_1;
  776. with CHAPTER_2; use CHAPTER_2;
  777. procedure CLI_2 is
  778.  
  779.   type COMMAND_TYPE is (COPY, CHARCOUNT, LINECOUNT, WORDCOUNT, DETAB,
  780.                         ENTAB, OVERSTRIKE, COMPRESS, EXPAND, TRANSLIT,
  781.                         QUIT, QUERY, UNKNOWN, REFUSED, ABORTING);
  782.   COMMAND : COMMAND_TYPE;
  783.   COMMAND_LINE : STRING(1..MAXLINE);
  784.   UNKNOWN_COUNT : INTEGER := 0;
  785.   TRYING_AGAIN : BOOLEAN := FALSE;
  786.  
  787.   function CONVERT_TO_UPPER_CASE(C : CHARACTER) return CHARACTER is
  788.   --UPPER_CASE : array(CHARACTER range 'a'..'z') of CHARACTER
  789.             --:= ('A','B','C','D','E','F','G','H','I','J','K','L','M',
  790.                 --'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
  791.     function UPPER_CASE(C : CHARACTER) return CHARACTER is
  792.     begin
  793.       if C in 'a'..'z'  then
  794.         return CHARACTER'VAL(CHARACTER'POS(C) - 32);
  795.       else
  796.         return C;
  797.       end if;
  798.     end UPPER_CASE;
  799. begin
  800.   if C in 'a'..'z'  then
  801.     return UPPER_CASE(C);
  802.   else
  803.     return C;
  804.   end if;
  805. end CONVERT_TO_UPPER_CASE;
  806.  
  807. function CONVERT_TO_UPPER_CASE(S : STRING) return STRING is
  808.   T : STRING(S'FIRST..S'LAST);
  809. begin
  810.   for I in S'RANGE  loop
  811.     T(I) := CONVERT_TO_UPPER_CASE(S(I));
  812.   end loop;
  813.   return T;
  814. end CONVERT_TO_UPPER_CASE;
  815.  
  816.  
  817.  
  818.   procedure PUT_OUT(LINE : in STRING) is
  819.   --  Always have the PUT before the GET if possible so you can use it
  820.   --  in diagnostics in developing the GET if necessary
  821.   begin
  822.     for I in 1..MAXLINE  loop
  823.       if LINE(I) = ENDLINE  then
  824.         NEW_LINE;
  825.         exit;
  826.       else
  827.         PUT(LINE(I));
  828.       end if;
  829.     end loop;
  830.   end PUT_OUT;
  831.  
  832.  
  833. procedure GET_COMMAND_LINE(COMMAND_LINE : out STRING) is
  834.     C : CHARACTER := ' ';
  835.     LINE_LENGTH : INTEGER := MAXLINE;
  836.   begin
  837.     GET_LINE(COMMAND_LINE, LINE_LENGTH);
  838.     COMMAND_LINE(LINE_LENGTH + 1) := ENDLINE;
  839.   exception
  840.     when others  =>
  841.       null;
  842.   end GET_COMMAND_LINE;
  843.  
  844.   function IS_ARGUMENT_SEPARATOR(C : CHARACTER) return BOOLEAN is
  845.   begin
  846.     return  C = BLANK
  847.         or  C = TAB
  848.         or  C = LF
  849.         or  C = '('
  850.         or  C = ','
  851.         or  C = '/';
  852.   end IS_ARGUMENT_SEPARATOR;
  853.  
  854.   function IS_ARGUMENT_TERMINATOR(C : CHARACTER) return BOOLEAN is
  855.   begin
  856.     return  C = ENDSTR
  857.         or  C = NEWLINE
  858.         or  C = ')'
  859.         or  C = ';';
  860.   end IS_ARGUMENT_TERMINATOR;
  861.  
  862.   procedure GET_ARGUMENTS(S : in STRING; I : in INTEGER) is
  863.     N : INTEGER := 0;
  864.     J : INTEGER := 1;
  865.     K : INTEGER := 1;
  866.     QUOTE : BOOLEAN := FALSE;
  867.   begin
  868.     J := I;
  869.     while ( not IS_ARGUMENT_SEPARATOR(S(J)) )
  870.       and ( not IS_ARGUMENT_TERMINATOR(S(J)) )  loop
  871.       J := J + 1;                            --  eat rest of the command
  872.     end loop;
  873.     while not IS_ARGUMENT_TERMINATOR(S(J))  loop
  874.       N := N + 1;
  875.       while S(J) = BLANK  or  S(J) = TAB  loop
  876.         J := J + 1;                          --  eat blanks
  877.       end loop;
  878.       K := 1;
  879.       while ( ( not IS_ARGUMENT_SEPARATOR(S(J)) )
  880.           and ( not IS_ARGUMENT_TERMINATOR(S(J)) ) )
  881.           or QUOTE  loop
  882.         if not QUOTE and S(J) = '"'  then
  883.           QUOTE := TRUE;
  884.         elsif QUOTE and S(J) = '"'  then
  885.           QUOTE := FALSE;
  886.         else
  887.           ARGUMENTS(N)(K) := S(J);
  888.           K := K + 1;
  889.         end if;
  890.         J := J + 1;
  891.       end loop;
  892.       ARGUMENTS(N)(K) := ENDSTR;
  893.     end loop;
  894.     NUMBER_OF_ARGUMENTS := N;
  895.     return;
  896.   end GET_ARGUMENTS;
  897.  
  898.   procedure PUT_ARGUMENTS is
  899.   begin
  900.     for N in 1..NUMBER_OF_ARGUMENTS  loop
  901.       for K in 1..MAXSTR  loop
  902.         exit when ARGUMENTS(N)(K) = ENDSTR;
  903.         PUT(ARGUMENTS(N)(K));
  904.       end loop;
  905.       exit when N = NUMBER_OF_ARGUMENTS;  --  So you dont finish on a comma
  906.       PUT(""", """);                      --  Only string arguments so far
  907.     end loop;
  908.     return;
  909.   end PUT_ARGUMENTS;
  910.  
  911.   procedure PARSE_2(COMMAND_LINE : in STRING;
  912.                          COMMAND : out COMMAND_TYPE) is
  913.     CONFIRMATION : STRING(1..MAXLINE);
  914.     CONFIRMATION_LENGTH : NATURAL := 0;
  915.     I : INTEGER;
  916.           
  917.     procedure LIST_COMMANDS is
  918.     begin
  919.       PUT("COMMANDS ARE -- COPY, CHARCOUNT, LINECOUNT, WORDCOUNT, DETAB,");
  920.       NEW_LINE;
  921.       PUT("                ENTAB, OVERSTRIKE, COMPRESS, EXPAND, TRANSLIT,"); 
  922.       NEW_LINE;
  923.       PUT("                QUIT"); NEW_LINE;
  924.     end LIST_COMMANDS;
  925.     
  926.   begin
  927.     NEW_LINE;
  928.     I := 0;
  929.     loop
  930.       I := I + 1;
  931.       if I = MAXLINE  then
  932.         COMMAND := UNKNOWN;
  933.         UNKNOWN_COUNT := UNKNOWN_COUNT + 1;
  934.         return;
  935.       end if;
  936.       if COMMAND_LINE(I) /= BLANK  then      --  Eat blanks
  937.         if CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+2)) = "COP"  then
  938.           COMMAND := COPY;
  939.           PUT("COPY;  [Confirm with CR] -->");
  940.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "CH"  then
  941.           COMMAND := CHARCOUNT;
  942.           PUT("CHARCOUNT;  [Confirm with CR] -->");
  943.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "LI"  then
  944.           COMMAND := LINECOUNT;
  945.           PUT("LINECOUNT;  [Confirm with CR] -->");
  946.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "WO"  then
  947.           COMMAND := WORDCOUNT;
  948.           PUT("WORDCOUNT;  [Confirm with CR] -->");
  949.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "DE"  then
  950.           COMMAND := DETAB;
  951.           PUT("DETAB;  [Confirm with CR] -->");
  952.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "DE"  then
  953.           COMMAND := DETAB;
  954.           PUT("DETAB;  [Confirm with CR] -->");
  955.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "EN"  then
  956.           COMMAND := ENTAB;
  957.           PUT("ENTAB;  [Confirm with CR] -->");
  958.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "OV"  then
  959.           COMMAND := OVERSTRIKE;
  960.           PUT("OVERSTRIKE;  [Confirm with CR] -->");
  961.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+2)) = "COM"  then
  962.           COMMAND := COMPRESS;
  963.           PUT("COMPRESS;  [Confirm with CR] -->");
  964.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+2)) = "EXP"  then
  965.           COMMAND := EXPAND;
  966.           PUT("EXPAND;  [Confirm with CR] -->");
  967.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+1)) = "TR"  then
  968.           COMMAND := TRANSLIT;
  969.           GET_ARGUMENTS(COMMAND_LINE, I+2);
  970.           PUT("TRANSLIT(""");                 --  Only string arguments so far
  971.           PUT_ARGUMENTS;
  972.           PUT(""");  [Confirm with CR] -->");
  973.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I  )) = "Q"  then
  974.           COMMAND := QUIT;
  975.           PUT("QUIT;  [Confirm with CR] -->");
  976.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I+2)) = "EXI"  then
  977.           COMMAND := QUIT;
  978.           PUT("QUIT;  [Confirm with CR] -->");
  979.         elsif CONVERT_TO_UPPER_CASE(COMMAND_LINE(I..I  )) = "?"  then
  980.           COMMAND := QUERY;
  981.           LIST_COMMANDS;
  982.           return;
  983.         else
  984.           COMMAND := UNKNOWN;
  985.           PUT("COMMAND NOT RECOGNIZED ");
  986.           NEW_LINE;
  987.           UNKNOWN_COUNT := UNKNOWN_COUNT + 1;
  988.           if UNKNOWN_COUNT < 3  then
  989.             PUT("TRY AGAIN "); NEW_LINE;
  990.             return;
  991.           end if;
  992.         end if;
  993.         exit;
  994.       end if;
  995.     end loop;
  996.  
  997.     if UNKNOWN_COUNT >= 3  then
  998.       if TRYING_AGAIN = FALSE  then
  999.         PUT("YOU ARE NOT MAKING IT"); NEW_LINE;
  1000.         LIST_COMMANDS;
  1001.         COMMAND := UNKNOWN;
  1002.         UNKNOWN_COUNT := 0;
  1003.         TRYING_AGAIN := TRUE;
  1004.         return;
  1005.       else
  1006.         PUT("THREE FAILURES IN A ROW  -- AGAIN --  ABORTING CLI_1 ");
  1007.         COMMAND := ABORTING;
  1008.         return;
  1009.       end if;
  1010.     end if;
  1011.  
  1012.     GET_LINE(CONFIRMATION, CONFIRMATION_LENGTH);
  1013.     if CONFIRMATION_LENGTH /= 0  then        --  Just a CR gives no LENGTH
  1014.       COMMAND := REFUSED;
  1015.       UNKNOWN_COUNT := UNKNOWN_COUNT + 1;
  1016.       PUT("?"); NEW_LINE;
  1017.     else
  1018.       PUT("CONFIRMED!"); NEW_LINE;
  1019.       UNKNOWN_COUNT := 0;
  1020.       TRYING_AGAIN := FALSE;
  1021.     end if;
  1022.     return;
  1023.  
  1024.   end PARSE_2;
  1025.  
  1026.  
  1027. begin
  1028.   loop
  1029.     NEW_LINE;
  1030.     PUT("CLI_2 -->");
  1031.     GET_COMMAND_LINE(COMMAND_LINE);
  1032.     PARSE_2(COMMAND_LINE, COMMAND);
  1033.     case COMMAND is
  1034.     when COPY         =>  CHAPTER_1.COPY;
  1035.     when CHARCOUNT    =>  CHAPTER_1.CHARCOUNT;
  1036.     when LINECOUNT    =>  CHAPTER_1.LINECOUNT;
  1037.     when WORDCOUNT    =>  CHAPTER_1.WORDCOUNT;
  1038.     when DETAB        =>  CHAPTER_1.DETAB;
  1039.     when ENTAB        =>  CHAPTER_2.ENTAB;
  1040.     when OVERSTRIKE   =>  CHAPTER_2.OVERSTRIKE;
  1041.     when COMPRESS     =>  CHAPTER_2.COMPRESS;
  1042.     when EXPAND       =>  CHAPTER_2.EXPAND;
  1043.     when TRANSLIT     =>  CHAPTER_2.TRANSLIT;
  1044.     when QUIT         =>  exit;
  1045.     when QUERY        =>  null;
  1046.     when UNKNOWN      =>  null;  
  1047.     when REFUSED      =>  null;
  1048.     when ABORTING     =>  exit;
  1049.     end case;
  1050.   end loop;
  1051.   PUT("QUIT CLI_2");
  1052.   NEW_LINE;
  1053.  
  1054. end CLI_2;
  1055.  
  1056.