1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990, 1991, 1993, 1998-1999 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>
28 #include "bumpalloc.h"
31 #include "long-options.h"
34 /* Number of possible characters in a byte. */
35 #define CHAR_SET_SIZE 256
37 /* The ctype definitions should work for all 256 characters. */
41 # define isspace(C) ((C) == ' ' || (C) == '\t' || (C) == '\n')
42 # define isxdigit(C) \
43 (((unsigned char) (C) >= 'a' && (unsigned char) (C) <= 'f') \
44 || ((unsigned char) (C) >= 'A' && (unsigned char) (C) <= 'F') \
45 || ((unsigned char) (C) >= '0' && (unsigned char) (C) <= '9'))
46 # define islower(C) ((unsigned char) (C) >= 'a' && (unsigned char) (C) <= 'z')
47 # define isupper(C) ((unsigned char) (C) >= 'A' && (unsigned char) (C) <= 'Z')
48 # define isalpha(C) (islower (C) || isupper (C))
49 # define toupper(C) (islower (C) ? (C) - 'a' + 'A' : (C))
52 #if !defined (isascii) || defined (STDC_HEADERS)
58 # define ISXDIGIT(C) (isascii (C) && isxdigit (C))
60 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
61 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
62 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
63 #define OCTTOBIN(C) ((C) - '0')
65 /* Debugging the memory allocator. */
68 # define MALLOC_FUNC_CHECK 1
72 /* Global definitions. */
74 /* Reallocation step when swallowing non regular files. The value is not
75 the actual reallocation step, but its base two logarithm. */
76 #define SWALLOW_REALLOC_LOG 12
78 /* Imported from "regex.c". */
81 /* The name this program was run with. */
84 /* Program options. */
88 UNKNOWN_FORMAT, /* output format still unknown */
89 DUMB_FORMAT, /* output for a dumb terminal */
90 ROFF_FORMAT, /* output for `troff' or `nroff' */
91 TEX_FORMAT /* output for `TeX' or `LaTeX' */
94 int gnu_extensions = 1; /* trigger all GNU extensions */
95 int auto_reference = 0; /* references are `file_name:line_number:' */
96 int input_reference = 0; /* references at beginning of input lines */
97 int right_reference = 0; /* output references after right context */
98 int line_width = 72; /* output line width in characters */
99 int gap_size = 3; /* number of spaces between output fields */
100 const char *truncation_string = "/";
101 /* string used to mark line truncations */
102 const char *macro_name = "xx"; /* macro name for roff or TeX output */
103 enum Format output_format = UNKNOWN_FORMAT;
106 int ignore_case = 0; /* fold lower to upper case for sorting */
107 const char *context_regex_string = NULL;
108 /* raw regex for end of context */
109 const char *word_regex_string = NULL;
110 /* raw regex for a keyword */
111 const char *break_file = NULL; /* name of the `Break characters' file */
112 const char *only_file = NULL; /* name of the `Only words' file */
113 const char *ignore_file = NULL; /* name of the `Ignore words' file */
115 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
116 whole file. A WORD is something smaller, its length should fit in a
117 short integer. A WORD_TABLE may contain several WORDs. */
121 char *start; /* pointer to beginning of region */
122 char *end; /* pointer to end + 1 of region */
128 char *start; /* pointer to beginning of region */
129 short size; /* length of the region */
135 WORD *start; /* array of WORDs */
136 size_t length; /* number of entries */
140 /* Pattern description tables. */
142 /* For each character, provide its folded equivalent. */
143 unsigned char folded_chars[CHAR_SET_SIZE];
145 /* For each character, indicate if it is part of a word. */
146 char syntax_table[CHAR_SET_SIZE];
147 char *re_syntax_table = syntax_table;
149 /* Compiled regex for end of context. */
150 struct re_pattern_buffer *context_regex;
152 /* End of context pattern register indices. */
153 struct re_registers context_regs;
155 /* Compiled regex for a keyword. */
156 struct re_pattern_buffer *word_regex;
158 /* Keyword pattern register indices. */
159 struct re_registers word_regs;
161 /* A word characters fastmap is used only when no word regexp has been
162 provided. A word is then made up of a sequence of one or more characters
163 allowed by the fastmap. Contains !0 if character allowed in word. Not
164 only this is faster in most cases, but it simplifies the implementation
165 of the Break files. */
166 char word_fastmap[CHAR_SET_SIZE];
168 /* Maximum length of any word read. */
169 int maximum_word_length;
171 /* Maximum width of any reference used. */
172 int reference_max_width;
174 /* Ignore and Only word tables. */
176 WORD_TABLE ignore_table; /* table of words to ignore */
177 WORD_TABLE only_table; /* table of words to select */
179 #define ALLOC_NEW_WORD(table) \
180 BUMP_ALLOC ((table)->start, (table)->length, 8, WORD)
182 /* Source text table, and scanning macros. */
184 int number_input_files; /* number of text input files */
185 int total_line_count; /* total number of lines seen so far */
186 const char **input_file_name; /* array of text input file names */
187 int *file_line_count; /* array of `total_line_count' values at end */
189 BLOCK text_buffer; /* file to study */
190 char *text_buffer_maxend; /* allocated end of text_buffer */
192 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
194 #define SKIP_NON_WHITE(cursor, limit) \
195 while (cursor < limit && !isspace(*cursor)) \
198 #define SKIP_WHITE(cursor, limit) \
199 while (cursor < limit && isspace(*cursor)) \
202 #define SKIP_WHITE_BACKWARDS(cursor, start) \
203 while (cursor > start && isspace(cursor[-1])) \
206 #define SKIP_SOMETHING(cursor, limit) \
207 if (word_regex_string) \
210 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
211 cursor += count <= 0 ? 1 : count; \
213 else if (word_fastmap[(unsigned char) *cursor]) \
214 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
219 /* Occurrences table.
221 The `keyword' pointer provides the central word, which is surrounded
222 by a left context and a right context. The `keyword' and `length'
223 field allow full 8-bit characters keys, even including NULs. At other
224 places in this program, the name `keyafter' refers to the keyword
225 followed by its right context.
227 The left context does not extend, towards the beginning of the file,
228 further than a distance given by the `left' value. This value is
229 relative to the keyword beginning, it is usually negative. This
230 insures that, except for white space, we will never have to backward
231 scan the source text, when it is time to generate the final output
234 The right context, indirectly attainable through the keyword end, does
235 not extend, towards the end of the file, further than a distance given
236 by the `right' value. This value is relative to the keyword
237 beginning, it is usually positive.
239 When automatic references are used, the `reference' value is the
240 overall line number in all input files read so far, in this case, it
241 is of type (int). When input references are used, the `reference'
242 value indicates the distance between the keyword beginning and the
243 start of the reference field, it is of type (DELTA) and usually
246 typedef short DELTA; /* to hold displacement within one context */
250 WORD key; /* description of the keyword */
251 DELTA left; /* distance to left context start */
252 DELTA right; /* distance to right context end */
253 int reference; /* reference descriptor */
257 /* The various OCCURS tables are indexed by the language. But the time
258 being, there is no such multiple language support. */
260 OCCURS *occurs_table[1]; /* all words retained from the read text */
261 size_t number_of_occurs[1]; /* number of used slots in occurs_table */
263 #define ALLOC_NEW_OCCURS(language) \
264 BUMP_ALLOC (occurs_table[language], number_of_occurs[language], 9, OCCURS)
266 /* Communication among output routines. */
268 /* Indicate if special output processing is requested for each character. */
269 char edited_flag[CHAR_SET_SIZE];
271 int half_line_width; /* half of line width, reference excluded */
272 int before_max_width; /* maximum width of before field */
273 int keyafter_max_width; /* maximum width of keyword-and-after field */
274 int truncation_string_length; /* length of string used to flag truncation */
276 /* When context is limited by lines, wraparound may happen on final output:
277 the `head' pointer gives access to some supplementary left context which
278 will be seen at the end of the output line, the `tail' pointer gives
279 access to some supplementary right context which will be seen at the
280 beginning of the output line. */
282 BLOCK tail; /* tail field */
283 int tail_truncation; /* flag truncation after the tail field */
285 BLOCK before; /* before field */
286 int before_truncation; /* flag truncation before the before field */
288 BLOCK keyafter; /* keyword-and-after field */
289 int keyafter_truncation; /* flag truncation after the keyafter field */
291 BLOCK head; /* head field */
292 int head_truncation; /* flag truncation before the head field */
294 BLOCK reference; /* reference field for input reference mode */
296 /* Miscellaneous routines. */
298 /*------------------------------------------------------.
299 | Duplicate string STRING, while evaluating \-escapes. |
300 `------------------------------------------------------*/
302 /* Loosely adapted from GNU sh-utils printf.c code. */
305 copy_unescaped_string (const char *string)
307 char *result; /* allocated result */
308 char *cursor; /* cursor in result */
309 int value; /* value of \nnn escape */
310 int length; /* length of \nnn escape */
312 result = xmalloc (strlen (string) + 1);
321 case 'x': /* \xhhh escape, 3 chars maximum */
323 for (length = 0, string++;
324 length < 3 && ISXDIGIT (*string);
326 value = value * 16 + HEXTOBIN (*string);
336 case '0': /* \0ooo escape, 3 chars maximum */
338 for (length = 0, string++;
339 length < 3 && ISODIGIT (*string);
341 value = value * 8 + OCTTOBIN (*string);
345 case 'a': /* alert */
354 case 'b': /* backspace */
359 case 'c': /* cancel the rest of the output */
364 case 'f': /* form feed */
369 case 'n': /* new line */
374 case 'r': /* carriage return */
379 case 't': /* horizontal tab */
384 case 'v': /* vertical tab */
395 *cursor++ = *string++;
400 *cursor++ = *string++;
406 /*-------------------------------------------------------------------.
407 | Compile the regex represented by STRING, diagnose and abort if any |
408 | error. Returns the compiled regex structure. |
409 `-------------------------------------------------------------------*/
411 static struct re_pattern_buffer *
412 alloc_and_compile_regex (const char *string)
414 struct re_pattern_buffer *pattern; /* newly allocated structure */
415 const char *message; /* error message returned by regex.c */
417 pattern = (struct re_pattern_buffer *)
418 xmalloc (sizeof (struct re_pattern_buffer));
419 memset (pattern, 0, sizeof (struct re_pattern_buffer));
421 pattern->buffer = NULL;
422 pattern->allocated = 0;
423 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
424 pattern->fastmap = (char *) xmalloc ((size_t) CHAR_SET_SIZE);
426 message = re_compile_pattern (string, (int) strlen (string), pattern);
428 error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);
430 /* The fastmap should be compiled before `re_match'. The following
431 call is not mandatory, because `re_search' is always called sooner,
432 and it compiles the fastmap if this has not been done yet. */
434 re_compile_fastmap (pattern);
436 /* Do not waste extra allocated space. */
438 if (pattern->allocated > pattern->used)
441 = (unsigned char *) xrealloc (pattern->buffer, (size_t) pattern->used);
442 pattern->allocated = pattern->used;
448 /*------------------------------------------------------------------------.
449 | This will initialize various tables for pattern match and compiles some |
451 `------------------------------------------------------------------------*/
454 initialize_regex (void)
456 int character; /* character value */
458 /* Initialize the regex syntax table. */
460 for (character = 0; character < CHAR_SET_SIZE; character++)
461 syntax_table[character] = isalpha (character) ? Sword : 0;
463 /* Initialize the case folding table. */
466 for (character = 0; character < CHAR_SET_SIZE; character++)
467 folded_chars[character] = toupper (character);
469 /* Unless the user already provided a description of the end of line or
470 end of sentence sequence, select an end of line sequence to compile.
471 If the user provided an empty definition, thus disabling end of line
472 or sentence feature, make it NULL to speed up tests. If GNU
473 extensions are enabled, use end of sentence like in GNU emacs. If
474 disabled, use end of lines. */
476 if (context_regex_string)
478 if (!*context_regex_string)
479 context_regex_string = NULL;
481 else if (gnu_extensions && !input_reference)
482 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
484 context_regex_string = "\n";
486 if (context_regex_string)
487 context_regex = alloc_and_compile_regex (context_regex_string);
489 /* If the user has already provided a non-empty regexp to describe
490 words, compile it. Else, unless this has already been done through
491 a user provided Break character file, construct a fastmap of
492 characters that may appear in a word. If GNU extensions enabled,
493 include only letters of the underlying character set. If disabled,
494 include almost everything, even punctuations; stop only on white
497 if (word_regex_string && *word_regex_string)
498 word_regex = alloc_and_compile_regex (word_regex_string);
499 else if (!break_file)
506 for (character = 0; character < CHAR_SET_SIZE; character++)
507 word_fastmap[character] = isalpha (character) ? 1 : 0;
512 /* Simulate [^ \t\n]+. */
514 memset (word_fastmap, 1, CHAR_SET_SIZE);
515 word_fastmap[' '] = 0;
516 word_fastmap['\t'] = 0;
517 word_fastmap['\n'] = 0;
522 /*------------------------------------------------------------------------.
523 | This routine will attempt to swallow a whole file name FILE_NAME into a |
524 | contiguous region of memory and return a description of it into BLOCK. |
525 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
527 | Previously, in some cases, white space compression was attempted while |
528 | inputting text. This was defeating some regexps like default end of |
529 | sentence, which checks for two consecutive spaces. If white space |
530 | compression is ever reinstated, it should be in output routines. |
531 `------------------------------------------------------------------------*/
534 swallow_file_in_memory (const char *file_name, BLOCK *block)
536 int file_handle; /* file descriptor number */
537 struct stat stat_block; /* stat block for file */
538 size_t allocated_length; /* allocated length of memory buffer */
539 size_t used_length; /* used length in memory buffer */
540 int read_length; /* number of character gotten on last read */
542 /* As special cases, a file name which is NULL or "-" indicates standard
543 input, which is already opened. In all other cases, open the file from
546 if (!file_name || !*file_name || strcmp (file_name, "-") == 0)
547 file_handle = fileno (stdin);
549 if ((file_handle = open (file_name, O_RDONLY)) < 0)
550 error (EXIT_FAILURE, errno, file_name);
552 /* If the file is a plain, regular file, allocate the memory buffer all at
553 once and swallow the file in one blow. In other cases, read the file
554 repeatedly in smaller chunks until we have it all, reallocating memory
555 once in a while, as we go. */
557 if (fstat (file_handle, &stat_block) < 0)
558 error (EXIT_FAILURE, errno, file_name);
560 if (S_ISREG (stat_block.st_mode))
562 size_t in_memory_size;
564 block->start = (char *) xmalloc ((size_t) stat_block.st_size);
566 if ((in_memory_size = read (file_handle,
567 block->start, (size_t) stat_block.st_size))
568 != stat_block.st_size)
571 /* On MSDOS, in memory size may be smaller than the file
572 size, because of end of line conversions. But it can
573 never be smaller than half the file size, because the
574 minimum is when all lines are empty and terminated by
576 if (in_memory_size != (size_t)-1
577 && in_memory_size >= stat_block.st_size / 2)
578 block->start = (char *) xrealloc (block->start, in_memory_size);
580 #endif /* not MSDOS */
582 error (EXIT_FAILURE, errno, file_name);
584 block->end = block->start + in_memory_size;
588 block->start = (char *) xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
590 allocated_length = (1 << SWALLOW_REALLOC_LOG);
592 while (read_length = read (file_handle,
593 block->start + used_length,
594 allocated_length - used_length),
597 used_length += read_length;
598 if (used_length == allocated_length)
600 allocated_length += (1 << SWALLOW_REALLOC_LOG);
602 = (char *) xrealloc (block->start, allocated_length);
607 error (EXIT_FAILURE, errno, file_name);
609 block->end = block->start + used_length;
612 /* Close the file, but only if it was not the standard input. */
614 if (file_handle != fileno (stdin))
618 /* Sort and search routines. */
620 /*--------------------------------------------------------------------------.
621 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
622 | Return less than 0 if the first word goes before the second; return |
623 | greater than 0 if the first word goes after the second. |
625 | If a word is indeed a prefix of the other, the shorter should go first. |
626 `--------------------------------------------------------------------------*/
629 compare_words (const void *void_first, const void *void_second)
631 #define first ((const WORD *) void_first)
632 #define second ((const WORD *) void_second)
633 int length; /* minimum of two lengths */
634 int counter; /* cursor in words */
635 int value; /* value of comparison */
637 length = first->size < second->size ? first->size : second->size;
641 for (counter = 0; counter < length; counter++)
643 value = (folded_chars [(unsigned char) (first->start[counter])]
644 - folded_chars [(unsigned char) (second->start[counter])]);
651 for (counter = 0; counter < length; counter++)
653 value = ((unsigned char) first->start[counter]
654 - (unsigned char) second->start[counter]);
660 return first->size - second->size;
665 /*-----------------------------------------------------------------------.
666 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
667 | go first. In case of a tie, preserve the original order through a |
668 | pointer comparison. |
669 `-----------------------------------------------------------------------*/
672 compare_occurs (const void *void_first, const void *void_second)
674 #define first ((const OCCURS *) void_first)
675 #define second ((const OCCURS *) void_second)
678 value = compare_words (&first->key, &second->key);
679 return value == 0 ? first->key.start - second->key.start : value;
684 /*------------------------------------------------------------.
685 | Return !0 if WORD appears in TABLE. Uses a binary search. |
686 `------------------------------------------------------------*/
689 search_table (WORD *word, WORD_TABLE *table)
691 int lowest; /* current lowest possible index */
692 int highest; /* current highest possible index */
693 int middle; /* current middle index */
694 int value; /* value from last comparison */
697 highest = table->length - 1;
698 while (lowest <= highest)
700 middle = (lowest + highest) / 2;
701 value = compare_words (word, table->start + middle);
703 highest = middle - 1;
712 /*---------------------------------------------------------------------.
713 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
714 | take intermediate copies or table elements, so the sort will be |
715 | stabilized throughout the comparison routine. |
716 `---------------------------------------------------------------------*/
719 sort_found_occurs (void)
722 /* Only one language for the time being. */
724 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
728 /* Parameter files reading routines. */
730 /*----------------------------------------------------------------------.
731 | Read a file named FILE_NAME, containing a set of break characters. |
732 | Build a content to the array word_fastmap in which all characters are |
733 | allowed except those found in the file. Characters may be repeated. |
734 `----------------------------------------------------------------------*/
737 digest_break_file (const char *file_name)
739 BLOCK file_contents; /* to receive a copy of the file */
740 char *cursor; /* cursor in file copy */
742 swallow_file_in_memory (file_name, &file_contents);
744 /* Make the fastmap and record the file contents in it. */
746 memset (word_fastmap, 1, CHAR_SET_SIZE);
747 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
748 word_fastmap[(unsigned char) *cursor] = 0;
753 /* If GNU extensions are enabled, the only way to avoid newline as
754 a break character is to write all the break characters in the
755 file with no newline at all, not even at the end of the file.
756 If disabled, spaces, tabs and newlines are always considered as
757 break characters even if not included in the break file. */
759 word_fastmap[' '] = 0;
760 word_fastmap['\t'] = 0;
761 word_fastmap['\n'] = 0;
764 /* Return the space of the file, which is no more required. */
766 free (file_contents.start);
769 /*-----------------------------------------------------------------------.
770 | Read a file named FILE_NAME, containing one word per line, then |
771 | construct in TABLE a table of WORD descriptors for them. The routine |
772 | swallows the whole file in memory; this is at the expense of space |
773 | needed for newlines, which are useless; however, the reading is fast. |
774 `-----------------------------------------------------------------------*/
777 digest_word_file (const char *file_name, WORD_TABLE *table)
779 BLOCK file_contents; /* to receive a copy of the file */
780 char *cursor; /* cursor in file copy */
781 char *word_start; /* start of the current word */
783 swallow_file_in_memory (file_name, &file_contents);
788 /* Read the whole file. */
790 cursor = file_contents.start;
791 while (cursor < file_contents.end)
794 /* Read one line, and save the word in contains. */
797 while (cursor < file_contents.end && *cursor != '\n')
800 /* Record the word in table if it is not empty. */
802 if (cursor > word_start)
804 ALLOC_NEW_WORD (table);
805 table->start[table->length].start = word_start;
806 table->start[table->length].size = cursor - word_start;
810 /* This test allows for an incomplete line at end of file. */
812 if (cursor < file_contents.end)
816 /* Finally, sort all the words read. */
818 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
821 /* Keyword recognition and selection. */
823 /*----------------------------------------------------------------------.
824 | For each keyword in the source text, constructs an OCCURS structure. |
825 `----------------------------------------------------------------------*/
828 find_occurs_in_text (void)
830 char *cursor; /* for scanning the source text */
831 char *scan; /* for scanning the source text also */
832 char *line_start; /* start of the current input line */
833 char *line_scan; /* newlines scanned until this point */
834 int reference_length; /* length of reference in input mode */
835 WORD possible_key; /* possible key, to ease searches */
836 OCCURS *occurs_cursor; /* current OCCURS under construction */
838 char *context_start; /* start of left context */
839 char *context_end; /* end of right context */
840 char *word_start; /* start of word */
841 char *word_end; /* end of word */
842 char *next_context_start; /* next start of left context */
844 /* reference_length is always used within `if (input_reference)'.
845 However, GNU C diagnoses that it may be used uninitialized. The
846 following assignment is merely to shut it up. */
848 reference_length = 0;
850 /* Tracking where lines start is helpful for reference processing. In
851 auto reference mode, this allows counting lines. In input reference
852 mode, this permits finding the beginning of the references.
854 The first line begins with the file, skip immediately this very first
855 reference in input reference mode, to help further rejection any word
856 found inside it. Also, unconditionally assigning these variable has
857 the happy effect of shutting up lint. */
859 line_start = text_buffer.start;
860 line_scan = line_start;
863 SKIP_NON_WHITE (line_scan, text_buffer.end);
864 reference_length = line_scan - line_start;
865 SKIP_WHITE (line_scan, text_buffer.end);
868 /* Process the whole buffer, one line or one sentence at a time. */
870 for (cursor = text_buffer.start;
871 cursor < text_buffer.end;
872 cursor = next_context_start)
875 /* `context_start' gets initialized before the processing of each
876 line, or once for the whole buffer if no end of line or sentence
877 sequence separator. */
879 context_start = cursor;
881 /* If a end of line or end of sentence sequence is defined and
882 non-empty, `next_context_start' will be recomputed to be the end of
883 each line or sentence, before each one is processed. If no such
884 sequence, then `next_context_start' is set at the end of the whole
885 buffer, which is then considered to be a single line or sentence.
886 This test also accounts for the case of an incomplete line or
887 sentence at the end of the buffer. */
889 if (context_regex_string
890 && (re_search (context_regex, cursor, text_buffer.end - cursor,
891 0, text_buffer.end - cursor, &context_regs)
893 next_context_start = cursor + context_regs.end[0];
896 next_context_start = text_buffer.end;
898 /* Include the separator into the right context, but not any suffix
899 white space in this separator; this insures it will be seen in
900 output and will not take more space than necessary. */
902 context_end = next_context_start;
903 SKIP_WHITE_BACKWARDS (context_end, context_start);
905 /* Read and process a single input line or sentence, one word at a
912 /* If a word regexp has been compiled, use it to skip at the
913 beginning of the next word. If there is no such word, exit
917 if (re_search (word_regex, cursor, context_end - cursor,
918 0, context_end - cursor, &word_regs)
921 word_start = cursor + word_regs.start[0];
922 word_end = cursor + word_regs.end[0];
926 /* Avoid re_search and use the fastmap to skip to the
927 beginning of the next word. If there is no more word in
928 the buffer, exit the loop. */
932 while (scan < context_end
933 && !word_fastmap[(unsigned char) *scan])
936 if (scan == context_end)
941 while (scan < context_end
942 && word_fastmap[(unsigned char) *scan])
948 /* Skip right to the beginning of the found word. */
952 /* Skip any zero length word. Just advance a single position,
953 then go fetch the next word. */
955 if (word_end == word_start)
961 /* This is a genuine, non empty word, so save it as a possible
962 key. Then skip over it. Also, maintain the maximum length of
963 all words read so far. It is mandatory to take the maximum
964 length of all words in the file, without considering if they
965 are actually kept or rejected, because backward jumps at output
966 generation time may fall in *any* word. */
968 possible_key.start = cursor;
969 possible_key.size = word_end - word_start;
970 cursor += possible_key.size;
972 if (possible_key.size > maximum_word_length)
973 maximum_word_length = possible_key.size;
975 /* In input reference mode, update `line_start' from its previous
976 value. Count the lines just in case auto reference mode is
977 also selected. If it happens that the word just matched is
978 indeed part of a reference; just ignore it. */
982 while (line_scan < possible_key.start)
983 if (*line_scan == '\n')
987 line_start = line_scan;
988 SKIP_NON_WHITE (line_scan, text_buffer.end);
989 reference_length = line_scan - line_start;
993 if (line_scan > possible_key.start)
997 /* Ignore the word if an `Ignore words' table exists and if it is
998 part of it. Also ignore the word if an `Only words' table and
999 if it is *not* part of it.
1001 It is allowed that both tables be used at once, even if this
1002 may look strange for now. Just ignore a word that would appear
1003 in both. If regexps are eventually implemented for these
1004 tables, the Ignore table could then reject words that would
1005 have been previously accepted by the Only table. */
1007 if (ignore_file && search_table (&possible_key, &ignore_table))
1009 if (only_file && !search_table (&possible_key, &only_table))
1012 /* A non-empty word has been found. First of all, insure
1013 proper allocation of the next OCCURS, and make a pointer to
1014 where it will be constructed. */
1016 ALLOC_NEW_OCCURS (0);
1017 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1019 /* Define the refence field, if any. */
1024 /* While auto referencing, update `line_start' from its
1025 previous value, counting lines as we go. If input
1026 referencing at the same time, `line_start' has been
1027 advanced earlier, and the following loop is never really
1030 while (line_scan < possible_key.start)
1031 if (*line_scan == '\n')
1035 line_start = line_scan;
1036 SKIP_NON_WHITE (line_scan, text_buffer.end);
1041 occurs_cursor->reference = total_line_count;
1043 else if (input_reference)
1046 /* If only input referencing, `line_start' has been computed
1047 earlier to detect the case the word matched would be part
1048 of the reference. The reference position is simply the
1049 value of `line_start'. */
1051 occurs_cursor->reference
1052 = (DELTA) (line_start - possible_key.start);
1053 if (reference_length > reference_max_width)
1054 reference_max_width = reference_length;
1057 /* Exclude the reference from the context in simple cases. */
1059 if (input_reference && line_start == context_start)
1061 SKIP_NON_WHITE (context_start, context_end);
1062 SKIP_WHITE (context_start, context_end);
1065 /* Completes the OCCURS structure. */
1067 occurs_cursor->key = possible_key;
1068 occurs_cursor->left = context_start - possible_key.start;
1069 occurs_cursor->right = context_end - possible_key.start;
1071 number_of_occurs[0]++;
1076 /* Formatting and actual output - service routines. */
1078 /*-----------------------------------------.
1079 | Prints some NUMBER of spaces on stdout. |
1080 `-----------------------------------------*/
1083 print_spaces (int number)
1087 for (counter = number; counter > 0; counter--)
1091 /*-------------------------------------.
1092 | Prints the field provided by FIELD. |
1093 `-------------------------------------*/
1096 print_field (BLOCK field)
1098 char *cursor; /* Cursor in field to print */
1099 int character; /* Current character */
1100 int base; /* Base character, without diacritic */
1101 int diacritic; /* Diacritic code for the character */
1103 /* Whitespace is not really compressed. Instead, each white space
1104 character (tab, vt, ht etc.) is printed as one single space. */
1106 for (cursor = field.start; cursor < field.end; cursor++)
1108 character = (unsigned char) *cursor;
1109 if (edited_flag[character])
1112 /* First check if this is a diacriticized character.
1114 This works only for TeX. I do not know how diacriticized
1115 letters work with `roff'. Please someone explain it to me! */
1117 diacritic = todiac (character);
1118 if (diacritic != 0 && output_format == TEX_FORMAT)
1120 base = tobase (character);
1124 case 1: /* Latin diphthongs */
1128 fputs ("\\oe{}", stdout);
1132 fputs ("\\OE{}", stdout);
1136 fputs ("\\ae{}", stdout);
1140 fputs ("\\AE{}", stdout);
1148 case 2: /* Acute accent */
1149 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1152 case 3: /* Grave accent */
1153 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1156 case 4: /* Circumflex accent */
1157 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1160 case 5: /* Diaeresis */
1161 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1164 case 6: /* Tilde accent */
1165 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1168 case 7: /* Cedilla */
1169 printf ("\\c{%c}", base);
1172 case 8: /* Small circle beneath */
1176 fputs ("\\aa{}", stdout);
1180 fputs ("\\AA{}", stdout);
1188 case 9: /* Strike through */
1192 fputs ("\\o{}", stdout);
1196 fputs ("\\O{}", stdout);
1207 /* This is not a diacritic character, so handle cases which are
1208 really specific to `roff' or TeX. All white space processing
1209 is done as the default case of this switch. */
1214 /* In roff output format, double any quote. */
1224 /* In TeX output format, precede these with a backslash. */
1226 putchar (character);
1231 /* In TeX output format, precede these with a backslash and
1232 force mathematical mode. */
1233 printf ("$\\%c$", character);
1237 /* In TeX output mode, request production of a backslash. */
1238 fputs ("\\backslash{}", stdout);
1242 /* Any other flagged character produces a single space. */
1251 /* Formatting and actual output - planning routines. */
1253 /*--------------------------------------------------------------------.
1254 | From information collected from command line options and input file |
1255 | readings, compute and fix some output parameter values. |
1256 `--------------------------------------------------------------------*/
1259 fix_output_parameters (void)
1261 int file_index; /* index in text input file arrays */
1262 int line_ordinal; /* line ordinal value for reference */
1263 char ordinal_string[12]; /* edited line ordinal for reference */
1264 int reference_width; /* width for the whole reference */
1265 int character; /* character ordinal */
1266 const char *cursor; /* cursor in some constant strings */
1268 /* In auto reference mode, the maximum width of this field is
1269 precomputed and subtracted from the overall line width. Add one for
1270 the column which separate the file name from the line number. */
1274 reference_max_width = 0;
1275 for (file_index = 0; file_index < number_input_files; file_index++)
1277 line_ordinal = file_line_count[file_index] + 1;
1279 line_ordinal -= file_line_count[file_index - 1];
1280 sprintf (ordinal_string, "%d", line_ordinal);
1281 reference_width = strlen (ordinal_string);
1282 if (input_file_name[file_index])
1283 reference_width += strlen (input_file_name[file_index]);
1284 if (reference_width > reference_max_width)
1285 reference_max_width = reference_width;
1287 reference_max_width++;
1288 reference.start = (char *) xmalloc ((size_t) reference_max_width + 1);
1291 /* If the reference appears to the left of the output line, reserve some
1292 space for it right away, including one gap size. */
1294 if ((auto_reference || input_reference) && !right_reference)
1295 line_width -= reference_max_width + gap_size;
1297 /* The output lines, minimally, will contain from left to right a left
1298 context, a gap, and a keyword followed by the right context with no
1299 special intervening gap. Half of the line width is dedicated to the
1300 left context and the gap, the other half is dedicated to the keyword
1301 and the right context; these values are computed once and for all here.
1302 There also are tail and head wrap around fields, used when the keyword
1303 is near the beginning or the end of the line, or when some long word
1304 cannot fit in, but leave place from wrapped around shorter words. The
1305 maximum width of these fields are recomputed separately for each line,
1306 on a case by case basis. It is worth noting that it cannot happen that
1307 both the tail and head fields are used at once. */
1309 half_line_width = line_width / 2;
1310 before_max_width = half_line_width - gap_size;
1311 keyafter_max_width = half_line_width;
1313 /* If truncation_string is the empty string, make it NULL to speed up
1314 tests. In this case, truncation_string_length will never get used, so
1315 there is no need to set it. */
1317 if (truncation_string && *truncation_string)
1318 truncation_string_length = strlen (truncation_string);
1320 truncation_string = NULL;
1325 /* When flagging truncation at the left of the keyword, the
1326 truncation mark goes at the beginning of the before field,
1327 unless there is a head field, in which case the mark goes at the
1328 left of the head field. When flagging truncation at the right
1329 of the keyword, the mark goes at the end of the keyafter field,
1330 unless there is a tail field, in which case the mark goes at the
1331 end of the tail field. Only eight combination cases could arise
1332 for truncation marks:
1335 . One beginning the before field.
1336 . One beginning the head field.
1337 . One ending the keyafter field.
1338 . One ending the tail field.
1339 . One beginning the before field, another ending the keyafter field.
1340 . One ending the tail field, another beginning the before field.
1341 . One ending the keyafter field, another beginning the head field.
1343 So, there is at most two truncation marks, which could appear both
1344 on the left side of the center of the output line, both on the
1345 right side, or one on either side. */
1347 before_max_width -= 2 * truncation_string_length;
1348 keyafter_max_width -= 2 * truncation_string_length;
1353 /* I never figured out exactly how UNIX' ptx plans the output width
1354 of its various fields. If GNU extensions are disabled, do not
1355 try computing the field widths correctly; instead, use the
1356 following formula, which does not completely imitate UNIX' ptx,
1359 keyafter_max_width -= 2 * truncation_string_length + 1;
1362 /* Compute which characters need special output processing. Initialize
1363 by flagging any white space character. Some systems do not consider
1364 form feed as a space character, but we do. */
1366 for (character = 0; character < CHAR_SET_SIZE; character++)
1367 edited_flag[character] = isspace (character) != 0;
1368 edited_flag['\f'] = 1;
1370 /* Complete the special character flagging according to selected output
1373 switch (output_format)
1375 case UNKNOWN_FORMAT:
1376 /* Should never happen. */
1383 /* `Quote' characters should be doubled. */
1385 edited_flag['"'] = 1;
1390 /* Various characters need special processing. */
1392 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1393 edited_flag[(unsigned char) *cursor] = 1;
1395 /* Any character with 8th bit set will print to a single space, unless
1396 it is diacriticized. */
1398 for (character = 0200; character < CHAR_SET_SIZE; character++)
1399 edited_flag[character] = todiac (character) != 0;
1404 /*------------------------------------------------------------------.
1405 | Compute the position and length of all the output fields, given a |
1406 | pointer to some OCCURS. |
1407 `------------------------------------------------------------------*/
1410 define_all_fields (OCCURS *occurs)
1412 int tail_max_width; /* allowable width of tail field */
1413 int head_max_width; /* allowable width of head field */
1414 char *cursor; /* running cursor in source text */
1415 char *left_context_start; /* start of left context */
1416 char *right_context_end; /* end of right context */
1417 char *left_field_start; /* conservative start for `head'/`before' */
1418 int file_index; /* index in text input file arrays */
1419 const char *file_name; /* file name for reference */
1420 int line_ordinal; /* line ordinal for reference */
1422 /* Define `keyafter', start of left context and end of right context.
1423 `keyafter' starts at the saved position for keyword and extend to the
1424 right from the end of the keyword, eating separators or full words, but
1425 not beyond maximum allowed width for `keyafter' field or limit for the
1426 right context. Suffix spaces will be removed afterwards. */
1428 keyafter.start = occurs->key.start;
1429 keyafter.end = keyafter.start + occurs->key.size;
1430 left_context_start = keyafter.start + occurs->left;
1431 right_context_end = keyafter.start + occurs->right;
1433 cursor = keyafter.end;
1434 while (cursor < right_context_end
1435 && cursor <= keyafter.start + keyafter_max_width)
1437 keyafter.end = cursor;
1438 SKIP_SOMETHING (cursor, right_context_end);
1440 if (cursor <= keyafter.start + keyafter_max_width)
1441 keyafter.end = cursor;
1443 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1445 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1447 /* When the left context is wide, it might take some time to catch up from
1448 the left context boundary to the beginning of the `head' or `before'
1449 fields. So, in this case, to speed the catchup, we jump back from the
1450 keyword, using some secure distance, possibly falling in the middle of
1451 a word. A secure backward jump would be at least half the maximum
1452 width of a line, plus the size of the longest word met in the whole
1453 input. We conclude this backward jump by a skip forward of at least
1454 one word. In this manner, we should not inadvertently accept only part
1455 of a word. From the reached point, when it will be time to fix the
1456 beginning of `head' or `before' fields, we will skip forward words or
1457 delimiters until we get sufficiently near. */
1459 if (-occurs->left > half_line_width + maximum_word_length)
1462 = keyafter.start - (half_line_width + maximum_word_length);
1463 SKIP_SOMETHING (left_field_start, keyafter.start);
1466 left_field_start = keyafter.start + occurs->left;
1468 /* `before' certainly ends at the keyword, but not including separating
1469 spaces. It starts after than the saved value for the left context, by
1470 advancing it until it falls inside the maximum allowed width for the
1471 before field. There will be no prefix spaces either. `before' only
1472 advances by skipping single separators or whole words. */
1474 before.start = left_field_start;
1475 before.end = keyafter.start;
1476 SKIP_WHITE_BACKWARDS (before.end, before.start);
1478 while (before.start + before_max_width < before.end)
1479 SKIP_SOMETHING (before.start, before.end);
1481 if (truncation_string)
1483 cursor = before.start;
1484 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1485 before_truncation = cursor > left_context_start;
1488 before_truncation = 0;
1490 SKIP_WHITE (before.start, text_buffer.end);
1492 /* The tail could not take more columns than what has been left in the
1493 left context field, and a gap is mandatory. It starts after the
1494 right context, and does not contain prefixed spaces. It ends at
1495 the end of line, the end of buffer or when the tail field is full,
1496 whichever comes first. It cannot contain only part of a word, and
1497 has no suffixed spaces. */
1500 = before_max_width - (before.end - before.start) - gap_size;
1502 if (tail_max_width > 0)
1504 tail.start = keyafter.end;
1505 SKIP_WHITE (tail.start, text_buffer.end);
1507 tail.end = tail.start;
1509 while (cursor < right_context_end
1510 && cursor < tail.start + tail_max_width)
1513 SKIP_SOMETHING (cursor, right_context_end);
1516 if (cursor < tail.start + tail_max_width)
1519 if (tail.end > tail.start)
1521 keyafter_truncation = 0;
1522 tail_truncation = truncation_string && tail.end < right_context_end;
1525 tail_truncation = 0;
1527 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1532 /* No place left for a tail field. */
1536 tail_truncation = 0;
1539 /* `head' could not take more columns than what has been left in the right
1540 context field, and a gap is mandatory. It ends before the left
1541 context, and does not contain suffixed spaces. Its pointer is advanced
1542 until the head field has shrunk to its allowed width. It cannot
1543 contain only part of a word, and has no suffixed spaces. */
1546 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1548 if (head_max_width > 0)
1550 head.end = before.start;
1551 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1553 head.start = left_field_start;
1554 while (head.start + head_max_width < head.end)
1555 SKIP_SOMETHING (head.start, head.end);
1557 if (head.end > head.start)
1559 before_truncation = 0;
1560 head_truncation = (truncation_string
1561 && head.start > left_context_start);
1564 head_truncation = 0;
1566 SKIP_WHITE (head.start, head.end);
1571 /* No place left for a head field. */
1575 head_truncation = 0;
1581 /* Construct the reference text in preallocated space from the file
1582 name and the line number. Find out in which file the reference
1583 occurred. Standard input yields an empty file name. Insure line
1584 numbers are one based, even if they are computed zero based. */
1587 while (file_line_count[file_index] < occurs->reference)
1590 file_name = input_file_name[file_index];
1594 line_ordinal = occurs->reference + 1;
1596 line_ordinal -= file_line_count[file_index - 1];
1598 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1599 reference.end = reference.start + strlen (reference.start);
1601 else if (input_reference)
1604 /* Reference starts at saved position for reference and extends right
1605 until some white space is met. */
1607 reference.start = keyafter.start + (DELTA) occurs->reference;
1608 reference.end = reference.start;
1609 SKIP_NON_WHITE (reference.end, right_context_end);
1613 /* Formatting and actual output - control routines. */
1615 /*----------------------------------------------------------------------.
1616 | Output the current output fields as one line for `troff' or `nroff'. |
1617 `----------------------------------------------------------------------*/
1620 output_one_roff_line (void)
1622 /* Output the `tail' field. */
1624 printf (".%s \"", macro_name);
1626 if (tail_truncation)
1627 fputs (truncation_string, stdout);
1630 /* Output the `before' field. */
1632 fputs (" \"", stdout);
1633 if (before_truncation)
1634 fputs (truncation_string, stdout);
1635 print_field (before);
1638 /* Output the `keyafter' field. */
1640 fputs (" \"", stdout);
1641 print_field (keyafter);
1642 if (keyafter_truncation)
1643 fputs (truncation_string, stdout);
1646 /* Output the `head' field. */
1648 fputs (" \"", stdout);
1649 if (head_truncation)
1650 fputs (truncation_string, stdout);
1654 /* Conditionally output the `reference' field. */
1656 if (auto_reference || input_reference)
1658 fputs (" \"", stdout);
1659 print_field (reference);
1666 /*---------------------------------------------------------.
1667 | Output the current output fields as one line for `TeX'. |
1668 `---------------------------------------------------------*/
1671 output_one_tex_line (void)
1673 BLOCK key; /* key field, isolated */
1674 BLOCK after; /* after field, isolated */
1675 char *cursor; /* running cursor in source text */
1677 printf ("\\%s ", macro_name);
1678 fputs ("{", stdout);
1680 fputs ("}{", stdout);
1681 print_field (before);
1682 fputs ("}{", stdout);
1683 key.start = keyafter.start;
1684 after.end = keyafter.end;
1685 cursor = keyafter.start;
1686 SKIP_SOMETHING (cursor, keyafter.end);
1688 after.start = cursor;
1690 fputs ("}{", stdout);
1691 print_field (after);
1692 fputs ("}{", stdout);
1694 fputs ("}", stdout);
1695 if (auto_reference || input_reference)
1697 fputs ("{", stdout);
1698 print_field (reference);
1699 fputs ("}", stdout);
1701 fputs ("\n", stdout);
1704 /*-------------------------------------------------------------------.
1705 | Output the current output fields as one line for a dumb terminal. |
1706 `-------------------------------------------------------------------*/
1709 output_one_dumb_line (void)
1711 if (!right_reference)
1716 /* Output the `reference' field, in such a way that GNU emacs
1717 next-error will handle it. The ending colon is taken from the
1718 gap which follows. */
1720 print_field (reference);
1722 print_spaces (reference_max_width
1724 - (reference.end - reference.start)
1730 /* Output the `reference' field and its following gap. */
1732 print_field (reference);
1733 print_spaces (reference_max_width
1735 - (reference.end - reference.start));
1739 if (tail.start < tail.end)
1741 /* Output the `tail' field. */
1744 if (tail_truncation)
1745 fputs (truncation_string, stdout);
1747 print_spaces (half_line_width - gap_size
1748 - (before.end - before.start)
1749 - (before_truncation ? truncation_string_length : 0)
1750 - (tail.end - tail.start)
1751 - (tail_truncation ? truncation_string_length : 0));
1754 print_spaces (half_line_width - gap_size
1755 - (before.end - before.start)
1756 - (before_truncation ? truncation_string_length : 0));
1758 /* Output the `before' field. */
1760 if (before_truncation)
1761 fputs (truncation_string, stdout);
1762 print_field (before);
1764 print_spaces (gap_size);
1766 /* Output the `keyafter' field. */
1768 print_field (keyafter);
1769 if (keyafter_truncation)
1770 fputs (truncation_string, stdout);
1772 if (head.start < head.end)
1774 /* Output the `head' field. */
1776 print_spaces (half_line_width
1777 - (keyafter.end - keyafter.start)
1778 - (keyafter_truncation ? truncation_string_length : 0)
1779 - (head.end - head.start)
1780 - (head_truncation ? truncation_string_length : 0));
1781 if (head_truncation)
1782 fputs (truncation_string, stdout);
1787 if ((auto_reference || input_reference) && right_reference)
1788 print_spaces (half_line_width
1789 - (keyafter.end - keyafter.start)
1790 - (keyafter_truncation ? truncation_string_length : 0));
1792 if ((auto_reference || input_reference) && right_reference)
1794 /* Output the `reference' field. */
1796 print_spaces (gap_size);
1797 print_field (reference);
1800 fputs ("\n", stdout);
1803 /*------------------------------------------------------------------------.
1804 | Scan the whole occurs table and, for each entry, output one line in the |
1805 | appropriate format. |
1806 `------------------------------------------------------------------------*/
1809 generate_all_output (void)
1811 int occurs_index; /* index of keyword entry being processed */
1812 OCCURS *occurs_cursor; /* current keyword entry being processed */
1814 /* The following assignments are useful to provide default values in case
1815 line contexts or references are not used, in which case these variables
1816 would never be computed. */
1820 tail_truncation = 0;
1824 head_truncation = 0;
1826 /* Loop over all keyword occurrences. */
1828 occurs_cursor = occurs_table[0];
1830 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1832 /* Compute the exact size of every field and whenever truncation flags
1833 are present or not. */
1835 define_all_fields (occurs_cursor);
1837 /* Produce one output line according to selected format. */
1839 switch (output_format)
1841 case UNKNOWN_FORMAT:
1842 /* Should never happen. */
1845 output_one_dumb_line ();
1849 output_one_roff_line ();
1853 output_one_tex_line ();
1857 /* Advance the cursor into the occurs table. */
1863 /* Option decoding and main program. */
1865 /*------------------------------------------------------.
1866 | Print program identification and options, then exit. |
1867 `------------------------------------------------------*/
1872 if (status != EXIT_SUCCESS)
1873 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1878 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1879 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1880 program_name, program_name);
1882 Mandatory arguments to long options are mandatory for short options too.\n\
1884 -A, --auto-reference output automatically generated references\n\
1885 -C, --copyright display Copyright and copying conditions\n\
1886 -G, --traditional behave more like System V `ptx'\n\
1887 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1888 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1889 -O, --format=roff generate output as roff directives\n\
1890 -R, --right-side-refs put references at right, not counted in -w\n\
1891 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1892 -T, --format=tex generate output as TeX directives\n\
1893 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1894 -b, --break-file=FILE word break characters in this FILE\n\
1895 -f, --ignore-case fold lower case to upper case for sorting\n\
1896 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1897 -i, --ignore-file=FILE read ignore word list from FILE\n\
1898 -o, --only-file=FILE read only word list from this FILE\n\
1899 -r, --references first field of each line is a reference\n\
1900 -t, --typeset-mode - not implemented -\n\
1901 -w, --width=NUMBER output width in columns, reference excluded\n\
1902 --help display this help and exit\n\
1903 --version output version information and exit\n\
1905 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n"),
1911 /*----------------------------------------------------------------------.
1912 | Main program. Decode ARGC arguments passed through the ARGV array of |
1913 | strings, then launch execution. |
1914 `----------------------------------------------------------------------*/
1916 /* Long options equivalences. */
1917 static const struct option long_options[] =
1919 {"auto-reference", no_argument, NULL, 'A'},
1920 {"break-file", required_argument, NULL, 'b'},
1921 {"copyright", no_argument, NULL, 'C'},
1922 {"flag-truncation", required_argument, NULL, 'F'},
1923 {"ignore-case", no_argument, NULL, 'f'},
1924 {"gap-size", required_argument, NULL, 'g'},
1925 {"ignore-file", required_argument, NULL, 'i'},
1926 {"macro-name", required_argument, NULL, 'M'},
1927 {"only-file", required_argument, NULL, 'o'},
1928 {"references", no_argument, NULL, 'r'},
1929 {"right-side-refs", no_argument, NULL, 'R'},
1930 {"format", required_argument, NULL, 10},
1931 {"sentence-regexp", required_argument, NULL, 'S'},
1932 {"traditional", no_argument, NULL, 'G'},
1933 {"typeset-mode", no_argument, NULL, 't'},
1934 {"width", required_argument, NULL, 'w'},
1935 {"word-regexp", required_argument, NULL, 'W'},
1939 static char const* const format_args[] =
1944 static enum Format const format_vals[] =
1946 ROFF_FORMAT, TEX_FORMAT
1950 main (int argc, char **argv)
1952 int optchar; /* argument character */
1953 int file_index; /* index in text input file arrays */
1955 /* Decode program options. */
1957 program_name = argv[0];
1958 setlocale (LC_ALL, "");
1959 bindtextdomain (PACKAGE, LOCALEDIR);
1960 textdomain (PACKAGE);
1962 #if HAVE_SETCHRCLASS
1966 parse_long_options (argc, argv, "ptx", GNU_PACKAGE, VERSION,
1967 "François Pinard", usage);
1969 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1970 long_options, NULL),
1976 usage (EXIT_FAILURE);
1983 This program is free software; you can redistribute it and/or modify\n\
1984 it under the terms of the GNU General Public License as published by\n\
1985 the Free Software Foundation; either version 2, or (at your option)\n\
1986 any later version.\n\
1988 This program is distributed in the hope that it will be useful,\n\
1989 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
1990 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
1991 GNU General Public License for more details.\n\
1993 You should have received a copy of the GNU General Public License\n\
1994 along with this program; if not, write to the Free Software Foundation,\n\
1995 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
1998 exit (EXIT_SUCCESS);
2005 break_file = optarg;
2013 gap_size = atoi (optarg);
2017 ignore_file = optarg;
2025 input_reference = 1;
2029 /* Yet to understand... */
2033 line_width = atoi (optarg);
2041 truncation_string = copy_unescaped_string (optarg);
2045 macro_name = optarg;
2049 output_format = ROFF_FORMAT;
2053 right_reference = 1;
2057 context_regex_string = copy_unescaped_string (optarg);
2061 output_format = TEX_FORMAT;
2065 word_regex_string = copy_unescaped_string (optarg);
2069 output_format = XARGMATCH ("--format", optarg,
2070 format_args, format_vals);
2074 /* Change the default Ignore file if one is defined. */
2076 #ifdef DEFAULT_IGNORE_FILE
2078 ignore_file = DEFAULT_IGNORE_FILE;
2081 /* Process remaining arguments. If GNU extensions are enabled, process
2082 all arguments as input parameters. If disabled, accept at most two
2083 arguments, the second of which is an output parameter. */
2088 /* No more argument simply means: read standard input. */
2090 input_file_name = (const char **) xmalloc (sizeof (const char *));
2091 file_line_count = (int *) xmalloc (sizeof (int));
2092 number_input_files = 1;
2093 input_file_name[0] = NULL;
2095 else if (gnu_extensions)
2097 number_input_files = argc - optind;
2099 = (const char **) xmalloc (number_input_files * sizeof (const char *));
2101 = (int *) xmalloc (number_input_files * sizeof (int));
2103 for (file_index = 0; file_index < number_input_files; file_index++)
2105 input_file_name[file_index] = argv[optind];
2106 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2107 input_file_name[0] = NULL;
2109 input_file_name[0] = argv[optind];
2116 /* There is one necessary input file. */
2118 number_input_files = 1;
2119 input_file_name = (const char **) xmalloc (sizeof (const char *));
2120 file_line_count = (int *) xmalloc (sizeof (int));
2121 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2122 input_file_name[0] = NULL;
2124 input_file_name[0] = argv[optind];
2127 /* Redirect standard output, only if requested. */
2132 if (fopen (argv[optind], "w") == NULL)
2133 error (EXIT_FAILURE, errno, argv[optind]);
2137 /* Diagnose any other argument as an error. */
2140 usage (EXIT_FAILURE);
2143 /* If the output format has not been explicitly selected, choose dumb
2144 terminal format if GNU extensions are enabled, else `roff' format. */
2146 if (output_format == UNKNOWN_FORMAT)
2147 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2149 /* Initialize the main tables. */
2151 initialize_regex ();
2153 /* Read `Break character' file, if any. */
2156 digest_break_file (break_file);
2158 /* Read `Ignore words' file and `Only words' files, if any. If any of
2159 these files is empty, reset the name of the file to NULL, to avoid
2160 unnecessary calls to search_table. */
2164 digest_word_file (ignore_file, &ignore_table);
2165 if (ignore_table.length == 0)
2171 digest_word_file (only_file, &only_table);
2172 if (only_table.length == 0)
2176 /* Prepare to study all the input files. */
2178 number_of_occurs[0] = 0;
2179 total_line_count = 0;
2180 maximum_word_length = 0;
2181 reference_max_width = 0;
2183 for (file_index = 0; file_index < number_input_files; file_index++)
2186 /* Read the file in core, than study it. */
2188 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2189 find_occurs_in_text ();
2191 /* Maintain for each file how many lines has been read so far when its
2192 end is reached. Incrementing the count first is a simple kludge to
2193 handle a possible incomplete line at end of file. */
2196 file_line_count[file_index] = total_line_count;
2199 /* Do the output process phase. */
2201 sort_found_occurs ();
2202 fix_output_parameters ();
2203 generate_all_output ();
2207 exit (EXIT_SUCCESS);