home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-11-01 | 57.4 KB | 2,484 lines |
- /* Makeinfo -- convert texinfo format files into info files
-
- Copyright (C) 1987 Free Software Foundation, Inc.
-
- This file is part of GNU Info.
-
- Makeinfo is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY. No author or distributor accepts
- responsibility to anyone for the consequences of using it or for
- whether it serves any particular purpose or works at all, unless he
- says so in writing. Refer to the GNU Emacs General Public License
- for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Makeinfo, but only under the conditions described in the GNU Emacs
- General Public License. A copy of this license is supposed to
- have been given to you along with GNU Emacs so you can know your
- rights and responsibilities. It should be in a file named COPYING.
- Among other things, the copyright notice and this notice must be
- preserved on all copies. */
-
- /* MS-DOS port (c) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
- This port is also distributed under the terms of the
- GNU General Public License as published by the
- Free Software Foundation.
-
- Please note that this file is not identical to the
- original GNU release, you should have received this
- code as patch to the official release.
-
- $Header: e:/gnu/info/RCS/makeinfo.d 0.1.1.1 90/10/05 11:25:31 tho Exp $
- */
-
- /* cont'd from makeinfo.c */
-
- /*===(cut here)===*/
-
- /* **************************************************************** */
- /* */
- /* Converting the File */
- /* */
- /* **************************************************************** */
-
- /* Convert the file named by NAME. The output is saved on the file
- named as the argument to the @setfilename command. */
- VOID
- convert (name)
- char *name;
- {
- char *real_output_filename, *expand_filename (), *filename_part ();
- init_tag_table ();
- init_indices ();
- init_internals ();
- init_paragraph ();
-
- if (!find_and_load (name))
- {
- /* For some reason, the file couldn't be loaded. Print a message
- to that affect, and split. */
- fs_error (name);
- return;
- }
- else
- input_filename = savestring (name);
-
- /* Search this file looking for the special string which starts conversion.
- Once found, we may truly begin. */
-
- input_text_offset = search_forward ("@setfilename", 0);
- if (input_text_offset < 0)
- {
- error ("No `@setfilename' found in `%s'", name);
- goto finished;
- }
- else
- input_text_offset += strlen ("@setfilename");
-
- get_until ("\n", &output_filename); /* no braces expected. */
- canon_white (output_filename);
-
- printf ("Making info file `%s' from `%s'.\n", output_filename, name);
- real_output_filename = expand_filename (output_filename, name);
- output_stream = fopen (real_output_filename, "w");
- if (output_stream == NULL)
- {
- fs_error (real_output_filename);
- goto finished;
- }
-
- /* Make the displayable filename from output_filename. Only the root
- portion of the filename need be displayed. */
- pretty_output_filename = filename_part (output_filename);
-
- /* For this file only, count the number of newlines from the top of
- the file to here. This way, we keep track of line numbers for
- error reporting. Line_number starts at 1, since the user isn't
- zero-based. */
- {
- LONG temp = 0;
- line_number = 1;
- while (temp != input_text_offset)
- if (input_text[temp++] == '\n')
- line_number++;
- }
-
- add_word_args ("Info file %s, produced by Makeinfo, -*- Text -*-\n\
- from input file %s.\n", output_filename, input_filename);
- close_paragraph ();
-
- reader_loop ();
-
- finished:
- close_paragraph ();
- flush_file_stack ();
- if (output_stream != NULL)
- {
- output_pending_notes ();
- free_pending_notes ();
- if (tag_table != NULL)
- {
- tag_table = (TAG_ENTRY *) reverse_list (tag_table);
- write_tag_table ();
- }
-
- fclose (output_stream);
-
- /* If validating, then validate the entire file right now. */
- if (validating)
- validate_file (real_output_filename, tag_table);
-
- /* This used to test && !errors_printed.
- But some files might have legit warnings. So split anyway. */
- if (splitting)
- split_file (real_output_filename, 0);
- }
- }
-
- VOID
- free_and_clear (pointer)
- char **pointer;
- {
- if ((*pointer) != (char *) NULL)
- {
- free (*pointer);
- *pointer = (char *) NULL;
- }
- }
-
- /* Initialize some state. */
- VOID
- init_internals ()
- {
- free_and_clear (¤t_node);
- free_and_clear (&output_filename);
- free_and_clear (&command);
- free_and_clear (&input_filename);
- free_node_references ();
- init_insertion_stack ();
- init_brace_stack ();
- command_index = 0;
- in_menu = 0;
- }
-
- VOID
- init_paragraph ()
- {
- free_and_clear (&output_paragraph);
- output_paragraph = xmalloc (paragraph_buffer_len);
- output_position = 0;
- output_paragraph[0] = '\0';
- output_paragraph_offset = 0;
- output_column = 0;
- paragraph_is_open = false;
- current_indent = 0;
- }
-
- /* Okay, we are ready to start the conversion. Call the reader on
- some text, and fill the text as it is output. Handle commands by
- remembering things like open braces and the current file position on a
- stack, and when the corresponding close brace is found, you can call
- the function with the proper arguments. */
- VOID
- reader_loop ()
- {
- int character;
- boolean done = false;
- int dash_count = 0;
-
- while (!done)
- {
- if (input_text_offset >= size_of_input_text)
- {
- if (filestack)
- {
- free (input_filename);
- free (input_text);
- popfile ();
- }
- else
- break;
- }
- character = curchar ();
-
- if (character == '-')
- {
- dash_count++;
- if (dash_count == 3)
- {
- input_text_offset++;
- continue;
- }
- }
- else
- {
- dash_count = 0;
- }
-
- if (character == '\n')
- {
- line_number++;
- if (in_menu && input_text_offset + 1 < size_of_input_text)
- {
- glean_node_from_menu ();
- }
- }
-
- switch (character)
- {
- case COMMAND_PREFIX:
- read_command ();
- if (strcmp (command, "bye") == 0)
- {
- done = true;
- continue;
- }
- break;
-
- case '{':
-
- /* Special case. I'm not supposed to see this character by itself.
- If I do, it means there is a syntax error in the input text.
- Report the error here, but remember this brace on the stack so
- you can ignore its partner. */
-
- line_error ("Misplaced `{'");
- remember_brace (misplaced_brace);
-
- /* Don't advance input_text_offset since this happens in
- remember_brace ().
- input_text_offset++;
- */
- break;
-
- case '}':
- pop_and_call_brace ();
- input_text_offset++;
- break;
-
- default:
- add_char (character);
- input_text_offset++;
- }
- }
- }
-
- /* Find the command corresponding to STRING. If the command
- is found, return a pointer to the data structure. Otherwise
- return (-1). */
- COMMAND *
- get_command_entry (string)
- char *string;
- {
- register int i;
-
- for (i = 0; CommandTable[i].name; i++)
- if (strcmp (CommandTable[i].name, string) == 0)
- return (&CommandTable[i]);
-
- /* This command is not in our predefined command table. Perhaps
- it is a user defined command. */
- for (i = 0; i < user_command_array_len; i++)
- if (user_command_array[i] &&
- (strcmp (user_command_array[i]->name, string) == 0))
- return (user_command_array[i]);
-
- /* Nope, we never heard of this command. */
- return ((COMMAND *) - 1);
- }
-
- /* input_text_offset is right at the command prefix character.
- Read the next token to determine what to do. */
- VOID
- read_command ()
- {
- COMMAND *entry;
- input_text_offset++;
- free_and_clear (&command);
- command = read_token ();
-
- entry = get_command_entry (command);
- if ((LONG) entry < 0)
- {
- line_error ("Unknown info command `%s'", command);
- return;
- }
-
- if (entry->argument_in_braces)
- remember_brace (entry->proc);
-
- (*(entry->proc)) (START);
- }
-
- /* Return the string which invokes PROC; a pointer to a function. */
- char *
- find_proc_name (proc)
- FUNCTION *proc;
- {
- register int i;
-
- for (i = 0; CommandTable[i].name; i++)
- if (proc == CommandTable[i].proc)
- return (CommandTable[i].name);
- return ("NO_NAME!");
- }
-
- VOID
- init_brace_stack ()
- {
- brace_stack = (BRACE_ELEMENT *) NULL;
- }
-
- VOID
- remember_brace (proc)
- FUNCTION *proc;
- {
- if (curchar () != '{')
- line_error ("@%s expected `{..}'", command);
- else
- input_text_offset++;
- remember_brace_1 (proc, output_paragraph_offset);
- }
-
- /* Remember the current output position here. Save PROC
- along with it so you can call it later. */
- VOID
- remember_brace_1 (proc, position)
- FUNCTION *proc;
- LONG position;
- {
- BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
- new->next = brace_stack;
- new->proc = proc;
- new->pos = position;
- new->line = line_number;
- brace_stack = new;
- }
-
- /* Pop the top of the brace stack, and call the associated function
- with the args END and POS. */
- pop_and_call_brace ()
- {
- BRACE_ELEMENT *temp;
- FUNCTION *proc;
- LONG pos;
-
- if (brace_stack == (BRACE_ELEMENT *) NULL)
- return (line_error ("Unmatched close bracket"));
-
- pos = brace_stack->pos;
- proc = brace_stack->proc;
- temp = brace_stack->next;
- free (brace_stack);
- brace_stack = temp;
-
- return ((*proc) (END, pos, output_paragraph_offset));
- }
-
- /* You call discard_braces () when you shouldn't have any braces on the stack.
- I used to think that this happens for commands that don't take arguments
- in braces, but that was wrong because of things like @code{foo @@}. So now
- I only detect it at the beginning of nodes. */
- VOID
- discard_braces ()
- {
- int temp_line_number = line_number;
- char *proc_name;
-
- if (!brace_stack)
- return;
-
- while (brace_stack)
- {
- line_number = brace_stack->line;
- proc_name = find_proc_name (brace_stack->proc);
- line_error ("@%s missing close brace", proc_name);
- line_number = temp_line_number;
- pop_and_call_brace ();
- }
- }
-
- get_char_len (character)
- int character;
- {
- /* Return the printed length of the character. */
- int len;
-
- switch (character)
- {
- case '\t':
- len = (output_column + 8) & 0xf7;
- if (len > fill_column)
- len = fill_column - output_column;
- else
- len = len - output_column;
- break;
-
- case '\n':
- len = fill_column - output_column;
- break;
-
- default:
- if (character < ' ')
- len = 2;
- else
- len = 1;
- }
- return (len);
- }
-
-
- #ifdef MSDOS
-
- VOID CDECL
- add_word_args (char *format, ...)
- {
- char buffer[1000];
- va_list arg_ptr;
- va_start (arg_ptr, format);
-
- vsprintf (buffer, format, arg_ptr);
- add_word (buffer);
- }
-
- #else /* not MSDOS */
-
- VOID
- add_word_args (format, arg1, arg2, arg3, arg4, arg5)
- char *format;
- {
- char buffer[1000];
- sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
- add_word (buffer);
- }
-
- #endif /* not MSDOS */
-
-
- /* Add STRING to output_paragraph. */
- VOID
- add_word (string)
- char *string;
- {
- while (*string)
- add_char (*string++);
- }
-
- boolean last_char_was_newline = true;
- int last_inserted_character = 0;
-
- /* Add the character to the current paragraph. If filling_enabled is
- true, then do filling as well. */
- VOID
- add_char (character)
- int character;
- {
- extern int must_start_paragraph;
-
- /* If we are adding a character now, then we don't have to
- ignore close_paragraph () calls any more. */
- if (must_start_paragraph)
- {
- must_start_paragraph = 0;
- if (current_indent > output_column)
- {
- indent (current_indent - output_column);
- output_column = current_indent;
- }
- }
-
- if (non_splitting_words && member (character, " \t\n"))
- character = ' ' | 0x80;
-
- switch (character)
- {
-
- case '\n':
- if (!filling_enabled)
- {
- insert ('\n');
-
- /* Should I be flushing output here? * /
- flush_output (); */
-
- output_column = 0;
- if (!no_indent)
- indent (output_column = current_indent);
- break;
- }
- else
- {
- if (sentence_ender (last_inserted_character))
- {
- insert (' ');
- output_column++;
- last_inserted_character = character;
- }
- }
-
- if (last_char_was_newline)
- {
- close_paragraph ();
- pending_indent = 0;
- }
- else
- {
- last_char_was_newline = true;
- insert (' ');
- output_column++;
- }
- break;
-
- default:
- {
- int len = get_char_len (character);
- if ((character == ' ') && (last_char_was_newline))
- {
- if (!paragraph_is_open)
- {
- pending_indent++;
- return;
- }
- }
- if (!paragraph_is_open)
- {
- start_paragraph ();
-
- /* If the paragraph is supposed to be indented a certain way,
- then discard all of the pending whitespace. Otherwise, we
- let the whitespace stay. */
- if (!paragraph_start_indent)
- indent (pending_indent);
- pending_indent = 0;
- }
- if ((output_column += len) >= fill_column)
- {
- if (filling_enabled)
- {
- int temp = output_paragraph_offset - 1;
- while (temp > 0 && output_paragraph[--temp] != '\n')
- {
- if (output_paragraph[temp] == ' ')
- {
- output_paragraph[temp++] = '\n';
-
- /* We have correctly broken the line where we want
- to. What we don't want is spaces following where
- we have decided to break the line. We get rid of
- them. */
- {
- int t1 = temp;
- while (t1 < output_paragraph_offset
- && whitespace (output_paragraph[t1]))
- t1++;
-
- if (t1 != temp)
- {
- strncpy (&output_paragraph[temp],
- &output_paragraph[t1],
- (output_paragraph_offset - t1));
- output_paragraph_offset -= (t1 - temp);
- }
- }
-
- /* Filled, but now indent if that is right. */
- if (indented_fill && current_indent)
- {
- int buffer_len = ((output_paragraph_offset - temp)
- + current_indent);
- char *temp_buffer = xmalloc (buffer_len);
- int indentation = 0;
-
- /* We have to shift any markers that are in
- front of the wrap point. */
- {
- register BRACE_ELEMENT *stack = brace_stack;
-
- while (stack)
- {
- if (stack->pos > temp)
- stack->pos += current_indent;
- stack = stack->next;
- }
- }
-
- while (indentation != current_indent)
- temp_buffer[indentation++] = ' ';
-
- strncpy (&temp_buffer[current_indent],
- &output_paragraph[temp],
- buffer_len - current_indent);
-
- if (output_paragraph_offset + buffer_len
- >= paragraph_buffer_len)
- {
- char *tt =
- (char *) xrealloc (output_paragraph,
- (paragraph_buffer_len += buffer_len));
- output_paragraph = tt;
- }
- strncpy (&output_paragraph[temp], temp_buffer, buffer_len);
- output_paragraph_offset += current_indent;
- free (temp_buffer);
- }
- output_column = 0;
- while (temp != output_paragraph_offset)
- output_column += get_char_len (output_paragraph[temp++]);
- output_column += len;
- break;
- }
- }
- }
- }
- insert (character);
- last_char_was_newline = false;
- last_inserted_character = character;
- }
- }
- }
-
- /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
- VOID
- insert (character)
- int character;
- {
- output_paragraph[output_paragraph_offset++] = character;
- if (output_paragraph_offset == paragraph_buffer_len)
- {
- output_paragraph =
- (char *) xrealloc (output_paragraph,
- (paragraph_buffer_len += 100));
- }
- }
-
- /* Remove upto COUNT characters of whitespace from the
- the current output line. If COUNT is less than zero,
- then remove until none left. */
- VOID
- kill_self_indent (count)
- int count;
- {
- /* Handle infinite case first. */
- if (count < 0)
- {
- output_column = 0;
- while (output_paragraph_offset)
- {
- if (whitespace (output_paragraph[output_paragraph_offset - 1]))
- output_paragraph_offset--;
- else
- break;
- }
- }
- else
- {
- while (output_paragraph_offset && count--)
- if (whitespace (output_paragraph[output_paragraph_offset - 1]))
- output_paragraph_offset--;
- else
- break;
- }
- }
-
- VOID
- flush_output ()
- {
- register int i;
-
- if (!output_paragraph_offset)
- return;
- for (i = 0; i < output_paragraph_offset; i++)
- output_paragraph[i] &= 0x7f;
-
- fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
- output_position += output_paragraph_offset;
- output_paragraph_offset = 0;
- }
-
- /* How to close a paragraph controlling the number of lines between
- this one and the last one. */
-
- /* Paragraph spacing is controlled by this variable. It is the number of
- blank lines that you wish to appear between paragraphs. A value of
- 1 creates a single blank line between paragraphs. */
- int paragraph_spacing = 1;
-
-
- /* Close the current paragraph, leaving no blank lines between them. */
- VOID
- close_single_paragraph ()
- {
- close_paragraph_with_lines (0);
- }
-
- VOID
- close_paragraph_with_lines (lines)
- int lines;
- {
- int old_spacing = paragraph_spacing;
- paragraph_spacing = lines;
- close_paragraph ();
- paragraph_spacing = old_spacing;
- }
-
- /* Non-zero means that start_paragraph () MUST be called before we pay
- any attention to close_paragraph () calls. */
- int must_start_paragraph = 0;
-
- /* Close the currently open paragraph. */
- VOID
- close_paragraph ()
- {
- if (paragraph_is_open && !must_start_paragraph)
- {
- /* Gobble up blank lines that are extra... */
- register int tindex = output_paragraph_offset;
- register int c;
- while (tindex && ((c = output_paragraph[tindex - 1]) == ' ' || c == '\n'))
- output_paragraph[--tindex] = '\n';
-
- output_paragraph_offset = tindex;
-
- insert ('\n');
- {
- register int i;
- for (i = 0; i < paragraph_spacing; i++)
- insert ('\n');
- }
- flush_output ();
- paragraph_is_open = false;
- no_indent = false;
- }
- last_char_was_newline = true;
- }
-
- /* Begin a new paragraph. */
- VOID
- start_paragraph ()
- {
- close_paragraph (); /* First close existing one. */
-
- paragraph_is_open = true;
-
- if (!must_start_paragraph)
- {
- output_column = 0;
-
- /* If doing indentation, then insert the appropriate amount. */
- if (!no_indent)
- {
- if (inhibit_paragraph_indentation || paragraph_start_indent < 0)
- output_column = current_indent;
- else
- output_column = current_indent + paragraph_start_indent;
-
- indent (output_column);
- }
- }
- else
- must_start_paragraph = 0;
- }
-
- /* Insert the indentation specified by AMOUNT. */
- VOID
- indent (amount)
- int amount;
- {
- while (--amount >= 0)
- insert (' ');
- }
-
- /* Search forward for STRING in input_text.
- FROM says where where to start. */
- LONG
- search_forward (string, from)
- char *string;
- LONG from;
- {
- int len = strlen (string);
-
- while (from < size_of_input_text)
- {
- if (strnicmp (input_text + from, string, len) == 0)
- return (from);
- from++;
- }
- return ((LONG) -1);
- }
-
-
- #ifndef MSDOS
- /* Whoops, Unix doesn't have stricmp, or strnicmp. */
-
- /* Case independent string compare. */
- stricmp (string1, string2)
- char *string1, *string2;
- {
- char ch1, ch2;
-
- for (;;)
- {
- ch1 = *string1++;
- ch2 = *string2++;
- if (!(ch1 | ch2))
- return (0);
-
- ch1 = coerce_to_upper (ch1);
- ch2 = coerce_to_upper (ch2);
-
- if (ch1 != ch2)
- return (1);
- }
- }
-
- /* Compare at most COUNT characters from string1 to string2. Case
- doesn't matter. */
- strnicmp (string1, string2, count)
- char *string1, *string2;
- {
- char ch1, ch2;
-
- while (count)
- {
- ch1 = *string1++;
- ch2 = *string2++;
- if (coerce_to_upper (ch1) == coerce_to_upper (ch2))
- count--;
- else
- break;
- }
- return (count);
- }
- #endif /* not MSDOS */
-
- enum insertion_type
- {
- menu, quotation, lisp, example, smallexample, display,
- itemize, format, enumerate, table, group, ifinfo,
- defun, defvar, defopt, deffn, defspec, defmac,
- bad_type
- };
-
- char *insertion_type_names[] = {
- "menu", "quotation", "lisp", "example", "smallexample", "display",
- "itemize", "format", "enumerate", "table", "group", "ifinfo",
- "defun", "defvar", "defopt", "deffn", "defspec", "defmac",
- };
-
- int insertion_level = 0;
- typedef struct istack_elt
- {
- struct istack_elt *next;
- char *item_function;
- int line_number;
- int filling_enabled;
- int indented_fill;
- enum insertion_type insertion;
- int inhibited;
- } INSERTION_ELT;
-
- INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
-
- VOID
- init_insertion_stack ()
- {
- insertion_stack = (INSERTION_ELT *) NULL;
- }
-
- /* Return the type of the current insertion. */
- enum insertion_type
- current_insertion_type ()
- {
- if (!insertion_level)
- return (bad_type);
- else
- return (insertion_stack->insertion);
- }
-
- /* Return a pointer to the string which is the function
- to wrap around items. */
- char *
- current_item_function ()
- {
- if (!insertion_level)
- return ((char *) NULL);
- else
- return (insertion_stack->item_function);
- }
-
- char *
- get_item_function ()
- {
- char *item_function;
- get_until ("\n", &item_function);
- canon_white (item_function);
- return (item_function);
- }
-
- /* Push the state of the current insertion on the stack. */
- VOID
- push_insertion (type, item_function)
- enum insertion_type type;
- char *item_function;
- {
- INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
-
- new->item_function = item_function;
- new->filling_enabled = filling_enabled;
- new->indented_fill = indented_fill;
- new->insertion = type;
- new->line_number = line_number;
- new->inhibited = inhibit_paragraph_indentation;
- new->next = insertion_stack;
- insertion_stack = new;
- insertion_level++;
- }
-
- /* Pop the value on top of the insertion stack into the
- global variables. */
- VOID
- pop_insertion ()
- {
- INSERTION_ELT *temp = insertion_stack;
- if (temp == (INSERTION_ELT *) NULL)
- return;
- inhibit_paragraph_indentation = temp->inhibited;
- filling_enabled = insertion_stack->filling_enabled;
- indented_fill = insertion_stack->indented_fill;
- free_and_clear (&(temp->item_function));
- insertion_stack = insertion_stack->next;
- free (temp);
- insertion_level--;
- }
-
- /* Return a pointer to the print name of this
- enumerated type. */
- char *
- insertion_type_pname (type)
- enum insertion_type type;
- {
- if ((int) type < (int) bad_type)
- return (insertion_type_names[(int) type]);
- else
- return ("Broken-Type in insertion_type_pname");
- }
-
- /* Return the insertion_type associated with NAME.
- If the type is not one of the known ones, return BAD_TYPE. */
- enum insertion_type
- find_type_from_name (name)
- char *name;
- {
- int index = 0;
- while (index < (int) bad_type)
- {
- if (stricmp (name, insertion_type_names[index]) == 0)
- return (enum insertion_type) index;
- index++;
- }
- return (bad_type);
- }
-
- VOID
- do_nothing ()
- {
- }
-
- int
- defun_insertion (type)
- enum insertion_type type;
- {
- return (type == defun ||
- type == defvar ||
- type == defopt ||
- type == deffn ||
- type == defspec ||
- type == defmac);
- }
-
- /* This is where the work for all the "insertion" style
- commands is done. A huge switch statement handles the
- various setups, and generic code is on both sides. */
- VOID
- begin_insertion (type)
- enum insertion_type type;
- {
- int no_discard = 0;
-
- close_paragraph ();
-
- if (defun_insertion (type))
- {
- push_insertion (type, savestring (""));
- no_discard = 1;
- }
- else
- push_insertion (type, get_item_function ());
-
- filling_enabled = false; /* the general case for insertions. */
- inhibit_paragraph_indentation = 1;
- no_indent = false;
-
- switch (type)
- {
- case menu:
- add_word ("* Menu:\n");
- in_menu++;
- discard_until ("\n");
- input_text_offset--;
- /* discard_until () has already counted the newline. Discount it. */
- line_number--;
- return;
-
- /* I think @quotation is meant to do filling.
- If you don't want filling, then use @example. */
- case quotation:
- last_char_was_newline = 0;
- indented_fill = filling_enabled = true;
- current_indent += default_indentation_increment;
- break;
-
- /* Just like @example, but no indentation. */
- case format:
- break;
-
- case display:
- case example:
- case smallexample:
- case lisp:
- last_char_was_newline = 0;
- current_indent += default_indentation_increment;
- break;
-
- case table:
- case itemize:
- current_indent += default_indentation_increment;
- filling_enabled = indented_fill = true;
-
- /* Make things work for losers who forget the itemize syntax. */
- if (type == itemize)
- {
- if (!(*insertion_stack->item_function))
- {
- free (insertion_stack->item_function);
- insertion_stack->item_function = savestring ("*");
- }
- }
- break;
-
- case enumerate:
- inhibit_paragraph_indentation = 0;
- current_indent += default_indentation_increment;
- start_numbering (1);
- filling_enabled = indented_fill = true;
- break;
-
- case group:
- inhibit_paragraph_indentation = 0;
- break;
-
- case ifinfo:
- /* Undo whatever we just did. This is a no-op. */
- inhibit_paragraph_indentation = 0;
- filling_enabled = insertion_stack->filling_enabled;
- indented_fill = insertion_stack->indented_fill;
- break;
-
- case defun:
- case defvar:
- case defopt:
- case deffn:
- case defspec:
- case defmac:
- inhibit_paragraph_indentation = 0;
- filling_enabled = indented_fill = true;
- current_indent += default_indentation_increment;
- break;
- }
- if (!no_discard)
- discard_until ("\n");
- }
-
- /* Try to end the quotation with the specified type.
- Like begin_insertion (), this uses a giant switch statement as
- well. A big difference is that you *must* pass a valid type to
- this function, and a value of bad_type gets translated to match
- the value currently on top of the stack. If, however, the value
- passed is a valid type, and it doesn't match the top of the
- stack, then we produce an error. Should fix this, somewhat
- unclean. */
- VOID
- end_insertion (type)
- enum insertion_type type;
- {
- enum insertion_type temp_type;
-
- if (!insertion_level)
- return;
-
- temp_type = current_insertion_type ();
- if (type == bad_type)
- type = temp_type;
-
- if (type != temp_type)
- {
- line_error ("Expected `%s', but saw `%s'. Token unread",
- insertion_type_pname (temp_type), insertion_type_pname (type));
- return;
- }
- pop_insertion ();
-
- switch (type)
- {
-
- case menu:
- in_menu--; /* no longer hacking menus. */
- break;
-
- case enumerate:
- stop_numbering ();
- current_indent -= default_indentation_increment;
- break;
-
- case group:
- case ifinfo:
- case format:
- break;
-
- default:
- current_indent -= default_indentation_increment;
- break;
- }
- close_paragraph ();
- }
-
- /* Insertions cannot cross certain boundaries, such as node beginnings. In
- code that creates such boundaries, you should call discard_insertions ()
- before doing anything else. It prints the errors for you, and cleans up
- the insertion stack. */
- VOID
- discard_insertions ()
- {
- int real_line_number = line_number;
- while (insertion_stack)
- {
- if (insertion_stack->insertion == ifinfo)
- break;
- else
- {
- char *offender = (char *) insertion_type_pname (insertion_stack->insertion);
-
- line_number = insertion_stack->line_number;
- line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
- COMMAND_PREFIX, offender);
- pop_insertion ();
- }
- }
- line_number = real_line_number;
- }
-
- /* MAX_NS is the maximum nesting level for enumerations. I picked 100
- which seemed reasonable. This doesn't control the number of items,
- just the number of nested lists. */
- #define max_ns 100
- int number_stack[max_ns];
- int number_offset = 0;
- int the_current_number = 0;
-
- VOID
- start_numbering (at_number)
- int at_number;
- {
- if (number_offset + 1 == max_ns)
- {
- line_error ("Enumeration stack overflow");
- return;
- }
- number_stack[number_offset++] = the_current_number;
- the_current_number = at_number;
- }
-
- VOID
- stop_numbering ()
- {
- the_current_number = number_stack[--number_offset];
- if (number_offset < 0)
- number_offset = 0;
- }
-
- /* Place a number into the output stream. */
- VOID
- number_item ()
- {
- char temp[10];
- sprintf (temp, "%d. ", the_current_number);
- indent (output_column += (current_indent - strlen (temp)));
- add_word (temp);
- the_current_number++;
- }
-
- /* The actual commands themselves. */
-
- /* Commands which insert themselves. */
- VOID
- insert_self ()
- {
- add_word (command);
- }
-
- /* Force line break */
- VOID
- cm_asterisk ()
- {
- /* Force a line break in the output. */
- insert ('\n');
- indent (output_column = current_indent);
- }
-
- /* Insert ellipsis. */
- VOID
- cm_dots (arg)
- int arg;
- {
- if (arg == START)
- add_word ("...");
- }
-
- VOID
- cm_bullet (arg)
- int arg;
- {
- if (arg == START)
- add_char ('*');
- }
-
- VOID
- cm_minus (arg)
- int arg;
- {
- if (arg == START)
- add_char ('-');
- }
-
- /* Insert "TeX". */
- VOID
- cm_TeX (arg)
- int arg;
- {
- if (arg == START)
- add_word ("TeX");
- }
-
- VOID
- cm_copyright (arg)
- int arg;
- {
- if (arg == START)
- add_word ("(C)");
- }
-
- VOID
- cm_code (arg)
- int arg;
- {
- if (arg == START)
- add_char ('`');
- else
- add_word ("'");
- }
-
- VOID
- cm_samp (arg)
- int arg;
- {
- cm_code (arg);
- }
-
- VOID
- cm_file (arg)
- int arg;
- {
- cm_code (arg);
- }
-
- VOID
- cm_kbd (arg)
- int arg;
- {
- cm_code (arg);
- }
-
- VOID
- cm_key (arg)
- int arg;
- {
- }
-
- /* Convert the character at position-1 into CTL. */
- VOID
- cm_ctrl (arg, position)
- int arg, position;
- {
- if (arg == END)
- output_paragraph[position - 1] = CTL (output_paragraph[position - 1]);
- }
-
- /* Small Caps in makeinfo just does all caps. */
- VOID
- cm_sc (arg, start_pos, end_pos)
- int arg, start_pos, end_pos;
- {
- if (arg == END)
- {
- while (start_pos < end_pos)
- {
- output_paragraph[start_pos] =
- coerce_to_upper (output_paragraph[start_pos]);
- start_pos++;
- }
- }
- }
-
- /* @var in makeinfo just uppercases the text. */
- VOID
- cm_var (arg, start_pos, end_pos)
- int arg, start_pos, end_pos;
- {
- if (arg == END)
- {
- while (start_pos < end_pos)
- {
- output_paragraph[start_pos] =
- coerce_to_upper (output_paragraph[start_pos]);
- start_pos++;
- }
- }
- }
-
- VOID
- cm_dfn (arg, position)
- int arg, position;
- {
- add_char ('"');
- }
-
- VOID
- cm_emph (arg)
- int arg;
- {
- add_char ('*');
- }
-
- VOID
- cm_strong (arg, position)
- int arg, position;
- {
- cm_emph (arg);
- }
-
- VOID
- cm_cite (arg, position)
- int arg, position;
- {
- if (arg == START)
- add_word ("``");
- else
- add_word ("''");
- }
-
- VOID
- cm_italic (arg)
- int arg;
- {
- }
-
- VOID
- cm_bold (arg)
- int arg;
- {
- cm_italic (arg);
- }
-
- VOID
- cm_roman (arg)
- int arg;
- {
- }
-
- VOID
- cm_title (arg)
- int arg;
- {
- cm_italic (arg);
- }
-
- VOID
- cm_refill ()
- {
- }
-
- /* Prevent the argument from being split across two lines. */
- VOID
- cm_w (arg)
- int arg;
- {
- if (arg == START)
- non_splitting_words++;
- else
- non_splitting_words--;
- }
-
-
- /* Explain that this command is obsolete, thus the user shouldn't
- do anything with it. */
- VOID
- cm_obsolete (arg)
- int arg;
- {
- if (arg == START)
- warning ("The command `@%s' is obsolete", command);
- }
-
- /* Insert the text following input_text_offset up to the end of the line
- in a new, separate paragraph. Directly underneath it, insert a
- line of WITH_CHAR, the same length of the inserted text. */
- VOID
- insert_and_underscore (with_char)
- int with_char;
- {
- LONG len;
- int i, old_no_indent;
- char *temp;
-
- close_paragraph ();
- filling_enabled = indented_fill = false;
- old_no_indent = no_indent;
- no_indent = true;
- get_rest_of_line (&temp);
-
- len = output_position;
- execute_string ("%s\n", temp);
- free (temp);
-
- len = ((output_position + output_paragraph_offset) - 1) - len;
- #ifdef MSDOS
- assert (len < (1L<<16));
- for (i = 0; i < (int) len; i++)
- add_char (with_char);
- #else /* not MSDOS */
- for (i = 0; i < len; i++)
- add_char (with_char);
- #endif /* not MSDOS */
- insert ('\n');
- close_paragraph ();
- filling_enabled = true;
- no_indent = old_no_indent;
- }
-
- VOID
- cm_chapter ()
- {
- insert_and_underscore ('*');
- }
-
- VOID
- cm_section ()
- {
- insert_and_underscore ('=');
- }
-
- VOID
- cm_subsection ()
- {
- insert_and_underscore ('-');
- }
-
- VOID
- cm_subsubsection ()
- {
- insert_and_underscore ('.');
- }
-
- VOID
- cm_unnumbered ()
- {
- cm_chapter ();
- }
-
- VOID
- cm_unnumberedsec ()
- {
- cm_section ();
- }
-
- VOID
- cm_unnumberedsubsec ()
- {
- cm_subsection ();
- }
-
- VOID
- cm_unnumberedsubsubsec ()
- {
- cm_subsubsection ();
- }
-
- VOID
- cm_appendix ()
- {
- cm_chapter ();
- }
-
- VOID
- cm_appendixsec ()
- {
- cm_section ();
- }
-
- VOID
- cm_appendixsubsec ()
- {
- cm_subsection ();
- }
-
- VOID
- cm_appendixsubsubsec ()
- {
- cm_subsubsection ();
- }
-
- /* **************************************************************** */
- /* */
- /* Adding nodes, and making tags */
- /* */
- /* **************************************************************** */
-
- /* Start a new tag table. */
- VOID
- init_tag_table ()
- {
- while (tag_table != (TAG_ENTRY *) NULL)
- {
- TAG_ENTRY *temp = tag_table;
- free (temp->node);
- free (temp->prev);
- free (temp->next);
- free (temp->up);
- tag_table = tag_table->next_ent;
- free (temp);
- }
- }
-
- write_tag_table ()
- {
- return (write_tag_table_internal (false)); /* Not indirect. */
- }
-
- write_tag_table_indirect ()
- {
- return (write_tag_table_internal (true));
- }
-
- /* Write out the contents of the existing tag table.
- INDIRECT_P says how to format the output. */
- VOID
- write_tag_table_internal (indirect_p)
- boolean indirect_p;
- {
- TAG_ENTRY *node = tag_table;
-
- close_paragraph ();
- filling_enabled = false;
- add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
-
- while (node != (TAG_ENTRY *) NULL)
- {
- #ifdef MSDOS
- add_word_args ("Node: %s\177%ld\n", node->node, node->position);
- #else /* not MSDOS */
- add_word_args ("Node: %s\177%d\n", node->node, node->position);
- #endif /* not MSDOS */
- node = node->next_ent;
- }
- add_word ("\037\nEnd Tag Table\n");
- flush_output ();
- }
-
- char *
- get_node_token ()
- {
- char *string;
-
- get_until_in_line (",", &string);
-
- if (curchar () == ',')
- input_text_offset++;
-
- canon_white (string);
-
- /* Allow things like @@nodename. */
- normalize_node_name (string);
-
- return (string);
- }
-
- /* Given a node name in STRING, remove double @ signs, replacing them
- with just one. */
- VOID
- normalize_node_name (string)
- char *string;
- {
- register int i, l = strlen (string);
-
- for (i = 0; i < l; i++)
- {
- if (string[i] == '@' && string[i + 1] == '@')
- {
- strncpy (string + i, string + i + 1, l - i);
- l--;
- }
- }
- }
-
- /* Look up NAME in the tag table, and return the associated
- tag_entry. If the node is not in the table return NULL. */
- TAG_ENTRY *
- find_node (name)
- char *name;
- {
- TAG_ENTRY *tag = tag_table;
-
- while (tag != (TAG_ENTRY *) NULL)
- {
- if (stricmp (tag->node, name) == 0)
- return (tag);
- tag = tag->next_ent;
- }
- return ((TAG_ENTRY *) NULL);
- }
-
- /* Remember NODE and associates. */
- VOID
- remember_node (node, prev, next, up, position, line_no, no_warn)
- char *node, *prev, *next, *up;
- LONG position;
- int line_no, no_warn;
- {
- /* Check for existence of this tag already. */
- if (validating)
- {
- register TAG_ENTRY *tag = find_node (node);
- if (tag)
- {
- line_error ("Node `%s' multiply defined (%d is first definition)",
- node, tag->line_no);
- return;
- }
- }
-
- /* First, make this the current node. */
- current_node = node;
-
- /* Now add it to the list. */
- {
- TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
- new->node = node;
- new->prev = prev;
- new->next = next;
- new->up = up;
- new->position = position;
- new->line_no = line_no;
- new->filename = node_filename;
- new->touched = 0; /* not yet referenced. */
- new->flags = 0;
- if (no_warn)
- new->flags |= NO_WARN;
- new->next_ent = tag_table;
- tag_table = new;
- }
- }
-
- /* Here is a structure which associates sectioning commands with
- an integer, hopefully to reflect the `depth' of the current
- section. */
- struct {
- char *name;
- int level;
- } section_alist[] = {
- { "chapter", 1 },
- { "section", 2},
- { "subsec", 3},
- { "subsubsec", 4},
- { "unnumbered", 1},
- { "unnumberedsec", 2},
- { "unnumberedsubsec", 3},
- { "unnumberedsubsubsec", 4},
- { "appendix", 1},
- { "appendixsec", 2},
- { "appendixsubsec", 3},
- { "appendixsubsubsec", 4},
- { (char *)NULL, 0 }
- };
-
- /* Return an integer which identifies the type section present in TEXT. */
- int
- what_section (text)
- char *text;
- {
- int i, j;
- char *t;
-
- for (j = 0; text[j] && whitespace (text[j]) || text[j] == '\n'; j++);
- if (text[j] != '@')
- return (-1);
-
- text = text + j + 1;
-
- /* Handle italicized sectioning commands. */
- if (*text == 'i')
- text++;
-
- for (i = 0; t = section_alist[i].name; i++)
- {
- if (strncmp (t, text, strlen (t)) == 0)
- return (section_alist[i].level);
- }
- return (-1);
- }
-
- /* The order is: nodename, nextnode, prevnode, upnode.
- The next, prev, and up fields can be defaulted.
- You must follow a node command which has those fields
- defaulted with a sectioning command (e.g. @chapter) giving
- the "level" of that node. It is an error not to do so.
- The defaults come from the menu in this nodes parent. */
- VOID
- cm_node ()
- {
- char *node, *prev, *next, *up;
- LONG new_node_pos;
- int defaulting, this_section, no_warn = 0;
- extern int already_outputting_pending_notes;
-
- if (strcmp (command, "nwnode") == 0)
- no_warn = 1;
-
- /* Get rid of unmatched brace arguments from previous commands. */
- discard_braces ();
-
- /* There also might be insertions left lying around that haven't been
- ended yet. Do that also. */
- discard_insertions ();
-
- if (!already_outputting_pending_notes)
- {
- close_paragraph ();
- output_pending_notes ();
- free_pending_notes ();
- }
-
- filling_enabled = indented_fill = false;
- new_node_pos = output_position + 1;
-
- node = get_node_token ();
- next = get_node_token ();
- prev = get_node_token ();
- up = get_node_token ();
-
- this_section = what_section (input_text + input_text_offset);
-
- /* ??? The first \n in the following string shouldn't be there, but I have
- to revamp the @example & @group things so that they always leave a \n
- as the last character output. Until that time, this is the only way
- I can produce reliable output. */
- no_indent = true;
- add_word_args ("\n\037\nFile: %s, Node: %s", pretty_output_filename, node);
-
- /* Check for defaulting of this node's next, prev, and up fields. */
- defaulting = ((strlen (next) == 0) &&
- (strlen (prev) == 0) &&
- (strlen (up) == 0));
-
- /* If we are defaulting, then look at the immediately following
- sectioning command (error if none) to determine the node's
- level. Find the node that contains the menu mentioning this node
- that is one level up (error if not found). That node is the "Up"
- of this node. Default the "Next" and "Prev" from the menu. */
- if (defaulting)
- {
- NODE_REF *last_ref = (NODE_REF *)NULL;
- NODE_REF *ref = node_references;
-
- if (this_section < 0)
- {
- char *polite_section_name = "chapter";
- int i;
-
- for (i = 0; section_alist[i].name; i++)
- if (section_alist[i].level == current_section + 1)
- {
- polite_section_name = section_alist[i].name;
- break;
- }
-
- line_error
- ("Node `%s' requires a sectioning command (e.g. @%s)",
- node, polite_section_name);
- }
- else
- {
- while (ref)
- {
- if (ref->section == (this_section - 1) &&
- ref->type == menu_reference &&
- stricmp (ref->node, node) == 0)
- {
- free (up);
- up = savestring (ref->containing_node);
-
- if (last_ref &&
- strcmp
- (last_ref->containing_node, ref->containing_node) == 0)
- {
- free (next);
- next = savestring (last_ref->node);
- }
-
- if (ref->next &&
- strcmp
- (ref->next->containing_node, ref->containing_node) == 0)
- {
- free (prev);
- prev = savestring (ref->next->node);
- }
- break;
- }
- last_ref = ref;
- ref = ref->next;
- }
- }
- }
-
- if (*next)
- add_word_args (", Next: %s", next);
-
- if (*prev)
- add_word_args (", Prev: %s", prev);
-
- if (*up)
- add_word_args (", Up: %s", up);
-
- insert ('\n');
- close_paragraph ();
- no_indent = false;
-
- if (!*node)
- {
- line_error ("No node name specified for `@%s' command", command);
- free (node);
- free (next);
- free (prev);
- free (up);
- }
- else
- {
- if (!*next) { free (next); next = (char *)NULL; }
- if (!*prev) { free (prev); prev = (char *)NULL; }
- if (!*up) { free (up); up = (char *)NULL; }
- remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
- }
-
- /* Change the section only if there was a sectioning command. */
- if (this_section >= 0)
- current_section = this_section;
-
- filling_enabled = true;
- }
-
- /* Validation of an info file.
- Scan through the list of tag entrys touching the Prev, Next, and Up
- elements of each. It is an error not to be able to touch one of them,
- except in the case of external node references, such as "(DIR)".
-
- If the Prev is different from the Up,
- then the Prev node must have a Next pointing at this node.
-
- Every node except Top must have an Up.
- The Up node must contain some sort of reference, other than a Next,
- to this node.
-
- If the Next is different from the Next of the Up,
- then the Next node must have a Prev pointing at this node. */
- VOID
- validate_file (filename, tag_table)
- char *filename;
- TAG_ENTRY *tag_table;
- {
- char *old_input_filename = input_filename;
- TAG_ENTRY *tags = tag_table;
-
- while (tags != (TAG_ENTRY *) NULL)
- {
- register TAG_ENTRY *temp_tag;
-
- input_filename = tags->filename;
- line_number = tags->line_no;
-
- /* If this node has a Next, then make sure that the Next exists. */
- if (tags->next)
- {
- validate (tags->next, tags->line_no, "Next");
-
- /* If the Next node exists, and there is no Up, then make
- sure that the Prev of the Next points back. */
- if (temp_tag = find_node (tags->next))
- {
- char *prev = temp_tag->prev;
- if (!prev || (stricmp (prev, tags->node) != 0))
- {
- line_error
- ("Node `%s''s Next field not pointed back to", tags->node);
- line_number = temp_tag->line_no;
- input_filename = temp_tag->filename;
- line_error
- ("This node (`%s') is the one with the bad `Prev'",
- temp_tag->node);
- input_filename = tags->filename;
- line_number = tags->line_no;
- temp_tag->flags |= PREV_ERROR;
- }
- }
- }
-
- /* Validate the Prev field if there is one, and we haven't already
- complained about it in some way. You don't have to have a Prev
- field at this stage. */
- if (!(tags->flags & PREV_ERROR) && tags->prev)
- {
- int valid = validate (tags->prev, tags->line_no, "Prev");
-
- if (!valid)
- tags->flags |= PREV_ERROR;
- else
- {
- /* If the Prev field is not the same as the Up field,
- then the node pointed to by the Prev field must have
- a Next field which points to this node. */
- if (tags->up && (stricmp (tags->prev, tags->up) != 0))
- {
- temp_tag = find_node (tags->prev);
- if (!temp_tag->next ||
- (stricmp (temp_tag->next, tags->node) != 0))
- {
- line_error ("Node `%s''s Prev field not pointed back to",
- tags->node);
- line_number = temp_tag->line_no;
- input_filename = temp_tag->filename;
- line_error
- ("This node (`%s') is the one with the bad `Next'",
- temp_tag->node);
- input_filename = tags->filename;
- line_number = tags->line_no;
- temp_tag->flags |= NEXT_ERROR;
- }
- }
- }
- }
-
- if (!tags->up && (stricmp (tags->node, "Top") != 0))
- line_error ("Node `%s' is missing an \"Up\" field", tags->node);
- else if (tags->up)
- {
- int valid = validate (tags->up, tags->line_no, "Up");
-
- /* If node X has Up: Y, then warn if Y fails to have a menu item
- or note pointing at X, if Y isn't of the form "(Y)". */
- if (valid && *tags->up != '(')
- {
- NODE_REF *nref, *tref, *list;
- NODE_REF *find_node_reference ();
-
- tref = (NODE_REF *) NULL;
- list = node_references;
-
- for (;;)
- {
- if (!(nref = find_node_reference (tags->node, list)))
- break;
-
- if (stricmp (nref->containing_node, tags->up) == 0)
- {
- if (nref->type != menu_reference)
- {
- tref = nref;
- list = nref->next;
- }
- else
- break;
- }
- list = nref->next;
- }
-
- if (!nref)
- {
- temp_tag = find_node (tags->up);
- line_number = temp_tag->line_no;
- filename = temp_tag->filename;
- if (!tref)
- line_error ("`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
- tags->node, tags->up, tags->up, tags->node);
- line_number = tags->line_no;
- filename = tags->filename;
- }
- }
- }
- tags = tags->next_ent;
- }
-
- validate_other_references (node_references);
- /* We have told the user about the references which didn't exist.
- Now tell him about the nodes which aren't referenced. */
-
- tags = tag_table;
- while (tags != (TAG_ENTRY *) NULL)
- {
- /* Special hack. If the node in question appears to have
- been referenced more than REFERENCE_WARNING_LIMIT times,
- give a warning. */
- if (tags->touched > reference_warning_limit)
- {
- input_filename = tags->filename;
- line_number = tags->line_no;
- warning ("Node `%s' has been referenced %d times",
- tags->node, tags->touched);
- }
-
- if (tags->touched == 0)
- {
- input_filename = tags->filename;
- line_number = tags->line_no;
-
- /* Notice that the node "Top" is special, and doesn't have to
- be referenced. */
- if (stricmp (tags->node, "Top") != 0)
- warning ("Unreferenced node `%s'", tags->node);
- }
- tags = tags->next_ent;
- }
- input_filename = old_input_filename;
- }
-
- /* Return 1 if tag correctly validated, or 0 if not. */
- validate (tag, line, label)
- char *tag;
- int line;
- char *label;
- {
- TAG_ENTRY *result;
-
- /* If there isn't a tag to verify, or if the tag is in another file,
- then it must be okay. */
- if (!tag || !*tag || *tag == '(')
- return (1);
-
- /* Otherwise, the tag must exist. */
- result = find_node (tag);
-
- if (!result)
- {
- line_number = line;
- line_error ("Validation error. `%s' field points to node `%s', which doesn't exist",
- label, tag);
- return (0);
- }
- result->touched++;
- return (1);
- }
-
- /* Split large output files into a series of smaller files. Each file
- is pointed to in the tag table, which then gets written out as the
- original file. The new files have the same name as the original file
- with a "-num" attached. SIZE is the largest number of bytes to allow
- in any single split file. */
- VOID
- split_file (filename, size)
- char *filename;
- LONG size;
- {
- char *root_filename, *root_pathname;
- char HUGE *the_file;
- char *filename_part ();
- struct stat fileinfo;
- char *the_header;
- SIZE_T header_size;
-
- /* Can only do this to files with tag tables. */
- if (!tag_table)
- return;
-
- if (size == 0)
- size = DEFAULT_SPLIT_SIZE;
-
- if ((stat (filename, &fileinfo) != 0) ||
- (fileinfo.st_size < SPLIT_SIZE_THRESHOLD))
- return;
-
- the_file = find_and_load (filename);
- if (!the_file)
- return;
-
- root_filename = filename_part (filename);
- root_pathname = pathname_part (filename);
-
- if (!root_pathname)
- root_pathname = savestring ("");
-
- /* Start splitting the file. Walk along the tag table
- outputting sections of the file. When we have written
- all of the nodes in the tag table, make the top-level
- pointer file, which contains indirect pointers and
- tags for the nodes. */
- {
- int which_file = 1;
- TAG_ENTRY *tags = tag_table;
- char HUGE *indirect_info = (char HUGE *) 0x00;
-
- /* Remember the `header' of this file. The first tag in the file is
- the bottom of the header; the top of the file is the start. */
- #ifdef MSDOS
- assert (tags->position < (1L<<16));
- the_header = xmalloc (1 + (header_size = (size_t) (tags->position - 2)));
- #else /* not MSDOS */
- the_header = xmalloc (1 + (header_size = (tags->position - 2)));
- #endif /* not MSDOS */
- bcopy (the_file, the_header, header_size);
-
- while (tags)
- {
- LONG file_top, file_bot, limit;
-
- /* Have to include the Control-_. */
- file_top = file_bot = tags->position - 2;
- limit = file_top + size;
-
- /* If the rest of this file is only one node, then
- that is the entire subfile. */
- if (!tags->next_ent)
- {
- LONG i = tags->position + 1;
- char last_char = the_file[i];
-
- while (i < fileinfo.st_size)
- {
- if ((the_file[i] == '\037') &&
- ((last_char == '\n') ||
- (last_char == '\014')))
- break;
- else
- last_char = the_file[i];
- i++;
- }
- file_bot = i;
- tags = tags->next_ent;
- goto write_region;
- }
-
- /* Otherwise, find the largest number of nodes that can fit in
- this subfile. */
- for (; tags; tags = tags->next_ent)
- {
- if (!tags->next_ent)
- {
- /* This entry is the last node. Search forward for the end
- of this node, and that is the end of this file. */
- LONG i = tags->position + 1;
- char last_char = the_file[i];
-
- while (i < fileinfo.st_size)
- {
- if ((the_file[i] == '\037') &&
- ((last_char == '\n') ||
- (last_char == '\014')))
- break;
- else
- last_char = the_file[i];
- i++;
- }
- file_bot = i;
-
- if (file_bot < limit)
- {
- tags = tags->next_ent;
- goto write_region;
- }
- else
- {
- /* Here we want to write out everything before the last
- node, and then write the last node out in a file
- by itself. */
- file_bot = tags->position;
- goto write_region;
- }
- }
-
- if (tags->next_ent->position > limit)
- {
- if ((tags->position) - 2 == file_top)
- tags = tags->next_ent;
- file_bot = tags->position;
- write_region:
- {
- int fd;
- char *split_file = xmalloc (10 + strlen (root_pathname)
- + strlen (root_filename));
- #ifdef MSDOS
- sprintf (split_file,
- "%s%s.%d", root_pathname, root_filename, which_file);
- assert (file_bot - file_top < (1L<<16));
-
- if (((fd = open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
- || (write (fd, the_header, header_size) != header_size)
- || (write (fd, the_file + file_top,
- (size_t) (file_bot - file_top))
- != (size_t) (file_bot - file_top))
- || ((close (fd)) < 0))
- #else /* not MSDOS */
- sprintf (split_file,
- "%s%s-%d", root_pathname, root_filename, which_file);
-
- if (((fd = open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
- || (write (fd, the_header, header_size) != header_size)
- || (write (fd, the_file + file_top, file_bot - file_top)
- != (file_bot - file_top))
- || ((close (fd)) < 0))
- #endif /* not MSDOS */
- {
- perror (split_file);
- close (fd);
- exit (2);
- }
-
- if (!indirect_info)
- {
- indirect_info = the_file + file_top;
- sprintf (indirect_info, "\037\nIndirect:\n");
- indirect_info += strlen (indirect_info);
- }
-
- #ifdef MSDOS
- sprintf (indirect_info, "%s.%d: %ld\n",
- root_filename, which_file, file_top);
- #else /* not MSDOS */
- sprintf (indirect_info, "%s-%d: %d\n",
- root_filename, which_file, file_top);
- #endif /* not MSDOS */
-
- free (split_file);
- indirect_info += strlen (indirect_info);
- which_file++;
- break;
- }
- }
- }
- }
-
- /* We have sucessfully created the subfiles. Now write out the
- original again. We must use `output_stream', or
- write_tag_table_indirect () won't know where to place the output. */
- output_stream = fopen (filename, "w");
- if (!output_stream)
- {
- perror (filename);
- exit (2);
- }
-
- {
- #ifdef MSDOS
- size_t distance;
- assert ((long) (indirect_info - the_file) < (1L << 16));
- distance = (size_t) (long) (indirect_info - the_file);
- #else /* not MSDOS */
- int distance = indirect_info - the_file;
- #endif /* not MSDOS */
- fwrite (the_file, 1, distance, output_stream);
-
- /* Inhibit newlines. */
- paragraph_is_open = false;
-
- write_tag_table_indirect ();
- fclose (output_stream);
- free (the_header);
- free (the_file);
- return;
- }
- }
- }
-
- /* Some menu hacking. This is used to remember menu references while
- reading the input file. After the output file has been written, if
- validation is on, then we use the contents of NODE_REFERENCES as a
- list of nodes to validate. */
- char *
- reftype_type_string (type)
- enum reftype type;
- {
- switch (type)
- {
- case menu_reference:
- return ("Menu");
- case followed_reference:
- return ("Followed-Reference");
- default:
- return ("Internal-bad-reference-type");
- }
- }
-
- /* Remember this node name for later validation use. */
- VOID
- remember_node_reference (node, line, type)
- char *node;
- int line;
- enum reftype type;
- {
- NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
-
- temp->next = node_references;
- temp->node = savestring (node);
- temp->line_no = line;
- temp->section = current_section;
- temp->type = type;
- temp->containing_node = savestring (current_node);
- temp->filename = node_filename;
-
- node_references = temp;
- }
-
- VOID
- validate_other_references (ref_list)
- register NODE_REF *ref_list;
- {
- char *old_input_filename = input_filename;
-
- while (ref_list != (NODE_REF *) NULL)
- {
- input_filename = ref_list->filename;
- validate (ref_list->node, ref_list->line_no,
- reftype_type_string (ref_list->type));
- ref_list = ref_list->next;
- }
- input_filename = old_input_filename;
- }
-
- /* Find NODE in REF_LIST. */
- NODE_REF *
- find_node_reference (node, ref_list)
- char *node;
- register NODE_REF *ref_list;
- {
- while (ref_list)
- {
- if (stricmp (node, ref_list->node) == 0)
- break;
- ref_list = ref_list->next;
- }
- return (ref_list);
- }
-
- VOID
- free_node_references ()
- {
- register NODE_REF *list, *temp;
-
- list = node_references;
-
- while (list)
- {
- temp = list;
- free (list->node);
- free (list->containing_node);
- list = list->next;
- free (temp);
- }
- node_references = (NODE_REF *) NULL;
- }
-
- #define menu_starter "* "
- VOID
- glean_node_from_menu ()
- {
- /* This function gets called at the start of every line while inside of
- a menu. It checks to see if the line starts with "* ", and if so,
- remembers the node reference that this menu refers to.
-
- input_text_offset is at the \n just before the line start. */
-
- SIZE_T i;
- LONG orig_offset = input_text_offset;
- char *nodename;
-
- if (strncmp (&input_text[input_text_offset + 1],
- menu_starter,
- strlen (menu_starter)) != 0)
- return;
- else
- input_text_offset += strlen (menu_starter) + 1;
-
- get_until_in_line (":", &nodename);
- if (curchar () == ':')
- input_text_offset++;
- canon_white (nodename);
-
- if (curchar () == ':')
- goto save_node;
- free (nodename);
- get_rest_of_line (&nodename);
-
- /* Special hack: If the nodename follows the menu item name,
- then we have to read the rest of the line in order to find
- out what the nodename is. But we still have to read the
- line later, in order to process any formatting commands that
- might be present. So un-count the carriage return that has just
- been counted. */
- line_number--;
-
- canon_white (nodename);
- for (i = 0; i < strlen (nodename); i++)
- {
- if (nodename[i] == '\t' ||
- nodename[i] == '.' ||
- nodename[i] == ',')
- {
- nodename[i] = '\0';
- break;
- }
- }
- save_node:
- normalize_node_name (nodename);
- i = strlen (nodename);
- if (i && nodename[i - 1] == ':')
- nodename[i - 1] = '\0';
-
- remember_node_reference (nodename, line_number, menu_reference);
- free (nodename);
- input_text_offset = orig_offset;
- }
-
- VOID
- cm_menu ()
- {
- begin_insertion (menu);
- }
-
- /* cont'd in makeinfo.e */
-
- /*===(cut here)===*/
-
-
- /*
- * Local Variables:
- * mode:C
- * ChangeLog:ChangeLog
- * End:
- */
-
-