1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990, 1991, 1993, 1998-2003 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>
32 /* The official name of this program (e.g., no `g' prefix). */
33 #define PROGRAM_NAME "ptx"
35 #define AUTHORS "François Pinard"
37 /* Number of possible characters in a byte. */
38 #define CHAR_SET_SIZE 256
40 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
41 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
42 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
43 #define OCTTOBIN(C) ((C) - '0')
45 /* Debugging the memory allocator. */
48 # define MALLOC_FUNC_CHECK 1
52 /* Global definitions. */
54 /* Reallocation step when swallowing non regular files. The value is not
55 the actual reallocation step, but its base two logarithm. */
56 #define SWALLOW_REALLOC_LOG 12
58 /* Imported from "regex.c". */
61 /* The name this program was run with. */
64 /* Program options. */
68 UNKNOWN_FORMAT, /* output format still unknown */
69 DUMB_FORMAT, /* output for a dumb terminal */
70 ROFF_FORMAT, /* output for `troff' or `nroff' */
71 TEX_FORMAT /* output for `TeX' or `LaTeX' */
74 int gnu_extensions = 1; /* trigger all GNU extensions */
75 int auto_reference = 0; /* references are `file_name:line_number:' */
76 int input_reference = 0; /* references at beginning of input lines */
77 int right_reference = 0; /* output references after right context */
78 int line_width = 72; /* output line width in characters */
79 int gap_size = 3; /* number of spaces between output fields */
80 const char *truncation_string = "/";
81 /* string used to mark line truncations */
82 const char *macro_name = "xx"; /* macro name for roff or TeX output */
83 enum Format output_format = UNKNOWN_FORMAT;
86 int ignore_case = 0; /* fold lower to upper case for sorting */
87 const char *context_regex_string = NULL;
88 /* raw regex for end of context */
89 const char *word_regex_string = NULL;
90 /* raw regex for a keyword */
91 const char *break_file = NULL; /* name of the `Break characters' file */
92 const char *only_file = NULL; /* name of the `Only words' file */
93 const char *ignore_file = NULL; /* name of the `Ignore words' file */
95 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
96 whole file. A WORD is something smaller, its length should fit in a
97 short integer. A WORD_TABLE may contain several WORDs. */
101 char *start; /* pointer to beginning of region */
102 char *end; /* pointer to end + 1 of region */
108 char *start; /* pointer to beginning of region */
109 short size; /* length of the region */
115 WORD *start; /* array of WORDs */
116 size_t alloc; /* allocated length */
117 size_t length; /* number of used entries */
121 /* Pattern description tables. */
123 /* For each character, provide its folded equivalent. */
124 unsigned char folded_chars[CHAR_SET_SIZE];
126 /* For each character, indicate if it is part of a word. */
127 char syntax_table[CHAR_SET_SIZE];
128 char *re_syntax_table = syntax_table;
130 /* Compiled regex for end of context. */
131 struct re_pattern_buffer *context_regex;
133 /* End of context pattern register indices. */
134 struct re_registers context_regs;
136 /* Compiled regex for a keyword. */
137 struct re_pattern_buffer *word_regex;
139 /* Keyword pattern register indices. */
140 struct re_registers word_regs;
142 /* A word characters fastmap is used only when no word regexp has been
143 provided. A word is then made up of a sequence of one or more characters
144 allowed by the fastmap. Contains !0 if character allowed in word. Not
145 only this is faster in most cases, but it simplifies the implementation
146 of the Break files. */
147 char word_fastmap[CHAR_SET_SIZE];
149 /* Maximum length of any word read. */
150 int maximum_word_length;
152 /* Maximum width of any reference used. */
153 int reference_max_width;
155 /* Ignore and Only word tables. */
157 WORD_TABLE ignore_table; /* table of words to ignore */
158 WORD_TABLE only_table; /* table of words to select */
160 /* Source text table, and scanning macros. */
162 int number_input_files; /* number of text input files */
163 int total_line_count; /* total number of lines seen so far */
164 const char **input_file_name; /* array of text input file names */
165 int *file_line_count; /* array of `total_line_count' values at end */
167 BLOCK text_buffer; /* file to study */
168 char *text_buffer_maxend; /* allocated end of text_buffer */
170 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
172 #define SKIP_NON_WHITE(cursor, limit) \
173 while (cursor < limit && !ISSPACE(*cursor)) \
176 #define SKIP_WHITE(cursor, limit) \
177 while (cursor < limit && ISSPACE(*cursor)) \
180 #define SKIP_WHITE_BACKWARDS(cursor, start) \
181 while (cursor > start && ISSPACE(cursor[-1])) \
184 #define SKIP_SOMETHING(cursor, limit) \
185 if (word_regex_string) \
188 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
189 cursor += count <= 0 ? 1 : count; \
191 else if (word_fastmap[(unsigned char) *cursor]) \
192 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
197 /* Occurrences table.
199 The `keyword' pointer provides the central word, which is surrounded
200 by a left context and a right context. The `keyword' and `length'
201 field allow full 8-bit characters keys, even including NULs. At other
202 places in this program, the name `keyafter' refers to the keyword
203 followed by its right context.
205 The left context does not extend, towards the beginning of the file,
206 further than a distance given by the `left' value. This value is
207 relative to the keyword beginning, it is usually negative. This
208 insures that, except for white space, we will never have to backward
209 scan the source text, when it is time to generate the final output
212 The right context, indirectly attainable through the keyword end, does
213 not extend, towards the end of the file, further than a distance given
214 by the `right' value. This value is relative to the keyword
215 beginning, it is usually positive.
217 When automatic references are used, the `reference' value is the
218 overall line number in all input files read so far, in this case, it
219 is of type (int). When input references are used, the `reference'
220 value indicates the distance between the keyword beginning and the
221 start of the reference field, it is of type (DELTA) and usually
224 typedef short DELTA; /* to hold displacement within one context */
228 WORD key; /* description of the keyword */
229 DELTA left; /* distance to left context start */
230 DELTA right; /* distance to right context end */
231 int reference; /* reference descriptor */
235 /* The various OCCURS tables are indexed by the language. But the time
236 being, there is no such multiple language support. */
238 OCCURS *occurs_table[1]; /* all words retained from the read text */
239 size_t occurs_alloc[1]; /* allocated size of occurs_table */
240 size_t number_of_occurs[1]; /* number of used slots in occurs_table */
243 /* Communication among output routines. */
245 /* Indicate if special output processing is requested for each character. */
246 char edited_flag[CHAR_SET_SIZE];
248 int half_line_width; /* half of line width, reference excluded */
249 int before_max_width; /* maximum width of before field */
250 int keyafter_max_width; /* maximum width of keyword-and-after field */
251 int truncation_string_length; /* length of string used to flag truncation */
253 /* When context is limited by lines, wraparound may happen on final output:
254 the `head' pointer gives access to some supplementary left context which
255 will be seen at the end of the output line, the `tail' pointer gives
256 access to some supplementary right context which will be seen at the
257 beginning of the output line. */
259 BLOCK tail; /* tail field */
260 int tail_truncation; /* flag truncation after the tail field */
262 BLOCK before; /* before field */
263 int before_truncation; /* flag truncation before the before field */
265 BLOCK keyafter; /* keyword-and-after field */
266 int keyafter_truncation; /* flag truncation after the keyafter field */
268 BLOCK head; /* head field */
269 int head_truncation; /* flag truncation before the head field */
271 BLOCK reference; /* reference field for input reference mode */
273 /* Miscellaneous routines. */
275 /*------------------------------------------------------.
276 | Duplicate string STRING, while evaluating \-escapes. |
277 `------------------------------------------------------*/
279 /* Loosely adapted from GNU sh-utils printf.c code. */
282 copy_unescaped_string (const char *string)
284 char *result; /* allocated result */
285 char *cursor; /* cursor in result */
286 int value; /* value of \nnn escape */
287 int length; /* length of \nnn escape */
289 result = xmalloc (strlen (string) + 1);
298 case 'x': /* \xhhh escape, 3 chars maximum */
300 for (length = 0, string++;
301 length < 3 && ISXDIGIT (*string);
303 value = value * 16 + HEXTOBIN (*string);
313 case '0': /* \0ooo escape, 3 chars maximum */
315 for (length = 0, string++;
316 length < 3 && ISODIGIT (*string);
318 value = value * 8 + OCTTOBIN (*string);
322 case 'a': /* alert */
331 case 'b': /* backspace */
336 case 'c': /* cancel the rest of the output */
341 case 'f': /* form feed */
346 case 'n': /* new line */
351 case 'r': /* carriage return */
356 case 't': /* horizontal tab */
361 case 'v': /* vertical tab */
372 *cursor++ = *string++;
377 *cursor++ = *string++;
383 /*-------------------------------------------------------------------.
384 | Compile the regex represented by STRING, diagnose and abort if any |
385 | error. Returns the compiled regex structure. |
386 `-------------------------------------------------------------------*/
388 static struct re_pattern_buffer *
389 alloc_and_compile_regex (const char *string)
391 struct re_pattern_buffer *pattern; /* newly allocated structure */
392 const char *message; /* error message returned by regex.c */
394 pattern = (struct re_pattern_buffer *)
395 xmalloc (sizeof (struct re_pattern_buffer));
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 regex syntax table. */
437 for (character = 0; character < CHAR_SET_SIZE; character++)
438 syntax_table[character] = ISALPHA (character) ? Sword : 0;
440 /* Initialize the case folding table. */
443 for (character = 0; character < CHAR_SET_SIZE; character++)
444 folded_chars[character] = TOUPPER (character);
446 /* Unless the user already provided a description of the end of line or
447 end of sentence sequence, select an end of line sequence to compile.
448 If the user provided an empty definition, thus disabling end of line
449 or sentence feature, make it NULL to speed up tests. If GNU
450 extensions are enabled, use end of sentence like in GNU emacs. If
451 disabled, use end of lines. */
453 if (context_regex_string)
455 if (!*context_regex_string)
456 context_regex_string = NULL;
458 else if (gnu_extensions && !input_reference)
459 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
461 context_regex_string = "\n";
463 if (context_regex_string)
464 context_regex = alloc_and_compile_regex (context_regex_string);
466 /* If the user has already provided a non-empty regexp to describe
467 words, compile it. Else, unless this has already been done through
468 a user provided Break character file, construct a fastmap of
469 characters that may appear in a word. If GNU extensions enabled,
470 include only letters of the underlying character set. If disabled,
471 include almost everything, even punctuations; stop only on white
474 if (word_regex_string && *word_regex_string)
475 word_regex = alloc_and_compile_regex (word_regex_string);
476 else if (!break_file)
483 for (character = 0; character < CHAR_SET_SIZE; character++)
484 word_fastmap[character] = ISALPHA (character) ? 1 : 0;
489 /* Simulate [^ \t\n]+. */
491 memset (word_fastmap, 1, CHAR_SET_SIZE);
492 word_fastmap[' '] = 0;
493 word_fastmap['\t'] = 0;
494 word_fastmap['\n'] = 0;
499 /*------------------------------------------------------------------------.
500 | This routine will attempt to swallow a whole file name FILE_NAME into a |
501 | contiguous region of memory and return a description of it into BLOCK. |
502 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
504 | Previously, in some cases, white space compression was attempted while |
505 | inputting text. This was defeating some regexps like default end of |
506 | sentence, which checks for two consecutive spaces. If white space |
507 | compression is ever reinstated, it should be in output routines. |
508 `------------------------------------------------------------------------*/
511 swallow_file_in_memory (const char *file_name, BLOCK *block)
513 int file_handle; /* file descriptor number */
514 struct stat stat_block; /* stat block for file */
515 size_t allocated_length; /* allocated length of memory buffer */
516 size_t used_length; /* used length in memory buffer */
517 int read_length; /* number of character gotten on last read */
519 /* As special cases, a file name which is NULL or "-" indicates standard
520 input, which is already opened. In all other cases, open the file from
522 bool using_stdin = !file_name || !*file_name || strcmp (file_name, "-") == 0;
524 file_handle = STDIN_FILENO;
526 if ((file_handle = open (file_name, O_RDONLY)) < 0)
527 error (EXIT_FAILURE, errno, "%s", file_name);
529 /* If the file is a plain, regular file, allocate the memory buffer all at
530 once and swallow the file in one blow. In other cases, read the file
531 repeatedly in smaller chunks until we have it all, reallocating memory
532 once in a while, as we go. */
534 if (fstat (file_handle, &stat_block) < 0)
535 error (EXIT_FAILURE, errno, "%s", file_name);
537 if (S_ISREG (stat_block.st_mode))
539 size_t in_memory_size;
541 block->start = xmalloc ((size_t) stat_block.st_size);
543 if ((in_memory_size = read (file_handle,
544 block->start, (size_t) stat_block.st_size))
545 != stat_block.st_size)
548 /* On MSDOS, in memory size may be smaller than the file
549 size, because of end of line conversions. But it can
550 never be smaller than half the file size, because the
551 minimum is when all lines are empty and terminated by
553 if (in_memory_size != (size_t)-1
554 && in_memory_size >= stat_block.st_size / 2)
555 block->start = xrealloc (block->start, in_memory_size);
557 #endif /* not MSDOS */
559 error (EXIT_FAILURE, errno, "%s", file_name);
561 block->end = block->start + in_memory_size;
565 block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
567 allocated_length = (1 << SWALLOW_REALLOC_LOG);
569 while (read_length = read (file_handle,
570 block->start + used_length,
571 allocated_length - used_length),
574 used_length += read_length;
575 if (used_length == allocated_length)
577 allocated_length += (1 << SWALLOW_REALLOC_LOG);
579 = xrealloc (block->start, allocated_length);
584 error (EXIT_FAILURE, errno, "%s", file_name);
586 block->end = block->start + used_length;
589 /* Close the file, but only if it was not the standard input. */
591 if (! using_stdin && close (file_handle) != 0)
592 error (EXIT_FAILURE, errno, "%s", file_name);
595 /* Sort and search routines. */
597 /*--------------------------------------------------------------------------.
598 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
599 | Return less than 0 if the first word goes before the second; return |
600 | greater than 0 if the first word goes after the second. |
602 | If a word is indeed a prefix of the other, the shorter should go first. |
603 `--------------------------------------------------------------------------*/
606 compare_words (const void *void_first, const void *void_second)
608 #define first ((const WORD *) void_first)
609 #define second ((const WORD *) void_second)
610 int length; /* minimum of two lengths */
611 int counter; /* cursor in words */
612 int value; /* value of comparison */
614 length = first->size < second->size ? first->size : second->size;
618 for (counter = 0; counter < length; counter++)
620 value = (folded_chars [(unsigned char) (first->start[counter])]
621 - folded_chars [(unsigned char) (second->start[counter])]);
628 for (counter = 0; counter < length; counter++)
630 value = ((unsigned char) first->start[counter]
631 - (unsigned char) second->start[counter]);
637 return first->size - second->size;
642 /*-----------------------------------------------------------------------.
643 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
644 | go first. In case of a tie, preserve the original order through a |
645 | pointer comparison. |
646 `-----------------------------------------------------------------------*/
649 compare_occurs (const void *void_first, const void *void_second)
651 #define first ((const OCCURS *) void_first)
652 #define second ((const OCCURS *) void_second)
655 value = compare_words (&first->key, &second->key);
656 return value == 0 ? first->key.start - second->key.start : value;
661 /*------------------------------------------------------------.
662 | Return !0 if WORD appears in TABLE. Uses a binary search. |
663 `------------------------------------------------------------*/
666 search_table (WORD *word, WORD_TABLE *table)
668 int lowest; /* current lowest possible index */
669 int highest; /* current highest possible index */
670 int middle; /* current middle index */
671 int value; /* value from last comparison */
674 highest = table->length - 1;
675 while (lowest <= highest)
677 middle = (lowest + highest) / 2;
678 value = compare_words (word, table->start + middle);
680 highest = middle - 1;
689 /*---------------------------------------------------------------------.
690 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
691 | take intermediate copies or table elements, so the sort will be |
692 | stabilized throughout the comparison routine. |
693 `---------------------------------------------------------------------*/
696 sort_found_occurs (void)
699 /* Only one language for the time being. */
701 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
705 /* Parameter files reading routines. */
707 /*----------------------------------------------------------------------.
708 | Read a file named FILE_NAME, containing a set of break characters. |
709 | Build a content to the array word_fastmap in which all characters are |
710 | allowed except those found in the file. Characters may be repeated. |
711 `----------------------------------------------------------------------*/
714 digest_break_file (const char *file_name)
716 BLOCK file_contents; /* to receive a copy of the file */
717 char *cursor; /* cursor in file copy */
719 swallow_file_in_memory (file_name, &file_contents);
721 /* Make the fastmap and record the file contents in it. */
723 memset (word_fastmap, 1, CHAR_SET_SIZE);
724 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
725 word_fastmap[(unsigned char) *cursor] = 0;
730 /* If GNU extensions are enabled, the only way to avoid newline as
731 a break character is to write all the break characters in the
732 file with no newline at all, not even at the end of the file.
733 If disabled, spaces, tabs and newlines are always considered as
734 break characters even if not included in the break file. */
736 word_fastmap[' '] = 0;
737 word_fastmap['\t'] = 0;
738 word_fastmap['\n'] = 0;
741 /* Return the space of the file, which is no more required. */
743 free (file_contents.start);
746 /*-----------------------------------------------------------------------.
747 | Read a file named FILE_NAME, containing one word per line, then |
748 | construct in TABLE a table of WORD descriptors for them. The routine |
749 | swallows the whole file in memory; this is at the expense of space |
750 | needed for newlines, which are useless; however, the reading is fast. |
751 `-----------------------------------------------------------------------*/
754 digest_word_file (const char *file_name, WORD_TABLE *table)
756 BLOCK file_contents; /* to receive a copy of the file */
757 char *cursor; /* cursor in file copy */
758 char *word_start; /* start of the current word */
760 swallow_file_in_memory (file_name, &file_contents);
766 /* Read the whole file. */
768 cursor = file_contents.start;
769 while (cursor < file_contents.end)
772 /* Read one line, and save the word in contains. */
775 while (cursor < file_contents.end && *cursor != '\n')
778 /* Record the word in table if it is not empty. */
780 if (cursor > word_start)
782 if (table->length == table->alloc)
784 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
786 table->alloc = table->alloc * 2 + 1;
787 table->start = xrealloc (table->start,
788 table->alloc * sizeof *table->start);
791 table->start[table->length].start = word_start;
792 table->start[table->length].size = cursor - word_start;
796 /* This test allows for an incomplete line at end of file. */
798 if (cursor < file_contents.end)
802 /* Finally, sort all the words read. */
804 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
807 /* Keyword recognition and selection. */
809 /*----------------------------------------------------------------------.
810 | For each keyword in the source text, constructs an OCCURS structure. |
811 `----------------------------------------------------------------------*/
814 find_occurs_in_text (void)
816 char *cursor; /* for scanning the source text */
817 char *scan; /* for scanning the source text also */
818 char *line_start; /* start of the current input line */
819 char *line_scan; /* newlines scanned until this point */
820 int reference_length; /* length of reference in input mode */
821 WORD possible_key; /* possible key, to ease searches */
822 OCCURS *occurs_cursor; /* current OCCURS under construction */
824 char *context_start; /* start of left context */
825 char *context_end; /* end of right context */
826 char *word_start; /* start of word */
827 char *word_end; /* end of word */
828 char *next_context_start; /* next start of left context */
830 /* reference_length is always used within `if (input_reference)'.
831 However, GNU C diagnoses that it may be used uninitialized. The
832 following assignment is merely to shut it up. */
834 reference_length = 0;
836 /* Tracking where lines start is helpful for reference processing. In
837 auto reference mode, this allows counting lines. In input reference
838 mode, this permits finding the beginning of the references.
840 The first line begins with the file, skip immediately this very first
841 reference in input reference mode, to help further rejection any word
842 found inside it. Also, unconditionally assigning these variable has
843 the happy effect of shutting up lint. */
845 line_start = text_buffer.start;
846 line_scan = line_start;
849 SKIP_NON_WHITE (line_scan, text_buffer.end);
850 reference_length = line_scan - line_start;
851 SKIP_WHITE (line_scan, text_buffer.end);
854 /* Process the whole buffer, one line or one sentence at a time. */
856 for (cursor = text_buffer.start;
857 cursor < text_buffer.end;
858 cursor = next_context_start)
861 /* `context_start' gets initialized before the processing of each
862 line, or once for the whole buffer if no end of line or sentence
863 sequence separator. */
865 context_start = cursor;
867 /* If a end of line or end of sentence sequence is defined and
868 non-empty, `next_context_start' will be recomputed to be the end of
869 each line or sentence, before each one is processed. If no such
870 sequence, then `next_context_start' is set at the end of the whole
871 buffer, which is then considered to be a single line or sentence.
872 This test also accounts for the case of an incomplete line or
873 sentence at the end of the buffer. */
875 if (context_regex_string
876 && (re_search (context_regex, cursor, text_buffer.end - cursor,
877 0, text_buffer.end - cursor, &context_regs)
879 next_context_start = cursor + context_regs.end[0];
882 next_context_start = text_buffer.end;
884 /* Include the separator into the right context, but not any suffix
885 white space in this separator; this insures it will be seen in
886 output and will not take more space than necessary. */
888 context_end = next_context_start;
889 SKIP_WHITE_BACKWARDS (context_end, context_start);
891 /* Read and process a single input line or sentence, one word at a
898 /* If a word regexp has been compiled, use it to skip at the
899 beginning of the next word. If there is no such word, exit
903 if (re_search (word_regex, cursor, context_end - cursor,
904 0, context_end - cursor, &word_regs)
907 word_start = cursor + word_regs.start[0];
908 word_end = cursor + word_regs.end[0];
912 /* Avoid re_search and use the fastmap to skip to the
913 beginning of the next word. If there is no more word in
914 the buffer, exit the loop. */
918 while (scan < context_end
919 && !word_fastmap[(unsigned char) *scan])
922 if (scan == context_end)
927 while (scan < context_end
928 && word_fastmap[(unsigned char) *scan])
934 /* Skip right to the beginning of the found word. */
938 /* Skip any zero length word. Just advance a single position,
939 then go fetch the next word. */
941 if (word_end == word_start)
947 /* This is a genuine, non empty word, so save it as a possible
948 key. Then skip over it. Also, maintain the maximum length of
949 all words read so far. It is mandatory to take the maximum
950 length of all words in the file, without considering if they
951 are actually kept or rejected, because backward jumps at output
952 generation time may fall in *any* word. */
954 possible_key.start = cursor;
955 possible_key.size = word_end - word_start;
956 cursor += possible_key.size;
958 if (possible_key.size > maximum_word_length)
959 maximum_word_length = possible_key.size;
961 /* In input reference mode, update `line_start' from its previous
962 value. Count the lines just in case auto reference mode is
963 also selected. If it happens that the word just matched is
964 indeed part of a reference; just ignore it. */
968 while (line_scan < possible_key.start)
969 if (*line_scan == '\n')
973 line_start = line_scan;
974 SKIP_NON_WHITE (line_scan, text_buffer.end);
975 reference_length = line_scan - line_start;
979 if (line_scan > possible_key.start)
983 /* Ignore the word if an `Ignore words' table exists and if it is
984 part of it. Also ignore the word if an `Only words' table and
985 if it is *not* part of it.
987 It is allowed that both tables be used at once, even if this
988 may look strange for now. Just ignore a word that would appear
989 in both. If regexps are eventually implemented for these
990 tables, the Ignore table could then reject words that would
991 have been previously accepted by the Only table. */
993 if (ignore_file && search_table (&possible_key, &ignore_table))
995 if (only_file && !search_table (&possible_key, &only_table))
998 /* A non-empty word has been found. First of all, insure
999 proper allocation of the next OCCURS, and make a pointer to
1000 where it will be constructed. */
1002 if (number_of_occurs[0] == occurs_alloc[0])
1004 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1007 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
1008 occurs_table[0] = xrealloc (occurs_table[0],
1009 occurs_alloc[0] * sizeof *occurs_table[0]);
1012 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1014 /* Define the refence field, if any. */
1019 /* While auto referencing, update `line_start' from its
1020 previous value, counting lines as we go. If input
1021 referencing at the same time, `line_start' has been
1022 advanced earlier, and the following loop is never really
1025 while (line_scan < possible_key.start)
1026 if (*line_scan == '\n')
1030 line_start = line_scan;
1031 SKIP_NON_WHITE (line_scan, text_buffer.end);
1036 occurs_cursor->reference = total_line_count;
1038 else if (input_reference)
1041 /* If only input referencing, `line_start' has been computed
1042 earlier to detect the case the word matched would be part
1043 of the reference. The reference position is simply the
1044 value of `line_start'. */
1046 occurs_cursor->reference
1047 = (DELTA) (line_start - possible_key.start);
1048 if (reference_length > reference_max_width)
1049 reference_max_width = reference_length;
1052 /* Exclude the reference from the context in simple cases. */
1054 if (input_reference && line_start == context_start)
1056 SKIP_NON_WHITE (context_start, context_end);
1057 SKIP_WHITE (context_start, context_end);
1060 /* Completes the OCCURS structure. */
1062 occurs_cursor->key = possible_key;
1063 occurs_cursor->left = context_start - possible_key.start;
1064 occurs_cursor->right = context_end - possible_key.start;
1066 number_of_occurs[0]++;
1071 /* Formatting and actual output - service routines. */
1073 /*-----------------------------------------.
1074 | Prints some NUMBER of spaces on stdout. |
1075 `-----------------------------------------*/
1078 print_spaces (int number)
1082 for (counter = number; counter > 0; counter--)
1086 /*-------------------------------------.
1087 | Prints the field provided by FIELD. |
1088 `-------------------------------------*/
1091 print_field (BLOCK field)
1093 char *cursor; /* Cursor in field to print */
1094 int character; /* Current character */
1095 int base; /* Base character, without diacritic */
1096 int diacritic; /* Diacritic code for the character */
1098 /* Whitespace is not really compressed. Instead, each white space
1099 character (tab, vt, ht etc.) is printed as one single space. */
1101 for (cursor = field.start; cursor < field.end; cursor++)
1103 character = (unsigned char) *cursor;
1104 if (edited_flag[character])
1107 /* First check if this is a diacriticized character.
1109 This works only for TeX. I do not know how diacriticized
1110 letters work with `roff'. Please someone explain it to me! */
1112 diacritic = todiac (character);
1113 if (diacritic != 0 && output_format == TEX_FORMAT)
1115 base = tobase (character);
1119 case 1: /* Latin diphthongs */
1123 fputs ("\\oe{}", stdout);
1127 fputs ("\\OE{}", stdout);
1131 fputs ("\\ae{}", stdout);
1135 fputs ("\\AE{}", stdout);
1143 case 2: /* Acute accent */
1144 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1147 case 3: /* Grave accent */
1148 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1151 case 4: /* Circumflex accent */
1152 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1155 case 5: /* Diaeresis */
1156 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1159 case 6: /* Tilde accent */
1160 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1163 case 7: /* Cedilla */
1164 printf ("\\c{%c}", base);
1167 case 8: /* Small circle beneath */
1171 fputs ("\\aa{}", stdout);
1175 fputs ("\\AA{}", stdout);
1183 case 9: /* Strike through */
1187 fputs ("\\o{}", stdout);
1191 fputs ("\\O{}", stdout);
1202 /* This is not a diacritic character, so handle cases which are
1203 really specific to `roff' or TeX. All white space processing
1204 is done as the default case of this switch. */
1209 /* In roff output format, double any quote. */
1219 /* In TeX output format, precede these with a backslash. */
1221 putchar (character);
1226 /* In TeX output format, precede these with a backslash and
1227 force mathematical mode. */
1228 printf ("$\\%c$", character);
1232 /* In TeX output mode, request production of a backslash. */
1233 fputs ("\\backslash{}", stdout);
1237 /* Any other flagged character produces a single space. */
1246 /* Formatting and actual output - planning routines. */
1248 /*--------------------------------------------------------------------.
1249 | From information collected from command line options and input file |
1250 | readings, compute and fix some output parameter values. |
1251 `--------------------------------------------------------------------*/
1254 fix_output_parameters (void)
1256 int file_index; /* index in text input file arrays */
1257 int line_ordinal; /* line ordinal value for reference */
1258 char ordinal_string[12]; /* edited line ordinal for reference */
1259 int reference_width; /* width for the whole reference */
1260 int character; /* character ordinal */
1261 const char *cursor; /* cursor in some constant strings */
1263 /* In auto reference mode, the maximum width of this field is
1264 precomputed and subtracted from the overall line width. Add one for
1265 the column which separate the file name from the line number. */
1269 reference_max_width = 0;
1270 for (file_index = 0; file_index < number_input_files; file_index++)
1272 line_ordinal = file_line_count[file_index] + 1;
1274 line_ordinal -= file_line_count[file_index - 1];
1275 sprintf (ordinal_string, "%d", line_ordinal);
1276 reference_width = strlen (ordinal_string);
1277 if (input_file_name[file_index])
1278 reference_width += strlen (input_file_name[file_index]);
1279 if (reference_width > reference_max_width)
1280 reference_max_width = reference_width;
1282 reference_max_width++;
1283 reference.start = xmalloc ((size_t) reference_max_width + 1);
1286 /* If the reference appears to the left of the output line, reserve some
1287 space for it right away, including one gap size. */
1289 if ((auto_reference || input_reference) && !right_reference)
1290 line_width -= reference_max_width + gap_size;
1292 /* The output lines, minimally, will contain from left to right a left
1293 context, a gap, and a keyword followed by the right context with no
1294 special intervening gap. Half of the line width is dedicated to the
1295 left context and the gap, the other half is dedicated to the keyword
1296 and the right context; these values are computed once and for all here.
1297 There also are tail and head wrap around fields, used when the keyword
1298 is near the beginning or the end of the line, or when some long word
1299 cannot fit in, but leave place from wrapped around shorter words. The
1300 maximum width of these fields are recomputed separately for each line,
1301 on a case by case basis. It is worth noting that it cannot happen that
1302 both the tail and head fields are used at once. */
1304 half_line_width = line_width / 2;
1305 before_max_width = half_line_width - gap_size;
1306 keyafter_max_width = half_line_width;
1308 /* If truncation_string is the empty string, make it NULL to speed up
1309 tests. In this case, truncation_string_length will never get used, so
1310 there is no need to set it. */
1312 if (truncation_string && *truncation_string)
1313 truncation_string_length = strlen (truncation_string);
1315 truncation_string = NULL;
1320 /* When flagging truncation at the left of the keyword, the
1321 truncation mark goes at the beginning of the before field,
1322 unless there is a head field, in which case the mark goes at the
1323 left of the head field. When flagging truncation at the right
1324 of the keyword, the mark goes at the end of the keyafter field,
1325 unless there is a tail field, in which case the mark goes at the
1326 end of the tail field. Only eight combination cases could arise
1327 for truncation marks:
1330 . One beginning the before field.
1331 . One beginning the head field.
1332 . One ending the keyafter field.
1333 . One ending the tail field.
1334 . One beginning the before field, another ending the keyafter field.
1335 . One ending the tail field, another beginning the before field.
1336 . One ending the keyafter field, another beginning the head field.
1338 So, there is at most two truncation marks, which could appear both
1339 on the left side of the center of the output line, both on the
1340 right side, or one on either side. */
1342 before_max_width -= 2 * truncation_string_length;
1343 keyafter_max_width -= 2 * truncation_string_length;
1348 /* I never figured out exactly how UNIX' ptx plans the output width
1349 of its various fields. If GNU extensions are disabled, do not
1350 try computing the field widths correctly; instead, use the
1351 following formula, which does not completely imitate UNIX' ptx,
1354 keyafter_max_width -= 2 * truncation_string_length + 1;
1357 /* Compute which characters need special output processing. Initialize
1358 by flagging any white space character. Some systems do not consider
1359 form feed as a space character, but we do. */
1361 for (character = 0; character < CHAR_SET_SIZE; character++)
1362 edited_flag[character] = ISSPACE (character) != 0;
1363 edited_flag['\f'] = 1;
1365 /* Complete the special character flagging according to selected output
1368 switch (output_format)
1370 case UNKNOWN_FORMAT:
1371 /* Should never happen. */
1378 /* `Quote' characters should be doubled. */
1380 edited_flag['"'] = 1;
1385 /* Various characters need special processing. */
1387 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1388 edited_flag[(unsigned char) *cursor] = 1;
1390 /* Any character with 8th bit set will print to a single space, unless
1391 it is diacriticized. */
1393 for (character = 0200; character < CHAR_SET_SIZE; character++)
1394 edited_flag[character] = todiac (character) != 0;
1399 /*------------------------------------------------------------------.
1400 | Compute the position and length of all the output fields, given a |
1401 | pointer to some OCCURS. |
1402 `------------------------------------------------------------------*/
1405 define_all_fields (OCCURS *occurs)
1407 int tail_max_width; /* allowable width of tail field */
1408 int head_max_width; /* allowable width of head field */
1409 char *cursor; /* running cursor in source text */
1410 char *left_context_start; /* start of left context */
1411 char *right_context_end; /* end of right context */
1412 char *left_field_start; /* conservative start for `head'/`before' */
1413 int file_index; /* index in text input file arrays */
1414 const char *file_name; /* file name for reference */
1415 int line_ordinal; /* line ordinal for reference */
1417 /* Define `keyafter', start of left context and end of right context.
1418 `keyafter' starts at the saved position for keyword and extend to the
1419 right from the end of the keyword, eating separators or full words, but
1420 not beyond maximum allowed width for `keyafter' field or limit for the
1421 right context. Suffix spaces will be removed afterwards. */
1423 keyafter.start = occurs->key.start;
1424 keyafter.end = keyafter.start + occurs->key.size;
1425 left_context_start = keyafter.start + occurs->left;
1426 right_context_end = keyafter.start + occurs->right;
1428 cursor = keyafter.end;
1429 while (cursor < right_context_end
1430 && cursor <= keyafter.start + keyafter_max_width)
1432 keyafter.end = cursor;
1433 SKIP_SOMETHING (cursor, right_context_end);
1435 if (cursor <= keyafter.start + keyafter_max_width)
1436 keyafter.end = cursor;
1438 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1440 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1442 /* When the left context is wide, it might take some time to catch up from
1443 the left context boundary to the beginning of the `head' or `before'
1444 fields. So, in this case, to speed the catchup, we jump back from the
1445 keyword, using some secure distance, possibly falling in the middle of
1446 a word. A secure backward jump would be at least half the maximum
1447 width of a line, plus the size of the longest word met in the whole
1448 input. We conclude this backward jump by a skip forward of at least
1449 one word. In this manner, we should not inadvertently accept only part
1450 of a word. From the reached point, when it will be time to fix the
1451 beginning of `head' or `before' fields, we will skip forward words or
1452 delimiters until we get sufficiently near. */
1454 if (-occurs->left > half_line_width + maximum_word_length)
1457 = keyafter.start - (half_line_width + maximum_word_length);
1458 SKIP_SOMETHING (left_field_start, keyafter.start);
1461 left_field_start = keyafter.start + occurs->left;
1463 /* `before' certainly ends at the keyword, but not including separating
1464 spaces. It starts after than the saved value for the left context, by
1465 advancing it until it falls inside the maximum allowed width for the
1466 before field. There will be no prefix spaces either. `before' only
1467 advances by skipping single separators or whole words. */
1469 before.start = left_field_start;
1470 before.end = keyafter.start;
1471 SKIP_WHITE_BACKWARDS (before.end, before.start);
1473 while (before.start + before_max_width < before.end)
1474 SKIP_SOMETHING (before.start, before.end);
1476 if (truncation_string)
1478 cursor = before.start;
1479 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1480 before_truncation = cursor > left_context_start;
1483 before_truncation = 0;
1485 SKIP_WHITE (before.start, text_buffer.end);
1487 /* The tail could not take more columns than what has been left in the
1488 left context field, and a gap is mandatory. It starts after the
1489 right context, and does not contain prefixed spaces. It ends at
1490 the end of line, the end of buffer or when the tail field is full,
1491 whichever comes first. It cannot contain only part of a word, and
1492 has no suffixed spaces. */
1495 = before_max_width - (before.end - before.start) - gap_size;
1497 if (tail_max_width > 0)
1499 tail.start = keyafter.end;
1500 SKIP_WHITE (tail.start, text_buffer.end);
1502 tail.end = tail.start;
1504 while (cursor < right_context_end
1505 && cursor < tail.start + tail_max_width)
1508 SKIP_SOMETHING (cursor, right_context_end);
1511 if (cursor < tail.start + tail_max_width)
1514 if (tail.end > tail.start)
1516 keyafter_truncation = 0;
1517 tail_truncation = truncation_string && tail.end < right_context_end;
1520 tail_truncation = 0;
1522 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1527 /* No place left for a tail field. */
1531 tail_truncation = 0;
1534 /* `head' could not take more columns than what has been left in the right
1535 context field, and a gap is mandatory. It ends before the left
1536 context, and does not contain suffixed spaces. Its pointer is advanced
1537 until the head field has shrunk to its allowed width. It cannot
1538 contain only part of a word, and has no suffixed spaces. */
1541 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1543 if (head_max_width > 0)
1545 head.end = before.start;
1546 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1548 head.start = left_field_start;
1549 while (head.start + head_max_width < head.end)
1550 SKIP_SOMETHING (head.start, head.end);
1552 if (head.end > head.start)
1554 before_truncation = 0;
1555 head_truncation = (truncation_string
1556 && head.start > left_context_start);
1559 head_truncation = 0;
1561 SKIP_WHITE (head.start, head.end);
1566 /* No place left for a head field. */
1570 head_truncation = 0;
1576 /* Construct the reference text in preallocated space from the file
1577 name and the line number. Find out in which file the reference
1578 occurred. Standard input yields an empty file name. Insure line
1579 numbers are one based, even if they are computed zero based. */
1582 while (file_line_count[file_index] < occurs->reference)
1585 file_name = input_file_name[file_index];
1589 line_ordinal = occurs->reference + 1;
1591 line_ordinal -= file_line_count[file_index - 1];
1593 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1594 reference.end = reference.start + strlen (reference.start);
1596 else if (input_reference)
1599 /* Reference starts at saved position for reference and extends right
1600 until some white space is met. */
1602 reference.start = keyafter.start + (DELTA) occurs->reference;
1603 reference.end = reference.start;
1604 SKIP_NON_WHITE (reference.end, right_context_end);
1608 /* Formatting and actual output - control routines. */
1610 /*----------------------------------------------------------------------.
1611 | Output the current output fields as one line for `troff' or `nroff'. |
1612 `----------------------------------------------------------------------*/
1615 output_one_roff_line (void)
1617 /* Output the `tail' field. */
1619 printf (".%s \"", macro_name);
1621 if (tail_truncation)
1622 fputs (truncation_string, stdout);
1625 /* Output the `before' field. */
1627 fputs (" \"", stdout);
1628 if (before_truncation)
1629 fputs (truncation_string, stdout);
1630 print_field (before);
1633 /* Output the `keyafter' field. */
1635 fputs (" \"", stdout);
1636 print_field (keyafter);
1637 if (keyafter_truncation)
1638 fputs (truncation_string, stdout);
1641 /* Output the `head' field. */
1643 fputs (" \"", stdout);
1644 if (head_truncation)
1645 fputs (truncation_string, stdout);
1649 /* Conditionally output the `reference' field. */
1651 if (auto_reference || input_reference)
1653 fputs (" \"", stdout);
1654 print_field (reference);
1661 /*---------------------------------------------------------.
1662 | Output the current output fields as one line for `TeX'. |
1663 `---------------------------------------------------------*/
1666 output_one_tex_line (void)
1668 BLOCK key; /* key field, isolated */
1669 BLOCK after; /* after field, isolated */
1670 char *cursor; /* running cursor in source text */
1672 printf ("\\%s ", macro_name);
1675 fputs ("}{", stdout);
1676 print_field (before);
1677 fputs ("}{", stdout);
1678 key.start = keyafter.start;
1679 after.end = keyafter.end;
1680 cursor = keyafter.start;
1681 SKIP_SOMETHING (cursor, keyafter.end);
1683 after.start = cursor;
1685 fputs ("}{", stdout);
1686 print_field (after);
1687 fputs ("}{", stdout);
1690 if (auto_reference || input_reference)
1693 print_field (reference);
1699 /*-------------------------------------------------------------------.
1700 | Output the current output fields as one line for a dumb terminal. |
1701 `-------------------------------------------------------------------*/
1704 output_one_dumb_line (void)
1706 if (!right_reference)
1711 /* Output the `reference' field, in such a way that GNU emacs
1712 next-error will handle it. The ending colon is taken from the
1713 gap which follows. */
1715 print_field (reference);
1717 print_spaces (reference_max_width
1719 - (reference.end - reference.start)
1725 /* Output the `reference' field and its following gap. */
1727 print_field (reference);
1728 print_spaces (reference_max_width
1730 - (reference.end - reference.start));
1734 if (tail.start < tail.end)
1736 /* Output the `tail' field. */
1739 if (tail_truncation)
1740 fputs (truncation_string, stdout);
1742 print_spaces (half_line_width - gap_size
1743 - (before.end - before.start)
1744 - (before_truncation ? truncation_string_length : 0)
1745 - (tail.end - tail.start)
1746 - (tail_truncation ? truncation_string_length : 0));
1749 print_spaces (half_line_width - gap_size
1750 - (before.end - before.start)
1751 - (before_truncation ? truncation_string_length : 0));
1753 /* Output the `before' field. */
1755 if (before_truncation)
1756 fputs (truncation_string, stdout);
1757 print_field (before);
1759 print_spaces (gap_size);
1761 /* Output the `keyafter' field. */
1763 print_field (keyafter);
1764 if (keyafter_truncation)
1765 fputs (truncation_string, stdout);
1767 if (head.start < head.end)
1769 /* Output the `head' field. */
1771 print_spaces (half_line_width
1772 - (keyafter.end - keyafter.start)
1773 - (keyafter_truncation ? truncation_string_length : 0)
1774 - (head.end - head.start)
1775 - (head_truncation ? truncation_string_length : 0));
1776 if (head_truncation)
1777 fputs (truncation_string, stdout);
1782 if ((auto_reference || input_reference) && right_reference)
1783 print_spaces (half_line_width
1784 - (keyafter.end - keyafter.start)
1785 - (keyafter_truncation ? truncation_string_length : 0));
1787 if ((auto_reference || input_reference) && right_reference)
1789 /* Output the `reference' field. */
1791 print_spaces (gap_size);
1792 print_field (reference);
1798 /*------------------------------------------------------------------------.
1799 | Scan the whole occurs table and, for each entry, output one line in the |
1800 | appropriate format. |
1801 `------------------------------------------------------------------------*/
1804 generate_all_output (void)
1806 size_t occurs_index; /* index of keyword entry being processed */
1807 OCCURS *occurs_cursor; /* current keyword entry being processed */
1809 /* The following assignments are useful to provide default values in case
1810 line contexts or references are not used, in which case these variables
1811 would never be computed. */
1815 tail_truncation = 0;
1819 head_truncation = 0;
1821 /* Loop over all keyword occurrences. */
1823 occurs_cursor = occurs_table[0];
1825 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1827 /* Compute the exact size of every field and whenever truncation flags
1828 are present or not. */
1830 define_all_fields (occurs_cursor);
1832 /* Produce one output line according to selected format. */
1834 switch (output_format)
1836 case UNKNOWN_FORMAT:
1837 /* Should never happen. */
1840 output_one_dumb_line ();
1844 output_one_roff_line ();
1848 output_one_tex_line ();
1852 /* Advance the cursor into the occurs table. */
1858 /* Option decoding and main program. */
1860 /*------------------------------------------------------.
1861 | Print program identification and options, then exit. |
1862 `------------------------------------------------------*/
1867 if (status != EXIT_SUCCESS)
1868 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1873 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1874 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1875 program_name, program_name);
1877 Output a permuted index, including context, of the words in the input files.\n\
1881 Mandatory arguments to long options are mandatory for short options too.\n\
1884 -A, --auto-reference output automatically generated references\n\
1885 -C, --copyright display Copyright and copying conditions\n\
1886 -G, --traditional behave more like System V `ptx'\n\
1887 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1890 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1891 -O, --format=roff generate output as roff directives\n\
1892 -R, --right-side-refs put references at right, not counted in -w\n\
1893 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1894 -T, --format=tex generate output as TeX directives\n\
1897 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1898 -b, --break-file=FILE word break characters in this FILE\n\
1899 -f, --ignore-case fold lower case to upper case for sorting\n\
1900 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1901 -i, --ignore-file=FILE read ignore word list from FILE\n\
1902 -o, --only-file=FILE read only word list from this FILE\n\
1905 -r, --references first field of each line is a reference\n\
1906 -t, --typeset-mode - not implemented -\n\
1907 -w, --width=NUMBER output width in columns, reference excluded\n\
1909 fputs (HELP_OPTION_DESCRIPTION, stdout);
1910 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1913 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1919 /*----------------------------------------------------------------------.
1920 | Main program. Decode ARGC arguments passed through the ARGV array of |
1921 | strings, then launch execution. |
1922 `----------------------------------------------------------------------*/
1924 /* Long options equivalences. */
1925 static const struct option long_options[] =
1927 {"auto-reference", no_argument, NULL, 'A'},
1928 {"break-file", required_argument, NULL, 'b'},
1929 {"copyright", no_argument, NULL, 'C'},
1930 {"flag-truncation", required_argument, NULL, 'F'},
1931 {"ignore-case", no_argument, NULL, 'f'},
1932 {"gap-size", required_argument, NULL, 'g'},
1933 {"ignore-file", required_argument, NULL, 'i'},
1934 {"macro-name", required_argument, NULL, 'M'},
1935 {"only-file", required_argument, NULL, 'o'},
1936 {"references", no_argument, NULL, 'r'},
1937 {"right-side-refs", no_argument, NULL, 'R'},
1938 {"format", required_argument, NULL, 10},
1939 {"sentence-regexp", required_argument, NULL, 'S'},
1940 {"traditional", no_argument, NULL, 'G'},
1941 {"typeset-mode", no_argument, NULL, 't'},
1942 {"width", required_argument, NULL, 'w'},
1943 {"word-regexp", required_argument, NULL, 'W'},
1944 {GETOPT_HELP_OPTION_DECL},
1945 {GETOPT_VERSION_OPTION_DECL},
1949 static char const* const format_args[] =
1954 static enum Format const format_vals[] =
1956 ROFF_FORMAT, TEX_FORMAT
1960 main (int argc, char **argv)
1962 int optchar; /* argument character */
1963 int file_index; /* index in text input file arrays */
1965 /* Decode program options. */
1967 initialize_main (&argc, &argv);
1968 program_name = argv[0];
1969 setlocale (LC_ALL, "");
1970 bindtextdomain (PACKAGE, LOCALEDIR);
1971 textdomain (PACKAGE);
1973 atexit (close_stdout);
1975 #if HAVE_SETCHRCLASS
1979 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1980 long_options, NULL),
1986 usage (EXIT_FAILURE);
1993 This program is free software; you can redistribute it and/or modify\n\
1994 it under the terms of the GNU General Public License as published by\n\
1995 the Free Software Foundation; either version 2, or (at your option)\n\
1996 any later version.\n\
2000 This program is distributed in the hope that it will be useful,\n\
2001 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
2002 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
2003 GNU General Public License for more details.\n\
2007 You should have received a copy of the GNU General Public License\n\
2008 along with this program; if not, write to the Free Software Foundation,\n\
2009 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
2012 exit (EXIT_SUCCESS);
2019 break_file = optarg;
2027 gap_size = atoi (optarg);
2031 ignore_file = optarg;
2039 input_reference = 1;
2043 /* Yet to understand... */
2047 line_width = atoi (optarg);
2055 truncation_string = copy_unescaped_string (optarg);
2059 macro_name = optarg;
2063 output_format = ROFF_FORMAT;
2067 right_reference = 1;
2071 context_regex_string = copy_unescaped_string (optarg);
2075 output_format = TEX_FORMAT;
2079 word_regex_string = copy_unescaped_string (optarg);
2083 output_format = XARGMATCH ("--format", optarg,
2084 format_args, format_vals);
2085 case_GETOPT_HELP_CHAR;
2087 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2091 /* Change the default Ignore file if one is defined. */
2093 #ifdef DEFAULT_IGNORE_FILE
2095 ignore_file = DEFAULT_IGNORE_FILE;
2098 /* Process remaining arguments. If GNU extensions are enabled, process
2099 all arguments as input parameters. If disabled, accept at most two
2100 arguments, the second of which is an output parameter. */
2105 /* No more argument simply means: read standard input. */
2107 input_file_name = xmalloc (sizeof (const char *));
2108 file_line_count = xmalloc (sizeof (int));
2109 number_input_files = 1;
2110 input_file_name[0] = NULL;
2112 else if (gnu_extensions)
2114 number_input_files = argc - optind;
2116 = xmalloc (number_input_files * sizeof (const char *));
2118 = xmalloc (number_input_files * sizeof (int));
2120 for (file_index = 0; file_index < number_input_files; file_index++)
2122 input_file_name[file_index] = argv[optind];
2123 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2124 input_file_name[0] = NULL;
2126 input_file_name[0] = argv[optind];
2133 /* There is one necessary input file. */
2135 number_input_files = 1;
2136 input_file_name = xmalloc (sizeof (const char *));
2137 file_line_count = xmalloc (sizeof (int));
2138 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2139 input_file_name[0] = NULL;
2141 input_file_name[0] = argv[optind];
2144 /* Redirect standard output, only if requested. */
2148 /* FIXME: don't fclose here? */
2150 if (fopen (argv[optind], "w") == NULL)
2151 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2155 /* Diagnose any other argument as an error. */
2158 usage (EXIT_FAILURE);
2161 /* If the output format has not been explicitly selected, choose dumb
2162 terminal format if GNU extensions are enabled, else `roff' format. */
2164 if (output_format == UNKNOWN_FORMAT)
2165 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2167 /* Initialize the main tables. */
2169 initialize_regex ();
2171 /* Read `Break character' file, if any. */
2174 digest_break_file (break_file);
2176 /* Read `Ignore words' file and `Only words' files, if any. If any of
2177 these files is empty, reset the name of the file to NULL, to avoid
2178 unnecessary calls to search_table. */
2182 digest_word_file (ignore_file, &ignore_table);
2183 if (ignore_table.length == 0)
2189 digest_word_file (only_file, &only_table);
2190 if (only_table.length == 0)
2194 /* Prepare to study all the input files. */
2196 number_of_occurs[0] = 0;
2197 total_line_count = 0;
2198 maximum_word_length = 0;
2199 reference_max_width = 0;
2201 for (file_index = 0; file_index < number_input_files; file_index++)
2204 /* Read the file in core, than study it. */
2206 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2207 find_occurs_in_text ();
2209 /* Maintain for each file how many lines has been read so far when its
2210 end is reached. Incrementing the count first is a simple kludge to
2211 handle a possible incomplete line at end of file. */
2214 file_line_count[file_index] = total_line_count;
2217 /* Do the output process phase. */
2219 sort_found_occurs ();
2220 fix_output_parameters ();
2221 generate_all_output ();
2225 exit (EXIT_SUCCESS);