home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / ada_1 / Examples_demos_ada_pager2 < prev    next >
Encoding:
Text File  |  1992-10-01  |  50.0 KB  |  1,596 lines

  1.  
  2. --::::::::::
  3. --cli.ads
  4. --::::::::::
  5. -- **************************************
  6. -- *                                    *
  7. -- * CLI (Command Line Interface)       * SPEC
  8. -- *                                    *
  9. -- **************************************
  10. package CLI is
  11.  
  12. --| Purpose
  13. --|   CLI is a package which implements a Command
  14. --| Line Interface.  It mirrors the UNIX/C
  15. --| command line interface, providing an argument
  16. --| count and the arguments themselves.
  17. --|
  18. --| Initialization Exceptions (none)
  19. --|
  20. --| Notes
  21. --|   Compiler limit on string length and dynamic memory.
  22. --|   INITIALIZE must be called once, and only once, during
  23. --| the execution of the main Ada proc.
  24. --|
  25. --| Modifications
  26. --|  2/25/88  Richard Conn    Initial Version
  27. --|  5/12/89  Richard Conn    Review and Upgrade
  28. --|  4/11/90  Richard Conn    MIL-HDBK-1804 Annotations and
  29. --|                           Meridian Ada Interface Added
  30.    
  31.   -- ...................................
  32.   -- .                                 .
  33.   -- . CLI.INITIALIZE                  . SPEC
  34.   -- .                                 .
  35.   -- ...................................
  36.   procedure Initialize (Program_Name        : in STRING;
  37.                         Command_Line_Prompt : in STRING);
  38.   --| Purpose
  39.   --|   Initialize this package.  This routine must be called
  40.   --| before any other routines or objects are called or referenced.
  41.   --|
  42.   --| Exceptions (none)
  43.   --|
  44.   --| Notes
  45.   --|   CALL THIS PROCEDURE ONLY ONE TIME
  46.  
  47.   -- ...................................
  48.   -- .                                 .
  49.   -- . CLI.ARGC (Argument Count)       . SPEC
  50.   -- .                                 .
  51.   -- ...................................
  52.   function ArgC return NATURAL;
  53.   --| Purpose
  54.   --|   Return the number (1 to N) of command line arguments.
  55.   --| ARGC is at least 1 because the name of the program or
  56.   --| process is always ARGV(0).
  57.   --|
  58.   --| Exceptions (none)
  59.   --| Notes (none)
  60.  
  61.   -- ...................................
  62.   -- .                                 .
  63.   -- . CLI.ARGV (Argument Value)       . SPEC
  64.   -- .                                 .
  65.   -- ...................................
  66.   function ArgV (Index : in NATURAL) return STRING;
  67.   --| Purpose
  68.   --|   Return the INDEXth (0 <= INDEX < ARGC) command line
  69.   --| argument.  Example: if ARGC = 1, ARGV(0) is the only
  70.   --| valid argument string.  ARGV(0) is always the name of
  71.   --| the program or process.
  72.   --|
  73.   --| Exceptions
  74.   --|   INVALID_INDEX     raised if Index >= ARGC
  75.   --|
  76.   --| Notes (none)
  77.  
  78.   INVALID_INDEX    : exception;
  79.   UNEXPECTED_ERROR : exception;  -- raised anytime
  80.    
  81. end CLI;
  82. --::::::::::
  83. --cli.adb
  84. --::::::::::
  85. -- This implementation of Package Body CLI is general-purpose.
  86. -- Using TEXT_IO, it prompts the user for input arguments and
  87. -- accepts these arguments via a GET_LINE call.
  88. with TEXT_IO;
  89. package body CLI is
  90.  
  91.    LOCAL_ARGC : NATURAL := 0;
  92.    
  93.    package STRING_LIST is
  94.       
  95.       NUMBER_OF_STRINGS : NATURAL := 0;
  96.       
  97.       procedure ADD_TO_LIST (ITEM : in STRING);
  98.       function GET_FROM_LIST (ITEM : in NATURAL) return STRING;
  99.       
  100.    end STRING_LIST;
  101.    
  102.    package body STRING_LIST is
  103.       
  104.       type DYNAMIC_STRING_OBJECT (LENGTH : NATURAL);
  105.       type DYNAMIC_STRING is access DYNAMIC_STRING_OBJECT;
  106.       type DYNAMIC_STRING_OBJECT (LENGTH : NATURAL) is 
  107.          record
  108.             DS   : STRING (1 .. LENGTH);
  109.             NEXT : DYNAMIC_STRING;
  110.          end record;
  111.       
  112.       FIRST : DYNAMIC_STRING := null;
  113.       LAST  : DYNAMIC_STRING := null;
  114.       
  115.       procedure ADD_TO_LIST (ITEM : in STRING) is
  116.          
  117.          --========================= PDL ===========================
  118.          --|ABSTRACT:
  119.          --|    ADD_TO_LIST adds the ITEM string to the linked list
  120.          --|    of dynamic strings implemented by this package.
  121.          --|DESIGN DESCRIPTION:
  122.          --|    Create new DYNAMIC_STRING_OBJECT of the proper length
  123.          --|    Set DS field of new object to the ITEM string
  124.          --|    Set the NEXT field of the new object to NULL
  125.          --|    If FIRST pointer is null
  126.          --|      Set FIRST and LAST to point to the new object
  127.          --|    Else
  128.          --|      Set LAST.NEXT to point to the new object
  129.          --|      Set LAST to point to the new object
  130.          --|    End if
  131.          --|    Increment NUMBER_OF_STRINGS
  132.          --=========================================================
  133.          
  134.          TEMP : DYNAMIC_STRING;
  135.       begin
  136.          TEMP := new DYNAMIC_STRING_OBJECT (ITEM'LENGTH);
  137.          TEMP.DS (1 .. ITEM'LENGTH) := ITEM;
  138.          TEMP.NEXT                  := null;
  139.          if FIRST = null then
  140.             FIRST := TEMP;
  141.             LAST  := TEMP;
  142.          else
  143.             LAST.NEXT := TEMP;
  144.             LAST      := TEMP;
  145.          end if;
  146.          NUMBER_OF_STRINGS := NUMBER_OF_STRINGS + 1;
  147.       end ADD_TO_LIST;
  148.       
  149.       function GET_FROM_LIST (ITEM : in NATURAL) return STRING is
  150.          
  151.          --========================= PDL ===========================
  152.          --|ABSTRACT:
  153.          --|    GET_FROM_LIST returns the ITEM string from the linked list
  154.          --|    of dynamic strings implemented by this package.
  155.          --|DESIGN DESCRIPTION:
  156.          --|    If ITEM > 0
  157.          --|        Advance to desired item
  158.          --|    End If
  159.          --|    Return the DS field of the desired item
  160.          --=========================================================
  161.          
  162.          ROVER : DYNAMIC_STRING := FIRST;
  163.       begin
  164.          if ITEM > 0 then
  165.             for I in 1 .. ITEM loop
  166.                ROVER := ROVER.NEXT;
  167.             end loop;
  168.          end if;
  169.          return ROVER.DS;
  170.       end GET_FROM_LIST;
  171.       
  172.    end STRING_LIST;
  173.    
  174.    procedure INITIALIZE (PROGRAM_NAME        : in STRING;
  175.                          COMMAND_LINE_PROMPT : in STRING) is
  176.       
  177.       --========================= PDL ===========================
  178.       --|ABSTRACT:
  179.       --|    INITIALIZE prompts the user for the command line
  180.       --|    arguments and loads the linked list with them.
  181.       --|DESIGN DESCRIPTION:
  182.       --|    Set CURRENT_STATE to LOOKING_FOR_TOKEN
  183.       --|    Set the first list object to PROGRAM_NAME
  184.       --|    Prompt the user with COMMAND_LINE_PROMPT and
  185.       --|      get his response
  186.       --|    Over number of characters in line, loop
  187.       --|        Case CURRENT_STATE
  188.       --|            When LOOKING_FOR_TOKEN
  189.       --|                If character is not white-space
  190.       --|                    Set CURRENT_STATE to IN_TOKEN
  191.       --|                    If character is quote (")
  192.       --|                        Set QUOTED to TRUE
  193.       --|                        Set START to the character's index + 1
  194.       --|                    Else
  195.       --|                        Set QUOTED to FALSE
  196.       --|                        Set START to the character's index
  197.       --|                    End IF
  198.       --|                End If
  199.       --|            When IN_TOKEN
  200.       --|                If QUOTED
  201.       --|                    If character is quote (")
  202.       --|                        Set STOP to the previous character's index
  203.       --|                        Add slice from START to STOP to list
  204.       --|                        Set CURRENT_STATE to LOOKING_FOR_TOKEN
  205.       --|                    End If
  206.       --|                ElsIF character is white-space
  207.       --|                    Set STOP to the previous character's index
  208.       --|                    Add slice from START to STOP to list
  209.       --|                    Set CURRENT_STATE to LOOKING_FOR_TOKEN
  210.       --|                End If
  211.       --|        End Case
  212.       --|    End Loop
  213.       --|    If CURRENT_STATE is IN_TOKEN
  214.       --|        Set STOP to the previous character's index
  215.       --|        Add slice from START to STOP to list
  216.       --|    End if
  217.       --|    Set LOCAL_ARGC to NUMBER_OF_STRINGS
  218.       --|    Output NEW_LINE (to reset column count in TEXT_IO)
  219.       --=========================================================
  220.       
  221.       ARGCOUNT      : NATURAL := 1;
  222.       INLINE        : STRING (1 .. 400);
  223.       LAST          : NATURAL;
  224.       START         : NATURAL;
  225.       STOP          : NATURAL;
  226.       QUOTED        : BOOLEAN;
  227.       type STATE is (LOOKING_FOR_TOKEN, IN_TOKEN);
  228.       CURRENT_STATE : STATE   := LOOKING_FOR_TOKEN;
  229.    begin
  230.       STRING_LIST.ADD_TO_LIST (PROGRAM_NAME);
  231.       TEXT_IO.PUT (COMMAND_LINE_PROMPT);
  232.       TEXT_IO.GET_LINE (INLINE, LAST);
  233.       for I in 1 .. LAST loop
  234.          case CURRENT_STATE is
  235.             when LOOKING_FOR_TOKEN  =>
  236.                if INLINE (I) > ' ' then
  237.                   CURRENT_STATE := IN_TOKEN;
  238.                   if INLINE (I) = '"' then
  239.                      QUOTED := TRUE;
  240.                      START  := I;
  241.                   else
  242.                      QUOTED := FALSE;
  243.                      START  := I;
  244.                   end if;
  245.                end if;
  246.             when IN_TOKEN =>
  247.                if QUOTED then
  248.                   if INLINE (I) = '"' then
  249.                      STOP          := I;
  250.                      STRING_LIST.ADD_TO_LIST (INLINE (START .. STOP));
  251.                      CURRENT_STATE := LOOKING_FOR_TOKEN;
  252.                   end if;
  253.                elsif INLINE (I) <= ' ' then
  254.                   STOP          := I - 1;
  255.                   STRING_LIST.ADD_TO_LIST (INLINE (START .. STOP));
  256.                   CURRENT_STATE := LOOKING_FOR_TOKEN;
  257.                end if;
  258.          end case;
  259.       end loop;
  260.       if CURRENT_STATE = IN_TOKEN then
  261.          STOP := LAST;
  262.          STRING_LIST.ADD_TO_LIST (INLINE (START .. STOP));
  263.       end if;
  264.       LOCAL_ARGC := STRING_LIST.NUMBER_OF_STRINGS;
  265.       TEXT_IO.NEW_LINE;
  266.    end INITIALIZE;
  267.    
  268.    function ARGC return NATURAL is
  269.       
  270.       --========================= PDL ===========================
  271.       --|ABSTRACT:
  272.       --|    ARGC returns the argument count.
  273.       --|DESIGN DESCRIPTION:
  274.       --|    Return LOCAL_ARGC
  275.       --=========================================================
  276.       
  277.    begin
  278.       return LOCAL_ARGC;
  279.    end ARGC;
  280.  
  281.    function ARGV (INDEX : in NATURAL) return STRING is
  282.       
  283.       --========================= PDL ===========================
  284.       --|ABSTRACT:
  285.       --|    ARGV returns the indicated argument string.
  286.       --|DESIGN DESCRIPTION:
  287.       --|    If INDEX is out of range, raise INVALID_INDEX
  288.       --|    Return GET_FROM_LIST(INDEX)
  289.       --=========================================================
  290.       
  291.    begin
  292.       if INDEX >= LOCAL_ARGC then
  293.          raise INVALID_INDEX;
  294.       end if;
  295.       return STRING_LIST.GET_FROM_LIST (INDEX);
  296.    exception
  297.       when INVALID_INDEX  =>
  298.          raise ;
  299.       when others    =>
  300.          raise UNEXPECTED_ERROR;
  301.    end ARGV;
  302.    
  303. end CLI;
  304. --::::::::::
  305. --pager2.ada
  306. --::::::::::
  307. -- PROGRAM/CODE BODY NAME:    PAGER2
  308. -- AUTHOR:            Richard Conn
  309. -- VERSION:            1.1
  310. -- DATE:            6/12/89
  311. -- REVISION HISTORY -
  312. -- Version    Date    Author        Comments
  313. --    1.0    8/28/87    Richard Conn    Initial
  314. --    1.1       6/12/89 Richard Conn    CLI interface added
  315. -- KEYWORDS -
  316. --    pager, pager2, paged files, page, unpage
  317. -- CALLING SYNTAX -
  318. --    From the command line: pager2
  319. --    From the command line: pager2 verb arguments
  320. -- EXTERNAL ROUTINES -
  321. --    Package CLI
  322. --    Package TEXT_IO
  323. -- DESCRIPTION -
  324. --    PAGER2 assembles, extracts elements from, and lists paged files.
  325. -- Paged files are text files which contain one or more component files
  326. -- prefixed by a banner like:
  327. --
  328. --    ::::::::::
  329. --    filename
  330. --    ::::::::::
  331. --
  332. -- or
  333. --
  334. --    --::::::::::
  335. --    --filename
  336. --    --::::::::::
  337. --
  338. --    PAGER2 will manipulate paged files whose components
  339. -- are prefixed with either banner, but it assembles paged files with only
  340. -- the second banner (beginning with the Ada comment characters).
  341.  
  342. --===========================================================================
  343. -------------------------- PACKAGE LINE_DEFINITION --------------------------
  344. --===========================================================================
  345.  
  346. -- The following package defines an object of type LINE
  347. package LINE_DEFINITION is
  348.  
  349.     -- The maximum length of a line
  350.     MAX_LINE_LENGTH : constant NATURAL := 200;
  351.  
  352.     -- Type definition for LINE
  353.     type LINE is record
  354.     CONTENT : STRING(1 .. MAX_LINE_LENGTH);
  355.     LAST    : NATURAL;
  356.     end record;
  357.     type LINE_LIST_ELEMENT;
  358.     type LINE_LIST        is access LINE_LIST_ELEMENT;
  359.     type LINE_LIST_ELEMENT is record
  360.     CONTENT : LINE;
  361.     NEXT    : LINE_LIST;
  362.     end record;
  363.  
  364.     -- Banners
  365.     COMMENT_BANNER  : constant STRING  := "--::::::::::";
  366.     BANNER          : constant STRING  := "::::::::::";
  367.  
  368.     -- Convert strings to LINEs and back
  369.     function CONVERT(FROM : in STRING) return LINE;
  370.     function CONVERT(FROM : in LINE) return STRING;
  371.  
  372.     -- Convert a LINE to lower-case characters
  373.     procedure TOLOWER(ITEM : in out LINE);
  374.     function TOLOWER(ITEM : in LINE) return LINE;
  375.  
  376. end LINE_DEFINITION;
  377.  
  378. package body LINE_DEFINITION is
  379.  
  380.     -- Convert strings to LINEs
  381.     function CONVERT(FROM : in STRING) return LINE is
  382.     TO : LINE_DEFINITION.LINE;
  383.     begin
  384.     TO.CONTENT(TO.CONTENT'FIRST .. TO.CONTENT'FIRST + FROM'LENGTH - 1) :=
  385.       FROM;
  386.     TO.LAST := FROM'LENGTH;
  387.     return TO;
  388.     end CONVERT;
  389.  
  390.     function CONVERT(FROM : in LINE) return STRING is
  391.     begin
  392.     return FROM.CONTENT(FROM.CONTENT'FIRST .. FROM.LAST);
  393.     end CONVERT;
  394.  
  395.     procedure TOLOWER(ITEM : in out LINE) is
  396.     begin
  397.     for I in ITEM.CONTENT'FIRST .. ITEM.LAST loop
  398.         if ITEM.CONTENT(I) in 'A' .. 'Z' then
  399.         ITEM.CONTENT(I) :=
  400.                   CHARACTER'VAL(CHARACTER'POS(ITEM.CONTENT(I)) -
  401.           CHARACTER'POS('A') + CHARACTER'POS('a'));
  402.         end if;
  403.     end loop;
  404.     end TOLOWER;
  405.  
  406.     function TOLOWER(ITEM : in LINE) return LINE is
  407.         MYLINE : LINE;
  408.     begin
  409.         MYLINE := ITEM;
  410.         TOLOWER(MYLINE);
  411.         return MYLINE;
  412.     end TOLOWER;
  413.  
  414. end LINE_DEFINITION;
  415.  
  416. --===========================================================================
  417. -------------------------- PACKAGE INPUT_FILE -------------------------------
  418. --===========================================================================
  419.  
  420. -- The following package manipulates an object called an INPUT_FILE,
  421. -- which is a text file that is composed of objects of type LINE.
  422. -- LINEs can only be read from an INPUT_FILE.
  423. with LINE_DEFINITION;
  424. package INPUT_FILE is
  425.  
  426.     -- Open the input file
  427.     -- Exceptions which may be raised: FILE_NOT_FOUND, FILE_ALREADY_OPEN
  428.     procedure OPEN(FILE_NAME : in STRING);
  429.     procedure OPEN(FILE_NAME : in LINE_DEFINITION.LINE);
  430.  
  431.     -- Close the input file
  432.     -- Exceptions which may be raised: FILE_NOT_OPEN
  433.     procedure CLOSE;
  434.  
  435.     -- Read a line from the input file
  436.     -- Exceptions which may be raised: FILE_NOT_OPEN, READ_PAST_END_OF_FILE
  437.     procedure READ(TO : out LINE_DEFINITION.LINE);
  438.  
  439.     -- Return TRUE if the input file is empty (no more lines remain)
  440.     -- Exceptions which may be raised: FILE_NOT_OPEN
  441.     function END_OF_FILE return BOOLEAN;
  442.  
  443.     -- Exceptional conditions
  444.     FILE_NOT_FOUND        : exception;
  445.     FILE_ALREADY_OPEN     : exception;
  446.     FILE_NOT_OPEN         : exception;
  447.     READ_PAST_END_OF_FILE : exception;
  448.  
  449. end INPUT_FILE;
  450.  
  451. with TEXT_IO;
  452. package body INPUT_FILE is
  453.  
  454.     -- The file descriptor for the input file
  455.     FD : TEXT_IO.FILE_TYPE;
  456.  
  457.     -- Open the input file
  458.     procedure OPEN(FILE_NAME : in STRING) is
  459.     begin
  460.     TEXT_IO.OPEN(FD, TEXT_IO.IN_FILE, FILE_NAME);
  461.     exception
  462.     when TEXT_IO.NAME_ERROR =>
  463.         raise FILE_NOT_FOUND;
  464.     when TEXT_IO.STATUS_ERROR =>
  465.         raise FILE_ALREADY_OPEN;
  466.     end OPEN;
  467.  
  468.     procedure OPEN(FILE_NAME : in LINE_DEFINITION.LINE) is
  469.     begin
  470.     OPEN(LINE_DEFINITION.CONVERT(FILE_NAME));
  471.     end OPEN;
  472.  
  473.     -- Close the input file
  474.     procedure CLOSE is
  475.     begin
  476.     TEXT_IO.CLOSE(FD);
  477.     exception
  478.     when TEXT_IO.STATUS_ERROR =>
  479.         raise FILE_NOT_OPEN;
  480.     end CLOSE;
  481.  
  482.     -- Read a line from the input file
  483.     procedure READ(TO : out LINE_DEFINITION.LINE) is
  484.     begin
  485.     TEXT_IO.GET_LINE(FD, TO.CONTENT, TO.LAST);
  486.     exception
  487.     when TEXT_IO.END_ERROR =>
  488.         raise READ_PAST_END_OF_FILE;
  489.     when TEXT_IO.STATUS_ERROR =>
  490.         raise FILE_NOT_OPEN;
  491.     end READ;
  492.  
  493.     -- Return TRUE if the input file is empty (no more lines remain)
  494.     function END_OF_FILE return BOOLEAN is
  495.     begin
  496.     return TEXT_IO.END_OF_FILE(FD);
  497.     exception
  498.     when TEXT_IO.STATUS_ERROR =>
  499.         raise FILE_NOT_OPEN;
  500.     end END_OF_FILE;
  501.  
  502. end INPUT_FILE;
  503.  
  504. --===========================================================================
  505. -------------------------- PACKAGE OUTPUT_FILE ------------------------------
  506. --===========================================================================
  507.  
  508. -- The following package manipulates an object called an OUTPUT_FILE,
  509. -- which is a text file that is composed of objects of type LINE.
  510. -- LINEs can only be written to an OUTPUT_FILE.
  511. with LINE_DEFINITION;
  512. package OUTPUT_FILE is
  513.  
  514.     -- Open the output file
  515.     -- Exceptions which may be raised: CANNOT_CREATE_FILE, FILE_ALREADY_OPEN
  516.     procedure OPEN(FILE_NAME : in STRING);
  517.     procedure OPEN(FILE_NAME : in LINE_DEFINITION.LINE);
  518.  
  519.     -- Close the output file
  520.     -- Exceptions which may be raised: FILE_NOT_OPEN
  521.     procedure CLOSE;
  522.  
  523.     -- Write a line to the output file
  524.     -- Exceptions which may be raised: FILE_NOT_OPEN, DISK_FULL
  525.     procedure WRITE(FROM : in LINE_DEFINITION.LINE);
  526.     procedure WRITE(FROM : in STRING);
  527.  
  528.     -- Exceptional conditions
  529.     CANNOT_CREATE_FILE : exception;
  530.     FILE_ALREADY_OPEN  : exception;
  531.     FILE_NOT_OPEN      : exception;
  532.     DISK_FULL          : exception;
  533.  
  534. end OUTPUT_FILE;
  535.  
  536. with TEXT_IO;
  537. package body OUTPUT_FILE is
  538.  
  539.     -- File descriptor for the output file
  540.     FD : TEXT_IO.FILE_TYPE;
  541.  
  542.     -- Open the output file
  543.     procedure OPEN(FILE_NAME : in STRING) is
  544.     INLINE : STRING(1 .. 80);
  545.     LAST   : NATURAL;
  546.     begin
  547.     TEXT_IO.CREATE(FD, TEXT_IO.OUT_FILE, FILE_NAME);
  548.     exception
  549.     when TEXT_IO.STATUS_ERROR =>
  550.         raise FILE_ALREADY_OPEN;
  551.     when TEXT_IO.USE_ERROR =>
  552.         raise CANNOT_CREATE_FILE;
  553.     when TEXT_IO.NAME_ERROR =>
  554.         TEXT_IO.PUT_LINE(" Cannot create " & FILE_NAME);
  555.         loop
  556.         begin
  557.             TEXT_IO.PUT(" Enter New File Name: ");
  558.             TEXT_IO.GET_LINE(INLINE, LAST);
  559.             TEXT_IO.CREATE(FD, TEXT_IO.OUT_FILE,
  560.               INLINE(INLINE'FIRST .. LAST));
  561.             exit;
  562.         exception
  563.             when TEXT_IO.NAME_ERROR =>
  564.             TEXT_IO.PUT_LINE(" Cannot create " &
  565.               INLINE(INLINE'FIRST .. LAST));
  566.             when others =>
  567.             raise ;
  568.         end;
  569.         end loop;
  570.     end OPEN;
  571.  
  572.     procedure OPEN(FILE_NAME : in LINE_DEFINITION.LINE) is
  573.     begin
  574.     OPEN(LINE_DEFINITION.CONVERT(FILE_NAME));
  575.     end OPEN;
  576.  
  577.     -- Close the output file
  578.     procedure CLOSE is
  579.     begin
  580.     TEXT_IO.CLOSE(FD);
  581.     exception
  582.     when TEXT_IO.STATUS_ERROR =>
  583.         raise FILE_NOT_OPEN;
  584.     end CLOSE;
  585.  
  586.     -- Write a line to the output file
  587.     procedure WRITE(FROM : in LINE_DEFINITION.LINE) is
  588.     begin
  589.     TEXT_IO.PUT_LINE(FD, LINE_DEFINITION.CONVERT(FROM));
  590.     exception
  591.     when TEXT_IO.STATUS_ERROR =>
  592.         raise FILE_NOT_OPEN;
  593.     when others =>
  594.         raise DISK_FULL;
  595.     end WRITE;
  596.  
  597.     procedure WRITE(FROM : in STRING) is
  598.     begin
  599.     WRITE(LINE_DEFINITION.CONVERT(FROM));
  600.     end WRITE;
  601.  
  602. end OUTPUT_FILE;
  603.  
  604. --===========================================================================
  605. -------------------------- PACKAGE INCLUDE_FILE -----------------------------
  606. --===========================================================================
  607.  
  608. -- The following package manipulates an object called an INCLUDE_FILE,
  609. -- which is a text file that is composed of objects of type LINE.
  610. -- LINEs can only be read from an INCLUDE_FILE.  An INCLUDE_FILE contains
  611. -- the following types of LINE objects:
  612. --    blank lines
  613. --    comment lines ('-' is the first character in the line)
  614. --    file names (a string of non-blank characters which does not
  615. --        begin with the character '-' or '@')
  616. --    include file names (a string of non-blank characters which
  617. --        begins with the character '@', where the '@' is used to
  618. --        prefix the file name within the include file and is not
  619. --        a part of the file name of the actual disk file)
  620. -- Include files may be nested several levels (defined by the constant
  621. -- NESTING_DEPTH).
  622. with LINE_DEFINITION;
  623. package INCLUDE_FILE is
  624.  
  625.     -- Maximum number of levels include files may be nested
  626.     NESTING_DEPTH     : constant NATURAL   := 40;
  627.  
  628.     -- Character which begins an include file name
  629.     INCLUDE_CHARACTER : constant CHARACTER := '@';
  630.  
  631.     -- Character which begins a comment line
  632.     COMMENT_CHARACTER : constant CHARACTER := '-';
  633.  
  634.     -- Open the include file (the LINE input string contains the leading '@')
  635.     -- Exceptions which may be raised: FILE_NOT_FOUND, NESTING_LEVEL_EXCEEDED
  636.     procedure OPEN(FILE_NAME : in LINE_DEFINITION.LINE);
  637.     procedure OPEN(FILE_NAME : in STRING);
  638.  
  639.     -- Read a LINE containing a file name from the include file
  640.     -- Exceptions which may be raised: FILE_NOT_OPEN, READ_PAST_END_OF_FILE
  641.     procedure READ(TO : out LINE_DEFINITION.LINE);
  642.  
  643.     -- Abort processing the include file (close all open files)
  644.     -- Exceptions which may be raised: FILE_NOT_OPEN
  645.     procedure STOP;
  646.  
  647.     -- Exceptional conditions
  648.     FILE_NOT_FOUND         : exception;
  649.     NESTING_LEVEL_EXCEEDED : exception;
  650.     FILE_NOT_OPEN          : exception;
  651.     READ_PAST_END_OF_FILE  : exception;
  652.     INCLUDE_FILE_EMPTY     : exception;
  653.  
  654. end INCLUDE_FILE;
  655.  
  656. with TEXT_IO;
  657. package body INCLUDE_FILE is
  658.  
  659.     -- File Descriptor for main include file
  660.     FD              : array(1 .. NESTING_DEPTH) of TEXT_IO.FILE_TYPE;
  661.     CURRENT_LEVEL   : NATURAL := 0;
  662.     NEXT_LINE       : LINE_DEFINITION.LINE;    -- next line to return by READ
  663.     NEXT_LINE_READY : BOOLEAN := FALSE;        -- indicates next line is
  664.                                                -- available
  665.  
  666.     -- Open the include file (the LINE input string contains the leading '@')
  667.     -- Exceptions which may be raised: FILE_NOT_FOUND, NESTING_LEVEL_EXCEEDED
  668.     procedure OPEN(FILE_NAME : in LINE_DEFINITION.LINE) is
  669.     begin
  670.     if CURRENT_LEVEL = NESTING_DEPTH then
  671.         raise NESTING_LEVEL_EXCEEDED;
  672.     else
  673.         CURRENT_LEVEL := CURRENT_LEVEL + 1;
  674.         TEXT_IO.OPEN(FD(CURRENT_LEVEL), TEXT_IO.IN_FILE,
  675.           FILE_NAME.CONTENT(2..FILE_NAME.LAST));
  676.     end if;
  677.     exception
  678.     when TEXT_IO.NAME_ERROR =>
  679.         TEXT_IO.PUT_LINE("Include File " &
  680.           LINE_DEFINITION.CONVERT(FILE_NAME) &
  681.               " not Found");
  682.         raise FILE_NOT_FOUND;
  683.     when others =>
  684.         TEXT_IO.PUT_LINE("Unexpected error with Include File " &
  685.           LINE_DEFINITION.CONVERT(FILE_NAME));
  686.         raise FILE_NOT_FOUND;
  687.     end OPEN;
  688.  
  689.     procedure OPEN(FILE_NAME : in STRING) is
  690.     begin
  691.     OPEN(LINE_DEFINITION.CONVERT(FILE_NAME));
  692.     end OPEN;
  693.  
  694.     -- Close the include file
  695.     -- Exceptions which may be raised: FILE_NOT_OPEN
  696.     procedure CLOSE is
  697.     begin
  698.     TEXT_IO.CLOSE(FD(CURRENT_LEVEL));
  699.     CURRENT_LEVEL := CURRENT_LEVEL - 1;
  700.     if CURRENT_LEVEL = 0 then
  701.         raise INCLUDE_FILE_EMPTY;
  702.     end if;
  703.     end CLOSE;
  704.  
  705.     -- Abort processing the include file
  706.     procedure STOP is
  707.     begin
  708.     while CURRENT_LEVEL > 0 loop
  709.         TEXT_IO.CLOSE(FD(CURRENT_LEVEL));
  710.         CURRENT_LEVEL := CURRENT_LEVEL - 1;
  711.     end loop;
  712.     end STOP;
  713.  
  714.     -- Read a LINE containing a file name from the include file
  715.     -- Exceptions which may be raised: FILE_NOT_OPEN, READ_PAST_END_OF_FILE
  716.     procedure READ(TO : out LINE_DEFINITION.LINE) is
  717.     INLINE : LINE_DEFINITION.LINE;
  718.     begin
  719.     loop
  720.         begin
  721.         TEXT_IO.GET_LINE(FD(CURRENT_LEVEL), INLINE.CONTENT,
  722.           INLINE.LAST);
  723.         if INLINE.LAST > 0 and INLINE.CONTENT(1) =
  724.           INCLUDE_CHARACTER then
  725.             OPEN(INLINE);
  726.         elsif (INLINE.LAST > 0 and INLINE.CONTENT(1) = COMMENT_CHARACTER) or
  727.           (INLINE.LAST = 0) then
  728.             null;    -- skip comment lines and empty lines
  729.         else
  730.             exit;
  731.         end if;
  732.         exception
  733.         when TEXT_IO.END_ERROR =>
  734.             CLOSE;
  735.         end;
  736.     end loop;
  737.     TO := INLINE;
  738.     end READ;
  739.  
  740. end INCLUDE_FILE;
  741.  
  742. --===========================================================================
  743. ---------------------------- PROCEDURE PARSER -------------------------------
  744. --===========================================================================
  745. -- PARSER parses a LINE and returns the number of tokens in that LINE
  746. -- and the first token as COMMAND (converted to lower-case) with the
  747. -- rest of the tokens in ARGS (a linked list of argument LINEs)
  748.  
  749. with LINE_DEFINITION;
  750. use  LINE_DEFINITION;
  751. procedure PARSER(INLINE  : in LINE_DEFINITION.LINE;
  752.          NARGS   : out NATURAL;
  753.          COMMAND : out LINE_DEFINITION.LINE;
  754.          ARGS    : in out LINE_DEFINITION.LINE_LIST) is
  755.  
  756.     ROVER    : NATURAL;
  757.     LROVER   : LINE_DEFINITION.LINE_LIST := null;
  758.     LFIRST   : LINE_DEFINITION.LINE_LIST := null;
  759.     LCOMMAND : LINE_DEFINITION.LINE;
  760.     LTEMP    : LINE_DEFINITION.LINE;
  761.     LARGS    : NATURAL                   := 0;
  762.  
  763.     procedure SKIP_SPACES is
  764.     begin
  765.     while INLINE.CONTENT(ROVER) <= ' ' and ROVER <= INLINE.LAST loop
  766.         ROVER := ROVER + 1;
  767.     end loop;
  768.     end SKIP_SPACES;
  769.  
  770.     procedure EXTRACT(ITEM : out LINE_DEFINITION.LINE) is
  771.     EXTRACT_ROVER : NATURAL := 0;
  772.     begin
  773.     while INLINE.CONTENT(ROVER) > ' ' and ROVER <= INLINE.LAST loop
  774.         EXTRACT_ROVER := EXTRACT_ROVER + 1;
  775.         ITEM.CONTENT(EXTRACT_ROVER) := INLINE.CONTENT(ROVER);
  776.         ROVER := ROVER + 1;
  777.     end loop;
  778.     ITEM.LAST := EXTRACT_ROVER;
  779.     end EXTRACT;
  780.  
  781. begin
  782.     COMMAND.LAST := 0;
  783.     ROVER := INLINE.CONTENT'FIRST;
  784.     SKIP_SPACES;
  785.     if ROVER <= INLINE.LAST then
  786.     EXTRACT(LCOMMAND);
  787.     LCOMMAND.LAST := LCOMMAND.LAST + 1;
  788.     LCOMMAND.CONTENT(LCOMMAND.LAST) := ' ';
  789.     COMMAND := LINE_DEFINITION.TOLOWER(LCOMMAND);
  790.     LARGS := 1;
  791.         LROVER := ARGS;
  792.     while ROVER <= INLINE.LAST loop
  793.         SKIP_SPACES;
  794.         if ROVER <= INLINE.LAST then
  795.         EXTRACT(LTEMP);
  796.         if ARGS = null then
  797.             ARGS := new LINE_DEFINITION.LINE_LIST_ELEMENT;
  798.             LROVER := ARGS;
  799.             LROVER.NEXT := null;
  800.         end if;
  801.         LROVER.CONTENT := LTEMP;
  802.         LARGS := LARGS + 1;
  803.                 if LROVER.NEXT = null then
  804.                     LROVER.NEXT := new LINE_DEFINITION.LINE_LIST_ELEMENT;
  805.                 end if;
  806.                 LROVER := LROVER.NEXT;
  807.         end if;
  808.     end loop;
  809.     end if;
  810.     NARGS := LARGS;
  811. end PARSER;
  812.  
  813. --===========================================================================
  814. ---------------------------- PACKAGE PAGED_FILE -----------------------------
  815. --===========================================================================
  816. with LINE_DEFINITION;
  817. package PAGED_FILE is
  818.  
  819.     procedure COMPUTE_CHECKSUM (NARGS   : in NATURAL;
  820.                                 ARGLIST : in LINE_DEFINITION.LINE_LIST);
  821.     -- Compute the checksum of a paged file
  822.  
  823.     procedure MAKE_INCLUDE_FILE (NARGS   : in NATURAL;
  824.                                  ARGLIST : in LINE_DEFINITION.LINE_LIST);
  825.     -- Create an include file which names the elements of a paged file
  826.  
  827.     procedure LIST (NARGS   : in NATURAL;
  828.                     ARGLIST : in LINE_DEFINITION.LINE_LIST);
  829.     -- List the names of the elements of a paged file
  830.  
  831.     procedure CREATE (NARGS   : in NATURAL;
  832.                       ARGLIST : in LINE_DEFINITION.LINE_LIST);
  833.     -- Create a paged file
  834.  
  835.     procedure UNPAGE (NARGS   : in NATURAL;
  836.                       ARGLIST : in LINE_DEFINITION.LINE_LIST);
  837.     -- Extract the elements of a paged file
  838.  
  839. end PAGED_FILE;
  840.  
  841. with INCLUDE_FILE, INPUT_FILE, OUTPUT_FILE, PARSER;
  842. with TEXT_IO;
  843. package body PAGED_FILE is
  844.  
  845.     INLINE          : LINE_DEFINITION.LINE;
  846.  
  847.     --=======================================================================
  848.     -- PAGED_FILE, Support Utilities
  849.     --=======================================================================
  850.  
  851.     use  LINE_DEFINITION;
  852.  
  853.     -- Determine if ITEM contains a BANNER or COMMENT_BANNER
  854.     function IS_BANNER(ITEM : in LINE_DEFINITION.LINE) return BOOLEAN is
  855.     RESULT : BOOLEAN;
  856.     begin
  857.     if ITEM.LAST >= LINE_DEFINITION.BANNER'LENGTH and then
  858.       ITEM.CONTENT(1 .. LINE_DEFINITION.BANNER'LENGTH) =
  859.       LINE_DEFINITION.BANNER then
  860.         RESULT := TRUE;
  861.     elsif ITEM.LAST >= LINE_DEFINITION.COMMENT_BANNER'LENGTH and then
  862.       ITEM.CONTENT(1 .. LINE_DEFINITION.COMMENT_BANNER'LENGTH) =
  863.       LINE_DEFINITION.COMMENT_BANNER then
  864.         RESULT := TRUE;
  865.     else
  866.         RESULT := FALSE;
  867.     end if;
  868.     return RESULT;
  869.     end IS_BANNER;
  870.  
  871.     -- Package to handle line counting
  872.     package COUNTER is
  873.  
  874.         -- Reset the Counter
  875.     procedure SET;
  876.  
  877.         -- Increment the Counter
  878.     procedure INCREMENT;
  879.  
  880.         -- Print the counter
  881.     procedure PRINT;
  882.  
  883.     end COUNTER;
  884.  
  885.     package body COUNTER is
  886.  
  887.     type LINE_COUNT is range 0 .. 10001;
  888.     package LINE_COUNT_IO is new TEXT_IO.INTEGER_IO(LINE_COUNT);
  889.  
  890.     LCOUNT : LINE_COUNT;
  891.  
  892.         -- Reset the LCOUNT variable
  893.     procedure SET is
  894.     begin
  895.         LCOUNT := 0;
  896.     end SET;
  897.  
  898.         -- Increment the LCOUNT variable
  899.     procedure INCREMENT is
  900.     begin
  901.         if LCOUNT < LINE_COUNT'LAST then
  902.         LCOUNT := LCOUNT + 1;
  903.         end if;
  904.     end INCREMENT;
  905.  
  906.         -- Print a count of the number of lines and reset the LCOUNT variable
  907.     procedure PRINT is
  908.     begin
  909.         TEXT_IO.PUT(" -- ");
  910.         if LCOUNT = LINE_COUNT'LAST then
  911.         TEXT_IO.PUT("More Than" & LINE_COUNT'IMAGE(LINE_COUNT'LAST -
  912.           1));
  913.         else
  914.         LINE_COUNT_IO.PUT(LCOUNT, 1);
  915.         end if;
  916.         TEXT_IO.PUT_LINE(" Lines");
  917.         LCOUNT := 0;
  918.     end PRINT;
  919.  
  920.     end COUNTER;
  921.  
  922.     --=======================================================================
  923.     -- PAGED_FILE, COMPUTE_CHECKSUM Command
  924.     --=======================================================================
  925.     procedure COMPUTE_CHECKSUM (NARGS   : in NATURAL;
  926.                                 ARGLIST : in LINE_DEFINITION.LINE_LIST) is
  927.     CHECKSUM : INTEGER;
  928.     package VALUE_IO is new TEXT_IO.INTEGER_IO(INTEGER);
  929.     begin
  930.     if NARGS = 1 then
  931.         TEXT_IO.PUT_LINE(" CHECK Command requires the name of a file");
  932.         TEXT_IO.PUT_LINE("   Syntax: list file_name");
  933.     else
  934.  
  935.             -- Step 1: Open the input file
  936.         INPUT_FILE.OPEN(ARGLIST.CONTENT);
  937.  
  938.             -- Step 2: Compute the Hash (Checksum)
  939.         CHECKSUM := 0;
  940.         while not INPUT_FILE.END_OF_FILE loop
  941.         INPUT_FILE.READ(INLINE);
  942.         for I in 1 .. INLINE.LAST loop
  943.             if INLINE.CONTENT(I) > ' ' then
  944.             CHECKSUM := CHECKSUM +
  945.               CHARACTER'POS(INLINE.CONTENT(I));
  946.             if CHECKSUM >= 128 then
  947.                 CHECKSUM := CHECKSUM - 128;
  948.             end if;
  949.             end if;
  950.         end loop;
  951.         end loop;
  952.         INPUT_FILE.CLOSE;
  953.  
  954.             -- Step 3: Print the result
  955.         TEXT_IO.PUT(" Pager Checksum (Hash) of " &
  956.           LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) & " is ");
  957.         VALUE_IO.PUT(CHECKSUM, 1);
  958.         TEXT_IO.NEW_LINE;
  959.  
  960.     end if;
  961.  
  962.     exception
  963.     when INPUT_FILE.FILE_NOT_FOUND =>
  964.             TEXT_IO.PUT(" CHECK:");
  965.         TEXT_IO.PUT_LINE(" File " &
  966.               LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  967.           " not Found");
  968.     when INPUT_FILE.READ_PAST_END_OF_FILE =>
  969.             TEXT_IO.PUT(" CHECK:");
  970.         TEXT_IO.PUT_LINE(" Premature EOF on " &
  971.           LINE_DEFINITION.CONVERT(ARGLIST.CONTENT));
  972.         INPUT_FILE.CLOSE;
  973.     when others =>
  974.             TEXT_IO.PUT(" CHECK:");
  975.         TEXT_IO.PUT_LINE(" Unexpected Error");
  976.         INPUT_FILE.CLOSE;
  977.  
  978.     end COMPUTE_CHECKSUM;
  979.  
  980.     --=======================================================================
  981.     -- PAGED_FILE, MAKE_INCLUDE_FILE Command
  982.     --=======================================================================
  983.     procedure MAKE_INCLUDE_FILE (NARGS   : in NATURAL;
  984.                                  ARGLIST : in LINE_DEFINITION.LINE_LIST) is
  985.     IN_FILE   : BOOLEAN;
  986.         ARG_ROVER : LINE_DEFINITION.LINE_LIST;
  987.     begin
  988.     if NARGS < 3 then
  989.         TEXT_IO.PUT_LINE
  990.               (" INCLUDE Command requires the name of a paged file");
  991.         TEXT_IO.PUT_LINE
  992.               ("   Syntax: include paged_file_name output_include_file");
  993.     else
  994.  
  995.             -- Step 1: Open the input and output files
  996.         COUNTER.SET;
  997.         ARG_ROVER := ARGLIST.NEXT;
  998.         INPUT_FILE.OPEN(ARGLIST.CONTENT);
  999.         OUTPUT_FILE.OPEN(ARG_ROVER.CONTENT);
  1000.         OUTPUT_FILE.WRITE("-- Include file for " &
  1001.           LINE_DEFINITION.CONVERT(ARGLIST.CONTENT));
  1002.  
  1003.             -- Step 2: Look for the first banner in the paged file
  1004.         IN_FILE := TRUE;
  1005.         while not INPUT_FILE.END_OF_FILE loop
  1006.         INPUT_FILE.READ(INLINE);
  1007.         if IS_BANNER(INLINE) then
  1008.             IN_FILE := FALSE;
  1009.             exit;
  1010.         end if;
  1011.         end loop;
  1012.  
  1013.             -- Step 3: If first banner not found, issue error message,
  1014.             --         else process component files
  1015.         if IN_FILE then
  1016.         TEXT_IO.PUT_LINE
  1017.                   (" File " & LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  1018.           " does not contain any components");
  1019.         else
  1020.  
  1021.                 -- Loop until the end of the input paged file
  1022.         while not INPUT_FILE.END_OF_FILE loop
  1023.  
  1024.                     -- Read the next line from the input paged file
  1025.             INPUT_FILE.READ(INLINE);
  1026.  
  1027.                     -- If we are not in the text of the file, the line just
  1028.                     -- read contains the name of a new file, else it contains
  1029.                     -- a line of the current component file
  1030.             if not IN_FILE then
  1031.  
  1032.                     -- Remove leading comment character if any and print the
  1033.                     -- name of the component file
  1034.             if INLINE.CONTENT(1 .. 2) = "--" then
  1035.                 TEXT_IO.PUT(" " &
  1036.                   INLINE.CONTENT(3 .. INLINE.LAST));
  1037.                 OUTPUT_FILE.WRITE
  1038.                               (INLINE.CONTENT(3 .. INLINE.LAST));
  1039.             else
  1040.                 TEXT_IO.PUT(" " &
  1041.                   INLINE.CONTENT(1 .. INLINE.LAST));
  1042.                 OUTPUT_FILE.WRITE
  1043.                               (INLINE.CONTENT(1 .. INLINE.LAST));
  1044.             end if;
  1045.  
  1046.                         -- Flush the trailing banner line and note that we are
  1047.                         -- now within the text of a component file
  1048.             INPUT_FILE.READ(INLINE);
  1049.             COUNTER.SET;
  1050.             IN_FILE := TRUE;
  1051.  
  1052.             else
  1053.  
  1054.                     -- We are within the text of a component file, so check
  1055.                     -- for a banner in order to determine if we are at the end
  1056.                     -- of the component file
  1057.             if IS_BANNER(INLINE) then
  1058.                 IN_FILE := FALSE;
  1059.                 COUNTER.PRINT;
  1060.             else
  1061.                 COUNTER.INCREMENT;
  1062.             end if;
  1063.  
  1064.             end if;
  1065.  
  1066.         end loop;
  1067.  
  1068.         end if;
  1069.  
  1070.         COUNTER.PRINT;
  1071.         INPUT_FILE.CLOSE;
  1072.         OUTPUT_FILE.CLOSE;
  1073.  
  1074.     end if;
  1075.  
  1076.     exception
  1077.     when OUTPUT_FILE.CANNOT_CREATE_FILE =>
  1078.             TEXT_IO.PUT(" INCLUDE:");
  1079.         TEXT_IO.PUT_LINE(" Cannot create " &
  1080.           LINE_DEFINITION.CONVERT(ARG_ROVER.CONTENT));
  1081.     when INPUT_FILE.FILE_NOT_FOUND =>
  1082.             TEXT_IO.PUT(" INCLUDE:");
  1083.         TEXT_IO.PUT_LINE
  1084.               (" File " & LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  1085.           " not Found");
  1086.     when INPUT_FILE.READ_PAST_END_OF_FILE =>
  1087.             TEXT_IO.PUT(" INCLUDE:");
  1088.         TEXT_IO.PUT_LINE(" Premature EOF on " &
  1089.           LINE_DEFINITION.CONVERT(ARGLIST.CONTENT));
  1090.         INPUT_FILE.CLOSE;
  1091.     when others =>
  1092.             TEXT_IO.PUT(" INCLUDE:");
  1093.         TEXT_IO.PUT_LINE(" Unexpected Error");
  1094.         INPUT_FILE.CLOSE;
  1095.  
  1096.     end MAKE_INCLUDE_FILE;
  1097.  
  1098.     --=======================================================================
  1099.     -- PAGED_FILE, LIST Command
  1100.     --=======================================================================
  1101.     procedure LIST (NARGS   : in NATURAL;
  1102.                     ARGLIST : in LINE_DEFINITION.LINE_LIST) is
  1103.     IN_FILE : BOOLEAN;
  1104.     begin
  1105.     if NARGS = 1 then
  1106.         TEXT_IO.PUT_LINE
  1107.               (" LIST Command requires the name of a paged file");
  1108.         TEXT_IO.PUT_LINE
  1109.               ("   Syntax: list paged_file_name");
  1110.     else
  1111.  
  1112.             -- Step 1: Open the input file
  1113.         COUNTER.SET;
  1114.         INPUT_FILE.OPEN(ARGLIST.CONTENT);
  1115.  
  1116.             -- Step 2: Look for the first banner in the paged file
  1117.         IN_FILE := TRUE;
  1118.         while not INPUT_FILE.END_OF_FILE loop
  1119.         INPUT_FILE.READ(INLINE);
  1120.         if IS_BANNER(INLINE) then
  1121.             IN_FILE := FALSE;
  1122.             exit;
  1123.         end if;
  1124.         end loop;
  1125.  
  1126.             -- Step 3: If first banner not found, issue error message,
  1127.             --         else process component files
  1128.         if IN_FILE then
  1129.         TEXT_IO.PUT_LINE
  1130.                   (" File " & LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  1131.           " does not contain any components");
  1132.         else
  1133.  
  1134.                 -- Loop until the end of the input paged file
  1135.         while not INPUT_FILE.END_OF_FILE loop
  1136.  
  1137.                     -- Read the next line from the input paged file
  1138.             INPUT_FILE.READ(INLINE);
  1139.  
  1140.                     -- If we are not in the text of the file, the line just
  1141.                     -- read contains the name of a new file, else it contains
  1142.                     -- a line of the current component file
  1143.             if not IN_FILE then
  1144.  
  1145.                         -- Remove leading comment character if any and print
  1146.                         -- the name of the component file
  1147.             if INLINE.CONTENT(1 .. 2) = "--" then
  1148.                 TEXT_IO.PUT(" " &
  1149.                   INLINE.CONTENT(3 .. INLINE.LAST));
  1150.             else
  1151.                 TEXT_IO.PUT(" " &
  1152.                   INLINE.CONTENT(1 .. INLINE.LAST));
  1153.             end if;
  1154.  
  1155.                         -- Flush the trailing banner line and note that we are
  1156.                         -- now within the text of a component file
  1157.             INPUT_FILE.READ(INLINE);
  1158.             COUNTER.SET;
  1159.             IN_FILE := TRUE;
  1160.  
  1161.             else
  1162.  
  1163.                         -- We are within the text of a component file, so
  1164.                         -- check for a banner in order to determine if we
  1165.                         -- are at the end of the component file
  1166.             if IS_BANNER(INLINE) then
  1167.                 IN_FILE := FALSE;
  1168.                 COUNTER.PRINT;
  1169.             else
  1170.                 COUNTER.INCREMENT;
  1171.             end if;
  1172.  
  1173.             end if;
  1174.  
  1175.         end loop;
  1176.  
  1177.         end if;
  1178.  
  1179.         COUNTER.PRINT;
  1180.         INPUT_FILE.CLOSE;
  1181.  
  1182.     end if;
  1183.  
  1184.     exception
  1185.     when INPUT_FILE.FILE_NOT_FOUND =>
  1186.             TEXT_IO.PUT(" LIST:");
  1187.         TEXT_IO.PUT_LINE
  1188.               (" File " & LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  1189.           " not Found");
  1190.     when INPUT_FILE.READ_PAST_END_OF_FILE =>
  1191.             TEXT_IO.PUT(" LIST:");
  1192.         TEXT_IO.PUT_LINE(" Premature EOF on " &
  1193.           LINE_DEFINITION.CONVERT(ARGLIST.CONTENT));
  1194.         INPUT_FILE.CLOSE;
  1195.     when others =>
  1196.             TEXT_IO.PUT(" LIST:");
  1197.         TEXT_IO.PUT_LINE(" Unexpected Error");
  1198.         INPUT_FILE.CLOSE;
  1199.  
  1200.     end LIST;
  1201.  
  1202.     --=======================================================================
  1203.     -- PAGED_FILE, CREATE Command
  1204.     --=======================================================================
  1205.     procedure CREATE (NARGS   : in NATURAL;
  1206.                       ARGLIST : in LINE_DEFINITION.LINE_LIST) is
  1207.     COMPONENT_FILE_NAME : LINE_DEFINITION.LINE;
  1208.     OUTPUT_FILE_NAME    : LINE_DEFINITION.LINE;
  1209.         ARG_ROVER           : LINE_DEFINITION.LINE_LIST;
  1210.     begin
  1211.     if NARGS < 3 then
  1212.         TEXT_IO.PUT_LINE
  1213.               (" PAGE Command requires the name of the paged file and include file");
  1214.         TEXT_IO.PUT_LINE
  1215.               ("   Syntax: page [@include_file_name|file_name]+ paged_file_name");
  1216.     else
  1217.         ARG_ROVER := ARGLIST;
  1218.             for I in 1 .. NARGS-2 loop
  1219.         ARG_ROVER := ARG_ROVER.NEXT;
  1220.         end loop;
  1221.         OUTPUT_FILE_NAME := ARG_ROVER.CONTENT;
  1222.         OUTPUT_FILE.OPEN(OUTPUT_FILE_NAME);
  1223.         ARG_ROVER := ARGLIST;
  1224.         for I in 1 .. NARGS-2 loop
  1225.         if ARG_ROVER.CONTENT.CONTENT(1) =
  1226.           INCLUDE_FILE.INCLUDE_CHARACTER then
  1227.             INCLUDE_FILE.OPEN
  1228.                       (LINE_DEFINITION.CONVERT(ARG_ROVER.CONTENT));
  1229.             begin
  1230.             loop
  1231.                 INCLUDE_FILE.READ(COMPONENT_FILE_NAME);
  1232.                 INPUT_FILE.OPEN(COMPONENT_FILE_NAME);
  1233.                 OUTPUT_FILE.WRITE(LINE_DEFINITION.COMMENT_BANNER);
  1234.                 OUTPUT_FILE.WRITE("--" &
  1235.                   LINE_DEFINITION.CONVERT(COMPONENT_FILE_NAME));
  1236.                 OUTPUT_FILE.WRITE(LINE_DEFINITION.COMMENT_BANNER);
  1237.                 TEXT_IO.PUT(" Adding " &
  1238.                   LINE_DEFINITION.CONVERT(COMPONENT_FILE_NAME));
  1239.                 COUNTER.SET;
  1240.                 while not INPUT_FILE.END_OF_FILE loop
  1241.                 INPUT_FILE.READ(INLINE);
  1242.                 OUTPUT_FILE.WRITE(INLINE);
  1243.                 COUNTER.INCREMENT;
  1244.                 end loop;
  1245.                 COUNTER.PRINT;
  1246.                 INPUT_FILE.CLOSE;
  1247.             end loop;
  1248.             exception
  1249.             when INCLUDE_FILE.READ_PAST_END_OF_FILE |
  1250.               INCLUDE_FILE.INCLUDE_FILE_EMPTY |
  1251.                           INCLUDE_FILE.NESTING_LEVEL_EXCEEDED =>
  1252.                             INCLUDE_FILE.STOP;
  1253.             when INPUT_FILE.FILE_NOT_FOUND =>
  1254.                 TEXT_IO.PUT_LINE(" File " &
  1255.                   LINE_DEFINITION.CONVERT(COMPONENT_FILE_NAME) &
  1256.                   " not Found");
  1257.                 INCLUDE_FILE.STOP;
  1258.             when others =>
  1259.                 TEXT_IO.PUT_LINE
  1260.                               (" Unexpected Error During Processing " &
  1261.                               "of Component File " &
  1262.                   LINE_DEFINITION.CONVERT(COMPONENT_FILE_NAME));
  1263.                 INCLUDE_FILE.STOP;
  1264.             end;
  1265.         else
  1266.             INPUT_FILE.OPEN(ARG_ROVER.CONTENT);
  1267.             OUTPUT_FILE.WRITE(LINE_DEFINITION.COMMENT_BANNER);
  1268.             OUTPUT_FILE.WRITE("--" &
  1269.               LINE_DEFINITION.CONVERT(ARG_ROVER.CONTENT));
  1270.             OUTPUT_FILE.WRITE(LINE_DEFINITION.COMMENT_BANNER);
  1271.             TEXT_IO.PUT(" Adding " &
  1272.               LINE_DEFINITION.CONVERT(ARG_ROVER.CONTENT));
  1273.             COUNTER.SET;
  1274.             while not INPUT_FILE.END_OF_FILE loop
  1275.             INPUT_FILE.READ(INLINE);
  1276.             OUTPUT_FILE.WRITE(INLINE);
  1277.             COUNTER.INCREMENT;
  1278.             end loop;
  1279.             COUNTER.PRINT;
  1280.             INPUT_FILE.CLOSE;
  1281.         end if;
  1282.                 ARG_ROVER := ARG_ROVER.NEXT;
  1283.         end loop;
  1284.             OUTPUT_FILE.CLOSE;
  1285.     end if;
  1286.  
  1287.     exception
  1288.     when OUTPUT_FILE.CANNOT_CREATE_FILE =>
  1289.             TEXT_IO.PUT(" PAGE:");
  1290.         TEXT_IO.PUT_LINE(" Cannot create " &
  1291.           LINE_DEFINITION.CONVERT(OUTPUT_FILE_NAME));
  1292.     when INCLUDE_FILE.FILE_NOT_FOUND =>
  1293.             TEXT_IO.PUT(" PAGE:");
  1294.         TEXT_IO.PUT_LINE(" Cannot open include file");
  1295.     when others =>
  1296.             TEXT_IO.PUT(" PAGE:");
  1297.         TEXT_IO.PUT_LINE(" Unexpected Error");
  1298.         INPUT_FILE.CLOSE;
  1299.  
  1300.     end CREATE;
  1301.  
  1302.     --=======================================================================
  1303.     -- PAGED_FILE, UNPAGE Command
  1304.     --=======================================================================
  1305.     procedure UNPAGE (NARGS   : in NATURAL;
  1306.                       ARGLIST : in LINE_DEFINITION.LINE_LIST) is
  1307.     IN_FILE          : BOOLEAN;
  1308.     OUTPUT_FILE_NAME : LINE_DEFINITION.LINE;
  1309.     begin
  1310.     if NARGS = 1 then
  1311.         TEXT_IO.PUT_LINE
  1312.               (" UNPAGE Command requires the name of a paged file");
  1313.         TEXT_IO.PUT_LINE("   Syntax: unpage paged_file_name");
  1314.     else
  1315.  
  1316.             -- Step 1: Open the input file
  1317.         COUNTER.SET;
  1318.         INPUT_FILE.OPEN(ARGLIST.CONTENT);
  1319.  
  1320.             -- Step 2: Look for the first banner in the paged file
  1321.         IN_FILE := TRUE;
  1322.         while not INPUT_FILE.END_OF_FILE loop
  1323.         INPUT_FILE.READ(INLINE);
  1324.         if IS_BANNER(INLINE) then
  1325.             IN_FILE := FALSE;
  1326.             exit;
  1327.         end if;
  1328.         end loop;
  1329.  
  1330.             -- Step 3: If first banner not found, issue error message,
  1331.             --         else process component files
  1332.         if IN_FILE then
  1333.         TEXT_IO.PUT_LINE(" File " &
  1334.                   LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  1335.           " does not contain any components");
  1336.         else
  1337.  
  1338.                 -- Loop until the end of the input paged file
  1339.         while not INPUT_FILE.END_OF_FILE loop
  1340.  
  1341.                     -- Read the next line from the input paged file
  1342.             INPUT_FILE.READ(INLINE);
  1343.  
  1344.                     -- If we are not in the text of the file, the line just
  1345.                     -- read contains the name of a new file, else it contains
  1346.                     -- a line of the current component file
  1347.             if not IN_FILE then
  1348.  
  1349.                         -- Remove leading comment character if any and
  1350.                         -- store the name of the component file
  1351.             if INLINE.CONTENT(1 .. 2) = "--" then
  1352.                 OUTPUT_FILE_NAME :=
  1353.                   LINE_DEFINITION.CONVERT
  1354.                                 (INLINE.CONTENT(3 .. INLINE.LAST));
  1355.             else
  1356.                 OUTPUT_FILE_NAME :=
  1357.                   LINE_DEFINITION.CONVERT
  1358.                                 (INLINE.CONTENT(1 .. INLINE.LAST));
  1359.             end if;
  1360.  
  1361.                         -- Open the new component file
  1362.             OUTPUT_FILE.OPEN(OUTPUT_FILE_NAME);
  1363.             TEXT_IO.PUT(" Extracting " &
  1364.               LINE_DEFINITION.CONVERT(OUTPUT_FILE_NAME));
  1365.  
  1366.                         -- Flush the trailing banner line and note that we are
  1367.                         -- now within the text of a component file
  1368.             INPUT_FILE.READ(INLINE);
  1369.             IN_FILE := TRUE;
  1370.             COUNTER.SET;
  1371.  
  1372.             else
  1373.  
  1374.                         -- We are within the text of a component file, so
  1375.                         -- check for a banner in order to determine if we
  1376.                         -- are at the end of the component file
  1377.             if IS_BANNER(INLINE) then
  1378.                 OUTPUT_FILE.CLOSE;
  1379.                 IN_FILE := FALSE;
  1380.                 COUNTER.PRINT;
  1381.             else
  1382.                 OUTPUT_FILE.WRITE(INLINE);
  1383.                 COUNTER.INCREMENT;
  1384.             end if;
  1385.  
  1386.             end if;
  1387.  
  1388.         end loop;
  1389.  
  1390.         OUTPUT_FILE.CLOSE;
  1391.  
  1392.         end if;
  1393.  
  1394.         COUNTER.PRINT;
  1395.         INPUT_FILE.CLOSE;
  1396.  
  1397.     end if;
  1398.  
  1399.     exception
  1400.     when OUTPUT_FILE.CANNOT_CREATE_FILE =>
  1401.             TEXT_IO.PUT(" UNPAGE:");
  1402.         TEXT_IO.PUT_LINE(" Cannot create " &
  1403.           LINE_DEFINITION.CONVERT(OUTPUT_FILE_NAME));
  1404.     when INPUT_FILE.FILE_NOT_FOUND =>
  1405.             TEXT_IO.PUT(" UNPAGE:");
  1406.         TEXT_IO.PUT_LINE(" File " &
  1407.               LINE_DEFINITION.CONVERT(ARGLIST.CONTENT) &
  1408.           " not Found");
  1409.     when INPUT_FILE.READ_PAST_END_OF_FILE =>
  1410.             TEXT_IO.PUT(" UNPAGE:");
  1411.         TEXT_IO.PUT_LINE(" Premature EOF on " &
  1412.           LINE_DEFINITION.CONVERT(ARGLIST.CONTENT));
  1413.         INPUT_FILE.CLOSE;
  1414.     when others =>
  1415.             TEXT_IO.PUT(" UNPAGE:");
  1416.         TEXT_IO.PUT_LINE(" Unexpected Error");
  1417.         INPUT_FILE.CLOSE;
  1418.  
  1419.     end UNPAGE;
  1420.  
  1421. end PAGED_FILE;
  1422.  
  1423. --===========================================================================
  1424. --------------------------------- MAINLINE ----------------------------------
  1425. --===========================================================================
  1426. with LINE_DEFINITION, PAGED_FILE, PARSER;
  1427. use  LINE_DEFINITION;
  1428. with TEXT_IO;
  1429. with CLI;
  1430. procedure PAGER2 is
  1431.  
  1432.     TITLE           : constant STRING := "PAGER2, Ada Version 1.1";
  1433.  
  1434.     INLINE          : LINE_DEFINITION.LINE;
  1435.  
  1436.     NARGS           : NATURAL;
  1437.     COMMAND         : LINE_DEFINITION.LINE;
  1438.     ARGLIST         : LINE_DEFINITION.LINE_LIST;
  1439.     ARG_ROVER       : LINE_DEFINITION.LINE_LIST;
  1440.  
  1441.     -- Command Verbs
  1442.     HELP_COMMAND    : constant STRING := "help ";
  1443.     H_COMMAND       : constant STRING := "h ";
  1444.     EXIT_COMMAND    : constant STRING := "exit ";
  1445.     X_COMMAND       : constant STRING := "x ";    -- same as exit
  1446.     CHECK_COMMAND   : constant STRING := "check ";
  1447.     C_COMMAND       : constant STRING := "c ";    -- same as check
  1448.     INCLUDE_COMMAND : constant STRING := "include ";
  1449.     I_COMMAND       : constant STRING := "i ";    -- same as include
  1450.     LIST_COMMAND    : constant STRING := "list ";
  1451.     L_COMMAND       : constant STRING := "l ";    -- same as list
  1452.     PAGE_COMMAND    : constant STRING := "page ";
  1453.     P_COMMAND       : constant STRING := "p ";    -- same as page
  1454.     UNPAGE_COMMAND  : constant STRING := "unpage ";
  1455.     U_COMMAND       : constant STRING := "u ";    -- same as unpage
  1456.  
  1457.     --=======================================================================
  1458.     -- PAGER2, Support Utilities
  1459.     --=======================================================================
  1460.  
  1461.     -- Determine if COMMAND contains one of the two target command strings
  1462.     function IS_COMMAND(TARGET1_COMMAND, TARGET2_COMMAND : in STRING)
  1463.             return BOOLEAN is
  1464.         START : NATURAL;
  1465.     begin
  1466.         if COMMAND.CONTENT(1) = '-' then
  1467.             START := 2;
  1468.         else
  1469.             START := 1;
  1470.         end if;
  1471.     if COMMAND.CONTENT(START .. TARGET1_COMMAND'LENGTH + START - 1)
  1472.               = TARGET1_COMMAND or
  1473.       COMMAND.CONTENT(START .. TARGET2_COMMAND'LENGTH + START - 1)
  1474.               = TARGET2_COMMAND then
  1475.         return TRUE;
  1476.     else
  1477.         return FALSE;
  1478.     end if;
  1479.     end IS_COMMAND;
  1480.  
  1481.     --=======================================================================
  1482.     -- PAGER2, HELP Command
  1483.     --=======================================================================
  1484.     procedure HELP is
  1485.     procedure SPACER is
  1486.     begin
  1487.         TEXT_IO.PUT("                  ");
  1488.     end SPACER;
  1489.     begin
  1490.     TEXT_IO.PUT_LINE(" Command Summary");
  1491.     TEXT_IO.PUT_LINE("  help or h   - this summary");
  1492.     SPACER;
  1493.     TEXT_IO.PUT_LINE("Syntax: help");
  1494.     TEXT_IO.PUT_LINE("  exit or x   - exit from program");
  1495.     SPACER;
  1496.     TEXT_IO.PUT_LINE("Syntax: exit");
  1497.     TEXT_IO.PUT_LINE
  1498.           ("  include or i- list components into an include file");
  1499.     SPACER;
  1500.     TEXT_IO.PUT_LINE
  1501.           ("Syntax: include paged_file_name output_include_file");
  1502.     TEXT_IO.PUT_LINE("  list or l   - list components of paged file");
  1503.     SPACER;
  1504.     TEXT_IO.PUT_LINE("Syntax: list paged_file_name");
  1505.     TEXT_IO.PUT_LINE
  1506.           ("  page or p   - create paged file from include file");
  1507.     SPACER;
  1508.     TEXT_IO.PUT_LINE
  1509.           ("Syntax: page [@include_file_name|file_name]+ paged_file_name");
  1510.     TEXT_IO.PUT_LINE
  1511.           ("  unpage or u - extract components from paged file");
  1512.     SPACER;
  1513.     TEXT_IO.PUT_LINE("Syntax: unpage paged_file_name");
  1514.     end HELP;
  1515.  
  1516. --=======================================================================
  1517. -- PAGER2, Mainline
  1518. --=======================================================================
  1519. begin
  1520.     CLI.INITIALIZE ("PAGER2", "Enter verb and arguments: ");
  1521.  
  1522.     -- Interactive mode if no arguments
  1523.     if CLI.ARGC = 1 then
  1524.     TEXT_IO.PUT_LINE(TITLE);
  1525.     TEXT_IO.PUT_LINE("Type 'h' for Help");
  1526.     loop
  1527.         begin
  1528.         TEXT_IO.PUT("PAGER2> ");
  1529.         TEXT_IO.GET_LINE(INLINE.CONTENT, INLINE.LAST);
  1530.         PARSER(INLINE, NARGS, COMMAND, ARGLIST);
  1531.         if NARGS > 0 then
  1532.             exit when IS_COMMAND(EXIT_COMMAND, X_COMMAND);
  1533.             if IS_COMMAND(HELP_COMMAND, H_COMMAND) then
  1534.             HELP;
  1535.             elsif IS_COMMAND(CHECK_COMMAND, C_COMMAND) then
  1536.             PAGED_FILE.COMPUTE_CHECKSUM (NARGS, ARGLIST);
  1537.             elsif IS_COMMAND(INCLUDE_COMMAND, I_COMMAND) then
  1538.             PAGED_FILE.MAKE_INCLUDE_FILE (NARGS, ARGLIST);
  1539.             elsif IS_COMMAND(LIST_COMMAND, L_COMMAND) then
  1540.             PAGED_FILE.LIST (NARGS, ARGLIST);
  1541.             elsif IS_COMMAND(PAGE_COMMAND, P_COMMAND) then
  1542.             PAGED_FILE.CREATE (NARGS, ARGLIST);
  1543.             elsif IS_COMMAND(UNPAGE_COMMAND, U_COMMAND) then
  1544.             PAGED_FILE.UNPAGE (NARGS, ARGLIST);
  1545.             else
  1546.             TEXT_IO.PUT_LINE(" Invalid Command: " &
  1547.               LINE_DEFINITION.CONVERT(COMMAND));
  1548.             end if;
  1549.         end if;
  1550.         exception
  1551.         when others =>
  1552.             null;
  1553.         end;
  1554.     end loop;
  1555.     -- Non-interactive mode if one or more arguments
  1556.     else
  1557.     COMMAND := TOLOWER(LINE_DEFINITION.CONVERT(CLI.ARGV(1) & " "));
  1558.     NARGS := CLI.ARGC - 1;
  1559.     ARGLIST := null;
  1560.     for I in 2 .. CLI.ARGC - 1 loop
  1561.         if I = 2 then
  1562.         ARGLIST := new LINE_DEFINITION.LINE_LIST_ELEMENT;
  1563.         ARG_ROVER := ARGLIST;
  1564.         else
  1565.         ARG_ROVER.NEXT := new LINE_DEFINITION.LINE_LIST_ELEMENT;
  1566.         ARG_ROVER := ARG_ROVER.NEXT;
  1567.         end if;
  1568.         ARG_ROVER.NEXT := null;
  1569.         ARG_ROVER.CONTENT := LINE_DEFINITION.CONVERT(CLI.ARGV(I));
  1570.     end loop;
  1571.     if NARGS > 0 then
  1572.         if IS_COMMAND(HELP_COMMAND, H_COMMAND) then
  1573.         HELP;
  1574.         elsif IS_COMMAND(CHECK_COMMAND, C_COMMAND) then
  1575.         PAGED_FILE.COMPUTE_CHECKSUM (NARGS, ARGLIST);
  1576.         elsif IS_COMMAND(INCLUDE_COMMAND, I_COMMAND) then
  1577.         PAGED_FILE.MAKE_INCLUDE_FILE (NARGS, ARGLIST);
  1578.         elsif IS_COMMAND(LIST_COMMAND, L_COMMAND) then
  1579.         PAGED_FILE.LIST (NARGS, ARGLIST);
  1580.         elsif IS_COMMAND(PAGE_COMMAND, P_COMMAND) then
  1581.         PAGED_FILE.CREATE (NARGS, ARGLIST);
  1582.         elsif IS_COMMAND(UNPAGE_COMMAND, U_COMMAND) then
  1583.         PAGED_FILE.UNPAGE (NARGS, ARGLIST);
  1584.         elsif IS_COMMAND(EXIT_COMMAND, X_COMMAND) then
  1585.         null;
  1586.         else
  1587.         TEXT_IO.PUT_LINE(" Invalid Command: " &
  1588.           LINE_DEFINITION.CONVERT(COMMAND));
  1589.         end if;
  1590.     end if;
  1591.     end if;
  1592. exception
  1593.     when others =>
  1594.     null;
  1595. end PAGER2;
  1596.