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 /* The official name of this program (e.g., no `g' prefix). */
35 #define PROGRAM_NAME "ptx"
37 #define AUTHORS "François Pinard"
39 /* Number of possible characters in a byte. */
40 #define CHAR_SET_SIZE 256
42 /* The ctype definitions should work for all 256 characters. */
46 # define isspace(C) ((C) == ' ' || (C) == '\t' || (C) == '\n')
47 # define isxdigit(C) \
48 (((unsigned char) (C) >= 'a' && (unsigned char) (C) <= 'f') \
49 || ((unsigned char) (C) >= 'A' && (unsigned char) (C) <= 'F') \
50 || ((unsigned char) (C) >= '0' && (unsigned char) (C) <= '9'))
51 # define islower(C) ((unsigned char) (C) >= 'a' && (unsigned char) (C) <= 'z')
52 # define isupper(C) ((unsigned char) (C) >= 'A' && (unsigned char) (C) <= 'Z')
53 # define isalpha(C) (islower (C) || isupper (C))
54 # define toupper(C) (islower (C) ? (C) - 'a' + 'A' : (C))
57 #if !defined (isascii) || defined (STDC_HEADERS)
63 # define ISXDIGIT(C) (isascii (C) && isxdigit (C))
65 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
66 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
67 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
68 #define OCTTOBIN(C) ((C) - '0')
70 /* Debugging the memory allocator. */
73 # define MALLOC_FUNC_CHECK 1
77 /* Global definitions. */
79 /* Reallocation step when swallowing non regular files. The value is not
80 the actual reallocation step, but its base two logarithm. */
81 #define SWALLOW_REALLOC_LOG 12
83 /* Imported from "regex.c". */
86 /* The name this program was run with. */
89 /* Program options. */
93 UNKNOWN_FORMAT, /* output format still unknown */
94 DUMB_FORMAT, /* output for a dumb terminal */
95 ROFF_FORMAT, /* output for `troff' or `nroff' */
96 TEX_FORMAT /* output for `TeX' or `LaTeX' */
99 int gnu_extensions = 1; /* trigger all GNU extensions */
100 int auto_reference = 0; /* references are `file_name:line_number:' */
101 int input_reference = 0; /* references at beginning of input lines */
102 int right_reference = 0; /* output references after right context */
103 int line_width = 72; /* output line width in characters */
104 int gap_size = 3; /* number of spaces between output fields */
105 const char *truncation_string = "/";
106 /* string used to mark line truncations */
107 const char *macro_name = "xx"; /* macro name for roff or TeX output */
108 enum Format output_format = UNKNOWN_FORMAT;
111 int ignore_case = 0; /* fold lower to upper case for sorting */
112 const char *context_regex_string = NULL;
113 /* raw regex for end of context */
114 const char *word_regex_string = NULL;
115 /* raw regex for a keyword */
116 const char *break_file = NULL; /* name of the `Break characters' file */
117 const char *only_file = NULL; /* name of the `Only words' file */
118 const char *ignore_file = NULL; /* name of the `Ignore words' file */
120 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
121 whole file. A WORD is something smaller, its length should fit in a
122 short integer. A WORD_TABLE may contain several WORDs. */
126 char *start; /* pointer to beginning of region */
127 char *end; /* pointer to end + 1 of region */
133 char *start; /* pointer to beginning of region */
134 short size; /* length of the region */
140 WORD *start; /* array of WORDs */
141 size_t length; /* number of entries */
145 /* Pattern description tables. */
147 /* For each character, provide its folded equivalent. */
148 unsigned char folded_chars[CHAR_SET_SIZE];
150 /* For each character, indicate if it is part of a word. */
151 char syntax_table[CHAR_SET_SIZE];
152 char *re_syntax_table = syntax_table;
154 /* Compiled regex for end of context. */
155 struct re_pattern_buffer *context_regex;
157 /* End of context pattern register indices. */
158 struct re_registers context_regs;
160 /* Compiled regex for a keyword. */
161 struct re_pattern_buffer *word_regex;
163 /* Keyword pattern register indices. */
164 struct re_registers word_regs;
166 /* A word characters fastmap is used only when no word regexp has been
167 provided. A word is then made up of a sequence of one or more characters
168 allowed by the fastmap. Contains !0 if character allowed in word. Not
169 only this is faster in most cases, but it simplifies the implementation
170 of the Break files. */
171 char word_fastmap[CHAR_SET_SIZE];
173 /* Maximum length of any word read. */
174 int maximum_word_length;
176 /* Maximum width of any reference used. */
177 int reference_max_width;
179 /* Ignore and Only word tables. */
181 WORD_TABLE ignore_table; /* table of words to ignore */
182 WORD_TABLE only_table; /* table of words to select */
184 #define ALLOC_NEW_WORD(table) \
185 BUMP_ALLOC ((table)->start, (table)->length, 8, WORD)
187 /* Source text table, and scanning macros. */
189 int number_input_files; /* number of text input files */
190 int total_line_count; /* total number of lines seen so far */
191 const char **input_file_name; /* array of text input file names */
192 int *file_line_count; /* array of `total_line_count' values at end */
194 BLOCK text_buffer; /* file to study */
195 char *text_buffer_maxend; /* allocated end of text_buffer */
197 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
199 #define SKIP_NON_WHITE(cursor, limit) \
200 while (cursor < limit && !isspace(*cursor)) \
203 #define SKIP_WHITE(cursor, limit) \
204 while (cursor < limit && isspace(*cursor)) \
207 #define SKIP_WHITE_BACKWARDS(cursor, start) \
208 while (cursor > start && isspace(cursor[-1])) \
211 #define SKIP_SOMETHING(cursor, limit) \
212 if (word_regex_string) \
215 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
216 cursor += count <= 0 ? 1 : count; \
218 else if (word_fastmap[(unsigned char) *cursor]) \
219 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
224 /* Occurrences table.
226 The `keyword' pointer provides the central word, which is surrounded
227 by a left context and a right context. The `keyword' and `length'
228 field allow full 8-bit characters keys, even including NULs. At other
229 places in this program, the name `keyafter' refers to the keyword
230 followed by its right context.
232 The left context does not extend, towards the beginning of the file,
233 further than a distance given by the `left' value. This value is
234 relative to the keyword beginning, it is usually negative. This
235 insures that, except for white space, we will never have to backward
236 scan the source text, when it is time to generate the final output
239 The right context, indirectly attainable through the keyword end, does
240 not extend, towards the end of the file, further than a distance given
241 by the `right' value. This value is relative to the keyword
242 beginning, it is usually positive.
244 When automatic references are used, the `reference' value is the
245 overall line number in all input files read so far, in this case, it
246 is of type (int). When input references are used, the `reference'
247 value indicates the distance between the keyword beginning and the
248 start of the reference field, it is of type (DELTA) and usually
251 typedef short DELTA; /* to hold displacement within one context */
255 WORD key; /* description of the keyword */
256 DELTA left; /* distance to left context start */
257 DELTA right; /* distance to right context end */
258 int reference; /* reference descriptor */
262 /* The various OCCURS tables are indexed by the language. But the time
263 being, there is no such multiple language support. */
265 OCCURS *occurs_table[1]; /* all words retained from the read text */
266 size_t number_of_occurs[1]; /* number of used slots in occurs_table */
268 #define ALLOC_NEW_OCCURS(language) \
269 BUMP_ALLOC (occurs_table[language], number_of_occurs[language], 9, OCCURS)
271 /* Communication among output routines. */
273 /* Indicate if special output processing is requested for each character. */
274 char edited_flag[CHAR_SET_SIZE];
276 int half_line_width; /* half of line width, reference excluded */
277 int before_max_width; /* maximum width of before field */
278 int keyafter_max_width; /* maximum width of keyword-and-after field */
279 int truncation_string_length; /* length of string used to flag truncation */
281 /* When context is limited by lines, wraparound may happen on final output:
282 the `head' pointer gives access to some supplementary left context which
283 will be seen at the end of the output line, the `tail' pointer gives
284 access to some supplementary right context which will be seen at the
285 beginning of the output line. */
287 BLOCK tail; /* tail field */
288 int tail_truncation; /* flag truncation after the tail field */
290 BLOCK before; /* before field */
291 int before_truncation; /* flag truncation before the before field */
293 BLOCK keyafter; /* keyword-and-after field */
294 int keyafter_truncation; /* flag truncation after the keyafter field */
296 BLOCK head; /* head field */
297 int head_truncation; /* flag truncation before the head field */
299 BLOCK reference; /* reference field for input reference mode */
301 /* Miscellaneous routines. */
303 /*------------------------------------------------------.
304 | Duplicate string STRING, while evaluating \-escapes. |
305 `------------------------------------------------------*/
307 /* Loosely adapted from GNU sh-utils printf.c code. */
310 copy_unescaped_string (const char *string)
312 char *result; /* allocated result */
313 char *cursor; /* cursor in result */
314 int value; /* value of \nnn escape */
315 int length; /* length of \nnn escape */
317 result = xmalloc (strlen (string) + 1);
326 case 'x': /* \xhhh escape, 3 chars maximum */
328 for (length = 0, string++;
329 length < 3 && ISXDIGIT (*string);
331 value = value * 16 + HEXTOBIN (*string);
341 case '0': /* \0ooo escape, 3 chars maximum */
343 for (length = 0, string++;
344 length < 3 && ISODIGIT (*string);
346 value = value * 8 + OCTTOBIN (*string);
350 case 'a': /* alert */
359 case 'b': /* backspace */
364 case 'c': /* cancel the rest of the output */
369 case 'f': /* form feed */
374 case 'n': /* new line */
379 case 'r': /* carriage return */
384 case 't': /* horizontal tab */
389 case 'v': /* vertical tab */
400 *cursor++ = *string++;
405 *cursor++ = *string++;
411 /*-------------------------------------------------------------------.
412 | Compile the regex represented by STRING, diagnose and abort if any |
413 | error. Returns the compiled regex structure. |
414 `-------------------------------------------------------------------*/
416 static struct re_pattern_buffer *
417 alloc_and_compile_regex (const char *string)
419 struct re_pattern_buffer *pattern; /* newly allocated structure */
420 const char *message; /* error message returned by regex.c */
422 pattern = (struct re_pattern_buffer *)
423 xmalloc (sizeof (struct re_pattern_buffer));
424 memset (pattern, 0, sizeof (struct re_pattern_buffer));
426 pattern->buffer = NULL;
427 pattern->allocated = 0;
428 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
429 pattern->fastmap = (char *) xmalloc ((size_t) CHAR_SET_SIZE);
431 message = re_compile_pattern (string, (int) strlen (string), pattern);
433 error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);
435 /* The fastmap should be compiled before `re_match'. The following
436 call is not mandatory, because `re_search' is always called sooner,
437 and it compiles the fastmap if this has not been done yet. */
439 re_compile_fastmap (pattern);
441 /* Do not waste extra allocated space. */
443 if (pattern->allocated > pattern->used)
446 = (unsigned char *) xrealloc (pattern->buffer, (size_t) pattern->used);
447 pattern->allocated = pattern->used;
453 /*------------------------------------------------------------------------.
454 | This will initialize various tables for pattern match and compiles some |
456 `------------------------------------------------------------------------*/
459 initialize_regex (void)
461 int character; /* character value */
463 /* Initialize the regex syntax table. */
465 for (character = 0; character < CHAR_SET_SIZE; character++)
466 syntax_table[character] = isalpha (character) ? Sword : 0;
468 /* Initialize the case folding table. */
471 for (character = 0; character < CHAR_SET_SIZE; character++)
472 folded_chars[character] = toupper (character);
474 /* Unless the user already provided a description of the end of line or
475 end of sentence sequence, select an end of line sequence to compile.
476 If the user provided an empty definition, thus disabling end of line
477 or sentence feature, make it NULL to speed up tests. If GNU
478 extensions are enabled, use end of sentence like in GNU emacs. If
479 disabled, use end of lines. */
481 if (context_regex_string)
483 if (!*context_regex_string)
484 context_regex_string = NULL;
486 else if (gnu_extensions && !input_reference)
487 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
489 context_regex_string = "\n";
491 if (context_regex_string)
492 context_regex = alloc_and_compile_regex (context_regex_string);
494 /* If the user has already provided a non-empty regexp to describe
495 words, compile it. Else, unless this has already been done through
496 a user provided Break character file, construct a fastmap of
497 characters that may appear in a word. If GNU extensions enabled,
498 include only letters of the underlying character set. If disabled,
499 include almost everything, even punctuations; stop only on white
502 if (word_regex_string && *word_regex_string)
503 word_regex = alloc_and_compile_regex (word_regex_string);
504 else if (!break_file)
511 for (character = 0; character < CHAR_SET_SIZE; character++)
512 word_fastmap[character] = isalpha (character) ? 1 : 0;
517 /* Simulate [^ \t\n]+. */
519 memset (word_fastmap, 1, CHAR_SET_SIZE);
520 word_fastmap[' '] = 0;
521 word_fastmap['\t'] = 0;
522 word_fastmap['\n'] = 0;
527 /*------------------------------------------------------------------------.
528 | This routine will attempt to swallow a whole file name FILE_NAME into a |
529 | contiguous region of memory and return a description of it into BLOCK. |
530 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
532 | Previously, in some cases, white space compression was attempted while |
533 | inputting text. This was defeating some regexps like default end of |
534 | sentence, which checks for two consecutive spaces. If white space |
535 | compression is ever reinstated, it should be in output routines. |
536 `------------------------------------------------------------------------*/
539 swallow_file_in_memory (const char *file_name, BLOCK *block)
541 int file_handle; /* file descriptor number */
542 struct stat stat_block; /* stat block for file */
543 size_t allocated_length; /* allocated length of memory buffer */
544 size_t used_length; /* used length in memory buffer */
545 int read_length; /* number of character gotten on last read */
547 /* As special cases, a file name which is NULL or "-" indicates standard
548 input, which is already opened. In all other cases, open the file from
551 if (!file_name || !*file_name || strcmp (file_name, "-") == 0)
552 file_handle = fileno (stdin);
554 if ((file_handle = open (file_name, O_RDONLY)) < 0)
555 error (EXIT_FAILURE, errno, "%s", file_name);
557 /* If the file is a plain, regular file, allocate the memory buffer all at
558 once and swallow the file in one blow. In other cases, read the file
559 repeatedly in smaller chunks until we have it all, reallocating memory
560 once in a while, as we go. */
562 if (fstat (file_handle, &stat_block) < 0)
563 error (EXIT_FAILURE, errno, "%s", file_name);
565 if (S_ISREG (stat_block.st_mode))
567 size_t in_memory_size;
569 block->start = (char *) xmalloc ((size_t) stat_block.st_size);
571 if ((in_memory_size = read (file_handle,
572 block->start, (size_t) stat_block.st_size))
573 != stat_block.st_size)
576 /* On MSDOS, in memory size may be smaller than the file
577 size, because of end of line conversions. But it can
578 never be smaller than half the file size, because the
579 minimum is when all lines are empty and terminated by
581 if (in_memory_size != (size_t)-1
582 && in_memory_size >= stat_block.st_size / 2)
583 block->start = (char *) xrealloc (block->start, in_memory_size);
585 #endif /* not MSDOS */
587 error (EXIT_FAILURE, errno, "%s", file_name);
589 block->end = block->start + in_memory_size;
593 block->start = (char *) xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
595 allocated_length = (1 << SWALLOW_REALLOC_LOG);
597 while (read_length = read (file_handle,
598 block->start + used_length,
599 allocated_length - used_length),
602 used_length += read_length;
603 if (used_length == allocated_length)
605 allocated_length += (1 << SWALLOW_REALLOC_LOG);
607 = (char *) xrealloc (block->start, allocated_length);
612 error (EXIT_FAILURE, errno, "%s", file_name);
614 block->end = block->start + used_length;
617 /* Close the file, but only if it was not the standard input. */
619 if (file_handle != fileno (stdin))
623 /* Sort and search routines. */
625 /*--------------------------------------------------------------------------.
626 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
627 | Return less than 0 if the first word goes before the second; return |
628 | greater than 0 if the first word goes after the second. |
630 | If a word is indeed a prefix of the other, the shorter should go first. |
631 `--------------------------------------------------------------------------*/
634 compare_words (const void *void_first, const void *void_second)
636 #define first ((const WORD *) void_first)
637 #define second ((const WORD *) void_second)
638 int length; /* minimum of two lengths */
639 int counter; /* cursor in words */
640 int value; /* value of comparison */
642 length = first->size < second->size ? first->size : second->size;
646 for (counter = 0; counter < length; counter++)
648 value = (folded_chars [(unsigned char) (first->start[counter])]
649 - folded_chars [(unsigned char) (second->start[counter])]);
656 for (counter = 0; counter < length; counter++)
658 value = ((unsigned char) first->start[counter]
659 - (unsigned char) second->start[counter]);
665 return first->size - second->size;
670 /*-----------------------------------------------------------------------.
671 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
672 | go first. In case of a tie, preserve the original order through a |
673 | pointer comparison. |
674 `-----------------------------------------------------------------------*/
677 compare_occurs (const void *void_first, const void *void_second)
679 #define first ((const OCCURS *) void_first)
680 #define second ((const OCCURS *) void_second)
683 value = compare_words (&first->key, &second->key);
684 return value == 0 ? first->key.start - second->key.start : value;
689 /*------------------------------------------------------------.
690 | Return !0 if WORD appears in TABLE. Uses a binary search. |
691 `------------------------------------------------------------*/
694 search_table (WORD *word, WORD_TABLE *table)
696 int lowest; /* current lowest possible index */
697 int highest; /* current highest possible index */
698 int middle; /* current middle index */
699 int value; /* value from last comparison */
702 highest = table->length - 1;
703 while (lowest <= highest)
705 middle = (lowest + highest) / 2;
706 value = compare_words (word, table->start + middle);
708 highest = middle - 1;
717 /*---------------------------------------------------------------------.
718 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
719 | take intermediate copies or table elements, so the sort will be |
720 | stabilized throughout the comparison routine. |
721 `---------------------------------------------------------------------*/
724 sort_found_occurs (void)
727 /* Only one language for the time being. */
729 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
733 /* Parameter files reading routines. */
735 /*----------------------------------------------------------------------.
736 | Read a file named FILE_NAME, containing a set of break characters. |
737 | Build a content to the array word_fastmap in which all characters are |
738 | allowed except those found in the file. Characters may be repeated. |
739 `----------------------------------------------------------------------*/
742 digest_break_file (const char *file_name)
744 BLOCK file_contents; /* to receive a copy of the file */
745 char *cursor; /* cursor in file copy */
747 swallow_file_in_memory (file_name, &file_contents);
749 /* Make the fastmap and record the file contents in it. */
751 memset (word_fastmap, 1, CHAR_SET_SIZE);
752 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
753 word_fastmap[(unsigned char) *cursor] = 0;
758 /* If GNU extensions are enabled, the only way to avoid newline as
759 a break character is to write all the break characters in the
760 file with no newline at all, not even at the end of the file.
761 If disabled, spaces, tabs and newlines are always considered as
762 break characters even if not included in the break file. */
764 word_fastmap[' '] = 0;
765 word_fastmap['\t'] = 0;
766 word_fastmap['\n'] = 0;
769 /* Return the space of the file, which is no more required. */
771 free (file_contents.start);
774 /*-----------------------------------------------------------------------.
775 | Read a file named FILE_NAME, containing one word per line, then |
776 | construct in TABLE a table of WORD descriptors for them. The routine |
777 | swallows the whole file in memory; this is at the expense of space |
778 | needed for newlines, which are useless; however, the reading is fast. |
779 `-----------------------------------------------------------------------*/
782 digest_word_file (const char *file_name, WORD_TABLE *table)
784 BLOCK file_contents; /* to receive a copy of the file */
785 char *cursor; /* cursor in file copy */
786 char *word_start; /* start of the current word */
788 swallow_file_in_memory (file_name, &file_contents);
793 /* Read the whole file. */
795 cursor = file_contents.start;
796 while (cursor < file_contents.end)
799 /* Read one line, and save the word in contains. */
802 while (cursor < file_contents.end && *cursor != '\n')
805 /* Record the word in table if it is not empty. */
807 if (cursor > word_start)
809 ALLOC_NEW_WORD (table);
810 table->start[table->length].start = word_start;
811 table->start[table->length].size = cursor - word_start;
815 /* This test allows for an incomplete line at end of file. */
817 if (cursor < file_contents.end)
821 /* Finally, sort all the words read. */
823 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
826 /* Keyword recognition and selection. */
828 /*----------------------------------------------------------------------.
829 | For each keyword in the source text, constructs an OCCURS structure. |
830 `----------------------------------------------------------------------*/
833 find_occurs_in_text (void)
835 char *cursor; /* for scanning the source text */
836 char *scan; /* for scanning the source text also */
837 char *line_start; /* start of the current input line */
838 char *line_scan; /* newlines scanned until this point */
839 int reference_length; /* length of reference in input mode */
840 WORD possible_key; /* possible key, to ease searches */
841 OCCURS *occurs_cursor; /* current OCCURS under construction */
843 char *context_start; /* start of left context */
844 char *context_end; /* end of right context */
845 char *word_start; /* start of word */
846 char *word_end; /* end of word */
847 char *next_context_start; /* next start of left context */
849 /* reference_length is always used within `if (input_reference)'.
850 However, GNU C diagnoses that it may be used uninitialized. The
851 following assignment is merely to shut it up. */
853 reference_length = 0;
855 /* Tracking where lines start is helpful for reference processing. In
856 auto reference mode, this allows counting lines. In input reference
857 mode, this permits finding the beginning of the references.
859 The first line begins with the file, skip immediately this very first
860 reference in input reference mode, to help further rejection any word
861 found inside it. Also, unconditionally assigning these variable has
862 the happy effect of shutting up lint. */
864 line_start = text_buffer.start;
865 line_scan = line_start;
868 SKIP_NON_WHITE (line_scan, text_buffer.end);
869 reference_length = line_scan - line_start;
870 SKIP_WHITE (line_scan, text_buffer.end);
873 /* Process the whole buffer, one line or one sentence at a time. */
875 for (cursor = text_buffer.start;
876 cursor < text_buffer.end;
877 cursor = next_context_start)
880 /* `context_start' gets initialized before the processing of each
881 line, or once for the whole buffer if no end of line or sentence
882 sequence separator. */
884 context_start = cursor;
886 /* If a end of line or end of sentence sequence is defined and
887 non-empty, `next_context_start' will be recomputed to be the end of
888 each line or sentence, before each one is processed. If no such
889 sequence, then `next_context_start' is set at the end of the whole
890 buffer, which is then considered to be a single line or sentence.
891 This test also accounts for the case of an incomplete line or
892 sentence at the end of the buffer. */
894 if (context_regex_string
895 && (re_search (context_regex, cursor, text_buffer.end - cursor,
896 0, text_buffer.end - cursor, &context_regs)
898 next_context_start = cursor + context_regs.end[0];
901 next_context_start = text_buffer.end;
903 /* Include the separator into the right context, but not any suffix
904 white space in this separator; this insures it will be seen in
905 output and will not take more space than necessary. */
907 context_end = next_context_start;
908 SKIP_WHITE_BACKWARDS (context_end, context_start);
910 /* Read and process a single input line or sentence, one word at a
917 /* If a word regexp has been compiled, use it to skip at the
918 beginning of the next word. If there is no such word, exit
922 if (re_search (word_regex, cursor, context_end - cursor,
923 0, context_end - cursor, &word_regs)
926 word_start = cursor + word_regs.start[0];
927 word_end = cursor + word_regs.end[0];
931 /* Avoid re_search and use the fastmap to skip to the
932 beginning of the next word. If there is no more word in
933 the buffer, exit the loop. */
937 while (scan < context_end
938 && !word_fastmap[(unsigned char) *scan])
941 if (scan == context_end)
946 while (scan < context_end
947 && word_fastmap[(unsigned char) *scan])
953 /* Skip right to the beginning of the found word. */
957 /* Skip any zero length word. Just advance a single position,
958 then go fetch the next word. */
960 if (word_end == word_start)
966 /* This is a genuine, non empty word, so save it as a possible
967 key. Then skip over it. Also, maintain the maximum length of
968 all words read so far. It is mandatory to take the maximum
969 length of all words in the file, without considering if they
970 are actually kept or rejected, because backward jumps at output
971 generation time may fall in *any* word. */
973 possible_key.start = cursor;
974 possible_key.size = word_end - word_start;
975 cursor += possible_key.size;
977 if (possible_key.size > maximum_word_length)
978 maximum_word_length = possible_key.size;
980 /* In input reference mode, update `line_start' from its previous
981 value. Count the lines just in case auto reference mode is
982 also selected. If it happens that the word just matched is
983 indeed part of a reference; just ignore it. */
987 while (line_scan < possible_key.start)
988 if (*line_scan == '\n')
992 line_start = line_scan;
993 SKIP_NON_WHITE (line_scan, text_buffer.end);
994 reference_length = line_scan - line_start;
998 if (line_scan > possible_key.start)
1002 /* Ignore the word if an `Ignore words' table exists and if it is
1003 part of it. Also ignore the word if an `Only words' table and
1004 if it is *not* part of it.
1006 It is allowed that both tables be used at once, even if this
1007 may look strange for now. Just ignore a word that would appear
1008 in both. If regexps are eventually implemented for these
1009 tables, the Ignore table could then reject words that would
1010 have been previously accepted by the Only table. */
1012 if (ignore_file && search_table (&possible_key, &ignore_table))
1014 if (only_file && !search_table (&possible_key, &only_table))
1017 /* A non-empty word has been found. First of all, insure
1018 proper allocation of the next OCCURS, and make a pointer to
1019 where it will be constructed. */
1021 ALLOC_NEW_OCCURS (0);
1022 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1024 /* Define the refence field, if any. */
1029 /* While auto referencing, update `line_start' from its
1030 previous value, counting lines as we go. If input
1031 referencing at the same time, `line_start' has been
1032 advanced earlier, and the following loop is never really
1035 while (line_scan < possible_key.start)
1036 if (*line_scan == '\n')
1040 line_start = line_scan;
1041 SKIP_NON_WHITE (line_scan, text_buffer.end);
1046 occurs_cursor->reference = total_line_count;
1048 else if (input_reference)
1051 /* If only input referencing, `line_start' has been computed
1052 earlier to detect the case the word matched would be part
1053 of the reference. The reference position is simply the
1054 value of `line_start'. */
1056 occurs_cursor->reference
1057 = (DELTA) (line_start - possible_key.start);
1058 if (reference_length > reference_max_width)
1059 reference_max_width = reference_length;
1062 /* Exclude the reference from the context in simple cases. */
1064 if (input_reference && line_start == context_start)
1066 SKIP_NON_WHITE (context_start, context_end);
1067 SKIP_WHITE (context_start, context_end);
1070 /* Completes the OCCURS structure. */
1072 occurs_cursor->key = possible_key;
1073 occurs_cursor->left = context_start - possible_key.start;
1074 occurs_cursor->right = context_end - possible_key.start;
1076 number_of_occurs[0]++;
1081 /* Formatting and actual output - service routines. */
1083 /*-----------------------------------------.
1084 | Prints some NUMBER of spaces on stdout. |
1085 `-----------------------------------------*/
1088 print_spaces (int number)
1092 for (counter = number; counter > 0; counter--)
1096 /*-------------------------------------.
1097 | Prints the field provided by FIELD. |
1098 `-------------------------------------*/
1101 print_field (BLOCK field)
1103 char *cursor; /* Cursor in field to print */
1104 int character; /* Current character */
1105 int base; /* Base character, without diacritic */
1106 int diacritic; /* Diacritic code for the character */
1108 /* Whitespace is not really compressed. Instead, each white space
1109 character (tab, vt, ht etc.) is printed as one single space. */
1111 for (cursor = field.start; cursor < field.end; cursor++)
1113 character = (unsigned char) *cursor;
1114 if (edited_flag[character])
1117 /* First check if this is a diacriticized character.
1119 This works only for TeX. I do not know how diacriticized
1120 letters work with `roff'. Please someone explain it to me! */
1122 diacritic = todiac (character);
1123 if (diacritic != 0 && output_format == TEX_FORMAT)
1125 base = tobase (character);
1129 case 1: /* Latin diphthongs */
1133 fputs ("\\oe{}", stdout);
1137 fputs ("\\OE{}", stdout);
1141 fputs ("\\ae{}", stdout);
1145 fputs ("\\AE{}", stdout);
1153 case 2: /* Acute accent */
1154 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1157 case 3: /* Grave accent */
1158 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1161 case 4: /* Circumflex accent */
1162 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1165 case 5: /* Diaeresis */
1166 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1169 case 6: /* Tilde accent */
1170 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1173 case 7: /* Cedilla */
1174 printf ("\\c{%c}", base);
1177 case 8: /* Small circle beneath */
1181 fputs ("\\aa{}", stdout);
1185 fputs ("\\AA{}", stdout);
1193 case 9: /* Strike through */
1197 fputs ("\\o{}", stdout);
1201 fputs ("\\O{}", stdout);
1212 /* This is not a diacritic character, so handle cases which are
1213 really specific to `roff' or TeX. All white space processing
1214 is done as the default case of this switch. */
1219 /* In roff output format, double any quote. */
1229 /* In TeX output format, precede these with a backslash. */
1231 putchar (character);
1236 /* In TeX output format, precede these with a backslash and
1237 force mathematical mode. */
1238 printf ("$\\%c$", character);
1242 /* In TeX output mode, request production of a backslash. */
1243 fputs ("\\backslash{}", stdout);
1247 /* Any other flagged character produces a single space. */
1256 /* Formatting and actual output - planning routines. */
1258 /*--------------------------------------------------------------------.
1259 | From information collected from command line options and input file |
1260 | readings, compute and fix some output parameter values. |
1261 `--------------------------------------------------------------------*/
1264 fix_output_parameters (void)
1266 int file_index; /* index in text input file arrays */
1267 int line_ordinal; /* line ordinal value for reference */
1268 char ordinal_string[12]; /* edited line ordinal for reference */
1269 int reference_width; /* width for the whole reference */
1270 int character; /* character ordinal */
1271 const char *cursor; /* cursor in some constant strings */
1273 /* In auto reference mode, the maximum width of this field is
1274 precomputed and subtracted from the overall line width. Add one for
1275 the column which separate the file name from the line number. */
1279 reference_max_width = 0;
1280 for (file_index = 0; file_index < number_input_files; file_index++)
1282 line_ordinal = file_line_count[file_index] + 1;
1284 line_ordinal -= file_line_count[file_index - 1];
1285 sprintf (ordinal_string, "%d", line_ordinal);
1286 reference_width = strlen (ordinal_string);
1287 if (input_file_name[file_index])
1288 reference_width += strlen (input_file_name[file_index]);
1289 if (reference_width > reference_max_width)
1290 reference_max_width = reference_width;
1292 reference_max_width++;
1293 reference.start = (char *) xmalloc ((size_t) reference_max_width + 1);
1296 /* If the reference appears to the left of the output line, reserve some
1297 space for it right away, including one gap size. */
1299 if ((auto_reference || input_reference) && !right_reference)
1300 line_width -= reference_max_width + gap_size;
1302 /* The output lines, minimally, will contain from left to right a left
1303 context, a gap, and a keyword followed by the right context with no
1304 special intervening gap. Half of the line width is dedicated to the
1305 left context and the gap, the other half is dedicated to the keyword
1306 and the right context; these values are computed once and for all here.
1307 There also are tail and head wrap around fields, used when the keyword
1308 is near the beginning or the end of the line, or when some long word
1309 cannot fit in, but leave place from wrapped around shorter words. The
1310 maximum width of these fields are recomputed separately for each line,
1311 on a case by case basis. It is worth noting that it cannot happen that
1312 both the tail and head fields are used at once. */
1314 half_line_width = line_width / 2;
1315 before_max_width = half_line_width - gap_size;
1316 keyafter_max_width = half_line_width;
1318 /* If truncation_string is the empty string, make it NULL to speed up
1319 tests. In this case, truncation_string_length will never get used, so
1320 there is no need to set it. */
1322 if (truncation_string && *truncation_string)
1323 truncation_string_length = strlen (truncation_string);
1325 truncation_string = NULL;
1330 /* When flagging truncation at the left of the keyword, the
1331 truncation mark goes at the beginning of the before field,
1332 unless there is a head field, in which case the mark goes at the
1333 left of the head field. When flagging truncation at the right
1334 of the keyword, the mark goes at the end of the keyafter field,
1335 unless there is a tail field, in which case the mark goes at the
1336 end of the tail field. Only eight combination cases could arise
1337 for truncation marks:
1340 . One beginning the before field.
1341 . One beginning the head field.
1342 . One ending the keyafter field.
1343 . One ending the tail field.
1344 . One beginning the before field, another ending the keyafter field.
1345 . One ending the tail field, another beginning the before field.
1346 . One ending the keyafter field, another beginning the head field.
1348 So, there is at most two truncation marks, which could appear both
1349 on the left side of the center of the output line, both on the
1350 right side, or one on either side. */
1352 before_max_width -= 2 * truncation_string_length;
1353 keyafter_max_width -= 2 * truncation_string_length;
1358 /* I never figured out exactly how UNIX' ptx plans the output width
1359 of its various fields. If GNU extensions are disabled, do not
1360 try computing the field widths correctly; instead, use the
1361 following formula, which does not completely imitate UNIX' ptx,
1364 keyafter_max_width -= 2 * truncation_string_length + 1;
1367 /* Compute which characters need special output processing. Initialize
1368 by flagging any white space character. Some systems do not consider
1369 form feed as a space character, but we do. */
1371 for (character = 0; character < CHAR_SET_SIZE; character++)
1372 edited_flag[character] = isspace (character) != 0;
1373 edited_flag['\f'] = 1;
1375 /* Complete the special character flagging according to selected output
1378 switch (output_format)
1380 case UNKNOWN_FORMAT:
1381 /* Should never happen. */
1388 /* `Quote' characters should be doubled. */
1390 edited_flag['"'] = 1;
1395 /* Various characters need special processing. */
1397 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1398 edited_flag[(unsigned char) *cursor] = 1;
1400 /* Any character with 8th bit set will print to a single space, unless
1401 it is diacriticized. */
1403 for (character = 0200; character < CHAR_SET_SIZE; character++)
1404 edited_flag[character] = todiac (character) != 0;
1409 /*------------------------------------------------------------------.
1410 | Compute the position and length of all the output fields, given a |
1411 | pointer to some OCCURS. |
1412 `------------------------------------------------------------------*/
1415 define_all_fields (OCCURS *occurs)
1417 int tail_max_width; /* allowable width of tail field */
1418 int head_max_width; /* allowable width of head field */
1419 char *cursor; /* running cursor in source text */
1420 char *left_context_start; /* start of left context */
1421 char *right_context_end; /* end of right context */
1422 char *left_field_start; /* conservative start for `head'/`before' */
1423 int file_index; /* index in text input file arrays */
1424 const char *file_name; /* file name for reference */
1425 int line_ordinal; /* line ordinal for reference */
1427 /* Define `keyafter', start of left context and end of right context.
1428 `keyafter' starts at the saved position for keyword and extend to the
1429 right from the end of the keyword, eating separators or full words, but
1430 not beyond maximum allowed width for `keyafter' field or limit for the
1431 right context. Suffix spaces will be removed afterwards. */
1433 keyafter.start = occurs->key.start;
1434 keyafter.end = keyafter.start + occurs->key.size;
1435 left_context_start = keyafter.start + occurs->left;
1436 right_context_end = keyafter.start + occurs->right;
1438 cursor = keyafter.end;
1439 while (cursor < right_context_end
1440 && cursor <= keyafter.start + keyafter_max_width)
1442 keyafter.end = cursor;
1443 SKIP_SOMETHING (cursor, right_context_end);
1445 if (cursor <= keyafter.start + keyafter_max_width)
1446 keyafter.end = cursor;
1448 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1450 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1452 /* When the left context is wide, it might take some time to catch up from
1453 the left context boundary to the beginning of the `head' or `before'
1454 fields. So, in this case, to speed the catchup, we jump back from the
1455 keyword, using some secure distance, possibly falling in the middle of
1456 a word. A secure backward jump would be at least half the maximum
1457 width of a line, plus the size of the longest word met in the whole
1458 input. We conclude this backward jump by a skip forward of at least
1459 one word. In this manner, we should not inadvertently accept only part
1460 of a word. From the reached point, when it will be time to fix the
1461 beginning of `head' or `before' fields, we will skip forward words or
1462 delimiters until we get sufficiently near. */
1464 if (-occurs->left > half_line_width + maximum_word_length)
1467 = keyafter.start - (half_line_width + maximum_word_length);
1468 SKIP_SOMETHING (left_field_start, keyafter.start);
1471 left_field_start = keyafter.start + occurs->left;
1473 /* `before' certainly ends at the keyword, but not including separating
1474 spaces. It starts after than the saved value for the left context, by
1475 advancing it until it falls inside the maximum allowed width for the
1476 before field. There will be no prefix spaces either. `before' only
1477 advances by skipping single separators or whole words. */
1479 before.start = left_field_start;
1480 before.end = keyafter.start;
1481 SKIP_WHITE_BACKWARDS (before.end, before.start);
1483 while (before.start + before_max_width < before.end)
1484 SKIP_SOMETHING (before.start, before.end);
1486 if (truncation_string)
1488 cursor = before.start;
1489 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1490 before_truncation = cursor > left_context_start;
1493 before_truncation = 0;
1495 SKIP_WHITE (before.start, text_buffer.end);
1497 /* The tail could not take more columns than what has been left in the
1498 left context field, and a gap is mandatory. It starts after the
1499 right context, and does not contain prefixed spaces. It ends at
1500 the end of line, the end of buffer or when the tail field is full,
1501 whichever comes first. It cannot contain only part of a word, and
1502 has no suffixed spaces. */
1505 = before_max_width - (before.end - before.start) - gap_size;
1507 if (tail_max_width > 0)
1509 tail.start = keyafter.end;
1510 SKIP_WHITE (tail.start, text_buffer.end);
1512 tail.end = tail.start;
1514 while (cursor < right_context_end
1515 && cursor < tail.start + tail_max_width)
1518 SKIP_SOMETHING (cursor, right_context_end);
1521 if (cursor < tail.start + tail_max_width)
1524 if (tail.end > tail.start)
1526 keyafter_truncation = 0;
1527 tail_truncation = truncation_string && tail.end < right_context_end;
1530 tail_truncation = 0;
1532 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1537 /* No place left for a tail field. */
1541 tail_truncation = 0;
1544 /* `head' could not take more columns than what has been left in the right
1545 context field, and a gap is mandatory. It ends before the left
1546 context, and does not contain suffixed spaces. Its pointer is advanced
1547 until the head field has shrunk to its allowed width. It cannot
1548 contain only part of a word, and has no suffixed spaces. */
1551 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1553 if (head_max_width > 0)
1555 head.end = before.start;
1556 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1558 head.start = left_field_start;
1559 while (head.start + head_max_width < head.end)
1560 SKIP_SOMETHING (head.start, head.end);
1562 if (head.end > head.start)
1564 before_truncation = 0;
1565 head_truncation = (truncation_string
1566 && head.start > left_context_start);
1569 head_truncation = 0;
1571 SKIP_WHITE (head.start, head.end);
1576 /* No place left for a head field. */
1580 head_truncation = 0;
1586 /* Construct the reference text in preallocated space from the file
1587 name and the line number. Find out in which file the reference
1588 occurred. Standard input yields an empty file name. Insure line
1589 numbers are one based, even if they are computed zero based. */
1592 while (file_line_count[file_index] < occurs->reference)
1595 file_name = input_file_name[file_index];
1599 line_ordinal = occurs->reference + 1;
1601 line_ordinal -= file_line_count[file_index - 1];
1603 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1604 reference.end = reference.start + strlen (reference.start);
1606 else if (input_reference)
1609 /* Reference starts at saved position for reference and extends right
1610 until some white space is met. */
1612 reference.start = keyafter.start + (DELTA) occurs->reference;
1613 reference.end = reference.start;
1614 SKIP_NON_WHITE (reference.end, right_context_end);
1618 /* Formatting and actual output - control routines. */
1620 /*----------------------------------------------------------------------.
1621 | Output the current output fields as one line for `troff' or `nroff'. |
1622 `----------------------------------------------------------------------*/
1625 output_one_roff_line (void)
1627 /* Output the `tail' field. */
1629 printf (".%s \"", macro_name);
1631 if (tail_truncation)
1632 fputs (truncation_string, stdout);
1635 /* Output the `before' field. */
1637 fputs (" \"", stdout);
1638 if (before_truncation)
1639 fputs (truncation_string, stdout);
1640 print_field (before);
1643 /* Output the `keyafter' field. */
1645 fputs (" \"", stdout);
1646 print_field (keyafter);
1647 if (keyafter_truncation)
1648 fputs (truncation_string, stdout);
1651 /* Output the `head' field. */
1653 fputs (" \"", stdout);
1654 if (head_truncation)
1655 fputs (truncation_string, stdout);
1659 /* Conditionally output the `reference' field. */
1661 if (auto_reference || input_reference)
1663 fputs (" \"", stdout);
1664 print_field (reference);
1671 /*---------------------------------------------------------.
1672 | Output the current output fields as one line for `TeX'. |
1673 `---------------------------------------------------------*/
1676 output_one_tex_line (void)
1678 BLOCK key; /* key field, isolated */
1679 BLOCK after; /* after field, isolated */
1680 char *cursor; /* running cursor in source text */
1682 printf ("\\%s ", macro_name);
1683 fputs ("{", stdout);
1685 fputs ("}{", stdout);
1686 print_field (before);
1687 fputs ("}{", stdout);
1688 key.start = keyafter.start;
1689 after.end = keyafter.end;
1690 cursor = keyafter.start;
1691 SKIP_SOMETHING (cursor, keyafter.end);
1693 after.start = cursor;
1695 fputs ("}{", stdout);
1696 print_field (after);
1697 fputs ("}{", stdout);
1699 fputs ("}", stdout);
1700 if (auto_reference || input_reference)
1702 fputs ("{", stdout);
1703 print_field (reference);
1704 fputs ("}", stdout);
1706 fputs ("\n", stdout);
1709 /*-------------------------------------------------------------------.
1710 | Output the current output fields as one line for a dumb terminal. |
1711 `-------------------------------------------------------------------*/
1714 output_one_dumb_line (void)
1716 if (!right_reference)
1721 /* Output the `reference' field, in such a way that GNU emacs
1722 next-error will handle it. The ending colon is taken from the
1723 gap which follows. */
1725 print_field (reference);
1727 print_spaces (reference_max_width
1729 - (reference.end - reference.start)
1735 /* Output the `reference' field and its following gap. */
1737 print_field (reference);
1738 print_spaces (reference_max_width
1740 - (reference.end - reference.start));
1744 if (tail.start < tail.end)
1746 /* Output the `tail' field. */
1749 if (tail_truncation)
1750 fputs (truncation_string, stdout);
1752 print_spaces (half_line_width - gap_size
1753 - (before.end - before.start)
1754 - (before_truncation ? truncation_string_length : 0)
1755 - (tail.end - tail.start)
1756 - (tail_truncation ? truncation_string_length : 0));
1759 print_spaces (half_line_width - gap_size
1760 - (before.end - before.start)
1761 - (before_truncation ? truncation_string_length : 0));
1763 /* Output the `before' field. */
1765 if (before_truncation)
1766 fputs (truncation_string, stdout);
1767 print_field (before);
1769 print_spaces (gap_size);
1771 /* Output the `keyafter' field. */
1773 print_field (keyafter);
1774 if (keyafter_truncation)
1775 fputs (truncation_string, stdout);
1777 if (head.start < head.end)
1779 /* Output the `head' field. */
1781 print_spaces (half_line_width
1782 - (keyafter.end - keyafter.start)
1783 - (keyafter_truncation ? truncation_string_length : 0)
1784 - (head.end - head.start)
1785 - (head_truncation ? truncation_string_length : 0));
1786 if (head_truncation)
1787 fputs (truncation_string, stdout);
1792 if ((auto_reference || input_reference) && right_reference)
1793 print_spaces (half_line_width
1794 - (keyafter.end - keyafter.start)
1795 - (keyafter_truncation ? truncation_string_length : 0));
1797 if ((auto_reference || input_reference) && right_reference)
1799 /* Output the `reference' field. */
1801 print_spaces (gap_size);
1802 print_field (reference);
1805 fputs ("\n", stdout);
1808 /*------------------------------------------------------------------------.
1809 | Scan the whole occurs table and, for each entry, output one line in the |
1810 | appropriate format. |
1811 `------------------------------------------------------------------------*/
1814 generate_all_output (void)
1816 int occurs_index; /* index of keyword entry being processed */
1817 OCCURS *occurs_cursor; /* current keyword entry being processed */
1819 /* The following assignments are useful to provide default values in case
1820 line contexts or references are not used, in which case these variables
1821 would never be computed. */
1825 tail_truncation = 0;
1829 head_truncation = 0;
1831 /* Loop over all keyword occurrences. */
1833 occurs_cursor = occurs_table[0];
1835 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1837 /* Compute the exact size of every field and whenever truncation flags
1838 are present or not. */
1840 define_all_fields (occurs_cursor);
1842 /* Produce one output line according to selected format. */
1844 switch (output_format)
1846 case UNKNOWN_FORMAT:
1847 /* Should never happen. */
1850 output_one_dumb_line ();
1854 output_one_roff_line ();
1858 output_one_tex_line ();
1862 /* Advance the cursor into the occurs table. */
1868 /* Option decoding and main program. */
1870 /*------------------------------------------------------.
1871 | Print program identification and options, then exit. |
1872 `------------------------------------------------------*/
1877 if (status != EXIT_SUCCESS)
1878 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1883 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1884 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1885 program_name, program_name);
1887 Mandatory arguments to long options are mandatory for short options too.\n\
1889 -A, --auto-reference output automatically generated references\n\
1890 -C, --copyright display Copyright and copying conditions\n\
1891 -G, --traditional behave more like System V `ptx'\n\
1892 -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\
1898 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1899 -b, --break-file=FILE word break characters in this FILE\n\
1900 -f, --ignore-case fold lower case to upper case for sorting\n\
1901 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1902 -i, --ignore-file=FILE read ignore word list from FILE\n\
1903 -o, --only-file=FILE read only word list from this FILE\n\
1904 -r, --references first field of each line is a reference\n\
1905 -t, --typeset-mode - not implemented -\n\
1906 -w, --width=NUMBER output width in columns, reference excluded\n\
1907 --help display this help and exit\n\
1908 --version output version information and exit\n\
1910 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n"),
1916 /*----------------------------------------------------------------------.
1917 | Main program. Decode ARGC arguments passed through the ARGV array of |
1918 | strings, then launch execution. |
1919 `----------------------------------------------------------------------*/
1921 /* Long options equivalences. */
1922 static const struct option long_options[] =
1924 {"auto-reference", no_argument, NULL, 'A'},
1925 {"break-file", required_argument, NULL, 'b'},
1926 {"copyright", no_argument, NULL, 'C'},
1927 {"flag-truncation", required_argument, NULL, 'F'},
1928 {"ignore-case", no_argument, NULL, 'f'},
1929 {"gap-size", required_argument, NULL, 'g'},
1930 {"ignore-file", required_argument, NULL, 'i'},
1931 {"macro-name", required_argument, NULL, 'M'},
1932 {"only-file", required_argument, NULL, 'o'},
1933 {"references", no_argument, NULL, 'r'},
1934 {"right-side-refs", no_argument, NULL, 'R'},
1935 {"format", required_argument, NULL, 10},
1936 {"sentence-regexp", required_argument, NULL, 'S'},
1937 {"traditional", no_argument, NULL, 'G'},
1938 {"typeset-mode", no_argument, NULL, 't'},
1939 {"width", required_argument, NULL, 'w'},
1940 {"word-regexp", required_argument, NULL, 'W'},
1944 static char const* const format_args[] =
1949 static enum Format const format_vals[] =
1951 ROFF_FORMAT, TEX_FORMAT
1955 main (int argc, char **argv)
1957 int optchar; /* argument character */
1958 int file_index; /* index in text input file arrays */
1960 /* Decode program options. */
1962 program_name = argv[0];
1963 setlocale (LC_ALL, "");
1964 bindtextdomain (PACKAGE, LOCALEDIR);
1965 textdomain (PACKAGE);
1967 #if HAVE_SETCHRCLASS
1971 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
1972 "François Pinard", usage);
1974 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1975 long_options, NULL),
1981 usage (EXIT_FAILURE);
1988 This program is free software; you can redistribute it and/or modify\n\
1989 it under the terms of the GNU General Public License as published by\n\
1990 the Free Software Foundation; either version 2, or (at your option)\n\
1991 any later version.\n\
1993 This program is distributed in the hope that it will be useful,\n\
1994 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
1995 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
1996 GNU General Public License for more details.\n\
1998 You should have received a copy of the GNU General Public License\n\
1999 along with this program; if not, write to the Free Software Foundation,\n\
2000 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
2003 exit (EXIT_SUCCESS);
2010 break_file = optarg;
2018 gap_size = atoi (optarg);
2022 ignore_file = optarg;
2030 input_reference = 1;
2034 /* Yet to understand... */
2038 line_width = atoi (optarg);
2046 truncation_string = copy_unescaped_string (optarg);
2050 macro_name = optarg;
2054 output_format = ROFF_FORMAT;
2058 right_reference = 1;
2062 context_regex_string = copy_unescaped_string (optarg);
2066 output_format = TEX_FORMAT;
2070 word_regex_string = copy_unescaped_string (optarg);
2074 output_format = XARGMATCH ("--format", optarg,
2075 format_args, format_vals);
2079 /* Change the default Ignore file if one is defined. */
2081 #ifdef DEFAULT_IGNORE_FILE
2083 ignore_file = DEFAULT_IGNORE_FILE;
2086 /* Process remaining arguments. If GNU extensions are enabled, process
2087 all arguments as input parameters. If disabled, accept at most two
2088 arguments, the second of which is an output parameter. */
2093 /* No more argument simply means: read standard input. */
2095 input_file_name = (const char **) xmalloc (sizeof (const char *));
2096 file_line_count = (int *) xmalloc (sizeof (int));
2097 number_input_files = 1;
2098 input_file_name[0] = NULL;
2100 else if (gnu_extensions)
2102 number_input_files = argc - optind;
2104 = (const char **) xmalloc (number_input_files * sizeof (const char *));
2106 = (int *) xmalloc (number_input_files * sizeof (int));
2108 for (file_index = 0; file_index < number_input_files; file_index++)
2110 input_file_name[file_index] = argv[optind];
2111 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2112 input_file_name[0] = NULL;
2114 input_file_name[0] = argv[optind];
2121 /* There is one necessary input file. */
2123 number_input_files = 1;
2124 input_file_name = (const char **) xmalloc (sizeof (const char *));
2125 file_line_count = (int *) xmalloc (sizeof (int));
2126 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2127 input_file_name[0] = NULL;
2129 input_file_name[0] = argv[optind];
2132 /* Redirect standard output, only if requested. */
2137 if (fopen (argv[optind], "w") == NULL)
2138 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2142 /* Diagnose any other argument as an error. */
2145 usage (EXIT_FAILURE);
2148 /* If the output format has not been explicitly selected, choose dumb
2149 terminal format if GNU extensions are enabled, else `roff' format. */
2151 if (output_format == UNKNOWN_FORMAT)
2152 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2154 /* Initialize the main tables. */
2156 initialize_regex ();
2158 /* Read `Break character' file, if any. */
2161 digest_break_file (break_file);
2163 /* Read `Ignore words' file and `Only words' files, if any. If any of
2164 these files is empty, reset the name of the file to NULL, to avoid
2165 unnecessary calls to search_table. */
2169 digest_word_file (ignore_file, &ignore_table);
2170 if (ignore_table.length == 0)
2176 digest_word_file (only_file, &only_table);
2177 if (only_table.length == 0)
2181 /* Prepare to study all the input files. */
2183 number_of_occurs[0] = 0;
2184 total_line_count = 0;
2185 maximum_word_length = 0;
2186 reference_max_width = 0;
2188 for (file_index = 0; file_index < number_input_files; file_index++)
2191 /* Read the file in core, than study it. */
2193 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2194 find_occurs_in_text ();
2196 /* Maintain for each file how many lines has been read so far when its
2197 end is reached. Incrementing the count first is a simple kludge to
2198 handle a possible incomplete line at end of file. */
2201 file_line_count[file_index] = total_line_count;
2204 /* Do the output process phase. */
2206 sort_found_occurs ();
2207 fix_output_parameters ();
2208 generate_all_output ();
2212 exit (EXIT_SUCCESS);