home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / OUT18.ZIP / OUTINDEX.INC < prev    next >
Encoding:
Text File  |  1986-04-29  |  11.1 KB  |  395 lines

  1.  
  2. (*
  3.  * outline - a simple "outline" oriented document generator
  4.  *
  5.  * outindex.inc -  this module contains all of the procedures
  6.  *                 for generating and formatting a keyword index.
  7.  *
  8.  * Author: S.H.Smith, 11-Apr-86
  9.  *
  10.  *)
  11.  
  12. {----------------------------------------------------
  13.  --                                                --
  14.  --            Index package data                  --
  15.  --                                                --
  16.  ----------------------------------------------------}
  17.  
  18. type phrase_ptr = ^phrase_string;
  19.      phrase_string = string[45];    {longest phrase that can be indexed}
  20.  
  21. type page_num = 0..255;          {legal page numbers}
  22.  
  23. const max_pages = 20;               {number of pages on which a phrase
  24.                                      can be used}
  25.  
  26. type page_list_index = 0..max_pages;
  27. type page_list = array [page_list_index] of page_num;
  28.  
  29. type index_node = ^index_node_rec;
  30.      index_node_rec = record
  31.         word:    phrase_ptr;        {pointer to the word or phrase}
  32.         count:   page_list_index;   {number of pages where word is used}
  33.         pages:   page_list;         {list of pages where word is used}
  34.         higher:  index_node;        {all higher sort order words}
  35.         lower:   index_node;        {all lower sort order words}
  36.      end;
  37.  
  38.  
  39. var index_root: index_node;         {the root of the index}
  40.  
  41. var longest_word: integer;          {the longest word in the index}
  42.  
  43.  
  44.  
  45. {----------------------------------------------------
  46.  --                                                --
  47.  --        Private internal procedures             --
  48.  --                                                --
  49.  ----------------------------------------------------}
  50.  
  51. function string_save (word: phrase_string): phrase_ptr;
  52.    {-- allocate space for a phrase on the heap and
  53.     -- return a pointer to it.  allocates only enough
  54.     -- space as is needed.}
  55. var
  56.    buf:  phrase_ptr;
  57. begin
  58.  
  59.    if length(word) > longest_word then
  60.       longest_word := length(word);
  61.  
  62.    getmem(buf,length(word)+1);
  63.    buf^ := word;
  64.    string_save := buf;
  65. end;
  66.  
  67.  
  68.  
  69. {----------------------------------------------------
  70.  --                                                --
  71.  --        Index creation and disposal             --
  72.  --                                                --
  73.  ----------------------------------------------------}
  74.  
  75. procedure init_index;
  76.    {-- initialize index for operation}
  77. begin
  78.    new(index_root);
  79.  
  80.    with index_root^ do
  81.    begin
  82.       word := string_save('Root');
  83.       count := 0;
  84.       pages[0] := 0;
  85.       higher := nil;
  86.       lower := nil;
  87.    end;
  88.  
  89.    longest_word := 0;
  90. end;
  91.  
  92.  
  93. procedure dispose_index;
  94.    {-- dispose of the current index data and release the memory
  95.     -- used by it}
  96.  
  97.    procedure dispose_node(var node: index_node);
  98.       {-- dispose of an index node and all of the
  99.        -- subordinate nodes; sets the node to nil}
  100.    begin
  101.       if node <> nil then
  102.       begin
  103.          dispose_node(node^.higher);
  104.          dispose_node(node^.lower);
  105.          freemem(node^.word,length(node^.word^)+1);
  106.          dispose(node);
  107.          node := nil;
  108.       end;
  109.    end;
  110.  
  111. begin
  112.    dispose_node(index_root);
  113. end;
  114.  
  115.  
  116.  
  117. {----------------------------------------------------
  118.  --                                                --
  119.  --        Index building                          --
  120.  --                                                --
  121.  ----------------------------------------------------}
  122.  
  123. procedure index_word (word: phrase_string; on_page: page_num);
  124.    {-- associate a word or phrase with a page number}
  125.  
  126.    procedure index_node (var node: index_node);
  127.       {-- search for and update the proper node}
  128.  
  129.    begin
  130.       if node = nil then           {create a new node, if needed}
  131.       begin
  132.          new(node);
  133.          node^.word := string_save(word);
  134.          node^.count := 1;
  135.          node^.pages[1] := on_page;
  136.          node^.higher := nil;
  137.          node^.lower := nil;
  138.       end
  139.       else
  140.  
  141.       if word = node^.word^ then   {if the phrase has been found, update its
  142.                                     page list with on_page and exit}
  143.       begin
  144.          if (node^.pages[node^.count] <> on_page) then
  145.             if node^.count < max_pages then
  146.                node^.count := node^.count + 1
  147.             else
  148.                on_page := 255;     {too many page references}
  149.  
  150.          node^.pages[node^.count] := on_page;
  151.       end
  152.       else
  153.  
  154.       if word > node^.word^ then
  155.          index_node(node^.higher)    {search up the higher branch}
  156.       else
  157.          index_node(node^.lower);    {search down the lower branch}
  158.    end;
  159.  
  160. begin
  161.    word[1] := upcase(word[1]);    {force first word of all index entries
  162.                                    to be upper case}
  163.  
  164.    index_node(index_root);        {search for and update a node, starting
  165.                                    at the root of the index}
  166. end;
  167.  
  168.  
  169.  
  170. procedure index_line (line: anystring; on_page: page_num);
  171.    {-- divide a line into words and index each one}
  172. var
  173.    i:         integer;
  174.    c:         char;
  175.    w:         phrase_string;
  176.    {prev_word: phrase_string;}
  177.  
  178.    procedure index_next;
  179.    begin
  180.  
  181.       if length(w) > 1 then     {skip single character words}
  182.       begin
  183.  
  184.          case w[length(w)] of
  185.             '0'..'9':           {skip over numbers at both ends}
  186.                if w[1] in ['0'..'9'] then
  187.                   w := '';
  188.             '-':                {skip over dashes at both ends}
  189.                if w[1] = '-' then
  190.                   w := '';
  191.             else ;              {include all others}
  192.          end;
  193.  
  194.          if (w <> '') {and (prev_word <> '')} then
  195.             index_word({prev_word+' '+}w,on_page);
  196.  
  197.          {prev_word := w;}
  198.       end;
  199.  
  200.       w := '';
  201.    end;
  202.  
  203. begin
  204.    {prev_word := '';}
  205.    line := line + ' ';    {make sure that the line ends with a
  206.                            delimiter - this simplifies the scanner}
  207.  
  208.    w := '';
  209.    for i := 1 to length(line) do
  210.    begin
  211.       c := line[i];
  212.  
  213.       case c of
  214.          '0'..'9','A'..'Z','a'..'z','-','_','''':   {collect words}
  215.             w := w + c;
  216.  
  217.          '.':                           {allow . only within words}
  218.             if (line[i+1] in ['A'..'Z','a'..'z','0'..'9','-','_']) and
  219.                (w > '') then
  220.                   w := w + c
  221.             else
  222.                   index_next;
  223.  
  224.          else
  225.             index_next;
  226.       end;
  227.  
  228.    end;
  229.  
  230. end;
  231.  
  232.  
  233.  
  234. procedure index_text_lines(var sec:    section_ptr);
  235.                                  {index the text line portion
  236.                                   of a section of the outline}
  237. var
  238.    i:       integer;
  239.  
  240. begin
  241.  
  242.    with sec^ do
  243.    begin
  244.       write(con,'.');
  245.       index_word(title,onpage);     {index the title}
  246.       index_line(title,onpage);     {index the words in the title}
  247.  
  248.       for i := 1 to max_text do     {index the words in the text}
  249.          if text^[i] <> '' then
  250.             if not (text^[i][1] in ['@','&']) then
  251.                index_line(text^[i],onpage);
  252.    end;
  253.  
  254. end;
  255.  
  256.  
  257.  
  258. {----------------------------------------------------
  259.  --                                                --
  260.  --        Index output formatting                 --
  261.  --                                                --
  262.  ----------------------------------------------------}
  263.  
  264. procedure output_index (var tofile: textfile);
  265.    {-- prepare and output the index to a text file}
  266.  
  267. var
  268.    outpos:  integer;
  269.    pletter: char;
  270.  
  271.  
  272.    procedure output_node (node: index_node);
  273.       {-- format and output a node in the index; recursively
  274.        -- outputs all higher and lower branching nodes}
  275.  
  276.       procedure output_number(n: page_num);
  277.          {-- output a single page number and adjust output position}
  278.       begin
  279.          case n of
  280.             0..9:   outpos := outpos + 1;
  281.             10..99: outpos := outpos + 2;
  282.             else    outpos := outpos + 3;
  283.          end;
  284.  
  285.          if n = 255 then          {if more references than can be stored}
  286.             write(tofile,'***')   {print *** to indicate more}
  287.          else
  288.             write(tofile, n);     {otherwise print the actual page number}
  289.       end;
  290.  
  291.       procedure output_node_page_list;
  292.          {-- output the page list for a single node}
  293.       var
  294.          i:       page_list_index;
  295.          prev:    page_list_index;
  296.  
  297.       begin
  298.          prev := 0;
  299.          i := 1;
  300.  
  301.          with node^ do
  302.          while i <= count do
  303.          begin
  304.             prev := i;         {locate page number ranges}
  305.  
  306.             while (i < count) and (pages[i]+1 = pages[i+1]) do
  307.                i := i + 1;
  308.  
  309.             if prev = i then       {output single page number}
  310.                output_number(pages[i])
  311.             else
  312.             begin                  {output a range of page numbers}
  313.                output_number(pages[prev]);
  314.                write(tofile,'-');
  315.                outpos := outpos + 1;
  316.                output_number(pages[i]);
  317.             end;
  318.  
  319.             if i < node^.count then
  320.             begin                 {insert , delimiter if needed}
  321.                write(tofile,', ');
  322.                outpos := outpos + 2;
  323.  
  324.                if (outpos > 70) then
  325.                begin              {start a new line if needed}
  326.                   writeln(tofile);
  327.                   write(tofile,'':longest_word+8);
  328.                   outpos := longest_word+8;
  329.                   prnline := prnline + 1;
  330.                end;
  331.             end;
  332.  
  333.             i := i + 1;
  334.          end;
  335.  
  336.          writeln(tofile);
  337.          prnline := prnline + 1;
  338.       end;
  339.  
  340.  
  341.    begin   {output_node}
  342.  
  343.       if keypressed then
  344.          exit;
  345.  
  346.       if node <> nil then
  347.       begin
  348.          output_node(node^.lower);      {output all lower words}
  349.  
  350.          if node^.count > 0 then
  351.          begin
  352.             if break_into_pages and (prnline > minlines) then
  353.                if (prnline + 4) > pagelen then
  354.                begin
  355.                   write(tofile, ^L);  {generate a formfeed if this section will
  356.                                        not fit completely on the current page}
  357.                   prnline := 1;
  358.                end;
  359.  
  360.             check_page_header(tofile, index_format, document);
  361.  
  362.             if pletter <> node^.word^[1] then
  363.             begin                       {start new section for each letter}
  364.                pletter := node^.word^[1];
  365.                writeln(tofile);
  366.                writeln(tofile,pletter);
  367.                prnline := prnline + 2;
  368.             end;
  369.  
  370.             write(tofile,'   ',node^.word^);
  371.             outpos := 3 + length(node^.word^);
  372.  
  373.             for outpos := outpos+1 to longest_word+8 do
  374.                if odd(outpos) then      {output the word and ...s}
  375.                   write(tofile,'.')
  376.                else
  377.                   write(tofile,' ');
  378.  
  379.             output_node_page_list;
  380.          end;
  381.  
  382.          output_node(node^.higher);     {output all higher words}
  383.       end;
  384.    end;
  385.  
  386. begin
  387.    pletter := #0;
  388.    if odd(longest_word) then
  389.       longest_word := longest_word + 1;
  390.  
  391.    output_node(index_root);
  392. end;
  393.  
  394.  
  395.