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>
34 /* The official name of this program (e.g., no `g' prefix). */
35 #define PROGRAM_NAME "ptx"
37 /* Note to translator: Please translate "F. Pinard" to "François
38 Pinard" if "ç" (c-with-cedilla) is available in the
39 translation's character set and encoding. */
40 #define AUTHORS _("F. Pinard")
42 /* Number of possible characters in a byte. */
43 #define CHAR_SET_SIZE 256
45 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
46 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
47 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
48 #define OCTTOBIN(C) ((C) - '0')
50 /* Debugging the memory allocator. */
53 # define MALLOC_FUNC_CHECK 1
57 /* Global definitions. */
59 /* Reallocation step when swallowing non regular files. The value is not
60 the actual reallocation step, but its base two logarithm. */
61 #define SWALLOW_REALLOC_LOG 12
63 /* Imported from "regex.c". */
66 /* The name this program was run with. */
69 /* Program options. */
73 UNKNOWN_FORMAT, /* output format still unknown */
74 DUMB_FORMAT, /* output for a dumb terminal */
75 ROFF_FORMAT, /* output for `troff' or `nroff' */
76 TEX_FORMAT /* output for `TeX' or `LaTeX' */
79 int gnu_extensions = 1; /* trigger all GNU extensions */
80 int auto_reference = 0; /* references are `file_name:line_number:' */
81 int input_reference = 0; /* references at beginning of input lines */
82 int right_reference = 0; /* output references after right context */
83 int line_width = 72; /* output line width in characters */
84 int gap_size = 3; /* number of spaces between output fields */
85 const char *truncation_string = "/";
86 /* string used to mark line truncations */
87 const char *macro_name = "xx"; /* macro name for roff or TeX output */
88 enum Format output_format = UNKNOWN_FORMAT;
91 int ignore_case = 0; /* fold lower to upper case for sorting */
92 const char *context_regex_string = NULL;
93 /* raw regex for end of context */
94 const char *word_regex_string = NULL;
95 /* raw regex for a keyword */
96 const char *break_file = NULL; /* name of the `Break characters' file */
97 const char *only_file = NULL; /* name of the `Only words' file */
98 const char *ignore_file = NULL; /* name of the `Ignore words' file */
100 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
101 whole file. A WORD is something smaller, its length should fit in a
102 short integer. A WORD_TABLE may contain several WORDs. */
106 char *start; /* pointer to beginning of region */
107 char *end; /* pointer to end + 1 of region */
113 char *start; /* pointer to beginning of region */
114 short size; /* length of the region */
120 WORD *start; /* array of WORDs */
121 size_t alloc; /* allocated length */
122 size_t length; /* number of used entries */
126 /* Pattern description tables. */
128 /* For each character, provide its folded equivalent. */
129 unsigned char folded_chars[CHAR_SET_SIZE];
131 /* For each character, indicate if it is part of a word. */
132 char syntax_table[CHAR_SET_SIZE];
133 char *re_syntax_table = syntax_table;
135 /* Compiled regex for end of context. */
136 struct re_pattern_buffer *context_regex;
138 /* End of context pattern register indices. */
139 struct re_registers context_regs;
141 /* Compiled regex for a keyword. */
142 struct re_pattern_buffer *word_regex;
144 /* Keyword pattern register indices. */
145 struct re_registers word_regs;
147 /* A word characters fastmap is used only when no word regexp has been
148 provided. A word is then made up of a sequence of one or more characters
149 allowed by the fastmap. Contains !0 if character allowed in word. Not
150 only this is faster in most cases, but it simplifies the implementation
151 of the Break files. */
152 char word_fastmap[CHAR_SET_SIZE];
154 /* Maximum length of any word read. */
155 int maximum_word_length;
157 /* Maximum width of any reference used. */
158 int reference_max_width;
160 /* Ignore and Only word tables. */
162 WORD_TABLE ignore_table; /* table of words to ignore */
163 WORD_TABLE only_table; /* table of words to select */
165 /* Source text table, and scanning macros. */
167 int number_input_files; /* number of text input files */
168 int total_line_count; /* total number of lines seen so far */
169 const char **input_file_name; /* array of text input file names */
170 int *file_line_count; /* array of `total_line_count' values at end */
172 BLOCK text_buffer; /* file to study */
173 char *text_buffer_maxend; /* allocated end of text_buffer */
175 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
177 #define SKIP_NON_WHITE(cursor, limit) \
178 while (cursor < limit && !ISSPACE(*cursor)) \
181 #define SKIP_WHITE(cursor, limit) \
182 while (cursor < limit && ISSPACE(*cursor)) \
185 #define SKIP_WHITE_BACKWARDS(cursor, start) \
186 while (cursor > start && ISSPACE(cursor[-1])) \
189 #define SKIP_SOMETHING(cursor, limit) \
190 if (word_regex_string) \
193 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
194 cursor += count <= 0 ? 1 : count; \
196 else if (word_fastmap[(unsigned char) *cursor]) \
197 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
202 /* Occurrences table.
204 The `keyword' pointer provides the central word, which is surrounded
205 by a left context and a right context. The `keyword' and `length'
206 field allow full 8-bit characters keys, even including NULs. At other
207 places in this program, the name `keyafter' refers to the keyword
208 followed by its right context.
210 The left context does not extend, towards the beginning of the file,
211 further than a distance given by the `left' value. This value is
212 relative to the keyword beginning, it is usually negative. This
213 insures that, except for white space, we will never have to backward
214 scan the source text, when it is time to generate the final output
217 The right context, indirectly attainable through the keyword end, does
218 not extend, towards the end of the file, further than a distance given
219 by the `right' value. This value is relative to the keyword
220 beginning, it is usually positive.
222 When automatic references are used, the `reference' value is the
223 overall line number in all input files read so far, in this case, it
224 is of type (int). When input references are used, the `reference'
225 value indicates the distance between the keyword beginning and the
226 start of the reference field, it is of type (DELTA) and usually
229 typedef short DELTA; /* to hold displacement within one context */
233 WORD key; /* description of the keyword */
234 DELTA left; /* distance to left context start */
235 DELTA right; /* distance to right context end */
236 int reference; /* reference descriptor */
240 /* The various OCCURS tables are indexed by the language. But the time
241 being, there is no such multiple language support. */
243 OCCURS *occurs_table[1]; /* all words retained from the read text */
244 size_t occurs_alloc[1]; /* allocated size of occurs_table */
245 size_t number_of_occurs[1]; /* number of used slots in occurs_table */
248 /* Communication among output routines. */
250 /* Indicate if special output processing is requested for each character. */
251 char edited_flag[CHAR_SET_SIZE];
253 int half_line_width; /* half of line width, reference excluded */
254 int before_max_width; /* maximum width of before field */
255 int keyafter_max_width; /* maximum width of keyword-and-after field */
256 int truncation_string_length; /* length of string used to flag truncation */
258 /* When context is limited by lines, wraparound may happen on final output:
259 the `head' pointer gives access to some supplementary left context which
260 will be seen at the end of the output line, the `tail' pointer gives
261 access to some supplementary right context which will be seen at the
262 beginning of the output line. */
264 BLOCK tail; /* tail field */
265 int tail_truncation; /* flag truncation after the tail field */
267 BLOCK before; /* before field */
268 int before_truncation; /* flag truncation before the before field */
270 BLOCK keyafter; /* keyword-and-after field */
271 int keyafter_truncation; /* flag truncation after the keyafter field */
273 BLOCK head; /* head field */
274 int head_truncation; /* flag truncation before the head field */
276 BLOCK reference; /* reference field for input reference mode */
278 /* Miscellaneous routines. */
280 /*------------------------------------------------------.
281 | Duplicate string STRING, while evaluating \-escapes. |
282 `------------------------------------------------------*/
284 /* Loosely adapted from GNU sh-utils printf.c code. */
287 copy_unescaped_string (const char *string)
289 char *result; /* allocated result */
290 char *cursor; /* cursor in result */
291 int value; /* value of \nnn escape */
292 int length; /* length of \nnn escape */
294 result = xmalloc (strlen (string) + 1);
303 case 'x': /* \xhhh escape, 3 chars maximum */
305 for (length = 0, string++;
306 length < 3 && ISXDIGIT (*string);
308 value = value * 16 + HEXTOBIN (*string);
318 case '0': /* \0ooo escape, 3 chars maximum */
320 for (length = 0, string++;
321 length < 3 && ISODIGIT (*string);
323 value = value * 8 + OCTTOBIN (*string);
327 case 'a': /* alert */
336 case 'b': /* backspace */
341 case 'c': /* cancel the rest of the output */
346 case 'f': /* form feed */
351 case 'n': /* new line */
356 case 'r': /* carriage return */
361 case 't': /* horizontal tab */
366 case 'v': /* vertical tab */
377 *cursor++ = *string++;
382 *cursor++ = *string++;
388 /*-------------------------------------------------------------------.
389 | Compile the regex represented by STRING, diagnose and abort if any |
390 | error. Returns the compiled regex structure. |
391 `-------------------------------------------------------------------*/
393 static struct re_pattern_buffer *
394 alloc_and_compile_regex (const char *string)
396 struct re_pattern_buffer *pattern; /* newly allocated structure */
397 const char *message; /* error message returned by regex.c */
399 pattern = xmalloc (sizeof *pattern);
400 memset (pattern, 0, sizeof (struct re_pattern_buffer));
402 pattern->buffer = NULL;
403 pattern->allocated = 0;
404 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
405 pattern->fastmap = xmalloc ((size_t) CHAR_SET_SIZE);
407 message = re_compile_pattern (string, (int) strlen (string), pattern);
409 error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);
411 /* The fastmap should be compiled before `re_match'. The following
412 call is not mandatory, because `re_search' is always called sooner,
413 and it compiles the fastmap if this has not been done yet. */
415 re_compile_fastmap (pattern);
417 /* Do not waste extra allocated space. */
419 if (pattern->allocated > pattern->used)
422 = xrealloc (pattern->buffer, (size_t) pattern->used);
423 pattern->allocated = pattern->used;
429 /*------------------------------------------------------------------------.
430 | This will initialize various tables for pattern match and compiles some |
432 `------------------------------------------------------------------------*/
435 initialize_regex (void)
437 int character; /* character value */
439 /* Initialize the regex syntax table. */
441 for (character = 0; character < CHAR_SET_SIZE; character++)
442 syntax_table[character] = ISALPHA (character) ? Sword : 0;
444 /* Initialize the case folding table. */
447 for (character = 0; character < CHAR_SET_SIZE; character++)
448 folded_chars[character] = TOUPPER (character);
450 /* Unless the user already provided a description of the end of line or
451 end of sentence sequence, select an end of line sequence to compile.
452 If the user provided an empty definition, thus disabling end of line
453 or sentence feature, make it NULL to speed up tests. If GNU
454 extensions are enabled, use end of sentence like in GNU emacs. If
455 disabled, use end of lines. */
457 if (context_regex_string)
459 if (!*context_regex_string)
460 context_regex_string = NULL;
462 else if (gnu_extensions && !input_reference)
463 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
465 context_regex_string = "\n";
467 if (context_regex_string)
468 context_regex = alloc_and_compile_regex (context_regex_string);
470 /* If the user has already provided a non-empty regexp to describe
471 words, compile it. Else, unless this has already been done through
472 a user provided Break character file, construct a fastmap of
473 characters that may appear in a word. If GNU extensions enabled,
474 include only letters of the underlying character set. If disabled,
475 include almost everything, even punctuations; stop only on white
478 if (word_regex_string && *word_regex_string)
479 word_regex = alloc_and_compile_regex (word_regex_string);
480 else if (!break_file)
487 for (character = 0; character < CHAR_SET_SIZE; character++)
488 word_fastmap[character] = ISALPHA (character) ? 1 : 0;
493 /* Simulate [^ \t\n]+. */
495 memset (word_fastmap, 1, CHAR_SET_SIZE);
496 word_fastmap[' '] = 0;
497 word_fastmap['\t'] = 0;
498 word_fastmap['\n'] = 0;
503 /*------------------------------------------------------------------------.
504 | This routine will attempt to swallow a whole file name FILE_NAME into a |
505 | contiguous region of memory and return a description of it into BLOCK. |
506 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
508 | Previously, in some cases, white space compression was attempted while |
509 | inputting text. This was defeating some regexps like default end of |
510 | sentence, which checks for two consecutive spaces. If white space |
511 | compression is ever reinstated, it should be in output routines. |
512 `------------------------------------------------------------------------*/
515 swallow_file_in_memory (const char *file_name, BLOCK *block)
517 int file_handle; /* file descriptor number */
518 struct stat stat_block; /* stat block for file */
519 size_t allocated_length; /* allocated length of memory buffer */
520 size_t used_length; /* used length in memory buffer */
521 int read_length; /* number of character gotten on last read */
523 /* As special cases, a file name which is NULL or "-" indicates standard
524 input, which is already opened. In all other cases, open the file from
526 bool using_stdin = !file_name || !*file_name || strcmp (file_name, "-") == 0;
528 file_handle = STDIN_FILENO;
530 if ((file_handle = open (file_name, O_RDONLY)) < 0)
531 error (EXIT_FAILURE, errno, "%s", file_name);
533 /* If the file is a plain, regular file, allocate the memory buffer all at
534 once and swallow the file in one blow. In other cases, read the file
535 repeatedly in smaller chunks until we have it all, reallocating memory
536 once in a while, as we go. */
538 if (fstat (file_handle, &stat_block) < 0)
539 error (EXIT_FAILURE, errno, "%s", file_name);
541 if (S_ISREG (stat_block.st_mode))
543 size_t in_memory_size;
545 block->start = xmalloc ((size_t) stat_block.st_size);
547 if ((in_memory_size = read (file_handle,
548 block->start, (size_t) stat_block.st_size))
549 != stat_block.st_size)
552 /* On MSDOS, in memory size may be smaller than the file
553 size, because of end of line conversions. But it can
554 never be smaller than half the file size, because the
555 minimum is when all lines are empty and terminated by
557 if (in_memory_size != (size_t)-1
558 && in_memory_size >= stat_block.st_size / 2)
559 block->start = xrealloc (block->start, in_memory_size);
561 #endif /* not MSDOS */
563 error (EXIT_FAILURE, errno, "%s", file_name);
565 block->end = block->start + in_memory_size;
569 block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
571 allocated_length = (1 << SWALLOW_REALLOC_LOG);
573 while (read_length = read (file_handle,
574 block->start + used_length,
575 allocated_length - used_length),
578 used_length += read_length;
579 if (used_length == allocated_length)
581 allocated_length += (1 << SWALLOW_REALLOC_LOG);
583 = xrealloc (block->start, allocated_length);
588 error (EXIT_FAILURE, errno, "%s", file_name);
590 block->end = block->start + used_length;
593 /* Close the file, but only if it was not the standard input. */
595 if (! using_stdin && close (file_handle) != 0)
596 error (EXIT_FAILURE, errno, "%s", file_name);
599 /* Sort and search routines. */
601 /*--------------------------------------------------------------------------.
602 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
603 | Return less than 0 if the first word goes before the second; return |
604 | greater than 0 if the first word goes after the second. |
606 | If a word is indeed a prefix of the other, the shorter should go first. |
607 `--------------------------------------------------------------------------*/
610 compare_words (const void *void_first, const void *void_second)
612 #define first ((const WORD *) void_first)
613 #define second ((const WORD *) void_second)
614 int length; /* minimum of two lengths */
615 int counter; /* cursor in words */
616 int value; /* value of comparison */
618 length = first->size < second->size ? first->size : second->size;
622 for (counter = 0; counter < length; counter++)
624 value = (folded_chars [(unsigned char) (first->start[counter])]
625 - folded_chars [(unsigned char) (second->start[counter])]);
632 for (counter = 0; counter < length; counter++)
634 value = ((unsigned char) first->start[counter]
635 - (unsigned char) second->start[counter]);
641 return first->size - second->size;
646 /*-----------------------------------------------------------------------.
647 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
648 | go first. In case of a tie, preserve the original order through a |
649 | pointer comparison. |
650 `-----------------------------------------------------------------------*/
653 compare_occurs (const void *void_first, const void *void_second)
655 #define first ((const OCCURS *) void_first)
656 #define second ((const OCCURS *) void_second)
659 value = compare_words (&first->key, &second->key);
660 return value == 0 ? first->key.start - second->key.start : value;
665 /*------------------------------------------------------------.
666 | Return !0 if WORD appears in TABLE. Uses a binary search. |
667 `------------------------------------------------------------*/
670 search_table (WORD *word, WORD_TABLE *table)
672 int lowest; /* current lowest possible index */
673 int highest; /* current highest possible index */
674 int middle; /* current middle index */
675 int value; /* value from last comparison */
678 highest = table->length - 1;
679 while (lowest <= highest)
681 middle = (lowest + highest) / 2;
682 value = compare_words (word, table->start + middle);
684 highest = middle - 1;
693 /*---------------------------------------------------------------------.
694 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
695 | take intermediate copies or table elements, so the sort will be |
696 | stabilized throughout the comparison routine. |
697 `---------------------------------------------------------------------*/
700 sort_found_occurs (void)
703 /* Only one language for the time being. */
705 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
709 /* Parameter files reading routines. */
711 /*----------------------------------------------------------------------.
712 | Read a file named FILE_NAME, containing a set of break characters. |
713 | Build a content to the array word_fastmap in which all characters are |
714 | allowed except those found in the file. Characters may be repeated. |
715 `----------------------------------------------------------------------*/
718 digest_break_file (const char *file_name)
720 BLOCK file_contents; /* to receive a copy of the file */
721 char *cursor; /* cursor in file copy */
723 swallow_file_in_memory (file_name, &file_contents);
725 /* Make the fastmap and record the file contents in it. */
727 memset (word_fastmap, 1, CHAR_SET_SIZE);
728 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
729 word_fastmap[(unsigned char) *cursor] = 0;
734 /* If GNU extensions are enabled, the only way to avoid newline as
735 a break character is to write all the break characters in the
736 file with no newline at all, not even at the end of the file.
737 If disabled, spaces, tabs and newlines are always considered as
738 break characters even if not included in the break file. */
740 word_fastmap[' '] = 0;
741 word_fastmap['\t'] = 0;
742 word_fastmap['\n'] = 0;
745 /* Return the space of the file, which is no more required. */
747 free (file_contents.start);
750 /*-----------------------------------------------------------------------.
751 | Read a file named FILE_NAME, containing one word per line, then |
752 | construct in TABLE a table of WORD descriptors for them. The routine |
753 | swallows the whole file in memory; this is at the expense of space |
754 | needed for newlines, which are useless; however, the reading is fast. |
755 `-----------------------------------------------------------------------*/
758 digest_word_file (const char *file_name, WORD_TABLE *table)
760 BLOCK file_contents; /* to receive a copy of the file */
761 char *cursor; /* cursor in file copy */
762 char *word_start; /* start of the current word */
764 swallow_file_in_memory (file_name, &file_contents);
770 /* Read the whole file. */
772 cursor = file_contents.start;
773 while (cursor < file_contents.end)
776 /* Read one line, and save the word in contains. */
779 while (cursor < file_contents.end && *cursor != '\n')
782 /* Record the word in table if it is not empty. */
784 if (cursor > word_start)
786 if (table->length == table->alloc)
788 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
790 table->alloc = table->alloc * 2 + 1;
791 table->start = xrealloc (table->start,
792 table->alloc * sizeof *table->start);
795 table->start[table->length].start = word_start;
796 table->start[table->length].size = cursor - word_start;
800 /* This test allows for an incomplete line at end of file. */
802 if (cursor < file_contents.end)
806 /* Finally, sort all the words read. */
808 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
811 /* Keyword recognition and selection. */
813 /*----------------------------------------------------------------------.
814 | For each keyword in the source text, constructs an OCCURS structure. |
815 `----------------------------------------------------------------------*/
818 find_occurs_in_text (void)
820 char *cursor; /* for scanning the source text */
821 char *scan; /* for scanning the source text also */
822 char *line_start; /* start of the current input line */
823 char *line_scan; /* newlines scanned until this point */
824 int reference_length; /* length of reference in input mode */
825 WORD possible_key; /* possible key, to ease searches */
826 OCCURS *occurs_cursor; /* current OCCURS under construction */
828 char *context_start; /* start of left context */
829 char *context_end; /* end of right context */
830 char *word_start; /* start of word */
831 char *word_end; /* end of word */
832 char *next_context_start; /* next start of left context */
834 /* reference_length is always used within `if (input_reference)'.
835 However, GNU C diagnoses that it may be used uninitialized. The
836 following assignment is merely to shut it up. */
838 reference_length = 0;
840 /* Tracking where lines start is helpful for reference processing. In
841 auto reference mode, this allows counting lines. In input reference
842 mode, this permits finding the beginning of the references.
844 The first line begins with the file, skip immediately this very first
845 reference in input reference mode, to help further rejection any word
846 found inside it. Also, unconditionally assigning these variable has
847 the happy effect of shutting up lint. */
849 line_start = text_buffer.start;
850 line_scan = line_start;
853 SKIP_NON_WHITE (line_scan, text_buffer.end);
854 reference_length = line_scan - line_start;
855 SKIP_WHITE (line_scan, text_buffer.end);
858 /* Process the whole buffer, one line or one sentence at a time. */
860 for (cursor = text_buffer.start;
861 cursor < text_buffer.end;
862 cursor = next_context_start)
865 /* `context_start' gets initialized before the processing of each
866 line, or once for the whole buffer if no end of line or sentence
867 sequence separator. */
869 context_start = cursor;
871 /* If a end of line or end of sentence sequence is defined and
872 non-empty, `next_context_start' will be recomputed to be the end of
873 each line or sentence, before each one is processed. If no such
874 sequence, then `next_context_start' is set at the end of the whole
875 buffer, which is then considered to be a single line or sentence.
876 This test also accounts for the case of an incomplete line or
877 sentence at the end of the buffer. */
879 if (context_regex_string
880 && (re_search (context_regex, cursor, text_buffer.end - cursor,
881 0, text_buffer.end - cursor, &context_regs)
883 next_context_start = cursor + context_regs.end[0];
886 next_context_start = text_buffer.end;
888 /* Include the separator into the right context, but not any suffix
889 white space in this separator; this insures it will be seen in
890 output and will not take more space than necessary. */
892 context_end = next_context_start;
893 SKIP_WHITE_BACKWARDS (context_end, context_start);
895 /* Read and process a single input line or sentence, one word at a
902 /* If a word regexp has been compiled, use it to skip at the
903 beginning of the next word. If there is no such word, exit
907 if (re_search (word_regex, cursor, context_end - cursor,
908 0, context_end - cursor, &word_regs)
911 word_start = cursor + word_regs.start[0];
912 word_end = cursor + word_regs.end[0];
916 /* Avoid re_search and use the fastmap to skip to the
917 beginning of the next word. If there is no more word in
918 the buffer, exit the loop. */
922 while (scan < context_end
923 && !word_fastmap[(unsigned char) *scan])
926 if (scan == context_end)
931 while (scan < context_end
932 && word_fastmap[(unsigned char) *scan])
938 /* Skip right to the beginning of the found word. */
942 /* Skip any zero length word. Just advance a single position,
943 then go fetch the next word. */
945 if (word_end == word_start)
951 /* This is a genuine, non empty word, so save it as a possible
952 key. Then skip over it. Also, maintain the maximum length of
953 all words read so far. It is mandatory to take the maximum
954 length of all words in the file, without considering if they
955 are actually kept or rejected, because backward jumps at output
956 generation time may fall in *any* word. */
958 possible_key.start = cursor;
959 possible_key.size = word_end - word_start;
960 cursor += possible_key.size;
962 if (possible_key.size > maximum_word_length)
963 maximum_word_length = possible_key.size;
965 /* In input reference mode, update `line_start' from its previous
966 value. Count the lines just in case auto reference mode is
967 also selected. If it happens that the word just matched is
968 indeed part of a reference; just ignore it. */
972 while (line_scan < possible_key.start)
973 if (*line_scan == '\n')
977 line_start = line_scan;
978 SKIP_NON_WHITE (line_scan, text_buffer.end);
979 reference_length = line_scan - line_start;
983 if (line_scan > possible_key.start)
987 /* Ignore the word if an `Ignore words' table exists and if it is
988 part of it. Also ignore the word if an `Only words' table and
989 if it is *not* part of it.
991 It is allowed that both tables be used at once, even if this
992 may look strange for now. Just ignore a word that would appear
993 in both. If regexps are eventually implemented for these
994 tables, the Ignore table could then reject words that would
995 have been previously accepted by the Only table. */
997 if (ignore_file && search_table (&possible_key, &ignore_table))
999 if (only_file && !search_table (&possible_key, &only_table))
1002 /* A non-empty word has been found. First of all, insure
1003 proper allocation of the next OCCURS, and make a pointer to
1004 where it will be constructed. */
1006 if (number_of_occurs[0] == occurs_alloc[0])
1008 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1011 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
1012 occurs_table[0] = xrealloc (occurs_table[0],
1013 occurs_alloc[0] * sizeof *occurs_table[0]);
1016 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1018 /* Define the refence field, if any. */
1023 /* While auto referencing, update `line_start' from its
1024 previous value, counting lines as we go. If input
1025 referencing at the same time, `line_start' has been
1026 advanced earlier, and the following loop is never really
1029 while (line_scan < possible_key.start)
1030 if (*line_scan == '\n')
1034 line_start = line_scan;
1035 SKIP_NON_WHITE (line_scan, text_buffer.end);
1040 occurs_cursor->reference = total_line_count;
1042 else if (input_reference)
1045 /* If only input referencing, `line_start' has been computed
1046 earlier to detect the case the word matched would be part
1047 of the reference. The reference position is simply the
1048 value of `line_start'. */
1050 occurs_cursor->reference
1051 = (DELTA) (line_start - possible_key.start);
1052 if (reference_length > reference_max_width)
1053 reference_max_width = reference_length;
1056 /* Exclude the reference from the context in simple cases. */
1058 if (input_reference && line_start == context_start)
1060 SKIP_NON_WHITE (context_start, context_end);
1061 SKIP_WHITE (context_start, context_end);
1064 /* Completes the OCCURS structure. */
1066 occurs_cursor->key = possible_key;
1067 occurs_cursor->left = context_start - possible_key.start;
1068 occurs_cursor->right = context_end - possible_key.start;
1070 number_of_occurs[0]++;
1075 /* Formatting and actual output - service routines. */
1077 /*-----------------------------------------.
1078 | Prints some NUMBER of spaces on stdout. |
1079 `-----------------------------------------*/
1082 print_spaces (int number)
1086 for (counter = number; counter > 0; counter--)
1090 /*-------------------------------------.
1091 | Prints the field provided by FIELD. |
1092 `-------------------------------------*/
1095 print_field (BLOCK field)
1097 char *cursor; /* Cursor in field to print */
1098 int character; /* Current character */
1099 int base; /* Base character, without diacritic */
1100 int diacritic; /* Diacritic code for the character */
1102 /* Whitespace is not really compressed. Instead, each white space
1103 character (tab, vt, ht etc.) is printed as one single space. */
1105 for (cursor = field.start; cursor < field.end; cursor++)
1107 character = (unsigned char) *cursor;
1108 if (edited_flag[character])
1111 /* First check if this is a diacriticized character.
1113 This works only for TeX. I do not know how diacriticized
1114 letters work with `roff'. Please someone explain it to me! */
1116 diacritic = todiac (character);
1117 if (diacritic != 0 && output_format == TEX_FORMAT)
1119 base = tobase (character);
1123 case 1: /* Latin diphthongs */
1127 fputs ("\\oe{}", stdout);
1131 fputs ("\\OE{}", stdout);
1135 fputs ("\\ae{}", stdout);
1139 fputs ("\\AE{}", stdout);
1147 case 2: /* Acute accent */
1148 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1151 case 3: /* Grave accent */
1152 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1155 case 4: /* Circumflex accent */
1156 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1159 case 5: /* Diaeresis */
1160 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1163 case 6: /* Tilde accent */
1164 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1167 case 7: /* Cedilla */
1168 printf ("\\c{%c}", base);
1171 case 8: /* Small circle beneath */
1175 fputs ("\\aa{}", stdout);
1179 fputs ("\\AA{}", stdout);
1187 case 9: /* Strike through */
1191 fputs ("\\o{}", stdout);
1195 fputs ("\\O{}", stdout);
1206 /* This is not a diacritic character, so handle cases which are
1207 really specific to `roff' or TeX. All white space processing
1208 is done as the default case of this switch. */
1213 /* In roff output format, double any quote. */
1223 /* In TeX output format, precede these with a backslash. */
1225 putchar (character);
1230 /* In TeX output format, precede these with a backslash and
1231 force mathematical mode. */
1232 printf ("$\\%c$", character);
1236 /* In TeX output mode, request production of a backslash. */
1237 fputs ("\\backslash{}", stdout);
1241 /* Any other flagged character produces a single space. */
1250 /* Formatting and actual output - planning routines. */
1252 /*--------------------------------------------------------------------.
1253 | From information collected from command line options and input file |
1254 | readings, compute and fix some output parameter values. |
1255 `--------------------------------------------------------------------*/
1258 fix_output_parameters (void)
1260 int file_index; /* index in text input file arrays */
1261 int line_ordinal; /* line ordinal value for reference */
1262 char ordinal_string[12]; /* edited line ordinal for reference */
1263 int reference_width; /* width for the whole reference */
1264 int character; /* character ordinal */
1265 const char *cursor; /* cursor in some constant strings */
1267 /* In auto reference mode, the maximum width of this field is
1268 precomputed and subtracted from the overall line width. Add one for
1269 the column which separate the file name from the line number. */
1273 reference_max_width = 0;
1274 for (file_index = 0; file_index < number_input_files; file_index++)
1276 line_ordinal = file_line_count[file_index] + 1;
1278 line_ordinal -= file_line_count[file_index - 1];
1279 sprintf (ordinal_string, "%d", line_ordinal);
1280 reference_width = strlen (ordinal_string);
1281 if (input_file_name[file_index])
1282 reference_width += strlen (input_file_name[file_index]);
1283 if (reference_width > reference_max_width)
1284 reference_max_width = reference_width;
1286 reference_max_width++;
1287 reference.start = xmalloc ((size_t) reference_max_width + 1);
1290 /* If the reference appears to the left of the output line, reserve some
1291 space for it right away, including one gap size. */
1293 if ((auto_reference || input_reference) && !right_reference)
1294 line_width -= reference_max_width + gap_size;
1296 /* The output lines, minimally, will contain from left to right a left
1297 context, a gap, and a keyword followed by the right context with no
1298 special intervening gap. Half of the line width is dedicated to the
1299 left context and the gap, the other half is dedicated to the keyword
1300 and the right context; these values are computed once and for all here.
1301 There also are tail and head wrap around fields, used when the keyword
1302 is near the beginning or the end of the line, or when some long word
1303 cannot fit in, but leave place from wrapped around shorter words. The
1304 maximum width of these fields are recomputed separately for each line,
1305 on a case by case basis. It is worth noting that it cannot happen that
1306 both the tail and head fields are used at once. */
1308 half_line_width = line_width / 2;
1309 before_max_width = half_line_width - gap_size;
1310 keyafter_max_width = half_line_width;
1312 /* If truncation_string is the empty string, make it NULL to speed up
1313 tests. In this case, truncation_string_length will never get used, so
1314 there is no need to set it. */
1316 if (truncation_string && *truncation_string)
1317 truncation_string_length = strlen (truncation_string);
1319 truncation_string = NULL;
1324 /* When flagging truncation at the left of the keyword, the
1325 truncation mark goes at the beginning of the before field,
1326 unless there is a head field, in which case the mark goes at the
1327 left of the head field. When flagging truncation at the right
1328 of the keyword, the mark goes at the end of the keyafter field,
1329 unless there is a tail field, in which case the mark goes at the
1330 end of the tail field. Only eight combination cases could arise
1331 for truncation marks:
1334 . One beginning the before field.
1335 . One beginning the head field.
1336 . One ending the keyafter field.
1337 . One ending the tail field.
1338 . One beginning the before field, another ending the keyafter field.
1339 . One ending the tail field, another beginning the before field.
1340 . One ending the keyafter field, another beginning the head field.
1342 So, there is at most two truncation marks, which could appear both
1343 on the left side of the center of the output line, both on the
1344 right side, or one on either side. */
1346 before_max_width -= 2 * truncation_string_length;
1347 keyafter_max_width -= 2 * truncation_string_length;
1352 /* I never figured out exactly how UNIX' ptx plans the output width
1353 of its various fields. If GNU extensions are disabled, do not
1354 try computing the field widths correctly; instead, use the
1355 following formula, which does not completely imitate UNIX' ptx,
1358 keyafter_max_width -= 2 * truncation_string_length + 1;
1361 /* Compute which characters need special output processing. Initialize
1362 by flagging any white space character. Some systems do not consider
1363 form feed as a space character, but we do. */
1365 for (character = 0; character < CHAR_SET_SIZE; character++)
1366 edited_flag[character] = ISSPACE (character) != 0;
1367 edited_flag['\f'] = 1;
1369 /* Complete the special character flagging according to selected output
1372 switch (output_format)
1374 case UNKNOWN_FORMAT:
1375 /* Should never happen. */
1382 /* `Quote' characters should be doubled. */
1384 edited_flag['"'] = 1;
1389 /* Various characters need special processing. */
1391 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1392 edited_flag[(unsigned char) *cursor] = 1;
1394 /* Any character with 8th bit set will print to a single space, unless
1395 it is diacriticized. */
1397 for (character = 0200; character < CHAR_SET_SIZE; character++)
1398 edited_flag[character] = todiac (character) != 0;
1403 /*------------------------------------------------------------------.
1404 | Compute the position and length of all the output fields, given a |
1405 | pointer to some OCCURS. |
1406 `------------------------------------------------------------------*/
1409 define_all_fields (OCCURS *occurs)
1411 int tail_max_width; /* allowable width of tail field */
1412 int head_max_width; /* allowable width of head field */
1413 char *cursor; /* running cursor in source text */
1414 char *left_context_start; /* start of left context */
1415 char *right_context_end; /* end of right context */
1416 char *left_field_start; /* conservative start for `head'/`before' */
1417 int file_index; /* index in text input file arrays */
1418 const char *file_name; /* file name for reference */
1419 int line_ordinal; /* line ordinal for reference */
1421 /* Define `keyafter', start of left context and end of right context.
1422 `keyafter' starts at the saved position for keyword and extend to the
1423 right from the end of the keyword, eating separators or full words, but
1424 not beyond maximum allowed width for `keyafter' field or limit for the
1425 right context. Suffix spaces will be removed afterwards. */
1427 keyafter.start = occurs->key.start;
1428 keyafter.end = keyafter.start + occurs->key.size;
1429 left_context_start = keyafter.start + occurs->left;
1430 right_context_end = keyafter.start + occurs->right;
1432 cursor = keyafter.end;
1433 while (cursor < right_context_end
1434 && cursor <= keyafter.start + keyafter_max_width)
1436 keyafter.end = cursor;
1437 SKIP_SOMETHING (cursor, right_context_end);
1439 if (cursor <= keyafter.start + keyafter_max_width)
1440 keyafter.end = cursor;
1442 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1444 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1446 /* When the left context is wide, it might take some time to catch up from
1447 the left context boundary to the beginning of the `head' or `before'
1448 fields. So, in this case, to speed the catchup, we jump back from the
1449 keyword, using some secure distance, possibly falling in the middle of
1450 a word. A secure backward jump would be at least half the maximum
1451 width of a line, plus the size of the longest word met in the whole
1452 input. We conclude this backward jump by a skip forward of at least
1453 one word. In this manner, we should not inadvertently accept only part
1454 of a word. From the reached point, when it will be time to fix the
1455 beginning of `head' or `before' fields, we will skip forward words or
1456 delimiters until we get sufficiently near. */
1458 if (-occurs->left > half_line_width + maximum_word_length)
1461 = keyafter.start - (half_line_width + maximum_word_length);
1462 SKIP_SOMETHING (left_field_start, keyafter.start);
1465 left_field_start = keyafter.start + occurs->left;
1467 /* `before' certainly ends at the keyword, but not including separating
1468 spaces. It starts after than the saved value for the left context, by
1469 advancing it until it falls inside the maximum allowed width for the
1470 before field. There will be no prefix spaces either. `before' only
1471 advances by skipping single separators or whole words. */
1473 before.start = left_field_start;
1474 before.end = keyafter.start;
1475 SKIP_WHITE_BACKWARDS (before.end, before.start);
1477 while (before.start + before_max_width < before.end)
1478 SKIP_SOMETHING (before.start, before.end);
1480 if (truncation_string)
1482 cursor = before.start;
1483 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1484 before_truncation = cursor > left_context_start;
1487 before_truncation = 0;
1489 SKIP_WHITE (before.start, text_buffer.end);
1491 /* The tail could not take more columns than what has been left in the
1492 left context field, and a gap is mandatory. It starts after the
1493 right context, and does not contain prefixed spaces. It ends at
1494 the end of line, the end of buffer or when the tail field is full,
1495 whichever comes first. It cannot contain only part of a word, and
1496 has no suffixed spaces. */
1499 = before_max_width - (before.end - before.start) - gap_size;
1501 if (tail_max_width > 0)
1503 tail.start = keyafter.end;
1504 SKIP_WHITE (tail.start, text_buffer.end);
1506 tail.end = tail.start;
1508 while (cursor < right_context_end
1509 && cursor < tail.start + tail_max_width)
1512 SKIP_SOMETHING (cursor, right_context_end);
1515 if (cursor < tail.start + tail_max_width)
1518 if (tail.end > tail.start)
1520 keyafter_truncation = 0;
1521 tail_truncation = truncation_string && tail.end < right_context_end;
1524 tail_truncation = 0;
1526 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1531 /* No place left for a tail field. */
1535 tail_truncation = 0;
1538 /* `head' could not take more columns than what has been left in the right
1539 context field, and a gap is mandatory. It ends before the left
1540 context, and does not contain suffixed spaces. Its pointer is advanced
1541 until the head field has shrunk to its allowed width. It cannot
1542 contain only part of a word, and has no suffixed spaces. */
1545 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1547 if (head_max_width > 0)
1549 head.end = before.start;
1550 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1552 head.start = left_field_start;
1553 while (head.start + head_max_width < head.end)
1554 SKIP_SOMETHING (head.start, head.end);
1556 if (head.end > head.start)
1558 before_truncation = 0;
1559 head_truncation = (truncation_string
1560 && head.start > left_context_start);
1563 head_truncation = 0;
1565 SKIP_WHITE (head.start, head.end);
1570 /* No place left for a head field. */
1574 head_truncation = 0;
1580 /* Construct the reference text in preallocated space from the file
1581 name and the line number. Find out in which file the reference
1582 occurred. Standard input yields an empty file name. Insure line
1583 numbers are one based, even if they are computed zero based. */
1586 while (file_line_count[file_index] < occurs->reference)
1589 file_name = input_file_name[file_index];
1593 line_ordinal = occurs->reference + 1;
1595 line_ordinal -= file_line_count[file_index - 1];
1597 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1598 reference.end = reference.start + strlen (reference.start);
1600 else if (input_reference)
1603 /* Reference starts at saved position for reference and extends right
1604 until some white space is met. */
1606 reference.start = keyafter.start + (DELTA) occurs->reference;
1607 reference.end = reference.start;
1608 SKIP_NON_WHITE (reference.end, right_context_end);
1612 /* Formatting and actual output - control routines. */
1614 /*----------------------------------------------------------------------.
1615 | Output the current output fields as one line for `troff' or `nroff'. |
1616 `----------------------------------------------------------------------*/
1619 output_one_roff_line (void)
1621 /* Output the `tail' field. */
1623 printf (".%s \"", macro_name);
1625 if (tail_truncation)
1626 fputs (truncation_string, stdout);
1629 /* Output the `before' field. */
1631 fputs (" \"", stdout);
1632 if (before_truncation)
1633 fputs (truncation_string, stdout);
1634 print_field (before);
1637 /* Output the `keyafter' field. */
1639 fputs (" \"", stdout);
1640 print_field (keyafter);
1641 if (keyafter_truncation)
1642 fputs (truncation_string, stdout);
1645 /* Output the `head' field. */
1647 fputs (" \"", stdout);
1648 if (head_truncation)
1649 fputs (truncation_string, stdout);
1653 /* Conditionally output the `reference' field. */
1655 if (auto_reference || input_reference)
1657 fputs (" \"", stdout);
1658 print_field (reference);
1665 /*---------------------------------------------------------.
1666 | Output the current output fields as one line for `TeX'. |
1667 `---------------------------------------------------------*/
1670 output_one_tex_line (void)
1672 BLOCK key; /* key field, isolated */
1673 BLOCK after; /* after field, isolated */
1674 char *cursor; /* running cursor in source text */
1676 printf ("\\%s ", macro_name);
1679 fputs ("}{", stdout);
1680 print_field (before);
1681 fputs ("}{", stdout);
1682 key.start = keyafter.start;
1683 after.end = keyafter.end;
1684 cursor = keyafter.start;
1685 SKIP_SOMETHING (cursor, keyafter.end);
1687 after.start = cursor;
1689 fputs ("}{", stdout);
1690 print_field (after);
1691 fputs ("}{", stdout);
1694 if (auto_reference || input_reference)
1697 print_field (reference);
1703 /*-------------------------------------------------------------------.
1704 | Output the current output fields as one line for a dumb terminal. |
1705 `-------------------------------------------------------------------*/
1708 output_one_dumb_line (void)
1710 if (!right_reference)
1715 /* Output the `reference' field, in such a way that GNU emacs
1716 next-error will handle it. The ending colon is taken from the
1717 gap which follows. */
1719 print_field (reference);
1721 print_spaces (reference_max_width
1723 - (reference.end - reference.start)
1729 /* Output the `reference' field and its following gap. */
1731 print_field (reference);
1732 print_spaces (reference_max_width
1734 - (reference.end - reference.start));
1738 if (tail.start < tail.end)
1740 /* Output the `tail' field. */
1743 if (tail_truncation)
1744 fputs (truncation_string, stdout);
1746 print_spaces (half_line_width - gap_size
1747 - (before.end - before.start)
1748 - (before_truncation ? truncation_string_length : 0)
1749 - (tail.end - tail.start)
1750 - (tail_truncation ? truncation_string_length : 0));
1753 print_spaces (half_line_width - gap_size
1754 - (before.end - before.start)
1755 - (before_truncation ? truncation_string_length : 0));
1757 /* Output the `before' field. */
1759 if (before_truncation)
1760 fputs (truncation_string, stdout);
1761 print_field (before);
1763 print_spaces (gap_size);
1765 /* Output the `keyafter' field. */
1767 print_field (keyafter);
1768 if (keyafter_truncation)
1769 fputs (truncation_string, stdout);
1771 if (head.start < head.end)
1773 /* Output the `head' field. */
1775 print_spaces (half_line_width
1776 - (keyafter.end - keyafter.start)
1777 - (keyafter_truncation ? truncation_string_length : 0)
1778 - (head.end - head.start)
1779 - (head_truncation ? truncation_string_length : 0));
1780 if (head_truncation)
1781 fputs (truncation_string, stdout);
1786 if ((auto_reference || input_reference) && right_reference)
1787 print_spaces (half_line_width
1788 - (keyafter.end - keyafter.start)
1789 - (keyafter_truncation ? truncation_string_length : 0));
1791 if ((auto_reference || input_reference) && right_reference)
1793 /* Output the `reference' field. */
1795 print_spaces (gap_size);
1796 print_field (reference);
1802 /*------------------------------------------------------------------------.
1803 | Scan the whole occurs table and, for each entry, output one line in the |
1804 | appropriate format. |
1805 `------------------------------------------------------------------------*/
1808 generate_all_output (void)
1810 size_t occurs_index; /* index of keyword entry being processed */
1811 OCCURS *occurs_cursor; /* current keyword entry being processed */
1813 /* The following assignments are useful to provide default values in case
1814 line contexts or references are not used, in which case these variables
1815 would never be computed. */
1819 tail_truncation = 0;
1823 head_truncation = 0;
1825 /* Loop over all keyword occurrences. */
1827 occurs_cursor = occurs_table[0];
1829 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1831 /* Compute the exact size of every field and whenever truncation flags
1832 are present or not. */
1834 define_all_fields (occurs_cursor);
1836 /* Produce one output line according to selected format. */
1838 switch (output_format)
1840 case UNKNOWN_FORMAT:
1841 /* Should never happen. */
1844 output_one_dumb_line ();
1848 output_one_roff_line ();
1852 output_one_tex_line ();
1856 /* Advance the cursor into the occurs table. */
1862 /* Option decoding and main program. */
1864 /*------------------------------------------------------.
1865 | Print program identification and options, then exit. |
1866 `------------------------------------------------------*/
1871 if (status != EXIT_SUCCESS)
1872 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1877 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1878 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1879 program_name, program_name);
1881 Output a permuted index, including context, of the words in the input files.\n\
1885 Mandatory arguments to long options are mandatory for short options too.\n\
1888 -A, --auto-reference output automatically generated references\n\
1889 -C, --copyright display Copyright and copying conditions\n\
1890 -G, --traditional behave more like System V `ptx'\n\
1891 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1894 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1895 -O, --format=roff generate output as roff directives\n\
1896 -R, --right-side-refs put references at right, not counted in -w\n\
1897 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1898 -T, --format=tex generate output as TeX directives\n\
1901 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1902 -b, --break-file=FILE word break characters in this FILE\n\
1903 -f, --ignore-case fold lower case to upper case for sorting\n\
1904 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1905 -i, --ignore-file=FILE read ignore word list from FILE\n\
1906 -o, --only-file=FILE read only word list from this FILE\n\
1909 -r, --references first field of each line is a reference\n\
1910 -t, --typeset-mode - not implemented -\n\
1911 -w, --width=NUMBER output width in columns, reference excluded\n\
1913 fputs (HELP_OPTION_DESCRIPTION, stdout);
1914 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1917 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1919 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1924 /*----------------------------------------------------------------------.
1925 | Main program. Decode ARGC arguments passed through the ARGV array of |
1926 | strings, then launch execution. |
1927 `----------------------------------------------------------------------*/
1929 /* Long options equivalences. */
1930 static const struct option long_options[] =
1932 {"auto-reference", no_argument, NULL, 'A'},
1933 {"break-file", required_argument, NULL, 'b'},
1934 {"copyright", no_argument, NULL, 'C'},
1935 {"flag-truncation", required_argument, NULL, 'F'},
1936 {"ignore-case", no_argument, NULL, 'f'},
1937 {"gap-size", required_argument, NULL, 'g'},
1938 {"ignore-file", required_argument, NULL, 'i'},
1939 {"macro-name", required_argument, NULL, 'M'},
1940 {"only-file", required_argument, NULL, 'o'},
1941 {"references", no_argument, NULL, 'r'},
1942 {"right-side-refs", no_argument, NULL, 'R'},
1943 {"format", required_argument, NULL, 10},
1944 {"sentence-regexp", required_argument, NULL, 'S'},
1945 {"traditional", no_argument, NULL, 'G'},
1946 {"typeset-mode", no_argument, NULL, 't'},
1947 {"width", required_argument, NULL, 'w'},
1948 {"word-regexp", required_argument, NULL, 'W'},
1949 {GETOPT_HELP_OPTION_DECL},
1950 {GETOPT_VERSION_OPTION_DECL},
1954 static char const* const format_args[] =
1959 static enum Format const format_vals[] =
1961 ROFF_FORMAT, TEX_FORMAT
1965 main (int argc, char **argv)
1967 int optchar; /* argument character */
1968 int file_index; /* index in text input file arrays */
1970 /* Decode program options. */
1972 initialize_main (&argc, &argv);
1973 program_name = argv[0];
1974 setlocale (LC_ALL, "");
1975 bindtextdomain (PACKAGE, LOCALEDIR);
1976 textdomain (PACKAGE);
1978 atexit (close_stdout);
1980 #if HAVE_SETCHRCLASS
1984 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1985 long_options, NULL),
1991 usage (EXIT_FAILURE);
1998 This program is free software; you can redistribute it and/or modify\n\
1999 it under the terms of the GNU General Public License as published by\n\
2000 the Free Software Foundation; either version 2, or (at your option)\n\
2001 any later version.\n\
2005 This program is distributed in the hope that it will be useful,\n\
2006 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
2007 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
2008 GNU General Public License for more details.\n\
2012 You should have received a copy of the GNU General Public License\n\
2013 along with this program; if not, write to the Free Software Foundation,\n\
2014 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
2017 exit (EXIT_SUCCESS);
2024 break_file = optarg;
2033 unsigned long int tmp_ulong;
2034 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2035 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2036 error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
2038 gap_size = tmp_ulong;
2043 ignore_file = optarg;
2051 input_reference = 1;
2055 /* Yet to understand... */
2059 line_width = atoi (optarg);
2061 unsigned long int tmp_ulong;
2062 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2063 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2064 error (EXIT_FAILURE, 0, _("invalid line width: %s"),
2066 line_width = tmp_ulong;
2076 truncation_string = copy_unescaped_string (optarg);
2080 macro_name = optarg;
2084 output_format = ROFF_FORMAT;
2088 right_reference = 1;
2092 context_regex_string = copy_unescaped_string (optarg);
2096 output_format = TEX_FORMAT;
2100 word_regex_string = copy_unescaped_string (optarg);
2104 output_format = XARGMATCH ("--format", optarg,
2105 format_args, format_vals);
2106 case_GETOPT_HELP_CHAR;
2108 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2112 /* Change the default Ignore file if one is defined. */
2114 #ifdef DEFAULT_IGNORE_FILE
2116 ignore_file = DEFAULT_IGNORE_FILE;
2119 /* Process remaining arguments. If GNU extensions are enabled, process
2120 all arguments as input parameters. If disabled, accept at most two
2121 arguments, the second of which is an output parameter. */
2126 /* No more argument simply means: read standard input. */
2128 input_file_name = xmalloc (sizeof *input_file_name);
2129 file_line_count = xmalloc (sizeof *file_line_count);
2130 number_input_files = 1;
2131 input_file_name[0] = NULL;
2133 else if (gnu_extensions)
2135 number_input_files = argc - optind;
2136 input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
2137 file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
2139 for (file_index = 0; file_index < number_input_files; file_index++)
2141 input_file_name[file_index] = argv[optind];
2142 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2143 input_file_name[0] = NULL;
2145 input_file_name[0] = argv[optind];
2152 /* There is one necessary input file. */
2154 number_input_files = 1;
2155 input_file_name = xmalloc (sizeof *input_file_name);
2156 file_line_count = xmalloc (sizeof *file_line_count);
2157 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2158 input_file_name[0] = NULL;
2160 input_file_name[0] = argv[optind];
2163 /* Redirect standard output, only if requested. */
2167 /* FIXME: don't fclose here? */
2169 if (fopen (argv[optind], "w") == NULL)
2170 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2174 /* Diagnose any other argument as an error. */
2177 usage (EXIT_FAILURE);
2180 /* If the output format has not been explicitly selected, choose dumb
2181 terminal format if GNU extensions are enabled, else `roff' format. */
2183 if (output_format == UNKNOWN_FORMAT)
2184 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2186 /* Initialize the main tables. */
2188 initialize_regex ();
2190 /* Read `Break character' file, if any. */
2193 digest_break_file (break_file);
2195 /* Read `Ignore words' file and `Only words' files, if any. If any of
2196 these files is empty, reset the name of the file to NULL, to avoid
2197 unnecessary calls to search_table. */
2201 digest_word_file (ignore_file, &ignore_table);
2202 if (ignore_table.length == 0)
2208 digest_word_file (only_file, &only_table);
2209 if (only_table.length == 0)
2213 /* Prepare to study all the input files. */
2215 number_of_occurs[0] = 0;
2216 total_line_count = 0;
2217 maximum_word_length = 0;
2218 reference_max_width = 0;
2220 for (file_index = 0; file_index < number_input_files; file_index++)
2223 /* Read the file in core, than study it. */
2225 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2226 find_occurs_in_text ();
2228 /* Maintain for each file how many lines has been read so far when its
2229 end is reached. Incrementing the count first is a simple kludge to
2230 handle a possible incomplete line at end of file. */
2233 file_line_count[file_index] = total_line_count;
2236 /* Do the output process phase. */
2238 sort_found_occurs ();
2239 fix_output_parameters ();
2240 generate_all_output ();
2244 exit (EXIT_SUCCESS);