build: merge in bootstrap changes from gnulib
[coreutils.git] / src / ptx.c
blobff5e4331548112078d9b5953267e919cbd94c4fc
1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990-1991, 1993, 1998-2011 Free Software Foundation, Inc.
3 François Pinard <pinard@iro.umontreal.ca>, 1988.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 François Pinard <pinard@iro.umontreal.ca> */
20 #include <config.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24 #include "system.h"
25 #include <regex.h>
26 #include "argmatch.h"
27 #include "diacrit.h"
28 #include "error.h"
29 #include "fadvise.h"
30 #include "quote.h"
31 #include "quotearg.h"
32 #include "read-file.h"
33 #include "stdio--.h"
34 #include "xstrtol.h"
36 /* The official name of this program (e.g., no `g' prefix). */
37 #define PROGRAM_NAME "ptx"
39 /* TRANSLATORS: Please translate "F. Pinard" to "François Pinard"
40 if "ç" (c-with-cedilla) is available in the translation's character
41 set and encoding. */
42 #define AUTHORS proper_name_utf8 ("F. Pinard", "Fran\xc3\xa7ois Pinard")
44 /* Number of possible characters in a byte. */
45 #define CHAR_SET_SIZE 256
47 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
48 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
49 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
50 #define OCTTOBIN(C) ((C) - '0')
52 /* Debugging the memory allocator. */
54 #if WITH_DMALLOC
55 # define MALLOC_FUNC_CHECK 1
56 # include <dmalloc.h>
57 #endif
59 /* Global definitions. */
61 /* FIXME: There are many unchecked integer overflows in this file,
62 that will cause this command to misbehave given large inputs or
63 options. Many of the "int" values below should be "size_t" or
64 something else like that. */
66 /* Program options. */
68 enum Format
70 UNKNOWN_FORMAT, /* output format still unknown */
71 DUMB_FORMAT, /* output for a dumb terminal */
72 ROFF_FORMAT, /* output for `troff' or `nroff' */
73 TEX_FORMAT /* output for `TeX' or `LaTeX' */
76 static bool gnu_extensions = true; /* trigger all GNU extensions */
77 static bool auto_reference = false; /* refs are `file_name:line_number:' */
78 static bool input_reference = false; /* refs at beginning of input lines */
79 static bool right_reference = false; /* output refs after right context */
80 static int line_width = 72; /* output line width in characters */
81 static int gap_size = 3; /* number of spaces between output fields */
82 static const char *truncation_string = "/";
83 /* string used to mark line truncations */
84 static const char *macro_name = "xx"; /* macro name for roff or TeX output */
85 static enum Format output_format = UNKNOWN_FORMAT;
86 /* output format */
88 static bool ignore_case = false; /* fold lower to upper for sorting */
89 static const char *break_file = NULL; /* name of the `Break chars' file */
90 static const char *only_file = NULL; /* name of the `Only words' file */
91 static const char *ignore_file = NULL; /* name of the `Ignore words' file */
93 /* Options that use regular expressions. */
94 struct regex_data
96 /* The original regular expression, as a string. */
97 char const *string;
99 /* The compiled regular expression, and its fastmap. */
100 struct re_pattern_buffer pattern;
101 char fastmap[UCHAR_MAX + 1];
104 static struct regex_data context_regex; /* end of context */
105 static struct regex_data word_regex; /* keyword */
107 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
108 whole file. A WORD is something smaller, its length should fit in a
109 short integer. A WORD_TABLE may contain several WORDs. */
111 typedef struct
113 char *start; /* pointer to beginning of region */
114 char *end; /* pointer to end + 1 of region */
116 BLOCK;
118 typedef struct
120 char *start; /* pointer to beginning of region */
121 short int size; /* length of the region */
123 WORD;
125 typedef struct
127 WORD *start; /* array of WORDs */
128 size_t alloc; /* allocated length */
129 size_t length; /* number of used entries */
131 WORD_TABLE;
133 /* Pattern description tables. */
135 /* For each character, provide its folded equivalent. */
136 static unsigned char folded_chars[CHAR_SET_SIZE];
138 /* End of context pattern register indices. */
139 static struct re_registers context_regs;
141 /* Keyword pattern register indices. */
142 static struct re_registers word_regs;
144 /* A word characters fastmap is used only when no word regexp has been
145 provided. A word is then made up of a sequence of one or more characters
146 allowed by the fastmap. Contains !0 if character allowed in word. Not
147 only this is faster in most cases, but it simplifies the implementation
148 of the Break files. */
149 static char word_fastmap[CHAR_SET_SIZE];
151 /* Maximum length of any word read. */
152 static int maximum_word_length;
154 /* Maximum width of any reference used. */
155 static int reference_max_width;
157 /* Ignore and Only word tables. */
159 static WORD_TABLE ignore_table; /* table of words to ignore */
160 static WORD_TABLE only_table; /* table of words to select */
162 /* Source text table, and scanning macros. */
164 static int number_input_files; /* number of text input files */
165 static int total_line_count; /* total number of lines seen so far */
166 static const char **input_file_name; /* array of text input file names */
167 static int *file_line_count; /* array of `total_line_count' values at end */
169 static BLOCK text_buffer; /* file to study */
171 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
173 #define SKIP_NON_WHITE(cursor, limit) \
174 while (cursor < limit && ! isspace (to_uchar (*cursor))) \
175 cursor++
177 #define SKIP_WHITE(cursor, limit) \
178 while (cursor < limit && isspace (to_uchar (*cursor))) \
179 cursor++
181 #define SKIP_WHITE_BACKWARDS(cursor, start) \
182 while (cursor > start && isspace (to_uchar (cursor[-1]))) \
183 cursor--
185 #define SKIP_SOMETHING(cursor, limit) \
186 if (word_regex.string) \
188 regoff_t count; \
189 count = re_match (&word_regex.pattern, cursor, limit - cursor, 0, NULL); \
190 if (count == -2) \
191 matcher_error (); \
192 cursor += count == -1 ? 1 : count; \
194 else if (word_fastmap[to_uchar (*cursor)]) \
195 while (cursor < limit && word_fastmap[to_uchar (*cursor)]) \
196 cursor++; \
197 else \
198 cursor++
200 /* Occurrences table.
202 The `keyword' pointer provides the central word, which is surrounded
203 by a left context and a right context. The `keyword' and `length'
204 field allow full 8-bit characters keys, even including NULs. At other
205 places in this program, the name `keyafter' refers to the keyword
206 followed by its right context.
208 The left context does not extend, towards the beginning of the file,
209 further than a distance given by the `left' value. This value is
210 relative to the keyword beginning, it is usually negative. This
211 insures that, except for white space, we will never have to backward
212 scan the source text, when it is time to generate the final output
213 lines.
215 The right context, indirectly attainable through the keyword end, does
216 not extend, towards the end of the file, further than a distance given
217 by the `right' value. This value is relative to the keyword
218 beginning, it is usually positive.
220 When automatic references are used, the `reference' value is the
221 overall line number in all input files read so far, in this case, it
222 is of type (int). When input references are used, the `reference'
223 value indicates the distance between the keyword beginning and the
224 start of the reference field, it is of type (DELTA) and usually
225 negative. */
227 typedef short int DELTA; /* to hold displacement within one context */
229 typedef struct
231 WORD key; /* description of the keyword */
232 DELTA left; /* distance to left context start */
233 DELTA right; /* distance to right context end */
234 int reference; /* reference descriptor */
236 OCCURS;
238 /* The various OCCURS tables are indexed by the language. But the time
239 being, there is no such multiple language support. */
241 static OCCURS *occurs_table[1]; /* all words retained from the read text */
242 static size_t occurs_alloc[1]; /* allocated size of occurs_table */
243 static size_t number_of_occurs[1]; /* number of used slots in occurs_table */
246 /* Communication among output routines. */
248 /* Indicate if special output processing is requested for each character. */
249 static char edited_flag[CHAR_SET_SIZE];
251 static int half_line_width; /* half of line width, reference excluded */
252 static int before_max_width; /* maximum width of before field */
253 static int keyafter_max_width; /* maximum width of keyword-and-after field */
254 static int truncation_string_length;/* length of string that flags truncation */
256 /* When context is limited by lines, wraparound may happen on final output:
257 the `head' pointer gives access to some supplementary left context which
258 will be seen at the end of the output line, the `tail' pointer gives
259 access to some supplementary right context which will be seen at the
260 beginning of the output line. */
262 static BLOCK tail; /* tail field */
263 static int tail_truncation; /* flag truncation after the tail field */
265 static BLOCK before; /* before field */
266 static int before_truncation; /* flag truncation before the before field */
268 static BLOCK keyafter; /* keyword-and-after field */
269 static int keyafter_truncation; /* flag truncation after the keyafter field */
271 static BLOCK head; /* head field */
272 static int head_truncation; /* flag truncation before the head field */
274 static BLOCK reference; /* reference field for input reference mode */
276 /* Miscellaneous routines. */
278 /* Diagnose an error in the regular expression matcher. Then exit. */
280 static void ATTRIBUTE_NORETURN
281 matcher_error (void)
283 error (0, errno, _("error in regular expression matcher"));
284 exit (EXIT_FAILURE);
287 /*------------------------------------------------------.
288 | Duplicate string STRING, while evaluating \-escapes. |
289 `------------------------------------------------------*/
291 /* Loosely adapted from GNU sh-utils printf.c code. */
293 static char *
294 copy_unescaped_string (const char *string)
296 char *result; /* allocated result */
297 char *cursor; /* cursor in result */
298 int value; /* value of \nnn escape */
299 int length; /* length of \nnn escape */
301 result = xmalloc (strlen (string) + 1);
302 cursor = result;
304 while (*string)
306 if (*string == '\\')
308 string++;
309 switch (*string)
311 case 'x': /* \xhhh escape, 3 chars maximum */
312 value = 0;
313 for (length = 0, string++;
314 length < 3 && isxdigit (to_uchar (*string));
315 length++, string++)
316 value = value * 16 + HEXTOBIN (*string);
317 if (length == 0)
319 *cursor++ = '\\';
320 *cursor++ = 'x';
322 else
323 *cursor++ = value;
324 break;
326 case '0': /* \0ooo escape, 3 chars maximum */
327 value = 0;
328 for (length = 0, string++;
329 length < 3 && ISODIGIT (*string);
330 length++, string++)
331 value = value * 8 + OCTTOBIN (*string);
332 *cursor++ = value;
333 break;
335 case 'a': /* alert */
336 #if __STDC__
337 *cursor++ = '\a';
338 #else
339 *cursor++ = 7;
340 #endif
341 string++;
342 break;
344 case 'b': /* backspace */
345 *cursor++ = '\b';
346 string++;
347 break;
349 case 'c': /* cancel the rest of the output */
350 while (*string)
351 string++;
352 break;
354 case 'f': /* form feed */
355 *cursor++ = '\f';
356 string++;
357 break;
359 case 'n': /* new line */
360 *cursor++ = '\n';
361 string++;
362 break;
364 case 'r': /* carriage return */
365 *cursor++ = '\r';
366 string++;
367 break;
369 case 't': /* horizontal tab */
370 *cursor++ = '\t';
371 string++;
372 break;
374 case 'v': /* vertical tab */
375 #if __STDC__
376 *cursor++ = '\v';
377 #else
378 *cursor++ = 11;
379 #endif
380 string++;
381 break;
383 case '\0': /* lone backslash at end of string */
384 /* ignore it */
385 break;
387 default:
388 *cursor++ = '\\';
389 *cursor++ = *string++;
390 break;
393 else
394 *cursor++ = *string++;
397 *cursor = '\0';
398 return result;
401 /*--------------------------------------------------------------------------.
402 | Compile the regex represented by REGEX, diagnose and abort if any error. |
403 `--------------------------------------------------------------------------*/
405 static void
406 compile_regex (struct regex_data *regex)
408 struct re_pattern_buffer *pattern = &regex->pattern;
409 char const *string = regex->string;
410 char const *message;
412 pattern->buffer = NULL;
413 pattern->allocated = 0;
414 pattern->fastmap = regex->fastmap;
415 pattern->translate = ignore_case ? folded_chars : NULL;
417 message = re_compile_pattern (string, strlen (string), pattern);
418 if (message)
419 error (EXIT_FAILURE, 0, _("%s (for regexp %s)"), message, quote (string));
421 /* The fastmap should be compiled before `re_match'. The following
422 call is not mandatory, because `re_search' is always called sooner,
423 and it compiles the fastmap if this has not been done yet. */
425 re_compile_fastmap (pattern);
428 /*------------------------------------------------------------------------.
429 | This will initialize various tables for pattern match and compiles some |
430 | regexps. |
431 `------------------------------------------------------------------------*/
433 static void
434 initialize_regex (void)
436 int character; /* character value */
438 /* Initialize the case folding table. */
440 if (ignore_case)
441 for (character = 0; character < CHAR_SET_SIZE; character++)
442 folded_chars[character] = toupper (character);
444 /* Unless the user already provided a description of the end of line or
445 end of sentence sequence, select an end of line sequence to compile.
446 If the user provided an empty definition, thus disabling end of line
447 or sentence feature, make it NULL to speed up tests. If GNU
448 extensions are enabled, use end of sentence like in GNU emacs. If
449 disabled, use end of lines. */
451 if (context_regex.string)
453 if (!*context_regex.string)
454 context_regex.string = NULL;
456 else if (gnu_extensions && !input_reference)
457 context_regex.string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
458 else
459 context_regex.string = "\n";
461 if (context_regex.string)
462 compile_regex (&context_regex);
464 /* If the user has already provided a non-empty regexp to describe
465 words, compile it. Else, unless this has already been done through
466 a user provided Break character file, construct a fastmap of
467 characters that may appear in a word. If GNU extensions enabled,
468 include only letters of the underlying character set. If disabled,
469 include almost everything, even punctuations; stop only on white
470 space. */
472 if (word_regex.string)
473 compile_regex (&word_regex);
474 else if (!break_file)
476 if (gnu_extensions)
479 /* Simulate \w+. */
481 for (character = 0; character < CHAR_SET_SIZE; character++)
482 word_fastmap[character] = !! isalpha (character);
484 else
487 /* Simulate [^ \t\n]+. */
489 memset (word_fastmap, 1, CHAR_SET_SIZE);
490 word_fastmap[' '] = 0;
491 word_fastmap['\t'] = 0;
492 word_fastmap['\n'] = 0;
497 /*------------------------------------------------------------------------.
498 | This routine will attempt to swallow a whole file name FILE_NAME into a |
499 | contiguous region of memory and return a description of it into BLOCK. |
500 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
502 | Previously, in some cases, white space compression was attempted while |
503 | inputting text. This was defeating some regexps like default end of |
504 | sentence, which checks for two consecutive spaces. If white space |
505 | compression is ever reinstated, it should be in output routines. |
506 `------------------------------------------------------------------------*/
508 static void
509 swallow_file_in_memory (const char *file_name, BLOCK *block)
511 size_t used_length; /* used length in memory buffer */
513 /* As special cases, a file name which is NULL or "-" indicates standard
514 input, which is already opened. In all other cases, open the file from
515 its name. */
516 bool using_stdin = !file_name || !*file_name || STREQ (file_name, "-");
517 if (using_stdin)
518 block->start = fread_file (stdin, &used_length);
519 else
520 block->start = read_file (file_name, &used_length);
522 if (!block->start)
523 error (EXIT_FAILURE, errno, "%s", quote (using_stdin ? "-" : file_name));
525 block->end = block->start + used_length;
528 /* Sort and search routines. */
530 /*--------------------------------------------------------------------------.
531 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
532 | Return less than 0 if the first word goes before the second; return |
533 | greater than 0 if the first word goes after the second. |
535 | If a word is indeed a prefix of the other, the shorter should go first. |
536 `--------------------------------------------------------------------------*/
538 static int
539 compare_words (const void *void_first, const void *void_second)
541 #define first ((const WORD *) void_first)
542 #define second ((const WORD *) void_second)
543 int length; /* minimum of two lengths */
544 int counter; /* cursor in words */
545 int value; /* value of comparison */
547 length = first->size < second->size ? first->size : second->size;
549 if (ignore_case)
551 for (counter = 0; counter < length; counter++)
553 value = (folded_chars [to_uchar (first->start[counter])]
554 - folded_chars [to_uchar (second->start[counter])]);
555 if (value != 0)
556 return value;
559 else
561 for (counter = 0; counter < length; counter++)
563 value = (to_uchar (first->start[counter])
564 - to_uchar (second->start[counter]));
565 if (value != 0)
566 return value;
570 return first->size - second->size;
571 #undef first
572 #undef second
575 /*-----------------------------------------------------------------------.
576 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
577 | go first. In case of a tie, preserve the original order through a |
578 | pointer comparison. |
579 `-----------------------------------------------------------------------*/
581 static int
582 compare_occurs (const void *void_first, const void *void_second)
584 #define first ((const OCCURS *) void_first)
585 #define second ((const OCCURS *) void_second)
586 int value;
588 value = compare_words (&first->key, &second->key);
589 return value == 0 ? first->key.start - second->key.start : value;
590 #undef first
591 #undef second
594 /*------------------------------------------------------------.
595 | Return !0 if WORD appears in TABLE. Uses a binary search. |
596 `------------------------------------------------------------*/
598 static int _GL_ATTRIBUTE_PURE
599 search_table (WORD *word, WORD_TABLE *table)
601 int lowest; /* current lowest possible index */
602 int highest; /* current highest possible index */
603 int middle; /* current middle index */
604 int value; /* value from last comparison */
606 lowest = 0;
607 highest = table->length - 1;
608 while (lowest <= highest)
610 middle = (lowest + highest) / 2;
611 value = compare_words (word, table->start + middle);
612 if (value < 0)
613 highest = middle - 1;
614 else if (value > 0)
615 lowest = middle + 1;
616 else
617 return 1;
619 return 0;
622 /*---------------------------------------------------------------------.
623 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
624 | take intermediate copies or table elements, so the sort will be |
625 | stabilized throughout the comparison routine. |
626 `---------------------------------------------------------------------*/
628 static void
629 sort_found_occurs (void)
632 /* Only one language for the time being. */
634 qsort (occurs_table[0], number_of_occurs[0], sizeof **occurs_table,
635 compare_occurs);
638 /* Parameter files reading routines. */
640 /*----------------------------------------------------------------------.
641 | Read a file named FILE_NAME, containing a set of break characters. |
642 | Build a content to the array word_fastmap in which all characters are |
643 | allowed except those found in the file. Characters may be repeated. |
644 `----------------------------------------------------------------------*/
646 static void
647 digest_break_file (const char *file_name)
649 BLOCK file_contents; /* to receive a copy of the file */
650 char *cursor; /* cursor in file copy */
652 swallow_file_in_memory (file_name, &file_contents);
654 /* Make the fastmap and record the file contents in it. */
656 memset (word_fastmap, 1, CHAR_SET_SIZE);
657 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
658 word_fastmap[to_uchar (*cursor)] = 0;
660 if (!gnu_extensions)
663 /* If GNU extensions are enabled, the only way to avoid newline as
664 a break character is to write all the break characters in the
665 file with no newline at all, not even at the end of the file.
666 If disabled, spaces, tabs and newlines are always considered as
667 break characters even if not included in the break file. */
669 word_fastmap[' '] = 0;
670 word_fastmap['\t'] = 0;
671 word_fastmap['\n'] = 0;
674 /* Return the space of the file, which is no more required. */
676 free (file_contents.start);
679 /*-----------------------------------------------------------------------.
680 | Read a file named FILE_NAME, containing one word per line, then |
681 | construct in TABLE a table of WORD descriptors for them. The routine |
682 | swallows the whole file in memory; this is at the expense of space |
683 | needed for newlines, which are useless; however, the reading is fast. |
684 `-----------------------------------------------------------------------*/
686 static void
687 digest_word_file (const char *file_name, WORD_TABLE *table)
689 BLOCK file_contents; /* to receive a copy of the file */
690 char *cursor; /* cursor in file copy */
691 char *word_start; /* start of the current word */
693 swallow_file_in_memory (file_name, &file_contents);
695 table->start = NULL;
696 table->alloc = 0;
697 table->length = 0;
699 /* Read the whole file. */
701 cursor = file_contents.start;
702 while (cursor < file_contents.end)
705 /* Read one line, and save the word in contains. */
707 word_start = cursor;
708 while (cursor < file_contents.end && *cursor != '\n')
709 cursor++;
711 /* Record the word in table if it is not empty. */
713 if (cursor > word_start)
715 if (table->length == table->alloc)
717 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
718 xalloc_die ();
719 table->alloc = table->alloc * 2 + 1;
720 table->start = xrealloc (table->start,
721 table->alloc * sizeof *table->start);
724 table->start[table->length].start = word_start;
725 table->start[table->length].size = cursor - word_start;
726 table->length++;
729 /* This test allows for an incomplete line at end of file. */
731 if (cursor < file_contents.end)
732 cursor++;
735 /* Finally, sort all the words read. */
737 qsort (table->start, table->length, sizeof table->start[0], compare_words);
740 /* Keyword recognition and selection. */
742 /*----------------------------------------------------------------------.
743 | For each keyword in the source text, constructs an OCCURS structure. |
744 `----------------------------------------------------------------------*/
746 static void
747 find_occurs_in_text (void)
749 char *cursor; /* for scanning the source text */
750 char *scan; /* for scanning the source text also */
751 char *line_start; /* start of the current input line */
752 char *line_scan; /* newlines scanned until this point */
753 int reference_length; /* length of reference in input mode */
754 WORD possible_key; /* possible key, to ease searches */
755 OCCURS *occurs_cursor; /* current OCCURS under construction */
757 char *context_start; /* start of left context */
758 char *context_end; /* end of right context */
759 char *word_start; /* start of word */
760 char *word_end; /* end of word */
761 char *next_context_start; /* next start of left context */
763 /* reference_length is always used within `if (input_reference)'.
764 However, GNU C diagnoses that it may be used uninitialized. The
765 following assignment is merely to shut it up. */
767 reference_length = 0;
769 /* Tracking where lines start is helpful for reference processing. In
770 auto reference mode, this allows counting lines. In input reference
771 mode, this permits finding the beginning of the references.
773 The first line begins with the file, skip immediately this very first
774 reference in input reference mode, to help further rejection any word
775 found inside it. Also, unconditionally assigning these variable has
776 the happy effect of shutting up lint. */
778 line_start = text_buffer.start;
779 line_scan = line_start;
780 if (input_reference)
782 SKIP_NON_WHITE (line_scan, text_buffer.end);
783 reference_length = line_scan - line_start;
784 SKIP_WHITE (line_scan, text_buffer.end);
787 /* Process the whole buffer, one line or one sentence at a time. */
789 for (cursor = text_buffer.start;
790 cursor < text_buffer.end;
791 cursor = next_context_start)
794 /* `context_start' gets initialized before the processing of each
795 line, or once for the whole buffer if no end of line or sentence
796 sequence separator. */
798 context_start = cursor;
800 /* If an end of line or end of sentence sequence is defined and
801 non-empty, `next_context_start' will be recomputed to be the end of
802 each line or sentence, before each one is processed. If no such
803 sequence, then `next_context_start' is set at the end of the whole
804 buffer, which is then considered to be a single line or sentence.
805 This test also accounts for the case of an incomplete line or
806 sentence at the end of the buffer. */
808 next_context_start = text_buffer.end;
809 if (context_regex.string)
810 switch (re_search (&context_regex.pattern, cursor,
811 text_buffer.end - cursor,
812 0, text_buffer.end - cursor, &context_regs))
814 case -2:
815 matcher_error ();
817 case -1:
818 break;
820 default:
821 next_context_start = cursor + context_regs.end[0];
822 break;
825 /* Include the separator into the right context, but not any suffix
826 white space in this separator; this insures it will be seen in
827 output and will not take more space than necessary. */
829 context_end = next_context_start;
830 SKIP_WHITE_BACKWARDS (context_end, context_start);
832 /* Read and process a single input line or sentence, one word at a
833 time. */
835 while (1)
837 if (word_regex.string)
839 /* If a word regexp has been compiled, use it to skip at the
840 beginning of the next word. If there is no such word, exit
841 the loop. */
844 regoff_t r = re_search (&word_regex.pattern, cursor,
845 context_end - cursor,
846 0, context_end - cursor, &word_regs);
847 if (r == -2)
848 matcher_error ();
849 if (r == -1)
850 break;
851 word_start = cursor + word_regs.start[0];
852 word_end = cursor + word_regs.end[0];
854 else
856 /* Avoid re_search and use the fastmap to skip to the
857 beginning of the next word. If there is no more word in
858 the buffer, exit the loop. */
861 scan = cursor;
862 while (scan < context_end
863 && !word_fastmap[to_uchar (*scan)])
864 scan++;
866 if (scan == context_end)
867 break;
869 word_start = scan;
871 while (scan < context_end
872 && word_fastmap[to_uchar (*scan)])
873 scan++;
875 word_end = scan;
878 /* Skip right to the beginning of the found word. */
880 cursor = word_start;
882 /* Skip any zero length word. Just advance a single position,
883 then go fetch the next word. */
885 if (word_end == word_start)
887 cursor++;
888 continue;
891 /* This is a genuine, non empty word, so save it as a possible
892 key. Then skip over it. Also, maintain the maximum length of
893 all words read so far. It is mandatory to take the maximum
894 length of all words in the file, without considering if they
895 are actually kept or rejected, because backward jumps at output
896 generation time may fall in *any* word. */
898 possible_key.start = cursor;
899 possible_key.size = word_end - word_start;
900 cursor += possible_key.size;
902 if (possible_key.size > maximum_word_length)
903 maximum_word_length = possible_key.size;
905 /* In input reference mode, update `line_start' from its previous
906 value. Count the lines just in case auto reference mode is
907 also selected. If it happens that the word just matched is
908 indeed part of a reference; just ignore it. */
910 if (input_reference)
912 while (line_scan < possible_key.start)
913 if (*line_scan == '\n')
915 total_line_count++;
916 line_scan++;
917 line_start = line_scan;
918 SKIP_NON_WHITE (line_scan, text_buffer.end);
919 reference_length = line_scan - line_start;
921 else
922 line_scan++;
923 if (line_scan > possible_key.start)
924 continue;
927 /* Ignore the word if an `Ignore words' table exists and if it is
928 part of it. Also ignore the word if an `Only words' table and
929 if it is *not* part of it.
931 It is allowed that both tables be used at once, even if this
932 may look strange for now. Just ignore a word that would appear
933 in both. If regexps are eventually implemented for these
934 tables, the Ignore table could then reject words that would
935 have been previously accepted by the Only table. */
937 if (ignore_file && search_table (&possible_key, &ignore_table))
938 continue;
939 if (only_file && !search_table (&possible_key, &only_table))
940 continue;
942 /* A non-empty word has been found. First of all, insure
943 proper allocation of the next OCCURS, and make a pointer to
944 where it will be constructed. */
946 if (number_of_occurs[0] == occurs_alloc[0])
948 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
949 < occurs_alloc[0])
950 xalloc_die ();
951 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
952 occurs_table[0] =
953 xrealloc (occurs_table[0],
954 occurs_alloc[0] * sizeof *occurs_table[0]);
957 occurs_cursor = occurs_table[0] + number_of_occurs[0];
959 /* Define the refence field, if any. */
961 if (auto_reference)
964 /* While auto referencing, update `line_start' from its
965 previous value, counting lines as we go. If input
966 referencing at the same time, `line_start' has been
967 advanced earlier, and the following loop is never really
968 executed. */
970 while (line_scan < possible_key.start)
971 if (*line_scan == '\n')
973 total_line_count++;
974 line_scan++;
975 line_start = line_scan;
976 SKIP_NON_WHITE (line_scan, text_buffer.end);
978 else
979 line_scan++;
981 occurs_cursor->reference = total_line_count;
983 else if (input_reference)
986 /* If only input referencing, `line_start' has been computed
987 earlier to detect the case the word matched would be part
988 of the reference. The reference position is simply the
989 value of `line_start'. */
991 occurs_cursor->reference
992 = (DELTA) (line_start - possible_key.start);
993 if (reference_length > reference_max_width)
994 reference_max_width = reference_length;
997 /* Exclude the reference from the context in simple cases. */
999 if (input_reference && line_start == context_start)
1001 SKIP_NON_WHITE (context_start, context_end);
1002 SKIP_WHITE (context_start, context_end);
1005 /* Completes the OCCURS structure. */
1007 occurs_cursor->key = possible_key;
1008 occurs_cursor->left = context_start - possible_key.start;
1009 occurs_cursor->right = context_end - possible_key.start;
1011 number_of_occurs[0]++;
1016 /* Formatting and actual output - service routines. */
1018 /*-----------------------------------------.
1019 | Prints some NUMBER of spaces on stdout. |
1020 `-----------------------------------------*/
1022 static void
1023 print_spaces (int number)
1025 int counter;
1027 for (counter = number; counter > 0; counter--)
1028 putchar (' ');
1031 /*-------------------------------------.
1032 | Prints the field provided by FIELD. |
1033 `-------------------------------------*/
1035 static void
1036 print_field (BLOCK field)
1038 char *cursor; /* Cursor in field to print */
1039 int base; /* Base character, without diacritic */
1040 int diacritic; /* Diacritic code for the character */
1042 /* Whitespace is not really compressed. Instead, each white space
1043 character (tab, vt, ht etc.) is printed as one single space. */
1045 for (cursor = field.start; cursor < field.end; cursor++)
1047 unsigned char character = *cursor;
1048 if (edited_flag[character])
1051 /* First check if this is a diacriticized character.
1053 This works only for TeX. I do not know how diacriticized
1054 letters work with `roff'. Please someone explain it to me! */
1056 diacritic = todiac (character);
1057 if (diacritic != 0 && output_format == TEX_FORMAT)
1059 base = tobase (character);
1060 switch (diacritic)
1063 case 1: /* Latin diphthongs */
1064 switch (base)
1066 case 'o':
1067 fputs ("\\oe{}", stdout);
1068 break;
1070 case 'O':
1071 fputs ("\\OE{}", stdout);
1072 break;
1074 case 'a':
1075 fputs ("\\ae{}", stdout);
1076 break;
1078 case 'A':
1079 fputs ("\\AE{}", stdout);
1080 break;
1082 default:
1083 putchar (' ');
1085 break;
1087 case 2: /* Acute accent */
1088 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1089 break;
1091 case 3: /* Grave accent */
1092 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1093 break;
1095 case 4: /* Circumflex accent */
1096 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1097 break;
1099 case 5: /* Diaeresis */
1100 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1101 break;
1103 case 6: /* Tilde accent */
1104 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1105 break;
1107 case 7: /* Cedilla */
1108 printf ("\\c{%c}", base);
1109 break;
1111 case 8: /* Small circle beneath */
1112 switch (base)
1114 case 'a':
1115 fputs ("\\aa{}", stdout);
1116 break;
1118 case 'A':
1119 fputs ("\\AA{}", stdout);
1120 break;
1122 default:
1123 putchar (' ');
1125 break;
1127 case 9: /* Strike through */
1128 switch (base)
1130 case 'o':
1131 fputs ("\\o{}", stdout);
1132 break;
1134 case 'O':
1135 fputs ("\\O{}", stdout);
1136 break;
1138 default:
1139 putchar (' ');
1141 break;
1144 else
1146 /* This is not a diacritic character, so handle cases which are
1147 really specific to `roff' or TeX. All white space processing
1148 is done as the default case of this switch. */
1150 switch (character)
1152 case '"':
1153 /* In roff output format, double any quote. */
1154 putchar ('"');
1155 putchar ('"');
1156 break;
1158 case '$':
1159 case '%':
1160 case '&':
1161 case '#':
1162 case '_':
1163 /* In TeX output format, precede these with a backslash. */
1164 putchar ('\\');
1165 putchar (character);
1166 break;
1168 case '{':
1169 case '}':
1170 /* In TeX output format, precede these with a backslash and
1171 force mathematical mode. */
1172 printf ("$\\%c$", character);
1173 break;
1175 case '\\':
1176 /* In TeX output mode, request production of a backslash. */
1177 fputs ("\\backslash{}", stdout);
1178 break;
1180 default:
1181 /* Any other flagged character produces a single space. */
1182 putchar (' ');
1185 else
1186 putchar (*cursor);
1190 /* Formatting and actual output - planning routines. */
1192 /*--------------------------------------------------------------------.
1193 | From information collected from command line options and input file |
1194 | readings, compute and fix some output parameter values. |
1195 `--------------------------------------------------------------------*/
1197 static void
1198 fix_output_parameters (void)
1200 int file_index; /* index in text input file arrays */
1201 int line_ordinal; /* line ordinal value for reference */
1202 char ordinal_string[12]; /* edited line ordinal for reference */
1203 int reference_width; /* width for the whole reference */
1204 int character; /* character ordinal */
1205 const char *cursor; /* cursor in some constant strings */
1207 /* In auto reference mode, the maximum width of this field is
1208 precomputed and subtracted from the overall line width. Add one for
1209 the column which separate the file name from the line number. */
1211 if (auto_reference)
1213 reference_max_width = 0;
1214 for (file_index = 0; file_index < number_input_files; file_index++)
1216 line_ordinal = file_line_count[file_index] + 1;
1217 if (file_index > 0)
1218 line_ordinal -= file_line_count[file_index - 1];
1219 sprintf (ordinal_string, "%d", line_ordinal);
1220 reference_width = strlen (ordinal_string);
1221 if (input_file_name[file_index])
1222 reference_width += strlen (input_file_name[file_index]);
1223 if (reference_width > reference_max_width)
1224 reference_max_width = reference_width;
1226 reference_max_width++;
1227 reference.start = xmalloc ((size_t) reference_max_width + 1);
1230 /* If the reference appears to the left of the output line, reserve some
1231 space for it right away, including one gap size. */
1233 if ((auto_reference || input_reference) && !right_reference)
1234 line_width -= reference_max_width + gap_size;
1236 /* The output lines, minimally, will contain from left to right a left
1237 context, a gap, and a keyword followed by the right context with no
1238 special intervening gap. Half of the line width is dedicated to the
1239 left context and the gap, the other half is dedicated to the keyword
1240 and the right context; these values are computed once and for all here.
1241 There also are tail and head wrap around fields, used when the keyword
1242 is near the beginning or the end of the line, or when some long word
1243 cannot fit in, but leave place from wrapped around shorter words. The
1244 maximum width of these fields are recomputed separately for each line,
1245 on a case by case basis. It is worth noting that it cannot happen that
1246 both the tail and head fields are used at once. */
1248 half_line_width = line_width / 2;
1249 before_max_width = half_line_width - gap_size;
1250 keyafter_max_width = half_line_width;
1252 /* If truncation_string is the empty string, make it NULL to speed up
1253 tests. In this case, truncation_string_length will never get used, so
1254 there is no need to set it. */
1256 if (truncation_string && *truncation_string)
1257 truncation_string_length = strlen (truncation_string);
1258 else
1259 truncation_string = NULL;
1261 if (gnu_extensions)
1264 /* When flagging truncation at the left of the keyword, the
1265 truncation mark goes at the beginning of the before field,
1266 unless there is a head field, in which case the mark goes at the
1267 left of the head field. When flagging truncation at the right
1268 of the keyword, the mark goes at the end of the keyafter field,
1269 unless there is a tail field, in which case the mark goes at the
1270 end of the tail field. Only eight combination cases could arise
1271 for truncation marks:
1273 . None.
1274 . One beginning the before field.
1275 . One beginning the head field.
1276 . One ending the keyafter field.
1277 . One ending the tail field.
1278 . One beginning the before field, another ending the keyafter field.
1279 . One ending the tail field, another beginning the before field.
1280 . One ending the keyafter field, another beginning the head field.
1282 So, there is at most two truncation marks, which could appear both
1283 on the left side of the center of the output line, both on the
1284 right side, or one on either side. */
1286 before_max_width -= 2 * truncation_string_length;
1287 if (before_max_width < 0)
1288 before_max_width = 0;
1289 keyafter_max_width -= 2 * truncation_string_length;
1291 else
1294 /* I never figured out exactly how UNIX' ptx plans the output width
1295 of its various fields. If GNU extensions are disabled, do not
1296 try computing the field widths correctly; instead, use the
1297 following formula, which does not completely imitate UNIX' ptx,
1298 but almost. */
1300 keyafter_max_width -= 2 * truncation_string_length + 1;
1303 /* Compute which characters need special output processing. Initialize
1304 by flagging any white space character. Some systems do not consider
1305 form feed as a space character, but we do. */
1307 for (character = 0; character < CHAR_SET_SIZE; character++)
1308 edited_flag[character] = !! isspace (character);
1309 edited_flag['\f'] = 1;
1311 /* Complete the special character flagging according to selected output
1312 format. */
1314 switch (output_format)
1316 case UNKNOWN_FORMAT:
1317 /* Should never happen. */
1319 case DUMB_FORMAT:
1320 break;
1322 case ROFF_FORMAT:
1324 /* `Quote' characters should be doubled. */
1326 edited_flag['"'] = 1;
1327 break;
1329 case TEX_FORMAT:
1331 /* Various characters need special processing. */
1333 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1334 edited_flag[to_uchar (*cursor)] = 1;
1336 /* Any character with 8th bit set will print to a single space, unless
1337 it is diacriticized. */
1339 for (character = 0200; character < CHAR_SET_SIZE; character++)
1340 edited_flag[character] = todiac (character) != 0;
1341 break;
1345 /*------------------------------------------------------------------.
1346 | Compute the position and length of all the output fields, given a |
1347 | pointer to some OCCURS. |
1348 `------------------------------------------------------------------*/
1350 static void
1351 define_all_fields (OCCURS *occurs)
1353 int tail_max_width; /* allowable width of tail field */
1354 int head_max_width; /* allowable width of head field */
1355 char *cursor; /* running cursor in source text */
1356 char *left_context_start; /* start of left context */
1357 char *right_context_end; /* end of right context */
1358 char *left_field_start; /* conservative start for `head'/`before' */
1359 int file_index; /* index in text input file arrays */
1360 const char *file_name; /* file name for reference */
1361 int line_ordinal; /* line ordinal for reference */
1363 /* Define `keyafter', start of left context and end of right context.
1364 `keyafter' starts at the saved position for keyword and extend to the
1365 right from the end of the keyword, eating separators or full words, but
1366 not beyond maximum allowed width for `keyafter' field or limit for the
1367 right context. Suffix spaces will be removed afterwards. */
1369 keyafter.start = occurs->key.start;
1370 keyafter.end = keyafter.start + occurs->key.size;
1371 left_context_start = keyafter.start + occurs->left;
1372 right_context_end = keyafter.start + occurs->right;
1374 cursor = keyafter.end;
1375 while (cursor < right_context_end
1376 && cursor <= keyafter.start + keyafter_max_width)
1378 keyafter.end = cursor;
1379 SKIP_SOMETHING (cursor, right_context_end);
1381 if (cursor <= keyafter.start + keyafter_max_width)
1382 keyafter.end = cursor;
1384 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1386 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1388 /* When the left context is wide, it might take some time to catch up from
1389 the left context boundary to the beginning of the `head' or `before'
1390 fields. So, in this case, to speed the catchup, we jump back from the
1391 keyword, using some secure distance, possibly falling in the middle of
1392 a word. A secure backward jump would be at least half the maximum
1393 width of a line, plus the size of the longest word met in the whole
1394 input. We conclude this backward jump by a skip forward of at least
1395 one word. In this manner, we should not inadvertently accept only part
1396 of a word. From the reached point, when it will be time to fix the
1397 beginning of `head' or `before' fields, we will skip forward words or
1398 delimiters until we get sufficiently near. */
1400 if (-occurs->left > half_line_width + maximum_word_length)
1402 left_field_start
1403 = keyafter.start - (half_line_width + maximum_word_length);
1404 SKIP_SOMETHING (left_field_start, keyafter.start);
1406 else
1407 left_field_start = keyafter.start + occurs->left;
1409 /* `before' certainly ends at the keyword, but not including separating
1410 spaces. It starts after than the saved value for the left context, by
1411 advancing it until it falls inside the maximum allowed width for the
1412 before field. There will be no prefix spaces either. `before' only
1413 advances by skipping single separators or whole words. */
1415 before.start = left_field_start;
1416 before.end = keyafter.start;
1417 SKIP_WHITE_BACKWARDS (before.end, before.start);
1419 while (before.start + before_max_width < before.end)
1420 SKIP_SOMETHING (before.start, before.end);
1422 if (truncation_string)
1424 cursor = before.start;
1425 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1426 before_truncation = cursor > left_context_start;
1428 else
1429 before_truncation = 0;
1431 SKIP_WHITE (before.start, text_buffer.end);
1433 /* The tail could not take more columns than what has been left in the
1434 left context field, and a gap is mandatory. It starts after the
1435 right context, and does not contain prefixed spaces. It ends at
1436 the end of line, the end of buffer or when the tail field is full,
1437 whichever comes first. It cannot contain only part of a word, and
1438 has no suffixed spaces. */
1440 tail_max_width
1441 = before_max_width - (before.end - before.start) - gap_size;
1443 if (tail_max_width > 0)
1445 tail.start = keyafter.end;
1446 SKIP_WHITE (tail.start, text_buffer.end);
1448 tail.end = tail.start;
1449 cursor = tail.end;
1450 while (cursor < right_context_end
1451 && cursor < tail.start + tail_max_width)
1453 tail.end = cursor;
1454 SKIP_SOMETHING (cursor, right_context_end);
1457 if (cursor < tail.start + tail_max_width)
1458 tail.end = cursor;
1460 if (tail.end > tail.start)
1462 keyafter_truncation = 0;
1463 tail_truncation = truncation_string && tail.end < right_context_end;
1465 else
1466 tail_truncation = 0;
1468 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1470 else
1473 /* No place left for a tail field. */
1475 tail.start = NULL;
1476 tail.end = NULL;
1477 tail_truncation = 0;
1480 /* `head' could not take more columns than what has been left in the right
1481 context field, and a gap is mandatory. It ends before the left
1482 context, and does not contain suffixed spaces. Its pointer is advanced
1483 until the head field has shrunk to its allowed width. It cannot
1484 contain only part of a word, and has no suffixed spaces. */
1486 head_max_width
1487 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1489 if (head_max_width > 0)
1491 head.end = before.start;
1492 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1494 head.start = left_field_start;
1495 while (head.start + head_max_width < head.end)
1496 SKIP_SOMETHING (head.start, head.end);
1498 if (head.end > head.start)
1500 before_truncation = 0;
1501 head_truncation = (truncation_string
1502 && head.start > left_context_start);
1504 else
1505 head_truncation = 0;
1507 SKIP_WHITE (head.start, head.end);
1509 else
1512 /* No place left for a head field. */
1514 head.start = NULL;
1515 head.end = NULL;
1516 head_truncation = 0;
1519 if (auto_reference)
1522 /* Construct the reference text in preallocated space from the file
1523 name and the line number. Find out in which file the reference
1524 occurred. Standard input yields an empty file name. Insure line
1525 numbers are one based, even if they are computed zero based. */
1527 file_index = 0;
1528 while (file_line_count[file_index] < occurs->reference)
1529 file_index++;
1531 file_name = input_file_name[file_index];
1532 if (!file_name)
1533 file_name = "";
1535 line_ordinal = occurs->reference + 1;
1536 if (file_index > 0)
1537 line_ordinal -= file_line_count[file_index - 1];
1539 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1540 reference.end = reference.start + strlen (reference.start);
1542 else if (input_reference)
1545 /* Reference starts at saved position for reference and extends right
1546 until some white space is met. */
1548 reference.start = keyafter.start + (DELTA) occurs->reference;
1549 reference.end = reference.start;
1550 SKIP_NON_WHITE (reference.end, right_context_end);
1554 /* Formatting and actual output - control routines. */
1556 /*----------------------------------------------------------------------.
1557 | Output the current output fields as one line for `troff' or `nroff'. |
1558 `----------------------------------------------------------------------*/
1560 static void
1561 output_one_roff_line (void)
1563 /* Output the `tail' field. */
1565 printf (".%s \"", macro_name);
1566 print_field (tail);
1567 if (tail_truncation)
1568 fputs (truncation_string, stdout);
1569 putchar ('"');
1571 /* Output the `before' field. */
1573 fputs (" \"", stdout);
1574 if (before_truncation)
1575 fputs (truncation_string, stdout);
1576 print_field (before);
1577 putchar ('"');
1579 /* Output the `keyafter' field. */
1581 fputs (" \"", stdout);
1582 print_field (keyafter);
1583 if (keyafter_truncation)
1584 fputs (truncation_string, stdout);
1585 putchar ('"');
1587 /* Output the `head' field. */
1589 fputs (" \"", stdout);
1590 if (head_truncation)
1591 fputs (truncation_string, stdout);
1592 print_field (head);
1593 putchar ('"');
1595 /* Conditionally output the `reference' field. */
1597 if (auto_reference || input_reference)
1599 fputs (" \"", stdout);
1600 print_field (reference);
1601 putchar ('"');
1604 putchar ('\n');
1607 /*---------------------------------------------------------.
1608 | Output the current output fields as one line for `TeX'. |
1609 `---------------------------------------------------------*/
1611 static void
1612 output_one_tex_line (void)
1614 BLOCK key; /* key field, isolated */
1615 BLOCK after; /* after field, isolated */
1616 char *cursor; /* running cursor in source text */
1618 printf ("\\%s ", macro_name);
1619 putchar ('{');
1620 print_field (tail);
1621 fputs ("}{", stdout);
1622 print_field (before);
1623 fputs ("}{", stdout);
1624 key.start = keyafter.start;
1625 after.end = keyafter.end;
1626 cursor = keyafter.start;
1627 SKIP_SOMETHING (cursor, keyafter.end);
1628 key.end = cursor;
1629 after.start = cursor;
1630 print_field (key);
1631 fputs ("}{", stdout);
1632 print_field (after);
1633 fputs ("}{", stdout);
1634 print_field (head);
1635 putchar ('}');
1636 if (auto_reference || input_reference)
1638 putchar ('{');
1639 print_field (reference);
1640 putchar ('}');
1642 putchar ('\n');
1645 /*-------------------------------------------------------------------.
1646 | Output the current output fields as one line for a dumb terminal. |
1647 `-------------------------------------------------------------------*/
1649 static void
1650 output_one_dumb_line (void)
1652 if (!right_reference)
1654 if (auto_reference)
1657 /* Output the `reference' field, in such a way that GNU emacs
1658 next-error will handle it. The ending colon is taken from the
1659 gap which follows. */
1661 print_field (reference);
1662 putchar (':');
1663 print_spaces (reference_max_width
1664 + gap_size
1665 - (reference.end - reference.start)
1666 - 1);
1668 else
1671 /* Output the `reference' field and its following gap. */
1673 print_field (reference);
1674 print_spaces (reference_max_width
1675 + gap_size
1676 - (reference.end - reference.start));
1680 if (tail.start < tail.end)
1682 /* Output the `tail' field. */
1684 print_field (tail);
1685 if (tail_truncation)
1686 fputs (truncation_string, stdout);
1688 print_spaces (half_line_width - gap_size
1689 - (before.end - before.start)
1690 - (before_truncation ? truncation_string_length : 0)
1691 - (tail.end - tail.start)
1692 - (tail_truncation ? truncation_string_length : 0));
1694 else
1695 print_spaces (half_line_width - gap_size
1696 - (before.end - before.start)
1697 - (before_truncation ? truncation_string_length : 0));
1699 /* Output the `before' field. */
1701 if (before_truncation)
1702 fputs (truncation_string, stdout);
1703 print_field (before);
1705 print_spaces (gap_size);
1707 /* Output the `keyafter' field. */
1709 print_field (keyafter);
1710 if (keyafter_truncation)
1711 fputs (truncation_string, stdout);
1713 if (head.start < head.end)
1715 /* Output the `head' field. */
1717 print_spaces (half_line_width
1718 - (keyafter.end - keyafter.start)
1719 - (keyafter_truncation ? truncation_string_length : 0)
1720 - (head.end - head.start)
1721 - (head_truncation ? truncation_string_length : 0));
1722 if (head_truncation)
1723 fputs (truncation_string, stdout);
1724 print_field (head);
1726 else
1728 if ((auto_reference || input_reference) && right_reference)
1729 print_spaces (half_line_width
1730 - (keyafter.end - keyafter.start)
1731 - (keyafter_truncation ? truncation_string_length : 0));
1733 if ((auto_reference || input_reference) && right_reference)
1735 /* Output the `reference' field. */
1737 print_spaces (gap_size);
1738 print_field (reference);
1741 putchar ('\n');
1744 /*------------------------------------------------------------------------.
1745 | Scan the whole occurs table and, for each entry, output one line in the |
1746 | appropriate format. |
1747 `------------------------------------------------------------------------*/
1749 static void
1750 generate_all_output (void)
1752 size_t occurs_index; /* index of keyword entry being processed */
1753 OCCURS *occurs_cursor; /* current keyword entry being processed */
1755 /* The following assignments are useful to provide default values in case
1756 line contexts or references are not used, in which case these variables
1757 would never be computed. */
1759 tail.start = NULL;
1760 tail.end = NULL;
1761 tail_truncation = 0;
1763 head.start = NULL;
1764 head.end = NULL;
1765 head_truncation = 0;
1767 /* Loop over all keyword occurrences. */
1769 occurs_cursor = occurs_table[0];
1771 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1773 /* Compute the exact size of every field and whenever truncation flags
1774 are present or not. */
1776 define_all_fields (occurs_cursor);
1778 /* Produce one output line according to selected format. */
1780 switch (output_format)
1782 case UNKNOWN_FORMAT:
1783 /* Should never happen. */
1785 case DUMB_FORMAT:
1786 output_one_dumb_line ();
1787 break;
1789 case ROFF_FORMAT:
1790 output_one_roff_line ();
1791 break;
1793 case TEX_FORMAT:
1794 output_one_tex_line ();
1795 break;
1798 /* Advance the cursor into the occurs table. */
1800 occurs_cursor++;
1804 /* Option decoding and main program. */
1806 /*------------------------------------------------------.
1807 | Print program identification and options, then exit. |
1808 `------------------------------------------------------*/
1810 void
1811 usage (int status)
1813 if (status != EXIT_SUCCESS)
1814 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1815 program_name);
1816 else
1818 printf (_("\
1819 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1820 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1821 program_name, program_name);
1822 fputs (_("\
1823 Output a permuted index, including context, of the words in the input files.\n\
1825 "), stdout);
1826 fputs (_("\
1827 Mandatory arguments to long options are mandatory for short options too.\n\
1828 "), stdout);
1829 fputs (_("\
1830 -A, --auto-reference output automatically generated references\n\
1831 -G, --traditional behave more like System V `ptx'\n\
1832 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1833 "), stdout);
1834 fputs (_("\
1835 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1836 -O, --format=roff generate output as roff directives\n\
1837 -R, --right-side-refs put references at right, not counted in -w\n\
1838 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1839 -T, --format=tex generate output as TeX directives\n\
1840 "), stdout);
1841 fputs (_("\
1842 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1843 -b, --break-file=FILE word break characters in this FILE\n\
1844 -f, --ignore-case fold lower case to upper case for sorting\n\
1845 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1846 -i, --ignore-file=FILE read ignore word list from FILE\n\
1847 -o, --only-file=FILE read only word list from this FILE\n\
1848 "), stdout);
1849 fputs (_("\
1850 -r, --references first field of each line is a reference\n\
1851 -t, --typeset-mode - not implemented -\n\
1852 -w, --width=NUMBER output width in columns, reference excluded\n\
1853 "), stdout);
1854 fputs (HELP_OPTION_DESCRIPTION, stdout);
1855 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1856 fputs (_("\
1858 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1859 "), stdout);
1860 emit_ancillary_info ();
1862 exit (status);
1865 /*----------------------------------------------------------------------.
1866 | Main program. Decode ARGC arguments passed through the ARGV array of |
1867 | strings, then launch execution. |
1868 `----------------------------------------------------------------------*/
1870 /* Long options equivalences. */
1871 static struct option const long_options[] =
1873 {"auto-reference", no_argument, NULL, 'A'},
1874 {"break-file", required_argument, NULL, 'b'},
1875 {"flag-truncation", required_argument, NULL, 'F'},
1876 {"ignore-case", no_argument, NULL, 'f'},
1877 {"gap-size", required_argument, NULL, 'g'},
1878 {"ignore-file", required_argument, NULL, 'i'},
1879 {"macro-name", required_argument, NULL, 'M'},
1880 {"only-file", required_argument, NULL, 'o'},
1881 {"references", no_argument, NULL, 'r'},
1882 {"right-side-refs", no_argument, NULL, 'R'},
1883 {"format", required_argument, NULL, 10},
1884 {"sentence-regexp", required_argument, NULL, 'S'},
1885 {"traditional", no_argument, NULL, 'G'},
1886 {"typeset-mode", no_argument, NULL, 't'},
1887 {"width", required_argument, NULL, 'w'},
1888 {"word-regexp", required_argument, NULL, 'W'},
1889 {GETOPT_HELP_OPTION_DECL},
1890 {GETOPT_VERSION_OPTION_DECL},
1891 {NULL, 0, NULL, 0},
1894 static char const* const format_args[] =
1896 "roff", "tex", NULL
1899 static enum Format const format_vals[] =
1901 ROFF_FORMAT, TEX_FORMAT
1905 main (int argc, char **argv)
1907 int optchar; /* argument character */
1908 int file_index; /* index in text input file arrays */
1910 /* Decode program options. */
1912 initialize_main (&argc, &argv);
1913 set_program_name (argv[0]);
1914 setlocale (LC_ALL, "");
1915 bindtextdomain (PACKAGE, LOCALEDIR);
1916 textdomain (PACKAGE);
1918 atexit (close_stdout);
1920 #if HAVE_SETCHRCLASS
1921 setchrclass (NULL);
1922 #endif
1924 while (optchar = getopt_long (argc, argv, "AF:GM:ORS:TW:b:i:fg:o:trw:",
1925 long_options, NULL),
1926 optchar != EOF)
1928 switch (optchar)
1930 default:
1931 usage (EXIT_FAILURE);
1933 case 'G':
1934 gnu_extensions = false;
1935 break;
1937 case 'b':
1938 break_file = optarg;
1939 break;
1941 case 'f':
1942 ignore_case = true;
1943 break;
1945 case 'g':
1947 unsigned long int tmp_ulong;
1948 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
1949 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
1950 error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
1951 quotearg (optarg));
1952 gap_size = tmp_ulong;
1953 break;
1956 case 'i':
1957 ignore_file = optarg;
1958 break;
1960 case 'o':
1961 only_file = optarg;
1962 break;
1964 case 'r':
1965 input_reference = true;
1966 break;
1968 case 't':
1969 /* Yet to understand... */
1970 break;
1972 case 'w':
1974 unsigned long int tmp_ulong;
1975 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
1976 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
1977 error (EXIT_FAILURE, 0, _("invalid line width: %s"),
1978 quotearg (optarg));
1979 line_width = tmp_ulong;
1980 break;
1983 case 'A':
1984 auto_reference = true;
1985 break;
1987 case 'F':
1988 truncation_string = copy_unescaped_string (optarg);
1989 break;
1991 case 'M':
1992 macro_name = optarg;
1993 break;
1995 case 'O':
1996 output_format = ROFF_FORMAT;
1997 break;
1999 case 'R':
2000 right_reference = true;
2001 break;
2003 case 'S':
2004 context_regex.string = copy_unescaped_string (optarg);
2005 break;
2007 case 'T':
2008 output_format = TEX_FORMAT;
2009 break;
2011 case 'W':
2012 word_regex.string = copy_unescaped_string (optarg);
2013 if (!*word_regex.string)
2014 word_regex.string = NULL;
2015 break;
2017 case 10:
2018 output_format = XARGMATCH ("--format", optarg,
2019 format_args, format_vals);
2020 case_GETOPT_HELP_CHAR;
2022 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2026 /* Process remaining arguments. If GNU extensions are enabled, process
2027 all arguments as input parameters. If disabled, accept at most two
2028 arguments, the second of which is an output parameter. */
2030 if (optind == argc)
2033 /* No more argument simply means: read standard input. */
2035 input_file_name = xmalloc (sizeof *input_file_name);
2036 file_line_count = xmalloc (sizeof *file_line_count);
2037 number_input_files = 1;
2038 input_file_name[0] = NULL;
2040 else if (gnu_extensions)
2042 number_input_files = argc - optind;
2043 input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
2044 file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
2046 for (file_index = 0; file_index < number_input_files; file_index++)
2048 if (!*argv[optind] || STREQ (argv[optind], "-"))
2049 input_file_name[file_index] = NULL;
2050 else
2051 input_file_name[file_index] = argv[optind];
2052 optind++;
2055 else
2058 /* There is one necessary input file. */
2060 number_input_files = 1;
2061 input_file_name = xmalloc (sizeof *input_file_name);
2062 file_line_count = xmalloc (sizeof *file_line_count);
2063 if (!*argv[optind] || STREQ (argv[optind], "-"))
2064 input_file_name[0] = NULL;
2065 else
2066 input_file_name[0] = argv[optind];
2067 optind++;
2069 /* Redirect standard output, only if requested. */
2071 if (optind < argc)
2073 if (! freopen (argv[optind], "w", stdout))
2074 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2075 optind++;
2078 /* Diagnose any other argument as an error. */
2080 if (optind < argc)
2082 error (0, 0, _("extra operand %s"), quote (argv[optind]));
2083 usage (EXIT_FAILURE);
2087 /* If the output format has not been explicitly selected, choose dumb
2088 terminal format if GNU extensions are enabled, else `roff' format. */
2090 if (output_format == UNKNOWN_FORMAT)
2091 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2093 /* Initialize the main tables. */
2095 initialize_regex ();
2097 /* Read `Break character' file, if any. */
2099 if (break_file)
2100 digest_break_file (break_file);
2102 /* Read `Ignore words' file and `Only words' files, if any. If any of
2103 these files is empty, reset the name of the file to NULL, to avoid
2104 unnecessary calls to search_table. */
2106 if (ignore_file)
2108 digest_word_file (ignore_file, &ignore_table);
2109 if (ignore_table.length == 0)
2110 ignore_file = NULL;
2113 if (only_file)
2115 digest_word_file (only_file, &only_table);
2116 if (only_table.length == 0)
2117 only_file = NULL;
2120 /* Prepare to study all the input files. */
2122 number_of_occurs[0] = 0;
2123 total_line_count = 0;
2124 maximum_word_length = 0;
2125 reference_max_width = 0;
2127 for (file_index = 0; file_index < number_input_files; file_index++)
2130 /* Read the file in core, than study it. */
2132 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2133 find_occurs_in_text ();
2135 /* Maintain for each file how many lines has been read so far when its
2136 end is reached. Incrementing the count first is a simple kludge to
2137 handle a possible incomplete line at end of file. */
2139 total_line_count++;
2140 file_line_count[file_index] = total_line_count;
2143 /* Do the output process phase. */
2145 sort_found_occurs ();
2146 fix_output_parameters ();
2147 generate_all_output ();
2149 /* All done. */
2151 exit (EXIT_SUCCESS);