1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990, 1991, 1993, 1998-2004 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 2, or (at your option)
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 François Pinard <pinard@iro.umontreal.ca> */
25 #include <sys/types.h>
35 /* The official name of this program (e.g., no `g' prefix). */
36 #define PROGRAM_NAME "ptx"
38 /* Note to translator: Please translate "F. Pinard" to "François
39 Pinard" if "ç" (c-with-cedilla) is available in the
40 translation's character set and encoding. */
41 #define AUTHORS _("F. Pinard")
43 /* Number of possible characters in a byte. */
44 #define CHAR_SET_SIZE 256
46 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
47 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
48 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
49 #define OCTTOBIN(C) ((C) - '0')
51 /* Debugging the memory allocator. */
54 # define MALLOC_FUNC_CHECK 1
58 /* Global definitions. */
60 /* Reallocation step when swallowing non regular files. The value is not
61 the actual reallocation step, but its base two logarithm. */
62 #define SWALLOW_REALLOC_LOG 12
64 /* Imported from "regex.c". */
67 /* The name this program was run with. */
70 /* Program options. */
74 UNKNOWN_FORMAT, /* output format still unknown */
75 DUMB_FORMAT, /* output for a dumb terminal */
76 ROFF_FORMAT, /* output for `troff' or `nroff' */
77 TEX_FORMAT /* output for `TeX' or `LaTeX' */
80 static int gnu_extensions = 1; /* trigger all GNU extensions */
81 static int auto_reference = 0; /* references are `file_name:line_number:' */
82 static int input_reference = 0; /* references at beginning of input lines */
83 static int right_reference = 0; /* output references after right context */
84 static int line_width = 72; /* output line width in characters */
85 static int gap_size = 3; /* number of spaces between output fields */
86 static const char *truncation_string = "/";
87 /* string used to mark line truncations */
88 static const char *macro_name = "xx"; /* macro name for roff or TeX output */
89 static enum Format output_format = UNKNOWN_FORMAT;
92 static int ignore_case = 0; /* fold lower to upper case for sorting */
93 static const char *context_regex_string = NULL;
94 /* raw regex for end of context */
95 static const char *word_regex_string = NULL;
96 /* raw regex for a keyword */
97 static const char *break_file = NULL; /* name of the `Break characters' file */
98 static const char *only_file = NULL; /* name of the `Only words' file */
99 static const char *ignore_file = NULL; /* name of the `Ignore words' file */
101 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
102 whole file. A WORD is something smaller, its length should fit in a
103 short integer. A WORD_TABLE may contain several WORDs. */
107 char *start; /* pointer to beginning of region */
108 char *end; /* pointer to end + 1 of region */
114 char *start; /* pointer to beginning of region */
115 short size; /* length of the region */
121 WORD *start; /* array of WORDs */
122 size_t alloc; /* allocated length */
123 size_t length; /* number of used entries */
127 /* Pattern description tables. */
129 /* For each character, provide its folded equivalent. */
130 static unsigned char folded_chars[CHAR_SET_SIZE];
132 /* Compiled regex for end of context. */
133 static struct re_pattern_buffer *context_regex;
135 /* End of context pattern register indices. */
136 static struct re_registers context_regs;
138 /* Compiled regex for a keyword. */
139 static struct re_pattern_buffer *word_regex;
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(*cursor)) \
177 #define SKIP_WHITE(cursor, limit) \
178 while (cursor < limit && ISSPACE(*cursor)) \
181 #define SKIP_WHITE_BACKWARDS(cursor, start) \
182 while (cursor > start && ISSPACE(cursor[-1])) \
185 #define SKIP_SOMETHING(cursor, limit) \
186 if (word_regex_string) \
189 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
190 cursor += count <= 0 ? 1 : count; \
192 else if (word_fastmap[(unsigned char) *cursor]) \
193 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
198 /* Occurrences table.
200 The `keyword' pointer provides the central word, which is surrounded
201 by a left context and a right context. The `keyword' and `length'
202 field allow full 8-bit characters keys, even including NULs. At other
203 places in this program, the name `keyafter' refers to the keyword
204 followed by its right context.
206 The left context does not extend, towards the beginning of the file,
207 further than a distance given by the `left' value. This value is
208 relative to the keyword beginning, it is usually negative. This
209 insures that, except for white space, we will never have to backward
210 scan the source text, when it is time to generate the final output
213 The right context, indirectly attainable through the keyword end, does
214 not extend, towards the end of the file, further than a distance given
215 by the `right' value. This value is relative to the keyword
216 beginning, it is usually positive.
218 When automatic references are used, the `reference' value is the
219 overall line number in all input files read so far, in this case, it
220 is of type (int). When input references are used, the `reference'
221 value indicates the distance between the keyword beginning and the
222 start of the reference field, it is of type (DELTA) and usually
225 typedef short DELTA; /* to hold displacement within one context */
229 WORD key; /* description of the keyword */
230 DELTA left; /* distance to left context start */
231 DELTA right; /* distance to right context end */
232 int reference; /* reference descriptor */
236 /* The various OCCURS tables are indexed by the language. But the time
237 being, there is no such multiple language support. */
239 static OCCURS *occurs_table[1]; /* all words retained from the read text */
240 static size_t occurs_alloc[1]; /* allocated size of occurs_table */
241 static size_t number_of_occurs[1]; /* number of used slots in occurs_table */
244 /* Communication among output routines. */
246 /* Indicate if special output processing is requested for each character. */
247 static char edited_flag[CHAR_SET_SIZE];
249 static int half_line_width; /* half of line width, reference excluded */
250 static int before_max_width; /* maximum width of before field */
251 static int keyafter_max_width; /* maximum width of keyword-and-after field */
252 static int truncation_string_length;/* length of string used to flag truncation */
254 /* When context is limited by lines, wraparound may happen on final output:
255 the `head' pointer gives access to some supplementary left context which
256 will be seen at the end of the output line, the `tail' pointer gives
257 access to some supplementary right context which will be seen at the
258 beginning of the output line. */
260 static BLOCK tail; /* tail field */
261 static int tail_truncation; /* flag truncation after the tail field */
263 static BLOCK before; /* before field */
264 static int before_truncation; /* flag truncation before the before field */
266 static BLOCK keyafter; /* keyword-and-after field */
267 static int keyafter_truncation; /* flag truncation after the keyafter field */
269 static BLOCK head; /* head field */
270 static int head_truncation; /* flag truncation before the head field */
272 static BLOCK reference; /* reference field for input reference mode */
274 /* Miscellaneous routines. */
276 /*------------------------------------------------------.
277 | Duplicate string STRING, while evaluating \-escapes. |
278 `------------------------------------------------------*/
280 /* Loosely adapted from GNU sh-utils printf.c code. */
283 copy_unescaped_string (const char *string)
285 char *result; /* allocated result */
286 char *cursor; /* cursor in result */
287 int value; /* value of \nnn escape */
288 int length; /* length of \nnn escape */
290 result = xmalloc (strlen (string) + 1);
299 case 'x': /* \xhhh escape, 3 chars maximum */
301 for (length = 0, string++;
302 length < 3 && ISXDIGIT (*string);
304 value = value * 16 + HEXTOBIN (*string);
314 case '0': /* \0ooo escape, 3 chars maximum */
316 for (length = 0, string++;
317 length < 3 && ISODIGIT (*string);
319 value = value * 8 + OCTTOBIN (*string);
323 case 'a': /* alert */
332 case 'b': /* backspace */
337 case 'c': /* cancel the rest of the output */
342 case 'f': /* form feed */
347 case 'n': /* new line */
352 case 'r': /* carriage return */
357 case 't': /* horizontal tab */
362 case 'v': /* vertical tab */
373 *cursor++ = *string++;
378 *cursor++ = *string++;
384 /*-------------------------------------------------------------------.
385 | Compile the regex represented by STRING, diagnose and abort if any |
386 | error. Returns the compiled regex structure. |
387 `-------------------------------------------------------------------*/
389 static struct re_pattern_buffer *
390 alloc_and_compile_regex (const char *string)
392 struct re_pattern_buffer *pattern; /* newly allocated structure */
393 const char *message; /* error message returned by regex.c */
395 pattern = xmalloc (sizeof *pattern);
396 memset (pattern, 0, sizeof (struct re_pattern_buffer));
398 pattern->buffer = NULL;
399 pattern->allocated = 0;
400 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
401 pattern->fastmap = xmalloc ((size_t) CHAR_SET_SIZE);
403 message = re_compile_pattern (string, (int) strlen (string), pattern);
405 error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);
407 /* The fastmap should be compiled before `re_match'. The following
408 call is not mandatory, because `re_search' is always called sooner,
409 and it compiles the fastmap if this has not been done yet. */
411 re_compile_fastmap (pattern);
413 /* Do not waste extra allocated space. */
415 if (pattern->allocated > pattern->used)
418 = xrealloc (pattern->buffer, (size_t) pattern->used);
419 pattern->allocated = pattern->used;
425 /*------------------------------------------------------------------------.
426 | This will initialize various tables for pattern match and compiles some |
428 `------------------------------------------------------------------------*/
431 initialize_regex (void)
433 int character; /* character value */
435 /* Initialize the case folding table. */
438 for (character = 0; character < CHAR_SET_SIZE; character++)
439 folded_chars[character] = TOUPPER (character);
441 /* Unless the user already provided a description of the end of line or
442 end of sentence sequence, select an end of line sequence to compile.
443 If the user provided an empty definition, thus disabling end of line
444 or sentence feature, make it NULL to speed up tests. If GNU
445 extensions are enabled, use end of sentence like in GNU emacs. If
446 disabled, use end of lines. */
448 if (context_regex_string)
450 if (!*context_regex_string)
451 context_regex_string = NULL;
453 else if (gnu_extensions && !input_reference)
454 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
456 context_regex_string = "\n";
458 if (context_regex_string)
459 context_regex = alloc_and_compile_regex (context_regex_string);
461 /* If the user has already provided a non-empty regexp to describe
462 words, compile it. Else, unless this has already been done through
463 a user provided Break character file, construct a fastmap of
464 characters that may appear in a word. If GNU extensions enabled,
465 include only letters of the underlying character set. If disabled,
466 include almost everything, even punctuations; stop only on white
469 if (word_regex_string && *word_regex_string)
470 word_regex = alloc_and_compile_regex (word_regex_string);
471 else if (!break_file)
478 for (character = 0; character < CHAR_SET_SIZE; character++)
479 word_fastmap[character] = ISALPHA (character) ? 1 : 0;
484 /* Simulate [^ \t\n]+. */
486 memset (word_fastmap, 1, CHAR_SET_SIZE);
487 word_fastmap[' '] = 0;
488 word_fastmap['\t'] = 0;
489 word_fastmap['\n'] = 0;
494 /*------------------------------------------------------------------------.
495 | This routine will attempt to swallow a whole file name FILE_NAME into a |
496 | contiguous region of memory and return a description of it into BLOCK. |
497 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
499 | Previously, in some cases, white space compression was attempted while |
500 | inputting text. This was defeating some regexps like default end of |
501 | sentence, which checks for two consecutive spaces. If white space |
502 | compression is ever reinstated, it should be in output routines. |
503 `------------------------------------------------------------------------*/
506 swallow_file_in_memory (const char *file_name, BLOCK *block)
508 int file_handle; /* file descriptor number */
509 struct stat stat_block; /* stat block for file */
510 size_t allocated_length; /* allocated length of memory buffer */
511 size_t used_length; /* used length in memory buffer */
512 int read_length; /* number of character gotten on last read */
514 /* As special cases, a file name which is NULL or "-" indicates standard
515 input, which is already opened. In all other cases, open the file from
517 bool using_stdin = !file_name || !*file_name || STREQ (file_name, "-");
519 file_handle = STDIN_FILENO;
521 if ((file_handle = open (file_name, O_RDONLY)) < 0)
522 error (EXIT_FAILURE, errno, "%s", file_name);
524 /* If the file is a plain, regular file, allocate the memory buffer all at
525 once and swallow the file in one blow. In other cases, read the file
526 repeatedly in smaller chunks until we have it all, reallocating memory
527 once in a while, as we go. */
529 if (fstat (file_handle, &stat_block) < 0)
530 error (EXIT_FAILURE, errno, "%s", file_name);
532 if (S_ISREG (stat_block.st_mode))
534 size_t in_memory_size;
536 block->start = xmalloc ((size_t) stat_block.st_size);
538 if ((in_memory_size = read (file_handle,
539 block->start, (size_t) stat_block.st_size))
540 != stat_block.st_size)
543 /* On MSDOS, in memory size may be smaller than the file
544 size, because of end of line conversions. But it can
545 never be smaller than half the file size, because the
546 minimum is when all lines are empty and terminated by
548 if (in_memory_size != (size_t)-1
549 && in_memory_size >= stat_block.st_size / 2)
550 block->start = xrealloc (block->start, in_memory_size);
552 #endif /* not MSDOS */
554 error (EXIT_FAILURE, errno, "%s", file_name);
556 block->end = block->start + in_memory_size;
560 block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
562 allocated_length = (1 << SWALLOW_REALLOC_LOG);
564 while (read_length = read (file_handle,
565 block->start + used_length,
566 allocated_length - used_length),
569 used_length += read_length;
570 if (used_length == allocated_length)
572 allocated_length += (1 << SWALLOW_REALLOC_LOG);
574 = xrealloc (block->start, allocated_length);
579 error (EXIT_FAILURE, errno, "%s", file_name);
581 block->end = block->start + used_length;
584 /* Close the file, but only if it was not the standard input. */
586 if (! using_stdin && close (file_handle) != 0)
587 error (EXIT_FAILURE, errno, "%s", file_name);
590 /* Sort and search routines. */
592 /*--------------------------------------------------------------------------.
593 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
594 | Return less than 0 if the first word goes before the second; return |
595 | greater than 0 if the first word goes after the second. |
597 | If a word is indeed a prefix of the other, the shorter should go first. |
598 `--------------------------------------------------------------------------*/
601 compare_words (const void *void_first, const void *void_second)
603 #define first ((const WORD *) void_first)
604 #define second ((const WORD *) void_second)
605 int length; /* minimum of two lengths */
606 int counter; /* cursor in words */
607 int value; /* value of comparison */
609 length = first->size < second->size ? first->size : second->size;
613 for (counter = 0; counter < length; counter++)
615 value = (folded_chars [(unsigned char) (first->start[counter])]
616 - folded_chars [(unsigned char) (second->start[counter])]);
623 for (counter = 0; counter < length; counter++)
625 value = ((unsigned char) first->start[counter]
626 - (unsigned char) second->start[counter]);
632 return first->size - second->size;
637 /*-----------------------------------------------------------------------.
638 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
639 | go first. In case of a tie, preserve the original order through a |
640 | pointer comparison. |
641 `-----------------------------------------------------------------------*/
644 compare_occurs (const void *void_first, const void *void_second)
646 #define first ((const OCCURS *) void_first)
647 #define second ((const OCCURS *) void_second)
650 value = compare_words (&first->key, &second->key);
651 return value == 0 ? first->key.start - second->key.start : value;
656 /*------------------------------------------------------------.
657 | Return !0 if WORD appears in TABLE. Uses a binary search. |
658 `------------------------------------------------------------*/
661 search_table (WORD *word, WORD_TABLE *table)
663 int lowest; /* current lowest possible index */
664 int highest; /* current highest possible index */
665 int middle; /* current middle index */
666 int value; /* value from last comparison */
669 highest = table->length - 1;
670 while (lowest <= highest)
672 middle = (lowest + highest) / 2;
673 value = compare_words (word, table->start + middle);
675 highest = middle - 1;
684 /*---------------------------------------------------------------------.
685 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
686 | take intermediate copies or table elements, so the sort will be |
687 | stabilized throughout the comparison routine. |
688 `---------------------------------------------------------------------*/
691 sort_found_occurs (void)
694 /* Only one language for the time being. */
696 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
700 /* Parameter files reading routines. */
702 /*----------------------------------------------------------------------.
703 | Read a file named FILE_NAME, containing a set of break characters. |
704 | Build a content to the array word_fastmap in which all characters are |
705 | allowed except those found in the file. Characters may be repeated. |
706 `----------------------------------------------------------------------*/
709 digest_break_file (const char *file_name)
711 BLOCK file_contents; /* to receive a copy of the file */
712 char *cursor; /* cursor in file copy */
714 swallow_file_in_memory (file_name, &file_contents);
716 /* Make the fastmap and record the file contents in it. */
718 memset (word_fastmap, 1, CHAR_SET_SIZE);
719 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
720 word_fastmap[(unsigned char) *cursor] = 0;
725 /* If GNU extensions are enabled, the only way to avoid newline as
726 a break character is to write all the break characters in the
727 file with no newline at all, not even at the end of the file.
728 If disabled, spaces, tabs and newlines are always considered as
729 break characters even if not included in the break file. */
731 word_fastmap[' '] = 0;
732 word_fastmap['\t'] = 0;
733 word_fastmap['\n'] = 0;
736 /* Return the space of the file, which is no more required. */
738 free (file_contents.start);
741 /*-----------------------------------------------------------------------.
742 | Read a file named FILE_NAME, containing one word per line, then |
743 | construct in TABLE a table of WORD descriptors for them. The routine |
744 | swallows the whole file in memory; this is at the expense of space |
745 | needed for newlines, which are useless; however, the reading is fast. |
746 `-----------------------------------------------------------------------*/
749 digest_word_file (const char *file_name, WORD_TABLE *table)
751 BLOCK file_contents; /* to receive a copy of the file */
752 char *cursor; /* cursor in file copy */
753 char *word_start; /* start of the current word */
755 swallow_file_in_memory (file_name, &file_contents);
761 /* Read the whole file. */
763 cursor = file_contents.start;
764 while (cursor < file_contents.end)
767 /* Read one line, and save the word in contains. */
770 while (cursor < file_contents.end && *cursor != '\n')
773 /* Record the word in table if it is not empty. */
775 if (cursor > word_start)
777 if (table->length == table->alloc)
779 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
781 table->alloc = table->alloc * 2 + 1;
782 table->start = xrealloc (table->start,
783 table->alloc * sizeof *table->start);
786 table->start[table->length].start = word_start;
787 table->start[table->length].size = cursor - word_start;
791 /* This test allows for an incomplete line at end of file. */
793 if (cursor < file_contents.end)
797 /* Finally, sort all the words read. */
799 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
802 /* Keyword recognition and selection. */
804 /*----------------------------------------------------------------------.
805 | For each keyword in the source text, constructs an OCCURS structure. |
806 `----------------------------------------------------------------------*/
809 find_occurs_in_text (void)
811 char *cursor; /* for scanning the source text */
812 char *scan; /* for scanning the source text also */
813 char *line_start; /* start of the current input line */
814 char *line_scan; /* newlines scanned until this point */
815 int reference_length; /* length of reference in input mode */
816 WORD possible_key; /* possible key, to ease searches */
817 OCCURS *occurs_cursor; /* current OCCURS under construction */
819 char *context_start; /* start of left context */
820 char *context_end; /* end of right context */
821 char *word_start; /* start of word */
822 char *word_end; /* end of word */
823 char *next_context_start; /* next start of left context */
825 /* reference_length is always used within `if (input_reference)'.
826 However, GNU C diagnoses that it may be used uninitialized. The
827 following assignment is merely to shut it up. */
829 reference_length = 0;
831 /* Tracking where lines start is helpful for reference processing. In
832 auto reference mode, this allows counting lines. In input reference
833 mode, this permits finding the beginning of the references.
835 The first line begins with the file, skip immediately this very first
836 reference in input reference mode, to help further rejection any word
837 found inside it. Also, unconditionally assigning these variable has
838 the happy effect of shutting up lint. */
840 line_start = text_buffer.start;
841 line_scan = line_start;
844 SKIP_NON_WHITE (line_scan, text_buffer.end);
845 reference_length = line_scan - line_start;
846 SKIP_WHITE (line_scan, text_buffer.end);
849 /* Process the whole buffer, one line or one sentence at a time. */
851 for (cursor = text_buffer.start;
852 cursor < text_buffer.end;
853 cursor = next_context_start)
856 /* `context_start' gets initialized before the processing of each
857 line, or once for the whole buffer if no end of line or sentence
858 sequence separator. */
860 context_start = cursor;
862 /* If a end of line or end of sentence sequence is defined and
863 non-empty, `next_context_start' will be recomputed to be the end of
864 each line or sentence, before each one is processed. If no such
865 sequence, then `next_context_start' is set at the end of the whole
866 buffer, which is then considered to be a single line or sentence.
867 This test also accounts for the case of an incomplete line or
868 sentence at the end of the buffer. */
870 if (context_regex_string
871 && (re_search (context_regex, cursor, text_buffer.end - cursor,
872 0, text_buffer.end - cursor, &context_regs)
874 next_context_start = cursor + context_regs.end[0];
877 next_context_start = text_buffer.end;
879 /* Include the separator into the right context, but not any suffix
880 white space in this separator; this insures it will be seen in
881 output and will not take more space than necessary. */
883 context_end = next_context_start;
884 SKIP_WHITE_BACKWARDS (context_end, context_start);
886 /* Read and process a single input line or sentence, one word at a
893 /* If a word regexp has been compiled, use it to skip at the
894 beginning of the next word. If there is no such word, exit
898 if (re_search (word_regex, cursor, context_end - cursor,
899 0, context_end - cursor, &word_regs)
902 word_start = cursor + word_regs.start[0];
903 word_end = cursor + word_regs.end[0];
907 /* Avoid re_search and use the fastmap to skip to the
908 beginning of the next word. If there is no more word in
909 the buffer, exit the loop. */
913 while (scan < context_end
914 && !word_fastmap[(unsigned char) *scan])
917 if (scan == context_end)
922 while (scan < context_end
923 && word_fastmap[(unsigned char) *scan])
929 /* Skip right to the beginning of the found word. */
933 /* Skip any zero length word. Just advance a single position,
934 then go fetch the next word. */
936 if (word_end == word_start)
942 /* This is a genuine, non empty word, so save it as a possible
943 key. Then skip over it. Also, maintain the maximum length of
944 all words read so far. It is mandatory to take the maximum
945 length of all words in the file, without considering if they
946 are actually kept or rejected, because backward jumps at output
947 generation time may fall in *any* word. */
949 possible_key.start = cursor;
950 possible_key.size = word_end - word_start;
951 cursor += possible_key.size;
953 if (possible_key.size > maximum_word_length)
954 maximum_word_length = possible_key.size;
956 /* In input reference mode, update `line_start' from its previous
957 value. Count the lines just in case auto reference mode is
958 also selected. If it happens that the word just matched is
959 indeed part of a reference; just ignore it. */
963 while (line_scan < possible_key.start)
964 if (*line_scan == '\n')
968 line_start = line_scan;
969 SKIP_NON_WHITE (line_scan, text_buffer.end);
970 reference_length = line_scan - line_start;
974 if (line_scan > possible_key.start)
978 /* Ignore the word if an `Ignore words' table exists and if it is
979 part of it. Also ignore the word if an `Only words' table and
980 if it is *not* part of it.
982 It is allowed that both tables be used at once, even if this
983 may look strange for now. Just ignore a word that would appear
984 in both. If regexps are eventually implemented for these
985 tables, the Ignore table could then reject words that would
986 have been previously accepted by the Only table. */
988 if (ignore_file && search_table (&possible_key, &ignore_table))
990 if (only_file && !search_table (&possible_key, &only_table))
993 /* A non-empty word has been found. First of all, insure
994 proper allocation of the next OCCURS, and make a pointer to
995 where it will be constructed. */
997 if (number_of_occurs[0] == occurs_alloc[0])
999 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1002 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
1003 occurs_table[0] = xrealloc (occurs_table[0],
1004 occurs_alloc[0] * sizeof *occurs_table[0]);
1007 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1009 /* Define the refence field, if any. */
1014 /* While auto referencing, update `line_start' from its
1015 previous value, counting lines as we go. If input
1016 referencing at the same time, `line_start' has been
1017 advanced earlier, and the following loop is never really
1020 while (line_scan < possible_key.start)
1021 if (*line_scan == '\n')
1025 line_start = line_scan;
1026 SKIP_NON_WHITE (line_scan, text_buffer.end);
1031 occurs_cursor->reference = total_line_count;
1033 else if (input_reference)
1036 /* If only input referencing, `line_start' has been computed
1037 earlier to detect the case the word matched would be part
1038 of the reference. The reference position is simply the
1039 value of `line_start'. */
1041 occurs_cursor->reference
1042 = (DELTA) (line_start - possible_key.start);
1043 if (reference_length > reference_max_width)
1044 reference_max_width = reference_length;
1047 /* Exclude the reference from the context in simple cases. */
1049 if (input_reference && line_start == context_start)
1051 SKIP_NON_WHITE (context_start, context_end);
1052 SKIP_WHITE (context_start, context_end);
1055 /* Completes the OCCURS structure. */
1057 occurs_cursor->key = possible_key;
1058 occurs_cursor->left = context_start - possible_key.start;
1059 occurs_cursor->right = context_end - possible_key.start;
1061 number_of_occurs[0]++;
1066 /* Formatting and actual output - service routines. */
1068 /*-----------------------------------------.
1069 | Prints some NUMBER of spaces on stdout. |
1070 `-----------------------------------------*/
1073 print_spaces (int number)
1077 for (counter = number; counter > 0; counter--)
1081 /*-------------------------------------.
1082 | Prints the field provided by FIELD. |
1083 `-------------------------------------*/
1086 print_field (BLOCK field)
1088 char *cursor; /* Cursor in field to print */
1089 int character; /* Current character */
1090 int base; /* Base character, without diacritic */
1091 int diacritic; /* Diacritic code for the character */
1093 /* Whitespace is not really compressed. Instead, each white space
1094 character (tab, vt, ht etc.) is printed as one single space. */
1096 for (cursor = field.start; cursor < field.end; cursor++)
1098 character = (unsigned char) *cursor;
1099 if (edited_flag[character])
1102 /* First check if this is a diacriticized character.
1104 This works only for TeX. I do not know how diacriticized
1105 letters work with `roff'. Please someone explain it to me! */
1107 diacritic = todiac (character);
1108 if (diacritic != 0 && output_format == TEX_FORMAT)
1110 base = tobase (character);
1114 case 1: /* Latin diphthongs */
1118 fputs ("\\oe{}", stdout);
1122 fputs ("\\OE{}", stdout);
1126 fputs ("\\ae{}", stdout);
1130 fputs ("\\AE{}", stdout);
1138 case 2: /* Acute accent */
1139 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1142 case 3: /* Grave accent */
1143 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1146 case 4: /* Circumflex accent */
1147 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1150 case 5: /* Diaeresis */
1151 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1154 case 6: /* Tilde accent */
1155 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1158 case 7: /* Cedilla */
1159 printf ("\\c{%c}", base);
1162 case 8: /* Small circle beneath */
1166 fputs ("\\aa{}", stdout);
1170 fputs ("\\AA{}", stdout);
1178 case 9: /* Strike through */
1182 fputs ("\\o{}", stdout);
1186 fputs ("\\O{}", stdout);
1197 /* This is not a diacritic character, so handle cases which are
1198 really specific to `roff' or TeX. All white space processing
1199 is done as the default case of this switch. */
1204 /* In roff output format, double any quote. */
1214 /* In TeX output format, precede these with a backslash. */
1216 putchar (character);
1221 /* In TeX output format, precede these with a backslash and
1222 force mathematical mode. */
1223 printf ("$\\%c$", character);
1227 /* In TeX output mode, request production of a backslash. */
1228 fputs ("\\backslash{}", stdout);
1232 /* Any other flagged character produces a single space. */
1241 /* Formatting and actual output - planning routines. */
1243 /*--------------------------------------------------------------------.
1244 | From information collected from command line options and input file |
1245 | readings, compute and fix some output parameter values. |
1246 `--------------------------------------------------------------------*/
1249 fix_output_parameters (void)
1251 int file_index; /* index in text input file arrays */
1252 int line_ordinal; /* line ordinal value for reference */
1253 char ordinal_string[12]; /* edited line ordinal for reference */
1254 int reference_width; /* width for the whole reference */
1255 int character; /* character ordinal */
1256 const char *cursor; /* cursor in some constant strings */
1258 /* In auto reference mode, the maximum width of this field is
1259 precomputed and subtracted from the overall line width. Add one for
1260 the column which separate the file name from the line number. */
1264 reference_max_width = 0;
1265 for (file_index = 0; file_index < number_input_files; file_index++)
1267 line_ordinal = file_line_count[file_index] + 1;
1269 line_ordinal -= file_line_count[file_index - 1];
1270 sprintf (ordinal_string, "%d", line_ordinal);
1271 reference_width = strlen (ordinal_string);
1272 if (input_file_name[file_index])
1273 reference_width += strlen (input_file_name[file_index]);
1274 if (reference_width > reference_max_width)
1275 reference_max_width = reference_width;
1277 reference_max_width++;
1278 reference.start = xmalloc ((size_t) reference_max_width + 1);
1281 /* If the reference appears to the left of the output line, reserve some
1282 space for it right away, including one gap size. */
1284 if ((auto_reference || input_reference) && !right_reference)
1285 line_width -= reference_max_width + gap_size;
1287 /* The output lines, minimally, will contain from left to right a left
1288 context, a gap, and a keyword followed by the right context with no
1289 special intervening gap. Half of the line width is dedicated to the
1290 left context and the gap, the other half is dedicated to the keyword
1291 and the right context; these values are computed once and for all here.
1292 There also are tail and head wrap around fields, used when the keyword
1293 is near the beginning or the end of the line, or when some long word
1294 cannot fit in, but leave place from wrapped around shorter words. The
1295 maximum width of these fields are recomputed separately for each line,
1296 on a case by case basis. It is worth noting that it cannot happen that
1297 both the tail and head fields are used at once. */
1299 half_line_width = line_width / 2;
1300 before_max_width = half_line_width - gap_size;
1301 keyafter_max_width = half_line_width;
1303 /* If truncation_string is the empty string, make it NULL to speed up
1304 tests. In this case, truncation_string_length will never get used, so
1305 there is no need to set it. */
1307 if (truncation_string && *truncation_string)
1308 truncation_string_length = strlen (truncation_string);
1310 truncation_string = NULL;
1315 /* When flagging truncation at the left of the keyword, the
1316 truncation mark goes at the beginning of the before field,
1317 unless there is a head field, in which case the mark goes at the
1318 left of the head field. When flagging truncation at the right
1319 of the keyword, the mark goes at the end of the keyafter field,
1320 unless there is a tail field, in which case the mark goes at the
1321 end of the tail field. Only eight combination cases could arise
1322 for truncation marks:
1325 . One beginning the before field.
1326 . One beginning the head field.
1327 . One ending the keyafter field.
1328 . One ending the tail field.
1329 . One beginning the before field, another ending the keyafter field.
1330 . One ending the tail field, another beginning the before field.
1331 . One ending the keyafter field, another beginning the head field.
1333 So, there is at most two truncation marks, which could appear both
1334 on the left side of the center of the output line, both on the
1335 right side, or one on either side. */
1337 before_max_width -= 2 * truncation_string_length;
1338 keyafter_max_width -= 2 * truncation_string_length;
1343 /* I never figured out exactly how UNIX' ptx plans the output width
1344 of its various fields. If GNU extensions are disabled, do not
1345 try computing the field widths correctly; instead, use the
1346 following formula, which does not completely imitate UNIX' ptx,
1349 keyafter_max_width -= 2 * truncation_string_length + 1;
1352 /* Compute which characters need special output processing. Initialize
1353 by flagging any white space character. Some systems do not consider
1354 form feed as a space character, but we do. */
1356 for (character = 0; character < CHAR_SET_SIZE; character++)
1357 edited_flag[character] = ISSPACE (character) != 0;
1358 edited_flag['\f'] = 1;
1360 /* Complete the special character flagging according to selected output
1363 switch (output_format)
1365 case UNKNOWN_FORMAT:
1366 /* Should never happen. */
1373 /* `Quote' characters should be doubled. */
1375 edited_flag['"'] = 1;
1380 /* Various characters need special processing. */
1382 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1383 edited_flag[(unsigned char) *cursor] = 1;
1385 /* Any character with 8th bit set will print to a single space, unless
1386 it is diacriticized. */
1388 for (character = 0200; character < CHAR_SET_SIZE; character++)
1389 edited_flag[character] = todiac (character) != 0;
1394 /*------------------------------------------------------------------.
1395 | Compute the position and length of all the output fields, given a |
1396 | pointer to some OCCURS. |
1397 `------------------------------------------------------------------*/
1400 define_all_fields (OCCURS *occurs)
1402 int tail_max_width; /* allowable width of tail field */
1403 int head_max_width; /* allowable width of head field */
1404 char *cursor; /* running cursor in source text */
1405 char *left_context_start; /* start of left context */
1406 char *right_context_end; /* end of right context */
1407 char *left_field_start; /* conservative start for `head'/`before' */
1408 int file_index; /* index in text input file arrays */
1409 const char *file_name; /* file name for reference */
1410 int line_ordinal; /* line ordinal for reference */
1412 /* Define `keyafter', start of left context and end of right context.
1413 `keyafter' starts at the saved position for keyword and extend to the
1414 right from the end of the keyword, eating separators or full words, but
1415 not beyond maximum allowed width for `keyafter' field or limit for the
1416 right context. Suffix spaces will be removed afterwards. */
1418 keyafter.start = occurs->key.start;
1419 keyafter.end = keyafter.start + occurs->key.size;
1420 left_context_start = keyafter.start + occurs->left;
1421 right_context_end = keyafter.start + occurs->right;
1423 cursor = keyafter.end;
1424 while (cursor < right_context_end
1425 && cursor <= keyafter.start + keyafter_max_width)
1427 keyafter.end = cursor;
1428 SKIP_SOMETHING (cursor, right_context_end);
1430 if (cursor <= keyafter.start + keyafter_max_width)
1431 keyafter.end = cursor;
1433 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1435 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1437 /* When the left context is wide, it might take some time to catch up from
1438 the left context boundary to the beginning of the `head' or `before'
1439 fields. So, in this case, to speed the catchup, we jump back from the
1440 keyword, using some secure distance, possibly falling in the middle of
1441 a word. A secure backward jump would be at least half the maximum
1442 width of a line, plus the size of the longest word met in the whole
1443 input. We conclude this backward jump by a skip forward of at least
1444 one word. In this manner, we should not inadvertently accept only part
1445 of a word. From the reached point, when it will be time to fix the
1446 beginning of `head' or `before' fields, we will skip forward words or
1447 delimiters until we get sufficiently near. */
1449 if (-occurs->left > half_line_width + maximum_word_length)
1452 = keyafter.start - (half_line_width + maximum_word_length);
1453 SKIP_SOMETHING (left_field_start, keyafter.start);
1456 left_field_start = keyafter.start + occurs->left;
1458 /* `before' certainly ends at the keyword, but not including separating
1459 spaces. It starts after than the saved value for the left context, by
1460 advancing it until it falls inside the maximum allowed width for the
1461 before field. There will be no prefix spaces either. `before' only
1462 advances by skipping single separators or whole words. */
1464 before.start = left_field_start;
1465 before.end = keyafter.start;
1466 SKIP_WHITE_BACKWARDS (before.end, before.start);
1468 while (before.start + before_max_width < before.end)
1469 SKIP_SOMETHING (before.start, before.end);
1471 if (truncation_string)
1473 cursor = before.start;
1474 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1475 before_truncation = cursor > left_context_start;
1478 before_truncation = 0;
1480 SKIP_WHITE (before.start, text_buffer.end);
1482 /* The tail could not take more columns than what has been left in the
1483 left context field, and a gap is mandatory. It starts after the
1484 right context, and does not contain prefixed spaces. It ends at
1485 the end of line, the end of buffer or when the tail field is full,
1486 whichever comes first. It cannot contain only part of a word, and
1487 has no suffixed spaces. */
1490 = before_max_width - (before.end - before.start) - gap_size;
1492 if (tail_max_width > 0)
1494 tail.start = keyafter.end;
1495 SKIP_WHITE (tail.start, text_buffer.end);
1497 tail.end = tail.start;
1499 while (cursor < right_context_end
1500 && cursor < tail.start + tail_max_width)
1503 SKIP_SOMETHING (cursor, right_context_end);
1506 if (cursor < tail.start + tail_max_width)
1509 if (tail.end > tail.start)
1511 keyafter_truncation = 0;
1512 tail_truncation = truncation_string && tail.end < right_context_end;
1515 tail_truncation = 0;
1517 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1522 /* No place left for a tail field. */
1526 tail_truncation = 0;
1529 /* `head' could not take more columns than what has been left in the right
1530 context field, and a gap is mandatory. It ends before the left
1531 context, and does not contain suffixed spaces. Its pointer is advanced
1532 until the head field has shrunk to its allowed width. It cannot
1533 contain only part of a word, and has no suffixed spaces. */
1536 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1538 if (head_max_width > 0)
1540 head.end = before.start;
1541 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1543 head.start = left_field_start;
1544 while (head.start + head_max_width < head.end)
1545 SKIP_SOMETHING (head.start, head.end);
1547 if (head.end > head.start)
1549 before_truncation = 0;
1550 head_truncation = (truncation_string
1551 && head.start > left_context_start);
1554 head_truncation = 0;
1556 SKIP_WHITE (head.start, head.end);
1561 /* No place left for a head field. */
1565 head_truncation = 0;
1571 /* Construct the reference text in preallocated space from the file
1572 name and the line number. Find out in which file the reference
1573 occurred. Standard input yields an empty file name. Insure line
1574 numbers are one based, even if they are computed zero based. */
1577 while (file_line_count[file_index] < occurs->reference)
1580 file_name = input_file_name[file_index];
1584 line_ordinal = occurs->reference + 1;
1586 line_ordinal -= file_line_count[file_index - 1];
1588 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1589 reference.end = reference.start + strlen (reference.start);
1591 else if (input_reference)
1594 /* Reference starts at saved position for reference and extends right
1595 until some white space is met. */
1597 reference.start = keyafter.start + (DELTA) occurs->reference;
1598 reference.end = reference.start;
1599 SKIP_NON_WHITE (reference.end, right_context_end);
1603 /* Formatting and actual output - control routines. */
1605 /*----------------------------------------------------------------------.
1606 | Output the current output fields as one line for `troff' or `nroff'. |
1607 `----------------------------------------------------------------------*/
1610 output_one_roff_line (void)
1612 /* Output the `tail' field. */
1614 printf (".%s \"", macro_name);
1616 if (tail_truncation)
1617 fputs (truncation_string, stdout);
1620 /* Output the `before' field. */
1622 fputs (" \"", stdout);
1623 if (before_truncation)
1624 fputs (truncation_string, stdout);
1625 print_field (before);
1628 /* Output the `keyafter' field. */
1630 fputs (" \"", stdout);
1631 print_field (keyafter);
1632 if (keyafter_truncation)
1633 fputs (truncation_string, stdout);
1636 /* Output the `head' field. */
1638 fputs (" \"", stdout);
1639 if (head_truncation)
1640 fputs (truncation_string, stdout);
1644 /* Conditionally output the `reference' field. */
1646 if (auto_reference || input_reference)
1648 fputs (" \"", stdout);
1649 print_field (reference);
1656 /*---------------------------------------------------------.
1657 | Output the current output fields as one line for `TeX'. |
1658 `---------------------------------------------------------*/
1661 output_one_tex_line (void)
1663 BLOCK key; /* key field, isolated */
1664 BLOCK after; /* after field, isolated */
1665 char *cursor; /* running cursor in source text */
1667 printf ("\\%s ", macro_name);
1670 fputs ("}{", stdout);
1671 print_field (before);
1672 fputs ("}{", stdout);
1673 key.start = keyafter.start;
1674 after.end = keyafter.end;
1675 cursor = keyafter.start;
1676 SKIP_SOMETHING (cursor, keyafter.end);
1678 after.start = cursor;
1680 fputs ("}{", stdout);
1681 print_field (after);
1682 fputs ("}{", stdout);
1685 if (auto_reference || input_reference)
1688 print_field (reference);
1694 /*-------------------------------------------------------------------.
1695 | Output the current output fields as one line for a dumb terminal. |
1696 `-------------------------------------------------------------------*/
1699 output_one_dumb_line (void)
1701 if (!right_reference)
1706 /* Output the `reference' field, in such a way that GNU emacs
1707 next-error will handle it. The ending colon is taken from the
1708 gap which follows. */
1710 print_field (reference);
1712 print_spaces (reference_max_width
1714 - (reference.end - reference.start)
1720 /* Output the `reference' field and its following gap. */
1722 print_field (reference);
1723 print_spaces (reference_max_width
1725 - (reference.end - reference.start));
1729 if (tail.start < tail.end)
1731 /* Output the `tail' field. */
1734 if (tail_truncation)
1735 fputs (truncation_string, stdout);
1737 print_spaces (half_line_width - gap_size
1738 - (before.end - before.start)
1739 - (before_truncation ? truncation_string_length : 0)
1740 - (tail.end - tail.start)
1741 - (tail_truncation ? truncation_string_length : 0));
1744 print_spaces (half_line_width - gap_size
1745 - (before.end - before.start)
1746 - (before_truncation ? truncation_string_length : 0));
1748 /* Output the `before' field. */
1750 if (before_truncation)
1751 fputs (truncation_string, stdout);
1752 print_field (before);
1754 print_spaces (gap_size);
1756 /* Output the `keyafter' field. */
1758 print_field (keyafter);
1759 if (keyafter_truncation)
1760 fputs (truncation_string, stdout);
1762 if (head.start < head.end)
1764 /* Output the `head' field. */
1766 print_spaces (half_line_width
1767 - (keyafter.end - keyafter.start)
1768 - (keyafter_truncation ? truncation_string_length : 0)
1769 - (head.end - head.start)
1770 - (head_truncation ? truncation_string_length : 0));
1771 if (head_truncation)
1772 fputs (truncation_string, stdout);
1777 if ((auto_reference || input_reference) && right_reference)
1778 print_spaces (half_line_width
1779 - (keyafter.end - keyafter.start)
1780 - (keyafter_truncation ? truncation_string_length : 0));
1782 if ((auto_reference || input_reference) && right_reference)
1784 /* Output the `reference' field. */
1786 print_spaces (gap_size);
1787 print_field (reference);
1793 /*------------------------------------------------------------------------.
1794 | Scan the whole occurs table and, for each entry, output one line in the |
1795 | appropriate format. |
1796 `------------------------------------------------------------------------*/
1799 generate_all_output (void)
1801 size_t occurs_index; /* index of keyword entry being processed */
1802 OCCURS *occurs_cursor; /* current keyword entry being processed */
1804 /* The following assignments are useful to provide default values in case
1805 line contexts or references are not used, in which case these variables
1806 would never be computed. */
1810 tail_truncation = 0;
1814 head_truncation = 0;
1816 /* Loop over all keyword occurrences. */
1818 occurs_cursor = occurs_table[0];
1820 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1822 /* Compute the exact size of every field and whenever truncation flags
1823 are present or not. */
1825 define_all_fields (occurs_cursor);
1827 /* Produce one output line according to selected format. */
1829 switch (output_format)
1831 case UNKNOWN_FORMAT:
1832 /* Should never happen. */
1835 output_one_dumb_line ();
1839 output_one_roff_line ();
1843 output_one_tex_line ();
1847 /* Advance the cursor into the occurs table. */
1853 /* Option decoding and main program. */
1855 /*------------------------------------------------------.
1856 | Print program identification and options, then exit. |
1857 `------------------------------------------------------*/
1862 if (status != EXIT_SUCCESS)
1863 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1868 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1869 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1870 program_name, program_name);
1872 Output a permuted index, including context, of the words in the input files.\n\
1876 Mandatory arguments to long options are mandatory for short options too.\n\
1879 -A, --auto-reference output automatically generated references\n\
1880 -C, --copyright display Copyright and copying conditions\n\
1881 -G, --traditional behave more like System V `ptx'\n\
1882 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1885 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1886 -O, --format=roff generate output as roff directives\n\
1887 -R, --right-side-refs put references at right, not counted in -w\n\
1888 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1889 -T, --format=tex generate output as TeX directives\n\
1892 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1893 -b, --break-file=FILE word break characters in this FILE\n\
1894 -f, --ignore-case fold lower case to upper case for sorting\n\
1895 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1896 -i, --ignore-file=FILE read ignore word list from FILE\n\
1897 -o, --only-file=FILE read only word list from this FILE\n\
1900 -r, --references first field of each line is a reference\n\
1901 -t, --typeset-mode - not implemented -\n\
1902 -w, --width=NUMBER output width in columns, reference excluded\n\
1904 fputs (HELP_OPTION_DESCRIPTION, stdout);
1905 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1908 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1910 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1915 /*----------------------------------------------------------------------.
1916 | Main program. Decode ARGC arguments passed through the ARGV array of |
1917 | strings, then launch execution. |
1918 `----------------------------------------------------------------------*/
1920 /* Long options equivalences. */
1921 static const struct option long_options[] =
1923 {"auto-reference", no_argument, NULL, 'A'},
1924 {"break-file", required_argument, NULL, 'b'},
1925 {"copyright", no_argument, NULL, 'C'},
1926 {"flag-truncation", required_argument, NULL, 'F'},
1927 {"ignore-case", no_argument, NULL, 'f'},
1928 {"gap-size", required_argument, NULL, 'g'},
1929 {"ignore-file", required_argument, NULL, 'i'},
1930 {"macro-name", required_argument, NULL, 'M'},
1931 {"only-file", required_argument, NULL, 'o'},
1932 {"references", no_argument, NULL, 'r'},
1933 {"right-side-refs", no_argument, NULL, 'R'},
1934 {"format", required_argument, NULL, 10},
1935 {"sentence-regexp", required_argument, NULL, 'S'},
1936 {"traditional", no_argument, NULL, 'G'},
1937 {"typeset-mode", no_argument, NULL, 't'},
1938 {"width", required_argument, NULL, 'w'},
1939 {"word-regexp", required_argument, NULL, 'W'},
1940 {GETOPT_HELP_OPTION_DECL},
1941 {GETOPT_VERSION_OPTION_DECL},
1945 static char const* const format_args[] =
1950 static enum Format const format_vals[] =
1952 ROFF_FORMAT, TEX_FORMAT
1956 main (int argc, char **argv)
1958 int optchar; /* argument character */
1959 int file_index; /* index in text input file arrays */
1961 /* Decode program options. */
1963 initialize_main (&argc, &argv);
1964 program_name = argv[0];
1965 setlocale (LC_ALL, "");
1966 bindtextdomain (PACKAGE, LOCALEDIR);
1967 textdomain (PACKAGE);
1969 atexit (close_stdout);
1971 #if HAVE_SETCHRCLASS
1975 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1976 long_options, NULL),
1982 usage (EXIT_FAILURE);
1989 This program is free software; you can redistribute it and/or modify\n\
1990 it under the terms of the GNU General Public License as published by\n\
1991 the Free Software Foundation; either version 2, or (at your option)\n\
1992 any later version.\n\
1996 This program is distributed in the hope that it will be useful,\n\
1997 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
1998 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
1999 GNU General Public License for more details.\n\
2003 You should have received a copy of the GNU General Public License\n\
2004 along with this program; if not, write to the Free Software Foundation,\n\
2005 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
2008 exit (EXIT_SUCCESS);
2015 break_file = optarg;
2024 unsigned long int tmp_ulong;
2025 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2026 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2027 error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
2029 gap_size = tmp_ulong;
2034 ignore_file = optarg;
2042 input_reference = 1;
2046 /* Yet to understand... */
2051 unsigned long int tmp_ulong;
2052 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2053 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2054 error (EXIT_FAILURE, 0, _("invalid line width: %s"),
2056 line_width = tmp_ulong;
2065 truncation_string = copy_unescaped_string (optarg);
2069 macro_name = optarg;
2073 output_format = ROFF_FORMAT;
2077 right_reference = 1;
2081 context_regex_string = copy_unescaped_string (optarg);
2085 output_format = TEX_FORMAT;
2089 word_regex_string = copy_unescaped_string (optarg);
2093 output_format = XARGMATCH ("--format", optarg,
2094 format_args, format_vals);
2095 case_GETOPT_HELP_CHAR;
2097 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2101 /* Change the default Ignore file if one is defined. */
2103 #ifdef DEFAULT_IGNORE_FILE
2105 ignore_file = DEFAULT_IGNORE_FILE;
2108 /* Process remaining arguments. If GNU extensions are enabled, process
2109 all arguments as input parameters. If disabled, accept at most two
2110 arguments, the second of which is an output parameter. */
2115 /* No more argument simply means: read standard input. */
2117 input_file_name = xmalloc (sizeof *input_file_name);
2118 file_line_count = xmalloc (sizeof *file_line_count);
2119 number_input_files = 1;
2120 input_file_name[0] = NULL;
2122 else if (gnu_extensions)
2124 number_input_files = argc - optind;
2125 input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
2126 file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
2128 for (file_index = 0; file_index < number_input_files; file_index++)
2130 input_file_name[file_index] = argv[optind];
2131 if (!*argv[optind] || STREQ (argv[optind], "-"))
2132 input_file_name[0] = NULL;
2134 input_file_name[0] = argv[optind];
2141 /* There is one necessary input file. */
2143 number_input_files = 1;
2144 input_file_name = xmalloc (sizeof *input_file_name);
2145 file_line_count = xmalloc (sizeof *file_line_count);
2146 if (!*argv[optind] || STREQ (argv[optind], "-"))
2147 input_file_name[0] = NULL;
2149 input_file_name[0] = argv[optind];
2152 /* Redirect standard output, only if requested. */
2156 /* FIXME: don't fclose here? */
2158 if (fopen (argv[optind], "w") == NULL)
2159 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2163 /* Diagnose any other argument as an error. */
2167 error (0, 0, _("extra operand %s"), quote (argv[optind]));
2168 usage (EXIT_FAILURE);
2172 /* If the output format has not been explicitly selected, choose dumb
2173 terminal format if GNU extensions are enabled, else `roff' format. */
2175 if (output_format == UNKNOWN_FORMAT)
2176 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2178 /* Initialize the main tables. */
2180 initialize_regex ();
2182 /* Read `Break character' file, if any. */
2185 digest_break_file (break_file);
2187 /* Read `Ignore words' file and `Only words' files, if any. If any of
2188 these files is empty, reset the name of the file to NULL, to avoid
2189 unnecessary calls to search_table. */
2193 digest_word_file (ignore_file, &ignore_table);
2194 if (ignore_table.length == 0)
2200 digest_word_file (only_file, &only_table);
2201 if (only_table.length == 0)
2205 /* Prepare to study all the input files. */
2207 number_of_occurs[0] = 0;
2208 total_line_count = 0;
2209 maximum_word_length = 0;
2210 reference_max_width = 0;
2212 for (file_index = 0; file_index < number_input_files; file_index++)
2215 /* Read the file in core, than study it. */
2217 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2218 find_occurs_in_text ();
2220 /* Maintain for each file how many lines has been read so far when its
2221 end is reached. Incrementing the count first is a simple kludge to
2222 handle a possible incomplete line at end of file. */
2225 file_line_count[file_index] = total_line_count;
2228 /* Do the output process phase. */
2230 sort_found_occurs ();
2231 fix_output_parameters ();
2232 generate_all_output ();
2236 exit (EXIT_SUCCESS);