Mercurial > hg > xemacs-beta
comparison man/makeinfo.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
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 | |
2907 /* Remove upto COUNT characters of whitespace from the | |
2908 the current output line. If COUNT is less than zero, | |
2909 then remove until none left. */ | |
2910 static void | |
2911 kill_self_indent (int count) | |
2912 { | |
2913 /* Handle infinite case first. */ | |
2914 if (count < 0) | |
2915 { | |
2916 output_column = 0; | |
2917 while (output_paragraph_offset) | |
2918 { | |
2919 if (whitespace (output_paragraph[output_paragraph_offset - 1])) | |
2920 output_paragraph_offset--; | |
2921 else | |
2922 break; | |
2923 } | |
2924 } | |
2925 else | |
2926 { | |
2927 while (output_paragraph_offset && count--) | |
2928 if (whitespace (output_paragraph[output_paragraph_offset - 1])) | |
2929 output_paragraph_offset--; | |
2930 else | |
2931 break; | |
2932 } | |
2933 } | |
2934 | |
2935 /* Non-zero means do not honor calls to flush_output (). */ | |
2936 static int flushing_ignored = 0; | |
2937 | |
2938 /* Prevent calls to flush_output () from having any effect. */ | |
2939 static void | |
2940 inhibit_output_flushing (void) | |
2941 { | |
2942 flushing_ignored++; | |
2943 } | |
2944 | |
2945 /* Allow calls to flush_output () to write the paragraph data. */ | |
2946 static void | |
2947 uninhibit_output_flushing (void) | |
2948 { | |
2949 flushing_ignored--; | |
2950 } | |
2951 | |
2952 static void | |
2953 flush_output (void) | |
2954 { | |
2955 register int i; | |
2956 | |
2957 if (!output_paragraph_offset || flushing_ignored) | |
2958 return; | |
2959 | |
2960 for (i = 0; i < output_paragraph_offset; i++) | |
2961 { | |
2962 if (output_paragraph[i] == (unsigned char)(' ' | 0x80) || | |
2963 output_paragraph[i] == (unsigned char)('\t' | 0x80) || | |
2964 output_paragraph[i] == (unsigned char)('\n' | 0x80) || | |
2965 sentence_ender (UNMETA (output_paragraph[i]))) | |
2966 output_paragraph[i] &= 0x7f; | |
2967 } | |
2968 | |
2969 fwrite (output_paragraph, 1, output_paragraph_offset, output_stream); | |
2970 | |
2971 output_position += output_paragraph_offset; | |
2972 output_paragraph_offset = 0; | |
2973 } | |
2974 | |
2975 /* How to close a paragraph controlling the number of lines between | |
2976 this one and the last one. */ | |
2977 | |
2978 /* Paragraph spacing is controlled by this variable. It is the number of | |
2979 blank lines that you wish to appear between paragraphs. A value of | |
2980 1 creates a single blank line between paragraphs. */ | |
2981 int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING; | |
2982 | |
2983 /* Close the current paragraph, leaving no blank lines between them. */ | |
2984 static void | |
2985 close_single_paragraph (void) | |
2986 { | |
2987 close_paragraph_with_lines (0); | |
2988 } | |
2989 | |
2990 /* Close a paragraph after an insertion has ended. */ | |
2991 static void | |
2992 close_insertion_paragraph (void) | |
2993 { | |
2994 if (!insertion_paragraph_closed) | |
2995 { | |
2996 /* Close the current paragraph, breaking the line. */ | |
2997 close_single_paragraph (); | |
2998 | |
2999 /* Start a new paragraph here, inserting whatever indention is correct | |
3000 for the now current insertion level (one above the one that we are | |
3001 ending). */ | |
3002 start_paragraph (); | |
3003 | |
3004 /* Tell close_paragraph () that the previous line has already been | |
3005 broken, so it should insert one less newline. */ | |
3006 line_already_broken = 1; | |
3007 | |
3008 /* Let functions such as add_char () know that we have already found a | |
3009 newline. */ | |
3010 ignore_blank_line (); | |
3011 } | |
3012 else | |
3013 { | |
3014 /* If the insertion paragraph is closed already, then we are seeing | |
3015 two `@end' commands in a row. Note that the first one we saw was | |
3016 handled in the first part of this if-then-else clause, and at that | |
3017 time start_paragraph () was called, partially to handle the proper | |
3018 indentation of the current line. However, the indentation level | |
3019 may have just changed again, so we may have to outdent the current | |
3020 line to the new indentation level. */ | |
3021 if (current_indent < output_column) | |
3022 kill_self_indent (output_column - current_indent); | |
3023 } | |
3024 | |
3025 insertion_paragraph_closed = 1; | |
3026 } | |
3027 | |
3028 static void | |
3029 close_paragraph_with_lines (int lines) | |
3030 { | |
3031 int old_spacing = paragraph_spacing; | |
3032 paragraph_spacing = lines; | |
3033 close_paragraph (); | |
3034 paragraph_spacing = old_spacing; | |
3035 } | |
3036 | |
3037 /* Close the currently open paragraph. */ | |
3038 static void | |
3039 close_paragraph (void) | |
3040 { | |
3041 register int i; | |
3042 | |
3043 /* The insertion paragraph is no longer closed. */ | |
3044 insertion_paragraph_closed = 0; | |
3045 | |
3046 if (paragraph_is_open && !must_start_paragraph) | |
3047 { | |
3048 register int tindex, c; | |
3049 | |
3050 tindex = output_paragraph_offset; | |
3051 | |
3052 /* Back up to last non-newline/space character, forcing all such | |
3053 subsequent characters to be newlines. This isn't strictly | |
3054 necessary, but a couple of functions use the presence of a newline | |
3055 to make decisions. */ | |
3056 for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex) | |
3057 { | |
3058 c = output_paragraph[tindex]; | |
3059 | |
3060 if (c == ' '|| c == '\n') | |
3061 output_paragraph[tindex] = '\n'; | |
3062 else | |
3063 break; | |
3064 } | |
3065 | |
3066 /* All trailing whitespace is ignored. */ | |
3067 output_paragraph_offset = ++tindex; | |
3068 | |
3069 /* Break the line if that is appropriate. */ | |
3070 if (paragraph_spacing >= 0) | |
3071 insert ('\n'); | |
3072 | |
3073 /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */ | |
3074 if (!force_flush_right) | |
3075 { | |
3076 for (i = 0; i < (paragraph_spacing - line_already_broken); i++) | |
3077 insert ('\n'); | |
3078 } | |
3079 | |
3080 /* If we are doing flush right indentation, then do it now | |
3081 on the paragraph (really a single line). */ | |
3082 if (force_flush_right) | |
3083 do_flush_right_indentation (); | |
3084 | |
3085 flush_output (); | |
3086 paragraph_is_open = 0; | |
3087 no_indent = 0; | |
3088 output_column = 0; | |
3089 } | |
3090 ignore_blank_line (); | |
3091 } | |
3092 | |
3093 /* Make the last line just read look as if it were only a newline. */ | |
3094 static void | |
3095 ignore_blank_line (void) | |
3096 { | |
3097 last_inserted_character = '\n'; | |
3098 last_char_was_newline = 1; | |
3099 } | |
3100 | |
3101 /* Align the end of the text in output_paragraph with fill_column. */ | |
3102 static void | |
3103 do_flush_right_indentation (void) | |
3104 { | |
3105 char *temp; | |
3106 int temp_len; | |
3107 | |
3108 kill_self_indent (-1); | |
3109 | |
3110 if (output_paragraph[0] != '\n') | |
3111 { | |
3112 output_paragraph[output_paragraph_offset] = '\0'; | |
3113 | |
3114 if (output_paragraph_offset < fill_column) | |
3115 { | |
3116 register int i; | |
3117 | |
3118 if (fill_column >= paragraph_buffer_len) | |
3119 output_paragraph = | |
3120 xrealloc (output_paragraph, | |
3121 (paragraph_buffer_len += fill_column)); | |
3122 | |
3123 temp_len = strlen ((char *)output_paragraph); | |
3124 temp = (char *)xmalloc (temp_len + 1); | |
3125 memcpy (temp, (char *)output_paragraph, temp_len); | |
3126 | |
3127 for (i = 0; i < fill_column - output_paragraph_offset; i++) | |
3128 output_paragraph[i] = ' '; | |
3129 | |
3130 memcpy ((char *)output_paragraph + i, temp, temp_len); | |
3131 free (temp); | |
3132 output_paragraph_offset = fill_column; | |
3133 } | |
3134 } | |
3135 } | |
3136 | |
3137 /* Begin a new paragraph. */ | |
3138 static void | |
3139 start_paragraph (void) | |
3140 { | |
3141 /* First close existing one. */ | |
3142 if (paragraph_is_open) | |
3143 close_paragraph (); | |
3144 | |
3145 /* In either case, the insertion paragraph is no longer closed. */ | |
3146 insertion_paragraph_closed = 0; | |
3147 | |
3148 /* However, the paragraph is open! */ | |
3149 paragraph_is_open = 1; | |
3150 | |
3151 /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph () | |
3152 had to be called before we would allow any other paragraph operations | |
3153 to have an effect. */ | |
3154 if (!must_start_paragraph) | |
3155 { | |
3156 int amount_to_indent = 0; | |
3157 | |
3158 /* If doing indentation, then insert the appropriate amount. */ | |
3159 if (!no_indent) | |
3160 { | |
3161 if (inhibit_paragraph_indentation) | |
3162 { | |
3163 amount_to_indent = current_indent; | |
3164 if (inhibit_paragraph_indentation < 0) | |
3165 inhibit_paragraph_indentation++; | |
3166 } | |
3167 else if (paragraph_start_indent < 0) | |
3168 amount_to_indent = current_indent; | |
3169 else | |
3170 amount_to_indent = current_indent + paragraph_start_indent; | |
3171 | |
3172 if (amount_to_indent >= output_column) | |
3173 { | |
3174 amount_to_indent -= output_column; | |
3175 indent (amount_to_indent); | |
3176 output_column += amount_to_indent; | |
3177 } | |
3178 } | |
3179 } | |
3180 else | |
3181 must_start_paragraph = 0; | |
3182 } | |
3183 | |
3184 /* Insert the indentation specified by AMOUNT. */ | |
3185 static void | |
3186 indent (int amount) | |
3187 { | |
3188 register BRACE_ELEMENT *elt = brace_stack; | |
3189 | |
3190 /* For every START_POS saved within the brace stack which will be affected | |
3191 by this indentation, bump that start pos forward. */ | |
3192 while (elt) | |
3193 { | |
3194 if (elt->pos >= output_paragraph_offset) | |
3195 elt->pos += amount; | |
3196 elt = elt->next; | |
3197 } | |
3198 | |
3199 while (--amount >= 0) | |
3200 insert (' '); | |
3201 } | |
3202 | |
3203 /* Search forward for STRING in input_text. | |
3204 FROM says where where to start. */ | |
3205 static int | |
3206 search_forward (char *string, int from) | |
3207 { | |
3208 int len = strlen (string); | |
3209 | |
3210 while (from < size_of_input_text) | |
3211 { | |
3212 if (strncmp (input_text + from, string, len) == 0) | |
3213 return (from); | |
3214 from++; | |
3215 } | |
3216 return (-1); | |
3217 } | |
3218 | |
3219 /* Whoops, Unix doesn't have strcasecmp. */ | |
3220 | |
3221 /* Case independent string compare. */ | |
3222 #if !defined (HAVE_STRCASECMP) | |
3223 static int | |
3224 strcasecmp (char *string1, char *string2) | |
3225 { | |
3226 char ch1, ch2; | |
3227 | |
3228 for (;;) | |
3229 { | |
3230 ch1 = *string1++; | |
3231 ch2 = *string2++; | |
3232 | |
3233 if (!(ch1 | ch2)) | |
3234 return (0); | |
3235 | |
3236 ch1 = coerce_to_upper (ch1); | |
3237 ch2 = coerce_to_upper (ch2); | |
3238 | |
3239 if (ch1 != ch2) | |
3240 return (ch1 - ch2); | |
3241 } | |
3242 } | |
3243 #endif /* !HAVE_STRCASECMP */ | |
3244 | |
3245 enum insertion_type { menu, quotation, lisp, smalllisp, example, | |
3246 smallexample, display, itemize, format, enumerate, cartouche, table, | |
3247 ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn, | |
3248 defun, defmac, defspec, defvr, defvar, defopt, deftypefn, | |
3249 deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod, | |
3250 deftypemethod, deftp, bad_type }; | |
3251 | |
3252 char *insertion_type_names[] = { "menu", "quotation", "lisp", | |
3253 "smalllisp", "example", "smallexample", "display", "itemize", | |
3254 "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group", | |
3255 "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn", | |
3256 "defun", "defmac", "defspec", "defvr", "defvar", "defopt", | |
3257 "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv", | |
3258 "defivar", "defop", "defmethod", "deftypemethod", "deftp", | |
3259 "bad_type" }; | |
3260 | |
3261 int insertion_level = 0; | |
3262 typedef struct istack_elt | |
3263 { | |
3264 struct istack_elt *next; | |
3265 char *item_function; | |
3266 char *filename; | |
3267 int line_number; | |
3268 int filling_enabled; | |
3269 int indented_fill; | |
3270 enum insertion_type insertion; | |
3271 int inhibited; | |
3272 } INSERTION_ELT; | |
3273 | |
3274 INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL; | |
3275 | |
3276 static void | |
3277 init_insertion_stack (void) | |
3278 { | |
3279 insertion_stack = (INSERTION_ELT *) NULL; | |
3280 } | |
3281 | |
3282 /* Return the type of the current insertion. */ | |
3283 static enum insertion_type | |
3284 current_insertion_type (void) | |
3285 { | |
3286 if (!insertion_level) | |
3287 return (bad_type); | |
3288 else | |
3289 return (insertion_stack->insertion); | |
3290 } | |
3291 | |
3292 /* Return a pointer to the string which is the function to wrap around | |
3293 items. */ | |
3294 static char * | |
3295 current_item_function (void) | |
3296 { | |
3297 register int level, done; | |
3298 register INSERTION_ELT *elt; | |
3299 | |
3300 level = insertion_level; | |
3301 elt = insertion_stack; | |
3302 done = 0; | |
3303 | |
3304 /* Skip down through the stack until we find a non-conditional insertion. */ | |
3305 while (!done && (elt != NULL)) | |
3306 { | |
3307 switch (elt->insertion) | |
3308 { | |
3309 case ifinfo: | |
3310 case ifset: | |
3311 case ifclear: | |
3312 case cartouche: | |
3313 elt = elt->next; | |
3314 level--; | |
3315 break; | |
3316 | |
3317 default: | |
3318 done = 1; | |
3319 } | |
3320 } | |
3321 | |
3322 if (!level) | |
3323 return ((char *) NULL); | |
3324 else | |
3325 return (elt->item_function); | |
3326 } | |
3327 | |
3328 static char * | |
3329 get_item_function (void) | |
3330 { | |
3331 char *item_function; | |
3332 get_rest_of_line (&item_function); | |
3333 backup_input_pointer (); | |
3334 canon_white (item_function); | |
3335 return (item_function); | |
3336 } | |
3337 | |
3338 /* Push the state of the current insertion on the stack. */ | |
3339 static void | |
3340 push_insertion (enum insertion_type type, char *item_function) | |
3341 { | |
3342 INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT)); | |
3343 | |
3344 new->item_function = item_function; | |
3345 new->filling_enabled = filling_enabled; | |
3346 new->indented_fill = indented_fill; | |
3347 new->insertion = type; | |
3348 new->line_number = line_number; | |
3349 new->filename = strdup (input_filename); | |
3350 new->inhibited = inhibit_paragraph_indentation; | |
3351 new->next = insertion_stack; | |
3352 insertion_stack = new; | |
3353 insertion_level++; | |
3354 } | |
3355 | |
3356 /* Pop the value on top of the insertion stack into the | |
3357 global variables. */ | |
3358 static void | |
3359 pop_insertion (void) | |
3360 { | |
3361 INSERTION_ELT *temp = insertion_stack; | |
3362 | |
3363 if (temp == (INSERTION_ELT *) NULL) | |
3364 return; | |
3365 | |
3366 inhibit_paragraph_indentation = temp->inhibited; | |
3367 filling_enabled = temp->filling_enabled; | |
3368 indented_fill = temp->indented_fill; | |
3369 free_and_clear (&(temp->item_function)); | |
3370 free_and_clear (&(temp->filename)); | |
3371 insertion_stack = insertion_stack->next; | |
3372 free (temp); | |
3373 insertion_level--; | |
3374 } | |
3375 | |
3376 /* Return a pointer to the print name of this | |
3377 enumerated type. */ | |
3378 static char * | |
3379 insertion_type_pname (enum insertion_type type) | |
3380 { | |
3381 if ((int) type < (int) bad_type) | |
3382 return (insertion_type_names[(int) type]); | |
3383 else | |
3384 return ("Broken-Type in insertion_type_pname"); | |
3385 } | |
3386 | |
3387 /* Return the insertion_type associated with NAME. | |
3388 If the type is not one of the known ones, return BAD_TYPE. */ | |
3389 static enum insertion_type | |
3390 find_type_from_name (char *name) | |
3391 { | |
3392 int indecks = 0; | |
3393 while (indecks < (int) bad_type) | |
3394 { | |
3395 if (strcmp (name, insertion_type_names[indecks]) == 0) | |
3396 return (enum insertion_type) indecks; | |
3397 indecks++; | |
3398 } | |
3399 return (bad_type); | |
3400 } | |
3401 | |
3402 static void | |
3403 do_nothing (void) | |
3404 { | |
3405 } | |
3406 | |
3407 static int | |
3408 defun_insertion (enum insertion_type type) | |
3409 { | |
3410 return | |
3411 ((type == deffn) | |
3412 || (type == defun) | |
3413 || (type == defmac) | |
3414 || (type == defspec) | |
3415 || (type == defvr) | |
3416 || (type == defvar) | |
3417 || (type == defopt) | |
3418 || (type == deftypefn) | |
3419 || (type == deftypefun) | |
3420 || (type == deftypevr) | |
3421 || (type == deftypevar) | |
3422 || (type == defcv) | |
3423 || (type == defivar) | |
3424 || (type == defop) | |
3425 || (type == defmethod) | |
3426 || (type == deftypemethod) | |
3427 || (type == deftp)); | |
3428 } | |
3429 | |
3430 /* MAX_NS is the maximum nesting level for enumerations. I picked 100 | |
3431 which seemed reasonable. This doesn't control the number of items, | |
3432 just the number of nested lists. */ | |
3433 #define max_stack_depth 100 | |
3434 #define ENUM_DIGITS 1 | |
3435 #define ENUM_ALPHA 2 | |
3436 typedef struct { | |
3437 int enumtype; | |
3438 int enumval; | |
3439 } DIGIT_ALPHA; | |
3440 | |
3441 DIGIT_ALPHA enumstack[max_stack_depth]; | |
3442 int enumstack_offset = 0; | |
3443 int current_enumval = 1; | |
3444 int current_enumtype = ENUM_DIGITS; | |
3445 char *enumeration_arg = (char *)NULL; | |
3446 | |
3447 static void | |
3448 start_enumerating (int at, int type) | |
3449 { | |
3450 if ((enumstack_offset + 1) == max_stack_depth) | |
3451 { | |
3452 line_error ("Enumeration stack overflow"); | |
3453 return; | |
3454 } | |
3455 enumstack[enumstack_offset].enumtype = current_enumtype; | |
3456 enumstack[enumstack_offset].enumval = current_enumval; | |
3457 enumstack_offset++; | |
3458 current_enumval = at; | |
3459 current_enumtype = type; | |
3460 } | |
3461 | |
3462 static void | |
3463 stop_enumerating (void) | |
3464 { | |
3465 --enumstack_offset; | |
3466 if (enumstack_offset < 0) | |
3467 enumstack_offset = 0; | |
3468 | |
3469 current_enumval = enumstack[enumstack_offset].enumval; | |
3470 current_enumtype = enumstack[enumstack_offset].enumtype; | |
3471 } | |
3472 | |
3473 /* Place a letter or digits into the output stream. */ | |
3474 static void | |
3475 enumerate_item (void) | |
3476 { | |
3477 char temp[10]; | |
3478 | |
3479 if (current_enumtype == ENUM_ALPHA) | |
3480 { | |
3481 if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1)) | |
3482 { | |
3483 current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A'); | |
3484 warning ("Lettering overflow, restarting at %c", current_enumval); | |
3485 } | |
3486 sprintf (temp, "%c. ", current_enumval); | |
3487 } | |
3488 else | |
3489 sprintf (temp, "%d. ", current_enumval); | |
3490 | |
3491 indent (output_column += (current_indent - strlen (temp))); | |
3492 add_word (temp); | |
3493 current_enumval++; | |
3494 } | |
3495 | |
3496 /* This is where the work for all the "insertion" style | |
3497 commands is done. A huge switch statement handles the | |
3498 various setups, and generic code is on both sides. */ | |
3499 static void | |
3500 begin_insertion (enum insertion_type type) | |
3501 { | |
3502 int no_discard = 0; | |
3503 | |
3504 if (defun_insertion (type)) | |
3505 { | |
3506 push_insertion (type, strdup ("")); | |
3507 no_discard++; | |
3508 } | |
3509 else | |
3510 push_insertion (type, get_item_function ()); | |
3511 | |
3512 switch (type) | |
3513 { | |
3514 case menu: | |
3515 if (!no_headers) | |
3516 close_paragraph (); | |
3517 | |
3518 filling_enabled = no_indent = 0; | |
3519 inhibit_paragraph_indentation = 1; | |
3520 | |
3521 if (!no_headers) | |
3522 add_word ("* Menu:\n"); | |
3523 | |
3524 in_menu++; | |
3525 no_discard++; | |
3526 break; | |
3527 | |
3528 /* I think @quotation is meant to do filling. | |
3529 If you don't want filling, then use @example. */ | |
3530 case quotation: | |
3531 close_single_paragraph (); | |
3532 last_char_was_newline = no_indent = 0; | |
3533 indented_fill = filling_enabled = 1; | |
3534 inhibit_paragraph_indentation = 1; | |
3535 current_indent += default_indentation_increment; | |
3536 break; | |
3537 | |
3538 case display: | |
3539 case example: | |
3540 case smallexample: | |
3541 case lisp: | |
3542 case smalllisp: | |
3543 /* Just like @example, but no indentation. */ | |
3544 case format: | |
3545 | |
3546 close_single_paragraph (); | |
3547 inhibit_paragraph_indentation = 1; | |
3548 in_fixed_width_font++; | |
3549 filling_enabled = 0; | |
3550 last_char_was_newline = 0; | |
3551 | |
3552 if (type != format) | |
3553 current_indent += default_indentation_increment; | |
3554 | |
3555 break; | |
3556 | |
3557 case table: | |
3558 case ftable: | |
3559 case vtable: | |
3560 case itemize: | |
3561 close_single_paragraph (); | |
3562 current_indent += default_indentation_increment; | |
3563 filling_enabled = indented_fill = 1; | |
3564 #if defined (INDENT_PARAGRAPHS_IN_TABLE) | |
3565 inhibit_paragraph_indentation = 0; | |
3566 #else | |
3567 inhibit_paragraph_indentation = 1; | |
3568 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */ | |
3569 | |
3570 /* Make things work for losers who forget the itemize syntax. */ | |
3571 if (allow_lax_format && (type == itemize)) | |
3572 { | |
3573 if (!(*insertion_stack->item_function)) | |
3574 { | |
3575 free (insertion_stack->item_function); | |
3576 insertion_stack->item_function = strdup ("@bullet"); | |
3577 insertion_stack->item_function[0] = COMMAND_PREFIX; | |
3578 } | |
3579 } | |
3580 | |
3581 if (!*insertion_stack->item_function) | |
3582 { | |
3583 line_error ("%s requires an argument: the formatter for %citem", | |
3584 insertion_type_pname (type), COMMAND_PREFIX); | |
3585 } | |
3586 break; | |
3587 | |
3588 case enumerate: | |
3589 close_single_paragraph (); | |
3590 no_indent = 0; | |
3591 #if defined (INDENT_PARAGRAPHS_IN_TABLE) | |
3592 inhibit_paragraph_indentation = 0; | |
3593 #else | |
3594 inhibit_paragraph_indentation = 1; | |
3595 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */ | |
3596 | |
3597 current_indent += default_indentation_increment; | |
3598 filling_enabled = indented_fill = 1; | |
3599 | |
3600 if (isdigit (*enumeration_arg)) | |
3601 start_enumerating (atoi (enumeration_arg), ENUM_DIGITS); | |
3602 else | |
3603 start_enumerating (*enumeration_arg, ENUM_ALPHA); | |
3604 break; | |
3605 | |
3606 /* Does nothing special in makeinfo. */ | |
3607 case group: | |
3608 /* Only close the paragraph if we are not inside of an @example. */ | |
3609 if (!insertion_stack->next || | |
3610 insertion_stack->next->insertion != example) | |
3611 close_single_paragraph (); | |
3612 break; | |
3613 | |
3614 /* Insertions that are no-ops in info, but do something in TeX. */ | |
3615 case ifinfo: | |
3616 case ifset: | |
3617 case ifclear: | |
3618 case cartouche: | |
3619 if (in_menu) | |
3620 no_discard++; | |
3621 break; | |
3622 | |
3623 case deffn: | |
3624 case defun: | |
3625 case defmac: | |
3626 case defspec: | |
3627 case defvr: | |
3628 case defvar: | |
3629 case defopt: | |
3630 case deftypefn: | |
3631 case deftypefun: | |
3632 case deftypevr: | |
3633 case deftypevar: | |
3634 case defcv: | |
3635 case defivar: | |
3636 case defop: | |
3637 case defmethod: | |
3638 case deftypemethod: | |
3639 case deftp: | |
3640 inhibit_paragraph_indentation = 1; | |
3641 filling_enabled = indented_fill = 1; | |
3642 current_indent += default_indentation_increment; | |
3643 no_indent = 0; | |
3644 break; | |
3645 | |
3646 case flushleft: | |
3647 close_single_paragraph (); | |
3648 inhibit_paragraph_indentation = 1; | |
3649 filling_enabled = indented_fill = no_indent = 0; | |
3650 break; | |
3651 | |
3652 case flushright: | |
3653 close_single_paragraph (); | |
3654 filling_enabled = indented_fill = no_indent = 0; | |
3655 inhibit_paragraph_indentation = 1; | |
3656 force_flush_right++; | |
3657 break; | |
3658 | |
3659 default: | |
3660 break; | |
3661 } | |
3662 | |
3663 if (!no_discard) | |
3664 discard_until ("\n"); | |
3665 } | |
3666 | |
3667 /* Try to end the insertion with the specified TYPE. | |
3668 TYPE, with a value of bad_type, gets translated to match | |
3669 the value currently on top of the stack. | |
3670 Otherwise, if TYPE doesn't match the top of the insertion stack, | |
3671 give error. */ | |
3672 static void | |
3673 end_insertion (enum insertion_type type) | |
3674 { | |
3675 enum insertion_type temp_type; | |
3676 | |
3677 if (!insertion_level) | |
3678 return; | |
3679 | |
3680 temp_type = current_insertion_type (); | |
3681 | |
3682 if (type == bad_type) | |
3683 type = temp_type; | |
3684 | |
3685 if (type != temp_type) | |
3686 { | |
3687 line_error | |
3688 ("`%cend' expected `%s', but saw `%s'", COMMAND_PREFIX, | |
3689 insertion_type_pname (temp_type), insertion_type_pname (type)); | |
3690 return; | |
3691 } | |
3692 | |
3693 pop_insertion (); | |
3694 | |
3695 switch (type) | |
3696 { | |
3697 /* Insertions which have no effect on paragraph formatting. */ | |
3698 case ifinfo: | |
3699 case ifset: | |
3700 case ifclear: | |
3701 break; | |
3702 | |
3703 case menu: | |
3704 in_menu--; /* No longer hacking menus. */ | |
3705 if (!no_headers) | |
3706 close_insertion_paragraph (); | |
3707 break; | |
3708 | |
3709 case enumerate: | |
3710 stop_enumerating (); | |
3711 close_insertion_paragraph (); | |
3712 current_indent -= default_indentation_increment; | |
3713 break; | |
3714 | |
3715 case flushleft: | |
3716 case group: | |
3717 case cartouche: | |
3718 close_insertion_paragraph (); | |
3719 break; | |
3720 | |
3721 case format: | |
3722 case display: | |
3723 case example: | |
3724 case smallexample: | |
3725 case lisp: | |
3726 case smalllisp: | |
3727 case quotation: | |
3728 | |
3729 /* @quotation is the only one of the above without a fixed width | |
3730 font. */ | |
3731 if (type != quotation) | |
3732 in_fixed_width_font--; | |
3733 | |
3734 /* @format is the only fixed_width insertion without a change | |
3735 in indentation. */ | |
3736 if (type != format) | |
3737 current_indent -= default_indentation_increment; | |
3738 | |
3739 /* The ending of one of these insertions always marks the | |
3740 start of a new paragraph. */ | |
3741 close_insertion_paragraph (); | |
3742 break; | |
3743 | |
3744 case table: | |
3745 case ftable: | |
3746 case vtable: | |
3747 case itemize: | |
3748 current_indent -= default_indentation_increment; | |
3749 break; | |
3750 | |
3751 case flushright: | |
3752 force_flush_right--; | |
3753 close_insertion_paragraph (); | |
3754 break; | |
3755 | |
3756 /* Handle the @defun style insertions with a default clause. */ | |
3757 default: | |
3758 current_indent -= default_indentation_increment; | |
3759 close_insertion_paragraph (); | |
3760 break; | |
3761 } | |
3762 } | |
3763 | |
3764 /* Insertions cannot cross certain boundaries, such as node beginnings. In | |
3765 code that creates such boundaries, you should call discard_insertions () | |
3766 before doing anything else. It prints the errors for you, and cleans up | |
3767 the insertion stack. */ | |
3768 static void | |
3769 discard_insertions (void) | |
3770 { | |
3771 int real_line_number = line_number; | |
3772 while (insertion_stack) | |
3773 { | |
3774 if (insertion_stack->insertion == ifinfo || | |
3775 insertion_stack->insertion == ifset || | |
3776 insertion_stack->insertion == ifclear || | |
3777 insertion_stack->insertion == cartouche) | |
3778 break; | |
3779 else | |
3780 { | |
3781 char *offender; | |
3782 char *current_filename; | |
3783 | |
3784 current_filename = input_filename; | |
3785 offender = (char *)insertion_type_pname (insertion_stack->insertion); | |
3786 input_filename = insertion_stack->filename; | |
3787 line_number = insertion_stack->line_number; | |
3788 line_error ("This `%s' doesn't have a matching `%cend %s'", offender, | |
3789 COMMAND_PREFIX, offender); | |
3790 input_filename = current_filename; | |
3791 pop_insertion (); | |
3792 } | |
3793 } | |
3794 line_number = real_line_number; | |
3795 } | |
3796 | |
3797 /* The actual commands themselves. */ | |
3798 | |
3799 /* Commands which insert themselves. */ | |
3800 static void | |
3801 insert_self (void) | |
3802 { | |
3803 add_word (command); | |
3804 } | |
3805 | |
3806 /* Force a line break in the output. */ | |
3807 static void | |
3808 cm_asterisk (void) | |
3809 { | |
3810 close_single_paragraph (); | |
3811 #if !defined (ASTERISK_NEW_PARAGRAPH) | |
3812 cm_noindent (); | |
3813 #endif /* ASTERISK_NEW_PARAGRAPH */ | |
3814 } | |
3815 | |
3816 /* Insert ellipsis. */ | |
3817 static void | |
3818 cm_dots (int arg) | |
3819 { | |
3820 if (arg == START) | |
3821 add_word ("..."); | |
3822 } | |
3823 | |
3824 static void | |
3825 cm_bullet (int arg) | |
3826 { | |
3827 if (arg == START) | |
3828 add_char ('*'); | |
3829 } | |
3830 | |
3831 static void | |
3832 cm_minus (int arg) | |
3833 { | |
3834 if (arg == START) | |
3835 add_char ('-'); | |
3836 } | |
3837 | |
3838 /* Insert "TeX". */ | |
3839 static void | |
3840 cm_TeX (int arg) | |
3841 { | |
3842 if (arg == START) | |
3843 add_word ("TeX"); | |
3844 } | |
3845 | |
3846 static void | |
3847 cm_copyright (int arg) | |
3848 { | |
3849 if (arg == START) | |
3850 add_word ("(C)"); | |
3851 } | |
3852 | |
3853 #if defined (__osf__) | |
3854 #define LOCALTIME_CAST(x) (time_t *)(x) | |
3855 #else | |
3856 #define LOCALTIME_CAST(x) (x) | |
3857 #endif | |
3858 | |
3859 static void | |
3860 cm_today (int arg) | |
3861 { | |
3862 static char * months [12] = | |
3863 { "January", "February", "March", "April", "May", "June", "July", | |
3864 "August", "September", "October", "November", "December" }; | |
3865 if (arg == START) | |
3866 { | |
3867 long timer = time (0); | |
3868 struct tm *ts = localtime (LOCALTIME_CAST (&timer)); | |
3869 add_word_args | |
3870 ("%d %s %d", | |
3871 (ts -> tm_mday), | |
3872 (months [ts -> tm_mon]), | |
3873 ((ts -> tm_year) + 1900)); | |
3874 } | |
3875 } | |
3876 | |
3877 static void | |
3878 cm_code (int arg) | |
3879 { | |
3880 extern int printing_index; | |
3881 | |
3882 if (printing_index) | |
3883 return; | |
3884 | |
3885 if (arg == START) | |
3886 { | |
3887 in_fixed_width_font++; | |
3888 add_char ('`'); | |
3889 } | |
3890 else | |
3891 { | |
3892 add_word ("'"); | |
3893 in_fixed_width_font--; | |
3894 } | |
3895 } | |
3896 | |
3897 static void | |
3898 cm_samp (int arg) | |
3899 { | |
3900 cm_code (arg); | |
3901 } | |
3902 | |
3903 static void | |
3904 cm_file (int arg) | |
3905 { | |
3906 cm_code (arg); | |
3907 } | |
3908 | |
3909 static void | |
3910 cm_kbd (int arg) | |
3911 { | |
3912 cm_code (arg); | |
3913 } | |
3914 | |
3915 static void | |
3916 cm_key (int arg) | |
3917 { | |
3918 } | |
3919 | |
3920 /* Convert the character at position into CTL. */ | |
3921 static void | |
3922 cm_ctrl (int arg, int start, int end) | |
3923 { | |
3924 /* Should we allow multiple character arguments? I think yes. */ | |
3925 if (arg == END) | |
3926 { | |
3927 register int i, character; | |
3928 #if defined (NO_MULTIPLE_CTRL) | |
3929 if ((end - start) != 1) | |
3930 line_error ("%c%s expects a single character as an argument", | |
3931 COMMAND_PREFIX, command); | |
3932 else | |
3933 #endif | |
3934 for (i = start; i < end; i++) | |
3935 { | |
3936 character = output_paragraph[i]; | |
3937 | |
3938 if (isletter (character)) | |
3939 output_paragraph[i] = CTL (coerce_to_upper (character)); | |
3940 } | |
3941 } | |
3942 } | |
3943 | |
3944 /* Small Caps in makeinfo just does all caps. */ | |
3945 static void | |
3946 cm_sc (int arg, int start_pos, int end_pos) | |
3947 { | |
3948 if (arg == END) | |
3949 { | |
3950 while (start_pos < end_pos) | |
3951 { | |
3952 output_paragraph[start_pos] = | |
3953 coerce_to_upper (output_paragraph[start_pos]); | |
3954 start_pos++; | |
3955 } | |
3956 } | |
3957 } | |
3958 | |
3959 /* @var in makeinfo just uppercases the text. */ | |
3960 static void | |
3961 cm_var (int arg, int start_pos, int end_pos) | |
3962 { | |
3963 if (arg == END) | |
3964 { | |
3965 while (start_pos < end_pos) | |
3966 { | |
3967 output_paragraph[start_pos] = | |
3968 coerce_to_upper (output_paragraph[start_pos]); | |
3969 start_pos++; | |
3970 } | |
3971 } | |
3972 } | |
3973 | |
3974 static void | |
3975 cm_dfn (int arg, int position) | |
3976 { | |
3977 add_char ('"'); | |
3978 } | |
3979 | |
3980 static void | |
3981 cm_emph (int arg) | |
3982 { | |
3983 add_char ('*'); | |
3984 } | |
3985 | |
3986 static void | |
3987 cm_strong (int arg, int position) | |
3988 { | |
3989 cm_emph (arg); | |
3990 } | |
3991 | |
3992 static void | |
3993 cm_cite (int arg, int position) | |
3994 { | |
3995 if (arg == START) | |
3996 add_word ("`"); | |
3997 else | |
3998 add_word ("'"); | |
3999 } | |
4000 | |
4001 /* Current text is italicized. */ | |
4002 static void | |
4003 cm_italic (int arg, int start, int end) | |
4004 { | |
4005 } | |
4006 | |
4007 /* Current text is highlighted. */ | |
4008 static void | |
4009 cm_bold (int arg, int start, int end) | |
4010 { | |
4011 cm_italic (arg, start, end); | |
4012 } | |
4013 | |
4014 /* Current text is in roman font. */ | |
4015 static void | |
4016 cm_roman (int arg, int start, int end) | |
4017 { | |
4018 } | |
4019 | |
4020 /* Current text is in roman font. */ | |
4021 static void | |
4022 cm_titlefont (int arg, int start, int end) | |
4023 { | |
4024 } | |
4025 | |
4026 /* Italicize titles. */ | |
4027 static void | |
4028 cm_title (int arg, int start, int end) | |
4029 { | |
4030 cm_italic (arg, start, end); | |
4031 } | |
4032 | |
4033 /* @refill is a NOP. */ | |
4034 static void | |
4035 cm_refill (void) | |
4036 { | |
4037 } | |
4038 | |
4039 /* Prevent the argument from being split across two lines. */ | |
4040 static void | |
4041 cm_w (int arg, int start, int end) | |
4042 { | |
4043 if (arg == START) | |
4044 non_splitting_words++; | |
4045 else | |
4046 non_splitting_words--; | |
4047 } | |
4048 | |
4049 | |
4050 /* Explain that this command is obsolete, thus the user shouldn't | |
4051 do anything with it. */ | |
4052 static void | |
4053 cm_obsolete (int arg, int start, int end) | |
4054 { | |
4055 if (arg == START) | |
4056 warning ("The command `%c%s' is obsolete", COMMAND_PREFIX, command); | |
4057 } | |
4058 | |
4059 /* Insert the text following input_text_offset up to the end of the line | |
4060 in a new, separate paragraph. Directly underneath it, insert a | |
4061 line of WITH_CHAR, the same length of the inserted text. */ | |
4062 static void | |
4063 insert_and_underscore (int with_char) | |
4064 { | |
4065 register int i, len; | |
4066 int old_no_indent, starting_pos, ending_pos; | |
4067 char *temp; | |
4068 | |
4069 close_paragraph (); | |
4070 filling_enabled = indented_fill = 0; | |
4071 old_no_indent = no_indent; | |
4072 no_indent = 1; | |
4073 | |
4074 #if defined (HAVE_MACROS) | |
4075 if (macro_expansion_output_stream) | |
4076 append_to_expansion_output (input_text_offset + 1); | |
4077 #endif /* HAVE_MACROS */ | |
4078 | |
4079 get_rest_of_line (&temp); | |
4080 | |
4081 starting_pos = output_position + output_paragraph_offset; | |
4082 #if defined (HAVE_MACROS) | |
4083 if (macro_expansion_output_stream) | |
4084 { | |
4085 char *temp1; | |
4086 | |
4087 temp1 = (char *)xmalloc (2 + strlen (temp)); | |
4088 sprintf (temp1, "%s\n", temp); | |
4089 remember_itext (input_text, input_text_offset); | |
4090 me_execute_string (temp1); | |
4091 free (temp1); | |
4092 } | |
4093 else | |
4094 #endif /* HAVE_MACROS */ | |
4095 execute_string ("%s\n", temp); | |
4096 | |
4097 ending_pos = output_position + output_paragraph_offset; | |
4098 free (temp); | |
4099 | |
4100 len = (ending_pos - starting_pos) - 1; | |
4101 for (i = 0; i < len; i++) | |
4102 add_char (with_char); | |
4103 insert ('\n'); | |
4104 close_paragraph (); | |
4105 filling_enabled = 1; | |
4106 no_indent = old_no_indent; | |
4107 } | |
4108 | |
4109 /* Here is a structure which associates sectioning commands with | |
4110 an integer, hopefully to reflect the `depth' of the current | |
4111 section. */ | |
4112 struct { | |
4113 char *name; | |
4114 int level; | |
4115 } section_alist[] = { | |
4116 { "unnumberedsubsubsec", 5 }, | |
4117 { "unnumberedsubsec", 4 }, | |
4118 { "unnumberedsec", 3 }, | |
4119 { "unnumbered", 2 }, | |
4120 { "appendixsubsubsec", 5 }, | |
4121 { "appendixsubsec", 4 }, | |
4122 { "appendixsec", 3 }, | |
4123 { "appendixsection", 3 }, | |
4124 { "appendix", 2 }, | |
4125 { "subsubsec", 5 }, | |
4126 { "subsubsection", 5 }, | |
4127 { "subsection", 4 }, | |
4128 { "section", 3 }, | |
4129 { "chapter", 2 }, | |
4130 { "top", 1 }, | |
4131 | |
4132 { (char *)NULL, 0 } | |
4133 }; | |
4134 | |
4135 /* Amount to offset the name of sectioning commands to levels by. */ | |
4136 int section_alist_offset = 0; | |
4137 | |
4138 /* Shift the meaning of @section to @chapter. */ | |
4139 static void | |
4140 cm_raisesections (void) | |
4141 { | |
4142 discard_until ("\n"); | |
4143 section_alist_offset--; | |
4144 } | |
4145 | |
4146 /* Shift the meaning of @chapter to @section. */ | |
4147 static void | |
4148 cm_lowersections (void) | |
4149 { | |
4150 discard_until ("\n"); | |
4151 section_alist_offset++; | |
4152 } | |
4153 | |
4154 /* Return an integer which identifies the type section present in TEXT. */ | |
4155 static int | |
4156 what_section (char *text) | |
4157 { | |
4158 register int i, j; | |
4159 char *t; | |
4160 | |
4161 find_section_command: | |
4162 for (j = 0; text[j] && cr_or_whitespace (text[j]); j++); | |
4163 if (text[j] != COMMAND_PREFIX) | |
4164 return (-1); | |
4165 | |
4166 text = text + j + 1; | |
4167 | |
4168 /* We skip @c, @comment, and @?index commands. */ | |
4169 if ((strncmp (text, "comment", strlen ("comment")) == 0) || | |
4170 (text[0] == 'c' && cr_or_whitespace (text[1])) || | |
4171 (strcmp (text + 1, "index") == 0)) | |
4172 { | |
4173 while (*text++ != '\n'); | |
4174 goto find_section_command; | |
4175 } | |
4176 | |
4177 /* Handle italicized sectioning commands. */ | |
4178 if (*text == 'i') | |
4179 text++; | |
4180 | |
4181 for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++); | |
4182 | |
4183 for (i = 0; (t = section_alist[i].name); i++) | |
4184 { | |
4185 if (j == strlen (t) && strncmp (t, text, j) == 0) | |
4186 { | |
4187 int return_val; | |
4188 | |
4189 return_val = (section_alist[i].level + section_alist_offset); | |
4190 | |
4191 if (return_val < 0) | |
4192 return_val = 0; | |
4193 else if (return_val > 5) | |
4194 return_val = 5; | |
4195 return (return_val); | |
4196 } | |
4197 } | |
4198 return (-1); | |
4199 } | |
4200 | |
4201 /* Set the level of @top to LEVEL. Return the old level of @top. */ | |
4202 static int | |
4203 set_top_section_level (int level) | |
4204 { | |
4205 register int i, result = -1; | |
4206 | |
4207 for (i = 0; section_alist[i].name; i++) | |
4208 if (strcmp (section_alist[i].name, "top") == 0) | |
4209 { | |
4210 result = section_alist[i].level; | |
4211 section_alist[i].level = level; | |
4212 break; | |
4213 } | |
4214 return (result); | |
4215 } | |
4216 | |
4217 /* Treat this just like @unnumbered. The only difference is | |
4218 in node defaulting. */ | |
4219 static void | |
4220 cm_top (void) | |
4221 { | |
4222 /* It is an error to have more than one @top. */ | |
4223 if (top_node_seen) | |
4224 { | |
4225 TAG_ENTRY *tag = tag_table; | |
4226 | |
4227 line_error ("There already is a node having %ctop as a section", | |
4228 COMMAND_PREFIX); | |
4229 | |
4230 while (tag != (TAG_ENTRY *)NULL) | |
4231 { | |
4232 if ((tag->flags & IS_TOP)) | |
4233 { | |
4234 int old_line_number = line_number; | |
4235 char *old_input_filename = input_filename; | |
4236 | |
4237 line_number = tag->line_no; | |
4238 input_filename = tag->filename; | |
4239 line_error ("Here is the %ctop node", COMMAND_PREFIX); | |
4240 input_filename = old_input_filename; | |
4241 line_number = old_line_number; | |
4242 return; | |
4243 } | |
4244 tag = tag->next_ent; | |
4245 } | |
4246 } | |
4247 else | |
4248 { | |
4249 top_node_seen = 1; | |
4250 | |
4251 /* It is an error to use @top before you have used @node. */ | |
4252 if (!tag_table) | |
4253 { | |
4254 char *top_name; | |
4255 | |
4256 get_rest_of_line (&top_name); | |
4257 free (top_name); | |
4258 line_error ("%ctop used before %cnode, defaulting to %s", | |
4259 COMMAND_PREFIX, COMMAND_PREFIX, top_name); | |
4260 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name); | |
4261 return; | |
4262 } | |
4263 | |
4264 cm_unnumbered (); | |
4265 | |
4266 /* The most recently defined node is the top node. */ | |
4267 tag_table->flags |= IS_TOP; | |
4268 | |
4269 /* Now set the logical hierarchical level of the Top node. */ | |
4270 { | |
4271 int orig_offset = input_text_offset; | |
4272 | |
4273 input_text_offset = search_forward (node_search_string, orig_offset); | |
4274 | |
4275 if (input_text_offset > 0) | |
4276 { | |
4277 int this_section; | |
4278 | |
4279 /* We have encountered a non-top node, so mark that one exists. */ | |
4280 non_top_node_seen = 1; | |
4281 | |
4282 /* Move to the end of this line, and find out what the | |
4283 sectioning command is here. */ | |
4284 while (input_text[input_text_offset] != '\n') | |
4285 input_text_offset++; | |
4286 | |
4287 if (input_text_offset < size_of_input_text) | |
4288 input_text_offset++; | |
4289 | |
4290 this_section = what_section (input_text + input_text_offset); | |
4291 | |
4292 /* If we found a sectioning command, then give the top section | |
4293 a level of this section - 1. */ | |
4294 if (this_section != -1) | |
4295 set_top_section_level (this_section - 1); | |
4296 } | |
4297 input_text_offset = orig_offset; | |
4298 } | |
4299 } | |
4300 } | |
4301 | |
4302 /* Organized by level commands. That is, "*" == chapter, "=" == section. */ | |
4303 char *scoring_characters = "*=-."; | |
4304 | |
4305 static void | |
4306 sectioning_underscore (char *commanned) | |
4307 { | |
4308 char character; | |
4309 char *temp; | |
4310 int level; | |
4311 | |
4312 temp = (char *)xmalloc (2 + strlen (commanned)); | |
4313 temp[0] = COMMAND_PREFIX; | |
4314 strcpy (&temp[1], commanned); | |
4315 level = what_section (temp); | |
4316 free (temp); | |
4317 level -= 2; | |
4318 | |
4319 if (level < 0) | |
4320 level = 0; | |
4321 | |
4322 character = scoring_characters[level]; | |
4323 | |
4324 insert_and_underscore (character); | |
4325 } | |
4326 | |
4327 /* The remainder of the text on this line is a chapter heading. */ | |
4328 static void | |
4329 cm_chapter (void) | |
4330 { | |
4331 sectioning_underscore ("chapter"); | |
4332 } | |
4333 | |
4334 /* The remainder of the text on this line is a section heading. */ | |
4335 static void | |
4336 cm_section (void) | |
4337 { | |
4338 sectioning_underscore ("section"); | |
4339 } | |
4340 | |
4341 /* The remainder of the text on this line is a subsection heading. */ | |
4342 static void | |
4343 cm_subsection (void) | |
4344 { | |
4345 sectioning_underscore ("subsection"); | |
4346 } | |
4347 | |
4348 /* The remainder of the text on this line is a subsubsection heading. */ | |
4349 static void | |
4350 cm_subsubsection (void) | |
4351 { | |
4352 sectioning_underscore ("subsubsection"); | |
4353 } | |
4354 | |
4355 /* The remainder of the text on this line is an unnumbered heading. */ | |
4356 static void | |
4357 cm_unnumbered (void) | |
4358 { | |
4359 cm_chapter (); | |
4360 } | |
4361 | |
4362 /* The remainder of the text on this line is an unnumbered section heading. */ | |
4363 static void | |
4364 cm_unnumberedsec (void) | |
4365 { | |
4366 cm_section (); | |
4367 } | |
4368 | |
4369 /* The remainder of the text on this line is an unnumbered | |
4370 subsection heading. */ | |
4371 static void | |
4372 cm_unnumberedsubsec (void) | |
4373 { | |
4374 cm_subsection (); | |
4375 } | |
4376 | |
4377 /* The remainder of the text on this line is an unnumbered | |
4378 subsubsection heading. */ | |
4379 static void | |
4380 cm_unnumberedsubsubsec (void) | |
4381 { | |
4382 cm_subsubsection (); | |
4383 } | |
4384 | |
4385 /* The remainder of the text on this line is an appendix heading. */ | |
4386 static void | |
4387 cm_appendix (void) | |
4388 { | |
4389 cm_chapter (); | |
4390 } | |
4391 | |
4392 /* The remainder of the text on this line is an appendix section heading. */ | |
4393 static void | |
4394 cm_appendixsec (void) | |
4395 { | |
4396 cm_section (); | |
4397 } | |
4398 | |
4399 /* The remainder of the text on this line is an appendix subsection heading. */ | |
4400 static void | |
4401 cm_appendixsubsec (void) | |
4402 { | |
4403 cm_subsection (); | |
4404 } | |
4405 | |
4406 /* The remainder of the text on this line is an appendix | |
4407 subsubsection heading. */ | |
4408 static void | |
4409 cm_appendixsubsubsec (void) | |
4410 { | |
4411 cm_subsubsection (); | |
4412 } | |
4413 | |
4414 /* Compatibility functions substitute for chapter, section, etc. */ | |
4415 static void | |
4416 cm_majorheading (void) | |
4417 { | |
4418 cm_chapheading (); | |
4419 } | |
4420 | |
4421 static void | |
4422 cm_chapheading (void) | |
4423 { | |
4424 cm_chapter (); | |
4425 } | |
4426 | |
4427 static void | |
4428 cm_heading (void) | |
4429 { | |
4430 cm_section (); | |
4431 } | |
4432 | |
4433 static void | |
4434 cm_subheading (void) | |
4435 { | |
4436 cm_subsection (); | |
4437 } | |
4438 | |
4439 static void | |
4440 cm_subsubheading (void) | |
4441 { | |
4442 cm_subsubsection (); | |
4443 } | |
4444 | |
4445 | |
4446 /* **************************************************************** */ | |
4447 /* */ | |
4448 /* Adding nodes, and making tags */ | |
4449 /* */ | |
4450 /* **************************************************************** */ | |
4451 | |
4452 /* Start a new tag table. */ | |
4453 static void | |
4454 init_tag_table (void) | |
4455 { | |
4456 while (tag_table != (TAG_ENTRY *) NULL) | |
4457 { | |
4458 TAG_ENTRY *temp = tag_table; | |
4459 free (temp->node); | |
4460 free (temp->prev); | |
4461 free (temp->next); | |
4462 free (temp->up); | |
4463 tag_table = tag_table->next_ent; | |
4464 free (temp); | |
4465 } | |
4466 } | |
4467 | |
4468 static void | |
4469 write_tag_table (void) | |
4470 { | |
4471 write_tag_table_internal (0); /* Not indirect. */ | |
4472 } | |
4473 | |
4474 static void | |
4475 write_tag_table_indirect (void) | |
4476 { | |
4477 write_tag_table_internal (1); | |
4478 } | |
4479 | |
4480 /* Write out the contents of the existing tag table. | |
4481 INDIRECT_P says how to format the output. */ | |
4482 static void | |
4483 write_tag_table_internal (int indirect_p) | |
4484 { | |
4485 TAG_ENTRY *node = tag_table; | |
4486 int old_indent = no_indent; | |
4487 | |
4488 no_indent = 1; | |
4489 filling_enabled = 0; | |
4490 must_start_paragraph = 0; | |
4491 close_paragraph (); | |
4492 | |
4493 if (!indirect_p) | |
4494 { | |
4495 no_indent = 1; | |
4496 insert ('\n'); | |
4497 } | |
4498 | |
4499 add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : ""); | |
4500 | |
4501 while (node != (TAG_ENTRY *) NULL) | |
4502 { | |
4503 execute_string ("Node: %s", node->node); | |
4504 add_word_args ("\177%d\n", node->position); | |
4505 node = node->next_ent; | |
4506 } | |
4507 | |
4508 add_word ("\037\nEnd Tag Table\n"); | |
4509 flush_output (); | |
4510 no_indent = old_indent; | |
4511 } | |
4512 | |
4513 static char * | |
4514 get_node_token (void) | |
4515 { | |
4516 char *string; | |
4517 | |
4518 get_until_in_line (",", &string); | |
4519 | |
4520 if (curchar () == ',') | |
4521 input_text_offset++; | |
4522 | |
4523 canon_white (string); | |
4524 | |
4525 /* Force all versions of "top" to be "Top". */ | |
4526 normalize_node_name (string); | |
4527 | |
4528 return (string); | |
4529 } | |
4530 | |
4531 /* Convert "top" and friends into "Top". */ | |
4532 static void | |
4533 normalize_node_name (char *string) | |
4534 { | |
4535 if (strcasecmp (string, "Top") == 0) | |
4536 strcpy (string, "Top"); | |
4537 } | |
4538 | |
4539 /* Look up NAME in the tag table, and return the associated | |
4540 tag_entry. If the node is not in the table return NULL. */ | |
4541 static TAG_ENTRY * | |
4542 find_node (char *name) | |
4543 { | |
4544 TAG_ENTRY *tag = tag_table; | |
4545 | |
4546 while (tag != (TAG_ENTRY *) NULL) | |
4547 { | |
4548 if (strcmp (tag->node, name) == 0) | |
4549 return (tag); | |
4550 tag = tag->next_ent; | |
4551 } | |
4552 return ((TAG_ENTRY *) NULL); | |
4553 } | |
4554 | |
4555 /* Remember NODE and associates. */ | |
4556 static void | |
4557 remember_node (char *node, char *prev, char *next, char *up, int position, | |
4558 int line_no, int no_warn) | |
4559 { | |
4560 /* Check for existence of this tag already. */ | |
4561 if (validating) | |
4562 { | |
4563 register TAG_ENTRY *tag = find_node (node); | |
4564 if (tag) | |
4565 { | |
4566 line_error ("Node `%s' multiply defined (%d is first definition)", | |
4567 node, tag->line_no); | |
4568 return; | |
4569 } | |
4570 } | |
4571 | |
4572 /* First, make this the current node. */ | |
4573 current_node = node; | |
4574 | |
4575 /* Now add it to the list. */ | |
4576 { | |
4577 TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY)); | |
4578 new->node = node; | |
4579 new->prev = prev; | |
4580 new->next = next; | |
4581 new->up = up; | |
4582 new->position = position; | |
4583 new->line_no = line_no; | |
4584 new->filename = node_filename; | |
4585 new->touched = 0; /* not yet referenced. */ | |
4586 new->flags = 0; | |
4587 if (no_warn) | |
4588 new->flags |= NO_WARN; | |
4589 new->next_ent = tag_table; | |
4590 tag_table = new; | |
4591 } | |
4592 } | |
4593 | |
4594 /* The order is: nodename, nextnode, prevnode, upnode. | |
4595 If all of the NEXT, PREV, and UP fields are empty, they are defaulted. | |
4596 You must follow a node command which has those fields defaulted | |
4597 with a sectioning command (e.g. @chapter) giving the "level" of that node. | |
4598 It is an error not to do so. | |
4599 The defaults come from the menu in this node's parent. */ | |
4600 static void | |
4601 cm_node (void) | |
4602 { | |
4603 char *node, *prev, *next, *up; | |
4604 int new_node_pos, defaulting, this_section, no_warn = 0; | |
4605 extern int already_outputting_pending_notes; | |
4606 | |
4607 if (strcmp (command, "nwnode") == 0) | |
4608 no_warn = 1; | |
4609 | |
4610 /* Get rid of unmatched brace arguments from previous commands. */ | |
4611 discard_braces (); | |
4612 | |
4613 /* There also might be insertions left lying around that haven't been | |
4614 ended yet. Do that also. */ | |
4615 discard_insertions (); | |
4616 | |
4617 if (!already_outputting_pending_notes) | |
4618 { | |
4619 close_paragraph (); | |
4620 output_pending_notes (); | |
4621 free_pending_notes (); | |
4622 } | |
4623 | |
4624 filling_enabled = indented_fill = 0; | |
4625 new_node_pos = output_position; | |
4626 current_footnote_number = 1; | |
4627 | |
4628 #if defined (HAVE_MACROS) | |
4629 if (macro_expansion_output_stream) | |
4630 append_to_expansion_output (input_text_offset + 1); | |
4631 #endif /* HAVE_MACROS */ | |
4632 | |
4633 node = get_node_token (); | |
4634 next = get_node_token (); | |
4635 prev = get_node_token (); | |
4636 up = get_node_token (); | |
4637 | |
4638 #if defined (HAVE_MACROS) | |
4639 if (macro_expansion_output_stream) | |
4640 remember_itext (input_text, input_text_offset); | |
4641 #endif /* HAVE_MACROS */ | |
4642 | |
4643 no_indent = 1; | |
4644 if (!no_headers) | |
4645 { | |
4646 add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename); | |
4647 | |
4648 #if defined (HAVE_MACROS) | |
4649 if (macro_expansion_output_stream) | |
4650 me_execute_string (node); | |
4651 else | |
4652 #endif /* HAVE_MACROS */ | |
4653 execute_string ("%s", node); | |
4654 filling_enabled = indented_fill = 0; | |
4655 } | |
4656 | |
4657 /* Check for defaulting of this node's next, prev, and up fields. */ | |
4658 defaulting = ((strlen (next) == 0) && | |
4659 (strlen (prev) == 0) && | |
4660 (strlen (up) == 0)); | |
4661 | |
4662 this_section = what_section (input_text + input_text_offset); | |
4663 | |
4664 /* If we are defaulting, then look at the immediately following | |
4665 sectioning command (error if none) to determine the node's | |
4666 level. Find the node that contains the menu mentioning this node | |
4667 that is one level up (error if not found). That node is the "Up" | |
4668 of this node. Default the "Next" and "Prev" from the menu. */ | |
4669 if (defaulting) | |
4670 { | |
4671 NODE_REF *last_ref = (NODE_REF *)NULL; | |
4672 NODE_REF *ref = node_references; | |
4673 | |
4674 if ((this_section < 0) && (strcmp (node, "Top") != 0)) | |
4675 { | |
4676 char *polite_section_name = "top"; | |
4677 int i; | |
4678 | |
4679 for (i = 0; section_alist[i].name; i++) | |
4680 if (section_alist[i].level == current_section + 1) | |
4681 { | |
4682 polite_section_name = section_alist[i].name; | |
4683 break; | |
4684 } | |
4685 | |
4686 line_error | |
4687 ("Node `%s' requires a sectioning command (e.g. %c%s)", | |
4688 node, COMMAND_PREFIX, polite_section_name); | |
4689 } | |
4690 else | |
4691 { | |
4692 if (strcmp (node, "Top") == 0) | |
4693 { | |
4694 /* Default the NEXT pointer to be the first menu item in | |
4695 this node, if there is a menu in this node. We have to | |
4696 try very hard to find the menu, as it may be obscured | |
4697 by execution_strings which are on the filestack. For | |
4698 every member of the filestack which has a FILENAME | |
4699 member which is identical to the current INPUT_FILENAME, | |
4700 search forward from that offset. */ | |
4701 int saved_input_text_offset = input_text_offset; | |
4702 int saved_size_of_input_text = size_of_input_text; | |
4703 char *saved_input_text = input_text; | |
4704 FSTACK *next_file = filestack; | |
4705 | |
4706 int orig_offset, orig_size; | |
4707 | |
4708 /* No matter what, make this file point back at `(dir)'. */ | |
4709 free (up); up = strdup ("(dir)"); | |
4710 | |
4711 while (1) | |
4712 { | |
4713 orig_offset = input_text_offset; | |
4714 orig_size = | |
4715 search_forward (node_search_string, orig_offset); | |
4716 | |
4717 if (orig_size < 0) | |
4718 orig_size = size_of_input_text; | |
4719 | |
4720 input_text_offset = | |
4721 search_forward (menu_search_string, orig_offset); | |
4722 | |
4723 if (input_text_offset > -1) | |
4724 { | |
4725 char *nodename_from_menu = (char *)NULL; | |
4726 | |
4727 input_text_offset = | |
4728 search_forward ("\n* ", input_text_offset); | |
4729 | |
4730 if (input_text_offset != -1) | |
4731 nodename_from_menu = glean_node_from_menu (0); | |
4732 | |
4733 if (nodename_from_menu) | |
4734 { | |
4735 free (next); next = nodename_from_menu; | |
4736 break; | |
4737 } | |
4738 } | |
4739 | |
4740 /* We got here, so it hasn't been found yet. Try | |
4741 the next file on the filestack if there is one. */ | |
4742 if (next_file && | |
4743 (strcmp (next_file->filename, input_filename) == 0)) | |
4744 { | |
4745 input_text = next_file->text; | |
4746 input_text_offset = next_file->offset; | |
4747 size_of_input_text = next_file->size; | |
4748 next_file = next_file->next; | |
4749 } | |
4750 else | |
4751 { | |
4752 /* No more input files to check. */ | |
4753 break; | |
4754 } | |
4755 } | |
4756 | |
4757 input_text = saved_input_text; | |
4758 input_text_offset = saved_input_text_offset; | |
4759 size_of_input_text = saved_size_of_input_text; | |
4760 } | |
4761 } | |
4762 | |
4763 /* Fix the level of the menu references in the Top node, iff it | |
4764 was declared with @top, and no subsequent reference was found. */ | |
4765 if (top_node_seen && !non_top_node_seen) | |
4766 { | |
4767 /* Then this is the first non-@top node seen. */ | |
4768 int level; | |
4769 | |
4770 level = set_top_section_level (this_section - 1); | |
4771 non_top_node_seen = 1; | |
4772 | |
4773 while (ref) | |
4774 { | |
4775 if (ref->section == level) | |
4776 ref->section = this_section - 1; | |
4777 ref = ref->next; | |
4778 } | |
4779 | |
4780 ref = node_references; | |
4781 } | |
4782 | |
4783 while (ref) | |
4784 { | |
4785 if (ref->section == (this_section - 1) && | |
4786 ref->type == menu_reference && | |
4787 strcmp (ref->node, node) == 0) | |
4788 { | |
4789 char *containing_node = ref->containing_node; | |
4790 | |
4791 free (up); | |
4792 up = strdup (containing_node); | |
4793 | |
4794 if (last_ref && | |
4795 last_ref->type == menu_reference && | |
4796 (strcmp (last_ref->containing_node, | |
4797 containing_node) == 0)) | |
4798 { | |
4799 free (next); | |
4800 next = strdup (last_ref->node); | |
4801 } | |
4802 | |
4803 while ((ref->section == this_section - 1) && | |
4804 (ref->next) && | |
4805 (ref->next->type != menu_reference)) | |
4806 ref = ref->next; | |
4807 | |
4808 if (ref->next && ref->type == menu_reference && | |
4809 (strcmp (ref->next->containing_node, | |
4810 containing_node) == 0)) | |
4811 { | |
4812 free (prev); | |
4813 prev = strdup (ref->next->node); | |
4814 } | |
4815 else if (!ref->next && | |
4816 strcasecmp (ref->containing_node, "Top") == 0) | |
4817 { | |
4818 free (prev); | |
4819 prev = strdup (ref->containing_node); | |
4820 } | |
4821 break; | |
4822 } | |
4823 last_ref = ref; | |
4824 ref = ref->next; | |
4825 } | |
4826 } | |
4827 | |
4828 #if defined (HAVE_MACROS) | |
4829 /* Insert the correct args if we are expanding macros, and the node's | |
4830 pointers weren't defaulted. */ | |
4831 if (macro_expansion_output_stream && !defaulting) | |
4832 { | |
4833 char *temp; | |
4834 int op_orig = output_paragraph_offset; | |
4835 | |
4836 temp = (char *)xmalloc (3 + strlen (next)); | |
4837 sprintf (temp, ", %s", next); | |
4838 me_execute_string (temp); | |
4839 free (temp); | |
4840 | |
4841 temp = (char *)xmalloc (3 + strlen (prev)); | |
4842 sprintf (temp, ", %s", prev); | |
4843 me_execute_string (temp); | |
4844 free (temp); | |
4845 | |
4846 temp = (char *)xmalloc (4 + strlen (up)); | |
4847 sprintf (temp, ", %s", up); | |
4848 me_execute_string (temp); | |
4849 free (temp); | |
4850 | |
4851 output_paragraph_offset = op_orig; | |
4852 } | |
4853 #endif /* HAVE_MACROS */ | |
4854 | |
4855 if (!no_headers) | |
4856 { | |
4857 #if defined (HAVE_MACROS) | |
4858 if (macro_expansion_output_stream) | |
4859 me_inhibit_expansion++; | |
4860 #endif /* HAVE_MACROS */ | |
4861 | |
4862 if (*next) | |
4863 { | |
4864 execute_string (", Next: %s", next); | |
4865 filling_enabled = indented_fill = 0; | |
4866 } | |
4867 | |
4868 if (*prev) | |
4869 { | |
4870 execute_string (", Prev: %s", prev); | |
4871 filling_enabled = indented_fill = 0; | |
4872 } | |
4873 | |
4874 if (*up) | |
4875 { | |
4876 execute_string (", Up: %s", up); | |
4877 filling_enabled = indented_fill = 0; | |
4878 } | |
4879 #if defined (HAVE_MACROS) | |
4880 if (macro_expansion_output_stream) | |
4881 me_inhibit_expansion--; | |
4882 #endif /* HAVE_MACROS */ | |
4883 } | |
4884 | |
4885 close_paragraph (); | |
4886 no_indent = 0; | |
4887 | |
4888 if (!*node) | |
4889 { | |
4890 line_error ("No node name specified for `%c%s' command", | |
4891 COMMAND_PREFIX, command); | |
4892 free (node); | |
4893 free (next); | |
4894 free (prev); | |
4895 free (up); | |
4896 } | |
4897 else | |
4898 { | |
4899 if (!*next) { free (next); next = (char *)NULL; } | |
4900 if (!*prev) { free (prev); prev = (char *)NULL; } | |
4901 if (!*up) { free (up); up = (char *)NULL; } | |
4902 remember_node (node, prev, next, up, new_node_pos, line_number, no_warn); | |
4903 } | |
4904 | |
4905 /* Change the section only if there was a sectioning command. */ | |
4906 if (this_section >= 0) | |
4907 current_section = this_section; | |
4908 | |
4909 filling_enabled = 1; | |
4910 } | |
4911 | |
4912 /* Validation of an info file. | |
4913 Scan through the list of tag entrys touching the Prev, Next, and Up | |
4914 elements of each. It is an error not to be able to touch one of them, | |
4915 except in the case of external node references, such as "(DIR)". | |
4916 | |
4917 If the Prev is different from the Up, | |
4918 then the Prev node must have a Next pointing at this node. | |
4919 | |
4920 Every node except Top must have an Up. | |
4921 The Up node must contain some sort of reference, other than a Next, | |
4922 to this node. | |
4923 | |
4924 If the Next is different from the Next of the Up, | |
4925 then the Next node must have a Prev pointing at this node. */ | |
4926 static void | |
4927 validate_file (TAG_ENTRY *tag_tayble) | |
4928 { | |
4929 char *old_input_filename = input_filename; | |
4930 TAG_ENTRY *tags = tag_tayble; | |
4931 | |
4932 while (tags != (TAG_ENTRY *) NULL) | |
4933 { | |
4934 register TAG_ENTRY *temp_tag; | |
4935 | |
4936 input_filename = tags->filename; | |
4937 line_number = tags->line_no; | |
4938 | |
4939 /* If this is a "no warn" node, don't validate it in any way. */ | |
4940 if (tags->flags & NO_WARN) | |
4941 { | |
4942 tags = tags->next_ent; | |
4943 continue; | |
4944 } | |
4945 | |
4946 /* If this node has a Next, then make sure that the Next exists. */ | |
4947 if (tags->next) | |
4948 { | |
4949 validate (tags->next, tags->line_no, "Next"); | |
4950 | |
4951 /* If the Next node exists, and there is no Up, then make | |
4952 sure that the Prev of the Next points back. */ | |
4953 if ((temp_tag = find_node (tags->next))) | |
4954 { | |
4955 char *prev; | |
4956 | |
4957 if (temp_tag->flags & NO_WARN) | |
4958 { | |
4959 /* Do nothing if we aren't supposed to issue warnings | |
4960 about this node. */ | |
4961 } | |
4962 else | |
4963 { | |
4964 prev = temp_tag->prev; | |
4965 if (!prev || (strcmp (prev, tags->node) != 0)) | |
4966 { | |
4967 line_error ("Node `%s''s Next field not pointed back to", | |
4968 tags->node); | |
4969 line_number = temp_tag->line_no; | |
4970 input_filename = temp_tag->filename; | |
4971 line_error | |
4972 ("This node (`%s') is the one with the bad `Prev'", | |
4973 temp_tag->node); | |
4974 input_filename = tags->filename; | |
4975 line_number = tags->line_no; | |
4976 temp_tag->flags |= PREV_ERROR; | |
4977 } | |
4978 } | |
4979 } | |
4980 } | |
4981 | |
4982 /* Validate the Prev field if there is one, and we haven't already | |
4983 complained about it in some way. You don't have to have a Prev | |
4984 field at this stage. */ | |
4985 if (!(tags->flags & PREV_ERROR) && tags->prev) | |
4986 { | |
4987 int valid = validate (tags->prev, tags->line_no, "Prev"); | |
4988 | |
4989 if (!valid) | |
4990 tags->flags |= PREV_ERROR; | |
4991 else | |
4992 { | |
4993 /* If the Prev field is not the same as the Up field, | |
4994 then the node pointed to by the Prev field must have | |
4995 a Next field which points to this node. */ | |
4996 if (tags->up && (strcmp (tags->prev, tags->up) != 0)) | |
4997 { | |
4998 temp_tag = find_node (tags->prev); | |
4999 | |
5000 /* If we aren't supposed to issue warnings about the | |
5001 target node, do nothing. */ | |
5002 if (!temp_tag || (temp_tag->flags & NO_WARN)) | |
5003 { | |
5004 /* Do nothing. */ | |
5005 } | |
5006 else | |
5007 { | |
5008 if (!temp_tag->next || | |
5009 (strcmp (temp_tag->next, tags->node) != 0)) | |
5010 { | |
5011 line_error | |
5012 ("Node `%s''s Prev field not pointed back to", | |
5013 tags->node); | |
5014 line_number = temp_tag->line_no; | |
5015 input_filename = temp_tag->filename; | |
5016 line_error | |
5017 ("This node (`%s') is the one with the bad `Next'", | |
5018 temp_tag->node); | |
5019 input_filename = tags->filename; | |
5020 line_number = tags->line_no; | |
5021 temp_tag->flags |= NEXT_ERROR; | |
5022 } | |
5023 } | |
5024 } | |
5025 } | |
5026 } | |
5027 | |
5028 if (!tags->up && (strcasecmp (tags->node, "Top") != 0)) | |
5029 line_error ("Node `%s' is missing an \"Up\" field", tags->node); | |
5030 else if (tags->up) | |
5031 { | |
5032 int valid = validate (tags->up, tags->line_no, "Up"); | |
5033 | |
5034 /* If node X has Up: Y, then warn if Y fails to have a menu item | |
5035 or note pointing at X, if Y isn't of the form "(Y)". */ | |
5036 if (valid && *tags->up != '(') | |
5037 { | |
5038 NODE_REF *nref, *tref, *list; | |
5039 | |
5040 tref = (NODE_REF *) NULL; | |
5041 list = node_references; | |
5042 | |
5043 for (;;) | |
5044 { | |
5045 if (!(nref = find_node_reference (tags->node, list))) | |
5046 break; | |
5047 | |
5048 if (strcmp (nref->containing_node, tags->up) == 0) | |
5049 { | |
5050 if (nref->type != menu_reference) | |
5051 { | |
5052 tref = nref; | |
5053 list = nref->next; | |
5054 } | |
5055 else | |
5056 break; | |
5057 } | |
5058 list = nref->next; | |
5059 } | |
5060 | |
5061 if (!nref) | |
5062 { | |
5063 temp_tag = find_node (tags->up); | |
5064 line_number = temp_tag->line_no; | |
5065 input_filename = temp_tag->filename; | |
5066 if (!tref) | |
5067 line_error ( | |
5068 "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'", | |
5069 tags->node, tags->up, tags->up, tags->node); | |
5070 line_number = tags->line_no; | |
5071 input_filename = tags->filename; | |
5072 } | |
5073 } | |
5074 } | |
5075 tags = tags->next_ent; | |
5076 } | |
5077 | |
5078 validate_other_references (node_references); | |
5079 /* We have told the user about the references which didn't exist. | |
5080 Now tell him about the nodes which aren't referenced. */ | |
5081 | |
5082 tags = tag_tayble; | |
5083 while (tags != (TAG_ENTRY *) NULL) | |
5084 { | |
5085 /* If this node is a "no warn" node, do nothing. */ | |
5086 if (tags->flags & NO_WARN) | |
5087 { | |
5088 tags = tags->next_ent; | |
5089 continue; | |
5090 } | |
5091 | |
5092 /* Special hack. If the node in question appears to have | |
5093 been referenced more than REFERENCE_WARNING_LIMIT times, | |
5094 give a warning. */ | |
5095 if (tags->touched > reference_warning_limit) | |
5096 { | |
5097 input_filename = tags->filename; | |
5098 line_number = tags->line_no; | |
5099 warning ("Node `%s' has been referenced %d times", | |
5100 tags->node, tags->touched); | |
5101 } | |
5102 | |
5103 if (tags->touched == 0) | |
5104 { | |
5105 input_filename = tags->filename; | |
5106 line_number = tags->line_no; | |
5107 | |
5108 /* Notice that the node "Top" is special, and doesn't have to | |
5109 be referenced. */ | |
5110 if (strcasecmp (tags->node, "Top") != 0) | |
5111 warning ("Unreferenced node `%s'", tags->node); | |
5112 } | |
5113 tags = tags->next_ent; | |
5114 } | |
5115 input_filename = old_input_filename; | |
5116 } | |
5117 | |
5118 /* Return 1 if tag correctly validated, or 0 if not. */ | |
5119 static int | |
5120 validate (char *tag, int line, char *label) | |
5121 { | |
5122 TAG_ENTRY *result; | |
5123 | |
5124 /* If there isn't a tag to verify, or if the tag is in another file, | |
5125 then it must be okay. */ | |
5126 if (!tag || !*tag || *tag == '(') | |
5127 return (1); | |
5128 | |
5129 /* Otherwise, the tag must exist. */ | |
5130 result = find_node (tag); | |
5131 | |
5132 if (!result) | |
5133 { | |
5134 line_number = line; | |
5135 line_error ( | |
5136 "Validation error. `%s' field points to node `%s', which doesn't exist", | |
5137 label, tag); | |
5138 return (0); | |
5139 } | |
5140 result->touched++; | |
5141 return (1); | |
5142 } | |
5143 | |
5144 /* Split large output files into a series of smaller files. Each file | |
5145 is pointed to in the tag table, which then gets written out as the | |
5146 original file. The new files have the same name as the original file | |
5147 with a "-num" attached. SIZE is the largest number of bytes to allow | |
5148 in any single split file. */ | |
5149 static void | |
5150 split_file (char *filename, int size) | |
5151 { | |
5152 char *root_filename, *root_pathname; | |
5153 char *the_file; | |
5154 struct stat fileinfo; | |
5155 long file_size; | |
5156 char *the_header; | |
5157 int header_size; | |
5158 | |
5159 /* Can only do this to files with tag tables. */ | |
5160 if (!tag_table) | |
5161 return; | |
5162 | |
5163 if (size == 0) | |
5164 size = DEFAULT_SPLIT_SIZE; | |
5165 | |
5166 if ((stat (filename, &fileinfo) != 0) || | |
5167 (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD)) | |
5168 return; | |
5169 file_size = (long) fileinfo.st_size; | |
5170 | |
5171 the_file = find_and_load (filename); | |
5172 if (!the_file) | |
5173 return; | |
5174 | |
5175 root_filename = filename_part (filename); | |
5176 root_pathname = pathname_part (filename); | |
5177 | |
5178 if (!root_pathname) | |
5179 root_pathname = strdup (""); | |
5180 | |
5181 /* Start splitting the file. Walk along the tag table | |
5182 outputting sections of the file. When we have written | |
5183 all of the nodes in the tag table, make the top-level | |
5184 pointer file, which contains indirect pointers and | |
5185 tags for the nodes. */ | |
5186 { | |
5187 int which_file = 1; | |
5188 TAG_ENTRY *tags = tag_table; | |
5189 char *indirect_info = (char *)NULL; | |
5190 | |
5191 /* Remember the `header' of this file. The first tag in the file is | |
5192 the bottom of the header; the top of the file is the start. */ | |
5193 the_header = (char *)xmalloc (1 + (header_size = tags->position)); | |
5194 memcpy (the_header, the_file, header_size); | |
5195 | |
5196 while (tags) | |
5197 { | |
5198 int file_top, file_bot, limit; | |
5199 | |
5200 /* Have to include the Control-_. */ | |
5201 file_top = file_bot = tags->position; | |
5202 limit = file_top + size; | |
5203 | |
5204 /* If the rest of this file is only one node, then | |
5205 that is the entire subfile. */ | |
5206 if (!tags->next_ent) | |
5207 { | |
5208 int i = tags->position + 1; | |
5209 char last_char = the_file[i]; | |
5210 | |
5211 while (i < file_size) | |
5212 { | |
5213 if ((the_file[i] == '\037') && | |
5214 ((last_char == '\n') || | |
5215 (last_char == '\014'))) | |
5216 break; | |
5217 else | |
5218 last_char = the_file[i]; | |
5219 i++; | |
5220 } | |
5221 file_bot = i; | |
5222 tags = tags->next_ent; | |
5223 goto write_region; | |
5224 } | |
5225 | |
5226 /* Otherwise, find the largest number of nodes that can fit in | |
5227 this subfile. */ | |
5228 for (; tags; tags = tags->next_ent) | |
5229 { | |
5230 if (!tags->next_ent) | |
5231 { | |
5232 /* This entry is the last node. Search forward for the end | |
5233 of this node, and that is the end of this file. */ | |
5234 int i = tags->position + 1; | |
5235 char last_char = the_file[i]; | |
5236 | |
5237 while (i < file_size) | |
5238 { | |
5239 if ((the_file[i] == '\037') && | |
5240 ((last_char == '\n') || | |
5241 (last_char == '\014'))) | |
5242 break; | |
5243 else | |
5244 last_char = the_file[i]; | |
5245 i++; | |
5246 } | |
5247 file_bot = i; | |
5248 | |
5249 if (file_bot < limit) | |
5250 { | |
5251 tags = tags->next_ent; | |
5252 goto write_region; | |
5253 } | |
5254 else | |
5255 { | |
5256 /* Here we want to write out everything before the last | |
5257 node, and then write the last node out in a file | |
5258 by itself. */ | |
5259 file_bot = tags->position; | |
5260 goto write_region; | |
5261 } | |
5262 } | |
5263 | |
5264 if (tags->next_ent->position > limit) | |
5265 { | |
5266 if (tags->position == file_top) | |
5267 tags = tags->next_ent; | |
5268 | |
5269 file_bot = tags->position; | |
5270 | |
5271 write_region: | |
5272 { | |
5273 int fd; | |
5274 char *split_filename; | |
5275 | |
5276 split_filename = (char *) xmalloc | |
5277 (10 + strlen (root_pathname) + strlen (root_filename)); | |
5278 sprintf | |
5279 (split_filename, | |
5280 #ifdef MSDOS | |
5281 "%s%s.%d", root_pathname, root_filename, which_file); | |
5282 #else /* !MSDOS */ | |
5283 "%s%s-%d", root_pathname, root_filename, which_file); | |
5284 #endif /* !MSDOS */ | |
5285 | |
5286 fd = open | |
5287 (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); | |
5288 | |
5289 if ((fd < 0) || | |
5290 (write (fd, the_header, header_size) != header_size) || | |
5291 (write (fd, the_file + file_top, file_bot - file_top) | |
5292 != (file_bot - file_top)) || | |
5293 ((close (fd)) < 0)) | |
5294 { | |
5295 perror (split_filename); | |
5296 if (fd != -1) | |
5297 close (fd); | |
5298 exit (FATAL); | |
5299 } | |
5300 | |
5301 if (!indirect_info) | |
5302 { | |
5303 indirect_info = the_file + file_top; | |
5304 sprintf (indirect_info, "\037\nIndirect:\n"); | |
5305 indirect_info += strlen (indirect_info); | |
5306 } | |
5307 | |
5308 #ifdef MSDOS | |
5309 sprintf (indirect_info, "%s.%d: %d\n", | |
5310 #else | |
5311 sprintf (indirect_info, "%s-%d: %d\n", | |
5312 #endif | |
5313 root_filename, which_file, file_top); | |
5314 | |
5315 free (split_filename); | |
5316 indirect_info += strlen (indirect_info); | |
5317 which_file++; | |
5318 break; | |
5319 } | |
5320 } | |
5321 } | |
5322 } | |
5323 | |
5324 /* We have sucessfully created the subfiles. Now write out the | |
5325 original again. We must use `output_stream', or | |
5326 write_tag_table_indirect () won't know where to place the output. */ | |
5327 output_stream = fopen (filename, "w"); | |
5328 if (!output_stream) | |
5329 { | |
5330 perror (filename); | |
5331 exit (FATAL); | |
5332 } | |
5333 | |
5334 { | |
5335 int distance = indirect_info - the_file; | |
5336 fwrite (the_file, 1, distance, output_stream); | |
5337 | |
5338 /* Inhibit newlines. */ | |
5339 paragraph_is_open = 0; | |
5340 | |
5341 write_tag_table_indirect (); | |
5342 fclose (output_stream); | |
5343 free (the_header); | |
5344 free (the_file); | |
5345 return; | |
5346 } | |
5347 } | |
5348 } | |
5349 | |
5350 /* Some menu hacking. This is used to remember menu references while | |
5351 reading the input file. After the output file has been written, if | |
5352 validation is on, then we use the contents of NODE_REFERENCES as a | |
5353 list of nodes to validate. */ | |
5354 static char * | |
5355 reftype_type_string (enum reftype type) | |
5356 { | |
5357 switch (type) | |
5358 { | |
5359 case menu_reference: | |
5360 return ("Menu"); | |
5361 case followed_reference: | |
5362 return ("Followed-Reference"); | |
5363 default: | |
5364 return ("Internal-bad-reference-type"); | |
5365 } | |
5366 } | |
5367 | |
5368 /* Remember this node name for later validation use. */ | |
5369 static void | |
5370 remember_node_reference (char *node, int line, enum reftype type) | |
5371 { | |
5372 NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF)); | |
5373 | |
5374 temp->next = node_references; | |
5375 temp->node = strdup (node); | |
5376 temp->line_no = line; | |
5377 temp->section = current_section; | |
5378 temp->type = type; | |
5379 temp->containing_node = (current_node ? strdup (current_node) : 0); | |
5380 temp->filename = node_filename; | |
5381 | |
5382 node_references = temp; | |
5383 } | |
5384 | |
5385 static void | |
5386 validate_other_references (register NODE_REF *ref_list) | |
5387 { | |
5388 char *old_input_filename = input_filename; | |
5389 | |
5390 while (ref_list != (NODE_REF *) NULL) | |
5391 { | |
5392 input_filename = ref_list->filename; | |
5393 validate (ref_list->node, ref_list->line_no, | |
5394 reftype_type_string (ref_list->type)); | |
5395 ref_list = ref_list->next; | |
5396 } | |
5397 input_filename = old_input_filename; | |
5398 } | |
5399 | |
5400 /* Find NODE in REF_LIST. */ | |
5401 static NODE_REF * | |
5402 find_node_reference (char *node, register NODE_REF *ref_list) | |
5403 { | |
5404 while (ref_list) | |
5405 { | |
5406 if (strcmp (node, ref_list->node) == 0) | |
5407 break; | |
5408 ref_list = ref_list->next; | |
5409 } | |
5410 return (ref_list); | |
5411 } | |
5412 | |
5413 static void | |
5414 free_node_references (void) | |
5415 { | |
5416 register NODE_REF *list, *temp; | |
5417 | |
5418 list = node_references; | |
5419 | |
5420 while (list) | |
5421 { | |
5422 temp = list; | |
5423 free (list->node); | |
5424 free (list->containing_node); | |
5425 list = list->next; | |
5426 free (temp); | |
5427 } | |
5428 node_references = (NODE_REF *) NULL; | |
5429 } | |
5430 | |
5431 /* This function gets called at the start of every line while inside of | |
5432 a menu. It checks to see if the line starts with "* ", and if so, | |
5433 remembers the node reference that this menu refers to. | |
5434 input_text_offset is at the \n just before the line start. */ | |
5435 #define menu_starter "* " | |
5436 static char * | |
5437 glean_node_from_menu (int remember_reference) | |
5438 { | |
5439 int i, orig_offset = input_text_offset; | |
5440 char *nodename; | |
5441 | |
5442 if (strncmp (&input_text[input_text_offset + 1], | |
5443 menu_starter, | |
5444 strlen (menu_starter)) != 0) | |
5445 return ((char *)NULL); | |
5446 else | |
5447 input_text_offset += strlen (menu_starter) + 1; | |
5448 | |
5449 get_until_in_line (":", &nodename); | |
5450 if (curchar () == ':') | |
5451 input_text_offset++; | |
5452 canon_white (nodename); | |
5453 | |
5454 if (curchar () == ':') | |
5455 goto save_node; | |
5456 | |
5457 free (nodename); | |
5458 get_rest_of_line (&nodename); | |
5459 | |
5460 /* Special hack: If the nodename follows the menu item name, | |
5461 then we have to read the rest of the line in order to find | |
5462 out what the nodename is. But we still have to read the | |
5463 line later, in order to process any formatting commands that | |
5464 might be present. So un-count the carriage return that has just | |
5465 been counted. */ | |
5466 line_number--; | |
5467 | |
5468 isolate_nodename (nodename); | |
5469 | |
5470 save_node: | |
5471 input_text_offset = orig_offset; | |
5472 normalize_node_name (nodename); | |
5473 i = strlen (nodename); | |
5474 if (i && nodename[i - 1] == ':') | |
5475 nodename[i - 1] = '\0'; | |
5476 | |
5477 if (remember_reference) | |
5478 { | |
5479 remember_node_reference (nodename, line_number, menu_reference); | |
5480 free (nodename); | |
5481 return ((char *)NULL); | |
5482 } | |
5483 else | |
5484 return (nodename); | |
5485 } | |
5486 | |
5487 static void | |
5488 isolate_nodename (char *nodename) | |
5489 { | |
5490 register int i, c; | |
5491 int paren_seen, paren; | |
5492 | |
5493 if (!nodename) | |
5494 return; | |
5495 | |
5496 canon_white (nodename); | |
5497 paren_seen = paren = i = 0; | |
5498 | |
5499 if (*nodename == '.' || !*nodename) | |
5500 { | |
5501 *nodename = '\0'; | |
5502 return; | |
5503 } | |
5504 | |
5505 if (*nodename == '(') | |
5506 { | |
5507 paren++; | |
5508 paren_seen++; | |
5509 i++; | |
5510 } | |
5511 | |
5512 for (; (c = nodename[i]); i++) | |
5513 { | |
5514 if (paren) | |
5515 { | |
5516 if (c == '(') | |
5517 paren++; | |
5518 else if (c == ')') | |
5519 paren--; | |
5520 | |
5521 continue; | |
5522 } | |
5523 | |
5524 /* If the character following the close paren is a space, then this | |
5525 node has no more characters associated with it. */ | |
5526 if (c == '\t' || | |
5527 c == '\n' || | |
5528 c == ',' || | |
5529 ((paren_seen && nodename[i - 1] == ')') && | |
5530 (c == ' ' || c == '.')) || | |
5531 (c == '.' && | |
5532 ((!nodename[i + 1] || | |
5533 (cr_or_whitespace (nodename[i + 1])) || | |
5534 (nodename[i + 1] == ')'))))) | |
5535 break; | |
5536 } | |
5537 nodename[i] = '\0'; | |
5538 } | |
5539 | |
5540 static void | |
5541 cm_menu (void) | |
5542 { | |
5543 begin_insertion (menu); | |
5544 } | |
5545 | |
5546 | |
5547 /* **************************************************************** */ | |
5548 /* */ | |
5549 /* Cross Reference Hacking */ | |
5550 /* */ | |
5551 /* **************************************************************** */ | |
5552 | |
5553 static char * | |
5554 get_xref_token (void) | |
5555 { | |
5556 char *string; | |
5557 | |
5558 get_until_in_braces (",", &string); | |
5559 if (curchar () == ',') | |
5560 input_text_offset++; | |
5561 fix_whitespace (string); | |
5562 return (string); | |
5563 } | |
5564 | |
5565 int px_ref_flag = 0; /* Controls initial output string. */ | |
5566 | |
5567 /* Make a cross reference. */ | |
5568 static void | |
5569 cm_xref (int arg) | |
5570 { | |
5571 if (arg == START) | |
5572 { | |
5573 char *arg1, *arg2, *arg3, *arg4, *arg5; | |
5574 | |
5575 arg1 = get_xref_token (); | |
5576 arg2 = get_xref_token (); | |
5577 arg3 = get_xref_token (); | |
5578 arg4 = get_xref_token (); | |
5579 arg5 = get_xref_token (); | |
5580 | |
5581 add_word_args ("%s", px_ref_flag ? "*note " : "*Note "); | |
5582 | |
5583 if (*arg5 || *arg4) | |
5584 { | |
5585 char *node_name; | |
5586 | |
5587 if (!*arg2) | |
5588 { | |
5589 if (*arg3) | |
5590 node_name = arg3; | |
5591 else | |
5592 node_name = arg1; | |
5593 } | |
5594 else | |
5595 node_name = arg2; | |
5596 | |
5597 execute_string ("%s: (%s)%s", node_name, arg4, arg1); | |
5598 return; | |
5599 } | |
5600 else | |
5601 remember_node_reference (arg1, line_number, followed_reference); | |
5602 | |
5603 if (*arg3) | |
5604 { | |
5605 if (!*arg2) | |
5606 execute_string ("%s: %s", arg3, arg1); | |
5607 else | |
5608 execute_string ("%s: %s", arg2, arg1); | |
5609 } | |
5610 else | |
5611 { | |
5612 if (*arg2) | |
5613 execute_string ("%s: %s", arg2, arg1); | |
5614 else | |
5615 execute_string ("%s::", arg1); | |
5616 } | |
5617 | |
5618 /* Free all of the arguments found. */ | |
5619 if (arg1) free (arg1); | |
5620 if (arg2) free (arg2); | |
5621 if (arg3) free (arg3); | |
5622 if (arg4) free (arg4); | |
5623 if (arg5) free (arg5); | |
5624 } | |
5625 else | |
5626 { | |
5627 /* Check to make sure that the next non-whitespace character is either | |
5628 a period or a comma. input_text_offset is pointing at the "}" which | |
5629 ended the xref or pxref command. */ | |
5630 int temp = input_text_offset + 1; | |
5631 | |
5632 if (output_paragraph[output_paragraph_offset - 2] == ':' && | |
5633 output_paragraph[output_paragraph_offset - 1] == ':') | |
5634 return; | |
5635 while (temp < size_of_input_text) | |
5636 { | |
5637 if (cr_or_whitespace (input_text[temp])) | |
5638 temp++; | |
5639 else | |
5640 { | |
5641 if (input_text[temp] == '.' || | |
5642 input_text[temp] == ',' || | |
5643 input_text[temp] == '\t') | |
5644 return; | |
5645 else | |
5646 { | |
5647 line_error ( | |
5648 "Cross-reference must be terminated with a period or a comma"); | |
5649 return; | |
5650 } | |
5651 } | |
5652 } | |
5653 } | |
5654 } | |
5655 | |
5656 static void | |
5657 cm_pxref (int arg) | |
5658 { | |
5659 if (arg == START) | |
5660 { | |
5661 px_ref_flag++; | |
5662 cm_xref (arg); | |
5663 px_ref_flag--; | |
5664 } | |
5665 else | |
5666 add_char ('.'); | |
5667 } | |
5668 | |
5669 static void | |
5670 cm_inforef (int arg) | |
5671 { | |
5672 if (arg == START) | |
5673 { | |
5674 char *node, *pname, *file; | |
5675 | |
5676 node = get_xref_token (); | |
5677 pname = get_xref_token (); | |
5678 file = get_xref_token (); | |
5679 | |
5680 execute_string ("*note %s: (%s)%s", pname, file, node); | |
5681 } | |
5682 } | |
5683 | |
5684 /* **************************************************************** */ | |
5685 /* */ | |
5686 /* Insertion Command Stubs */ | |
5687 /* */ | |
5688 /* **************************************************************** */ | |
5689 | |
5690 static void | |
5691 cm_quotation (void) | |
5692 { | |
5693 begin_insertion (quotation); | |
5694 } | |
5695 | |
5696 static void | |
5697 cm_example (void) | |
5698 { | |
5699 begin_insertion (example); | |
5700 } | |
5701 | |
5702 static void | |
5703 cm_smallexample (void) | |
5704 { | |
5705 begin_insertion (smallexample); | |
5706 } | |
5707 | |
5708 static void | |
5709 cm_lisp (void) | |
5710 { | |
5711 begin_insertion (lisp); | |
5712 } | |
5713 | |
5714 static void | |
5715 cm_smalllisp (void) | |
5716 { | |
5717 begin_insertion (smalllisp); | |
5718 } | |
5719 | |
5720 /* @cartouche/@end cartouche draws box with rounded corners in | |
5721 TeX output. Right now, just a NOP insertion. */ | |
5722 static void | |
5723 cm_cartouche (void) | |
5724 { | |
5725 begin_insertion (cartouche); | |
5726 } | |
5727 | |
5728 static void | |
5729 cm_format (void) | |
5730 { | |
5731 begin_insertion (format); | |
5732 } | |
5733 | |
5734 static void | |
5735 cm_display (void) | |
5736 { | |
5737 begin_insertion (display); | |
5738 } | |
5739 | |
5740 static void | |
5741 cm_itemize (void) | |
5742 { | |
5743 begin_insertion (itemize); | |
5744 } | |
5745 | |
5746 static void | |
5747 cm_enumerate (void) | |
5748 { | |
5749 do_enumeration (enumerate, "1"); | |
5750 } | |
5751 | |
5752 /* Start an enumeration insertion of type TYPE. If the user supplied | |
5753 no argument on the line, then use DEFAULT_STRING as the initial string. */ | |
5754 static void | |
5755 do_enumeration (int type, char *default_string) | |
5756 { | |
5757 get_until_in_line (".", &enumeration_arg); | |
5758 canon_white (enumeration_arg); | |
5759 | |
5760 if (!*enumeration_arg) | |
5761 { | |
5762 free (enumeration_arg); | |
5763 enumeration_arg = strdup (default_string); | |
5764 } | |
5765 | |
5766 if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg)) | |
5767 { | |
5768 warning ("%s requires a letter or a digit", insertion_type_pname (type)); | |
5769 | |
5770 switch (type) | |
5771 { | |
5772 case enumerate: | |
5773 default_string = "1"; | |
5774 break; | |
5775 } | |
5776 enumeration_arg = strdup (default_string); | |
5777 } | |
5778 begin_insertion (type); | |
5779 } | |
5780 | |
5781 static void | |
5782 cm_table (void) | |
5783 { | |
5784 begin_insertion (table); | |
5785 } | |
5786 | |
5787 static void | |
5788 cm_ftable (void) | |
5789 { | |
5790 begin_insertion (ftable); | |
5791 } | |
5792 | |
5793 static void | |
5794 cm_vtable (void) | |
5795 { | |
5796 begin_insertion (vtable); | |
5797 } | |
5798 | |
5799 static void | |
5800 cm_group (void) | |
5801 { | |
5802 begin_insertion (group); | |
5803 } | |
5804 | |
5805 static void | |
5806 cm_ifinfo (void) | |
5807 { | |
5808 /* begin_insertion (ifinfo); */ | |
5809 /* BPW: @ifinfo can cross hierarchies */ | |
5810 ifinfo_count++; | |
5811 if (in_menu) | |
5812 discard_until ("\n"); | |
5813 } | |
5814 | |
5815 /* Begin an insertion where the lines are not filled or indented. */ | |
5816 static void | |
5817 cm_flushleft (void) | |
5818 { | |
5819 begin_insertion (flushleft); | |
5820 } | |
5821 | |
5822 /* Begin an insertion where the lines are not filled, and each line is | |
5823 forced to the right-hand side of the page. */ | |
5824 static void | |
5825 cm_flushright (void) | |
5826 { | |
5827 begin_insertion (flushright); | |
5828 } | |
5829 | |
5830 | |
5831 /* **************************************************************** */ | |
5832 /* */ | |
5833 /* Conditional Handling */ | |
5834 /* */ | |
5835 /* **************************************************************** */ | |
5836 | |
5837 /* A structure which contains `defined' variables. */ | |
5838 typedef struct _defines { | |
5839 struct _defines *next; | |
5840 char *name; | |
5841 char *value; | |
5842 } DEFINE; | |
5843 | |
5844 /* The linked list of `set' defines. */ | |
5845 DEFINE *defines = (DEFINE *)NULL; | |
5846 | |
5847 /* Add NAME to the list of `set' defines. */ | |
5848 static void | |
5849 set (char *name, char *value) | |
5850 { | |
5851 DEFINE *temp; | |
5852 | |
5853 for (temp = defines; temp; temp = temp->next) | |
5854 if (strcmp (name, temp->name) == 0) | |
5855 { | |
5856 free (temp->value); | |
5857 temp->value = strdup (value); | |
5858 return; | |
5859 } | |
5860 | |
5861 temp = (DEFINE *)xmalloc (sizeof (DEFINE)); | |
5862 temp->next = defines; | |
5863 temp->name = strdup (name); | |
5864 temp->value = strdup (value); | |
5865 defines = temp; | |
5866 } | |
5867 | |
5868 /* Remove NAME from the list of `set' defines. */ | |
5869 static void | |
5870 clear (char *name) | |
5871 { | |
5872 register DEFINE *temp, *last; | |
5873 | |
5874 last = (DEFINE *)NULL; | |
5875 temp = defines; | |
5876 | |
5877 while (temp) | |
5878 { | |
5879 if (strcmp (temp->name, name) == 0) | |
5880 { | |
5881 if (last) | |
5882 last->next = temp->next; | |
5883 else | |
5884 defines = temp->next; | |
5885 | |
5886 free (temp->name); | |
5887 free (temp->value); | |
5888 free (temp); | |
5889 break; | |
5890 } | |
5891 last = temp; | |
5892 temp = temp->next; | |
5893 } | |
5894 } | |
5895 | |
5896 /* Return the value of NAME. The return value is NULL if NAME is unset. */ | |
5897 static char * | |
5898 set_p (char *name) | |
5899 { | |
5900 register DEFINE *temp; | |
5901 | |
5902 for (temp = defines; temp; temp = temp->next) | |
5903 if (strcmp (temp->name, name) == 0) | |
5904 return (temp->value); | |
5905 | |
5906 return ((char *)NULL); | |
5907 } | |
5908 | |
5909 /* Conditionally parse based on the current command name. */ | |
5910 static void | |
5911 command_name_condition (void) | |
5912 { | |
5913 char *discarder; | |
5914 | |
5915 discarder = (char *)xmalloc (8 + strlen (command)); | |
5916 | |
5917 sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command); | |
5918 discard_until (discarder); | |
5919 discard_until ("\n"); | |
5920 | |
5921 free (discarder); | |
5922 } | |
5923 | |
5924 /* Create a variable whose name appears as the first word on this line. */ | |
5925 static void | |
5926 cm_set (void) | |
5927 { | |
5928 handle_variable (SET); | |
5929 } | |
5930 | |
5931 /* Remove a variable whose name appears as the first word on this line. */ | |
5932 static void | |
5933 cm_clear (void) | |
5934 { | |
5935 handle_variable (CLEAR); | |
5936 } | |
5937 | |
5938 static void | |
5939 cm_ifset (void) | |
5940 { | |
5941 handle_variable (IFSET); | |
5942 } | |
5943 | |
5944 static void | |
5945 cm_ifclear (void) | |
5946 { | |
5947 handle_variable (IFCLEAR); | |
5948 } | |
5949 | |
5950 /* This command takes braces, but we parse the contents specially, so we | |
5951 don't use the standard brace popping code. | |
5952 | |
5953 The syntax @ifeq{arg1, arg2, texinfo commands} performs texinfo commands | |
5954 if ARG1 and ARG2 caselessly string compare to the same string, otherwise, | |
5955 it produces no output. */ | |
5956 static void | |
5957 cm_ifeq (void) | |
5958 { | |
5959 char **arglist; | |
5960 | |
5961 arglist = get_brace_args (0); | |
5962 | |
5963 if (arglist) | |
5964 { | |
5965 if (array_len (arglist) > 1) | |
5966 { | |
5967 if ((strcasecmp (arglist[0], arglist[1]) == 0) && | |
5968 (arglist[2] != (char *)NULL)) | |
5969 execute_string ("%s\n", arglist[2]); | |
5970 } | |
5971 | |
5972 free_array (arglist); | |
5973 } | |
5974 } | |
5975 | |
5976 static void | |
5977 cm_value (int arg, int start_pos, int end_pos) | |
5978 { | |
5979 if (arg == END) | |
5980 { | |
5981 char *name, *value; | |
5982 name = (char *)&output_paragraph[start_pos]; | |
5983 output_paragraph[end_pos] = '\0'; | |
5984 name = strdup (name); | |
5985 value = set_p (name); | |
5986 output_column -= end_pos - start_pos; | |
5987 output_paragraph_offset = start_pos; | |
5988 | |
5989 if (value) | |
5990 execute_string ("%s", value); | |
5991 else | |
5992 add_word_args ("{No Value For \"%s\"}", name); | |
5993 | |
5994 free (name); | |
5995 } | |
5996 } | |
5997 | |
5998 /* Set, clear, or conditionalize based on ACTION. */ | |
5999 static void | |
6000 handle_variable (int action) | |
6001 { | |
6002 char *name; | |
6003 | |
6004 get_rest_of_line (&name); | |
6005 backup_input_pointer (); | |
6006 canon_white (name); | |
6007 handle_variable_internal (action, name); | |
6008 free (name); | |
6009 } | |
6010 | |
6011 static void | |
6012 handle_variable_internal (int action, char *name) | |
6013 { | |
6014 char *temp; | |
6015 int delimiter, additional_text_present = 0; | |
6016 | |
6017 /* Only the first word of NAME is a valid tag. */ | |
6018 temp = name; | |
6019 delimiter = 0; | |
6020 while (*temp && (delimiter || !whitespace (*temp))) | |
6021 { | |
6022 /* #if defined (SET_WITH_EQUAL) */ | |
6023 if (*temp == '"' || *temp == '\'') | |
6024 { | |
6025 if (*temp == delimiter) | |
6026 delimiter = 0; | |
6027 else | |
6028 delimiter = *temp; | |
6029 } | |
6030 /* #endif SET_WITH_EQUAL */ | |
6031 temp++; | |
6032 } | |
6033 | |
6034 if (*temp) | |
6035 additional_text_present++; | |
6036 | |
6037 *temp = '\0'; | |
6038 | |
6039 if (!*name) | |
6040 line_error ("%c%s requires a name", COMMAND_PREFIX, command); | |
6041 else | |
6042 { | |
6043 switch (action) | |
6044 { | |
6045 case SET: | |
6046 { | |
6047 char *value; | |
6048 | |
6049 #if defined (SET_WITH_EQUAL) | |
6050 /* Allow a value to be saved along with a variable. The value is | |
6051 the text following an `=' sign in NAME, if any is present. */ | |
6052 | |
6053 for (value = name; *value && *value != '='; value++); | |
6054 | |
6055 if (*value) | |
6056 *value++ = '\0'; | |
6057 | |
6058 if (*value == '"' || *value == '\'') | |
6059 { | |
6060 value++; | |
6061 value[strlen (value) - 1] = '\0'; | |
6062 } | |
6063 | |
6064 #else /* !SET_WITH_EQUAL */ | |
6065 /* The VALUE of NAME is the remainder of the line sans | |
6066 whitespace. */ | |
6067 if (additional_text_present) | |
6068 { | |
6069 value = temp + 1; | |
6070 canon_white (value); | |
6071 } | |
6072 else | |
6073 value = ""; | |
6074 #endif /* !SET_WITH_VALUE */ | |
6075 | |
6076 set (name, value); | |
6077 } | |
6078 break; | |
6079 | |
6080 case CLEAR: | |
6081 clear (name); | |
6082 break; | |
6083 | |
6084 case IFSET: | |
6085 case IFCLEAR: | |
6086 /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set, | |
6087 read lines from the the file until we reach a matching | |
6088 "@end CONDITION". This means that we only take note of | |
6089 "@ifset/clear" and "@end" commands. */ | |
6090 { | |
6091 char condition[8]; | |
6092 int condition_len; | |
6093 | |
6094 if (action == IFSET) | |
6095 strcpy (condition, "ifset"); | |
6096 else | |
6097 strcpy (condition, "ifclear"); | |
6098 | |
6099 condition_len = strlen (condition); | |
6100 | |
6101 if ((action == IFSET && !set_p (name)) || | |
6102 (action == IFCLEAR && set_p (name))) | |
6103 { | |
6104 int level = 0, done = 0; | |
6105 | |
6106 while (!done) | |
6107 { | |
6108 char *freeable_line, *line; | |
6109 | |
6110 get_rest_of_line (&freeable_line); | |
6111 | |
6112 for (line = freeable_line; whitespace (*line); line++); | |
6113 | |
6114 if (*line == COMMAND_PREFIX && | |
6115 (strncmp (line + 1, condition, condition_len) == 0)) | |
6116 level++; | |
6117 else if (strncmp (line, "@end", 4) == 0) | |
6118 { | |
6119 char *cname = line + 4; | |
6120 char *temp2; | |
6121 | |
6122 while (*cname && whitespace (*cname)) | |
6123 cname++; | |
6124 temp2 = cname; | |
6125 | |
6126 while (*temp2 && !whitespace (*temp2)) | |
6127 temp2++; | |
6128 *temp2 = '\0'; | |
6129 | |
6130 if (strcmp (cname, condition) == 0) | |
6131 { | |
6132 if (!level) | |
6133 { | |
6134 done = 1; | |
6135 } | |
6136 else | |
6137 level--; | |
6138 } | |
6139 } | |
6140 free (freeable_line); | |
6141 } | |
6142 /* We found the end of a false @ifset/ifclear. If we are | |
6143 in a menu, back up over the newline that ends the ifset, | |
6144 since that newline may also begin the next menu entry. */ | |
6145 break; | |
6146 } | |
6147 else | |
6148 { | |
6149 if (action == IFSET) | |
6150 begin_insertion (ifset); | |
6151 else | |
6152 begin_insertion (ifclear); | |
6153 } | |
6154 } | |
6155 break; | |
6156 } | |
6157 } | |
6158 } | |
6159 | |
6160 | |
6161 /* **************************************************************** */ | |
6162 /* */ | |
6163 /* Execution of Random Text not in file */ | |
6164 /* */ | |
6165 /* **************************************************************** */ | |
6166 | |
6167 typedef struct { | |
6168 char *string; /* The string buffer. */ | |
6169 int size; /* The size of the buffer. */ | |
6170 int in_use; /* Non-zero means string currently in use. */ | |
6171 } EXECUTION_STRING; | |
6172 | |
6173 static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL; | |
6174 static int execution_strings_index = 0; | |
6175 static int execution_strings_slots = 0; | |
6176 | |
6177 static EXECUTION_STRING * | |
6178 get_execution_string (int initial_size) | |
6179 { | |
6180 register int i = 0; | |
6181 EXECUTION_STRING *es = (EXECUTION_STRING *)NULL; | |
6182 | |
6183 if (execution_strings) | |
6184 { | |
6185 for (i = 0; i < execution_strings_index; i++) | |
6186 if (execution_strings[i] && (execution_strings[i]->in_use == 0)) | |
6187 { | |
6188 es = execution_strings[i]; | |
6189 break; | |
6190 } | |
6191 } | |
6192 | |
6193 if (!es) | |
6194 { | |
6195 if (execution_strings_index + 1 >= execution_strings_slots) | |
6196 { | |
6197 execution_strings = (EXECUTION_STRING **)xrealloc | |
6198 (execution_strings, | |
6199 (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *)); | |
6200 for (; i < execution_strings_slots; i++) | |
6201 execution_strings[i] = (EXECUTION_STRING *)NULL; | |
6202 } | |
6203 | |
6204 execution_strings[execution_strings_index] = | |
6205 (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING)); | |
6206 es = execution_strings[execution_strings_index]; | |
6207 execution_strings_index++; | |
6208 | |
6209 es->size = 0; | |
6210 es->string = (char *)NULL; | |
6211 es->in_use = 0; | |
6212 } | |
6213 | |
6214 if (initial_size > es->size) | |
6215 { | |
6216 es->string = (char *) xrealloc (es->string, initial_size); | |
6217 es->size = initial_size; | |
6218 } | |
6219 return (es); | |
6220 } | |
6221 | |
6222 /* Execute the string produced by formatting the ARGs with FORMAT. This | |
6223 is like submitting a new file with @include. */ | |
6224 | |
6225 static void | |
6226 execute_string (char *formatte, ...) | |
6227 { | |
6228 va_list ap; | |
6229 EXECUTION_STRING *es; | |
6230 char *temp_string; | |
6231 | |
6232 es = get_execution_string (4000); | |
6233 temp_string = es->string; | |
6234 es->in_use = 1; | |
6235 | |
6236 va_start (ap, formatte); | |
6237 vsprintf (temp_string, formatte, ap); | |
6238 va_end (ap); | |
6239 | |
6240 pushfile (); | |
6241 input_text_offset = 0; | |
6242 input_text = temp_string; | |
6243 input_filename = strdup (input_filename); | |
6244 size_of_input_text = strlen (temp_string); | |
6245 | |
6246 executing_string++; | |
6247 reader_loop (); | |
6248 free (input_filename); | |
6249 | |
6250 popfile (); | |
6251 executing_string--; | |
6252 es->in_use = 0; | |
6253 } | |
6254 | |
6255 /* **************************************************************** */ | |
6256 /* */ | |
6257 /* @itemx, @item */ | |
6258 /* */ | |
6259 /* **************************************************************** */ | |
6260 | |
6261 static int itemx_flag = 0; | |
6262 | |
6263 static void | |
6264 cm_itemx (void) | |
6265 { | |
6266 itemx_flag++; | |
6267 cm_item (); | |
6268 itemx_flag--; | |
6269 } | |
6270 | |
6271 static void | |
6272 cm_item (void) | |
6273 { | |
6274 char *rest_of_line, *item_func; | |
6275 | |
6276 /* Can only hack "@item" while inside of an insertion. */ | |
6277 if (insertion_level) | |
6278 { | |
6279 INSERTION_ELT *stack = insertion_stack; | |
6280 int original_input_text_offset; | |
6281 | |
6282 skip_whitespace (); | |
6283 original_input_text_offset = input_text_offset; | |
6284 | |
6285 get_rest_of_line (&rest_of_line); | |
6286 canon_white (rest_of_line); | |
6287 item_func = current_item_function (); | |
6288 | |
6289 /* Okay, do the right thing depending on which insertion function | |
6290 is active. */ | |
6291 | |
6292 switch_top: | |
6293 switch (stack->insertion) | |
6294 { | |
6295 case ifinfo: | |
6296 case ifset: | |
6297 case ifclear: | |
6298 case cartouche: | |
6299 stack = stack->next; | |
6300 if (!stack) | |
6301 goto no_insertion; | |
6302 else | |
6303 goto switch_top; | |
6304 break; | |
6305 | |
6306 case menu: | |
6307 case quotation: | |
6308 case example: | |
6309 case smallexample: | |
6310 case lisp: | |
6311 case format: | |
6312 case display: | |
6313 case group: | |
6314 line_error ("The `%c%s' command is meaningless within a `@%s' block", | |
6315 COMMAND_PREFIX, command, | |
6316 insertion_type_pname (current_insertion_type ())); | |
6317 break; | |
6318 | |
6319 case itemize: | |
6320 case enumerate: | |
6321 if (itemx_flag) | |
6322 { | |
6323 line_error ("%citemx is not meaningful inside of a `%s' block", | |
6324 COMMAND_PREFIX, | |
6325 insertion_type_pname (current_insertion_type ())); | |
6326 } | |
6327 else | |
6328 { | |
6329 start_paragraph (); | |
6330 kill_self_indent (-1); | |
6331 filling_enabled = indented_fill = 1; | |
6332 | |
6333 if (current_insertion_type () == itemize) | |
6334 { | |
6335 indent (output_column = current_indent - 2); | |
6336 | |
6337 /* I need some way to determine whether this command | |
6338 takes braces or not. I believe the user can type | |
6339 either "@bullet" or "@bullet{}". Of course, they | |
6340 can also type "o" or "#" or whatever else they want. */ | |
6341 if (item_func && *item_func) | |
6342 { | |
6343 if (*item_func == COMMAND_PREFIX) | |
6344 if (item_func[strlen (item_func) - 1] != '}') | |
6345 execute_string ("%s{}", item_func); | |
6346 else | |
6347 execute_string ("%s", item_func); | |
6348 else | |
6349 execute_string ("%s", item_func); | |
6350 } | |
6351 insert (' '); | |
6352 output_column++; | |
6353 } | |
6354 else | |
6355 enumerate_item (); | |
6356 | |
6357 /* Special hack. This makes close paragraph ignore you until | |
6358 the start_paragraph () function has been called. */ | |
6359 must_start_paragraph = 1; | |
6360 | |
6361 /* Ultra special hack. It appears that some people incorrectly | |
6362 place text directly after the @item, instead of on a new line | |
6363 by itself. This happens to work in TeX, so I make it work | |
6364 here. */ | |
6365 if (*rest_of_line) | |
6366 { | |
6367 line_number--; | |
6368 input_text_offset = original_input_text_offset; | |
6369 } | |
6370 } | |
6371 break; | |
6372 | |
6373 case table: | |
6374 case ftable: | |
6375 case vtable: | |
6376 { | |
6377 /* Get rid of extra characters. */ | |
6378 kill_self_indent (-1); | |
6379 | |
6380 /* close_paragraph () almost does what we want. The problem | |
6381 is when paragraph_is_open, and last_char_was_newline, and | |
6382 the last newline has been turned into a space, because | |
6383 filling_enabled. I handle it here. */ | |
6384 if (last_char_was_newline && filling_enabled && paragraph_is_open) | |
6385 insert ('\n'); | |
6386 close_paragraph (); | |
6387 | |
6388 #if defined (INDENT_PARAGRAPHS_IN_TABLE) | |
6389 /* Indent on a new line, but back up one indentation level. */ | |
6390 { | |
6391 int t; | |
6392 | |
6393 t = inhibit_paragraph_indentation; | |
6394 inhibit_paragraph_indentation = 1; | |
6395 /* At this point, inserting any non-whitespace character will | |
6396 force the existing indentation to be output. */ | |
6397 add_char ('i'); | |
6398 inhibit_paragraph_indentation = t; | |
6399 } | |
6400 #else /* !INDENT_PARAGRAPHS_IN_TABLE */ | |
6401 add_char ('i'); | |
6402 #endif /* !INDENT_PARAGRAPHS_IN_TABLE */ | |
6403 | |
6404 output_paragraph_offset--; | |
6405 kill_self_indent (default_indentation_increment + 1); | |
6406 | |
6407 /* Add item's argument to the line. */ | |
6408 filling_enabled = 0; | |
6409 if (item_func && *item_func) | |
6410 execute_string ("%s{%s}", item_func, rest_of_line); | |
6411 else | |
6412 execute_string ("%s", rest_of_line); | |
6413 | |
6414 if (current_insertion_type () == ftable) | |
6415 execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line); | |
6416 | |
6417 if (current_insertion_type () == vtable) | |
6418 execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line); | |
6419 | |
6420 /* Start a new line, and let start_paragraph () | |
6421 do the indenting of it for you. */ | |
6422 close_single_paragraph (); | |
6423 indented_fill = filling_enabled = 1; | |
6424 } | |
6425 | |
6426 default: | |
6427 break; | |
6428 } | |
6429 free (rest_of_line); | |
6430 } | |
6431 else | |
6432 { | |
6433 no_insertion: | |
6434 line_error ("%c%s found outside of an insertion block", | |
6435 COMMAND_PREFIX, command); | |
6436 } | |
6437 } | |
6438 | |
6439 | |
6440 /* **************************************************************** */ | |
6441 /* */ | |
6442 /* Defun and Friends */ | |
6443 /* */ | |
6444 /* **************************************************************** */ | |
6445 | |
6446 #define DEFUN_SELF_DELIMITING(c) \ | |
6447 (((c) == '(') \ | |
6448 || ((c) == ')') \ | |
6449 || ((c) == '[') \ | |
6450 || ((c) == ']')) | |
6451 | |
6452 struct token_accumulator | |
6453 { | |
6454 unsigned int length; | |
6455 unsigned int index; | |
6456 char **tokens; | |
6457 }; | |
6458 | |
6459 static void | |
6460 initialize_token_accumulator (struct token_accumulator *accumulator) | |
6461 { | |
6462 (accumulator->length) = 0; | |
6463 (accumulator->index) = 0; | |
6464 (accumulator->tokens) = NULL; | |
6465 } | |
6466 | |
6467 static void | |
6468 accumulate_token (struct token_accumulator *accumulator, char *token) | |
6469 { | |
6470 if ((accumulator->index) >= (accumulator->length)) | |
6471 { | |
6472 (accumulator->length) += 10; | |
6473 (accumulator->tokens) = (char **) xrealloc | |
6474 (accumulator->tokens, (accumulator->length * sizeof (char *))); | |
6475 } | |
6476 accumulator->tokens[accumulator->index] = token; | |
6477 accumulator->index += 1; | |
6478 } | |
6479 | |
6480 static char * | |
6481 copy_substring (char *start, char *end) | |
6482 { | |
6483 char *result, *scan, *scan_result; | |
6484 | |
6485 result = (char *) xmalloc ((end - start) + 1); | |
6486 scan_result = result; | |
6487 scan = start; | |
6488 | |
6489 while (scan < end) | |
6490 *scan_result++ = *scan++; | |
6491 | |
6492 *scan_result = '\0'; | |
6493 return (result); | |
6494 } | |
6495 | |
6496 /* Given `string' pointing at an open brace, skip forward and return a | |
6497 pointer to just past the matching close brace. */ | |
6498 static int | |
6499 scan_group_in_string (char **string_pointer) | |
6500 { | |
6501 register int c; | |
6502 register char *scan_string; | |
6503 register unsigned int level = 1; | |
6504 | |
6505 scan_string = (*string_pointer) + 1; | |
6506 | |
6507 while (1) | |
6508 { | |
6509 if (level == 0) | |
6510 { | |
6511 (*string_pointer) = scan_string; | |
6512 return (1); | |
6513 } | |
6514 c = (*scan_string++); | |
6515 if (c == '\0') | |
6516 { | |
6517 /* Tweak line_number to compensate for fact that | |
6518 we gobbled the whole line before coming here. */ | |
6519 line_number -= 1; | |
6520 line_error ("Missing `}' in %cdef arg", COMMAND_PREFIX); | |
6521 line_number += 1; | |
6522 (*string_pointer) = (scan_string - 1); | |
6523 return (0); | |
6524 } | |
6525 if (c == '{') | |
6526 level += 1; | |
6527 if (c == '}') | |
6528 level -= 1; | |
6529 } | |
6530 } | |
6531 | |
6532 /* Return a list of tokens from the contents of `string'. | |
6533 Commands and brace-delimited groups count as single tokens. | |
6534 Contiguous whitespace characters are converted to a token | |
6535 consisting of a single space. */ | |
6536 static char ** | |
6537 args_from_string (char *string) | |
6538 { | |
6539 struct token_accumulator accumulator; | |
6540 register char *scan_string = string; | |
6541 char *token_start, *token_end; | |
6542 | |
6543 initialize_token_accumulator (&accumulator); | |
6544 | |
6545 while ((*scan_string) != '\0') | |
6546 { | |
6547 /* Replace arbitrary whitespace by a single space. */ | |
6548 if (whitespace (*scan_string)) | |
6549 { | |
6550 scan_string += 1; | |
6551 while (whitespace (*scan_string)) | |
6552 scan_string += 1; | |
6553 accumulate_token ((&accumulator), (strdup (" "))); | |
6554 continue; | |
6555 } | |
6556 | |
6557 /* Commands count as single tokens. */ | |
6558 if ((*scan_string) == COMMAND_PREFIX) | |
6559 { | |
6560 token_start = scan_string; | |
6561 scan_string += 1; | |
6562 if (self_delimiting (*scan_string)) | |
6563 scan_string += 1; | |
6564 else | |
6565 { | |
6566 register int c; | |
6567 while (1) | |
6568 { | |
6569 c = *scan_string++; | |
6570 | |
6571 if ((c == '\0') || (c == '{') || (whitespace (c))) | |
6572 { | |
6573 scan_string -= 1; | |
6574 break; | |
6575 } | |
6576 } | |
6577 | |
6578 if (*scan_string == '{') | |
6579 { | |
6580 char *s = scan_string; | |
6581 (void) scan_group_in_string (&s); | |
6582 scan_string = s; | |
6583 } | |
6584 } | |
6585 token_end = scan_string; | |
6586 } | |
6587 | |
6588 /* Parentheses and brackets are self-delimiting. */ | |
6589 else if (DEFUN_SELF_DELIMITING (*scan_string)) | |
6590 { | |
6591 token_start = scan_string; | |
6592 scan_string += 1; | |
6593 token_end = scan_string; | |
6594 } | |
6595 | |
6596 /* Open brace introduces a group that is a single token. */ | |
6597 else if (*scan_string == '{') | |
6598 { | |
6599 char *s = scan_string; | |
6600 int balanced = scan_group_in_string (&s); | |
6601 | |
6602 token_start = scan_string + 1; | |
6603 scan_string = s; | |
6604 token_end = balanced ? (scan_string - 1) : scan_string; | |
6605 } | |
6606 | |
6607 /* Otherwise a token is delimited by whitespace, parentheses, | |
6608 brackets, or braces. A token is also ended by a command. */ | |
6609 else | |
6610 { | |
6611 token_start = scan_string; | |
6612 | |
6613 while (1) | |
6614 { | |
6615 register int c; | |
6616 | |
6617 c = *scan_string++; | |
6618 | |
6619 if (!c || | |
6620 (whitespace (c) || DEFUN_SELF_DELIMITING (c) || | |
6621 c == '{' || c == '}')) | |
6622 { | |
6623 scan_string--; | |
6624 break; | |
6625 } | |
6626 | |
6627 /* If we encounter a command imbedded within a token, | |
6628 then end the token. */ | |
6629 if (c == COMMAND_PREFIX) | |
6630 { | |
6631 scan_string--; | |
6632 break; | |
6633 } | |
6634 } | |
6635 token_end = scan_string; | |
6636 } | |
6637 | |
6638 accumulate_token | |
6639 (&accumulator, copy_substring (token_start, token_end)); | |
6640 } | |
6641 accumulate_token (&accumulator, NULL); | |
6642 return (accumulator.tokens); | |
6643 } | |
6644 | |
6645 static void | |
6646 process_defun_args (char **defun_args, int auto_var_p) | |
6647 { | |
6648 int pending_space = 0; | |
6649 | |
6650 while (1) | |
6651 { | |
6652 char *defun_arg = *defun_args++; | |
6653 | |
6654 if (defun_arg == NULL) | |
6655 break; | |
6656 | |
6657 if (defun_arg[0] == ' ') | |
6658 { | |
6659 pending_space = 1; | |
6660 continue; | |
6661 } | |
6662 | |
6663 if (pending_space) | |
6664 { | |
6665 add_char (' '); | |
6666 pending_space = 0; | |
6667 } | |
6668 | |
6669 if (DEFUN_SELF_DELIMITING (defun_arg[0])) | |
6670 add_char (defun_arg[0]); | |
6671 else if (defun_arg[0] == '&') | |
6672 add_word (defun_arg); | |
6673 else if (defun_arg[0] == COMMAND_PREFIX) | |
6674 execute_string ("%s", defun_arg); | |
6675 else if (auto_var_p) | |
6676 execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg); | |
6677 else | |
6678 add_word (defun_arg); | |
6679 } | |
6680 } | |
6681 | |
6682 static char * | |
6683 next_nonwhite_defun_arg (char ***arg_pointer) | |
6684 { | |
6685 char **scan = (*arg_pointer); | |
6686 char *arg = (*scan++); | |
6687 | |
6688 if ((arg != 0) && (*arg == ' ')) | |
6689 arg = *scan++; | |
6690 | |
6691 if (arg == 0) | |
6692 scan -= 1; | |
6693 | |
6694 *arg_pointer = scan; | |
6695 | |
6696 return ((arg == 0) ? "" : arg); | |
6697 } | |
6698 | |
6699 /* Make the defun type insertion. | |
6700 TYPE says which insertion this is. | |
6701 X_P says not to start a new insertion if non-zero. */ | |
6702 static void | |
6703 defun_internal (enum insertion_type type, int x_p) | |
6704 { | |
6705 enum insertion_type base_type; | |
6706 char **defun_args, **scan_args; | |
6707 char *category, *defined_name, *type_name, *type_name2; | |
6708 | |
6709 { | |
6710 char *line; | |
6711 get_rest_of_line (&line); | |
6712 defun_args = (args_from_string (line)); | |
6713 free (line); | |
6714 } | |
6715 | |
6716 scan_args = defun_args; | |
6717 | |
6718 switch (type) | |
6719 { | |
6720 case defun: | |
6721 category = "Function"; | |
6722 base_type = deffn; | |
6723 break; | |
6724 case defmac: | |
6725 category = "Macro"; | |
6726 base_type = deffn; | |
6727 break; | |
6728 case defspec: | |
6729 category = "Special Form"; | |
6730 base_type = deffn; | |
6731 break; | |
6732 case defvar: | |
6733 category = "Variable"; | |
6734 base_type = defvr; | |
6735 break; | |
6736 case defopt: | |
6737 category = "User Option"; | |
6738 base_type = defvr; | |
6739 break; | |
6740 case deftypefun: | |
6741 category = "Function"; | |
6742 base_type = deftypefn; | |
6743 break; | |
6744 case deftypevar: | |
6745 category = "Variable"; | |
6746 base_type = deftypevr; | |
6747 break; | |
6748 case defivar: | |
6749 category = "Instance Variable"; | |
6750 base_type = defcv; | |
6751 break; | |
6752 case defmethod: | |
6753 category = "Method"; | |
6754 base_type = defop; | |
6755 break; | |
6756 case deftypemethod: | |
6757 category = "Method"; | |
6758 base_type = deftypemethod; | |
6759 break; | |
6760 default: | |
6761 category = next_nonwhite_defun_arg (&scan_args); | |
6762 base_type = type; | |
6763 break; | |
6764 } | |
6765 | |
6766 if ((base_type == deftypefn) | |
6767 || (base_type == deftypevr) | |
6768 || (base_type == defcv) | |
6769 || (base_type == defop) | |
6770 || (base_type == deftypemethod)) | |
6771 type_name = next_nonwhite_defun_arg (&scan_args); | |
6772 | |
6773 if (base_type == deftypemethod) | |
6774 type_name2 = next_nonwhite_defun_arg (&scan_args); | |
6775 | |
6776 defined_name = next_nonwhite_defun_arg (&scan_args); | |
6777 | |
6778 /* This hack exists solely for the purposes of formatting the texinfo | |
6779 manual. I couldn't think of a better way. The token might be | |
6780 a simple @@ followed immediately by more text. If this is the case, | |
6781 then the next defun arg is part of this one, and we should concatenate | |
6782 them. */ | |
6783 if (*scan_args && **scan_args && !whitespace (**scan_args) && | |
6784 (strcmp (defined_name, "@@") == 0)) | |
6785 { | |
6786 char *tem = (char *)xmalloc (3 + strlen (scan_args[0])); | |
6787 | |
6788 sprintf (tem, "@@%s", scan_args[0]); | |
6789 | |
6790 free (scan_args[0]); | |
6791 scan_args[0] = tem; | |
6792 scan_args++; | |
6793 defined_name = tem; | |
6794 } | |
6795 | |
6796 if (!x_p) | |
6797 begin_insertion (type); | |
6798 | |
6799 /* Write the definition header line. | |
6800 This should start at the normal indentation. */ | |
6801 current_indent -= default_indentation_increment; | |
6802 start_paragraph (); | |
6803 | |
6804 switch (base_type) | |
6805 { | |
6806 case deffn: | |
6807 case defvr: | |
6808 case deftp: | |
6809 execute_string (" -- %s: %s", category, defined_name); | |
6810 break; | |
6811 case deftypefn: | |
6812 case deftypevr: | |
6813 execute_string (" -- %s: %s %s", category, type_name, defined_name); | |
6814 break; | |
6815 case defcv: | |
6816 execute_string (" -- %s of %s: %s", category, type_name, defined_name); | |
6817 break; | |
6818 case defop: | |
6819 execute_string (" -- %s on %s: %s", category, type_name, defined_name); | |
6820 break; | |
6821 case deftypemethod: | |
6822 execute_string (" -- %s on %s: %s %s", category, type_name, type_name2, | |
6823 defined_name); | |
6824 break; | |
6825 default: | |
6826 break; | |
6827 } | |
6828 current_indent += default_indentation_increment; | |
6829 | |
6830 /* Now process the function arguments, if any. | |
6831 If these carry onto the next line, they should be indented by two | |
6832 increments to distinguish them from the body of the definition, | |
6833 which is indented by one increment. */ | |
6834 current_indent += default_indentation_increment; | |
6835 | |
6836 switch (base_type) | |
6837 { | |
6838 case deffn: | |
6839 case defop: | |
6840 process_defun_args (scan_args, 1); | |
6841 break; | |
6842 case deftp: | |
6843 case deftypefn: | |
6844 case deftypemethod: | |
6845 process_defun_args (scan_args, 0); | |
6846 break; | |
6847 default: | |
6848 break; | |
6849 } | |
6850 current_indent -= default_indentation_increment; | |
6851 close_single_paragraph (); | |
6852 | |
6853 /* Make an entry in the appropriate index. */ | |
6854 switch (base_type) | |
6855 { | |
6856 case deffn: | |
6857 case deftypefn: | |
6858 execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name); | |
6859 break; | |
6860 case defvr: | |
6861 case deftypevr: | |
6862 case defcv: | |
6863 execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name); | |
6864 break; | |
6865 case defop: | |
6866 case deftypemethod: | |
6867 execute_string ("%cfindex %s on %s\n", | |
6868 COMMAND_PREFIX, defined_name, type_name); | |
6869 break; | |
6870 case deftp: | |
6871 execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name); | |
6872 break; | |
6873 default: | |
6874 break; | |
6875 } | |
6876 | |
6877 /* Deallocate the token list. */ | |
6878 scan_args = defun_args; | |
6879 while (1) | |
6880 { | |
6881 char * arg = (*scan_args++); | |
6882 if (arg == NULL) | |
6883 break; | |
6884 free (arg); | |
6885 } | |
6886 free (defun_args); | |
6887 } | |
6888 | |
6889 /* Add an entry for a function, macro, special form, variable, or option. | |
6890 If the name of the calling command ends in `x', then this is an extra | |
6891 entry included in the body of an insertion of the same type. */ | |
6892 static void | |
6893 cm_defun (void) | |
6894 { | |
6895 int x_p; | |
6896 enum insertion_type type; | |
6897 char *temp = strdup (command); | |
6898 | |
6899 x_p = (command[strlen (command) - 1] == 'x'); | |
6900 | |
6901 if (x_p) | |
6902 temp[strlen (temp) - 1] = '\0'; | |
6903 | |
6904 type = find_type_from_name (temp); | |
6905 free (temp); | |
6906 | |
6907 /* If we are adding to an already existing insertion, then make sure | |
6908 that we are already in an insertion of type TYPE. */ | |
6909 if (x_p && | |
6910 (!insertion_level || insertion_stack->insertion != type)) | |
6911 { | |
6912 line_error ("Must be in a `%s' insertion in order to use `%s'x", | |
6913 command, command); | |
6914 discard_until ("\n"); | |
6915 return; | |
6916 } | |
6917 | |
6918 defun_internal (type, x_p); | |
6919 } | |
6920 | |
6921 /* End existing insertion block. */ | |
6922 static void | |
6923 cm_end (void) | |
6924 { | |
6925 char *temp; | |
6926 enum insertion_type type; | |
6927 | |
6928 get_rest_of_line (&temp); | |
6929 canon_white (temp); | |
6930 | |
6931 if (strlen (temp) == 0) | |
6932 line_error ("`%c%s' needs something after it", COMMAND_PREFIX, command); | |
6933 | |
6934 type = find_type_from_name (temp); | |
6935 | |
6936 if (type == bad_type) | |
6937 { | |
6938 line_error ("Bad argument to `%s', `%s', using `%s'", | |
6939 command, temp, insertion_type_pname (current_insertion_type ())); | |
6940 } | |
6941 | |
6942 /* see comment under cm_ifinfo() */ | |
6943 if (type == ifinfo) | |
6944 { | |
6945 if (!ifinfo_count) | |
6946 line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command); | |
6947 else | |
6948 ifinfo_count--; | |
6949 } | |
6950 else | |
6951 { | |
6952 if (!insertion_level) | |
6953 line_error ("Unmatched `%c%s'", COMMAND_PREFIX, command); | |
6954 else | |
6955 end_insertion (type); | |
6956 } | |
6957 | |
6958 free (temp); | |
6959 } | |
6960 | |
6961 | |
6962 /* **************************************************************** */ | |
6963 /* */ | |
6964 /* Other Random Commands */ | |
6965 /* */ | |
6966 /* **************************************************************** */ | |
6967 | |
6968 /* This says to inhibit the indentation of the next paragraph, but | |
6969 not of following paragraphs. */ | |
6970 static void | |
6971 cm_noindent (void) | |
6972 { | |
6973 if (!inhibit_paragraph_indentation) | |
6974 inhibit_paragraph_indentation = -1; | |
6975 } | |
6976 | |
6977 /* I don't know exactly what to do with this. Should I allow | |
6978 someone to switch filenames in the middle of output? Since the | |
6979 file could be partially written, this doesn't seem to make sense. | |
6980 Another option: ignore it, since they don't *really* want to | |
6981 switch files. Finally, complain, or at least warn. */ | |
6982 static void | |
6983 cm_setfilename (void) | |
6984 { | |
6985 char *filename; | |
6986 get_rest_of_line (&filename); | |
6987 /* warning ("`@%s %s' encountered and ignored", command, filename); */ | |
6988 free (filename); | |
6989 } | |
6990 | |
6991 static void | |
6992 cm_ignore_line (void) | |
6993 { | |
6994 discard_until ("\n"); | |
6995 } | |
6996 | |
6997 /* @br can be immediately followed by `{}', so we have to read those here. | |
6998 It should simply close the paragraph. */ | |
6999 static void | |
7000 cm_br (void) | |
7001 { | |
7002 if (looking_at ("{}")) | |
7003 input_text_offset += 2; | |
7004 | |
7005 if (curchar () == '\n') | |
7006 { | |
7007 input_text_offset++; | |
7008 line_number++; | |
7009 } | |
7010 | |
7011 close_paragraph (); | |
7012 } | |
7013 | |
7014 /* Insert the number of blank lines passed as argument. */ | |
7015 static void | |
7016 cm_sp (void) | |
7017 { | |
7018 int lines; | |
7019 char *line; | |
7020 | |
7021 get_rest_of_line (&line); | |
7022 | |
7023 if (sscanf (line, "%d", &lines) != 1) | |
7024 { | |
7025 line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX); | |
7026 } | |
7027 else | |
7028 { | |
7029 if (lines < 0) | |
7030 lines = 0; | |
7031 | |
7032 while (lines--) | |
7033 add_char ('\n'); | |
7034 } | |
7035 free (line); | |
7036 } | |
7037 | |
7038 /* Start a new line with just this text on it. | |
7039 Then center the line of text. | |
7040 This always ends the current paragraph. */ | |
7041 static void | |
7042 cm_center (void) | |
7043 { | |
7044 register int i, start, length; | |
7045 int fudge_factor = 1; | |
7046 unsigned char *line; | |
7047 | |
7048 close_paragraph (); | |
7049 filling_enabled = indented_fill = 0; | |
7050 cm_noindent (); | |
7051 start = output_paragraph_offset; | |
7052 inhibit_output_flushing (); | |
7053 get_rest_of_line ((char **)&line); | |
7054 execute_string ((char *)line); | |
7055 free (line); | |
7056 uninhibit_output_flushing (); | |
7057 | |
7058 i = output_paragraph_offset - 1; | |
7059 while (i > (start - 1) && output_paragraph[i] == '\n') | |
7060 i--; | |
7061 | |
7062 output_paragraph_offset = ++i; | |
7063 length = output_paragraph_offset - start; | |
7064 | |
7065 if (length < (fill_column - fudge_factor)) | |
7066 { | |
7067 line = (unsigned char *)xmalloc (1 + length); | |
7068 memcpy (line, (char *)(output_paragraph + start), length); | |
7069 | |
7070 i = (fill_column - fudge_factor - length) / 2; | |
7071 output_paragraph_offset = start; | |
7072 | |
7073 while (i--) | |
7074 insert (' '); | |
7075 | |
7076 for (i = 0; i < length; i++) | |
7077 insert (line[i]); | |
7078 | |
7079 free (line); | |
7080 } | |
7081 | |
7082 insert ('\n'); | |
7083 close_paragraph (); | |
7084 filling_enabled = 1; | |
7085 } | |
7086 | |
7087 /* Show what an expression returns. */ | |
7088 static void | |
7089 cm_result (int arg) | |
7090 { | |
7091 if (arg == END) | |
7092 add_word ("=>"); | |
7093 } | |
7094 | |
7095 /* What an expression expands to. */ | |
7096 static void | |
7097 cm_expansion (int arg) | |
7098 { | |
7099 if (arg == END) | |
7100 add_word ("==>"); | |
7101 } | |
7102 | |
7103 /* Indicates two expressions are equivalent. */ | |
7104 static void | |
7105 cm_equiv (int arg) | |
7106 { | |
7107 if (arg == END) | |
7108 add_word ("=="); | |
7109 } | |
7110 | |
7111 /* What an expression may print. */ | |
7112 static void | |
7113 cm_print (int arg) | |
7114 { | |
7115 if (arg == END) | |
7116 add_word ("-|"); | |
7117 } | |
7118 | |
7119 /* An error signaled. */ | |
7120 static void | |
7121 cm_error (int arg) | |
7122 { | |
7123 if (arg == END) | |
7124 add_word ("error-->"); | |
7125 } | |
7126 | |
7127 /* The location of point in an example of a buffer. */ | |
7128 static void | |
7129 cm_point (int arg) | |
7130 { | |
7131 if (arg == END) | |
7132 add_word ("-!-"); | |
7133 } | |
7134 | |
7135 /* Start a new line with just this text on it. | |
7136 The text is outdented one level if possible. */ | |
7137 static void | |
7138 cm_exdent (void) | |
7139 { | |
7140 char *line; | |
7141 int i = current_indent; | |
7142 | |
7143 if (current_indent) | |
7144 current_indent -= default_indentation_increment; | |
7145 | |
7146 get_rest_of_line (&line); | |
7147 close_single_paragraph (); | |
7148 execute_string ("%s", line); | |
7149 current_indent = i; | |
7150 free (line); | |
7151 close_single_paragraph (); | |
7152 } | |
7153 | |
7154 static void | |
7155 cm_include (void) | |
7156 { | |
7157 cm_infoinclude (); | |
7158 } | |
7159 | |
7160 #if !defined (HAVE_STRERROR) | |
7161 extern char *sys_errlist[]; | |
7162 extern int sys_nerr; | |
7163 | |
7164 char * | |
7165 strerror (num) | |
7166 int num; | |
7167 { | |
7168 if (num >= sys_nerr) | |
7169 return ("Unknown file system error"); | |
7170 else | |
7171 return (sys_errlist[num]); | |
7172 } | |
7173 #endif /* !HAVE_STRERROR */ | |
7174 | |
7175 /* Remember this file, and move onto the next. */ | |
7176 static void | |
7177 cm_infoinclude (void) | |
7178 { | |
7179 char *filename; | |
7180 | |
7181 #if defined (HAVE_MACROS) | |
7182 if (macro_expansion_output_stream) | |
7183 me_append_before_this_command (); | |
7184 #endif /* HAVE_MACROS */ | |
7185 | |
7186 close_paragraph (); | |
7187 get_rest_of_line (&filename); | |
7188 | |
7189 #if defined (HAVE_MACROS) | |
7190 if (macro_expansion_output_stream) | |
7191 remember_itext (input_text, input_text_offset); | |
7192 #endif /* HAVE_MACROS */ | |
7193 | |
7194 pushfile (); | |
7195 | |
7196 /* In verbose mode we print info about including another file. */ | |
7197 if (verbose_mode) | |
7198 { | |
7199 register int i = 0; | |
7200 register FSTACK *stack = filestack; | |
7201 | |
7202 for (i = 0, stack = filestack; stack; stack = stack->next, i++); | |
7203 | |
7204 i *= 2; | |
7205 | |
7206 printf ("%*s", i, ""); | |
7207 printf ("%c%s %s\n", COMMAND_PREFIX, command, filename); | |
7208 fflush (stdout); | |
7209 } | |
7210 | |
7211 if (!find_and_load (filename)) | |
7212 { | |
7213 popfile (); | |
7214 line_number--; | |
7215 | |
7216 /* Cannot "@include foo", in line 5 of "/wh/bar". */ | |
7217 line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename, | |
7218 strerror (errno)); | |
7219 | |
7220 free (filename); | |
7221 return; | |
7222 } | |
7223 else | |
7224 { | |
7225 #if defined (HAVE_MACROS) | |
7226 if (macro_expansion_output_stream) | |
7227 remember_itext (input_text, input_text_offset); | |
7228 #endif /* HAVE_MACROS */ | |
7229 reader_loop (); | |
7230 } | |
7231 free (filename); | |
7232 popfile (); | |
7233 } | |
7234 | |
7235 /* The other side of a malformed expression. */ | |
7236 static void | |
7237 misplaced_brace (void) | |
7238 { | |
7239 line_error ("Misplaced `}'"); | |
7240 } | |
7241 | |
7242 /* Don't let the filling algorithm insert extra whitespace here. */ | |
7243 static void | |
7244 cm_force_abbreviated_whitespace (void) | |
7245 { | |
7246 } | |
7247 | |
7248 /* Do not let this character signify the end of a sentence, though | |
7249 if it was seen without the command prefix it normally would. We | |
7250 do this by turning on the 8th bit of the character. */ | |
7251 static void | |
7252 cm_ignore_sentence_ender (void) | |
7253 { | |
7254 add_char (META ((*command))); | |
7255 } | |
7256 | |
7257 /* Signals end of processing. Easy to make this happen. */ | |
7258 static void | |
7259 cm_bye (void) | |
7260 { | |
7261 input_text_offset = size_of_input_text; | |
7262 } | |
7263 | |
7264 static void | |
7265 cm_asis (void) | |
7266 { | |
7267 } | |
7268 | |
7269 static void | |
7270 cm_math (void) | |
7271 { | |
7272 } | |
7273 | |
7274 | |
7275 /* **************************************************************** */ | |
7276 /* */ | |
7277 /* Indexing Stuff */ | |
7278 /* */ | |
7279 /* **************************************************************** */ | |
7280 | |
7281 | |
7282 /* An index element... */ | |
7283 typedef struct index_elt | |
7284 { | |
7285 struct index_elt *next; | |
7286 char *entry; /* The index entry itself. */ | |
7287 char *node; /* The node from whence it came. */ | |
7288 int code; /* Non-zero means add `@code{...}' when | |
7289 printing this element. */ | |
7290 int defining_line; /* Line number where this entry was written. */ | |
7291 } INDEX_ELT; | |
7292 | |
7293 /* A list of short-names for each index, and the index to that index in our | |
7294 index array, the_indices. In addition, for each index, it is remembered | |
7295 whether that index is a code index or not. Code indices have @code{} | |
7296 inserted around the first word when they are printed with printindex. */ | |
7297 typedef struct | |
7298 { | |
7299 char *name; | |
7300 int index; | |
7301 int code; | |
7302 } INDEX_ALIST; | |
7303 | |
7304 INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL; | |
7305 | |
7306 /* An array of pointers. Each one is for a different index. The | |
7307 "synindex" command changes which array slot is pointed to by a | |
7308 given "index". */ | |
7309 INDEX_ELT **the_indices = (INDEX_ELT **) NULL; | |
7310 | |
7311 /* The number of defined indices. */ | |
7312 int defined_indices = 0; | |
7313 | |
7314 /* We predefine these. */ | |
7315 #define program_index 0 | |
7316 #define function_index 1 | |
7317 #define concept_index 2 | |
7318 #define variable_index 3 | |
7319 #define datatype_index 4 | |
7320 #define key_index 5 | |
7321 | |
7322 static void | |
7323 init_indices (void) | |
7324 { | |
7325 int i; | |
7326 | |
7327 /* Create the default data structures. */ | |
7328 | |
7329 /* Initialize data space. */ | |
7330 if (!the_indices) | |
7331 { | |
7332 the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) * | |
7333 sizeof (INDEX_ELT *)); | |
7334 the_indices[defined_indices] = (INDEX_ELT *) NULL; | |
7335 | |
7336 name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) * | |
7337 sizeof (INDEX_ALIST *)); | |
7338 name_index_alist[defined_indices] = (INDEX_ALIST *) NULL; | |
7339 } | |
7340 | |
7341 /* If there were existing indices, get rid of them now. */ | |
7342 for (i = 0; i < defined_indices; i++) | |
7343 undefindex (name_index_alist[i]->name); | |
7344 | |
7345 /* Add the default indices. */ | |
7346 top_defindex ("pg", 0); | |
7347 top_defindex ("fn", 1); /* "fn" is a code index. */ | |
7348 top_defindex ("cp", 0); | |
7349 top_defindex ("vr", 0); | |
7350 top_defindex ("tp", 0); | |
7351 top_defindex ("ky", 0); | |
7352 | |
7353 } | |
7354 | |
7355 /* Find which element in the known list of indices has this name. | |
7356 Returns -1 if NAME isn't found. */ | |
7357 static int | |
7358 find_index_offset (char *name) | |
7359 { | |
7360 register int i; | |
7361 for (i = 0; i < defined_indices; i++) | |
7362 if (name_index_alist[i] && | |
7363 strcmp (name, name_index_alist[i]->name) == 0) | |
7364 return (name_index_alist[i]->index); | |
7365 return (-1); | |
7366 } | |
7367 | |
7368 /* Return a pointer to the entry of (name . index) for this name. | |
7369 Return NULL if the index doesn't exist. */ | |
7370 static INDEX_ALIST * | |
7371 find_index (char *name) | |
7372 { | |
7373 int offset = find_index_offset (name); | |
7374 if (offset > -1) | |
7375 return (name_index_alist[offset]); | |
7376 else | |
7377 return ((INDEX_ALIST *) NULL); | |
7378 } | |
7379 | |
7380 /* Given an index name, return the offset in the_indices of this index, | |
7381 or -1 if there is no such index. */ | |
7382 static int | |
7383 translate_index (char *name) | |
7384 { | |
7385 INDEX_ALIST *which = find_index (name); | |
7386 | |
7387 if (which) | |
7388 return (which->index); | |
7389 else | |
7390 return (-1); | |
7391 } | |
7392 | |
7393 /* Return the index list which belongs to NAME. */ | |
7394 static INDEX_ELT * | |
7395 index_list (char *name) | |
7396 { | |
7397 int which = translate_index (name); | |
7398 if (which < 0) | |
7399 return ((INDEX_ELT *) -1); | |
7400 else | |
7401 return (the_indices[which]); | |
7402 } | |
7403 | |
7404 /* Please release me, let me go... */ | |
7405 static void | |
7406 free_index (INDEX_ELT *indecks) | |
7407 { | |
7408 INDEX_ELT *temp; | |
7409 | |
7410 while ((temp = indecks) != (INDEX_ELT *) NULL) | |
7411 { | |
7412 free (temp->entry); | |
7413 free (temp->node); | |
7414 indecks = indecks->next; | |
7415 free (temp); | |
7416 } | |
7417 } | |
7418 | |
7419 /* Flush an index by name. */ | |
7420 static void | |
7421 undefindex (char *name) | |
7422 { | |
7423 int i; | |
7424 int which = find_index_offset (name); | |
7425 | |
7426 if (which < 0) | |
7427 return; | |
7428 | |
7429 i = name_index_alist[which]->index; | |
7430 | |
7431 free_index (the_indices[i]); | |
7432 the_indices[i] = (INDEX_ELT *) NULL; | |
7433 | |
7434 free (name_index_alist[which]->name); | |
7435 free (name_index_alist[which]); | |
7436 name_index_alist[which] = (INDEX_ALIST *) NULL; | |
7437 } | |
7438 | |
7439 /* Define an index known as NAME. We assign the slot number. | |
7440 CODE if non-zero says to make this a code index. */ | |
7441 static void | |
7442 defindex (char *name, int code) | |
7443 { | |
7444 register int i, slot; | |
7445 | |
7446 /* If it already exists, flush it. */ | |
7447 undefindex (name); | |
7448 | |
7449 /* Try to find an empty slot. */ | |
7450 slot = -1; | |
7451 for (i = 0; i < defined_indices; i++) | |
7452 if (!name_index_alist[i]) | |
7453 { | |
7454 slot = i; | |
7455 break; | |
7456 } | |
7457 | |
7458 if (slot < 0) | |
7459 { | |
7460 /* No such luck. Make space for another index. */ | |
7461 slot = defined_indices; | |
7462 defined_indices++; | |
7463 | |
7464 name_index_alist = (INDEX_ALIST **) | |
7465 xrealloc ((char *)name_index_alist, | |
7466 (1 + defined_indices) * sizeof (INDEX_ALIST *)); | |
7467 the_indices = (INDEX_ELT **) | |
7468 xrealloc ((char *)the_indices, | |
7469 (1 + defined_indices) * sizeof (INDEX_ELT *)); | |
7470 } | |
7471 | |
7472 /* We have a slot. Start assigning. */ | |
7473 name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST)); | |
7474 name_index_alist[slot]->name = strdup (name); | |
7475 name_index_alist[slot]->index = slot; | |
7476 name_index_alist[slot]->code = code; | |
7477 | |
7478 the_indices[slot] = (INDEX_ELT *) NULL; | |
7479 } | |
7480 | |
7481 /* Add the arguments to the current index command to the index NAME. */ | |
7482 static void | |
7483 index_add_arg (char *name) | |
7484 { | |
7485 int which; | |
7486 char *index_entry; | |
7487 INDEX_ALIST *tem; | |
7488 | |
7489 tem = find_index (name); | |
7490 | |
7491 which = tem ? tem->index : -1; | |
7492 | |
7493 #if defined (HAVE_MACROS) | |
7494 if (macro_expansion_output_stream) | |
7495 append_to_expansion_output (input_text_offset + 1); | |
7496 #endif /* HAVE_MACROS */ | |
7497 | |
7498 get_rest_of_line (&index_entry); | |
7499 ignore_blank_line (); | |
7500 | |
7501 #if defined (HAVE_MACROS) | |
7502 if (macro_expansion_output_stream) | |
7503 { | |
7504 int op_orig; | |
7505 | |
7506 remember_itext (input_text, input_text_offset); | |
7507 op_orig = output_paragraph_offset; | |
7508 me_execute_string (index_entry); | |
7509 me_execute_string ("\n"); | |
7510 output_paragraph_offset = op_orig; | |
7511 } | |
7512 #endif /* HAVE_MACROS */ | |
7513 | |
7514 if (which < 0) | |
7515 { | |
7516 line_error ("Unknown index reference `%s'", name); | |
7517 free (index_entry); | |
7518 } | |
7519 else | |
7520 { | |
7521 INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT)); | |
7522 new->next = the_indices[which]; | |
7523 new->entry = index_entry; | |
7524 new->node = current_node; | |
7525 new->code = tem->code; | |
7526 new->defining_line = line_number - 1; | |
7527 the_indices[which] = new; | |
7528 } | |
7529 } | |
7530 | |
7531 #define INDEX_COMMAND_SUFFIX "index" | |
7532 | |
7533 /* The function which user defined index commands call. */ | |
7534 static void | |
7535 gen_index (void) | |
7536 { | |
7537 char *name = strdup (command); | |
7538 if ((int) strlen (name) >= (int) strlen ("index")) | |
7539 name[strlen (name) - strlen ("index")] = '\0'; | |
7540 index_add_arg (name); | |
7541 free (name); | |
7542 } | |
7543 | |
7544 static void | |
7545 top_defindex (char *name, int code) | |
7546 { | |
7547 char *temp; | |
7548 | |
7549 temp = (char *) xmalloc (1 + strlen (name) + strlen ("index")); | |
7550 sprintf (temp, "%sindex", name); | |
7551 define_user_command (temp, gen_index, 0); | |
7552 defindex (name, code); | |
7553 free (temp); | |
7554 } | |
7555 | |
7556 /* Define a new index command. Arg is name of index. */ | |
7557 static void | |
7558 cm_defindex (void) | |
7559 { | |
7560 gen_defindex (0); | |
7561 } | |
7562 | |
7563 static void | |
7564 cm_defcodeindex (void) | |
7565 { | |
7566 gen_defindex (1); | |
7567 } | |
7568 | |
7569 static void | |
7570 gen_defindex (int code) | |
7571 { | |
7572 char *name; | |
7573 get_rest_of_line (&name); | |
7574 | |
7575 if (find_index (name)) | |
7576 { | |
7577 line_error ("Index `%s' already exists", name); | |
7578 free (name); | |
7579 return; | |
7580 } | |
7581 else | |
7582 { | |
7583 char *temp = (char *) alloca (1 + strlen (name) + strlen ("index")); | |
7584 sprintf (temp, "%sindex", name); | |
7585 define_user_command (temp, gen_index, 0); | |
7586 defindex (name, code); | |
7587 free (name); | |
7588 } | |
7589 } | |
7590 | |
7591 /* Append LIST2 to LIST1. Return the head of the list. */ | |
7592 static INDEX_ELT * | |
7593 index_append (INDEX_ELT *head, INDEX_ELT *tail) | |
7594 { | |
7595 register INDEX_ELT *t_head = head; | |
7596 | |
7597 if (!t_head) | |
7598 return (tail); | |
7599 | |
7600 while (t_head->next) | |
7601 t_head = t_head->next; | |
7602 t_head->next = tail; | |
7603 return (head); | |
7604 } | |
7605 | |
7606 /* Expects 2 args, on the same line. Both are index abbreviations. | |
7607 Make the first one be a synonym for the second one, i.e. make the | |
7608 first one have the same index as the second one. */ | |
7609 static void | |
7610 cm_synindex (void) | |
7611 { | |
7612 int redirector, redirectee; | |
7613 char *temp; | |
7614 | |
7615 skip_whitespace (); | |
7616 get_until_in_line (" ", &temp); | |
7617 redirectee = find_index_offset (temp); | |
7618 skip_whitespace (); | |
7619 free_and_clear (&temp); | |
7620 get_until_in_line (" ", &temp); | |
7621 redirector = find_index_offset (temp); | |
7622 free (temp); | |
7623 if (redirector < 0 || redirectee < 0) | |
7624 { | |
7625 line_error ("Unknown index reference"); | |
7626 } | |
7627 else | |
7628 { | |
7629 /* I think that we should let the user make indices synonymous to | |
7630 each other without any lossage of info. This means that one can | |
7631 say @synindex cp dt anywhere in the file, and things that used to | |
7632 be in cp will go into dt. */ | |
7633 INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector]; | |
7634 | |
7635 if (i1 || i2) | |
7636 { | |
7637 if (i1) | |
7638 the_indices[redirectee] = index_append (i1, i2); | |
7639 else | |
7640 the_indices[redirectee] = index_append (i2, i1); | |
7641 } | |
7642 | |
7643 name_index_alist[redirectee]->index = | |
7644 name_index_alist[redirector]->index; | |
7645 } | |
7646 } | |
7647 | |
7648 static void | |
7649 cm_pindex (void) /* Pinhead index. */ | |
7650 { | |
7651 index_add_arg ("pg"); | |
7652 } | |
7653 | |
7654 static void | |
7655 cm_vindex (void) /* Variable index. */ | |
7656 { | |
7657 index_add_arg ("vr"); | |
7658 } | |
7659 | |
7660 static void | |
7661 cm_kindex (void) /* Key index. */ | |
7662 { | |
7663 index_add_arg ("ky"); | |
7664 } | |
7665 | |
7666 static void | |
7667 cm_cindex (void) /* Concept index. */ | |
7668 { | |
7669 index_add_arg ("cp"); | |
7670 } | |
7671 | |
7672 static void | |
7673 cm_findex (void) /* Function index. */ | |
7674 { | |
7675 index_add_arg ("fn"); | |
7676 } | |
7677 | |
7678 static void | |
7679 cm_tindex (void) /* Data Type index. */ | |
7680 { | |
7681 index_add_arg ("tp"); | |
7682 } | |
7683 | |
7684 /* Sorting the index. */ | |
7685 static int | |
7686 index_element_compare (INDEX_ELT **element1, INDEX_ELT **element2) | |
7687 { | |
7688 /* This needs to ignore leading non-text characters. */ | |
7689 return (strcasecmp ((*element1)->entry, (*element2)->entry)); | |
7690 } | |
7691 | |
7692 /* Force all index entries to be unique. */ | |
7693 static void | |
7694 make_index_entries_unique (INDEX_ELT **array, int count) | |
7695 { | |
7696 register int i, j; | |
7697 INDEX_ELT **copy; | |
7698 | |
7699 copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *)); | |
7700 | |
7701 for (i = 0, j = 0; i < count; i++) | |
7702 { | |
7703 if ((i == (count - 1)) || | |
7704 (array[i]->node != array[i + 1]->node) || | |
7705 (strcasecmp (array[i]->entry, array[i + 1]->entry) != 0)) | |
7706 copy[j++] = array[i]; | |
7707 else | |
7708 { | |
7709 free (array[i]->entry); | |
7710 free (array[i]); | |
7711 } | |
7712 } | |
7713 copy[j] = (INDEX_ELT *)NULL; | |
7714 | |
7715 /* Now COPY contains only unique entries. Duplicated entries in the | |
7716 original array have been freed. Replace the current array with | |
7717 the copy, fixing the NEXT pointers. */ | |
7718 for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++) | |
7719 { | |
7720 int counter = 1; | |
7721 copy[i]->next = copy[i + 1]; | |
7722 | |
7723 /* Fix entry names which are the same. They point to different nodes, | |
7724 so we make the entry name unique. */ | |
7725 if ((copy[i + 1] != (INDEX_ELT *)NULL) && | |
7726 (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0)) | |
7727 { | |
7728 char *new_entry_name; | |
7729 | |
7730 new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry)); | |
7731 sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter); | |
7732 free (copy[i]->entry); | |
7733 copy[i]->entry = new_entry_name; | |
7734 counter++; | |
7735 } | |
7736 else | |
7737 counter = 1; | |
7738 | |
7739 array[i] = copy[i]; | |
7740 } | |
7741 array[i] = (INDEX_ELT *)NULL; | |
7742 | |
7743 /* Free the storage used only by COPY. */ | |
7744 free (copy); | |
7745 } | |
7746 | |
7747 /* Sort the index passed in INDEX, returning an array of | |
7748 pointers to elements. The array is terminated with a NULL | |
7749 pointer. We call qsort because it's supposed to be fast. | |
7750 I think this looks bad. */ | |
7751 static INDEX_ELT ** | |
7752 sort_index (INDEX_ELT *indecks) | |
7753 { | |
7754 INDEX_ELT *temp = indecks; | |
7755 INDEX_ELT **array; | |
7756 int count = 0; | |
7757 | |
7758 while (temp != (INDEX_ELT *) NULL) | |
7759 { | |
7760 count++; | |
7761 temp = temp->next; | |
7762 } | |
7763 | |
7764 /* We have the length. Make an array. */ | |
7765 | |
7766 array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *)); | |
7767 count = 0; | |
7768 temp = indecks; | |
7769 | |
7770 while (temp != (INDEX_ELT *) NULL) | |
7771 { | |
7772 array[count++] = temp; | |
7773 temp = temp->next; | |
7774 } | |
7775 array[count] = (INDEX_ELT *) NULL; /* terminate the array. */ | |
7776 | |
7777 /* Sort the array. */ | |
7778 qsort (array, count, sizeof (INDEX_ELT *), (int (*)()) index_element_compare); | |
7779 make_index_entries_unique (array, count); | |
7780 return (array); | |
7781 } | |
7782 | |
7783 /* Non-zero means that we are in the middle of printing an index. */ | |
7784 int printing_index = 0; | |
7785 | |
7786 /* Takes one arg, a short name of an index to print. | |
7787 Outputs a menu of the sorted elements of the index. */ | |
7788 static void | |
7789 cm_printindex (void) | |
7790 { | |
7791 int item; | |
7792 INDEX_ELT *indecks; | |
7793 INDEX_ELT **array; | |
7794 char *index_name; | |
7795 int old_inhibitions = inhibit_paragraph_indentation; | |
7796 int previous_filling_enabled_value = filling_enabled; | |
7797 | |
7798 close_paragraph (); | |
7799 get_rest_of_line (&index_name); | |
7800 | |
7801 indecks = index_list (index_name); | |
7802 if (indecks == (INDEX_ELT *)-1) | |
7803 { | |
7804 line_error ("Unknown index name `%s'", index_name); | |
7805 free (index_name); | |
7806 return; | |
7807 } | |
7808 else | |
7809 free (index_name); | |
7810 | |
7811 array = sort_index (indecks); | |
7812 | |
7813 filling_enabled = 0; | |
7814 inhibit_paragraph_indentation = 1; | |
7815 close_paragraph (); | |
7816 add_word ("* Menu:\n\n"); | |
7817 | |
7818 printing_index = 1; | |
7819 | |
7820 #if defined (HAVE_MACROS) | |
7821 me_inhibit_expansion++; | |
7822 #endif /* HAVE_MACROS */ | |
7823 | |
7824 for (item = 0; (indecks = array[item]); item++) | |
7825 { | |
7826 int real_line_number = line_number; | |
7827 | |
7828 /* Let errors generated while making the index entry point back | |
7829 at the line which contains the entry. */ | |
7830 line_number = indecks->defining_line; | |
7831 | |
7832 /* If this particular entry should be printed as a "code" index, | |
7833 then wrap the entry with "@code{...}". */ | |
7834 if (indecks->code) | |
7835 execute_string ("* %ccode{%s}: ", COMMAND_PREFIX, indecks->entry); | |
7836 else | |
7837 execute_string ("* %s: ", indecks->entry); | |
7838 | |
7839 /* Pad the front of the destination nodename so that | |
7840 the output looks nice. */ | |
7841 if (fill_column > 40 && output_column < 40) | |
7842 indent (40 - output_column); | |
7843 | |
7844 execute_string ("%s.\n", indecks->node); | |
7845 | |
7846 line_number = real_line_number; | |
7847 flush_output (); | |
7848 } | |
7849 | |
7850 #if defined (HAVE_MACROS) | |
7851 me_inhibit_expansion--; | |
7852 #endif /* HAVE_MACROS */ | |
7853 | |
7854 printing_index = 0; | |
7855 free (array); | |
7856 close_single_paragraph (); | |
7857 filling_enabled = previous_filling_enabled_value; | |
7858 inhibit_paragraph_indentation = old_inhibitions; | |
7859 } | |
7860 | |
7861 | |
7862 /* **************************************************************** */ | |
7863 /* */ | |
7864 /* Making User Defined Commands */ | |
7865 /* */ | |
7866 /* **************************************************************** */ | |
7867 | |
7868 static void | |
7869 define_user_command (char *name, COMMAND_FUNCTION *proc, int needs_braces_p) | |
7870 { | |
7871 int slot = user_command_array_len; | |
7872 user_command_array_len++; | |
7873 | |
7874 if (!user_command_array) | |
7875 user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *)); | |
7876 | |
7877 user_command_array = (COMMAND **) xrealloc (user_command_array, | |
7878 (1 + user_command_array_len) * | |
7879 sizeof (COMMAND *)); | |
7880 | |
7881 user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND)); | |
7882 user_command_array[slot]->name = strdup (name); | |
7883 user_command_array[slot]->proc = proc; | |
7884 user_command_array[slot]->argument_in_braces = needs_braces_p; | |
7885 } | |
7886 | |
7887 #if 0 /* unused */ | |
7888 /* Make ALIAS run the named FUNCTION. Copies properties from FUNCTION. */ | |
7889 static void | |
7890 define_alias (char *alias, char *function) | |
7891 { | |
7892 } | |
7893 #endif | |
7894 | |
7895 /* Set the paragraph indentation variable to the value specified in STRING. | |
7896 Values can be: | |
7897 `asis': Don't change existing indentation. | |
7898 `none': Remove existing indentation. | |
7899 NUM: Indent NUM spaces at the starts of paragraphs. | |
7900 Note that if NUM is zero, we assume `none'. | |
7901 | |
7902 Returns 0 if successful, or non-zero if STRING isn't one of the above. */ | |
7903 static int | |
7904 set_paragraph_indent (char *string) | |
7905 { | |
7906 if (strcmp (string, "asis") == 0) | |
7907 paragraph_start_indent = 0; | |
7908 else if (strcmp (string, "none") == 0) | |
7909 paragraph_start_indent = -1; | |
7910 else | |
7911 { | |
7912 if (sscanf (string, "%d", ¶graph_start_indent) != 1) | |
7913 return (-1); | |
7914 else | |
7915 { | |
7916 if (paragraph_start_indent == 0) | |
7917 paragraph_start_indent = -1; | |
7918 } | |
7919 } | |
7920 return (0); | |
7921 } | |
7922 | |
7923 static void | |
7924 cm_paragraphindent (void) | |
7925 { | |
7926 char *arg; | |
7927 | |
7928 get_rest_of_line (&arg); | |
7929 if (set_paragraph_indent (arg) != 0) | |
7930 line_error ("Bad argument to %c%s", COMMAND_PREFIX, command); | |
7931 | |
7932 free (arg); | |
7933 } | |
7934 | |
7935 /* Some support for footnotes. */ | |
7936 | |
7937 /* Footnotes are a new construct in Info. We don't know the best method | |
7938 of implementing them for sure, so we present two possiblities. | |
7939 | |
7940 SeparateNode: | |
7941 Make them look like followed references, with the reference | |
7942 destinations in a makeinfo manufactured node or, | |
7943 | |
7944 EndNode: | |
7945 Make them appear at the bottom of the node that they originally | |
7946 appeared in. */ | |
7947 #define SeparateNode 0 | |
7948 #define EndNode 1 | |
7949 | |
7950 int footnote_style = EndNode; | |
7951 int first_footnote_this_node = 1; | |
7952 int footnote_count = 0; | |
7953 | |
7954 /* Set the footnote style based on he style identifier in STRING. */ | |
7955 static int | |
7956 set_footnote_style (char *string) | |
7957 { | |
7958 if ((strcasecmp (string, "separate") == 0) || | |
7959 (strcasecmp (string, "MN") == 0)) | |
7960 footnote_style = SeparateNode; | |
7961 else if ((strcasecmp (string, "end") == 0) || | |
7962 (strcasecmp (string, "EN") == 0)) | |
7963 footnote_style = EndNode; | |
7964 else | |
7965 return (-1); | |
7966 | |
7967 return (0); | |
7968 } | |
7969 | |
7970 static void | |
7971 cm_footnotestyle (void) | |
7972 { | |
7973 char *arg; | |
7974 | |
7975 get_rest_of_line (&arg); | |
7976 | |
7977 if (set_footnote_style (arg) != 0) | |
7978 line_error ("Bad argument to %c%s", COMMAND_PREFIX, command); | |
7979 | |
7980 free (arg); | |
7981 } | |
7982 | |
7983 typedef struct fn | |
7984 { | |
7985 struct fn *next; | |
7986 char *marker; | |
7987 char *note; | |
7988 } FN; | |
7989 | |
7990 FN *pending_notes = (FN *) NULL; | |
7991 | |
7992 /* A method for remembering footnotes. Note that this list gets output | |
7993 at the end of the current node. */ | |
7994 static void | |
7995 remember_note (char *marker, char *note) | |
7996 { | |
7997 FN *temp = (FN *) xmalloc (sizeof (FN)); | |
7998 | |
7999 temp->marker = strdup (marker); | |
8000 temp->note = strdup (note); | |
8001 temp->next = pending_notes; | |
8002 pending_notes = temp; | |
8003 footnote_count++; | |
8004 } | |
8005 | |
8006 /* How to get rid of existing footnotes. */ | |
8007 static void | |
8008 free_pending_notes (void) | |
8009 { | |
8010 FN *temp; | |
8011 | |
8012 while ((temp = pending_notes) != (FN *) NULL) | |
8013 { | |
8014 free (temp->marker); | |
8015 free (temp->note); | |
8016 pending_notes = pending_notes->next; | |
8017 free (temp); | |
8018 } | |
8019 first_footnote_this_node = 1; | |
8020 footnote_count = 0; | |
8021 } | |
8022 | |
8023 /* What to do when you see a @footnote construct. */ | |
8024 | |
8025 /* Handle a "footnote". | |
8026 footnote *{this is a footnote} | |
8027 where "*" is the marker character for this note. */ | |
8028 static void | |
8029 cm_footnote (void) | |
8030 { | |
8031 char *marker; | |
8032 char *note; | |
8033 | |
8034 get_until ("{", &marker); | |
8035 canon_white (marker); | |
8036 | |
8037 /* Read the argument in braces. */ | |
8038 if (curchar () != '{') | |
8039 { | |
8040 line_error ("`%c%s' expected more than just `%s'. It needs something in `{...}'", | |
8041 COMMAND_PREFIX, command, marker); | |
8042 free (marker); | |
8043 return; | |
8044 } | |
8045 else | |
8046 { | |
8047 int braces = 1; | |
8048 int temp = ++input_text_offset; | |
8049 int len; | |
8050 | |
8051 while (braces) | |
8052 { | |
8053 if (temp == size_of_input_text) | |
8054 { | |
8055 line_error ("No closing brace for footnote `%s'", marker); | |
8056 return; | |
8057 } | |
8058 | |
8059 if (input_text[temp] == '{') | |
8060 braces++; | |
8061 else if (input_text[temp] == '}') | |
8062 braces--; | |
8063 else if (input_text[temp] == '\n') | |
8064 line_number ++; | |
8065 | |
8066 temp++; | |
8067 } | |
8068 | |
8069 len = (temp - input_text_offset) - 1; | |
8070 note = (char *)xmalloc (len + 1); | |
8071 strncpy (note, &input_text[input_text_offset], len); | |
8072 note[len] = '\0'; | |
8073 input_text_offset = temp; | |
8074 } | |
8075 | |
8076 if (!current_node || !*current_node) | |
8077 { | |
8078 line_error ("Footnote defined without parent node"); | |
8079 free (marker); | |
8080 free (note); | |
8081 return; | |
8082 } | |
8083 | |
8084 if (!*marker) | |
8085 { | |
8086 free (marker); | |
8087 | |
8088 if (number_footnotes) | |
8089 { | |
8090 marker = (char *)xmalloc (10); | |
8091 sprintf (marker, "%d", current_footnote_number); | |
8092 current_footnote_number++; | |
8093 } | |
8094 else | |
8095 marker = strdup ("*"); | |
8096 } | |
8097 | |
8098 remember_note (marker, note); | |
8099 | |
8100 /* Your method should at least insert MARKER. */ | |
8101 switch (footnote_style) | |
8102 { | |
8103 case SeparateNode: | |
8104 add_word_args ("(%s)", marker); | |
8105 if (first_footnote_this_node) | |
8106 { | |
8107 char *temp_string; | |
8108 | |
8109 temp_string = (char *) | |
8110 xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1); | |
8111 | |
8112 add_word_args (" (*note %s-Footnotes::)", current_node); | |
8113 strcpy (temp_string, current_node); | |
8114 strcat (temp_string, "-Footnotes"); | |
8115 remember_node_reference (temp_string, line_number, followed_reference); | |
8116 free (temp_string); | |
8117 first_footnote_this_node = 0; | |
8118 } | |
8119 break; | |
8120 | |
8121 case EndNode: | |
8122 add_word_args ("(%s)", marker); | |
8123 break; | |
8124 | |
8125 default: | |
8126 break; | |
8127 } | |
8128 free (marker); | |
8129 free (note); | |
8130 } | |
8131 | |
8132 /* Non-zero means that we are currently in the process of outputting | |
8133 footnotes. */ | |
8134 int already_outputting_pending_notes = 0; | |
8135 | |
8136 /* Output the footnotes. We are at the end of the current node. */ | |
8137 static void | |
8138 output_pending_notes (void) | |
8139 { | |
8140 FN *footnote = pending_notes; | |
8141 | |
8142 if (!pending_notes) | |
8143 return; | |
8144 | |
8145 switch (footnote_style) | |
8146 { | |
8147 | |
8148 case SeparateNode: | |
8149 { | |
8150 char *old_current_node = current_node; | |
8151 char *old_command = strdup (command); | |
8152 | |
8153 already_outputting_pending_notes++; | |
8154 execute_string ("%cnode %s-Footnotes,,,%s\n", | |
8155 COMMAND_PREFIX, current_node, current_node); | |
8156 already_outputting_pending_notes--; | |
8157 current_node = old_current_node; | |
8158 free (command); | |
8159 command = old_command; | |
8160 } | |
8161 break; | |
8162 | |
8163 case EndNode: | |
8164 close_paragraph (); | |
8165 in_fixed_width_font++; | |
8166 execute_string ("---------- Footnotes ----------\n\n"); | |
8167 in_fixed_width_font--; | |
8168 break; | |
8169 } | |
8170 | |
8171 /* Handle the footnotes in reverse order. */ | |
8172 { | |
8173 FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *)); | |
8174 | |
8175 array[footnote_count] = (FN *) NULL; | |
8176 | |
8177 while (--footnote_count > -1) | |
8178 { | |
8179 array[footnote_count] = footnote; | |
8180 footnote = footnote->next; | |
8181 } | |
8182 | |
8183 filling_enabled = 1; | |
8184 indented_fill = 1; | |
8185 | |
8186 while ((footnote = array[++footnote_count])) | |
8187 { | |
8188 | |
8189 switch (footnote_style) | |
8190 { | |
8191 case SeparateNode: | |
8192 case EndNode: | |
8193 execute_string ("(%s) %s", footnote->marker, footnote->note); | |
8194 close_paragraph (); | |
8195 break; | |
8196 } | |
8197 } | |
8198 close_paragraph (); | |
8199 free (array); | |
8200 } | |
8201 } | |
8202 | |
8203 | |
8204 /* **************************************************************** */ | |
8205 /* */ | |
8206 /* User definable Macros (text substitution) */ | |
8207 /* */ | |
8208 /* **************************************************************** */ | |
8209 | |
8210 #if defined (HAVE_MACROS) | |
8211 | |
8212 /* Array of macros and definitions. */ | |
8213 MACRO_DEF **macro_list = (MACRO_DEF **)NULL; | |
8214 | |
8215 int macro_list_len = 0; /* Number of elements. */ | |
8216 int macro_list_size = 0; /* Number of slots in total. */ | |
8217 | |
8218 /* Return the macro definition of NAME or NULL if NAME is not defined. */ | |
8219 static MACRO_DEF * | |
8220 find_macro (char *name) | |
8221 { | |
8222 register int i; | |
8223 register MACRO_DEF *def; | |
8224 | |
8225 def = (MACRO_DEF *)NULL; | |
8226 for (i = 0; macro_list && (def = macro_list[i]); i++) | |
8227 { | |
8228 if ((!def->inhibited) && (strcmp (def->name, name) == 0)) | |
8229 break; | |
8230 } | |
8231 return (def); | |
8232 } | |
8233 | |
8234 /* Add the macro NAME with ARGLIST and BODY to the list of defined macros. | |
8235 SOURCE_FILE is the name of the file where this definition can be found, | |
8236 and SOURCE_LINENO is the line number within that file. If a macro already | |
8237 exists with NAME, then a warning is produced, and that previous | |
8238 definition is overwritten. */ | |
8239 void | |
8240 add_macro (char *name, char **arglist, char *body, char *source_file, | |
8241 int source_lineno, int flags) | |
8242 { | |
8243 register MACRO_DEF *def; | |
8244 | |
8245 def = find_macro (name); | |
8246 | |
8247 if (!def) | |
8248 { | |
8249 if (macro_list_len + 2 >= macro_list_size) | |
8250 macro_list = (MACRO_DEF **)xrealloc | |
8251 (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *))); | |
8252 | |
8253 macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF)); | |
8254 macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL; | |
8255 | |
8256 def = macro_list[macro_list_len]; | |
8257 macro_list_len += 1; | |
8258 def->name = name; | |
8259 } | |
8260 else | |
8261 { | |
8262 char *temp_filename = input_filename; | |
8263 int temp_line = line_number; | |
8264 | |
8265 warning ("The macro `%s' is previously defined", name); | |
8266 | |
8267 input_filename = def->source_file; | |
8268 line_number = def->source_lineno; | |
8269 | |
8270 warning ("Here is the previous definition of `%s'", name); | |
8271 | |
8272 input_filename = temp_filename; | |
8273 line_number = temp_line; | |
8274 | |
8275 if (def->arglist) | |
8276 { | |
8277 register int i; | |
8278 | |
8279 for (i = 0; def->arglist[i]; i++) | |
8280 free (def->arglist[i]); | |
8281 | |
8282 free (def->arglist); | |
8283 } | |
8284 free (def->source_file); | |
8285 free (def->body); | |
8286 } | |
8287 | |
8288 def->source_file = strdup (source_file); | |
8289 def->source_lineno = source_lineno; | |
8290 def->body = body; | |
8291 def->arglist = arglist; | |
8292 def->inhibited = 0; | |
8293 def->flags = flags; | |
8294 } | |
8295 | |
8296 /* Delete the macro with name NAME. The macro is deleted from the list, | |
8297 but it is also returned. If there was no macro defined, NULL is | |
8298 returned. */ | |
8299 static MACRO_DEF * | |
8300 delete_macro (char *name) | |
8301 { | |
8302 register int i; | |
8303 register MACRO_DEF *def; | |
8304 | |
8305 def = (MACRO_DEF *)NULL; | |
8306 | |
8307 for (i = 0; macro_list && (def = macro_list[i]); i++) | |
8308 if (strcmp (def->name, name) == 0) | |
8309 { | |
8310 memcpy (macro_list + i, macro_list + i + 1, | |
8311 ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *)); | |
8312 break; | |
8313 } | |
8314 return (def); | |
8315 } | |
8316 | |
8317 /* Return the arglist on the current line. This can behave in two different | |
8318 ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */ | |
8319 int braces_required_for_macro_args = 0; | |
8320 | |
8321 static char ** | |
8322 get_macro_args (MACRO_DEF *def) | |
8323 { | |
8324 register int i; | |
8325 char *word; | |
8326 | |
8327 /* Quickly check to see if this macro has been invoked with any arguments. | |
8328 If not, then don't skip any of the following whitespace. */ | |
8329 for (i = input_text_offset; i < size_of_input_text; i++) | |
8330 if (!cr_or_whitespace (input_text[i])) | |
8331 break; | |
8332 | |
8333 if (input_text[i] != '{') | |
8334 { | |
8335 if (braces_required_for_macro_args) | |
8336 { | |
8337 return ((char **)NULL); | |
8338 } | |
8339 else | |
8340 { | |
8341 /* Braces are not required to fill out the macro arguments. If | |
8342 this macro takes one argument, it is considered to be the | |
8343 remainder of the line, sans whitespace. */ | |
8344 if (def->arglist && def->arglist[0] && !def->arglist[1]) | |
8345 { | |
8346 char **arglist; | |
8347 | |
8348 get_rest_of_line (&word); | |
8349 input_text_offset--; | |
8350 canon_white (word); | |
8351 arglist = (char **)xmalloc (2 * sizeof (char *)); | |
8352 arglist[0] = word; | |
8353 arglist[1] = (char *)NULL; | |
8354 return (arglist); | |
8355 } | |
8356 else | |
8357 { | |
8358 /* The macro either took no arguments, or took more than | |
8359 one argument. In that case, it must be invoked with | |
8360 arguments surrounded by braces. */ | |
8361 return ((char **)NULL); | |
8362 } | |
8363 } | |
8364 } | |
8365 return (get_brace_args (def->flags & ME_QUOTE_ARG)); | |
8366 } | |
8367 | |
8368 /* Substitute actual parameters for named parameters in body. | |
8369 The named parameters which appear in BODY must by surrounded | |
8370 reverse slashes, as in \foo\. */ | |
8371 static char * | |
8372 apply (char **named, char **actuals, char *body) | |
8373 { | |
8374 register int i; | |
8375 int new_body_index, new_body_size; | |
8376 char *new_body, *text; | |
8377 int length_of_actuals; | |
8378 | |
8379 length_of_actuals = array_len (actuals); | |
8380 new_body_size = strlen (body); | |
8381 new_body = (char *)xmalloc (1 + new_body_size); | |
8382 | |
8383 /* Copy chars from BODY into NEW_BODY. */ | |
8384 i = 0; new_body_index = 0; | |
8385 | |
8386 while (1) | |
8387 { | |
8388 if (!body[i]) | |
8389 break; | |
8390 | |
8391 if (body[i] != '\\') | |
8392 new_body[new_body_index++] = body[i++]; | |
8393 else | |
8394 { | |
8395 /* Snarf parameter name, check against named parameters. */ | |
8396 char *param; | |
8397 int param_start, which, len; | |
8398 | |
8399 param_start = ++i; | |
8400 while ((body[i]) && (body[i] != '\\')) | |
8401 i++; | |
8402 | |
8403 len = i - param_start; | |
8404 param = (char *)xmalloc (1 + len); | |
8405 memcpy (param, body + param_start, len); | |
8406 param[len] = '\0'; | |
8407 | |
8408 if (body[i]) | |
8409 i++; | |
8410 | |
8411 /* Now check against named parameters. */ | |
8412 for (which = 0; named && named[which]; which++) | |
8413 if (strcmp (named[which], param) == 0) | |
8414 break; | |
8415 | |
8416 if (named[which]) | |
8417 { | |
8418 if (which < length_of_actuals) | |
8419 text = actuals[which]; | |
8420 else | |
8421 text = (char *)NULL; | |
8422 | |
8423 if (!text) | |
8424 text = ""; | |
8425 | |
8426 len = strlen (text); | |
8427 } | |
8428 else | |
8429 { | |
8430 len += 2; | |
8431 text = (char *)xmalloc (1 + len); | |
8432 sprintf (text, "\\%s\\", param); | |
8433 } | |
8434 | |
8435 if ((int) (2 + strlen (param)) < len) | |
8436 new_body = (char *)xrealloc | |
8437 (new_body, new_body_size += (1 + len)); | |
8438 | |
8439 free (param); | |
8440 | |
8441 strcpy (new_body + new_body_index, text); | |
8442 new_body_index += len; | |
8443 | |
8444 if (!named[which]) | |
8445 free (text); | |
8446 } | |
8447 } | |
8448 new_body[new_body_index] = '\0'; | |
8449 return (new_body); | |
8450 } | |
8451 | |
8452 /* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */ | |
8453 static void | |
8454 execute_macro (MACRO_DEF *def) | |
8455 { | |
8456 char **arglist; | |
8457 int num_args; | |
8458 char *execution_string = (char *)NULL; | |
8459 | |
8460 if (macro_expansion_output_stream && !me_inhibit_expansion) | |
8461 me_append_before_this_command (); | |
8462 | |
8463 /* Find out how many arguments this macro definition takes. */ | |
8464 num_args = array_len (def->arglist); | |
8465 | |
8466 /* Gather the arguments present on the line if there are any. */ | |
8467 arglist = get_macro_args (def); | |
8468 | |
8469 if (num_args < array_len (arglist)) | |
8470 { | |
8471 free_array (arglist); | |
8472 line_error ("Macro `%s' called with too many args", def->name); | |
8473 return; | |
8474 } | |
8475 | |
8476 if (def->body) | |
8477 execution_string = apply (def->arglist, arglist, def->body); | |
8478 | |
8479 free_array (arglist); | |
8480 | |
8481 if (def->body) | |
8482 { | |
8483 if (macro_expansion_output_stream && !me_inhibit_expansion) | |
8484 { | |
8485 remember_itext (input_text, input_text_offset); | |
8486 me_execute_string (execution_string); | |
8487 } | |
8488 else | |
8489 execute_string (execution_string); | |
8490 | |
8491 free (execution_string); | |
8492 } | |
8493 } | |
8494 | |
8495 /* Read and remember the definition of a macro. */ | |
8496 static void | |
8497 cm_macro (void) | |
8498 { | |
8499 register int i; | |
8500 char *name, **arglist, *body, *line; | |
8501 int body_size, body_index; | |
8502 int depth = 1; | |
8503 int defining_line = line_number; | |
8504 int flags = 0; | |
8505 | |
8506 arglist = (char **)NULL; | |
8507 body = (char *)NULL; | |
8508 body_size = 0; | |
8509 body_index = 0; | |
8510 | |
8511 if (macro_expansion_output_stream) | |
8512 me_append_before_this_command (); | |
8513 | |
8514 skip_whitespace (); | |
8515 | |
8516 /* Get the name of the macro. This is the set of characters which are | |
8517 not whitespace and are not `{' immediately following the @macro. */ | |
8518 { | |
8519 int start = input_text_offset; | |
8520 int len; | |
8521 | |
8522 for (i = start; | |
8523 (i < size_of_input_text) && | |
8524 (input_text[i] != '{') && | |
8525 (!cr_or_whitespace (input_text[i])); | |
8526 i++); | |
8527 | |
8528 len = i - start; | |
8529 name = (char *)xmalloc (1 + len); | |
8530 strncpy (name, input_text + start, len); | |
8531 name[len] = '\0'; | |
8532 input_text_offset = i; | |
8533 } | |
8534 | |
8535 skip_whitespace (); | |
8536 | |
8537 /* It is not required that the definition of a macro includes an arglist. | |
8538 If not, don't try to get the named parameters, just use a null list. */ | |
8539 if (curchar () == '{') | |
8540 { | |
8541 int arglist_index = 0, arglist_size = 0; | |
8542 int gathering_words = 1; | |
8543 char *word = (char *)NULL; | |
8544 int character; | |
8545 | |
8546 /* Read the words inside of the braces which determine the arglist. | |
8547 These words will be replaced within the body of the macro at | |
8548 execution time. */ | |
8549 | |
8550 input_text_offset++; | |
8551 skip_whitespace_and_newlines (); | |
8552 | |
8553 while (gathering_words) | |
8554 { | |
8555 int len; | |
8556 | |
8557 for (i = input_text_offset; | |
8558 (character = input_text[i]); | |
8559 i++) | |
8560 { | |
8561 switch (character) | |
8562 { | |
8563 case '\n': | |
8564 line_number++; | |
8565 case ' ': | |
8566 case '\t': | |
8567 case ',': | |
8568 case '}': | |
8569 /* Found the end of the current arglist word. Save it. */ | |
8570 len = i - input_text_offset; | |
8571 word = (char *)xmalloc (1 + len); | |
8572 strncpy (word, input_text + input_text_offset, len); | |
8573 word[len] = '\0'; | |
8574 input_text_offset = i; | |
8575 | |
8576 /* Advance to the comma or close-brace that signified | |
8577 the end of the argument. */ | |
8578 while ((character = curchar ()) | |
8579 && character != ',' | |
8580 && character != '}') | |
8581 input_text_offset++; | |
8582 | |
8583 /* Add the word to our list of words. */ | |
8584 if ((arglist_index + 2) >= arglist_size) | |
8585 arglist = (char **)xrealloc | |
8586 (arglist, (arglist_size += 10) * sizeof (char *)); | |
8587 | |
8588 arglist[arglist_index++] = word; | |
8589 arglist[arglist_index] = (char *)NULL; | |
8590 break; | |
8591 } | |
8592 | |
8593 if (character == '}') | |
8594 { | |
8595 input_text_offset++; | |
8596 gathering_words = 0; | |
8597 break; | |
8598 } | |
8599 | |
8600 if (character == ',') | |
8601 { | |
8602 input_text_offset++; | |
8603 skip_whitespace_and_newlines (); | |
8604 i = input_text_offset - 1; | |
8605 } | |
8606 } | |
8607 } | |
8608 } | |
8609 | |
8610 /* Read the text carefully until we find an "@end macro" which | |
8611 matches this one. The text in between is the body of the macro. */ | |
8612 skip_whitespace_and_newlines (); | |
8613 | |
8614 while (depth) | |
8615 { | |
8616 if ((input_text_offset + 9) > size_of_input_text) | |
8617 { | |
8618 int temp_line = line_number; | |
8619 line_number = defining_line; | |
8620 line_error ("%cend macro not found", COMMAND_PREFIX); | |
8621 line_number = temp_line; | |
8622 return; | |
8623 } | |
8624 | |
8625 get_rest_of_line (&line); | |
8626 | |
8627 /* Handle commands only meaningful within a macro. */ | |
8628 if ((*line == COMMAND_PREFIX) && (depth == 1) && | |
8629 (strncmp (line + 1, "allow-recursion", 15) == 0) && | |
8630 (line[16] == '\0' || whitespace (line[16]))) | |
8631 { | |
8632 for (i = 16; whitespace (line[i]); i++); | |
8633 strcpy (line, line + i); | |
8634 flags |= ME_RECURSE; | |
8635 if (!*line) | |
8636 { | |
8637 free (line); | |
8638 continue; | |
8639 } | |
8640 } | |
8641 | |
8642 if ((*line == COMMAND_PREFIX) && (depth == 1) && | |
8643 (strncmp (line + 1, "quote-arg", 9) == 0) && | |
8644 (line[10] == '\0' || whitespace (line[10]))) | |
8645 { | |
8646 for (i = 16; whitespace (line[i]); i++); | |
8647 strcpy (line, line + i); | |
8648 | |
8649 if (arglist && arglist[0] && !arglist[1]) | |
8650 { | |
8651 flags |= ME_QUOTE_ARG; | |
8652 if (!*line) | |
8653 { | |
8654 free (line); | |
8655 continue; | |
8656 } | |
8657 } | |
8658 else | |
8659 { | |
8660 line_error ("%cquote-arg only useful when the macro takes a single argument", | |
8661 COMMAND_PREFIX); | |
8662 } | |
8663 } | |
8664 | |
8665 if ((*line == COMMAND_PREFIX) && | |
8666 (strncmp (line + 1, "macro ", 6) == 0)) | |
8667 depth++; | |
8668 | |
8669 if ((*line == COMMAND_PREFIX) && | |
8670 (strncmp (line + 1, "end macro", 9) == 0)) | |
8671 depth--; | |
8672 | |
8673 if (depth) | |
8674 { | |
8675 if ((int) (body_index + strlen (line) + 3) >= body_size) | |
8676 body = (char *)xrealloc | |
8677 (body, body_size += 3 + strlen (line)); | |
8678 strcpy (body + body_index, line); | |
8679 body_index += strlen (line); | |
8680 body[body_index++] = '\n'; | |
8681 body[body_index] = '\0'; | |
8682 } | |
8683 free (line); | |
8684 } | |
8685 | |
8686 /* We now have the name, the arglist, and the body. However, BODY | |
8687 includes the final newline which preceded the `@end macro' text. | |
8688 Delete it. */ | |
8689 if (body && strlen (body)) | |
8690 body[strlen (body) - 1] = '\0'; | |
8691 | |
8692 add_macro (name, arglist, body, input_filename, defining_line, flags); | |
8693 | |
8694 if (macro_expansion_output_stream) | |
8695 remember_itext (input_text, input_text_offset); | |
8696 } | |
8697 | |
8698 static void | |
8699 cm_unmacro (void) | |
8700 { | |
8701 register int i; | |
8702 char *line, *name; | |
8703 MACRO_DEF *def; | |
8704 | |
8705 if (macro_expansion_output_stream) | |
8706 me_append_before_this_command (); | |
8707 | |
8708 get_rest_of_line (&line); | |
8709 canon_white (line); | |
8710 | |
8711 for (i = 0; line[i] && !whitespace (line[i]); i++); | |
8712 name = (char *)xmalloc (i); | |
8713 strncpy (name, line, i); | |
8714 name[i] = '\0'; | |
8715 | |
8716 def = delete_macro (name); | |
8717 | |
8718 if (def) | |
8719 { | |
8720 free (def->source_file); | |
8721 free (def->name); | |
8722 free (def->body); | |
8723 | |
8724 if (def->arglist) | |
8725 { | |
8726 register int ii; | |
8727 | |
8728 for (ii = 0; def->arglist[ii]; i++) | |
8729 free (def->arglist[ii]); | |
8730 | |
8731 free (def->arglist); | |
8732 } | |
8733 | |
8734 free (def); | |
8735 } | |
8736 | |
8737 free (line); | |
8738 free (name); | |
8739 | |
8740 if (macro_expansion_output_stream) | |
8741 remember_itext (input_text, input_text_offset); | |
8742 } | |
8743 | |
8744 /* How to output sections of the input file verbatim. */ | |
8745 | |
8746 /* Set the value of POINTER's offset to OFFSET. */ | |
8747 static ITEXT * | |
8748 remember_itext (char *pointer, int offset) | |
8749 { | |
8750 register int i; | |
8751 ITEXT *itext = (ITEXT *)NULL; | |
8752 | |
8753 /* If we have no info, initialize a blank list. */ | |
8754 if (!itext_info) | |
8755 { | |
8756 itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *)); | |
8757 for (i = 0; i < itext_size; i++) | |
8758 itext_info[i] = (ITEXT *)NULL; | |
8759 } | |
8760 | |
8761 /* If the pointer is already present in the list, then set the offset. */ | |
8762 for (i = 0; i < itext_size; i++) | |
8763 if ((itext_info[i] != (ITEXT *)NULL) && | |
8764 (itext_info[i]->pointer == pointer)) | |
8765 { | |
8766 itext = itext_info[i]; | |
8767 itext_info[i]->offset = offset; | |
8768 break; | |
8769 } | |
8770 | |
8771 if (i == itext_size) | |
8772 { | |
8773 /* Find a blank slot, (or create a new one), and remember the | |
8774 pointer and offset. */ | |
8775 for (i = 0; i < itext_size; i++) | |
8776 if (itext_info[i] == (ITEXT *)NULL) | |
8777 break; | |
8778 | |
8779 /* If not found, then add some slots. */ | |
8780 if (i == itext_size) | |
8781 { | |
8782 register int j; | |
8783 | |
8784 itext_info = (ITEXT **)xrealloc | |
8785 (itext_info, (itext_size += 10) * sizeof (ITEXT *)); | |
8786 | |
8787 for (j = i; j < itext_size; j++) | |
8788 itext_info[j] = (ITEXT *)NULL; | |
8789 } | |
8790 | |
8791 /* Now add the pointer and the offset. */ | |
8792 itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT)); | |
8793 itext_info[i]->pointer = pointer; | |
8794 itext_info[i]->offset = offset; | |
8795 itext = itext_info[i]; | |
8796 } | |
8797 return (itext); | |
8798 } | |
8799 | |
8800 /* Forget the input text associated with POINTER. */ | |
8801 static void | |
8802 forget_itext (char *pointer) | |
8803 { | |
8804 register int i; | |
8805 | |
8806 for (i = 0; i < itext_size; i++) | |
8807 if (itext_info[i] && (itext_info[i]->pointer == pointer)) | |
8808 { | |
8809 free (itext_info[i]); | |
8810 itext_info[i] = (ITEXT *)NULL; | |
8811 break; | |
8812 } | |
8813 } | |
8814 | |
8815 /* Append the text which appeared in input_text from the last offset to | |
8816 the character just before the command that we are currently executing. */ | |
8817 static void | |
8818 me_append_before_this_command (void) | |
8819 { | |
8820 register int i; | |
8821 | |
8822 for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--); | |
8823 maybe_write_itext (input_text, i); | |
8824 } | |
8825 | |
8826 /* Similar to execute_string (), but only takes a single string argument, | |
8827 and remembers the input text location, etc. */ | |
8828 static void | |
8829 me_execute_string (char *execution_string) | |
8830 { | |
8831 pushfile (); | |
8832 input_text_offset = 0; | |
8833 input_text = execution_string; | |
8834 input_filename = strdup (input_filename); | |
8835 size_of_input_text = strlen (execution_string); | |
8836 | |
8837 remember_itext (execution_string, 0); | |
8838 | |
8839 executing_string++; | |
8840 reader_loop (); | |
8841 popfile (); | |
8842 executing_string--; | |
8843 } | |
8844 | |
8845 /* Append the text which appears in input_text from the last offset to | |
8846 the current OFFSET. */ | |
8847 static void | |
8848 append_to_expansion_output (int offset) | |
8849 { | |
8850 register int i; | |
8851 ITEXT *itext = (ITEXT *)NULL; | |
8852 | |
8853 for (i = 0; i < itext_size; i++) | |
8854 if (itext_info[i] && itext_info[i]->pointer == input_text) | |
8855 { | |
8856 itext = itext_info[i]; | |
8857 break; | |
8858 } | |
8859 | |
8860 if (!itext) | |
8861 itext = remember_itext (input_text, 0); | |
8862 | |
8863 if (offset > itext_info[i]->offset) | |
8864 { | |
8865 write_region_to_macro_output | |
8866 (input_text, itext_info[i]->offset, offset); | |
8867 remember_itext (input_text, offset); | |
8868 } | |
8869 } | |
8870 | |
8871 /* Only write this input text iff it appears in our itext list. */ | |
8872 static void | |
8873 maybe_write_itext (char *pointer, int offset) | |
8874 { | |
8875 register int i; | |
8876 ITEXT *itext = (ITEXT *)NULL; | |
8877 | |
8878 for (i = 0; i < itext_size; i++) | |
8879 if (itext_info[i] && (itext_info[i]->pointer == pointer)) | |
8880 { | |
8881 itext = itext_info[i]; | |
8882 break; | |
8883 } | |
8884 | |
8885 if (itext && (itext->offset < offset)) | |
8886 { | |
8887 write_region_to_macro_output (itext->pointer, itext->offset, offset); | |
8888 remember_itext (pointer, offset); | |
8889 } | |
8890 } | |
8891 | |
8892 static void | |
8893 write_region_to_macro_output (char *string, int start, int end) | |
8894 { | |
8895 if (macro_expansion_output_stream) | |
8896 fwrite (string + start, 1, end - start, macro_expansion_output_stream); | |
8897 } | |
8898 | |
8899 #endif /* HAVE_MACROS */ | |
8900 | |
8901 /* Return the length of the array in ARRAY. */ | |
8902 static int | |
8903 array_len (char **array) | |
8904 { | |
8905 register int i = 0; | |
8906 | |
8907 if (array) | |
8908 for (i = 0; array[i] != (char *)NULL; i++); | |
8909 | |
8910 return (i); | |
8911 } | |
8912 | |
8913 static void | |
8914 free_array (char **array) | |
8915 { | |
8916 if (array) | |
8917 { | |
8918 register int i; | |
8919 | |
8920 for (i = 0; array[i] != (char *)NULL; i++) | |
8921 free (array[i]); | |
8922 | |
8923 free (array); | |
8924 } | |
8925 } | |
8926 | |
8927 /* Function is used even when we don't have macros. Although, I have | |
8928 to admit, it is unlikely that you would have a use for it if you | |
8929 aren't using macros. */ | |
8930 static char ** | |
8931 get_brace_args (int quote_single) | |
8932 { | |
8933 char **arglist, *word; | |
8934 int arglist_index, arglist_size; | |
8935 int character, escape_seen, start; | |
8936 int depth = 1; | |
8937 | |
8938 /* There is an arglist in braces here, so gather the args inside of it. */ | |
8939 skip_whitespace_and_newlines (); | |
8940 input_text_offset++; | |
8941 arglist = (char **)NULL; | |
8942 arglist_index = arglist_size = 0; | |
8943 | |
8944 get_arg: | |
8945 skip_whitespace_and_newlines (); | |
8946 start = input_text_offset; | |
8947 escape_seen = 0; | |
8948 | |
8949 while ((character = curchar ())) | |
8950 { | |
8951 if (character == '\\') | |
8952 { | |
8953 input_text_offset += 2; | |
8954 escape_seen = 1; | |
8955 } | |
8956 else if (character == '{') | |
8957 { | |
8958 depth++; | |
8959 input_text_offset++; | |
8960 } | |
8961 else if ((character == ',' && !quote_single) || | |
8962 ((character == '}') && depth == 1)) | |
8963 { | |
8964 int len = input_text_offset - start; | |
8965 | |
8966 if (len || (character != '}')) | |
8967 { | |
8968 word = (char *)xmalloc (1 + len); | |
8969 strncpy (word, input_text + start, len); | |
8970 word[len] = '\0'; | |
8971 | |
8972 /* Clean up escaped characters. */ | |
8973 if (escape_seen) | |
8974 { | |
8975 register int i; | |
8976 | |
8977 for (i = 0; word[i]; i++) | |
8978 if (word[i] == '\\') | |
8979 memcpy (word + i, word + i + 1, strlen (word + i + 1)); | |
8980 } | |
8981 | |
8982 if (arglist_index + 2 >= arglist_size) | |
8983 arglist = (char **)xrealloc | |
8984 (arglist, (arglist_size += 10) * sizeof (char *)); | |
8985 | |
8986 arglist[arglist_index++] = word; | |
8987 arglist[arglist_index] = (char *)NULL; | |
8988 } | |
8989 | |
8990 input_text_offset++; | |
8991 if (character == '}') | |
8992 break; | |
8993 else | |
8994 goto get_arg; | |
8995 } | |
8996 else if (character == '}') | |
8997 { | |
8998 depth--; | |
8999 input_text_offset++; | |
9000 } | |
9001 else | |
9002 input_text_offset++; | |
9003 } | |
9004 return (arglist); | |
9005 } | |
9006 | |
9007 /* **************************************************************** */ | |
9008 /* */ | |
9009 /* Looking For Include Files */ | |
9010 /* */ | |
9011 /* **************************************************************** */ | |
9012 | |
9013 /* Given a string containing units of information separated by colons, | |
9014 return the next one pointed to by INDEX, or NULL if there are no more. | |
9015 Advance INDEX to the character after the colon. */ | |
9016 static char * | |
9017 extract_colon_unit (char *string, int *indecks) | |
9018 { | |
9019 int i, start; | |
9020 | |
9021 i = *indecks; | |
9022 | |
9023 if (!string || (i >= (int) strlen (string))) | |
9024 return ((char *)NULL); | |
9025 | |
9026 /* Each call to this routine leaves the index pointing at a colon if | |
9027 there is more to the path. If I is > 0, then increment past the | |
9028 `:'. If I is 0, then the path has a leading colon. Trailing colons | |
9029 are handled OK by the `else' part of the if statement; an empty | |
9030 string is returned in that case. */ | |
9031 if (i && string[i] == ':') | |
9032 i++; | |
9033 | |
9034 start = i; | |
9035 | |
9036 while (string[i] && string[i] != ':') i++; | |
9037 | |
9038 *indecks = i; | |
9039 | |
9040 if (i == start) | |
9041 { | |
9042 if (string[i]) | |
9043 (*indecks)++; | |
9044 | |
9045 /* Return "" in the case of a trailing `:'. */ | |
9046 return (strdup ("")); | |
9047 } | |
9048 else | |
9049 { | |
9050 char *value; | |
9051 | |
9052 value = (char *)xmalloc (1 + (i - start)); | |
9053 strncpy (value, &string[start], (i - start)); | |
9054 value [i - start] = '\0'; | |
9055 | |
9056 return (value); | |
9057 } | |
9058 } | |
9059 | |
9060 /* Return the full pathname for FILENAME by searching along PATH. | |
9061 When found, return the stat () info for FILENAME in FINFO. | |
9062 If PATH is NULL, only the current directory is searched. | |
9063 If the file could not be found, return a NULL pointer. */ | |
9064 static char * | |
9065 get_file_info_in_path (char *filename, char *path, struct stat *finfo) | |
9066 { | |
9067 char *dir; | |
9068 int result, indecks = 0; | |
9069 | |
9070 if (path == (char *)NULL) | |
9071 path = "."; | |
9072 | |
9073 /* Handle absolute pathnames. "./foo", "/foo", "../foo". */ | |
9074 if (*filename == '/' || | |
9075 (*filename == '.' && | |
9076 (filename[1] == '/' || | |
9077 (filename[1] == '.' && filename[2] == '/')))) | |
9078 { | |
9079 if (stat (filename, finfo) == 0) | |
9080 return (strdup (filename)); | |
9081 else | |
9082 return ((char *)NULL); | |
9083 } | |
9084 | |
9085 while ((dir = extract_colon_unit (path, &indecks))) | |
9086 { | |
9087 char *fullpath; | |
9088 | |
9089 if (!*dir) | |
9090 { | |
9091 free (dir); | |
9092 dir = strdup ("."); | |
9093 } | |
9094 | |
9095 fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename)); | |
9096 sprintf (fullpath, "%s/%s", dir, filename); | |
9097 free (dir); | |
9098 | |
9099 result = stat (fullpath, finfo); | |
9100 | |
9101 if (result == 0) | |
9102 return (fullpath); | |
9103 else | |
9104 free (fullpath); | |
9105 } | |
9106 return ((char *)NULL); | |
9107 } |