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 /* Note to translator: Please translate "F. Pinard" to "François
36 Pinard" if "ç" (c-with-cedilla) is available in the
37 translation's character set and encoding. */
38 #define AUTHORS _("F. Pinard")
40 /* Number of possible characters in a byte. */
41 #define CHAR_SET_SIZE 256
43 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
44 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
45 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
46 #define OCTTOBIN(C) ((C) - '0')
48 /* Debugging the memory allocator. */
51 # define MALLOC_FUNC_CHECK 1
55 /* Global definitions. */
57 /* Reallocation step when swallowing non regular files. The value is not
58 the actual reallocation step, but its base two logarithm. */
59 #define SWALLOW_REALLOC_LOG 12
61 /* Imported from "regex.c". */
64 /* The name this program was run with. */
67 /* Program options. */
71 UNKNOWN_FORMAT, /* output format still unknown */
72 DUMB_FORMAT, /* output for a dumb terminal */
73 ROFF_FORMAT, /* output for `troff' or `nroff' */
74 TEX_FORMAT /* output for `TeX' or `LaTeX' */
77 int gnu_extensions = 1; /* trigger all GNU extensions */
78 int auto_reference = 0; /* references are `file_name:line_number:' */
79 int input_reference = 0; /* references at beginning of input lines */
80 int right_reference = 0; /* output references after right context */
81 int line_width = 72; /* output line width in characters */
82 int gap_size = 3; /* number of spaces between output fields */
83 const char *truncation_string = "/";
84 /* string used to mark line truncations */
85 const char *macro_name = "xx"; /* macro name for roff or TeX output */
86 enum Format output_format = UNKNOWN_FORMAT;
89 int ignore_case = 0; /* fold lower to upper case for sorting */
90 const char *context_regex_string = NULL;
91 /* raw regex for end of context */
92 const char *word_regex_string = NULL;
93 /* raw regex for a keyword */
94 const char *break_file = NULL; /* name of the `Break characters' file */
95 const char *only_file = NULL; /* name of the `Only words' file */
96 const char *ignore_file = NULL; /* name of the `Ignore words' file */
98 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
99 whole file. A WORD is something smaller, its length should fit in a
100 short integer. A WORD_TABLE may contain several WORDs. */
104 char *start; /* pointer to beginning of region */
105 char *end; /* pointer to end + 1 of region */
111 char *start; /* pointer to beginning of region */
112 short size; /* length of the region */
118 WORD *start; /* array of WORDs */
119 size_t alloc; /* allocated length */
120 size_t length; /* number of used entries */
124 /* Pattern description tables. */
126 /* For each character, provide its folded equivalent. */
127 unsigned char folded_chars[CHAR_SET_SIZE];
129 /* For each character, indicate if it is part of a word. */
130 char syntax_table[CHAR_SET_SIZE];
131 char *re_syntax_table = syntax_table;
133 /* Compiled regex for end of context. */
134 struct re_pattern_buffer *context_regex;
136 /* End of context pattern register indices. */
137 struct re_registers context_regs;
139 /* Compiled regex for a keyword. */
140 struct re_pattern_buffer *word_regex;
142 /* Keyword pattern register indices. */
143 struct re_registers word_regs;
145 /* A word characters fastmap is used only when no word regexp has been
146 provided. A word is then made up of a sequence of one or more characters
147 allowed by the fastmap. Contains !0 if character allowed in word. Not
148 only this is faster in most cases, but it simplifies the implementation
149 of the Break files. */
150 char word_fastmap[CHAR_SET_SIZE];
152 /* Maximum length of any word read. */
153 int maximum_word_length;
155 /* Maximum width of any reference used. */
156 int reference_max_width;
158 /* Ignore and Only word tables. */
160 WORD_TABLE ignore_table; /* table of words to ignore */
161 WORD_TABLE only_table; /* table of words to select */
163 /* Source text table, and scanning macros. */
165 int number_input_files; /* number of text input files */
166 int total_line_count; /* total number of lines seen so far */
167 const char **input_file_name; /* array of text input file names */
168 int *file_line_count; /* array of `total_line_count' values at end */
170 BLOCK text_buffer; /* file to study */
171 char *text_buffer_maxend; /* allocated end of text_buffer */
173 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
175 #define SKIP_NON_WHITE(cursor, limit) \
176 while (cursor < limit && !ISSPACE(*cursor)) \
179 #define SKIP_WHITE(cursor, limit) \
180 while (cursor < limit && ISSPACE(*cursor)) \
183 #define SKIP_WHITE_BACKWARDS(cursor, start) \
184 while (cursor > start && ISSPACE(cursor[-1])) \
187 #define SKIP_SOMETHING(cursor, limit) \
188 if (word_regex_string) \
191 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
192 cursor += count <= 0 ? 1 : count; \
194 else if (word_fastmap[(unsigned char) *cursor]) \
195 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
200 /* Occurrences table.
202 The `keyword' pointer provides the central word, which is surrounded
203 by a left context and a right context. The `keyword' and `length'
204 field allow full 8-bit characters keys, even including NULs. At other
205 places in this program, the name `keyafter' refers to the keyword
206 followed by its right context.
208 The left context does not extend, towards the beginning of the file,
209 further than a distance given by the `left' value. This value is
210 relative to the keyword beginning, it is usually negative. This
211 insures that, except for white space, we will never have to backward
212 scan the source text, when it is time to generate the final output
215 The right context, indirectly attainable through the keyword end, does
216 not extend, towards the end of the file, further than a distance given
217 by the `right' value. This value is relative to the keyword
218 beginning, it is usually positive.
220 When automatic references are used, the `reference' value is the
221 overall line number in all input files read so far, in this case, it
222 is of type (int). When input references are used, the `reference'
223 value indicates the distance between the keyword beginning and the
224 start of the reference field, it is of type (DELTA) and usually
227 typedef short DELTA; /* to hold displacement within one context */
231 WORD key; /* description of the keyword */
232 DELTA left; /* distance to left context start */
233 DELTA right; /* distance to right context end */
234 int reference; /* reference descriptor */
238 /* The various OCCURS tables are indexed by the language. But the time
239 being, there is no such multiple language support. */
241 OCCURS *occurs_table[1]; /* all words retained from the read text */
242 size_t occurs_alloc[1]; /* allocated size of occurs_table */
243 size_t number_of_occurs[1]; /* number of used slots in occurs_table */
246 /* Communication among output routines. */
248 /* Indicate if special output processing is requested for each character. */
249 char edited_flag[CHAR_SET_SIZE];
251 int half_line_width; /* half of line width, reference excluded */
252 int before_max_width; /* maximum width of before field */
253 int keyafter_max_width; /* maximum width of keyword-and-after field */
254 int truncation_string_length; /* length of string used to flag truncation */
256 /* When context is limited by lines, wraparound may happen on final output:
257 the `head' pointer gives access to some supplementary left context which
258 will be seen at the end of the output line, the `tail' pointer gives
259 access to some supplementary right context which will be seen at the
260 beginning of the output line. */
262 BLOCK tail; /* tail field */
263 int tail_truncation; /* flag truncation after the tail field */
265 BLOCK before; /* before field */
266 int before_truncation; /* flag truncation before the before field */
268 BLOCK keyafter; /* keyword-and-after field */
269 int keyafter_truncation; /* flag truncation after the keyafter field */
271 BLOCK head; /* head field */
272 int head_truncation; /* flag truncation before the head field */
274 BLOCK reference; /* reference field for input reference mode */
276 /* Miscellaneous routines. */
278 /*------------------------------------------------------.
279 | Duplicate string STRING, while evaluating \-escapes. |
280 `------------------------------------------------------*/
282 /* Loosely adapted from GNU sh-utils printf.c code. */
285 copy_unescaped_string (const char *string)
287 char *result; /* allocated result */
288 char *cursor; /* cursor in result */
289 int value; /* value of \nnn escape */
290 int length; /* length of \nnn escape */
292 result = xmalloc (strlen (string) + 1);
301 case 'x': /* \xhhh escape, 3 chars maximum */
303 for (length = 0, string++;
304 length < 3 && ISXDIGIT (*string);
306 value = value * 16 + HEXTOBIN (*string);
316 case '0': /* \0ooo escape, 3 chars maximum */
318 for (length = 0, string++;
319 length < 3 && ISODIGIT (*string);
321 value = value * 8 + OCTTOBIN (*string);
325 case 'a': /* alert */
334 case 'b': /* backspace */
339 case 'c': /* cancel the rest of the output */
344 case 'f': /* form feed */
349 case 'n': /* new line */
354 case 'r': /* carriage return */
359 case 't': /* horizontal tab */
364 case 'v': /* vertical tab */
375 *cursor++ = *string++;
380 *cursor++ = *string++;
386 /*-------------------------------------------------------------------.
387 | Compile the regex represented by STRING, diagnose and abort if any |
388 | error. Returns the compiled regex structure. |
389 `-------------------------------------------------------------------*/
391 static struct re_pattern_buffer *
392 alloc_and_compile_regex (const char *string)
394 struct re_pattern_buffer *pattern; /* newly allocated structure */
395 const char *message; /* error message returned by regex.c */
397 pattern = (struct re_pattern_buffer *)
398 xmalloc (sizeof (struct re_pattern_buffer));
399 memset (pattern, 0, sizeof (struct re_pattern_buffer));
401 pattern->buffer = NULL;
402 pattern->allocated = 0;
403 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
404 pattern->fastmap = xmalloc ((size_t) CHAR_SET_SIZE);
406 message = re_compile_pattern (string, (int) strlen (string), pattern);
408 error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);
410 /* The fastmap should be compiled before `re_match'. The following
411 call is not mandatory, because `re_search' is always called sooner,
412 and it compiles the fastmap if this has not been done yet. */
414 re_compile_fastmap (pattern);
416 /* Do not waste extra allocated space. */
418 if (pattern->allocated > pattern->used)
421 = xrealloc (pattern->buffer, (size_t) pattern->used);
422 pattern->allocated = pattern->used;
428 /*------------------------------------------------------------------------.
429 | This will initialize various tables for pattern match and compiles some |
431 `------------------------------------------------------------------------*/
434 initialize_regex (void)
436 int character; /* character value */
438 /* Initialize the regex syntax table. */
440 for (character = 0; character < CHAR_SET_SIZE; character++)
441 syntax_table[character] = ISALPHA (character) ? Sword : 0;
443 /* Initialize the case folding table. */
446 for (character = 0; character < CHAR_SET_SIZE; character++)
447 folded_chars[character] = TOUPPER (character);
449 /* Unless the user already provided a description of the end of line or
450 end of sentence sequence, select an end of line sequence to compile.
451 If the user provided an empty definition, thus disabling end of line
452 or sentence feature, make it NULL to speed up tests. If GNU
453 extensions are enabled, use end of sentence like in GNU emacs. If
454 disabled, use end of lines. */
456 if (context_regex_string)
458 if (!*context_regex_string)
459 context_regex_string = NULL;
461 else if (gnu_extensions && !input_reference)
462 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
464 context_regex_string = "\n";
466 if (context_regex_string)
467 context_regex = alloc_and_compile_regex (context_regex_string);
469 /* If the user has already provided a non-empty regexp to describe
470 words, compile it. Else, unless this has already been done through
471 a user provided Break character file, construct a fastmap of
472 characters that may appear in a word. If GNU extensions enabled,
473 include only letters of the underlying character set. If disabled,
474 include almost everything, even punctuations; stop only on white
477 if (word_regex_string && *word_regex_string)
478 word_regex = alloc_and_compile_regex (word_regex_string);
479 else if (!break_file)
486 for (character = 0; character < CHAR_SET_SIZE; character++)
487 word_fastmap[character] = ISALPHA (character) ? 1 : 0;
492 /* Simulate [^ \t\n]+. */
494 memset (word_fastmap, 1, CHAR_SET_SIZE);
495 word_fastmap[' '] = 0;
496 word_fastmap['\t'] = 0;
497 word_fastmap['\n'] = 0;
502 /*------------------------------------------------------------------------.
503 | This routine will attempt to swallow a whole file name FILE_NAME into a |
504 | contiguous region of memory and return a description of it into BLOCK. |
505 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
507 | Previously, in some cases, white space compression was attempted while |
508 | inputting text. This was defeating some regexps like default end of |
509 | sentence, which checks for two consecutive spaces. If white space |
510 | compression is ever reinstated, it should be in output routines. |
511 `------------------------------------------------------------------------*/
514 swallow_file_in_memory (const char *file_name, BLOCK *block)
516 int file_handle; /* file descriptor number */
517 struct stat stat_block; /* stat block for file */
518 size_t allocated_length; /* allocated length of memory buffer */
519 size_t used_length; /* used length in memory buffer */
520 int read_length; /* number of character gotten on last read */
522 /* As special cases, a file name which is NULL or "-" indicates standard
523 input, which is already opened. In all other cases, open the file from
525 bool using_stdin = !file_name || !*file_name || strcmp (file_name, "-") == 0;
527 file_handle = STDIN_FILENO;
529 if ((file_handle = open (file_name, O_RDONLY)) < 0)
530 error (EXIT_FAILURE, errno, "%s", file_name);
532 /* If the file is a plain, regular file, allocate the memory buffer all at
533 once and swallow the file in one blow. In other cases, read the file
534 repeatedly in smaller chunks until we have it all, reallocating memory
535 once in a while, as we go. */
537 if (fstat (file_handle, &stat_block) < 0)
538 error (EXIT_FAILURE, errno, "%s", file_name);
540 if (S_ISREG (stat_block.st_mode))
542 size_t in_memory_size;
544 block->start = xmalloc ((size_t) stat_block.st_size);
546 if ((in_memory_size = read (file_handle,
547 block->start, (size_t) stat_block.st_size))
548 != stat_block.st_size)
551 /* On MSDOS, in memory size may be smaller than the file
552 size, because of end of line conversions. But it can
553 never be smaller than half the file size, because the
554 minimum is when all lines are empty and terminated by
556 if (in_memory_size != (size_t)-1
557 && in_memory_size >= stat_block.st_size / 2)
558 block->start = xrealloc (block->start, in_memory_size);
560 #endif /* not MSDOS */
562 error (EXIT_FAILURE, errno, "%s", file_name);
564 block->end = block->start + in_memory_size;
568 block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
570 allocated_length = (1 << SWALLOW_REALLOC_LOG);
572 while (read_length = read (file_handle,
573 block->start + used_length,
574 allocated_length - used_length),
577 used_length += read_length;
578 if (used_length == allocated_length)
580 allocated_length += (1 << SWALLOW_REALLOC_LOG);
582 = xrealloc (block->start, allocated_length);
587 error (EXIT_FAILURE, errno, "%s", file_name);
589 block->end = block->start + used_length;
592 /* Close the file, but only if it was not the standard input. */
594 if (! using_stdin && close (file_handle) != 0)
595 error (EXIT_FAILURE, errno, "%s", file_name);
598 /* Sort and search routines. */
600 /*--------------------------------------------------------------------------.
601 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
602 | Return less than 0 if the first word goes before the second; return |
603 | greater than 0 if the first word goes after the second. |
605 | If a word is indeed a prefix of the other, the shorter should go first. |
606 `--------------------------------------------------------------------------*/
609 compare_words (const void *void_first, const void *void_second)
611 #define first ((const WORD *) void_first)
612 #define second ((const WORD *) void_second)
613 int length; /* minimum of two lengths */
614 int counter; /* cursor in words */
615 int value; /* value of comparison */
617 length = first->size < second->size ? first->size : second->size;
621 for (counter = 0; counter < length; counter++)
623 value = (folded_chars [(unsigned char) (first->start[counter])]
624 - folded_chars [(unsigned char) (second->start[counter])]);
631 for (counter = 0; counter < length; counter++)
633 value = ((unsigned char) first->start[counter]
634 - (unsigned char) second->start[counter]);
640 return first->size - second->size;
645 /*-----------------------------------------------------------------------.
646 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
647 | go first. In case of a tie, preserve the original order through a |
648 | pointer comparison. |
649 `-----------------------------------------------------------------------*/
652 compare_occurs (const void *void_first, const void *void_second)
654 #define first ((const OCCURS *) void_first)
655 #define second ((const OCCURS *) void_second)
658 value = compare_words (&first->key, &second->key);
659 return value == 0 ? first->key.start - second->key.start : value;
664 /*------------------------------------------------------------.
665 | Return !0 if WORD appears in TABLE. Uses a binary search. |
666 `------------------------------------------------------------*/
669 search_table (WORD *word, WORD_TABLE *table)
671 int lowest; /* current lowest possible index */
672 int highest; /* current highest possible index */
673 int middle; /* current middle index */
674 int value; /* value from last comparison */
677 highest = table->length - 1;
678 while (lowest <= highest)
680 middle = (lowest + highest) / 2;
681 value = compare_words (word, table->start + middle);
683 highest = middle - 1;
692 /*---------------------------------------------------------------------.
693 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
694 | take intermediate copies or table elements, so the sort will be |
695 | stabilized throughout the comparison routine. |
696 `---------------------------------------------------------------------*/
699 sort_found_occurs (void)
702 /* Only one language for the time being. */
704 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
708 /* Parameter files reading routines. */
710 /*----------------------------------------------------------------------.
711 | Read a file named FILE_NAME, containing a set of break characters. |
712 | Build a content to the array word_fastmap in which all characters are |
713 | allowed except those found in the file. Characters may be repeated. |
714 `----------------------------------------------------------------------*/
717 digest_break_file (const char *file_name)
719 BLOCK file_contents; /* to receive a copy of the file */
720 char *cursor; /* cursor in file copy */
722 swallow_file_in_memory (file_name, &file_contents);
724 /* Make the fastmap and record the file contents in it. */
726 memset (word_fastmap, 1, CHAR_SET_SIZE);
727 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
728 word_fastmap[(unsigned char) *cursor] = 0;
733 /* If GNU extensions are enabled, the only way to avoid newline as
734 a break character is to write all the break characters in the
735 file with no newline at all, not even at the end of the file.
736 If disabled, spaces, tabs and newlines are always considered as
737 break characters even if not included in the break file. */
739 word_fastmap[' '] = 0;
740 word_fastmap['\t'] = 0;
741 word_fastmap['\n'] = 0;
744 /* Return the space of the file, which is no more required. */
746 free (file_contents.start);
749 /*-----------------------------------------------------------------------.
750 | Read a file named FILE_NAME, containing one word per line, then |
751 | construct in TABLE a table of WORD descriptors for them. The routine |
752 | swallows the whole file in memory; this is at the expense of space |
753 | needed for newlines, which are useless; however, the reading is fast. |
754 `-----------------------------------------------------------------------*/
757 digest_word_file (const char *file_name, WORD_TABLE *table)
759 BLOCK file_contents; /* to receive a copy of the file */
760 char *cursor; /* cursor in file copy */
761 char *word_start; /* start of the current word */
763 swallow_file_in_memory (file_name, &file_contents);
769 /* Read the whole file. */
771 cursor = file_contents.start;
772 while (cursor < file_contents.end)
775 /* Read one line, and save the word in contains. */
778 while (cursor < file_contents.end && *cursor != '\n')
781 /* Record the word in table if it is not empty. */
783 if (cursor > word_start)
785 if (table->length == table->alloc)
787 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
789 table->alloc = table->alloc * 2 + 1;
790 table->start = xrealloc (table->start,
791 table->alloc * sizeof *table->start);
794 table->start[table->length].start = word_start;
795 table->start[table->length].size = cursor - word_start;
799 /* This test allows for an incomplete line at end of file. */
801 if (cursor < file_contents.end)
805 /* Finally, sort all the words read. */
807 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
810 /* Keyword recognition and selection. */
812 /*----------------------------------------------------------------------.
813 | For each keyword in the source text, constructs an OCCURS structure. |
814 `----------------------------------------------------------------------*/
817 find_occurs_in_text (void)
819 char *cursor; /* for scanning the source text */
820 char *scan; /* for scanning the source text also */
821 char *line_start; /* start of the current input line */
822 char *line_scan; /* newlines scanned until this point */
823 int reference_length; /* length of reference in input mode */
824 WORD possible_key; /* possible key, to ease searches */
825 OCCURS *occurs_cursor; /* current OCCURS under construction */
827 char *context_start; /* start of left context */
828 char *context_end; /* end of right context */
829 char *word_start; /* start of word */
830 char *word_end; /* end of word */
831 char *next_context_start; /* next start of left context */
833 /* reference_length is always used within `if (input_reference)'.
834 However, GNU C diagnoses that it may be used uninitialized. The
835 following assignment is merely to shut it up. */
837 reference_length = 0;
839 /* Tracking where lines start is helpful for reference processing. In
840 auto reference mode, this allows counting lines. In input reference
841 mode, this permits finding the beginning of the references.
843 The first line begins with the file, skip immediately this very first
844 reference in input reference mode, to help further rejection any word
845 found inside it. Also, unconditionally assigning these variable has
846 the happy effect of shutting up lint. */
848 line_start = text_buffer.start;
849 line_scan = line_start;
852 SKIP_NON_WHITE (line_scan, text_buffer.end);
853 reference_length = line_scan - line_start;
854 SKIP_WHITE (line_scan, text_buffer.end);
857 /* Process the whole buffer, one line or one sentence at a time. */
859 for (cursor = text_buffer.start;
860 cursor < text_buffer.end;
861 cursor = next_context_start)
864 /* `context_start' gets initialized before the processing of each
865 line, or once for the whole buffer if no end of line or sentence
866 sequence separator. */
868 context_start = cursor;
870 /* If a end of line or end of sentence sequence is defined and
871 non-empty, `next_context_start' will be recomputed to be the end of
872 each line or sentence, before each one is processed. If no such
873 sequence, then `next_context_start' is set at the end of the whole
874 buffer, which is then considered to be a single line or sentence.
875 This test also accounts for the case of an incomplete line or
876 sentence at the end of the buffer. */
878 if (context_regex_string
879 && (re_search (context_regex, cursor, text_buffer.end - cursor,
880 0, text_buffer.end - cursor, &context_regs)
882 next_context_start = cursor + context_regs.end[0];
885 next_context_start = text_buffer.end;
887 /* Include the separator into the right context, but not any suffix
888 white space in this separator; this insures it will be seen in
889 output and will not take more space than necessary. */
891 context_end = next_context_start;
892 SKIP_WHITE_BACKWARDS (context_end, context_start);
894 /* Read and process a single input line or sentence, one word at a
901 /* If a word regexp has been compiled, use it to skip at the
902 beginning of the next word. If there is no such word, exit
906 if (re_search (word_regex, cursor, context_end - cursor,
907 0, context_end - cursor, &word_regs)
910 word_start = cursor + word_regs.start[0];
911 word_end = cursor + word_regs.end[0];
915 /* Avoid re_search and use the fastmap to skip to the
916 beginning of the next word. If there is no more word in
917 the buffer, exit the loop. */
921 while (scan < context_end
922 && !word_fastmap[(unsigned char) *scan])
925 if (scan == context_end)
930 while (scan < context_end
931 && word_fastmap[(unsigned char) *scan])
937 /* Skip right to the beginning of the found word. */
941 /* Skip any zero length word. Just advance a single position,
942 then go fetch the next word. */
944 if (word_end == word_start)
950 /* This is a genuine, non empty word, so save it as a possible
951 key. Then skip over it. Also, maintain the maximum length of
952 all words read so far. It is mandatory to take the maximum
953 length of all words in the file, without considering if they
954 are actually kept or rejected, because backward jumps at output
955 generation time may fall in *any* word. */
957 possible_key.start = cursor;
958 possible_key.size = word_end - word_start;
959 cursor += possible_key.size;
961 if (possible_key.size > maximum_word_length)
962 maximum_word_length = possible_key.size;
964 /* In input reference mode, update `line_start' from its previous
965 value. Count the lines just in case auto reference mode is
966 also selected. If it happens that the word just matched is
967 indeed part of a reference; just ignore it. */
971 while (line_scan < possible_key.start)
972 if (*line_scan == '\n')
976 line_start = line_scan;
977 SKIP_NON_WHITE (line_scan, text_buffer.end);
978 reference_length = line_scan - line_start;
982 if (line_scan > possible_key.start)
986 /* Ignore the word if an `Ignore words' table exists and if it is
987 part of it. Also ignore the word if an `Only words' table and
988 if it is *not* part of it.
990 It is allowed that both tables be used at once, even if this
991 may look strange for now. Just ignore a word that would appear
992 in both. If regexps are eventually implemented for these
993 tables, the Ignore table could then reject words that would
994 have been previously accepted by the Only table. */
996 if (ignore_file && search_table (&possible_key, &ignore_table))
998 if (only_file && !search_table (&possible_key, &only_table))
1001 /* A non-empty word has been found. First of all, insure
1002 proper allocation of the next OCCURS, and make a pointer to
1003 where it will be constructed. */
1005 if (number_of_occurs[0] == occurs_alloc[0])
1007 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1010 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
1011 occurs_table[0] = xrealloc (occurs_table[0],
1012 occurs_alloc[0] * sizeof *occurs_table[0]);
1015 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1017 /* Define the refence field, if any. */
1022 /* While auto referencing, update `line_start' from its
1023 previous value, counting lines as we go. If input
1024 referencing at the same time, `line_start' has been
1025 advanced earlier, and the following loop is never really
1028 while (line_scan < possible_key.start)
1029 if (*line_scan == '\n')
1033 line_start = line_scan;
1034 SKIP_NON_WHITE (line_scan, text_buffer.end);
1039 occurs_cursor->reference = total_line_count;
1041 else if (input_reference)
1044 /* If only input referencing, `line_start' has been computed
1045 earlier to detect the case the word matched would be part
1046 of the reference. The reference position is simply the
1047 value of `line_start'. */
1049 occurs_cursor->reference
1050 = (DELTA) (line_start - possible_key.start);
1051 if (reference_length > reference_max_width)
1052 reference_max_width = reference_length;
1055 /* Exclude the reference from the context in simple cases. */
1057 if (input_reference && line_start == context_start)
1059 SKIP_NON_WHITE (context_start, context_end);
1060 SKIP_WHITE (context_start, context_end);
1063 /* Completes the OCCURS structure. */
1065 occurs_cursor->key = possible_key;
1066 occurs_cursor->left = context_start - possible_key.start;
1067 occurs_cursor->right = context_end - possible_key.start;
1069 number_of_occurs[0]++;
1074 /* Formatting and actual output - service routines. */
1076 /*-----------------------------------------.
1077 | Prints some NUMBER of spaces on stdout. |
1078 `-----------------------------------------*/
1081 print_spaces (int number)
1085 for (counter = number; counter > 0; counter--)
1089 /*-------------------------------------.
1090 | Prints the field provided by FIELD. |
1091 `-------------------------------------*/
1094 print_field (BLOCK field)
1096 char *cursor; /* Cursor in field to print */
1097 int character; /* Current character */
1098 int base; /* Base character, without diacritic */
1099 int diacritic; /* Diacritic code for the character */
1101 /* Whitespace is not really compressed. Instead, each white space
1102 character (tab, vt, ht etc.) is printed as one single space. */
1104 for (cursor = field.start; cursor < field.end; cursor++)
1106 character = (unsigned char) *cursor;
1107 if (edited_flag[character])
1110 /* First check if this is a diacriticized character.
1112 This works only for TeX. I do not know how diacriticized
1113 letters work with `roff'. Please someone explain it to me! */
1115 diacritic = todiac (character);
1116 if (diacritic != 0 && output_format == TEX_FORMAT)
1118 base = tobase (character);
1122 case 1: /* Latin diphthongs */
1126 fputs ("\\oe{}", stdout);
1130 fputs ("\\OE{}", stdout);
1134 fputs ("\\ae{}", stdout);
1138 fputs ("\\AE{}", stdout);
1146 case 2: /* Acute accent */
1147 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1150 case 3: /* Grave accent */
1151 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1154 case 4: /* Circumflex accent */
1155 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1158 case 5: /* Diaeresis */
1159 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1162 case 6: /* Tilde accent */
1163 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1166 case 7: /* Cedilla */
1167 printf ("\\c{%c}", base);
1170 case 8: /* Small circle beneath */
1174 fputs ("\\aa{}", stdout);
1178 fputs ("\\AA{}", stdout);
1186 case 9: /* Strike through */
1190 fputs ("\\o{}", stdout);
1194 fputs ("\\O{}", stdout);
1205 /* This is not a diacritic character, so handle cases which are
1206 really specific to `roff' or TeX. All white space processing
1207 is done as the default case of this switch. */
1212 /* In roff output format, double any quote. */
1222 /* In TeX output format, precede these with a backslash. */
1224 putchar (character);
1229 /* In TeX output format, precede these with a backslash and
1230 force mathematical mode. */
1231 printf ("$\\%c$", character);
1235 /* In TeX output mode, request production of a backslash. */
1236 fputs ("\\backslash{}", stdout);
1240 /* Any other flagged character produces a single space. */
1249 /* Formatting and actual output - planning routines. */
1251 /*--------------------------------------------------------------------.
1252 | From information collected from command line options and input file |
1253 | readings, compute and fix some output parameter values. |
1254 `--------------------------------------------------------------------*/
1257 fix_output_parameters (void)
1259 int file_index; /* index in text input file arrays */
1260 int line_ordinal; /* line ordinal value for reference */
1261 char ordinal_string[12]; /* edited line ordinal for reference */
1262 int reference_width; /* width for the whole reference */
1263 int character; /* character ordinal */
1264 const char *cursor; /* cursor in some constant strings */
1266 /* In auto reference mode, the maximum width of this field is
1267 precomputed and subtracted from the overall line width. Add one for
1268 the column which separate the file name from the line number. */
1272 reference_max_width = 0;
1273 for (file_index = 0; file_index < number_input_files; file_index++)
1275 line_ordinal = file_line_count[file_index] + 1;
1277 line_ordinal -= file_line_count[file_index - 1];
1278 sprintf (ordinal_string, "%d", line_ordinal);
1279 reference_width = strlen (ordinal_string);
1280 if (input_file_name[file_index])
1281 reference_width += strlen (input_file_name[file_index]);
1282 if (reference_width > reference_max_width)
1283 reference_max_width = reference_width;
1285 reference_max_width++;
1286 reference.start = xmalloc ((size_t) reference_max_width + 1);
1289 /* If the reference appears to the left of the output line, reserve some
1290 space for it right away, including one gap size. */
1292 if ((auto_reference || input_reference) && !right_reference)
1293 line_width -= reference_max_width + gap_size;
1295 /* The output lines, minimally, will contain from left to right a left
1296 context, a gap, and a keyword followed by the right context with no
1297 special intervening gap. Half of the line width is dedicated to the
1298 left context and the gap, the other half is dedicated to the keyword
1299 and the right context; these values are computed once and for all here.
1300 There also are tail and head wrap around fields, used when the keyword
1301 is near the beginning or the end of the line, or when some long word
1302 cannot fit in, but leave place from wrapped around shorter words. The
1303 maximum width of these fields are recomputed separately for each line,
1304 on a case by case basis. It is worth noting that it cannot happen that
1305 both the tail and head fields are used at once. */
1307 half_line_width = line_width / 2;
1308 before_max_width = half_line_width - gap_size;
1309 keyafter_max_width = half_line_width;
1311 /* If truncation_string is the empty string, make it NULL to speed up
1312 tests. In this case, truncation_string_length will never get used, so
1313 there is no need to set it. */
1315 if (truncation_string && *truncation_string)
1316 truncation_string_length = strlen (truncation_string);
1318 truncation_string = NULL;
1323 /* When flagging truncation at the left of the keyword, the
1324 truncation mark goes at the beginning of the before field,
1325 unless there is a head field, in which case the mark goes at the
1326 left of the head field. When flagging truncation at the right
1327 of the keyword, the mark goes at the end of the keyafter field,
1328 unless there is a tail field, in which case the mark goes at the
1329 end of the tail field. Only eight combination cases could arise
1330 for truncation marks:
1333 . One beginning the before field.
1334 . One beginning the head field.
1335 . One ending the keyafter field.
1336 . One ending the tail field.
1337 . One beginning the before field, another ending the keyafter field.
1338 . One ending the tail field, another beginning the before field.
1339 . One ending the keyafter field, another beginning the head field.
1341 So, there is at most two truncation marks, which could appear both
1342 on the left side of the center of the output line, both on the
1343 right side, or one on either side. */
1345 before_max_width -= 2 * truncation_string_length;
1346 keyafter_max_width -= 2 * truncation_string_length;
1351 /* I never figured out exactly how UNIX' ptx plans the output width
1352 of its various fields. If GNU extensions are disabled, do not
1353 try computing the field widths correctly; instead, use the
1354 following formula, which does not completely imitate UNIX' ptx,
1357 keyafter_max_width -= 2 * truncation_string_length + 1;
1360 /* Compute which characters need special output processing. Initialize
1361 by flagging any white space character. Some systems do not consider
1362 form feed as a space character, but we do. */
1364 for (character = 0; character < CHAR_SET_SIZE; character++)
1365 edited_flag[character] = ISSPACE (character) != 0;
1366 edited_flag['\f'] = 1;
1368 /* Complete the special character flagging according to selected output
1371 switch (output_format)
1373 case UNKNOWN_FORMAT:
1374 /* Should never happen. */
1381 /* `Quote' characters should be doubled. */
1383 edited_flag['"'] = 1;
1388 /* Various characters need special processing. */
1390 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1391 edited_flag[(unsigned char) *cursor] = 1;
1393 /* Any character with 8th bit set will print to a single space, unless
1394 it is diacriticized. */
1396 for (character = 0200; character < CHAR_SET_SIZE; character++)
1397 edited_flag[character] = todiac (character) != 0;
1402 /*------------------------------------------------------------------.
1403 | Compute the position and length of all the output fields, given a |
1404 | pointer to some OCCURS. |
1405 `------------------------------------------------------------------*/
1408 define_all_fields (OCCURS *occurs)
1410 int tail_max_width; /* allowable width of tail field */
1411 int head_max_width; /* allowable width of head field */
1412 char *cursor; /* running cursor in source text */
1413 char *left_context_start; /* start of left context */
1414 char *right_context_end; /* end of right context */
1415 char *left_field_start; /* conservative start for `head'/`before' */
1416 int file_index; /* index in text input file arrays */
1417 const char *file_name; /* file name for reference */
1418 int line_ordinal; /* line ordinal for reference */
1420 /* Define `keyafter', start of left context and end of right context.
1421 `keyafter' starts at the saved position for keyword and extend to the
1422 right from the end of the keyword, eating separators or full words, but
1423 not beyond maximum allowed width for `keyafter' field or limit for the
1424 right context. Suffix spaces will be removed afterwards. */
1426 keyafter.start = occurs->key.start;
1427 keyafter.end = keyafter.start + occurs->key.size;
1428 left_context_start = keyafter.start + occurs->left;
1429 right_context_end = keyafter.start + occurs->right;
1431 cursor = keyafter.end;
1432 while (cursor < right_context_end
1433 && cursor <= keyafter.start + keyafter_max_width)
1435 keyafter.end = cursor;
1436 SKIP_SOMETHING (cursor, right_context_end);
1438 if (cursor <= keyafter.start + keyafter_max_width)
1439 keyafter.end = cursor;
1441 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1443 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1445 /* When the left context is wide, it might take some time to catch up from
1446 the left context boundary to the beginning of the `head' or `before'
1447 fields. So, in this case, to speed the catchup, we jump back from the
1448 keyword, using some secure distance, possibly falling in the middle of
1449 a word. A secure backward jump would be at least half the maximum
1450 width of a line, plus the size of the longest word met in the whole
1451 input. We conclude this backward jump by a skip forward of at least
1452 one word. In this manner, we should not inadvertently accept only part
1453 of a word. From the reached point, when it will be time to fix the
1454 beginning of `head' or `before' fields, we will skip forward words or
1455 delimiters until we get sufficiently near. */
1457 if (-occurs->left > half_line_width + maximum_word_length)
1460 = keyafter.start - (half_line_width + maximum_word_length);
1461 SKIP_SOMETHING (left_field_start, keyafter.start);
1464 left_field_start = keyafter.start + occurs->left;
1466 /* `before' certainly ends at the keyword, but not including separating
1467 spaces. It starts after than the saved value for the left context, by
1468 advancing it until it falls inside the maximum allowed width for the
1469 before field. There will be no prefix spaces either. `before' only
1470 advances by skipping single separators or whole words. */
1472 before.start = left_field_start;
1473 before.end = keyafter.start;
1474 SKIP_WHITE_BACKWARDS (before.end, before.start);
1476 while (before.start + before_max_width < before.end)
1477 SKIP_SOMETHING (before.start, before.end);
1479 if (truncation_string)
1481 cursor = before.start;
1482 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1483 before_truncation = cursor > left_context_start;
1486 before_truncation = 0;
1488 SKIP_WHITE (before.start, text_buffer.end);
1490 /* The tail could not take more columns than what has been left in the
1491 left context field, and a gap is mandatory. It starts after the
1492 right context, and does not contain prefixed spaces. It ends at
1493 the end of line, the end of buffer or when the tail field is full,
1494 whichever comes first. It cannot contain only part of a word, and
1495 has no suffixed spaces. */
1498 = before_max_width - (before.end - before.start) - gap_size;
1500 if (tail_max_width > 0)
1502 tail.start = keyafter.end;
1503 SKIP_WHITE (tail.start, text_buffer.end);
1505 tail.end = tail.start;
1507 while (cursor < right_context_end
1508 && cursor < tail.start + tail_max_width)
1511 SKIP_SOMETHING (cursor, right_context_end);
1514 if (cursor < tail.start + tail_max_width)
1517 if (tail.end > tail.start)
1519 keyafter_truncation = 0;
1520 tail_truncation = truncation_string && tail.end < right_context_end;
1523 tail_truncation = 0;
1525 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1530 /* No place left for a tail field. */
1534 tail_truncation = 0;
1537 /* `head' could not take more columns than what has been left in the right
1538 context field, and a gap is mandatory. It ends before the left
1539 context, and does not contain suffixed spaces. Its pointer is advanced
1540 until the head field has shrunk to its allowed width. It cannot
1541 contain only part of a word, and has no suffixed spaces. */
1544 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1546 if (head_max_width > 0)
1548 head.end = before.start;
1549 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1551 head.start = left_field_start;
1552 while (head.start + head_max_width < head.end)
1553 SKIP_SOMETHING (head.start, head.end);
1555 if (head.end > head.start)
1557 before_truncation = 0;
1558 head_truncation = (truncation_string
1559 && head.start > left_context_start);
1562 head_truncation = 0;
1564 SKIP_WHITE (head.start, head.end);
1569 /* No place left for a head field. */
1573 head_truncation = 0;
1579 /* Construct the reference text in preallocated space from the file
1580 name and the line number. Find out in which file the reference
1581 occurred. Standard input yields an empty file name. Insure line
1582 numbers are one based, even if they are computed zero based. */
1585 while (file_line_count[file_index] < occurs->reference)
1588 file_name = input_file_name[file_index];
1592 line_ordinal = occurs->reference + 1;
1594 line_ordinal -= file_line_count[file_index - 1];
1596 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1597 reference.end = reference.start + strlen (reference.start);
1599 else if (input_reference)
1602 /* Reference starts at saved position for reference and extends right
1603 until some white space is met. */
1605 reference.start = keyafter.start + (DELTA) occurs->reference;
1606 reference.end = reference.start;
1607 SKIP_NON_WHITE (reference.end, right_context_end);
1611 /* Formatting and actual output - control routines. */
1613 /*----------------------------------------------------------------------.
1614 | Output the current output fields as one line for `troff' or `nroff'. |
1615 `----------------------------------------------------------------------*/
1618 output_one_roff_line (void)
1620 /* Output the `tail' field. */
1622 printf (".%s \"", macro_name);
1624 if (tail_truncation)
1625 fputs (truncation_string, stdout);
1628 /* Output the `before' field. */
1630 fputs (" \"", stdout);
1631 if (before_truncation)
1632 fputs (truncation_string, stdout);
1633 print_field (before);
1636 /* Output the `keyafter' field. */
1638 fputs (" \"", stdout);
1639 print_field (keyafter);
1640 if (keyafter_truncation)
1641 fputs (truncation_string, stdout);
1644 /* Output the `head' field. */
1646 fputs (" \"", stdout);
1647 if (head_truncation)
1648 fputs (truncation_string, stdout);
1652 /* Conditionally output the `reference' field. */
1654 if (auto_reference || input_reference)
1656 fputs (" \"", stdout);
1657 print_field (reference);
1664 /*---------------------------------------------------------.
1665 | Output the current output fields as one line for `TeX'. |
1666 `---------------------------------------------------------*/
1669 output_one_tex_line (void)
1671 BLOCK key; /* key field, isolated */
1672 BLOCK after; /* after field, isolated */
1673 char *cursor; /* running cursor in source text */
1675 printf ("\\%s ", macro_name);
1678 fputs ("}{", stdout);
1679 print_field (before);
1680 fputs ("}{", stdout);
1681 key.start = keyafter.start;
1682 after.end = keyafter.end;
1683 cursor = keyafter.start;
1684 SKIP_SOMETHING (cursor, keyafter.end);
1686 after.start = cursor;
1688 fputs ("}{", stdout);
1689 print_field (after);
1690 fputs ("}{", stdout);
1693 if (auto_reference || input_reference)
1696 print_field (reference);
1702 /*-------------------------------------------------------------------.
1703 | Output the current output fields as one line for a dumb terminal. |
1704 `-------------------------------------------------------------------*/
1707 output_one_dumb_line (void)
1709 if (!right_reference)
1714 /* Output the `reference' field, in such a way that GNU emacs
1715 next-error will handle it. The ending colon is taken from the
1716 gap which follows. */
1718 print_field (reference);
1720 print_spaces (reference_max_width
1722 - (reference.end - reference.start)
1728 /* Output the `reference' field and its following gap. */
1730 print_field (reference);
1731 print_spaces (reference_max_width
1733 - (reference.end - reference.start));
1737 if (tail.start < tail.end)
1739 /* Output the `tail' field. */
1742 if (tail_truncation)
1743 fputs (truncation_string, stdout);
1745 print_spaces (half_line_width - gap_size
1746 - (before.end - before.start)
1747 - (before_truncation ? truncation_string_length : 0)
1748 - (tail.end - tail.start)
1749 - (tail_truncation ? truncation_string_length : 0));
1752 print_spaces (half_line_width - gap_size
1753 - (before.end - before.start)
1754 - (before_truncation ? truncation_string_length : 0));
1756 /* Output the `before' field. */
1758 if (before_truncation)
1759 fputs (truncation_string, stdout);
1760 print_field (before);
1762 print_spaces (gap_size);
1764 /* Output the `keyafter' field. */
1766 print_field (keyafter);
1767 if (keyafter_truncation)
1768 fputs (truncation_string, stdout);
1770 if (head.start < head.end)
1772 /* Output the `head' field. */
1774 print_spaces (half_line_width
1775 - (keyafter.end - keyafter.start)
1776 - (keyafter_truncation ? truncation_string_length : 0)
1777 - (head.end - head.start)
1778 - (head_truncation ? truncation_string_length : 0));
1779 if (head_truncation)
1780 fputs (truncation_string, stdout);
1785 if ((auto_reference || input_reference) && right_reference)
1786 print_spaces (half_line_width
1787 - (keyafter.end - keyafter.start)
1788 - (keyafter_truncation ? truncation_string_length : 0));
1790 if ((auto_reference || input_reference) && right_reference)
1792 /* Output the `reference' field. */
1794 print_spaces (gap_size);
1795 print_field (reference);
1801 /*------------------------------------------------------------------------.
1802 | Scan the whole occurs table and, for each entry, output one line in the |
1803 | appropriate format. |
1804 `------------------------------------------------------------------------*/
1807 generate_all_output (void)
1809 size_t occurs_index; /* index of keyword entry being processed */
1810 OCCURS *occurs_cursor; /* current keyword entry being processed */
1812 /* The following assignments are useful to provide default values in case
1813 line contexts or references are not used, in which case these variables
1814 would never be computed. */
1818 tail_truncation = 0;
1822 head_truncation = 0;
1824 /* Loop over all keyword occurrences. */
1826 occurs_cursor = occurs_table[0];
1828 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1830 /* Compute the exact size of every field and whenever truncation flags
1831 are present or not. */
1833 define_all_fields (occurs_cursor);
1835 /* Produce one output line according to selected format. */
1837 switch (output_format)
1839 case UNKNOWN_FORMAT:
1840 /* Should never happen. */
1843 output_one_dumb_line ();
1847 output_one_roff_line ();
1851 output_one_tex_line ();
1855 /* Advance the cursor into the occurs table. */
1861 /* Option decoding and main program. */
1863 /*------------------------------------------------------.
1864 | Print program identification and options, then exit. |
1865 `------------------------------------------------------*/
1870 if (status != EXIT_SUCCESS)
1871 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1876 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1877 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1878 program_name, program_name);
1880 Output a permuted index, including context, of the words in the input files.\n\
1884 Mandatory arguments to long options are mandatory for short options too.\n\
1887 -A, --auto-reference output automatically generated references\n\
1888 -C, --copyright display Copyright and copying conditions\n\
1889 -G, --traditional behave more like System V `ptx'\n\
1890 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1893 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1894 -O, --format=roff generate output as roff directives\n\
1895 -R, --right-side-refs put references at right, not counted in -w\n\
1896 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1897 -T, --format=tex generate output as TeX directives\n\
1900 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1901 -b, --break-file=FILE word break characters in this FILE\n\
1902 -f, --ignore-case fold lower case to upper case for sorting\n\
1903 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1904 -i, --ignore-file=FILE read ignore word list from FILE\n\
1905 -o, --only-file=FILE read only word list from this FILE\n\
1908 -r, --references first field of each line is a reference\n\
1909 -t, --typeset-mode - not implemented -\n\
1910 -w, --width=NUMBER output width in columns, reference excluded\n\
1912 fputs (HELP_OPTION_DESCRIPTION, stdout);
1913 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1916 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1922 /*----------------------------------------------------------------------.
1923 | Main program. Decode ARGC arguments passed through the ARGV array of |
1924 | strings, then launch execution. |
1925 `----------------------------------------------------------------------*/
1927 /* Long options equivalences. */
1928 static const struct option long_options[] =
1930 {"auto-reference", no_argument, NULL, 'A'},
1931 {"break-file", required_argument, NULL, 'b'},
1932 {"copyright", no_argument, NULL, 'C'},
1933 {"flag-truncation", required_argument, NULL, 'F'},
1934 {"ignore-case", no_argument, NULL, 'f'},
1935 {"gap-size", required_argument, NULL, 'g'},
1936 {"ignore-file", required_argument, NULL, 'i'},
1937 {"macro-name", required_argument, NULL, 'M'},
1938 {"only-file", required_argument, NULL, 'o'},
1939 {"references", no_argument, NULL, 'r'},
1940 {"right-side-refs", no_argument, NULL, 'R'},
1941 {"format", required_argument, NULL, 10},
1942 {"sentence-regexp", required_argument, NULL, 'S'},
1943 {"traditional", no_argument, NULL, 'G'},
1944 {"typeset-mode", no_argument, NULL, 't'},
1945 {"width", required_argument, NULL, 'w'},
1946 {"word-regexp", required_argument, NULL, 'W'},
1947 {GETOPT_HELP_OPTION_DECL},
1948 {GETOPT_VERSION_OPTION_DECL},
1952 static char const* const format_args[] =
1957 static enum Format const format_vals[] =
1959 ROFF_FORMAT, TEX_FORMAT
1963 main (int argc, char **argv)
1965 int optchar; /* argument character */
1966 int file_index; /* index in text input file arrays */
1968 /* Decode program options. */
1970 initialize_main (&argc, &argv);
1971 program_name = argv[0];
1972 setlocale (LC_ALL, "");
1973 bindtextdomain (PACKAGE, LOCALEDIR);
1974 textdomain (PACKAGE);
1976 atexit (close_stdout);
1978 #if HAVE_SETCHRCLASS
1982 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1983 long_options, NULL),
1989 usage (EXIT_FAILURE);
1996 This program is free software; you can redistribute it and/or modify\n\
1997 it under the terms of the GNU General Public License as published by\n\
1998 the Free Software Foundation; either version 2, or (at your option)\n\
1999 any later version.\n\
2003 This program is distributed in the hope that it will be useful,\n\
2004 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
2005 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
2006 GNU General Public License for more details.\n\
2010 You should have received a copy of the GNU General Public License\n\
2011 along with this program; if not, write to the Free Software Foundation,\n\
2012 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
2015 exit (EXIT_SUCCESS);
2022 break_file = optarg;
2030 gap_size = atoi (optarg);
2034 ignore_file = optarg;
2042 input_reference = 1;
2046 /* Yet to understand... */
2050 line_width = atoi (optarg);
2058 truncation_string = copy_unescaped_string (optarg);
2062 macro_name = optarg;
2066 output_format = ROFF_FORMAT;
2070 right_reference = 1;
2074 context_regex_string = copy_unescaped_string (optarg);
2078 output_format = TEX_FORMAT;
2082 word_regex_string = copy_unescaped_string (optarg);
2086 output_format = XARGMATCH ("--format", optarg,
2087 format_args, format_vals);
2088 case_GETOPT_HELP_CHAR;
2090 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2094 /* Change the default Ignore file if one is defined. */
2096 #ifdef DEFAULT_IGNORE_FILE
2098 ignore_file = DEFAULT_IGNORE_FILE;
2101 /* Process remaining arguments. If GNU extensions are enabled, process
2102 all arguments as input parameters. If disabled, accept at most two
2103 arguments, the second of which is an output parameter. */
2108 /* No more argument simply means: read standard input. */
2110 input_file_name = xmalloc (sizeof (const char *));
2111 file_line_count = xmalloc (sizeof (int));
2112 number_input_files = 1;
2113 input_file_name[0] = NULL;
2115 else if (gnu_extensions)
2117 number_input_files = argc - optind;
2119 = xmalloc (number_input_files * sizeof (const char *));
2121 = xmalloc (number_input_files * sizeof (int));
2123 for (file_index = 0; file_index < number_input_files; file_index++)
2125 input_file_name[file_index] = argv[optind];
2126 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2127 input_file_name[0] = NULL;
2129 input_file_name[0] = argv[optind];
2136 /* There is one necessary input file. */
2138 number_input_files = 1;
2139 input_file_name = xmalloc (sizeof (const char *));
2140 file_line_count = xmalloc (sizeof (int));
2141 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2142 input_file_name[0] = NULL;
2144 input_file_name[0] = argv[optind];
2147 /* Redirect standard output, only if requested. */
2151 /* FIXME: don't fclose here? */
2153 if (fopen (argv[optind], "w") == NULL)
2154 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2158 /* Diagnose any other argument as an error. */
2161 usage (EXIT_FAILURE);
2164 /* If the output format has not been explicitly selected, choose dumb
2165 terminal format if GNU extensions are enabled, else `roff' format. */
2167 if (output_format == UNKNOWN_FORMAT)
2168 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2170 /* Initialize the main tables. */
2172 initialize_regex ();
2174 /* Read `Break character' file, if any. */
2177 digest_break_file (break_file);
2179 /* Read `Ignore words' file and `Only words' files, if any. If any of
2180 these files is empty, reset the name of the file to NULL, to avoid
2181 unnecessary calls to search_table. */
2185 digest_word_file (ignore_file, &ignore_table);
2186 if (ignore_table.length == 0)
2192 digest_word_file (only_file, &only_table);
2193 if (only_table.length == 0)
2197 /* Prepare to study all the input files. */
2199 number_of_occurs[0] = 0;
2200 total_line_count = 0;
2201 maximum_word_length = 0;
2202 reference_max_width = 0;
2204 for (file_index = 0; file_index < number_input_files; file_index++)
2207 /* Read the file in core, than study it. */
2209 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2210 find_occurs_in_text ();
2212 /* Maintain for each file how many lines has been read so far when its
2213 end is reached. Incrementing the count first is a simple kludge to
2214 handle a possible incomplete line at end of file. */
2217 file_line_count[file_index] = total_line_count;
2220 /* Do the output process phase. */
2222 sort_found_occurs ();
2223 fix_output_parameters ();
2224 generate_all_output ();
2228 exit (EXIT_SUCCESS);