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

  1. ::::::::::::::                                  
  2. CREATETB.PRO
  3. ::::::::::::::
  4.  
  5.  
  6.  
  7. -------- SIMTEL20 Ada Software Repository Prologue ------------
  8. --                                                           -*
  9. -- Unit name    : CREATE_TB
  10. -- Version      : 850121
  11. -- Author       : Mitre Corp.
  12. -- DDN Address  : wis_ada at mitre
  13. -- Date created : 21 JAN 85
  14. -- Release date : 26 JAN 85
  15. -- Last update  : 26 JAN 85
  16. -- Machine/System Compiled/Run on : Intellimac 7000M
  17. --                                  UNIX
  18. --                                  Telesoft unvalidated
  19. --                                                           -*
  20. ---------------------------------------------------------------
  21. --                                                           -*
  22. -- Keywords     :  Table builder, Text formatter
  23. --
  24. ----------------:
  25. --
  26. -- Abstract     :  
  27. --     CREATE_TB scans text files and creates a table by selecting
  28. -- specified line entries.  The text files are assumed to contain
  29. -- standardized entries which are repeated in each file.  CREATE_TB
  30. -- extracts a subset of these entries compressing their text into
  31. -- a specified column width for printing in a tabular form.  The
  32. -- entries to be extracted and the width of each column may be
  33. -- specified by the user for each table.  CREATE_TB will scan a group
  34. -- of files identified by a UNIX file pattern(including *, ?, or
  35. -- selectors [aeiou]) and it also recognizes PAGE headers of the form:
  36. --
  37. --              ::::::::::               --::::::::::
  38. --              FILENAME       or        --FILENAME
  39. --              ::::::::::               --::::::::::
  40. --
  41. -- as file separators.
  42. ----------------:  
  43. --                                                           -*
  44. ------------------ Revision history ---------------------------
  45. --                                                           -*
  46. -- DATE         VERSION    AUTHOR       HISTORY
  47. -- 12/15/84    1.0    Mitre Corp   Initial Release
  48. -- 01/21/85     850121  Mitre Corp   Mod to accept --::::::: headers
  49. --                                   Mod to correctly process blank
  50. --                                       lines when building heading
  51. --                                   Mod to stop on subfile header
  52. --                                       when building heading
  53. --                                   Moved PUT_DYNAMIC procedure
  54. --                                       from STR_PACK to CREATE_TB
  55. --                                   Added comments to STR_PACK and
  56. --                                       deleted several string 
  57. --                                       procedures
  58. --                                   Converted subfile headers to
  59. --                                       Ada comments  --:::::::::
  60. --                                                           -*
  61. ------------------ Distribution and Copyright -----------------
  62. --                                                           -*
  63. -- This prologue must be included in all copies of this software.
  64. --
  65. -- This software is released to the Public Domain (note:
  66. --   software released to the Public Domain is not subject
  67. --   to copyright protection).
  68. --
  69. -- Restrictions on use or distribution:  Although there are
  70. --      no current plans to provide maintenance for CREATE_TB,
  71. --      we would appreciate your reporting problems and
  72. --      experiences to:
  73. --              
  74. --                wis_ada at mitre (net address)
  75. --
  76. --      or call at:
  77. --
  78. --                (703)  883-7697
  79. --                                                           -*
  80. ------------------ Disclaimer ---------------------------------
  81. --                                                           -*
  82. -- This software and its documentation are provided "AS IS" and
  83. -- without any expressed or implied warranties whatsoever.
  84. -- No warranties as to performance, merchantability, or fitness
  85. -- for a particular purpose exist.
  86. --
  87. -- Because of the diversity of conditions and hardware under
  88. -- which this software may be used, no warranty of fitness for
  89. -- a particular purpose is offered.  The user is advised to
  90. -- test the software thoroughly before relying on it.  The user
  91. -- must assume the entire risk and liability of using this
  92. -- software.
  93. --
  94. -- In no event shall any person or organization of people be
  95. -- held responsible for any direct, indirect, consequential
  96. -- or inconsequential damages or lost profits.
  97. --                                                           -*
  98. -------------------END-PROLOGUE--------------------------------
  99.  
  100.  
  101. ::::::::::::::
  102. calendar.text
  103. ::::::::::::::
  104.  
  105. package CALENDAR is
  106.  
  107.     subtype TIME is  LONG_INTEGER;
  108.  
  109. end CALENDAR;
  110.  
  111. ::::::::::::::
  112. create_tb.text
  113. ::::::::::::::
  114. pragma SOURCE_INFO(ON);
  115. with STR_PACK, HOST, TEXT_IO;
  116. use  STR_PACK,       TEXT_IO, INTEGER_IO;
  117. procedure CREATE_TABLE is
  118.   MAX_NUMBER_OF_FIELDS : constant POSITIVE := 10;
  119.   MAX_ID               : constant POSITIVE := 3;
  120.   MAX_LINE             : constant POSITIVE := 55;
  121.   MAX_COLUMN           : CONSTANT INTEGER :=152;
  122.   MAX_STRING           : CONSTANT INTEGER :=255;
  123.   FIELD_ID  : array(1..MAX_NUMBER_OF_FIELDS) of STRING(1..MAX_ID);
  124.   FIELD_STR : array(1..MAX_NUMBER_OF_FIELDS) of DYNAMIC_STRING;
  125.   FIELD_LEN : array(1..MAX_NUMBER_OF_FIELDS) of INTEGER;
  126.   NUMBER_OF_FIELDS     : NATURAL;
  127.   OPTION_SPACES        : BOOLEAN := FALSE;
  128.   OPTION_BAR           : BOOLEAN := FALSE;
  129.   LINE_IGNORED         : BOOLEAN;
  130.   LINE_COUNT           : INTEGER := MAX_LINE;
  131.   COLUMN_SEPARATOR : STRING(1..3) := " | ";
  132.   HEADER           : STRING(1..MAX_STRING);
  133.   TABLE_WIDTH      : NATURAL;
  134.  
  135.   SOURCE_FILE   : FILE_TYPE;
  136.   TAB_FILE      : FILE_TYPE;
  137.   FILE_LIST     : DYNAMIC_STRING;
  138.   FNAME         : DYNAMIC_STRING;
  139.   TOKEN         : DYNAMIC_STRING;
  140.   FILENAME      : STRING(1..MAX_STRING);
  141.   SEPARATOR     : CHARACTER;
  142.   LINE          : STRING(1..MAX_STRING);
  143.   LAST          : INTEGER;
  144.   FILE_LENGTH   : INTEGER;
  145.   GLOBAL_ERROR  : exception;
  146.   BLANKS        : constant STRING(1..MAX_COLUMN) := 
  147. "                                                                            "&
  148. "                                                                            ";
  149.   HEADER_BAR    : constant STRING(1..MAX_COLUMN) :=
  150. "============================================================================"&
  151. "============================================================================";
  152.  
  153.  
  154.   procedure WRITE_ERROR
  155.     (PART1 : in STRING;
  156.      PART2 : in STRING) is
  157.   begin
  158.     NEW_LINE;
  159.     PUT("**ERROR**");
  160.     PUT(PART1);
  161.     PUT(PART2);
  162.     NEW_LINE;
  163.   end WRITE_ERROR;
  164.  
  165.  
  166.  
  167.   procedure OPEN_CO is
  168.     TAB_NAME  : STRING(1..MAX_STRING);
  169.   begin
  170.     NEW_LINE;
  171.     PUT("ENTER TABLE_FILE_NAME =>");
  172.     GET_LINE(TAB_NAME, LAST);
  173.     CREATE(TAB_FILE, OUT_FILE, TAB_NAME(1..LAST));
  174.   end;
  175.  
  176.  
  177.   function FILE_NAME_LINE return BOOLEAN is
  178.   begin
  179.     if LAST >= 10 and then (LINE(1..10) = "::::::::::" or
  180.                             LINE(1..10) = "--::::::::")then
  181.       return TRUE;
  182.     else
  183.       return FALSE;
  184.     end if;
  185.   end FILE_NAME_LINE;
  186.  
  187.  
  188.   procedure READ_SUBFILE_HEADER(
  189.       PRINT_FLAG: BOOLEAN := TRUE) is
  190.   begin
  191.     GET_LINE(SOURCE_FILE, LINE, LAST);
  192.     if PRINT_FLAG then
  193.       PUT("::");  PUT_LINE(LINE(1..LAST));
  194.     end if;
  195.     --Skip over the trailing ":::::::::::::" line
  196.     GET_LINE(SOURCE_FILE, LINE, LAST);
  197.   end READ_SUBFILE_HEADER;
  198.  
  199.  
  200.   procedure PUT_DYNAMIC
  201.      (STR    : in out DYNAMIC_STRING;
  202.       LINE   : in out STRING) is
  203.     POS     : NATURAL;
  204.     CHAR    : CHARACTER;
  205.     STR_LEN : NATURAL;
  206.   begin
  207.     begin --guard for END_OF_STRING
  208.       STR_LEN := LINE'LENGTH;
  209.  
  210.       --Delete leading blanks
  211.       READ(CHAR, STR);
  212.       while CHAR = ' ' loop
  213.         DELETE_CHAR(STR);
  214.         READ(CHAR,STR);
  215.       end loop;
  216.     
  217.       -- remove partial words from the tail
  218.       READ(CHAR, STR_LEN+1, STR);
  219.       while CHAR /= ' ' loop
  220.         READ(CHAR, STR_LEN, STR);
  221.         STR_LEN := STR_LEN - 1;
  222.         exit when STR_LEN = 0;
  223.       end loop;
  224.     exception
  225.       when END_OF_STRING => null; --This exception should occur only when the
  226.                                   --dynamic string is shorter than the output
  227.                                   --line.  Since, then the first READ is beyond
  228.                                   --the string. At this point, STR_LEN is 
  229.                                   --properly set.
  230.     end; --guard for END_OF_STRING
  231.  
  232.     --Make sure that a word which is longer than the field width is split
  233.     --across multiple lines
  234.     if STR_LEN = 0 then
  235.       STR_LEN := LINE'LENGTH;
  236.     end if;
  237.  
  238.     begin --Guard for END_OF_STRING
  239.       --Transfer the appropriate text
  240.       POS := 0;
  241.       for I in 1..STR_LEN loop
  242.         READ(CHAR, STR);
  243.         LINE(I) := CHAR;
  244.         DELETE_CHAR(STR);
  245.         POS := I;
  246.       end loop;
  247.     exception
  248.       when END_OF_STRING => null;
  249.     end; --Guard for END_OF_STRING
  250.  
  251.     --Pad the string with blanks when it is not completely filled
  252.     for I in POS+1..LINE'LENGTH loop
  253.       LINE(I) := ' ';
  254.     end loop;
  255.   end PUT_DYNAMIC;
  256.  
  257.  
  258.  
  259.   procedure GET_LIST_OF_FILENAMES is
  260.     TAB_TEMP       : FILE_TYPE;
  261.     COMMAND       : STRING(1..MAX_STRING);
  262.     D_COMMAND     : DYNAMIC_STRING;
  263.  
  264.   -- This procedure prompts the user for a filename search pattern,
  265.   -- builds a command to place a list of valid filenames in a file, and
  266.   -- then reads that file building a dynamic string of files to be 
  267.   -- scanned.  It calls the system dependent routine HOST.EXECUTE_CMDS
  268.   -- and also builds a system dependent string wich is passed as the
  269.   -- command to be executed.
  270.   -->>>>>>>>>>>>>>>>>>>>>>>S Y S T E M   D E P E N D E N T<<<<<<<<<<<<<<<--
  271.   begin 
  272.     ASSIGN(FILE_LIST, EMPTY_STR);
  273.     PUT("ENTER NAMES OF FILES TO BE SCANNED => ");
  274.     GET_LINE(LINE, LAST);
  275.     COMMAND(1..3)            := "ls ";
  276.     COMMAND(4..LAST+3)       := LINE(1..LAST);
  277.     COMMAND(LAST+4..LAST+21) := " >> CREATE_CO.TEMP";
  278.  
  279.     begin --build file list
  280.       CONVERT_TO_DYNAMIC(COMMAND(1..LAST+21), D_COMMAND);
  281.       HOST.EXECUTE_CMDS(D_COMMAND);
  282.       OPEN(TAB_TEMP, IN_FILE, "CREATE_CO.TEMP");
  283.  
  284.       loop  --Until end of file
  285.         GET_LINE(TAB_TEMP, LINE, LAST);
  286.         APPEND(" ", FILE_LIST);
  287.         APPEND(LINE(1..LAST), FILE_LIST);
  288.       end loop;
  289.     exception
  290.       when HOST.EXECUTION_ERROR =>
  291.                                        PUT_LINE("**ERROR** NO FILES FOUND");
  292.                                        DELETE(TAB_TEMP);
  293.       when END_ERROR =>
  294.                         DELETE(TAB_TEMP); --End of file expected.
  295.     end;
  296.   end GET_LIST_OF_FILENAMES;
  297.  
  298.  
  299.   function VALUE(STR : in STRING) return NATURAL is
  300.     TOTAL : NATURAL;
  301.   begin
  302.     TOTAL := 0;
  303.     for I in 1..STR'LENGTH loop
  304.       TOTAL := TOTAL*10;
  305.       case STR(I) is
  306.          when '0' => TOTAL := TOTAL+0;
  307.          when '1' => TOTAL := TOTAL+1;
  308.          when '2' => TOTAL := TOTAL+2;
  309.          when '3' => TOTAL := TOTAL+3;
  310.          when '4' => TOTAL := TOTAL+4;
  311.          when '5' => TOTAL := TOTAL+5;
  312.          when '6' => TOTAL := TOTAL+6;
  313.          when '7' => TOTAL := TOTAL+7;
  314.          when '8' => TOTAL := TOTAL+8;
  315.          when '9' => TOTAL := TOTAL+9;
  316.          when others => raise CONSTRAINT_ERROR;
  317.       end case;
  318.     end loop;
  319.     return TOTAL;
  320.   end VALUE;
  321.  
  322.  
  323.  
  324.   procedure READ_SET_UP is
  325.     SELECTOR      : STRING(1..MAX_STRING);
  326.     D_SELECTOR    : DYNAMIC_STRING;
  327.     ID            : STRING(1..MAX_ID);
  328.     LEN           : STRING(1..10);
  329.   begin
  330.     PUT("ENTER FIELD SELECTOR =>");
  331.     GET_LINE(SELECTOR, LAST);
  332.     CONVERT_TO_DYNAMIC(SELECTOR(1..LAST), D_SELECTOR);
  333.     NUMBER_OF_FIELDS := 0;
  334.  
  335.     while not EMPTY(D_SELECTOR) loop
  336.       --Extract FIELD_ID
  337.       GET_TOKEN(D_SELECTOR, TOKEN, SEPARATOR);
  338.       if SEPARATOR    = ';' then
  339.         OPTION_SPACES :=TRUE;
  340.       elsif SEPARATOR = ':' then
  341.         OPTION_BAR    := TRUE;
  342.       end if;
  343.       if LENGTH(TOKEN) > MAX_ID then
  344.         CONVERT_TO_STRING(TOKEN, ID);
  345.         WRITE_ERROR("--ILLEGAL FIELD_ID: ", ID);
  346.         raise GLOBAL_ERROR;
  347.       else
  348.         ID := BLANKS(1..MAX_ID);
  349.         CONVERT_TO_STRING(TOKEN, ID);
  350.         while ID(MAX_ID) = ' ' loop
  351.           ID(2..MAX_ID) := ID(1..MAX_ID-1);
  352.           ID(1)         := ' ';
  353.         end loop;
  354.       end if;
  355.  
  356.       --Extract FIELD_LEN
  357.       GET_TOKEN(D_SELECTOR, TOKEN, SEPARATOR);
  358.       CONVERT_TO_STRING(TOKEN, LEN);
  359.  
  360.       --Store into selector descriptor table
  361.       NUMBER_OF_FIELDS            := NUMBER_OF_FIELDS+1;
  362.       FIELD_ID (NUMBER_OF_FIELDS) := ID;
  363.       FIELD_LEN(NUMBER_OF_FIELDS) := VALUE(LEN(1..LENGTH(TOKEN)));
  364.     end loop;
  365.   exception
  366.     when CONSTRAINT_ERROR =>
  367.              WRITE_ERROR("--ILLEGAL FIELD_SIZE: ", LEN(1..LENGTH(TOKEN)));
  368.              raise GLOBAL_ERROR;
  369.     when END_OF_STRING    =>
  370.              WRITE_ERROR("--ILLEGAL SELECTOR FORMAT: ", SELECTOR(1..LAST));
  371.   end READ_SET_UP;
  372.  
  373.  
  374.   procedure BUILD_HEADER
  375.     (FILENAME    : in STRING) is
  376.     HEADER_FIELD   : array(1..MAX_NUMBER_OF_FIELDS) of STRING(1..MAX_STRING);
  377.     HEADER_SIZE    : array(1..MAX_NUMBER_OF_FIELDS) of NATURAL;
  378.     POS            : NATURAL;
  379.     POS_START      : NATURAL;
  380.     HEAD           : NATURAL;
  381.     MID            : NATURAL;
  382.     TAIL           : NATURAL;
  383.     PAGED_FILE     : BOOLEAN := FALSE;
  384.   begin
  385.     begin --process a file
  386.       OPEN(SOURCE_FILE, IN_FILE, FILENAME);
  387.       PROCESS_A_FILE: loop
  388.         GET_LINE(SOURCE_FILE, LINE, LAST);
  389.         if LAST >= MAX_ID then
  390.           for I in 1..NUMBER_OF_FIELDS loop
  391.             --Check for subfile header in a paged file so that only one
  392.             --subfile is read to create the header.
  393.             if FILE_NAME_LINE then
  394.               if PAGED_FILE then
  395.                 exit;
  396.               else
  397.                 PAGED_FILE := TRUE;
  398.                 READ_SUBFILE_HEADER(FALSE);
  399.               end if;
  400.             end if;
  401.  
  402.             if LINE(1..MAX_ID) = FIELD_ID(I) then
  403.  
  404.               POS := MAX_ID+1;
  405.               while LINE(POS) = ' ' loop
  406.                 POS := POS+1;
  407.               end loop;
  408.               POS_START := POS;
  409.               for J in POS..LAST loop
  410.                 exit when LINE( J ) = ':';
  411.                 POS := J;
  412.               end loop;
  413.  
  414.               HEADER_SIZE (I)                    := POS - POS_START + 1;
  415.               HEADER_FIELD(I)(1..HEADER_SIZE(I)) := LINE(POS_START..POS);
  416.               exit;
  417.             end if;
  418.           end loop;
  419.         end if;
  420.  
  421.       end loop PROCESS_A_FILE;
  422.     exception
  423.       when END_ERROR => CLOSE(SOURCE_FILE);
  424.       when NAME_ERROR=> WRITE_ERROR("ABORTED, FIRST FILE IS NOT ACCESSIBLE",
  425.                                     FILENAME);
  426.                         raise GLOBAL_ERROR;
  427.     end;  --process a file
  428.  
  429.     --Now build the header line, centering or truncating each header field
  430.     --as necessary.
  431.     POS          := 0;
  432.     for I in 1..NUMBER_OF_FIELDS loop
  433.       if HEADER_SIZE(I) >= FIELD_LEN(I) then
  434.         HEADER(POS+1..POS+FIELD_LEN(I)) := HEADER_FIELD(I)(1..FIELD_LEN(I));
  435.         POS := POS+FIELD_LEN(I);
  436.       else
  437.         HEAD := (FIELD_LEN(I) - HEADER_SIZE(I) + 1)/2;
  438.         TAIL := (FIELD_LEN(I) - HEADER_SIZE(I) - HEAD);
  439.         MID  := HEADER_SIZE(I);
  440.         HEADER(POS+1..POS+HEAD)       := BLANKS(1..HEAD);
  441.         POS := POS+HEAD;
  442.         HEADER(POS+1..POS+MID )       := HEADER_FIELD(I)(1..MID);
  443.         POS := POS+MID ;
  444.         HEADER(POS+1..POS+TAIL)       := BLANKS(1..TAIL);
  445.         POS := POS+TAIL;
  446.       end if;
  447.       
  448.       HEADER(POS+1..POS+3) := COLUMN_SEPARATOR;
  449.       POS := POS+3;
  450.     end loop;
  451.     TABLE_WIDTH := POS-1;
  452.     PUT("THE TABLE WIDTH IS CALCULATED TO BE: "); PUT(POS,3);  NEW_LINE;
  453.     if TABLE_WIDTH > MAX_COLUMN then
  454.       raise GLOBAL_ERROR;
  455.     end if;
  456.   end BUILD_HEADER;
  457.  
  458.  
  459.   procedure SAVE_FIELD (I   : in POSITIVE) is
  460.     J : POSITIVE;
  461.   begin
  462.     --Skip title
  463.     J := 1;
  464.     while LINE(J) /= ':' loop
  465.      J := J+1;
  466.     end loop;
  467.      
  468.     --Skip leading blanks
  469.     if J < LAST then
  470.       J := J+1;
  471.       while LINE(J) = ' ' loop
  472.        exit when J = LAST;
  473.        J := J+1;
  474.       end loop;
  475.       LINE(1..LAST-J+1) := LINE(J..LAST);
  476.       CONVERT_TO_DYNAMIC(LINE(1..LAST-J+1), FIELD_STR(I));
  477.     end if;
  478.      
  479.     --Read continuation lines
  480.     GET_LINE(SOURCE_FILE, LINE, LAST);
  481.      
  482.     while LINE(1..MAX_ID) = BLANKS(1..MAX_ID) loop
  483.       J := 1;
  484.       while LINE(J) = ' ' loop
  485.         J := J + 1;
  486.         exit when J=LAST;
  487.       end loop;
  488.       APPEND( " ",          FIELD_STR(I));
  489.       LINE(1..LAST-J+1) := LINE(J..LAST);
  490.       APPEND(LINE(1..LAST-J+1), FIELD_STR(I));
  491.       GET_LINE(SOURCE_FILE, LINE, LAST);
  492.     end loop;
  493.  
  494.   end SAVE_FIELD;
  495.  
  496.  
  497.   procedure PUT_GRAPH_ENTRY is
  498.     ALL_EMPTY        : BOOLEAN;
  499.     OUT_LINE         : STRING(1..MAX_STRING);
  500.   begin
  501.     loop
  502.       ALL_EMPTY := TRUE;
  503.       for I in 1..NUMBER_OF_FIELDS loop
  504.         if not EMPTY(FIELD_STR(I)) then
  505.           ALL_EMPTY := FALSE;
  506.           exit;
  507.         end if;
  508.       end loop;
  509.       exit when ALL_EMPTY;
  510.  
  511.       if LINE_COUNT >= MAX_LINE then
  512.         NEW_PAGE(TAB_FILE);
  513.         LINE_COUNT := 3;
  514.         PUT_LINE(TAB_FILE, HEADER_BAR(1..TABLE_WIDTH));
  515.         PUT_LINE(TAB_FILE, HEADER    (1..TABLE_WIDTH));
  516.         PUT_LINE(TAB_FILE, HEADER_BAR(1..TABLE_WIDTH));
  517.       else
  518.         NEW_LINE(TAB_FILE);
  519.       end if;
  520.  
  521.       LINE_COUNT := LINE_COUNT+1;
  522.       for I in 1..NUMBER_OF_FIELDS loop
  523.         PUT_DYNAMIC(FIELD_STR(I), OUT_LINE(1..FIELD_LEN(I)));
  524.         PUT(TAB_FILE, OUT_LINE(1..FIELD_LEN(I)));
  525.         PUT(TAB_FILE,COLUMN_SEPARATOR);
  526.       end loop;
  527.  
  528.     end loop;
  529.   end PUT_GRAPH_ENTRY;
  530.  
  531.  
  532.   procedure PUT_END_OF_ENTRY is
  533.   begin
  534.     if OPTION_SPACES then
  535.       NEW_LINE(TAB_FILE);
  536.       LINE_COUNT := LINE_COUNT+1;
  537.     end if;
  538.     if OPTION_BAR    then
  539.       NEW_LINE(TAB_FILE);
  540.       PUT(TAB_FILE, HEADER_BAR(1..TABLE_WIDTH));
  541.       LINE_COUNT := LINE_COUNT+1;
  542.     end if;
  543.     if OPTION_SPACES then 
  544.       NEW_LINE(TAB_FILE);
  545.       LINE_COUNT := LINE_COUNT+1;
  546.     end if;
  547.   end PUT_END_OF_ENTRY;
  548.  
  549.  
  550. begin
  551.  
  552.   --Prompt user for co_file name. Then get list of files to be processed
  553.   --and added to the co_file.
  554.   PUT_LINE("               CREATE_TB     VERSION 850121");
  555.   OPEN_CO;
  556.   READ_SET_UP;
  557.   GET_LIST_OF_FILENAMES;
  558.  
  559.   GET_TOKEN(FILE_LIST, FNAME, SEPARATOR);
  560.   CONVERT_TO_STRING(FNAME, FILENAME);
  561.   FILE_LENGTH := LENGTH(FNAME);
  562.   BUILD_HEADER(FILENAME(1..FILE_LENGTH));
  563.  
  564.   while not EMPTY( FNAME ) loop
  565.     CONVERT_TO_STRING(FNAME, FILENAME);
  566.     FILE_LENGTH := LENGTH(FNAME);
  567.     PUT_LINE( FILENAME(1..FILE_LENGTH) );
  568.  
  569.     begin  -- Protect for illegal files
  570.       OPEN( SOURCE_FILE, IN_FILE, FILENAME(1..LENGTH(FNAME)) );
  571.       --Check for subfile in a PAGED file
  572.       GET_LINE(SOURCE_FILE, LINE, LAST);
  573.       if FILE_NAME_LINE then
  574.         READ_SUBFILE_HEADER(TRUE);
  575.         GET_LINE(SOURCE_FILE, LINE, LAST);
  576.       end if;
  577.  
  578.       begin --process a file
  579.         PROCESS_A_FILE: loop
  580.           LINE_IGNORED := TRUE;
  581.  
  582.           --Check for subfile in a PAGED file
  583.           if FILE_NAME_LINE then
  584.             PUT_GRAPH_ENTRY;
  585.             PUT_END_OF_ENTRY;
  586.             READ_SUBFILE_HEADER(TRUE);
  587.           end if;
  588.  
  589.           for I in 1..NUMBER_OF_FIELDS loop
  590.             if LINE(1..MAX_ID) = FIELD_ID(I) then
  591.               SAVE_FIELD(I);
  592.               LINE_IGNORED := FALSE;
  593.               exit;
  594.             end if;
  595.           end loop;
  596.  
  597.           --SAVE_FIELD always reads an extra line. Therefore, only read another
  598.           --line if this line was ignored.
  599.           if LINE_IGNORED then
  600.             GET_LINE(SOURCE_FILE, LINE, LAST);
  601.           end if;
  602.         end loop PROCESS_A_FILE;
  603.       exception
  604.         when END_ERROR => CLOSE(SOURCE_FILE);
  605.                           PUT_GRAPH_ENTRY;
  606.                           PUT_END_OF_ENTRY;
  607.       end;  --process a file
  608.       GET_TOKEN(FILE_LIST, FNAME, SEPARATOR);
  609.     exception
  610.       when NAME_ERROR=> NEW_LINE;
  611.                         PUT("**ERROR** FILE:");
  612.                         PUT(FILENAME(1..FILE_LENGTH));
  613.                         PUT_LINE("CAN'T BE OPENED.");
  614.                         GET_TOKEN(FILE_LIST, FNAME, SEPARATOR);
  615.     end;  -- Protect for illegal files
  616.   end loop;
  617. exception
  618.   -- END_OF_STRING may be raised by a call to GET_TOKEN in an attempt to get
  619.   -- another file name after the last name in the list has been read.
  620.   when END_OF_STRING | GLOBAL_ERROR => CLOSE(TAB_FILE);
  621. end CREATE_TABLE;
  622.  
  623. ::::::::::::::
  624. environs.text
  625. ::::::::::::::
  626. package ENVIRONS is
  627.  
  628.    ERROR : exception;
  629.  
  630. -->>>>>>>>>>>>>>>>>>>C O N S T A N T   D E C L A R A T I O N S<<<<<<<<<<<<<<<--
  631.    CONTINUATION              : constant CHARACTER := '\';
  632.    COMMENT                   : constant CHARACTER := '-';
  633.    TARGET_LIST_TERMINATOR    : constant CHARACTER := ':';
  634.    DEPENDENT_LIST_TERMINATOR : constant CHARACTER := ';';
  635.    MAX_LINE_LENGTH           : constant INTEGER   := 80;
  636.    MAX_CMD_LENGTH            : constant INTEGER   := 200;
  637.    MAX_FILENAME_LENGTH       : constant INTEGER   := 14;
  638.    END_OF_COMMANDS           : constant CHARACTER := '$';
  639.    FIRST_CHAR_OF_ENTRY_NAME  : constant CHARACTER := '%';
  640. -->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--
  641.  
  642.  
  643.  
  644. end ENVIRONS;
  645. ::::::::::::::
  646. host.text
  647. ::::::::::::::
  648.  
  649. with STR_PACK;
  650. with CALENDAR;
  651.  
  652. package HOST is
  653.  
  654.  
  655.    function MODIFICATION_TIMESTAMP
  656.       (FOR_DBO : STR_PACK.DYNAMIC_STRING)  
  657.       return CALENDAR.TIME;
  658.  
  659.    ACCESSIBILITY_ERROR : exception;
  660.  
  661.    function ARGS_EXIST return BOOLEAN;
  662.    
  663.    procedure EXECUTE_CMDS (CMD_LIST : in STR_PACK.DYNAMIC_STRING); 
  664.  
  665.    EXECUTION_ERROR : EXCEPTION;
  666.  
  667.  
  668. end HOST;
  669.  
  670.  
  671. with HOST_LCD_IF;
  672. with UNIX_CALL; 
  673. with SYSTEM;
  674. with UNCHECKED_CONVERSION;
  675. with ENVIRONS;
  676. package body HOST is
  677.  
  678.  
  679.    function MODIFICATION_TIMESTAMP
  680.       (FOR_DBO : STR_PACK.DYNAMIC_STRING)  
  681.       return CALENDAR.TIME is
  682.  
  683.       GET_TIME : constant UNIX_CALL.CALL := 666;
  684.  
  685.       NAME_LENGTH : INTEGER;
  686.    
  687.       function ADDR_TO_INT is new UNCHECKED_CONVERSION
  688.         (SYSTEM.ADDRESS, LONG_INTEGER);
  689.    
  690.       TIMESTAMP  : CALENDAR.TIME;
  691.  
  692.       RSLT : UNIX_CALL.RESULT;
  693.    
  694.       FILENAME : STRING(1 .. ENVIRONS.MAX_FILENAME_LENGTH);
  695.  
  696.    begin
  697.  
  698.  
  699.       STR_PACK.CONVERT_TO_STRING (FOR_DBO, FILENAME);
  700.  
  701.       NAME_LENGTH := STR_PACK.LENGTH (FOR_DBO);
  702.       if NAME_LENGTH < 14 then
  703.          FILENAME (NAME_LENGTH + 1) := ASCII.NUL;
  704.       end if;
  705.  
  706.       RSLT := UNIX_CALL.SYSCALL1 (GET_TIME, ADDR_TO_INT(FILENAME'address));
  707.  
  708.       if (RSLT = -1) then
  709.          raise ACCESSIBILITY_ERROR;
  710.       end if;
  711.  
  712.       TIMESTAMP := RSLT; -- This may become less trivial when CALENDAR
  713.                          -- changes ( and Time /= Long_Integer!)
  714.  
  715.       return (TIMESTAMP);
  716.  
  717.    end MODIFICATION_TIMESTAMP;
  718.  
  719.  
  720.  
  721.    function ARGS_EXIST return BOOLEAN is
  722.       PARMS : STRING(1 .. 100);
  723.       PARM_COUNT : INTEGER;
  724.    
  725.    begin
  726.  
  727.       HOST_LCD_IF.PARAM_STRING (PARMS, PARM_COUNT);
  728.       return (PARM_COUNT > 0);
  729.  
  730.    end ARGS_EXIST;
  731.  
  732.  
  733.    procedure EXECUTE_CMDS (CMD_LIST : in STR_PACK.DYNAMIC_STRING) is
  734.  
  735.       RET_CODE : UNIX_CALL.RESULT;
  736.       INVOKE : constant UNIX_CALL.CALL := 667;
  737.       STR_BUFFER : STRING(1 .. ENVIRONS.MAX_CMD_LENGTH);
  738.      
  739.       function ADDR_TO_INT is new UNCHECKED_CONVERSION
  740.         (SYSTEM.ADDRESS, LONG_INTEGER);
  741.  
  742.    begin
  743.  
  744.       for I in STR_BUFFER'range loop
  745.          STR_BUFFER(I) := ' ';
  746.       end loop;
  747.  
  748.       STR_PACK.CONVERT_TO_STRING (CMD_LIST, STR_BUFFER);
  749.  
  750.       RET_CODE := UNIX_CALL.SYSCALL1 (INVOKE, ADDR_TO_INT(STR_BUFFER'address)); 
  751.    
  752.       if (RET_CODE /= 0) then
  753.          raise EXECUTION_ERROR;
  754.       end if;
  755.    
  756.    end EXECUTE_CMDS;
  757.  
  758.  
  759. end HOST;
  760. ::::::::::::::
  761. str_pack.text
  762. ::::::::::::::
  763. -- This package defines and maintains dynamic strings that are used
  764. -- by the other packages and the procedure CONSTRUCT.
  765.  
  766. package STR_PACK is
  767.  
  768.   -- Each variable of this type is composed of a sequence of
  769.   -- characters and an indication of the number of characters in the
  770.   -- sequence (i.e., its length >= 0).  Each character in the sequence
  771.   -- is ordered relative to its position, where the first character
  772.   -- has position l, and the position of each character thereafter is
  773.   -- incremented by l.  If there are no characters in the sequence,
  774.   -- then the length is 0, else the length is the position of the
  775.   -- last character in the sequence.  Initially, a variable of this
  776.   -- type is an empty string (i.e., its length is zero).
  777.   type DYNAMIC_STRING is limited private;
  778.  
  779.   -- This constant represents an empty string (i.e., its length is
  780.   -- zero and thus contains no characters).
  781.   -- EMPTY_STR : constant DYNAMIC_STRING;   
  782.   -- Deferred constants not yet implemented so a function is used.
  783.  
  784.  
  785.   function EMPTY_STR return DYNAMIC_STRING;
  786.  
  787.   procedure ASSIGN
  788.     (STR       : in out DYNAMIC_STRING;
  789.      D_STRING  : in     DYNAMIC_STRING);
  790.  
  791.  
  792.   procedure CONVERT_TO_DYNAMIC
  793.     (CHAR_STRING : in STRING;
  794.      STR         : in out DYNAMIC_STRING);
  795.  
  796.  
  797.   procedure DELETE_CHAR
  798.     (STR  : in out DYNAMIC_STRING);
  799.  
  800.  
  801.   procedure APPEND
  802.     (CHAR : in CHARACTER;
  803.      STR  : in out DYNAMIC_STRING);
  804.  
  805.  
  806.   procedure APPEND
  807.     (CHAR_STRING : in STRING;
  808.      STR         : in out DYNAMIC_STRING);
  809.  
  810.   procedure APPEND
  811.     (D_STRING : in DYNAMIC_STRING;
  812.      STR      : in out DYNAMIC_STRING);
  813.  
  814.   procedure CONVERT_TO_STRING
  815.      (STR    : in DYNAMIC_STRING;
  816.       CH_STR : out STRING);
  817.  
  818.   procedure SET_TOKEN_STREAM
  819.     (LINE  : in STRING);
  820.   
  821.  
  822.   procedure GET_TOKEN
  823.     (TOKEN       : in out DYNAMIC_STRING;
  824.      SEPARATOR   : in out CHARACTER);
  825.  
  826.   procedure RETURN_TOKEN;
  827.  
  828.  
  829.   procedure GET_TOKEN
  830.     (STR      : in out DYNAMIC_STRING;
  831.      TOKEN    : out    DYNAMIC_STRING;
  832.      SEPARATOR: out    CHARACTER);
  833.  
  834.   function LENGTH
  835.     (STR : in DYNAMIC_STRING)
  836.      return NATURAL;
  837.  
  838.   
  839.   procedure READ
  840.     (CHAR : out CHARACTER;
  841.      STR  : in DYNAMIC_STRING);
  842.   
  843.  
  844.  
  845.   procedure READ
  846.     (CHAR : out CHARACTER;
  847.      ITH  : in POSITIVE;
  848.      STR  : in DYNAMIC_STRING);
  849.  
  850.  
  851.   function EMPTY(STR: DYNAMIC_STRING) return BOOLEAN;
  852.  
  853.   function "="
  854.     (LEFT  : DYNAMIC_STRING;
  855.      RIGHT : DYNAMIC_STRING)
  856.      return BOOLEAN;
  857.  
  858.   procedure DUMP
  859.     (TITLE : in STRING;
  860.      STR   : in DYNAMIC_STRING);
  861.  
  862.  
  863.   function NUM_OF_TOKENS 
  864.     (DYN_STR : DYNAMIC_STRING) return NATURAL;
  865.  
  866.  
  867.   END_OF_STRING : exception;
  868.  
  869.  
  870. private
  871.   -- Each dynamic string is decomposed into segments which are at
  872.   -- most SEGMENT_SIZE characters in length.  The head and tail
  873.   -- segments may be partial(i.e. less than SEGMENT_SIZE), but all
  874.   -- middle segments must be a full SEGMENT_SIZE in length.
  875.  
  876.   SEGMENT_SIZE : constant INTEGER := 10;
  877.   BLANKS       : CONSTANT STRING (1..SEGMENT_SIZE) := "          ";
  878.  
  879.   type STR_REC;
  880.  
  881.   type STR_SEGMENT is access STR_REC;
  882.  
  883.   -- This data structure defines a segment of a dynamic string as
  884.   -- follows:
  885.   -- (  i)  STR contains this part of the string
  886.   -- ( ii)  NEXT_SEGMENT points to the next segment in the
  887.   --        dynamic string.
  888.   -- (iii)  PRIOR_SEGMENT points to the previous segment in the
  889.   --        dynamic string.
  890.   --======================================================================
  891.   --Note that the procedures RESET, ASSIGN, and APPEND have been written
  892.   --so that when a new value is assigned to a dynamic string already
  893.   --allocated segment blocks are kept in the linked list even though they
  894.   --are not currently in use. Depending on the storage manager, this 
  895.   --approach may need modification.  Modification will also be required if
  896.   --new strings are created using ADD rather than APPEND.
  897.   type STR_REC is
  898.     record
  899.       -- SEGMENT   : STRING (1..SEGMENT_SIZE) := (1..SEGMENT_SIZE => ' ');
  900.       -- Packed aggregates are not yet implemented.  Array must be
  901.       -- initialized  with string of spaces.
  902.       SEGMENT      : STRING (1..SEGMENT_SIZE) := "          "; 
  903.       NEXT_SEGMENT : STR_SEGMENT;
  904.       PRIOR_SEGMENT: STR_SEGMENT;
  905.     end record;
  906.  
  907.   -- This data structure defines a dynamic string that is composed
  908.   -- of a linked list of segments that are ordered from HEAD to TAIL.
  909.   -- NUM_CHAR indicates the length of the dynamic string (i.e., the
  910.   -- sum of the lengths of all segments of which it is composed).
  911.   -- FIRST is the position within the head segment of the first
  912.   -- character.  LAST is the position within the tail segment of the
  913.   -- last character.
  914.   type DYNAMIC_STRING is
  915.     record
  916.       HEAD     : STR_SEGMENT := null;
  917.       TAIL     : STR_SEGMENT := null;
  918.       NUM_CHAR : NATURAL :=0;
  919.       FIRST    : NATURAL :=0;
  920.       LAST     : NATURAL :=0;
  921.     end record;
  922.  
  923. end STR_PACK;
  924.  
  925.  
  926.  
  927. with TEXT_IO; use TEXT_IO, INTEGER_IO;
  928. with SYSTEM;
  929. package body STR_PACK is
  930.  
  931.   TOKEN_STREAM        : DYNAMIC_STRING;
  932.   PSEUDO_TOKEN_STREAM : DYNAMIC_STRING;
  933.   OLD_TOKEN_HEAD      : STR_SEGMENT;
  934.   OLD_TOKEN_FIRST     : NATURAL;
  935.   OLD_TOKEN_NUM_CHAR  : NATURAL;
  936.  
  937.   -- A token stream is useful to conserve heap space when a file is being
  938.   -- processed as a sequence of dynamic strings.  By having two pointers
  939.   -- to the same dynamic string(aliasing) the same space may be used for
  940.   -- the next dynamic string after breaking the first one into tokens.  A
  941.   -- special TOKEN procedure must be used in conjunction with SET_TOKEN_STREAM.
  942.   -- That TOKEN procedure doesn't have a dynamic string for input and thus uses.
  943.   -- PSEUDO_TOKEN_STREAM.
  944.   procedure SET_TOKEN_STREAM
  945.     (LINE  : in STRING) is
  946.   begin
  947.     CONVERT_TO_DYNAMIC(LINE, TOKEN_STREAM);
  948.     PSEUDO_TOKEN_STREAM  := TOKEN_STREAM;
  949.     OLD_TOKEN_HEAD       := PSEUDO_TOKEN_STREAM.HEAD;
  950.     OLD_TOKEN_FIRST      := PSEUDO_TOKEN_STREAM.FIRST;
  951.     OLD_TOKEN_NUM_CHAR   := PSEUDO_TOKEN_STREAM.NUM_CHAR;
  952.   end SET_TOKEN_STREAM;
  953.  
  954.   -- This GET_TOKEN procedure may only be used in conjunction with
  955.   -- SET_TOKEN_STREAM.  This procedure extracts a token from the
  956.   -- PSEUDO_TOKEN_STREAM.  It also makes an additional check to convert
  957.   -- the standard GET_TOKEN's "end of string marker  ;  " to an 'E' so that
  958.   -- Ada syntax may be properly analyzed.
  959.   procedure GET_TOKEN
  960.     (TOKEN     : in out DYNAMIC_STRING;
  961.      SEPARATOR : in out CHARACTER) is
  962.   begin
  963.     OLD_TOKEN_HEAD       := PSEUDO_TOKEN_STREAM.HEAD;
  964.     OLD_TOKEN_FIRST      := PSEUDO_TOKEN_STREAM.FIRST;
  965.     OLD_TOKEN_NUM_CHAR   := PSEUDO_TOKEN_STREAM.NUM_CHAR;
  966.     GET_TOKEN(PSEUDO_TOKEN_STREAM, TOKEN, SEPARATOR);
  967.     if SEPARATOR                                     = ';' and then
  968.        LENGTH(PSEUDO_TOKEN_STREAM)                   = 0   and then
  969.        TOKEN_STREAM.TAIL.SEGMENT(TOKEN_STREAM.LAST) /= ';' and then
  970.        TOKEN_STREAM.TAIL.SEGMENT(TOKEN_STREAM.LAST) /= ' ' then
  971.          SEPARATOR := 'E';
  972.     end if;
  973.   end GET_TOKEN;
  974.  
  975.  
  976.   -- This procedure resets the pointers to the token stream so that the most
  977.   -- recently obtained token is reinstated at the head of the stream.
  978.   procedure RETURN_TOKEN is
  979.   begin
  980.     PSEUDO_TOKEN_STREAM.HEAD     := OLD_TOKEN_HEAD;
  981.     PSEUDO_TOKEN_STREAM.FIRST    := OLD_TOKEN_FIRST;
  982.     PSEUDO_TOKEN_STREAM.NUM_CHAR := OLD_TOKEN_NUM_CHAR;
  983.   end RETURN_TOKEN;
  984.  
  985.  
  986.  
  987.   -- The is a debugging procedure which prints out a complete dynamic string,
  988.   -- including its header information as well as all allocated space.
  989.   procedure DUMP
  990.     (TITLE : in STRING;
  991.      STR   : in DYNAMIC_STRING) is
  992.     CURRENT : STR_SEGMENT;
  993.     COUNT   : INTEGER;
  994.   begin
  995.     NEW_LINE;
  996.     PUT(TITLE);
  997.     PUT(STR.NUM_CHAR,5); PUT(STR.FIRST,5); PUT(STR.LAST,5);
  998.    
  999.     CURRENT := STR.HEAD;
  1000.     while CURRENT /= null loop
  1001.       PUT(CURRENT.SEGMENT);
  1002.       CURRENT := CURRENT.NEXT_SEGMENT;
  1003.       COUNT := COUNT+1;
  1004.       if COUNT = 5 then
  1005.         NEW_LINE;
  1006.         COUNT := 0;
  1007.       end if;
  1008.     end loop;
  1009.     NEW_LINE;
  1010.   end DUMP;
  1011.  
  1012.  
  1013.   -- This function returns an empty string value for the private type
  1014.   -- DYNAMIC_STRING.
  1015.   function EMPTY_STR return DYNAMIC_STRING is
  1016.     STR : DYNAMIC_STRING;
  1017.   begin
  1018.     return STR;
  1019.   end EMPTY_STR;
  1020.  
  1021.  
  1022.  
  1023.   -- This function returns TRUE if the number of characters in a dynamic
  1024.   -- string is zero. FALSE, otherwise.
  1025.   function EMPTY(STR: DYNAMIC_STRING) return BOOLEAN is
  1026.     begin
  1027.        return STR.NUM_CHAR = 0;
  1028.     end EMPTY;
  1029.  
  1030.   
  1031.   -- This procedure resets a DYNAMIC_STRING to the empty value.  However, the
  1032.   -- HEAD pointer is left pointing to the linked list of segments so that the
  1033.   -- space may be reused.
  1034.   procedure RESET(STR : in out DYNAMIC_STRING) is
  1035.   begin
  1036.     STR.NUM_CHAR:= 0;
  1037.     STR.FIRST   := 0;
  1038.     STR.LAST    := 0;
  1039.     STR.TAIL    := STR.HEAD;
  1040.   end RESET;
  1041.  
  1042.   
  1043.   -- This procedure returns the lead character in a dynamic string.  The
  1044.   -- character is not deleted. The END_OF_STRING exception is returned
  1045.   -- if the dynamic string is empty.
  1046.   procedure READ(CHAR : out CHARACTER;
  1047.                  STR  : in DYNAMIC_STRING) is
  1048.   begin
  1049.     if STR.NUM_CHAR = 0 then
  1050.       raise END_OF_STRING;
  1051.     else
  1052.       CHAR := STR.HEAD.SEGMENT(STR.FIRST);
  1053.     end if;
  1054.   end READ;
  1055.  
  1056.  
  1057.   -- This function returns the number of characters in a dynamic string, not
  1058.   -- the amount of allocated space.
  1059.   function LENGTH
  1060.     (STR : in DYNAMIC_STRING)
  1061.      return NATURAL is
  1062.   begin
  1063.      return STR.NUM_CHAR;
  1064.   end;
  1065.  
  1066.  
  1067.   -- This procedure assigns the value of one dynamic string to another. It
  1068.   -- does not merely copy the pointer values since thsi would result in
  1069.   -- undesirable aliasing.  Each segment is copied in its entirety without
  1070.   -- realigning characters across the segments.
  1071.   procedure ASSIGN
  1072.     (STR     : in out DYNAMIC_STRING;
  1073.      D_STRING: in DYNAMIC_STRING) is
  1074.     CURRENT_SEGMENT : STR_SEGMENT;
  1075.     OLD_SEGMENT     : STR_SEGMENT;
  1076.     NEW_SEGMENT     : STR_SEGMENT;
  1077.   begin
  1078.     --First check for the empty string.
  1079.     RESET(STR);
  1080.     if D_STRING.NUM_CHAR = 0 then
  1081.       --String is empty.  We are all done.
  1082.       return;
  1083.     end if;
  1084.  
  1085.  
  1086.     --If the string was not empty, copy its contents over to STR.
  1087.     --First, set the dynamic string fields and copy over the complete
  1088.     --contents of D_STRING.HEAD.  This way the null NEXT and PRIOR_
  1089.     --SEGMENT pointers are captured for one segment strings. Note we
  1090.     --must be carefull always to create new segments and never to 
  1091.     --point to segments from D_STRING.
  1092.     STR.FIRST    := D_STRING.FIRST;
  1093.     STR.LAST     := D_STRING.LAST;
  1094.     STR.NUM_CHAR := D_STRING.NUM_CHAR; 
  1095.     if STR.HEAD   = null then
  1096.       STR.HEAD     := new STR_REC;
  1097.     end if;
  1098.     STR.HEAD.SEGMENT := D_STRING.HEAD.SEGMENT;
  1099.  
  1100.     -- Now copy over any middle segments.
  1101.     CURRENT_SEGMENT          := STR.HEAD;
  1102.     OLD_SEGMENT              := D_STRING.HEAD;
  1103.     while OLD_SEGMENT /= D_STRING.TAIL loop
  1104.       OLD_SEGMENT      := OLD_SEGMENT.NEXT_SEGMENT;
  1105.  
  1106.       if CURRENT_SEGMENT.NEXT_SEGMENT = null then
  1107.         NEW_SEGMENT                  := new STR_REC;
  1108.         CURRENT_SEGMENT.NEXT_SEGMENT := NEW_SEGMENT;
  1109.         NEW_SEGMENT.PRIOR_SEGMENT    := CURRENT_SEGMENT;
  1110.       else
  1111.         NEW_SEGMENT                  := CURRENT_SEGMENT.NEXT_SEGMENT;
  1112.       end if;
  1113.       NEW_SEGMENT.SEGMENT          := OLD_SEGMENT.SEGMENT;
  1114.      
  1115.       CURRENT_SEGMENT  := NEW_SEGMENT;
  1116.     end loop;
  1117.  
  1118.     --Since the contents of tail have been copied over already, merely
  1119.     --point TAIL to the current segment.
  1120.     STR.TAIL := CURRENT_SEGMENT;
  1121.   end ASSIGN;
  1122.  
  1123.  
  1124.   -- This procedure removes the first character in a dynamic string, reducing
  1125.   -- by one thelength of the string.  When the HEAD segment is emptied the HEAD
  1126.   -- pointer is moved and the space for that segment may be returned to the 
  1127.   -- heap.
  1128.   procedure DELETE_CHAR
  1129.     (STR  : in out DYNAMIC_STRING) is
  1130.   begin
  1131.     if STR.NUM_CHAR = 0 then
  1132.       raise END_OF_STRING;
  1133.   
  1134.     else
  1135.       --Delete the first character and see if the head segment has become
  1136.       --empty.
  1137.       STR.NUM_CHAR := STR.NUM_CHAR-1;
  1138.       if STR.FIRST < SEGMENT_SIZE then
  1139.         STR.FIRST := STR.FIRST+1;
  1140.       else
  1141.         --Remove this head segment.
  1142.         STR.HEAD := STR.HEAD.NEXT_SEGMENT;
  1143.         STR.FIRST:= 1;
  1144.       end if;
  1145.  
  1146.       --Now check for an empty string and clean up just in case.
  1147.       if STR.NUM_CHAR = 0 then
  1148.         STR.HEAD := null;
  1149.         STR.TAIL := null;
  1150.         STR.FIRST:= 0;
  1151.         STR.LAST := 0;
  1152.       end if;
  1153.     end if;
  1154.   end DELETE_CHAR;
  1155.  
  1156.  
  1157.  
  1158.   -- This procedure moves characters from the dynamic string to the output
  1159.   -- string.  The number of characters moved is the minimum of the two string
  1160.   -- lengths.  The dynamic is not modified by this procedure.
  1161.   procedure CONVERT_TO_STRING
  1162.      (STR         : in DYNAMIC_STRING;
  1163.       CH_STR      : out STRING ) is
  1164.     SIZE              : NATURAL;
  1165.     POS               : NATURAL;
  1166.     CURRENT_SEGMENT   : STR_SEGMENT;
  1167.   begin
  1168.     if CH_STR'LENGTH > STR.NUM_CHAR then
  1169.       SIZE := STR.NUM_CHAR;
  1170.     else
  1171.       SIZE := CH_STR'LENGTH;
  1172.     end if;
  1173.     if SIZE = 0 then
  1174.       return;
  1175.     elsif SIZE <= SEGMENT_SIZE-STR.FIRST+1 then
  1176.       --Requested string is completely within the head segment.
  1177.       CH_STR(1..SIZE) := STR.HEAD.SEGMENT(STR.FIRST..STR.FIRST+SIZE-1);
  1178.  
  1179.     else
  1180.       --Requested string is split across several segments
  1181.         --Get head segment.
  1182.         POS := SEGMENT_SIZE-STR.FIRST+1;
  1183.         CH_STR(1..POS) := STR.HEAD.SEGMENT(STR.FIRST..SEGMENT_SIZE);
  1184.  
  1185.         --Copy over the middle segments in their entirety.
  1186.         CURRENT_SEGMENT := STR.HEAD.NEXT_SEGMENT;
  1187.         while POS+(SEGMENT_SIZE) <= SIZE loop
  1188.           CH_STR(POS+1..POS+(SEGMENT_SIZE)) := CURRENT_SEGMENT.SEGMENT;
  1189.           CURRENT_SEGMENT := CURRENT_SEGMENT.NEXT_SEGMENT;
  1190.           POS             := POS + SEGMENT_SIZE;
  1191.         end loop;
  1192.  
  1193.         --Get tailing segment, if necessary.
  1194.         if POS < SIZE then
  1195.           CH_STR(POS+1..SIZE) := 
  1196.                           CURRENT_SEGMENT.SEGMENT(1..SIZE-POS);
  1197.         end if;
  1198.     end if;
  1199.   end CONVERT_TO_STRING;
  1200.  
  1201.  
  1202.   -- This function is used to check for separators which are two characters in
  1203.   -- in length.  A true value is returned only if the 1st two characters of the
  1204.   -- dynamic string match the two character parameters.  The dynamic string is
  1205.   -- not modified.
  1206.   function LOOKAHEAD(STR: DYNAMIC_STRING;
  1207.                      FIRST_CH: CHARACTER;
  1208.                      NEXT_CH : CHARACTER) return boolean is
  1209.   begin
  1210.     if STR.NUM_CHAR <= 1 then
  1211.       return FALSE;
  1212.     elsif STR.HEAD.SEGMENT(STR.FIRST) /= FIRST_CH then
  1213.       return FALSE;
  1214.     else
  1215.        if STR.FIRST /= SEGMENT_SIZE then
  1216.          return STR.HEAD.SEGMENT(STR.FIRST+1)    = NEXT_CH;
  1217.        else
  1218.          return STR.HEAD.NEXT_SEGMENT.SEGMENT(1) = NEXT_CH;
  1219.        end if;
  1220.     end if;
  1221.   end LOOKAHEAD;
  1222.  
  1223.  
  1224.  
  1225.  
  1226.  
  1227.  
  1228.   -- This procedure reads the Ith character within a dynamic string, where
  1229.   -- 0 < I <= LENGTH(string).  If I <= 0, a constraint error is raised; if
  1230.   -- I > LENGTH(string) an END_OF_STRING error is raised.  The dynamic
  1231.   -- string is unmodified.
  1232.   procedure READ
  1233.     (CHAR : out CHARACTER;
  1234.      ITH  : in POSITIVE;
  1235.      STR  : in DYNAMIC_STRING) is
  1236.     POS                : NATURAL;
  1237.     CURRENT_SEGMENT    : STR_SEGMENT;
  1238.   begin
  1239.     if ITH > STR.NUM_CHAR then
  1240.       raise END_OF_STRING;
  1241.  
  1242.     elsif ITH <= SEGMENT_SIZE - STR.FIRST + 1 then
  1243.       --The character is positioned within the head segment so
  1244.       --retrieval is immediate.
  1245.       CHAR := STR.HEAD.SEGMENT( STR.FIRST+ITH-1 );
  1246.  
  1247.     else
  1248.       --The character is positioned in a segment other than the
  1249.       --head so the correct segment must be found prior to retrieval.
  1250.       POS         := ITH - (SEGMENT_SIZE-STR.FIRST+1);
  1251.       CURRENT_SEGMENT := STR.HEAD.NEXT_SEGMENT;
  1252.       while POS > SEGMENT_SIZE loop
  1253.         POS             := POS - SEGMENT_SIZE;
  1254.         CURRENT_SEGMENT := CURRENT_SEGMENT.NEXT_SEGMENT;
  1255.       end loop;
  1256.       CHAR := CURRENT_SEGMENT.SEGMENT(POS);
  1257.     end if;
  1258.   end READ;
  1259.  
  1260.  
  1261.   -- This function compares two dynamic strings on a character by character
  1262.   -- basis.  Truth is returned only if the two string are of equal length
  1263.   -- and characters at each position are equal.
  1264.   function "="
  1265.     (LEFT  : DYNAMIC_STRING;
  1266.      RIGHT : DYNAMIC_STRING)
  1267.     return BOOLEAN is
  1268.  
  1269.     LEFT_CHAR  : CHARACTER;
  1270.     RIGHT_CHAR : CHARACTER;
  1271.   begin
  1272.     if LEFT.NUM_CHAR = RIGHT.NUM_CHAR then
  1273.  
  1274.       for I in 1..LEFT.NUM_CHAR loop
  1275.         READ(LEFT_CHAR, I,  LEFT);
  1276.         READ(RIGHT_CHAR,I,  RIGHT);
  1277.         if LEFT_CHAR /= RIGHT_CHAR then
  1278.           return FALSE;
  1279.         end if;
  1280.       end loop;
  1281.       return TRUE;
  1282.     
  1283.     else
  1284.       return FALSE;
  1285.     end if;
  1286.   end "=";
  1287.  
  1288.  
  1289.   -- This procedure adds a character to the end of a dynamic string, allocating
  1290.   -- a new segment when necessary.
  1291.   procedure APPEND
  1292.     (CHAR : in CHARACTER;
  1293.      STR  : in out DYNAMIC_STRING) is
  1294.      OLD_TAIL : STR_SEGMENT;
  1295.   begin
  1296.     if STR.NUM_CHAR = 0 then
  1297.       --This is a null string.  Allocate one segment and insert a
  1298.       --character.
  1299.       if STR.HEAD = null then
  1300.         STR.HEAD := new STR_REC;
  1301.       end if;
  1302.       STR.TAIL := STR.HEAD;
  1303.       STR.FIRST:= 1;
  1304.       STR.LAST := 1;
  1305.       STR.HEAD.SEGMENT    := BLANKS;
  1306.       STR.HEAD.SEGMENT(1) := CHAR;
  1307.  
  1308.     elsif STR.LAST = SEGMENT_SIZE then
  1309.       --A new segment must be added to hold this character.
  1310.       OLD_TAIL         := STR.TAIL;
  1311.       if STR.TAIL.NEXT_SEGMENT = null then
  1312.         STR.TAIL               := new STR_REC;
  1313.         STR.TAIL.PRIOR_SEGMENT := OLD_TAIL;
  1314.       else
  1315.         STR.TAIL               := STR.TAIL.NEXT_SEGMENT;
  1316.       end if;
  1317.       STR.TAIL.SEGMENT       := BLANKS;
  1318.       STR.TAIL.SEGMENT(1)    := CHAR;
  1319.       STR.LAST               := 1;
  1320.       OLD_TAIL.NEXT_SEGMENT  := STR.TAIL;
  1321.  
  1322.     else
  1323.       --There is room in this segment for the character.
  1324.       STR.LAST := STR.LAST+1;
  1325.       STR.TAIL.SEGMENT(STR.LAST) := CHAR;
  1326.  
  1327.     end if;
  1328.  
  1329.     STR.NUM_CHAR := STR.NUM_CHAR+1;
  1330.   end APPEND;
  1331.  
  1332.  
  1333.  
  1334.   -- This procedure uses the APPEND character procedure to add a character
  1335.   -- string to the end of a dynamic string.
  1336.   procedure APPEND
  1337.     (CHAR_STRING : in STRING;
  1338.      STR  : in out DYNAMIC_STRING) is
  1339.   begin
  1340.     for I in 1..CHAR_STRING'LENGTH loop
  1341.       APPEND(CHAR_STRING(I), STR);
  1342.     end loop;
  1343.   end APPEND;
  1344.  
  1345.  
  1346.   -- This procedure uses the APPEND charater procedure to add one dynamic 
  1347.   -- string to the end of another.  Characters are appended one at a time.
  1348.   -- Only the string being appended to is modified.
  1349.   procedure APPEND
  1350.     (D_STRING : in DYNAMIC_STRING;
  1351.      STR      : in out DYNAMIC_STRING) is
  1352.     
  1353.     CHAR : CHARACTER;
  1354.   begin
  1355.     for I in 1..D_STRING.NUM_CHAR loop
  1356.       READ(CHAR, I, D_STRING);
  1357.       APPEND(CHAR, STR);
  1358.     end loop;
  1359.   end APPEND;
  1360.  
  1361.  
  1362.   -- This procedure transfers the value of a character string to a dynamic
  1363.   -- string, replacing the original value of the dynamic string.
  1364.   procedure CONVERT_TO_DYNAMIC
  1365.     (CHAR_STRING : in STRING;
  1366.      STR  : in out DYNAMIC_STRING) is
  1367.   begin
  1368.     RESET(STR);
  1369.     APPEND(CHAR_STRING, STR);
  1370.   end CONVERT_TO_DYNAMIC;
  1371.  
  1372.   
  1373.   
  1374.   -- This procedure removes a token from the front of a dynamic string and
  1375.   -- also removes the token's separator.  Both the token and the separator
  1376.   -- are returned to the caller.  The separators are "  ();:\,  ", blank, and
  1377.   -- the comment delimiter --.  Extraneous blanks are ignored.  A semicolon is
  1378.   -- returned as the separator when the token is terminated by the end of string  -- An END_OF_STRING exception is raised only when the dynamic string from
  1379.   -- which the token is being extracted is already empty whe GET_TOKEN is called
  1380.   procedure GET_TOKEN
  1381.      (STR      : in out DYNAMIC_STRING;
  1382.       TOKEN    : out    DYNAMIC_STRING;
  1383.       SEPARATOR: out CHARACTER) is
  1384.      CHAR : CHARACTER;
  1385.   begin
  1386.     RESET(TOKEN);
  1387.     if EMPTY(STR) then
  1388.       raise END_OF_STRING;
  1389.     end if;
  1390.  
  1391.     --Get the next token and protect against end of string.
  1392.     begin
  1393.       --First skip all leading blanks
  1394.       READ(CHAR, STR);
  1395.       while CHAR = ' ' loop
  1396.         DELETE_CHAR(STR);
  1397.         READ(CHAR, STR);
  1398.       end loop;
  1399.  
  1400.       --Now copy characters until a separator is found.
  1401.       while CHAR /= ' ' and CHAR /= ',' and
  1402.             CHAR /= ':' and CHAR /= '\' and
  1403.             CHAR /= ';' and CHAR /= ')' and
  1404.             CHAR /= '(' and
  1405.             not LOOKAHEAD(STR, '-', '-')       loop
  1406.         APPEND(CHAR, TOKEN);
  1407.         DELETE_CHAR(STR);
  1408.         READ(CHAR, STR);
  1409.       end loop;
  1410.  
  1411.       --Gobble up the separator;
  1412.       SEPARATOR := CHAR;
  1413.       DELETE_CHAR(STR);
  1414.       if SEPARATOR = '-' then
  1415.         DELETE_CHAR(STR);
  1416.  
  1417.       else
  1418.       --Gobble up redundant blanks
  1419.         while not EMPTY(STR) loop
  1420.           READ(CHAR, STR);
  1421.           if CHAR = ' ' then
  1422.             DELETE_CHAR(STR);
  1423.           else
  1424.             exit;
  1425.           end if;
  1426.         end loop;
  1427.         if (SEPARATOR = ' ') and
  1428.            (CHAR = ',' or CHAR = ';'  or CHAR = ')'  or
  1429.             CHAR = '\' or CHAR = '('  or CHAR = ':') then
  1430.           SEPARATOR := CHAR;
  1431.           DELETE_CHAR(STR);
  1432.         end if;
  1433.       end if;
  1434.  
  1435.  
  1436.     exception
  1437.       when END_OF_STRING => SEPARATOR := ';';  --end of string is o.k. here.
  1438.     end;
  1439.  
  1440.   end GET_TOKEN;
  1441.  
  1442.   -- This function counts the number of non-empty tokens within a dynamic 
  1443.   -- string.  The string is unmodified.
  1444.   function NUM_OF_TOKENS (DYN_STR : DYNAMIC_STRING) return NATURAL is
  1445.  
  1446.     COUNT              : NATURAL := 0;
  1447.     TEMP_STR, TOKEN : DYNAMIC_STRING;
  1448.     SEPARATOR       : CHARACTER;
  1449.  
  1450.   begin
  1451.     ASSIGN(TEMP_STR, DYN_STR);
  1452.     loop
  1453.       GET_TOKEN(TEMP_STR, TOKEN, SEPARATOR); -- Raises exception at EOL.
  1454.       if not EMPTY(TOKEN) then
  1455.         COUNT := COUNT + 1;
  1456.       end if;
  1457.     end loop;
  1458.  
  1459.   exception
  1460.     when END_OF_STRING =>
  1461.       return (COUNT);
  1462.   end;
  1463.  
  1464.  
  1465. end STR_PACK;
  1466. ::::::::::::::
  1467. unpage.text
  1468. ::::::::::::::
  1469. with TEXT_IO; use TEXT_IO;
  1470. procedure UNPAGE is
  1471.   MAX_STRING   : constant INTEGER := 255;
  1472.   NEW_FILE     : FILE_TYPE;
  1473.   PAGED_FILE   : FILE_TYPE;
  1474.   LINE         : STRING(1..MAX_STRING);
  1475.   LAST         : INTEGER;
  1476.  
  1477.   procedure WRITE_ERROR
  1478.     (PART1 : in STRING;
  1479.      PART2 : in STRING) is
  1480.   begin
  1481.     NEW_LINE;
  1482.     PUT("**ERROR**");
  1483.     PUT(PART1);
  1484.     PUT(PART2);
  1485.     NEW_LINE;
  1486.   end WRITE_ERROR;
  1487.  
  1488.  
  1489.  
  1490.   procedure OPEN_INPUT is
  1491.     TAB_NAME  : STRING(1..MAX_STRING);
  1492.   begin
  1493.     NEW_LINE;
  1494.     PUT("ENTER NAME OF PAGE-CATENATED FILE =>");
  1495.     GET_LINE(TAB_NAME, LAST);
  1496.     OPEN(PAGED_FILE, IN_FILE, TAB_NAME(1..LAST));
  1497.   end;
  1498.  
  1499.  
  1500.   function FILE_NAME_LINE return BOOLEAN is
  1501.   begin
  1502.     if LAST >= 10 and then LINE(1..10) = "::::::::::" then
  1503.       return TRUE;
  1504.     else
  1505.       return FALSE;
  1506.     end if;
  1507.   end FILE_NAME_LINE;
  1508.  
  1509.  
  1510.   procedure OPEN_NEW_FILE is
  1511.   begin
  1512.     GET_LINE(PAGED_FILE, LINE, LAST);
  1513.     PUT_LINE(LINE(1..LAST));
  1514.     if IS_OPEN(NEW_FILE) then
  1515.       CLOSE(NEW_FILE);
  1516.     end if;
  1517.     CREATE (NEW_FILE, OUT_FILE, LINE(1..LAST));
  1518.     --Skip over the trailing ":::::::::::::" line
  1519.     GET_LINE(PAGED_FILE, LINE, LAST);
  1520.   end OPEN_NEW_FILE;
  1521.  
  1522.  
  1523.  
  1524. begin
  1525.  
  1526.   --Prompt user for paged file name. Then get name of first file to be processed
  1527.   OPEN_INPUT;
  1528.  
  1529.   GET_LINE(PAGED_FILE, LINE, LAST);
  1530.   if FILE_NAME_LINE then
  1531.     OPEN_NEW_FILE;
  1532.     loop
  1533.         GET_LINE(PAGED_FILE, LINE, LAST);
  1534.         if FILE_NAME_LINE then
  1535.           CLOSE(NEW_FILE);
  1536.           OPEN_NEW_FILE;
  1537.         else
  1538.           PUT_LINE(NEW_FILE, LINE(1..LAST));
  1539.         end if;
  1540.     end loop;
  1541.   else
  1542.     WRITE_ERROR("FILE IS NOT IN PAGED FORMAT, EXECUTION ABORTED", ".");
  1543.   end if;
  1544.  
  1545. exception
  1546.   when END_ERROR => CLOSE(NEW_FILE);
  1547.                     CLOSE(PAGED_FILE);
  1548. end UNPAGE;
  1549.  
  1550.