0
|
1 /* Makeinfo -- convert texinfo format files into info files.
|
|
2
|
|
3 Copyright (C) 1987, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
|
|
4 Copyright (C) 1994, 1995 Sun Microsystems.
|
|
5 Copyright (C) 1995 Amdahl Corporation.
|
|
6
|
|
7 This file is part of GNU Info.
|
|
8
|
|
9 Makeinfo is distributed in the hope that it will be useful,
|
|
10 but WITHOUT ANY WARRANTY. No author or distributor accepts
|
|
11 responsibility to anyone for the consequences of using it or for
|
|
12 whether it serves any particular purpose or works at all, unless he
|
|
13 says so in writing. Refer to the GNU Emacs General Public License
|
|
14 for full details.
|
|
15
|
|
16 Everyone is granted permission to copy, modify and redistribute
|
|
17 Makeinfo, but only under the conditions described in the GNU Emacs
|
|
18 General Public License. A copy of this license is supposed to
|
|
19 have been given to you along with GNU Emacs so you can know your
|
|
20 rights and responsibilities. It should be in a file named COPYING.
|
|
21 Among other things, the copyright notice and this notice must be
|
|
22 preserved on all copies. */
|
|
23
|
|
24 /* This is Makeinfo version 1.63. If you change the version number of
|
|
25 Makeinfo, please change it here and at the lines reading:
|
|
26
|
|
27 int major_version = 1;
|
|
28 int minor_version = 63;
|
|
29
|
|
30 in the code below.
|
|
31
|
|
32 Makeinfo is authored by Brian Fox (bfox@ai.mit.edu).
|
|
33 March 1994: additions by Ben Wing (wing@netcom.com).
|
|
34 */
|
|
35
|
|
36 #if defined (HAVE_CONFIG_H)
|
|
37 #include <config.h>
|
|
38 #endif /* HAVE_CONFIG_H */
|
|
39
|
|
40 /* You can change some of the behaviour of Makeinfo by changing the
|
|
41 following defines: */
|
|
42
|
|
43 /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
|
|
44 appear within an @table, @ftable, or @itemize environment to have
|
|
45 standard paragraph indentation. Without this, such paragraphs have
|
|
46 no starting indentation. */
|
|
47 /* #define INDENT_PARAGRAPHS_IN_TABLE */
|
|
48
|
|
49 /* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
|
|
50 that @example should increase indentation by. This incremement is used
|
|
51 for all insertions which indent the enclosed text. */
|
|
52 #define DEFAULT_INDENTATION_INCREMENT 5
|
|
53
|
|
54 /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
|
|
55 the first lines of paragraphs receive by default, where no other
|
|
56 value has been specified. Users can change this value on the command
|
|
57 line, with the --paragraph-indent option, or within the texinfo file,
|
|
58 with the @paragraphindent command. */
|
|
59 #define PARAGRAPH_START_INDENT 3
|
|
60
|
|
61 /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
|
|
62 wish to appear between paragraphs. A value of 1 creates a single blank
|
|
63 line between paragraphs. Paragraphs are defined by 2 or more consecutive
|
|
64 newlines in the input file (i.e., one or more blank lines). */
|
|
65 #define DEFAULT_PARAGRAPH_SPACING 1
|
|
66
|
|
67 /* Define HAVE_MACROS to enable the macro facility of TeXinfo. Using this
|
|
68 facility, users can create their own command procedures with arguments. */
|
|
69 #define HAVE_MACROS
|
|
70
|
|
71 /* **************************************************************** */
|
|
72 /* */
|
|
73 /* Include File Declarations */
|
|
74 /* */
|
|
75 /* **************************************************************** */
|
|
76
|
|
77 /* Indent #pragma so that older Cpp's don't try to parse it. */
|
|
78 #if defined (_AIX)
|
|
79 # pragma alloca
|
|
80 #endif /* _AIX */
|
|
81
|
|
82 #include <stdio.h>
|
|
83 #include <unistd.h>
|
|
84 #include <sys/types.h>
|
|
85 #include <ctype.h>
|
|
86 #include <sys/stat.h>
|
|
87 #include <pwd.h>
|
|
88 #include <errno.h>
|
|
89
|
|
90 #include <stdarg.h>
|
|
91 #include "getopt.h"
|
|
92 #if defined (HAVE_UNISTD_H)
|
|
93 #include <unistd.h>
|
|
94 #endif /* HAVE_UNISTD_H */
|
|
95
|
|
96 #if defined (VMS)
|
|
97 #include <perror.h>
|
|
98 #endif
|
|
99
|
|
100 #include <stdlib.h>
|
|
101
|
|
102 #if defined (HAVE_STRING_H)
|
|
103 #include <string.h>
|
|
104 #define HAVE_STRDUP
|
|
105 #define HAVE_STRCASECMP
|
|
106 #else
|
|
107 #include <strings.h>
|
|
108 #endif /* !HAVE_STRING_H */
|
|
109
|
|
110 #if defined (TM_IN_SYS_TIME)
|
|
111 #include <sys/time.h>
|
|
112 #else
|
|
113 #include <time.h>
|
|
114 #endif /* !TM_IN_SYS_TIME */
|
|
115
|
|
116 #if defined (HAVE_SYS_FCNTL_H)
|
|
117 #include <sys/fcntl.h>
|
|
118 #else
|
|
119 #include <fcntl.h>
|
|
120 #endif /* !HAVE_SYS_FCNTL_H */
|
|
121
|
|
122 #if defined (HAVE_SYS_FILE_H)
|
|
123 #include <sys/file.h>
|
|
124 #endif /* HAVE_SYS_FILE_H */
|
|
125
|
|
126 #if defined (__GNUC__) && !defined (alloca)
|
|
127 #define alloca __builtin_alloca
|
|
128 #else
|
|
129 #if defined(HAVE_ALLOCA_H)
|
|
130 #include <alloca.h>
|
|
131 #else /* !HAVE_ALLOCA_H */
|
|
132 #if !defined (_AIX)
|
|
133 extern char *alloca ();
|
|
134 #endif /* !_AIX */
|
|
135 #endif /* !HAVE_ALLOCA_H */
|
|
136 #endif /* !__GNUC__ */
|
|
137
|
|
138 #ifdef __SUNPRO_C
|
|
139 void *__builtin_alloca (unsigned int);
|
|
140 #endif
|
|
141
|
|
142 static void *xmalloc (unsigned int), *xrealloc (void *, unsigned int);
|
|
143 #if defined (__osf__)
|
|
144 extern void *malloc (), *realloc ();
|
|
145 #endif /* __osf__ */
|
|
146
|
|
147 static char **get_brace_args (int);
|
|
148 static int array_len (char **);
|
|
149 static void free_array (char **);
|
|
150 static void isolate_nodename (char *);
|
|
151
|
|
152 /* Non-zero means that we are currently hacking the insides of an
|
|
153 insertion which would use a fixed width font. */
|
|
154 static int in_fixed_width_font = 0;
|
|
155
|
|
156 /* Non-zero means that start_paragraph () MUST be called before we pay
|
|
157 any attention to close_paragraph () calls. */
|
|
158 int must_start_paragraph = 0;
|
|
159
|
|
160 /* Non-zero means a string is in execution, as opposed to a file. */
|
|
161 static int executing_string = 0;
|
|
162
|
|
163 #if defined (HAVE_MACROS)
|
|
164 /* If non-NULL, this is an output stream to write the full macro expansion
|
|
165 of the input text to. The resultant file is another texinfo file, but
|
|
166 missing @include, @infoinclude, @macro, and macro invocations. Instead,
|
|
167 all of the text is placed within the file. */
|
|
168 FILE *macro_expansion_output_stream = (FILE *)NULL;
|
|
169
|
|
170 /* Here is a structure used to remember input text strings and offsets
|
|
171 within them. */
|
|
172 typedef struct {
|
|
173 char *pointer; /* Pointer to the input text. */
|
|
174 int offset; /* Offset of the last character output. */
|
|
175 } ITEXT;
|
|
176
|
|
177 static ITEXT **itext_info = (ITEXT **)NULL;
|
|
178 static int itext_size = 0;
|
|
179
|
|
180 /* Non-zero means to inhibit the writing of macro expansions to the output
|
|
181 stream. This is used in special cases where the output has already been
|
|
182 written. */
|
|
183 int me_inhibit_expansion = 0;
|
|
184
|
|
185 static ITEXT *remember_itext (char *, int);
|
|
186 static void forget_itext (char *);
|
|
187 static void me_append_before_this_command (void);
|
|
188 static void append_to_expansion_output (int);
|
|
189 static void write_region_to_macro_output (char *, int, int);
|
|
190 static void maybe_write_itext (char *, int);
|
|
191 static void me_execute_string (char *);
|
|
192 #endif /* HAVE_MACROS */
|
|
193
|
|
194 /* Some systems don't declare this function in pwd.h. */
|
|
195 struct passwd *getpwnam ();
|
|
196
|
|
197
|
|
198 /* **************************************************************** */
|
|
199 /* */
|
|
200 /* Global Defines */
|
|
201 /* */
|
|
202 /* **************************************************************** */
|
|
203
|
|
204 /* Error levels */
|
|
205 #define NO_ERROR 0
|
|
206 #define SYNTAX 2
|
|
207 #define FATAL 4
|
|
208
|
|
209 /* C's standard macros don't check to make sure that the characters being
|
|
210 changed are within range. So I have to check explicitly. */
|
|
211
|
|
212 /* GNU Library doesn't have toupper(). Until GNU gets this fixed, I will
|
|
213 have to do it. */
|
|
214 #ifndef toupper
|
|
215 #define toupper(c) ((c) - 32)
|
|
216 #endif
|
|
217
|
|
218 #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
|
|
219 #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
|
|
220
|
|
221 #define control_character_bit 0x40 /* %01000000, must be off. */
|
|
222 #define meta_character_bit 0x080/* %10000000, must be on. */
|
|
223 #define CTL(c) ((c) & (~control_character_bit))
|
|
224 #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
|
|
225 #define META(c) ((c) | (meta_character_bit))
|
|
226 #define UNMETA(c) ((c) & (~meta_character_bit))
|
|
227
|
|
228 #define whitespace(c) (((c) == '\t') || ((c) == ' '))
|
|
229 #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
|
|
230 #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
|
|
231
|
|
232 #ifndef isletter
|
|
233 #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
|
|
234 #endif
|
|
235
|
|
236 #ifndef isupper
|
|
237 #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
|
|
238 #endif
|
|
239
|
|
240 #ifndef isdigit
|
|
241 #define isdigit(c) ((c) >= '0' && (c) <= '9')
|
|
242 #endif
|
|
243
|
|
244 #ifndef digit_value
|
|
245 #define digit_value(c) ((c) - '0')
|
|
246 #endif
|
|
247
|
|
248 #define member(c, s) (strchr (s, c) != NULL)
|
|
249
|
|
250 #define COMMAND_PREFIX '@'
|
|
251
|
|
252 /* Stuff for splitting large files. */
|
|
253 #define SPLIT_SIZE_THRESHOLD 70000 /* What's good enough for Stallman... */
|
|
254 #define DEFAULT_SPLIT_SIZE 50000 /* Is probably good enough for me. */
|
|
255 int splitting = 1; /* Always true for now. */
|
|
256
|
|
257 typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */
|
|
258
|
|
259
|
|
260 /* **************************************************************** */
|
|
261 /* */
|
|
262 /* Global Variables */
|
|
263 /* */
|
|
264 /* **************************************************************** */
|
|
265
|
|
266 /* Global pointer to argv[0]. */
|
|
267 char *progname;
|
|
268
|
|
269 /* The current input file state. */
|
|
270 char *input_filename;
|
|
271 char *input_text;
|
|
272 int size_of_input_text;
|
|
273 int input_text_offset;
|
|
274 int line_number;
|
|
275
|
|
276 #define curchar() input_text[input_text_offset]
|
|
277
|
|
278 #define command_char(c) ((!whitespace(c)) && \
|
|
279 ((c) != '\n') && \
|
|
280 ((c) != '{') && \
|
|
281 ((c) != '}') && \
|
|
282 ((c) != '='))
|
|
283
|
|
284 #define skip_whitespace() while (input_text_offset != size_of_input_text \
|
|
285 && whitespace(curchar()))\
|
|
286 input_text_offset++
|
|
287
|
|
288 #define skip_whitespace_and_newlines() \
|
|
289 do { \
|
|
290 while (input_text_offset != size_of_input_text \
|
|
291 && (whitespace (curchar ()) \
|
|
292 || (curchar () == '\n'))) \
|
|
293 { \
|
|
294 if (curchar () == '\n') \
|
|
295 line_number++; \
|
|
296 input_text_offset++; \
|
|
297 } \
|
|
298 } while (0)
|
|
299
|
|
300 /* Return non-zero if STRING is the text at input_text + input_text_offset,
|
|
301 else zero. */
|
|
302 #define looking_at(string) \
|
|
303 (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
|
|
304
|
|
305 /* And writing to the output. */
|
|
306
|
|
307 /* The output file name. */
|
|
308 char *output_filename = (char *)NULL;
|
|
309 char *pretty_output_filename;
|
|
310
|
|
311 /* Name of the output file that the user elected to pass on the command line.
|
|
312 Such a name overrides any name found with the @setfilename command. */
|
|
313 char *command_output_filename = (char *)NULL;
|
|
314
|
|
315 /* A colon separated list of directories to search for files included
|
|
316 with @include. This can be controlled with the `-I' option to makeinfo. */
|
|
317 char *include_files_path = (char *)NULL;
|
|
318
|
|
319 /* Current output stream. */
|
|
320 FILE *output_stream;
|
|
321
|
|
322 /* Position in the output file. */
|
|
323 int output_position;
|
|
324
|
|
325 /* Output paragraph buffer. */
|
|
326 unsigned char *output_paragraph;
|
|
327
|
|
328 /* Offset into OUTPUT_PARAGRAPH. */
|
|
329 int output_paragraph_offset;
|
|
330
|
|
331 /* The output paragraph "cursor" horizontal position. */
|
|
332 int output_column = 0;
|
|
333
|
|
334 /* Non-zero means output_paragraph contains text. */
|
|
335 int paragraph_is_open = 0;
|
|
336
|
|
337 #define INITIAL_PARAGRAPH_SPACE 5000
|
|
338 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
|
|
339
|
|
340 /* Filling.. */
|
|
341 /* Non-zero indicates that filling will take place on long lines. */
|
|
342 int filling_enabled = 1;
|
|
343
|
|
344 /* Non-zero means that words are not to be split, even in long lines. This
|
|
345 gets changed for cm_w (). */
|
|
346 int non_splitting_words = 0;
|
|
347
|
|
348 /* Non-zero indicates that filling a line also indents the new line. */
|
|
349 int indented_fill = 0;
|
|
350
|
|
351 /* The column at which long lines are broken. */
|
|
352 int fill_column = 72;
|
|
353
|
|
354 /* The amount of indentation to apply at the start of each line. */
|
|
355 int current_indent = 0;
|
|
356
|
|
357 /* The amount of indentation to add at the starts of paragraphs.
|
|
358 0 means don't change existing indentation at paragraph starts.
|
|
359 > 0 is amount to indent new paragraphs by.
|
|
360 < 0 means indent to column zero by removing indentation if necessary.
|
|
361
|
|
362 This is normally zero, but some people prefer paragraph starts to be
|
|
363 somewhat more indented than paragraph bodies. A pretty value for
|
|
364 this is 3. */
|
|
365 int paragraph_start_indent = PARAGRAPH_START_INDENT;
|
|
366
|
|
367 /* Non-zero means that the use of paragraph_start_indent is inhibited.
|
|
368 @example uses this to line up the left columns of the example text.
|
|
369 A negative value for this variable is incremented each time it is used.
|
|
370 @noindent uses this to inhibit indentation for a single paragraph. */
|
|
371 int inhibit_paragraph_indentation = 0;
|
|
372
|
|
373 /* Indentation that is pending insertion. We have this for hacking lines
|
|
374 which look blank, but contain whitespace. We want to treat those as
|
|
375 blank lines. */
|
|
376 int pending_indent = 0;
|
|
377
|
|
378 /* The amount that indentation increases/decreases by. */
|
|
379 int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
|
|
380
|
|
381 /* Non-zero indicates that indentation is temporarily turned off. */
|
|
382 int no_indent = 1;
|
|
383
|
|
384 /* Non-zero means forcing output text to be flushright. */
|
|
385 int force_flush_right = 0;
|
|
386
|
|
387 /* Non-zero means that the footnote style for this document was set on
|
|
388 the command line, which overrides any other settings. */
|
|
389 int footnote_style_preset = 0;
|
|
390
|
|
391 /* Non-zero means that we automatically number footnotes that have no
|
|
392 specified marker. */
|
|
393 int number_footnotes = 1;
|
|
394
|
|
395 /* The current footnote number in this node. Each time a new node is
|
|
396 started this is reset to 1. */
|
|
397 int current_footnote_number = 1;
|
|
398
|
|
399 /* Command name in the process of being hacked. */
|
|
400 char *command;
|
|
401
|
|
402 /* The index in our internal command table of the currently
|
|
403 executing command. */
|
|
404 int command_index;
|
|
405
|
|
406 /* A search string which is used to find a line defining a node. */
|
|
407 char node_search_string[] =
|
|
408 { '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', '\0' };
|
|
409
|
|
410 /* A search string which is used to find a line defining a menu. */
|
|
411 char menu_search_string[] =
|
|
412 { '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', '\0' };
|
|
413
|
|
414 /* A search string which is used to find the first @setfilename. */
|
|
415 char setfilename_search[] =
|
|
416 { COMMAND_PREFIX,
|
|
417 's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '\0' };
|
|
418
|
|
419 /* A stack of file information records. If a new file is read in with
|
|
420 "@input", we remember the old input file state on this stack. */
|
|
421 typedef struct fstack
|
|
422 {
|
|
423 struct fstack *next;
|
|
424 char *filename;
|
|
425 char *text;
|
|
426 int size;
|
|
427 int offset;
|
|
428 int line_number;
|
|
429 } FSTACK;
|
|
430
|
|
431 FSTACK *filestack = (FSTACK *) NULL;
|
|
432
|
|
433 /* Stuff for nodes. */
|
|
434 /* The current nodes node name. */
|
|
435 char *current_node = (char *)NULL;
|
|
436
|
|
437 /* The current nodes section level. */
|
|
438 int current_section = 0;
|
|
439
|
|
440 /* The filename of the current input file. This is never freed. */
|
|
441 char *node_filename = (char *)NULL;
|
|
442
|
|
443 /* What we remember for each node. */
|
|
444 typedef struct tentry
|
|
445 {
|
|
446 struct tentry *next_ent;
|
|
447 char *node; /* name of this node. */
|
|
448 char *prev; /* name of "Prev:" for this node. */
|
|
449 char *next; /* name of "Next:" for this node. */
|
|
450 char *up; /* name of "Up:" for this node. */
|
|
451 int position; /* output file position of this node. */
|
|
452 int line_no; /* defining line in source file. */
|
|
453 char *filename; /* The file that this node was found in. */
|
|
454 int touched; /* non-zero means this node has been referenced. */
|
|
455 int flags; /* Room for growth. Right now, contains 1 bit. */
|
|
456 } TAG_ENTRY;
|
|
457
|
|
458 /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
|
|
459 we turn on this flag bit in node-b's tag entry. This means that when
|
|
460 it is time to validate node-b, we don't report an additional error
|
|
461 if there was no "Prev" field. */
|
|
462 #define PREV_ERROR 0x1
|
|
463 #define NEXT_ERROR 0x2
|
|
464 #define UP_ERROR 0x4
|
|
465 #define NO_WARN 0x8
|
|
466 #define IS_TOP 0x10
|
|
467
|
|
468 TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
|
|
469
|
|
470 #if defined (HAVE_MACROS)
|
|
471 #define ME_RECURSE 0x01
|
|
472 #define ME_QUOTE_ARG 0x02
|
|
473
|
|
474 /* Macro definitions for user-defined commands. */
|
|
475 typedef struct {
|
|
476 char *name; /* Name of the macro. */
|
|
477 char **arglist; /* Args to replace when executing. */
|
|
478 char *body; /* Macro body. */
|
|
479 char *source_file; /* File where this macro is defined. */
|
|
480 int source_lineno; /* Line number within FILENAME. */
|
|
481 int inhibited; /* Non-zero means make find_macro () fail. */
|
|
482 int flags; /* ME_RECURSE, ME_QUOTE_ARG, etc. */
|
|
483 } MACRO_DEF;
|
|
484
|
|
485 static void add_macro (char *name, char **arglist, char *body,
|
|
486 char *source_file, int source_lineno, int flags);
|
|
487 static void execute_macro (MACRO_DEF *);
|
|
488 static MACRO_DEF *find_macro (char *), *delete_macro (char *);
|
|
489 #endif /* HAVE_MACROS */
|
|
490
|
|
491 /* Menu reference, *note reference, and validation hacking. */
|
|
492
|
|
493 /* The various references that we know about. */
|
|
494 enum reftype
|
|
495 {
|
|
496 menu_reference, followed_reference
|
|
497 };
|
|
498
|
|
499 /* A structure to remember references with. A reference to a node is
|
|
500 either an entry in a menu, or a cross-reference made with [px]ref. */
|
|
501 typedef struct node_ref
|
|
502 {
|
|
503 struct node_ref *next;
|
|
504 char *node; /* Name of node referred to. */
|
|
505 char *containing_node; /* Name of node containing this reference. */
|
|
506 int line_no; /* Line number where the reference occurs. */
|
|
507 int section; /* Section level where the reference occurs. */
|
|
508 char *filename; /* Name of file where the reference occurs. */
|
|
509 enum reftype type; /* Type of reference, either menu or note. */
|
|
510 } NODE_REF;
|
|
511
|
|
512 /* The linked list of such structures. */
|
|
513 NODE_REF *node_references = (NODE_REF *) NULL;
|
|
514
|
|
515 /* Flag which tells us whether to examine menu lines or not. */
|
|
516 int in_menu = 0;
|
|
517
|
|
518 /* Non-zero means that we have seen "@top" once already. */
|
|
519 int top_node_seen = 0;
|
|
520
|
|
521 /* Non-zero means that we have seen a non-"@top" node already. */
|
|
522 int non_top_node_seen = 0;
|
|
523
|
|
524 /* Flags controlling the operation of the program. */
|
|
525
|
|
526 /* Default is to notify users of bad choices. */
|
|
527 int print_warnings = 1;
|
|
528
|
|
529 /* Default is to check node references. */
|
|
530 int validating = 1;
|
|
531
|
|
532 /* Non-zero means do not output "Node: Foo" for node separations. */
|
|
533 int no_headers = 0;
|
|
534
|
|
535 /* Number of errors that we tolerate on a given fileset. */
|
|
536 int max_error_level = 100;
|
|
537
|
|
538 /* Maximum number of references to a single node before complaining. */
|
|
539 int reference_warning_limit = 1000;
|
|
540
|
|
541 /* Non-zero means print out information about what is going on when it
|
|
542 is going on. */
|
|
543 int verbose_mode = 0;
|
|
544
|
|
545 /* Non-zero means to be relaxed about the input file. This is useful when
|
|
546 we can successfully format the input, but it doesn't strictly match our
|
|
547 somewhat pedantic ideas of correctness. Right now, it affects what
|
|
548 @table and @itemize do without arguments. */
|
|
549 int allow_lax_format = 0;
|
|
550
|
|
551 /* Count of @ifinfo commands seen. @ifinfo is handled specially
|
|
552 to allow non-hierarchical contstructions like
|
|
553
|
|
554 @ifinfo
|
|
555 @table foo
|
|
556 @end ifinfo
|
|
557 */
|
|
558 int ifinfo_count = 0;
|
|
559
|
|
560 /* The list of commands that we hack in texinfo. Each one
|
|
561 has an associated function. When the command is encountered in the
|
|
562 text, the associated function is called with START as the argument.
|
|
563 If the function expects arguments in braces, it remembers itself on
|
|
564 the stack. When the corresponding close brace is encountered, the
|
|
565 function is called with END as the argument. */
|
|
566
|
|
567 #define START 0
|
|
568 #define END 1
|
|
569
|
|
570 typedef struct brace_element
|
|
571 {
|
|
572 struct brace_element *next;
|
|
573 COMMAND_FUNCTION *proc;
|
|
574 int pos, line;
|
|
575 } BRACE_ELEMENT;
|
|
576
|
|
577 BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
|
|
578
|
|
579 /* Forward declarations. */
|
|
580 #if !defined (HAVE_STRDUP)
|
|
581 extern char *strdup ();
|
|
582 #endif /* HAVE_STRDUP */
|
|
583
|
|
584 static void convert_from_stream (FILE *, char *);
|
|
585 static void convert_from_file (char *);
|
|
586 static void top_defindex (char *, int);
|
|
587
|
|
588 static void insert_self (), cm_ignore_line (void);
|
|
589
|
|
590 static void
|
|
591 cm_asterisk (void), cm_dots (int), cm_bullet (int), cm_TeX (int),
|
|
592 cm_copyright (int), cm_code (int), cm_samp (int), cm_file (int),
|
|
593 cm_kbd (int), cm_key (int), cm_ctrl (int, int, int), cm_var (int, int, int),
|
|
594 cm_dfn (int, int), cm_emph (int), cm_strong (int, int), cm_cite (int, int),
|
|
595 cm_italic (int, int, int), cm_bold (int, int, int), cm_roman (int, int, int),
|
|
596 cm_title (int, int, int), cm_w (int, int, int), cm_refill (void),
|
|
597 cm_titlefont (int, int, int);
|
|
598
|
|
599 static void
|
|
600 cm_chapter (void), cm_unnumbered (void), cm_appendix (void), cm_top (void),
|
|
601 cm_section (void), cm_unnumberedsec (void), cm_appendixsec (void),
|
|
602 cm_subsection (void), cm_unnumberedsubsec (void), cm_appendixsubsec (void),
|
|
603 cm_subsubsection (void), cm_unnumberedsubsubsec (void),
|
|
604 cm_appendixsubsubsec (void), cm_heading (void), cm_chapheading (void),
|
|
605 cm_subheading (void), cm_subsubheading (void), cm_majorheading (void),
|
|
606 cm_raisesections (void), cm_lowersections (void);
|
|
607
|
|
608 /* All @defxxx commands map to cm_defun (). */
|
|
609 static void cm_defun (void);
|
|
610
|
|
611 static void
|
|
612 cm_node (void), cm_menu (void), cm_xref (int), cm_ftable (void),
|
|
613 cm_vtable (void), cm_pxref (int), cm_inforef (int), cm_quotation (void),
|
|
614 cm_display (void), cm_itemize (void), cm_enumerate (void), cm_table (void),
|
|
615 cm_itemx (void), cm_noindent (void), cm_setfilename (void), cm_br (void),
|
|
616 cm_sp (void), cm_group (void), cm_center (void), cm_include (void),
|
|
617 cm_bye (void), cm_item (void), cm_end (void), cm_infoinclude (void),
|
|
618 cm_ifinfo (void), cm_kindex (void), cm_cindex (void), cm_findex (void),
|
|
619 cm_pindex (void), cm_vindex (void), cm_tindex (void), cm_asis (void),
|
|
620 cm_synindex (void), cm_printindex (void), cm_minus (int),
|
|
621 cm_footnote (void), cm_force_abbreviated_whitespace (void),
|
|
622 cm_example (void), cm_smallexample (void), cm_lisp (void), cm_format (void),
|
|
623 cm_exdent (void), cm_defindex (void), cm_defcodeindex (void),
|
|
624 cm_sc (int, int, int), cm_result (int), cm_expansion (int), cm_equiv (int),
|
|
625 cm_print (int), cm_error (int), cm_point (int), cm_today (int),
|
|
626 cm_flushleft (void), cm_flushright (void), cm_smalllisp (void),
|
|
627 cm_math (void), cm_cartouche (void), cm_ignore_sentence_ender (void);
|
|
628
|
|
629 /* Conditionals. */
|
|
630 static void cm_set (void), cm_clear (void), cm_ifset (void), cm_ifclear (void);
|
|
631 static void cm_value (int, int, int), cm_ifeq (void);
|
|
632
|
|
633 #if defined (HAVE_MACROS)
|
|
634 /* Define a user-defined command which is simple substitution. */
|
|
635 static void cm_macro (void), cm_unmacro (void);
|
|
636 #endif /* HAVE_MACROS */
|
|
637
|
|
638 /* Options. */
|
|
639 static void cm_paragraphindent (void), cm_footnotestyle (void);
|
|
640
|
|
641 /* Internals. */
|
|
642 static void do_nothing (void);
|
|
643 static void command_name_condition (void);
|
|
644 static void insert_self (void);
|
|
645 static void misplaced_brace (void);
|
|
646 static void cm_obsolete (int arg, int start, int end);
|
|
647
|
|
648 static int error (char *, ...);
|
|
649 static int line_error (char *, ...);
|
|
650 static int warning (char *, ...);
|
|
651 static void add_word_args (char *, ...);
|
|
652 static void execute_string (char *, ...);
|
|
653
|
|
654 static void print_version_info (void);
|
|
655 static void memory_error (char *callers_name, int bytes_wanted);
|
|
656 static void usage (FILE *stream, int exit_value);
|
|
657 static void push_node_filename (void);
|
|
658 static void pop_node_filename (void);
|
|
659 static void remember_error (void);
|
|
660 static void init_internals (void);
|
|
661 static void init_paragraph (void);
|
|
662 static void reader_loop (void);
|
|
663 static void read_command (void);
|
|
664 static void init_brace_stack (void);
|
|
665 static void remember_brace (COMMAND_FUNCTION *proc);
|
|
666 static void remember_brace_1 (COMMAND_FUNCTION *proc, int position);
|
|
667 static void discard_braces (void);
|
|
668 static void add_word (char *string);
|
|
669 static void add_char (int character);
|
|
670 static void insert (int character);
|
|
671 static void flush_output (void);
|
|
672 static void close_paragraph_with_lines (int lines);
|
|
673 static void close_paragraph (void);
|
|
674 static void ignore_blank_line (void);
|
|
675 static void do_flush_right_indentation (void);
|
|
676 static void start_paragraph (void);
|
|
677 static void indent (int amount);
|
|
678 static void init_insertion_stack (void);
|
|
679 static void init_tag_table (void);
|
|
680 static void write_tag_table (void);
|
|
681 static void write_tag_table_internal (int indirect_p);
|
|
682 static void normalize_node_name (char *string);
|
|
683 static void validate_file (TAG_ENTRY *tag_tayble);
|
|
684 static void split_file (char *filename, int size);
|
|
685 static void validate_other_references (register NODE_REF *ref_list);
|
|
686 static NODE_REF *find_node_reference (char *node, register NODE_REF *ref_list);
|
|
687 static void do_enumeration (int type, char *default_string);
|
|
688 static void handle_variable (int action);
|
|
689 static void handle_variable_internal (int action, char *name);
|
|
690 static void init_indices (void);
|
|
691 static void undefindex (char *name);
|
|
692 static void defindex (char *name, int code);
|
|
693 static void gen_defindex (int code);
|
|
694 static void define_user_command (char *name, COMMAND_FUNCTION *proc,
|
|
695 int needs_braces_p);
|
|
696 static void convert_from_loaded_file (char *name);
|
|
697 static void free_pending_notes (void);
|
|
698 static void output_pending_notes (void);
|
|
699 static void free_node_references (void);
|
|
700 static int set_paragraph_indent (char *string);
|
|
701 static int set_footnote_style (char *string);
|
|
702 static int self_delimiting (int character);
|
|
703 static int search_forward (char *string, int from);
|
|
704 static int validate (char *tag, int line, char *label);
|
|
705 static void pop_and_call_brace (void);
|
|
706 static char *expand_filename (char *filename, char *input_name);
|
|
707 static char *filename_part (char *filename);
|
|
708 static char *full_pathname (char *filename);
|
|
709 static char *get_file_info_in_path (char *filename, char *path,
|
|
710 struct stat *finfo);
|
|
711 static char *glean_node_from_menu (int remember_reference);
|
|
712
|
|
713 typedef struct
|
|
714 {
|
|
715 char *name;
|
|
716 COMMAND_FUNCTION *proc;
|
|
717 int argument_in_braces;
|
|
718 } COMMAND;
|
|
719
|
|
720 /* Stuff for defining commands on the fly. */
|
|
721 COMMAND **user_command_array = (COMMAND **) NULL;
|
|
722 int user_command_array_len = 0;
|
|
723
|
|
724 #define NO_BRACE_ARGS 0
|
|
725 #define BRACE_ARGS 1
|
|
726
|
|
727 static COMMAND CommandTable[] = {
|
|
728 { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
|
|
729 { "'", insert_self, NO_BRACE_ARGS },
|
|
730 { "*", cm_asterisk, NO_BRACE_ARGS },
|
|
731 { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
|
|
732 { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
|
|
733 { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
|
|
734 { "|", do_nothing, NO_BRACE_ARGS },
|
|
735 { "@", insert_self, NO_BRACE_ARGS },
|
|
736 { " ", insert_self, NO_BRACE_ARGS },
|
|
737 { "\n", insert_self, NO_BRACE_ARGS },
|
|
738 { "TeX", cm_TeX, BRACE_ARGS },
|
|
739 { "`", insert_self, NO_BRACE_ARGS },
|
|
740 { "appendix", cm_appendix, NO_BRACE_ARGS },
|
|
741 { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
|
|
742 { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
|
|
743 { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
|
|
744 { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
|
|
745 { "asis", cm_asis, BRACE_ARGS },
|
|
746 { "b", cm_bold, BRACE_ARGS },
|
|
747 { "br", cm_br, NO_BRACE_ARGS },
|
|
748 { "bullet", cm_bullet, BRACE_ARGS },
|
|
749 { "bye", cm_bye, NO_BRACE_ARGS },
|
|
750 { "c", cm_ignore_line, NO_BRACE_ARGS },
|
|
751 { "cartouche", cm_cartouche, NO_BRACE_ARGS },
|
|
752 { "center", cm_center, NO_BRACE_ARGS },
|
|
753 { "chapheading", cm_chapheading, NO_BRACE_ARGS },
|
|
754 { "chapter", cm_chapter, NO_BRACE_ARGS },
|
|
755 { "cindex", cm_cindex, NO_BRACE_ARGS },
|
|
756 { "cite", cm_cite, BRACE_ARGS },
|
|
757 { "clear", cm_clear, NO_BRACE_ARGS },
|
|
758 { "code", cm_code, BRACE_ARGS },
|
|
759 { "comment", cm_ignore_line, NO_BRACE_ARGS },
|
|
760 { "contents", do_nothing, NO_BRACE_ARGS },
|
|
761 { "copyright", cm_copyright, BRACE_ARGS },
|
|
762 { "ctrl", cm_ctrl, BRACE_ARGS },
|
|
763 { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
|
|
764 { "defindex", cm_defindex, NO_BRACE_ARGS },
|
|
765 { "dfn", cm_dfn, BRACE_ARGS },
|
|
766
|
|
767 /* The `def' commands. */
|
|
768 { "deffn", cm_defun, NO_BRACE_ARGS },
|
|
769 { "deffnx", cm_defun, NO_BRACE_ARGS },
|
|
770 { "defun", cm_defun, NO_BRACE_ARGS },
|
|
771 { "defunx", cm_defun, NO_BRACE_ARGS },
|
|
772 { "defmac", cm_defun, NO_BRACE_ARGS },
|
|
773 { "defmacx", cm_defun, NO_BRACE_ARGS },
|
|
774 { "defspec", cm_defun, NO_BRACE_ARGS },
|
|
775 { "defspecx", cm_defun, NO_BRACE_ARGS },
|
|
776 { "defvr", cm_defun, NO_BRACE_ARGS },
|
|
777 { "defvrx", cm_defun, NO_BRACE_ARGS },
|
|
778 { "defvar", cm_defun, NO_BRACE_ARGS },
|
|
779 { "defvarx", cm_defun, NO_BRACE_ARGS },
|
|
780 { "defopt", cm_defun, NO_BRACE_ARGS },
|
|
781 { "defoptx", cm_defun, NO_BRACE_ARGS },
|
|
782 { "deftypefn", cm_defun, NO_BRACE_ARGS },
|
|
783 { "deftypefnx", cm_defun, NO_BRACE_ARGS },
|
|
784 { "deftypefun", cm_defun, NO_BRACE_ARGS },
|
|
785 { "deftypefunx", cm_defun, NO_BRACE_ARGS },
|
|
786 { "deftypevr", cm_defun, NO_BRACE_ARGS },
|
|
787 { "deftypevrx", cm_defun, NO_BRACE_ARGS },
|
|
788 { "deftypevar", cm_defun, NO_BRACE_ARGS },
|
|
789 { "deftypevarx", cm_defun, NO_BRACE_ARGS },
|
|
790 { "defcv", cm_defun, NO_BRACE_ARGS },
|
|
791 { "defcvx", cm_defun, NO_BRACE_ARGS },
|
|
792 { "defivar", cm_defun, NO_BRACE_ARGS },
|
|
793 { "defivarx", cm_defun, NO_BRACE_ARGS },
|
|
794 { "defop", cm_defun, NO_BRACE_ARGS },
|
|
795 { "defopx", cm_defun, NO_BRACE_ARGS },
|
|
796 { "defmethod", cm_defun, NO_BRACE_ARGS },
|
|
797 { "defmethodx", cm_defun, NO_BRACE_ARGS },
|
|
798 { "deftypemethod", cm_defun, NO_BRACE_ARGS },
|
|
799 { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
|
|
800 { "deftp", cm_defun, NO_BRACE_ARGS },
|
|
801 { "deftpx", cm_defun, NO_BRACE_ARGS },
|
|
802 /* The end of the `def' commands. */
|
|
803
|
|
804 { "display", cm_display, NO_BRACE_ARGS },
|
|
805 { "dots", cm_dots, BRACE_ARGS },
|
|
806 { "dmn", do_nothing, BRACE_ARGS },
|
|
807 { "emph", cm_emph, BRACE_ARGS },
|
|
808 { "end", cm_end, NO_BRACE_ARGS },
|
|
809 { "enumerate", cm_enumerate, NO_BRACE_ARGS },
|
|
810 { "equiv", cm_equiv, BRACE_ARGS },
|
|
811 { "error", cm_error, BRACE_ARGS },
|
|
812 { "example", cm_example, NO_BRACE_ARGS },
|
|
813 { "exdent", cm_exdent, NO_BRACE_ARGS },
|
|
814 { "expansion", cm_expansion, BRACE_ARGS },
|
|
815 { "file", cm_file, BRACE_ARGS },
|
|
816 { "findex", cm_findex, NO_BRACE_ARGS },
|
|
817 { "finalout", do_nothing, NO_BRACE_ARGS },
|
|
818 { "flushleft", cm_flushleft, NO_BRACE_ARGS },
|
|
819 { "flushright", cm_flushright, NO_BRACE_ARGS },
|
|
820 { "format", cm_format, NO_BRACE_ARGS },
|
|
821 { "ftable", cm_ftable, NO_BRACE_ARGS },
|
|
822 { "group", cm_group, NO_BRACE_ARGS },
|
|
823 { "heading", cm_heading, NO_BRACE_ARGS },
|
|
824 { "headings", cm_ignore_line, NO_BRACE_ARGS },
|
|
825 { "i", cm_italic, BRACE_ARGS },
|
|
826 { "iappendix", cm_appendix, NO_BRACE_ARGS },
|
|
827 { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
|
|
828 { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
|
|
829 { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
|
|
830 { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
|
|
831 { "ichapter", cm_chapter, NO_BRACE_ARGS },
|
|
832 { "ifclear", cm_ifclear, NO_BRACE_ARGS },
|
|
833 { "ifeq", cm_ifeq, NO_BRACE_ARGS },
|
|
834 { "ifhtml", command_name_condition, NO_BRACE_ARGS },
|
|
835 { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
|
|
836 { "ifset", cm_ifset, NO_BRACE_ARGS },
|
|
837 { "iftex", command_name_condition, NO_BRACE_ARGS },
|
|
838 { "ignore", command_name_condition, NO_BRACE_ARGS },
|
|
839 { "include", cm_include, NO_BRACE_ARGS },
|
|
840 { "inforef", cm_inforef, BRACE_ARGS },
|
|
841 { "input", cm_include, NO_BRACE_ARGS },
|
|
842 { "isection", cm_section, NO_BRACE_ARGS },
|
|
843 { "isubsection", cm_subsection, NO_BRACE_ARGS },
|
|
844 { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
|
|
845 { "item", cm_item, NO_BRACE_ARGS },
|
|
846 { "itemize", cm_itemize, NO_BRACE_ARGS },
|
|
847 { "itemx", cm_itemx, NO_BRACE_ARGS },
|
|
848 { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
|
|
849 { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
|
|
850 { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
|
|
851 { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
|
|
852 { "kbd", cm_kbd, BRACE_ARGS },
|
|
853 { "key", cm_key, BRACE_ARGS },
|
|
854 { "kindex", cm_kindex, NO_BRACE_ARGS },
|
|
855 { "lowersections", cm_lowersections, NO_BRACE_ARGS },
|
|
856 { "lisp", cm_lisp, NO_BRACE_ARGS },
|
|
857 #if defined (HAVE_MACROS)
|
|
858 { "macro", cm_macro, NO_BRACE_ARGS },
|
|
859 #endif
|
|
860 { "majorheading", cm_majorheading, NO_BRACE_ARGS },
|
|
861 { "math", cm_math, BRACE_ARGS },
|
|
862 { "medbreak", cm_br, NO_BRACE_ARGS },
|
|
863 { "menu", cm_menu, NO_BRACE_ARGS },
|
|
864 { "minus", cm_minus, BRACE_ARGS },
|
|
865 { "need", cm_ignore_line, NO_BRACE_ARGS },
|
|
866 { "node", cm_node, NO_BRACE_ARGS },
|
|
867 { "noindent", cm_noindent, NO_BRACE_ARGS },
|
|
868 { "nwnode", cm_node, NO_BRACE_ARGS },
|
|
869 { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
|
|
870 { "page", do_nothing, NO_BRACE_ARGS },
|
|
871 { "pindex", cm_pindex, NO_BRACE_ARGS },
|
|
872 { "point", cm_point, BRACE_ARGS },
|
|
873 { "print", cm_print, BRACE_ARGS },
|
|
874 { "printindex", cm_printindex, NO_BRACE_ARGS },
|
|
875 { "pxref", cm_pxref, BRACE_ARGS },
|
|
876 { "quotation", cm_quotation, NO_BRACE_ARGS },
|
|
877 { "r", cm_roman, BRACE_ARGS },
|
|
878 { "raisesections", cm_raisesections, NO_BRACE_ARGS },
|
|
879 { "ref", cm_xref, BRACE_ARGS },
|
|
880 { "refill", cm_refill, NO_BRACE_ARGS },
|
|
881 { "result", cm_result, BRACE_ARGS },
|
|
882 { "samp", cm_samp, BRACE_ARGS },
|
|
883 { "sc", cm_sc, BRACE_ARGS },
|
|
884 { "section", cm_section, NO_BRACE_ARGS },
|
|
885 { "set", cm_set, NO_BRACE_ARGS },
|
|
886 { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
|
|
887 { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
|
|
888 { "setfilename", cm_setfilename, NO_BRACE_ARGS },
|
|
889 { "settitle", cm_ignore_line, NO_BRACE_ARGS },
|
|
890 { "shortcontents", do_nothing, NO_BRACE_ARGS },
|
|
891 { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
|
|
892 { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
|
|
893 { "smallbreak", cm_br, NO_BRACE_ARGS },
|
|
894 { "smallexample", cm_smallexample, NO_BRACE_ARGS },
|
|
895 { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
|
|
896 { "sp", cm_sp, NO_BRACE_ARGS },
|
|
897 { "strong", cm_strong, BRACE_ARGS },
|
|
898 { "subheading", cm_subheading, NO_BRACE_ARGS },
|
|
899 { "subsection", cm_subsection, NO_BRACE_ARGS },
|
|
900 { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
|
|
901 { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
|
|
902 { "summarycontents", do_nothing, NO_BRACE_ARGS },
|
|
903 { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
|
|
904 { "synindex", cm_synindex, NO_BRACE_ARGS },
|
|
905 { "t", cm_title, BRACE_ARGS },
|
|
906 { "table", cm_table, NO_BRACE_ARGS },
|
|
907 { "tex", command_name_condition, NO_BRACE_ARGS },
|
|
908 { "tindex", cm_tindex, NO_BRACE_ARGS },
|
|
909 { "titlefont", cm_titlefont, BRACE_ARGS },
|
|
910 { "titlepage", command_name_condition, NO_BRACE_ARGS },
|
|
911 { "titlespec", command_name_condition, NO_BRACE_ARGS },
|
|
912 { "today", cm_today, BRACE_ARGS },
|
|
913 { "top", cm_top, NO_BRACE_ARGS },
|
|
914 #if defined (HAVE_MACROS)
|
|
915 { "unmacro", cm_unmacro, NO_BRACE_ARGS },
|
|
916 #endif
|
|
917 { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
|
|
918 { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
|
|
919 { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
|
|
920 { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
|
|
921 { "value", cm_value, BRACE_ARGS },
|
|
922 { "var", cm_var, BRACE_ARGS },
|
|
923 { "vindex", cm_vindex, NO_BRACE_ARGS },
|
|
924 { "vtable", cm_vtable, NO_BRACE_ARGS },
|
|
925 { "w", cm_w, BRACE_ARGS },
|
|
926 { "xref", cm_xref, BRACE_ARGS },
|
|
927 { "{", insert_self, NO_BRACE_ARGS },
|
|
928 { "}", insert_self, NO_BRACE_ARGS },
|
|
929
|
|
930 /* Some obsoleted commands. */
|
|
931 { "infotop", cm_obsolete, NO_BRACE_ARGS },
|
|
932 { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
|
|
933 { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
|
|
934 { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
|
|
935 { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
|
|
936 { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
|
|
937 { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
|
|
938 { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
|
|
939 { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
|
|
940 { "infochapter", cm_obsolete, NO_BRACE_ARGS },
|
|
941 { "infosection", cm_obsolete, NO_BRACE_ARGS },
|
|
942 { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
|
|
943 { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
|
|
944
|
|
945 /* Now @include does what this was supposed to. */
|
|
946 { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
|
|
947 { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
|
|
948 { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
|
|
949 { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
|
|
950
|
|
951 {(char *) NULL, (COMMAND_FUNCTION *) NULL, NO_BRACE_ARGS}};
|
|
952
|
|
953 int major_version = 1;
|
|
954 int minor_version = 63;
|
|
955
|
|
956 struct option long_options[] =
|
|
957 {
|
|
958 { "error-limit", 1, 0, 'e' }, /* formerly -el */
|
|
959 { "fill-column", 1, 0, 'f' }, /* formerly -fc */
|
|
960 { "footnote-style", 1, 0, 's' }, /* formerly -ft */
|
|
961 { "no-headers", 0, &no_headers, 1 }, /* Do not output Node: foo */
|
|
962 { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
|
|
963 { "no-validate", 0, &validating, 0 }, /* formerly -nv */
|
|
964 { "no-split", 0, &splitting, 0 }, /* formerly -ns */
|
|
965 { "no-warn", 0, &print_warnings, 0 }, /* formerly -nw */
|
|
966 #if defined (HAVE_MACROS)
|
|
967 { "macro-expand", 1, 0, 'E' },
|
|
968 #endif /* HAVE_MACROS */
|
|
969 { "number-footnotes", 0, &number_footnotes, 1 },
|
|
970 { "no-number-footnotes", 0, &number_footnotes, 0 },
|
|
971 { "output", 1, 0, 'o' },
|
|
972 { "paragraph-indent", 1, 0, 'p' }, /* formerly -pi */
|
|
973 { "reference-limit", 1, 0, 'r' }, /* formerly -rl */
|
|
974 { "verbose", 0, &verbose_mode, 1 }, /* formerly -verbose */
|
|
975 { "help", 0, 0, 'h' },
|
|
976 { "version", 0, 0, 'V' },
|
|
977 {NULL, 0, NULL, 0}
|
|
978 };
|
|
979
|
|
980 /* Values for calling handle_variable_internal (). */
|
|
981 #define SET 1
|
|
982 #define CLEAR 2
|
|
983 #define IFSET 3
|
|
984 #define IFCLEAR 4
|
|
985
|
|
986 /* **************************************************************** */
|
|
987 /* */
|
|
988 /* Main () Start of code */
|
|
989 /* */
|
|
990 /* **************************************************************** */
|
|
991
|
|
992 /* For each file mentioned in the command line, process it, turning
|
|
993 texinfo commands into wonderfully formatted output text. */
|
|
994 int
|
|
995 main (int argc, char **argv)
|
|
996 {
|
|
997 extern int errors_printed;
|
|
998 int c, ind;
|
|
999 int reading_from_stdin = 0;
|
|
1000
|
|
1001 /* The name of this program is the last filename in argv[0]. */
|
|
1002 progname = filename_part (argv[0]);
|
|
1003
|
|
1004 /* Parse argument flags from the input line. */
|
|
1005 while ((c = getopt_long
|
|
1006 (argc, argv,
|
|
1007 #if defined (HAVE_MACROS)
|
|
1008 "D:E:U:I:f:o:p:e:r:s:V",
|
|
1009 #else
|
|
1010 "D:U:I:f:o:p:e:r:s:V",
|
|
1011 #endif /* !HAVE_MACROS */
|
|
1012 long_options, &ind))
|
|
1013 != EOF)
|
|
1014 {
|
|
1015 if (c == 0 && long_options[ind].flag == 0)
|
|
1016 c = long_options[ind].val;
|
|
1017
|
|
1018 switch (c)
|
|
1019 {
|
|
1020 /* User specified variable to set or clear? */
|
|
1021 case 'D':
|
|
1022 case 'U':
|
|
1023 handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
|
|
1024 break;
|
|
1025
|
|
1026 #if defined (HAVE_MACROS)
|
|
1027 /* Use specified a macro expansion output file? */
|
|
1028 case 'E':
|
|
1029 if (!macro_expansion_output_stream)
|
|
1030 {
|
|
1031 macro_expansion_output_stream = fopen (optarg, "w");
|
|
1032 if (!macro_expansion_output_stream)
|
|
1033 error ("Couldn't open macro expansion output \"%s\"", optarg);
|
|
1034 }
|
|
1035 else
|
|
1036 error ("Cannot specify more than one macro expansion output");
|
|
1037 break;
|
|
1038 #endif /* HAVE_MACROS */
|
|
1039
|
|
1040 /* User specified include file path? */
|
|
1041 case 'I':
|
|
1042 if (!include_files_path)
|
|
1043 include_files_path = strdup (".");
|
|
1044
|
|
1045 include_files_path = (char *)
|
|
1046 xrealloc (include_files_path,
|
|
1047 2 + strlen (include_files_path) + strlen (optarg));
|
|
1048 strcat (include_files_path, ":");
|
|
1049 strcat (include_files_path, optarg);
|
|
1050 break;
|
|
1051
|
|
1052 /* User specified fill_column? */
|
|
1053 case 'f':
|
|
1054 if (sscanf (optarg, "%d", &fill_column) != 1)
|
|
1055 usage (stderr, FATAL);
|
|
1056 break;
|
|
1057
|
|
1058 /* User specified output file? */
|
|
1059 case 'o':
|
|
1060 command_output_filename = strdup (optarg);
|
|
1061 break;
|
|
1062
|
|
1063 /* User specified paragraph indent (paragraph_start_index)? */
|
|
1064 case 'p':
|
|
1065 if (set_paragraph_indent (optarg) < 0)
|
|
1066 usage (stderr, FATAL);
|
|
1067 break;
|
|
1068
|
|
1069 /* User specified error level? */
|
|
1070 case 'e':
|
|
1071 if (sscanf (optarg, "%d", &max_error_level) != 1)
|
|
1072 usage (stderr, FATAL);
|
|
1073 break;
|
|
1074
|
|
1075 /* User specified reference warning limit? */
|
|
1076 case 'r':
|
|
1077 if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
|
|
1078 usage (stderr, FATAL);
|
|
1079 break;
|
|
1080
|
|
1081 /* User specified footnote style? */
|
|
1082 case 's':
|
|
1083 if (set_footnote_style (optarg) < 0)
|
|
1084 usage (stderr, FATAL);
|
|
1085 footnote_style_preset = 1;
|
|
1086 break;
|
|
1087
|
|
1088 case 'h':
|
|
1089 usage (stdout, NO_ERROR);
|
|
1090 break;
|
|
1091
|
|
1092 /* User requested version info? */
|
|
1093 case 'V':
|
|
1094 print_version_info ();
|
|
1095 exit (NO_ERROR);
|
|
1096 break;
|
|
1097
|
|
1098 case '?':
|
|
1099 usage (stderr, FATAL);
|
|
1100 break;
|
|
1101 }
|
|
1102 }
|
|
1103
|
|
1104 if (optind == argc)
|
|
1105 {
|
|
1106 /* Check to see if input is a file. If so, process that. */
|
|
1107 if (!isatty (fileno (stdin)))
|
|
1108 reading_from_stdin = 1;
|
|
1109 else
|
|
1110 usage (stderr, FATAL);
|
|
1111 }
|
|
1112
|
|
1113 /* If the user has specified --no-headers, this should imply --no-split.
|
|
1114 Do that here. I think it might also imply that we should ignore the
|
|
1115 setfilename at the top of the file, but this might break some FSF things,
|
|
1116 so I will hold off on that. */
|
|
1117 if (no_headers)
|
|
1118 {
|
|
1119 splitting = 0;
|
|
1120
|
|
1121 /* If the user has not specified an output file, then use stdout by
|
|
1122 default. */
|
|
1123 if (!command_output_filename)
|
|
1124 command_output_filename = strdup ("-");
|
|
1125 }
|
|
1126
|
|
1127 if (verbose_mode)
|
|
1128 print_version_info ();
|
|
1129
|
|
1130 /* Remaining arguments are file names of texinfo files.
|
|
1131 Convert them, one by one. */
|
|
1132 if (!reading_from_stdin)
|
|
1133 {
|
|
1134 while (optind != argc)
|
|
1135 convert_from_file (argv[optind++]);
|
|
1136 }
|
|
1137 else
|
|
1138 convert_from_stream (stdin, "stdin");
|
|
1139
|
|
1140 if (errors_printed)
|
|
1141 return (SYNTAX);
|
|
1142 else
|
|
1143 return (NO_ERROR);
|
|
1144 }
|
|
1145
|
|
1146 /* Display the version info of this invocation of Makeinfo. */
|
|
1147 static void
|
|
1148 print_version_info (void)
|
|
1149 {
|
|
1150 printf ("This is GNU Makeinfo version %d.%d.\n",
|
|
1151 major_version, minor_version);
|
|
1152 }
|
|
1153
|
|
1154 /* **************************************************************** */
|
|
1155 /* */
|
|
1156 /* Generic Utilities */
|
|
1157 /* */
|
|
1158 /* **************************************************************** */
|
|
1159
|
|
1160 #if !defined (HAVE_STRDUP)
|
|
1161 char *
|
|
1162 strdup (string)
|
|
1163 char *string;
|
|
1164 {
|
|
1165 char *result;
|
|
1166
|
|
1167 result = (char *)xmalloc (1 + strlen (string));
|
|
1168 strcpy (result, string);
|
|
1169
|
|
1170 return (result);
|
|
1171 }
|
|
1172 #endif /* !HAVE_STRDUP */
|
|
1173
|
|
1174 static void
|
|
1175 memory_error (callers_name, bytes_wanted)
|
|
1176 char *callers_name;
|
|
1177 int bytes_wanted;
|
|
1178 {
|
|
1179 char printable_string[80];
|
|
1180
|
|
1181 sprintf (printable_string,
|
|
1182 "Virtual memory exhausted in %s ()! Needed %d bytes.",
|
|
1183 callers_name, bytes_wanted);
|
|
1184
|
|
1185 error (printable_string);
|
|
1186 abort ();
|
|
1187 }
|
|
1188
|
|
1189 /* Just like malloc, but kills the program in case of fatal error. */
|
|
1190 static void *
|
|
1191 xmalloc (unsigned int nbytes)
|
|
1192 {
|
|
1193 void *temp = (void *) malloc (nbytes);
|
|
1194
|
|
1195 if (nbytes && temp == (void *)NULL)
|
|
1196 memory_error ("xmalloc", nbytes);
|
|
1197
|
|
1198 return (temp);
|
|
1199 }
|
|
1200
|
|
1201 /* Like realloc (), but barfs if there isn't enough memory. */
|
|
1202 static void *
|
|
1203 xrealloc (void *pointer, unsigned int nbytes)
|
|
1204 {
|
|
1205 void *temp;
|
|
1206
|
|
1207 if (!pointer)
|
|
1208 temp = (void *)xmalloc (nbytes);
|
|
1209 else
|
|
1210 temp = (void *)realloc (pointer, nbytes);
|
|
1211
|
|
1212 if (nbytes && !temp)
|
|
1213 memory_error ("xrealloc", nbytes);
|
|
1214
|
|
1215 return (temp);
|
|
1216 }
|
|
1217
|
|
1218 /* Tell the user how to use this program.
|
|
1219 Print the message to STREAM, and then exit with EXIT_VALUE. */
|
|
1220 static void
|
|
1221 usage (FILE *stream, int exit_value)
|
|
1222 {
|
|
1223 fprintf (stream, "Usage: %s [options] texinfo-file...\n\
|
|
1224 \n\
|
|
1225 This program accepts as input files of texinfo commands and text\n\
|
|
1226 and outputs a file suitable for reading with GNU Info.\n\
|
|
1227 \n\
|
|
1228 Options:\n\
|
|
1229 `-I DIR' add DIR to the directory search list for including\n\
|
|
1230 files with the `@include' command.\n\
|
|
1231 -D VAR define a variable, as with `@set'.\n\
|
|
1232 -U VAR undefine a variable, as with `@clear'.\n\
|
|
1233 -E MACRO-OFILE process macros, and output texinfo source code for TeX.\n\
|
|
1234 --no-validate suppress node cross reference validation.\n\
|
|
1235 --no-warn suppress warning messages (errors are still output).\n\
|
|
1236 --no-split suppress the splitting of large files.\n\
|
|
1237 --no-headers suppress the output of Node: Foo headers.\n\
|
|
1238 --verbose print information about what is being done.\n\
|
|
1239 --version print the version number of Makeinfo.\n\
|
|
1240 --output FILE or -o FILE\n\
|
|
1241 specify the output file. When you specify the\n\
|
|
1242 output file in this way, any `@setfilename' in the\n\
|
|
1243 input file is ignored.\n\
|
|
1244 --paragraph-indent NUM\n\
|
|
1245 set the paragraph indent to NUM (default %d).\n\
|
|
1246 --fill-column NUM set the filling column to NUM (default %d).\n\
|
|
1247 --error-limit NUM set the error limit to NUM (default %d).\n\
|
|
1248 --reference-limit NUM\n\
|
|
1249 set the reference warning limit to NUM (default %d).\n\
|
|
1250 --footnote-style STYLE\n\
|
|
1251 set the footnote style to STYLE. STYLE should\n\
|
|
1252 either be `separate' to place footnotes in their own\n\
|
|
1253 node, or `end', to place the footnotes at the end of\n\
|
|
1254 the node in which they are defined (the default).\n\
|
|
1255 --help print this message and exit.\n\n",
|
|
1256 progname, paragraph_start_indent,
|
|
1257 fill_column, max_error_level, reference_warning_limit);
|
|
1258 exit (exit_value);
|
|
1259 }
|
|
1260
|
|
1261 #if defined (MSDOS)
|
|
1262
|
|
1263 static struct passwd *
|
|
1264 getpwnam (char *name)
|
|
1265 {
|
|
1266 return (struct passwd *) 0;
|
|
1267 }
|
|
1268
|
|
1269 #endif /* MSDOS */
|
|
1270
|
|
1271 /* **************************************************************** */
|
|
1272 /* */
|
|
1273 /* Manipulating Lists */
|
|
1274 /* */
|
|
1275 /* **************************************************************** */
|
|
1276
|
|
1277 typedef struct generic_list {
|
|
1278 struct generic_list *next;
|
|
1279 } GENERIC_LIST;
|
|
1280
|
|
1281 /* Reverse the chain of structures in LIST. Output the new head
|
|
1282 of the chain. You should always assign the output value of this
|
|
1283 function to something, or you will lose the chain. */
|
|
1284 static GENERIC_LIST *
|
|
1285 reverse_list (register GENERIC_LIST *list)
|
|
1286 {
|
|
1287 register GENERIC_LIST *next;
|
|
1288 register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
|
|
1289
|
|
1290 while (list)
|
|
1291 {
|
|
1292 next = list->next;
|
|
1293 list->next = prev;
|
|
1294 prev = list;
|
|
1295 list = next;
|
|
1296 }
|
|
1297 return (prev);
|
|
1298 }
|
|
1299
|
|
1300
|
|
1301 /* **************************************************************** */
|
|
1302 /* */
|
|
1303 /* Pushing and Popping Files */
|
|
1304 /* */
|
|
1305 /* **************************************************************** */
|
|
1306
|
|
1307 /* Find and load the file named FILENAME. Return a pointer to
|
|
1308 the loaded file, or NULL if it can't be loaded. */
|
|
1309 static char *
|
|
1310 find_and_load (char *filename)
|
|
1311 {
|
|
1312 struct stat fileinfo;
|
|
1313 long file_size;
|
|
1314 int file = -1, count = 0;
|
|
1315 char *fullpath, *result;
|
|
1316
|
|
1317 result = fullpath = (char *)NULL;
|
|
1318
|
|
1319 fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
|
|
1320
|
|
1321 if (!fullpath)
|
|
1322 goto error_exit;
|
|
1323
|
|
1324 filename = fullpath;
|
|
1325 file_size = (long) fileinfo.st_size;
|
|
1326
|
|
1327 file = open (filename, O_RDONLY);
|
|
1328 if (file < 0)
|
|
1329 goto error_exit;
|
|
1330
|
|
1331 /* Load the file. */
|
|
1332 result = (char *)xmalloc (1 + file_size);
|
|
1333
|
|
1334 /* VMS stat lies about the st_size value. The actual number of
|
|
1335 readable bytes is always less than this value. The arcane
|
|
1336 mysteries of VMS/RMS are too much to probe, so this hack
|
|
1337 suffices to make things work.
|
|
1338
|
|
1339 BPW: same business applies under MS-DOS. */
|
|
1340 #if defined (VMS) || defined (MSDOS)
|
|
1341 while ((n = read (file, result + count, file_size)) > 0)
|
|
1342 count += n;
|
|
1343 if (n == -1)
|
|
1344 #else /* !(VMS && MSDOS) */
|
|
1345 count = file_size;
|
|
1346 if (read (file, result, file_size) != file_size)
|
|
1347 #endif /* !(VMS && MSDOS) */
|
|
1348 error_exit:
|
|
1349 {
|
|
1350 if (result)
|
|
1351 free (result);
|
|
1352
|
|
1353 if (fullpath)
|
|
1354 free (fullpath);
|
|
1355
|
|
1356 if (file != -1)
|
|
1357 close (file);
|
|
1358
|
|
1359 return ((char *) NULL);
|
|
1360 }
|
|
1361 close (file);
|
|
1362
|
|
1363 /* Set the globals to the new file. */
|
|
1364 input_text = result;
|
|
1365 size_of_input_text = count;
|
|
1366 input_filename = strdup (fullpath);
|
|
1367 node_filename = strdup (fullpath);
|
|
1368 input_text_offset = 0;
|
|
1369 line_number = 1;
|
|
1370 /* Not strictly necessary. This magic prevents read_token () from doing
|
|
1371 extra unnecessary work each time it is called (that is a lot of times).
|
|
1372 The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
|
|
1373 input_text[size_of_input_text] = '\n';
|
|
1374 return (result);
|
|
1375 }
|
|
1376
|
|
1377 /* Save the state of the current input file. */
|
|
1378 static void
|
|
1379 pushfile (void)
|
|
1380 {
|
|
1381 FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
|
|
1382 newstack->filename = input_filename;
|
|
1383 newstack->text = input_text;
|
|
1384 newstack->size = size_of_input_text;
|
|
1385 newstack->offset = input_text_offset;
|
|
1386 newstack->line_number = line_number;
|
|
1387 newstack->next = filestack;
|
|
1388
|
|
1389 filestack = newstack;
|
|
1390 push_node_filename ();
|
|
1391 }
|
|
1392
|
|
1393 /* Make the current file globals be what is on top of the file stack. */
|
|
1394 static void
|
|
1395 popfile (void)
|
|
1396 {
|
|
1397 FSTACK *tos = filestack;
|
|
1398
|
|
1399 if (!tos)
|
|
1400 abort (); /* My fault. I wonder what I did? */
|
|
1401
|
|
1402 #if defined (HAVE_MACROS)
|
|
1403 if (macro_expansion_output_stream)
|
|
1404 {
|
|
1405 maybe_write_itext (input_text, input_text_offset);
|
|
1406 forget_itext (input_text);
|
|
1407 }
|
|
1408 #endif /* HAVE_MACROS */
|
|
1409
|
|
1410 /* Pop the stack. */
|
|
1411 filestack = filestack->next;
|
|
1412
|
|
1413 /* Make sure that commands with braces have been satisfied. */
|
|
1414 if (!executing_string)
|
|
1415 discard_braces ();
|
|
1416
|
|
1417 /* Get the top of the stack into the globals. */
|
|
1418 input_filename = tos->filename;
|
|
1419 input_text = tos->text;
|
|
1420 size_of_input_text = tos->size;
|
|
1421 input_text_offset = tos->offset;
|
|
1422 line_number = tos->line_number;
|
|
1423 free (tos);
|
|
1424
|
|
1425 /* Go back to the (now) current node. */
|
|
1426 pop_node_filename ();
|
|
1427 }
|
|
1428
|
|
1429 /* Flush all open files on the file stack. */
|
|
1430 static void
|
|
1431 flush_file_stack (void)
|
|
1432 {
|
|
1433 while (filestack)
|
|
1434 {
|
|
1435 char *fname = input_filename;
|
|
1436 char *text = input_text;
|
|
1437 popfile ();
|
|
1438 free (fname);
|
|
1439 free (text);
|
|
1440 }
|
|
1441 }
|
|
1442
|
|
1443 int node_filename_stack_index = 0;
|
|
1444 int node_filename_stack_size = 0;
|
|
1445 char **node_filename_stack = (char **)NULL;
|
|
1446
|
|
1447 static void
|
|
1448 push_node_filename (void)
|
|
1449 {
|
|
1450 if (node_filename_stack_index + 1 > node_filename_stack_size)
|
|
1451 {
|
|
1452 if (!node_filename_stack)
|
|
1453 node_filename_stack =
|
|
1454 (char **)xmalloc ((node_filename_stack_size += 10)
|
|
1455 * sizeof (char *));
|
|
1456 else
|
|
1457 node_filename_stack =
|
|
1458 (char **)xrealloc (node_filename_stack,
|
|
1459 (node_filename_stack_size + 10)
|
|
1460 * sizeof (char *));
|
|
1461 }
|
|
1462
|
|
1463 node_filename_stack[node_filename_stack_index] = node_filename;
|
|
1464 node_filename_stack_index++;
|
|
1465 }
|
|
1466
|
|
1467 static void
|
|
1468 pop_node_filename (void)
|
|
1469 {
|
|
1470 node_filename = node_filename_stack[--node_filename_stack_index];
|
|
1471 }
|
|
1472
|
|
1473 /* Return just the simple part of the filename; i.e. the
|
|
1474 filename without the path information, or extensions.
|
|
1475 This conses up a new string. */
|
|
1476 static char *
|
|
1477 filename_part (char *filename)
|
|
1478 {
|
|
1479 char *basename;
|
|
1480
|
|
1481 basename = strrchr (filename, '/');
|
|
1482 if (!basename)
|
|
1483 basename = filename;
|
|
1484 else
|
|
1485 basename++;
|
|
1486
|
|
1487 basename = strdup (basename);
|
|
1488 #if defined (REMOVE_OUTPUT_EXTENSIONS)
|
|
1489
|
|
1490 /* See if there is an extension to remove. If so, remove it. */
|
|
1491 {
|
|
1492 char *temp;
|
|
1493
|
|
1494 temp = strrchr (basename, '.');
|
|
1495 if (temp)
|
|
1496 *temp = '\0';
|
|
1497 }
|
|
1498 #endif /* REMOVE_OUTPUT_EXTENSIONS */
|
|
1499 return (basename);
|
|
1500 }
|
|
1501
|
|
1502 /* Return the pathname part of filename. This can be NULL. */
|
|
1503 static char *
|
|
1504 pathname_part (char *filename)
|
|
1505 {
|
|
1506 char *result = (char *) NULL;
|
|
1507 register int i;
|
|
1508
|
|
1509 filename = expand_filename (filename, "");
|
|
1510
|
|
1511 i = strlen (filename) - 1;
|
|
1512
|
|
1513 while (i && filename[i] != '/')
|
|
1514 i--;
|
|
1515 if (filename[i] == '/')
|
|
1516 i++;
|
|
1517
|
|
1518 if (i)
|
|
1519 {
|
|
1520 result = (char *)xmalloc (1 + i);
|
|
1521 strncpy (result, filename, i);
|
|
1522 result[i] = '\0';
|
|
1523 }
|
|
1524 free (filename);
|
|
1525 return (result);
|
|
1526 }
|
|
1527
|
|
1528 static char *
|
|
1529 filename_non_directory (char *name)
|
|
1530 {
|
|
1531 register int i;
|
|
1532
|
|
1533 for (i = strlen (name) - 1; i; i--)
|
|
1534 if (name[i] == '/')
|
|
1535 return (strdup (name + i + 1));
|
|
1536
|
|
1537 return (strdup (name));
|
|
1538 }
|
|
1539
|
|
1540 /* Return the expansion of FILENAME. */
|
|
1541 static char *
|
|
1542 expand_filename (char *filename, char *input_name)
|
|
1543 {
|
|
1544 register int i;
|
|
1545
|
|
1546 if (filename)
|
|
1547 filename = full_pathname (filename);
|
|
1548 else
|
|
1549 {
|
|
1550 filename = filename_non_directory (input_name);
|
|
1551
|
|
1552 if (!*filename)
|
|
1553 {
|
|
1554 free (filename);
|
|
1555 filename = strdup ("noname.texi");
|
|
1556 }
|
|
1557
|
|
1558 for (i = strlen (filename) - 1; i; i--)
|
|
1559 if (filename[i] == '.')
|
|
1560 break;
|
|
1561
|
|
1562 if (!i)
|
|
1563 i = strlen (filename);
|
|
1564
|
|
1565 if (i + 6 > (int) (strlen (filename)))
|
|
1566 filename = (char *)xrealloc (filename, i + 6);
|
|
1567 strcpy (filename + i, ".info");
|
|
1568 return (filename);
|
|
1569 }
|
|
1570
|
|
1571 if (filename[0] == '.' || filename[0] == '/')
|
|
1572 return (filename);
|
|
1573
|
|
1574 if (filename[0] != '/' && input_name[0] == '/')
|
|
1575 {
|
|
1576 /* Make it so that relative names work. */
|
|
1577 char *result;
|
|
1578
|
|
1579 i = strlen (input_name) - 1;
|
|
1580
|
|
1581 result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
|
|
1582 strcpy (result, input_name);
|
|
1583
|
|
1584 while (result[i] != '/' && i)
|
|
1585 i--;
|
|
1586
|
|
1587 if (result[i] == '/')
|
|
1588 i++;
|
|
1589
|
|
1590 strcpy (&result[i], filename);
|
|
1591 free (filename);
|
|
1592 return (result);
|
|
1593 }
|
|
1594 return (filename);
|
|
1595 }
|
|
1596
|
|
1597 /* Return the full path to FILENAME. */
|
|
1598 static char *
|
|
1599 full_pathname (char *filename)
|
|
1600 {
|
|
1601 int initial_character;
|
|
1602 char *result;
|
|
1603
|
|
1604 /* No filename given? */
|
|
1605 if (!filename || !(initial_character = *filename))
|
|
1606 return (strdup (""));
|
|
1607
|
|
1608 /* Already absolute? */
|
|
1609 if ((initial_character == '/') ||
|
|
1610 ((strncmp (filename, "./", 2) == 0) ||
|
|
1611 (strncmp (filename, "../", 3) == 0)))
|
|
1612 return (strdup (filename));
|
|
1613
|
|
1614 if (initial_character != '~')
|
|
1615 {
|
|
1616 char *localdir;
|
|
1617
|
|
1618 localdir = (char *)xmalloc (1025);
|
|
1619 #if defined (HAVE_GETWD)
|
|
1620 if (!getwd (localdir))
|
|
1621 #else /* !HAVE_GETWD */
|
|
1622 if (!getcwd (localdir, 1024))
|
|
1623 #endif /* !HAVE_GETWD */
|
|
1624 {
|
|
1625 fprintf (stderr, "%s: getwd: %s, %s\n",
|
|
1626 progname, filename, localdir);
|
|
1627 exit (1);
|
|
1628 }
|
|
1629
|
|
1630 strcat (localdir, "/");
|
|
1631 strcat (localdir, filename);
|
|
1632 result = strdup (localdir);
|
|
1633 free (localdir);
|
|
1634 }
|
|
1635 else
|
|
1636 {
|
|
1637 if (filename[1] == '/')
|
|
1638 {
|
|
1639 /* Return the concatenation of the environment variable HOME
|
|
1640 and the rest of the string. */
|
|
1641 char *temp_home;
|
|
1642
|
|
1643 temp_home = (char *) getenv ("HOME");
|
|
1644 result = (char *)xmalloc (strlen (&filename[1])
|
|
1645 + 1
|
|
1646 + temp_home ? strlen (temp_home)
|
|
1647 : 0);
|
|
1648 *result = '\0';
|
|
1649
|
|
1650 if (temp_home)
|
|
1651 strcpy (result, temp_home);
|
|
1652
|
|
1653 strcat (result, &filename[1]);
|
|
1654 }
|
|
1655 else
|
|
1656 {
|
|
1657 struct passwd *user_entry;
|
|
1658 int i, c;
|
|
1659 char *username = (char *)xmalloc (257);
|
|
1660
|
|
1661 for (i = 1; (c = filename[i]); i++)
|
|
1662 {
|
|
1663 if (c == '/')
|
|
1664 break;
|
|
1665 else
|
|
1666 username[i - 1] = c;
|
|
1667 }
|
|
1668 if (c)
|
|
1669 username[i - 1] = '\0';
|
|
1670
|
|
1671 user_entry = getpwnam (username);
|
|
1672
|
|
1673 if (!user_entry)
|
|
1674 return (strdup (filename));
|
|
1675
|
|
1676 result = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
|
|
1677 + strlen (&filename[i]));
|
|
1678 strcpy (result, user_entry->pw_dir);
|
|
1679 strcat (result, &filename[i]);
|
|
1680 }
|
|
1681 }
|
|
1682 return (result);
|
|
1683 }
|
|
1684
|
|
1685 static char *
|
|
1686 output_name_from_input_name (char *name)
|
|
1687 {
|
|
1688 return (expand_filename ((char *)NULL, name));
|
|
1689 }
|
|
1690
|
|
1691 /* **************************************************************** */
|
|
1692 /* */
|
|
1693 /* Error Handling */
|
|
1694 /* */
|
|
1695 /* **************************************************************** */
|
|
1696
|
|
1697 /* Number of errors encountered. */
|
|
1698 int errors_printed = 0;
|
|
1699
|
|
1700 /* Print the last error gotten from the file system. */
|
|
1701 static int
|
|
1702 fs_error (char *filename)
|
|
1703 {
|
|
1704 remember_error ();
|
|
1705 perror (filename);
|
|
1706 return (0);
|
|
1707 }
|
|
1708
|
|
1709 static int
|
|
1710 error (char *format, ...)
|
|
1711 {
|
|
1712 va_list ap;
|
|
1713
|
|
1714 remember_error ();
|
|
1715 va_start (ap, format);
|
|
1716 vfprintf (stderr, format, ap);
|
|
1717 fprintf (stderr, "\n");
|
|
1718 va_end (ap);
|
|
1719 return ((int) 0);
|
|
1720 }
|
|
1721
|
|
1722 /* Just like error (), but print the line number as well. */
|
|
1723 static int
|
|
1724 line_error (char *format, ...)
|
|
1725 {
|
|
1726 va_list ap;
|
|
1727
|
|
1728 remember_error ();
|
|
1729 va_start (ap, format);
|
|
1730 fprintf (stderr, "%s:%d: ", input_filename, line_number);
|
|
1731 vfprintf (stderr, format, ap);
|
|
1732 fprintf (stderr, ".\n");
|
|
1733 va_end (ap);
|
|
1734 return ((int) 0);
|
|
1735 }
|
|
1736
|
|
1737 static int
|
|
1738 warning (char *format, ...)
|
|
1739 {
|
|
1740 va_list ap;
|
|
1741
|
|
1742 va_start (ap, format);
|
|
1743 if (print_warnings)
|
|
1744 {
|
|
1745 fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
|
|
1746 vfprintf (stderr, format, ap);
|
|
1747 fprintf (stderr, ".\n");
|
|
1748 }
|
|
1749 va_end (ap);
|
|
1750 return ((int) 0);
|
|
1751 }
|
|
1752
|
|
1753 /* Remember that an error has been printed. If this is the first
|
|
1754 error printed, then tell them which program is printing them.
|
|
1755 If more than max_error_level have been printed, then exit the
|
|
1756 program. */
|
|
1757 static void
|
|
1758 remember_error (void)
|
|
1759 {
|
|
1760 errors_printed++;
|
|
1761 if (max_error_level && (errors_printed > max_error_level))
|
|
1762 {
|
|
1763 fprintf (stderr, "Too many errors! Gave up.\n");
|
|
1764 flush_file_stack ();
|
|
1765 cm_bye ();
|
|
1766 exit (1);
|
|
1767 }
|
|
1768 }
|
|
1769
|
|
1770 /* **************************************************************** */
|
|
1771 /* */
|
|
1772 /* Hacking Tokens and Strings */
|
|
1773 /* */
|
|
1774 /* **************************************************************** */
|
|
1775
|
|
1776 /* Return the next token as a string pointer. We cons the
|
|
1777 string. */
|
|
1778 static char *
|
|
1779 read_token (void)
|
|
1780 {
|
|
1781 int i, character;
|
|
1782 char *result;
|
|
1783
|
|
1784 /* If the first character to be read is self-delimiting, then that
|
|
1785 is the command itself. */
|
|
1786 character = curchar ();
|
|
1787 if (self_delimiting (character))
|
|
1788 {
|
|
1789 input_text_offset++;
|
|
1790 result = strdup (" ");
|
|
1791 *result = character;
|
|
1792 return (result);
|
|
1793 }
|
|
1794
|
|
1795 for (i = 0; ((input_text_offset != size_of_input_text)
|
|
1796 && (character = curchar ())
|
|
1797 && command_char (character));
|
|
1798 i++, input_text_offset++);
|
|
1799 result = (char *)xmalloc (i + 1);
|
|
1800 memcpy (result, &input_text[input_text_offset - i], i);
|
|
1801 result[i] = '\0';
|
|
1802 return (result);
|
|
1803 }
|
|
1804
|
|
1805 /* Return non-zero if CHARACTER is self-delimiting. */
|
|
1806 static int
|
|
1807 self_delimiting (int character)
|
|
1808 {
|
|
1809 return (member (character, "{}:.@*'`,!?; \n\t"));
|
|
1810 }
|
|
1811
|
|
1812 /* Clear whitespace from the front and end of string. */
|
|
1813 static void
|
|
1814 canon_white (char *string)
|
|
1815 {
|
|
1816 int len = strlen (string);
|
|
1817 int x;
|
|
1818
|
|
1819 if (!len)
|
|
1820 return;
|
|
1821
|
|
1822 for (x = 0; x < len; x++)
|
|
1823 {
|
|
1824 if (!cr_or_whitespace (string[x]))
|
|
1825 {
|
|
1826 strcpy (string, string + x);
|
|
1827 break;
|
|
1828 }
|
|
1829 }
|
|
1830 len = strlen (string);
|
|
1831 if (len)
|
|
1832 len--;
|
|
1833 while (len > -1 && cr_or_whitespace (string[len]))
|
|
1834 len--;
|
|
1835 string[len + 1] = '\0';
|
|
1836 }
|
|
1837
|
|
1838 /* Bash STRING, replacing all whitespace with just one space. */
|
|
1839 static void
|
|
1840 fix_whitespace (char *string)
|
|
1841 {
|
|
1842 char *temp = (char *)xmalloc (strlen (string) + 1);
|
|
1843 int string_index = 0;
|
|
1844 int temp_index = 0;
|
|
1845 int c;
|
|
1846
|
|
1847 canon_white (string);
|
|
1848
|
|
1849 while (string[string_index])
|
|
1850 {
|
|
1851 c = temp[temp_index++] = string[string_index++];
|
|
1852
|
|
1853 if (c == ' ' || c == '\n' || c == '\t')
|
|
1854 {
|
|
1855 temp[temp_index - 1] = ' ';
|
|
1856 while ((c = string[string_index]) && (c == ' ' ||
|
|
1857 c == '\t' ||
|
|
1858 c == '\n'))
|
|
1859 string_index++;
|
|
1860 }
|
|
1861 }
|
|
1862 temp[temp_index] = '\0';
|
|
1863 strcpy (string, temp);
|
|
1864 free (temp);
|
|
1865 }
|
|
1866
|
|
1867 /* Discard text until the desired string is found. The string is
|
|
1868 included in the discarded text. */
|
|
1869 static void
|
|
1870 discard_until (char *string)
|
|
1871 {
|
|
1872 int temp = search_forward (string, input_text_offset);
|
|
1873
|
|
1874 int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
|
|
1875 int from = input_text_offset;
|
|
1876
|
|
1877 /* Find out what line we are on. */
|
|
1878 while (from != tt)
|
|
1879 if (input_text[from++] == '\n')
|
|
1880 line_number++;
|
|
1881
|
|
1882 if (temp < 0)
|
|
1883 {
|
|
1884 input_text_offset = size_of_input_text - strlen (string);
|
|
1885
|
|
1886 if (strcmp (string, "\n") != 0)
|
|
1887 {
|
|
1888 line_error ("Expected `%s'", string);
|
|
1889 return;
|
|
1890 }
|
|
1891 }
|
|
1892 else
|
|
1893 input_text_offset = temp;
|
|
1894
|
|
1895 input_text_offset += strlen (string);
|
|
1896 }
|
|
1897
|
|
1898 /* Read characters from the file until we are at MATCH.
|
|
1899 Place the characters read into STRING.
|
|
1900 On exit input_text_offset is after the match string.
|
|
1901 Return the offset where the string starts. */
|
|
1902 static int
|
|
1903 get_until (char *match, char **string)
|
|
1904 {
|
|
1905 int len, current_point, x, new_point, tem;
|
|
1906
|
|
1907 current_point = x = input_text_offset;
|
|
1908 new_point = search_forward (match, input_text_offset);
|
|
1909
|
|
1910 if (new_point < 0)
|
|
1911 new_point = size_of_input_text;
|
|
1912 len = new_point - current_point;
|
|
1913
|
|
1914 /* Keep track of which line number we are at. */
|
|
1915 tem = new_point + (strlen (match) - 1);
|
|
1916 while (x != tem)
|
|
1917 if (input_text[x++] == '\n')
|
|
1918 line_number++;
|
|
1919
|
|
1920 *string = (char *)xmalloc (len + 1);
|
|
1921
|
|
1922 memcpy (*string, &input_text[current_point], len);
|
|
1923 (*string)[len] = '\0';
|
|
1924
|
|
1925 /* Now leave input_text_offset in a consistent state. */
|
|
1926 input_text_offset = tem;
|
|
1927
|
|
1928 if (input_text_offset > size_of_input_text)
|
|
1929 input_text_offset = size_of_input_text;
|
|
1930
|
|
1931 return (new_point);
|
|
1932 }
|
|
1933
|
|
1934 /* Read characters from the file until we are at MATCH or end of line.
|
|
1935 Place the characters read into STRING. */
|
|
1936 static void
|
|
1937 get_until_in_line (char *match, char **string)
|
|
1938 {
|
|
1939 int real_bottom, temp;
|
|
1940
|
|
1941 real_bottom = size_of_input_text;
|
|
1942 temp = search_forward ("\n", input_text_offset);
|
|
1943
|
|
1944 if (temp < 0)
|
|
1945 temp = size_of_input_text;
|
|
1946
|
|
1947 size_of_input_text = temp;
|
|
1948 get_until (match, string);
|
|
1949 size_of_input_text = real_bottom;
|
|
1950 }
|
|
1951
|
|
1952 static void
|
|
1953 get_rest_of_line (char **string)
|
|
1954 {
|
|
1955 get_until ("\n", string);
|
|
1956 canon_white (*string);
|
|
1957
|
|
1958 if (curchar () == '\n') /* as opposed to the end of the file... */
|
|
1959 {
|
|
1960 line_number++;
|
|
1961 input_text_offset++;
|
|
1962 }
|
|
1963 }
|
|
1964
|
|
1965 /* Backup the input pointer to the previous character, keeping track
|
|
1966 of the current line number. */
|
|
1967 static void
|
|
1968 backup_input_pointer (void)
|
|
1969 {
|
|
1970 if (input_text_offset)
|
|
1971 {
|
|
1972 input_text_offset--;
|
|
1973 if (curchar () == '\n')
|
|
1974 line_number--;
|
|
1975 }
|
|
1976 }
|
|
1977
|
|
1978 /* Read characters from the file until we are at MATCH or closing brace.
|
|
1979 Place the characters read into STRING. */
|
|
1980 static void
|
|
1981 get_until_in_braces (char *match, char **string)
|
|
1982 {
|
|
1983 int i, brace = 0;
|
|
1984 int match_len = strlen (match);
|
|
1985 char *temp;
|
|
1986
|
|
1987 for (i = input_text_offset; i < size_of_input_text; i++)
|
|
1988 {
|
|
1989 if (input_text[i] == '{')
|
|
1990 brace++;
|
|
1991 else if (input_text[i] == '}')
|
|
1992 brace--;
|
|
1993 else if (input_text[i] == '\n')
|
|
1994 line_number++;
|
|
1995
|
|
1996 if (brace < 0 ||
|
|
1997 (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
|
|
1998 break;
|
|
1999 }
|
|
2000
|
|
2001 match_len = i - input_text_offset;
|
|
2002 temp = (char *)xmalloc (2 + match_len);
|
|
2003 strncpy (temp, input_text + input_text_offset, match_len);
|
|
2004 temp[match_len] = '\0';
|
|
2005 input_text_offset = i;
|
|
2006 *string = temp;
|
|
2007 }
|
|
2008
|
|
2009 /* **************************************************************** */
|
|
2010 /* */
|
|
2011 /* Converting the File */
|
|
2012 /* */
|
|
2013 /* **************************************************************** */
|
|
2014
|
|
2015 /* Convert the file named by NAME. The output is saved on the file
|
|
2016 named as the argument to the @setfilename command. */
|
|
2017 static char *suffixes[] = {
|
|
2018 "",
|
|
2019 ".texinfo",
|
|
2020 ".texi",
|
|
2021 ".txinfo",
|
|
2022 (char *)NULL
|
|
2023 };
|
|
2024
|
|
2025 static void
|
|
2026 initialize_conversion (void)
|
|
2027 {
|
|
2028 init_tag_table ();
|
|
2029 init_indices ();
|
|
2030 init_internals ();
|
|
2031 init_paragraph ();
|
|
2032 }
|
|
2033
|
|
2034 /* We read in multiples of 4k, simply because it is a typical pipe size
|
|
2035 on unix systems. */
|
|
2036 #define _READ_BUFFER_GROWTH (4 * 4096)
|
|
2037
|
|
2038 /* Convert the texinfo file coming from the open stream STREAM. Assume the
|
|
2039 source of the stream is named NAME. */
|
|
2040 static void
|
|
2041 convert_from_stream (FILE *stream, char *name)
|
|
2042 {
|
|
2043 char *buffer = (char *)NULL;
|
|
2044 int buffer_offset = 0, buffer_size = 0;
|
|
2045
|
|
2046 initialize_conversion ();
|
|
2047
|
|
2048 /* Read until the end of the stream. This isn't strictly correct, since
|
|
2049 the texinfo input may end before the stream ends, but it is a quick
|
|
2050 working hueristic. */
|
|
2051 while (!feof (stream))
|
|
2052 {
|
|
2053 int count;
|
|
2054
|
|
2055 if (buffer_offset + (_READ_BUFFER_GROWTH + 1) >= buffer_size)
|
|
2056 buffer = (char *)
|
|
2057 xrealloc (buffer, (buffer_size += _READ_BUFFER_GROWTH));
|
|
2058
|
|
2059 count = fread (buffer + buffer_offset, 1, _READ_BUFFER_GROWTH, stream);
|
|
2060
|
|
2061 if (count < 0)
|
|
2062 {
|
|
2063 perror (name);
|
|
2064 exit (FATAL);
|
|
2065 }
|
|
2066
|
|
2067 buffer_offset += count;
|
|
2068 if (count == 0)
|
|
2069 break;
|
|
2070 }
|
|
2071
|
|
2072 /* Set the globals to the new file. */
|
|
2073 input_text = buffer;
|
|
2074 size_of_input_text = buffer_offset;
|
|
2075 input_filename = strdup (name);
|
|
2076 node_filename = strdup (name);
|
|
2077 input_text_offset = 0;
|
|
2078 line_number = 1;
|
|
2079
|
|
2080 /* Not strictly necessary. This magic prevents read_token () from doing
|
|
2081 extra unnecessary work each time it is called (that is a lot of times).
|
|
2082 The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
|
|
2083 input_text[size_of_input_text] = '\n';
|
|
2084
|
|
2085 convert_from_loaded_file (name);
|
|
2086 }
|
|
2087
|
|
2088 static void
|
|
2089 convert_from_file (char *name)
|
|
2090 {
|
|
2091 register int i;
|
|
2092 char *filename = (char *)xmalloc (strlen (name) + 50);
|
|
2093
|
|
2094 initialize_conversion ();
|
|
2095
|
|
2096 /* Try to load the file specified by NAME. If the file isn't found, and
|
|
2097 there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
|
|
2098 for (i = 0; suffixes[i]; i++)
|
|
2099 {
|
|
2100 strcpy (filename, name);
|
|
2101 strcat (filename, suffixes[i]);
|
|
2102
|
|
2103 if (find_and_load (filename))
|
|
2104 break;
|
|
2105
|
|
2106 if (!suffixes[i][0] && strrchr (filename, '.'))
|
|
2107 {
|
|
2108 fs_error (filename);
|
|
2109 free (filename);
|
|
2110 return;
|
|
2111 }
|
|
2112 }
|
|
2113
|
|
2114 if (!suffixes[i])
|
|
2115 {
|
|
2116 fs_error (name);
|
|
2117 free (filename);
|
|
2118 return;
|
|
2119 }
|
|
2120
|
|
2121 input_filename = filename;
|
|
2122
|
|
2123 convert_from_loaded_file (name);
|
|
2124 }
|
|
2125
|
|
2126 static void
|
|
2127 convert_from_loaded_file (char *name)
|
|
2128 {
|
|
2129 char *real_output_filename = (char *)NULL;
|
|
2130
|
|
2131 #if defined (HAVE_MACROS)
|
|
2132 remember_itext (input_text, 0);
|
|
2133 #endif /* HAVE_MACROS */
|
|
2134
|
|
2135 /* Search this file looking for the special string which starts conversion.
|
|
2136 Once found, we may truly begin. */
|
|
2137 input_text_offset = 0;
|
|
2138 while (input_text_offset >= 0)
|
|
2139 {
|
|
2140 input_text_offset =
|
|
2141 search_forward (setfilename_search, input_text_offset);
|
|
2142
|
|
2143 if ((input_text_offset == 0) ||
|
|
2144 ((input_text_offset > 0) &&
|
|
2145 (input_text[input_text_offset -1] == '\n')))
|
|
2146 break;
|
|
2147 else if (input_text_offset > 0)
|
|
2148 input_text_offset++;
|
|
2149 }
|
|
2150
|
|
2151 if (input_text_offset < 0)
|
|
2152 {
|
|
2153 if (!command_output_filename)
|
|
2154 {
|
|
2155 #if defined (REQUIRE_SETFILENAME)
|
|
2156 error ("No `%s' found in `%s'", setfilename_search, name);
|
|
2157 goto finished;
|
|
2158 #else
|
|
2159 register int i, end_of_first_line;
|
|
2160
|
|
2161 /* Find the end of the first line in the file. */
|
|
2162 for (i = 0; i < size_of_input_text - 1; i++)
|
|
2163 if (input_text[i] == '\n')
|
|
2164 break;
|
|
2165
|
|
2166 end_of_first_line = i + 1;
|
|
2167
|
|
2168 input_text_offset = 0;
|
|
2169
|
|
2170 for (i = 0; i < end_of_first_line; i++)
|
|
2171 {
|
|
2172 if ((input_text[i] == '\\') &&
|
|
2173 (strncmp (input_text + i + 1, "include", 7) == 0))
|
|
2174 {
|
|
2175 input_text_offset = end_of_first_line;
|
|
2176 break;
|
|
2177 }
|
|
2178 }
|
|
2179 command_output_filename = output_name_from_input_name (name);
|
|
2180 #endif /* !REQUIRE_SETFILENAME */
|
|
2181 }
|
|
2182 }
|
|
2183 else
|
|
2184 input_text_offset += strlen (setfilename_search);
|
|
2185
|
|
2186 if (!command_output_filename)
|
|
2187 get_until ("\n", &output_filename);
|
|
2188 else
|
|
2189 {
|
|
2190 if (input_text_offset != -1)
|
|
2191 discard_until ("\n");
|
|
2192 else
|
|
2193 input_text_offset = 0;
|
|
2194
|
|
2195 real_output_filename = output_filename = command_output_filename;
|
|
2196 command_output_filename = (char *)NULL;
|
|
2197 }
|
|
2198
|
|
2199 canon_white (output_filename);
|
|
2200
|
|
2201 if (real_output_filename &&
|
|
2202 strcmp (real_output_filename, "-") == 0)
|
|
2203 {
|
|
2204 real_output_filename = strdup (real_output_filename);
|
|
2205 output_stream = stdout;
|
|
2206 splitting = 0; /* Cannot split when writing to stdout. */
|
|
2207 }
|
|
2208 else
|
|
2209 {
|
|
2210 if (!real_output_filename)
|
|
2211 real_output_filename = expand_filename (output_filename, name);
|
|
2212 else
|
|
2213 real_output_filename = strdup (real_output_filename);
|
|
2214
|
|
2215 output_stream = fopen (real_output_filename, "w");
|
|
2216 }
|
|
2217
|
|
2218 if (output_stream != stdout)
|
|
2219 printf ("Making info file `%s' from `%s'.\n", output_filename, name);
|
|
2220
|
|
2221 if (output_stream == NULL)
|
|
2222 {
|
|
2223 fs_error (real_output_filename);
|
|
2224 goto finished;
|
|
2225 }
|
|
2226
|
|
2227 /* Make the displayable filename from output_filename. Only the base
|
|
2228 portion of the filename need be displayed. */
|
|
2229 if (output_stream != stdout)
|
|
2230 pretty_output_filename = filename_part (output_filename);
|
|
2231 else
|
|
2232 pretty_output_filename = strdup ("stdout");
|
|
2233
|
|
2234 /* For this file only, count the number of newlines from the top of
|
|
2235 the file to here. This way, we keep track of line numbers for
|
|
2236 error reporting. Line_number starts at 1, since the user isn't
|
|
2237 zero-based. */
|
|
2238 {
|
|
2239 int temp = 0;
|
|
2240 line_number = 1;
|
|
2241 while (temp != input_text_offset)
|
|
2242 if (input_text[temp++] == '\n')
|
|
2243 line_number++;
|
|
2244 }
|
|
2245
|
|
2246 if (!no_headers)
|
|
2247 {
|
|
2248 add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
|
|
2249 output_filename, major_version, minor_version);
|
|
2250 add_word_args ("the input file %s.\n", input_filename);
|
|
2251 }
|
|
2252
|
|
2253 close_paragraph ();
|
|
2254 reader_loop ();
|
|
2255
|
|
2256 finished:
|
|
2257 close_paragraph ();
|
|
2258 flush_file_stack ();
|
|
2259
|
|
2260 #if defined (HAVE_MACROS)
|
|
2261 if (macro_expansion_output_stream)
|
|
2262 fclose (macro_expansion_output_stream);
|
|
2263 #endif /* HAVE_MACROS */
|
|
2264
|
|
2265 if (output_stream != NULL)
|
|
2266 {
|
|
2267 output_pending_notes ();
|
|
2268 free_pending_notes ();
|
|
2269 if (tag_table != NULL)
|
|
2270 {
|
|
2271 tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
|
|
2272 if (!no_headers)
|
|
2273 write_tag_table ();
|
|
2274 }
|
|
2275
|
|
2276 if (output_stream != stdout)
|
|
2277 fclose (output_stream);
|
|
2278
|
|
2279 /* If validating, then validate the entire file right now. */
|
|
2280 if (validating)
|
|
2281 validate_file (tag_table);
|
|
2282
|
|
2283 /* This used to test && !errors_printed.
|
|
2284 But some files might have legit warnings. So split anyway. */
|
|
2285 if (splitting)
|
|
2286 split_file (real_output_filename, 0);
|
|
2287 }
|
|
2288 free (real_output_filename);
|
|
2289 }
|
|
2290
|
|
2291 static void
|
|
2292 free_and_clear (char **pointer)
|
|
2293 {
|
|
2294 if ((*pointer) != (char *) NULL)
|
|
2295 {
|
|
2296 free (*pointer);
|
|
2297 *pointer = (char *) NULL;
|
|
2298 }
|
|
2299 }
|
|
2300
|
|
2301 /* Initialize some state. */
|
|
2302 static void
|
|
2303 init_internals (void)
|
|
2304 {
|
|
2305 free_and_clear (¤t_node);
|
|
2306 free_and_clear (&output_filename);
|
|
2307 free_and_clear (&command);
|
|
2308 free_and_clear (&input_filename);
|
|
2309 free_node_references ();
|
|
2310 init_insertion_stack ();
|
|
2311 init_brace_stack ();
|
|
2312 command_index = 0;
|
|
2313 in_menu = 0;
|
|
2314 top_node_seen = 0;
|
|
2315 non_top_node_seen = 0;
|
|
2316 }
|
|
2317
|
|
2318 static void
|
|
2319 init_paragraph (void)
|
|
2320 {
|
|
2321 free_and_clear ((char **) &output_paragraph);
|
|
2322 output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
|
|
2323 output_position = 0;
|
|
2324 output_paragraph[0] = '\0';
|
|
2325 output_paragraph_offset = 0;
|
|
2326 output_column = 0;
|
|
2327 paragraph_is_open = 0;
|
|
2328 current_indent = 0;
|
|
2329 }
|
|
2330
|
|
2331 /* Okay, we are ready to start the conversion. Call the reader on
|
|
2332 some text, and fill the text as it is output. Handle commands by
|
|
2333 remembering things like open braces and the current file position on a
|
|
2334 stack, and when the corresponding close brace is found, you can call
|
|
2335 the function with the proper arguments. */
|
|
2336 static void
|
|
2337 reader_loop (void)
|
|
2338 {
|
|
2339 int character;
|
|
2340 int done = 0;
|
|
2341 int dash_count = 0;
|
|
2342
|
|
2343 while (!done)
|
|
2344 {
|
|
2345 if (input_text_offset >= size_of_input_text)
|
|
2346 break;
|
|
2347
|
|
2348 character = curchar ();
|
|
2349
|
|
2350 if (!in_fixed_width_font &&
|
|
2351 (character == '\'' || character == '`') &&
|
|
2352 input_text[input_text_offset + 1] == character)
|
|
2353 {
|
|
2354 input_text_offset++;
|
|
2355 character = '"';
|
|
2356 }
|
|
2357
|
|
2358 if (character == '-')
|
|
2359 {
|
|
2360 dash_count++;
|
|
2361 if (dash_count == 2 && !in_fixed_width_font)
|
|
2362 {
|
|
2363 input_text_offset++;
|
|
2364 continue;
|
|
2365 }
|
|
2366 }
|
|
2367 else
|
|
2368 {
|
|
2369 dash_count = 0;
|
|
2370 }
|
|
2371
|
|
2372 /* If this is a whitespace character, then check to see if the line
|
|
2373 is blank. If so, advance to the carriage return. */
|
|
2374 if (whitespace (character))
|
|
2375 {
|
|
2376 register int i = input_text_offset + 1;
|
|
2377
|
|
2378 while (i < size_of_input_text && whitespace (input_text[i]))
|
|
2379 i++;
|
|
2380
|
|
2381 if (i == size_of_input_text || input_text[i] == '\n')
|
|
2382 {
|
|
2383 if (i == size_of_input_text)
|
|
2384 i--;
|
|
2385
|
|
2386 input_text_offset = i;
|
|
2387 character = curchar ();
|
|
2388 }
|
|
2389 }
|
|
2390
|
|
2391 if (character == '\n')
|
|
2392 {
|
|
2393 line_number++;
|
|
2394
|
|
2395 /* Check for a menu entry here, since the "escape sequence"
|
|
2396 that begins menu entrys is "\n* ". */
|
|
2397 if (in_menu && input_text_offset + 1 < size_of_input_text)
|
|
2398 {
|
|
2399 char *tem;
|
|
2400
|
|
2401 /* Note that the value of TEM is discarded, since it is
|
|
2402 gauranteed to be NULL when glean_node_from_menu () is
|
|
2403 called with a non-zero argument. */
|
|
2404 tem = glean_node_from_menu (1);
|
|
2405 }
|
|
2406 }
|
|
2407
|
|
2408 switch (character)
|
|
2409 {
|
|
2410 case COMMAND_PREFIX:
|
|
2411 read_command ();
|
|
2412 break;
|
|
2413
|
|
2414 case '{':
|
|
2415
|
|
2416 /* Special case. I'm not supposed to see this character by itself.
|
|
2417 If I do, it means there is a syntax error in the input text.
|
|
2418 Report the error here, but remember this brace on the stack so
|
|
2419 you can ignore its partner. */
|
|
2420
|
|
2421 line_error ("Misplaced `{'");
|
|
2422 remember_brace (misplaced_brace);
|
|
2423
|
|
2424 /* Don't advance input_text_offset since this happens in
|
|
2425 remember_brace ().
|
|
2426 input_text_offset++;
|
|
2427 */
|
|
2428 break;
|
|
2429
|
|
2430 case '}':
|
|
2431 pop_and_call_brace ();
|
|
2432 input_text_offset++;
|
|
2433 break;
|
|
2434
|
|
2435 default:
|
|
2436 add_char (character);
|
|
2437 input_text_offset++;
|
|
2438 }
|
|
2439 }
|
|
2440 #if defined (HAVE_MACROS)
|
|
2441 if (macro_expansion_output_stream)
|
|
2442 maybe_write_itext (input_text, input_text_offset);
|
|
2443 #endif /* HAVE_MACROS */
|
|
2444 }
|
|
2445
|
|
2446 /* Find the command corresponding to STRING. If the command
|
|
2447 is found, return a pointer to the data structure. Otherwise
|
|
2448 return (-1). */
|
|
2449 static COMMAND *
|
|
2450 get_command_entry (char *string)
|
|
2451 {
|
|
2452 register int i;
|
|
2453
|
|
2454 for (i = 0; CommandTable[i].name; i++)
|
|
2455 if (strcmp (CommandTable[i].name, string) == 0)
|
|
2456 return (&CommandTable[i]);
|
|
2457
|
|
2458 /* This command is not in our predefined command table. Perhaps
|
|
2459 it is a user defined command. */
|
|
2460 for (i = 0; i < user_command_array_len; i++)
|
|
2461 if (user_command_array[i] &&
|
|
2462 (strcmp (user_command_array[i]->name, string) == 0))
|
|
2463 return (user_command_array[i]);
|
|
2464
|
|
2465 /* Nope, we never heard of this command. */
|
|
2466 return ((COMMAND *) -1);
|
|
2467 }
|
|
2468
|
|
2469 /* input_text_offset is right at the command prefix character.
|
|
2470 Read the next token to determine what to do. */
|
|
2471 static void
|
|
2472 read_command (void)
|
|
2473 {
|
|
2474 COMMAND *entry;
|
|
2475
|
|
2476 input_text_offset++;
|
|
2477 free_and_clear (&command);
|
|
2478 command = read_token ();
|
|
2479
|
|
2480 #if defined (HAVE_MACROS)
|
|
2481 /* Check to see if this command is a macro. If so, execute it here. */
|
|
2482 {
|
|
2483 MACRO_DEF *def;
|
|
2484
|
|
2485 def = find_macro (command);
|
|
2486
|
|
2487 if (def)
|
|
2488 {
|
|
2489 /* We disallow recursive use of a macro call. Inhibit the expansion
|
|
2490 of this macro during the life of its execution. */
|
|
2491 if (!(def->flags & ME_RECURSE))
|
|
2492 def->inhibited = 1;
|
|
2493
|
|
2494 execute_macro (def);
|
|
2495
|
|
2496 if (!(def->flags & ME_RECURSE))
|
|
2497 def->inhibited = 0;
|
|
2498
|
|
2499 return;
|
|
2500 }
|
|
2501 }
|
|
2502 #endif /* HAVE_MACROS */
|
|
2503
|
|
2504 entry = get_command_entry (command);
|
|
2505
|
|
2506 if (entry == (COMMAND *)-1)
|
|
2507 {
|
|
2508 line_error ("Unknown info command `%s'", command);
|
|
2509 return;
|
|
2510 }
|
|
2511
|
|
2512 if (entry->argument_in_braces)
|
|
2513 remember_brace (entry->proc);
|
|
2514
|
|
2515 (*(entry->proc)) (START, output_paragraph_offset, 0);
|
|
2516 }
|
|
2517
|
|
2518 /* Return the string which invokes PROC; a pointer to a function. */
|
|
2519 static char *
|
|
2520 find_proc_name (COMMAND_FUNCTION *proc)
|
|
2521 {
|
|
2522 register int i;
|
|
2523
|
|
2524 for (i = 0; CommandTable[i].name; i++)
|
|
2525 if (proc == CommandTable[i].proc)
|
|
2526 return (CommandTable[i].name);
|
|
2527 return ("NO_NAME!");
|
|
2528 }
|
|
2529
|
|
2530 static void
|
|
2531 init_brace_stack (void)
|
|
2532 {
|
|
2533 brace_stack = (BRACE_ELEMENT *) NULL;
|
|
2534 }
|
|
2535
|
|
2536 static void
|
|
2537 remember_brace (COMMAND_FUNCTION *proc)
|
|
2538 {
|
|
2539 if (curchar () != '{')
|
|
2540 line_error ("%c%s expected `{..}'", COMMAND_PREFIX, command);
|
|
2541 else
|
|
2542 input_text_offset++;
|
|
2543 remember_brace_1 (proc, output_paragraph_offset);
|
|
2544 }
|
|
2545
|
|
2546 /* Remember the current output position here. Save PROC
|
|
2547 along with it so you can call it later. */
|
|
2548 static void
|
|
2549 remember_brace_1 (COMMAND_FUNCTION *proc, int position)
|
|
2550 {
|
|
2551 BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
|
|
2552 new->next = brace_stack;
|
|
2553 new->proc = proc;
|
|
2554 new->pos = position;
|
|
2555 new->line = line_number;
|
|
2556 brace_stack = new;
|
|
2557 }
|
|
2558
|
|
2559 /* Pop the top of the brace stack, and call the associated function
|
|
2560 with the args END and POS. */
|
|
2561 static void
|
|
2562 pop_and_call_brace (void)
|
|
2563 {
|
|
2564 BRACE_ELEMENT *temp;
|
|
2565 COMMAND_FUNCTION *proc;
|
|
2566 int pos;
|
|
2567
|
|
2568 if (brace_stack == (BRACE_ELEMENT *) NULL)
|
|
2569 {
|
|
2570 line_error ("Unmatched close brace");
|
|
2571 return;
|
|
2572 }
|
|
2573
|
|
2574 pos = brace_stack->pos;
|
|
2575 proc = brace_stack->proc;
|
|
2576 temp = brace_stack->next;
|
|
2577 free (brace_stack);
|
|
2578 brace_stack = temp;
|
|
2579
|
|
2580 (*proc) (END, pos, output_paragraph_offset);
|
|
2581 }
|
|
2582
|
|
2583 /* Shift all of the markers in `brace_stack' by AMOUNT. */
|
|
2584 static void
|
|
2585 adjust_braces_following (int here, int amount)
|
|
2586 {
|
|
2587 register BRACE_ELEMENT *stack = brace_stack;
|
|
2588
|
|
2589 while (stack)
|
|
2590 {
|
|
2591 if (stack->pos >= here)
|
|
2592 stack->pos += amount;
|
|
2593 stack = stack->next;
|
|
2594 }
|
|
2595 }
|
|
2596
|
|
2597 /* You call discard_braces () when you shouldn't have any braces on the stack.
|
|
2598 I used to think that this happens for commands that don't take arguments
|
|
2599 in braces, but that was wrong because of things like @code{foo @@}. So now
|
|
2600 I only detect it at the beginning of nodes. */
|
|
2601 static void
|
|
2602 discard_braces (void)
|
|
2603 {
|
|
2604 if (!brace_stack)
|
|
2605 return;
|
|
2606
|
|
2607 while (brace_stack)
|
|
2608 {
|
|
2609 if (brace_stack->proc != misplaced_brace)
|
|
2610 {
|
|
2611 char *proc_name;
|
|
2612 int temp_line_number = line_number;
|
|
2613
|
|
2614 line_number = brace_stack->line;
|
|
2615 proc_name = find_proc_name (brace_stack->proc);
|
|
2616 line_error ("%c%s missing close brace", COMMAND_PREFIX, proc_name);
|
|
2617 line_number = temp_line_number;
|
|
2618 pop_and_call_brace ();
|
|
2619 }
|
|
2620 else
|
|
2621 {
|
|
2622 BRACE_ELEMENT *temp;
|
|
2623 temp = brace_stack->next;
|
|
2624 free (brace_stack);
|
|
2625 brace_stack = temp;
|
|
2626 }
|
|
2627 }
|
|
2628 }
|
|
2629
|
|
2630 static int
|
|
2631 get_char_len (int character)
|
|
2632 {
|
|
2633 /* Return the printed length of the character. */
|
|
2634 int len;
|
|
2635
|
|
2636 switch (character)
|
|
2637 {
|
|
2638 case '\t':
|
|
2639 len = (output_column + 8) & 0xf7;
|
|
2640 if (len > fill_column)
|
|
2641 len = fill_column - output_column;
|
|
2642 else
|
|
2643 len = len - output_column;
|
|
2644 break;
|
|
2645
|
|
2646 case '\n':
|
|
2647 len = fill_column - output_column;
|
|
2648 break;
|
|
2649
|
|
2650 default:
|
|
2651 if (character < ' ')
|
|
2652 len = 2;
|
|
2653 else
|
|
2654 len = 1;
|
|
2655 }
|
|
2656 return (len);
|
|
2657 }
|
|
2658
|
|
2659 static void
|
|
2660 add_word_args (char *format, ...)
|
|
2661 {
|
|
2662 va_list ap;
|
|
2663 char buffer[1000];
|
|
2664
|
|
2665 va_start (ap, format);
|
|
2666 vsprintf (buffer, format, ap);
|
|
2667 add_word (buffer);
|
|
2668 va_end (ap);
|
|
2669 }
|
|
2670
|
|
2671 /* Add STRING to output_paragraph. */
|
|
2672 static void
|
|
2673 add_word (char *string)
|
|
2674 {
|
|
2675 while (*string)
|
|
2676 add_char (*string++);
|
|
2677 }
|
|
2678
|
|
2679 /* Non-zero if the last character inserted has the syntax class of NEWLINE. */
|
|
2680 int last_char_was_newline = 1;
|
|
2681
|
|
2682 /* The actual last inserted character. Note that this may be something
|
|
2683 other than NEWLINE even if last_char_was_newline is 1. */
|
|
2684 int last_inserted_character = 0;
|
|
2685
|
|
2686 /* Non-zero means that a newline character has already been
|
|
2687 inserted, so close_paragraph () should insert one less. */
|
|
2688 int line_already_broken = 0;
|
|
2689
|
|
2690 /* When non-zero we have finished an insertion (see end_insertion ()) and we
|
|
2691 want to ignore false continued paragraph closings. */
|
|
2692 int insertion_paragraph_closed = 0;
|
|
2693
|
|
2694 /* Non-zero means attempt to make all of the lines have fill_column width. */
|
|
2695 int do_justification = 0;
|
|
2696
|
|
2697 /* Add the character to the current paragraph. If filling_enabled is
|
|
2698 non-zero, then do filling as well. */
|
|
2699 static void
|
|
2700 add_char (int character)
|
|
2701 {
|
|
2702 /* If we are avoiding outputting headers, and we are currently
|
|
2703 in a menu, then simply return. */
|
|
2704 if (no_headers && in_menu)
|
|
2705 return;
|
|
2706
|
|
2707 /* If we are adding a character now, then we don't have to
|
|
2708 ignore close_paragraph () calls any more. */
|
|
2709 if (must_start_paragraph && character != '\n')
|
|
2710 {
|
|
2711 must_start_paragraph = 0;
|
|
2712 line_already_broken = 0; /* The line is no longer broken. */
|
|
2713 if (current_indent > output_column)
|
|
2714 {
|
|
2715 indent (current_indent - output_column);
|
|
2716 output_column = current_indent;
|
|
2717 }
|
|
2718 }
|
|
2719
|
|
2720 if (non_splitting_words && member (character, " \t\n"))
|
|
2721 character = ' ' | 0x80;
|
|
2722
|
|
2723 insertion_paragraph_closed = 0;
|
|
2724
|
|
2725 switch (character)
|
|
2726 {
|
|
2727 case '\n':
|
|
2728 if (!filling_enabled)
|
|
2729 {
|
|
2730 insert ('\n');
|
|
2731
|
|
2732 if (force_flush_right)
|
|
2733 {
|
|
2734 close_paragraph ();
|
|
2735 /* Hack to force single blank lines out in this mode. */
|
|
2736 flush_output ();
|
|
2737 }
|
|
2738
|
|
2739 output_column = 0;
|
|
2740
|
|
2741 if (!no_indent && paragraph_is_open)
|
|
2742 indent (output_column = current_indent);
|
|
2743 break;
|
|
2744 }
|
|
2745 else /* CHARACTER is newline, and filling is enabled. */
|
|
2746 {
|
|
2747 if (sentence_ender (last_inserted_character))
|
|
2748 {
|
|
2749 insert (' ');
|
|
2750 output_column++;
|
|
2751 last_inserted_character = character;
|
|
2752 }
|
|
2753 }
|
|
2754
|
|
2755 if (last_char_was_newline)
|
|
2756 {
|
|
2757 close_paragraph ();
|
|
2758 pending_indent = 0;
|
|
2759 }
|
|
2760 else
|
|
2761 {
|
|
2762 last_char_was_newline = 1;
|
|
2763 insert (' ');
|
|
2764 output_column++;
|
|
2765 }
|
|
2766 break;
|
|
2767
|
|
2768 default:
|
|
2769 {
|
|
2770 int len = get_char_len (character);
|
|
2771 int suppress_insert = 0;
|
|
2772
|
|
2773 if ((character == ' ') && (last_char_was_newline))
|
|
2774 {
|
|
2775 if (!paragraph_is_open)
|
|
2776 {
|
|
2777 pending_indent++;
|
|
2778 return;
|
|
2779 }
|
|
2780 }
|
|
2781
|
|
2782 if (!paragraph_is_open)
|
|
2783 {
|
|
2784 start_paragraph ();
|
|
2785
|
|
2786 /* If the paragraph is supposed to be indented a certain way,
|
|
2787 then discard all of the pending whitespace. Otherwise, we
|
|
2788 let the whitespace stay. */
|
|
2789 if (!paragraph_start_indent)
|
|
2790 indent (pending_indent);
|
|
2791 pending_indent = 0;
|
|
2792 }
|
|
2793
|
|
2794 if ((output_column += len) > fill_column)
|
|
2795 {
|
|
2796 if (filling_enabled)
|
|
2797 {
|
|
2798 int temp = output_paragraph_offset;
|
|
2799 while (--temp > 0 && output_paragraph[temp] != '\n')
|
|
2800 {
|
|
2801 /* If we have found a space, we have the place to break
|
|
2802 the line. */
|
|
2803 if (output_paragraph[temp] == ' ')
|
|
2804 {
|
|
2805 /* Remove trailing whitespace from output. */
|
|
2806 while (temp && whitespace (output_paragraph[temp - 1]))
|
|
2807 temp--;
|
|
2808
|
|
2809 output_paragraph[temp++] = '\n';
|
|
2810
|
|
2811 /* We have correctly broken the line where we want
|
|
2812 to. What we don't want is spaces following where
|
|
2813 we have decided to break the line. We get rid of
|
|
2814 them. */
|
|
2815 {
|
|
2816 int t1 = temp;
|
|
2817
|
|
2818 for (;; t1++)
|
|
2819 {
|
|
2820 if (t1 == output_paragraph_offset)
|
|
2821 {
|
|
2822 if (whitespace (character))
|
|
2823 suppress_insert = 1;
|
|
2824 break;
|
|
2825 }
|
|
2826 if (!whitespace (output_paragraph[t1]))
|
|
2827 break;
|
|
2828 }
|
|
2829
|
|
2830 if (t1 != temp)
|
|
2831 {
|
|
2832 adjust_braces_following (temp, (- (t1 - temp)));
|
|
2833 strncpy ((char *) &output_paragraph[temp],
|
|
2834 (char *) &output_paragraph[t1],
|
|
2835 (output_paragraph_offset - t1));
|
|
2836 output_paragraph_offset -= (t1 - temp);
|
|
2837 }
|
|
2838 }
|
|
2839
|
|
2840 /* Filled, but now indent if that is right. */
|
|
2841 if (indented_fill && current_indent)
|
|
2842 {
|
|
2843 int buffer_len = ((output_paragraph_offset - temp)
|
|
2844 + current_indent);
|
|
2845 char *temp_buffer = (char *)xmalloc (buffer_len);
|
|
2846 int indentation = 0;
|
|
2847
|
|
2848 /* We have to shift any markers that are in
|
|
2849 front of the wrap point. */
|
|
2850 adjust_braces_following (temp, current_indent);
|
|
2851
|
|
2852 while (current_indent > 0 &&
|
|
2853 indentation != current_indent)
|
|
2854 temp_buffer[indentation++] = ' ';
|
|
2855
|
|
2856 strncpy ((char *) &temp_buffer[current_indent],
|
|
2857 (char *) &output_paragraph[temp],
|
|
2858 buffer_len - current_indent);
|
|
2859
|
|
2860 if (output_paragraph_offset + buffer_len
|
|
2861 >= paragraph_buffer_len)
|
|
2862 {
|
|
2863 unsigned char *tt = xrealloc
|
|
2864 (output_paragraph,
|
|
2865 (paragraph_buffer_len += buffer_len));
|
|
2866 output_paragraph = tt;
|
|
2867 }
|
|
2868 strncpy ((char *) &output_paragraph[temp],
|
|
2869 temp_buffer, buffer_len);
|
|
2870 output_paragraph_offset += current_indent;
|
|
2871 free (temp_buffer);
|
|
2872 }
|
|
2873 output_column = 0;
|
|
2874 while (temp < output_paragraph_offset)
|
|
2875 output_column +=
|
|
2876 get_char_len (output_paragraph[temp++]);
|
|
2877 output_column += len;
|
|
2878 break;
|
|
2879 }
|
|
2880 }
|
|
2881 }
|
|
2882 }
|
|
2883
|
|
2884 if (!suppress_insert)
|
|
2885 {
|
|
2886 insert (character);
|
|
2887 last_inserted_character = character;
|
|
2888 }
|
|
2889 last_char_was_newline = 0;
|
|
2890 line_already_broken = 0;
|
|
2891 }
|
|
2892 }
|
|
2893 }
|
|
2894
|
|
2895 /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
|
|
2896 static void
|
|
2897 insert (int character)
|
|
2898 {
|
|
2899 output_paragraph[output_paragraph_offset++] = character;
|
|
2900 if (output_paragraph_offset == paragraph_buffer_len)
|
|
2901 {
|
|
2902 output_paragraph =
|
|
2903 xrealloc (output_paragraph, (paragraph_buffer_len += 100));
|
|
2904 }
|
|
2905 }
|
|
2906
|
2
|
2907 /* Remove upto COUNT characters of whitespace from the current output
|
|
2908 line. If COUNT is less than zero, then remove until none left. */
|
0
|
2909 static void
|
|
2910 kill_self_indent (int count)
|
|
2911 {
|
|
2912 /* Handle infinite case first. */
|
|
2913 if (count < 0)
|
|
2914 {
|
|
2915 output_column = 0;
|
|
2916 while (output_paragraph_offset)
|
|
2917 {
|
|
2918 if (whitespace (output_paragraph[output_paragraph_offset - 1]))
|
|
2919 output_paragraph_offset--;
|
|
2920 else
|
|
2921 break;
|
|
2922 }
|
|
2923 }
|
|
2924 else
|
|
2925 {
|
|
2926 while (output_paragraph_offset && count--)
|
|
2927 if (whitespace (output_paragraph[output_paragraph_offset - 1]))
|
|
2928 output_paragraph_offset--;
|
|
2929 else
|
|
2930 break;
|
|
2931 }
|
|
2932 }
|
|
2933
|
|
2934 /* Non-zero means do not honor calls to flush_output (). */
|
|
2935 static int flushing_ignored = 0;
|
|
2936
|
|
2937 /* Prevent calls to flush_output () from having any effect. */
|
|
2938 static void
|
|
2939 inhibit_output_flushing (void)
|
|
2940 {
|
|
2941 flushing_ignored++;
|
|
2942 }
|
|
2943
|
|
2944 /* Allow calls to flush_output () to write the paragraph data. */
|
|
2945 static void
|
|
2946 uninhibit_output_flushing (void)
|
|
2947 {
|
|
2948 flushing_ignored--;
|
|
2949 }
|
|
2950
|
|
2951 static void
|
|
2952 flush_output (void)
|
|
2953 {
|
|
2954 register int i;
|
|
2955
|
|
2956 if (!output_paragraph_offset || flushing_ignored)
|
|
2957 return;
|
|
2958
|
|
2959 for (i = 0; i < output_paragraph_offset; i++)
|
|
2960 {
|
|
2961 if (output_paragraph[i] == (unsigned char)(' ' | 0x80) ||
|
|
2962 output_paragraph[i] == (unsigned char)('\t' | 0x80) ||
|
|
2963 output_paragraph[i] == (unsigned char)('\n' | 0x80) ||
|
|
2964 sentence_ender (UNMETA (output_paragraph[i])))
|
|
2965 output_paragraph[i] &= 0x7f;
|
|
2966 }
|
|
2967
|
|
2968 fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
|
|
2969
|
|
2970 output_position += output_paragraph_offset;
|
|
2971 output_paragraph_offset = 0;
|
|
2972 }
|
|
2973
|
|
2974 /* How to close a paragraph controlling the number of lines between
|
|
2975 this one and the last one. */
|
|
2976
|
|
2977 /* Paragraph spacing is controlled by this variable. It is the number of
|
|
2978 blank lines that you wish to appear between paragraphs. A value of
|
|
2979 1 creates a single blank line between paragraphs. */
|
|
2980 int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
|
|
2981
|
|
2982 /* Close the current paragraph, leaving no blank lines between them. */
|
|
2983 static void
|
|
2984 close_single_paragraph (void)
|
|
2985 {
|
|
2986 close_paragraph_with_lines (0);
|
|
2987 }
|
|
2988
|
|
2989 /* Close a paragraph after an insertion has ended. */
|
|
2990 static void
|
|
2991 close_insertion_paragraph (void)
|
|
2992 {
|
|
2993 if (!insertion_paragraph_closed)
|
|
2994 {
|
|
2995 /* Close the current paragraph, breaking the line. */
|
|
2996 close_single_paragraph ();
|
|
2997
|
|
2998 /* Start a new paragraph here, inserting whatever indention is correct
|
|
2999 for the now current insertion level (one above the one that we are
|
|
3000 ending). */
|
|
3001 start_paragraph ();
|
|
3002
|
|
3003 /* Tell close_paragraph () that the previous line has already been
|
|
3004 broken, so it should insert one less newline. */
|
|
3005 line_already_broken = 1;
|
|
3006
|
|
3007 /* Let functions such as add_char () know that we have already found a
|
|
3008 newline. */
|
|
3009 ignore_blank_line ();
|
|
3010 }
|
|
3011 else
|
|
3012 {
|
|
3013 /* If the insertion paragraph is closed already, then we are seeing
|
|
3014 two `@end' commands in a row. Note that the first one we saw was
|
|
3015 handled in the first part of this if-then-else clause, and at that
|
|
3016 time start_paragraph () was called, partially to handle the proper
|
|
3017 indentation of the current line. However, the indentation level
|
|
3018 may have just changed again, so we may have to outdent the current
|
|
3019 line to the new indentation level. */
|
|
3020 if (current_indent < output_column)
|
|
3021 kill_self_indent (output_column - current_indent);
|
|
3022 }
|
|
3023
|
|
3024 insertion_paragraph_closed = 1;
|
|
3025 }
|
|
3026
|
|
3027 static void
|
|
3028 close_paragraph_with_lines (int lines)
|
|
3029 {
|
|
3030 int old_spacing = paragraph_spacing;
|
|
3031 paragraph_spacing = lines;
|
|
3032 close_paragraph ();
|
|
3033 paragraph_spacing = old_spacing;
|
|
3034 }
|
|
3035
|
|
3036 /* Close the currently open paragraph. */
|
|
3037 static void
|
|
3038 close_paragraph (void)
|
|
3039 {
|
|
3040 register int i;
|
|
3041
|
|
3042 /* The insertion paragraph is no longer closed. */
|
|
3043 insertion_paragraph_closed = 0;
|
|
3044
|
|
3045 if (paragraph_is_open && !must_start_paragraph)
|
|
3046 {
|
|
3047 register int tindex, c;
|
|
3048
|
|
3049 tindex = output_paragraph_offset;
|
|
3050
|
|
3051 /* Back up to last non-newline/space character, forcing all such
|
|
3052 subsequent characters to be newlines. This isn't strictly
|
|
3053 necessary, but a couple of functions use the presence of a newline
|
|
3054 to make decisions. */
|
|
3055 for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
|
|
3056 {
|
|
3057 c = output_paragraph[tindex];
|
|
3058
|
|
3059 if (c == ' '|| c == '\n')
|
|
3060 output_paragraph[tindex] = '\n';
|
|
3061 else
|
|
3062 break;
|
|
3063 }
|
|
3064
|
|
3065 /* All trailing whitespace is ignored. */
|
|
3066 output_paragraph_offset = ++tindex;
|
|
3067
|
|
3068 /* Break the line if that is appropriate. */
|
|
3069 if (paragraph_spacing >= 0)
|
|
3070 insert ('\n');
|
|
3071
|
|
3072 /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
|
|
3073 if (!force_flush_right)
|
|
3074 {
|
|
3075 for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
|
|
3076 insert ('\n');
|
|
3077 }
|
|
3078
|
|
3079 /* If we are doing flush right indentation, then do it now
|
|
3080 on the paragraph (really a single line). */
|
|
3081 if (force_flush_right)
|
|
3082 do_flush_right_indentation ();
|
|
3083
|
|
3084 flush_output ();
|
|
3085 paragraph_is_open = 0;
|
|
3086 no_indent = 0;
|
|
3087 output_column = 0;
|
|
3088 }
|
|
3089 ignore_blank_line ();
|
|
3090 }
|
|
3091
|
|
3092 /* Make the last line just read look as if it were only a newline. */
|
|
3093 static void
|
|
3094 ignore_blank_line (void)
|
|
3095 {
|
|
3096 last_inserted_character = '\n';
|
|
3097 last_char_was_newline = 1;
|
|
3098 }
|
|
3099
|
|
3100 /* Align the end of the text in output_paragraph with fill_column. */
|
|
3101 static void
|
|
3102 do_flush_right_indentation (void)
|
|
3103 {
|
|
3104 char *temp;
|
|
3105 int temp_len;
|
|
3106
|
|
3107 kill_self_indent (-1);
|
|
3108
|
|
3109 if (output_paragraph[0] != '\n')
|
|
3110 {
|
|
3111 output_paragraph[output_paragraph_offset] = '\0';
|
|
3112
|
|
3113 if (output_paragraph_offset < fill_column)
|
|
3114 {
|
|
3115 register int i;
|
|
3116
|
|
3117 if (fill_column >= paragraph_buffer_len)
|
|
3118 output_paragraph =
|
|
3119 xrealloc (output_paragraph,
|
|
3120 (paragraph_buffer_len += fill_column));
|
|
3121
|
|
3122 temp_len = strlen ((char *)output_paragraph);
|
|
3123 temp = (char *)xmalloc (temp_len + 1);
|
|
3124 memcpy (temp, (char *)output_paragraph, temp_len);
|
|
3125
|
|
3126 for (i = 0; i < fill_column - output_paragraph_offset; i++)
|
|
3127 output_paragraph[i] = ' ';
|
|
3128
|
|
3129 memcpy ((char *)output_paragraph + i, temp, temp_len);
|
|
3130 free (temp);
|
|
3131 output_paragraph_offset = fill_column;
|
|
3132 }
|
|
3133 }
|
|
3134 }
|
|
3135
|
|
3136 /* Begin a new paragraph. */
|
|
3137 static void
|
|
3138 start_paragraph (void)
|
|
3139 {
|
|
3140 /* First close existing one. */
|
|
3141 if (paragraph_is_open)
|
|
3142 close_paragraph ();
|
|
3143
|
|
3144 /* In either case, the insertion paragraph is no longer closed. */
|
|
3145 insertion_paragraph_closed = 0;
|
|
3146
|
|
3147 /* However, the paragraph is open! */
|
|
3148 paragraph_is_open = 1;
|
|
3149
|
|
3150 /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
|
|
3151 had to be called before we would allow any other paragraph operations
|
|
3152 to have an effect. */
|
|
3153 if (!must_start_paragraph)
|
|
3154 {
|
|
3155 int amount_to_indent = 0;
|
|
3156
|
|
3157 /* If doing indentation, then insert the appropriate amount. */
|
|
3158 if (!no_indent)
|
|
3159 {
|
|
3160 if (inhibit_paragraph_indentation)
|
|
3161 {
|
|
3162 amount_to_indent = current_indent;
|
|
3163 if (inhibit_paragraph_indentation < 0)
|
|
3164 inhibit_paragraph_indentation++;
|
|
3165 }
|
|
3166 else if (paragraph_start_indent < 0)
|
|
3167 amount_to_indent = current_indent;
|
|
3168 else
|
|
3169 amount_to_indent = current_indent + paragraph_start_indent;
|
|
3170
|
|
3171 if (amount_to_indent >= output_column)
|
|
3172 {
|
|
3173 amount_to_indent -= output_column;
|
|
3174 indent (amount_to_indent);
|
|
3175 output_column += amount_to_indent;
|
|
3176 }
|
|
3177 }
|
|
3178 }
|
|
3179 else
|
|
3180 must_start_paragraph = 0;
|
|
3181 }
|
|
3182
|
|
3183 /* Insert the indentation specified by AMOUNT. */
|
|
3184 static void
|
|
3185 indent (int amount)
|
|
3186 {
|
|
3187 register BRACE_ELEMENT *elt = brace_stack;
|
|
3188
|
|
3189 /* For every START_POS saved within the brace stack which will be affected
|
|
3190 by this indentation, bump that start pos forward. */
|
|
3191 while (elt)
|
|
3192 {
|
|
3193 if (elt->pos >= output_paragraph_offset)
|
|
3194 elt->pos += amount;
|
|
3195 elt = elt->next;
|
|
3196 }
|
|
3197
|
|
3198 while (--amount >= 0)
|
|
3199 insert (' ');
|
|
3200 }
|
|
3201
|
|
3202 /* Search forward for STRING in input_text.
|
2
|
3203 FROM says where to start. */
|
0
|
3204 static int
|
|
3205 search_forward (char *string, int from)
|
|
3206 {
|
|
3207 int len = strlen (string);
|
|
3208
|
|
3209 while (from < size_of_input_text)
|
|
3210 {
|
|
3211 if (strncmp (input_text + from, string, len) == 0)
|
|
3212 return (from);
|
|
3213 from++;
|
|
3214 }
|
|
3215 return (-1);
|
|
3216 }
|
|
3217
|
|
3218 /* Whoops, Unix doesn't have strcasecmp. */
|
|
3219
|
|
3220 /* Case independent string compare. */
|
|
3221 #if !defined (HAVE_STRCASECMP)
|
|
3222 static int
|
|
3223 strcasecmp (char *string1, char *string2)
|
|
3224 {
|
|
3225 char ch1, ch2;
|
|
3226
|
|
3227 for (;;)
|
|
3228 {
|
|
3229 ch1 = *string1++;
|
|
3230 ch2 = *string2++;
|
|
3231
|
|
3232 if (!(ch1 | ch2))
|
|
3233 return (0);
|
|
3234
|
|
3235 ch1 = coerce_to_upper (ch1);
|
|
3236 ch2 = coerce_to_upper (ch2);
|
|
3237
|
|
3238 if (ch1 != ch2)
|
|
3239 return (ch1 - ch2);
|
|
3240 }
|
|
3241 }
|
|
3242 #endif /* !HAVE_STRCASECMP */
|
|
3243
|
|
3244 enum insertion_type { menu, quotation, lisp, smalllisp, example,
|
|
3245 smallexample, display, itemize, format, enumerate, cartouche, table,
|
|
3246 ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
|
|
3247 defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
|
|
3248 deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
|
|
3249 deftypemethod, deftp, bad_type };
|
|
3250
|
|
3251 char *insertion_type_names[] = { "menu", "quotation", "lisp",
|
|
3252 "smalllisp", "example", "smallexample", "display", "itemize",
|
|
3253 "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
|
|
3254 "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
|
|
3255 "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
|
|
3256 "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
|
|
3257 "defivar", "defop", "defmethod", "deftypemethod", "deftp",
|
|
3258 "bad_type" };
|
|
3259
|
|
3260 int insertion_level = 0;
|
|
3261 typedef struct istack_elt
|
|
3262 {
|
|
3263 struct istack_elt *next;
|
|
3264 char *item_function;
|
|
3265 char *filename;
|
|
3266 int line_number;
|
|
3267 int filling_enabled;
|
|
3268 int indented_fill;
|
|
3269 enum insertion_type insertion;
|
|
3270 int inhibited;
|
|
3271 } INSERTION_ELT;
|
|
3272
|
|
3273 INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
|
|
3274
|
|
3275 static void
|
|
3276 init_insertion_stack (void)
|
|
3277 {
|
|
3278 insertion_stack = (INSERTION_ELT *) NULL;
|
|
3279 }
|
|
3280
|
|
3281 /* Return the type of the current insertion. */
|
|
3282 static enum insertion_type
|
|
3283 current_insertion_type (void)
|
|
3284 {
|
|
3285 if (!insertion_level)
|
|
3286 return (bad_type);
|
|
3287 else
|
|
3288 return (insertion_stack->insertion);
|
|
3289 }
|
|
3290
|
|
3291 /* Return a pointer to the string which is the function to wrap around
|
|
3292 items. */
|
|
3293 static char *
|
|
3294 current_item_function (void)
|
|
3295 {
|
|
3296 register int level, done;
|
|
3297 register INSERTION_ELT *elt;
|
|
3298
|
|
3299 level = insertion_level;
|
|
3300 elt = insertion_stack;
|
|
3301 done = 0;
|
|
3302
|
|
3303 /* Skip down through the stack until we find a non-conditional insertion. */
|
|
3304 while (!done && (elt != NULL))
|
|
3305 {
|
|
3306 switch (elt->insertion)
|
|
3307 {
|
|
3308 case ifinfo:
|
|
3309 case ifset:
|
|
3310 case ifclear:
|
|
3311 case cartouche:
|
|
3312 elt = elt->next;
|
|
3313 level--;
|
|
3314 break;
|
|
3315
|
|
3316 default:
|
|
3317 done = 1;
|
|
3318 }
|
|
3319 }
|
|
3320
|
|
3321 if (!level)
|
|
3322 return ((char *) NULL);
|
|
3323 else
|
|
3324 return (elt->item_function);
|
|
3325 }
|
|
3326
|
|
3327 static char *
|
|
3328 get_item_function (void)
|
|
3329 {
|
|
3330 char *item_function;
|
|
3331 get_rest_of_line (&item_function);
|
|
3332 backup_input_pointer ();
|
|
3333 canon_white (item_function);
|
|
3334 return (item_function);
|
|
3335 }
|
|
3336
|
|
3337 /* Push the state of the current insertion on the stack. */
|
|
3338 static void
|
|
3339 push_insertion (enum insertion_type type, char *item_function)
|
|
3340 {
|
|
3341 INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
|
|
3342
|
|
3343 new->item_function = item_function;
|
|
3344 new->filling_enabled = filling_enabled;
|
|
3345 new->indented_fill = indented_fill;
|
|
3346 new->insertion = type;
|
|
3347 new->line_number = line_number;
|
|
3348 new->filename = strdup (input_filename);
|
|
3349 new->inhibited = inhibit_paragraph_indentation;
|
|
3350 new->next = insertion_stack;
|
|
3351 insertion_stack = new;
|
|
3352 insertion_level++;
|
|
3353 }
|
|
3354
|
|
3355 /* Pop the value on top of the insertion stack into the
|
|
3356 global variables. */
|
|
3357 static void
|
|
3358 pop_insertion (void)
|
|
3359 {
|
|
3360 INSERTION_ELT *temp = insertion_stack;
|
|
3361
|
|
3362 if (temp == (INSERTION_ELT *) NULL)
|
|
3363 return;
|
|
3364
|
|
3365 inhibit_paragraph_indentation = temp->inhibited;
|
|
3366 filling_enabled = temp->filling_enabled;
|
|
3367 indented_fill = temp->indented_fill;
|
|
3368 free_and_clear (&(temp->item_function));
|
|
3369 free_and_clear (&(temp->filename));
|
|
3370 insertion_stack = insertion_stack->next;
|
|
3371 free (temp);
|
|
3372 insertion_level--;
|
|
3373 }
|
|
3374
|
|
3375 /* Return a pointer to the print name of this
|
|
3376 enumerated type. */
|
|
3377 static char *
|
|
3378 insertion_type_pname (enum insertion_type type)
|
|
3379 {
|
|
3380 if ((int) type < (int) bad_type)
|
|
3381 return (insertion_type_names[(int) type]);
|
|
3382 else
|
|
3383 return ("Broken-Type in insertion_type_pname");
|
|
3384 }
|
|
3385
|
|
3386 /* Return the insertion_type associated with NAME.
|
|
3387 If the type is not one of the known ones, return BAD_TYPE. */
|
|
3388 static enum insertion_type
|
|
3389 find_type_from_name (char *name)
|
|
3390 {
|
|
3391 int indecks = 0;
|
|
3392 while (indecks < (int) bad_type)
|
|
3393 {
|
|
3394 if (strcmp (name, insertion_type_names[indecks]) == 0)
|
|
3395 return (enum insertion_type) indecks;
|
|
3396 indecks++;
|
|
3397 }
|
|
3398 return (bad_type);
|
|
3399 }
|
|
3400
|
|
3401 static void
|
|
3402 do_nothing (void)
|
|
3403 {
|
|
3404 }
|
|
3405
|
|
3406 static int
|
|
3407 defun_insertion (enum insertion_type type)
|
|
3408 {
|
|
3409 return
|
|
3410 ((type == deffn)
|
|
3411 || (type == defun)
|
|
3412 || (type == defmac)
|
|
3413 || (type == defspec)
|
|
3414 || (type == defvr)
|
|
3415 || (type == defvar)
|
|
3416 || (type == defopt)
|
|
3417 || (type == deftypefn)
|
|
3418 || (type == deftypefun)
|
|
3419 || (type == deftypevr)
|
|
3420 || (type == deftypevar)
|
|
3421 || (type == defcv)
|
|
3422 || (type == defivar)
|
|
3423 || (type == defop)
|
|
3424 || (type == defmethod)
|
|
3425 || (type == deftypemethod)
|
|
3426 || (type == deftp));
|
|
3427 }
|
|
3428
|
|
3429 /* MAX_NS is the maximum nesting level for enumerations. I picked 100
|
|
3430 which seemed reasonable. This doesn't control the number of items,
|
|
3431 just the number of nested lists. */
|
|
3432 #define max_stack_depth 100
|
|
3433 #define ENUM_DIGITS 1
|
|
3434 #define ENUM_ALPHA 2
|
|
3435 typedef struct {
|
|
3436 int enumtype;
|
|
3437 int enumval;
|
|
3438 } DIGIT_ALPHA;
|
|
3439
|
|
3440 DIGIT_ALPHA enumstack[max_stack_depth];
|
|
3441 int enumstack_offset = 0;
|
|
3442 int current_enumval = 1;
|
|
3443 int current_enumtype = ENUM_DIGITS;
|
|
3444 char *enumeration_arg = (char *)NULL;
|
|
3445
|
|
3446 static void
|
|
3447 start_enumerating (int at, int type)
|
|
3448 {
|
|
3449 if ((enumstack_offset + 1) == max_stack_depth)
|
|
3450 {
|
|
3451 line_error ("Enumeration stack overflow");
|
|
3452 return;
|
|
3453 }
|
|
3454 enumstack[enumstack_offset].enumtype = current_enumtype;
|
|
3455 enumstack[enumstack_offset].enumval = current_enumval;
|
|
3456 enumstack_offset++;
|
|
3457 current_enumval = at;
|
|
3458 current_enumtype = type;
|
|
3459 }
|
|
3460
|
|
3461 static void
|
|
3462 stop_enumerating (void)
|
|
3463 {
|
|
3464 --enumstack_offset;
|
|
3465 if (enumstack_offset < 0)
|
|
3466 enumstack_offset = 0;
|
|
3467
|
|
3468 current_enumval = enumstack[enumstack_offset].enumval;
|
|
3469 current_enumtype = enumstack[enumstack_offset].enumtype;
|
|
3470 }
|
|
3471
|
|
3472 /* Place a letter or digits into the output stream. */
|
|
3473 static void
|
|
3474 enumerate_item (void)
|
|
3475 {
|
|
3476 char temp[10];
|
|
3477
|
|
3478 if (current_enumtype == ENUM_ALPHA)
|
|
3479 {
|
|
3480 if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
|
|
3481 {
|
|
3482 current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
|
|
3483 warning ("Lettering overflow, restarting at %c", current_enumval);
|
|
3484 }
|
|
3485 sprintf (temp, "%c. ", current_enumval);
|
|
3486 }
|
|
3487 else
|
|
3488 sprintf (temp, "%d. ", current_enumval);
|
|
3489
|
|
3490 indent (output_column += (current_indent - strlen (temp)));
|
|
3491 add_word (temp);
|
|
3492 current_enumval++;
|
|
3493 }
|
|
3494
|
|
3495 /* This is where the work for all the "insertion" style
|
|
3496 commands is done. A huge switch statement handles the
|
|
3497 various setups, and generic code is on both sides. */
|
|
3498 static void
|
|
3499 begin_insertion (enum insertion_type type)
|
|
3500 {
|
|
3501 int no_discard = 0;
|
|
3502
|
|
3503 if (defun_insertion (type))
|
|
3504 {
|
|
3505 push_insertion (type, strdup (""));
|
|
3506 no_discard++;
|
|
3507 }
|
|
3508 else
|
|
3509 push_insertion (type, get_item_function ());
|
|
3510
|
|
3511 switch (type)
|
|
3512 {
|
|
3513 case menu:
|
|
3514 if (!no_headers)
|
|
3515 close_paragraph ();
|
|
3516
|
|
3517 filling_enabled = no_indent = 0;
|
|
3518 inhibit_paragraph_indentation = 1;
|
|
3519
|
|
3520 if (!no_headers)
|
|
3521 add_word ("* Menu:\n");
|
|
3522
|
|
3523 in_menu++;
|
|
3524 no_discard++;
|
|
3525 break;
|
|
3526
|
|
3527 /* I think @quotation is meant to do filling.
|
|
3528 If you don't want filling, then use @example. */
|
|
3529 case quotation:
|
|
3530 close_single_paragraph ();
|
|
3531 last_char_was_newline = no_indent = 0;
|
|
3532 indented_fill = filling_enabled = 1;
|
|
3533 inhibit_paragraph_indentation = 1;
|
|
3534 current_indent += default_indentation_increment;
|
|
3535 break;
|
|
3536
|
|
3537 case display:
|
|
3538 case example:
|
|
3539 case smallexample:
|
|
3540 case lisp:
|
|
3541 case smalllisp:
|
|
3542 /* Just like @example, but no indentation. */
|
|
3543 case format:
|
|
3544
|
|
3545 close_single_paragraph ();
|
|
3546 inhibit_paragraph_indentation = 1;
|
|
3547 in_fixed_width_font++;
|
|
3548 filling_enabled = 0;
|
|
3549 last_char_was_newline = 0;
|
|
3550
|
|
3551 if (type != format)
|
|
3552 current_indent += default_indentation_increment;
|
|
3553
|
|
3554 break;
|
|
3555
|
|
3556 case table:
|
|
3557 case ftable:
|
|
3558 case vtable:
|
|
3559 case itemize:
|
|
3560 close_single_paragraph ();
|
|
3561 current_indent += default_indentation_increment;
|
|
3562 filling_enabled = indented_fill = 1;
|
|
3563 #if defined (INDENT_PARAGRAPHS_IN_TABLE)
|
|
3564 inhibit_paragraph_indentation = 0;
|
|
3565 #else
|
|
3566 inhibit_paragraph_indentation = 1;
|
|
3567 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
3568
|
|
3569 /* Make things work for losers who forget the itemize syntax. */
|
|
3570 if (allow_lax_format && (type == itemize))
|
|
3571 {
|
|
3572 if (!(*insertion_stack->item_function))
|
|
3573 {
|
|
3574 free (insertion_stack->item_function);
|
|
3575 insertion_stack->item_function = strdup ("@bullet");
|
|
3576 insertion_stack->item_function[0] = COMMAND_PREFIX;
|
|
3577 }
|
|
3578 }
|
|
3579
|
|
3580 if (!*insertion_stack->item_function)
|
|
3581 {
|
|
3582 line_error ("%s requires an argument: the formatter for %citem",
|
|
3583 insertion_type_pname (type), COMMAND_PREFIX);
|
|
3584 }
|
|
3585 break;
|
|
3586
|
|
3587 case enumerate:
|
|
3588 close_single_paragraph ();
|
|
3589 no_indent = 0;
|
|
3590 #if defined (INDENT_PARAGRAPHS_IN_TABLE)
|
|
3591 inhibit_paragraph_indentation = 0;
|
|
3592 #else
|
|
3593 inhibit_paragraph_indentation = 1;
|
|
3594 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
3595
|
|
3596 current_indent += default_indentation_increment;
|
|
3597 filling_enabled = indented_fill = 1;
|
|
3598
|
|
3599 if (isdigit (*enumeration_arg))
|
|
3600 start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
|
|
3601 else
|
|
3602 start_enumerating (*enumeration_arg, ENUM_ALPHA);
|
|
3603 break;
|
|
3604
|
|
3605 /* Does nothing special in makeinfo. */
|
|
3606 case group:
|
|
3607 /* Only close the paragraph if we are not inside of an @example. */
|
|
3608 if (!insertion_stack->next ||
|
|
3609 insertion_stack->next->insertion != example)
|
|
3610 close_single_paragraph ();
|
|
3611 break;
|
|
3612
|
|
3613 /* Insertions that are no-ops in info, but do something in TeX. */
|
|
3614 case ifinfo:
|
|
3615 case ifset:
|
|
3616 case ifclear:
|
|
3617 case cartouche:
|
|
3618 if (in_menu)
|
|
3619 no_discard++;
|
|
3620 break;
|
|
3621
|
|
3622 case deffn:
|
|
3623 case defun:
|
|
3624 case defmac:
|
|
3625 case defspec:
|
|
3626 case defvr:
|
|
3627 case defvar:
|
|
3628 case defopt:
|
|
3629 case deftypefn:
|
|
3630 case deftypefun:
|
|
3631 case deftypevr:
|
|
3632 case deftypevar:
|
|
3633 case defcv:
|
|
3634 case defivar:
|
|
3635 case defop:
|
|
3636 case defmethod:
|
|
3637 case deftypemethod:
|
|
3638 case deftp:
|
|
3639 inhibit_paragraph_indentation = 1;
|
|
3640 filling_enabled = indented_fill = 1;
|
|
3641 current_indent += default_indentation_increment;
|
|
3642 no_indent = 0;
|
|
3643 break;
|
|
3644
|
|
3645 case flushleft:
|
|
3646 close_single_paragraph ();
|
|
3647 inhibit_paragraph_indentation = 1;
|
|
3648 filling_enabled = indented_fill = no_indent = 0;
|
|
3649 break;
|
|
3650
|
|
3651 case flushright:
|
|
3652 close_single_paragraph ();
|
|
3653 filling_enabled = indented_fill = no_indent = 0;
|
|
3654 inhibit_paragraph_indentation = 1;
|
|
3655 force_flush_right++;
|
|
3656 break;
|
|
3657
|
|
3658 default:
|
|
3659 break;
|
|
3660 }
|
|
3661
|
|
3662 if (!no_discard)
|
|
3663 discard_until ("\n");
|
|
3664 }
|
|
3665
|
|
3666 /* Try to end the insertion with the specified TYPE.
|
|
3667 TYPE, with a value of bad_type, gets translated to match
|
|
3668 the value currently on top of the stack.
|
|
3669 Otherwise, if TYPE doesn't match the top of the insertion stack,
|
|
3670 give error. */
|
|
3671 static void
|
|
3672 end_insertion (enum insertion_type type)
|
|
3673 {
|
|
3674 enum insertion_type temp_type;
|
|
3675
|
|
3676 if (!insertion_level)
|
|
3677 return;
|
|
3678
|
|
3679 temp_type = current_insertion_type ();
|
|
3680
|
|
3681 if (type == bad_type)
|
|
3682 type = temp_type;
|
|
3683
|
|
3684 if (type != temp_type)
|
|
3685 {
|
|
3686 line_error
|
|
3687 ("`%cend' expected `%s', but saw `%s'", COMMAND_PREFIX,
|
|
3688 insertion_type_pname (temp_type), insertion_type_pname (type));
|
|
3689 return;
|
|
3690 }
|
|
3691
|
|
3692 pop_insertion ();
|
|
3693
|
|
3694 switch (type)
|
|
3695 {
|
|
3696 /* Insertions which have no effect on paragraph formatting. */
|
|
3697 case ifinfo:
|
|
3698 case ifset:
|
|
3699 case ifclear:
|
|
3700 break;
|
|
3701
|
|
3702 case menu:
|
|
3703 in_menu--; /* No longer hacking menus. */
|
|
3704 if (!no_headers)
|
|
3705 close_insertion_paragraph ();
|
|
3706 break;
|
|
3707
|
|
3708 case enumerate:
|
|
3709 stop_enumerating ();
|
|
3710 close_insertion_paragraph ();
|
|
3711 current_indent -= default_indentation_increment;
|
|
3712 break;
|
|
3713
|
|
3714 case flushleft:
|
|
3715 case group:
|
|
3716 case cartouche:
|
|
3717 close_insertion_paragraph ();
|
|
3718 break;
|
|
3719
|
|
3720 case format:
|
|
3721 case display:
|
|
3722 case example:
|
|
3723 case smallexample:
|
|
3724 case lisp:
|
|
3725 case smalllisp:
|
|
3726 case quotation:
|
|
3727
|
|
3728 /* @quotation is the only one of the above without a fixed width
|
|
3729 font. */
|
|
3730 if (type != quotation)
|
|
3731 in_fixed_width_font--;
|
|
3732
|
|
3733 /* @format is the only fixed_width insertion without a change
|
|
3734 in indentation. */
|
|
3735 if (type != format)
|
|
3736 current_indent -= default_indentation_increment;
|
|
3737
|
|
3738 /* The ending of one of these insertions always marks the
|
|
3739 start of a new paragraph. */
|
|
3740 close_insertion_paragraph ();
|
|
3741 break;
|
|
3742
|
|
3743 case table:
|
|
3744 case ftable:
|
|
3745 case vtable:
|
|
3746 case itemize:
|
|
3747 current_indent -= default_indentation_increment;
|
|
3748 break;
|
|
3749
|
|
3750 case flushright:
|
|
3751 force_flush_right--;
|
|
3752 close_insertion_paragraph ();
|
|
3753 break;
|
|
3754
|
|
3755 /* Handle the @defun style insertions with a default clause. */
|
|
3756 default:
|
|
3757 current_indent -= default_indentation_increment;
|
|
3758 close_insertion_paragraph ();
|
|
3759 break;
|
|
3760 }
|
|
3761 }
|
|
3762
|
|
3763 /* Insertions cannot cross certain boundaries, such as node beginnings. In
|
|
3764 code that creates such boundaries, you should call discard_insertions ()
|
|
3765 before doing anything else. It prints the errors for you, and cleans up
|
|
3766 the insertion stack. */
|
|
3767 static void
|
|
3768 discard_insertions (void)
|
|
3769 {
|
|
3770 int real_line_number = line_number;
|
|
3771 while (insertion_stack)
|
|
3772 {
|
|
3773 if (insertion_stack->insertion == ifinfo ||
|
|
3774 insertion_stack->insertion == ifset ||
|
|
3775 insertion_stack->insertion == ifclear ||
|
|
3776 insertion_stack->insertion == cartouche)
|
|
3777 break;
|
|
3778 else
|
|
3779 {
|
|
3780 char *offender;
|
|
3781 char *current_filename;
|
|
3782
|
|
3783 current_filename = input_filename;
|
|
3784 offender = (char *)insertion_type_pname (insertion_stack->insertion);
|
|
3785 input_filename = insertion_stack->filename;
|
|
3786 line_number = insertion_stack->line_number;
|
|
3787 line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
|
|
3788 COMMAND_PREFIX, offender);
|
|
3789 input_filename = current_filename;
|
|
3790 pop_insertion ();
|
|
3791 }
|
|
3792 }
|
|
3793 line_number = real_line_number;
|
|
3794 }
|
|
3795
|
|
3796 /* The actual commands themselves. */
|
|
3797
|
|
3798 /* Commands which insert themselves. */
|
|
3799 static void
|
|
3800 insert_self (void)
|
|
3801 {
|
|
3802 add_word (command);
|
|
3803 }
|
|
3804
|
|
3805 /* Force a line break in the output. */
|
|
3806 static void
|
|
3807 cm_asterisk (void)
|
|
3808 {
|
|
3809 close_single_paragraph ();
|
|
3810 #if !defined (ASTERISK_NEW_PARAGRAPH)
|
|
3811 cm_noindent ();
|
|
3812 #endif /* ASTERISK_NEW_PARAGRAPH */
|
|
3813 }
|
|
3814
|
|
3815 /* Insert ellipsis. */
|
|
3816 static void
|
|
3817 cm_dots (int arg)
|
|
3818 {
|
|
3819 if (arg == START)
|
|
3820 add_word ("...");
|
|
3821 }
|
|
3822
|
|
3823 static void
|
|
3824 cm_bullet (int arg)
|
|
3825 {
|
|
3826 if (arg == START)
|
|
3827 add_char ('*');
|
|
3828 }
|
|
3829
|
|
3830 static void
|
|
3831 cm_minus (int arg)
|
|
3832 {
|
|
3833 if (arg == START)
|
|
3834 add_char ('-');
|
|
3835 }
|
|
3836
|
|
3837 /* Insert "TeX". */
|
|
3838 static void
|
|
3839 cm_TeX (int arg)
|
|
3840 {
|
|
3841 if (arg == START)
|
|
3842 add_word ("TeX");
|
|
3843 }
|
|
3844
|
|
3845 static void
|
|
3846 cm_copyright (int arg)
|
|
3847 {
|
|
3848 if (arg == START)
|
|
3849 add_word ("(C)");
|
|
3850 }
|
|
3851
|
|
3852 #if defined (__osf__)
|
|
3853 #define LOCALTIME_CAST(x) (time_t *)(x)
|
|
3854 #else
|
|
3855 #define LOCALTIME_CAST(x) (x)
|
|
3856 #endif
|
|
3857
|
|
3858 static void
|
|
3859 cm_today (int arg)
|
|
3860 {
|
|
3861 static char * months [12] =
|
|
3862 { "January", "February", "March", "April", "May", "June", "July",
|
|
3863 "August", "September", "October", "November", "December" };
|
|
3864 if (arg == START)
|
|
3865 {
|
|
3866 long timer = time (0);
|
|
3867 struct tm *ts = localtime (LOCALTIME_CAST (&timer));
|
|
3868 add_word_args
|
|
3869 ("%d %s %d",
|
|
3870 (ts -> tm_mday),
|
|
3871 (months [ts -> tm_mon]),
|
|
3872 ((ts -> tm_year) + 1900));
|
|
3873 }
|
|
3874 }
|
|
3875
|
|
3876 static void
|
|
3877 cm_code (int arg)
|
|
3878 {
|
|
3879 extern int printing_index;
|
|
3880
|
|
3881 if (printing_index)
|
|
3882 return;
|
|
3883
|
|
3884 if (arg == START)
|
|
3885 {
|
|
3886 in_fixed_width_font++;
|
|
3887 add_char ('`');
|
|
3888 }
|
|
3889 else
|
|
3890 {
|
|
3891 add_word ("'");
|
|
3892 in_fixed_width_font--;
|
|
3893 }
|
|
3894 }
|
|
3895
|
|
3896 static void
|
|
3897 cm_samp (int arg)
|
|
3898 {
|
|
3899 cm_code (arg);
|
|
3900 }
|
|
3901
|
|
3902 static void
|
|
3903 cm_file (int arg)
|
|
3904 {
|
|
3905 cm_code (arg);
|
|
3906 }
|
|
3907
|
|
3908 static void
|
|
3909 cm_kbd (int arg)
|
|
3910 {
|
|
3911 cm_code (arg);
|
|
3912 }
|
|
3913
|
|
3914 static void
|
|
3915 cm_key (int arg)
|
|
3916 {
|
|
3917 }
|
|
3918
|
|
3919 /* Convert the character at position into CTL. */
|
|
3920 static void
|
|
3921 cm_ctrl (int arg, int start, int end)
|
|
3922 {
|
|
3923 /* Should we allow multiple character arguments? I think yes. */
|
|
3924 if (arg == END)
|
|
3925 {
|
|
3926 register int i, character;
|
|
3927 #if defined (NO_MULTIPLE_CTRL)
|
|
3928 if ((end - start) != 1)
|
|
3929 line_error ("%c%s expects a single character as an argument",
|
|
3930 COMMAND_PREFIX, command);
|
|
3931 else
|
|
3932 #endif
|
|
3933 for (i = start; i < end; i++)
|
|
3934 {
|
|
3935 character = output_paragraph[i];
|
|
3936
|
|
3937 if (isletter (character))
|
|
3938 output_paragraph[i] = CTL (coerce_to_upper (character));
|
|
3939 }
|
|
3940 }
|
|
3941 }
|
|
3942
|
|
3943 /* Small Caps in makeinfo just does all caps. */
|
|
3944 static void
|
|
3945 cm_sc (int arg, int start_pos, int end_pos)
|
|
3946 {
|
|
3947 if (arg == END)
|
|
3948 {
|
|
3949 while (start_pos < end_pos)
|
|
3950 {
|
|
3951 output_paragraph[start_pos] =
|
|
3952 coerce_to_upper (output_paragraph[start_pos]);
|
|
3953 start_pos++;
|
|
3954 }
|
|
3955 }
|
|
3956 }
|
|
3957
|
|
3958 /* @var in makeinfo just uppercases the text. */
|
|
3959 static void
|
|
3960 cm_var (int arg, int start_pos, int end_pos)
|
|
3961 {
|
|
3962 if (arg == END)
|
|
3963 {
|
|
3964 while (start_pos < end_pos)
|
|
3965 {
|
|
3966 output_paragraph[start_pos] =
|
|
3967 coerce_to_upper (output_paragraph[start_pos]);
|
|
3968 start_pos++;
|
|
3969 }
|
|
3970 }
|
|
3971 }
|
|
3972
|
|
3973 static void
|
|
3974 cm_dfn (int arg, int position)
|
|
3975 {
|
|
3976 add_char ('"');
|
|
3977 }
|
|
3978
|
|
3979 static void
|
|
3980 cm_emph (int arg)
|
|
3981 {
|
|
3982 add_char ('*');
|
|
3983 }
|
|
3984
|
|
3985 static void
|
|
3986 cm_strong (int arg, int position)
|
|
3987 {
|
|
3988 cm_emph (arg);
|
|
3989 }
|
|
3990
|
|
3991 static void
|
|
3992 cm_cite (int arg, int position)
|
|
3993 {
|
|
3994 if (arg == START)
|
|
3995 add_word ("`");
|
|
3996 else
|
|
3997 add_word ("'");
|
|
3998 }
|
|
3999
|
|
4000 /* Current text is italicized. */
|
|
4001 static void
|
|
4002 cm_italic (int arg, int start, int end)
|
|
4003 {
|
|
4004 }
|
|
4005
|
|
4006 /* Current text is highlighted. */
|
|
4007 static void
|
|
4008 cm_bold (int arg, int start, int end)
|
|
4009 {
|
|
4010 cm_italic (arg, start, end);
|
|
4011 }
|
|
4012
|
|
4013 /* Current text is in roman font. */
|
|
4014 static void
|
|
4015 cm_roman (int arg, int start, int end)
|
|
4016 {
|
|
4017 }
|
|
4018
|
|
4019 /* Current text is in roman font. */
|
|
4020 static void
|
|
4021 cm_titlefont (int arg, int start, int end)
|
|
4022 {
|
|
4023 }
|
|
4024
|
|
4025 /* Italicize titles. */
|
|
4026 static void
|
|
4027 cm_title (int arg, int start, int end)
|
|
4028 {
|
|
4029 cm_italic (arg, start, end);
|
|
4030 }
|
|
4031
|
|
4032 /* @refill is a NOP. */
|
|
4033 static void
|
|
4034 cm_refill (void)
|
|
4035 {
|
|
4036 }
|
|
4037
|
|
4038 /* Prevent the argument from being split across two lines. */
|
|
4039 static void
|
|
4040 cm_w (int arg, int start, int end)
|
|
4041 {
|
|
4042 if (arg == START)
|
|
4043 non_splitting_words++;
|
|
4044 else
|
|
4045 non_splitting_words--;
|
|
4046 }
|
|
4047
|
|
4048
|
|
4049 /* Explain that this command is obsolete, thus the user shouldn't
|
|
4050 do anything with it. */
|
|
4051 static void
|
|
4052 cm_obsolete (int arg, int start, int end)
|
|
4053 {
|
|
4054 if (arg == START)
|
|
4055 warning ("The command `%c%s' is obsolete", COMMAND_PREFIX, command);
|
|
4056 }
|
|
4057
|
|
4058 /* Insert the text following input_text_offset up to the end of the line
|
|
4059 in a new, separate paragraph. Directly underneath it, insert a
|
|
4060 line of WITH_CHAR, the same length of the inserted text. */
|
|
4061 static void
|
|
4062 insert_and_underscore (int with_char)
|
|
4063 {
|
|
4064 register int i, len;
|
|
4065 int old_no_indent, starting_pos, ending_pos;
|
|
4066 char *temp;
|
|
4067
|
|
4068 close_paragraph ();
|
|
4069 filling_enabled = indented_fill = 0;
|
|
4070 old_no_indent = no_indent;
|
|
4071 no_indent = 1;
|
|
4072
|
|
4073 #if defined (HAVE_MACROS)
|
|
4074 if (macro_expansion_output_stream)
|
|
4075 append_to_expansion_output (input_text_offset + 1);
|
|
4076 #endif /* HAVE_MACROS */
|
|
4077
|
|
4078 get_rest_of_line (&temp);
|
|
4079
|
|
4080 starting_pos = output_position + output_paragraph_offset;
|
|
4081 #if defined (HAVE_MACROS)
|
|
4082 if (macro_expansion_output_stream)
|
|
4083 {
|
|
4084 char *temp1;
|
|
4085
|
|
4086 temp1 = (char *)xmalloc (2 + strlen (temp));
|
|
4087 sprintf (temp1, "%s\n", temp);
|
|
4088 remember_itext (input_text, input_text_offset);
|
|
4089 me_execute_string (temp1);
|
|
4090 free (temp1);
|
|
4091 }
|
|
4092 else
|
|
4093 #endif /* HAVE_MACROS */
|
|
4094 execute_string ("%s\n", temp);
|
|
4095
|
|
4096 ending_pos = output_position + output_paragraph_offset;
|
|
4097 free (temp);
|
|
4098
|
|
4099 len = (ending_pos - starting_pos) - 1;
|
|
4100 for (i = 0; i < len; i++)
|
|
4101 add_char (with_char);
|
|
4102 insert ('\n');
|
|
4103 close_paragraph ();
|
|
4104 filling_enabled = 1;
|
|
4105 no_indent = old_no_indent;
|
|
4106 }
|
|
4107
|
|
4108 /* Here is a structure which associates sectioning commands with
|
|
4109 an integer, hopefully to reflect the `depth' of the current
|
|
4110 section. */
|
|
4111 struct {
|
|
4112 char *name;
|
|
4113 int level;
|
|
4114 } section_alist[] = {
|
|
4115 { "unnumberedsubsubsec", 5 },
|
|
4116 { "unnumberedsubsec", 4 },
|
|
4117 { "unnumberedsec", 3 },
|
|
4118 { "unnumbered", 2 },
|
|
4119 { "appendixsubsubsec", 5 },
|
|
4120 { "appendixsubsec", 4 },
|
|
4121 { "appendixsec", 3 },
|
|
4122 { "appendixsection", 3 },
|
|
4123 { "appendix", 2 },
|
|
4124 { "subsubsec", 5 },
|
|
4125 { "subsubsection", 5 },
|
|
4126 { "subsection", 4 },
|
|
4127 { "section", 3 },
|
|
4128 { "chapter", 2 },
|
|
4129 { "top", 1 },
|
|
4130
|
|
4131 { (char *)NULL, 0 }
|
|
4132 };
|
|
4133
|
|
4134 /* Amount to offset the name of sectioning commands to levels by. */
|
|
4135 int section_alist_offset = 0;
|
|
4136
|
|
4137 /* Shift the meaning of @section to @chapter. */
|
|
4138 static void
|
|
4139 cm_raisesections (void)
|
|
4140 {
|
|
4141 discard_until ("\n");
|
|
4142 section_alist_offset--;
|
|
4143 }
|
|
4144
|
|
4145 /* Shift the meaning of @chapter to @section. */
|
|
4146 static void
|
|
4147 cm_lowersections (void)
|
|
4148 {
|
|
4149 discard_until ("\n");
|
|
4150 section_alist_offset++;
|
|
4151 }
|
|
4152
|
|
4153 /* Return an integer which identifies the type section present in TEXT. */
|
|
4154 static int
|
|
4155 what_section (char *text)
|
|
4156 {
|
|
4157 register int i, j;
|
|
4158 char *t;
|
|
4159
|
|
4160 find_section_command:
|
|
4161 for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
|
|
4162 if (text[j] != COMMAND_PREFIX)
|
|
4163 return (-1);
|
|
4164
|
|
4165 text = text + j + 1;
|
|
4166
|
|
4167 /* We skip @c, @comment, and @?index commands. */
|
|
4168 if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
|
|
4169 (text[0] == 'c' && cr_or_whitespace (text[1])) ||
|
|
4170 (strcmp (text + 1, "index") == 0))
|
|
4171 {
|
|
4172 while (*text++ != '\n');
|
|
4173 goto find_section_command;
|
|
4174 }
|
|
4175
|
|
4176 /* Handle italicized sectioning commands. */
|
|
4177 if (*text == 'i')
|
|
4178 text++;
|
|
4179
|
|
4180 for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
|
|
4181
|
|
4182 for (i = 0; (t = section_alist[i].name); i++)
|
|
4183 {
|
|
4184 if (j == strlen (t) && strncmp (t, text, j) == 0)
|
|
4185 {
|
|
4186 int return_val;
|
|
4187
|
|
4188 return_val = (section_alist[i].level + section_alist_offset);
|
|
4189
|
|
4190 if (return_val < 0)
|
|
4191 return_val = 0;
|
|
4192 else if (return_val > 5)
|
|
4193 return_val = 5;
|
|
4194 return (return_val);
|
|
4195 }
|
|
4196 }
|
|
4197 return (-1);
|
|
4198 }
|
|
4199
|
|
4200 /* Set the level of @top to LEVEL. Return the old level of @top. */
|
|
4201 static int
|
|
4202 set_top_section_level (int level)
|
|
4203 {
|
|
4204 register int i, result = -1;
|
|
4205
|
|
4206 for (i = 0; section_alist[i].name; i++)
|
|
4207 if (strcmp (section_alist[i].name, "top") == 0)
|
|
4208 {
|
|
4209 result = section_alist[i].level;
|
|
4210 section_alist[i].level = level;
|
|
4211 break;
|
|
4212 }
|
|
4213 return (result);
|
|
4214 }
|
|
4215
|
|
4216 /* Treat this just like @unnumbered. The only difference is
|
|
4217 in node defaulting. */
|
|
4218 static void
|
|
4219 cm_top (void)
|
|
4220 {
|
|
4221 /* It is an error to have more than one @top. */
|
|
4222 if (top_node_seen)
|
|
4223 {
|
|
4224 TAG_ENTRY *tag = tag_table;
|
|
4225
|
|
4226 line_error ("There already is a node having %ctop as a section",
|
|
4227 COMMAND_PREFIX);
|
|
4228
|
|
4229 while (tag != (TAG_ENTRY *)NULL)
|
|
4230 {
|
|
4231 if ((tag->flags & IS_TOP))
|
|
4232 {
|
|
4233 int old_line_number = line_number;
|
|
4234 char *old_input_filename = input_filename;
|
|
4235
|
|
4236 line_number = tag->line_no;
|
|
4237 input_filename = tag->filename;
|
|
4238 line_error ("Here is the %ctop node", COMMAND_PREFIX);
|
|
4239 input_filename = old_input_filename;
|
|
4240 line_number = old_line_number;
|
|
4241 return;
|
|
4242 }
|
|
4243 tag = tag->next_ent;
|
|
4244 }
|
|
4245 }
|
|
4246 else
|
|
4247 {
|
|
4248 top_node_seen = 1;
|
|
4249
|
|
4250 /* It is an error to use @top before you have used @node. */
|
|
4251 if (!tag_table)
|
|
4252 {
|
|
4253 char *top_name;
|
|
4254
|
|
4255 get_rest_of_line (&top_name);
|
|
4256 free (top_name);
|
|
4257 line_error ("%ctop used before %cnode, defaulting to %s",
|
|
4258 COMMAND_PREFIX, COMMAND_PREFIX, top_name);
|
|
4259 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
|
|
4260 return;
|
|
4261 }
|
|
4262
|
|
4263 cm_unnumbered ();
|
|
4264
|
|
4265 /* The most recently defined node is the top node. */
|
|
4266 tag_table->flags |= IS_TOP;
|
|
4267
|
|
4268 /* Now set the logical hierarchical level of the Top node. */
|
|
4269 {
|
|
4270 int orig_offset = input_text_offset;
|
|
4271
|
|
4272 input_text_offset = search_forward (node_search_string, orig_offset);
|
|
4273
|
|
4274 if (input_text_offset > 0)
|
|
4275 {
|
|
4276 int this_section;
|
|
4277
|
|
4278 /* We have encountered a non-top node, so mark that one exists. */
|
|
4279 non_top_node_seen = 1;
|
|
4280
|
|
4281 /* Move to the end of this line, and find out what the
|
|
4282 sectioning command is here. */
|
|
4283 while (input_text[input_text_offset] != '\n')
|
|
4284 input_text_offset++;
|
|
4285
|
|
4286 if (input_text_offset < size_of_input_text)
|
|
4287 input_text_offset++;
|
|
4288
|
|
4289 this_section = what_section (input_text + input_text_offset);
|
|
4290
|
|
4291 /* If we found a sectioning command, then give the top section
|
|
4292 a level of this section - 1. */
|
|
4293 if (this_section != -1)
|
|
4294 set_top_section_level (this_section - 1);
|
|
4295 }
|
|
4296 input_text_offset = orig_offset;
|
|
4297 }
|
|
4298 }
|
|
4299 }
|
|
4300
|
|
4301 /* Organized by level commands. That is, "*" == chapter, "=" == section. */
|
|
4302 char *scoring_characters = "*=-.";
|
|
4303
|
|
4304 static void
|
|
4305 sectioning_underscore (char *commanned)
|
|
4306 {
|
|
4307 char character;
|
|
4308 char *temp;
|
|
4309 int level;
|
|
4310
|
|
4311 temp = (char *)xmalloc (2 + strlen (commanned));
|
|
4312 temp[0] = COMMAND_PREFIX;
|
|
4313 strcpy (&temp[1], commanned);
|
|
4314 level = what_section (temp);
|
|
4315 free (temp);
|
|
4316 level -= 2;
|
|
4317
|
|
4318 if (level < 0)
|
|
4319 level = 0;
|
|
4320
|
|
4321 character = scoring_characters[level];
|
|
4322
|
|
4323 insert_and_underscore (character);
|
|
4324 }
|
|
4325
|
|
4326 /* The remainder of the text on this line is a chapter heading. */
|
|
4327 static void
|
|
4328 cm_chapter (void)
|
|
4329 {
|
|
4330 sectioning_underscore ("chapter");
|
|
4331 }
|
|
4332
|
|
4333 /* The remainder of the text on this line is a section heading. */
|
|
4334 static void
|
|
4335 cm_section (void)
|
|
4336 {
|
|
4337 sectioning_underscore ("section");
|
|
4338 }
|
|
4339
|
|
4340 /* The remainder of the text on this line is a subsection heading. */
|
|
4341 static void
|
|
4342 cm_subsection (void)
|
|
4343 {
|
|
4344 sectioning_underscore ("subsection");
|
|
4345 }
|
|
4346
|
|
4347 /* The remainder of the text on this line is a subsubsection heading. */
|
|
4348 static void
|
|
4349 cm_subsubsection (void)
|
|
4350 {
|
|
4351 sectioning_underscore ("subsubsection");
|
|
4352 }
|
|
4353
|
|
4354 /* The remainder of the text on this line is an unnumbered heading. */
|
|
4355 static void
|
|
4356 cm_unnumbered (void)
|
|
4357 {
|
|
4358 cm_chapter ();
|
|
4359 }
|
|
4360
|
|
4361 /* The remainder of the text on this line is an unnumbered section heading. */
|
|
4362 static void
|
|
4363 cm_unnumberedsec (void)
|
|
4364 {
|
|
4365 cm_section ();
|
|
4366 }
|
|
4367
|
|
4368 /* The remainder of the text on this line is an unnumbered
|
|
4369 subsection heading. */
|
|
4370 static void
|
|
4371 cm_unnumberedsubsec (void)
|
|
4372 {
|
|
4373 cm_subsection ();
|
|
4374 }
|
|
4375
|
|
4376 /* The remainder of the text on this line is an unnumbered
|
|
4377 subsubsection heading. */
|
|
4378 static void
|
|
4379 cm_unnumberedsubsubsec (void)
|
|
4380 {
|
|
4381 cm_subsubsection ();
|
|
4382 }
|
|
4383
|
|
4384 /* The remainder of the text on this line is an appendix heading. */
|
|
4385 static void
|
|
4386 cm_appendix (void)
|
|
4387 {
|
|
4388 cm_chapter ();
|
|
4389 }
|
|
4390
|
|
4391 /* The remainder of the text on this line is an appendix section heading. */
|
|
4392 static void
|
|
4393 cm_appendixsec (void)
|
|
4394 {
|
|
4395 cm_section ();
|
|
4396 }
|
|
4397
|
|
4398 /* The remainder of the text on this line is an appendix subsection heading. */
|
|
4399 static void
|
|
4400 cm_appendixsubsec (void)
|
|
4401 {
|
|
4402 cm_subsection ();
|
|
4403 }
|
|
4404
|
|
4405 /* The remainder of the text on this line is an appendix
|
|
4406 subsubsection heading. */
|
|
4407 static void
|
|
4408 cm_appendixsubsubsec (void)
|
|
4409 {
|
|
4410 cm_subsubsection ();
|
|
4411 }
|
|
4412
|
|
4413 /* Compatibility functions substitute for chapter, section, etc. */
|
|
4414 static void
|
|
4415 cm_majorheading (void)
|
|
4416 {
|
|
4417 cm_chapheading ();
|
|
4418 }
|
|
4419
|
|
4420 static void
|
|
4421 cm_chapheading (void)
|
|
4422 {
|
|
4423 cm_chapter ();
|
|
4424 }
|
|
4425
|
|
4426 static void
|
|
4427 cm_heading (void)
|
|
4428 {
|
|
4429 cm_section ();
|
|
4430 }
|
|
4431
|
|
4432 static void
|
|
4433 cm_subheading (void)
|
|
4434 {
|
|
4435 cm_subsection ();
|
|
4436 }
|
|
4437
|
|
4438 static void
|
|
4439 cm_subsubheading (void)
|
|
4440 {
|
|
4441 cm_subsubsection ();
|
|
4442 }
|
|
4443
|
|
4444
|
|
4445 /* **************************************************************** */
|
|
4446 /* */
|
|
4447 /* Adding nodes, and making tags */
|
|
4448 /* */
|
|
4449 /* **************************************************************** */
|
|
4450
|
|
4451 /* Start a new tag table. */
|
|
4452 static void
|
|
4453 init_tag_table (void)
|
|
4454 {
|
|
4455 while (tag_table != (TAG_ENTRY *) NULL)
|
|
4456 {
|
|
4457 TAG_ENTRY *temp = tag_table;
|
|
4458 free (temp->node);
|
|
4459 free (temp->prev);
|
|
4460 free (temp->next);
|
|
4461 free (temp->up);
|
|
4462 tag_table = tag_table->next_ent;
|
|
4463 free (temp);
|
|
4464 }
|
|
4465 }
|
|
4466
|
|
4467 static void
|
|
4468 write_tag_table (void)
|
|
4469 {
|
|
4470 write_tag_table_internal (0); /* Not indirect. */
|
|
4471 }
|
|
4472
|
|
4473 static void
|
|
4474 write_tag_table_indirect (void)
|
|
4475 {
|
|
4476 write_tag_table_internal (1);
|
|
4477 }
|
|
4478
|
|
4479 /* Write out the contents of the existing tag table.
|
|
4480 INDIRECT_P says how to format the output. */
|
|
4481 static void
|
|
4482 write_tag_table_internal (int indirect_p)
|
|
4483 {
|
|
4484 TAG_ENTRY *node = tag_table;
|
|
4485 int old_indent = no_indent;
|
|
4486
|
|
4487 no_indent = 1;
|
|
4488 filling_enabled = 0;
|
|
4489 must_start_paragraph = 0;
|
|
4490 close_paragraph ();
|
|
4491
|
|
4492 if (!indirect_p)
|
|
4493 {
|
|
4494 no_indent = 1;
|
|
4495 insert ('\n');
|
|
4496 }
|
|
4497
|
|
4498 add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
|
|
4499
|
|
4500 while (node != (TAG_ENTRY *) NULL)
|
|
4501 {
|
|
4502 execute_string ("Node: %s", node->node);
|
|
4503 add_word_args ("\177%d\n", node->position);
|
|
4504 node = node->next_ent;
|
|
4505 }
|
|
4506
|
|
4507 add_word ("\037\nEnd Tag Table\n");
|
|
4508 flush_output ();
|
|
4509 no_indent = old_indent;
|
|
4510 }
|
|
4511
|
|
4512 static char *
|
|
4513 get_node_token (void)
|
|
4514 {
|
|
4515 char *string;
|
|
4516
|
|
4517 get_until_in_line (",", &string);
|
|
4518
|
|
4519 if (curchar () == ',')
|
|
4520 input_text_offset++;
|
|
4521
|
|
4522 canon_white (string);
|
|
4523
|
|
4524 /* Force all versions of "top" to be "Top". */
|
|
4525 normalize_node_name (string);
|
|
4526
|
|
4527 return (string);
|
|
4528 }
|
|
4529
|
|
4530 /* Convert "top" and friends into "Top". */
|
|
4531 static void
|
|
4532 normalize_node_name (char *string)
|
|
4533 {
|
|
4534 if (strcasecmp (string, "Top") == 0)
|
|
4535 strcpy (string, "Top");
|
|
4536 }
|
|
4537
|
|
4538 /* Look up NAME in the tag table, and return the associated
|
|
4539 tag_entry. If the node is not in the table return NULL. */
|
|
4540 static TAG_ENTRY *
|
|
4541 find_node (char *name)
|
|
4542 {
|
|
4543 TAG_ENTRY *tag = tag_table;
|
|
4544
|
|
4545 while (tag != (TAG_ENTRY *) NULL)
|
|
4546 {
|
|
4547 if (strcmp (tag->node, name) == 0)
|
|
4548 return (tag);
|
|
4549 tag = tag->next_ent;
|
|
4550 }
|
|
4551 return ((TAG_ENTRY *) NULL);
|
|
4552 }
|
|
4553
|
|
4554 /* Remember NODE and associates. */
|
|
4555 static void
|
|
4556 remember_node (char *node, char *prev, char *next, char *up, int position,
|
|
4557 int line_no, int no_warn)
|
|
4558 {
|
|
4559 /* Check for existence of this tag already. */
|
|
4560 if (validating)
|
|
4561 {
|
|
4562 register TAG_ENTRY *tag = find_node (node);
|
|
4563 if (tag)
|
|
4564 {
|
|
4565 line_error ("Node `%s' multiply defined (%d is first definition)",
|
|
4566 node, tag->line_no);
|
|
4567 return;
|
|
4568 }
|
|
4569 }
|
|
4570
|
|
4571 /* First, make this the current node. */
|
|
4572 current_node = node;
|
|
4573
|
|
4574 /* Now add it to the list. */
|
|
4575 {
|
|
4576 TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
|
|
4577 new->node = node;
|
|
4578 new->prev = prev;
|
|
4579 new->next = next;
|
|
4580 new->up = up;
|
|
4581 new->position = position;
|
|
4582 new->line_no = line_no;
|
|
4583 new->filename = node_filename;
|
|
4584 new->touched = 0; /* not yet referenced. */
|
|
4585 new->flags = 0;
|
|
4586 if (no_warn)
|
|
4587 new->flags |= NO_WARN;
|
|
4588 new->next_ent = tag_table;
|
|
4589 tag_table = new;
|
|
4590 }
|
|
4591 }
|
|
4592
|
|
4593 /* The order is: nodename, nextnode, prevnode, upnode.
|
|
4594 If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
|
|
4595 You must follow a node command which has those fields defaulted
|
|
4596 with a sectioning command (e.g. @chapter) giving the "level" of that node.
|
|
4597 It is an error not to do so.
|
|
4598 The defaults come from the menu in this node's parent. */
|
|
4599 static void
|
|
4600 cm_node (void)
|
|
4601 {
|
|
4602 char *node, *prev, *next, *up;
|
|
4603 int new_node_pos, defaulting, this_section, no_warn = 0;
|
|
4604 extern int already_outputting_pending_notes;
|
|
4605
|
|
4606 if (strcmp (command, "nwnode") == 0)
|
|
4607 no_warn = 1;
|
|
4608
|
|
4609 /* Get rid of unmatched brace arguments from previous commands. */
|
|
4610 discard_braces ();
|
|
4611
|
|
4612 /* There also might be insertions left lying around that haven't been
|
|
4613 ended yet. Do that also. */
|
|
4614 discard_insertions ();
|
|
4615
|
|
4616 if (!already_outputting_pending_notes)
|
|
4617 {
|
|
4618 close_paragraph ();
|
|
4619 output_pending_notes ();
|
|
4620 free_pending_notes ();
|
|
4621 }
|
|
4622
|
|
4623 filling_enabled = indented_fill = 0;
|
|
4624 new_node_pos = output_position;
|
|
4625 current_footnote_number = 1;
|
|
4626
|
|
4627 #if defined (HAVE_MACROS)
|
|
4628 if (macro_expansion_output_stream)
|
|
4629 append_to_expansion_output (input_text_offset + 1);
|
|
4630 #endif /* HAVE_MACROS */
|
|
4631
|
|
4632 node = get_node_token ();
|
|
4633 next = get_node_token ();
|
|
4634 prev = get_node_token ();
|
|
4635 up = get_node_token ();
|
|
4636
|
|
4637 #if defined (HAVE_MACROS)
|
|
4638 if (macro_expansion_output_stream)
|
|
4639 remember_itext (input_text, input_text_offset);
|
|
4640 #endif /* HAVE_MACROS */
|
|
4641
|
|
4642 no_indent = 1;
|
|
4643 if (!no_headers)
|
|
4644 {
|
|
4645 add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename);
|
|
4646
|
|
4647 #if defined (HAVE_MACROS)
|
|
4648 if (macro_expansion_output_stream)
|
|
4649 me_execute_string (node);
|
|
4650 else
|
|
4651 #endif /* HAVE_MACROS */
|
|
4652 execute_string ("%s", node);
|
|
4653 filling_enabled = indented_fill = 0;
|
|
4654 }
|
|
4655
|
|
4656 /* Check for defaulting of this node's next, prev, and up fields. */
|
|
4657 defaulting = ((strlen (next) == 0) &&
|
|
4658 (strlen (prev) == 0) &&
|
|
4659 (strlen (up) == 0));
|
|
4660
|
|
4661 this_section = what_section (input_text + input_text_offset);
|
|
4662
|
|
4663 /* If we are defaulting, then look at the immediately following
|
|
4664 sectioning command (error if none) to determine the node's
|
|
4665 level. Find the node that contains the menu mentioning this node
|
|
4666 that is one level up (error if not found). That node is the "Up"
|
|
4667 of this node. Default the "Next" and "Prev" from the menu. */
|
|
4668 if (defaulting)
|
|
4669 {
|
|
4670 NODE_REF *last_ref = (NODE_REF *)NULL;
|
|
4671 NODE_REF *ref = node_references;
|
|
4672
|
|
4673 if ((this_section < 0) && (strcmp (node, "Top") != 0))
|
|
4674 {
|
|
4675 char *polite_section_name = "top";
|
|
4676 int i;
|
|
4677
|
|
4678 for (i = 0; section_alist[i].name; i++)
|
|
4679 if (section_alist[i].level == current_section + 1)
|
|
4680 {
|
|
4681 polite_section_name = section_alist[i].name;
|
|
4682 break;
|
|
4683 }
|
|
4684
|
|
4685 line_error
|
|
4686 ("Node `%s' requires a sectioning command (e.g. %c%s)",
|
|
4687 node, COMMAND_PREFIX, polite_section_name);
|
|
4688 }
|
|
4689 else
|
|
4690 {
|
|
4691 if (strcmp (node, "Top") == 0)
|
|
4692 {
|
|
4693 /* Default the NEXT pointer to be the first menu item in
|
|
4694 this node, if there is a menu in this node. We have to
|
|
4695 try very hard to find the menu, as it may be obscured
|
|
4696 by execution_strings which are on the filestack. For
|
|
4697 every member of the filestack which has a FILENAME
|
|
4698 member which is identical to the current INPUT_FILENAME,
|
|
4699 search forward from that offset. */
|
|
4700 int saved_input_text_offset = input_text_offset;
|
|
4701 int saved_size_of_input_text = size_of_input_text;
|
|
4702 char *saved_input_text = input_text;
|
|
4703 FSTACK *next_file = filestack;
|
|
4704
|
|
4705 int orig_offset, orig_size;
|
|
4706
|
|
4707 /* No matter what, make this file point back at `(dir)'. */
|
|
4708 free (up); up = strdup ("(dir)");
|
|
4709
|
|
4710 while (1)
|
|
4711 {
|
|
4712 orig_offset = input_text_offset;
|
|
4713 orig_size =
|
|
4714 search_forward (node_search_string, orig_offset);
|
|
4715
|
|
4716 if (orig_size < 0)
|
|
4717 orig_size = size_of_input_text;
|
|
4718
|
|
4719 input_text_offset =
|
|
4720 search_forward (menu_search_string, orig_offset);
|
|
4721
|
|
4722 if (input_text_offset > -1)
|
|
4723 {
|
|
4724 char *nodename_from_menu = (char *)NULL;
|
|
4725
|
|
4726 input_text_offset =
|
|
4727 search_forward ("\n* ", input_text_offset);
|
|
4728
|
|
4729 if (input_text_offset != -1)
|
|
4730 nodename_from_menu = glean_node_from_menu (0);
|
|
4731
|
|
4732 if (nodename_from_menu)
|
|
4733 {
|
|
4734 free (next); next = nodename_from_menu;
|
|
4735 break;
|
|
4736 }
|
|
4737 }
|
|
4738
|
|
4739 /* We got here, so it hasn't been found yet. Try
|
|
4740 the next file on the filestack if there is one. */
|
|
4741 if (next_file &&
|
|
4742 (strcmp (next_file->filename, input_filename) == 0))
|
|
4743 {
|
|
4744 input_text = next_file->text;
|
|
4745 input_text_offset = next_file->offset;
|
|
4746 size_of_input_text = next_file->size;
|
|
4747 next_file = next_file->next;
|
|
4748 }
|
|
4749 else
|
|
4750 {
|
|
4751 /* No more input files to check. */
|
|
4752 break;
|
|
4753 }
|
|
4754 }
|
|
4755
|
|
4756 input_text = saved_input_text;
|
|
4757 input_text_offset = saved_input_text_offset;
|
|
4758 size_of_input_text = saved_size_of_input_text;
|
|
4759 }
|
|
4760 }
|
|
4761
|
|
4762 /* Fix the level of the menu references in the Top node, iff it
|
|
4763 was declared with @top, and no subsequent reference was found. */
|
|
4764 if (top_node_seen && !non_top_node_seen)
|
|
4765 {
|
|
4766 /* Then this is the first non-@top node seen. */
|
|
4767 int level;
|
|
4768
|
|
4769 level = set_top_section_level (this_section - 1);
|
|
4770 non_top_node_seen = 1;
|
|
4771
|
|
4772 while (ref)
|
|
4773 {
|
|
4774 if (ref->section == level)
|
|
4775 ref->section = this_section - 1;
|
|
4776 ref = ref->next;
|
|
4777 }
|
|
4778
|
|
4779 ref = node_references;
|
|
4780 }
|
|
4781
|
|
4782 while (ref)
|
|
4783 {
|
|
4784 if (ref->section == (this_section - 1) &&
|
|
4785 ref->type == menu_reference &&
|
|
4786 strcmp (ref->node, node) == 0)
|
|
4787 {
|
|
4788 char *containing_node = ref->containing_node;
|
|
4789
|
|
4790 free (up);
|
|
4791 up = strdup (containing_node);
|
|
4792
|
|
4793 if (last_ref &&
|
|
4794 last_ref->type == menu_reference &&
|
|
4795 (strcmp (last_ref->containing_node,
|
|
4796 containing_node) == 0))
|
|
4797 {
|
|
4798 free (next);
|
|
4799 next = strdup (last_ref->node);
|
|
4800 }
|
|
4801
|
|
4802 while ((ref->section == this_section - 1) &&
|
|
4803 (ref->next) &&
|
|
4804 (ref->next->type != menu_reference))
|
|
4805 ref = ref->next;
|
|
4806
|
|
4807 if (ref->next && ref->type == menu_reference &&
|
|
4808 (strcmp (ref->next->containing_node,
|
|
4809 containing_node) == 0))
|
|
4810 {
|
|
4811 free (prev);
|
|
4812 prev = strdup (ref->next->node);
|
|
4813 }
|
|
4814 else if (!ref->next &&
|
|
4815 strcasecmp (ref->containing_node, "Top") == 0)
|
|
4816 {
|
|
4817 free (prev);
|
|
4818 prev = strdup (ref->containing_node);
|
|
4819 }
|
|
4820 break;
|
|
4821 }
|
|
4822 last_ref = ref;
|
|
4823 ref = ref->next;
|
|
4824 }
|
|
4825 }
|
|
4826
|
|
4827 #if defined (HAVE_MACROS)
|
|
4828 /* Insert the correct args if we are expanding macros, and the node's
|
|
4829 pointers weren't defaulted. */
|
|
4830 if (macro_expansion_output_stream && !defaulting)
|
|
4831 {
|
|
4832 char *temp;
|
|
4833 int op_orig = output_paragraph_offset;
|
|
4834
|
|
4835 temp = (char *)xmalloc (3 + strlen (next));
|
|
4836 sprintf (temp, ", %s", next);
|
|
4837 me_execute_string (temp);
|
|
4838 free (temp);
|
|
4839
|
|
4840 temp = (char *)xmalloc (3 + strlen (prev));
|
|
4841 sprintf (temp, ", %s", prev);
|
|
4842 me_execute_string (temp);
|
|
4843 free (temp);
|
|
4844
|
|
4845 temp = (char *)xmalloc (4 + strlen (up));
|
|
4846 sprintf (temp, ", %s", up);
|
|
4847 me_execute_string (temp);
|
|
4848 free (temp);
|
|
4849
|
|
4850 output_paragraph_offset = op_orig;
|
|
4851 }
|
|
4852 #endif /* HAVE_MACROS */
|
|
4853
|
|
4854 if (!no_headers)
|
|
4855 {
|
|
4856 #if defined (HAVE_MACROS)
|
|
4857 if (macro_expansion_output_stream)
|
|
4858 me_inhibit_expansion++;
|
|
4859 #endif /* HAVE_MACROS */
|
|
4860
|
|
4861 if (*next)
|
|
4862 {
|
|
4863 execute_string (", Next: %s", next);
|
|
4864 filling_enabled = indented_fill = 0;
|
|
4865 }
|
|
4866
|
|
4867 if (*prev)
|
|
4868 {
|
|
4869 execute_string (", Prev: %s", prev);
|
|
4870 filling_enabled = indented_fill = 0;
|
|
4871 }
|
|
4872
|
|
4873 if (*up)
|
|
4874 {
|
|
4875 execute_string (", Up: %s", up);
|
|
4876 filling_enabled = indented_fill = 0;
|
|
4877 }
|
|
4878 #if defined (HAVE_MACROS)
|
|
4879 if (macro_expansion_output_stream)
|
|
4880 me_inhibit_expansion--;
|
|
4881 #endif /* HAVE_MACROS */
|
|
4882 }
|
|
4883
|
|
4884 close_paragraph ();
|
|
4885 no_indent = 0;
|
|
4886
|
|
4887 if (!*node)
|
|
4888 {
|
|
4889 line_error ("No node name specified for `%c%s' command",
|
|
4890 COMMAND_PREFIX, command);
|
|
4891 free (node);
|
|
4892 free (next);
|
|
4893 free (prev);
|
|
4894 free (up);
|
|
4895 }
|
|
4896 else
|
|
4897 {
|
|
4898 if (!*next) { free (next); next = (char *)NULL; }
|
|
4899 if (!*prev) { free (prev); prev = (char *)NULL; }
|
|
4900 if (!*up) { free (up); up = (char *)NULL; }
|
|
4901 remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
|
|
4902 }
|
|
4903
|
|
4904 /* Change the section only if there was a sectioning command. */
|
|
4905 if (this_section >= 0)
|
|
4906 current_section = this_section;
|
|
4907
|
|
4908 filling_enabled = 1;
|
|
4909 }
|
|
4910
|
|
4911 /* Validation of an info file.
|
|
4912 Scan through the list of tag entrys touching the Prev, Next, and Up
|
|
4913 elements of each. It is an error not to be able to touch one of them,
|
|
4914 except in the case of external node references, such as "(DIR)".
|
|
4915
|
|
4916 If the Prev is different from the Up,
|
|
4917 then the Prev node must have a Next pointing at this node.
|
|
4918
|
|
4919 Every node except Top must have an Up.
|
|
4920 The Up node must contain some sort of reference, other than a Next,
|
|
4921 to this node.
|
|
4922
|
|
4923 If the Next is different from the Next of the Up,
|
|
4924 then the Next node must have a Prev pointing at this node. */
|
|
4925 static void
|
|
4926 validate_file (TAG_ENTRY *tag_tayble)
|
|
4927 {
|
|
4928 char *old_input_filename = input_filename;
|
|
4929 TAG_ENTRY *tags = tag_tayble;
|
|
4930
|
|
4931 while (tags != (TAG_ENTRY *) NULL)
|
|
4932 {
|
|
4933 register TAG_ENTRY *temp_tag;
|
|
4934
|
|
4935 input_filename = tags->filename;
|
|
4936 line_number = tags->line_no;
|
|
4937
|
|
4938 /* If this is a "no warn" node, don't validate it in any way. */
|
|
4939 if (tags->flags & NO_WARN)
|
|
4940 {
|
|
4941 tags = tags->next_ent;
|
|
4942 continue;
|
|
4943 }
|
|
4944
|
|
4945 /* If this node has a Next, then make sure that the Next exists. */
|
|
4946 if (tags->next)
|
|
4947 {
|
|
4948 validate (tags->next, tags->line_no, "Next");
|
|
4949
|
|
4950 /* If the Next node exists, and there is no Up, then make
|
|
4951 sure that the Prev of the Next points back. */
|
|
4952 if ((temp_tag = find_node (tags->next)))
|
|
4953 {
|
|
4954 char *prev;
|
|
4955
|
|
4956 if (temp_tag->flags & NO_WARN)
|
|
4957 {
|
|
4958 /* Do nothing if we aren't supposed to issue warnings
|
|
4959 about this node. */
|
|
4960 }
|
|
4961 else
|
|
4962 {
|
|
4963 prev = temp_tag->prev;
|
|
4964 if (!prev || (strcmp (prev, tags->node) != 0))
|
|
4965 {
|
|
4966 line_error ("Node `%s''s Next field not pointed back to",
|
|
4967 tags->node);
|
|
4968 line_number = temp_tag->line_no;
|
|
4969 input_filename = temp_tag->filename;
|
|
4970 line_error
|
|
4971 ("This node (`%s') is the one with the bad `Prev'",
|
|
4972 temp_tag->node);
|
|
4973 input_filename = tags->filename;
|
|
4974 line_number = tags->line_no;
|
|
4975 temp_tag->flags |= PREV_ERROR;
|
|
4976 }
|
|
4977 }
|
|
4978 }
|
|
4979 }
|
|
4980
|
|
4981 /* Validate the Prev field if there is one, and we haven't already
|
|
4982 complained about it in some way. You don't have to have a Prev
|
|
4983 field at this stage. */
|
|
4984 if (!(tags->flags & PREV_ERROR) && tags->prev)
|
|
4985 {
|
|
4986 int valid = validate (tags->prev, tags->line_no, "Prev");
|
|
4987
|
|
4988 if (!valid)
|
|
4989 tags->flags |= PREV_ERROR;
|
|
4990 else
|
|
4991 {
|
|
4992 /* If the Prev field is not the same as the Up field,
|
|
4993 then the node pointed to by the Prev field must have
|
|
4994 a Next field which points to this node. */
|
|
4995 if (tags->up && (strcmp (tags->prev, tags->up) != 0))
|
|
4996 {
|
|
4997 temp_tag = find_node (tags->prev);
|
|
4998
|
|
4999 /* If we aren't supposed to issue warnings about the
|
|
5000 target node, do nothing. */
|
|
5001 if (!temp_tag || (temp_tag->flags & NO_WARN))
|
|
5002 {
|
|
5003 /* Do nothing. */
|
|
5004 }
|
|
5005 else
|
|
5006 {
|
|
5007 if (!temp_tag->next ||
|
|
5008 (strcmp (temp_tag->next, tags->node) != 0))
|
|
5009 {
|
|
5010 line_error
|
|
5011 ("Node `%s''s Prev field not pointed back to",
|
|
5012 tags->node);
|
|
5013 line_number = temp_tag->line_no;
|
|
5014 input_filename = temp_tag->filename;
|
|
5015 line_error
|
|
5016 ("This node (`%s') is the one with the bad `Next'",
|
|
5017 temp_tag->node);
|
|
5018 input_filename = tags->filename;
|
|
5019 line_number = tags->line_no;
|
|
5020 temp_tag->flags |= NEXT_ERROR;
|
|
5021 }
|
|
5022 }
|
|
5023 }
|
|
5024 }
|
|
5025 }
|
|
5026
|
|
5027 if (!tags->up && (strcasecmp (tags->node, "Top") != 0))
|
|
5028 line_error ("Node `%s' is missing an \"Up\" field", tags->node);
|
|
5029 else if (tags->up)
|
|
5030 {
|
|
5031 int valid = validate (tags->up, tags->line_no, "Up");
|
|
5032
|
|
5033 /* If node X has Up: Y, then warn if Y fails to have a menu item
|
|
5034 or note pointing at X, if Y isn't of the form "(Y)". */
|
|
5035 if (valid && *tags->up != '(')
|
|
5036 {
|
|
5037 NODE_REF *nref, *tref, *list;
|
|
5038
|
|
5039 tref = (NODE_REF *) NULL;
|
|
5040 list = node_references;
|
|
5041
|
|
5042 for (;;)
|
|
5043 {
|
|
5044 if (!(nref = find_node_reference (tags->node, list)))
|
|
5045 break;
|
|
5046
|
|
5047 if (strcmp (nref->containing_node, tags->up) == 0)
|
|
5048 {
|
|
5049 if (nref->type != menu_reference)
|
|
5050 {
|
|
5051 tref = nref;
|
|
5052 list = nref->next;
|
|
5053 }
|
|
5054 else
|
|
5055 break;
|
|
5056 }
|
|
5057 list = nref->next;
|
|
5058 }
|
|
5059
|
|
5060 if (!nref)
|
|
5061 {
|
|
5062 temp_tag = find_node (tags->up);
|
|
5063 line_number = temp_tag->line_no;
|
|
5064 input_filename = temp_tag->filename;
|
|
5065 if (!tref)
|
|
5066 line_error (
|
|
5067 "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
|
|
5068 tags->node, tags->up, tags->up, tags->node);
|
|
5069 line_number = tags->line_no;
|
|
5070 input_filename = tags->filename;
|
|
5071 }
|
|
5072 }
|
|
5073 }
|
|
5074 tags = tags->next_ent;
|
|
5075 }
|
|
5076
|
|
5077 validate_other_references (node_references);
|
|
5078 /* We have told the user about the references which didn't exist.
|
|
5079 Now tell him about the nodes which aren't referenced. */
|
|
5080
|
|
5081 tags = tag_tayble;
|
|
5082 while (tags != (TAG_ENTRY *) NULL)
|
|
5083 {
|
|
5084 /* If this node is a "no warn" node, do nothing. */
|
|
5085 if (tags->flags & NO_WARN)
|
|
5086 {
|
|
5087 tags = tags->next_ent;
|
|
5088 continue;
|
|
5089 }
|
|
5090
|
|
5091 /* Special hack. If the node in question appears to have
|
|
5092 been referenced more than REFERENCE_WARNING_LIMIT times,
|
|
5093 give a warning. */
|
|
5094 if (tags->touched > reference_warning_limit)
|
|
5095 {
|
|
5096 input_filename = tags->filename;
|
|
5097 line_number = tags->line_no;
|
|
5098 warning ("Node `%s' has been referenced %d times",
|
|
5099 tags->node, tags->touched);
|
|
5100 }
|
|
5101
|
|
5102 if (tags->touched == 0)
|
|
5103 {
|
|
5104 input_filename = tags->filename;
|
|
5105 line_number = tags->line_no;
|
|
5106
|
|
5107 /* Notice that the node "Top" is special, and doesn't have to
|
|
5108 be referenced. */
|
|
5109 if (strcasecmp (tags->node, "Top") != 0)
|
|
5110 warning ("Unreferenced node `%s'", tags->node);
|
|
5111 }
|
|
5112 tags = tags->next_ent;
|
|
5113 }
|
|
5114 input_filename = old_input_filename;
|
|
5115 }
|
|
5116
|
|
5117 /* Return 1 if tag correctly validated, or 0 if not. */
|
|
5118 static int
|
|
5119 validate (char *tag, int line, char *label)
|
|
5120 {
|
|
5121 TAG_ENTRY *result;
|
|
5122
|
|
5123 /* If there isn't a tag to verify, or if the tag is in another file,
|
|
5124 then it must be okay. */
|
|
5125 if (!tag || !*tag || *tag == '(')
|
|
5126 return (1);
|
|
5127
|
|
5128 /* Otherwise, the tag must exist. */
|
|
5129 result = find_node (tag);
|
|
5130
|
|
5131 if (!result)
|
|
5132 {
|
|
5133 line_number = line;
|
|
5134 line_error (
|
|
5135 "Validation error. `%s' field points to node `%s', which doesn't exist",
|
|
5136 label, tag);
|
|
5137 return (0);
|
|
5138 }
|
|
5139 result->touched++;
|
|
5140 return (1);
|
|
5141 }
|
|
5142
|
|
5143 /* Split large output files into a series of smaller files. Each file
|
|
5144 is pointed to in the tag table, which then gets written out as the
|
|
5145 original file. The new files have the same name as the original file
|
|
5146 with a "-num" attached. SIZE is the largest number of bytes to allow
|
|
5147 in any single split file. */
|
|
5148 static void
|
|
5149 split_file (char *filename, int size)
|
|
5150 {
|
|
5151 char *root_filename, *root_pathname;
|
|
5152 char *the_file;
|
|
5153 struct stat fileinfo;
|
|
5154 long file_size;
|
|
5155 char *the_header;
|
|
5156 int header_size;
|
|
5157
|
|
5158 /* Can only do this to files with tag tables. */
|
|
5159 if (!tag_table)
|
|
5160 return;
|
|
5161
|
|
5162 if (size == 0)
|
|
5163 size = DEFAULT_SPLIT_SIZE;
|
|
5164
|
|
5165 if ((stat (filename, &fileinfo) != 0) ||
|
|
5166 (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
|
|
5167 return;
|
|
5168 file_size = (long) fileinfo.st_size;
|
|
5169
|
|
5170 the_file = find_and_load (filename);
|
|
5171 if (!the_file)
|
|
5172 return;
|
|
5173
|
|
5174 root_filename = filename_part (filename);
|
|
5175 root_pathname = pathname_part (filename);
|
|
5176
|
|
5177 if (!root_pathname)
|
|
5178 root_pathname = strdup ("");
|
|
5179
|
|
5180 /* Start splitting the file. Walk along the tag table
|
|
5181 outputting sections of the file. When we have written
|
|
5182 all of the nodes in the tag table, make the top-level
|
|
5183 pointer file, which contains indirect pointers and
|
|
5184 tags for the nodes. */
|
|
5185 {
|
|
5186 int which_file = 1;
|
|
5187 TAG_ENTRY *tags = tag_table;
|
|
5188 char *indirect_info = (char *)NULL;
|
|
5189
|
|
5190 /* Remember the `header' of this file. The first tag in the file is
|
|
5191 the bottom of the header; the top of the file is the start. */
|
|
5192 the_header = (char *)xmalloc (1 + (header_size = tags->position));
|
|
5193 memcpy (the_header, the_file, header_size);
|
|
5194
|
|
5195 while (tags)
|
|
5196 {
|
|
5197 int file_top, file_bot, limit;
|
|
5198
|
|
5199 /* Have to include the Control-_. */
|
|
5200 file_top = file_bot = tags->position;
|
|
5201 limit = file_top + size;
|
|
5202
|
|
5203 /* If the rest of this file is only one node, then
|
|
5204 that is the entire subfile. */
|
|
5205 if (!tags->next_ent)
|
|
5206 {
|
|
5207 int i = tags->position + 1;
|
|
5208 char last_char = the_file[i];
|
|
5209
|
|
5210 while (i < file_size)
|
|
5211 {
|
|
5212 if ((the_file[i] == '\037') &&
|
|
5213 ((last_char == '\n') ||
|
|
5214 (last_char == '\014')))
|
|
5215 break;
|
|
5216 else
|
|
5217 last_char = the_file[i];
|
|
5218 i++;
|
|
5219 }
|
|
5220 file_bot = i;
|
|
5221 tags = tags->next_ent;
|
|
5222 goto write_region;
|
|
5223 }
|
|
5224
|
|
5225 /* Otherwise, find the largest number of nodes that can fit in
|
|
5226 this subfile. */
|
|
5227 for (; tags; tags = tags->next_ent)
|
|
5228 {
|
|
5229 if (!tags->next_ent)
|
|
5230 {
|
|
5231 /* This entry is the last node. Search forward for the end
|
|
5232 of this node, and that is the end of this file. */
|
|
5233 int i = tags->position + 1;
|
|
5234 char last_char = the_file[i];
|
|
5235
|
|
5236 while (i < file_size)
|
|
5237 {
|
|
5238 if ((the_file[i] == '\037') &&
|
|
5239 ((last_char == '\n') ||
|
|
5240 (last_char == '\014')))
|
|
5241 break;
|
|
5242 else
|
|
5243 last_char = the_file[i];
|
|
5244 i++;
|
|
5245 }
|
|
5246 file_bot = i;
|
|
5247
|
|
5248 if (file_bot < limit)
|
|
5249 {
|
|
5250 tags = tags->next_ent;
|
|
5251 goto write_region;
|
|
5252 }
|
|
5253 else
|
|
5254 {
|
|
5255 /* Here we want to write out everything before the last
|
|
5256 node, and then write the last node out in a file
|
|
5257 by itself. */
|
|
5258 file_bot = tags->position;
|
|
5259 goto write_region;
|
|
5260 }
|
|
5261 }
|
|
5262
|
|
5263 if (tags->next_ent->position > limit)
|
|
5264 {
|
|
5265 if (tags->position == file_top)
|
|
5266 tags = tags->next_ent;
|
|
5267
|
|
5268 file_bot = tags->position;
|
|
5269
|
|
5270 write_region:
|
|
5271 {
|
|
5272 int fd;
|
|
5273 char *split_filename;
|
|
5274
|
|
5275 split_filename = (char *) xmalloc
|
|
5276 (10 + strlen (root_pathname) + strlen (root_filename));
|
|
5277 sprintf
|
|
5278 (split_filename,
|
|
5279 #ifdef MSDOS
|
|
5280 "%s%s.%d", root_pathname, root_filename, which_file);
|
|
5281 #else /* !MSDOS */
|
|
5282 "%s%s-%d", root_pathname, root_filename, which_file);
|
|
5283 #endif /* !MSDOS */
|
|
5284
|
|
5285 fd = open
|
|
5286 (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
|
|
5287
|
|
5288 if ((fd < 0) ||
|
|
5289 (write (fd, the_header, header_size) != header_size) ||
|
|
5290 (write (fd, the_file + file_top, file_bot - file_top)
|
|
5291 != (file_bot - file_top)) ||
|
|
5292 ((close (fd)) < 0))
|
|
5293 {
|
|
5294 perror (split_filename);
|
|
5295 if (fd != -1)
|
|
5296 close (fd);
|
|
5297 exit (FATAL);
|
|
5298 }
|
|
5299
|
|
5300 if (!indirect_info)
|
|
5301 {
|
|
5302 indirect_info = the_file + file_top;
|
|
5303 sprintf (indirect_info, "\037\nIndirect:\n");
|
|
5304 indirect_info += strlen (indirect_info);
|
|
5305 }
|
|
5306
|
|
5307 #ifdef MSDOS
|
|
5308 sprintf (indirect_info, "%s.%d: %d\n",
|
|
5309 #else
|
|
5310 sprintf (indirect_info, "%s-%d: %d\n",
|
|
5311 #endif
|
|
5312 root_filename, which_file, file_top);
|
|
5313
|
|
5314 free (split_filename);
|
|
5315 indirect_info += strlen (indirect_info);
|
|
5316 which_file++;
|
|
5317 break;
|
|
5318 }
|
|
5319 }
|
|
5320 }
|
|
5321 }
|
|
5322
|
|
5323 /* We have sucessfully created the subfiles. Now write out the
|
|
5324 original again. We must use `output_stream', or
|
|
5325 write_tag_table_indirect () won't know where to place the output. */
|
|
5326 output_stream = fopen (filename, "w");
|
|
5327 if (!output_stream)
|
|
5328 {
|
|
5329 perror (filename);
|
|
5330 exit (FATAL);
|
|
5331 }
|
|
5332
|
|
5333 {
|
|
5334 int distance = indirect_info - the_file;
|
|
5335 fwrite (the_file, 1, distance, output_stream);
|
|
5336
|
|
5337 /* Inhibit newlines. */
|
|
5338 paragraph_is_open = 0;
|
|
5339
|
|
5340 write_tag_table_indirect ();
|
|
5341 fclose (output_stream);
|
|
5342 free (the_header);
|
|
5343 free (the_file);
|
|
5344 return;
|
|
5345 }
|
|
5346 }
|
|
5347 }
|
|
5348
|
|
5349 /* Some menu hacking. This is used to remember menu references while
|
|
5350 reading the input file. After the output file has been written, if
|
|
5351 validation is on, then we use the contents of NODE_REFERENCES as a
|
|
5352 list of nodes to validate. */
|
|
5353 static char *
|
|
5354 reftype_type_string (enum reftype type)
|
|
5355 {
|
|
5356 switch (type)
|
|
5357 {
|
|
5358 case menu_reference:
|
|
5359 return ("Menu");
|
|
5360 case followed_reference:
|
|
5361 return ("Followed-Reference");
|
|
5362 default:
|
|
5363 return ("Internal-bad-reference-type");
|
|
5364 }
|
|
5365 }
|
|
5366
|
|
5367 /* Remember this node name for later validation use. */
|
|
5368 static void
|
|
5369 remember_node_reference (char *node, int line, enum reftype type)
|
|
5370 {
|
|
5371 NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
|
|
5372
|
|
5373 temp->next = node_references;
|
|
5374 temp->node = strdup (node);
|
|
5375 temp->line_no = line;
|
|
5376 temp->section = current_section;
|
|
5377 temp->type = type;
|
|
5378 temp->containing_node = (current_node ? strdup (current_node) : 0);
|
|
5379 temp->filename = node_filename;
|
|
5380
|
|
5381 node_references = temp;
|
|
5382 }
|
|
5383
|
|
5384 static void
|
|
5385 validate_other_references (register NODE_REF *ref_list)
|
|
5386 {
|
|
5387 char *old_input_filename = input_filename;
|
|
5388
|
|
5389 while (ref_list != (NODE_REF *) NULL)
|
|
5390 {
|
|
5391 input_filename = ref_list->filename;
|
|
5392 validate (ref_list->node, ref_list->line_no,
|
|
5393 reftype_type_string (ref_list->type));
|
|
5394 ref_list = ref_list->next;
|
|
5395 }
|
|
5396 input_filename = old_input_filename;
|
|
5397 }
|
|
5398
|
|
5399 /* Find NODE in REF_LIST. */
|
|
5400 static NODE_REF *
|
|
5401 find_node_reference (char *node, register NODE_REF *ref_list)
|
|
5402 {
|
|
5403 while (ref_list)
|
|
5404 {
|
|
5405 if (strcmp (node, ref_list->node) == 0)
|
|
5406 break;
|
|
5407 ref_list = ref_list->next;
|
|
5408 }
|
|
5409 return (ref_list);
|
|
5410 }
|
|
5411
|
|
5412 static void
|
|
5413 free_node_references (void)
|
|
5414 {
|
|
5415 register NODE_REF *list, *temp;
|
|
5416
|
|
5417 list = node_references;
|
|
5418
|
|
5419 while (list)
|
|
5420 {
|
|
5421 temp = list;
|
|
5422 free (list->node);
|
|
5423 free (list->containing_node);
|
|
5424 list = list->next;
|
|
5425 free (temp);
|
|
5426 }
|
|
5427 node_references = (NODE_REF *) NULL;
|
|
5428 }
|
|
5429
|
|
5430 /* This function gets called at the start of every line while inside of
|
|
5431 a menu. It checks to see if the line starts with "* ", and if so,
|
|
5432 remembers the node reference that this menu refers to.
|
|
5433 input_text_offset is at the \n just before the line start. */
|
|
5434 #define menu_starter "* "
|
|
5435 static char *
|
|
5436 glean_node_from_menu (int remember_reference)
|
|
5437 {
|
|
5438 int i, orig_offset = input_text_offset;
|
|
5439 char *nodename;
|
|
5440
|
|
5441 if (strncmp (&input_text[input_text_offset + 1],
|
|
5442 menu_starter,
|
|
5443 strlen (menu_starter)) != 0)
|
|
5444 return ((char *)NULL);
|
|
5445 else
|
|
5446 input_text_offset += strlen (menu_starter) + 1;
|
|
5447
|
|
5448 get_until_in_line (":", &nodename);
|
|
5449 if (curchar () == ':')
|
|
5450 input_text_offset++;
|
|
5451 canon_white (nodename);
|
|
5452
|
|
5453 if (curchar () == ':')
|
|
5454 goto save_node;
|
|
5455
|
|
5456 free (nodename);
|
|
5457 get_rest_of_line (&nodename);
|
|
5458
|
|
5459 /* Special hack: If the nodename follows the menu item name,
|
|
5460 then we have to read the rest of the line in order to find
|
|
5461 out what the nodename is. But we still have to read the
|
|
5462 line later, in order to process any formatting commands that
|
|
5463 might be present. So un-count the carriage return that has just
|
|
5464 been counted. */
|
|
5465 line_number--;
|
|
5466
|
|
5467 isolate_nodename (nodename);
|
|
5468
|
|
5469 save_node:
|
|
5470 input_text_offset = orig_offset;
|
|
5471 normalize_node_name (nodename);
|
|
5472 i = strlen (nodename);
|
|
5473 if (i && nodename[i - 1] == ':')
|
|
5474 nodename[i - 1] = '\0';
|
|
5475
|
|
5476 if (remember_reference)
|
|
5477 {
|
|
5478 remember_node_reference (nodename, line_number, menu_reference);
|
|
5479 free (nodename);
|
|
5480 return ((char *)NULL);
|
|
5481 }
|
|
5482 else
|
|
5483 return (nodename);
|
|
5484 }
|
|
5485
|
|
5486 static void
|
|
5487 isolate_nodename (char *nodename)
|
|
5488 {
|
|
5489 register int i, c;
|
|
5490 int paren_seen, paren;
|
|
5491
|
|
5492 if (!nodename)
|
|
5493 return;
|
|
5494
|
|
5495 canon_white (nodename);
|
|
5496 paren_seen = paren = i = 0;
|
|
5497
|
|
5498 if (*nodename == '.' || !*nodename)
|
|
5499 {
|
|
5500 *nodename = '\0';
|
|
5501 return;
|
|
5502 }
|
|
5503
|
|
5504 if (*nodename == '(')
|
|
5505 {
|
|
5506 paren++;
|
|
5507 paren_seen++;
|
|
5508 i++;
|
|
5509 }
|
|
5510
|
|
5511 for (; (c = nodename[i]); i++)
|
|
5512 {
|
|
5513 if (paren)
|
|
5514 {
|
|
5515 if (c == '(')
|
|
5516 paren++;
|
|
5517 else if (c == ')')
|
|
5518 paren--;
|
|
5519
|
|
5520 continue;
|
|
5521 }
|
|
5522
|
|
5523 /* If the character following the close paren is a space, then this
|
|
5524 node has no more characters associated with it. */
|
|
5525 if (c == '\t' ||
|
|
5526 c == '\n' ||
|
|
5527 c == ',' ||
|
|
5528 ((paren_seen && nodename[i - 1] == ')') &&
|
|
5529 (c == ' ' || c == '.')) ||
|
|
5530 (c == '.' &&
|
|
5531 ((!nodename[i + 1] ||
|
|
5532 (cr_or_whitespace (nodename[i + 1])) ||
|
|
5533 (nodename[i + 1] == ')')))))
|
|
5534 break;
|
|
5535 }
|
|
5536 nodename[i] = '\0';
|
|
5537 }
|
|
5538
|
|
5539 static void
|
|
5540 cm_menu (void)
|
|
5541 {
|
|
5542 begin_insertion (menu);
|
|
5543 }
|
|
5544
|
|
5545
|
|
5546 /* **************************************************************** */
|
|
5547 /* */
|
|
5548 /* Cross Reference Hacking */
|
|
5549 /* */
|
|
5550 /* **************************************************************** */
|
|
5551
|
|
5552 static char *
|
|
5553 get_xref_token (void)
|
|
5554 {
|
|
5555 char *string;
|
|
5556
|
|
5557 get_until_in_braces (",", &string);
|
|
5558 if (curchar () == ',')
|
|
5559 input_text_offset++;
|
|
5560 fix_whitespace (string);
|
|
5561 return (string);
|
|
5562 }
|
|
5563
|
|
5564 int px_ref_flag = 0; /* Controls initial output string. */
|
|
5565
|
|
5566 /* Make a cross reference. */
|
|
5567 static void
|
|
5568 cm_xref (int arg)
|
|
5569 {
|
|
5570 if (arg == START)
|
|
5571 {
|
|
5572 char *arg1, *arg2, *arg3, *arg4, *arg5;
|
|
5573
|
|
5574 arg1 = get_xref_token ();
|
|
5575 arg2 = get_xref_token ();
|
|
5576 arg3 = get_xref_token ();
|
|
5577 arg4 = get_xref_token ();
|
|
5578 arg5 = get_xref_token ();
|
|
5579
|
|
5580 add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
|
|
5581
|
|
5582 if (*arg5 || *arg4)
|
|
5583 {
|
|
5584 char *node_name;
|
|
5585
|
|
5586 if (!*arg2)
|
|
5587 {
|
|
5588 if (*arg3)
|
|
5589 node_name = arg3;
|
|
5590 else
|
|
5591 node_name = arg1;
|
|
5592 }
|
|
5593 else
|
|
5594 node_name = arg2;
|
|
5595
|
|
5596 execute_string ("%s: (%s)%s", node_name, arg4, arg1);
|
|
5597 return;
|
|
5598 }
|
|
5599 else
|
|
5600 remember_node_reference (arg1, line_number, followed_reference);
|
|
5601
|
|
5602 if (*arg3)
|
|
5603 {
|
|
5604 if (!*arg2)
|
|
5605 execute_string ("%s: %s", arg3, arg1);
|
|
5606 else
|
|
5607 execute_string ("%s: %s", arg2, arg1);
|
|
5608 }
|
|
5609 else
|
|
5610 {
|
|
5611 if (*arg2)
|
|
5612 execute_string ("%s: %s", arg2, arg1);
|
|
5613 else
|
|
5614 execute_string ("%s::", arg1);
|
|
5615 }
|
|
5616
|
|
5617 /* Free all of the arguments found. */
|
|
5618 if (arg1) free (arg1);
|
|
5619 if (arg2) free (arg2);
|
|
5620 if (arg3) free (arg3);
|
|
5621 if (arg4) free (arg4);
|
|
5622 if (arg5) free (arg5);
|
|
5623 }
|
|
5624 else
|
|
5625 {
|
|
5626 /* Check to make sure that the next non-whitespace character is either
|
|
5627 a period or a comma. input_text_offset is pointing at the "}" which
|
|
5628 ended the xref or pxref command. */
|
|
5629 int temp = input_text_offset + 1;
|
|
5630
|
|
5631 if (output_paragraph[output_paragraph_offset - 2] == ':' &&
|
|
5632 output_paragraph[output_paragraph_offset - 1] == ':')
|
|
5633 return;
|
|
5634 while (temp < size_of_input_text)
|
|
5635 {
|
|
5636 if (cr_or_whitespace (input_text[temp]))
|
|
5637 temp++;
|
|
5638 else
|
|
5639 {
|
|
5640 if (input_text[temp] == '.' ||
|
|
5641 input_text[temp] == ',' ||
|
|
5642 input_text[temp] == '\t')
|
|
5643 return;
|
|
5644 else
|
|
5645 {
|
|
5646 line_error (
|
|
5647 "Cross-reference must be terminated with a period or a comma");
|
|
5648 return;
|
|
5649 }
|
|
5650 }
|
|
5651 }
|
|
5652 }
|
|
5653 }
|
|
5654
|
|
5655 static void
|
|
5656 cm_pxref (int arg)
|
|
5657 {
|
|
5658 if (arg == START)
|
|
5659 {
|
|
5660 px_ref_flag++;
|
|
5661 cm_xref (arg);
|
|
5662 px_ref_flag--;
|
|
5663 }
|
|
5664 else
|
|
5665 add_char ('.');
|
|
5666 }
|
|
5667
|
|
5668 static void
|
|
5669 cm_inforef (int arg)
|
|
5670 {
|
|
5671 if (arg == START)
|
|
5672 {
|
|
5673 char *node, *pname, *file;
|
|
5674
|
|
5675 node = get_xref_token ();
|
|
5676 pname = get_xref_token ();
|
|
5677 file = get_xref_token ();
|
|
5678
|
|
5679 execute_string ("*note %s: (%s)%s", pname, file, node);
|
|
5680 }
|
|
5681 }
|
|
5682
|
|
5683 /* **************************************************************** */
|
|
5684 /* */
|
|
5685 /* Insertion Command Stubs */
|
|
5686 /* */
|
|
5687 /* **************************************************************** */
|
|
5688
|
|
5689 static void
|
|
5690 cm_quotation (void)
|
|
5691 {
|
|
5692 begin_insertion (quotation);
|
|
5693 }
|
|
5694
|
|
5695 static void
|
|
5696 cm_example (void)
|
|
5697 {
|
|
5698 begin_insertion (example);
|
|
5699 }
|
|
5700
|
|
5701 static void
|
|
5702 cm_smallexample (void)
|
|
5703 {
|
|
5704 begin_insertion (smallexample);
|
|
5705 }
|
|
5706
|
|
5707 static void
|
|
5708 cm_lisp (void)
|
|
5709 {
|
|
5710 begin_insertion (lisp);
|
|
5711 }
|
|
5712
|
|
5713 static void
|
|
5714 cm_smalllisp (void)
|
|
5715 {
|
|
5716 begin_insertion (smalllisp);
|
|
5717 }
|
|
5718
|
|
5719 /* @cartouche/@end cartouche draws box with rounded corners in
|
|
5720 TeX output. Right now, just a NOP insertion. */
|
|
5721 static void
|
|
5722 cm_cartouche (void)
|
|
5723 {
|
|
5724 begin_insertion (cartouche);
|
|
5725 }
|
|
5726
|
|
5727 static void
|
|
5728 cm_format (void)
|
|
5729 {
|
|
5730 begin_insertion (format);
|
|
5731 }
|
|
5732
|
|
5733 static void
|
|
5734 cm_display (void)
|
|
5735 {
|
|
5736 begin_insertion (display);
|
|
5737 }
|
|
5738
|
|
5739 static void
|
|
5740 cm_itemize (void)
|
|
5741 {
|
|
5742 begin_insertion (itemize);
|
|
5743 }
|
|
5744
|
|
5745 static void
|
|
5746 cm_enumerate (void)
|
|
5747 {
|
|
5748 do_enumeration (enumerate, "1");
|
|
5749 }
|
|
5750
|
|
5751 /* Start an enumeration insertion of type TYPE. If the user supplied
|
|
5752 no argument on the line, then use DEFAULT_STRING as the initial string. */
|
|
5753 static void
|
|
5754 do_enumeration (int type, char *default_string)
|
|
5755 {
|
|
5756 get_until_in_line (".", &enumeration_arg);
|
|
5757 canon_white (enumeration_arg);
|
|
5758
|
|
5759 if (!*enumeration_arg)
|
|
5760 {
|
|
5761 free (enumeration_arg);
|
|
5762 enumeration_arg = strdup (default_string);
|
|
5763 }
|
|
5764
|
|
5765 if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
|
|
5766 {
|
|
5767 warning ("%s requires a letter or a digit", insertion_type_pname (type));
|
|
5768
|
|
5769 switch (type)
|
|
5770 {
|
|
5771 case enumerate:
|
|
5772 default_string = "1";
|
|
5773 break;
|
|
5774 }
|
|
5775 enumeration_arg = strdup (default_string);
|
|
5776 }
|
|
5777 begin_insertion (type);
|
|
5778 }
|
|
5779
|
|
5780 static void
|
|
5781 cm_table (void)
|
|
5782 {
|
|
5783 begin_insertion (table);
|
|
5784 }
|
|
5785
|
|
5786 static void
|
|
5787 cm_ftable (void)
|
|
5788 {
|
|
5789 begin_insertion (ftable);
|
|
5790 }
|
|
5791
|
|
5792 static void
|
|
5793 cm_vtable (void)
|
|
5794 {
|
|
5795 begin_insertion (vtable);
|
|
5796 }
|
|
5797
|
|
5798 static void
|
|
5799 cm_group (void)
|
|
5800 {
|
|
5801 begin_insertion (group);
|
|
5802 }
|
|
5803
|
|
5804 static void
|
|
5805 cm_ifinfo (void)
|
|
5806 {
|
|
5807 /* begin_insertion (ifinfo); */
|
|
5808 /* BPW: @ifinfo can cross hierarchies */
|
|
5809 ifinfo_count++;
|
|
5810 if (in_menu)
|
|
5811 discard_until ("\n");
|
|
5812 }
|
|
5813
|
|
5814 /* Begin an insertion where the lines are not filled or indented. */
|
|
5815 static void
|
|
5816 cm_flushleft (void)
|
|
5817 {
|
|
5818 begin_insertion (flushleft);
|
|
5819 }
|
|
5820
|
|
5821 /* Begin an insertion where the lines are not filled, and each line is
|
|
5822 forced to the right-hand side of the page. */
|
|
5823 static void
|
|
5824 cm_flushright (void)
|
|
5825 {
|
|
5826 begin_insertion (flushright);
|
|
5827 }
|
|
5828
|
|
5829
|
|
5830 /* **************************************************************** */
|
|
5831 /* */
|
|
5832 /* Conditional Handling */
|
|
5833 /* */
|
|
5834 /* **************************************************************** */
|
|
5835
|
|
5836 /* A structure which contains `defined' variables. */
|
|
5837 typedef struct _defines {
|
|
5838 struct _defines *next;
|
|
5839 char *name;
|
|
5840 char *value;
|
|
5841 } DEFINE;
|
|
5842
|
|
5843 /* The linked list of `set' defines. */
|
|
5844 DEFINE *defines = (DEFINE *)NULL;
|
|
5845
|
|
5846 /* Add NAME to the list of `set' defines. */
|
|
5847 static void
|
|
5848 set (char *name, char *value)
|
|
5849 {
|
|
5850 DEFINE *temp;
|
|
5851
|
|
5852 for (temp = defines; temp; temp = temp->next)
|
|
5853 if (strcmp (name, temp->name) == 0)
|
|
5854 {
|
|
5855 free (temp->value);
|
|
5856 temp->value = strdup (value);
|
|
5857 return;
|
|
5858 }
|
|
5859
|
|
5860 temp = (DEFINE *)xmalloc (sizeof (DEFINE));
|
|
5861 temp->next = defines;
|
|
5862 temp->name = strdup (name);
|
|
5863 temp->value = strdup (value);
|
|
5864 defines = temp;
|
|
5865 }
|
|
5866
|
|
5867 /* Remove NAME from the list of `set' defines. */
|
|
5868 static void
|
|
5869 clear (char *name)
|
|
5870 {
|
|
5871 register DEFINE *temp, *last;
|
|
5872
|
|
5873 last = (DEFINE *)NULL;
|
|
5874 temp = defines;
|
|
5875
|
|
5876 while (temp)
|
|
5877 {
|
|
5878 if (strcmp (temp->name, name) == 0)
|
|
5879 {
|
|
5880 if (last)
|
|
5881 last->next = temp->next;
|
|
5882 else
|
|
5883 defines = temp->next;
|
|
5884
|
|
5885 free (temp->name);
|
|
5886 free (temp->value);
|
|
5887 free (temp);
|
|
5888 break;
|
|
5889 }
|
|
5890 last = temp;
|
|
5891 temp = temp->next;
|
|
5892 }
|
|
5893 }
|
|
5894
|
|
5895 /* Return the value of NAME. The return value is NULL if NAME is unset. */
|
|
5896 static char *
|
|
5897 set_p (char *name)
|
|
5898 {
|
|
5899 register DEFINE *temp;
|
|
5900
|
|
5901 for (temp = defines; temp; temp = temp->next)
|
|
5902 if (strcmp (temp->name, name) == 0)
|
|
5903 return (temp->value);
|
|
5904
|
|
5905 return ((char *)NULL);
|
|
5906 }
|
|
5907
|
|
5908 /* Conditionally parse based on the current command name. */
|
|
5909 static void
|
|
5910 command_name_condition (void)
|
|
5911 {
|
|
5912 char *discarder;
|
|
5913
|
|
5914 discarder = (char *)xmalloc (8 + strlen (command));
|
|
5915
|
|
5916 sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
|
|
5917 discard_until (discarder);
|
|
5918 discard_until ("\n");
|
|
5919
|
|
5920 free (discarder);
|
|
5921 }
|
|
5922
|
|
5923 /* Create a variable whose name appears as the first word on this line. */
|
|
5924 static void
|
|
5925 cm_set (void)
|
|
5926 {
|
|
5927 handle_variable (SET);
|
|
5928 }
|
|
5929
|
|
5930 /* Remove a variable whose name appears as the first word on this line. */
|
|
5931 static void
|
|
5932 cm_clear (void)
|
|
5933 {
|
|
5934 handle_variable (CLEAR);
|
|
5935 }
|
|
5936
|
|
5937 static void
|
|
5938 cm_ifset (void)
|
|
5939 {
|
|
5940 handle_variable (IFSET);
|
|
5941 }
|
|
5942
|
|
5943 static void
|
|
5944 cm_ifclear (void)
|
|
5945 {
|
|
5946 handle_variable (IFCLEAR);
|
|
5947 }
|
|
5948
|
|
5949 /* This command takes braces, but we parse the contents specially, so we
|
|
5950 don't use the standard brace popping code.
|
|
5951
|
|
5952 The syntax @ifeq{arg1, arg2, texinfo commands} performs texinfo commands
|
|
5953 if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
|
|
5954 it produces no output. */
|
|
5955 static void
|
|
5956 cm_ifeq (void)
|
|
5957 {
|
|
5958 char **arglist;
|
|
5959
|
|
5960 arglist = get_brace_args (0);
|
|
5961
|
|
5962 if (arglist)
|
|
5963 {
|
|
5964 if (array_len (arglist) > 1)
|
|
5965 {
|
|
5966 if ((strcasecmp (arglist[0], arglist[1]) == 0) &&
|
|
5967 (arglist[2] != (char *)NULL))
|
|
5968 execute_string ("%s\n", arglist[2]);
|
|
5969 }
|
|
5970
|
|
5971 free_array (arglist);
|
|
5972 }
|
|
5973 }
|
|
5974
|
|
5975 static void
|
|
5976 cm_value (int arg, int start_pos, int end_pos)
|
|
5977 {
|
|
5978 if (arg == END)
|
|
5979 {
|
|
5980 char *name, *value;
|
|
5981 name = (char *)&output_paragraph[start_pos];
|
|
5982 output_paragraph[end_pos] = '\0';
|
|
5983 name = strdup (name);
|
|
5984 value = set_p (name);
|
|
5985 output_column -= end_pos - start_pos;
|
|
5986 output_paragraph_offset = start_pos;
|
|
5987
|
|
5988 if (value)
|
|
5989 execute_string ("%s", value);
|
|
5990 else
|
|
5991 add_word_args ("{No Value For \"%s\"}", name);
|
|
5992
|
|
5993 free (name);
|
|
5994 }
|
|
5995 }
|
|
5996
|
|
5997 /* Set, clear, or conditionalize based on ACTION. */
|
|
5998 static void
|
|
5999 handle_variable (int action)
|
|
6000 {
|
|
6001 char *name;
|
|
6002
|
|
6003 get_rest_of_line (&name);
|
|
6004 backup_input_pointer ();
|
|
6005 canon_white (name);
|
|
6006 handle_variable_internal (action, name);
|
|
6007 free (name);
|
|
6008 }
|
|
6009
|
|
6010 static void
|
|
6011 handle_variable_internal (int action, char *name)
|
|
6012 {
|
|
6013 char *temp;
|
|
6014 int delimiter, additional_text_present = 0;
|
|
6015
|
|
6016 /* Only the first word of NAME is a valid tag. */
|
|
6017 temp = name;
|
|
6018 delimiter = 0;
|
|
6019 while (*temp && (delimiter || !whitespace (*temp)))
|
|
6020 {
|
|
6021 /* #if defined (SET_WITH_EQUAL) */
|
|
6022 if (*temp == '"' || *temp == '\'')
|
|
6023 {
|
|
6024 if (*temp == delimiter)
|
|
6025 delimiter = 0;
|
|
6026 else
|
|
6027 delimiter = *temp;
|
|
6028 }
|
|
6029 /* #endif SET_WITH_EQUAL */
|
|
6030 temp++;
|
|
6031 }
|
|
6032
|
|
6033 if (*temp)
|
|
6034 additional_text_present++;
|
|
6035
|
|
6036 *temp = '\0';
|
|
6037
|
|
6038 if (!*name)
|
|
6039 line_error ("%c%s requires a name", COMMAND_PREFIX, command);
|
|
6040 else
|
|
6041 {
|
|
6042 switch (action)
|
|
6043 {
|
|
6044 case SET:
|
|
6045 {
|
|
6046 char *value;
|
|
6047
|
|
6048 #if defined (SET_WITH_EQUAL)
|
|
6049 /* Allow a value to be saved along with a variable. The value is
|
|
6050 the text following an `=' sign in NAME, if any is present. */
|
|
6051
|
|
6052 for (value = name; *value && *value != '='; value++);
|
|
6053
|
|
6054 if (*value)
|
|
6055 *value++ = '\0';
|
|
6056
|
|
6057 if (*value == '"' || *value == '\'')
|
|
6058 {
|
|
6059 value++;
|
|
6060 value[strlen (value) - 1] = '\0';
|
|
6061 }
|
|
6062
|
|
6063 #else /* !SET_WITH_EQUAL */
|
|
6064 /* The VALUE of NAME is the remainder of the line sans
|
|
6065 whitespace. */
|
|
6066 if (additional_text_present)
|
|
6067 {
|
|
6068 value = temp + 1;
|
|
6069 canon_white (value);
|
|
6070 }
|
|
6071 else
|
|
6072 value = "";
|
|
6073 #endif /* !SET_WITH_VALUE */
|
|
6074
|
|
6075 set (name, value);
|
|
6076 }
|
|
6077 break;
|
|
6078
|
|
6079 case CLEAR:
|
|
6080 clear (name);
|
|
6081 break;
|
|
6082
|
|
6083 case IFSET:
|
|
6084 case IFCLEAR:
|
|
6085 /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
|
2
|
6086 read lines from the file until we reach a matching
|
0
|
6087 "@end CONDITION". This means that we only take note of
|
|
6088 "@ifset/clear" and "@end" commands. */
|
|
6089 {
|
|
6090 char condition[8];
|
|
6091 int condition_len;
|
|
6092
|
|
6093 if (action == IFSET)
|
|
6094 strcpy (condition, "ifset");
|
|
6095 else
|
|
6096 strcpy (condition, "ifclear");
|
|
6097
|
|
6098 condition_len = strlen (condition);
|
|
6099
|
|
6100 if ((action == IFSET && !set_p (name)) ||
|
|
6101 (action == IFCLEAR && set_p (name)))
|
|
6102 {
|
|
6103 int level = 0, done = 0;
|
|
6104
|
|
6105 while (!done)
|
|
6106 {
|
|
6107 char *freeable_line, *line;
|
|
6108
|
|
6109 get_rest_of_line (&freeable_line);
|
|
6110
|
|
6111 for (line = freeable_line; whitespace (*line); line++);
|
|
6112
|
|
6113 if (*line == COMMAND_PREFIX &&
|
|
6114 (strncmp (line + 1, condition, condition_len) == 0))
|
|
6115 level++;
|
|
6116 else if (strncmp (line, "@end", 4) == 0)
|
|
6117 {
|
|
6118 char *cname = line + 4;
|
|
6119 char *temp2;
|
|
6120
|
|
6121 while (*cname && whitespace (*cname))
|
|
6122 cname++;
|
|
6123 temp2 = cname;
|
|
6124
|
|
6125 while (*temp2 && !whitespace (*temp2))
|
|
6126 temp2++;
|
|
6127 *temp2 = '\0';
|
|
6128
|
|
6129 if (strcmp (cname, condition) == 0)
|
|
6130 {
|
|
6131 if (!level)
|
|
6132 {
|
|
6133 done = 1;
|
|
6134 }
|
|
6135 else
|
|
6136 level--;
|
|
6137 }
|
|
6138 }
|
|
6139 free (freeable_line);
|
|
6140 }
|
|
6141 /* We found the end of a false @ifset/ifclear. If we are
|
|
6142 in a menu, back up over the newline that ends the ifset,
|
|
6143 since that newline may also begin the next menu entry. */
|
|
6144 break;
|
|
6145 }
|
|
6146 else
|
|
6147 {
|
|
6148 if (action == IFSET)
|
|
6149 begin_insertion (ifset);
|
|
6150 else
|
|
6151 begin_insertion (ifclear);
|
|
6152 }
|
|
6153 }
|
|
6154 break;
|
|
6155 }
|
|
6156 }
|
|
6157 }
|
|
6158
|
|
6159
|
|
6160 /* **************************************************************** */
|
|
6161 /* */
|
|
6162 /* Execution of Random Text not in file */
|
|
6163 /* */
|
|
6164 /* **************************************************************** */
|
|
6165
|
|
6166 typedef struct {
|
|
6167 char *string; /* The string buffer. */
|
|
6168 int size; /* The size of the buffer. */
|
|
6169 int in_use; /* Non-zero means string currently in use. */
|
|
6170 } EXECUTION_STRING;
|
|
6171
|
|
6172 static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL;
|
|
6173 static int execution_strings_index = 0;
|
|
6174 static int execution_strings_slots = 0;
|
|
6175
|
|
6176 static EXECUTION_STRING *
|
|
6177 get_execution_string (int initial_size)
|
|
6178 {
|
|
6179 register int i = 0;
|
|
6180 EXECUTION_STRING *es = (EXECUTION_STRING *)NULL;
|
|
6181
|
|
6182 if (execution_strings)
|
|
6183 {
|
|
6184 for (i = 0; i < execution_strings_index; i++)
|
|
6185 if (execution_strings[i] && (execution_strings[i]->in_use == 0))
|
|
6186 {
|
|
6187 es = execution_strings[i];
|
|
6188 break;
|
|
6189 }
|
|
6190 }
|
|
6191
|
|
6192 if (!es)
|
|
6193 {
|
|
6194 if (execution_strings_index + 1 >= execution_strings_slots)
|
|
6195 {
|
|
6196 execution_strings = (EXECUTION_STRING **)xrealloc
|
|
6197 (execution_strings,
|
|
6198 (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
|
|
6199 for (; i < execution_strings_slots; i++)
|
|
6200 execution_strings[i] = (EXECUTION_STRING *)NULL;
|
|
6201 }
|
|
6202
|
|
6203 execution_strings[execution_strings_index] =
|
|
6204 (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING));
|
|
6205 es = execution_strings[execution_strings_index];
|
|
6206 execution_strings_index++;
|
|
6207
|
|
6208 es->size = 0;
|
|
6209 es->string = (char *)NULL;
|
|
6210 es->in_use = 0;
|
|
6211 }
|
|
6212
|
|
6213 if (initial_size > es->size)
|
|
6214 {
|
|
6215 es->string = (char *) xrealloc (es->string, initial_size);
|
|
6216 es->size = initial_size;
|
|
6217 }
|
|
6218 return (es);
|
|
6219 }
|
|
6220
|
|
6221 /* Execute the string produced by formatting the ARGs with FORMAT. This
|
|
6222 is like submitting a new file with @include. */
|
|
6223
|
|
6224 static void
|
|
6225 execute_string (char *formatte, ...)
|
|
6226 {
|
|
6227 va_list ap;
|
|
6228 EXECUTION_STRING *es;
|
|
6229 char *temp_string;
|
|
6230
|
|
6231 es = get_execution_string (4000);
|
|
6232 temp_string = es->string;
|
|
6233 es->in_use = 1;
|
|
6234
|
|
6235 va_start (ap, formatte);
|
|
6236 vsprintf (temp_string, formatte, ap);
|
|
6237 va_end (ap);
|
|
6238
|
|
6239 pushfile ();
|
|
6240 input_text_offset = 0;
|
|
6241 input_text = temp_string;
|
|
6242 input_filename = strdup (input_filename);
|
|
6243 size_of_input_text = strlen (temp_string);
|
|
6244
|
|
6245 executing_string++;
|
|
6246 reader_loop ();
|
|
6247 free (input_filename);
|
|
6248
|
|
6249 popfile ();
|
|
6250 executing_string--;
|
|
6251 es->in_use = 0;
|
|
6252 }
|
|
6253
|
|
6254 /* **************************************************************** */
|
|
6255 /* */
|
|
6256 /* @itemx, @item */
|
|
6257 /* */
|
|
6258 /* **************************************************************** */
|
|
6259
|
|
6260 static int itemx_flag = 0;
|
|
6261
|
|
6262 static void
|
|
6263 cm_itemx (void)
|
|
6264 {
|
|
6265 itemx_flag++;
|
|
6266 cm_item ();
|
|
6267 itemx_flag--;
|
|
6268 }
|
|
6269
|
|
6270 static void
|
|
6271 cm_item (void)
|
|
6272 {
|
|
6273 char *rest_of_line, *item_func;
|
|
6274
|
|
6275 /* Can only hack "@item" while inside of an insertion. */
|
|
6276 if (insertion_level)
|
|
6277 {
|
|
6278 INSERTION_ELT *stack = insertion_stack;
|
|
6279 int original_input_text_offset;
|
|
6280
|
|
6281 skip_whitespace ();
|
|
6282 original_input_text_offset = input_text_offset;
|
|
6283
|
|
6284 get_rest_of_line (&rest_of_line);
|
|
6285 canon_white (rest_of_line);
|
|
6286 item_func = current_item_function ();
|
|
6287
|
|
6288 /* Okay, do the right thing depending on which insertion function
|
|
6289 is active. */
|
|
6290
|
|
6291 switch_top:
|
|
6292 switch (stack->insertion)
|
|
6293 {
|
|
6294 case ifinfo:
|
|
6295 case ifset:
|
|
6296 case ifclear:
|
|
6297 case cartouche:
|
|
6298 stack = stack->next;
|
|
6299 if (!stack)
|
|
6300 goto no_insertion;
|
|
6301 else
|
|
6302 goto switch_top;
|
|
6303 break;
|
|
6304
|
|
6305 case menu:
|
|
6306 case quotation:
|
|
6307 case example:
|
|
6308 case smallexample:
|
|
6309 case lisp:
|
|
6310 case format:
|
|
6311 case display:
|
|
6312 case group:
|
|
6313 line_error ("The `%c%s' command is meaningless within a `@%s' block",
|
|
6314 COMMAND_PREFIX, command,
|
|
6315 insertion_type_pname (current_insertion_type ()));
|
|
6316 break;
|
|
6317
|
|
6318 case itemize:
|
|
6319 case enumerate:
|
|
6320 if (itemx_flag)
|
|
6321 {
|
|
6322 line_error ("%citemx is not meaningful inside of a `%s' block",
|
|
6323 COMMAND_PREFIX,
|
|
6324 insertion_type_pname (current_insertion_type ()));
|
|
6325 }
|
|
6326 else
|
|
6327 {
|
|
6328 start_paragraph ();
|
|
6329 kill_self_indent (-1);
|
|
6330 filling_enabled = indented_fill = 1;
|
|
6331
|
|
6332 if (current_insertion_type () == itemize)
|
|
6333 {
|
|
6334 indent (output_column = current_indent - 2);
|
|
6335
|
|
6336 /* I need some way to determine whether this command
|
|
6337 takes braces or not. I believe the user can type
|
|
6338 either "@bullet" or "@bullet{}". Of course, they
|
|
6339 can also type "o" or "#" or whatever else they want. */
|
|
6340 if (item_func && *item_func)
|
|
6341 {
|
|
6342 if (*item_func == COMMAND_PREFIX)
|
|
6343 if (item_func[strlen (item_func) - 1] != '}')
|
|
6344 execute_string ("%s{}", item_func);
|
|
6345 else
|
|
6346 execute_string ("%s", item_func);
|
|
6347 else
|
|
6348 execute_string ("%s", item_func);
|
|
6349 }
|
|
6350 insert (' ');
|
|
6351 output_column++;
|
|
6352 }
|
|
6353 else
|
|
6354 enumerate_item ();
|
|
6355
|
|
6356 /* Special hack. This makes close paragraph ignore you until
|
|
6357 the start_paragraph () function has been called. */
|
|
6358 must_start_paragraph = 1;
|
|
6359
|
|
6360 /* Ultra special hack. It appears that some people incorrectly
|
|
6361 place text directly after the @item, instead of on a new line
|
|
6362 by itself. This happens to work in TeX, so I make it work
|
|
6363 here. */
|
|
6364 if (*rest_of_line)
|
|
6365 {
|
|
6366 line_number--;
|
|
6367 input_text_offset = original_input_text_offset;
|
|
6368 }
|
|
6369 }
|
|
6370 break;
|
|
6371
|
|
6372 case table:
|
|
6373 case ftable:
|
|
6374 case vtable:
|
|
6375 {
|
|
6376 /* Get rid of extra characters. */
|
|
6377 kill_self_indent (-1);
|
|
6378
|
|
6379 /* close_paragraph () almost does what we want. The problem
|
|
6380 is when paragraph_is_open, and last_char_was_newline, and
|
|
6381 the last newline has been turned into a space, because
|
|
6382 filling_enabled. I handle it here. */
|
|
6383 if (last_char_was_newline && filling_enabled && paragraph_is_open)
|
|
6384 insert ('\n');
|
|
6385 close_paragraph ();
|
|
6386
|
|
6387 #if defined (INDENT_PARAGRAPHS_IN_TABLE)
|
|
6388 /* Indent on a new line, but back up one indentation level. */
|
|
6389 {
|
|
6390 int t;
|
|
6391
|
|
6392 t = inhibit_paragraph_indentation;
|
|
6393 inhibit_paragraph_indentation = 1;
|
|
6394 /* At this point, inserting any non-whitespace character will
|
|
6395 force the existing indentation to be output. */
|
|
6396 add_char ('i');
|
|
6397 inhibit_paragraph_indentation = t;
|
|
6398 }
|
|
6399 #else /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
6400 add_char ('i');
|
|
6401 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
|
|
6402
|
|
6403 output_paragraph_offset--;
|
|
6404 kill_self_indent (default_indentation_increment + 1);
|
|
6405
|
|
6406 /* Add item's argument to the line. */
|
|
6407 filling_enabled = 0;
|
|
6408 if (item_func && *item_func)
|
|
6409 execute_string ("%s{%s}", item_func, rest_of_line);
|
|
6410 else
|
|
6411 execute_string ("%s", rest_of_line);
|
|
6412
|
|
6413 if (current_insertion_type () == ftable)
|
|
6414 execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
|
|
6415
|
|
6416 if (current_insertion_type () == vtable)
|
|
6417 execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
|
|
6418
|
|
6419 /* Start a new line, and let start_paragraph ()
|
|
6420 do the indenting of it for you. */
|
|
6421 close_single_paragraph ();
|
|
6422 indented_fill = filling_enabled = 1;
|
|
6423 }
|
|
6424
|
|
6425 default:
|
|
6426 break;
|
|
6427 }
|
|
6428 free (rest_of_line);
|
|
6429 }
|
|
6430 else
|
|
6431 {
|
|
6432 no_insertion:
|
|
6433 line_error ("%c%s found outside of an insertion block",
|
|
6434 COMMAND_PREFIX, command);
|
|
6435 }
|
|
6436 }
|
|
6437
|
|
6438
|
|
6439 /* **************************************************************** */
|
|
6440 /* */
|
|
6441 /* Defun and Friends */
|
|
6442 /* */
|
|
6443 /* **************************************************************** */
|
|
6444
|
|
6445 #define DEFUN_SELF_DELIMITING(c) \
|
|
6446 (((c) == '(') \
|
|
6447 || ((c) == ')') \
|
|
6448 || ((c) == '[') \
|
|
6449 || ((c) == ']'))
|
|
6450
|
|
6451 struct token_accumulator
|
|
6452 {
|
|
6453 unsigned int length;
|
|
6454 unsigned int index;
|
|
6455 char **tokens;
|
|
6456 };
|
|
6457
|
|
6458 static void
|
|
6459 initialize_token_accumulator (struct token_accumulator *accumulator)
|
|
6460 {
|
|
6461 (accumulator->length) = 0;
|
|
6462 (accumulator->index) = 0;
|
|
6463 (accumulator->tokens) = NULL;
|
|
6464 }
|
|
6465
|
|
6466 static void
|
|
6467 accumulate_token (struct token_accumulator *accumulator, char *token)
|
|
6468 {
|
|
6469 if ((accumulator->index) >= (accumulator->length))
|
|
6470 {
|
|
6471 (accumulator->length) += 10;
|
|
6472 (accumulator->tokens) = (char **) xrealloc
|
|
6473 (accumulator->tokens, (accumulator->length * sizeof (char *)));
|
|
6474 }
|
|
6475 accumulator->tokens[accumulator->index] = token;
|
|
6476 accumulator->index += 1;
|
|
6477 }
|
|
6478
|
|
6479 static char *
|
|
6480 copy_substring (char *start, char *end)
|
|
6481 {
|
|
6482 char *result, *scan, *scan_result;
|
|
6483
|
|
6484 result = (char *) xmalloc ((end - start) + 1);
|
|
6485 scan_result = result;
|
|
6486 scan = start;
|
|
6487
|
|
6488 while (scan < end)
|
|
6489 *scan_result++ = *scan++;
|
|
6490
|
|
6491 *scan_result = '\0';
|
|
6492 return (result);
|
|
6493 }
|
|
6494
|
|
6495 /* Given `string' pointing at an open brace, skip forward and return a
|
|
6496 pointer to just past the matching close brace. */
|
|
6497 static int
|
|
6498 scan_group_in_string (char **string_pointer)
|
|
6499 {
|
|
6500 register int c;
|
|
6501 register char *scan_string;
|
|
6502 register unsigned int level = 1;
|
|
6503
|
|
6504 scan_string = (*string_pointer) + 1;
|
|
6505
|
|
6506 while (1)
|
|
6507 {
|
|
6508 if (level == 0)
|
|
6509 {
|
|
6510 (*string_pointer) = scan_string;
|
|
6511 return (1);
|
|
6512 }
|
|
6513 c = (*scan_string++);
|
|
6514 if (c == '\0')
|
|
6515 {
|
|
6516 /* Tweak line_number to compensate for fact that
|
|
6517 we gobbled the whole line before coming here. */
|
|
6518 line_number -= 1;
|
|
6519 line_error ("Missing `}' in %cdef arg", COMMAND_PREFIX);
|
|
6520 line_number += 1;
|
|
6521 (*string_pointer) = (scan_string - 1);
|
|
6522 return (0);
|
|
6523 }
|
|
6524 if (c == '{')
|
|
6525 level += 1;
|
|
6526 if (c == '}')
|
|
6527 level -= 1;
|
|
6528 }
|
|
6529 }
|
|
6530
|
|
6531 /* Return a list of tokens from the contents of `string'.
|
|
6532 Commands and brace-delimited groups count as single tokens.
|
|
6533 Contiguous whitespace characters are converted to a token
|
|
6534 consisting of a single space. */
|
|
6535 static char **
|
|
6536 args_from_string (char *string)
|
|
6537 {
|
|
6538 struct token_accumulator accumulator;
|
|
6539 register char *scan_string = string;
|
|
6540 char *token_start, *token_end;
|
|
6541
|
|
6542 initialize_token_accumulator (&accumulator);
|
|
6543
|
|
6544 while ((*scan_string) != '\0')
|
|
6545 {
|
|
6546 /* Replace arbitrary whitespace by a single space. */
|
|
6547 if (whitespace (*scan_string))
|
|
6548 {
|
|
6549 scan_string += 1;
|
|
6550 while (whitespace (*scan_string))
|
|
6551 scan_string += 1;
|
|
6552 accumulate_token ((&accumulator), (strdup (" ")));
|
|
6553 continue;
|
|
6554 }
|
|
6555
|
|
6556 /* Commands count as single tokens. */
|
|
6557 if ((*scan_string) == COMMAND_PREFIX)
|
|
6558 {
|
|
6559 token_start = scan_string;
|
|
6560 scan_string += 1;
|
|
6561 if (self_delimiting (*scan_string))
|
|
6562 scan_string += 1;
|
|
6563 else
|
|
6564 {
|
|
6565 register int c;
|
|
6566 while (1)
|
|
6567 {
|
|
6568 c = *scan_string++;
|
|
6569
|
|
6570 if ((c == '\0') || (c == '{') || (whitespace (c)))
|
|
6571 {
|
|
6572 scan_string -= 1;
|
|
6573 break;
|
|
6574 }
|
|
6575 }
|
|
6576
|
|
6577 if (*scan_string == '{')
|
|
6578 {
|
|
6579 char *s = scan_string;
|
|
6580 (void) scan_group_in_string (&s);
|
|
6581 scan_string = s;
|
|
6582 }
|
|
6583 }
|
|
6584 token_end = scan_string;
|
|
6585 }
|
|
6586
|
|
6587 /* Parentheses and brackets are self-delimiting. */
|
|
6588 else if (DEFUN_SELF_DELIMITING (*scan_string))
|
|
6589 {
|
|
6590 token_start = scan_string;
|
|
6591 scan_string += 1;
|
|
6592 token_end = scan_string;
|
|
6593 }
|
|
6594
|
|
6595 /* Open brace introduces a group that is a single token. */
|
|
6596 else if (*scan_string == '{')
|
|
6597 {
|
|
6598 char *s = scan_string;
|
|
6599 int balanced = scan_group_in_string (&s);
|
|
6600
|
|
6601 token_start = scan_string + 1;
|
|
6602 scan_string = s;
|
|
6603 token_end = balanced ? (scan_string - 1) : scan_string;
|
|
6604 }
|
|
6605
|
|
6606 /* Otherwise a token is delimited by whitespace, parentheses,
|
|
6607 brackets, or braces. A token is also ended by a command. */
|
|
6608 else
|
|
6609 {
|
|
6610 token_start = scan_string;
|
|
6611
|
|
6612 while (1)
|
|
6613 {
|
|
6614 register int c;
|
|
6615
|
|
6616 c = *scan_string++;
|
|
6617
|
|
6618 if (!c ||
|
|
6619 (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
|
|
6620 c == '{' || c == '}'))
|
|
6621 {
|
|
6622 scan_string--;
|
|
6623 break;
|
|
6624 }
|
|
6625
|
|
6626 /* If we encounter a command imbedded within a token,
|
|
6627 then end the token. */
|
|
6628 if (c == COMMAND_PREFIX)
|
|
6629 {
|
|
6630 scan_string--;
|
|
6631 break;
|
|
6632 }
|
|
6633 }
|
|
6634 token_end = scan_string;
|
|
6635 }
|
|
6636
|
|
6637 accumulate_token
|
|
6638 (&accumulator, copy_substring (token_start, token_end));
|
|
6639 }
|
|
6640 accumulate_token (&accumulator, NULL);
|
|
6641 return (accumulator.tokens);
|
|
6642 }
|
|
6643
|
|
6644 static void
|
|
6645 process_defun_args (char **defun_args, int auto_var_p)
|
|
6646 {
|
|
6647 int pending_space = 0;
|
|
6648
|
|
6649 while (1)
|
|
6650 {
|
|
6651 char *defun_arg = *defun_args++;
|
|
6652
|
|
6653 if (defun_arg == NULL)
|
|
6654 break;
|
|
6655
|
|
6656 if (defun_arg[0] == ' ')
|
|
6657 {
|
|
6658 pending_space = 1;
|
|
6659 continue;
|
|
6660 }
|
|
6661
|
|
6662 if (pending_space)
|
|
6663 {
|
|
6664 add_char (' ');
|
|
6665 pending_space = 0;
|
|
6666 }
|
|
6667
|
|
6668 if (DEFUN_SELF_DELIMITING (defun_arg[0]))
|
|
6669 add_char (defun_arg[0]);
|
|
6670 else if (defun_arg[0] == '&')
|
|
6671 add_word (defun_arg);
|
|
6672 else if (defun_arg[0] == COMMAND_PREFIX)
|
|
6673 execute_string ("%s", defun_arg);
|
|
6674 else if (auto_var_p)
|
|
6675 execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg);
|
|
6676 else
|
|
6677 add_word (defun_arg);
|
|
6678 }
|
|
6679 }
|
|
6680
|
|
6681 static char *
|
|
6682 next_nonwhite_defun_arg (char ***arg_pointer)
|
|
6683 {
|
|
6684 char **scan = (*arg_pointer);
|
|
6685 char *arg = (*scan++);
|
|
6686
|
|
6687 if ((arg != 0) && (*arg == ' '))
|
|
6688 arg = *scan++;
|
|
6689
|
|
6690 if (arg == 0)
|
|
6691 scan -= 1;
|
|
6692
|
|
6693 *arg_pointer = scan;
|
|
6694
|
|
6695 return ((arg == 0) ? "" : arg);
|
|
6696 }
|
|
6697
|
|
6698 /* Make the defun type insertion.
|
|
6699 TYPE says which insertion this is.
|
|
6700 X_P says not to start a new insertion if non-zero. */
|
|
6701 static void
|
|
6702 defun_internal (enum insertion_type type, int x_p)
|
|
6703 {
|
|
6704 enum insertion_type base_type;
|
|
6705 char **defun_args, **scan_args;
|
|
6706 char *category, *defined_name, *type_name, *type_name2;
|
|
6707
|
|
6708 {
|
|
6709 char *line;
|
|
6710 get_rest_of_line (&line);
|
|
6711 defun_args = (args_from_string (line));
|
|
6712 free (line);
|
|
6713 }
|
|
6714
|
|
6715 scan_args = defun_args;
|
|
6716
|
|
6717 switch (type)
|
|
6718 {
|
|
6719 case defun:
|
|
6720 category = "Function";
|
|
6721 base_type = deffn;
|
|
6722 break;
|
|
6723 case defmac:
|
|
6724 category = "Macro";
|
|
6725 base_type = deffn;
|
|
6726 break;
|
|
6727 case defspec:
|
|
6728 category = "Special Form";
|
|
6729 base_type = deffn;
|
|
6730 break;
|
|
6731 case defvar:
|
|
6732 category = "Variable";
|
|
6733 base_type = defvr;
|
|
6734 break;
|
|
6735 case defopt:
|
|
6736 category = "User Option";
|
|
6737 base_type = defvr;
|
|
6738 break;
|
|
6739 case deftypefun:
|
|
6740 category = "Function";
|
|
6741 base_type = deftypefn;
|
|
6742 break;
|
|
6743 case deftypevar:
|
|
6744 category = "Variable";
|
|
6745 base_type = deftypevr;
|
|
6746 break;
|
|
6747 case defivar:
|
|
6748 category = "Instance Variable";
|
|
6749 base_type = defcv;
|
|
6750 break;
|
|
6751 case defmethod:
|
|
6752 category = "Method";
|
|
6753 base_type = defop;
|
|
6754 break;
|
|
6755 case deftypemethod:
|
|
6756 category = "Method";
|
|
6757 base_type = deftypemethod;
|
|
6758 break;
|
|
6759 default:
|
|
6760 category = next_nonwhite_defun_arg (&scan_args);
|
|
6761 base_type = type;
|
|
6762 break;
|
|
6763 }
|
|
6764
|
|
6765 if ((base_type == deftypefn)
|
|
6766 || (base_type == deftypevr)
|
|
6767 || (base_type == defcv)
|
|
6768 || (base_type == defop)
|
|
6769 || (base_type == deftypemethod))
|
|
6770 type_name = next_nonwhite_defun_arg (&scan_args);
|
|
6771
|
|
6772 if (base_type == deftypemethod)
|
|
6773 type_name2 = next_nonwhite_defun_arg (&scan_args);
|
|
6774
|
|
6775 defined_name = next_nonwhite_defun_arg (&scan_args);
|
|
6776
|
|
6777 /* This hack exists solely for the purposes of formatting the texinfo
|
|
6778 manual. I couldn't think of a better way. The token might be
|
|
6779 a simple @@ followed immediately by more text. If this is the case,
|
|
6780 then the next defun arg is part of this one, and we should concatenate
|
|
6781 them. */
|
|
6782 if (*scan_args && **scan_args && !whitespace (**scan_args) &&
|
|
6783 (strcmp (defined_name, "@@") == 0))
|
|
6784 {
|
|
6785 char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
|
|
6786
|
|
6787 sprintf (tem, "@@%s", scan_args[0]);
|
|
6788
|
|
6789 free (scan_args[0]);
|
|
6790 scan_args[0] = tem;
|
|
6791 scan_args++;
|
|
6792 defined_name = tem;
|
|
6793 }
|
|
6794
|
|
6795 if (!x_p)
|
|
6796 begin_insertion (type);
|
|
6797
|
|
6798 /* Write the definition header line.
|
|
6799 This should start at the normal indentation. */
|
|
6800 current_indent -= default_indentation_increment;
|
|
6801 start_paragraph ();
|
|
6802
|
|
6803 switch (base_type)
|
|
6804 {
|
|
6805 case deffn:
|
|
6806 case defvr:
|
|
6807 case deftp:
|
|
6808 execute_string (" -- %s: %s", category, defined_name);
|
|
6809 break;
|
|
6810 case deftypefn:
|
|
6811 case deftypevr:
|
|
6812 execute_string (" -- %s: %s %s", category, type_name, defined_name);
|
|
6813 break;
|
|
6814 case defcv:
|
|
6815 execute_string (" -- %s of %s: %s", category, type_name, defined_name);
|
|
6816 break;
|
|
6817 case defop:
|
|
6818 execute_string (" -- %s on %s: %s", category, type_name, defined_name);
|
|
6819 break;
|
|
6820 case deftypemethod:
|
|
6821 execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
|
|
6822 defined_name);
|
|
6823 break;
|
|
6824 default:
|
|
6825 break;
|
|
6826 }
|
|
6827 current_indent += default_indentation_increment;
|
|
6828
|
|
6829 /* Now process the function arguments, if any.
|
|
6830 If these carry onto the next line, they should be indented by two
|
|
6831 increments to distinguish them from the body of the definition,
|
|
6832 which is indented by one increment. */
|
|
6833 current_indent += default_indentation_increment;
|
|
6834
|
|
6835 switch (base_type)
|
|
6836 {
|
|
6837 case deffn:
|
|
6838 case defop:
|
|
6839 process_defun_args (scan_args, 1);
|
|
6840 break;
|
|
6841 case deftp:
|
|
6842 case deftypefn:
|
|
6843 case deftypemethod:
|
|
6844 process_defun_args (scan_args, 0);
|
|
6845 break;
|
|
6846 default:
|
|
6847 break;
|
|
6848 }
|
|
6849 current_indent -= default_indentation_increment;
|
|
6850 close_single_paragraph ();
|
|
6851
|
|
6852 /* Make an entry in the appropriate index. */
|
|
6853 switch (base_type)
|
|
6854 {
|
|
6855 case deffn:
|
|
6856 case deftypefn:
|
|
6857 execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name);
|
|
6858 break;
|
|
6859 case defvr:
|
|
6860 case deftypevr:
|
|
6861 case defcv:
|
|
6862 execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name);
|
|
6863 break;
|
|
6864 case defop:
|
|
6865 case deftypemethod:
|
|
6866 execute_string ("%cfindex %s on %s\n",
|
|
6867 COMMAND_PREFIX, defined_name, type_name);
|
|
6868 break;
|
|
6869 case deftp:
|
|
6870 execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name);
|
|
6871 break;
|
|
6872 default:
|
|
6873 break;
|
|
6874 }
|
|
6875
|
|
6876 /* Deallocate the token list. */
|
|
6877 scan_args = defun_args;
|
|
6878 while (1)
|
|
6879 {
|
|
6880 char * arg = (*scan_args++);
|
|
6881 if (arg == NULL)
|
|
6882 break;
|
|
6883 free (arg);
|
|
6884 }
|
|
6885 free (defun_args);
|
|
6886 }
|
|
6887
|
|
6888 /* Add an entry for a function, macro, special form, variable, or option.
|
|
6889 If the name of the calling command ends in `x', then this is an extra
|
|
6890 entry included in the body of an insertion of the same type. */
|
|
6891 static void
|
|
6892 cm_defun (void)
|
|
6893 {
|
|
6894 int x_p;
|
|
6895 enum insertion_type type;
|
|
6896 char *temp = strdup (command);
|
|
6897
|
|
6898 x_p = (command[strlen (command) - 1] == 'x');
|
|
6899
|
|
6900 if (x_p)
|
|
6901 temp[strlen (temp) - 1] = '\0';
|
|
6902
|
|
6903 type = find_type_from_name (temp);
|
|
6904 free (temp);
|
|
6905
|
|
6906 /* If we are adding to an already existing insertion, then make sure
|
|
6907 that we are already in an insertion of type TYPE. */
|
|
6908 if (x_p &&
|
|
6909 (!insertion_level || insertion_stack->insertion != type))
|
|
6910 {
|
|
6911 line_error ("Must be in a `%s' insertion in order to use `%s'x",
|
|
6912 command, command);
|
|
6913 discard_until ("\n");
|
|
6914 return;
|
|
6915 }
|
|
6916
|
|
6917 defun_internal (type, x_p);
|
|
6918 }
|
|
6919
|
|
6920 /* End existing insertion block. */
|
|
6921 static void
|
|
6922 cm_end (void)
|
|
6923 {
|
|
6924 char *temp;
|
|
6925 enum insertion_type type;
|
|
6926
|
|
6927 get_rest_of_line (&temp);
|
|
6928 canon_white (temp);
|
|
6929
|
|
6930 if (strlen (temp) == 0)
|
|
6931 line_error ("`%c%s' needs something after it", COMMAND_PREFIX, command);
|
|
6932
|
|
6933 type = find_type_from_name (temp);
|
|
6934
|
|
6935 if (type == bad_type)
|
|
6936 {
|
|
6937 line_error ("Bad argument to `%s', `%s', using `%s'",
|
|
6938 command, temp, insertion_type_pname (current_insertion_type ()));
|
|
6939 }
|
|
6940
|
|
6941 /* see comment under cm_ifinfo() */
|
|
6942 if (type == ifinfo)
|
|
6943 {
|
|
6944 if (!ifinfo_count)
|
|
6945 line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
|
|
6946 else
|
|
6947 ifinfo_count--;
|
|
6948 }
|
|
6949 else
|
|
6950 {
|
|
6951 if (!insertion_level)
|
|
6952 line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command);
|
|
6953 else
|
|
6954 end_insertion (type);
|
|
6955 }
|
|
6956
|
|
6957 free (temp);
|
|
6958 }
|
|
6959
|
|
6960
|
|
6961 /* **************************************************************** */
|
|
6962 /* */
|
|
6963 /* Other Random Commands */
|
|
6964 /* */
|
|
6965 /* **************************************************************** */
|
|
6966
|
|
6967 /* This says to inhibit the indentation of the next paragraph, but
|
|
6968 not of following paragraphs. */
|
|
6969 static void
|
|
6970 cm_noindent (void)
|
|
6971 {
|
|
6972 if (!inhibit_paragraph_indentation)
|
|
6973 inhibit_paragraph_indentation = -1;
|
|
6974 }
|
|
6975
|
|
6976 /* I don't know exactly what to do with this. Should I allow
|
|
6977 someone to switch filenames in the middle of output? Since the
|
|
6978 file could be partially written, this doesn't seem to make sense.
|
|
6979 Another option: ignore it, since they don't *really* want to
|
|
6980 switch files. Finally, complain, or at least warn. */
|
|
6981 static void
|
|
6982 cm_setfilename (void)
|
|
6983 {
|
|
6984 char *filename;
|
|
6985 get_rest_of_line (&filename);
|
|
6986 /* warning ("`@%s %s' encountered and ignored", command, filename); */
|
|
6987 free (filename);
|
|
6988 }
|
|
6989
|
|
6990 static void
|
|
6991 cm_ignore_line (void)
|
|
6992 {
|
|
6993 discard_until ("\n");
|
|
6994 }
|
|
6995
|
|
6996 /* @br can be immediately followed by `{}', so we have to read those here.
|
|
6997 It should simply close the paragraph. */
|
|
6998 static void
|
|
6999 cm_br (void)
|
|
7000 {
|
|
7001 if (looking_at ("{}"))
|
|
7002 input_text_offset += 2;
|
|
7003
|
|
7004 if (curchar () == '\n')
|
|
7005 {
|
|
7006 input_text_offset++;
|
|
7007 line_number++;
|
|
7008 }
|
|
7009
|
|
7010 close_paragraph ();
|
|
7011 }
|
|
7012
|
|
7013 /* Insert the number of blank lines passed as argument. */
|
|
7014 static void
|
|
7015 cm_sp (void)
|
|
7016 {
|
|
7017 int lines;
|
|
7018 char *line;
|
|
7019
|
|
7020 get_rest_of_line (&line);
|
|
7021
|
|
7022 if (sscanf (line, "%d", &lines) != 1)
|
|
7023 {
|
|
7024 line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
|
|
7025 }
|
|
7026 else
|
|
7027 {
|
|
7028 if (lines < 0)
|
|
7029 lines = 0;
|
|
7030
|
|
7031 while (lines--)
|
|
7032 add_char ('\n');
|
|
7033 }
|
|
7034 free (line);
|
|
7035 }
|
|
7036
|
|
7037 /* Start a new line with just this text on it.
|
|
7038 Then center the line of text.
|
|
7039 This always ends the current paragraph. */
|
|
7040 static void
|
|
7041 cm_center (void)
|
|
7042 {
|
|
7043 register int i, start, length;
|
|
7044 int fudge_factor = 1;
|
|
7045 unsigned char *line;
|
|
7046
|
|
7047 close_paragraph ();
|
|
7048 filling_enabled = indented_fill = 0;
|
|
7049 cm_noindent ();
|
|
7050 start = output_paragraph_offset;
|
|
7051 inhibit_output_flushing ();
|
|
7052 get_rest_of_line ((char **)&line);
|
|
7053 execute_string ((char *)line);
|
|
7054 free (line);
|
|
7055 uninhibit_output_flushing ();
|
|
7056
|
|
7057 i = output_paragraph_offset - 1;
|
|
7058 while (i > (start - 1) && output_paragraph[i] == '\n')
|
|
7059 i--;
|
|
7060
|
|
7061 output_paragraph_offset = ++i;
|
|
7062 length = output_paragraph_offset - start;
|
|
7063
|
|
7064 if (length < (fill_column - fudge_factor))
|
|
7065 {
|
|
7066 line = (unsigned char *)xmalloc (1 + length);
|
|
7067 memcpy (line, (char *)(output_paragraph + start), length);
|
|
7068
|
|
7069 i = (fill_column - fudge_factor - length) / 2;
|
|
7070 output_paragraph_offset = start;
|
|
7071
|
|
7072 while (i--)
|
|
7073 insert (' ');
|
|
7074
|
|
7075 for (i = 0; i < length; i++)
|
|
7076 insert (line[i]);
|
|
7077
|
|
7078 free (line);
|
|
7079 }
|
|
7080
|
|
7081 insert ('\n');
|
|
7082 close_paragraph ();
|
|
7083 filling_enabled = 1;
|
|
7084 }
|
|
7085
|
|
7086 /* Show what an expression returns. */
|
|
7087 static void
|
|
7088 cm_result (int arg)
|
|
7089 {
|
|
7090 if (arg == END)
|
|
7091 add_word ("=>");
|
|
7092 }
|
|
7093
|
|
7094 /* What an expression expands to. */
|
|
7095 static void
|
|
7096 cm_expansion (int arg)
|
|
7097 {
|
|
7098 if (arg == END)
|
|
7099 add_word ("==>");
|
|
7100 }
|
|
7101
|
|
7102 /* Indicates two expressions are equivalent. */
|
|
7103 static void
|
|
7104 cm_equiv (int arg)
|
|
7105 {
|
|
7106 if (arg == END)
|
|
7107 add_word ("==");
|
|
7108 }
|
|
7109
|
|
7110 /* What an expression may print. */
|
|
7111 static void
|
|
7112 cm_print (int arg)
|
|
7113 {
|
|
7114 if (arg == END)
|
|
7115 add_word ("-|");
|
|
7116 }
|
|
7117
|
|
7118 /* An error signaled. */
|
|
7119 static void
|
|
7120 cm_error (int arg)
|
|
7121 {
|
|
7122 if (arg == END)
|
|
7123 add_word ("error-->");
|
|
7124 }
|
|
7125
|
|
7126 /* The location of point in an example of a buffer. */
|
|
7127 static void
|
|
7128 cm_point (int arg)
|
|
7129 {
|
|
7130 if (arg == END)
|
|
7131 add_word ("-!-");
|
|
7132 }
|
|
7133
|
|
7134 /* Start a new line with just this text on it.
|
|
7135 The text is outdented one level if possible. */
|
|
7136 static void
|
|
7137 cm_exdent (void)
|
|
7138 {
|
|
7139 char *line;
|
|
7140 int i = current_indent;
|
|
7141
|
|
7142 if (current_indent)
|
|
7143 current_indent -= default_indentation_increment;
|
|
7144
|
|
7145 get_rest_of_line (&line);
|
|
7146 close_single_paragraph ();
|
|
7147 execute_string ("%s", line);
|
|
7148 current_indent = i;
|
|
7149 free (line);
|
|
7150 close_single_paragraph ();
|
|
7151 }
|
|
7152
|
|
7153 static void
|
|
7154 cm_include (void)
|
|
7155 {
|
|
7156 cm_infoinclude ();
|
|
7157 }
|
|
7158
|
|
7159 #if !defined (HAVE_STRERROR)
|
|
7160 extern char *sys_errlist[];
|
|
7161 extern int sys_nerr;
|
|
7162
|
|
7163 char *
|
|
7164 strerror (num)
|
|
7165 int num;
|
|
7166 {
|
|
7167 if (num >= sys_nerr)
|
|
7168 return ("Unknown file system error");
|
|
7169 else
|
|
7170 return (sys_errlist[num]);
|
|
7171 }
|
|
7172 #endif /* !HAVE_STRERROR */
|
|
7173
|
|
7174 /* Remember this file, and move onto the next. */
|
|
7175 static void
|
|
7176 cm_infoinclude (void)
|
|
7177 {
|
|
7178 char *filename;
|
|
7179
|
|
7180 #if defined (HAVE_MACROS)
|
|
7181 if (macro_expansion_output_stream)
|
|
7182 me_append_before_this_command ();
|
|
7183 #endif /* HAVE_MACROS */
|
|
7184
|
|
7185 close_paragraph ();
|
|
7186 get_rest_of_line (&filename);
|
|
7187
|
|
7188 #if defined (HAVE_MACROS)
|
|
7189 if (macro_expansion_output_stream)
|
|
7190 remember_itext (input_text, input_text_offset);
|
|
7191 #endif /* HAVE_MACROS */
|
|
7192
|
|
7193 pushfile ();
|
|
7194
|
|
7195 /* In verbose mode we print info about including another file. */
|
|
7196 if (verbose_mode)
|
|
7197 {
|
|
7198 register int i = 0;
|
|
7199 register FSTACK *stack = filestack;
|
|
7200
|
|
7201 for (i = 0, stack = filestack; stack; stack = stack->next, i++);
|
|
7202
|
|
7203 i *= 2;
|
|
7204
|
|
7205 printf ("%*s", i, "");
|
|
7206 printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
|
|
7207 fflush (stdout);
|
|
7208 }
|
|
7209
|
|
7210 if (!find_and_load (filename))
|
|
7211 {
|
|
7212 popfile ();
|
|
7213 line_number--;
|
|
7214
|
|
7215 /* Cannot "@include foo", in line 5 of "/wh/bar". */
|
|
7216 line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
|
|
7217 strerror (errno));
|
|
7218
|
|
7219 free (filename);
|
|
7220 return;
|
|
7221 }
|
|
7222 else
|
|
7223 {
|
|
7224 #if defined (HAVE_MACROS)
|
|
7225 if (macro_expansion_output_stream)
|
|
7226 remember_itext (input_text, input_text_offset);
|
|
7227 #endif /* HAVE_MACROS */
|
|
7228 reader_loop ();
|
|
7229 }
|
|
7230 free (filename);
|
|
7231 popfile ();
|
|
7232 }
|
|
7233
|
|
7234 /* The other side of a malformed expression. */
|
|
7235 static void
|
|
7236 misplaced_brace (void)
|
|
7237 {
|
|
7238 line_error ("Misplaced `}'");
|
|
7239 }
|
|
7240
|
|
7241 /* Don't let the filling algorithm insert extra whitespace here. */
|
|
7242 static void
|
|
7243 cm_force_abbreviated_whitespace (void)
|
|
7244 {
|
|
7245 }
|
|
7246
|
|
7247 /* Do not let this character signify the end of a sentence, though
|
|
7248 if it was seen without the command prefix it normally would. We
|
|
7249 do this by turning on the 8th bit of the character. */
|
|
7250 static void
|
|
7251 cm_ignore_sentence_ender (void)
|
|
7252 {
|
|
7253 add_char (META ((*command)));
|
|
7254 }
|
|
7255
|
|
7256 /* Signals end of processing. Easy to make this happen. */
|
|
7257 static void
|
|
7258 cm_bye (void)
|
|
7259 {
|
|
7260 input_text_offset = size_of_input_text;
|
|
7261 }
|
|
7262
|
|
7263 static void
|
|
7264 cm_asis (void)
|
|
7265 {
|
|
7266 }
|
|
7267
|
|
7268 static void
|
|
7269 cm_math (void)
|
|
7270 {
|
|
7271 }
|
|
7272
|
|
7273
|
|
7274 /* **************************************************************** */
|
|
7275 /* */
|
|
7276 /* Indexing Stuff */
|
|
7277 /* */
|
|
7278 /* **************************************************************** */
|
|
7279
|
|
7280
|
|
7281 /* An index element... */
|
|
7282 typedef struct index_elt
|
|
7283 {
|
|
7284 struct index_elt *next;
|
|
7285 char *entry; /* The index entry itself. */
|
|
7286 char *node; /* The node from whence it came. */
|
|
7287 int code; /* Non-zero means add `@code{...}' when
|
|
7288 printing this element. */
|
|
7289 int defining_line; /* Line number where this entry was written. */
|
|
7290 } INDEX_ELT;
|
|
7291
|
|
7292 /* A list of short-names for each index, and the index to that index in our
|
|
7293 index array, the_indices. In addition, for each index, it is remembered
|
|
7294 whether that index is a code index or not. Code indices have @code{}
|
|
7295 inserted around the first word when they are printed with printindex. */
|
|
7296 typedef struct
|
|
7297 {
|
|
7298 char *name;
|
|
7299 int index;
|
|
7300 int code;
|
|
7301 } INDEX_ALIST;
|
|
7302
|
|
7303 INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
|
|
7304
|
|
7305 /* An array of pointers. Each one is for a different index. The
|
|
7306 "synindex" command changes which array slot is pointed to by a
|
|
7307 given "index". */
|
|
7308 INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
|
|
7309
|
|
7310 /* The number of defined indices. */
|
|
7311 int defined_indices = 0;
|
|
7312
|
|
7313 /* We predefine these. */
|
|
7314 #define program_index 0
|
|
7315 #define function_index 1
|
|
7316 #define concept_index 2
|
|
7317 #define variable_index 3
|
|
7318 #define datatype_index 4
|
|
7319 #define key_index 5
|
|
7320
|
|
7321 static void
|
|
7322 init_indices (void)
|
|
7323 {
|
|
7324 int i;
|
|
7325
|
|
7326 /* Create the default data structures. */
|
|
7327
|
|
7328 /* Initialize data space. */
|
|
7329 if (!the_indices)
|
|
7330 {
|
|
7331 the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
|
|
7332 sizeof (INDEX_ELT *));
|
|
7333 the_indices[defined_indices] = (INDEX_ELT *) NULL;
|
|
7334
|
|
7335 name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
|
|
7336 sizeof (INDEX_ALIST *));
|
|
7337 name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
|
|
7338 }
|
|
7339
|
|
7340 /* If there were existing indices, get rid of them now. */
|
|
7341 for (i = 0; i < defined_indices; i++)
|
|
7342 undefindex (name_index_alist[i]->name);
|
|
7343
|
|
7344 /* Add the default indices. */
|
|
7345 top_defindex ("pg", 0);
|
|
7346 top_defindex ("fn", 1); /* "fn" is a code index. */
|
|
7347 top_defindex ("cp", 0);
|
|
7348 top_defindex ("vr", 0);
|
|
7349 top_defindex ("tp", 0);
|
|
7350 top_defindex ("ky", 0);
|
|
7351
|
|
7352 }
|
|
7353
|
|
7354 /* Find which element in the known list of indices has this name.
|
|
7355 Returns -1 if NAME isn't found. */
|
|
7356 static int
|
|
7357 find_index_offset (char *name)
|
|
7358 {
|
|
7359 register int i;
|
|
7360 for (i = 0; i < defined_indices; i++)
|
|
7361 if (name_index_alist[i] &&
|
|
7362 strcmp (name, name_index_alist[i]->name) == 0)
|
|
7363 return (name_index_alist[i]->index);
|
|
7364 return (-1);
|
|
7365 }
|
|
7366
|
|
7367 /* Return a pointer to the entry of (name . index) for this name.
|
|
7368 Return NULL if the index doesn't exist. */
|
|
7369 static INDEX_ALIST *
|
|
7370 find_index (char *name)
|
|
7371 {
|
|
7372 int offset = find_index_offset (name);
|
|
7373 if (offset > -1)
|
|
7374 return (name_index_alist[offset]);
|
|
7375 else
|
|
7376 return ((INDEX_ALIST *) NULL);
|
|
7377 }
|
|
7378
|
|
7379 /* Given an index name, return the offset in the_indices of this index,
|
|
7380 or -1 if there is no such index. */
|
|
7381 static int
|
|
7382 translate_index (char *name)
|
|
7383 {
|
|
7384 INDEX_ALIST *which = find_index (name);
|
|
7385
|
|
7386 if (which)
|
|
7387 return (which->index);
|
|
7388 else
|
|
7389 return (-1);
|
|
7390 }
|
|
7391
|
|
7392 /* Return the index list which belongs to NAME. */
|
|
7393 static INDEX_ELT *
|
|
7394 index_list (char *name)
|
|
7395 {
|
|
7396 int which = translate_index (name);
|
|
7397 if (which < 0)
|
|
7398 return ((INDEX_ELT *) -1);
|
|
7399 else
|
|
7400 return (the_indices[which]);
|
|
7401 }
|
|
7402
|
|
7403 /* Please release me, let me go... */
|
|
7404 static void
|
|
7405 free_index (INDEX_ELT *indecks)
|
|
7406 {
|
|
7407 INDEX_ELT *temp;
|
|
7408
|
|
7409 while ((temp = indecks) != (INDEX_ELT *) NULL)
|
|
7410 {
|
|
7411 free (temp->entry);
|
|
7412 free (temp->node);
|
|
7413 indecks = indecks->next;
|
|
7414 free (temp);
|
|
7415 }
|
|
7416 }
|
|
7417
|
|
7418 /* Flush an index by name. */
|
|
7419 static void
|
|
7420 undefindex (char *name)
|
|
7421 {
|
|
7422 int i;
|
|
7423 int which = find_index_offset (name);
|
|
7424
|
|
7425 if (which < 0)
|
|
7426 return;
|
|
7427
|
|
7428 i = name_index_alist[which]->index;
|
|
7429
|
|
7430 free_index (the_indices[i]);
|
|
7431 the_indices[i] = (INDEX_ELT *) NULL;
|
|
7432
|
|
7433 free (name_index_alist[which]->name);
|
|
7434 free (name_index_alist[which]);
|
|
7435 name_index_alist[which] = (INDEX_ALIST *) NULL;
|
|
7436 }
|
|
7437
|
|
7438 /* Define an index known as NAME. We assign the slot number.
|
|
7439 CODE if non-zero says to make this a code index. */
|
|
7440 static void
|
|
7441 defindex (char *name, int code)
|
|
7442 {
|
|
7443 register int i, slot;
|
|
7444
|
|
7445 /* If it already exists, flush it. */
|
|
7446 undefindex (name);
|
|
7447
|
|
7448 /* Try to find an empty slot. */
|
|
7449 slot = -1;
|
|
7450 for (i = 0; i < defined_indices; i++)
|
|
7451 if (!name_index_alist[i])
|
|
7452 {
|
|
7453 slot = i;
|
|
7454 break;
|
|
7455 }
|
|
7456
|
|
7457 if (slot < 0)
|
|
7458 {
|
|
7459 /* No such luck. Make space for another index. */
|
|
7460 slot = defined_indices;
|
|
7461 defined_indices++;
|
|
7462
|
|
7463 name_index_alist = (INDEX_ALIST **)
|
|
7464 xrealloc ((char *)name_index_alist,
|
|
7465 (1 + defined_indices) * sizeof (INDEX_ALIST *));
|
|
7466 the_indices = (INDEX_ELT **)
|
|
7467 xrealloc ((char *)the_indices,
|
|
7468 (1 + defined_indices) * sizeof (INDEX_ELT *));
|
|
7469 }
|
|
7470
|
|
7471 /* We have a slot. Start assigning. */
|
|
7472 name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
|
|
7473 name_index_alist[slot]->name = strdup (name);
|
|
7474 name_index_alist[slot]->index = slot;
|
|
7475 name_index_alist[slot]->code = code;
|
|
7476
|
|
7477 the_indices[slot] = (INDEX_ELT *) NULL;
|
|
7478 }
|
|
7479
|
|
7480 /* Add the arguments to the current index command to the index NAME. */
|
|
7481 static void
|
|
7482 index_add_arg (char *name)
|
|
7483 {
|
|
7484 int which;
|
|
7485 char *index_entry;
|
|
7486 INDEX_ALIST *tem;
|
|
7487
|
|
7488 tem = find_index (name);
|
|
7489
|
|
7490 which = tem ? tem->index : -1;
|
|
7491
|
|
7492 #if defined (HAVE_MACROS)
|
|
7493 if (macro_expansion_output_stream)
|
|
7494 append_to_expansion_output (input_text_offset + 1);
|
|
7495 #endif /* HAVE_MACROS */
|
|
7496
|
|
7497 get_rest_of_line (&index_entry);
|
|
7498 ignore_blank_line ();
|
|
7499
|
|
7500 #if defined (HAVE_MACROS)
|
|
7501 if (macro_expansion_output_stream)
|
|
7502 {
|
|
7503 int op_orig;
|
|
7504
|
|
7505 remember_itext (input_text, input_text_offset);
|
|
7506 op_orig = output_paragraph_offset;
|
|
7507 me_execute_string (index_entry);
|
|
7508 me_execute_string ("\n");
|
|
7509 output_paragraph_offset = op_orig;
|
|
7510 }
|
|
7511 #endif /* HAVE_MACROS */
|
|
7512
|
|
7513 if (which < 0)
|
|
7514 {
|
|
7515 line_error ("Unknown index reference `%s'", name);
|
|
7516 free (index_entry);
|
|
7517 }
|
|
7518 else
|
|
7519 {
|
|
7520 INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
|
|
7521 new->next = the_indices[which];
|
|
7522 new->entry = index_entry;
|
|
7523 new->node = current_node;
|
|
7524 new->code = tem->code;
|
|
7525 new->defining_line = line_number - 1;
|
|
7526 the_indices[which] = new;
|
|
7527 }
|
|
7528 }
|
|
7529
|
|
7530 #define INDEX_COMMAND_SUFFIX "index"
|
|
7531
|
|
7532 /* The function which user defined index commands call. */
|
|
7533 static void
|
|
7534 gen_index (void)
|
|
7535 {
|
|
7536 char *name = strdup (command);
|
|
7537 if ((int) strlen (name) >= (int) strlen ("index"))
|
|
7538 name[strlen (name) - strlen ("index")] = '\0';
|
|
7539 index_add_arg (name);
|
|
7540 free (name);
|
|
7541 }
|
|
7542
|
|
7543 static void
|
|
7544 top_defindex (char *name, int code)
|
|
7545 {
|
|
7546 char *temp;
|
|
7547
|
|
7548 temp = (char *) xmalloc (1 + strlen (name) + strlen ("index"));
|
|
7549 sprintf (temp, "%sindex", name);
|
|
7550 define_user_command (temp, gen_index, 0);
|
|
7551 defindex (name, code);
|
|
7552 free (temp);
|
|
7553 }
|
|
7554
|
|
7555 /* Define a new index command. Arg is name of index. */
|
|
7556 static void
|
|
7557 cm_defindex (void)
|
|
7558 {
|
|
7559 gen_defindex (0);
|
|
7560 }
|
|
7561
|
|
7562 static void
|
|
7563 cm_defcodeindex (void)
|
|
7564 {
|
|
7565 gen_defindex (1);
|
|
7566 }
|
|
7567
|
|
7568 static void
|
|
7569 gen_defindex (int code)
|
|
7570 {
|
|
7571 char *name;
|
|
7572 get_rest_of_line (&name);
|
|
7573
|
|
7574 if (find_index (name))
|
|
7575 {
|
|
7576 line_error ("Index `%s' already exists", name);
|
|
7577 free (name);
|
|
7578 return;
|
|
7579 }
|
|
7580 else
|
|
7581 {
|
|
7582 char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
|
|
7583 sprintf (temp, "%sindex", name);
|
|
7584 define_user_command (temp, gen_index, 0);
|
|
7585 defindex (name, code);
|
|
7586 free (name);
|
|
7587 }
|
|
7588 }
|
|
7589
|
|
7590 /* Append LIST2 to LIST1. Return the head of the list. */
|
|
7591 static INDEX_ELT *
|
|
7592 index_append (INDEX_ELT *head, INDEX_ELT *tail)
|
|
7593 {
|
|
7594 register INDEX_ELT *t_head = head;
|
|
7595
|
|
7596 if (!t_head)
|
|
7597 return (tail);
|
|
7598
|
|
7599 while (t_head->next)
|
|
7600 t_head = t_head->next;
|
|
7601 t_head->next = tail;
|
|
7602 return (head);
|
|
7603 }
|
|
7604
|
|
7605 /* Expects 2 args, on the same line. Both are index abbreviations.
|
|
7606 Make the first one be a synonym for the second one, i.e. make the
|
|
7607 first one have the same index as the second one. */
|
|
7608 static void
|
|
7609 cm_synindex (void)
|
|
7610 {
|
|
7611 int redirector, redirectee;
|
|
7612 char *temp;
|
|
7613
|
|
7614 skip_whitespace ();
|
|
7615 get_until_in_line (" ", &temp);
|
|
7616 redirectee = find_index_offset (temp);
|
|
7617 skip_whitespace ();
|
|
7618 free_and_clear (&temp);
|
|
7619 get_until_in_line (" ", &temp);
|
|
7620 redirector = find_index_offset (temp);
|
|
7621 free (temp);
|
|
7622 if (redirector < 0 || redirectee < 0)
|
|
7623 {
|
|
7624 line_error ("Unknown index reference");
|
|
7625 }
|
|
7626 else
|
|
7627 {
|
|
7628 /* I think that we should let the user make indices synonymous to
|
|
7629 each other without any lossage of info. This means that one can
|
|
7630 say @synindex cp dt anywhere in the file, and things that used to
|
|
7631 be in cp will go into dt. */
|
|
7632 INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
|
|
7633
|
|
7634 if (i1 || i2)
|
|
7635 {
|
|
7636 if (i1)
|
|
7637 the_indices[redirectee] = index_append (i1, i2);
|
|
7638 else
|
|
7639 the_indices[redirectee] = index_append (i2, i1);
|
|
7640 }
|
|
7641
|
|
7642 name_index_alist[redirectee]->index =
|
|
7643 name_index_alist[redirector]->index;
|
|
7644 }
|
|
7645 }
|
|
7646
|
|
7647 static void
|
|
7648 cm_pindex (void) /* Pinhead index. */
|
|
7649 {
|
|
7650 index_add_arg ("pg");
|
|
7651 }
|
|
7652
|
|
7653 static void
|
|
7654 cm_vindex (void) /* Variable index. */
|
|
7655 {
|
|
7656 index_add_arg ("vr");
|
|
7657 }
|
|
7658
|
|
7659 static void
|
|
7660 cm_kindex (void) /* Key index. */
|
|
7661 {
|
|
7662 index_add_arg ("ky");
|
|
7663 }
|
|
7664
|
|
7665 static void
|
|
7666 cm_cindex (void) /* Concept index. */
|
|
7667 {
|
|
7668 index_add_arg ("cp");
|
|
7669 }
|
|
7670
|
|
7671 static void
|
|
7672 cm_findex (void) /* Function index. */
|
|
7673 {
|
|
7674 index_add_arg ("fn");
|
|
7675 }
|
|
7676
|
|
7677 static void
|
|
7678 cm_tindex (void) /* Data Type index. */
|
|
7679 {
|
|
7680 index_add_arg ("tp");
|
|
7681 }
|
|
7682
|
|
7683 /* Sorting the index. */
|
|
7684 static int
|
|
7685 index_element_compare (INDEX_ELT **element1, INDEX_ELT **element2)
|
|
7686 {
|
|
7687 /* This needs to ignore leading non-text characters. */
|
|
7688 return (strcasecmp ((*element1)->entry, (*element2)->entry));
|
|
7689 }
|
|
7690
|
|
7691 /* Force all index entries to be unique. */
|
|
7692 static void
|
|
7693 make_index_entries_unique (INDEX_ELT **array, int count)
|
|
7694 {
|
|
7695 register int i, j;
|
|
7696 INDEX_ELT **copy;
|
|
7697
|
|
7698 copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *));
|
|
7699
|
|
7700 for (i = 0, j = 0; i < count; i++)
|
|
7701 {
|
|
7702 if ((i == (count - 1)) ||
|
|
7703 (array[i]->node != array[i + 1]->node) ||
|
|
7704 (strcasecmp (array[i]->entry, array[i + 1]->entry) != 0))
|
|
7705 copy[j++] = array[i];
|
|
7706 else
|
|
7707 {
|
|
7708 free (array[i]->entry);
|
|
7709 free (array[i]);
|
|
7710 }
|
|
7711 }
|
|
7712 copy[j] = (INDEX_ELT *)NULL;
|
|
7713
|
|
7714 /* Now COPY contains only unique entries. Duplicated entries in the
|
|
7715 original array have been freed. Replace the current array with
|
|
7716 the copy, fixing the NEXT pointers. */
|
|
7717 for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++)
|
|
7718 {
|
|
7719 int counter = 1;
|
|
7720 copy[i]->next = copy[i + 1];
|
|
7721
|
|
7722 /* Fix entry names which are the same. They point to different nodes,
|
|
7723 so we make the entry name unique. */
|
|
7724 if ((copy[i + 1] != (INDEX_ELT *)NULL) &&
|
|
7725 (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0))
|
|
7726 {
|
|
7727 char *new_entry_name;
|
|
7728
|
|
7729 new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry));
|
|
7730 sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
|
|
7731 free (copy[i]->entry);
|
|
7732 copy[i]->entry = new_entry_name;
|
|
7733 counter++;
|
|
7734 }
|
|
7735 else
|
|
7736 counter = 1;
|
|
7737
|
|
7738 array[i] = copy[i];
|
|
7739 }
|
|
7740 array[i] = (INDEX_ELT *)NULL;
|
|
7741
|
|
7742 /* Free the storage used only by COPY. */
|
|
7743 free (copy);
|
|
7744 }
|
|
7745
|
|
7746 /* Sort the index passed in INDEX, returning an array of
|
|
7747 pointers to elements. The array is terminated with a NULL
|
|
7748 pointer. We call qsort because it's supposed to be fast.
|
|
7749 I think this looks bad. */
|
|
7750 static INDEX_ELT **
|
|
7751 sort_index (INDEX_ELT *indecks)
|
|
7752 {
|
|
7753 INDEX_ELT *temp = indecks;
|
|
7754 INDEX_ELT **array;
|
|
7755 int count = 0;
|
|
7756
|
|
7757 while (temp != (INDEX_ELT *) NULL)
|
|
7758 {
|
|
7759 count++;
|
|
7760 temp = temp->next;
|
|
7761 }
|
|
7762
|
|
7763 /* We have the length. Make an array. */
|
|
7764
|
|
7765 array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
|
|
7766 count = 0;
|
|
7767 temp = indecks;
|
|
7768
|
|
7769 while (temp != (INDEX_ELT *) NULL)
|
|
7770 {
|
|
7771 array[count++] = temp;
|
|
7772 temp = temp->next;
|
|
7773 }
|
|
7774 array[count] = (INDEX_ELT *) NULL; /* terminate the array. */
|
|
7775
|
|
7776 /* Sort the array. */
|
|
7777 qsort (array, count, sizeof (INDEX_ELT *), (int (*)()) index_element_compare);
|
|
7778 make_index_entries_unique (array, count);
|
|
7779 return (array);
|
|
7780 }
|
|
7781
|
|
7782 /* Non-zero means that we are in the middle of printing an index. */
|
|
7783 int printing_index = 0;
|
|
7784
|
|
7785 /* Takes one arg, a short name of an index to print.
|
|
7786 Outputs a menu of the sorted elements of the index. */
|
|
7787 static void
|
|
7788 cm_printindex (void)
|
|
7789 {
|
|
7790 int item;
|
|
7791 INDEX_ELT *indecks;
|
|
7792 INDEX_ELT **array;
|
|
7793 char *index_name;
|
|
7794 int old_inhibitions = inhibit_paragraph_indentation;
|
|
7795 int previous_filling_enabled_value = filling_enabled;
|
|
7796
|
|
7797 close_paragraph ();
|
|
7798 get_rest_of_line (&index_name);
|
|
7799
|
|
7800 indecks = index_list (index_name);
|
|
7801 if (indecks == (INDEX_ELT *)-1)
|
|
7802 {
|
|
7803 line_error ("Unknown index name `%s'", index_name);
|
|
7804 free (index_name);
|
|
7805 return;
|
|
7806 }
|
|
7807 else
|
|
7808 free (index_name);
|
|
7809
|
|
7810 array = sort_index (indecks);
|
|
7811
|
|
7812 filling_enabled = 0;
|
|
7813 inhibit_paragraph_indentation = 1;
|
|
7814 close_paragraph ();
|
|
7815 add_word ("* Menu:\n\n");
|
|
7816
|
|
7817 printing_index = 1;
|
|
7818
|
|
7819 #if defined (HAVE_MACROS)
|
|
7820 me_inhibit_expansion++;
|
|
7821 #endif /* HAVE_MACROS */
|
|
7822
|
|
7823 for (item = 0; (indecks = array[item]); item++)
|
|
7824 {
|
|
7825 int real_line_number = line_number;
|
|
7826
|
|
7827 /* Let errors generated while making the index entry point back
|
|
7828 at the line which contains the entry. */
|
|
7829 line_number = indecks->defining_line;
|
|
7830
|
|
7831 /* If this particular entry should be printed as a "code" index,
|
|
7832 then wrap the entry with "@code{...}". */
|
|
7833 if (indecks->code)
|
|
7834 execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, indecks->entry);
|
|
7835 else
|
|
7836 execute_string ("* %s: ", indecks->entry);
|
|
7837
|
|
7838 /* Pad the front of the destination nodename so that
|
|
7839 the output looks nice. */
|
|
7840 if (fill_column > 40 && output_column < 40)
|
|
7841 indent (40 - output_column);
|
|
7842
|
|
7843 execute_string ("%s.\n", indecks->node);
|
|
7844
|
|
7845 line_number = real_line_number;
|
|
7846 flush_output ();
|
|
7847 }
|
|
7848
|
|
7849 #if defined (HAVE_MACROS)
|
|
7850 me_inhibit_expansion--;
|
|
7851 #endif /* HAVE_MACROS */
|
|
7852
|
|
7853 printing_index = 0;
|
|
7854 free (array);
|
|
7855 close_single_paragraph ();
|
|
7856 filling_enabled = previous_filling_enabled_value;
|
|
7857 inhibit_paragraph_indentation = old_inhibitions;
|
|
7858 }
|
|
7859
|
|
7860
|
|
7861 /* **************************************************************** */
|
|
7862 /* */
|
|
7863 /* Making User Defined Commands */
|
|
7864 /* */
|
|
7865 /* **************************************************************** */
|
|
7866
|
|
7867 static void
|
|
7868 define_user_command (char *name, COMMAND_FUNCTION *proc, int needs_braces_p)
|
|
7869 {
|
|
7870 int slot = user_command_array_len;
|
|
7871 user_command_array_len++;
|
|
7872
|
|
7873 if (!user_command_array)
|
|
7874 user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
|
|
7875
|
|
7876 user_command_array = (COMMAND **) xrealloc (user_command_array,
|
|
7877 (1 + user_command_array_len) *
|
|
7878 sizeof (COMMAND *));
|
|
7879
|
|
7880 user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
|
|
7881 user_command_array[slot]->name = strdup (name);
|
|
7882 user_command_array[slot]->proc = proc;
|
|
7883 user_command_array[slot]->argument_in_braces = needs_braces_p;
|
|
7884 }
|
|
7885
|
|
7886 #if 0 /* unused */
|
|
7887 /* Make ALIAS run the named FUNCTION. Copies properties from FUNCTION. */
|
|
7888 static void
|
|
7889 define_alias (char *alias, char *function)
|
|
7890 {
|
|
7891 }
|
|
7892 #endif
|
|
7893
|
|
7894 /* Set the paragraph indentation variable to the value specified in STRING.
|
|
7895 Values can be:
|
|
7896 `asis': Don't change existing indentation.
|
|
7897 `none': Remove existing indentation.
|
|
7898 NUM: Indent NUM spaces at the starts of paragraphs.
|
|
7899 Note that if NUM is zero, we assume `none'.
|
|
7900
|
|
7901 Returns 0 if successful, or non-zero if STRING isn't one of the above. */
|
|
7902 static int
|
|
7903 set_paragraph_indent (char *string)
|
|
7904 {
|
|
7905 if (strcmp (string, "asis") == 0)
|
|
7906 paragraph_start_indent = 0;
|
|
7907 else if (strcmp (string, "none") == 0)
|
|
7908 paragraph_start_indent = -1;
|
|
7909 else
|
|
7910 {
|
|
7911 if (sscanf (string, "%d", ¶graph_start_indent) != 1)
|
|
7912 return (-1);
|
|
7913 else
|
|
7914 {
|
|
7915 if (paragraph_start_indent == 0)
|
|
7916 paragraph_start_indent = -1;
|
|
7917 }
|
|
7918 }
|
|
7919 return (0);
|
|
7920 }
|
|
7921
|
|
7922 static void
|
|
7923 cm_paragraphindent (void)
|
|
7924 {
|
|
7925 char *arg;
|
|
7926
|
|
7927 get_rest_of_line (&arg);
|
|
7928 if (set_paragraph_indent (arg) != 0)
|
|
7929 line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
|
|
7930
|
|
7931 free (arg);
|
|
7932 }
|
|
7933
|
|
7934 /* Some support for footnotes. */
|
|
7935
|
|
7936 /* Footnotes are a new construct in Info. We don't know the best method
|
|
7937 of implementing them for sure, so we present two possiblities.
|
|
7938
|
|
7939 SeparateNode:
|
|
7940 Make them look like followed references, with the reference
|
|
7941 destinations in a makeinfo manufactured node or,
|
|
7942
|
|
7943 EndNode:
|
|
7944 Make them appear at the bottom of the node that they originally
|
|
7945 appeared in. */
|
|
7946 #define SeparateNode 0
|
|
7947 #define EndNode 1
|
|
7948
|
|
7949 int footnote_style = EndNode;
|
|
7950 int first_footnote_this_node = 1;
|
|
7951 int footnote_count = 0;
|
|
7952
|
|
7953 /* Set the footnote style based on he style identifier in STRING. */
|
|
7954 static int
|
|
7955 set_footnote_style (char *string)
|
|
7956 {
|
|
7957 if ((strcasecmp (string, "separate") == 0) ||
|
|
7958 (strcasecmp (string, "MN") == 0))
|
|
7959 footnote_style = SeparateNode;
|
|
7960 else if ((strcasecmp (string, "end") == 0) ||
|
|
7961 (strcasecmp (string, "EN") == 0))
|
|
7962 footnote_style = EndNode;
|
|
7963 else
|
|
7964 return (-1);
|
|
7965
|
|
7966 return (0);
|
|
7967 }
|
|
7968
|
|
7969 static void
|
|
7970 cm_footnotestyle (void)
|
|
7971 {
|
|
7972 char *arg;
|
|
7973
|
|
7974 get_rest_of_line (&arg);
|
|
7975
|
|
7976 if (set_footnote_style (arg) != 0)
|
|
7977 line_error ("Bad argument to %c%s", COMMAND_PREFIX, command);
|
|
7978
|
|
7979 free (arg);
|
|
7980 }
|
|
7981
|
|
7982 typedef struct fn
|
|
7983 {
|
|
7984 struct fn *next;
|
|
7985 char *marker;
|
|
7986 char *note;
|
|
7987 } FN;
|
|
7988
|
|
7989 FN *pending_notes = (FN *) NULL;
|
|
7990
|
|
7991 /* A method for remembering footnotes. Note that this list gets output
|
|
7992 at the end of the current node. */
|
|
7993 static void
|
|
7994 remember_note (char *marker, char *note)
|
|
7995 {
|
|
7996 FN *temp = (FN *) xmalloc (sizeof (FN));
|
|
7997
|
|
7998 temp->marker = strdup (marker);
|
|
7999 temp->note = strdup (note);
|
|
8000 temp->next = pending_notes;
|
|
8001 pending_notes = temp;
|
|
8002 footnote_count++;
|
|
8003 }
|
|
8004
|
|
8005 /* How to get rid of existing footnotes. */
|
|
8006 static void
|
|
8007 free_pending_notes (void)
|
|
8008 {
|
|
8009 FN *temp;
|
|
8010
|
|
8011 while ((temp = pending_notes) != (FN *) NULL)
|
|
8012 {
|
|
8013 free (temp->marker);
|
|
8014 free (temp->note);
|
|
8015 pending_notes = pending_notes->next;
|
|
8016 free (temp);
|
|
8017 }
|
|
8018 first_footnote_this_node = 1;
|
|
8019 footnote_count = 0;
|
|
8020 }
|
|
8021
|
|
8022 /* What to do when you see a @footnote construct. */
|
|
8023
|
|
8024 /* Handle a "footnote".
|
|
8025 footnote *{this is a footnote}
|
|
8026 where "*" is the marker character for this note. */
|
|
8027 static void
|
|
8028 cm_footnote (void)
|
|
8029 {
|
|
8030 char *marker;
|
|
8031 char *note;
|
|
8032
|
|
8033 get_until ("{", &marker);
|
|
8034 canon_white (marker);
|
|
8035
|
|
8036 /* Read the argument in braces. */
|
|
8037 if (curchar () != '{')
|
|
8038 {
|
|
8039 line_error ("`%c%s' expected more than just `%s'. It needs something in `{...}'",
|
|
8040 COMMAND_PREFIX, command, marker);
|
|
8041 free (marker);
|
|
8042 return;
|
|
8043 }
|
|
8044 else
|
|
8045 {
|
|
8046 int braces = 1;
|
|
8047 int temp = ++input_text_offset;
|
|
8048 int len;
|
|
8049
|
|
8050 while (braces)
|
|
8051 {
|
|
8052 if (temp == size_of_input_text)
|
|
8053 {
|
|
8054 line_error ("No closing brace for footnote `%s'", marker);
|
|
8055 return;
|
|
8056 }
|
|
8057
|
|
8058 if (input_text[temp] == '{')
|
|
8059 braces++;
|
|
8060 else if (input_text[temp] == '}')
|
|
8061 braces--;
|
|
8062 else if (input_text[temp] == '\n')
|
|
8063 line_number ++;
|
|
8064
|
|
8065 temp++;
|
|
8066 }
|
|
8067
|
|
8068 len = (temp - input_text_offset) - 1;
|
|
8069 note = (char *)xmalloc (len + 1);
|
|
8070 strncpy (note, &input_text[input_text_offset], len);
|
|
8071 note[len] = '\0';
|
|
8072 input_text_offset = temp;
|
|
8073 }
|
|
8074
|
|
8075 if (!current_node || !*current_node)
|
|
8076 {
|
|
8077 line_error ("Footnote defined without parent node");
|
|
8078 free (marker);
|
|
8079 free (note);
|
|
8080 return;
|
|
8081 }
|
|
8082
|
|
8083 if (!*marker)
|
|
8084 {
|
|
8085 free (marker);
|
|
8086
|
|
8087 if (number_footnotes)
|
|
8088 {
|
|
8089 marker = (char *)xmalloc (10);
|
|
8090 sprintf (marker, "%d", current_footnote_number);
|
|
8091 current_footnote_number++;
|
|
8092 }
|
|
8093 else
|
|
8094 marker = strdup ("*");
|
|
8095 }
|
|
8096
|
|
8097 remember_note (marker, note);
|
|
8098
|
|
8099 /* Your method should at least insert MARKER. */
|
|
8100 switch (footnote_style)
|
|
8101 {
|
|
8102 case SeparateNode:
|
|
8103 add_word_args ("(%s)", marker);
|
|
8104 if (first_footnote_this_node)
|
|
8105 {
|
|
8106 char *temp_string;
|
|
8107
|
|
8108 temp_string = (char *)
|
|
8109 xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
|
|
8110
|
|
8111 add_word_args (" (*note %s-Footnotes::)", current_node);
|
|
8112 strcpy (temp_string, current_node);
|
|
8113 strcat (temp_string, "-Footnotes");
|
|
8114 remember_node_reference (temp_string, line_number, followed_reference);
|
|
8115 free (temp_string);
|
|
8116 first_footnote_this_node = 0;
|
|
8117 }
|
|
8118 break;
|
|
8119
|
|
8120 case EndNode:
|
|
8121 add_word_args ("(%s)", marker);
|
|
8122 break;
|
|
8123
|
|
8124 default:
|
|
8125 break;
|
|
8126 }
|
|
8127 free (marker);
|
|
8128 free (note);
|
|
8129 }
|
|
8130
|
|
8131 /* Non-zero means that we are currently in the process of outputting
|
|
8132 footnotes. */
|
|
8133 int already_outputting_pending_notes = 0;
|
|
8134
|
|
8135 /* Output the footnotes. We are at the end of the current node. */
|
|
8136 static void
|
|
8137 output_pending_notes (void)
|
|
8138 {
|
|
8139 FN *footnote = pending_notes;
|
|
8140
|
|
8141 if (!pending_notes)
|
|
8142 return;
|
|
8143
|
|
8144 switch (footnote_style)
|
|
8145 {
|
|
8146
|
|
8147 case SeparateNode:
|
|
8148 {
|
|
8149 char *old_current_node = current_node;
|
|
8150 char *old_command = strdup (command);
|
|
8151
|
|
8152 already_outputting_pending_notes++;
|
|
8153 execute_string ("%cnode %s-Footnotes,,,%s\n",
|
|
8154 COMMAND_PREFIX, current_node, current_node);
|
|
8155 already_outputting_pending_notes--;
|
|
8156 current_node = old_current_node;
|
|
8157 free (command);
|
|
8158 command = old_command;
|
|
8159 }
|
|
8160 break;
|
|
8161
|
|
8162 case EndNode:
|
|
8163 close_paragraph ();
|
|
8164 in_fixed_width_font++;
|
|
8165 execute_string ("---------- Footnotes ----------\n\n");
|
|
8166 in_fixed_width_font--;
|
|
8167 break;
|
|
8168 }
|
|
8169
|
|
8170 /* Handle the footnotes in reverse order. */
|
|
8171 {
|
|
8172 FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
|
|
8173
|
|
8174 array[footnote_count] = (FN *) NULL;
|
|
8175
|
|
8176 while (--footnote_count > -1)
|
|
8177 {
|
|
8178 array[footnote_count] = footnote;
|
|
8179 footnote = footnote->next;
|
|
8180 }
|
|
8181
|
|
8182 filling_enabled = 1;
|
|
8183 indented_fill = 1;
|
|
8184
|
|
8185 while ((footnote = array[++footnote_count]))
|
|
8186 {
|
|
8187
|
|
8188 switch (footnote_style)
|
|
8189 {
|
|
8190 case SeparateNode:
|
|
8191 case EndNode:
|
|
8192 execute_string ("(%s) %s", footnote->marker, footnote->note);
|
|
8193 close_paragraph ();
|
|
8194 break;
|
|
8195 }
|
|
8196 }
|
|
8197 close_paragraph ();
|
|
8198 free (array);
|
|
8199 }
|
|
8200 }
|
|
8201
|
|
8202
|
|
8203 /* **************************************************************** */
|
|
8204 /* */
|
|
8205 /* User definable Macros (text substitution) */
|
|
8206 /* */
|
|
8207 /* **************************************************************** */
|
|
8208
|
|
8209 #if defined (HAVE_MACROS)
|
|
8210
|
|
8211 /* Array of macros and definitions. */
|
|
8212 MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
|
|
8213
|
|
8214 int macro_list_len = 0; /* Number of elements. */
|
|
8215 int macro_list_size = 0; /* Number of slots in total. */
|
|
8216
|
|
8217 /* Return the macro definition of NAME or NULL if NAME is not defined. */
|
|
8218 static MACRO_DEF *
|
|
8219 find_macro (char *name)
|
|
8220 {
|
|
8221 register int i;
|
|
8222 register MACRO_DEF *def;
|
|
8223
|
|
8224 def = (MACRO_DEF *)NULL;
|
|
8225 for (i = 0; macro_list && (def = macro_list[i]); i++)
|
|
8226 {
|
|
8227 if ((!def->inhibited) && (strcmp (def->name, name) == 0))
|
|
8228 break;
|
|
8229 }
|
|
8230 return (def);
|
|
8231 }
|
|
8232
|
|
8233 /* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
|
|
8234 SOURCE_FILE is the name of the file where this definition can be found,
|
|
8235 and SOURCE_LINENO is the line number within that file. If a macro already
|
|
8236 exists with NAME, then a warning is produced, and that previous
|
|
8237 definition is overwritten. */
|
|
8238 void
|
|
8239 add_macro (char *name, char **arglist, char *body, char *source_file,
|
|
8240 int source_lineno, int flags)
|
|
8241 {
|
|
8242 register MACRO_DEF *def;
|
|
8243
|
|
8244 def = find_macro (name);
|
|
8245
|
|
8246 if (!def)
|
|
8247 {
|
|
8248 if (macro_list_len + 2 >= macro_list_size)
|
|
8249 macro_list = (MACRO_DEF **)xrealloc
|
|
8250 (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
|
|
8251
|
|
8252 macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
|
|
8253 macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
|
|
8254
|
|
8255 def = macro_list[macro_list_len];
|
|
8256 macro_list_len += 1;
|
|
8257 def->name = name;
|
|
8258 }
|
|
8259 else
|
|
8260 {
|
|
8261 char *temp_filename = input_filename;
|
|
8262 int temp_line = line_number;
|
|
8263
|
|
8264 warning ("The macro `%s' is previously defined", name);
|
|
8265
|
|
8266 input_filename = def->source_file;
|
|
8267 line_number = def->source_lineno;
|
|
8268
|
|
8269 warning ("Here is the previous definition of `%s'", name);
|
|
8270
|
|
8271 input_filename = temp_filename;
|
|
8272 line_number = temp_line;
|
|
8273
|
|
8274 if (def->arglist)
|
|
8275 {
|
|
8276 register int i;
|
|
8277
|
|
8278 for (i = 0; def->arglist[i]; i++)
|
|
8279 free (def->arglist[i]);
|
|
8280
|
|
8281 free (def->arglist);
|
|
8282 }
|
|
8283 free (def->source_file);
|
|
8284 free (def->body);
|
|
8285 }
|
|
8286
|
|
8287 def->source_file = strdup (source_file);
|
|
8288 def->source_lineno = source_lineno;
|
|
8289 def->body = body;
|
|
8290 def->arglist = arglist;
|
|
8291 def->inhibited = 0;
|
|
8292 def->flags = flags;
|
|
8293 }
|
|
8294
|
|
8295 /* Delete the macro with name NAME. The macro is deleted from the list,
|
|
8296 but it is also returned. If there was no macro defined, NULL is
|
|
8297 returned. */
|
|
8298 static MACRO_DEF *
|
|
8299 delete_macro (char *name)
|
|
8300 {
|
|
8301 register int i;
|
|
8302 register MACRO_DEF *def;
|
|
8303
|
|
8304 def = (MACRO_DEF *)NULL;
|
|
8305
|
|
8306 for (i = 0; macro_list && (def = macro_list[i]); i++)
|
|
8307 if (strcmp (def->name, name) == 0)
|
|
8308 {
|
|
8309 memcpy (macro_list + i, macro_list + i + 1,
|
|
8310 ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
|
|
8311 break;
|
|
8312 }
|
|
8313 return (def);
|
|
8314 }
|
|
8315
|
|
8316 /* Return the arglist on the current line. This can behave in two different
|
|
8317 ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
|
|
8318 int braces_required_for_macro_args = 0;
|
|
8319
|
|
8320 static char **
|
|
8321 get_macro_args (MACRO_DEF *def)
|
|
8322 {
|
|
8323 register int i;
|
|
8324 char *word;
|
|
8325
|
|
8326 /* Quickly check to see if this macro has been invoked with any arguments.
|
|
8327 If not, then don't skip any of the following whitespace. */
|
|
8328 for (i = input_text_offset; i < size_of_input_text; i++)
|
|
8329 if (!cr_or_whitespace (input_text[i]))
|
|
8330 break;
|
|
8331
|
|
8332 if (input_text[i] != '{')
|
|
8333 {
|
|
8334 if (braces_required_for_macro_args)
|
|
8335 {
|
|
8336 return ((char **)NULL);
|
|
8337 }
|
|
8338 else
|
|
8339 {
|
|
8340 /* Braces are not required to fill out the macro arguments. If
|
|
8341 this macro takes one argument, it is considered to be the
|
|
8342 remainder of the line, sans whitespace. */
|
|
8343 if (def->arglist && def->arglist[0] && !def->arglist[1])
|
|
8344 {
|
|
8345 char **arglist;
|
|
8346
|
|
8347 get_rest_of_line (&word);
|
|
8348 input_text_offset--;
|
|
8349 canon_white (word);
|
|
8350 arglist = (char **)xmalloc (2 * sizeof (char *));
|
|
8351 arglist[0] = word;
|
|
8352 arglist[1] = (char *)NULL;
|
|
8353 return (arglist);
|
|
8354 }
|
|
8355 else
|
|
8356 {
|
|
8357 /* The macro either took no arguments, or took more than
|
|
8358 one argument. In that case, it must be invoked with
|
|
8359 arguments surrounded by braces. */
|
|
8360 return ((char **)NULL);
|
|
8361 }
|
|
8362 }
|
|
8363 }
|
|
8364 return (get_brace_args (def->flags & ME_QUOTE_ARG));
|
|
8365 }
|
|
8366
|
|
8367 /* Substitute actual parameters for named parameters in body.
|
|
8368 The named parameters which appear in BODY must by surrounded
|
|
8369 reverse slashes, as in \foo\. */
|
|
8370 static char *
|
|
8371 apply (char **named, char **actuals, char *body)
|
|
8372 {
|
|
8373 register int i;
|
|
8374 int new_body_index, new_body_size;
|
|
8375 char *new_body, *text;
|
|
8376 int length_of_actuals;
|
|
8377
|
|
8378 length_of_actuals = array_len (actuals);
|
|
8379 new_body_size = strlen (body);
|
|
8380 new_body = (char *)xmalloc (1 + new_body_size);
|
|
8381
|
|
8382 /* Copy chars from BODY into NEW_BODY. */
|
|
8383 i = 0; new_body_index = 0;
|
|
8384
|
|
8385 while (1)
|
|
8386 {
|
|
8387 if (!body[i])
|
|
8388 break;
|
|
8389
|
|
8390 if (body[i] != '\\')
|
|
8391 new_body[new_body_index++] = body[i++];
|
|
8392 else
|
|
8393 {
|
|
8394 /* Snarf parameter name, check against named parameters. */
|
|
8395 char *param;
|
|
8396 int param_start, which, len;
|
|
8397
|
|
8398 param_start = ++i;
|
|
8399 while ((body[i]) && (body[i] != '\\'))
|
|
8400 i++;
|
|
8401
|
|
8402 len = i - param_start;
|
|
8403 param = (char *)xmalloc (1 + len);
|
|
8404 memcpy (param, body + param_start, len);
|
|
8405 param[len] = '\0';
|
|
8406
|
|
8407 if (body[i])
|
|
8408 i++;
|
|
8409
|
|
8410 /* Now check against named parameters. */
|
|
8411 for (which = 0; named && named[which]; which++)
|
|
8412 if (strcmp (named[which], param) == 0)
|
|
8413 break;
|
|
8414
|
|
8415 if (named[which])
|
|
8416 {
|
|
8417 if (which < length_of_actuals)
|
|
8418 text = actuals[which];
|
|
8419 else
|
|
8420 text = (char *)NULL;
|
|
8421
|
|
8422 if (!text)
|
|
8423 text = "";
|
|
8424
|
|
8425 len = strlen (text);
|
|
8426 }
|
|
8427 else
|
|
8428 {
|
|
8429 len += 2;
|
|
8430 text = (char *)xmalloc (1 + len);
|
|
8431 sprintf (text, "\\%s\\", param);
|
|
8432 }
|
|
8433
|
|
8434 if ((int) (2 + strlen (param)) < len)
|
|
8435 new_body = (char *)xrealloc
|
|
8436 (new_body, new_body_size += (1 + len));
|
|
8437
|
|
8438 free (param);
|
|
8439
|
|
8440 strcpy (new_body + new_body_index, text);
|
|
8441 new_body_index += len;
|
|
8442
|
|
8443 if (!named[which])
|
|
8444 free (text);
|
|
8445 }
|
|
8446 }
|
|
8447 new_body[new_body_index] = '\0';
|
|
8448 return (new_body);
|
|
8449 }
|
|
8450
|
|
8451 /* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
|
|
8452 static void
|
|
8453 execute_macro (MACRO_DEF *def)
|
|
8454 {
|
|
8455 char **arglist;
|
|
8456 int num_args;
|
|
8457 char *execution_string = (char *)NULL;
|
|
8458
|
|
8459 if (macro_expansion_output_stream && !me_inhibit_expansion)
|
|
8460 me_append_before_this_command ();
|
|
8461
|
|
8462 /* Find out how many arguments this macro definition takes. */
|
|
8463 num_args = array_len (def->arglist);
|
|
8464
|
|
8465 /* Gather the arguments present on the line if there are any. */
|
|
8466 arglist = get_macro_args (def);
|
|
8467
|
|
8468 if (num_args < array_len (arglist))
|
|
8469 {
|
|
8470 free_array (arglist);
|
|
8471 line_error ("Macro `%s' called with too many args", def->name);
|
|
8472 return;
|
|
8473 }
|
|
8474
|
|
8475 if (def->body)
|
|
8476 execution_string = apply (def->arglist, arglist, def->body);
|
|
8477
|
|
8478 free_array (arglist);
|
|
8479
|
|
8480 if (def->body)
|
|
8481 {
|
|
8482 if (macro_expansion_output_stream && !me_inhibit_expansion)
|
|
8483 {
|
|
8484 remember_itext (input_text, input_text_offset);
|
|
8485 me_execute_string (execution_string);
|
|
8486 }
|
|
8487 else
|
|
8488 execute_string (execution_string);
|
|
8489
|
|
8490 free (execution_string);
|
|
8491 }
|
|
8492 }
|
|
8493
|
|
8494 /* Read and remember the definition of a macro. */
|
|
8495 static void
|
|
8496 cm_macro (void)
|
|
8497 {
|
|
8498 register int i;
|
|
8499 char *name, **arglist, *body, *line;
|
|
8500 int body_size, body_index;
|
|
8501 int depth = 1;
|
|
8502 int defining_line = line_number;
|
|
8503 int flags = 0;
|
|
8504
|
|
8505 arglist = (char **)NULL;
|
|
8506 body = (char *)NULL;
|
|
8507 body_size = 0;
|
|
8508 body_index = 0;
|
|
8509
|
|
8510 if (macro_expansion_output_stream)
|
|
8511 me_append_before_this_command ();
|
|
8512
|
|
8513 skip_whitespace ();
|
|
8514
|
|
8515 /* Get the name of the macro. This is the set of characters which are
|
|
8516 not whitespace and are not `{' immediately following the @macro. */
|
|
8517 {
|
|
8518 int start = input_text_offset;
|
|
8519 int len;
|
|
8520
|
|
8521 for (i = start;
|
|
8522 (i < size_of_input_text) &&
|
|
8523 (input_text[i] != '{') &&
|
|
8524 (!cr_or_whitespace (input_text[i]));
|
|
8525 i++);
|
|
8526
|
|
8527 len = i - start;
|
|
8528 name = (char *)xmalloc (1 + len);
|
|
8529 strncpy (name, input_text + start, len);
|
|
8530 name[len] = '\0';
|
|
8531 input_text_offset = i;
|
|
8532 }
|
|
8533
|
|
8534 skip_whitespace ();
|
|
8535
|
|
8536 /* It is not required that the definition of a macro includes an arglist.
|
|
8537 If not, don't try to get the named parameters, just use a null list. */
|
|
8538 if (curchar () == '{')
|
|
8539 {
|
|
8540 int arglist_index = 0, arglist_size = 0;
|
|
8541 int gathering_words = 1;
|
|
8542 char *word = (char *)NULL;
|
|
8543 int character;
|
|
8544
|
|
8545 /* Read the words inside of the braces which determine the arglist.
|
|
8546 These words will be replaced within the body of the macro at
|
|
8547 execution time. */
|
|
8548
|
|
8549 input_text_offset++;
|
|
8550 skip_whitespace_and_newlines ();
|
|
8551
|
|
8552 while (gathering_words)
|
|
8553 {
|
|
8554 int len;
|
|
8555
|
|
8556 for (i = input_text_offset;
|
|
8557 (character = input_text[i]);
|
|
8558 i++)
|
|
8559 {
|
|
8560 switch (character)
|
|
8561 {
|
|
8562 case '\n':
|
|
8563 line_number++;
|
|
8564 case ' ':
|
|
8565 case '\t':
|
|
8566 case ',':
|
|
8567 case '}':
|
|
8568 /* Found the end of the current arglist word. Save it. */
|
|
8569 len = i - input_text_offset;
|
|
8570 word = (char *)xmalloc (1 + len);
|
|
8571 strncpy (word, input_text + input_text_offset, len);
|
|
8572 word[len] = '\0';
|
|
8573 input_text_offset = i;
|
|
8574
|
|
8575 /* Advance to the comma or close-brace that signified
|
|
8576 the end of the argument. */
|
|
8577 while ((character = curchar ())
|
|
8578 && character != ','
|
|
8579 && character != '}')
|
|
8580 input_text_offset++;
|
|
8581
|
|
8582 /* Add the word to our list of words. */
|
|
8583 if ((arglist_index + 2) >= arglist_size)
|
|
8584 arglist = (char **)xrealloc
|
|
8585 (arglist, (arglist_size += 10) * sizeof (char *));
|
|
8586
|
|
8587 arglist[arglist_index++] = word;
|
|
8588 arglist[arglist_index] = (char *)NULL;
|
|
8589 break;
|
|
8590 }
|
|
8591
|
|
8592 if (character == '}')
|
|
8593 {
|
|
8594 input_text_offset++;
|
|
8595 gathering_words = 0;
|
|
8596 break;
|
|
8597 }
|
|
8598
|
|
8599 if (character == ',')
|
|
8600 {
|
|
8601 input_text_offset++;
|
|
8602 skip_whitespace_and_newlines ();
|
|
8603 i = input_text_offset - 1;
|
|
8604 }
|
|
8605 }
|
|
8606 }
|
|
8607 }
|
|
8608
|
|
8609 /* Read the text carefully until we find an "@end macro" which
|
|
8610 matches this one. The text in between is the body of the macro. */
|
|
8611 skip_whitespace_and_newlines ();
|
|
8612
|
|
8613 while (depth)
|
|
8614 {
|
|
8615 if ((input_text_offset + 9) > size_of_input_text)
|
|
8616 {
|
|
8617 int temp_line = line_number;
|
|
8618 line_number = defining_line;
|
|
8619 line_error ("%cend macro not found", COMMAND_PREFIX);
|
|
8620 line_number = temp_line;
|
|
8621 return;
|
|
8622 }
|
|
8623
|
|
8624 get_rest_of_line (&line);
|
|
8625
|
|
8626 /* Handle commands only meaningful within a macro. */
|
|
8627 if ((*line == COMMAND_PREFIX) && (depth == 1) &&
|
|
8628 (strncmp (line + 1, "allow-recursion", 15) == 0) &&
|
|
8629 (line[16] == '\0' || whitespace (line[16])))
|
|
8630 {
|
|
8631 for (i = 16; whitespace (line[i]); i++);
|
|
8632 strcpy (line, line + i);
|
|
8633 flags |= ME_RECURSE;
|
|
8634 if (!*line)
|
|
8635 {
|
|
8636 free (line);
|
|
8637 continue;
|
|
8638 }
|
|
8639 }
|
|
8640
|
|
8641 if ((*line == COMMAND_PREFIX) && (depth == 1) &&
|
|
8642 (strncmp (line + 1, "quote-arg", 9) == 0) &&
|
|
8643 (line[10] == '\0' || whitespace (line[10])))
|
|
8644 {
|
|
8645 for (i = 16; whitespace (line[i]); i++);
|
|
8646 strcpy (line, line + i);
|
|
8647
|
|
8648 if (arglist && arglist[0] && !arglist[1])
|
|
8649 {
|
|
8650 flags |= ME_QUOTE_ARG;
|
|
8651 if (!*line)
|
|
8652 {
|
|
8653 free (line);
|
|
8654 continue;
|
|
8655 }
|
|
8656 }
|
|
8657 else
|
|
8658 {
|
|
8659 line_error ("%cquote-arg only useful when the macro takes a single argument",
|
|
8660 COMMAND_PREFIX);
|
|
8661 }
|
|
8662 }
|
|
8663
|
|
8664 if ((*line == COMMAND_PREFIX) &&
|
|
8665 (strncmp (line + 1, "macro ", 6) == 0))
|
|
8666 depth++;
|
|
8667
|
|
8668 if ((*line == COMMAND_PREFIX) &&
|
|
8669 (strncmp (line + 1, "end macro", 9) == 0))
|
|
8670 depth--;
|
|
8671
|
|
8672 if (depth)
|
|
8673 {
|
|
8674 if ((int) (body_index + strlen (line) + 3) >= body_size)
|
|
8675 body = (char *)xrealloc
|
|
8676 (body, body_size += 3 + strlen (line));
|
|
8677 strcpy (body + body_index, line);
|
|
8678 body_index += strlen (line);
|
|
8679 body[body_index++] = '\n';
|
|
8680 body[body_index] = '\0';
|
|
8681 }
|
|
8682 free (line);
|
|
8683 }
|
|
8684
|
|
8685 /* We now have the name, the arglist, and the body. However, BODY
|
|
8686 includes the final newline which preceded the `@end macro' text.
|
|
8687 Delete it. */
|
|
8688 if (body && strlen (body))
|
|
8689 body[strlen (body) - 1] = '\0';
|
|
8690
|
|
8691 add_macro (name, arglist, body, input_filename, defining_line, flags);
|
|
8692
|
|
8693 if (macro_expansion_output_stream)
|
|
8694 remember_itext (input_text, input_text_offset);
|
|
8695 }
|
|
8696
|
|
8697 static void
|
|
8698 cm_unmacro (void)
|
|
8699 {
|
|
8700 register int i;
|
|
8701 char *line, *name;
|
|
8702 MACRO_DEF *def;
|
|
8703
|
|
8704 if (macro_expansion_output_stream)
|
|
8705 me_append_before_this_command ();
|
|
8706
|
|
8707 get_rest_of_line (&line);
|
|
8708 canon_white (line);
|
|
8709
|
|
8710 for (i = 0; line[i] && !whitespace (line[i]); i++);
|
|
8711 name = (char *)xmalloc (i);
|
|
8712 strncpy (name, line, i);
|
|
8713 name[i] = '\0';
|
|
8714
|
|
8715 def = delete_macro (name);
|
|
8716
|
|
8717 if (def)
|
|
8718 {
|
|
8719 free (def->source_file);
|
|
8720 free (def->name);
|
|
8721 free (def->body);
|
|
8722
|
|
8723 if (def->arglist)
|
|
8724 {
|
|
8725 register int ii;
|
|
8726
|
|
8727 for (ii = 0; def->arglist[ii]; i++)
|
|
8728 free (def->arglist[ii]);
|
|
8729
|
|
8730 free (def->arglist);
|
|
8731 }
|
|
8732
|
|
8733 free (def);
|
|
8734 }
|
|
8735
|
|
8736 free (line);
|
|
8737 free (name);
|
|
8738
|
|
8739 if (macro_expansion_output_stream)
|
|
8740 remember_itext (input_text, input_text_offset);
|
|
8741 }
|
|
8742
|
|
8743 /* How to output sections of the input file verbatim. */
|
|
8744
|
|
8745 /* Set the value of POINTER's offset to OFFSET. */
|
|
8746 static ITEXT *
|
|
8747 remember_itext (char *pointer, int offset)
|
|
8748 {
|
|
8749 register int i;
|
|
8750 ITEXT *itext = (ITEXT *)NULL;
|
|
8751
|
|
8752 /* If we have no info, initialize a blank list. */
|
|
8753 if (!itext_info)
|
|
8754 {
|
|
8755 itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *));
|
|
8756 for (i = 0; i < itext_size; i++)
|
|
8757 itext_info[i] = (ITEXT *)NULL;
|
|
8758 }
|
|
8759
|
|
8760 /* If the pointer is already present in the list, then set the offset. */
|
|
8761 for (i = 0; i < itext_size; i++)
|
|
8762 if ((itext_info[i] != (ITEXT *)NULL) &&
|
|
8763 (itext_info[i]->pointer == pointer))
|
|
8764 {
|
|
8765 itext = itext_info[i];
|
|
8766 itext_info[i]->offset = offset;
|
|
8767 break;
|
|
8768 }
|
|
8769
|
|
8770 if (i == itext_size)
|
|
8771 {
|
|
8772 /* Find a blank slot, (or create a new one), and remember the
|
|
8773 pointer and offset. */
|
|
8774 for (i = 0; i < itext_size; i++)
|
|
8775 if (itext_info[i] == (ITEXT *)NULL)
|
|
8776 break;
|
|
8777
|
|
8778 /* If not found, then add some slots. */
|
|
8779 if (i == itext_size)
|
|
8780 {
|
|
8781 register int j;
|
|
8782
|
|
8783 itext_info = (ITEXT **)xrealloc
|
|
8784 (itext_info, (itext_size += 10) * sizeof (ITEXT *));
|
|
8785
|
|
8786 for (j = i; j < itext_size; j++)
|
|
8787 itext_info[j] = (ITEXT *)NULL;
|
|
8788 }
|
|
8789
|
|
8790 /* Now add the pointer and the offset. */
|
|
8791 itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT));
|
|
8792 itext_info[i]->pointer = pointer;
|
|
8793 itext_info[i]->offset = offset;
|
|
8794 itext = itext_info[i];
|
|
8795 }
|
|
8796 return (itext);
|
|
8797 }
|
|
8798
|
|
8799 /* Forget the input text associated with POINTER. */
|
|
8800 static void
|
|
8801 forget_itext (char *pointer)
|
|
8802 {
|
|
8803 register int i;
|
|
8804
|
|
8805 for (i = 0; i < itext_size; i++)
|
|
8806 if (itext_info[i] && (itext_info[i]->pointer == pointer))
|
|
8807 {
|
|
8808 free (itext_info[i]);
|
|
8809 itext_info[i] = (ITEXT *)NULL;
|
|
8810 break;
|
|
8811 }
|
|
8812 }
|
|
8813
|
|
8814 /* Append the text which appeared in input_text from the last offset to
|
|
8815 the character just before the command that we are currently executing. */
|
|
8816 static void
|
|
8817 me_append_before_this_command (void)
|
|
8818 {
|
|
8819 register int i;
|
|
8820
|
|
8821 for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--);
|
|
8822 maybe_write_itext (input_text, i);
|
|
8823 }
|
|
8824
|
|
8825 /* Similar to execute_string (), but only takes a single string argument,
|
|
8826 and remembers the input text location, etc. */
|
|
8827 static void
|
|
8828 me_execute_string (char *execution_string)
|
|
8829 {
|
|
8830 pushfile ();
|
|
8831 input_text_offset = 0;
|
|
8832 input_text = execution_string;
|
|
8833 input_filename = strdup (input_filename);
|
|
8834 size_of_input_text = strlen (execution_string);
|
|
8835
|
|
8836 remember_itext (execution_string, 0);
|
|
8837
|
|
8838 executing_string++;
|
|
8839 reader_loop ();
|
|
8840 popfile ();
|
|
8841 executing_string--;
|
|
8842 }
|
|
8843
|
|
8844 /* Append the text which appears in input_text from the last offset to
|
|
8845 the current OFFSET. */
|
|
8846 static void
|
|
8847 append_to_expansion_output (int offset)
|
|
8848 {
|
|
8849 register int i;
|
|
8850 ITEXT *itext = (ITEXT *)NULL;
|
|
8851
|
|
8852 for (i = 0; i < itext_size; i++)
|
|
8853 if (itext_info[i] && itext_info[i]->pointer == input_text)
|
|
8854 {
|
|
8855 itext = itext_info[i];
|
|
8856 break;
|
|
8857 }
|
|
8858
|
|
8859 if (!itext)
|
|
8860 itext = remember_itext (input_text, 0);
|
|
8861
|
|
8862 if (offset > itext_info[i]->offset)
|
|
8863 {
|
|
8864 write_region_to_macro_output
|
|
8865 (input_text, itext_info[i]->offset, offset);
|
|
8866 remember_itext (input_text, offset);
|
|
8867 }
|
|
8868 }
|
|
8869
|
|
8870 /* Only write this input text iff it appears in our itext list. */
|
|
8871 static void
|
|
8872 maybe_write_itext (char *pointer, int offset)
|
|
8873 {
|
|
8874 register int i;
|
|
8875 ITEXT *itext = (ITEXT *)NULL;
|
|
8876
|
|
8877 for (i = 0; i < itext_size; i++)
|
|
8878 if (itext_info[i] && (itext_info[i]->pointer == pointer))
|
|
8879 {
|
|
8880 itext = itext_info[i];
|
|
8881 break;
|
|
8882 }
|
|
8883
|
|
8884 if (itext && (itext->offset < offset))
|
|
8885 {
|
|
8886 write_region_to_macro_output (itext->pointer, itext->offset, offset);
|
|
8887 remember_itext (pointer, offset);
|
|
8888 }
|
|
8889 }
|
|
8890
|
|
8891 static void
|
|
8892 write_region_to_macro_output (char *string, int start, int end)
|
|
8893 {
|
|
8894 if (macro_expansion_output_stream)
|
|
8895 fwrite (string + start, 1, end - start, macro_expansion_output_stream);
|
|
8896 }
|
|
8897
|
|
8898 #endif /* HAVE_MACROS */
|
|
8899
|
|
8900 /* Return the length of the array in ARRAY. */
|
|
8901 static int
|
|
8902 array_len (char **array)
|
|
8903 {
|
|
8904 register int i = 0;
|
|
8905
|
|
8906 if (array)
|
|
8907 for (i = 0; array[i] != (char *)NULL; i++);
|
|
8908
|
|
8909 return (i);
|
|
8910 }
|
|
8911
|
|
8912 static void
|
|
8913 free_array (char **array)
|
|
8914 {
|
|
8915 if (array)
|
|
8916 {
|
|
8917 register int i;
|
|
8918
|
|
8919 for (i = 0; array[i] != (char *)NULL; i++)
|
|
8920 free (array[i]);
|
|
8921
|
|
8922 free (array);
|
|
8923 }
|
|
8924 }
|
|
8925
|
|
8926 /* Function is used even when we don't have macros. Although, I have
|
|
8927 to admit, it is unlikely that you would have a use for it if you
|
|
8928 aren't using macros. */
|
|
8929 static char **
|
|
8930 get_brace_args (int quote_single)
|
|
8931 {
|
|
8932 char **arglist, *word;
|
|
8933 int arglist_index, arglist_size;
|
|
8934 int character, escape_seen, start;
|
|
8935 int depth = 1;
|
|
8936
|
|
8937 /* There is an arglist in braces here, so gather the args inside of it. */
|
|
8938 skip_whitespace_and_newlines ();
|
|
8939 input_text_offset++;
|
|
8940 arglist = (char **)NULL;
|
|
8941 arglist_index = arglist_size = 0;
|
|
8942
|
|
8943 get_arg:
|
|
8944 skip_whitespace_and_newlines ();
|
|
8945 start = input_text_offset;
|
|
8946 escape_seen = 0;
|
|
8947
|
|
8948 while ((character = curchar ()))
|
|
8949 {
|
|
8950 if (character == '\\')
|
|
8951 {
|
|
8952 input_text_offset += 2;
|
|
8953 escape_seen = 1;
|
|
8954 }
|
|
8955 else if (character == '{')
|
|
8956 {
|
|
8957 depth++;
|
|
8958 input_text_offset++;
|
|
8959 }
|
|
8960 else if ((character == ',' && !quote_single) ||
|
|
8961 ((character == '}') && depth == 1))
|
|
8962 {
|
|
8963 int len = input_text_offset - start;
|
|
8964
|
|
8965 if (len || (character != '}'))
|
|
8966 {
|
|
8967 word = (char *)xmalloc (1 + len);
|
|
8968 strncpy (word, input_text + start, len);
|
|
8969 word[len] = '\0';
|
|
8970
|
|
8971 /* Clean up escaped characters. */
|
|
8972 if (escape_seen)
|
|
8973 {
|
|
8974 register int i;
|
|
8975
|
|
8976 for (i = 0; word[i]; i++)
|
|
8977 if (word[i] == '\\')
|
|
8978 memcpy (word + i, word + i + 1, strlen (word + i + 1));
|
|
8979 }
|
|
8980
|
|
8981 if (arglist_index + 2 >= arglist_size)
|
|
8982 arglist = (char **)xrealloc
|
|
8983 (arglist, (arglist_size += 10) * sizeof (char *));
|
|
8984
|
|
8985 arglist[arglist_index++] = word;
|
|
8986 arglist[arglist_index] = (char *)NULL;
|
|
8987 }
|
|
8988
|
|
8989 input_text_offset++;
|
|
8990 if (character == '}')
|
|
8991 break;
|
|
8992 else
|
|
8993 goto get_arg;
|
|
8994 }
|
|
8995 else if (character == '}')
|
|
8996 {
|
|
8997 depth--;
|
|
8998 input_text_offset++;
|
|
8999 }
|
|
9000 else
|
|
9001 input_text_offset++;
|
|
9002 }
|
|
9003 return (arglist);
|
|
9004 }
|
|
9005
|
|
9006 /* **************************************************************** */
|
|
9007 /* */
|
|
9008 /* Looking For Include Files */
|
|
9009 /* */
|
|
9010 /* **************************************************************** */
|
|
9011
|
|
9012 /* Given a string containing units of information separated by colons,
|
|
9013 return the next one pointed to by INDEX, or NULL if there are no more.
|
|
9014 Advance INDEX to the character after the colon. */
|
|
9015 static char *
|
|
9016 extract_colon_unit (char *string, int *indecks)
|
|
9017 {
|
|
9018 int i, start;
|
|
9019
|
|
9020 i = *indecks;
|
|
9021
|
|
9022 if (!string || (i >= (int) strlen (string)))
|
|
9023 return ((char *)NULL);
|
|
9024
|
|
9025 /* Each call to this routine leaves the index pointing at a colon if
|
|
9026 there is more to the path. If I is > 0, then increment past the
|
|
9027 `:'. If I is 0, then the path has a leading colon. Trailing colons
|
|
9028 are handled OK by the `else' part of the if statement; an empty
|
|
9029 string is returned in that case. */
|
|
9030 if (i && string[i] == ':')
|
|
9031 i++;
|
|
9032
|
|
9033 start = i;
|
|
9034
|
|
9035 while (string[i] && string[i] != ':') i++;
|
|
9036
|
|
9037 *indecks = i;
|
|
9038
|
|
9039 if (i == start)
|
|
9040 {
|
|
9041 if (string[i])
|
|
9042 (*indecks)++;
|
|
9043
|
|
9044 /* Return "" in the case of a trailing `:'. */
|
|
9045 return (strdup (""));
|
|
9046 }
|
|
9047 else
|
|
9048 {
|
|
9049 char *value;
|
|
9050
|
|
9051 value = (char *)xmalloc (1 + (i - start));
|
|
9052 strncpy (value, &string[start], (i - start));
|
|
9053 value [i - start] = '\0';
|
|
9054
|
|
9055 return (value);
|
|
9056 }
|
|
9057 }
|
|
9058
|
|
9059 /* Return the full pathname for FILENAME by searching along PATH.
|
|
9060 When found, return the stat () info for FILENAME in FINFO.
|
|
9061 If PATH is NULL, only the current directory is searched.
|
|
9062 If the file could not be found, return a NULL pointer. */
|
|
9063 static char *
|
|
9064 get_file_info_in_path (char *filename, char *path, struct stat *finfo)
|
|
9065 {
|
|
9066 char *dir;
|
|
9067 int result, indecks = 0;
|
|
9068
|
|
9069 if (path == (char *)NULL)
|
|
9070 path = ".";
|
|
9071
|
|
9072 /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
|
|
9073 if (*filename == '/' ||
|
|
9074 (*filename == '.' &&
|
|
9075 (filename[1] == '/' ||
|
|
9076 (filename[1] == '.' && filename[2] == '/'))))
|
|
9077 {
|
|
9078 if (stat (filename, finfo) == 0)
|
|
9079 return (strdup (filename));
|
|
9080 else
|
|
9081 return ((char *)NULL);
|
|
9082 }
|
|
9083
|
|
9084 while ((dir = extract_colon_unit (path, &indecks)))
|
|
9085 {
|
|
9086 char *fullpath;
|
|
9087
|
|
9088 if (!*dir)
|
|
9089 {
|
|
9090 free (dir);
|
|
9091 dir = strdup (".");
|
|
9092 }
|
|
9093
|
|
9094 fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
|
|
9095 sprintf (fullpath, "%s/%s", dir, filename);
|
|
9096 free (dir);
|
|
9097
|
|
9098 result = stat (fullpath, finfo);
|
|
9099
|
|
9100 if (result == 0)
|
|
9101 return (fullpath);
|
|
9102 else
|
|
9103 free (fullpath);
|
|
9104 }
|
|
9105 return ((char *)NULL);
|
|
9106 }
|