home *** CD-ROM | disk | FTP | other *** search
/ Mega CD-ROM 1 / megacd_rom_1.zip / megacd_rom_1 / GNUISH / MKINF10.ZIP / MAKEINFO.D < prev    next >
Text File  |  1990-11-01  |  59KB  |  2,484 lines

  1. /* Makeinfo -- convert texinfo format files into info files
  2.  
  3.    Copyright (C) 1987 Free Software Foundation, Inc.
  4.  
  5.    This file is part of GNU Info.
  6.  
  7.    Makeinfo is distributed in the hope that it will be useful,
  8.    but WITHOUT ANY WARRANTY.  No author or distributor accepts
  9.    responsibility to anyone for the consequences of using it or for
  10.    whether it serves any particular purpose or works at all, unless he
  11.    says so in writing.  Refer to the GNU Emacs General Public License
  12.    for full details.
  13.  
  14.    Everyone is granted permission to copy, modify and redistribute
  15.    Makeinfo, but only under the conditions described in the GNU Emacs
  16.    General Public License.   A copy of this license is supposed to
  17.    have been given to you along with GNU Emacs so you can know your
  18.    rights and responsibilities.  It should be in a file named COPYING.
  19.    Among other things, the copyright notice and this notice must be
  20.    preserved on all copies.  */
  21.  
  22. /* MS-DOS port (c) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
  23.    This port is also distributed under the terms of the
  24.    GNU General Public License as published by the
  25.    Free Software Foundation.
  26.  
  27.    Please note that this file is not identical to the
  28.    original GNU release, you should have received this
  29.    code as patch to the official release.
  30.  
  31.    $Header: e:/gnu/info/RCS/makeinfo.d 0.1.1.1 90/10/05 11:25:31 tho Exp $
  32.  */
  33.  
  34. /* cont'd from makeinfo.c  */
  35.  
  36. /*===(cut here)===*/
  37.  
  38. /* **************************************************************** */
  39. /*                                    */
  40. /*            Converting the File                 */
  41. /*                                    */
  42. /* **************************************************************** */
  43.  
  44. /* Convert the file named by NAME.  The output is saved on the file
  45.    named as the argument to the @setfilename command. */
  46. VOID
  47. convert (name)
  48.      char *name;
  49. {
  50.   char *real_output_filename, *expand_filename (), *filename_part ();
  51.   init_tag_table ();
  52.   init_indices ();
  53.   init_internals ();
  54.   init_paragraph ();
  55.  
  56.   if (!find_and_load (name))
  57.     {
  58.       /* For some reason, the file couldn't be loaded.  Print a message
  59.      to that affect, and split. */
  60.       fs_error (name);
  61.       return;
  62.     }
  63.   else
  64.     input_filename = savestring (name);
  65.  
  66.   /* Search this file looking for the special string which starts conversion.
  67.      Once found, we may truly begin. */
  68.  
  69.   input_text_offset = search_forward ("@setfilename", 0);
  70.   if (input_text_offset < 0)
  71.     {
  72.       error ("No `@setfilename' found in `%s'", name);
  73.       goto finished;
  74.     }
  75.   else
  76.     input_text_offset += strlen ("@setfilename");
  77.  
  78.   get_until ("\n", &output_filename);    /* no braces expected. */
  79.   canon_white (output_filename);
  80.  
  81.   printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  82.   real_output_filename = expand_filename (output_filename, name);
  83.   output_stream = fopen (real_output_filename, "w");
  84.   if (output_stream == NULL)
  85.     {
  86.       fs_error (real_output_filename);
  87.       goto finished;
  88.     }
  89.  
  90.   /* Make the displayable filename from output_filename.  Only the root
  91.      portion of the filename need be displayed. */
  92.   pretty_output_filename = filename_part (output_filename);
  93.  
  94.   /* For this file only, count the number of newlines from the top of
  95.      the file to here.  This way, we keep track of line numbers for
  96.      error reporting.  Line_number starts at 1, since the user isn't
  97.      zero-based. */
  98.   {
  99.     LONG temp = 0;
  100.     line_number = 1;
  101.     while (temp != input_text_offset)
  102.       if (input_text[temp++] == '\n')
  103.     line_number++;
  104.   }
  105.  
  106.   add_word_args ("Info file %s, produced by Makeinfo, -*- Text -*-\n\
  107. from input file %s.\n", output_filename, input_filename);
  108.   close_paragraph ();
  109.  
  110.   reader_loop ();
  111.  
  112. finished:
  113.   close_paragraph ();
  114.   flush_file_stack ();
  115.   if (output_stream != NULL)
  116.     {
  117.       output_pending_notes ();
  118.       free_pending_notes ();
  119.       if (tag_table != NULL)
  120.     {
  121.       tag_table = (TAG_ENTRY *) reverse_list (tag_table);
  122.       write_tag_table ();
  123.     }
  124.  
  125.       fclose (output_stream);
  126.  
  127.       /* If validating, then validate the entire file right now. */
  128.       if (validating)
  129.     validate_file (real_output_filename, tag_table);
  130.  
  131.       /* This used to test  && !errors_printed.
  132.      But some files might have legit warnings.  So split anyway.  */
  133.       if (splitting)
  134.     split_file (real_output_filename, 0);
  135.     }
  136. }
  137.  
  138. VOID
  139. free_and_clear (pointer)
  140.      char **pointer;
  141. {
  142.   if ((*pointer) != (char *) NULL)
  143.     {
  144.       free (*pointer);
  145.       *pointer = (char *) NULL;
  146.     }
  147. }
  148.  
  149.  /* Initialize some state. */
  150. VOID
  151. init_internals ()
  152. {
  153.   free_and_clear (¤t_node);
  154.   free_and_clear (&output_filename);
  155.   free_and_clear (&command);
  156.   free_and_clear (&input_filename);
  157.   free_node_references ();
  158.   init_insertion_stack ();
  159.   init_brace_stack ();
  160.   command_index = 0;
  161.   in_menu = 0;
  162. }
  163.  
  164. VOID
  165. init_paragraph ()
  166. {
  167.   free_and_clear (&output_paragraph);
  168.   output_paragraph = xmalloc (paragraph_buffer_len);
  169.   output_position = 0;
  170.   output_paragraph[0] = '\0';
  171.   output_paragraph_offset = 0;
  172.   output_column = 0;
  173.   paragraph_is_open = false;
  174.   current_indent = 0;
  175. }
  176.  
  177. /* Okay, we are ready to start the conversion.  Call the reader on
  178.    some text, and fill the text as it is output.  Handle commands by
  179.    remembering things like open braces and the current file position on a
  180.    stack, and when the corresponding close brace is found, you can call
  181.    the function with the proper arguments. */
  182. VOID
  183. reader_loop ()
  184. {
  185.   int character;
  186.   boolean done = false;
  187.   int dash_count = 0;
  188.  
  189.   while (!done)
  190.     {
  191.       if (input_text_offset >= size_of_input_text)
  192.     {
  193.       if (filestack)
  194.         {
  195.           free (input_filename);
  196.           free (input_text);
  197.           popfile ();
  198.         }
  199.       else
  200.         break;
  201.     }
  202.       character = curchar ();
  203.  
  204.       if (character == '-')
  205.     {
  206.       dash_count++;
  207.       if (dash_count == 3)
  208.         {
  209.           input_text_offset++;
  210.           continue;
  211.         }
  212.     }
  213.       else
  214.     {
  215.       dash_count = 0;
  216.     }
  217.  
  218.       if (character == '\n')
  219.     {
  220.       line_number++;
  221.       if (in_menu && input_text_offset + 1 < size_of_input_text)
  222.         {
  223.           glean_node_from_menu ();
  224.         }
  225.     }
  226.  
  227.       switch (character)
  228.     {
  229.     case COMMAND_PREFIX:
  230.       read_command ();
  231.       if (strcmp (command, "bye") == 0)
  232.         {
  233.           done = true;
  234.           continue;
  235.         }
  236.       break;
  237.  
  238.     case '{':
  239.  
  240.       /* Special case.  I'm not supposed to see this character by itself.
  241.          If I do, it means there is a syntax error in the input text.
  242.          Report the error here, but remember this brace on the stack so
  243.          you can ignore its partner. */
  244.  
  245.       line_error ("Misplaced `{'");
  246.       remember_brace (misplaced_brace);
  247.  
  248.       /* Don't advance input_text_offset since this happens in
  249.          remember_brace ().
  250.          input_text_offset++;
  251.            */
  252.       break;
  253.  
  254.     case '}':
  255.       pop_and_call_brace ();
  256.       input_text_offset++;
  257.       break;
  258.  
  259.     default:
  260.       add_char (character);
  261.       input_text_offset++;
  262.     }
  263.     }
  264. }
  265.  
  266. /* Find the command corresponding to STRING.  If the command
  267.    is found, return a pointer to the data structure.  Otherwise
  268.    return (-1). */
  269. COMMAND *
  270. get_command_entry (string)
  271.      char *string;
  272. {
  273.   register int i;
  274.  
  275.   for (i = 0; CommandTable[i].name; i++)
  276.     if (strcmp (CommandTable[i].name, string) == 0)
  277.       return (&CommandTable[i]);
  278.  
  279.   /* This command is not in our predefined command table.  Perhaps
  280.      it is a user defined command. */
  281.   for (i = 0; i < user_command_array_len; i++)
  282.     if (user_command_array[i] &&
  283.     (strcmp (user_command_array[i]->name, string) == 0))
  284.       return (user_command_array[i]);
  285.  
  286.   /* Nope, we never heard of this command. */
  287.   return ((COMMAND *) - 1);
  288. }
  289.  
  290. /* input_text_offset is right at the command prefix character.
  291.    Read the next token to determine what to do. */
  292. VOID
  293. read_command ()
  294. {
  295.   COMMAND *entry;
  296.   input_text_offset++;
  297.   free_and_clear (&command);
  298.   command = read_token ();
  299.  
  300.   entry = get_command_entry (command);
  301.   if ((LONG) entry < 0)
  302.     {
  303.       line_error ("Unknown info command `%s'", command);
  304.       return;
  305.     }
  306.  
  307.   if (entry->argument_in_braces)
  308.     remember_brace (entry->proc);
  309.  
  310.   (*(entry->proc)) (START);
  311. }
  312.  
  313. /* Return the string which invokes PROC; a pointer to a function. */
  314. char *
  315. find_proc_name (proc)
  316.      FUNCTION *proc;
  317. {
  318.   register int i;
  319.  
  320.   for (i = 0; CommandTable[i].name; i++)
  321.     if (proc == CommandTable[i].proc)
  322.       return (CommandTable[i].name);
  323.   return ("NO_NAME!");
  324. }
  325.  
  326. VOID
  327. init_brace_stack ()
  328. {
  329.   brace_stack = (BRACE_ELEMENT *) NULL;
  330. }
  331.  
  332. VOID
  333. remember_brace (proc)
  334.      FUNCTION *proc;
  335. {
  336.   if (curchar () != '{')
  337.     line_error ("@%s expected `{..}'", command);
  338.   else
  339.     input_text_offset++;
  340.   remember_brace_1 (proc, output_paragraph_offset);
  341. }
  342.  
  343. /* Remember the current output position here.  Save PROC
  344.    along with it so you can call it later. */
  345. VOID
  346. remember_brace_1 (proc, position)
  347.      FUNCTION *proc;
  348.      LONG position;
  349. {
  350.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  351.   new->next = brace_stack;
  352.   new->proc = proc;
  353.   new->pos = position;
  354.   new->line = line_number;
  355.   brace_stack = new;
  356. }
  357.  
  358. /* Pop the top of the brace stack, and call the associated function
  359.    with the args END and POS. */
  360. pop_and_call_brace ()
  361. {
  362.   BRACE_ELEMENT *temp;
  363.   FUNCTION *proc;
  364.   LONG pos;
  365.  
  366.   if (brace_stack == (BRACE_ELEMENT *) NULL)
  367.     return (line_error ("Unmatched close bracket"));
  368.  
  369.   pos = brace_stack->pos;
  370.   proc = brace_stack->proc;
  371.   temp = brace_stack->next;
  372.   free (brace_stack);
  373.   brace_stack = temp;
  374.  
  375.   return ((*proc) (END, pos, output_paragraph_offset));
  376. }
  377.  
  378. /* You call discard_braces () when you shouldn't have any braces on the stack.
  379.    I used to think that this happens for commands that don't take arguments
  380.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  381.    I only detect it at the beginning of nodes. */
  382. VOID
  383. discard_braces ()
  384. {
  385.   int temp_line_number = line_number;
  386.   char *proc_name;
  387.  
  388.   if (!brace_stack)
  389.     return;
  390.  
  391.   while (brace_stack)
  392.     {
  393.       line_number = brace_stack->line;
  394.       proc_name = find_proc_name (brace_stack->proc);
  395.       line_error ("@%s missing close brace", proc_name);
  396.       line_number = temp_line_number;
  397.       pop_and_call_brace ();
  398.     }
  399. }
  400.  
  401. get_char_len (character)
  402.      int character;
  403. {
  404.   /* Return the printed length of the character. */
  405.   int len;
  406.  
  407.   switch (character)
  408.     {
  409.     case '\t':
  410.       len = (output_column + 8) & 0xf7;
  411.       if (len > fill_column)
  412.     len = fill_column - output_column;
  413.       else
  414.     len = len - output_column;
  415.       break;
  416.  
  417.     case '\n':
  418.       len = fill_column - output_column;
  419.       break;
  420.  
  421.     default:
  422.       if (character < ' ')
  423.     len = 2;
  424.       else
  425.     len = 1;
  426.     }
  427.   return (len);
  428. }
  429.  
  430.  
  431. #ifdef MSDOS
  432.  
  433. VOID CDECL
  434. add_word_args (char *format, ...)
  435. {
  436.   char buffer[1000];
  437.   va_list arg_ptr;
  438.   va_start (arg_ptr, format);
  439.  
  440.   vsprintf (buffer, format, arg_ptr);
  441.   add_word (buffer);
  442. }
  443.  
  444. #else /* not MSDOS */
  445.  
  446. VOID
  447. add_word_args (format, arg1, arg2, arg3, arg4, arg5)
  448.      char *format;
  449. {
  450.   char buffer[1000];
  451.   sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
  452.   add_word (buffer);
  453. }
  454.  
  455. #endif /* not MSDOS */
  456.  
  457.  
  458. /* Add STRING to output_paragraph. */
  459. VOID
  460. add_word (string)
  461.      char *string;
  462. {
  463.   while (*string)
  464.     add_char (*string++);
  465. }
  466.  
  467. boolean last_char_was_newline = true;
  468. int last_inserted_character = 0;
  469.  
  470. /* Add the character to the current paragraph.  If filling_enabled is
  471.    true, then do filling as well. */
  472. VOID
  473. add_char (character)
  474.      int character;
  475. {
  476.   extern int must_start_paragraph;
  477.  
  478.   /* If we are adding a character now, then we don't have to
  479.      ignore close_paragraph () calls any more. */
  480.   if (must_start_paragraph)
  481.     {
  482.       must_start_paragraph = 0;
  483.       if (current_indent > output_column)
  484.     {
  485.       indent (current_indent - output_column);
  486.       output_column = current_indent;
  487.     }
  488.     }
  489.  
  490.   if (non_splitting_words && member (character, " \t\n"))
  491.     character = ' ' | 0x80;
  492.  
  493.   switch (character)
  494.     {
  495.  
  496.     case '\n':
  497.       if (!filling_enabled)
  498.     {
  499.       insert ('\n');
  500.  
  501.       /* Should I be flushing output here? * /
  502.           flush_output (); */
  503.  
  504.       output_column = 0;
  505.       if (!no_indent)
  506.         indent (output_column = current_indent);
  507.       break;
  508.     }
  509.       else
  510.     {
  511.       if (sentence_ender (last_inserted_character))
  512.         {
  513.           insert (' ');
  514.           output_column++;
  515.           last_inserted_character = character;
  516.         }
  517.     }
  518.  
  519.       if (last_char_was_newline)
  520.     {
  521.       close_paragraph ();
  522.       pending_indent = 0;
  523.     }
  524.       else
  525.     {
  526.       last_char_was_newline = true;
  527.       insert (' ');
  528.       output_column++;
  529.     }
  530.       break;
  531.  
  532.     default:
  533.       {
  534.     int len = get_char_len (character);
  535.     if ((character == ' ') && (last_char_was_newline))
  536.       {
  537.         if (!paragraph_is_open)
  538.           {
  539.         pending_indent++;
  540.         return;
  541.           }
  542.       }
  543.     if (!paragraph_is_open)
  544.       {
  545.         start_paragraph ();
  546.  
  547.         /* If the paragraph is supposed to be indented a certain way,
  548.            then discard all of the pending whitespace.  Otherwise, we
  549.            let the whitespace stay. */
  550.         if (!paragraph_start_indent)
  551.           indent (pending_indent);
  552.         pending_indent = 0;
  553.       }
  554.     if ((output_column += len) >= fill_column)
  555.       {
  556.         if (filling_enabled)
  557.           {
  558.         int temp = output_paragraph_offset - 1;
  559.         while (temp > 0 && output_paragraph[--temp] != '\n')
  560.           {
  561.             if (output_paragraph[temp] == ' ')
  562.               {
  563.             output_paragraph[temp++] = '\n';
  564.  
  565.             /* We have correctly broken the line where we want
  566.                to.  What we don't want is spaces following where
  567.                we have decided to break the line.  We get rid of
  568.                them. */
  569.             {
  570.               int t1 = temp;
  571.               while (t1 < output_paragraph_offset
  572.                  && whitespace (output_paragraph[t1]))
  573.                 t1++;
  574.  
  575.               if (t1 != temp)
  576.                 {
  577.                   strncpy (&output_paragraph[temp],
  578.                        &output_paragraph[t1],
  579.                        (output_paragraph_offset - t1));
  580.                   output_paragraph_offset -= (t1 - temp);
  581.                 }
  582.             }
  583.  
  584.             /* Filled, but now indent if that is right. */
  585.             if (indented_fill && current_indent)
  586.               {
  587.                 int buffer_len = ((output_paragraph_offset - temp)
  588.                           + current_indent);
  589.                 char *temp_buffer = xmalloc (buffer_len);
  590.                 int indentation = 0;
  591.  
  592.                 /* We have to shift any markers that are in
  593.                    front of the wrap point. */
  594.                 {
  595.                   register BRACE_ELEMENT *stack = brace_stack;
  596.  
  597.                   while (stack)
  598.                 {
  599.                   if (stack->pos > temp)
  600.                     stack->pos += current_indent;
  601.                   stack = stack->next;
  602.                 }
  603.                 }
  604.  
  605.                 while (indentation != current_indent)
  606.                   temp_buffer[indentation++] = ' ';
  607.  
  608.                 strncpy (&temp_buffer[current_indent],
  609.                      &output_paragraph[temp],
  610.                      buffer_len - current_indent);
  611.  
  612.                 if (output_paragraph_offset + buffer_len
  613.                 >= paragraph_buffer_len)
  614.                   {
  615.                 char *tt =
  616.                 (char *) xrealloc (output_paragraph,
  617.                       (paragraph_buffer_len += buffer_len));
  618.                 output_paragraph = tt;
  619.                   }
  620.                 strncpy (&output_paragraph[temp], temp_buffer, buffer_len);
  621.                 output_paragraph_offset += current_indent;
  622.                 free (temp_buffer);
  623.               }
  624.             output_column = 0;
  625.             while (temp != output_paragraph_offset)
  626.               output_column += get_char_len (output_paragraph[temp++]);
  627.             output_column += len;
  628.             break;
  629.               }
  630.           }
  631.           }
  632.       }
  633.     insert (character);
  634.     last_char_was_newline = false;
  635.     last_inserted_character = character;
  636.       }
  637.     }
  638. }
  639.  
  640. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  641. VOID
  642. insert (character)
  643.      int character;
  644. {
  645.   output_paragraph[output_paragraph_offset++] = character;
  646.   if (output_paragraph_offset == paragraph_buffer_len)
  647.     {
  648.       output_paragraph =
  649.     (char *) xrealloc (output_paragraph,
  650.               (paragraph_buffer_len += 100));
  651.     }
  652. }
  653.  
  654. /* Remove upto COUNT characters of whitespace from the
  655.    the current output line.  If COUNT is less than zero,
  656.    then remove until none left. */
  657. VOID
  658. kill_self_indent (count)
  659.      int count;
  660. {
  661.   /* Handle infinite case first. */
  662.   if (count < 0)
  663.     {
  664.       output_column = 0;
  665.       while (output_paragraph_offset)
  666.     {
  667.       if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  668.         output_paragraph_offset--;
  669.       else
  670.         break;
  671.     }
  672.     }
  673.   else
  674.     {
  675.       while (output_paragraph_offset && count--)
  676.     if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  677.       output_paragraph_offset--;
  678.     else
  679.       break;
  680.     }
  681. }
  682.  
  683. VOID
  684. flush_output ()
  685. {
  686.   register int i;
  687.  
  688.   if (!output_paragraph_offset)
  689.     return;
  690.   for (i = 0; i < output_paragraph_offset; i++)
  691.     output_paragraph[i] &= 0x7f;
  692.  
  693.   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
  694.   output_position += output_paragraph_offset;
  695.   output_paragraph_offset = 0;
  696. }
  697.  
  698. /* How to close a paragraph controlling the number of lines between
  699.    this one and the last one. */
  700.  
  701. /* Paragraph spacing is controlled by this variable.  It is the number of
  702.    blank lines that you wish to appear between paragraphs.  A value of
  703.    1 creates a single blank line between paragraphs. */
  704. int paragraph_spacing = 1;
  705.  
  706.  
  707. /* Close the current paragraph, leaving no blank lines between them. */
  708. VOID
  709. close_single_paragraph ()
  710. {
  711.   close_paragraph_with_lines (0);
  712. }
  713.  
  714. VOID
  715. close_paragraph_with_lines (lines)
  716.      int lines;
  717. {
  718.   int old_spacing = paragraph_spacing;
  719.   paragraph_spacing = lines;
  720.   close_paragraph ();
  721.   paragraph_spacing = old_spacing;
  722. }
  723.  
  724. /* Non-zero means that start_paragraph () MUST be called before we pay
  725.    any attention to close_paragraph () calls. */
  726. int must_start_paragraph = 0;
  727.  
  728. /* Close the currently open paragraph. */
  729. VOID
  730. close_paragraph ()
  731. {
  732.   if (paragraph_is_open && !must_start_paragraph)
  733.     {
  734.       /* Gobble up blank lines that are extra... */
  735.       register int tindex = output_paragraph_offset;
  736.       register int c;
  737.       while (tindex && ((c = output_paragraph[tindex - 1]) == ' ' || c == '\n'))
  738.     output_paragraph[--tindex] = '\n';
  739.  
  740.       output_paragraph_offset = tindex;
  741.  
  742.       insert ('\n');
  743.       {
  744.     register int i;
  745.     for (i = 0; i < paragraph_spacing; i++)
  746.       insert ('\n');
  747.       }
  748.       flush_output ();
  749.       paragraph_is_open = false;
  750.       no_indent = false;
  751.     }
  752.   last_char_was_newline = true;
  753. }
  754.  
  755. /* Begin a new paragraph. */
  756. VOID
  757. start_paragraph ()
  758. {
  759.   close_paragraph ();        /* First close existing one. */
  760.  
  761.   paragraph_is_open = true;
  762.  
  763.   if (!must_start_paragraph)
  764.     {
  765.       output_column = 0;
  766.  
  767.       /* If doing indentation, then insert the appropriate amount. */
  768.       if (!no_indent)
  769.     {
  770.       if (inhibit_paragraph_indentation || paragraph_start_indent < 0)
  771.         output_column = current_indent;
  772.       else
  773.         output_column = current_indent + paragraph_start_indent;
  774.  
  775.       indent (output_column);
  776.     }
  777.     }
  778.   else
  779.     must_start_paragraph = 0;
  780. }
  781.  
  782. /* Insert the indentation specified by AMOUNT. */
  783. VOID
  784. indent (amount)
  785.      int amount;
  786. {
  787.   while (--amount >= 0)
  788.     insert (' ');
  789. }
  790.  
  791. /* Search forward for STRING in input_text.
  792.    FROM says where where to start. */
  793. LONG
  794. search_forward (string, from)
  795.      char *string;
  796.      LONG from;
  797. {
  798.   int len = strlen (string);
  799.  
  800.   while (from < size_of_input_text)
  801.     {
  802.       if (strnicmp (input_text + from, string, len) == 0)
  803.     return (from);
  804.       from++;
  805.     }
  806.   return ((LONG) -1);
  807. }
  808.  
  809.  
  810. #ifndef MSDOS
  811. /* Whoops, Unix doesn't have stricmp, or strnicmp. */
  812.  
  813. /* Case independent string compare. */
  814. stricmp (string1, string2)
  815.      char *string1, *string2;
  816. {
  817.   char ch1, ch2;
  818.  
  819.   for (;;)
  820.     {
  821.       ch1 = *string1++;
  822.       ch2 = *string2++;
  823.       if (!(ch1 | ch2))
  824.     return (0);
  825.  
  826.       ch1 = coerce_to_upper (ch1);
  827.       ch2 = coerce_to_upper (ch2);
  828.  
  829.       if (ch1 != ch2)
  830.     return (1);
  831.     }
  832. }
  833.  
  834. /* Compare at most COUNT characters from string1 to string2.  Case
  835.    doesn't matter. */
  836. strnicmp (string1, string2, count)
  837.      char *string1, *string2;
  838. {
  839.   char ch1, ch2;
  840.  
  841.   while (count)
  842.     {
  843.       ch1 = *string1++;
  844.       ch2 = *string2++;
  845.       if (coerce_to_upper (ch1) == coerce_to_upper (ch2))
  846.     count--;
  847.       else
  848.     break;
  849.     }
  850.   return (count);
  851. }
  852. #endif /* not MSDOS */
  853.  
  854. enum insertion_type
  855. {
  856.   menu, quotation, lisp, example, smallexample, display,
  857.   itemize, format, enumerate, table, group, ifinfo,
  858.   defun, defvar, defopt, deffn, defspec, defmac,
  859.   bad_type
  860. };
  861.  
  862. char *insertion_type_names[] = {
  863.       "menu", "quotation", "lisp", "example", "smallexample", "display",
  864.       "itemize", "format", "enumerate", "table", "group", "ifinfo",
  865.       "defun", "defvar", "defopt", "deffn", "defspec", "defmac",
  866. };
  867.  
  868. int insertion_level = 0;
  869. typedef struct istack_elt
  870. {
  871.   struct istack_elt *next;
  872.   char *item_function;
  873.   int line_number;
  874.   int filling_enabled;
  875.   int indented_fill;
  876.   enum insertion_type insertion;
  877.   int inhibited;
  878. } INSERTION_ELT;
  879.  
  880. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  881.  
  882. VOID
  883. init_insertion_stack ()
  884. {
  885.   insertion_stack = (INSERTION_ELT *) NULL;
  886. }
  887.  
  888. /* Return the type of the current insertion. */
  889. enum insertion_type
  890. current_insertion_type ()
  891. {
  892.   if (!insertion_level)
  893.     return (bad_type);
  894.   else
  895.     return (insertion_stack->insertion);
  896. }
  897.  
  898. /* Return a pointer to the string which is the function
  899.    to wrap around items. */
  900. char *
  901. current_item_function ()
  902. {
  903.   if (!insertion_level)
  904.     return ((char *) NULL);
  905.   else
  906.     return (insertion_stack->item_function);
  907. }
  908.  
  909. char *
  910. get_item_function ()
  911. {
  912.   char *item_function;
  913.   get_until ("\n", &item_function);
  914.   canon_white (item_function);
  915.   return (item_function);
  916. }
  917.  
  918.  /* Push the state of the current insertion on the stack. */
  919. VOID
  920. push_insertion (type, item_function)
  921.      enum insertion_type type;
  922.      char *item_function;
  923. {
  924.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  925.  
  926.   new->item_function = item_function;
  927.   new->filling_enabled = filling_enabled;
  928.   new->indented_fill = indented_fill;
  929.   new->insertion = type;
  930.   new->line_number = line_number;
  931.   new->inhibited = inhibit_paragraph_indentation;
  932.   new->next = insertion_stack;
  933.   insertion_stack = new;
  934.   insertion_level++;
  935. }
  936.  
  937.  /* Pop the value on top of the insertion stack into the
  938.     global variables. */
  939. VOID
  940. pop_insertion ()
  941. {
  942.   INSERTION_ELT *temp = insertion_stack;
  943.   if (temp == (INSERTION_ELT *) NULL)
  944.     return;
  945.   inhibit_paragraph_indentation = temp->inhibited;
  946.   filling_enabled = insertion_stack->filling_enabled;
  947.   indented_fill = insertion_stack->indented_fill;
  948.   free_and_clear (&(temp->item_function));
  949.   insertion_stack = insertion_stack->next;
  950.   free (temp);
  951.   insertion_level--;
  952. }
  953.  
  954.  /* Return a pointer to the print name of this
  955.     enumerated type. */
  956. char *
  957. insertion_type_pname (type)
  958.      enum insertion_type type;
  959. {
  960.   if ((int) type < (int) bad_type)
  961.     return (insertion_type_names[(int) type]);
  962.   else
  963.     return ("Broken-Type in insertion_type_pname");
  964. }
  965.  
  966. /* Return the insertion_type associated with NAME.
  967.    If the type is not one of the known ones, return BAD_TYPE. */
  968. enum insertion_type
  969. find_type_from_name (name)
  970.      char *name;
  971. {
  972.   int index = 0;
  973.   while (index < (int) bad_type)
  974.     {
  975.       if (stricmp (name, insertion_type_names[index]) == 0)
  976.     return (enum insertion_type) index;
  977.       index++;
  978.     }
  979.   return (bad_type);
  980. }
  981.  
  982. VOID
  983. do_nothing ()
  984. {
  985. }
  986.  
  987. int
  988. defun_insertion (type)
  989.      enum insertion_type type;
  990. {
  991.   return (type == defun ||
  992.       type == defvar ||
  993.       type == defopt ||
  994.       type == deffn ||
  995.       type == defspec ||
  996.       type == defmac);
  997. }
  998.  
  999. /* This is where the work for all the "insertion" style
  1000.    commands is done.  A huge switch statement handles the
  1001.    various setups, and generic code is on both sides. */
  1002. VOID
  1003. begin_insertion (type)
  1004.      enum insertion_type type;
  1005. {
  1006.   int no_discard = 0;
  1007.  
  1008.   close_paragraph ();
  1009.  
  1010.   if (defun_insertion (type))
  1011.     {
  1012.       push_insertion (type, savestring (""));
  1013.       no_discard = 1;
  1014.     }
  1015.   else
  1016.     push_insertion (type, get_item_function ());
  1017.  
  1018.   filling_enabled = false;    /* the general case for insertions. */
  1019.   inhibit_paragraph_indentation = 1;
  1020.   no_indent = false;
  1021.  
  1022.   switch (type)
  1023.     {
  1024.     case menu:
  1025.       add_word ("* Menu:\n");
  1026.       in_menu++;
  1027.       discard_until ("\n");
  1028.       input_text_offset--;
  1029.       /* discard_until () has already counted the newline.  Discount it. */
  1030.       line_number--;
  1031.       return;
  1032.  
  1033.       /* I think @quotation is meant to do filling.
  1034.      If you don't want filling, then use @example. */
  1035.     case quotation:
  1036.       last_char_was_newline = 0;
  1037.       indented_fill = filling_enabled = true;
  1038.       current_indent += default_indentation_increment;
  1039.       break;
  1040.  
  1041.       /* Just like @example, but no indentation. */
  1042.     case format:
  1043.       break;
  1044.  
  1045.     case display:
  1046.     case example:
  1047.     case smallexample:
  1048.     case lisp:
  1049.       last_char_was_newline = 0;
  1050.       current_indent += default_indentation_increment;
  1051.       break;
  1052.  
  1053.     case table:
  1054.     case itemize:
  1055.       current_indent += default_indentation_increment;
  1056.       filling_enabled = indented_fill = true;
  1057.  
  1058.       /* Make things work for losers who forget the itemize syntax. */
  1059.       if (type == itemize)
  1060.     {
  1061.       if (!(*insertion_stack->item_function))
  1062.         {
  1063.           free (insertion_stack->item_function);
  1064.           insertion_stack->item_function = savestring ("*");
  1065.         }
  1066.     }
  1067.       break;
  1068.  
  1069.     case enumerate:
  1070.       inhibit_paragraph_indentation = 0;
  1071.       current_indent += default_indentation_increment;
  1072.       start_numbering (1);
  1073.       filling_enabled = indented_fill = true;
  1074.       break;
  1075.  
  1076.     case group:
  1077.       inhibit_paragraph_indentation = 0;
  1078.       break;
  1079.  
  1080.     case ifinfo:
  1081.       /* Undo whatever we just did.  This is a no-op. */
  1082.       inhibit_paragraph_indentation = 0;
  1083.       filling_enabled = insertion_stack->filling_enabled;
  1084.       indented_fill = insertion_stack->indented_fill;
  1085.       break;
  1086.  
  1087.     case defun:
  1088.     case defvar:
  1089.     case defopt:
  1090.     case deffn:
  1091.     case defspec:
  1092.     case defmac:
  1093.       inhibit_paragraph_indentation = 0;
  1094.       filling_enabled = indented_fill = true;
  1095.       current_indent += default_indentation_increment;
  1096.       break;
  1097.     }
  1098.   if (!no_discard)
  1099.     discard_until ("\n");
  1100. }
  1101.  
  1102. /* Try to end the quotation with the specified type.
  1103.    Like begin_insertion (), this uses a giant switch statement as
  1104.    well.  A big difference is that you *must* pass a valid type to
  1105.    this function, and a value of bad_type gets translated to match
  1106.    the value currently on top of the stack.  If, however, the value
  1107.    passed is a valid type, and it doesn't match the top of the
  1108.    stack, then we produce an error.  Should fix this, somewhat
  1109.    unclean. */
  1110. VOID
  1111. end_insertion (type)
  1112.      enum insertion_type type;
  1113. {
  1114.   enum insertion_type temp_type;
  1115.  
  1116.   if (!insertion_level)
  1117.     return;
  1118.  
  1119.   temp_type = current_insertion_type ();
  1120.   if (type == bad_type)
  1121.     type = temp_type;
  1122.  
  1123.   if (type != temp_type)
  1124.     {
  1125.       line_error ("Expected `%s', but saw `%s'.  Token unread",
  1126.          insertion_type_pname (temp_type), insertion_type_pname (type));
  1127.       return;
  1128.     }
  1129.   pop_insertion ();
  1130.  
  1131.   switch (type)
  1132.     {
  1133.  
  1134.     case menu:
  1135.       in_menu--;        /* no longer hacking menus. */
  1136.       break;
  1137.  
  1138.     case enumerate:
  1139.       stop_numbering ();
  1140.       current_indent -= default_indentation_increment;
  1141.       break;
  1142.  
  1143.     case group:
  1144.     case ifinfo:
  1145.     case format:
  1146.       break;
  1147.  
  1148.     default:
  1149.       current_indent -= default_indentation_increment;
  1150.       break;
  1151.     }
  1152.   close_paragraph ();
  1153. }
  1154.  
  1155. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  1156.    code that creates such boundaries, you should call discard_insertions ()
  1157.    before doing anything else.  It prints the errors for you, and cleans up
  1158.    the insertion stack. */
  1159. VOID
  1160. discard_insertions ()
  1161. {
  1162.   int real_line_number = line_number;
  1163.   while (insertion_stack)
  1164.     {
  1165.       if (insertion_stack->insertion == ifinfo)
  1166.     break;
  1167.       else
  1168.     {
  1169.       char *offender = (char *) insertion_type_pname (insertion_stack->insertion);
  1170.  
  1171.       line_number = insertion_stack->line_number;
  1172.       line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  1173.               COMMAND_PREFIX, offender);
  1174.       pop_insertion ();
  1175.     }
  1176.     }
  1177.   line_number = real_line_number;
  1178. }
  1179.  
  1180. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  1181.    which seemed reasonable.  This doesn't control the number of items,
  1182.    just the number of nested lists. */
  1183. #define max_ns 100
  1184. int number_stack[max_ns];
  1185. int number_offset = 0;
  1186. int the_current_number = 0;
  1187.  
  1188. VOID
  1189. start_numbering (at_number)
  1190.      int at_number;
  1191. {
  1192.   if (number_offset + 1 == max_ns)
  1193.     {
  1194.       line_error ("Enumeration stack overflow");
  1195.       return;
  1196.     }
  1197.   number_stack[number_offset++] = the_current_number;
  1198.   the_current_number = at_number;
  1199. }
  1200.  
  1201. VOID
  1202. stop_numbering ()
  1203. {
  1204.   the_current_number = number_stack[--number_offset];
  1205.   if (number_offset < 0)
  1206.     number_offset = 0;
  1207. }
  1208.  
  1209.  /* Place a number into the output stream. */
  1210. VOID
  1211. number_item ()
  1212. {
  1213.   char temp[10];
  1214.   sprintf (temp, "%d. ", the_current_number);
  1215.   indent (output_column += (current_indent - strlen (temp)));
  1216.   add_word (temp);
  1217.   the_current_number++;
  1218. }
  1219.  
  1220. /* The actual commands themselves. */
  1221.  
  1222. /* Commands which insert themselves. */
  1223. VOID
  1224. insert_self ()
  1225. {
  1226.   add_word (command);
  1227. }
  1228.  
  1229. /* Force line break */
  1230. VOID
  1231. cm_asterisk ()
  1232. {
  1233.   /* Force a line break in the output. */
  1234.   insert ('\n');
  1235.   indent (output_column = current_indent);
  1236. }
  1237.  
  1238. /* Insert ellipsis. */
  1239. VOID
  1240. cm_dots (arg)
  1241.      int arg;
  1242. {
  1243.   if (arg == START)
  1244.     add_word ("...");
  1245. }
  1246.  
  1247. VOID
  1248. cm_bullet (arg)
  1249.      int arg;
  1250. {
  1251.   if (arg == START)
  1252.     add_char ('*');
  1253. }
  1254.  
  1255. VOID
  1256. cm_minus (arg)
  1257.      int arg;
  1258. {
  1259.   if (arg == START)
  1260.     add_char ('-');
  1261. }
  1262.  
  1263. /* Insert "TeX". */
  1264. VOID
  1265. cm_TeX (arg)
  1266.      int arg;
  1267. {
  1268.   if (arg == START)
  1269.     add_word ("TeX");
  1270. }
  1271.  
  1272. VOID
  1273. cm_copyright (arg)
  1274.      int arg;
  1275. {
  1276.   if (arg == START)
  1277.     add_word ("(C)");
  1278. }
  1279.  
  1280. VOID
  1281. cm_code (arg)
  1282.      int arg;
  1283. {
  1284.   if (arg == START)
  1285.     add_char ('`');
  1286.   else
  1287.     add_word ("'");
  1288. }
  1289.  
  1290. VOID
  1291. cm_samp (arg)
  1292.      int arg;
  1293. {
  1294.   cm_code (arg);
  1295. }
  1296.  
  1297. VOID
  1298. cm_file (arg)
  1299.      int arg;
  1300. {
  1301.   cm_code (arg);
  1302. }
  1303.  
  1304. VOID
  1305. cm_kbd (arg)
  1306.      int arg;
  1307. {
  1308.   cm_code (arg);
  1309. }
  1310.  
  1311. VOID
  1312. cm_key (arg)
  1313.      int arg;
  1314. {
  1315. }
  1316.  
  1317.  /* Convert the character at position-1 into CTL. */
  1318. VOID
  1319. cm_ctrl (arg, position)
  1320.      int arg, position;
  1321. {
  1322.   if (arg == END)
  1323.     output_paragraph[position - 1] = CTL (output_paragraph[position - 1]);
  1324. }
  1325.  
  1326. /* Small Caps in makeinfo just does all caps. */
  1327. VOID
  1328. cm_sc (arg, start_pos, end_pos)
  1329.      int arg, start_pos, end_pos;
  1330. {
  1331.   if (arg == END)
  1332.     {
  1333.       while (start_pos < end_pos)
  1334.     {
  1335.       output_paragraph[start_pos] =
  1336.         coerce_to_upper (output_paragraph[start_pos]);
  1337.       start_pos++;
  1338.     }
  1339.     }
  1340. }
  1341.  
  1342. /* @var in makeinfo just uppercases the text. */
  1343. VOID
  1344. cm_var (arg, start_pos, end_pos)
  1345.      int arg, start_pos, end_pos;
  1346. {
  1347.   if (arg == END)
  1348.     {
  1349.       while (start_pos < end_pos)
  1350.     {
  1351.       output_paragraph[start_pos] =
  1352.         coerce_to_upper (output_paragraph[start_pos]);
  1353.       start_pos++;
  1354.     }
  1355.     }
  1356. }
  1357.  
  1358. VOID
  1359. cm_dfn (arg, position)
  1360.      int arg, position;
  1361. {
  1362.   add_char ('"');
  1363. }
  1364.  
  1365. VOID
  1366. cm_emph (arg)
  1367.      int arg;
  1368. {
  1369.   add_char ('*');
  1370. }
  1371.  
  1372. VOID
  1373. cm_strong (arg, position)
  1374.      int arg, position;
  1375. {
  1376.   cm_emph (arg);
  1377. }
  1378.  
  1379. VOID
  1380. cm_cite (arg, position)
  1381.      int arg, position;
  1382. {
  1383.   if (arg == START)
  1384.     add_word ("``");
  1385.   else
  1386.     add_word ("''");
  1387. }
  1388.  
  1389. VOID
  1390. cm_italic (arg)
  1391.      int arg;
  1392. {
  1393. }
  1394.  
  1395. VOID
  1396. cm_bold (arg)
  1397.      int arg;
  1398. {
  1399.   cm_italic (arg);
  1400. }
  1401.  
  1402. VOID
  1403. cm_roman (arg)
  1404.      int arg;
  1405. {
  1406. }
  1407.  
  1408. VOID
  1409. cm_title (arg)
  1410.      int arg;
  1411. {
  1412.   cm_italic (arg);
  1413. }
  1414.  
  1415. VOID
  1416. cm_refill ()
  1417. {
  1418. }
  1419.  
  1420. /* Prevent the argument from being split across two lines. */
  1421. VOID
  1422. cm_w (arg)
  1423.      int arg;
  1424. {
  1425.   if (arg == START)
  1426.     non_splitting_words++;
  1427.   else
  1428.     non_splitting_words--;
  1429. }
  1430.  
  1431.  
  1432. /* Explain that this command is obsolete, thus the user shouldn't
  1433.    do anything with it. */
  1434. VOID
  1435. cm_obsolete (arg)
  1436.      int arg;
  1437. {
  1438.   if (arg == START)
  1439.     warning ("The command `@%s' is obsolete", command);
  1440. }
  1441.  
  1442. /* Insert the text following input_text_offset up to the end of the line
  1443.    in a new, separate paragraph.  Directly underneath it, insert a
  1444.    line of WITH_CHAR, the same length of the inserted text. */
  1445. VOID
  1446. insert_and_underscore (with_char)
  1447.      int with_char;
  1448. {
  1449.   LONG len;
  1450.   int i, old_no_indent;
  1451.   char *temp;
  1452.  
  1453.   close_paragraph ();
  1454.   filling_enabled =  indented_fill = false;
  1455.   old_no_indent = no_indent;
  1456.   no_indent = true;
  1457.   get_rest_of_line (&temp);
  1458.  
  1459.   len = output_position;
  1460.   execute_string ("%s\n", temp);
  1461.   free (temp);
  1462.  
  1463.   len = ((output_position + output_paragraph_offset) - 1) - len;
  1464. #ifdef MSDOS
  1465.   assert (len < (1L<<16));
  1466.   for (i = 0; i < (int) len; i++)
  1467.     add_char (with_char);
  1468. #else /* not MSDOS */
  1469.   for (i = 0; i < len; i++)
  1470.     add_char (with_char);
  1471. #endif /* not MSDOS */
  1472.   insert ('\n');
  1473.   close_paragraph ();
  1474.   filling_enabled = true;
  1475.   no_indent = old_no_indent;
  1476. }
  1477.  
  1478. VOID
  1479. cm_chapter ()
  1480. {
  1481.   insert_and_underscore ('*');
  1482. }
  1483.  
  1484. VOID
  1485. cm_section ()
  1486. {
  1487.   insert_and_underscore ('=');
  1488. }
  1489.  
  1490. VOID
  1491. cm_subsection ()
  1492. {
  1493.   insert_and_underscore ('-');
  1494. }
  1495.  
  1496. VOID
  1497. cm_subsubsection ()
  1498. {
  1499.   insert_and_underscore ('.');
  1500. }
  1501.  
  1502. VOID
  1503. cm_unnumbered ()
  1504. {
  1505.   cm_chapter ();
  1506. }
  1507.  
  1508. VOID
  1509. cm_unnumberedsec ()
  1510. {
  1511.   cm_section ();
  1512. }
  1513.  
  1514. VOID
  1515. cm_unnumberedsubsec ()
  1516. {
  1517.   cm_subsection ();
  1518. }
  1519.  
  1520. VOID
  1521. cm_unnumberedsubsubsec ()
  1522. {
  1523.   cm_subsubsection ();
  1524. }
  1525.  
  1526. VOID
  1527. cm_appendix ()
  1528. {
  1529.   cm_chapter ();
  1530. }
  1531.  
  1532. VOID
  1533. cm_appendixsec ()
  1534. {
  1535.   cm_section ();
  1536. }
  1537.  
  1538. VOID
  1539. cm_appendixsubsec ()
  1540. {
  1541.   cm_subsection ();
  1542. }
  1543.  
  1544. VOID
  1545. cm_appendixsubsubsec ()
  1546. {
  1547.   cm_subsubsection ();
  1548. }
  1549.  
  1550. /* **************************************************************** */
  1551. /*                                    */
  1552. /*           Adding nodes, and making tags            */
  1553. /*                                    */
  1554. /* **************************************************************** */
  1555.  
  1556. /* Start a new tag table. */
  1557. VOID
  1558. init_tag_table ()
  1559. {
  1560.   while (tag_table != (TAG_ENTRY *) NULL)
  1561.     {
  1562.       TAG_ENTRY *temp = tag_table;
  1563.       free (temp->node);
  1564.       free (temp->prev);
  1565.       free (temp->next);
  1566.       free (temp->up);
  1567.       tag_table = tag_table->next_ent;
  1568.       free (temp);
  1569.     }
  1570. }
  1571.  
  1572. write_tag_table ()
  1573. {
  1574.   return (write_tag_table_internal (false));    /* Not indirect. */
  1575. }
  1576.  
  1577. write_tag_table_indirect ()
  1578. {
  1579.   return (write_tag_table_internal (true));
  1580. }
  1581.  
  1582. /* Write out the contents of the existing tag table.
  1583.    INDIRECT_P says how to format the output. */
  1584. VOID
  1585. write_tag_table_internal (indirect_p)
  1586.      boolean indirect_p;
  1587. {
  1588.   TAG_ENTRY *node = tag_table;
  1589.  
  1590.   close_paragraph ();
  1591.   filling_enabled = false;
  1592.   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  1593.  
  1594.   while (node != (TAG_ENTRY *) NULL)
  1595.     {
  1596. #ifdef MSDOS
  1597.       add_word_args ("Node: %s\177%ld\n", node->node, node->position);
  1598. #else /* not MSDOS */
  1599.       add_word_args ("Node: %s\177%d\n", node->node, node->position);
  1600. #endif /* not MSDOS */
  1601.       node = node->next_ent;
  1602.     }
  1603.   add_word ("\037\nEnd Tag Table\n");
  1604.   flush_output ();
  1605. }
  1606.  
  1607. char *
  1608. get_node_token ()
  1609. {
  1610.   char *string;
  1611.  
  1612.   get_until_in_line (",", &string);
  1613.  
  1614.   if (curchar () == ',')
  1615.     input_text_offset++;
  1616.  
  1617.   canon_white (string);
  1618.  
  1619.   /* Allow things like @@nodename. */
  1620.   normalize_node_name (string);
  1621.  
  1622.   return (string);
  1623. }
  1624.  
  1625. /* Given a node name in STRING, remove double @ signs, replacing them
  1626.    with just one. */
  1627. VOID
  1628. normalize_node_name (string)
  1629.      char *string;
  1630. {
  1631.   register int i, l = strlen (string);
  1632.  
  1633.   for (i = 0; i < l; i++)
  1634.     {
  1635.       if (string[i] == '@' && string[i + 1] == '@')
  1636.     {
  1637.       strncpy (string + i, string + i + 1, l - i);
  1638.       l--;
  1639.     }
  1640.     }
  1641. }
  1642.  
  1643. /* Look up NAME in the tag table, and return the associated
  1644.    tag_entry.  If the node is not in the table return NULL. */
  1645. TAG_ENTRY *
  1646. find_node (name)
  1647.      char *name;
  1648. {
  1649.   TAG_ENTRY *tag = tag_table;
  1650.  
  1651.   while (tag != (TAG_ENTRY *) NULL)
  1652.     {
  1653.       if (stricmp (tag->node, name) == 0)
  1654.     return (tag);
  1655.       tag = tag->next_ent;
  1656.     }
  1657.   return ((TAG_ENTRY *) NULL);
  1658. }
  1659.  
  1660. /* Remember NODE and associates. */
  1661. VOID
  1662. remember_node (node, prev, next, up, position, line_no, no_warn)
  1663.      char *node, *prev, *next, *up;
  1664.      LONG position;
  1665.      int line_no, no_warn;
  1666. {
  1667.   /* Check for existence of this tag already. */
  1668.   if (validating)
  1669.     {
  1670.       register TAG_ENTRY *tag = find_node (node);
  1671.       if (tag)
  1672.     {
  1673.       line_error ("Node `%s' multiply defined (%d is first definition)",
  1674.               node, tag->line_no);
  1675.       return;
  1676.     }
  1677.     }
  1678.  
  1679.   /* First, make this the current node. */
  1680.   current_node = node;
  1681.  
  1682.   /* Now add it to the list. */
  1683.   {
  1684.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  1685.     new->node = node;
  1686.     new->prev = prev;
  1687.     new->next = next;
  1688.     new->up = up;
  1689.     new->position = position;
  1690.     new->line_no = line_no;
  1691.     new->filename = node_filename;
  1692.     new->touched = 0;        /* not yet referenced. */
  1693.     new->flags = 0;
  1694.     if (no_warn)
  1695.       new->flags |= NO_WARN;
  1696.     new->next_ent = tag_table;
  1697.     tag_table = new;
  1698.   }
  1699. }
  1700.  
  1701. /* Here is a structure which associates sectioning commands with
  1702.    an integer, hopefully to reflect the `depth' of the current
  1703.    section. */
  1704. struct {
  1705.   char *name;
  1706.   int level;
  1707. } section_alist[] = {
  1708.   { "chapter", 1 },
  1709.   { "section", 2},
  1710.   { "subsec", 3},
  1711.   { "subsubsec", 4},
  1712.   { "unnumbered", 1},
  1713.   { "unnumberedsec", 2},
  1714.   { "unnumberedsubsec", 3},
  1715.   { "unnumberedsubsubsec", 4},
  1716.   { "appendix", 1},
  1717.   { "appendixsec", 2},
  1718.   { "appendixsubsec", 3},
  1719.   { "appendixsubsubsec", 4},
  1720.   { (char *)NULL, 0 }
  1721. };
  1722.  
  1723. /* Return an integer which identifies the type section present in TEXT. */
  1724. int
  1725. what_section (text)
  1726.      char *text;
  1727. {
  1728.   int i, j;
  1729.   char *t;
  1730.  
  1731.   for (j = 0; text[j] && whitespace (text[j]) || text[j] == '\n'; j++);
  1732.   if (text[j] != '@')
  1733.     return (-1);
  1734.  
  1735.   text = text + j + 1;
  1736.  
  1737.   /* Handle italicized sectioning commands. */
  1738.   if (*text == 'i')
  1739.     text++;
  1740.  
  1741.   for (i = 0; t = section_alist[i].name; i++)
  1742.     {
  1743.       if (strncmp (t, text, strlen (t)) == 0)
  1744.     return (section_alist[i].level);
  1745.     }
  1746.   return (-1);
  1747. }
  1748.  
  1749. /* The order is: nodename, nextnode, prevnode, upnode.
  1750.    The next, prev, and up fields can be defaulted.
  1751.    You must follow a node command which has those fields
  1752.    defaulted with a sectioning command (e.g. @chapter) giving
  1753.    the "level" of that node.  It is an error not to do so.
  1754.    The defaults come from the menu in this nodes parent. */
  1755. VOID
  1756. cm_node ()
  1757. {
  1758.   char *node, *prev, *next, *up;
  1759.   LONG new_node_pos;
  1760.   int defaulting, this_section, no_warn = 0;
  1761.   extern int already_outputting_pending_notes;
  1762.  
  1763.   if (strcmp (command, "nwnode") == 0)
  1764.     no_warn = 1;
  1765.  
  1766.   /* Get rid of unmatched brace arguments from previous commands. */
  1767.   discard_braces ();
  1768.  
  1769.   /* There also might be insertions left lying around that haven't been
  1770.      ended yet.  Do that also. */
  1771.   discard_insertions ();
  1772.  
  1773.   if (!already_outputting_pending_notes)
  1774.     {
  1775.       close_paragraph ();
  1776.       output_pending_notes ();
  1777.       free_pending_notes ();
  1778.     }
  1779.  
  1780.   filling_enabled = indented_fill = false;
  1781.   new_node_pos = output_position + 1;
  1782.  
  1783.   node = get_node_token ();
  1784.   next = get_node_token ();
  1785.   prev = get_node_token ();
  1786.   up = get_node_token ();
  1787.  
  1788.   this_section = what_section (input_text + input_text_offset);
  1789.  
  1790.   /* ??? The first \n in the following string shouldn't be there, but I have
  1791.      to revamp the @example & @group things so that they always leave a \n
  1792.      as the last character output.  Until that time, this is the only way
  1793.      I can produce reliable output. */
  1794.   no_indent = true;
  1795.   add_word_args ("\n\037\nFile: %s,  Node: %s", pretty_output_filename, node);
  1796.  
  1797.   /* Check for defaulting of this node's next, prev, and up fields. */
  1798.   defaulting = ((strlen (next) == 0) &&
  1799.         (strlen (prev) == 0) &&
  1800.         (strlen (up) == 0));
  1801.  
  1802.   /* If we are defaulting, then look at the immediately following
  1803.      sectioning command (error if none) to determine the node's
  1804.      level.  Find the node that contains the menu mentioning this node
  1805.      that is one level up (error if not found).  That node is the "Up"
  1806.      of this node.  Default the "Next" and "Prev" from the menu. */
  1807.   if (defaulting)
  1808.     {
  1809.       NODE_REF *last_ref = (NODE_REF *)NULL;
  1810.       NODE_REF *ref = node_references;
  1811.  
  1812.       if (this_section < 0)
  1813.     {
  1814.       char *polite_section_name = "chapter";
  1815.       int i;
  1816.  
  1817.       for (i = 0; section_alist[i].name; i++)
  1818.         if (section_alist[i].level == current_section + 1)
  1819.           {
  1820.         polite_section_name = section_alist[i].name;
  1821.         break;
  1822.           }
  1823.  
  1824.       line_error
  1825.         ("Node `%s' requires a sectioning command (e.g. @%s)",
  1826.          node, polite_section_name);
  1827.     }
  1828.       else
  1829.     {
  1830.       while (ref)
  1831.         {
  1832.           if (ref->section == (this_section - 1) &&
  1833.           ref->type == menu_reference &&
  1834.           stricmp (ref->node, node) == 0)
  1835.         {
  1836.           free (up);
  1837.           up = savestring (ref->containing_node);
  1838.  
  1839.           if (last_ref &&
  1840.               strcmp
  1841.               (last_ref->containing_node, ref->containing_node) == 0)
  1842.             {
  1843.               free (next);
  1844.               next = savestring (last_ref->node);
  1845.             }
  1846.  
  1847.           if (ref->next &&
  1848.               strcmp
  1849.               (ref->next->containing_node, ref->containing_node) == 0)
  1850.             {
  1851.               free (prev);
  1852.               prev = savestring (ref->next->node);
  1853.             }
  1854.           break;
  1855.         }
  1856.           last_ref = ref;
  1857.           ref = ref->next;
  1858.         }
  1859.     }
  1860.     }
  1861.  
  1862.   if (*next)
  1863.     add_word_args (",  Next: %s", next);
  1864.   
  1865.   if (*prev)
  1866.     add_word_args (",  Prev: %s", prev);
  1867.  
  1868.   if (*up)
  1869.     add_word_args (",  Up: %s", up);
  1870.  
  1871.   insert ('\n');
  1872.   close_paragraph ();
  1873.   no_indent = false;
  1874.  
  1875.   if (!*node)
  1876.     {
  1877.       line_error ("No node name specified for `@%s' command", command);
  1878.       free (node);
  1879.       free (next);
  1880.       free (prev);
  1881.       free (up);
  1882.     }
  1883.   else
  1884.     {
  1885.       if (!*next) { free (next); next = (char *)NULL; }
  1886.       if (!*prev) { free (prev); prev = (char *)NULL; }
  1887.       if (!*up) { free (up); up = (char *)NULL; }
  1888.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  1889.     }
  1890.  
  1891.   /* Change the section only if there was a sectioning command. */
  1892.   if (this_section >= 0)
  1893.     current_section = this_section;
  1894.  
  1895.   filling_enabled = true;
  1896. }
  1897.  
  1898. /* Validation of an info file.
  1899.    Scan through the list of tag entrys touching the Prev, Next, and Up
  1900.    elements of each.  It is an error not to be able to touch one of them,
  1901.    except in the case of external node references, such as "(DIR)".
  1902.  
  1903.    If the Prev is different from the Up,
  1904.    then the Prev node must have a Next pointing at this node.
  1905.  
  1906.    Every node except Top must have an Up.
  1907.    The Up node must contain some sort of reference, other than a Next,
  1908.    to this node.
  1909.  
  1910.    If the Next is different from the Next of the Up,
  1911.    then the Next node must have a Prev pointing at this node. */
  1912. VOID
  1913. validate_file (filename, tag_table)
  1914.      char *filename;
  1915.      TAG_ENTRY *tag_table;
  1916. {
  1917.   char *old_input_filename = input_filename;
  1918.   TAG_ENTRY *tags = tag_table;
  1919.  
  1920.   while (tags != (TAG_ENTRY *) NULL)
  1921.     {
  1922.       register TAG_ENTRY *temp_tag;
  1923.  
  1924.       input_filename = tags->filename;
  1925.       line_number = tags->line_no;
  1926.  
  1927.       /* If this node has a Next, then make sure that the Next exists. */
  1928.       if (tags->next)
  1929.     {
  1930.       validate (tags->next, tags->line_no, "Next");
  1931.  
  1932.       /* If the Next node exists, and there is no Up, then make
  1933.          sure that the Prev of the Next points back. */
  1934.       if (temp_tag = find_node (tags->next))
  1935.         {
  1936.           char *prev = temp_tag->prev;
  1937.           if (!prev || (stricmp (prev, tags->node) != 0))
  1938.         {
  1939.           line_error
  1940.             ("Node `%s''s Next field not pointed back to", tags->node);
  1941.           line_number = temp_tag->line_no;
  1942.           input_filename = temp_tag->filename;
  1943.           line_error
  1944.             ("This node (`%s') is the one with the bad `Prev'",
  1945.              temp_tag->node);
  1946.           input_filename = tags->filename;
  1947.           line_number = tags->line_no;
  1948.           temp_tag->flags |= PREV_ERROR;
  1949.         }
  1950.         }
  1951.     }
  1952.  
  1953.       /* Validate the Prev field if there is one, and we haven't already
  1954.      complained about it in some way.  You don't have to have a Prev
  1955.      field at this stage. */
  1956.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  1957.     {
  1958.       int valid = validate (tags->prev, tags->line_no, "Prev");
  1959.  
  1960.       if (!valid)
  1961.         tags->flags |= PREV_ERROR;
  1962.       else
  1963.         {
  1964.           /* If the Prev field is not the same as the Up field,
  1965.          then the node pointed to by the Prev field must have
  1966.          a Next field which points to this node. */
  1967.           if (tags->up && (stricmp (tags->prev, tags->up) != 0))
  1968.         {
  1969.           temp_tag = find_node (tags->prev);
  1970.           if (!temp_tag->next ||
  1971.               (stricmp (temp_tag->next, tags->node) != 0))
  1972.             {
  1973.               line_error ("Node `%s''s Prev field not pointed back to",
  1974.                   tags->node);
  1975.               line_number = temp_tag->line_no;
  1976.               input_filename = temp_tag->filename;
  1977.               line_error
  1978.             ("This node (`%s') is the one with the bad `Next'",
  1979.              temp_tag->node);
  1980.               input_filename = tags->filename;
  1981.               line_number = tags->line_no;
  1982.               temp_tag->flags |= NEXT_ERROR;
  1983.             }
  1984.         }
  1985.         }
  1986.     }
  1987.  
  1988.       if (!tags->up && (stricmp (tags->node, "Top") != 0))
  1989.     line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  1990.       else if (tags->up)
  1991.     {
  1992.       int valid = validate (tags->up, tags->line_no, "Up");
  1993.  
  1994.       /* If node X has Up: Y, then warn if Y fails to have a menu item
  1995.          or note pointing at X, if Y isn't of the form "(Y)". */
  1996.       if (valid && *tags->up != '(')
  1997.         {
  1998.           NODE_REF *nref, *tref, *list;
  1999.           NODE_REF *find_node_reference ();
  2000.  
  2001.           tref = (NODE_REF *) NULL;
  2002.           list = node_references;
  2003.  
  2004.           for (;;)
  2005.         {
  2006.           if (!(nref = find_node_reference (tags->node, list)))
  2007.             break;
  2008.  
  2009.           if (stricmp (nref->containing_node, tags->up) == 0)
  2010.             {
  2011.               if (nref->type != menu_reference)
  2012.             {
  2013.               tref = nref;
  2014.               list = nref->next;
  2015.             }
  2016.               else
  2017.             break;
  2018.             }
  2019.           list = nref->next;
  2020.         }
  2021.  
  2022.           if (!nref)
  2023.         {
  2024.           temp_tag = find_node (tags->up);
  2025.           line_number = temp_tag->line_no;
  2026.           filename = temp_tag->filename;
  2027.           if (!tref)
  2028.             line_error ("`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  2029.                 tags->node, tags->up, tags->up, tags->node);
  2030.           line_number = tags->line_no;
  2031.           filename = tags->filename;
  2032.         }
  2033.         }
  2034.     }
  2035.       tags = tags->next_ent;
  2036.     }
  2037.  
  2038.   validate_other_references (node_references);
  2039.   /* We have told the user about the references which didn't exist.
  2040.      Now tell him about the nodes which aren't referenced. */
  2041.  
  2042.   tags = tag_table;
  2043.   while (tags != (TAG_ENTRY *) NULL)
  2044.     {
  2045.       /* Special hack.  If the node in question appears to have
  2046.          been referenced more than REFERENCE_WARNING_LIMIT times,
  2047.          give a warning. */
  2048.       if (tags->touched > reference_warning_limit)
  2049.     {
  2050.       input_filename = tags->filename;
  2051.       line_number = tags->line_no;
  2052.       warning ("Node `%s' has been referenced %d times",
  2053.            tags->node, tags->touched);
  2054.     }
  2055.  
  2056.       if (tags->touched == 0)
  2057.     {
  2058.       input_filename = tags->filename;
  2059.       line_number = tags->line_no;
  2060.  
  2061.       /* Notice that the node "Top" is special, and doesn't have to
  2062.          be referenced. */
  2063.       if (stricmp (tags->node, "Top") != 0)
  2064.         warning ("Unreferenced node `%s'", tags->node);
  2065.     }
  2066.       tags = tags->next_ent;
  2067.     }
  2068.   input_filename = old_input_filename;
  2069. }
  2070.  
  2071. /* Return 1 if tag correctly validated, or 0 if not. */
  2072. validate (tag, line, label)
  2073.      char *tag;
  2074.      int line;
  2075.      char *label;
  2076. {
  2077.   TAG_ENTRY *result;
  2078.  
  2079.   /* If there isn't a tag to verify, or if the tag is in another file,
  2080.      then it must be okay. */
  2081.   if (!tag || !*tag || *tag == '(')
  2082.     return (1);
  2083.  
  2084.   /* Otherwise, the tag must exist. */
  2085.   result = find_node (tag);
  2086.  
  2087.   if (!result)
  2088.     {
  2089.       line_number = line;
  2090.       line_error ("Validation error.  `%s' field points to node `%s', which doesn't exist",
  2091.           label, tag);
  2092.       return (0);
  2093.     }
  2094.   result->touched++;
  2095.   return (1);
  2096. }
  2097.  
  2098. /* Split large output files into a series of smaller files.  Each file
  2099.    is pointed to in the tag table, which then gets written out as the
  2100.    original file.  The new files have the same name as the original file
  2101.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  2102.    in any single split file. */
  2103. VOID
  2104. split_file (filename, size)
  2105.      char *filename;
  2106.      LONG size;
  2107. {
  2108.   char *root_filename, *root_pathname;
  2109.   char HUGE *the_file;
  2110.   char *filename_part ();
  2111.   struct stat fileinfo;
  2112.   char *the_header;
  2113.   SIZE_T header_size;
  2114.  
  2115.   /* Can only do this to files with tag tables. */
  2116.   if (!tag_table)
  2117.     return;
  2118.  
  2119.   if (size == 0)
  2120.     size = DEFAULT_SPLIT_SIZE;
  2121.  
  2122.   if ((stat (filename, &fileinfo) != 0) ||
  2123.       (fileinfo.st_size < SPLIT_SIZE_THRESHOLD))
  2124.     return;
  2125.  
  2126.   the_file = find_and_load (filename);
  2127.   if (!the_file)
  2128.     return;
  2129.  
  2130.   root_filename = filename_part (filename);
  2131.   root_pathname = pathname_part (filename);
  2132.  
  2133.   if (!root_pathname)
  2134.     root_pathname = savestring ("");
  2135.  
  2136.   /* Start splitting the file.  Walk along the tag table
  2137.      outputting sections of the file.  When we have written
  2138.      all of the nodes in the tag table, make the top-level
  2139.      pointer file, which contains indirect pointers and
  2140.      tags for the nodes. */
  2141.   {
  2142.     int which_file = 1;
  2143.     TAG_ENTRY *tags = tag_table;
  2144.     char HUGE *indirect_info = (char HUGE *) 0x00;
  2145.  
  2146.     /* Remember the `header' of this file.  The first tag in the file is
  2147.        the bottom of the header; the top of the file is the start. */
  2148. #ifdef MSDOS
  2149.     assert (tags->position < (1L<<16));
  2150.     the_header = xmalloc (1 + (header_size = (size_t) (tags->position - 2)));
  2151. #else /* not MSDOS */
  2152.     the_header = xmalloc (1 + (header_size = (tags->position - 2)));
  2153. #endif /* not MSDOS */
  2154.     bcopy (the_file, the_header, header_size);
  2155.  
  2156.     while (tags)
  2157.       {
  2158.     LONG file_top, file_bot, limit;
  2159.  
  2160.     /* Have to include the Control-_. */
  2161.     file_top = file_bot = tags->position - 2;
  2162.     limit = file_top + size;
  2163.  
  2164.     /* If the rest of this file is only one node, then
  2165.        that is the entire subfile. */
  2166.     if (!tags->next_ent)
  2167.       {
  2168.         LONG i = tags->position + 1;
  2169.         char last_char = the_file[i];
  2170.  
  2171.         while (i < fileinfo.st_size)
  2172.           {
  2173.         if ((the_file[i] == '\037') &&
  2174.             ((last_char == '\n') ||
  2175.              (last_char == '\014')))
  2176.           break;
  2177.         else
  2178.           last_char = the_file[i];
  2179.         i++;
  2180.           }
  2181.         file_bot = i;
  2182.         tags = tags->next_ent;
  2183.         goto write_region;
  2184.       }
  2185.  
  2186.     /* Otherwise, find the largest number of nodes that can fit in
  2187.        this subfile. */
  2188.     for (; tags; tags = tags->next_ent)
  2189.       {
  2190.         if (!tags->next_ent)
  2191.           {
  2192.         /* This entry is the last node.  Search forward for the end
  2193.                of this node, and that is the end of this file. */
  2194.         LONG i = tags->position + 1;
  2195.         char last_char = the_file[i];
  2196.  
  2197.         while (i < fileinfo.st_size)
  2198.           {
  2199.             if ((the_file[i] == '\037') &&
  2200.             ((last_char == '\n') ||
  2201.              (last_char == '\014')))
  2202.               break;
  2203.             else
  2204.               last_char = the_file[i];
  2205.             i++;
  2206.           }
  2207.         file_bot = i;
  2208.  
  2209.         if (file_bot < limit)
  2210.           {
  2211.             tags = tags->next_ent;
  2212.             goto write_region;
  2213.           }
  2214.         else
  2215.           {
  2216.             /* Here we want to write out everything before the last
  2217.                node, and then write the last node out in a file
  2218.                by itself. */
  2219.             file_bot = tags->position;
  2220.             goto write_region;
  2221.           }
  2222.           }
  2223.  
  2224.         if (tags->next_ent->position > limit)
  2225.           {
  2226.         if ((tags->position) - 2 == file_top)
  2227.           tags = tags->next_ent;
  2228.         file_bot = tags->position;
  2229.           write_region:
  2230.         {
  2231.           int fd;
  2232.           char *split_file = xmalloc (10 + strlen (root_pathname)
  2233.                           + strlen (root_filename));
  2234. #ifdef MSDOS
  2235.           sprintf (split_file,
  2236.                "%s%s.%d", root_pathname, root_filename, which_file);
  2237.           assert (file_bot - file_top < (1L<<16));
  2238.  
  2239.           if (((fd = open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
  2240.               || (write (fd, the_header, header_size) != header_size)
  2241.               || (write (fd, the_file + file_top,
  2242.                  (size_t) (file_bot - file_top))
  2243.               != (size_t) (file_bot - file_top))
  2244.               || ((close (fd)) < 0))
  2245. #else /* not MSDOS */
  2246.           sprintf (split_file,
  2247.                "%s%s-%d", root_pathname, root_filename, which_file);
  2248.  
  2249.           if (((fd = open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
  2250.               || (write (fd, the_header, header_size) != header_size)
  2251.               || (write (fd, the_file + file_top, file_bot - file_top)
  2252.               != (file_bot - file_top))
  2253.               || ((close (fd)) < 0))
  2254. #endif /* not MSDOS */
  2255.             {
  2256.               perror (split_file);
  2257.               close (fd);
  2258.               exit (2);
  2259.             }
  2260.  
  2261.           if (!indirect_info)
  2262.             {
  2263.               indirect_info = the_file + file_top;
  2264.               sprintf (indirect_info, "\037\nIndirect:\n");
  2265.               indirect_info += strlen (indirect_info);
  2266.             }
  2267.  
  2268. #ifdef MSDOS
  2269.           sprintf (indirect_info, "%s.%d: %ld\n",
  2270.                root_filename, which_file, file_top);
  2271. #else /* not MSDOS */
  2272.           sprintf (indirect_info, "%s-%d: %d\n",
  2273.                root_filename, which_file, file_top);
  2274. #endif /* not MSDOS */
  2275.  
  2276.           free (split_file);
  2277.           indirect_info += strlen (indirect_info);
  2278.           which_file++;
  2279.           break;
  2280.         }
  2281.           }
  2282.       }
  2283.       }
  2284.  
  2285.     /* We have sucessfully created the subfiles.  Now write out the
  2286.        original again.  We must use `output_stream', or
  2287.        write_tag_table_indirect () won't know where to place the output. */
  2288.     output_stream = fopen (filename, "w");
  2289.     if (!output_stream)
  2290.       {
  2291.     perror (filename);
  2292.     exit (2);
  2293.       }
  2294.  
  2295.     {
  2296. #ifdef MSDOS
  2297.       size_t distance;
  2298.       assert ((long) (indirect_info - the_file) < (1L << 16));
  2299.       distance = (size_t) (long) (indirect_info - the_file);
  2300. #else /* not MSDOS */
  2301.       int distance = indirect_info - the_file;
  2302. #endif /* not MSDOS */
  2303.       fwrite (the_file, 1, distance, output_stream);
  2304.  
  2305.       /* Inhibit newlines. */
  2306.       paragraph_is_open = false;
  2307.  
  2308.       write_tag_table_indirect ();
  2309.       fclose (output_stream);
  2310.       free (the_header);
  2311.       free (the_file);
  2312.       return;
  2313.     }
  2314.   }
  2315. }
  2316.  
  2317. /* Some menu hacking.  This is used to remember menu references while
  2318.    reading the input file.  After the output file has been written, if
  2319.    validation is on, then we use the contents of NODE_REFERENCES as a
  2320.    list of nodes to validate. */
  2321. char *
  2322. reftype_type_string (type)
  2323.      enum reftype type;
  2324. {
  2325.   switch (type)
  2326.     {
  2327.     case menu_reference:
  2328.       return ("Menu");
  2329.     case followed_reference:
  2330.       return ("Followed-Reference");
  2331.     default:
  2332.       return ("Internal-bad-reference-type");
  2333.     }
  2334. }
  2335.  
  2336. /* Remember this node name for later validation use. */
  2337. VOID
  2338. remember_node_reference (node, line, type)
  2339.      char *node;
  2340.      int line;
  2341.      enum reftype type;
  2342. {
  2343.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  2344.  
  2345.   temp->next = node_references;
  2346.   temp->node = savestring (node);
  2347.   temp->line_no = line;
  2348.   temp->section = current_section;
  2349.   temp->type = type;
  2350.   temp->containing_node = savestring (current_node);
  2351.   temp->filename = node_filename;
  2352.  
  2353.   node_references = temp;
  2354. }
  2355.  
  2356. VOID
  2357. validate_other_references (ref_list)
  2358.      register NODE_REF *ref_list;
  2359. {
  2360.   char *old_input_filename = input_filename;
  2361.  
  2362.   while (ref_list != (NODE_REF *) NULL)
  2363.     {
  2364.       input_filename = ref_list->filename;
  2365.       validate (ref_list->node, ref_list->line_no,
  2366.         reftype_type_string (ref_list->type));
  2367.       ref_list = ref_list->next;
  2368.     }
  2369.   input_filename = old_input_filename;
  2370. }
  2371.  
  2372. /* Find NODE in REF_LIST. */
  2373. NODE_REF *
  2374. find_node_reference (node, ref_list)
  2375.      char *node;
  2376.      register NODE_REF *ref_list;
  2377. {
  2378.   while (ref_list)
  2379.     {
  2380.       if (stricmp (node, ref_list->node) == 0)
  2381.     break;
  2382.       ref_list = ref_list->next;
  2383.     }
  2384.   return (ref_list);
  2385. }
  2386.  
  2387. VOID
  2388. free_node_references ()
  2389. {
  2390.   register NODE_REF *list, *temp;
  2391.  
  2392.   list = node_references;
  2393.  
  2394.   while (list)
  2395.     {
  2396.       temp = list;
  2397.       free (list->node);
  2398.       free (list->containing_node);
  2399.       list = list->next;
  2400.       free (temp);
  2401.     }
  2402.   node_references = (NODE_REF *) NULL;
  2403. }
  2404.  
  2405. #define menu_starter "* "
  2406. VOID
  2407. glean_node_from_menu ()
  2408. {
  2409.   /* This function gets called at the start of every line while inside of
  2410.      a menu.  It checks to see if the line starts with "* ", and if so,
  2411.      remembers the node reference that this menu refers to.
  2412.  
  2413.      input_text_offset is at the \n just before the line start. */
  2414.  
  2415.   SIZE_T i;
  2416.   LONG orig_offset = input_text_offset;
  2417.   char *nodename;
  2418.  
  2419.   if (strncmp (&input_text[input_text_offset + 1],
  2420.            menu_starter,
  2421.            strlen (menu_starter)) != 0)
  2422.     return;
  2423.   else
  2424.     input_text_offset += strlen (menu_starter) + 1;
  2425.  
  2426.   get_until_in_line (":", &nodename);
  2427.   if (curchar () == ':')
  2428.     input_text_offset++;
  2429.   canon_white (nodename);
  2430.  
  2431.   if (curchar () == ':')
  2432.     goto save_node;
  2433.   free (nodename);
  2434.   get_rest_of_line (&nodename);
  2435.  
  2436.   /* Special hack: If the nodename follows the menu item name,
  2437.      then we have to read the rest of the line in order to find
  2438.      out what the nodename is.  But we still have to read the
  2439.      line later, in order to process any formatting commands that
  2440.      might be present.  So un-count the carriage return that has just
  2441.      been counted. */
  2442.   line_number--;
  2443.  
  2444.   canon_white (nodename);
  2445.   for (i = 0; i < strlen (nodename); i++)
  2446.     {
  2447.       if (nodename[i] == '\t' ||
  2448.       nodename[i] == '.' ||
  2449.       nodename[i] == ',')
  2450.     {
  2451.       nodename[i] = '\0';
  2452.       break;
  2453.     }
  2454.     }
  2455. save_node:
  2456.   normalize_node_name (nodename);
  2457.   i = strlen (nodename);
  2458.   if (i && nodename[i - 1] == ':')
  2459.     nodename[i - 1] = '\0';
  2460.  
  2461.   remember_node_reference (nodename, line_number, menu_reference);
  2462.   free (nodename);
  2463.   input_text_offset = orig_offset;
  2464. }
  2465.  
  2466. VOID
  2467. cm_menu ()
  2468. {
  2469.   begin_insertion (menu);
  2470. }
  2471.  
  2472. /* cont'd in makeinfo.e  */
  2473.  
  2474. /*===(cut here)===*/
  2475.  
  2476.  
  2477. /* 
  2478.  * Local Variables:
  2479.  * mode:C
  2480.  * ChangeLog:ChangeLog
  2481.  * End:
  2482.  */
  2483.  
  2484.