1999-01-14 19:56:32 +00:00
|
|
|
|
/* Makeinfo -- convert Texinfo source files into Info files.
|
|
|
|
|
$Id: makeinfo.c,v 1.60 1998/02/25 20:36:22 karl Exp $
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
Copyright (C) 1987, 92, 93, 94, 95, 96, 97, 98
|
|
|
|
|
Free Software Foundation, Inc.
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
Makeinfo was authored by Brian Fox (bfox@ai.mit.edu). */
|
|
|
|
|
|
|
|
|
|
/* Indent #pragma so that older Cpp's don't try to parse it. */
|
|
|
|
|
#ifdef _AIX
|
|
|
|
|
#pragma alloca
|
|
|
|
|
#endif /* _AIX */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
int major_version = 1;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int minor_version = 68;
|
|
|
|
|
|
|
|
|
|
#include "system.h"
|
|
|
|
|
#include "getopt.h"
|
|
|
|
|
|
|
|
|
|
#ifdef TM_IN_SYS_TIME
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#endif /* !TM_IN_SYS_TIME */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
# undef alloca
|
|
|
|
|
# define alloca __builtin_alloca
|
|
|
|
|
#else
|
|
|
|
|
# ifdef HAVE_ALLOCA_H
|
|
|
|
|
# include <alloca.h>
|
|
|
|
|
# else
|
|
|
|
|
# ifndef _AIX
|
|
|
|
|
char *alloca ();
|
|
|
|
|
# endif
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* We'd like to take advantage of _doprnt if it's around, a la error.c,
|
|
|
|
|
but then we'd have no VA_SPRINTF. */
|
|
|
|
|
#if HAVE_VPRINTF
|
|
|
|
|
# if __STDC__
|
|
|
|
|
# include <stdarg.h>
|
|
|
|
|
# define VA_START(args, lastarg) va_start(args, lastarg)
|
|
|
|
|
# else
|
|
|
|
|
# include <varargs.h>
|
|
|
|
|
# define VA_START(args, lastarg) va_start(args)
|
|
|
|
|
# endif
|
|
|
|
|
# define VA_FPRINTF(file, fmt, ap) vfprintf (file, fmt, ap)
|
|
|
|
|
# define VA_SPRINTF(str, fmt, ap) vsprintf (str, fmt, ap)
|
|
|
|
|
#else /* not HAVE_VPRINTF */
|
|
|
|
|
# define VA_START(args, lastarg)
|
|
|
|
|
# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
|
|
|
|
|
# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
|
|
|
|
|
# define va_end(args)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* You can change some of the behavior of Makeinfo by changing the
|
1997-01-11 02:12:38 +00:00
|
|
|
|
following defines: */
|
|
|
|
|
|
|
|
|
|
/* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
|
|
|
|
|
appear within an @table, @ftable, or @itemize environment to have
|
|
|
|
|
standard paragraph indentation. Without this, such paragraphs have
|
|
|
|
|
no starting indentation. */
|
|
|
|
|
/* #define INDENT_PARAGRAPHS_IN_TABLE */
|
|
|
|
|
|
|
|
|
|
/* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
|
|
|
|
|
that @example should increase indentation by. This incremement is used
|
|
|
|
|
for all insertions which indent the enclosed text. */
|
|
|
|
|
#define DEFAULT_INDENTATION_INCREMENT 5
|
|
|
|
|
|
|
|
|
|
/* Define PARAGRAPH_START_INDENT to be the amount of indentation that
|
|
|
|
|
the first lines of paragraphs receive by default, where no other
|
|
|
|
|
value has been specified. Users can change this value on the command
|
|
|
|
|
line, with the --paragraph-indent option, or within the texinfo file,
|
|
|
|
|
with the @paragraphindent command. */
|
|
|
|
|
#define PARAGRAPH_START_INDENT 3
|
|
|
|
|
|
|
|
|
|
/* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
|
|
|
|
|
wish to appear between paragraphs. A value of 1 creates a single blank
|
|
|
|
|
line between paragraphs. Paragraphs are defined by 2 or more consecutive
|
|
|
|
|
newlines in the input file (i.e., one or more blank lines). */
|
|
|
|
|
#define DEFAULT_PARAGRAPH_SPACING 1
|
|
|
|
|
|
|
|
|
|
/* Define HAVE_MACROS to enable the macro facility of Texinfo. Using this
|
1999-01-14 19:56:32 +00:00
|
|
|
|
facility, users can create their own command procedures with
|
|
|
|
|
arguments. Must always be defined. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#define HAVE_MACROS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define COMPILING_MAKEINFO
|
|
|
|
|
#include "makeinfo.h"
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that we are currently hacking the insides of an
|
1997-01-11 02:12:38 +00:00
|
|
|
|
insertion which would use a fixed width font. */
|
|
|
|
|
static int in_fixed_width_font = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that start_paragraph () MUST be called before we pay
|
1997-01-11 02:12:38 +00:00
|
|
|
|
any attention to close_paragraph () calls. */
|
|
|
|
|
int must_start_paragraph = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means a string is in execution, as opposed to a file. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
static int executing_string = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means a macro string is in execution, as opposed to a file. */
|
|
|
|
|
static int me_executing_string = 0;
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
/* If non-NULL, this is an output stream to write the full macro expansion
|
1999-01-14 19:56:32 +00:00
|
|
|
|
of the input text to. The result is another texinfo file, but
|
1997-01-11 02:12:38 +00:00
|
|
|
|
missing @include, @infoinclude, @macro, and macro invocations. Instead,
|
|
|
|
|
all of the text is placed within the file. */
|
|
|
|
|
FILE *macro_expansion_output_stream = (FILE *)NULL;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *macro_expansion_filename;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Here is a structure used to remember input text strings and offsets
|
|
|
|
|
within them. */
|
|
|
|
|
typedef struct {
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *pointer; /* Pointer to the input text. */
|
|
|
|
|
int offset; /* Offset of the last character output. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
} ITEXT;
|
|
|
|
|
|
|
|
|
|
static ITEXT **itext_info = (ITEXT **)NULL;
|
|
|
|
|
static int itext_size = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means to inhibit writing macro expansions to the output
|
|
|
|
|
stream, because it has already been written. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int me_inhibit_expansion = 0;
|
|
|
|
|
|
|
|
|
|
ITEXT *remember_itext ();
|
|
|
|
|
void forget_itext (), me_append_before_this_command ();
|
|
|
|
|
void append_to_expansion_output (), write_region_to_macro_output ();
|
|
|
|
|
void maybe_write_itext (), me_execute_string ();
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Global Variables */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Global pointer to argv[0]. */
|
|
|
|
|
char *progname;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Return nonzero if STRING is the text at input_text + input_text_offset,
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else zero. */
|
|
|
|
|
#define looking_at(string) \
|
|
|
|
|
(strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
|
|
|
|
|
|
|
|
|
|
/* And writing to the output. */
|
|
|
|
|
|
|
|
|
|
/* The output file name. */
|
|
|
|
|
char *output_filename = (char *)NULL;
|
|
|
|
|
char *pretty_output_filename;
|
|
|
|
|
|
|
|
|
|
/* Name of the output file that the user elected to pass on the command line.
|
|
|
|
|
Such a name overrides any name found with the @setfilename command. */
|
|
|
|
|
char *command_output_filename = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
/* A colon separated list of directories to search for files included
|
|
|
|
|
with @include. This can be controlled with the `-I' option to makeinfo. */
|
|
|
|
|
char *include_files_path = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
/* Position in the output file. */
|
|
|
|
|
int output_position;
|
|
|
|
|
|
|
|
|
|
#define INITIAL_PARAGRAPH_SPACE 5000
|
|
|
|
|
int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero indicates that filling will take place on long lines. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int filling_enabled = 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that words are not to be split, even in long lines. This
|
1997-01-11 02:12:38 +00:00
|
|
|
|
gets changed for cm_w (). */
|
|
|
|
|
int non_splitting_words = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero indicates that filling a line also indents the new line. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int indented_fill = 0;
|
|
|
|
|
|
|
|
|
|
/* The amount of indentation to add at the starts of paragraphs.
|
|
|
|
|
0 means don't change existing indentation at paragraph starts.
|
|
|
|
|
> 0 is amount to indent new paragraphs by.
|
|
|
|
|
< 0 means indent to column zero by removing indentation if necessary.
|
|
|
|
|
|
|
|
|
|
This is normally zero, but some people prefer paragraph starts to be
|
|
|
|
|
somewhat more indented than paragraph bodies. A pretty value for
|
|
|
|
|
this is 3. */
|
|
|
|
|
int paragraph_start_indent = PARAGRAPH_START_INDENT;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that the use of paragraph_start_indent is inhibited.
|
1997-01-11 02:12:38 +00:00
|
|
|
|
@example uses this to line up the left columns of the example text.
|
|
|
|
|
A negative value for this variable is incremented each time it is used.
|
|
|
|
|
@noindent uses this to inhibit indentation for a single paragraph. */
|
|
|
|
|
int inhibit_paragraph_indentation = 0;
|
|
|
|
|
|
|
|
|
|
/* Indentation that is pending insertion. We have this for hacking lines
|
|
|
|
|
which look blank, but contain whitespace. We want to treat those as
|
|
|
|
|
blank lines. */
|
|
|
|
|
int pending_indent = 0;
|
|
|
|
|
|
|
|
|
|
/* The amount that indentation increases/decreases by. */
|
|
|
|
|
int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero indicates that indentation is temporarily turned off. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int no_indent = 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means forcing output text to be flushright. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int force_flush_right = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that the footnote style for this document was set on
|
1997-01-11 02:12:38 +00:00
|
|
|
|
the command line, which overrides any other settings. */
|
|
|
|
|
int footnote_style_preset = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that we automatically number footnotes that have no
|
1997-01-11 02:12:38 +00:00
|
|
|
|
specified marker. */
|
|
|
|
|
int number_footnotes = 1;
|
|
|
|
|
|
|
|
|
|
/* The current footnote number in this node. Each time a new node is
|
|
|
|
|
started this is reset to 1. */
|
|
|
|
|
int current_footnote_number = 1;
|
|
|
|
|
|
|
|
|
|
/* Command name in the process of being hacked. */
|
|
|
|
|
char *command;
|
|
|
|
|
|
|
|
|
|
/* The index in our internal command table of the currently
|
|
|
|
|
executing command. */
|
|
|
|
|
int command_index;
|
|
|
|
|
|
|
|
|
|
/* A search string which is used to find a line defining a node. */
|
|
|
|
|
char node_search_string[] =
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', 0 };
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* A search string which is used to find a line defining a menu. */
|
|
|
|
|
char menu_search_string[] =
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', 0 };
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* A search string which is used to find the first @setfilename. */
|
|
|
|
|
char setfilename_search[] =
|
|
|
|
|
{ COMMAND_PREFIX,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 };
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* A stack of file information records. If a new file is read in with
|
|
|
|
|
"@input", we remember the old input file state on this stack. */
|
|
|
|
|
typedef struct fstack
|
|
|
|
|
{
|
|
|
|
|
struct fstack *next;
|
|
|
|
|
char *filename;
|
|
|
|
|
char *text;
|
|
|
|
|
int size;
|
|
|
|
|
int offset;
|
|
|
|
|
int line_number;
|
|
|
|
|
} FSTACK;
|
|
|
|
|
|
|
|
|
|
FSTACK *filestack = (FSTACK *) NULL;
|
|
|
|
|
|
|
|
|
|
/* Stuff for nodes. */
|
|
|
|
|
/* The current nodes node name. */
|
|
|
|
|
char *current_node = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
/* The current nodes section level. */
|
|
|
|
|
int current_section = 0;
|
|
|
|
|
|
|
|
|
|
/* The filename of the current input file. This is never freed. */
|
|
|
|
|
char *node_filename = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
/* What we remember for each node. */
|
|
|
|
|
typedef struct tentry
|
|
|
|
|
{
|
|
|
|
|
struct tentry *next_ent;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *node; /* name of this node. */
|
|
|
|
|
char *prev; /* name of "Prev:" for this node. */
|
|
|
|
|
char *next; /* name of "Next:" for this node. */
|
|
|
|
|
char *up; /* name of "Up:" for this node. */
|
|
|
|
|
int position; /* output file position of this node. */
|
|
|
|
|
int line_no; /* defining line in source file. */
|
|
|
|
|
char *filename; /* The file that this node was found in. */
|
|
|
|
|
int touched; /* Nonzero means this node has been referenced. */
|
|
|
|
|
int flags; /* Room for growth. Right now, contains 1 bit. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
} TAG_ENTRY;
|
|
|
|
|
|
|
|
|
|
/* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
|
|
|
|
|
we turn on this flag bit in node-b's tag entry. This means that when
|
|
|
|
|
it is time to validate node-b, we don't report an additional error
|
|
|
|
|
if there was no "Prev" field. */
|
|
|
|
|
#define PREV_ERROR 0x1
|
|
|
|
|
#define NEXT_ERROR 0x2
|
|
|
|
|
#define UP_ERROR 0x4
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#define NO_WARN 0x8
|
|
|
|
|
#define IS_TOP 0x10
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Values for calling handle_variable_internal (). */
|
|
|
|
|
#define SET 1
|
|
|
|
|
#define CLEAR 2
|
|
|
|
|
#define IFSET 3
|
|
|
|
|
#define IFCLEAR 4
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#define ME_RECURSE 0x01
|
|
|
|
|
#define ME_QUOTE_ARG 0x02
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Macro definitions for user-defined commands. */
|
|
|
|
|
typedef struct {
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *name; /* Name of the macro. */
|
|
|
|
|
char **arglist; /* Args to replace when executing. */
|
|
|
|
|
char *body; /* Macro body. */
|
|
|
|
|
char *source_file; /* File where this macro is defined. */
|
|
|
|
|
int source_lineno; /* Line number within FILENAME. */
|
|
|
|
|
int inhibited; /* Nonzero means make find_macro () fail. */
|
|
|
|
|
int flags; /* ME_RECURSE, ME_QUOTE_ARG, etc. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
} MACRO_DEF;
|
|
|
|
|
|
|
|
|
|
void add_macro (), execute_macro ();
|
|
|
|
|
MACRO_DEF *find_macro (), *delete_macro ();
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
/* Menu reference, *note reference, and validation hacking. */
|
|
|
|
|
|
|
|
|
|
/* The various references that we know about. */
|
|
|
|
|
enum reftype
|
|
|
|
|
{
|
|
|
|
|
menu_reference, followed_reference
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* A structure to remember references with. A reference to a node is
|
|
|
|
|
either an entry in a menu, or a cross-reference made with [px]ref. */
|
|
|
|
|
typedef struct node_ref
|
|
|
|
|
{
|
|
|
|
|
struct node_ref *next;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *node; /* Name of node referred to. */
|
|
|
|
|
char *containing_node; /* Name of node containing this reference. */
|
|
|
|
|
int line_no; /* Line number where the reference occurs. */
|
|
|
|
|
int section; /* Section level where the reference occurs. */
|
|
|
|
|
char *filename; /* Name of file where the reference occurs. */
|
|
|
|
|
enum reftype type; /* Type of reference, either menu or note. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
} NODE_REF;
|
|
|
|
|
|
|
|
|
|
/* The linked list of such structures. */
|
|
|
|
|
NODE_REF *node_references = (NODE_REF *) NULL;
|
|
|
|
|
|
|
|
|
|
/* Flag which tells us whether to examine menu lines or not. */
|
|
|
|
|
int in_menu = 0;
|
|
|
|
|
|
|
|
|
|
/* Flag which tells us how to examine menu lines. */
|
|
|
|
|
int in_detailmenu = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that we have seen "@top" once already. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int top_node_seen = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that we have seen a non-"@top" node already. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int non_top_node_seen = 0;
|
|
|
|
|
|
|
|
|
|
/* Flags controlling the operation of the program. */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Default is to remove output if there were errors. */
|
|
|
|
|
int force = 0;
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Default is to notify users of bad choices. */
|
|
|
|
|
int print_warnings = 1;
|
|
|
|
|
|
|
|
|
|
/* Default is to check node references. */
|
|
|
|
|
int validating = 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means do not output "Node: Foo" for node separations. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int no_headers = 0;
|
|
|
|
|
|
|
|
|
|
/* Number of errors that we tolerate on a given fileset. */
|
|
|
|
|
int max_error_level = 100;
|
|
|
|
|
|
|
|
|
|
/* Maximum number of references to a single node before complaining. */
|
|
|
|
|
int reference_warning_limit = 1000;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means print out information about what is going on when it
|
1997-01-11 02:12:38 +00:00
|
|
|
|
is going on. */
|
|
|
|
|
int verbose_mode = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means to be relaxed about the input file. This is useful when
|
1997-01-11 02:12:38 +00:00
|
|
|
|
we can successfully format the input, but it doesn't strictly match our
|
|
|
|
|
somewhat pedantic ideas of correctness. Right now, it affects what
|
|
|
|
|
@table and @itemize do without arguments. */
|
|
|
|
|
int allow_lax_format = 0;
|
|
|
|
|
|
|
|
|
|
/* The list of commands that we hack in texinfo. Each one
|
|
|
|
|
has an associated function. When the command is encountered in the
|
|
|
|
|
text, the associated function is called with START as the argument.
|
|
|
|
|
If the function expects arguments in braces, it remembers itself on
|
|
|
|
|
the stack. When the corresponding close brace is encountered, the
|
|
|
|
|
function is called with END as the argument. */
|
|
|
|
|
|
|
|
|
|
#define START 0
|
|
|
|
|
#define END 1
|
|
|
|
|
|
|
|
|
|
typedef struct brace_element
|
|
|
|
|
{
|
|
|
|
|
struct brace_element *next;
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
int pos, line;
|
|
|
|
|
int in_fixed_width_font;
|
|
|
|
|
} BRACE_ELEMENT;
|
|
|
|
|
|
|
|
|
|
BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
|
|
|
|
|
|
|
|
|
|
extern void do_multitable ();
|
|
|
|
|
|
|
|
|
|
void print_version_info ();
|
|
|
|
|
void usage ();
|
|
|
|
|
void push_node_filename (), pop_node_filename ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
void remember_error (), flush_file_stack ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void convert_from_stream (), convert_from_file (), convert_from_loaded_file ();
|
|
|
|
|
void init_internals (), init_paragraph (), init_brace_stack ();
|
|
|
|
|
void init_insertion_stack (), init_indices ();
|
|
|
|
|
void init_tag_table (), write_tag_table (), write_tag_table_internal ();
|
|
|
|
|
void validate_file (), validate_other_references (), split_file ();
|
|
|
|
|
void free_node_references (), do_enumeration (), handle_variable ();
|
|
|
|
|
void handle_variable_internal ();
|
|
|
|
|
void normalize_node_name ();
|
|
|
|
|
void undefindex (), top_defindex (), gen_defindex ();
|
|
|
|
|
void define_user_command ();
|
|
|
|
|
void free_pending_notes (), output_pending_notes ();
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char **get_brace_args ();
|
|
|
|
|
char *expansion ();
|
|
|
|
|
int array_len ();
|
|
|
|
|
void free_array ();
|
|
|
|
|
static int end_of_sentence_p ();
|
|
|
|
|
static void isolate_nodename ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void reader_loop (), read_command ();
|
|
|
|
|
void remember_brace (), remember_brace_1 ();
|
|
|
|
|
void pop_and_call_brace (), discard_braces ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
void add_word (), add_char (), insert (), flush_output ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void insert_string ();
|
|
|
|
|
void close_paragraph_with_lines (), close_paragraph ();
|
|
|
|
|
void ignore_blank_line ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
void do_flush_right_indentation (), discard_insertions ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void start_paragraph (), indent ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#if defined (VA_FPRINTF) && __STDC__
|
|
|
|
|
/* Unfortunately we must use prototypes if we are to use <stdarg.h>. */
|
|
|
|
|
void add_word_args (char *, ...);
|
|
|
|
|
void execute_string (char *, ...);
|
|
|
|
|
#else
|
|
|
|
|
void add_word_args ();
|
|
|
|
|
void execute_string ();
|
|
|
|
|
#endif /* will not use prototypes */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
void insert_self (), insert_space (), cm_ignore_line ();
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_TeX (), cm_asterisk (), cm_bullet (), cm_cite (),
|
|
|
|
|
cm_code (), cm_copyright (), cm_ctrl (), cm_dfn (), cm_dircategory (),
|
|
|
|
|
cm_direntry (), cm_dots (), cm_emph (), cm_enddots (),
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_kbd (), cm_key (), cm_no_op (), cm_no_op_line_arg (),
|
|
|
|
|
cm_not_fixed_width (), cm_strong (), cm_var_sc (), cm_w (), cm_image ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Sectioning. */
|
|
|
|
|
void
|
|
|
|
|
cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (),
|
|
|
|
|
cm_section (), cm_unnumberedsec (), cm_appendixsec (),
|
|
|
|
|
cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
|
|
|
|
|
cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
|
|
|
|
|
cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
|
|
|
|
|
cm_majorheading (), cm_raisesections (), cm_lowersections ();
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* All @def... commands map to cm_defun, most accent commands map to
|
1997-01-11 02:12:38 +00:00
|
|
|
|
cm_accent, most non-English letters map to cm_special_char. */
|
|
|
|
|
void cm_defun (), cm_accent (), cm_special_char (), cm_dotless ();
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_node (), cm_menu (), cm_xref (), cm_ftable (), cm_vtable (), cm_pxref (),
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_inforef (), cm_uref (), cm_email (), cm_quotation (),
|
|
|
|
|
cm_display (), cm_itemize (),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
cm_enumerate (), cm_tab (), cm_table (), cm_itemx (), cm_noindent (),
|
|
|
|
|
cm_setfilename (), cm_br (), cm_sp (), cm_page (), cm_group (),
|
|
|
|
|
cm_center (), cm_include (), cm_bye (), cm_item (), cm_end (),
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_ifinfo (), cm_ifnothtml (), cm_ifnottex (), cm_kindex (), cm_cindex (),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
cm_findex (), cm_pindex (), cm_vindex (), cm_tindex (),
|
|
|
|
|
cm_synindex (), cm_printindex (), cm_minus (), cm_footnote (),
|
|
|
|
|
cm_example (), cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (),
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_defindex (), cm_defcodeindex (), cm_result (), cm_expansion (),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
cm_equiv (), cm_print (), cm_error (), cm_point (), cm_today (),
|
|
|
|
|
cm_flushleft (), cm_flushright (), cm_smalllisp (), cm_finalout (),
|
|
|
|
|
cm_cartouche (), cm_detailmenu (), cm_multitable ();
|
|
|
|
|
|
|
|
|
|
/* Conditionals. */
|
|
|
|
|
void cm_set (), cm_clear (), cm_ifset (), cm_ifclear ();
|
|
|
|
|
void cm_value (), cm_ifeq ();
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
/* Define a user-defined command which is simple substitution. */
|
|
|
|
|
void cm_macro (), cm_unmacro ();
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
/* Options. */
|
|
|
|
|
void cm_paragraphindent (), cm_footnotestyle ();
|
|
|
|
|
|
|
|
|
|
/* Internals. */
|
|
|
|
|
void command_name_condition (), misplaced_brace (), cm_obsolete (),
|
|
|
|
|
cm_ideprecated ();
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
int argument_in_braces;
|
|
|
|
|
} COMMAND;
|
|
|
|
|
|
|
|
|
|
/* Stuff for defining commands on the fly. */
|
|
|
|
|
COMMAND **user_command_array = (COMMAND **) NULL;
|
|
|
|
|
int user_command_array_len = 0;
|
|
|
|
|
|
|
|
|
|
#define NO_BRACE_ARGS 0
|
|
|
|
|
#define BRACE_ARGS 1
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
static COMMAND command_table[] = {
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "\t", insert_space, NO_BRACE_ARGS },
|
|
|
|
|
{ "\n", insert_space, NO_BRACE_ARGS },
|
|
|
|
|
{ " ", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "!", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "\"", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "'", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "*", cm_asterisk, NO_BRACE_ARGS },
|
|
|
|
|
{ ",", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "-", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ ".", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ ":", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "=", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "?", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "@", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "^", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "`", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "{", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "|", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "}", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "~", insert_self, NO_BRACE_ARGS },
|
|
|
|
|
{ "AA", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "AE", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "H", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "L", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "O", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "OE", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "TeX", cm_TeX, BRACE_ARGS },
|
|
|
|
|
{ "aa", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "ae", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "appendix", cm_appendix, NO_BRACE_ARGS },
|
|
|
|
|
{ "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
|
|
|
|
|
{ "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
|
|
|
|
|
{ "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
|
|
|
|
|
{ "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
|
|
|
|
|
{ "asis", cm_no_op, BRACE_ARGS },
|
|
|
|
|
{ "b", cm_not_fixed_width, BRACE_ARGS },
|
|
|
|
|
{ "bullet", cm_bullet, BRACE_ARGS },
|
|
|
|
|
{ "bye", cm_bye, NO_BRACE_ARGS },
|
|
|
|
|
{ "c", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "cartouche", cm_cartouche, NO_BRACE_ARGS },
|
|
|
|
|
{ "center", cm_center, NO_BRACE_ARGS },
|
|
|
|
|
{ "centerchap", cm_unnumbered, NO_BRACE_ARGS },
|
|
|
|
|
{ "chapheading", cm_chapheading, NO_BRACE_ARGS },
|
|
|
|
|
{ "chapter", cm_chapter, NO_BRACE_ARGS },
|
|
|
|
|
{ "cindex", cm_cindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "cite", cm_cite, BRACE_ARGS },
|
|
|
|
|
{ "clear", cm_clear, NO_BRACE_ARGS },
|
|
|
|
|
{ "code", cm_code, BRACE_ARGS },
|
|
|
|
|
{ "comment", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "contents", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "copyright", cm_copyright, BRACE_ARGS },
|
|
|
|
|
{ "ctrl", cm_obsolete, BRACE_ARGS },
|
|
|
|
|
{ "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "defindex", cm_defindex, NO_BRACE_ARGS },
|
|
|
|
|
/* The `def' commands. */
|
|
|
|
|
{ "defcv", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defcvx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deffn", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deffnx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defivar", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defivarx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defmac", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defmacx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defmethod", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defmethodx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defop", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defopt", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defoptx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defopx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defspec", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defspecx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftp", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftpx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypefn", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypefnx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypefun", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypefunx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypemethod", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypemethodx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypevar", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypevarx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypevr", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "deftypevrx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defun", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defunx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defvar", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defvarx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defvr", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
{ "defvrx", cm_defun, NO_BRACE_ARGS },
|
|
|
|
|
/* The end of the `def' commands. */
|
|
|
|
|
{ "detailmenu", cm_detailmenu, NO_BRACE_ARGS },
|
|
|
|
|
{ "dfn", cm_dfn, BRACE_ARGS },
|
|
|
|
|
{ "dircategory", cm_dircategory, NO_BRACE_ARGS },
|
|
|
|
|
{ "direntry", cm_direntry, NO_BRACE_ARGS },
|
|
|
|
|
{ "display", cm_display, NO_BRACE_ARGS },
|
|
|
|
|
{ "dmn", cm_no_op, BRACE_ARGS },
|
|
|
|
|
{ "dotaccent", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "dotless", cm_dotless, BRACE_ARGS },
|
|
|
|
|
{ "dots", cm_dots, BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "email", cm_email, BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "emph", cm_emph, BRACE_ARGS },
|
|
|
|
|
{ "end", cm_end, NO_BRACE_ARGS },
|
|
|
|
|
{ "enddots", cm_enddots, BRACE_ARGS },
|
|
|
|
|
{ "enumerate", cm_enumerate, NO_BRACE_ARGS },
|
|
|
|
|
{ "equiv", cm_equiv, BRACE_ARGS },
|
|
|
|
|
{ "error", cm_error, BRACE_ARGS },
|
|
|
|
|
{ "example", cm_example, NO_BRACE_ARGS },
|
|
|
|
|
{ "exclamdown", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "exdent", cm_exdent, NO_BRACE_ARGS },
|
|
|
|
|
{ "expansion", cm_expansion, BRACE_ARGS },
|
|
|
|
|
{ "file", cm_code, BRACE_ARGS },
|
|
|
|
|
{ "finalout", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "findex", cm_findex, NO_BRACE_ARGS },
|
|
|
|
|
{ "flushleft", cm_flushleft, NO_BRACE_ARGS },
|
|
|
|
|
{ "flushright", cm_flushright, NO_BRACE_ARGS },
|
|
|
|
|
{ "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
|
|
|
|
|
{ "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
|
|
|
|
|
{ "format", cm_format, NO_BRACE_ARGS },
|
|
|
|
|
{ "ftable", cm_ftable, NO_BRACE_ARGS },
|
|
|
|
|
{ "group", cm_group, NO_BRACE_ARGS },
|
|
|
|
|
{ "heading", cm_heading, NO_BRACE_ARGS },
|
|
|
|
|
{ "headings", cm_ignore_line, NO_BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "html", command_name_condition, NO_BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "hyphenation", cm_no_op, BRACE_ARGS },
|
|
|
|
|
{ "i", cm_not_fixed_width, BRACE_ARGS },
|
|
|
|
|
{ "ifclear", cm_ifclear, NO_BRACE_ARGS },
|
|
|
|
|
{ "ifeq", cm_ifeq, NO_BRACE_ARGS },
|
|
|
|
|
{ "ifhtml", command_name_condition, NO_BRACE_ARGS },
|
|
|
|
|
{ "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "ifnothtml", cm_ifnothtml, NO_BRACE_ARGS },
|
|
|
|
|
{ "ifnotinfo", command_name_condition, NO_BRACE_ARGS },
|
|
|
|
|
{ "ifnottex", cm_ifnottex, NO_BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "ifset", cm_ifset, NO_BRACE_ARGS },
|
|
|
|
|
{ "iftex", command_name_condition, NO_BRACE_ARGS },
|
|
|
|
|
{ "ignore", command_name_condition, NO_BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "image", cm_image, BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "include", cm_include, NO_BRACE_ARGS },
|
|
|
|
|
{ "inforef", cm_inforef, BRACE_ARGS },
|
|
|
|
|
{ "item", cm_item, NO_BRACE_ARGS },
|
|
|
|
|
{ "itemize", cm_itemize, NO_BRACE_ARGS },
|
|
|
|
|
{ "itemx", cm_itemx, NO_BRACE_ARGS },
|
|
|
|
|
{ "kbd", cm_kbd, BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "kbdinputstyle", cm_no_op_line_arg, NO_BRACE_ARGS },
|
|
|
|
|
{ "key", cm_key, BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "kindex", cm_kindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "l", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "lisp", cm_lisp, NO_BRACE_ARGS },
|
|
|
|
|
{ "lowersections", cm_lowersections, NO_BRACE_ARGS },
|
|
|
|
|
{ "macro", cm_macro, NO_BRACE_ARGS },
|
|
|
|
|
{ "majorheading", cm_majorheading, NO_BRACE_ARGS },
|
|
|
|
|
{ "math", cm_no_op, BRACE_ARGS },
|
|
|
|
|
{ "menu", cm_menu, NO_BRACE_ARGS },
|
|
|
|
|
{ "minus", cm_minus, BRACE_ARGS },
|
|
|
|
|
{ "multitable", cm_multitable, NO_BRACE_ARGS },
|
|
|
|
|
{ "need", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "node", cm_node, NO_BRACE_ARGS },
|
|
|
|
|
{ "noindent", cm_noindent, NO_BRACE_ARGS },
|
|
|
|
|
{ "nwnode", cm_node, NO_BRACE_ARGS },
|
|
|
|
|
{ "o", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "oe", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "page", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
|
|
|
|
|
{ "pindex", cm_pindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "point", cm_point, BRACE_ARGS },
|
|
|
|
|
{ "pounds", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "print", cm_print, BRACE_ARGS },
|
|
|
|
|
{ "printindex", cm_printindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "pxref", cm_pxref, BRACE_ARGS },
|
|
|
|
|
{ "questiondown", cm_special_char, BRACE_ARGS },
|
|
|
|
|
{ "quotation", cm_quotation, NO_BRACE_ARGS },
|
|
|
|
|
{ "r", cm_not_fixed_width, BRACE_ARGS },
|
|
|
|
|
{ "raisesections", cm_raisesections, NO_BRACE_ARGS },
|
|
|
|
|
{ "ref", cm_xref, BRACE_ARGS },
|
|
|
|
|
{ "refill", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "result", cm_result, BRACE_ARGS },
|
|
|
|
|
{ "ringaccent", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "samp", cm_code, BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "sc", cm_var_sc, BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "section", cm_section, NO_BRACE_ARGS },
|
|
|
|
|
{ "set", cm_set, NO_BRACE_ARGS },
|
|
|
|
|
{ "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "setchapterstyle", cm_obsolete, NO_BRACE_ARGS },
|
|
|
|
|
{ "setfilename", cm_setfilename, NO_BRACE_ARGS },
|
|
|
|
|
{ "settitle", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "shortcontents", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "smallbook", cm_ignore_line, NO_BRACE_ARGS },
|
|
|
|
|
{ "smallexample", cm_smallexample, NO_BRACE_ARGS },
|
|
|
|
|
{ "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
|
|
|
|
|
{ "sp", cm_sp, NO_BRACE_ARGS },
|
|
|
|
|
{ "ss", insert_self, BRACE_ARGS },
|
|
|
|
|
{ "strong", cm_strong, BRACE_ARGS },
|
|
|
|
|
{ "subheading", cm_subheading, NO_BRACE_ARGS },
|
|
|
|
|
{ "subsection", cm_subsection, NO_BRACE_ARGS },
|
|
|
|
|
{ "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
|
|
|
|
|
{ "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
|
|
|
|
|
{ "summarycontents", cm_no_op, NO_BRACE_ARGS },
|
|
|
|
|
{ "syncodeindex", cm_synindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "synindex", cm_synindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "t", cm_no_op, BRACE_ARGS },
|
|
|
|
|
{ "tab", cm_tab, NO_BRACE_ARGS },
|
|
|
|
|
{ "table", cm_table, NO_BRACE_ARGS },
|
|
|
|
|
{ "tex", command_name_condition, NO_BRACE_ARGS },
|
|
|
|
|
{ "tieaccent", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "tindex", cm_tindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "titlefont", cm_not_fixed_width, BRACE_ARGS },
|
|
|
|
|
{ "titlepage", command_name_condition, NO_BRACE_ARGS },
|
|
|
|
|
{ "today", cm_today, BRACE_ARGS },
|
|
|
|
|
{ "top", cm_top, NO_BRACE_ARGS },
|
|
|
|
|
{ "u", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "ubaraccent", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "udotaccent", cm_accent, BRACE_ARGS },
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
{ "unmacro", cm_unmacro, NO_BRACE_ARGS },
|
|
|
|
|
#endif
|
|
|
|
|
{ "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
|
|
|
|
|
{ "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
|
|
|
|
|
{ "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
|
|
|
|
|
{ "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "uref", cm_uref, BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "url", cm_code, BRACE_ARGS },
|
|
|
|
|
{ "v", cm_accent, BRACE_ARGS },
|
|
|
|
|
{ "value", cm_value, BRACE_ARGS },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "var", cm_var_sc, BRACE_ARGS },
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "vindex", cm_vindex, NO_BRACE_ARGS },
|
|
|
|
|
{ "vtable", cm_vtable, NO_BRACE_ARGS },
|
|
|
|
|
{ "w", cm_w, BRACE_ARGS },
|
|
|
|
|
{ "xref", cm_xref, BRACE_ARGS },
|
|
|
|
|
|
|
|
|
|
/* Deprecated commands. These used to be for italics. */
|
|
|
|
|
{ "iappendix", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iappendixsec", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iappendixsection", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iappendixsubsec", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iappendixsubsubsec", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "ichapter", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "isection", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "isubsection", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "isubsubsection", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iunnumbered", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iunnumberedsec", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iunnumberedsubsec", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
{ "iunnumberedsubsubsec", cm_ideprecated, NO_BRACE_ARGS },
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Now @include does what this was used to. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "infoinclude", cm_obsolete, NO_BRACE_ARGS },
|
|
|
|
|
{ "titlespec", cm_obsolete, NO_BRACE_ARGS },
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ NULL, NULL, NO_BRACE_ARGS }
|
|
|
|
|
};
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
struct option long_options[] =
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "error-limit", 1, 0, 'e' }, /* formerly -el */
|
|
|
|
|
{ "fill-column", 1, 0, 'f' }, /* formerly -fc */
|
|
|
|
|
{ "footnote-style", 1, 0, 's' }, /* formerly -ft */
|
|
|
|
|
{ "force", 0, 0, 'F' }, /* do not remove output */
|
|
|
|
|
{ "no-headers", 0, &no_headers, 1 }, /* do not output Node: foo */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "no-validate", 0, &validating, 0 }, /* formerly -nv */
|
|
|
|
|
{ "no-split", 0, &splitting, 0 }, /* formerly -ns */
|
|
|
|
|
{ "no-warn", 0, &print_warnings, 0 }, /* formerly -nw */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "macro-expand", 1, 0, 'E' },
|
|
|
|
|
{ "number-footnotes", 0, &number_footnotes, 1 },
|
|
|
|
|
{ "no-number-footnotes", 0, &number_footnotes, 0 },
|
|
|
|
|
{ "output", 1, 0, 'o' },
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{ "paragraph-indent", 1, 0, 'p' }, /* formerly -pi */
|
|
|
|
|
{ "reference-limit", 1, 0, 'r' }, /* formerly -rl */
|
|
|
|
|
{ "verbose", 0, &verbose_mode, 1 }, /* formerly -verbose */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{ "help", 0, 0, 'h' },
|
|
|
|
|
{ "version", 0, 0, 'V' },
|
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
|
};
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
/* */
|
|
|
|
|
/* Error Handling */
|
|
|
|
|
/* */
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Number of errors encountered. */
|
|
|
|
|
int errors_printed = 0;
|
|
|
|
|
|
|
|
|
|
/* Print the last error gotten from the file system. */
|
|
|
|
|
int
|
|
|
|
|
fs_error (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
remember_error ();
|
|
|
|
|
perror (filename);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print an error message, and return false. */
|
|
|
|
|
void
|
|
|
|
|
#if defined (VA_FPRINTF) && __STDC__
|
|
|
|
|
error (char *format, ...)
|
|
|
|
|
#else
|
|
|
|
|
error (format, va_alist)
|
|
|
|
|
char *format;
|
|
|
|
|
va_dcl
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
va_list ap;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
remember_error ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
VA_START (ap, format);
|
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
VA_FPRINTF (stderr, format, ap);
|
|
|
|
|
#else
|
|
|
|
|
fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
|
|
|
|
|
#endif /* not VA_FPRINTF */
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
|
|
putc ('\n', stderr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Just like error (), but print the line number as well. */
|
|
|
|
|
void
|
|
|
|
|
#if defined (VA_FPRINTF) && __STDC__
|
|
|
|
|
line_error (char *format, ...)
|
|
|
|
|
#else
|
|
|
|
|
line_error (format, va_alist)
|
|
|
|
|
char *format;
|
|
|
|
|
va_dcl
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
va_list ap;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
remember_error ();
|
|
|
|
|
fprintf (stderr, "%s:%d: ", input_filename, line_number);
|
|
|
|
|
|
|
|
|
|
VA_START (ap, format);
|
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
VA_FPRINTF (stderr, format, ap);
|
|
|
|
|
#else
|
|
|
|
|
fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
|
|
|
|
|
#endif /* not VA_FPRINTF */
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
|
|
fprintf (stderr, ".\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
#if defined (VA_FPRINTF) && __STDC__
|
|
|
|
|
warning (char *format, ...)
|
|
|
|
|
#else
|
|
|
|
|
warning (format, va_alist)
|
|
|
|
|
char *format;
|
|
|
|
|
va_dcl
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
va_list ap;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (print_warnings)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number);
|
|
|
|
|
|
|
|
|
|
VA_START (ap, format);
|
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
VA_FPRINTF (stderr, format, ap);
|
|
|
|
|
#else
|
|
|
|
|
fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
|
|
|
|
|
#endif /* not VA_FPRINTF */
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
|
|
fprintf (stderr, ".\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Remember that an error has been printed. If more than
|
|
|
|
|
max_error_level have been printed, then exit the program. */
|
|
|
|
|
void
|
|
|
|
|
remember_error ()
|
|
|
|
|
{
|
|
|
|
|
errors_printed++;
|
|
|
|
|
if (max_error_level && (errors_printed > max_error_level))
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("Too many errors! Gave up.\n"));
|
|
|
|
|
flush_file_stack ();
|
|
|
|
|
cm_bye ();
|
|
|
|
|
exit (FATAL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Main () Start of code */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* For each file mentioned in the command line, process it, turning
|
|
|
|
|
Texinfo commands into wonderfully formatted output text. */
|
|
|
|
|
int
|
|
|
|
|
main (argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
{
|
|
|
|
|
extern int errors_printed;
|
|
|
|
|
char *filename_part ();
|
|
|
|
|
int c, ind;
|
|
|
|
|
int reading_from_stdin = 0;
|
|
|
|
|
|
|
|
|
|
/* The name of this program is the last filename in argv[0]. */
|
|
|
|
|
progname = filename_part (argv[0]);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#ifdef HAVE_SETLOCALE
|
|
|
|
|
/* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing
|
|
|
|
|
of the argument to @multicolumn. */
|
|
|
|
|
setlocale (LC_TIME, "");
|
|
|
|
|
setlocale (LC_MESSAGES, "");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Set the text message domain. */
|
|
|
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
|
textdomain (PACKAGE);
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Parse argument flags from the input line. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while ((c = getopt_long (argc, argv, "D:e:E:f:I:o:p:P:r:s:U:V",
|
|
|
|
|
long_options, &ind)) != EOF)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (c == 0 && long_options[ind].flag == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
c = long_options[ind].val;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
switch (c)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case 'D':
|
|
|
|
|
case 'U':
|
|
|
|
|
/* User specified variable to set or clear. */
|
|
|
|
|
handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'e':
|
|
|
|
|
/* User specified error level. */
|
|
|
|
|
if (sscanf (optarg, "%d", &max_error_level) != 1)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
_("%s: %s arg must be numeric, not `%s'.\n"),
|
|
|
|
|
"--error-limit", progname, optarg);
|
|
|
|
|
usage (stderr, FATAL);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
/* User specified a macro expansion output file. */
|
|
|
|
|
if (!macro_expansion_output_stream)
|
|
|
|
|
{
|
|
|
|
|
macro_expansion_filename = optarg;
|
|
|
|
|
macro_expansion_output_stream
|
|
|
|
|
= strcmp (optarg, "-") == 0 ? stdout : fopen (optarg, "w");
|
|
|
|
|
if (!macro_expansion_output_stream)
|
|
|
|
|
error (_("Couldn't open macro expansion output `%s'"), optarg);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
error (_("Cannot specify more than one macro expansion output"));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
|
/* User specified fill_column. */
|
|
|
|
|
if (sscanf (optarg, "%d", &fill_column) != 1)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
_("%s: %s arg must be numeric, not `%s'.\n"),
|
|
|
|
|
"--fill-column", progname, optarg);
|
|
|
|
|
usage (FATAL);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'F':
|
|
|
|
|
force++; /* Do not remove erroneous output. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
|
usage (NO_ERROR);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'I':
|
|
|
|
|
/* Append user-specified dir to include file path. */
|
|
|
|
|
if (!include_files_path)
|
|
|
|
|
include_files_path = xstrdup (".");
|
|
|
|
|
|
|
|
|
|
include_files_path = (char *)
|
|
|
|
|
xrealloc (include_files_path,
|
|
|
|
|
2 + strlen (include_files_path) + strlen (optarg));
|
|
|
|
|
strcat (include_files_path, ":");
|
|
|
|
|
strcat (include_files_path, optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
|
/* User specified output file. */
|
|
|
|
|
command_output_filename = xstrdup (optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
|
/* User specified paragraph indent (paragraph_start_index). */
|
|
|
|
|
if (set_paragraph_indent (optarg) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
_("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
progname, optarg);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
usage (FATAL);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'P':
|
|
|
|
|
/* Prepend user-specified include dir to include path. */
|
|
|
|
|
if (!include_files_path)
|
|
|
|
|
{
|
|
|
|
|
include_files_path = xstrdup (optarg);
|
|
|
|
|
include_files_path = (char *) xrealloc (include_files_path,
|
|
|
|
|
strlen (include_files_path) + 3); /* 3 for ":.\0" */
|
|
|
|
|
strcat (include_files_path, ":.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *tmp = xstrdup (include_files_path);
|
|
|
|
|
include_files_path = (char *) xrealloc (include_files_path,
|
|
|
|
|
strlen (include_files_path) + strlen (optarg) + 2); /* 2 for ":\0" */
|
|
|
|
|
strcpy (include_files_path, optarg);
|
|
|
|
|
strcat (include_files_path, ":");
|
|
|
|
|
strcat (include_files_path, tmp);
|
|
|
|
|
free (tmp);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'r':
|
|
|
|
|
/* User specified reference warning limit. */
|
|
|
|
|
if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
_("%s: %s arg must be numeric, not `%s'.\n"),
|
|
|
|
|
"--reference-limit", progname, optarg);
|
|
|
|
|
usage (FATAL);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
|
/* User specified footnote style. */
|
|
|
|
|
if (set_footnote_style (optarg) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
_("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
progname, optarg);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
usage (FATAL);
|
|
|
|
|
}
|
|
|
|
|
footnote_style_preset = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'V':
|
|
|
|
|
/* User requested version info. */
|
|
|
|
|
print_version_info ();
|
|
|
|
|
printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
|
1997-01-11 02:12:38 +00:00
|
|
|
|
There is NO warranty. You may redistribute this software\n\
|
|
|
|
|
under the terms of the GNU General Public License.\n\
|
1999-01-14 19:56:32 +00:00
|
|
|
|
For more information about these matters, see the files named COPYING.\n"),
|
|
|
|
|
"1998");
|
|
|
|
|
exit (NO_ERROR);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
|
usage (FATAL);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (optind == argc)
|
|
|
|
|
{
|
|
|
|
|
/* Check to see if input is a file. If so, process that. */
|
|
|
|
|
if (!isatty (fileno (stdin)))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
reading_from_stdin = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
fprintf (stderr, _("%s: missing file argument.\n"), progname);
|
|
|
|
|
usage (FATAL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the user has specified --no-headers, this should imply --no-split.
|
|
|
|
|
Do that here. I think it might also imply that we should ignore the
|
|
|
|
|
setfilename at the top of the file, but this might break some FSF things,
|
|
|
|
|
so I will hold off on that. */
|
|
|
|
|
if (no_headers)
|
|
|
|
|
{
|
|
|
|
|
splitting = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* If the user has not specified an output file, use stdout. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!command_output_filename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
command_output_filename = xstrdup ("-");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verbose_mode)
|
|
|
|
|
print_version_info ();
|
|
|
|
|
|
|
|
|
|
/* Remaining arguments are file names of texinfo files.
|
|
|
|
|
Convert them, one by one. */
|
|
|
|
|
if (!reading_from_stdin)
|
|
|
|
|
{
|
|
|
|
|
while (optind != argc)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
convert_from_file (argv[optind++]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
convert_from_stream (stdin, "stdin");
|
|
|
|
|
|
|
|
|
|
if (errors_printed)
|
|
|
|
|
return (SYNTAX);
|
|
|
|
|
else
|
|
|
|
|
return (NO_ERROR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Display the version info of this invocation of Makeinfo. */
|
|
|
|
|
void
|
|
|
|
|
print_version_info ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
printf ("makeinfo (GNU %s %s) %d.%d\n", PACKAGE, VERSION,
|
|
|
|
|
major_version, minor_version);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
/* */
|
|
|
|
|
/* Generic Utilities */
|
|
|
|
|
/* */
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
memory_error (callers_name, bytes_wanted)
|
|
|
|
|
char *callers_name;
|
|
|
|
|
int bytes_wanted;
|
|
|
|
|
{
|
|
|
|
|
char printable_string[80];
|
|
|
|
|
|
|
|
|
|
sprintf (printable_string,
|
|
|
|
|
"Virtual memory exhausted in %s ()! Needed %d bytes.",
|
|
|
|
|
callers_name, bytes_wanted);
|
|
|
|
|
|
|
|
|
|
error (printable_string);
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Just like malloc, but kills the program in case of fatal error. */
|
|
|
|
|
void *
|
|
|
|
|
xmalloc (nbytes)
|
|
|
|
|
unsigned int nbytes;
|
|
|
|
|
{
|
|
|
|
|
void *temp = (void *) malloc (nbytes);
|
|
|
|
|
|
|
|
|
|
if (nbytes && temp == (void *)NULL)
|
|
|
|
|
memory_error ("xmalloc", nbytes);
|
|
|
|
|
|
|
|
|
|
return (temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Like realloc (), but barfs if there isn't enough memory. */
|
|
|
|
|
void *
|
|
|
|
|
xrealloc (pointer, nbytes)
|
|
|
|
|
void *pointer;
|
|
|
|
|
unsigned int nbytes;
|
|
|
|
|
{
|
|
|
|
|
void *temp;
|
|
|
|
|
|
|
|
|
|
if (!pointer)
|
|
|
|
|
temp = (void *)xmalloc (nbytes);
|
|
|
|
|
else
|
|
|
|
|
temp = (void *)realloc (pointer, nbytes);
|
|
|
|
|
|
1999-01-15 12:24:56 +00:00
|
|
|
|
if (nbytes && temp == (void *)NULL)
|
|
|
|
|
memory_error ("xrealloc", nbytes);
|
|
|
|
|
|
|
|
|
|
return (temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* If EXIT_VALUE is zero, print the full usage message to stdout.
|
|
|
|
|
Otherwise, just say to use --help for more info.
|
|
|
|
|
Then exit with EXIT_VALUE. */
|
|
|
|
|
void
|
|
|
|
|
usage (exit_value)
|
|
|
|
|
int exit_value;
|
|
|
|
|
{
|
|
|
|
|
if (exit_value != 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
fprintf (stderr, _("Try `%s --help' for more information.\n"), progname);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n\
|
1997-01-11 02:12:38 +00:00
|
|
|
|
\n\
|
|
|
|
|
Translate Texinfo source documentation to a format suitable for reading\n\
|
|
|
|
|
with GNU Info.\n\
|
|
|
|
|
\n\
|
|
|
|
|
Options:\n\
|
|
|
|
|
-D VAR define a variable, as with @set.\n\
|
1999-01-14 19:56:32 +00:00
|
|
|
|
-E MACRO-OFILE process macros only, output texinfo source.\n\
|
|
|
|
|
-I DIR append DIR to the @include directory search path.\n\
|
|
|
|
|
-P DIR prepend DIR to the @include directory search path.\n\
|
1997-01-11 02:12:38 +00:00
|
|
|
|
-U VAR undefine a variable, as with @clear.\n\
|
|
|
|
|
--error-limit NUM quit after NUM errors (default %d).\n\
|
|
|
|
|
--fill-column NUM break lines at NUM characters (default %d).\n\
|
|
|
|
|
--footnote-style STYLE output footnotes according to STYLE:\n\
|
|
|
|
|
`separate' to place footnotes in their own node,\n\
|
|
|
|
|
`end' to place the footnotes at the end of\n\
|
|
|
|
|
the node in which they are defined (the default).\n\
|
1999-01-14 19:56:32 +00:00
|
|
|
|
--force preserve output even if errors.\n\
|
1997-01-11 02:12:38 +00:00
|
|
|
|
--help display this help and exit.\n\
|
|
|
|
|
--no-validate suppress node cross-reference validation.\n\
|
|
|
|
|
--no-warn suppress warnings (but not errors).\n\
|
|
|
|
|
--no-split suppress splitting of large files.\n\
|
|
|
|
|
--no-headers suppress node separators and Node: Foo headers.\n\
|
|
|
|
|
--output FILE, -o FILE output to FILE, and ignore any @setfilename.\n\
|
1999-01-14 19:56:32 +00:00
|
|
|
|
--paragraph-indent VAL indent paragraphs with VAL spaces (default %d).\n\
|
|
|
|
|
if VAL is `none', do not indent; if VAL is `asis',\n\
|
|
|
|
|
preserve any existing indentation.\n\
|
1997-01-11 02:12:38 +00:00
|
|
|
|
--reference-limit NUM complain about at most NUM references (default %d).\n\
|
|
|
|
|
--verbose report about what is being done.\n\
|
|
|
|
|
--version display version information and exit.\n\
|
|
|
|
|
\n\
|
1999-01-14 19:56:32 +00:00
|
|
|
|
Email bug reports to bug-texinfo@gnu.org.\n\
|
|
|
|
|
"),
|
|
|
|
|
progname, max_error_level, fill_column,
|
|
|
|
|
paragraph_start_indent, reference_warning_limit);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
exit (exit_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Manipulating Lists */
|
|
|
|
|
|
|
|
|
|
typedef struct generic_list {
|
|
|
|
|
struct generic_list *next;
|
|
|
|
|
} GENERIC_LIST;
|
|
|
|
|
|
|
|
|
|
/* Reverse the chain of structures in LIST. Output the new head
|
|
|
|
|
of the chain. You should always assign the output value of this
|
|
|
|
|
function to something, or you will lose the chain. */
|
|
|
|
|
GENERIC_LIST *
|
|
|
|
|
reverse_list (list)
|
|
|
|
|
register GENERIC_LIST *list;
|
|
|
|
|
{
|
|
|
|
|
register GENERIC_LIST *next;
|
|
|
|
|
register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
|
|
|
|
|
|
|
|
|
|
while (list)
|
|
|
|
|
{
|
|
|
|
|
next = list->next;
|
|
|
|
|
list->next = prev;
|
|
|
|
|
prev = list;
|
|
|
|
|
list = next;
|
|
|
|
|
}
|
|
|
|
|
return (prev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pushing and Popping Files */
|
|
|
|
|
|
|
|
|
|
/* Find and load the file named FILENAME. Return a pointer to
|
|
|
|
|
the loaded file, or NULL if it can't be loaded. */
|
|
|
|
|
char *
|
|
|
|
|
find_and_load (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
struct stat fileinfo;
|
|
|
|
|
long file_size;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int file = -1, count = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
char *fullpath, *result, *get_file_info_in_path ();
|
|
|
|
|
|
|
|
|
|
result = fullpath = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
|
|
|
|
|
|
|
|
|
|
if (!fullpath)
|
|
|
|
|
goto error_exit;
|
|
|
|
|
|
|
|
|
|
filename = fullpath;
|
|
|
|
|
file_size = (long) fileinfo.st_size;
|
|
|
|
|
|
|
|
|
|
file = open (filename, O_RDONLY);
|
|
|
|
|
if (file < 0)
|
|
|
|
|
goto error_exit;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Load the file, with enough room for a newline and a null. */
|
|
|
|
|
result = xmalloc (file_size + 2);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* VMS stat lies about the st_size value. The actual number of
|
|
|
|
|
readable bytes is always less than this value. The arcane
|
|
|
|
|
mysteries of VMS/RMS are too much to probe, so this hack
|
|
|
|
|
suffices to make things work. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#if defined (VMS) || defined (WIN32)
|
|
|
|
|
#ifdef VMS
|
1997-01-11 02:12:38 +00:00
|
|
|
|
while ((n = read (file, result + count, file_size)) > 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#else /* WIN32 */
|
|
|
|
|
while ((n = read (file, result + count, 1)) > 0)
|
|
|
|
|
#endif /* WIN32 */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
count += n;
|
|
|
|
|
if (n == -1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#else /* !VMS && !WIN32 */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
count = file_size;
|
|
|
|
|
if (read (file, result, file_size) != file_size)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#endif /* !VMS && !WIN32 */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
error_exit:
|
|
|
|
|
{
|
|
|
|
|
if (result)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (result);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (fullpath)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (fullpath);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (file != -1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
close (file);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
return ((char *) NULL);
|
|
|
|
|
}
|
|
|
|
|
close (file);
|
|
|
|
|
|
|
|
|
|
/* Set the globals to the new file. */
|
|
|
|
|
input_text = result;
|
|
|
|
|
size_of_input_text = count;
|
|
|
|
|
input_filename = fullpath;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
node_filename = xstrdup (fullpath);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
line_number = 1;
|
|
|
|
|
/* Not strictly necessary. This magic prevents read_token () from doing
|
|
|
|
|
extra unnecessary work each time it is called (that is a lot of times).
|
1999-01-14 19:56:32 +00:00
|
|
|
|
SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
input_text[size_of_input_text] = '\n';
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* This, on the other hand, is always necessary. */
|
|
|
|
|
input_text[size_of_input_text+1] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save the state of the current input file. */
|
|
|
|
|
void
|
|
|
|
|
pushfile ()
|
|
|
|
|
{
|
|
|
|
|
FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
|
|
|
|
|
newstack->filename = input_filename;
|
|
|
|
|
newstack->text = input_text;
|
|
|
|
|
newstack->size = size_of_input_text;
|
|
|
|
|
newstack->offset = input_text_offset;
|
|
|
|
|
newstack->line_number = line_number;
|
|
|
|
|
newstack->next = filestack;
|
|
|
|
|
|
|
|
|
|
filestack = newstack;
|
|
|
|
|
push_node_filename ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the current file globals be what is on top of the file stack. */
|
|
|
|
|
void
|
|
|
|
|
popfile ()
|
|
|
|
|
{
|
|
|
|
|
FSTACK *tos = filestack;
|
|
|
|
|
|
|
|
|
|
if (!tos)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
abort (); /* My fault. I wonder what I did? */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
if (macro_expansion_output_stream)
|
|
|
|
|
{
|
|
|
|
|
maybe_write_itext (input_text, input_text_offset);
|
|
|
|
|
forget_itext (input_text);
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
/* Pop the stack. */
|
|
|
|
|
filestack = filestack->next;
|
|
|
|
|
|
|
|
|
|
/* Make sure that commands with braces have been satisfied. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (!executing_string && !me_executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
discard_braces ();
|
|
|
|
|
|
|
|
|
|
/* Get the top of the stack into the globals. */
|
|
|
|
|
input_filename = tos->filename;
|
|
|
|
|
input_text = tos->text;
|
|
|
|
|
size_of_input_text = tos->size;
|
|
|
|
|
input_text_offset = tos->offset;
|
|
|
|
|
line_number = tos->line_number;
|
|
|
|
|
free (tos);
|
|
|
|
|
|
|
|
|
|
/* Go back to the (now) current node. */
|
|
|
|
|
pop_node_filename ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Flush all open files on the file stack. */
|
|
|
|
|
void
|
|
|
|
|
flush_file_stack ()
|
|
|
|
|
{
|
|
|
|
|
while (filestack)
|
|
|
|
|
{
|
|
|
|
|
char *fname = input_filename;
|
|
|
|
|
char *text = input_text;
|
|
|
|
|
popfile ();
|
|
|
|
|
free (fname);
|
|
|
|
|
free (text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int node_filename_stack_index = 0;
|
|
|
|
|
int node_filename_stack_size = 0;
|
|
|
|
|
char **node_filename_stack = (char **)NULL;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
push_node_filename ()
|
|
|
|
|
{
|
|
|
|
|
if (node_filename_stack_index + 1 > node_filename_stack_size)
|
|
|
|
|
node_filename_stack = (char **)xrealloc
|
|
|
|
|
(node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
|
|
|
|
|
|
|
|
|
|
node_filename_stack[node_filename_stack_index] = node_filename;
|
|
|
|
|
node_filename_stack_index++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
pop_node_filename ()
|
|
|
|
|
{
|
|
|
|
|
node_filename = node_filename_stack[--node_filename_stack_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return just the simple part of the filename; i.e. the
|
|
|
|
|
filename without the path information, or extensions.
|
|
|
|
|
This conses up a new string. */
|
|
|
|
|
char *
|
|
|
|
|
filename_part (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
char *basename;
|
|
|
|
|
|
|
|
|
|
basename = strrchr (filename, '/');
|
|
|
|
|
if (!basename)
|
|
|
|
|
basename = filename;
|
|
|
|
|
else
|
|
|
|
|
basename++;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
basename = xstrdup (basename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (REMOVE_OUTPUT_EXTENSIONS)
|
|
|
|
|
|
|
|
|
|
/* See if there is an extension to remove. If so, remove it. */
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
|
|
|
|
|
temp = strrchr (basename, '.');
|
|
|
|
|
if (temp)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
*temp = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
#endif /* REMOVE_OUTPUT_EXTENSIONS */
|
|
|
|
|
return (basename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the pathname part of filename. This can be NULL. */
|
|
|
|
|
char *
|
|
|
|
|
pathname_part (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
char *expand_filename ();
|
|
|
|
|
char *result = (char *) NULL;
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
filename = expand_filename (filename, "");
|
|
|
|
|
|
|
|
|
|
i = strlen (filename) - 1;
|
|
|
|
|
|
|
|
|
|
while (i && filename[i] != '/')
|
|
|
|
|
i--;
|
|
|
|
|
if (filename[i] == '/')
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
if (i)
|
|
|
|
|
{
|
|
|
|
|
result = (char *)xmalloc (1 + i);
|
|
|
|
|
strncpy (result, filename, i);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
result[i] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
free (filename);
|
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
filename_non_directory (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = strlen (name) - 1; i; i--)
|
|
|
|
|
if (name[i] == '/')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (xstrdup (name + i + 1));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (xstrdup (name));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the expansion of FILENAME. */
|
|
|
|
|
char *
|
|
|
|
|
expand_filename (filename, input_name)
|
|
|
|
|
char *filename, *input_name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *full_pathname ();
|
|
|
|
|
|
|
|
|
|
if (filename)
|
|
|
|
|
filename = full_pathname (filename);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
filename = filename_non_directory (input_name);
|
|
|
|
|
|
|
|
|
|
if (!*filename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
free (filename);
|
|
|
|
|
filename = xstrdup ("noname.texi");
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
for (i = strlen (filename) - 1; i; i--)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (filename[i] == '.')
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!i)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
i = strlen (filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (i + 6 > (strlen (filename)))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
filename = (char *)xrealloc (filename, i + 6);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
strcpy (filename + i, ".info");
|
|
|
|
|
return (filename);
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (filename[0] == '.' || filename[0] == '/')
|
|
|
|
|
return (filename);
|
|
|
|
|
|
|
|
|
|
if (filename[0] != '/' && input_name[0] == '/')
|
|
|
|
|
{
|
|
|
|
|
/* Make it so that relative names work. */
|
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
|
|
i = strlen (input_name) - 1;
|
|
|
|
|
|
|
|
|
|
result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
|
|
|
|
|
strcpy (result, input_name);
|
|
|
|
|
|
|
|
|
|
while (result[i] != '/' && i)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
i--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (result[i] == '/')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
i++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
strcpy (&result[i], filename);
|
|
|
|
|
free (filename);
|
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
return (filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the full path to FILENAME. */
|
|
|
|
|
char *
|
|
|
|
|
full_pathname (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
int initial_character;
|
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
|
|
/* No filename given? */
|
|
|
|
|
if (!filename || !(initial_character = *filename))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (xstrdup (""));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Already absolute? */
|
|
|
|
|
if ((initial_character == '/') ||
|
|
|
|
|
((strncmp (filename, "./", 2) == 0) ||
|
|
|
|
|
(strncmp (filename, "../", 3) == 0)))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (xstrdup (filename));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (initial_character != '~')
|
|
|
|
|
{
|
|
|
|
|
char *localdir;
|
|
|
|
|
|
|
|
|
|
localdir = (char *)xmalloc (1025);
|
|
|
|
|
#if defined (HAVE_GETCWD)
|
|
|
|
|
if (!getcwd (localdir, 1024))
|
|
|
|
|
#else /* !HAVE_GETCWD */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (!getwd (localdir))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* !HAVE_GETCWD */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("%s: getwd: %s, %s\n"),
|
|
|
|
|
progname, filename, localdir);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
strcat (localdir, "/");
|
|
|
|
|
strcat (localdir, filename);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
result = xstrdup (localdir);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (localdir);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#ifndef WIN32
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (filename[1] == '/')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* Return the concatenation of the environment variable HOME
|
|
|
|
|
and the rest of the string. */
|
|
|
|
|
char *temp_home;
|
|
|
|
|
|
|
|
|
|
temp_home = (char *) getenv ("HOME");
|
|
|
|
|
result = (char *)xmalloc (strlen (&filename[1])
|
|
|
|
|
+ 1
|
|
|
|
|
+ temp_home ? strlen (temp_home)
|
|
|
|
|
: 0);
|
|
|
|
|
*result = 0;
|
|
|
|
|
|
|
|
|
|
if (temp_home)
|
|
|
|
|
strcpy (result, temp_home);
|
|
|
|
|
|
|
|
|
|
strcat (result, &filename[1]);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
struct passwd *user_entry;
|
|
|
|
|
int i, c;
|
|
|
|
|
char *username = (char *)xmalloc (257);
|
|
|
|
|
|
|
|
|
|
for (i = 1; (c = filename[i]); i++)
|
|
|
|
|
{
|
|
|
|
|
if (c == '/')
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
username[i - 1] = c;
|
|
|
|
|
}
|
|
|
|
|
if (c)
|
|
|
|
|
username[i - 1] = 0;
|
|
|
|
|
|
|
|
|
|
user_entry = getpwnam (username);
|
|
|
|
|
|
|
|
|
|
if (!user_entry)
|
|
|
|
|
return (xstrdup (filename));
|
|
|
|
|
|
|
|
|
|
result = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
|
|
|
|
|
+ strlen (&filename[i]));
|
|
|
|
|
strcpy (result, user_entry->pw_dir);
|
|
|
|
|
strcat (result, &filename[i]);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#endif /* not WIN32 */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
output_name_from_input_name (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
return (expand_filename ((char *)NULL, name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Hacking Tokens and Strings */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Return the next token as a string pointer. We cons the string. */
|
|
|
|
|
char *
|
|
|
|
|
read_token ()
|
|
|
|
|
{
|
|
|
|
|
int i, character;
|
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
|
|
/* If the first character to be read is self-delimiting, then that
|
|
|
|
|
is the command itself. */
|
|
|
|
|
character = curchar ();
|
|
|
|
|
if (self_delimiting (character))
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
|
|
|
|
|
if (character == '\n')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_number++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
result = xstrdup (" ");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
*result = character;
|
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; ((input_text_offset != size_of_input_text)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
&& (character = curchar ())
|
|
|
|
|
&& command_char (character));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
i++, input_text_offset++);
|
|
|
|
|
result = (char *)xmalloc (i + 1);
|
|
|
|
|
memcpy (result, &input_text[input_text_offset - i], i);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
result[i] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Return nonzero if CHARACTER is self-delimiting. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int
|
|
|
|
|
self_delimiting (character)
|
|
|
|
|
int character;
|
|
|
|
|
{
|
|
|
|
|
/* @; and @\ are not Texinfo commands, but they are listed here
|
|
|
|
|
anyway. I don't know why. --karl, 10aug96. */
|
|
|
|
|
return member (character, "~{|}`^\\@?=;:.-,*\'\" !\n\t");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clear whitespace from the front and end of string. */
|
|
|
|
|
void
|
|
|
|
|
canon_white (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
int len = strlen (string);
|
|
|
|
|
int x;
|
|
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < len; x++)
|
|
|
|
|
{
|
|
|
|
|
if (!cr_or_whitespace (string[x]))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
strcpy (string, string + x);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
len = strlen (string);
|
|
|
|
|
if (len)
|
|
|
|
|
len--;
|
|
|
|
|
while (len > -1 && cr_or_whitespace (string[len]))
|
|
|
|
|
len--;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
string[len + 1] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Bash STRING, replacing all whitespace with just one space. */
|
|
|
|
|
void
|
|
|
|
|
fix_whitespace (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
char *temp = (char *)xmalloc (strlen (string) + 1);
|
|
|
|
|
int string_index = 0;
|
|
|
|
|
int temp_index = 0;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
canon_white (string);
|
|
|
|
|
|
|
|
|
|
while (string[string_index])
|
|
|
|
|
{
|
|
|
|
|
c = temp[temp_index++] = string[string_index++];
|
|
|
|
|
|
|
|
|
|
if (c == ' ' || c == '\n' || c == '\t')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
temp[temp_index - 1] = ' ';
|
|
|
|
|
while ((c = string[string_index]) && (c == ' ' ||
|
|
|
|
|
c == '\t' ||
|
|
|
|
|
c == '\n'))
|
|
|
|
|
string_index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
temp[temp_index] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
strcpy (string, temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Discard text until the desired string is found. The string is
|
|
|
|
|
included in the discarded text. */
|
|
|
|
|
void
|
|
|
|
|
discard_until (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
int temp = search_forward (string, input_text_offset);
|
|
|
|
|
|
|
|
|
|
int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
|
|
|
|
|
int from = input_text_offset;
|
|
|
|
|
|
|
|
|
|
/* Find out what line we are on. */
|
|
|
|
|
while (from != tt)
|
|
|
|
|
if (input_text[from++] == '\n')
|
|
|
|
|
line_number++;
|
|
|
|
|
|
|
|
|
|
if (temp < 0)
|
|
|
|
|
{
|
|
|
|
|
input_text_offset = size_of_input_text - strlen (string);
|
|
|
|
|
|
|
|
|
|
if (strcmp (string, "\n") != 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
line_error (_("Expected `%s'"), string);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
input_text_offset = temp;
|
|
|
|
|
|
|
|
|
|
input_text_offset += strlen (string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read characters from the file until we are at MATCH.
|
|
|
|
|
Place the characters read into STRING.
|
|
|
|
|
On exit input_text_offset is after the match string.
|
|
|
|
|
Return the offset where the string starts. */
|
|
|
|
|
int
|
|
|
|
|
get_until (match, string)
|
|
|
|
|
char *match, **string;
|
|
|
|
|
{
|
|
|
|
|
int len, current_point, x, new_point, tem;
|
|
|
|
|
|
|
|
|
|
current_point = x = input_text_offset;
|
|
|
|
|
new_point = search_forward (match, input_text_offset);
|
|
|
|
|
|
|
|
|
|
if (new_point < 0)
|
|
|
|
|
new_point = size_of_input_text;
|
|
|
|
|
len = new_point - current_point;
|
|
|
|
|
|
|
|
|
|
/* Keep track of which line number we are at. */
|
|
|
|
|
tem = new_point + (strlen (match) - 1);
|
|
|
|
|
while (x != tem)
|
|
|
|
|
if (input_text[x++] == '\n')
|
|
|
|
|
line_number++;
|
|
|
|
|
|
|
|
|
|
*string = (char *)xmalloc (len + 1);
|
|
|
|
|
|
|
|
|
|
memcpy (*string, &input_text[current_point], len);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(*string)[len] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Now leave input_text_offset in a consistent state. */
|
|
|
|
|
input_text_offset = tem;
|
|
|
|
|
|
|
|
|
|
if (input_text_offset > size_of_input_text)
|
|
|
|
|
input_text_offset = size_of_input_text;
|
|
|
|
|
|
|
|
|
|
return (new_point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read characters from the file until we are at MATCH or end of line.
|
|
|
|
|
Place the characters read into STRING. */
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_until_in_line (expand, match, string)
|
|
|
|
|
int expand;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
char *match, **string;
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int real_bottom = size_of_input_text;
|
|
|
|
|
int limit = search_forward ("\n", input_text_offset);
|
|
|
|
|
if (limit < 0)
|
|
|
|
|
limit = size_of_input_text;
|
|
|
|
|
|
|
|
|
|
/* Replace input_text[input_text_offset .. limit-1] with its macro
|
|
|
|
|
expansion (actually, we expand all commands). This allows the node
|
|
|
|
|
names themselves to be constructed via a macro, as in:
|
|
|
|
|
@macro foo{p, q}
|
|
|
|
|
Together: \p\ & \q\.
|
|
|
|
|
@end macro
|
|
|
|
|
|
|
|
|
|
@node @foo{A,B}, next, prev, top
|
|
|
|
|
|
|
|
|
|
Otherwise, the `,' separating the macro args A and B is taken as
|
|
|
|
|
the node argument separator, so the node name is `@foo{A'. This
|
|
|
|
|
expansion is only necessary on the first call, since we expand the
|
|
|
|
|
whole line then.
|
|
|
|
|
|
|
|
|
|
Furthermore, if we're executing a string, don't do it -- we'll end
|
|
|
|
|
up shrinking the execution string which is currently aliased to
|
|
|
|
|
`input_text', so it might get moved, and not updated in the
|
|
|
|
|
`execution_strings' array. This happens when processing the
|
|
|
|
|
(synthetic) Overview-Footnotes node in the Texinfo manual. */
|
|
|
|
|
|
|
|
|
|
if (expand && !executing_string && !me_executing_string)
|
|
|
|
|
{
|
|
|
|
|
char *xp;
|
|
|
|
|
unsigned xp_len, new_len;
|
|
|
|
|
|
|
|
|
|
/* Get original string from input. */
|
|
|
|
|
unsigned raw_len = limit - input_text_offset;
|
|
|
|
|
char *str = xmalloc (raw_len + 1);
|
|
|
|
|
strncpy (str, input_text + input_text_offset, raw_len);
|
|
|
|
|
str[raw_len] = 0;
|
|
|
|
|
|
|
|
|
|
/* Expand it. */
|
|
|
|
|
xp = expansion (str, 0);
|
|
|
|
|
xp_len = strlen (xp);
|
|
|
|
|
free (str);
|
|
|
|
|
|
|
|
|
|
/* Plunk the expansion into the middle of `input_text' --
|
|
|
|
|
which is terminated by a newline, not a null. */
|
|
|
|
|
str = xmalloc (real_bottom - limit + 1);
|
|
|
|
|
strncpy (str, input_text + limit, real_bottom - limit + 1);
|
|
|
|
|
new_len = input_text_offset + xp_len + real_bottom - limit + 1;
|
|
|
|
|
input_text = xrealloc (input_text, new_len);
|
|
|
|
|
strcpy (input_text + input_text_offset, xp);
|
|
|
|
|
strncpy (input_text + input_text_offset + xp_len, str,
|
|
|
|
|
real_bottom - limit + 1);
|
|
|
|
|
free (str);
|
|
|
|
|
free (xp);
|
|
|
|
|
|
|
|
|
|
limit += xp_len - raw_len;
|
|
|
|
|
real_bottom += xp_len - raw_len;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
size_of_input_text = limit;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
get_until (match, string);
|
|
|
|
|
size_of_input_text = real_bottom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
get_rest_of_line (string)
|
|
|
|
|
char **string;
|
|
|
|
|
{
|
|
|
|
|
get_until ("\n", string);
|
|
|
|
|
canon_white (*string);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (curchar () == '\n') /* as opposed to the end of the file... */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
line_number++;
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Backup the input pointer to the previous character, keeping track
|
|
|
|
|
of the current line number. */
|
|
|
|
|
void
|
|
|
|
|
backup_input_pointer ()
|
|
|
|
|
{
|
|
|
|
|
if (input_text_offset)
|
|
|
|
|
{
|
|
|
|
|
input_text_offset--;
|
|
|
|
|
if (curchar () == '\n')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_number--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read characters from the file until we are at MATCH or closing brace.
|
|
|
|
|
Place the characters read into STRING. */
|
|
|
|
|
void
|
|
|
|
|
get_until_in_braces (match, string)
|
|
|
|
|
char *match, **string;
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *temp;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int i, brace = 0;
|
|
|
|
|
int match_len = strlen (match);
|
|
|
|
|
|
|
|
|
|
for (i = input_text_offset; i < size_of_input_text; i++)
|
|
|
|
|
{
|
|
|
|
|
if (input_text[i] == '{')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
brace++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (input_text[i] == '}')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
brace--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (input_text[i] == '\n')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_number++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (brace < 0 ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(brace == 0 && strncmp (input_text + i, match, match_len) == 0))
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match_len = i - input_text_offset;
|
|
|
|
|
temp = (char *)xmalloc (2 + match_len);
|
|
|
|
|
strncpy (temp, input_text + input_text_offset, match_len);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp[match_len] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
input_text_offset = i;
|
|
|
|
|
*string = temp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Converting the File */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Convert the file named by NAME. The output is saved on the file
|
|
|
|
|
named as the argument to the @setfilename command. */
|
|
|
|
|
static char *suffixes[] = {
|
|
|
|
|
".texinfo",
|
|
|
|
|
".texi",
|
|
|
|
|
".txinfo",
|
|
|
|
|
"",
|
|
|
|
|
(char *)NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
initialize_conversion ()
|
|
|
|
|
{
|
|
|
|
|
init_tag_table ();
|
|
|
|
|
init_indices ();
|
|
|
|
|
init_internals ();
|
|
|
|
|
init_paragraph ();
|
|
|
|
|
|
|
|
|
|
/* This is used for splitting the output file and for doing section
|
|
|
|
|
headings. It was previously initialized in `init_paragraph', but its
|
|
|
|
|
use there loses with the `init_paragraph' calls done by the
|
|
|
|
|
multitable code; the tag indices get reset to zero. */
|
|
|
|
|
output_position = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We read in multiples of 4k, simply because it is a typical pipe size
|
|
|
|
|
on unix systems. */
|
|
|
|
|
#define READ_BUFFER_GROWTH (4 * 4096)
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Convert the Texinfo file coming from the open stream STREAM. Assume the
|
1997-01-11 02:12:38 +00:00
|
|
|
|
source of the stream is named NAME. */
|
|
|
|
|
void
|
|
|
|
|
convert_from_stream (stream, name)
|
|
|
|
|
FILE *stream;
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
char *buffer = (char *)NULL;
|
|
|
|
|
int buffer_offset = 0, buffer_size = 0;
|
|
|
|
|
|
|
|
|
|
initialize_conversion ();
|
|
|
|
|
|
|
|
|
|
/* Read until the end of the stream. This isn't strictly correct, since
|
|
|
|
|
the texinfo input may end before the stream ends, but it is a quick
|
|
|
|
|
working hueristic. */
|
|
|
|
|
while (!feof (stream))
|
|
|
|
|
{
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
if (buffer_offset + (READ_BUFFER_GROWTH + 1) >= buffer_size)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
buffer = (char *)
|
|
|
|
|
xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
count = fread (buffer + buffer_offset, 1, READ_BUFFER_GROWTH, stream);
|
|
|
|
|
|
|
|
|
|
if (count < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
perror (name);
|
|
|
|
|
exit (FATAL);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
buffer_offset += count;
|
|
|
|
|
if (count == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the globals to the new file. */
|
|
|
|
|
input_text = buffer;
|
|
|
|
|
size_of_input_text = buffer_offset;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_filename = xstrdup (name);
|
|
|
|
|
node_filename = xstrdup (name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
line_number = 1;
|
|
|
|
|
|
|
|
|
|
/* Not strictly necessary. This magic prevents read_token () from doing
|
|
|
|
|
extra unnecessary work each time it is called (that is a lot of times).
|
|
|
|
|
The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
|
|
|
|
|
input_text[size_of_input_text] = '\n';
|
|
|
|
|
|
|
|
|
|
convert_from_loaded_file (name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
convert_from_file (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *filename = (char *)xmalloc (strlen (name) + 50);
|
|
|
|
|
|
|
|
|
|
initialize_conversion ();
|
|
|
|
|
|
|
|
|
|
/* Try to load the file specified by NAME, concatenated with our
|
|
|
|
|
various suffixes. Prefer files like `makeinfo.texi' to
|
|
|
|
|
`makeinfo'. */
|
|
|
|
|
for (i = 0; suffixes[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
strcpy (filename, name);
|
|
|
|
|
strcat (filename, suffixes[i]);
|
|
|
|
|
|
|
|
|
|
if (find_and_load (filename))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!suffixes[i][0] && strrchr (filename, '.'))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
fs_error (filename);
|
|
|
|
|
free (filename);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!suffixes[i])
|
|
|
|
|
{
|
|
|
|
|
fs_error (name);
|
|
|
|
|
free (filename);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_filename = filename;
|
|
|
|
|
|
|
|
|
|
convert_from_loaded_file (name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
convert_from_loaded_file (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
char *expand_filename (), *filename_part ();
|
|
|
|
|
char *real_output_filename = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
remember_itext (input_text, 0);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
/* Search this file looking for the special string which starts conversion.
|
|
|
|
|
Once found, we may truly begin. */
|
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
while (input_text_offset >= 0)
|
|
|
|
|
{
|
|
|
|
|
input_text_offset =
|
1999-01-14 19:56:32 +00:00
|
|
|
|
search_forward (setfilename_search, input_text_offset);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if ((input_text_offset == 0) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
((input_text_offset > 0) &&
|
|
|
|
|
(input_text[input_text_offset -1] == '\n')))
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (input_text_offset > 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_text_offset++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (input_text_offset < 0)
|
|
|
|
|
{
|
|
|
|
|
if (!command_output_filename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (REQUIRE_SETFILENAME)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
error (_("No `%s' found in `%s'"), setfilename_search, name);
|
|
|
|
|
goto finished;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
register int i, end_of_first_line;
|
|
|
|
|
|
|
|
|
|
/* Find the end of the first line in the file. */
|
|
|
|
|
for (i = 0; i < size_of_input_text - 1; i++)
|
|
|
|
|
if (input_text[i] == '\n')
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
end_of_first_line = i + 1;
|
|
|
|
|
|
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < end_of_first_line; i++)
|
|
|
|
|
{
|
|
|
|
|
if ((input_text[i] == '\\') &&
|
|
|
|
|
(strncmp (input_text + i + 1, "include", 7) == 0))
|
|
|
|
|
{
|
|
|
|
|
input_text_offset = end_of_first_line;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
command_output_filename = output_name_from_input_name (name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* !REQUIRE_SETFILENAME */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
input_text_offset += strlen (setfilename_search);
|
|
|
|
|
|
|
|
|
|
if (!command_output_filename)
|
|
|
|
|
get_until ("\n", &output_filename);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (input_text_offset != -1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
discard_until ("\n");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_text_offset = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
real_output_filename = output_filename = command_output_filename;
|
|
|
|
|
command_output_filename = (char *)NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canon_white (output_filename);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (real_output_filename && strcmp (real_output_filename, "-") == 0)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_filename
|
|
|
|
|
&& strcmp (macro_expansion_filename, "-") == 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("%s: Skipping macro expansion to stdout as Info output is going there.\n"),
|
|
|
|
|
progname);
|
|
|
|
|
macro_expansion_output_stream = NULL;
|
|
|
|
|
}
|
|
|
|
|
real_output_filename = xstrdup (real_output_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
output_stream = stdout;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
splitting = 0; /* Cannot split when writing to stdout. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!real_output_filename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
real_output_filename = expand_filename (output_filename, name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
real_output_filename = xstrdup (real_output_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
output_stream = fopen (real_output_filename, "w");
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (output_stream != stdout)
|
|
|
|
|
printf (_("Making %s file `%s' from `%s'.\n"),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
no_headers ? "text" : "info", output_filename, input_filename);
|
|
|
|
|
|
|
|
|
|
if (output_stream == NULL)
|
|
|
|
|
{
|
|
|
|
|
fs_error (real_output_filename);
|
|
|
|
|
goto finished;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the displayable filename from output_filename. Only the base
|
|
|
|
|
portion of the filename need be displayed. */
|
|
|
|
|
if (output_stream != stdout)
|
|
|
|
|
pretty_output_filename = filename_part (output_filename);
|
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
pretty_output_filename = xstrdup ("stdout");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
{
|
|
|
|
|
int temp = 0;
|
|
|
|
|
line_number = 1;
|
|
|
|
|
while (temp != input_text_offset)
|
|
|
|
|
if (input_text[temp++] == '\n')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_number++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!no_headers)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word_args (_("This is Info file %s, produced by Makeinfo version %d.%d"),
|
|
|
|
|
output_filename, major_version, minor_version);
|
|
|
|
|
add_word_args (_(" from the input file %s.\n"), input_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
reader_loop ();
|
|
|
|
|
|
|
|
|
|
finished:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
discard_insertions (0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
close_paragraph ();
|
|
|
|
|
flush_file_stack ();
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
if (macro_expansion_output_stream)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
fclose (macro_expansion_output_stream);
|
|
|
|
|
if (errors_printed && !force
|
|
|
|
|
&& strcmp (macro_expansion_filename, "-") != 0
|
|
|
|
|
&& strcmp (macro_expansion_filename, "/dev/null") != 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"),
|
|
|
|
|
progname, macro_expansion_filename);
|
|
|
|
|
if (unlink (macro_expansion_filename) < 0)
|
|
|
|
|
perror (macro_expansion_filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (output_stream)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
output_pending_notes ();
|
|
|
|
|
free_pending_notes ();
|
|
|
|
|
if (tag_table != NULL)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
tag_table = (TAG_ENTRY *) reverse_list (tag_table);
|
|
|
|
|
if (!no_headers)
|
|
|
|
|
write_tag_table ();
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (output_stream != stdout)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
fclose (output_stream);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If validating, then validate the entire file right now. */
|
|
|
|
|
if (validating)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
validate_file (tag_table);
|
|
|
|
|
|
|
|
|
|
if (splitting && (!errors_printed || force))
|
|
|
|
|
split_file (real_output_filename, 0);
|
|
|
|
|
else if (errors_printed && !force
|
|
|
|
|
&& strcmp (real_output_filename, "-") != 0
|
|
|
|
|
&& strcmp (real_output_filename, "/dev/null") != 0)
|
|
|
|
|
{ /* If there were errors, and no --force, remove the output. */
|
|
|
|
|
fprintf (stderr, _("%s: Removing output file `%s' due to errors; use --force to preserve.\n"),
|
|
|
|
|
progname, real_output_filename);
|
|
|
|
|
if (unlink (real_output_filename) < 0)
|
|
|
|
|
perror (real_output_filename);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
free (real_output_filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
free_and_clear (pointer)
|
|
|
|
|
char **pointer;
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (*pointer)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
free (*pointer);
|
|
|
|
|
*pointer = (char *) NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize some state. */
|
|
|
|
|
void
|
|
|
|
|
init_internals ()
|
|
|
|
|
{
|
|
|
|
|
free_and_clear (&output_filename);
|
|
|
|
|
free_and_clear (&command);
|
|
|
|
|
free_and_clear (&input_filename);
|
|
|
|
|
free_node_references ();
|
|
|
|
|
init_insertion_stack ();
|
|
|
|
|
init_brace_stack ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
current_node = NULL; /* sometimes already freed */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
command_index = 0;
|
|
|
|
|
in_menu = 0;
|
|
|
|
|
in_detailmenu = 0;
|
|
|
|
|
top_node_seen = 0;
|
|
|
|
|
non_top_node_seen = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
init_paragraph ()
|
|
|
|
|
{
|
|
|
|
|
free_and_clear (&output_paragraph);
|
|
|
|
|
output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
output_paragraph[0] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
output_paragraph_offset = 0;
|
|
|
|
|
output_column = 0;
|
|
|
|
|
paragraph_is_open = 0;
|
|
|
|
|
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;
|
|
|
|
|
int done = 0;
|
|
|
|
|
int dash_count = 0;
|
|
|
|
|
|
|
|
|
|
while (!done)
|
|
|
|
|
{
|
|
|
|
|
if (input_text_offset >= size_of_input_text)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
character = curchar ();
|
|
|
|
|
|
|
|
|
|
if (!in_fixed_width_font &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(character == '\'' || character == '`') &&
|
|
|
|
|
input_text[input_text_offset + 1] == character)
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
character = '"';
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (character == '-')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
dash_count++;
|
|
|
|
|
if (dash_count == 2 && !in_fixed_width_font)
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
dash_count = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this is a whitespace character, then check to see if the line
|
1999-01-14 19:56:32 +00:00
|
|
|
|
is blank. If so, advance to the carriage return. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (whitespace (character))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
register int i = input_text_offset + 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while (i < size_of_input_text && whitespace (input_text[i]))
|
|
|
|
|
i++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (i == size_of_input_text || input_text[i] == '\n')
|
|
|
|
|
{
|
|
|
|
|
if (i == size_of_input_text)
|
|
|
|
|
i--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_text_offset = i;
|
|
|
|
|
character = curchar ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (character == '\n')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
line_number++;
|
|
|
|
|
|
|
|
|
|
/* Check for a menu entry here, since the "escape sequence"
|
|
|
|
|
that begins menu entries is "\n* ". */
|
|
|
|
|
if (in_menu && input_text_offset + 1 < size_of_input_text)
|
|
|
|
|
{
|
|
|
|
|
char *glean_node_from_menu (), *tem;
|
|
|
|
|
|
|
|
|
|
/* Note that the value of TEM is discarded, since it is
|
|
|
|
|
gauranteed to be NULL when glean_node_from_menu () is
|
|
|
|
|
called with a Nonzero argument. */
|
|
|
|
|
if (!in_detailmenu)
|
|
|
|
|
tem = glean_node_from_menu (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
switch (character)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case COMMAND_PREFIX:
|
|
|
|
|
read_command ();
|
|
|
|
|
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 %c"), '{');
|
|
|
|
|
remember_brace (misplaced_brace);
|
|
|
|
|
|
|
|
|
|
/* Don't advance input_text_offset since this happens in
|
|
|
|
|
remember_brace ().
|
|
|
|
|
input_text_offset++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
*/
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
case '}':
|
|
|
|
|
pop_and_call_brace ();
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
default:
|
|
|
|
|
add_char (character);
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
if (macro_expansion_output_stream)
|
|
|
|
|
maybe_write_itext (input_text, input_text_offset);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = 0; command_table[i].name; i++)
|
|
|
|
|
if (strcmp (command_table[i].name, string) == 0)
|
|
|
|
|
return (&command_table[i]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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] &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strcmp (user_command_array[i]->name, string) == 0))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (user_command_array[i]);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* We never heard of this command. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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 ();
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
/* Check to see if this command is a macro. If so, execute it here. */
|
|
|
|
|
{
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = find_macro (command);
|
|
|
|
|
|
|
|
|
|
if (def)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* We disallow recursive use of a macro call. Inhibit the expansion
|
|
|
|
|
of this macro during the life of its execution. */
|
|
|
|
|
if (!(def->flags & ME_RECURSE))
|
|
|
|
|
def->inhibited = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_macro (def);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (!(def->flags & ME_RECURSE))
|
|
|
|
|
def->inhibited = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
entry = get_command_entry (command);
|
|
|
|
|
if (entry == (COMMAND *)-1)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Unknown command `%s'"), command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (entry->argument_in_braces)
|
|
|
|
|
remember_brace (entry->proc);
|
|
|
|
|
|
|
|
|
|
(*(entry->proc)) (START, output_paragraph_offset, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the string which invokes PROC; a pointer to a function. */
|
|
|
|
|
char *
|
|
|
|
|
find_proc_name (proc)
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = 0; command_table[i].name; i++)
|
|
|
|
|
if (proc == command_table[i].proc)
|
|
|
|
|
return command_table[i].name;
|
|
|
|
|
return _("NO_NAME!");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
init_brace_stack ()
|
|
|
|
|
{
|
|
|
|
|
brace_stack = (BRACE_ELEMENT *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
remember_brace (proc)
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
{
|
|
|
|
|
if (curchar () != '{')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%c%s expected `{...}'"), COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
int position;
|
|
|
|
|
{
|
|
|
|
|
BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
|
|
|
|
|
new->next = brace_stack;
|
|
|
|
|
new->proc = proc;
|
|
|
|
|
new->pos = position;
|
|
|
|
|
new->line = line_number;
|
|
|
|
|
new->in_fixed_width_font = in_fixed_width_font;
|
|
|
|
|
brace_stack = new;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pop the top of the brace stack, and call the associated function
|
|
|
|
|
with the args END and POS. */
|
|
|
|
|
void
|
|
|
|
|
pop_and_call_brace ()
|
|
|
|
|
{
|
|
|
|
|
BRACE_ELEMENT *temp;
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
|
|
if (brace_stack == (BRACE_ELEMENT *) NULL)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Unmatched }"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pos = brace_stack->pos;
|
|
|
|
|
proc = brace_stack->proc;
|
|
|
|
|
in_fixed_width_font = brace_stack->in_fixed_width_font;
|
|
|
|
|
temp = brace_stack->next;
|
|
|
|
|
free (brace_stack);
|
|
|
|
|
brace_stack = temp;
|
|
|
|
|
|
|
|
|
|
(*proc) (END, pos, output_paragraph_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Shift all of the markers in `brace_stack' by AMOUNT. */
|
|
|
|
|
void
|
|
|
|
|
adjust_braces_following (here, amount)
|
|
|
|
|
int here, amount;
|
|
|
|
|
{
|
|
|
|
|
register BRACE_ELEMENT *stack = brace_stack;
|
|
|
|
|
|
|
|
|
|
while (stack)
|
|
|
|
|
{
|
|
|
|
|
if (stack->pos >= here)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
stack->pos += amount;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
stack = stack->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 ()
|
|
|
|
|
{
|
|
|
|
|
if (!brace_stack)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
while (brace_stack)
|
|
|
|
|
{
|
|
|
|
|
if (brace_stack->proc != misplaced_brace)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *proc_name;
|
|
|
|
|
int temp_line_number = line_number;
|
|
|
|
|
|
|
|
|
|
line_number = brace_stack->line;
|
|
|
|
|
proc_name = find_proc_name (brace_stack->proc);
|
|
|
|
|
line_error (_("%c%s missing close brace"), COMMAND_PREFIX, proc_name);
|
|
|
|
|
line_number = temp_line_number;
|
|
|
|
|
pop_and_call_brace ();
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
BRACE_ELEMENT *temp;
|
|
|
|
|
temp = brace_stack->next;
|
|
|
|
|
free (brace_stack);
|
|
|
|
|
brace_stack = temp;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
len = fill_column - output_column;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
len = len - output_column;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '\n':
|
|
|
|
|
len = fill_column - output_column;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* ASCII control characters appear as two characters in the output
|
|
|
|
|
(e.g., ^A). But characters with the high bit set are just one
|
|
|
|
|
on suitable terminals, so don't count them as two for line
|
|
|
|
|
breaking purposes. */
|
|
|
|
|
if (0 <= character && character < ' ')
|
|
|
|
|
len = 2;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
len = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#if defined (VA_FPRINTF) && __STDC__
|
|
|
|
|
add_word_args (char *format, ...)
|
|
|
|
|
#else
|
|
|
|
|
add_word_args (format, va_alist)
|
|
|
|
|
char *format;
|
|
|
|
|
va_dcl
|
|
|
|
|
#endif
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char buffer[1000];
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
va_list ap;
|
|
|
|
|
#endif
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
VA_START (ap, format);
|
|
|
|
|
#ifdef VA_SPRINTF
|
|
|
|
|
VA_SPRINTF (buffer, format, ap);
|
|
|
|
|
#else
|
|
|
|
|
sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
|
|
|
|
|
#endif /* not VA_SPRINTF */
|
|
|
|
|
va_end (ap);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
add_word (buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add STRING to output_paragraph. */
|
|
|
|
|
void
|
|
|
|
|
add_word (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
while (*string)
|
|
|
|
|
add_char (*string++);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero if the last character inserted has the syntax class of NEWLINE. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int last_char_was_newline = 1;
|
|
|
|
|
|
|
|
|
|
/* The actual last inserted character. Note that this may be something
|
|
|
|
|
other than NEWLINE even if last_char_was_newline is 1. */
|
|
|
|
|
int last_inserted_character = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that a newline character has already been
|
1997-01-11 02:12:38 +00:00
|
|
|
|
inserted, so close_paragraph () should insert one less. */
|
|
|
|
|
int line_already_broken = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* When nonzero we have finished an insertion (see `end_insertion') and we
|
1997-01-11 02:12:38 +00:00
|
|
|
|
want to ignore false continued paragraph closings. */
|
|
|
|
|
int insertion_paragraph_closed = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means attempt to make all of the lines have fill_column width. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int do_justification = 0;
|
|
|
|
|
|
|
|
|
|
/* Add the character to the current paragraph. If filling_enabled is
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nonzero, then do filling as well. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
add_char (character)
|
|
|
|
|
int character;
|
|
|
|
|
{
|
|
|
|
|
/* If we are avoiding outputting headers, and we are currently
|
|
|
|
|
in a menu, then simply return. */
|
|
|
|
|
if (no_headers && (in_menu || in_detailmenu))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* If we are adding a character now, then we don't have to
|
|
|
|
|
ignore close_paragraph () calls any more. */
|
|
|
|
|
if (must_start_paragraph && character != '\n')
|
|
|
|
|
{
|
|
|
|
|
must_start_paragraph = 0;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_already_broken = 0; /* The line is no longer broken. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (current_indent > output_column)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
indent (current_indent - output_column);
|
|
|
|
|
output_column = current_indent;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (non_splitting_words && member (character, " \t\n"))
|
|
|
|
|
character = ' ' | 0x80;
|
|
|
|
|
|
|
|
|
|
insertion_paragraph_closed = 0;
|
|
|
|
|
|
|
|
|
|
switch (character)
|
|
|
|
|
{
|
|
|
|
|
case '\n':
|
|
|
|
|
if (!filling_enabled)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
insert ('\n');
|
|
|
|
|
|
|
|
|
|
if (force_flush_right)
|
|
|
|
|
{
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
/* Hack to force single blank lines out in this mode. */
|
|
|
|
|
flush_output ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output_column = 0;
|
|
|
|
|
|
|
|
|
|
if (!no_indent && paragraph_is_open)
|
|
|
|
|
indent (output_column = current_indent);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else /* CHARACTER is newline, and filling is enabled. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (end_of_sentence_p ())
|
|
|
|
|
{
|
|
|
|
|
insert (' ');
|
|
|
|
|
output_column++;
|
|
|
|
|
last_inserted_character = character;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (last_char_was_newline)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
pending_indent = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
last_char_was_newline = 1;
|
|
|
|
|
insert (' ');
|
|
|
|
|
output_column++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int len = get_char_len (character);
|
|
|
|
|
int suppress_insert = 0;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
while (--temp > 0 && output_paragraph[temp] != '\n')
|
|
|
|
|
{
|
|
|
|
|
/* If we have found a space, we have the place to break
|
|
|
|
|
the line. */
|
|
|
|
|
if (output_paragraph[temp] == ' ')
|
|
|
|
|
{
|
|
|
|
|
/* Remove trailing whitespace from output. */
|
|
|
|
|
while (temp && whitespace (output_paragraph[temp - 1]))
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
for (;; t1++)
|
|
|
|
|
{
|
|
|
|
|
if (t1 == output_paragraph_offset)
|
|
|
|
|
{
|
|
|
|
|
if (whitespace (character))
|
|
|
|
|
suppress_insert = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!whitespace (output_paragraph[t1]))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (t1 != temp)
|
|
|
|
|
{
|
|
|
|
|
adjust_braces_following (temp, (- (t1 - temp)));
|
|
|
|
|
strncpy ((char *) &output_paragraph[temp],
|
|
|
|
|
(char *) &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 = (char *)xmalloc (buffer_len);
|
|
|
|
|
int indentation = 0;
|
|
|
|
|
|
|
|
|
|
/* We have to shift any markers that are in
|
|
|
|
|
front of the wrap point. */
|
|
|
|
|
adjust_braces_following (temp, current_indent);
|
|
|
|
|
|
|
|
|
|
while (current_indent > 0 &&
|
|
|
|
|
indentation != current_indent)
|
|
|
|
|
temp_buffer[indentation++] = ' ';
|
|
|
|
|
|
|
|
|
|
strncpy ((char *) &temp_buffer[current_indent],
|
|
|
|
|
(char *) &output_paragraph[temp],
|
|
|
|
|
buffer_len - current_indent);
|
|
|
|
|
|
|
|
|
|
if (output_paragraph_offset + buffer_len
|
|
|
|
|
>= paragraph_buffer_len)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *tt = xrealloc
|
|
|
|
|
(output_paragraph,
|
|
|
|
|
(paragraph_buffer_len += buffer_len));
|
|
|
|
|
output_paragraph = tt;
|
|
|
|
|
}
|
|
|
|
|
strncpy ((char *) &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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!suppress_insert)
|
|
|
|
|
{
|
|
|
|
|
insert (character);
|
|
|
|
|
last_inserted_character = character;
|
|
|
|
|
}
|
|
|
|
|
last_char_was_newline = 0;
|
|
|
|
|
line_already_broken = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 =
|
1999-01-14 19:56:32 +00:00
|
|
|
|
xrealloc (output_paragraph, (paragraph_buffer_len += 100));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert the null-terminated string STRING into `output_paragraph'. */
|
|
|
|
|
void
|
|
|
|
|
insert_string (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
while (*string)
|
|
|
|
|
insert (*string++);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* Sentences might have these characters after the period (or whatever). */
|
|
|
|
|
#define post_sentence(c) ((c) == ')' || (c) == '\'' || (c) == '"' \
|
|
|
|
|
|| (c) == ']')
|
|
|
|
|
|
|
|
|
|
/* Return true if at an end-of-sentence character, possibly followed by
|
|
|
|
|
post-sentence punctuation to ignore. */
|
|
|
|
|
static int
|
|
|
|
|
end_of_sentence_p ()
|
|
|
|
|
{
|
|
|
|
|
int loc = output_paragraph_offset - 1;
|
|
|
|
|
while (loc > 0 && post_sentence (output_paragraph[loc]))
|
|
|
|
|
loc--;
|
|
|
|
|
return sentence_ender (output_paragraph[loc]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* 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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (whitespace (output_paragraph[output_paragraph_offset - 1]))
|
|
|
|
|
output_paragraph_offset--;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (output_paragraph_offset && count--)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (whitespace (output_paragraph[output_paragraph_offset - 1]))
|
|
|
|
|
output_paragraph_offset--;
|
|
|
|
|
else
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means do not honor calls to flush_output (). */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
static int flushing_ignored = 0;
|
|
|
|
|
|
|
|
|
|
/* Prevent calls to flush_output () from having any effect. */
|
|
|
|
|
void
|
|
|
|
|
inhibit_output_flushing ()
|
|
|
|
|
{
|
|
|
|
|
flushing_ignored++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allow calls to flush_output () to write the paragraph data. */
|
|
|
|
|
void
|
|
|
|
|
uninhibit_output_flushing ()
|
|
|
|
|
{
|
|
|
|
|
flushing_ignored--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
flush_output ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
if (!output_paragraph_offset || flushing_ignored)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < output_paragraph_offset; i++)
|
|
|
|
|
{
|
|
|
|
|
/* If we turned on the 8th bit for a space
|
|
|
|
|
inside @w, turn it back off for output. */
|
|
|
|
|
if (output_paragraph[i] & meta_character_bit)
|
|
|
|
|
{
|
|
|
|
|
int temp = UNMETA (output_paragraph[i]);
|
|
|
|
|
if (temp == ' ')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
output_paragraph[i] &= 0x7f;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = DEFAULT_PARAGRAPH_SPACING;
|
|
|
|
|
|
|
|
|
|
/* Close the current paragraph, leaving no blank lines between them. */
|
|
|
|
|
void
|
|
|
|
|
close_single_paragraph ()
|
|
|
|
|
{
|
|
|
|
|
close_paragraph_with_lines (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Close a paragraph after an insertion has ended. */
|
|
|
|
|
void
|
|
|
|
|
close_insertion_paragraph ()
|
|
|
|
|
{
|
|
|
|
|
if (!insertion_paragraph_closed)
|
|
|
|
|
{
|
|
|
|
|
/* Close the current paragraph, breaking the line. */
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Start a new paragraph, with the correct indentation for the now
|
|
|
|
|
current insertion level (one above the one that we are ending). */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
start_paragraph ();
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Tell `close_paragraph' that the previous line has already been
|
|
|
|
|
broken, so it should insert one less newline. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
line_already_broken = 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Tell functions such as `add_char' we've already found a newline. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
ignore_blank_line ();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If the insertion paragraph is closed already, then we are seeing
|
1999-01-14 19:56:32 +00:00
|
|
|
|
two `@end' commands in a row. Note that the first one we saw was
|
|
|
|
|
handled in the first part of this if-then-else clause, and at that
|
|
|
|
|
time `start_paragraph' was called, partially to handle the proper
|
|
|
|
|
indentation of the current line. However, the indentation level
|
|
|
|
|
may have just changed again, so we may have to outdent the current
|
|
|
|
|
line to the new indentation level. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (current_indent < output_column)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
kill_self_indent (output_column - current_indent);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insertion_paragraph_closed = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
close_paragraph_with_lines (lines)
|
|
|
|
|
int lines;
|
|
|
|
|
{
|
|
|
|
|
int old_spacing = paragraph_spacing;
|
|
|
|
|
paragraph_spacing = lines;
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
paragraph_spacing = old_spacing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Close the currently open paragraph. */
|
|
|
|
|
void
|
|
|
|
|
close_paragraph ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
/* The insertion paragraph is no longer closed. */
|
|
|
|
|
insertion_paragraph_closed = 0;
|
|
|
|
|
|
|
|
|
|
if (paragraph_is_open && !must_start_paragraph)
|
|
|
|
|
{
|
|
|
|
|
register int tindex, c;
|
|
|
|
|
|
|
|
|
|
tindex = output_paragraph_offset;
|
|
|
|
|
|
|
|
|
|
/* Back up to last non-newline/space character, forcing all such
|
1999-01-14 19:56:32 +00:00
|
|
|
|
subsequent characters to be newlines. This isn't strictly
|
|
|
|
|
necessary, but a couple of functions use the presence of a newline
|
|
|
|
|
to make decisions. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
c = output_paragraph[tindex];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (c == ' '|| c == '\n')
|
|
|
|
|
output_paragraph[tindex] = '\n';
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* All trailing whitespace is ignored. */
|
|
|
|
|
output_paragraph_offset = ++tindex;
|
|
|
|
|
|
|
|
|
|
/* Break the line if that is appropriate. */
|
|
|
|
|
if (paragraph_spacing >= 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
insert ('\n');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Add as many blank lines as is specified in `paragraph_spacing'. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!force_flush_right)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
|
|
|
|
|
insert ('\n');
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If we are doing flush right indentation, then do it now
|
1999-01-14 19:56:32 +00:00
|
|
|
|
on the paragraph (really a single line). */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (force_flush_right)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
do_flush_right_indentation ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
flush_output ();
|
|
|
|
|
paragraph_is_open = 0;
|
|
|
|
|
no_indent = 0;
|
|
|
|
|
output_column = 0;
|
|
|
|
|
}
|
|
|
|
|
ignore_blank_line ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the last line just read look as if it were only a newline. */
|
|
|
|
|
void
|
|
|
|
|
ignore_blank_line ()
|
|
|
|
|
{
|
|
|
|
|
last_inserted_character = '\n';
|
|
|
|
|
last_char_was_newline = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Align the end of the text in output_paragraph with fill_column. */
|
|
|
|
|
void
|
|
|
|
|
do_flush_right_indentation ()
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
int temp_len;
|
|
|
|
|
|
|
|
|
|
kill_self_indent (-1);
|
|
|
|
|
|
|
|
|
|
if (output_paragraph[0] != '\n')
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
output_paragraph[output_paragraph_offset] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (output_paragraph_offset < fill_column)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (fill_column >= paragraph_buffer_len)
|
|
|
|
|
output_paragraph =
|
|
|
|
|
xrealloc (output_paragraph,
|
|
|
|
|
(paragraph_buffer_len += fill_column));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp_len = strlen ((char *)output_paragraph);
|
|
|
|
|
temp = (char *)xmalloc (temp_len + 1);
|
|
|
|
|
memcpy (temp, (char *)output_paragraph, temp_len);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = 0; i < fill_column - output_paragraph_offset; i++)
|
|
|
|
|
output_paragraph[i] = ' ';
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
memcpy ((char *)output_paragraph + i, temp, temp_len);
|
|
|
|
|
free (temp);
|
|
|
|
|
output_paragraph_offset = fill_column;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Begin a new paragraph. */
|
|
|
|
|
void
|
|
|
|
|
start_paragraph ()
|
|
|
|
|
{
|
|
|
|
|
/* First close existing one. */
|
|
|
|
|
if (paragraph_is_open)
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
|
|
|
|
|
/* In either case, the insertion paragraph is no longer closed. */
|
|
|
|
|
insertion_paragraph_closed = 0;
|
|
|
|
|
|
|
|
|
|
/* However, the paragraph is open! */
|
|
|
|
|
paragraph_is_open = 1;
|
|
|
|
|
|
|
|
|
|
/* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
|
|
|
|
|
had to be called before we would allow any other paragraph operations
|
|
|
|
|
to have an effect. */
|
|
|
|
|
if (!must_start_paragraph)
|
|
|
|
|
{
|
|
|
|
|
int amount_to_indent = 0;
|
|
|
|
|
|
|
|
|
|
/* If doing indentation, then insert the appropriate amount. */
|
|
|
|
|
if (!no_indent)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (inhibit_paragraph_indentation)
|
|
|
|
|
{
|
|
|
|
|
amount_to_indent = current_indent;
|
|
|
|
|
if (inhibit_paragraph_indentation < 0)
|
|
|
|
|
inhibit_paragraph_indentation++;
|
|
|
|
|
}
|
|
|
|
|
else if (paragraph_start_indent < 0)
|
|
|
|
|
amount_to_indent = current_indent;
|
|
|
|
|
else
|
|
|
|
|
amount_to_indent = current_indent + paragraph_start_indent;
|
|
|
|
|
|
|
|
|
|
if (amount_to_indent >= output_column)
|
|
|
|
|
{
|
|
|
|
|
amount_to_indent -= output_column;
|
|
|
|
|
indent (amount_to_indent);
|
|
|
|
|
output_column += amount_to_indent;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
must_start_paragraph = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert the indentation specified by AMOUNT. */
|
|
|
|
|
void
|
|
|
|
|
indent (amount)
|
|
|
|
|
int amount;
|
|
|
|
|
{
|
|
|
|
|
register BRACE_ELEMENT *elt = brace_stack;
|
|
|
|
|
|
|
|
|
|
/* For every START_POS saved within the brace stack which will be affected
|
|
|
|
|
by this indentation, bump that start pos forward. */
|
|
|
|
|
while (elt)
|
|
|
|
|
{
|
|
|
|
|
if (elt->pos >= output_paragraph_offset)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
elt->pos += amount;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
elt = elt->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (--amount >= 0)
|
|
|
|
|
insert (' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search forward for STRING in input_text.
|
|
|
|
|
FROM says where where to start. */
|
|
|
|
|
int
|
|
|
|
|
search_forward (string, from)
|
|
|
|
|
char *string;
|
|
|
|
|
int from;
|
|
|
|
|
{
|
|
|
|
|
int len = strlen (string);
|
|
|
|
|
|
|
|
|
|
while (from < size_of_input_text)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp (input_text + from, string, len) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (from);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
from++;
|
|
|
|
|
}
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Whoops, Unix doesn't have strcasecmp. */
|
|
|
|
|
|
|
|
|
|
/* Case independent string compare. */
|
|
|
|
|
#if !defined (HAVE_STRCASECMP)
|
|
|
|
|
int
|
|
|
|
|
strcasecmp (string1, string2)
|
|
|
|
|
char *string1, *string2;
|
|
|
|
|
{
|
|
|
|
|
char ch1, ch2;
|
|
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
ch1 = *string1++;
|
|
|
|
|
ch2 = *string2++;
|
|
|
|
|
|
|
|
|
|
if (!(ch1 | ch2))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
ch1 = coerce_to_upper (ch1);
|
|
|
|
|
ch2 = coerce_to_upper (ch2);
|
|
|
|
|
|
|
|
|
|
if (ch1 != ch2)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (ch1 - ch2);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* !HAVE_STRCASECMP */
|
|
|
|
|
|
|
|
|
|
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 ()
|
|
|
|
|
{
|
|
|
|
|
register int level, done;
|
|
|
|
|
register INSERTION_ELT *elt;
|
|
|
|
|
|
|
|
|
|
level = insertion_level;
|
|
|
|
|
elt = insertion_stack;
|
|
|
|
|
done = 0;
|
|
|
|
|
|
|
|
|
|
/* Skip down through the stack until we find a non-conditional insertion. */
|
|
|
|
|
while (!done && (elt != NULL))
|
|
|
|
|
{
|
|
|
|
|
switch (elt->insertion)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case ifinfo:
|
|
|
|
|
case ifnothtml:
|
|
|
|
|
case ifnottex:
|
|
|
|
|
case ifset:
|
|
|
|
|
case ifclear:
|
|
|
|
|
case cartouche:
|
|
|
|
|
elt = elt->next;
|
|
|
|
|
level--;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
done = 1;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!level)
|
|
|
|
|
return ((char *) NULL);
|
|
|
|
|
else
|
|
|
|
|
return (elt->item_function);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
get_item_function ()
|
|
|
|
|
{
|
|
|
|
|
char *item_function;
|
|
|
|
|
get_rest_of_line (&item_function);
|
|
|
|
|
backup_input_pointer ();
|
|
|
|
|
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;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
new->filename = xstrdup (input_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
new->inhibited = inhibit_paragraph_indentation;
|
|
|
|
|
new->in_fixed_width_font = in_fixed_width_font;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
in_fixed_width_font = temp->in_fixed_width_font;
|
|
|
|
|
inhibit_paragraph_indentation = temp->inhibited;
|
|
|
|
|
filling_enabled = temp->filling_enabled;
|
|
|
|
|
indented_fill = temp->indented_fill;
|
|
|
|
|
free_and_clear (&(temp->item_function));
|
|
|
|
|
free_and_clear (&(temp->filename));
|
|
|
|
|
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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (_("Broken-Type in insertion_type_pname"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 (strcmp (name, insertion_type_names[index]) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (enum insertion_type) index;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
return (bad_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
defun_insertion (type)
|
|
|
|
|
enum insertion_type type;
|
|
|
|
|
{
|
|
|
|
|
return
|
|
|
|
|
((type == deffn)
|
|
|
|
|
|| (type == defun)
|
|
|
|
|
|| (type == defmac)
|
|
|
|
|
|| (type == defspec)
|
|
|
|
|
|| (type == defvr)
|
|
|
|
|
|| (type == defvar)
|
|
|
|
|
|| (type == defopt)
|
|
|
|
|
|| (type == deftypefn)
|
|
|
|
|
|| (type == deftypefun)
|
|
|
|
|
|| (type == deftypevr)
|
|
|
|
|
|| (type == deftypevar)
|
|
|
|
|
|| (type == defcv)
|
|
|
|
|
|| (type == defivar)
|
|
|
|
|
|| (type == defop)
|
|
|
|
|
|| (type == defmethod)
|
|
|
|
|
|| (type == deftypemethod)
|
|
|
|
|
|| (type == deftp));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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_stack_depth 100
|
|
|
|
|
#define ENUM_DIGITS 1
|
|
|
|
|
#define ENUM_ALPHA 2
|
|
|
|
|
typedef struct {
|
|
|
|
|
int enumtype;
|
|
|
|
|
int enumval;
|
|
|
|
|
} DIGIT_ALPHA;
|
|
|
|
|
|
|
|
|
|
DIGIT_ALPHA enumstack[max_stack_depth];
|
|
|
|
|
int enumstack_offset = 0;
|
|
|
|
|
int current_enumval = 1;
|
|
|
|
|
int current_enumtype = ENUM_DIGITS;
|
|
|
|
|
char *enumeration_arg = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
start_enumerating (at, type)
|
|
|
|
|
int at, type;
|
|
|
|
|
{
|
|
|
|
|
if ((enumstack_offset + 1) == max_stack_depth)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Enumeration stack overflow"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
enumstack[enumstack_offset].enumtype = current_enumtype;
|
|
|
|
|
enumstack[enumstack_offset].enumval = current_enumval;
|
|
|
|
|
enumstack_offset++;
|
|
|
|
|
current_enumval = at;
|
|
|
|
|
current_enumtype = type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
stop_enumerating ()
|
|
|
|
|
{
|
|
|
|
|
--enumstack_offset;
|
|
|
|
|
if (enumstack_offset < 0)
|
|
|
|
|
enumstack_offset = 0;
|
|
|
|
|
|
|
|
|
|
current_enumval = enumstack[enumstack_offset].enumval;
|
|
|
|
|
current_enumtype = enumstack[enumstack_offset].enumtype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Place a letter or digits into the output stream. */
|
|
|
|
|
void
|
|
|
|
|
enumerate_item ()
|
|
|
|
|
{
|
|
|
|
|
char temp[10];
|
|
|
|
|
|
|
|
|
|
if (current_enumtype == ENUM_ALPHA)
|
|
|
|
|
{
|
|
|
|
|
if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
|
|
|
|
|
warning (_("lettering overflow, restarting at %c"), current_enumval);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
sprintf (temp, "%c. ", current_enumval);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
sprintf (temp, "%d. ", current_enumval);
|
|
|
|
|
|
|
|
|
|
indent (output_column += (current_indent - strlen (temp)));
|
|
|
|
|
add_word (temp);
|
|
|
|
|
current_enumval++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
if (defun_insertion (type))
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
push_insertion (type, xstrdup (""));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
no_discard++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
push_insertion (type, get_item_function ());
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case menu:
|
|
|
|
|
if (!no_headers)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
close_paragraph ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
filling_enabled = no_indent = 0;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
|
|
|
|
|
if (!no_headers)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word (_("* Menu:\n"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
in_menu++;
|
|
|
|
|
no_discard++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case detailmenu:
|
|
|
|
|
if (!in_menu)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (!no_headers)
|
|
|
|
|
close_paragraph ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
filling_enabled = no_indent = 0;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
no_discard++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
in_detailmenu++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case direntry:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
filling_enabled = no_indent = 0;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
insert_string ("START-INFO-DIR-ENTRY\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* I think @quotation is meant to do filling.
|
1999-01-14 19:56:32 +00:00
|
|
|
|
If you don't want filling, then use @display. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
case quotation:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
last_char_was_newline = no_indent = 0;
|
|
|
|
|
indented_fill = filling_enabled = 1;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
current_indent += default_indentation_increment;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case display:
|
|
|
|
|
case example:
|
|
|
|
|
case smallexample:
|
|
|
|
|
case lisp:
|
|
|
|
|
case smalllisp:
|
|
|
|
|
/* Just like @example, but no indentation. */
|
|
|
|
|
case format:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
in_fixed_width_font++;
|
|
|
|
|
filling_enabled = 0;
|
|
|
|
|
last_char_was_newline = 0;
|
|
|
|
|
if (type != format)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
current_indent += default_indentation_increment;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case multitable:
|
|
|
|
|
do_multitable ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case table:
|
|
|
|
|
case ftable:
|
|
|
|
|
case vtable:
|
|
|
|
|
case itemize:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
current_indent += default_indentation_increment;
|
|
|
|
|
filling_enabled = indented_fill = 1;
|
|
|
|
|
#if defined (INDENT_PARAGRAPHS_IN_TABLE)
|
|
|
|
|
inhibit_paragraph_indentation = 0;
|
|
|
|
|
#else
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
|
|
|
|
|
|
|
|
/* Make things work for losers who forget the itemize syntax. */
|
|
|
|
|
if (allow_lax_format && (type == itemize))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (!(*insertion_stack->item_function))
|
|
|
|
|
{
|
|
|
|
|
free (insertion_stack->item_function);
|
|
|
|
|
insertion_stack->item_function = xstrdup ("@bullet");
|
|
|
|
|
insertion_stack->item_function[0] = COMMAND_PREFIX;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!*insertion_stack->item_function)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
line_error (_("%s requires an argument: the formatter for %citem"),
|
|
|
|
|
insertion_type_pname (type), COMMAND_PREFIX);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case enumerate:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
no_indent = 0;
|
|
|
|
|
#if defined (INDENT_PARAGRAPHS_IN_TABLE)
|
|
|
|
|
inhibit_paragraph_indentation = 0;
|
|
|
|
|
#else
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
|
|
|
|
|
|
|
|
current_indent += default_indentation_increment;
|
|
|
|
|
filling_enabled = indented_fill = 1;
|
|
|
|
|
|
|
|
|
|
if (isdigit (*enumeration_arg))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
start_enumerating (*enumeration_arg, ENUM_ALPHA);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Does nothing special in makeinfo. */
|
|
|
|
|
case group:
|
|
|
|
|
/* Only close the paragraph if we are not inside of an @example. */
|
|
|
|
|
if (!insertion_stack->next ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
insertion_stack->next->insertion != example)
|
|
|
|
|
close_single_paragraph ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Insertions that are no-ops in info, but do something in TeX. */
|
|
|
|
|
case ifinfo:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
case ifnothtml:
|
|
|
|
|
case ifnottex:
|
1997-01-11 02:12:38 +00:00
|
|
|
|
case ifset:
|
|
|
|
|
case ifclear:
|
|
|
|
|
case cartouche:
|
|
|
|
|
if (in_menu)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
no_discard++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case deffn:
|
|
|
|
|
case defun:
|
|
|
|
|
case defmac:
|
|
|
|
|
case defspec:
|
|
|
|
|
case defvr:
|
|
|
|
|
case defvar:
|
|
|
|
|
case defopt:
|
|
|
|
|
case deftypefn:
|
|
|
|
|
case deftypefun:
|
|
|
|
|
case deftypevr:
|
|
|
|
|
case deftypevar:
|
|
|
|
|
case defcv:
|
|
|
|
|
case defivar:
|
|
|
|
|
case defop:
|
|
|
|
|
case defmethod:
|
|
|
|
|
case deftypemethod:
|
|
|
|
|
case deftp:
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
filling_enabled = indented_fill = 1;
|
|
|
|
|
current_indent += default_indentation_increment;
|
|
|
|
|
no_indent = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case flushleft:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
filling_enabled = indented_fill = no_indent = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case flushright:
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
filling_enabled = indented_fill = no_indent = 0;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
force_flush_right++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!no_discard)
|
|
|
|
|
discard_until ("\n");
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Try to end the insertion with the specified TYPE. With a value of
|
|
|
|
|
`bad_type', TYPE gets translated to match the value currently on top
|
|
|
|
|
of the stack. Otherwise, if TYPE doesn't match the top of the
|
|
|
|
|
insertion stack, give error. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(_("`%cend' expected `%s', but saw `%s'"), COMMAND_PREFIX,
|
|
|
|
|
insertion_type_pname (temp_type), insertion_type_pname (type));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pop_insertion ();
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
/* Insertions which have no effect on paragraph formatting. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
case ifnothtml:
|
|
|
|
|
case ifnottex:
|
1997-01-11 02:12:38 +00:00
|
|
|
|
case ifinfo:
|
|
|
|
|
case ifset:
|
|
|
|
|
case ifclear:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case direntry:
|
|
|
|
|
insert_string ("END-INFO-DIR-ENTRY\n\n");
|
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case detailmenu:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
in_detailmenu--; /* No longer hacking menus. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!in_menu)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (!no_headers)
|
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case menu:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
in_menu--; /* No longer hacking menus. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!no_headers)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
close_insertion_paragraph ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case multitable:
|
|
|
|
|
end_multitable ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case enumerate:
|
|
|
|
|
stop_enumerating ();
|
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
current_indent -= default_indentation_increment;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case flushleft:
|
|
|
|
|
case group:
|
|
|
|
|
case cartouche:
|
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case format:
|
|
|
|
|
case display:
|
|
|
|
|
case example:
|
|
|
|
|
case smallexample:
|
|
|
|
|
case lisp:
|
|
|
|
|
case smalllisp:
|
|
|
|
|
case quotation:
|
|
|
|
|
/* @format is the only fixed_width insertion without a change
|
1999-01-14 19:56:32 +00:00
|
|
|
|
in indentation. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (type != format)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
current_indent -= default_indentation_increment;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* The ending of one of these insertions always marks the
|
1999-01-14 19:56:32 +00:00
|
|
|
|
start of a new paragraph. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case table:
|
|
|
|
|
case ftable:
|
|
|
|
|
case vtable:
|
|
|
|
|
case itemize:
|
|
|
|
|
current_indent -= default_indentation_increment;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case flushright:
|
|
|
|
|
force_flush_right--;
|
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Handle the @defun style insertions with a default clause. */
|
|
|
|
|
default:
|
|
|
|
|
current_indent -= default_indentation_increment;
|
|
|
|
|
close_insertion_paragraph ();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insertions cannot cross certain boundaries, such as node beginnings. In
|
1999-01-14 19:56:32 +00:00
|
|
|
|
code that creates such boundaries, you should call `discard_insertions'
|
1997-01-11 02:12:38 +00:00
|
|
|
|
before doing anything else. It prints the errors for you, and cleans up
|
1999-01-14 19:56:32 +00:00
|
|
|
|
the insertion stack. With nonzero SPECIALS_OK, allows unmatched
|
|
|
|
|
ifinfo, ifset, ifclear, otherwise not. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
discard_insertions (specials_ok)
|
|
|
|
|
int specials_ok;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
int real_line_number = line_number;
|
|
|
|
|
while (insertion_stack)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (specials_ok && (insertion_stack->insertion == ifinfo
|
|
|
|
|
|| insertion_stack->insertion == ifset
|
|
|
|
|
|| insertion_stack->insertion == ifclear))
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *offender = insertion_type_pname (insertion_stack->insertion);
|
|
|
|
|
char *current_filename = input_filename;
|
|
|
|
|
|
|
|
|
|
input_filename = insertion_stack->filename;
|
|
|
|
|
line_number = insertion_stack->line_number;
|
|
|
|
|
line_error (_("No matching `%cend %s'"), COMMAND_PREFIX, offender);
|
|
|
|
|
input_filename = current_filename;
|
|
|
|
|
pop_insertion ();
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
line_number = real_line_number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The Texinfo commands. */
|
|
|
|
|
|
|
|
|
|
/* Commands which insert their own names. */
|
|
|
|
|
void
|
|
|
|
|
insert_self (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
add_word (command);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
insert_space (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
add_char (' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Force a line break in the output. */
|
|
|
|
|
void
|
|
|
|
|
cm_asterisk ()
|
|
|
|
|
{
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
cm_noindent ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert ellipsis. */
|
|
|
|
|
void
|
|
|
|
|
cm_dots (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
add_word ("...");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert ellipsis for sentence end. */
|
|
|
|
|
void
|
|
|
|
|
cm_enddots (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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copyright symbol. */
|
|
|
|
|
void
|
|
|
|
|
cm_copyright (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
add_word ("(C)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Accent commands that take explicit arguments. */
|
|
|
|
|
void
|
|
|
|
|
cm_accent (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (command, "dotaccent") == 0) /* overdot */
|
|
|
|
|
add_char ('.');
|
|
|
|
|
else if (strcmp (command, "H") == 0) /* Hungarian umlaut */
|
|
|
|
|
add_word ("''");
|
|
|
|
|
else if (strcmp (command, "ringaccent") == 0)
|
|
|
|
|
add_char ('*');
|
|
|
|
|
else if (strcmp (command, "tieaccent") == 0)
|
|
|
|
|
add_char ('[');
|
|
|
|
|
else if (strcmp (command, "u") == 0) /* breve */
|
|
|
|
|
add_char ('(');
|
|
|
|
|
else if (strcmp (command, "v") == 0) /* hacek/check */
|
|
|
|
|
add_char ('<');
|
|
|
|
|
}
|
|
|
|
|
else if (arg == END)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (command, "ubaraccent") == 0) /* underbar */
|
|
|
|
|
add_char ('_');
|
|
|
|
|
else if (strcmp (command, "udotaccent") == 0) /* underdot */
|
|
|
|
|
add_word ("-.");
|
|
|
|
|
else if (strcmp (command, ",") == 0) /* cedilla */
|
|
|
|
|
add_word (",");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Non-English letters/characters that don't insert themselves. */
|
|
|
|
|
void
|
|
|
|
|
cm_special_char (arg)
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
|
|
|
|
if ((*command == 'L' || *command == 'l'
|
|
|
|
|
|| *command == 'O' || *command == 'o')
|
|
|
|
|
&& command[1] == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Lslash lslash Oslash oslash */
|
|
|
|
|
add_char (*command);
|
|
|
|
|
add_char ('/');
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp (command, "exclamdown") == 0)
|
|
|
|
|
add_char ('!');
|
|
|
|
|
else if (strcmp (command, "pounds") == 0)
|
|
|
|
|
add_char ('#');
|
|
|
|
|
else if (strcmp (command, "questiondown") == 0)
|
|
|
|
|
add_char ('?');
|
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
fprintf (stderr, _("How did @%s end up in cm_special_char?\n"), command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dotless i or j. */
|
|
|
|
|
void
|
|
|
|
|
cm_dotless (arg, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
{
|
|
|
|
|
if (output_paragraph[start] != 'i' && output_paragraph[start] != 'j')
|
|
|
|
|
/* This error message isn't perfect if the argument is multiple
|
|
|
|
|
characters, but it doesn't seem worth getting right. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%c%s expects `i' or `j' as argument, not `%c'"),
|
1997-01-11 02:12:38 +00:00
|
|
|
|
COMMAND_PREFIX, command, output_paragraph[start]);
|
|
|
|
|
|
|
|
|
|
else if (end - start != 1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%c%s expects a single character `i' or `j' as argument"),
|
|
|
|
|
COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* We've already inserted the `i' or `j', so nothing to do. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_today (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
static char *months [12] =
|
|
|
|
|
{ N_("January"), N_("February"), N_("March"), N_("April"), N_("May"),
|
|
|
|
|
N_("June"), N_("July"), N_("August"), N_("September"), N_("October"),
|
|
|
|
|
N_("November"), N_("December") };
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
1998-06-29 15:52:49 +00:00
|
|
|
|
time_t timer = time (0);
|
|
|
|
|
struct tm *ts = localtime (&timer);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word_args ("%d %s %d", ts->tm_mday, _(months[ts->tm_mon]),
|
|
|
|
|
ts->tm_year + 1900);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_code (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
extern int printing_index;
|
|
|
|
|
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
|
|
|
|
in_fixed_width_font++;
|
|
|
|
|
|
|
|
|
|
if (!printing_index)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_char ('`');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!printing_index)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_char ('\'');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_kbd (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
/* People use @kbd in an example to get the "user input" font.
|
|
|
|
|
We don't want quotes in that case. */
|
|
|
|
|
if (!in_fixed_width_font)
|
|
|
|
|
cm_code (arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_key (arg)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
add_char (arg == START ? '<' : '>');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert the character at position into a true control character. */
|
|
|
|
|
void
|
|
|
|
|
cm_ctrl (arg, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
|
|
|
|
/* Should we allow multiple character arguments? I think yes. */
|
|
|
|
|
if (arg == END)
|
|
|
|
|
{
|
|
|
|
|
register int i, character;
|
|
|
|
|
#if defined (NO_MULTIPLE_CTRL)
|
|
|
|
|
if ((end - start) != 1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%c%s expects a single character as an argument"),
|
|
|
|
|
COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
#endif
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = start; i < end; i++)
|
|
|
|
|
{
|
|
|
|
|
character = output_paragraph[i];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (isletter (character))
|
|
|
|
|
output_paragraph[i] = CTL (coerce_to_upper (character));
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle a command that switches to a non-fixed-width font. */
|
|
|
|
|
void
|
|
|
|
|
not_fixed_width (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
in_fixed_width_font = 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Small caps and @var in makeinfo just uppercase the text. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_var_sc (arg, start_pos, end_pos)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int arg, start_pos, end_pos;
|
|
|
|
|
{
|
|
|
|
|
not_fixed_width (arg);
|
|
|
|
|
|
|
|
|
|
if (arg == END)
|
|
|
|
|
{
|
|
|
|
|
while (start_pos < end_pos)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
output_paragraph[start_pos] =
|
|
|
|
|
coerce_to_upper (output_paragraph[start_pos]);
|
|
|
|
|
start_pos++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 ("'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No highlighting, but argument switches fonts. */
|
|
|
|
|
void
|
|
|
|
|
cm_not_fixed_width (arg, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
|
|
|
|
not_fixed_width (arg);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Various commands are no-op's. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
cm_no_op ()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* No-op that eats its argument on same line. */
|
|
|
|
|
void
|
|
|
|
|
cm_no_op_line_arg ()
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
get_rest_of_line (&temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Prevent the argument from being split across two lines. */
|
|
|
|
|
void
|
|
|
|
|
cm_w (arg, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
|
|
|
|
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, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("%c%s is obsolete"), COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
{
|
|
|
|
|
register int i, len;
|
|
|
|
|
int old_no_indent, starting_pos, ending_pos;
|
|
|
|
|
char *temp;
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
old_no_indent = no_indent;
|
|
|
|
|
no_indent = 1;
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
append_to_expansion_output (input_text_offset + 1);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&temp);
|
|
|
|
|
|
|
|
|
|
starting_pos = output_position + output_paragraph_offset;
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *temp1 = (char *) xmalloc (2 + strlen (temp));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
sprintf (temp1, "%s\n", temp);
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
me_execute_string (temp1);
|
|
|
|
|
free (temp1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif /* HAVE_MACROS */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string ("%s\n", temp);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
ending_pos = output_position + output_paragraph_offset;
|
|
|
|
|
free (temp);
|
|
|
|
|
|
|
|
|
|
len = (ending_pos - starting_pos) - 1;
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
add_char (with_char);
|
|
|
|
|
insert ('\n');
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
filling_enabled = 1;
|
|
|
|
|
no_indent = old_no_indent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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[] = {
|
|
|
|
|
{ "unnumberedsubsubsec", 5 },
|
|
|
|
|
{ "unnumberedsubsec", 4 },
|
|
|
|
|
{ "unnumberedsec", 3 },
|
|
|
|
|
{ "unnumbered", 2 },
|
|
|
|
|
{ "appendixsubsubsec", 5 },
|
|
|
|
|
{ "appendixsubsec", 4 },
|
|
|
|
|
{ "appendixsec", 3 },
|
|
|
|
|
{ "appendixsection", 3 },
|
|
|
|
|
{ "appendix", 2 },
|
|
|
|
|
{ "subsubsec", 5 },
|
|
|
|
|
{ "subsubsection", 5 },
|
|
|
|
|
{ "subsection", 4 },
|
|
|
|
|
{ "section", 3 },
|
|
|
|
|
{ "chapter", 2 },
|
|
|
|
|
{ "top", 1 },
|
|
|
|
|
|
|
|
|
|
{ (char *)NULL, 0 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Amount to offset the name of sectioning commands to levels by. */
|
|
|
|
|
int section_alist_offset = 0;
|
|
|
|
|
|
|
|
|
|
/* Shift the meaning of @section to @chapter. */
|
|
|
|
|
void
|
|
|
|
|
cm_raisesections ()
|
|
|
|
|
{
|
|
|
|
|
discard_until ("\n");
|
|
|
|
|
section_alist_offset--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Shift the meaning of @chapter to @section. */
|
|
|
|
|
void
|
|
|
|
|
cm_lowersections ()
|
|
|
|
|
{
|
|
|
|
|
discard_until ("\n");
|
|
|
|
|
section_alist_offset++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return an integer which identifies the type section present in TEXT. */
|
|
|
|
|
int
|
|
|
|
|
what_section (text)
|
|
|
|
|
char *text;
|
|
|
|
|
{
|
|
|
|
|
register int i, j;
|
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
|
|
find_section_command:
|
|
|
|
|
for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
|
|
|
|
|
if (text[j] != COMMAND_PREFIX)
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
text = text + j + 1;
|
|
|
|
|
|
|
|
|
|
/* We skip @c, @comment, and @?index commands. */
|
|
|
|
|
if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
|
|
|
|
|
(text[0] == 'c' && cr_or_whitespace (text[1])) ||
|
|
|
|
|
(strcmp (text + 1, "index") == 0))
|
|
|
|
|
{
|
|
|
|
|
while (*text++ != '\n');
|
|
|
|
|
goto find_section_command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle italicized sectioning commands. */
|
|
|
|
|
if (*text == 'i')
|
|
|
|
|
text++;
|
|
|
|
|
|
|
|
|
|
for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = 0; (t = section_alist[i].name); i++)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (j == strlen (t) && strncmp (t, text, j) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int return_val;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return_val = (section_alist[i].level + section_alist_offset);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (return_val < 0)
|
|
|
|
|
return_val = 0;
|
|
|
|
|
else if (return_val > 5)
|
|
|
|
|
return_val = 5;
|
|
|
|
|
return (return_val);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the level of @top to LEVEL. Return the old level of @top. */
|
|
|
|
|
int
|
|
|
|
|
set_top_section_level (level)
|
|
|
|
|
int level;
|
|
|
|
|
{
|
|
|
|
|
register int i, result = -1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; section_alist[i].name; i++)
|
|
|
|
|
if (strcmp (section_alist[i].name, "top") == 0)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
result = section_alist[i].level;
|
|
|
|
|
section_alist[i].level = level;
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Treat this just like @unnumbered. The only difference is
|
|
|
|
|
in node defaulting. */
|
|
|
|
|
void
|
|
|
|
|
cm_top ()
|
|
|
|
|
{
|
|
|
|
|
/* It is an error to have more than one @top. */
|
|
|
|
|
if (top_node_seen)
|
|
|
|
|
{
|
|
|
|
|
TAG_ENTRY *tag = tag_table;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Node with %ctop as a section already exists"),
|
|
|
|
|
COMMAND_PREFIX);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
while (tag != (TAG_ENTRY *)NULL)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if ((tag->flags & IS_TOP))
|
|
|
|
|
{
|
|
|
|
|
int old_line_number = line_number;
|
|
|
|
|
char *old_input_filename = input_filename;
|
|
|
|
|
|
|
|
|
|
line_number = tag->line_no;
|
|
|
|
|
input_filename = tag->filename;
|
|
|
|
|
line_error (_("Here is the %ctop node"), COMMAND_PREFIX);
|
|
|
|
|
input_filename = old_input_filename;
|
|
|
|
|
line_number = old_line_number;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
tag = tag->next_ent;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
top_node_seen = 1;
|
|
|
|
|
|
|
|
|
|
/* It is an error to use @top before you have used @node. */
|
|
|
|
|
if (!tag_table)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *top_name;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&top_name);
|
|
|
|
|
free (top_name);
|
|
|
|
|
line_error (_("%ctop used before %cnode, defaulting to %s"),
|
|
|
|
|
COMMAND_PREFIX, COMMAND_PREFIX, top_name);
|
|
|
|
|
execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
cm_unnumbered ();
|
|
|
|
|
|
|
|
|
|
/* The most recently defined node is the top node. */
|
|
|
|
|
tag_table->flags |= IS_TOP;
|
|
|
|
|
|
|
|
|
|
/* Now set the logical hierarchical level of the Top node. */
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int orig_offset = input_text_offset;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_text_offset = search_forward (node_search_string, orig_offset);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (input_text_offset > 0)
|
|
|
|
|
{
|
|
|
|
|
int this_section;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* We have encountered a non-top node, so mark that one exists. */
|
|
|
|
|
non_top_node_seen = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Move to the end of this line, and find out what the
|
|
|
|
|
sectioning command is here. */
|
|
|
|
|
while (input_text[input_text_offset] != '\n')
|
|
|
|
|
input_text_offset++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (input_text_offset < size_of_input_text)
|
|
|
|
|
input_text_offset++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
this_section = what_section (input_text + input_text_offset);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* If we found a sectioning command, then give the top section
|
|
|
|
|
a level of this section - 1. */
|
|
|
|
|
if (this_section != -1)
|
|
|
|
|
set_top_section_level (this_section - 1);
|
|
|
|
|
}
|
|
|
|
|
input_text_offset = orig_offset;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Organized by level commands. That is, "*" == chapter, "=" == section. */
|
|
|
|
|
char *scoring_characters = "*=-.";
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sectioning_underscore (command)
|
|
|
|
|
char *command;
|
|
|
|
|
{
|
|
|
|
|
char character;
|
|
|
|
|
char *temp;
|
|
|
|
|
int level;
|
|
|
|
|
|
|
|
|
|
temp = (char *)xmalloc (2 + strlen (command));
|
|
|
|
|
temp[0] = COMMAND_PREFIX;
|
|
|
|
|
strcpy (&temp[1], command);
|
|
|
|
|
level = what_section (temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
level -= 2;
|
|
|
|
|
|
|
|
|
|
if (level < 0)
|
|
|
|
|
level = 0;
|
|
|
|
|
|
|
|
|
|
character = scoring_characters[level];
|
|
|
|
|
|
|
|
|
|
insert_and_underscore (character);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The command still works, but prints a warning message in addition. */
|
|
|
|
|
void
|
|
|
|
|
cm_ideprecated (arg, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("%c%s is obsolete; use %c%s instead"),
|
|
|
|
|
COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
sectioning_underscore (command + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is a chapter heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_chapter ()
|
|
|
|
|
{
|
|
|
|
|
sectioning_underscore ("chapter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is a section heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_section ()
|
|
|
|
|
{
|
|
|
|
|
sectioning_underscore ("section");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is a subsection heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_subsection ()
|
|
|
|
|
{
|
|
|
|
|
sectioning_underscore ("subsection");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is a subsubsection heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_subsubsection ()
|
|
|
|
|
{
|
|
|
|
|
sectioning_underscore ("subsubsection");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an unnumbered heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_unnumbered ()
|
|
|
|
|
{
|
|
|
|
|
cm_chapter ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an unnumbered section heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_unnumberedsec ()
|
|
|
|
|
{
|
|
|
|
|
cm_section ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an unnumbered
|
|
|
|
|
subsection heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_unnumberedsubsec ()
|
|
|
|
|
{
|
|
|
|
|
cm_subsection ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an unnumbered
|
|
|
|
|
subsubsection heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_unnumberedsubsubsec ()
|
|
|
|
|
{
|
|
|
|
|
cm_subsubsection ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an appendix heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_appendix ()
|
|
|
|
|
{
|
|
|
|
|
cm_chapter ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an appendix section heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_appendixsec ()
|
|
|
|
|
{
|
|
|
|
|
cm_section ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an appendix subsection heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_appendixsubsec ()
|
|
|
|
|
{
|
|
|
|
|
cm_subsection ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The remainder of the text on this line is an appendix
|
|
|
|
|
subsubsection heading. */
|
|
|
|
|
void
|
|
|
|
|
cm_appendixsubsubsec ()
|
|
|
|
|
{
|
|
|
|
|
cm_subsubsection ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compatibility functions substitute for chapter, section, etc. */
|
|
|
|
|
void
|
|
|
|
|
cm_majorheading ()
|
|
|
|
|
{
|
|
|
|
|
cm_chapheading ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_chapheading ()
|
|
|
|
|
{
|
|
|
|
|
cm_chapter ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_heading ()
|
|
|
|
|
{
|
|
|
|
|
cm_section ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_subheading ()
|
|
|
|
|
{
|
|
|
|
|
cm_subsection ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_subsubheading ()
|
|
|
|
|
{
|
|
|
|
|
cm_subsubsection ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Adding nodes, and making tags */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
write_tag_table ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
write_tag_table_internal (0); /* Not indirect. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
write_tag_table_indirect ()
|
|
|
|
|
{
|
|
|
|
|
write_tag_table_internal (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write out the contents of the existing tag table.
|
|
|
|
|
INDIRECT_P says how to format the output. */
|
|
|
|
|
void
|
|
|
|
|
write_tag_table_internal (indirect_p)
|
|
|
|
|
int indirect_p;
|
|
|
|
|
{
|
|
|
|
|
TAG_ENTRY *node = tag_table;
|
|
|
|
|
int old_indent = no_indent;
|
|
|
|
|
|
|
|
|
|
no_indent = 1;
|
|
|
|
|
filling_enabled = 0;
|
|
|
|
|
must_start_paragraph = 0;
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
|
|
|
|
|
if (!indirect_p)
|
|
|
|
|
{
|
|
|
|
|
no_indent = 1;
|
|
|
|
|
insert ('\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
|
|
|
|
|
|
|
|
|
|
while (node != (TAG_ENTRY *) NULL)
|
|
|
|
|
{
|
|
|
|
|
execute_string ("Node: %s", node->node);
|
|
|
|
|
add_word_args ("\177%d\n", node->position);
|
|
|
|
|
node = node->next_ent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add_word ("\037\nEnd Tag Table\n");
|
|
|
|
|
flush_output ();
|
|
|
|
|
no_indent = old_indent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_node_token (expand)
|
|
|
|
|
int expand;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *string;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_until_in_line (expand, ",", &string);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (curchar () == ',')
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
|
|
|
|
|
canon_white (string);
|
|
|
|
|
|
|
|
|
|
/* Force all versions of "top" to be "Top". */
|
|
|
|
|
normalize_node_name (string);
|
|
|
|
|
|
|
|
|
|
return (string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert "top" and friends into "Top". */
|
|
|
|
|
void
|
|
|
|
|
normalize_node_name (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
if (strcasecmp (string, "Top") == 0)
|
|
|
|
|
strcpy (string, "Top");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 (strcmp (tag->node, name) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (tag);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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;
|
|
|
|
|
int position, line_no, no_warn;
|
|
|
|
|
{
|
|
|
|
|
/* Check for existence of this tag already. */
|
|
|
|
|
if (validating)
|
|
|
|
|
{
|
|
|
|
|
register TAG_ENTRY *tag = find_node (node);
|
|
|
|
|
if (tag)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
line_error (
|
|
|
|
|
_("Node `%s' multiply defined (line %d is first definition at)"),
|
|
|
|
|
node, tag->line_no);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
new->touched = 0; /* not yet referenced. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
new->flags = 0;
|
|
|
|
|
if (no_warn)
|
|
|
|
|
new->flags |= NO_WARN;
|
|
|
|
|
new->next_ent = tag_table;
|
|
|
|
|
tag_table = new;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The order is: nodename, nextnode, prevnode, upnode.
|
|
|
|
|
If all of the NEXT, PREV, and UP fields are empty, they are 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 node's parent. */
|
|
|
|
|
void
|
|
|
|
|
cm_node ()
|
|
|
|
|
{
|
|
|
|
|
char *node, *prev, *next, *up;
|
|
|
|
|
int new_node_pos, 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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
discard_insertions (1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!already_outputting_pending_notes)
|
|
|
|
|
{
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
output_pending_notes ();
|
|
|
|
|
free_pending_notes ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
new_node_pos = output_position;
|
|
|
|
|
current_footnote_number = 1;
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
append_to_expansion_output (input_text_offset + 1);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
node = get_node_token (1);
|
|
|
|
|
next = get_node_token (0);
|
|
|
|
|
prev = get_node_token (0);
|
|
|
|
|
up = get_node_token (0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (verbose_mode)
|
|
|
|
|
printf (_("Formatting node %s...\n"), node);
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
no_indent = 1;
|
|
|
|
|
if (!no_headers)
|
|
|
|
|
{
|
|
|
|
|
add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename);
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
me_execute_string (node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
#endif /* HAVE_MACROS */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string ("%s", node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check for defaulting of this node's next, prev, and up fields. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
defaulting = (*next == 0 && *prev == 0 && *up == 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
this_section = what_section (input_text + input_text_offset);
|
|
|
|
|
|
|
|
|
|
/* 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) && (strcmp (node, "Top") != 0))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *polite_section_name = "top";
|
|
|
|
|
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. %c%s)"),
|
|
|
|
|
node, COMMAND_PREFIX, polite_section_name);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (strcmp (node, "Top") == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Default the NEXT pointer to be the first menu item in
|
|
|
|
|
this node, if there is a menu in this node. We have to
|
|
|
|
|
try very hard to find the menu, as it may be obscured
|
|
|
|
|
by execution_strings which are on the filestack. For
|
|
|
|
|
every member of the filestack which has a FILENAME
|
|
|
|
|
member which is identical to the current INPUT_FILENAME,
|
|
|
|
|
search forward from that offset. */
|
|
|
|
|
int saved_input_text_offset = input_text_offset;
|
|
|
|
|
int saved_size_of_input_text = size_of_input_text;
|
|
|
|
|
char *saved_input_text = input_text;
|
|
|
|
|
FSTACK *next_file = filestack;
|
|
|
|
|
|
|
|
|
|
int orig_offset, orig_size;
|
|
|
|
|
char *glean_node_from_menu ();
|
|
|
|
|
|
|
|
|
|
/* No matter what, make this file point back at `(dir)'. */
|
|
|
|
|
free (up); up = xstrdup ("(dir)");
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
orig_offset = input_text_offset;
|
|
|
|
|
orig_size =
|
|
|
|
|
search_forward (node_search_string, orig_offset);
|
|
|
|
|
|
|
|
|
|
if (orig_size < 0)
|
|
|
|
|
orig_size = size_of_input_text;
|
|
|
|
|
|
|
|
|
|
input_text_offset =
|
|
|
|
|
search_forward (menu_search_string, orig_offset);
|
|
|
|
|
|
|
|
|
|
if (input_text_offset > -1)
|
|
|
|
|
{
|
|
|
|
|
char *nodename_from_menu = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
input_text_offset =
|
|
|
|
|
search_forward ("\n* ", input_text_offset);
|
|
|
|
|
|
|
|
|
|
if (input_text_offset != -1)
|
|
|
|
|
nodename_from_menu = glean_node_from_menu (0);
|
|
|
|
|
|
|
|
|
|
if (nodename_from_menu)
|
|
|
|
|
{
|
|
|
|
|
free (next); next = nodename_from_menu;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We got here, so it hasn't been found yet. Try
|
|
|
|
|
the next file on the filestack if there is one. */
|
|
|
|
|
if (next_file &&
|
|
|
|
|
(strcmp (next_file->filename, input_filename) == 0))
|
|
|
|
|
{
|
|
|
|
|
input_text = next_file->text;
|
|
|
|
|
input_text_offset = next_file->offset;
|
|
|
|
|
size_of_input_text = next_file->size;
|
|
|
|
|
next_file = next_file->next;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* No more input files to check. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_text = saved_input_text;
|
|
|
|
|
input_text_offset = saved_input_text_offset;
|
|
|
|
|
size_of_input_text = saved_size_of_input_text;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Fix the level of the menu references in the Top node, iff it
|
1999-01-14 19:56:32 +00:00
|
|
|
|
was declared with @top, and no subsequent reference was found. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (top_node_seen && !non_top_node_seen)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* Then this is the first non-@top node seen. */
|
|
|
|
|
int level;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
level = set_top_section_level (this_section - 1);
|
|
|
|
|
non_top_node_seen = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while (ref)
|
|
|
|
|
{
|
|
|
|
|
if (ref->section == level)
|
|
|
|
|
ref->section = this_section - 1;
|
|
|
|
|
ref = ref->next;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
ref = node_references;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
while (ref)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (ref->section == (this_section - 1) &&
|
|
|
|
|
ref->type == menu_reference &&
|
|
|
|
|
strcmp (ref->node, node) == 0)
|
|
|
|
|
{
|
|
|
|
|
char *containing_node = ref->containing_node;
|
|
|
|
|
|
|
|
|
|
free (up);
|
|
|
|
|
up = xstrdup (containing_node);
|
|
|
|
|
|
|
|
|
|
if (last_ref &&
|
|
|
|
|
last_ref->type == menu_reference &&
|
|
|
|
|
(strcmp (last_ref->containing_node,
|
|
|
|
|
containing_node) == 0))
|
|
|
|
|
{
|
|
|
|
|
free (next);
|
|
|
|
|
next = xstrdup (last_ref->node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((ref->section == this_section - 1) &&
|
|
|
|
|
(ref->next) &&
|
|
|
|
|
(ref->next->type != menu_reference))
|
|
|
|
|
ref = ref->next;
|
|
|
|
|
|
|
|
|
|
if (ref->next && ref->type == menu_reference &&
|
|
|
|
|
(strcmp (ref->next->containing_node,
|
|
|
|
|
containing_node) == 0))
|
|
|
|
|
{
|
|
|
|
|
free (prev);
|
|
|
|
|
prev = xstrdup (ref->next->node);
|
|
|
|
|
}
|
|
|
|
|
else if (!ref->next &&
|
|
|
|
|
strcasecmp (ref->containing_node, "Top") == 0)
|
|
|
|
|
{
|
|
|
|
|
free (prev);
|
|
|
|
|
prev = xstrdup (ref->containing_node);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
last_ref = ref;
|
|
|
|
|
ref = ref->next;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
/* Insert the correct args if we are expanding macros, and the node's
|
|
|
|
|
pointers weren't defaulted. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string && !defaulting)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
int op_orig = output_paragraph_offset;
|
|
|
|
|
|
|
|
|
|
temp = (char *)xmalloc (3 + strlen (next));
|
|
|
|
|
sprintf (temp, ", %s", next);
|
|
|
|
|
me_execute_string (temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
|
|
|
|
|
temp = (char *)xmalloc (3 + strlen (prev));
|
|
|
|
|
sprintf (temp, ", %s", prev);
|
|
|
|
|
me_execute_string (temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
|
|
|
|
|
temp = (char *)xmalloc (4 + strlen (up));
|
|
|
|
|
sprintf (temp, ", %s", up);
|
|
|
|
|
me_execute_string (temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
|
|
|
|
|
output_paragraph_offset = op_orig;
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
if (!no_headers)
|
|
|
|
|
{
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
if (macro_expansion_output_stream)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
me_inhibit_expansion++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
if (*next)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
execute_string (", Next: %s", next);
|
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (*prev)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
execute_string (", Prev: %s", prev);
|
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (*up)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
execute_string (", Up: %s", up);
|
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
if (macro_expansion_output_stream)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
me_inhibit_expansion--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
no_indent = 0;
|
|
|
|
|
|
|
|
|
|
if (!*node)
|
|
|
|
|
{
|
|
|
|
|
line_error ("No node name specified for `%c%s' command",
|
1999-01-14 19:56:32 +00:00
|
|
|
|
COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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 = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Validation of an info file.
|
|
|
|
|
Scan through the list of tag entries 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 (tag_table)
|
|
|
|
|
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 is a "no warn" node, don't validate it in any way. */
|
|
|
|
|
if (tags->flags & NO_WARN)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
tags = tags->next_ent;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this node has a Next, then make sure that the Next exists. */
|
|
|
|
|
if (tags->next)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
|
|
|
|
temp_tag = find_node (tags->next);
|
|
|
|
|
if (temp_tag)
|
|
|
|
|
{
|
|
|
|
|
char *prev;
|
|
|
|
|
|
|
|
|
|
if (temp_tag->flags & NO_WARN)
|
|
|
|
|
{
|
|
|
|
|
/* Do nothing if we aren't supposed to issue warnings
|
|
|
|
|
about this node. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
prev = temp_tag->prev;
|
|
|
|
|
if (!prev || (strcmp (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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Validate the Prev field if there is one, and we haven't already
|
1999-01-14 19:56:32 +00:00
|
|
|
|
complained about it in some way. You don't have to have a Prev
|
|
|
|
|
field at this stage. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!(tags->flags & PREV_ERROR) && tags->prev)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int valid_p = validate (tags->prev, tags->line_no, "Prev");
|
|
|
|
|
|
|
|
|
|
if (!valid_p)
|
|
|
|
|
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 && (strcmp (tags->prev, tags->up) != 0))
|
|
|
|
|
{
|
|
|
|
|
temp_tag = find_node (tags->prev);
|
|
|
|
|
|
|
|
|
|
/* If we aren't supposed to issue warnings about the
|
|
|
|
|
target node, do nothing. */
|
|
|
|
|
if (!temp_tag || (temp_tag->flags & NO_WARN))
|
|
|
|
|
{
|
|
|
|
|
/* Do nothing. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!temp_tag->next ||
|
|
|
|
|
(strcmp (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') has the bad Next"),
|
|
|
|
|
temp_tag->node);
|
|
|
|
|
input_filename = tags->filename;
|
|
|
|
|
line_number = tags->line_no;
|
|
|
|
|
temp_tag->flags |= NEXT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tags->up && (strcasecmp (tags->node, _("Top")) != 0))
|
|
|
|
|
line_error (_("Node `%s' missing Up field"), tags->node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (tags->up)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int valid_p = 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_p && *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 (strcmp (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;
|
|
|
|
|
input_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;
|
|
|
|
|
input_filename = tags->filename;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
/* If this node is a "no warn" node, do nothing. */
|
|
|
|
|
if (tags->flags & NO_WARN)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
tags = tags->next_ent;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
input_filename = tags->filename;
|
|
|
|
|
line_number = tags->line_no;
|
|
|
|
|
warning (_("node `%s' has been referenced %d times"),
|
|
|
|
|
tags->node, tags->touched);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (tags->touched == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
input_filename = tags->filename;
|
|
|
|
|
line_number = tags->line_no;
|
|
|
|
|
|
|
|
|
|
/* Notice that the node "Top" is special, and doesn't have to
|
|
|
|
|
be referenced. */
|
|
|
|
|
if (strcasecmp (tags->node, _("Top")) != 0)
|
|
|
|
|
warning (_("unreferenced node `%s'"), tags->node);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
tags = tags->next_ent;
|
|
|
|
|
}
|
|
|
|
|
input_filename = old_input_filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 1 if tag correctly validated, or 0 if not. */
|
|
|
|
|
int
|
|
|
|
|
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;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%s reference to nonexistent node `%s'"), label, tag);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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;
|
|
|
|
|
int size;
|
|
|
|
|
{
|
|
|
|
|
char *root_filename, *root_pathname;
|
|
|
|
|
char *the_file, *filename_part ();
|
|
|
|
|
struct stat fileinfo;
|
|
|
|
|
long file_size;
|
|
|
|
|
char *the_header;
|
|
|
|
|
int 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) ||
|
|
|
|
|
(((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
|
|
|
|
|
return;
|
|
|
|
|
file_size = (long) fileinfo.st_size;
|
|
|
|
|
|
|
|
|
|
the_file = find_and_load (filename);
|
|
|
|
|
if (!the_file)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
root_filename = filename_part (filename);
|
|
|
|
|
root_pathname = pathname_part (filename);
|
|
|
|
|
|
|
|
|
|
if (!root_pathname)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
root_pathname = xstrdup ("");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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 *indirect_info = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
the_header = (char *)xmalloc (1 + (header_size = tags->position));
|
|
|
|
|
memcpy (the_header, the_file, header_size);
|
|
|
|
|
|
|
|
|
|
while (tags)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int file_top, file_bot, limit;
|
|
|
|
|
|
|
|
|
|
/* Have to include the Control-_. */
|
|
|
|
|
file_top = file_bot = tags->position;
|
|
|
|
|
limit = file_top + size;
|
|
|
|
|
|
|
|
|
|
/* If the rest of this file is only one node, then
|
|
|
|
|
that is the entire subfile. */
|
|
|
|
|
if (!tags->next_ent)
|
|
|
|
|
{
|
|
|
|
|
int i = tags->position + 1;
|
|
|
|
|
char last_char = the_file[i];
|
|
|
|
|
|
|
|
|
|
while (i < file_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. */
|
|
|
|
|
int i = tags->position + 1;
|
|
|
|
|
char last_char = the_file[i];
|
|
|
|
|
|
|
|
|
|
while (i < file_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 == file_top)
|
|
|
|
|
tags = tags->next_ent;
|
|
|
|
|
|
|
|
|
|
file_bot = tags->position;
|
|
|
|
|
|
|
|
|
|
write_region:
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
char *split_filename;
|
|
|
|
|
|
|
|
|
|
split_filename = (char *) xmalloc
|
|
|
|
|
(10 + strlen (root_pathname) + strlen (root_filename));
|
|
|
|
|
sprintf
|
|
|
|
|
(split_filename,
|
|
|
|
|
"%s%s-%d", root_pathname, root_filename, which_file);
|
|
|
|
|
|
|
|
|
|
fd = open
|
|
|
|
|
(split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
|
|
|
|
|
|
|
|
|
|
if ((fd < 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))
|
|
|
|
|
{
|
|
|
|
|
perror (split_filename);
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close (fd);
|
|
|
|
|
exit (FATAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!indirect_info)
|
|
|
|
|
{
|
|
|
|
|
indirect_info = the_file + file_top;
|
|
|
|
|
sprintf (indirect_info, "\037\nIndirect:\n");
|
|
|
|
|
indirect_info += strlen (indirect_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sprintf (indirect_info, "%s-%d: %d\n",
|
|
|
|
|
root_filename, which_file, file_top);
|
|
|
|
|
|
|
|
|
|
free (split_filename);
|
|
|
|
|
indirect_info += strlen (indirect_info);
|
|
|
|
|
which_file++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
perror (filename);
|
|
|
|
|
exit (FATAL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
int distance = indirect_info - the_file;
|
|
|
|
|
fwrite (the_file, 1, distance, output_stream);
|
|
|
|
|
|
|
|
|
|
/* Inhibit newlines. */
|
|
|
|
|
paragraph_is_open = 0;
|
|
|
|
|
|
|
|
|
|
write_tag_table_indirect ();
|
|
|
|
|
fclose (output_stream);
|
|
|
|
|
free (the_header);
|
|
|
|
|
free (the_file);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* The strings here are followed in the message by `reference to...' in
|
|
|
|
|
the `validate' routine. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
char *
|
|
|
|
|
reftype_type_string (type)
|
|
|
|
|
enum reftype type;
|
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case menu_reference:
|
|
|
|
|
return ("Menu");
|
|
|
|
|
case followed_reference:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return ("Cross");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
default:
|
|
|
|
|
return ("Internal-bad-reference-type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Remember this node name for later validation use. 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. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp->node = xstrdup (node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
temp->line_no = line;
|
|
|
|
|
temp->section = current_section;
|
|
|
|
|
temp->type = type;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp->containing_node = xstrdup (current_node ? current_node : "");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
reftype_type_string (ref_list->type));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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 (strcmp (node, ref_list->node) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
#define menu_starter "* "
|
|
|
|
|
char *
|
|
|
|
|
glean_node_from_menu (remember_reference)
|
|
|
|
|
int remember_reference;
|
|
|
|
|
{
|
|
|
|
|
int i, orig_offset = input_text_offset;
|
|
|
|
|
char *nodename;
|
|
|
|
|
|
|
|
|
|
if (strncmp (&input_text[input_text_offset + 1],
|
1999-01-14 19:56:32 +00:00
|
|
|
|
menu_starter,
|
|
|
|
|
strlen (menu_starter)) != 0)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return ((char *)NULL);
|
|
|
|
|
else
|
|
|
|
|
input_text_offset += strlen (menu_starter) + 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_until_in_line (0, ":", &nodename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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--;
|
|
|
|
|
|
|
|
|
|
isolate_nodename (nodename);
|
|
|
|
|
|
|
|
|
|
save_node:
|
|
|
|
|
input_text_offset = orig_offset;
|
|
|
|
|
normalize_node_name (nodename);
|
|
|
|
|
i = strlen (nodename);
|
|
|
|
|
if (i && nodename[i - 1] == ':')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nodename[i - 1] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (remember_reference)
|
|
|
|
|
{
|
|
|
|
|
remember_node_reference (nodename, line_number, menu_reference);
|
|
|
|
|
free (nodename);
|
|
|
|
|
return ((char *)NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return (nodename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
isolate_nodename (nodename)
|
|
|
|
|
char *nodename;
|
|
|
|
|
{
|
|
|
|
|
register int i, c;
|
|
|
|
|
int paren_seen, paren;
|
|
|
|
|
|
|
|
|
|
if (!nodename)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
canon_white (nodename);
|
|
|
|
|
paren_seen = paren = i = 0;
|
|
|
|
|
|
|
|
|
|
if (*nodename == '.' || !*nodename)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
*nodename = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*nodename == '(')
|
|
|
|
|
{
|
|
|
|
|
paren++;
|
|
|
|
|
paren_seen++;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (; (c = nodename[i]); i++)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (paren)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (c == '(')
|
|
|
|
|
paren++;
|
|
|
|
|
else if (c == ')')
|
|
|
|
|
paren--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If the character following the close paren is a space, then this
|
1999-01-14 19:56:32 +00:00
|
|
|
|
node has no more characters associated with it. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (c == '\t' ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
c == '\n' ||
|
|
|
|
|
c == ',' ||
|
|
|
|
|
((paren_seen && nodename[i - 1] == ')') &&
|
|
|
|
|
(c == ' ' || c == '.')) ||
|
|
|
|
|
(c == '.' &&
|
|
|
|
|
((!nodename[i + 1] ||
|
|
|
|
|
(cr_or_whitespace (nodename[i + 1])) ||
|
|
|
|
|
(nodename[i + 1] == ')')))))
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nodename[i] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_menu ()
|
|
|
|
|
{
|
|
|
|
|
if (current_node == (char *)NULL)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("%cmenu seen before first node"), COMMAND_PREFIX);
|
|
|
|
|
warning (_("creating `Top' node"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
execute_string ("@node Top");
|
|
|
|
|
}
|
|
|
|
|
begin_insertion (menu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_detailmenu ()
|
|
|
|
|
{
|
|
|
|
|
if (current_node == (char *)NULL)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("%cmenu seen before first node"), COMMAND_PREFIX);
|
|
|
|
|
warning (_("creating `Top' node"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
execute_string ("@node Top");
|
|
|
|
|
}
|
|
|
|
|
begin_insertion (detailmenu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Cross Reference Hacking */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Return next comma-delimited argument, but do not cross a close-brace
|
|
|
|
|
boundary. Clean up whitespace, too. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
char *
|
|
|
|
|
get_xref_token ()
|
|
|
|
|
{
|
|
|
|
|
char *string;
|
|
|
|
|
|
|
|
|
|
get_until_in_braces (",", &string);
|
|
|
|
|
if (curchar () == ',')
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
fix_whitespace (string);
|
|
|
|
|
return (string);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int px_ref_flag = 0; /* Controls initial output string. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Make a cross reference. */
|
|
|
|
|
void
|
|
|
|
|
cm_xref (arg)
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
|
|
|
|
char *arg1, *arg2, *arg3, *arg4, *arg5;
|
|
|
|
|
|
|
|
|
|
arg1 = get_xref_token ();
|
|
|
|
|
arg2 = get_xref_token ();
|
|
|
|
|
arg3 = get_xref_token ();
|
|
|
|
|
arg4 = get_xref_token ();
|
|
|
|
|
arg5 = get_xref_token ();
|
|
|
|
|
|
|
|
|
|
add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
|
|
|
|
|
|
|
|
|
|
if (*arg5 || *arg4)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *node_name;
|
|
|
|
|
|
|
|
|
|
if (!*arg2)
|
|
|
|
|
{
|
|
|
|
|
if (*arg3)
|
|
|
|
|
node_name = arg3;
|
|
|
|
|
else
|
|
|
|
|
node_name = arg1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
node_name = arg2;
|
|
|
|
|
|
|
|
|
|
execute_string ("%s: (%s)%s", node_name, arg4, arg1);
|
|
|
|
|
/* Free all of the arguments found. */
|
|
|
|
|
if (arg1) free (arg1);
|
|
|
|
|
if (arg2) free (arg2);
|
|
|
|
|
if (arg3) free (arg3);
|
|
|
|
|
if (arg4) free (arg4);
|
|
|
|
|
if (arg5) free (arg5);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
remember_node_reference (arg1, line_number, followed_reference);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (*arg3)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (!*arg2)
|
|
|
|
|
execute_string ("%s: %s", arg3, arg1);
|
|
|
|
|
else
|
|
|
|
|
execute_string ("%s: %s", arg2, arg1);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (*arg2)
|
|
|
|
|
execute_string ("%s: %s", arg2, arg1);
|
|
|
|
|
else
|
|
|
|
|
execute_string ("%s::", arg1);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Free all of the arguments found. */
|
|
|
|
|
if (arg1) free (arg1);
|
|
|
|
|
if (arg2) free (arg2);
|
|
|
|
|
if (arg3) free (arg3);
|
|
|
|
|
if (arg4) free (arg4);
|
|
|
|
|
if (arg5) free (arg5);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Check to make sure that the next non-whitespace character is either
|
|
|
|
|
a period or a comma. input_text_offset is pointing at the "}" which
|
|
|
|
|
ended the xref or pxref command. */
|
|
|
|
|
int temp = input_text_offset + 1;
|
|
|
|
|
|
|
|
|
|
if (output_paragraph[output_paragraph_offset - 2] == ':' &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
output_paragraph[output_paragraph_offset - 1] == ':')
|
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
while (temp < size_of_input_text)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (cr_or_whitespace (input_text[temp]))
|
|
|
|
|
temp++;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (input_text[temp] != '.'
|
|
|
|
|
&& input_text[temp] != ','
|
|
|
|
|
&& input_text[temp] != '\t')
|
|
|
|
|
{
|
|
|
|
|
line_error (
|
|
|
|
|
_("`.' or `,' must follow cross reference, not %c"),
|
|
|
|
|
input_text[temp]);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_pxref (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
|
|
|
|
px_ref_flag++;
|
|
|
|
|
cm_xref (arg);
|
|
|
|
|
px_ref_flag--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
add_char ('.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_inforef (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *node = get_xref_token ();
|
|
|
|
|
char *pname = get_xref_token ();
|
|
|
|
|
char *file = get_xref_token ();
|
|
|
|
|
|
|
|
|
|
if (*pname)
|
|
|
|
|
execute_string ("*note %s: (%s)%s", pname, file, node);
|
|
|
|
|
else
|
|
|
|
|
execute_string ("*note (%s)%s::", file, node);
|
|
|
|
|
|
|
|
|
|
free (node);
|
|
|
|
|
free (pname);
|
|
|
|
|
free (file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A URL reference. */
|
|
|
|
|
void
|
|
|
|
|
cm_uref (arg, start_pos, end_pos)
|
|
|
|
|
int arg, start_pos, end_pos;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
{
|
|
|
|
|
char *comma;
|
|
|
|
|
char *arg = (char *) &output_paragraph[start_pos];
|
|
|
|
|
|
|
|
|
|
output_paragraph[end_pos] = 0;
|
|
|
|
|
output_column -= end_pos - start_pos;
|
|
|
|
|
output_paragraph_offset = start_pos;
|
|
|
|
|
|
|
|
|
|
arg = xstrdup (arg);
|
|
|
|
|
comma = strchr (arg, ','); /* let's hope for no commas in the url */
|
|
|
|
|
if (comma)
|
|
|
|
|
{
|
|
|
|
|
*comma = 0;
|
|
|
|
|
/* Ignore spaces at beginning of second arg. */
|
|
|
|
|
for (comma++; isspace (*comma); comma++)
|
|
|
|
|
;
|
|
|
|
|
add_word (comma);
|
|
|
|
|
add_char (' ');
|
|
|
|
|
add_char ('(');
|
|
|
|
|
add_word (arg);
|
|
|
|
|
add_char (')');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
extern int printing_index;
|
|
|
|
|
|
|
|
|
|
if (!printing_index)
|
|
|
|
|
add_char ('`');
|
|
|
|
|
|
|
|
|
|
add_word (arg);
|
|
|
|
|
|
|
|
|
|
if (!printing_index)
|
|
|
|
|
add_char ('\'');
|
|
|
|
|
}
|
|
|
|
|
free (arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* An email reference. */
|
|
|
|
|
void
|
|
|
|
|
cm_email (arg, start_pos, end_pos)
|
|
|
|
|
int arg, start_pos, end_pos;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
{
|
|
|
|
|
char *comma;
|
|
|
|
|
char *arg = (char *) &output_paragraph[start_pos];
|
|
|
|
|
|
|
|
|
|
output_paragraph[end_pos] = 0;
|
|
|
|
|
output_column -= end_pos - start_pos;
|
|
|
|
|
output_paragraph_offset = start_pos;
|
|
|
|
|
|
|
|
|
|
arg = xstrdup (arg);
|
|
|
|
|
comma = strchr (arg, ',');
|
|
|
|
|
if (comma)
|
|
|
|
|
{
|
|
|
|
|
*comma = 0;
|
|
|
|
|
for (comma++; isspace (*comma); comma++)
|
|
|
|
|
;
|
|
|
|
|
add_word (comma);
|
|
|
|
|
add_char (' ');
|
|
|
|
|
}
|
|
|
|
|
add_char ('<');
|
|
|
|
|
add_word (arg);
|
|
|
|
|
add_char ('>');
|
|
|
|
|
free (arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* An external image is a reference, kind of. The parsing is (not
|
|
|
|
|
coincidentally) similar, anyway. */
|
|
|
|
|
void
|
|
|
|
|
cm_image (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
{
|
|
|
|
|
char *name_arg = get_xref_token ();
|
|
|
|
|
/* We don't yet care about any other args, but read them so they
|
|
|
|
|
don't end up in the text. */
|
|
|
|
|
char *arg = get_xref_token ();
|
|
|
|
|
if (arg) free (arg);
|
|
|
|
|
arg = get_xref_token ();
|
|
|
|
|
if (arg) free (arg);
|
|
|
|
|
|
|
|
|
|
if (*name_arg)
|
|
|
|
|
{
|
|
|
|
|
/* Try to open foo.txt. */
|
|
|
|
|
FILE *image_file;
|
|
|
|
|
char *name = xmalloc (strlen (name_arg) + 4);
|
|
|
|
|
strcpy (name, name_arg);
|
|
|
|
|
strcat (name, ".txt");
|
|
|
|
|
image_file = fopen (name, "r");
|
|
|
|
|
if (image_file)
|
|
|
|
|
{
|
|
|
|
|
int ch;
|
|
|
|
|
int save_inhibit_indentation = inhibit_paragraph_indentation;
|
|
|
|
|
int save_filling_enabled = filling_enabled;
|
|
|
|
|
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
filling_enabled = 0;
|
|
|
|
|
last_char_was_newline = 0;
|
|
|
|
|
|
|
|
|
|
/* Maybe we need to remove the final newline if the image
|
|
|
|
|
file is only one line to allow in-line images. On the
|
|
|
|
|
other hand, they could just make the file without a
|
|
|
|
|
final newline. */
|
|
|
|
|
while ((ch = getc (image_file)) != EOF)
|
|
|
|
|
add_char (ch);
|
|
|
|
|
|
|
|
|
|
inhibit_paragraph_indentation = save_inhibit_indentation;
|
|
|
|
|
filling_enabled = save_filling_enabled;
|
|
|
|
|
|
|
|
|
|
if (fclose (image_file) != 0) {
|
|
|
|
|
perror (name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
warning (_("@image file `%s' unreadable: %s"), name,
|
|
|
|
|
strerror (errno));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
line_error (_("@image missing filename argument"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (name_arg) free (name_arg);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Insertion Command Stubs */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_quotation ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (quotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_example ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (example);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_smallexample ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (smallexample);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_lisp ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (lisp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_smalllisp ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (smalllisp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* @cartouche/@end cartouche draws box with rounded corners in
|
1999-01-14 19:56:32 +00:00
|
|
|
|
TeX output. Right now, just a no-op insertion. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
cm_cartouche ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (cartouche);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_format ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (format);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_display ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_direntry ()
|
|
|
|
|
{
|
|
|
|
|
if (no_headers)
|
|
|
|
|
command_name_condition ();
|
|
|
|
|
else
|
|
|
|
|
begin_insertion (direntry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_itemize ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (itemize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_enumerate ()
|
|
|
|
|
{
|
|
|
|
|
do_enumeration (enumerate, "1");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start an enumeration insertion of type TYPE. If the user supplied
|
|
|
|
|
no argument on the line, then use DEFAULT_STRING as the initial string. */
|
|
|
|
|
void
|
|
|
|
|
do_enumeration (type, default_string)
|
|
|
|
|
int type;
|
|
|
|
|
char *default_string;
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_until_in_line (0, ".", &enumeration_arg);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
canon_white (enumeration_arg);
|
|
|
|
|
|
|
|
|
|
if (!*enumeration_arg)
|
|
|
|
|
{
|
|
|
|
|
free (enumeration_arg);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
enumeration_arg = xstrdup (default_string);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("%s requires letter or digit"), insertion_type_pname (type));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
switch (type)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case enumerate:
|
|
|
|
|
default_string = "1";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
enumeration_arg = xstrdup (default_string);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
begin_insertion (type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_table ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (table);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_multitable ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
begin_insertion (multitable); /* @@ */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_ftable ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (ftable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_vtable ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (vtable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_group ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (group);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_ifinfo ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (ifinfo);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
void
|
|
|
|
|
cm_ifnothtml ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (ifnothtml);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_ifnottex ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (ifnottex);
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Begin an insertion where the lines are not filled or indented. */
|
|
|
|
|
void
|
|
|
|
|
cm_flushleft ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (flushleft);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Begin an insertion where the lines are not filled, and each line is
|
|
|
|
|
forced to the right-hand side of the page. */
|
|
|
|
|
void
|
|
|
|
|
cm_flushright ()
|
|
|
|
|
{
|
|
|
|
|
begin_insertion (flushright);
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* End existing insertion block. */
|
|
|
|
|
void
|
|
|
|
|
cm_end ()
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
enum insertion_type type;
|
|
|
|
|
|
|
|
|
|
if (!insertion_level)
|
|
|
|
|
{
|
|
|
|
|
line_error (_("Unmatched `%c%s'"), COMMAND_PREFIX, command);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&temp);
|
|
|
|
|
|
|
|
|
|
if (temp[0] == 0)
|
|
|
|
|
line_error (_("`%c%s' needs something after it"), COMMAND_PREFIX, command);
|
|
|
|
|
|
|
|
|
|
type = find_type_from_name (temp);
|
|
|
|
|
|
|
|
|
|
if (type == bad_type)
|
|
|
|
|
{
|
|
|
|
|
line_error (_("Bad argument to `%s', `%s', using `%s'"),
|
|
|
|
|
command, temp, insertion_type_pname (current_insertion_type ()));
|
|
|
|
|
}
|
|
|
|
|
end_insertion (type);
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Conditional Handling */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* A structure which contains `defined' variables. */
|
|
|
|
|
typedef struct defines {
|
|
|
|
|
struct defines *next;
|
|
|
|
|
char *name;
|
|
|
|
|
char *value;
|
|
|
|
|
} DEFINE;
|
|
|
|
|
|
|
|
|
|
/* The linked list of `set' defines. */
|
|
|
|
|
DEFINE *defines = (DEFINE *)NULL;
|
|
|
|
|
|
|
|
|
|
/* Add NAME to the list of `set' defines. */
|
|
|
|
|
void
|
|
|
|
|
set (name, value)
|
|
|
|
|
char *name;
|
|
|
|
|
char *value;
|
|
|
|
|
{
|
|
|
|
|
DEFINE *temp;
|
|
|
|
|
|
|
|
|
|
for (temp = defines; temp; temp = temp->next)
|
|
|
|
|
if (strcmp (name, temp->name) == 0)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (temp->value);
|
|
|
|
|
temp->value = xstrdup (value);
|
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
temp = (DEFINE *)xmalloc (sizeof (DEFINE));
|
|
|
|
|
temp->next = defines;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp->name = xstrdup (name);
|
|
|
|
|
temp->value = xstrdup (value);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
defines = temp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove NAME from the list of `set' defines. */
|
|
|
|
|
void
|
|
|
|
|
clear (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register DEFINE *temp, *last;
|
|
|
|
|
|
|
|
|
|
last = (DEFINE *)NULL;
|
|
|
|
|
temp = defines;
|
|
|
|
|
|
|
|
|
|
while (temp)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (temp->name, name) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (last)
|
|
|
|
|
last->next = temp->next;
|
|
|
|
|
else
|
|
|
|
|
defines = temp->next;
|
|
|
|
|
|
|
|
|
|
free (temp->name);
|
|
|
|
|
free (temp->value);
|
|
|
|
|
free (temp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
last = temp;
|
|
|
|
|
temp = temp->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the value of NAME. The return value is NULL if NAME is unset. */
|
|
|
|
|
char *
|
|
|
|
|
set_p (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register DEFINE *temp;
|
|
|
|
|
|
|
|
|
|
for (temp = defines; temp; temp = temp->next)
|
|
|
|
|
if (strcmp (temp->name, name) == 0)
|
|
|
|
|
return (temp->value);
|
|
|
|
|
|
|
|
|
|
return ((char *)NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Conditionally parse based on the current command name. */
|
|
|
|
|
void
|
|
|
|
|
command_name_condition ()
|
|
|
|
|
{
|
|
|
|
|
char *discarder;
|
|
|
|
|
|
|
|
|
|
discarder = (char *)xmalloc (8 + strlen (command));
|
|
|
|
|
|
|
|
|
|
sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
|
|
|
|
|
discard_until (discarder);
|
|
|
|
|
discard_until ("\n");
|
|
|
|
|
|
|
|
|
|
free (discarder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a variable whose name appears as the first word on this line. */
|
|
|
|
|
void
|
|
|
|
|
cm_set ()
|
|
|
|
|
{
|
|
|
|
|
handle_variable (SET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove a variable whose name appears as the first word on this line. */
|
|
|
|
|
void
|
|
|
|
|
cm_clear ()
|
|
|
|
|
{
|
|
|
|
|
handle_variable (CLEAR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_ifset ()
|
|
|
|
|
{
|
|
|
|
|
handle_variable (IFSET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_ifclear ()
|
|
|
|
|
{
|
|
|
|
|
handle_variable (IFCLEAR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This command takes braces, but we parse the contents specially, so we
|
|
|
|
|
don't use the standard brace popping code.
|
|
|
|
|
|
|
|
|
|
The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands
|
|
|
|
|
if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
|
|
|
|
|
it produces no output. */
|
|
|
|
|
void
|
|
|
|
|
cm_ifeq ()
|
|
|
|
|
{
|
|
|
|
|
char **arglist;
|
|
|
|
|
|
|
|
|
|
arglist = get_brace_args (0);
|
|
|
|
|
|
|
|
|
|
if (arglist)
|
|
|
|
|
{
|
|
|
|
|
if (array_len (arglist) > 1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
|
|
|
|
|
(arglist[2] != (char *)NULL))
|
|
|
|
|
execute_string ("%s\n", arglist[2]);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free_array (arglist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_value (arg, start_pos, end_pos)
|
|
|
|
|
int arg, start_pos, end_pos;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *name = (char *) &output_paragraph[start_pos];
|
|
|
|
|
char *value;
|
|
|
|
|
output_paragraph[end_pos] = 0;
|
|
|
|
|
name = xstrdup (name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
value = set_p (name);
|
|
|
|
|
output_column -= end_pos - start_pos;
|
|
|
|
|
output_paragraph_offset = start_pos;
|
|
|
|
|
|
|
|
|
|
if (value)
|
|
|
|
|
execute_string ("%s", value);
|
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word_args (_("{No Value For \"%s\"}"), name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set, clear, or conditionalize based on ACTION. */
|
|
|
|
|
void
|
|
|
|
|
handle_variable (action)
|
|
|
|
|
int action;
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&name);
|
|
|
|
|
backup_input_pointer ();
|
|
|
|
|
handle_variable_internal (action, name);
|
|
|
|
|
free (name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
handle_variable_internal (action, name)
|
|
|
|
|
int action;
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
int delimiter, additional_text_present = 0;
|
|
|
|
|
|
|
|
|
|
/* Only the first word of NAME is a valid tag. */
|
|
|
|
|
temp = name;
|
|
|
|
|
delimiter = 0;
|
|
|
|
|
while (*temp && (delimiter || !whitespace (*temp)))
|
|
|
|
|
{
|
|
|
|
|
/* #if defined (SET_WITH_EQUAL) */
|
|
|
|
|
if (*temp == '"' || *temp == '\'')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (*temp == delimiter)
|
|
|
|
|
delimiter = 0;
|
|
|
|
|
else
|
|
|
|
|
delimiter = *temp;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* #endif SET_WITH_EQUAL */
|
|
|
|
|
temp++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*temp)
|
|
|
|
|
additional_text_present++;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
*temp = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!*name)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%c%s requires a name"), COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (action)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case SET:
|
|
|
|
|
{
|
|
|
|
|
char *value;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (SET_WITH_EQUAL)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Allow a value to be saved along with a variable. The value is
|
|
|
|
|
the text following an `=' sign in NAME, if any is present. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (value = name; *value && *value != '='; value++);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (*value)
|
|
|
|
|
*value++ = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (*value == '"' || *value == '\'')
|
|
|
|
|
{
|
|
|
|
|
value++;
|
|
|
|
|
value[strlen (value) - 1] = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#else /* !SET_WITH_EQUAL */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* The VALUE of NAME is the remainder of the line sans
|
|
|
|
|
whitespace. */
|
|
|
|
|
if (additional_text_present)
|
|
|
|
|
{
|
|
|
|
|
value = temp + 1;
|
|
|
|
|
canon_white (value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
value = "";
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* !SET_WITH_VALUE */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
set (name, value);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CLEAR:
|
|
|
|
|
clear (name);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFSET:
|
|
|
|
|
case IFCLEAR:
|
|
|
|
|
/* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
|
|
|
|
|
read lines from the the file until we reach a matching
|
|
|
|
|
"@end CONDITION". This means that we only take note of
|
|
|
|
|
"@ifset/clear" and "@end" commands. */
|
|
|
|
|
{
|
|
|
|
|
char condition[8];
|
|
|
|
|
int condition_len;
|
|
|
|
|
int orig_line_number = line_number;
|
|
|
|
|
|
|
|
|
|
if (action == IFSET)
|
|
|
|
|
strcpy (condition, "ifset");
|
|
|
|
|
else
|
|
|
|
|
strcpy (condition, "ifclear");
|
|
|
|
|
|
|
|
|
|
condition_len = strlen (condition);
|
|
|
|
|
|
|
|
|
|
if ((action == IFSET && !set_p (name))
|
|
|
|
|
|| (action == IFCLEAR && set_p (name)))
|
|
|
|
|
{
|
|
|
|
|
int level = 0, done = 0;
|
|
|
|
|
|
|
|
|
|
while (!done && input_text_offset < size_of_input_text)
|
|
|
|
|
{
|
|
|
|
|
char *freeable_line, *line;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&freeable_line);
|
|
|
|
|
|
|
|
|
|
for (line = freeable_line; whitespace (*line); line++);
|
|
|
|
|
|
|
|
|
|
if (*line == COMMAND_PREFIX &&
|
|
|
|
|
(strncmp (line + 1, condition, condition_len) == 0))
|
|
|
|
|
level++;
|
|
|
|
|
else if (strncmp (line, "@end", 4) == 0)
|
|
|
|
|
{
|
|
|
|
|
char *cname = line + 4;
|
|
|
|
|
char *temp;
|
|
|
|
|
|
|
|
|
|
while (*cname && whitespace (*cname))
|
|
|
|
|
cname++;
|
|
|
|
|
temp = cname;
|
|
|
|
|
|
|
|
|
|
while (*temp && !whitespace (*temp))
|
|
|
|
|
temp++;
|
|
|
|
|
*temp = 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp (cname, condition) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (!level)
|
|
|
|
|
{
|
|
|
|
|
done = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
level--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free (freeable_line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!done)
|
|
|
|
|
{
|
|
|
|
|
int save = line_number;
|
|
|
|
|
line_number = orig_line_number;
|
|
|
|
|
line_error (_("Reached eof before matching @end %s"),
|
|
|
|
|
condition);
|
|
|
|
|
line_number = save;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We found the end of a false @ifset/ifclear. If we are
|
|
|
|
|
in a menu, back up over the newline that ends the ifset,
|
|
|
|
|
since that newline may also begin the next menu entry. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (action == IFSET)
|
|
|
|
|
begin_insertion (ifset);
|
|
|
|
|
else
|
|
|
|
|
begin_insertion (ifclear);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Execution of random text not in file. */
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *string; /* The string buffer. */
|
|
|
|
|
int size; /* The size of the buffer. */
|
|
|
|
|
int in_use; /* Nonzero means string currently in use. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
} EXECUTION_STRING;
|
|
|
|
|
|
|
|
|
|
static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL;
|
|
|
|
|
static int execution_strings_index = 0;
|
|
|
|
|
static int execution_strings_slots = 0;
|
|
|
|
|
|
|
|
|
|
EXECUTION_STRING *
|
|
|
|
|
get_execution_string (initial_size)
|
|
|
|
|
int initial_size;
|
|
|
|
|
{
|
|
|
|
|
register int i = 0;
|
|
|
|
|
EXECUTION_STRING *es = (EXECUTION_STRING *)NULL;
|
|
|
|
|
|
|
|
|
|
if (execution_strings)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < execution_strings_index; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (execution_strings[i] && (execution_strings[i]->in_use == 0))
|
|
|
|
|
{
|
|
|
|
|
es = execution_strings[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!es)
|
|
|
|
|
{
|
|
|
|
|
if (execution_strings_index + 1 >= execution_strings_slots)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
execution_strings = (EXECUTION_STRING **)xrealloc
|
|
|
|
|
(execution_strings,
|
|
|
|
|
(execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
|
|
|
|
|
for (; i < execution_strings_slots; i++)
|
|
|
|
|
execution_strings[i] = (EXECUTION_STRING *)NULL;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
execution_strings[execution_strings_index] =
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
es = execution_strings[execution_strings_index];
|
|
|
|
|
execution_strings_index++;
|
|
|
|
|
|
|
|
|
|
es->size = 0;
|
|
|
|
|
es->string = (char *)NULL;
|
|
|
|
|
es->in_use = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (initial_size > es->size)
|
|
|
|
|
{
|
|
|
|
|
es->string = (char *) xrealloc (es->string, initial_size);
|
|
|
|
|
es->size = initial_size;
|
|
|
|
|
}
|
|
|
|
|
return (es);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Execute the string produced by formatting the ARGs with FORMAT. This
|
|
|
|
|
is like submitting a new file with @include. */
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#if defined (VA_FPRINTF) && __STDC__
|
|
|
|
|
execute_string (char *format, ...)
|
|
|
|
|
#else
|
|
|
|
|
execute_string (format, va_alist)
|
|
|
|
|
char *format;
|
|
|
|
|
va_dcl
|
|
|
|
|
#endif
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
EXECUTION_STRING *es;
|
|
|
|
|
char *temp_string;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#ifdef VA_FPRINTF
|
|
|
|
|
va_list ap;
|
|
|
|
|
#endif
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
es = get_execution_string (4000);
|
|
|
|
|
temp_string = es->string;
|
|
|
|
|
es->in_use = 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
VA_START (ap, format);
|
|
|
|
|
#ifdef VA_SPRINTF
|
|
|
|
|
VA_SPRINTF (temp_string, format, ap);
|
|
|
|
|
#else
|
|
|
|
|
sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
|
|
|
|
|
#endif /* not VA_SPRINTF */
|
|
|
|
|
va_end (ap);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
pushfile ();
|
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
input_text = temp_string;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_filename = xstrdup (input_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
size_of_input_text = strlen (temp_string);
|
|
|
|
|
|
|
|
|
|
executing_string++;
|
|
|
|
|
reader_loop ();
|
|
|
|
|
free (input_filename);
|
|
|
|
|
|
|
|
|
|
popfile ();
|
|
|
|
|
executing_string--;
|
|
|
|
|
es->in_use = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return what would be output for STR, i.e., expand Texinfo commands.
|
|
|
|
|
If IMPLICIT_CODE is set, expand @code{STR}. */
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
expansion (str, implicit_code)
|
|
|
|
|
char *str;
|
|
|
|
|
int implicit_code;
|
|
|
|
|
{
|
|
|
|
|
int length;
|
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
|
|
/* Inhibit any real output. */
|
|
|
|
|
int start = output_paragraph_offset;
|
|
|
|
|
int saved_paragraph_is_open = paragraph_is_open;
|
|
|
|
|
|
|
|
|
|
inhibit_output_flushing ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
paragraph_is_open = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
execute_string (implicit_code ? "@code{%s}" : "%s", str);
|
|
|
|
|
uninhibit_output_flushing ();
|
|
|
|
|
|
|
|
|
|
/* Copy the expansion from the buffer. */
|
|
|
|
|
length = output_paragraph_offset - start;
|
|
|
|
|
result = xmalloc (1 + length);
|
|
|
|
|
memcpy (result, (char *) (output_paragraph + start), length);
|
|
|
|
|
result[length] = 0;
|
|
|
|
|
|
|
|
|
|
/* Pretend it never happened. */
|
|
|
|
|
output_paragraph_offset = start;
|
|
|
|
|
paragraph_is_open = saved_paragraph_is_open;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* @itemx, @item. */
|
|
|
|
|
|
|
|
|
|
static int itemx_flag = 0;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_itemx ()
|
|
|
|
|
{
|
|
|
|
|
itemx_flag++;
|
|
|
|
|
cm_item ();
|
|
|
|
|
itemx_flag--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_item ()
|
|
|
|
|
{
|
|
|
|
|
char *rest_of_line, *item_func;
|
|
|
|
|
|
|
|
|
|
/* Can only hack "@item" while inside of an insertion. */
|
|
|
|
|
if (insertion_level)
|
|
|
|
|
{
|
|
|
|
|
INSERTION_ELT *stack = insertion_stack;
|
|
|
|
|
int original_input_text_offset;
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
original_input_text_offset = input_text_offset;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&rest_of_line);
|
|
|
|
|
item_func = current_item_function ();
|
|
|
|
|
|
|
|
|
|
/* Okay, do the right thing depending on which insertion function
|
1999-01-14 19:56:32 +00:00
|
|
|
|
is active. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
switch_top:
|
|
|
|
|
switch (stack->insertion)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case multitable:
|
|
|
|
|
multitable_item ();
|
|
|
|
|
/* Ultra special hack. It appears that some people incorrectly
|
|
|
|
|
place text directly after the @item, instead of on a new line
|
|
|
|
|
by itself. This happens to work in TeX, so I make it work
|
|
|
|
|
here. */
|
|
|
|
|
if (*rest_of_line)
|
|
|
|
|
{
|
|
|
|
|
line_number--;
|
|
|
|
|
input_text_offset = original_input_text_offset;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ifinfo:
|
|
|
|
|
case ifset:
|
|
|
|
|
case ifclear:
|
|
|
|
|
case cartouche:
|
|
|
|
|
stack = stack->next;
|
|
|
|
|
if (!stack)
|
|
|
|
|
goto no_insertion;
|
|
|
|
|
else
|
|
|
|
|
goto switch_top;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case menu:
|
|
|
|
|
case quotation:
|
|
|
|
|
case example:
|
|
|
|
|
case smallexample:
|
|
|
|
|
case lisp:
|
|
|
|
|
case format:
|
|
|
|
|
case display:
|
|
|
|
|
case group:
|
|
|
|
|
line_error (_("The `%c%s' command is meaningless within a `@%s' block"),
|
|
|
|
|
COMMAND_PREFIX, command,
|
|
|
|
|
insertion_type_pname (current_insertion_type ()));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case itemize:
|
|
|
|
|
case enumerate:
|
|
|
|
|
if (itemx_flag)
|
|
|
|
|
{
|
|
|
|
|
line_error (_("%citemx is not meaningful inside of a `%s' block"),
|
|
|
|
|
COMMAND_PREFIX,
|
|
|
|
|
insertion_type_pname (current_insertion_type ()));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
start_paragraph ();
|
|
|
|
|
kill_self_indent (-1);
|
|
|
|
|
filling_enabled = indented_fill = 1;
|
|
|
|
|
|
|
|
|
|
if (current_insertion_type () == itemize)
|
|
|
|
|
{
|
|
|
|
|
indent (output_column = current_indent - 2);
|
|
|
|
|
|
|
|
|
|
/* I need some way to determine whether this command
|
|
|
|
|
takes braces or not. I believe the user can type
|
|
|
|
|
either "@bullet" or "@bullet{}". Of course, they
|
|
|
|
|
can also type "o" or "#" or whatever else they want. */
|
|
|
|
|
if (item_func && *item_func)
|
|
|
|
|
{
|
|
|
|
|
if (*item_func == COMMAND_PREFIX)
|
|
|
|
|
if (item_func[strlen (item_func) - 1] != '}')
|
|
|
|
|
execute_string ("%s{}", item_func);
|
|
|
|
|
else
|
|
|
|
|
execute_string ("%s", item_func);
|
|
|
|
|
else
|
|
|
|
|
execute_string ("%s", item_func);
|
|
|
|
|
}
|
|
|
|
|
insert (' ');
|
|
|
|
|
output_column++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
enumerate_item ();
|
|
|
|
|
|
|
|
|
|
/* Special hack. This makes `close_paragraph' a no-op until
|
|
|
|
|
`start_paragraph' has been called. */
|
|
|
|
|
must_start_paragraph = 1;
|
|
|
|
|
|
|
|
|
|
/* Handle text directly after the @item. */
|
|
|
|
|
if (*rest_of_line)
|
|
|
|
|
{
|
|
|
|
|
line_number--;
|
|
|
|
|
input_text_offset = original_input_text_offset;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case table:
|
|
|
|
|
case ftable:
|
|
|
|
|
case vtable:
|
|
|
|
|
{
|
|
|
|
|
/* We need this to determine if we have two @item's in a row
|
|
|
|
|
(see test just below). */
|
|
|
|
|
static int last_item_output_position = 0;
|
|
|
|
|
|
|
|
|
|
/* Get rid of extra characters. */
|
|
|
|
|
kill_self_indent (-1);
|
|
|
|
|
|
|
|
|
|
/* If we have one @item followed directly by another @item,
|
|
|
|
|
we need to insert a blank line. This is not true for
|
|
|
|
|
@itemx, though. */
|
|
|
|
|
if (!itemx_flag && last_item_output_position == output_position)
|
|
|
|
|
insert ('\n');
|
|
|
|
|
|
|
|
|
|
/* `close_paragraph' almost does what we want. The problem
|
|
|
|
|
is when paragraph_is_open, and last_char_was_newline, and
|
|
|
|
|
the last newline has been turned into a space, because
|
|
|
|
|
filling_enabled. I handle it here. */
|
|
|
|
|
if (last_char_was_newline && filling_enabled && paragraph_is_open)
|
|
|
|
|
insert ('\n');
|
|
|
|
|
close_paragraph ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (INDENT_PARAGRAPHS_IN_TABLE)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Indent on a new line, but back up one indentation level. */
|
|
|
|
|
{
|
|
|
|
|
int save = inhibit_paragraph_indentation;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
/* At this point, inserting any non-whitespace character will
|
|
|
|
|
force the existing indentation to be output. */
|
|
|
|
|
add_char ('i');
|
|
|
|
|
inhibit_paragraph_indentation = save;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#else /* !INDENT_PARAGRAPHS_IN_TABLE */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_char ('i');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
output_paragraph_offset--;
|
|
|
|
|
kill_self_indent (default_indentation_increment + 1);
|
|
|
|
|
|
|
|
|
|
/* Add item's argument to the line. */
|
|
|
|
|
filling_enabled = 0;
|
|
|
|
|
if (item_func && *item_func)
|
|
|
|
|
execute_string ("%s{%s}", item_func, rest_of_line);
|
|
|
|
|
else
|
|
|
|
|
execute_string ("%s", rest_of_line);
|
|
|
|
|
|
|
|
|
|
if (current_insertion_type () == ftable)
|
|
|
|
|
execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
|
|
|
|
|
else if (current_insertion_type () == vtable)
|
|
|
|
|
execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
|
|
|
|
|
|
|
|
|
|
/* Start a new line, and let start_paragraph ()
|
|
|
|
|
do the indenting of it for you. */
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
indented_fill = filling_enabled = 1;
|
|
|
|
|
last_item_output_position = output_position;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (rest_of_line);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
no_insertion:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%c%s found outside of an insertion block"),
|
|
|
|
|
COMMAND_PREFIX, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Defun and Friends */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
#define DEFUN_SELF_DELIMITING(c) \
|
|
|
|
|
(((c) == '(') \
|
|
|
|
|
|| ((c) == ')') \
|
|
|
|
|
|| ((c) == '[') \
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|| ((c) == ']'))
|
|
|
|
|
|
|
|
|
|
struct token_accumulator
|
|
|
|
|
{
|
|
|
|
|
unsigned int length;
|
|
|
|
|
unsigned int index;
|
|
|
|
|
char **tokens;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
initialize_token_accumulator (accumulator)
|
|
|
|
|
struct token_accumulator *accumulator;
|
|
|
|
|
{
|
|
|
|
|
(accumulator->length) = 0;
|
|
|
|
|
(accumulator->index) = 0;
|
|
|
|
|
(accumulator->tokens) = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
accumulate_token (accumulator, token)
|
|
|
|
|
struct token_accumulator *accumulator;
|
|
|
|
|
char *token;
|
|
|
|
|
{
|
|
|
|
|
if ((accumulator->index) >= (accumulator->length))
|
|
|
|
|
{
|
|
|
|
|
(accumulator->length) += 10;
|
|
|
|
|
(accumulator->tokens) = (char **) xrealloc
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(accumulator->tokens, (accumulator->length * sizeof (char *)));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
accumulator->tokens[accumulator->index] = token;
|
|
|
|
|
accumulator->index += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
copy_substring (start, end)
|
|
|
|
|
char *start;
|
|
|
|
|
char *end;
|
|
|
|
|
{
|
|
|
|
|
char *result, *scan, *scan_result;
|
|
|
|
|
|
|
|
|
|
result = (char *) xmalloc ((end - start) + 1);
|
|
|
|
|
scan_result = result;
|
|
|
|
|
scan = start;
|
|
|
|
|
|
|
|
|
|
while (scan < end)
|
|
|
|
|
*scan_result++ = *scan++;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
*scan_result = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given `string' pointing at an open brace, skip forward and return a
|
|
|
|
|
pointer to just past the matching close brace. */
|
|
|
|
|
int
|
|
|
|
|
scan_group_in_string (string_pointer)
|
|
|
|
|
char **string_pointer;
|
|
|
|
|
{
|
|
|
|
|
register int c;
|
|
|
|
|
register char *scan_string;
|
|
|
|
|
register unsigned int level = 1;
|
|
|
|
|
|
|
|
|
|
scan_string = (*string_pointer) + 1;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
if (level == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
(*string_pointer) = scan_string;
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
c = (*scan_string++);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (c == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Tweak line_number to compensate for fact that
|
|
|
|
|
we gobbled the whole line before coming here. */
|
|
|
|
|
line_number -= 1;
|
|
|
|
|
line_error (_("Missing `}' in %cdef arg"), COMMAND_PREFIX);
|
|
|
|
|
line_number += 1;
|
|
|
|
|
(*string_pointer) = (scan_string - 1);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (c == '{')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
level += 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (c == '}')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
level -= 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a list of tokens from the contents of `string'.
|
|
|
|
|
Commands and brace-delimited groups count as single tokens.
|
|
|
|
|
Contiguous whitespace characters are converted to a token
|
|
|
|
|
consisting of a single space. */
|
|
|
|
|
char **
|
|
|
|
|
args_from_string (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
struct token_accumulator accumulator;
|
|
|
|
|
register char *scan_string = string;
|
|
|
|
|
char *token_start, *token_end;
|
|
|
|
|
|
|
|
|
|
initialize_token_accumulator (&accumulator);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while ((*scan_string) != 0)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
/* Replace arbitrary whitespace by a single space. */
|
|
|
|
|
if (whitespace (*scan_string))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
scan_string += 1;
|
|
|
|
|
while (whitespace (*scan_string))
|
|
|
|
|
scan_string += 1;
|
|
|
|
|
accumulate_token ((&accumulator), (xstrdup (" ")));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Commands count as single tokens. */
|
|
|
|
|
if ((*scan_string) == COMMAND_PREFIX)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
token_start = scan_string;
|
|
|
|
|
scan_string += 1;
|
|
|
|
|
if (self_delimiting (*scan_string))
|
|
|
|
|
scan_string += 1;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
register int c;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
c = *scan_string++;
|
|
|
|
|
|
|
|
|
|
if ((c == 0) || (c == '{') || (whitespace (c)))
|
|
|
|
|
{
|
|
|
|
|
scan_string -= 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*scan_string == '{')
|
|
|
|
|
{
|
|
|
|
|
char *s = scan_string;
|
|
|
|
|
(void) scan_group_in_string (&s);
|
|
|
|
|
scan_string = s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
token_end = scan_string;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Parentheses and brackets are self-delimiting. */
|
|
|
|
|
else if (DEFUN_SELF_DELIMITING (*scan_string))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
token_start = scan_string;
|
|
|
|
|
scan_string += 1;
|
|
|
|
|
token_end = scan_string;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Open brace introduces a group that is a single token. */
|
|
|
|
|
else if (*scan_string == '{')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *s = scan_string;
|
|
|
|
|
int balanced = scan_group_in_string (&s);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
token_start = scan_string + 1;
|
|
|
|
|
scan_string = s;
|
|
|
|
|
token_end = balanced ? (scan_string - 1) : scan_string;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Otherwise a token is delimited by whitespace, parentheses,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
brackets, or braces. A token is also ended by a command. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
token_start = scan_string;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
register int c;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
c = *scan_string++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Do not back up if we're looking at a }; since the only
|
|
|
|
|
valid }'s are those matched with {'s, we want to give
|
|
|
|
|
an error. If we back up, we go into an infinite loop. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (!c || whitespace (c) || DEFUN_SELF_DELIMITING (c)
|
|
|
|
|
|| c == '{')
|
|
|
|
|
{
|
|
|
|
|
scan_string--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we encounter a command embedded within a token,
|
|
|
|
|
then end the token. */
|
|
|
|
|
if (c == COMMAND_PREFIX)
|
|
|
|
|
{
|
|
|
|
|
scan_string--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
token_end = scan_string;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
accumulate_token
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(&accumulator, copy_substring (token_start, token_end));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
accumulate_token (&accumulator, NULL);
|
|
|
|
|
return (accumulator.tokens);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
process_defun_args (defun_args, auto_var_p)
|
|
|
|
|
char **defun_args;
|
|
|
|
|
int auto_var_p;
|
|
|
|
|
{
|
|
|
|
|
int pending_space = 0;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
char *defun_arg = *defun_args++;
|
|
|
|
|
|
|
|
|
|
if (defun_arg == NULL)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (defun_arg[0] == ' ')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
pending_space = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (pending_space)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
add_char (' ');
|
|
|
|
|
pending_space = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (DEFUN_SELF_DELIMITING (defun_arg[0]))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_char (defun_arg[0]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (defun_arg[0] == '&')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word (defun_arg);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (defun_arg[0] == COMMAND_PREFIX)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string ("%s", defun_arg);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (auto_var_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word (defun_arg);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
next_nonwhite_defun_arg (arg_pointer)
|
|
|
|
|
char ***arg_pointer;
|
|
|
|
|
{
|
|
|
|
|
char **scan = (*arg_pointer);
|
|
|
|
|
char *arg = (*scan++);
|
|
|
|
|
|
|
|
|
|
if ((arg != 0) && (*arg == ' '))
|
|
|
|
|
arg = *scan++;
|
|
|
|
|
|
|
|
|
|
if (arg == 0)
|
|
|
|
|
scan -= 1;
|
|
|
|
|
|
|
|
|
|
*arg_pointer = scan;
|
|
|
|
|
|
|
|
|
|
return ((arg == 0) ? "" : arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the defun type insertion.
|
|
|
|
|
TYPE says which insertion this is.
|
1999-01-14 19:56:32 +00:00
|
|
|
|
X_P, if nonzero, says not to start a new insertion. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
defun_internal (type, x_p)
|
|
|
|
|
enum insertion_type type;
|
|
|
|
|
int x_p;
|
|
|
|
|
{
|
|
|
|
|
enum insertion_type base_type;
|
|
|
|
|
char **defun_args, **scan_args;
|
|
|
|
|
char *category, *defined_name, *type_name, *type_name2;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
get_rest_of_line (&line);
|
|
|
|
|
defun_args = (args_from_string (line));
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scan_args = defun_args;
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case defun:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Function");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = deffn;
|
|
|
|
|
break;
|
|
|
|
|
case defmac:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Macro");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = deffn;
|
|
|
|
|
break;
|
|
|
|
|
case defspec:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Special Form");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = deffn;
|
|
|
|
|
break;
|
|
|
|
|
case defvar:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Variable");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = defvr;
|
|
|
|
|
break;
|
|
|
|
|
case defopt:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("User Option");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = defvr;
|
|
|
|
|
break;
|
|
|
|
|
case deftypefun:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Function");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = deftypefn;
|
|
|
|
|
break;
|
|
|
|
|
case deftypevar:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Variable");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = deftypevr;
|
|
|
|
|
break;
|
|
|
|
|
case defivar:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Instance Variable");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = defcv;
|
|
|
|
|
break;
|
|
|
|
|
case defmethod:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Method");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = defop;
|
|
|
|
|
break;
|
|
|
|
|
case deftypemethod:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
category = _("Method");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
base_type = deftypemethod;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
category = next_nonwhite_defun_arg (&scan_args);
|
|
|
|
|
base_type = type;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((base_type == deftypefn)
|
|
|
|
|
|| (base_type == deftypevr)
|
|
|
|
|
|| (base_type == defcv)
|
|
|
|
|
|| (base_type == defop)
|
|
|
|
|
|| (base_type == deftypemethod))
|
|
|
|
|
type_name = next_nonwhite_defun_arg (&scan_args);
|
|
|
|
|
|
|
|
|
|
if (base_type == deftypemethod)
|
|
|
|
|
type_name2 = next_nonwhite_defun_arg (&scan_args);
|
|
|
|
|
|
|
|
|
|
defined_name = next_nonwhite_defun_arg (&scan_args);
|
|
|
|
|
|
|
|
|
|
/* This hack exists solely for the purposes of formatting the texinfo
|
|
|
|
|
manual. I couldn't think of a better way. The token might be
|
|
|
|
|
a simple @@ followed immediately by more text. If this is the case,
|
|
|
|
|
then the next defun arg is part of this one, and we should concatenate
|
|
|
|
|
them. */
|
|
|
|
|
if (*scan_args && **scan_args && !whitespace (**scan_args) &&
|
|
|
|
|
(strcmp (defined_name, "@@") == 0))
|
|
|
|
|
{
|
|
|
|
|
char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
|
|
|
|
|
|
|
|
|
|
sprintf (tem, "@@%s", scan_args[0]);
|
|
|
|
|
|
|
|
|
|
free (scan_args[0]);
|
|
|
|
|
scan_args[0] = tem;
|
|
|
|
|
scan_args++;
|
|
|
|
|
defined_name = tem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!x_p)
|
|
|
|
|
begin_insertion (type);
|
|
|
|
|
|
|
|
|
|
/* Write the definition header line.
|
|
|
|
|
This should start at the normal indentation. */
|
|
|
|
|
current_indent -= default_indentation_increment;
|
|
|
|
|
start_paragraph ();
|
|
|
|
|
|
|
|
|
|
switch (base_type)
|
|
|
|
|
{
|
|
|
|
|
case deffn:
|
|
|
|
|
case defvr:
|
|
|
|
|
case deftp:
|
|
|
|
|
execute_string (" -- %s: %s", category, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
case deftypefn:
|
|
|
|
|
case deftypevr:
|
|
|
|
|
execute_string (" -- %s: %s %s", category, type_name, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
case defcv:
|
|
|
|
|
execute_string (" -- %s of %s: %s", category, type_name, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
case defop:
|
|
|
|
|
execute_string (" -- %s on %s: %s", category, type_name, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
case deftypemethod:
|
|
|
|
|
execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
defined_name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
current_indent += default_indentation_increment;
|
|
|
|
|
|
|
|
|
|
/* Now process the function arguments, if any.
|
|
|
|
|
If these carry onto the next line, they should be indented by two
|
|
|
|
|
increments to distinguish them from the body of the definition,
|
|
|
|
|
which is indented by one increment. */
|
|
|
|
|
current_indent += default_indentation_increment;
|
|
|
|
|
|
|
|
|
|
switch (base_type)
|
|
|
|
|
{
|
|
|
|
|
case deffn:
|
|
|
|
|
case defop:
|
|
|
|
|
process_defun_args (scan_args, 1);
|
|
|
|
|
break;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* Through Makeinfo 1.67 we processed remaining args only for deftp,
|
|
|
|
|
deftypefn, and deftypemethod. But the libc manual, for example,
|
|
|
|
|
needs to say:
|
|
|
|
|
@deftypevar {char *} tzname[2]
|
|
|
|
|
And simply allowing the extra text seems far simpler than trying
|
|
|
|
|
to invent yet more defn commands. In any case, we should either
|
|
|
|
|
output it or give an error, not silently ignore it. */
|
|
|
|
|
default:
|
1997-01-11 02:12:38 +00:00
|
|
|
|
process_defun_args (scan_args, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
current_indent -= default_indentation_increment;
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Make an entry in the appropriate index. */
|
|
|
|
|
switch (base_type)
|
|
|
|
|
{
|
|
|
|
|
case deffn:
|
|
|
|
|
case deftypefn:
|
|
|
|
|
execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
case defvr:
|
|
|
|
|
case deftypevr:
|
|
|
|
|
case defcv:
|
|
|
|
|
execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
case defop:
|
|
|
|
|
case deftypemethod:
|
|
|
|
|
execute_string ("%cfindex %s on %s\n",
|
|
|
|
|
COMMAND_PREFIX, defined_name, type_name);
|
|
|
|
|
break;
|
|
|
|
|
case deftp:
|
|
|
|
|
execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Deallocate the token list. */
|
|
|
|
|
scan_args = defun_args;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
char * arg = (*scan_args++);
|
|
|
|
|
if (arg == NULL)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (arg);
|
|
|
|
|
}
|
|
|
|
|
free (defun_args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add an entry for a function, macro, special form, variable, or option.
|
|
|
|
|
If the name of the calling command ends in `x', then this is an extra
|
|
|
|
|
entry included in the body of an insertion of the same type. */
|
|
|
|
|
void
|
|
|
|
|
cm_defun ()
|
|
|
|
|
{
|
|
|
|
|
int x_p;
|
|
|
|
|
enum insertion_type type;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *temp = xstrdup (command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
x_p = (command[strlen (command) - 1] == 'x');
|
|
|
|
|
|
|
|
|
|
if (x_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp[strlen (temp) - 1] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
type = find_type_from_name (temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
|
|
|
|
|
/* If we are adding to an already existing insertion, then make sure
|
|
|
|
|
that we are already in an insertion of type TYPE. */
|
|
|
|
|
if (x_p &&
|
|
|
|
|
(!insertion_level || insertion_stack->insertion != type))
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Must be in a `%s' insertion in order to use `%s'x"),
|
|
|
|
|
command, command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
discard_until ("\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defun_internal (type, x_p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Other Random Commands */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* This says to inhibit the indentation of the next paragraph, but
|
|
|
|
|
not of following paragraphs. */
|
|
|
|
|
void
|
|
|
|
|
cm_noindent ()
|
|
|
|
|
{
|
|
|
|
|
if (!inhibit_paragraph_indentation)
|
|
|
|
|
inhibit_paragraph_indentation = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* I don't know exactly what to do with this. Should I allow
|
|
|
|
|
someone to switch filenames in the middle of output? Since the
|
|
|
|
|
file could be partially written, this doesn't seem to make sense.
|
|
|
|
|
Another option: ignore it, since they don't *really* want to
|
|
|
|
|
switch files. Finally, complain, or at least warn. */
|
|
|
|
|
void
|
|
|
|
|
cm_setfilename ()
|
|
|
|
|
{
|
|
|
|
|
char *filename;
|
|
|
|
|
get_rest_of_line (&filename);
|
|
|
|
|
/* warning ("`@%s %s' encountered and ignored", command, filename); */
|
|
|
|
|
free (filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_ignore_line ()
|
|
|
|
|
{
|
|
|
|
|
discard_until ("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* @br can be immediately followed by `{}', so we have to read those here.
|
|
|
|
|
It should simply close the paragraph. */
|
|
|
|
|
void
|
|
|
|
|
cm_br ()
|
|
|
|
|
{
|
|
|
|
|
if (looking_at ("{}"))
|
|
|
|
|
input_text_offset += 2;
|
|
|
|
|
|
|
|
|
|
if (curchar () == '\n')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
line_number++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert the number of blank lines passed as argument. */
|
|
|
|
|
void
|
|
|
|
|
cm_sp ()
|
|
|
|
|
{
|
|
|
|
|
int lines;
|
|
|
|
|
char *line;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&line);
|
|
|
|
|
|
|
|
|
|
if (sscanf (line, "%d", &lines) != 1)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("%csp requires a positive numeric argument"), COMMAND_PREFIX);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (lines < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
lines = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
while (lines--)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_char ('\n');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* @dircategory LINE outputs INFO-DIR-SECTION LINE,
|
|
|
|
|
but not if --no-headers. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_dircategory ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *line;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
get_rest_of_line (&line);;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (!no_headers)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
insert_string ("INFO-DIR-SECTION ");
|
|
|
|
|
insert_string (line);
|
|
|
|
|
insert ('\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start a new line with just this text on it.
|
|
|
|
|
Then center the line of text.
|
|
|
|
|
This always ends the current paragraph. */
|
|
|
|
|
void
|
|
|
|
|
cm_center ()
|
|
|
|
|
{
|
|
|
|
|
register int i, start, length;
|
|
|
|
|
int fudge_factor = 1;
|
|
|
|
|
unsigned char *line;
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
filling_enabled = indented_fill = 0;
|
|
|
|
|
cm_noindent ();
|
|
|
|
|
start = output_paragraph_offset;
|
|
|
|
|
inhibit_output_flushing ();
|
|
|
|
|
get_rest_of_line ((char **)&line);
|
|
|
|
|
execute_string ("%s", (char *)line);
|
|
|
|
|
free (line);
|
|
|
|
|
uninhibit_output_flushing ();
|
|
|
|
|
|
|
|
|
|
i = output_paragraph_offset - 1;
|
|
|
|
|
while (i > (start - 1) && output_paragraph[i] == '\n')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
i--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
output_paragraph_offset = ++i;
|
|
|
|
|
length = output_paragraph_offset - start;
|
|
|
|
|
|
|
|
|
|
if (length < (fill_column - fudge_factor))
|
|
|
|
|
{
|
|
|
|
|
line = (unsigned char *)xmalloc (1 + length);
|
|
|
|
|
memcpy (line, (char *)(output_paragraph + start), length);
|
|
|
|
|
|
|
|
|
|
i = (fill_column - fudge_factor - length) / 2;
|
|
|
|
|
output_paragraph_offset = start;
|
|
|
|
|
|
|
|
|
|
while (i--)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
insert (' ');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
insert (line[i]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insert ('\n');
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
filling_enabled = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show what an expression returns. */
|
|
|
|
|
void
|
|
|
|
|
cm_result (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
add_word ("=>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* What an expression expands to. */
|
|
|
|
|
void
|
|
|
|
|
cm_expansion (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
add_word ("==>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Indicates two expressions are equivalent. */
|
|
|
|
|
void
|
|
|
|
|
cm_equiv (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
add_word ("==");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* What an expression may print. */
|
|
|
|
|
void
|
|
|
|
|
cm_print (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
add_word ("-|");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* An error signaled. */
|
|
|
|
|
void
|
|
|
|
|
cm_error (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
add_word ("error-->");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The location of point in an example of a buffer. */
|
|
|
|
|
void
|
|
|
|
|
cm_point (arg)
|
|
|
|
|
int arg;
|
|
|
|
|
{
|
|
|
|
|
if (arg == END)
|
|
|
|
|
add_word ("-!-");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start a new line with just this text on it.
|
|
|
|
|
The text is outdented one level if possible. */
|
|
|
|
|
void
|
|
|
|
|
cm_exdent ()
|
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
int i = current_indent;
|
|
|
|
|
|
|
|
|
|
if (current_indent)
|
|
|
|
|
current_indent -= default_indentation_increment;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&line);
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
execute_string ("%s", line);
|
|
|
|
|
current_indent = i;
|
|
|
|
|
free (line);
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Remember this file, and move onto the next. */
|
|
|
|
|
void
|
|
|
|
|
cm_include ()
|
|
|
|
|
{
|
|
|
|
|
char *filename;
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
get_rest_of_line (&filename);
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
pushfile ();
|
|
|
|
|
|
|
|
|
|
/* In verbose mode we print info about including another file. */
|
|
|
|
|
if (verbose_mode)
|
|
|
|
|
{
|
|
|
|
|
register int i = 0;
|
|
|
|
|
register FSTACK *stack = filestack;
|
|
|
|
|
|
|
|
|
|
for (i = 0, stack = filestack; stack; stack = stack->next, i++);
|
|
|
|
|
|
|
|
|
|
i *= 2;
|
|
|
|
|
|
|
|
|
|
printf ("%*s", i, "");
|
|
|
|
|
printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!find_and_load (filename))
|
|
|
|
|
{
|
|
|
|
|
extern int errno;
|
|
|
|
|
|
|
|
|
|
popfile ();
|
|
|
|
|
line_number--;
|
|
|
|
|
|
|
|
|
|
/* Cannot "@include foo", in line 5 of "/wh/bar". */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error ("%c%s %s: %s", COMMAND_PREFIX, command, filename,
|
|
|
|
|
strerror (errno));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (filename);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
reader_loop ();
|
|
|
|
|
}
|
|
|
|
|
free (filename);
|
|
|
|
|
popfile ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The other side of a malformed expression. */
|
|
|
|
|
void
|
|
|
|
|
misplaced_brace ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Misplaced %c"), '}');
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Signals end of processing. Easy to make this happen. */
|
|
|
|
|
void
|
|
|
|
|
cm_bye ()
|
|
|
|
|
{
|
|
|
|
|
input_text_offset = size_of_input_text;
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* Set the paragraph indentation variable to the value specified in STRING.
|
|
|
|
|
Values can be:
|
|
|
|
|
`asis': Don't change existing indentation.
|
|
|
|
|
`none': Remove existing indentation.
|
|
|
|
|
NUM: Indent NUM spaces at the starts of paragraphs.
|
|
|
|
|
If NUM is zero, we assume `none'.
|
|
|
|
|
Returns 0 if successful, or nonzero if STRING isn't one of the above. */
|
|
|
|
|
int
|
|
|
|
|
set_paragraph_indent (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
|
|
|
|
|
paragraph_start_indent = 0;
|
|
|
|
|
else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
|
|
|
|
|
paragraph_start_indent = -1;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (sscanf (string, "%d", ¶graph_start_indent) != 1)
|
|
|
|
|
return (-1);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (paragraph_start_indent == 0)
|
|
|
|
|
paragraph_start_indent = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_paragraphindent ()
|
|
|
|
|
{
|
|
|
|
|
char *arg;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&arg);
|
|
|
|
|
if (set_paragraph_indent (arg) != 0)
|
|
|
|
|
line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
|
|
|
|
|
|
|
|
|
|
free (arg);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Indexing Stuff */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* An index element... */
|
|
|
|
|
typedef struct index_elt
|
|
|
|
|
{
|
|
|
|
|
struct index_elt *next;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *entry; /* The index entry itself. */
|
|
|
|
|
char *node; /* The node from whence it came. */
|
|
|
|
|
int code; /* Nonzero means add `@code{...}' when
|
|
|
|
|
printing this element. */
|
|
|
|
|
int defining_line; /* Line number where this entry was written. */
|
|
|
|
|
char *defining_file; /* Source file for defining_line. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
} INDEX_ELT;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* A list of short-names for each index.
|
|
|
|
|
|
|
|
|
|
There are two indices into the the_indices array.
|
|
|
|
|
|
|
|
|
|
* read_index is the index that points to the list of index
|
|
|
|
|
entries that we will find if we ask for the list of entries for
|
|
|
|
|
this name.
|
|
|
|
|
|
|
|
|
|
* write_index is the index that points to the list of index entries
|
|
|
|
|
that we will add new entries to.
|
|
|
|
|
|
|
|
|
|
Initially, read_index and write index are the same, but the
|
|
|
|
|
@syncodeindex and @synindex commands can change the list we add
|
|
|
|
|
entries to.
|
|
|
|
|
|
|
|
|
|
For example, after the commands
|
|
|
|
|
|
|
|
|
|
@cindex foo
|
|
|
|
|
@defindex ii
|
|
|
|
|
@synindex cp ii
|
|
|
|
|
@cindex bar
|
|
|
|
|
|
|
|
|
|
the cp index will contain the entry `foo', and the new ii
|
|
|
|
|
index will contain the entry `bar'. This is consistent with the
|
|
|
|
|
way texinfo.tex handles the same situation.
|
|
|
|
|
|
|
|
|
|
In addition, for each index, it is remembered whether that index is
|
|
|
|
|
a code index or not. Code indices have @code{} inserted around the
|
|
|
|
|
first word when they are printed with printindex. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int read_index; /* index entries for `name' */
|
|
|
|
|
int write_index; /* store index entries here, @synindex can change it */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int code;
|
|
|
|
|
} INDEX_ALIST;
|
|
|
|
|
|
|
|
|
|
INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
|
|
|
|
|
|
|
|
|
|
/* An array of pointers. Each one is for a different index. The
|
|
|
|
|
"synindex" command changes which array slot is pointed to by a
|
|
|
|
|
given "index". */
|
|
|
|
|
INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
|
|
|
|
|
|
|
|
|
|
/* The number of defined indices. */
|
|
|
|
|
int defined_indices = 0;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
init_indices ()
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Create the default data structures. */
|
|
|
|
|
|
|
|
|
|
/* Initialize data space. */
|
|
|
|
|
if (!the_indices)
|
|
|
|
|
{
|
|
|
|
|
the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
|
1999-01-14 19:56:32 +00:00
|
|
|
|
sizeof (INDEX_ELT *));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
the_indices[defined_indices] = (INDEX_ELT *) NULL;
|
|
|
|
|
|
|
|
|
|
name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
|
1999-01-14 19:56:32 +00:00
|
|
|
|
sizeof (INDEX_ALIST *));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there were existing indices, get rid of them now. */
|
|
|
|
|
for (i = 0; i < defined_indices; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
undefindex (name_index_alist[i]->name);
|
|
|
|
|
if (name_index_alist[i])
|
|
|
|
|
{ /* Suppose we're called with two input files, and the first
|
|
|
|
|
does a @synindex pg cp. Then, when we get here to start
|
|
|
|
|
the second file, the "pg" element won't get freed by
|
|
|
|
|
undefindex (because it's pointing to "cp"). So free it
|
|
|
|
|
here; otherwise, when we try to define the pg index again
|
|
|
|
|
just below, it will still point to cp. */
|
|
|
|
|
free (name_index_alist[i]->name);
|
|
|
|
|
free (name_index_alist[i]);
|
|
|
|
|
name_index_alist[i] = (INDEX_ALIST *) NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Add the default indices. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
top_defindex ("cp", 0); /* cp is the only non-code index. */
|
|
|
|
|
top_defindex ("fn", 1);
|
|
|
|
|
top_defindex ("ky", 1);
|
|
|
|
|
top_defindex ("pg", 1);
|
|
|
|
|
top_defindex ("tp", 1);
|
|
|
|
|
top_defindex ("vr", 1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find which element in the known list of indices has this name.
|
|
|
|
|
Returns -1 if NAME isn't found. */
|
|
|
|
|
int
|
|
|
|
|
find_index_offset (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
for (i = 0; i < defined_indices; i++)
|
|
|
|
|
if (name_index_alist[i] &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
strcmp (name, name_index_alist[i]->name) == 0)
|
|
|
|
|
return (i);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a pointer to the entry of (name . index) for this name.
|
|
|
|
|
Return NULL if the index doesn't exist. */
|
|
|
|
|
INDEX_ALIST *
|
|
|
|
|
find_index (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
int offset = find_index_offset (name);
|
|
|
|
|
if (offset > -1)
|
|
|
|
|
return (name_index_alist[offset]);
|
|
|
|
|
else
|
|
|
|
|
return ((INDEX_ALIST *) NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given an index name, return the offset in the_indices of this index,
|
|
|
|
|
or -1 if there is no such index. */
|
|
|
|
|
int
|
|
|
|
|
translate_index (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
INDEX_ALIST *which = find_index (name);
|
|
|
|
|
|
|
|
|
|
if (which)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (which->read_index);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the index list which belongs to NAME. */
|
|
|
|
|
INDEX_ELT *
|
|
|
|
|
index_list (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
int which = translate_index (name);
|
|
|
|
|
if (which < 0)
|
|
|
|
|
return ((INDEX_ELT *) -1);
|
|
|
|
|
else
|
|
|
|
|
return (the_indices[which]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Please release me, let me go... */
|
|
|
|
|
void
|
|
|
|
|
free_index (index)
|
|
|
|
|
INDEX_ELT *index;
|
|
|
|
|
{
|
|
|
|
|
INDEX_ELT *temp;
|
|
|
|
|
|
|
|
|
|
while ((temp = index) != (INDEX_ELT *) NULL)
|
|
|
|
|
{
|
|
|
|
|
free (temp->entry);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Do not free the node, because we already freed the tag table,
|
|
|
|
|
which freed all the node names. */
|
|
|
|
|
/* free (temp->node); */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
index = index->next;
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Flush an index by name. This will delete the list of entries that
|
|
|
|
|
would be written by a @printindex command for this index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
undefindex (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int which = find_index_offset (name);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* The index might have already been freed if this was the target of
|
|
|
|
|
an @synindex. */
|
|
|
|
|
if (which < 0 || !name_index_alist[which])
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
i = name_index_alist[which]->read_index;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free_index (the_indices[i]);
|
|
|
|
|
the_indices[i] = (INDEX_ELT *) NULL;
|
|
|
|
|
|
|
|
|
|
free (name_index_alist[which]->name);
|
|
|
|
|
free (name_index_alist[which]);
|
|
|
|
|
name_index_alist[which] = (INDEX_ALIST *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Define an index known as NAME. We assign the slot number.
|
1999-01-14 19:56:32 +00:00
|
|
|
|
CODE if Nonzero says to make this a code index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
defindex (name, code)
|
|
|
|
|
char *name;
|
|
|
|
|
int code;
|
|
|
|
|
{
|
|
|
|
|
register int i, slot;
|
|
|
|
|
|
|
|
|
|
/* If it already exists, flush it. */
|
|
|
|
|
undefindex (name);
|
|
|
|
|
|
|
|
|
|
/* Try to find an empty slot. */
|
|
|
|
|
slot = -1;
|
|
|
|
|
for (i = 0; i < defined_indices; i++)
|
|
|
|
|
if (!name_index_alist[i])
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
slot = i;
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (slot < 0)
|
|
|
|
|
{
|
|
|
|
|
/* No such luck. Make space for another index. */
|
|
|
|
|
slot = defined_indices;
|
|
|
|
|
defined_indices++;
|
|
|
|
|
|
|
|
|
|
name_index_alist = (INDEX_ALIST **)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
xrealloc ((char *)name_index_alist,
|
|
|
|
|
(1 + defined_indices) * sizeof (INDEX_ALIST *));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
the_indices = (INDEX_ELT **)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
xrealloc ((char *)the_indices,
|
|
|
|
|
(1 + defined_indices) * sizeof (INDEX_ELT *));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We have a slot. Start assigning. */
|
|
|
|
|
name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
|
1999-01-14 19:56:32 +00:00
|
|
|
|
name_index_alist[slot]->name = xstrdup (name);
|
|
|
|
|
name_index_alist[slot]->read_index = slot;
|
|
|
|
|
name_index_alist[slot]->write_index = slot;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
name_index_alist[slot]->code = code;
|
|
|
|
|
|
|
|
|
|
the_indices[slot] = (INDEX_ELT *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the arguments to the current index command to the index NAME. */
|
|
|
|
|
void
|
|
|
|
|
index_add_arg (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
int which;
|
|
|
|
|
char *index_entry;
|
|
|
|
|
INDEX_ALIST *tem;
|
|
|
|
|
|
|
|
|
|
tem = find_index (name);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
which = tem ? tem->write_index : -1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
append_to_expansion_output (input_text_offset + 1);
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&index_entry);
|
|
|
|
|
ignore_blank_line ();
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
int op_orig;
|
|
|
|
|
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
op_orig = output_paragraph_offset;
|
|
|
|
|
me_execute_string (index_entry);
|
|
|
|
|
me_execute_string ("\n");
|
|
|
|
|
output_paragraph_offset = op_orig;
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
if (which < 0)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Unknown index `%s'"), name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (index_entry);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
|
|
|
|
|
new->next = the_indices[which];
|
|
|
|
|
new->entry = index_entry;
|
|
|
|
|
new->node = current_node;
|
|
|
|
|
new->code = tem->code;
|
|
|
|
|
new->defining_line = line_number - 1;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
new->defining_file = input_filename;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
the_indices[which] = new;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define INDEX_COMMAND_SUFFIX "index"
|
|
|
|
|
|
|
|
|
|
/* The function which user defined index commands call. */
|
|
|
|
|
void
|
|
|
|
|
gen_index ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *name = xstrdup (command);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (strlen (name) >= strlen ("index"))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
name[strlen (name) - strlen ("index")] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
index_add_arg (name);
|
|
|
|
|
free (name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
top_defindex (name, code)
|
|
|
|
|
char *name;
|
|
|
|
|
int code;
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
|
|
|
|
|
temp = (char *) xmalloc (1 + strlen (name) + strlen ("index"));
|
|
|
|
|
sprintf (temp, "%sindex", name);
|
|
|
|
|
define_user_command (temp, gen_index, 0);
|
|
|
|
|
defindex (name, code);
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Define a new index command. Arg is name of index. */
|
|
|
|
|
void
|
|
|
|
|
cm_defindex ()
|
|
|
|
|
{
|
|
|
|
|
gen_defindex (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_defcodeindex ()
|
|
|
|
|
{
|
|
|
|
|
gen_defindex (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gen_defindex (code)
|
|
|
|
|
int code;
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
get_rest_of_line (&name);
|
|
|
|
|
|
|
|
|
|
if (find_index (name))
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Index `%s' already exists"), name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
|
|
|
|
|
sprintf (temp, "%sindex", name);
|
|
|
|
|
define_user_command (temp, gen_index, 0);
|
|
|
|
|
defindex (name, code);
|
|
|
|
|
free (name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Expects 2 args, on the same line. Both are index abbreviations.
|
|
|
|
|
Make the first one be a synonym for the second one, i.e. make the
|
|
|
|
|
first one have the same index as the second one. */
|
|
|
|
|
void
|
|
|
|
|
cm_synindex ()
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int source, target;
|
|
|
|
|
char *abbrev1, *abbrev2;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_until_in_line (0, " ", &abbrev1);
|
|
|
|
|
target = find_index_offset (abbrev1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
skip_whitespace ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
get_until_in_line (0, " ", &abbrev2);
|
|
|
|
|
source = find_index_offset (abbrev2);
|
|
|
|
|
if (source < 0 || target < 0)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Unknown index `%s' and/or `%s' in @synindex"),
|
|
|
|
|
abbrev1, abbrev2);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
name_index_alist[target]->write_index
|
|
|
|
|
= name_index_alist[source]->write_index;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
free (abbrev1);
|
|
|
|
|
free (abbrev2);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_pindex () /* Pinhead index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
index_add_arg ("pg");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_vindex () /* Variable index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
index_add_arg ("vr");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_kindex () /* Key index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
index_add_arg ("ky");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_cindex () /* Concept index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
index_add_arg ("cp");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_findex () /* Function index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
index_add_arg ("fn");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
1999-01-14 19:56:32 +00:00
|
|
|
|
cm_tindex () /* Data Type index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
index_add_arg ("tp");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sorting the index. */
|
|
|
|
|
int
|
|
|
|
|
index_element_compare (element1, element2)
|
|
|
|
|
INDEX_ELT **element1, **element2;
|
|
|
|
|
{
|
|
|
|
|
return (strcasecmp ((*element1)->entry, (*element2)->entry));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Force all index entries to be unique. */
|
|
|
|
|
void
|
|
|
|
|
make_index_entries_unique (array, count)
|
|
|
|
|
INDEX_ELT **array;
|
|
|
|
|
int count;
|
|
|
|
|
{
|
|
|
|
|
register int i, j;
|
|
|
|
|
INDEX_ELT **copy;
|
|
|
|
|
int counter = 1;
|
|
|
|
|
|
|
|
|
|
copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *));
|
|
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
if ((i == (count - 1)) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(array[i]->node != array[i + 1]->node) ||
|
|
|
|
|
(strcmp (array[i]->entry, array[i + 1]->entry) != 0))
|
|
|
|
|
copy[j++] = array[i];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
free (array[i]->entry);
|
|
|
|
|
free (array[i]);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
copy[j] = (INDEX_ELT *)NULL;
|
|
|
|
|
|
|
|
|
|
/* Now COPY contains only unique entries. Duplicated entries in the
|
|
|
|
|
original array have been freed. Replace the current array with
|
|
|
|
|
the copy, fixing the NEXT pointers. */
|
|
|
|
|
for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
copy[i]->next = copy[i + 1];
|
|
|
|
|
|
|
|
|
|
/* Fix entry names which are the same. They point to different nodes,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
so we make the entry name unique. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if ((copy[i + 1] != (INDEX_ELT *)NULL) &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strcmp (copy[i]->entry, copy[i + 1]->entry) == 0))
|
|
|
|
|
{
|
|
|
|
|
char *new_entry_name;
|
|
|
|
|
|
|
|
|
|
new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry));
|
|
|
|
|
sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
|
|
|
|
|
free (copy[i]->entry);
|
|
|
|
|
copy[i]->entry = new_entry_name;
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
counter = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
array[i] = copy[i];
|
|
|
|
|
}
|
|
|
|
|
array[i] = (INDEX_ELT *)NULL;
|
|
|
|
|
|
|
|
|
|
/* Free the storage used only by COPY. */
|
|
|
|
|
free (copy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sort the index passed in INDEX, returning an array of
|
|
|
|
|
pointers to elements. The array is terminated with a NULL
|
|
|
|
|
pointer. We call qsort because it's supposed to be fast.
|
|
|
|
|
I think this looks bad. */
|
|
|
|
|
INDEX_ELT **
|
|
|
|
|
sort_index (index)
|
|
|
|
|
INDEX_ELT *index;
|
|
|
|
|
{
|
|
|
|
|
INDEX_ELT **array;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
INDEX_ELT *temp = index;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int count = 0;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int save_line_number = line_number;
|
|
|
|
|
char *save_input_filename = input_filename;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
while (temp != (INDEX_ELT *) NULL)
|
|
|
|
|
{
|
|
|
|
|
count++;
|
|
|
|
|
temp = temp->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We have the length. Make an array. */
|
|
|
|
|
|
|
|
|
|
array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
|
|
|
|
|
count = 0;
|
|
|
|
|
temp = index;
|
|
|
|
|
|
|
|
|
|
while (temp != (INDEX_ELT *) NULL)
|
|
|
|
|
{
|
|
|
|
|
array[count++] = temp;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Set line number and input filename to the source line for this
|
|
|
|
|
index entry, as this expansion finds any errors. */
|
|
|
|
|
line_number = array[count - 1]->defining_line;
|
|
|
|
|
input_filename = array[count - 1]->defining_file;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this particular entry should be printed as a "code" index,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
then wrap the entry with "@code{...}". */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
array[count - 1]->entry = expansion (temp->entry, index->code);
|
|
|
|
|
|
|
|
|
|
temp = temp->next;
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
array[count] = (INDEX_ELT *) NULL; /* terminate the array. */
|
|
|
|
|
line_number = save_line_number;
|
|
|
|
|
input_filename = save_input_filename;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Sort the array. */
|
|
|
|
|
qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
|
|
|
|
|
make_index_entries_unique (array, count);
|
|
|
|
|
return (array);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that we are in the middle of printing an index. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
int printing_index = 0;
|
|
|
|
|
|
|
|
|
|
/* Takes one arg, a short name of an index to print.
|
|
|
|
|
Outputs a menu of the sorted elements of the index. */
|
|
|
|
|
void
|
|
|
|
|
cm_printindex ()
|
|
|
|
|
{
|
|
|
|
|
int item;
|
|
|
|
|
INDEX_ELT *index;
|
|
|
|
|
INDEX_ELT **array;
|
|
|
|
|
char *index_name;
|
|
|
|
|
unsigned line_length;
|
|
|
|
|
char *line;
|
|
|
|
|
int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation;
|
|
|
|
|
int saved_filling_enabled = filling_enabled;
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
get_rest_of_line (&index_name);
|
|
|
|
|
|
|
|
|
|
index = index_list (index_name);
|
|
|
|
|
if (index == (INDEX_ELT *)-1)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Unknown index `%s' in @printindex"), index_name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (index_name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
free (index_name);
|
|
|
|
|
|
|
|
|
|
/* Do this before sorting, so execute_string in index_element_compare
|
|
|
|
|
will give the same results as when we actually print. */
|
|
|
|
|
printing_index = 1;
|
|
|
|
|
filling_enabled = 0;
|
|
|
|
|
inhibit_paragraph_indentation = 1;
|
|
|
|
|
array = sort_index (index);
|
|
|
|
|
|
|
|
|
|
close_paragraph ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_word (_("* Menu:\n\n"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
me_inhibit_expansion++;
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
/* This will probably be enough. */
|
|
|
|
|
line_length = 100;
|
|
|
|
|
line = xmalloc (line_length);
|
|
|
|
|
|
|
|
|
|
for (item = 0; (index = array[item]); item++)
|
|
|
|
|
{
|
|
|
|
|
/* A pathological document might have an index entry outside of any
|
|
|
|
|
node. Don't crash. Perhaps should warn. */
|
|
|
|
|
char *index_node = index->node ? index->node : "(none)";
|
1999-01-14 19:56:32 +00:00
|
|
|
|
unsigned new_length = strlen (index->entry);
|
|
|
|
|
|
|
|
|
|
if (new_length < 37) /* minimum length used below */
|
|
|
|
|
new_length = 37;
|
|
|
|
|
new_length += strlen (index_node) + 7; /* * : .\n\0 */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (new_length > line_length)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_length = new_length;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
line = xrealloc (line, line_length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print the entry, nicely formatted. We've already expanded any
|
|
|
|
|
commands, including any implicit @code. Thus, can't call
|
|
|
|
|
execute_string, since @@ has turned into @. */
|
|
|
|
|
sprintf (line, "* %-37s %s.\n", index->entry, index_node);
|
|
|
|
|
line[2 + strlen (index->entry)] = ':';
|
|
|
|
|
insert_string (line);
|
|
|
|
|
|
|
|
|
|
/* Previous `output_paragraph' from growing to the size of the
|
|
|
|
|
whole index. */
|
|
|
|
|
flush_output ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
me_inhibit_expansion--;
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
printing_index = 0;
|
|
|
|
|
free (array);
|
|
|
|
|
close_single_paragraph ();
|
|
|
|
|
filling_enabled = saved_filling_enabled;
|
|
|
|
|
inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation;
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* User-defined commands, which happens only from user-defined indexes. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
define_user_command (name, proc, needs_braces_p)
|
|
|
|
|
char *name;
|
|
|
|
|
COMMAND_FUNCTION *proc;
|
|
|
|
|
int needs_braces_p;
|
|
|
|
|
{
|
|
|
|
|
int slot = user_command_array_len;
|
|
|
|
|
user_command_array_len++;
|
|
|
|
|
|
|
|
|
|
if (!user_command_array)
|
|
|
|
|
user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
|
|
|
|
|
|
|
|
|
|
user_command_array = (COMMAND **) xrealloc (user_command_array,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(1 + user_command_array_len) *
|
|
|
|
|
sizeof (COMMAND *));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
|
1999-01-14 19:56:32 +00:00
|
|
|
|
user_command_array[slot]->name = xstrdup (name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
user_command_array[slot]->proc = proc;
|
|
|
|
|
user_command_array[slot]->argument_in_braces = needs_braces_p;
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Some support for footnotes. */
|
|
|
|
|
|
|
|
|
|
/* Footnotes are a new construct in Info. We don't know the best method
|
|
|
|
|
of implementing them for sure, so we present two possiblities.
|
|
|
|
|
|
|
|
|
|
SeparateNode:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
Make them look like followed references, with the reference
|
|
|
|
|
destinations in a makeinfo manufactured node or,
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
EndNode:
|
1999-01-14 19:56:32 +00:00
|
|
|
|
Make them appear at the bottom of the node that they originally
|
|
|
|
|
appeared in. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#define SeparateNode 0
|
|
|
|
|
#define EndNode 1
|
|
|
|
|
|
|
|
|
|
int footnote_style = EndNode;
|
|
|
|
|
int first_footnote_this_node = 1;
|
|
|
|
|
int footnote_count = 0;
|
|
|
|
|
|
|
|
|
|
/* Set the footnote style based on he style identifier in STRING. */
|
|
|
|
|
int
|
|
|
|
|
set_footnote_style (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
if ((strcasecmp (string, "separate") == 0) ||
|
|
|
|
|
(strcasecmp (string, "MN") == 0))
|
|
|
|
|
footnote_style = SeparateNode;
|
|
|
|
|
else if ((strcasecmp (string, "end") == 0) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strcasecmp (string, "EN") == 0))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
footnote_style = EndNode;
|
|
|
|
|
else
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_footnotestyle ()
|
|
|
|
|
{
|
|
|
|
|
char *arg;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&arg);
|
|
|
|
|
|
|
|
|
|
/* If set on command line, do not change the footnote style. */
|
|
|
|
|
if (!footnote_style_preset && set_footnote_style (arg) != 0)
|
|
|
|
|
line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
|
|
|
|
|
|
|
|
|
|
free (arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct fn
|
|
|
|
|
{
|
|
|
|
|
struct fn *next;
|
|
|
|
|
char *marker;
|
|
|
|
|
char *note;
|
|
|
|
|
} FN;
|
|
|
|
|
|
|
|
|
|
FN *pending_notes = (FN *) NULL;
|
|
|
|
|
|
|
|
|
|
/* A method for remembering footnotes. Note that this list gets output
|
|
|
|
|
at the end of the current node. */
|
|
|
|
|
void
|
|
|
|
|
remember_note (marker, note)
|
|
|
|
|
char *marker, *note;
|
|
|
|
|
{
|
|
|
|
|
FN *temp = (FN *) xmalloc (sizeof (FN));
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
temp->marker = xstrdup (marker);
|
|
|
|
|
temp->note = xstrdup (note);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
temp->next = pending_notes;
|
|
|
|
|
pending_notes = temp;
|
|
|
|
|
footnote_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to get rid of existing footnotes. */
|
|
|
|
|
void
|
|
|
|
|
free_pending_notes ()
|
|
|
|
|
{
|
|
|
|
|
FN *temp;
|
|
|
|
|
|
|
|
|
|
while ((temp = pending_notes) != (FN *) NULL)
|
|
|
|
|
{
|
|
|
|
|
free (temp->marker);
|
|
|
|
|
free (temp->note);
|
|
|
|
|
pending_notes = pending_notes->next;
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
first_footnote_this_node = 1;
|
|
|
|
|
footnote_count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* What to do when you see a @footnote construct. */
|
|
|
|
|
|
|
|
|
|
/* Handle a "footnote".
|
|
|
|
|
footnote *{this is a footnote}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
where "*" is the (optional) marker character for this note. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
|
|
|
|
cm_footnote ()
|
|
|
|
|
{
|
|
|
|
|
char *marker;
|
|
|
|
|
char *note;
|
|
|
|
|
|
|
|
|
|
get_until ("{", &marker);
|
|
|
|
|
canon_white (marker);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
append_to_expansion_output (input_text_offset + 1); /* include the { */
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Read the argument in braces. */
|
|
|
|
|
if (curchar () != '{')
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("`%c%s' needs an argument `{...}', not just `%s'"),
|
|
|
|
|
COMMAND_PREFIX, command, marker);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (marker);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int len;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int braces = 1;
|
|
|
|
|
int loc = ++input_text_offset;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
while (braces)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (loc == size_of_input_text)
|
|
|
|
|
{
|
|
|
|
|
line_error (_("No closing brace for footnote `%s'"), marker);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (input_text[loc] == '{')
|
|
|
|
|
braces++;
|
|
|
|
|
else if (input_text[loc] == '}')
|
|
|
|
|
braces--;
|
|
|
|
|
else if (input_text[loc] == '\n')
|
|
|
|
|
line_number++;
|
|
|
|
|
|
|
|
|
|
loc++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = (loc - input_text_offset) - 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
note = (char *)xmalloc (len + 1);
|
|
|
|
|
strncpy (note, &input_text[input_text_offset], len);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
note[len] = 0;
|
|
|
|
|
input_text_offset = loc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Must write the macro-expanded argument to the macro expansion
|
|
|
|
|
output stream. This is like the case in index_add_arg. */
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
{
|
|
|
|
|
int op_orig;
|
|
|
|
|
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
op_orig = output_paragraph_offset;
|
|
|
|
|
me_execute_string (note);
|
|
|
|
|
/* Calling me_execute_string on a lone } provokes an error, since
|
|
|
|
|
as far as the reader knows there is no matching {. We wrote
|
|
|
|
|
the { above in the call to append_to_expansion_output. */
|
|
|
|
|
write_region_to_macro_output ("}", 0, 1);
|
|
|
|
|
output_paragraph_offset = op_orig;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!current_node || !*current_node)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Footnote defined without parent node"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (marker);
|
|
|
|
|
free (note);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*marker)
|
|
|
|
|
{
|
|
|
|
|
free (marker);
|
|
|
|
|
|
|
|
|
|
if (number_footnotes)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
marker = (char *)xmalloc (10);
|
|
|
|
|
sprintf (marker, "%d", current_footnote_number);
|
|
|
|
|
current_footnote_number++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
marker = xstrdup ("*");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
remember_note (marker, note);
|
|
|
|
|
|
|
|
|
|
/* Your method should at least insert MARKER. */
|
|
|
|
|
switch (footnote_style)
|
|
|
|
|
{
|
|
|
|
|
case SeparateNode:
|
|
|
|
|
add_word_args ("(%s)", marker);
|
|
|
|
|
if (first_footnote_this_node)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
char *temp_string;
|
|
|
|
|
|
|
|
|
|
temp_string = (char *)
|
|
|
|
|
xmalloc ((strlen (current_node)) + (strlen (_("-Footnotes"))) + 1);
|
|
|
|
|
|
|
|
|
|
add_word_args (" (*note %s-Footnotes::)", current_node);
|
|
|
|
|
strcpy (temp_string, current_node);
|
|
|
|
|
strcat (temp_string, "-Footnotes");
|
|
|
|
|
remember_node_reference (temp_string, line_number, followed_reference);
|
|
|
|
|
free (temp_string);
|
|
|
|
|
first_footnote_this_node = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case EndNode:
|
|
|
|
|
add_word_args ("(%s)", marker);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
free (marker);
|
|
|
|
|
free (note);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Nonzero means that we are currently in the process of outputting
|
1997-01-11 02:12:38 +00:00
|
|
|
|
footnotes. */
|
|
|
|
|
int already_outputting_pending_notes = 0;
|
|
|
|
|
|
|
|
|
|
/* Output the footnotes. We are at the end of the current node. */
|
|
|
|
|
void
|
|
|
|
|
output_pending_notes ()
|
|
|
|
|
{
|
|
|
|
|
FN *footnote = pending_notes;
|
|
|
|
|
|
|
|
|
|
if (!pending_notes)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (footnote_style)
|
|
|
|
|
{
|
|
|
|
|
case SeparateNode:
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *old_current_node = current_node;
|
|
|
|
|
char *old_command = xstrdup (command);
|
|
|
|
|
|
|
|
|
|
already_outputting_pending_notes++;
|
|
|
|
|
execute_string ("%cnode %s-Footnotes,,,%s\n",
|
|
|
|
|
COMMAND_PREFIX, current_node, current_node);
|
|
|
|
|
already_outputting_pending_notes--;
|
|
|
|
|
current_node = old_current_node;
|
|
|
|
|
free (command);
|
|
|
|
|
command = old_command;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case EndNode:
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
in_fixed_width_font++;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string (_("---------- Footnotes ----------\n\n"));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
in_fixed_width_font--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle the footnotes in reverse order. */
|
|
|
|
|
{
|
|
|
|
|
FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
|
|
|
|
|
|
|
|
|
|
array[footnote_count] = (FN *) NULL;
|
|
|
|
|
|
|
|
|
|
while (--footnote_count > -1)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
array[footnote_count] = footnote;
|
|
|
|
|
footnote = footnote->next;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filling_enabled = 1;
|
|
|
|
|
indented_fill = 1;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while ((footnote = array[++footnote_count]))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string ("(%s) %s", footnote->marker, footnote->note);
|
|
|
|
|
close_paragraph ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
close_paragraph ();
|
|
|
|
|
free (array);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
/* */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* User definable Macros (text substitution) */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_MACROS)
|
|
|
|
|
|
|
|
|
|
/* Array of macros and definitions. */
|
|
|
|
|
MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int macro_list_len = 0; /* Number of elements. */
|
|
|
|
|
int macro_list_size = 0; /* Number of slots in total. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Return the macro definition of NAME or NULL if NAME is not defined. */
|
|
|
|
|
MACRO_DEF *
|
|
|
|
|
find_macro (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
register MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = (MACRO_DEF *)NULL;
|
|
|
|
|
for (i = 0; macro_list && (def = macro_list[i]); i++)
|
|
|
|
|
{
|
|
|
|
|
if ((!def->inhibited) && (strcmp (def->name, name) == 0))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
|
|
|
|
|
SOURCE_FILE is the name of the file where this definition can be found,
|
|
|
|
|
and SOURCE_LINENO is the line number within that file. If a macro already
|
|
|
|
|
exists with NAME, then a warning is produced, and that previous
|
|
|
|
|
definition is overwritten. */
|
|
|
|
|
void
|
|
|
|
|
add_macro (name, arglist, body, source_file, source_lineno, flags)
|
|
|
|
|
char *name;
|
|
|
|
|
char **arglist;
|
|
|
|
|
char *body;
|
|
|
|
|
char *source_file;
|
|
|
|
|
int source_lineno, flags;
|
|
|
|
|
{
|
|
|
|
|
register MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = find_macro (name);
|
|
|
|
|
|
|
|
|
|
if (!def)
|
|
|
|
|
{
|
|
|
|
|
if (macro_list_len + 2 >= macro_list_size)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
macro_list = (MACRO_DEF **)xrealloc
|
|
|
|
|
(macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
|
|
|
|
|
macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
|
|
|
|
|
|
|
|
|
|
def = macro_list[macro_list_len];
|
|
|
|
|
macro_list_len += 1;
|
|
|
|
|
def->name = name;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *temp_filename = input_filename;
|
|
|
|
|
int temp_line = line_number;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("macro `%s' previously defined"), name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
input_filename = def->source_file;
|
|
|
|
|
line_number = def->source_lineno;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
warning (_("here is the previous definition of `%s'"), name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
input_filename = temp_filename;
|
|
|
|
|
line_number = temp_line;
|
|
|
|
|
|
|
|
|
|
if (def->arglist)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = 0; def->arglist[i]; i++)
|
|
|
|
|
free (def->arglist[i]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (def->arglist);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (def->source_file);
|
|
|
|
|
free (def->body);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
def->source_file = xstrdup (source_file);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
def->source_lineno = source_lineno;
|
|
|
|
|
def->body = body;
|
|
|
|
|
def->arglist = arglist;
|
|
|
|
|
def->inhibited = 0;
|
|
|
|
|
def->flags = flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Delete the macro with name NAME. The macro is deleted from the list,
|
|
|
|
|
but it is also returned. If there was no macro defined, NULL is
|
|
|
|
|
returned. */
|
|
|
|
|
MACRO_DEF *
|
|
|
|
|
delete_macro (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
register MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = (MACRO_DEF *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; macro_list && (def = macro_list[i]); i++)
|
|
|
|
|
if (strcmp (def->name, name) == 0)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
memmove (macro_list + i, macro_list + i + 1,
|
|
|
|
|
((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
|
|
|
|
|
macro_list_len--;
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the arglist on the current line. This can behave in two different
|
|
|
|
|
ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
|
|
|
|
|
int braces_required_for_macro_args = 0;
|
|
|
|
|
|
|
|
|
|
char **
|
|
|
|
|
get_macro_args (def)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
MACRO_DEF *def;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *word;
|
|
|
|
|
|
|
|
|
|
/* Quickly check to see if this macro has been invoked with any arguments.
|
|
|
|
|
If not, then don't skip any of the following whitespace. */
|
|
|
|
|
for (i = input_text_offset; i < size_of_input_text; i++)
|
|
|
|
|
if (!cr_or_whitespace (input_text[i]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (input_text[i] != '{')
|
|
|
|
|
{
|
|
|
|
|
if (braces_required_for_macro_args)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
return ((char **)NULL);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* Braces are not required to fill out the macro arguments. If
|
|
|
|
|
this macro takes one argument, it is considered to be the
|
|
|
|
|
remainder of the line, sans whitespace. */
|
|
|
|
|
if (def->arglist && def->arglist[0] && !def->arglist[1])
|
|
|
|
|
{
|
|
|
|
|
char **arglist;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&word);
|
|
|
|
|
if (input_text[input_text_offset - 1] == '\n')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset--;
|
|
|
|
|
line_number--;
|
|
|
|
|
}
|
|
|
|
|
/* canon_white (word); */
|
|
|
|
|
arglist = (char **)xmalloc (2 * sizeof (char *));
|
|
|
|
|
arglist[0] = word;
|
|
|
|
|
arglist[1] = (char *)NULL;
|
|
|
|
|
return (arglist);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* The macro either took no arguments, or took more than
|
|
|
|
|
one argument. In that case, it must be invoked with
|
|
|
|
|
arguments surrounded by braces. */
|
|
|
|
|
return ((char **)NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (get_brace_args (def->flags & ME_QUOTE_ARG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Substitute actual parameters for named parameters in body.
|
|
|
|
|
The named parameters which appear in BODY must by surrounded
|
|
|
|
|
reverse slashes, as in \foo\. */
|
|
|
|
|
char *
|
|
|
|
|
apply (named, actuals, body)
|
|
|
|
|
char **named, **actuals, *body;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
int new_body_index, new_body_size;
|
|
|
|
|
char *new_body, *text;
|
|
|
|
|
int length_of_actuals;
|
|
|
|
|
|
|
|
|
|
length_of_actuals = array_len (actuals);
|
|
|
|
|
new_body_size = strlen (body);
|
|
|
|
|
new_body = (char *)xmalloc (1 + new_body_size);
|
|
|
|
|
|
|
|
|
|
/* Copy chars from BODY into NEW_BODY. */
|
|
|
|
|
i = 0; new_body_index = 0;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
if (!body[i])
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (body[i] != '\\')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
new_body[new_body_index++] = body[i++];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* Snarf parameter name, check against named parameters. */
|
|
|
|
|
char *param;
|
|
|
|
|
int param_start, which, len;
|
|
|
|
|
|
|
|
|
|
param_start = ++i;
|
|
|
|
|
while ((body[i]) && (body[i] != '\\'))
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
len = i - param_start;
|
|
|
|
|
param = (char *)xmalloc (1 + len);
|
|
|
|
|
memcpy (param, body + param_start, len);
|
|
|
|
|
param[len] = 0;
|
|
|
|
|
|
|
|
|
|
if (body[i]) /* move past \ */
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* Now check against named parameters. */
|
|
|
|
|
for (which = 0; named && named[which]; which++)
|
|
|
|
|
if (strcmp (named[which], param) == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (named && named[which])
|
|
|
|
|
{
|
|
|
|
|
if (which < length_of_actuals)
|
|
|
|
|
text = actuals[which];
|
|
|
|
|
else
|
|
|
|
|
text = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
if (!text)
|
|
|
|
|
text = "";
|
|
|
|
|
|
|
|
|
|
len = strlen (text);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ /* not a parameter, restore \'s */
|
|
|
|
|
i = body[i] ? (i - 1) : i;
|
|
|
|
|
len++;
|
|
|
|
|
text = xmalloc (1 + len);
|
|
|
|
|
sprintf (text, "\\%s", param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((2 + strlen (param)) < len)
|
|
|
|
|
{
|
|
|
|
|
new_body_size += len + 1;
|
|
|
|
|
new_body = xrealloc (new_body, new_body_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (param);
|
|
|
|
|
|
|
|
|
|
strcpy (new_body + new_body_index, text);
|
|
|
|
|
new_body_index += len;
|
|
|
|
|
|
|
|
|
|
if (!named || !named[which])
|
|
|
|
|
free (text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
new_body[new_body_index] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return (new_body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
|
|
|
|
|
void
|
|
|
|
|
execute_macro (def)
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
{
|
|
|
|
|
char **arglist;
|
|
|
|
|
int num_args;
|
|
|
|
|
char *execution_string = (char *)NULL;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
|
|
|
|
|
/* Find out how many arguments this macro definition takes. */
|
|
|
|
|
num_args = array_len (def->arglist);
|
|
|
|
|
|
|
|
|
|
/* Gather the arguments present on the line if there are any. */
|
|
|
|
|
arglist = get_macro_args (def);
|
|
|
|
|
|
|
|
|
|
if (num_args < array_len (arglist))
|
|
|
|
|
{
|
|
|
|
|
free_array (arglist);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line_error (_("Macro `%s' called with too many args"), def->name);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (def->body)
|
|
|
|
|
execution_string = apply (def->arglist, arglist, def->body);
|
|
|
|
|
|
|
|
|
|
free_array (arglist);
|
|
|
|
|
|
|
|
|
|
if (def->body)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
|
|
|
|
|
{
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
me_execute_string (execution_string);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
execute_string ("%s", execution_string);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (execution_string);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read and remember the definition of a macro. */
|
|
|
|
|
void
|
|
|
|
|
cm_macro ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *name, **arglist, *body, *line;
|
|
|
|
|
int body_size, body_index;
|
|
|
|
|
int depth = 1;
|
|
|
|
|
int defining_line = line_number;
|
|
|
|
|
int flags = 0;
|
|
|
|
|
|
|
|
|
|
arglist = (char **)NULL;
|
|
|
|
|
body = (char *)NULL;
|
|
|
|
|
body_size = 0;
|
|
|
|
|
body_index = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
|
|
|
|
|
/* Get the name of the macro. This is the set of characters which are
|
|
|
|
|
not whitespace and are not `{' immediately following the @macro. */
|
|
|
|
|
{
|
|
|
|
|
int start = input_text_offset;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
for (i = start;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(i < size_of_input_text) &&
|
|
|
|
|
(input_text[i] != '{') &&
|
|
|
|
|
(!cr_or_whitespace (input_text[i]));
|
|
|
|
|
i++);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
len = i - start;
|
|
|
|
|
name = (char *)xmalloc (1 + len);
|
|
|
|
|
strncpy (name, input_text + start, len);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
name[len] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
input_text_offset = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
|
|
|
|
|
/* It is not required that the definition of a macro includes an arglist.
|
|
|
|
|
If not, don't try to get the named parameters, just use a null list. */
|
|
|
|
|
if (curchar () == '{')
|
|
|
|
|
{
|
|
|
|
|
int arglist_index = 0, arglist_size = 0;
|
|
|
|
|
int gathering_words = 1;
|
|
|
|
|
char *word = (char *)NULL;
|
|
|
|
|
int character;
|
|
|
|
|
|
|
|
|
|
/* Read the words inside of the braces which determine the arglist.
|
1999-01-14 19:56:32 +00:00
|
|
|
|
These words will be replaced within the body of the macro at
|
|
|
|
|
execution time. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
|
|
|
|
|
while (gathering_words)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
for (i = input_text_offset;
|
|
|
|
|
(character = input_text[i]);
|
|
|
|
|
i++)
|
|
|
|
|
{
|
|
|
|
|
switch (character)
|
|
|
|
|
{
|
|
|
|
|
case '\n':
|
|
|
|
|
line_number++;
|
|
|
|
|
case ' ':
|
|
|
|
|
case '\t':
|
|
|
|
|
case ',':
|
|
|
|
|
case '}':
|
|
|
|
|
/* Found the end of the current arglist word. Save it. */
|
|
|
|
|
len = i - input_text_offset;
|
|
|
|
|
word = (char *)xmalloc (1 + len);
|
|
|
|
|
strncpy (word, input_text + input_text_offset, len);
|
|
|
|
|
word[len] = 0;
|
|
|
|
|
input_text_offset = i;
|
|
|
|
|
|
|
|
|
|
/* Advance to the comma or close-brace that signified
|
|
|
|
|
the end of the argument. */
|
|
|
|
|
while ((character = curchar ())
|
|
|
|
|
&& character != ','
|
|
|
|
|
&& character != '}')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
if (character == '\n')
|
|
|
|
|
line_number++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the word to our list of words. */
|
|
|
|
|
if ((arglist_index + 2) >= arglist_size)
|
|
|
|
|
arglist = (char **)xrealloc
|
|
|
|
|
(arglist, (arglist_size += 10) * sizeof (char *));
|
|
|
|
|
|
|
|
|
|
arglist[arglist_index++] = word;
|
|
|
|
|
arglist[arglist_index] = (char *)NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (character == '}')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
gathering_words = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (character == ',')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
i = input_text_offset - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read the text carefully until we find an "@end macro" which
|
|
|
|
|
matches this one. The text in between is the body of the macro. */
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
|
|
|
|
|
while (depth)
|
|
|
|
|
{
|
|
|
|
|
if ((input_text_offset + 9) > size_of_input_text)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int temp_line = line_number;
|
|
|
|
|
line_number = defining_line;
|
|
|
|
|
line_error (_("%cend macro not found"), COMMAND_PREFIX);
|
|
|
|
|
line_number = temp_line;
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
get_rest_of_line (&line);
|
|
|
|
|
|
|
|
|
|
/* Handle commands only meaningful within a macro. */
|
|
|
|
|
if ((*line == COMMAND_PREFIX) && (depth == 1) &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strncmp (line + 1, "allow-recursion", 15) == 0) &&
|
|
|
|
|
(line[16] == 0 || whitespace (line[16])))
|
|
|
|
|
{
|
|
|
|
|
for (i = 16; whitespace (line[i]); i++);
|
|
|
|
|
strcpy (line, line + i);
|
|
|
|
|
flags |= ME_RECURSE;
|
|
|
|
|
if (!*line)
|
|
|
|
|
{
|
|
|
|
|
free (line);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if ((*line == COMMAND_PREFIX) && (depth == 1) &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strncmp (line + 1, "quote-arg", 9) == 0) &&
|
|
|
|
|
(line[10] == 0 || whitespace (line[10])))
|
|
|
|
|
{
|
|
|
|
|
for (i = 10; whitespace (line[i]); i++);
|
|
|
|
|
strcpy (line, line + i);
|
|
|
|
|
|
|
|
|
|
if (arglist && arglist[0] && !arglist[1])
|
|
|
|
|
{
|
|
|
|
|
flags |= ME_QUOTE_ARG;
|
|
|
|
|
if (!*line)
|
|
|
|
|
{
|
|
|
|
|
free (line);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
line_error (_("%cquote-arg only useful when the macro takes a single argument"),
|
|
|
|
|
COMMAND_PREFIX);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if ((*line == COMMAND_PREFIX) &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strncmp (line + 1, "macro ", 6) == 0))
|
|
|
|
|
depth++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if ((*line == COMMAND_PREFIX) &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(strncmp (line + 1, "end macro", 9) == 0))
|
|
|
|
|
depth--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (depth)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if ((body_index + strlen (line) + 3) >= body_size)
|
|
|
|
|
body = (char *)xrealloc
|
|
|
|
|
(body, body_size += 3 + strlen (line));
|
|
|
|
|
strcpy (body + body_index, line);
|
|
|
|
|
body_index += strlen (line);
|
|
|
|
|
body[body_index++] = '\n';
|
|
|
|
|
body[body_index] = 0;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* If it was an empty macro like
|
|
|
|
|
@macro foo
|
|
|
|
|
@end macro
|
|
|
|
|
create an empty body. (Otherwise, the macro is not expanded.) */
|
|
|
|
|
if (!body)
|
|
|
|
|
{
|
|
|
|
|
body = (char *)malloc(1);
|
|
|
|
|
*body = 0;
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* We now have the name, the arglist, and the body. However, BODY
|
|
|
|
|
includes the final newline which preceded the `@end macro' text.
|
|
|
|
|
Delete it. */
|
|
|
|
|
if (body && strlen (body))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
body[strlen (body) - 1] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
add_macro (name, arglist, body, input_filename, defining_line, flags);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_unmacro ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *line, *name;
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (&line);
|
|
|
|
|
|
|
|
|
|
for (i = 0; line[i] && !whitespace (line[i]); i++);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
name = (char *)xmalloc (i + 1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
strncpy (name, line, i);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
name[i] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
def = delete_macro (name);
|
|
|
|
|
|
|
|
|
|
if (def)
|
|
|
|
|
{
|
|
|
|
|
free (def->source_file);
|
|
|
|
|
free (def->name);
|
|
|
|
|
free (def->body);
|
|
|
|
|
|
|
|
|
|
if (def->arglist)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (i = 0; def->arglist[i]; i++)
|
|
|
|
|
free (def->arglist[i]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (def->arglist);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
free (name);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to output sections of the input file verbatim. */
|
|
|
|
|
|
|
|
|
|
/* Set the value of POINTER's offset to OFFSET. */
|
|
|
|
|
ITEXT *
|
|
|
|
|
remember_itext (pointer, offset)
|
|
|
|
|
char *pointer;
|
|
|
|
|
int offset;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
ITEXT *itext = (ITEXT *)NULL;
|
|
|
|
|
|
|
|
|
|
/* If we have no info, initialize a blank list. */
|
|
|
|
|
if (!itext_info)
|
|
|
|
|
{
|
|
|
|
|
itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *));
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
itext_info[i] = (ITEXT *)NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the pointer is already present in the list, then set the offset. */
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if ((itext_info[i] != (ITEXT *)NULL) &&
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(itext_info[i]->pointer == pointer))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
itext_info[i]->offset = offset;
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == itext_size)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Find a blank slot (or create a new one), and remember the
|
|
|
|
|
pointer and offset. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
for (i = 0; i < itext_size; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (itext_info[i] == (ITEXT *)NULL)
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If not found, then add some slots. */
|
|
|
|
|
if (i == itext_size)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
register int j;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
itext_info = (ITEXT **)xrealloc
|
|
|
|
|
(itext_info, (itext_size += 10) * sizeof (ITEXT *));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (j = i; j < itext_size; j++)
|
|
|
|
|
itext_info[j] = (ITEXT *)NULL;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Now add the pointer and the offset. */
|
|
|
|
|
itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT));
|
|
|
|
|
itext_info[i]->pointer = pointer;
|
|
|
|
|
itext_info[i]->offset = offset;
|
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
}
|
|
|
|
|
return (itext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Forget the input text associated with POINTER. */
|
|
|
|
|
void
|
|
|
|
|
forget_itext (pointer)
|
|
|
|
|
char *pointer;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] && (itext_info[i]->pointer == pointer))
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (itext_info[i]);
|
|
|
|
|
itext_info[i] = (ITEXT *)NULL;
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append the text which appeared in input_text from the last offset to
|
|
|
|
|
the character just before the command that we are currently executing. */
|
|
|
|
|
void
|
|
|
|
|
me_append_before_this_command ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--);
|
|
|
|
|
maybe_write_itext (input_text, i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Similar to execute_string (), but only takes a single string argument,
|
|
|
|
|
and remembers the input text location, etc. */
|
|
|
|
|
void
|
|
|
|
|
me_execute_string (execution_string)
|
|
|
|
|
char *execution_string;
|
|
|
|
|
{
|
|
|
|
|
pushfile ();
|
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
input_text = execution_string;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
input_filename = xstrdup (input_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
size_of_input_text = strlen (execution_string);
|
|
|
|
|
|
|
|
|
|
remember_itext (execution_string, 0);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
me_executing_string++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
reader_loop ();
|
|
|
|
|
popfile ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
me_executing_string--;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append the text which appears in input_text from the last offset to
|
|
|
|
|
the current OFFSET. */
|
|
|
|
|
void
|
|
|
|
|
append_to_expansion_output (offset)
|
|
|
|
|
int offset;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
ITEXT *itext = (ITEXT *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] && itext_info[i]->pointer == input_text)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!itext)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (offset > itext->offset)
|
|
|
|
|
{
|
|
|
|
|
write_region_to_macro_output
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(input_text, itext->offset, offset);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
remember_itext (input_text, offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only write this input text iff it appears in our itext list. */
|
|
|
|
|
void
|
|
|
|
|
maybe_write_itext (pointer, offset)
|
|
|
|
|
char *pointer;
|
|
|
|
|
int offset;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
ITEXT *itext = (ITEXT *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] && (itext_info[i]->pointer == pointer))
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (itext && (itext->offset < offset))
|
|
|
|
|
{
|
|
|
|
|
write_region_to_macro_output (itext->pointer, itext->offset, offset);
|
|
|
|
|
remember_itext (pointer, offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
write_region_to_macro_output (string, start, end)
|
|
|
|
|
char *string;
|
|
|
|
|
int start, end;
|
|
|
|
|
{
|
|
|
|
|
if (macro_expansion_output_stream)
|
|
|
|
|
fwrite (string + start, 1, end - start, macro_expansion_output_stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_MACROS */
|
|
|
|
|
|
|
|
|
|
/* Return the length of the array in ARRAY. */
|
|
|
|
|
int
|
|
|
|
|
array_len (array)
|
|
|
|
|
char **array;
|
|
|
|
|
{
|
|
|
|
|
register int i = 0;
|
|
|
|
|
|
|
|
|
|
if (array)
|
|
|
|
|
for (i = 0; array[i] != (char *)NULL; i++);
|
|
|
|
|
|
|
|
|
|
return (i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
free_array (array)
|
|
|
|
|
char **array;
|
|
|
|
|
{
|
|
|
|
|
if (array)
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; array[i] != (char *)NULL; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (array[i]);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (array);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Function is used even when we don't have macros. Although, I have
|
|
|
|
|
to admit, it is unlikely that you would have a use for it if you
|
|
|
|
|
aren't using macros. */
|
|
|
|
|
char **
|
|
|
|
|
get_brace_args (quote_single)
|
|
|
|
|
int quote_single;
|
|
|
|
|
{
|
|
|
|
|
char **arglist, *word;
|
|
|
|
|
int arglist_index, arglist_size;
|
|
|
|
|
int character, escape_seen, start;
|
|
|
|
|
int depth = 1;
|
|
|
|
|
|
|
|
|
|
/* There is an arglist in braces here, so gather the args inside of it. */
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
arglist = (char **)NULL;
|
|
|
|
|
arglist_index = arglist_size = 0;
|
|
|
|
|
|
|
|
|
|
get_arg:
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
start = input_text_offset;
|
|
|
|
|
escape_seen = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while ((character = curchar ()))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (character == '\\')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
input_text_offset += 2;
|
|
|
|
|
escape_seen = 1;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (character == '{')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
depth++;
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if ((character == ',' && !quote_single) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
((character == '}') && depth == 1))
|
|
|
|
|
{
|
|
|
|
|
int len = input_text_offset - start;
|
|
|
|
|
|
|
|
|
|
if (len || (character != '}'))
|
|
|
|
|
{
|
|
|
|
|
word = (char *)xmalloc (1 + len);
|
|
|
|
|
strncpy (word, input_text + start, len);
|
|
|
|
|
word[len] = 0;
|
|
|
|
|
|
|
|
|
|
/* Clean up escaped characters. */
|
|
|
|
|
if (escape_seen)
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; word[i]; i++)
|
|
|
|
|
if (word[i] == '\\')
|
|
|
|
|
memmove (word + i, word + i + 1,
|
|
|
|
|
1 + strlen (word + i + 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arglist_index + 2 >= arglist_size)
|
|
|
|
|
arglist = (char **)xrealloc
|
|
|
|
|
(arglist, (arglist_size += 10) * sizeof (char *));
|
|
|
|
|
|
|
|
|
|
arglist[arglist_index++] = word;
|
|
|
|
|
arglist[arglist_index] = (char *)NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
if (character == '}')
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
goto get_arg;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (character == '}')
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
depth--;
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
if (character == '\n') line_number++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (arglist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
/* */
|
|
|
|
|
/* Looking For Include Files */
|
|
|
|
|
/* */
|
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Given a string containing units of information separated by colons,
|
|
|
|
|
return the next one pointed to by INDEX, or NULL if there are no more.
|
|
|
|
|
Advance INDEX to the character after the colon. */
|
|
|
|
|
char *
|
|
|
|
|
extract_colon_unit (string, index)
|
|
|
|
|
char *string;
|
|
|
|
|
int *index;
|
|
|
|
|
{
|
|
|
|
|
int i, start;
|
|
|
|
|
|
|
|
|
|
i = *index;
|
|
|
|
|
|
|
|
|
|
if (!string || (i >= strlen (string)))
|
|
|
|
|
return ((char *)NULL);
|
|
|
|
|
|
|
|
|
|
/* Each call to this routine leaves the index pointing at a colon if
|
|
|
|
|
there is more to the path. If I is > 0, then increment past the
|
|
|
|
|
`:'. If I is 0, then the path has a leading colon. Trailing colons
|
|
|
|
|
are handled OK by the `else' part of the if statement; an empty
|
|
|
|
|
string is returned in that case. */
|
|
|
|
|
if (i && string[i] == ':')
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
start = i;
|
|
|
|
|
|
|
|
|
|
while (string[i] && string[i] != ':') i++;
|
|
|
|
|
|
|
|
|
|
*index = i;
|
|
|
|
|
|
|
|
|
|
if (i == start)
|
|
|
|
|
{
|
|
|
|
|
if (string[i])
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(*index)++;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Return "" in the case of a trailing `:'. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (xstrdup (""));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
|
|
value = (char *)xmalloc (1 + (i - start));
|
|
|
|
|
strncpy (value, &string[start], (i - start));
|
1999-01-14 19:56:32 +00:00
|
|
|
|
value [i - start] = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
return (value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the full pathname for FILENAME by searching along PATH.
|
|
|
|
|
When found, return the stat () info for FILENAME in FINFO.
|
|
|
|
|
If PATH is NULL, only the current directory is searched.
|
|
|
|
|
If the file could not be found, return a NULL pointer. */
|
|
|
|
|
char *
|
|
|
|
|
get_file_info_in_path (filename, path, finfo)
|
|
|
|
|
char *filename, *path;
|
|
|
|
|
struct stat *finfo;
|
|
|
|
|
{
|
|
|
|
|
char *dir;
|
|
|
|
|
int result, index = 0;
|
|
|
|
|
|
|
|
|
|
if (path == (char *)NULL)
|
|
|
|
|
path = ".";
|
|
|
|
|
|
|
|
|
|
/* Handle absolute pathnames. "./foo", "/foo", "../foo". */
|
|
|
|
|
if (*filename == '/' ||
|
|
|
|
|
(*filename == '.' &&
|
|
|
|
|
(filename[1] == '/' ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(filename[1] == '.' && filename[2] == '/')))
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
/* Handle names that look like "d:/foo/bar" */
|
|
|
|
|
|| (isalpha (*filename) && filename [1] == ':'
|
|
|
|
|
&& (filename [2] == '/' || filename [2] == '\\'))
|
|
|
|
|
#endif
|
|
|
|
|
)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (stat (filename, finfo) == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (xstrdup (filename));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return ((char *)NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
while ((dir = extract_colon_unit (path, &index)))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *fullpath;
|
|
|
|
|
|
|
|
|
|
if (!*dir)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
free (dir);
|
|
|
|
|
dir = xstrdup (".");
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
|
|
|
|
|
sprintf (fullpath, "%s/%s", dir, filename);
|
|
|
|
|
free (dir);
|
|
|
|
|
|
|
|
|
|
result = stat (fullpath, finfo);
|
|
|
|
|
|
|
|
|
|
if (result == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (fullpath);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (fullpath);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|