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 /* Number of possible characters in a byte. */
38 #define CHAR_SET_SIZE 256
40 /* The ctype definitions should work for all 256 characters. */
44 # define isspace(C) ((C) == ' ' || (C) == '\t' || (C) == '\n')
45 # define isxdigit(C) \
46 (((unsigned char) (C) >= 'a' && (unsigned char) (C) <= 'f') \
47 || ((unsigned char) (C) >= 'A' && (unsigned char) (C) <= 'F') \
48 || ((unsigned char) (C) >= '0' && (unsigned char) (C) <= '9'))
49 # define islower(C) ((unsigned char) (C) >= 'a' && (unsigned char) (C) <= 'z')
50 # define isupper(C) ((unsigned char) (C) >= 'A' && (unsigned char) (C) <= 'Z')
51 # define isalpha(C) (islower (C) || isupper (C))
52 # define toupper(C) (islower (C) ? (C) - 'a' + 'A' : (C))
55 #if !defined (isascii) || defined (STDC_HEADERS)
61 # define ISXDIGIT(C) (isascii (C) && isxdigit (C))
63 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
64 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
65 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
66 #define OCTTOBIN(C) ((C) - '0')
68 /* Debugging the memory allocator. */
71 # define MALLOC_FUNC_CHECK 1
75 /* Global definitions. */
77 /* Reallocation step when swallowing non regular files. The value is not
78 the actual reallocation step, but its base two logarithm. */
79 #define SWALLOW_REALLOC_LOG 12
81 /* Imported from "regex.c". */
84 /* The name this program was run with. */
87 /* Program options. */
91 UNKNOWN_FORMAT, /* output format still unknown */
92 DUMB_FORMAT, /* output for a dumb terminal */
93 ROFF_FORMAT, /* output for `troff' or `nroff' */
94 TEX_FORMAT /* output for `TeX' or `LaTeX' */
97 int gnu_extensions = 1; /* trigger all GNU extensions */
98 int auto_reference = 0; /* references are `file_name:line_number:' */
99 int input_reference = 0; /* references at beginning of input lines */
100 int right_reference = 0; /* output references after right context */
101 int line_width = 72; /* output line width in characters */
102 int gap_size = 3; /* number of spaces between output fields */
103 const char *truncation_string = "/";
104 /* string used to mark line truncations */
105 const char *macro_name = "xx"; /* macro name for roff or TeX output */
106 enum Format output_format = UNKNOWN_FORMAT;
109 int ignore_case = 0; /* fold lower to upper case for sorting */
110 const char *context_regex_string = NULL;
111 /* raw regex for end of context */
112 const char *word_regex_string = NULL;
113 /* raw regex for a keyword */
114 const char *break_file = NULL; /* name of the `Break characters' file */
115 const char *only_file = NULL; /* name of the `Only words' file */
116 const char *ignore_file = NULL; /* name of the `Ignore words' file */
118 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
119 whole file. A WORD is something smaller, its length should fit in a
120 short integer. A WORD_TABLE may contain several WORDs. */
124 char *start; /* pointer to beginning of region */
125 char *end; /* pointer to end + 1 of region */
131 char *start; /* pointer to beginning of region */
132 short size; /* length of the region */
138 WORD *start; /* array of WORDs */
139 size_t length; /* number of entries */
143 /* Pattern description tables. */
145 /* For each character, provide its folded equivalent. */
146 unsigned char folded_chars[CHAR_SET_SIZE];
148 /* For each character, indicate if it is part of a word. */
149 char syntax_table[CHAR_SET_SIZE];
150 char *re_syntax_table = syntax_table;
152 /* Compiled regex for end of context. */
153 struct re_pattern_buffer *context_regex;
155 /* End of context pattern register indices. */
156 struct re_registers context_regs;
158 /* Compiled regex for a keyword. */
159 struct re_pattern_buffer *word_regex;
161 /* Keyword pattern register indices. */
162 struct re_registers word_regs;
164 /* A word characters fastmap is used only when no word regexp has been
165 provided. A word is then made up of a sequence of one or more characters
166 allowed by the fastmap. Contains !0 if character allowed in word. Not
167 only this is faster in most cases, but it simplifies the implementation
168 of the Break files. */
169 char word_fastmap[CHAR_SET_SIZE];
171 /* Maximum length of any word read. */
172 int maximum_word_length;
174 /* Maximum width of any reference used. */
175 int reference_max_width;
177 /* Ignore and Only word tables. */
179 WORD_TABLE ignore_table; /* table of words to ignore */
180 WORD_TABLE only_table; /* table of words to select */
182 #define ALLOC_NEW_WORD(table) \
183 BUMP_ALLOC ((table)->start, (table)->length, 8, WORD)
185 /* Source text table, and scanning macros. */
187 int number_input_files; /* number of text input files */
188 int total_line_count; /* total number of lines seen so far */
189 const char **input_file_name; /* array of text input file names */
190 int *file_line_count; /* array of `total_line_count' values at end */
192 BLOCK text_buffer; /* file to study */
193 char *text_buffer_maxend; /* allocated end of text_buffer */
195 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
197 #define SKIP_NON_WHITE(cursor, limit) \
198 while (cursor < limit && !isspace(*cursor)) \
201 #define SKIP_WHITE(cursor, limit) \
202 while (cursor < limit && isspace(*cursor)) \
205 #define SKIP_WHITE_BACKWARDS(cursor, start) \
206 while (cursor > start && isspace(cursor[-1])) \
209 #define SKIP_SOMETHING(cursor, limit) \
210 if (word_regex_string) \
213 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
214 cursor += count <= 0 ? 1 : count; \
216 else if (word_fastmap[(unsigned char) *cursor]) \
217 while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
222 /* Occurrences table.
224 The `keyword' pointer provides the central word, which is surrounded
225 by a left context and a right context. The `keyword' and `length'
226 field allow full 8-bit characters keys, even including NULs. At other
227 places in this program, the name `keyafter' refers to the keyword
228 followed by its right context.
230 The left context does not extend, towards the beginning of the file,
231 further than a distance given by the `left' value. This value is
232 relative to the keyword beginning, it is usually negative. This
233 insures that, except for white space, we will never have to backward
234 scan the source text, when it is time to generate the final output
237 The right context, indirectly attainable through the keyword end, does
238 not extend, towards the end of the file, further than a distance given
239 by the `right' value. This value is relative to the keyword
240 beginning, it is usually positive.
242 When automatic references are used, the `reference' value is the
243 overall line number in all input files read so far, in this case, it
244 is of type (int). When input references are used, the `reference'
245 value indicates the distance between the keyword beginning and the
246 start of the reference field, it is of type (DELTA) and usually
249 typedef short DELTA; /* to hold displacement within one context */
253 WORD key; /* description of the keyword */
254 DELTA left; /* distance to left context start */
255 DELTA right; /* distance to right context end */
256 int reference; /* reference descriptor */
260 /* The various OCCURS tables are indexed by the language. But the time
261 being, there is no such multiple language support. */
263 OCCURS *occurs_table[1]; /* all words retained from the read text */
264 size_t number_of_occurs[1]; /* number of used slots in occurs_table */
266 #define ALLOC_NEW_OCCURS(language) \
267 BUMP_ALLOC (occurs_table[language], number_of_occurs[language], 9, OCCURS)
269 /* Communication among output routines. */
271 /* Indicate if special output processing is requested for each character. */
272 char edited_flag[CHAR_SET_SIZE];
274 int half_line_width; /* half of line width, reference excluded */
275 int before_max_width; /* maximum width of before field */
276 int keyafter_max_width; /* maximum width of keyword-and-after field */
277 int truncation_string_length; /* length of string used to flag truncation */
279 /* When context is limited by lines, wraparound may happen on final output:
280 the `head' pointer gives access to some supplementary left context which
281 will be seen at the end of the output line, the `tail' pointer gives
282 access to some supplementary right context which will be seen at the
283 beginning of the output line. */
285 BLOCK tail; /* tail field */
286 int tail_truncation; /* flag truncation after the tail field */
288 BLOCK before; /* before field */
289 int before_truncation; /* flag truncation before the before field */
291 BLOCK keyafter; /* keyword-and-after field */
292 int keyafter_truncation; /* flag truncation after the keyafter field */
294 BLOCK head; /* head field */
295 int head_truncation; /* flag truncation before the head field */
297 BLOCK reference; /* reference field for input reference mode */
299 /* Miscellaneous routines. */
301 /*------------------------------------------------------.
302 | Duplicate string STRING, while evaluating \-escapes. |
303 `------------------------------------------------------*/
305 /* Loosely adapted from GNU sh-utils printf.c code. */
308 copy_unescaped_string (const char *string)
310 char *result; /* allocated result */
311 char *cursor; /* cursor in result */
312 int value; /* value of \nnn escape */
313 int length; /* length of \nnn escape */
315 result = xmalloc (strlen (string) + 1);
324 case 'x': /* \xhhh escape, 3 chars maximum */
326 for (length = 0, string++;
327 length < 3 && ISXDIGIT (*string);
329 value = value * 16 + HEXTOBIN (*string);
339 case '0': /* \0ooo escape, 3 chars maximum */
341 for (length = 0, string++;
342 length < 3 && ISODIGIT (*string);
344 value = value * 8 + OCTTOBIN (*string);
348 case 'a': /* alert */
357 case 'b': /* backspace */
362 case 'c': /* cancel the rest of the output */
367 case 'f': /* form feed */
372 case 'n': /* new line */
377 case 'r': /* carriage return */
382 case 't': /* horizontal tab */
387 case 'v': /* vertical tab */
398 *cursor++ = *string++;
403 *cursor++ = *string++;
409 /*-------------------------------------------------------------------.
410 | Compile the regex represented by STRING, diagnose and abort if any |
411 | error. Returns the compiled regex structure. |
412 `-------------------------------------------------------------------*/
414 static struct re_pattern_buffer *
415 alloc_and_compile_regex (const char *string)
417 struct re_pattern_buffer *pattern; /* newly allocated structure */
418 const char *message; /* error message returned by regex.c */
420 pattern = (struct re_pattern_buffer *)
421 xmalloc (sizeof (struct re_pattern_buffer));
422 memset (pattern, 0, sizeof (struct re_pattern_buffer));
424 pattern->buffer = NULL;
425 pattern->allocated = 0;
426 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
427 pattern->fastmap = (char *) xmalloc ((size_t) CHAR_SET_SIZE);
429 message = re_compile_pattern (string, (int) strlen (string), pattern);
431 error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);
433 /* The fastmap should be compiled before `re_match'. The following
434 call is not mandatory, because `re_search' is always called sooner,
435 and it compiles the fastmap if this has not been done yet. */
437 re_compile_fastmap (pattern);
439 /* Do not waste extra allocated space. */
441 if (pattern->allocated > pattern->used)
444 = (unsigned char *) xrealloc (pattern->buffer, (size_t) pattern->used);
445 pattern->allocated = pattern->used;
451 /*------------------------------------------------------------------------.
452 | This will initialize various tables for pattern match and compiles some |
454 `------------------------------------------------------------------------*/
457 initialize_regex (void)
459 int character; /* character value */
461 /* Initialize the regex syntax table. */
463 for (character = 0; character < CHAR_SET_SIZE; character++)
464 syntax_table[character] = isalpha (character) ? Sword : 0;
466 /* Initialize the case folding table. */
469 for (character = 0; character < CHAR_SET_SIZE; character++)
470 folded_chars[character] = toupper (character);
472 /* Unless the user already provided a description of the end of line or
473 end of sentence sequence, select an end of line sequence to compile.
474 If the user provided an empty definition, thus disabling end of line
475 or sentence feature, make it NULL to speed up tests. If GNU
476 extensions are enabled, use end of sentence like in GNU emacs. If
477 disabled, use end of lines. */
479 if (context_regex_string)
481 if (!*context_regex_string)
482 context_regex_string = NULL;
484 else if (gnu_extensions && !input_reference)
485 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
487 context_regex_string = "\n";
489 if (context_regex_string)
490 context_regex = alloc_and_compile_regex (context_regex_string);
492 /* If the user has already provided a non-empty regexp to describe
493 words, compile it. Else, unless this has already been done through
494 a user provided Break character file, construct a fastmap of
495 characters that may appear in a word. If GNU extensions enabled,
496 include only letters of the underlying character set. If disabled,
497 include almost everything, even punctuations; stop only on white
500 if (word_regex_string && *word_regex_string)
501 word_regex = alloc_and_compile_regex (word_regex_string);
502 else if (!break_file)
509 for (character = 0; character < CHAR_SET_SIZE; character++)
510 word_fastmap[character] = isalpha (character) ? 1 : 0;
515 /* Simulate [^ \t\n]+. */
517 memset (word_fastmap, 1, CHAR_SET_SIZE);
518 word_fastmap[' '] = 0;
519 word_fastmap['\t'] = 0;
520 word_fastmap['\n'] = 0;
525 /*------------------------------------------------------------------------.
526 | This routine will attempt to swallow a whole file name FILE_NAME into a |
527 | contiguous region of memory and return a description of it into BLOCK. |
528 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
530 | Previously, in some cases, white space compression was attempted while |
531 | inputting text. This was defeating some regexps like default end of |
532 | sentence, which checks for two consecutive spaces. If white space |
533 | compression is ever reinstated, it should be in output routines. |
534 `------------------------------------------------------------------------*/
537 swallow_file_in_memory (const char *file_name, BLOCK *block)
539 int file_handle; /* file descriptor number */
540 struct stat stat_block; /* stat block for file */
541 size_t allocated_length; /* allocated length of memory buffer */
542 size_t used_length; /* used length in memory buffer */
543 int read_length; /* number of character gotten on last read */
545 /* As special cases, a file name which is NULL or "-" indicates standard
546 input, which is already opened. In all other cases, open the file from
549 if (!file_name || !*file_name || strcmp (file_name, "-") == 0)
550 file_handle = fileno (stdin);
552 if ((file_handle = open (file_name, O_RDONLY)) < 0)
553 error (EXIT_FAILURE, errno, "%s", file_name);
555 /* If the file is a plain, regular file, allocate the memory buffer all at
556 once and swallow the file in one blow. In other cases, read the file
557 repeatedly in smaller chunks until we have it all, reallocating memory
558 once in a while, as we go. */
560 if (fstat (file_handle, &stat_block) < 0)
561 error (EXIT_FAILURE, errno, "%s", file_name);
563 if (S_ISREG (stat_block.st_mode))
565 size_t in_memory_size;
567 block->start = (char *) xmalloc ((size_t) stat_block.st_size);
569 if ((in_memory_size = read (file_handle,
570 block->start, (size_t) stat_block.st_size))
571 != stat_block.st_size)
574 /* On MSDOS, in memory size may be smaller than the file
575 size, because of end of line conversions. But it can
576 never be smaller than half the file size, because the
577 minimum is when all lines are empty and terminated by
579 if (in_memory_size != (size_t)-1
580 && in_memory_size >= stat_block.st_size / 2)
581 block->start = (char *) xrealloc (block->start, in_memory_size);
583 #endif /* not MSDOS */
585 error (EXIT_FAILURE, errno, "%s", file_name);
587 block->end = block->start + in_memory_size;
591 block->start = (char *) xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
593 allocated_length = (1 << SWALLOW_REALLOC_LOG);
595 while (read_length = read (file_handle,
596 block->start + used_length,
597 allocated_length - used_length),
600 used_length += read_length;
601 if (used_length == allocated_length)
603 allocated_length += (1 << SWALLOW_REALLOC_LOG);
605 = (char *) xrealloc (block->start, allocated_length);
610 error (EXIT_FAILURE, errno, "%s", file_name);
612 block->end = block->start + used_length;
615 /* Close the file, but only if it was not the standard input. */
617 if (file_handle != fileno (stdin))
621 /* Sort and search routines. */
623 /*--------------------------------------------------------------------------.
624 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
625 | Return less than 0 if the first word goes before the second; return |
626 | greater than 0 if the first word goes after the second. |
628 | If a word is indeed a prefix of the other, the shorter should go first. |
629 `--------------------------------------------------------------------------*/
632 compare_words (const void *void_first, const void *void_second)
634 #define first ((const WORD *) void_first)
635 #define second ((const WORD *) void_second)
636 int length; /* minimum of two lengths */
637 int counter; /* cursor in words */
638 int value; /* value of comparison */
640 length = first->size < second->size ? first->size : second->size;
644 for (counter = 0; counter < length; counter++)
646 value = (folded_chars [(unsigned char) (first->start[counter])]
647 - folded_chars [(unsigned char) (second->start[counter])]);
654 for (counter = 0; counter < length; counter++)
656 value = ((unsigned char) first->start[counter]
657 - (unsigned char) second->start[counter]);
663 return first->size - second->size;
668 /*-----------------------------------------------------------------------.
669 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
670 | go first. In case of a tie, preserve the original order through a |
671 | pointer comparison. |
672 `-----------------------------------------------------------------------*/
675 compare_occurs (const void *void_first, const void *void_second)
677 #define first ((const OCCURS *) void_first)
678 #define second ((const OCCURS *) void_second)
681 value = compare_words (&first->key, &second->key);
682 return value == 0 ? first->key.start - second->key.start : value;
687 /*------------------------------------------------------------.
688 | Return !0 if WORD appears in TABLE. Uses a binary search. |
689 `------------------------------------------------------------*/
692 search_table (WORD *word, WORD_TABLE *table)
694 int lowest; /* current lowest possible index */
695 int highest; /* current highest possible index */
696 int middle; /* current middle index */
697 int value; /* value from last comparison */
700 highest = table->length - 1;
701 while (lowest <= highest)
703 middle = (lowest + highest) / 2;
704 value = compare_words (word, table->start + middle);
706 highest = middle - 1;
715 /*---------------------------------------------------------------------.
716 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
717 | take intermediate copies or table elements, so the sort will be |
718 | stabilized throughout the comparison routine. |
719 `---------------------------------------------------------------------*/
722 sort_found_occurs (void)
725 /* Only one language for the time being. */
727 qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
731 /* Parameter files reading routines. */
733 /*----------------------------------------------------------------------.
734 | Read a file named FILE_NAME, containing a set of break characters. |
735 | Build a content to the array word_fastmap in which all characters are |
736 | allowed except those found in the file. Characters may be repeated. |
737 `----------------------------------------------------------------------*/
740 digest_break_file (const char *file_name)
742 BLOCK file_contents; /* to receive a copy of the file */
743 char *cursor; /* cursor in file copy */
745 swallow_file_in_memory (file_name, &file_contents);
747 /* Make the fastmap and record the file contents in it. */
749 memset (word_fastmap, 1, CHAR_SET_SIZE);
750 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
751 word_fastmap[(unsigned char) *cursor] = 0;
756 /* If GNU extensions are enabled, the only way to avoid newline as
757 a break character is to write all the break characters in the
758 file with no newline at all, not even at the end of the file.
759 If disabled, spaces, tabs and newlines are always considered as
760 break characters even if not included in the break file. */
762 word_fastmap[' '] = 0;
763 word_fastmap['\t'] = 0;
764 word_fastmap['\n'] = 0;
767 /* Return the space of the file, which is no more required. */
769 free (file_contents.start);
772 /*-----------------------------------------------------------------------.
773 | Read a file named FILE_NAME, containing one word per line, then |
774 | construct in TABLE a table of WORD descriptors for them. The routine |
775 | swallows the whole file in memory; this is at the expense of space |
776 | needed for newlines, which are useless; however, the reading is fast. |
777 `-----------------------------------------------------------------------*/
780 digest_word_file (const char *file_name, WORD_TABLE *table)
782 BLOCK file_contents; /* to receive a copy of the file */
783 char *cursor; /* cursor in file copy */
784 char *word_start; /* start of the current word */
786 swallow_file_in_memory (file_name, &file_contents);
791 /* Read the whole file. */
793 cursor = file_contents.start;
794 while (cursor < file_contents.end)
797 /* Read one line, and save the word in contains. */
800 while (cursor < file_contents.end && *cursor != '\n')
803 /* Record the word in table if it is not empty. */
805 if (cursor > word_start)
807 ALLOC_NEW_WORD (table);
808 table->start[table->length].start = word_start;
809 table->start[table->length].size = cursor - word_start;
813 /* This test allows for an incomplete line at end of file. */
815 if (cursor < file_contents.end)
819 /* Finally, sort all the words read. */
821 qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
824 /* Keyword recognition and selection. */
826 /*----------------------------------------------------------------------.
827 | For each keyword in the source text, constructs an OCCURS structure. |
828 `----------------------------------------------------------------------*/
831 find_occurs_in_text (void)
833 char *cursor; /* for scanning the source text */
834 char *scan; /* for scanning the source text also */
835 char *line_start; /* start of the current input line */
836 char *line_scan; /* newlines scanned until this point */
837 int reference_length; /* length of reference in input mode */
838 WORD possible_key; /* possible key, to ease searches */
839 OCCURS *occurs_cursor; /* current OCCURS under construction */
841 char *context_start; /* start of left context */
842 char *context_end; /* end of right context */
843 char *word_start; /* start of word */
844 char *word_end; /* end of word */
845 char *next_context_start; /* next start of left context */
847 /* reference_length is always used within `if (input_reference)'.
848 However, GNU C diagnoses that it may be used uninitialized. The
849 following assignment is merely to shut it up. */
851 reference_length = 0;
853 /* Tracking where lines start is helpful for reference processing. In
854 auto reference mode, this allows counting lines. In input reference
855 mode, this permits finding the beginning of the references.
857 The first line begins with the file, skip immediately this very first
858 reference in input reference mode, to help further rejection any word
859 found inside it. Also, unconditionally assigning these variable has
860 the happy effect of shutting up lint. */
862 line_start = text_buffer.start;
863 line_scan = line_start;
866 SKIP_NON_WHITE (line_scan, text_buffer.end);
867 reference_length = line_scan - line_start;
868 SKIP_WHITE (line_scan, text_buffer.end);
871 /* Process the whole buffer, one line or one sentence at a time. */
873 for (cursor = text_buffer.start;
874 cursor < text_buffer.end;
875 cursor = next_context_start)
878 /* `context_start' gets initialized before the processing of each
879 line, or once for the whole buffer if no end of line or sentence
880 sequence separator. */
882 context_start = cursor;
884 /* If a end of line or end of sentence sequence is defined and
885 non-empty, `next_context_start' will be recomputed to be the end of
886 each line or sentence, before each one is processed. If no such
887 sequence, then `next_context_start' is set at the end of the whole
888 buffer, which is then considered to be a single line or sentence.
889 This test also accounts for the case of an incomplete line or
890 sentence at the end of the buffer. */
892 if (context_regex_string
893 && (re_search (context_regex, cursor, text_buffer.end - cursor,
894 0, text_buffer.end - cursor, &context_regs)
896 next_context_start = cursor + context_regs.end[0];
899 next_context_start = text_buffer.end;
901 /* Include the separator into the right context, but not any suffix
902 white space in this separator; this insures it will be seen in
903 output and will not take more space than necessary. */
905 context_end = next_context_start;
906 SKIP_WHITE_BACKWARDS (context_end, context_start);
908 /* Read and process a single input line or sentence, one word at a
915 /* If a word regexp has been compiled, use it to skip at the
916 beginning of the next word. If there is no such word, exit
920 if (re_search (word_regex, cursor, context_end - cursor,
921 0, context_end - cursor, &word_regs)
924 word_start = cursor + word_regs.start[0];
925 word_end = cursor + word_regs.end[0];
929 /* Avoid re_search and use the fastmap to skip to the
930 beginning of the next word. If there is no more word in
931 the buffer, exit the loop. */
935 while (scan < context_end
936 && !word_fastmap[(unsigned char) *scan])
939 if (scan == context_end)
944 while (scan < context_end
945 && word_fastmap[(unsigned char) *scan])
951 /* Skip right to the beginning of the found word. */
955 /* Skip any zero length word. Just advance a single position,
956 then go fetch the next word. */
958 if (word_end == word_start)
964 /* This is a genuine, non empty word, so save it as a possible
965 key. Then skip over it. Also, maintain the maximum length of
966 all words read so far. It is mandatory to take the maximum
967 length of all words in the file, without considering if they
968 are actually kept or rejected, because backward jumps at output
969 generation time may fall in *any* word. */
971 possible_key.start = cursor;
972 possible_key.size = word_end - word_start;
973 cursor += possible_key.size;
975 if (possible_key.size > maximum_word_length)
976 maximum_word_length = possible_key.size;
978 /* In input reference mode, update `line_start' from its previous
979 value. Count the lines just in case auto reference mode is
980 also selected. If it happens that the word just matched is
981 indeed part of a reference; just ignore it. */
985 while (line_scan < possible_key.start)
986 if (*line_scan == '\n')
990 line_start = line_scan;
991 SKIP_NON_WHITE (line_scan, text_buffer.end);
992 reference_length = line_scan - line_start;
996 if (line_scan > possible_key.start)
1000 /* Ignore the word if an `Ignore words' table exists and if it is
1001 part of it. Also ignore the word if an `Only words' table and
1002 if it is *not* part of it.
1004 It is allowed that both tables be used at once, even if this
1005 may look strange for now. Just ignore a word that would appear
1006 in both. If regexps are eventually implemented for these
1007 tables, the Ignore table could then reject words that would
1008 have been previously accepted by the Only table. */
1010 if (ignore_file && search_table (&possible_key, &ignore_table))
1012 if (only_file && !search_table (&possible_key, &only_table))
1015 /* A non-empty word has been found. First of all, insure
1016 proper allocation of the next OCCURS, and make a pointer to
1017 where it will be constructed. */
1019 ALLOC_NEW_OCCURS (0);
1020 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1022 /* Define the refence field, if any. */
1027 /* While auto referencing, update `line_start' from its
1028 previous value, counting lines as we go. If input
1029 referencing at the same time, `line_start' has been
1030 advanced earlier, and the following loop is never really
1033 while (line_scan < possible_key.start)
1034 if (*line_scan == '\n')
1038 line_start = line_scan;
1039 SKIP_NON_WHITE (line_scan, text_buffer.end);
1044 occurs_cursor->reference = total_line_count;
1046 else if (input_reference)
1049 /* If only input referencing, `line_start' has been computed
1050 earlier to detect the case the word matched would be part
1051 of the reference. The reference position is simply the
1052 value of `line_start'. */
1054 occurs_cursor->reference
1055 = (DELTA) (line_start - possible_key.start);
1056 if (reference_length > reference_max_width)
1057 reference_max_width = reference_length;
1060 /* Exclude the reference from the context in simple cases. */
1062 if (input_reference && line_start == context_start)
1064 SKIP_NON_WHITE (context_start, context_end);
1065 SKIP_WHITE (context_start, context_end);
1068 /* Completes the OCCURS structure. */
1070 occurs_cursor->key = possible_key;
1071 occurs_cursor->left = context_start - possible_key.start;
1072 occurs_cursor->right = context_end - possible_key.start;
1074 number_of_occurs[0]++;
1079 /* Formatting and actual output - service routines. */
1081 /*-----------------------------------------.
1082 | Prints some NUMBER of spaces on stdout. |
1083 `-----------------------------------------*/
1086 print_spaces (int number)
1090 for (counter = number; counter > 0; counter--)
1094 /*-------------------------------------.
1095 | Prints the field provided by FIELD. |
1096 `-------------------------------------*/
1099 print_field (BLOCK field)
1101 char *cursor; /* Cursor in field to print */
1102 int character; /* Current character */
1103 int base; /* Base character, without diacritic */
1104 int diacritic; /* Diacritic code for the character */
1106 /* Whitespace is not really compressed. Instead, each white space
1107 character (tab, vt, ht etc.) is printed as one single space. */
1109 for (cursor = field.start; cursor < field.end; cursor++)
1111 character = (unsigned char) *cursor;
1112 if (edited_flag[character])
1115 /* First check if this is a diacriticized character.
1117 This works only for TeX. I do not know how diacriticized
1118 letters work with `roff'. Please someone explain it to me! */
1120 diacritic = todiac (character);
1121 if (diacritic != 0 && output_format == TEX_FORMAT)
1123 base = tobase (character);
1127 case 1: /* Latin diphthongs */
1131 fputs ("\\oe{}", stdout);
1135 fputs ("\\OE{}", stdout);
1139 fputs ("\\ae{}", stdout);
1143 fputs ("\\AE{}", stdout);
1151 case 2: /* Acute accent */
1152 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1155 case 3: /* Grave accent */
1156 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1159 case 4: /* Circumflex accent */
1160 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1163 case 5: /* Diaeresis */
1164 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1167 case 6: /* Tilde accent */
1168 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1171 case 7: /* Cedilla */
1172 printf ("\\c{%c}", base);
1175 case 8: /* Small circle beneath */
1179 fputs ("\\aa{}", stdout);
1183 fputs ("\\AA{}", stdout);
1191 case 9: /* Strike through */
1195 fputs ("\\o{}", stdout);
1199 fputs ("\\O{}", stdout);
1210 /* This is not a diacritic character, so handle cases which are
1211 really specific to `roff' or TeX. All white space processing
1212 is done as the default case of this switch. */
1217 /* In roff output format, double any quote. */
1227 /* In TeX output format, precede these with a backslash. */
1229 putchar (character);
1234 /* In TeX output format, precede these with a backslash and
1235 force mathematical mode. */
1236 printf ("$\\%c$", character);
1240 /* In TeX output mode, request production of a backslash. */
1241 fputs ("\\backslash{}", stdout);
1245 /* Any other flagged character produces a single space. */
1254 /* Formatting and actual output - planning routines. */
1256 /*--------------------------------------------------------------------.
1257 | From information collected from command line options and input file |
1258 | readings, compute and fix some output parameter values. |
1259 `--------------------------------------------------------------------*/
1262 fix_output_parameters (void)
1264 int file_index; /* index in text input file arrays */
1265 int line_ordinal; /* line ordinal value for reference */
1266 char ordinal_string[12]; /* edited line ordinal for reference */
1267 int reference_width; /* width for the whole reference */
1268 int character; /* character ordinal */
1269 const char *cursor; /* cursor in some constant strings */
1271 /* In auto reference mode, the maximum width of this field is
1272 precomputed and subtracted from the overall line width. Add one for
1273 the column which separate the file name from the line number. */
1277 reference_max_width = 0;
1278 for (file_index = 0; file_index < number_input_files; file_index++)
1280 line_ordinal = file_line_count[file_index] + 1;
1282 line_ordinal -= file_line_count[file_index - 1];
1283 sprintf (ordinal_string, "%d", line_ordinal);
1284 reference_width = strlen (ordinal_string);
1285 if (input_file_name[file_index])
1286 reference_width += strlen (input_file_name[file_index]);
1287 if (reference_width > reference_max_width)
1288 reference_max_width = reference_width;
1290 reference_max_width++;
1291 reference.start = (char *) xmalloc ((size_t) reference_max_width + 1);
1294 /* If the reference appears to the left of the output line, reserve some
1295 space for it right away, including one gap size. */
1297 if ((auto_reference || input_reference) && !right_reference)
1298 line_width -= reference_max_width + gap_size;
1300 /* The output lines, minimally, will contain from left to right a left
1301 context, a gap, and a keyword followed by the right context with no
1302 special intervening gap. Half of the line width is dedicated to the
1303 left context and the gap, the other half is dedicated to the keyword
1304 and the right context; these values are computed once and for all here.
1305 There also are tail and head wrap around fields, used when the keyword
1306 is near the beginning or the end of the line, or when some long word
1307 cannot fit in, but leave place from wrapped around shorter words. The
1308 maximum width of these fields are recomputed separately for each line,
1309 on a case by case basis. It is worth noting that it cannot happen that
1310 both the tail and head fields are used at once. */
1312 half_line_width = line_width / 2;
1313 before_max_width = half_line_width - gap_size;
1314 keyafter_max_width = half_line_width;
1316 /* If truncation_string is the empty string, make it NULL to speed up
1317 tests. In this case, truncation_string_length will never get used, so
1318 there is no need to set it. */
1320 if (truncation_string && *truncation_string)
1321 truncation_string_length = strlen (truncation_string);
1323 truncation_string = NULL;
1328 /* When flagging truncation at the left of the keyword, the
1329 truncation mark goes at the beginning of the before field,
1330 unless there is a head field, in which case the mark goes at the
1331 left of the head field. When flagging truncation at the right
1332 of the keyword, the mark goes at the end of the keyafter field,
1333 unless there is a tail field, in which case the mark goes at the
1334 end of the tail field. Only eight combination cases could arise
1335 for truncation marks:
1338 . One beginning the before field.
1339 . One beginning the head field.
1340 . One ending the keyafter field.
1341 . One ending the tail field.
1342 . One beginning the before field, another ending the keyafter field.
1343 . One ending the tail field, another beginning the before field.
1344 . One ending the keyafter field, another beginning the head field.
1346 So, there is at most two truncation marks, which could appear both
1347 on the left side of the center of the output line, both on the
1348 right side, or one on either side. */
1350 before_max_width -= 2 * truncation_string_length;
1351 keyafter_max_width -= 2 * truncation_string_length;
1356 /* I never figured out exactly how UNIX' ptx plans the output width
1357 of its various fields. If GNU extensions are disabled, do not
1358 try computing the field widths correctly; instead, use the
1359 following formula, which does not completely imitate UNIX' ptx,
1362 keyafter_max_width -= 2 * truncation_string_length + 1;
1365 /* Compute which characters need special output processing. Initialize
1366 by flagging any white space character. Some systems do not consider
1367 form feed as a space character, but we do. */
1369 for (character = 0; character < CHAR_SET_SIZE; character++)
1370 edited_flag[character] = isspace (character) != 0;
1371 edited_flag['\f'] = 1;
1373 /* Complete the special character flagging according to selected output
1376 switch (output_format)
1378 case UNKNOWN_FORMAT:
1379 /* Should never happen. */
1386 /* `Quote' characters should be doubled. */
1388 edited_flag['"'] = 1;
1393 /* Various characters need special processing. */
1395 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1396 edited_flag[(unsigned char) *cursor] = 1;
1398 /* Any character with 8th bit set will print to a single space, unless
1399 it is diacriticized. */
1401 for (character = 0200; character < CHAR_SET_SIZE; character++)
1402 edited_flag[character] = todiac (character) != 0;
1407 /*------------------------------------------------------------------.
1408 | Compute the position and length of all the output fields, given a |
1409 | pointer to some OCCURS. |
1410 `------------------------------------------------------------------*/
1413 define_all_fields (OCCURS *occurs)
1415 int tail_max_width; /* allowable width of tail field */
1416 int head_max_width; /* allowable width of head field */
1417 char *cursor; /* running cursor in source text */
1418 char *left_context_start; /* start of left context */
1419 char *right_context_end; /* end of right context */
1420 char *left_field_start; /* conservative start for `head'/`before' */
1421 int file_index; /* index in text input file arrays */
1422 const char *file_name; /* file name for reference */
1423 int line_ordinal; /* line ordinal for reference */
1425 /* Define `keyafter', start of left context and end of right context.
1426 `keyafter' starts at the saved position for keyword and extend to the
1427 right from the end of the keyword, eating separators or full words, but
1428 not beyond maximum allowed width for `keyafter' field or limit for the
1429 right context. Suffix spaces will be removed afterwards. */
1431 keyafter.start = occurs->key.start;
1432 keyafter.end = keyafter.start + occurs->key.size;
1433 left_context_start = keyafter.start + occurs->left;
1434 right_context_end = keyafter.start + occurs->right;
1436 cursor = keyafter.end;
1437 while (cursor < right_context_end
1438 && cursor <= keyafter.start + keyafter_max_width)
1440 keyafter.end = cursor;
1441 SKIP_SOMETHING (cursor, right_context_end);
1443 if (cursor <= keyafter.start + keyafter_max_width)
1444 keyafter.end = cursor;
1446 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1448 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1450 /* When the left context is wide, it might take some time to catch up from
1451 the left context boundary to the beginning of the `head' or `before'
1452 fields. So, in this case, to speed the catchup, we jump back from the
1453 keyword, using some secure distance, possibly falling in the middle of
1454 a word. A secure backward jump would be at least half the maximum
1455 width of a line, plus the size of the longest word met in the whole
1456 input. We conclude this backward jump by a skip forward of at least
1457 one word. In this manner, we should not inadvertently accept only part
1458 of a word. From the reached point, when it will be time to fix the
1459 beginning of `head' or `before' fields, we will skip forward words or
1460 delimiters until we get sufficiently near. */
1462 if (-occurs->left > half_line_width + maximum_word_length)
1465 = keyafter.start - (half_line_width + maximum_word_length);
1466 SKIP_SOMETHING (left_field_start, keyafter.start);
1469 left_field_start = keyafter.start + occurs->left;
1471 /* `before' certainly ends at the keyword, but not including separating
1472 spaces. It starts after than the saved value for the left context, by
1473 advancing it until it falls inside the maximum allowed width for the
1474 before field. There will be no prefix spaces either. `before' only
1475 advances by skipping single separators or whole words. */
1477 before.start = left_field_start;
1478 before.end = keyafter.start;
1479 SKIP_WHITE_BACKWARDS (before.end, before.start);
1481 while (before.start + before_max_width < before.end)
1482 SKIP_SOMETHING (before.start, before.end);
1484 if (truncation_string)
1486 cursor = before.start;
1487 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1488 before_truncation = cursor > left_context_start;
1491 before_truncation = 0;
1493 SKIP_WHITE (before.start, text_buffer.end);
1495 /* The tail could not take more columns than what has been left in the
1496 left context field, and a gap is mandatory. It starts after the
1497 right context, and does not contain prefixed spaces. It ends at
1498 the end of line, the end of buffer or when the tail field is full,
1499 whichever comes first. It cannot contain only part of a word, and
1500 has no suffixed spaces. */
1503 = before_max_width - (before.end - before.start) - gap_size;
1505 if (tail_max_width > 0)
1507 tail.start = keyafter.end;
1508 SKIP_WHITE (tail.start, text_buffer.end);
1510 tail.end = tail.start;
1512 while (cursor < right_context_end
1513 && cursor < tail.start + tail_max_width)
1516 SKIP_SOMETHING (cursor, right_context_end);
1519 if (cursor < tail.start + tail_max_width)
1522 if (tail.end > tail.start)
1524 keyafter_truncation = 0;
1525 tail_truncation = truncation_string && tail.end < right_context_end;
1528 tail_truncation = 0;
1530 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1535 /* No place left for a tail field. */
1539 tail_truncation = 0;
1542 /* `head' could not take more columns than what has been left in the right
1543 context field, and a gap is mandatory. It ends before the left
1544 context, and does not contain suffixed spaces. Its pointer is advanced
1545 until the head field has shrunk to its allowed width. It cannot
1546 contain only part of a word, and has no suffixed spaces. */
1549 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1551 if (head_max_width > 0)
1553 head.end = before.start;
1554 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1556 head.start = left_field_start;
1557 while (head.start + head_max_width < head.end)
1558 SKIP_SOMETHING (head.start, head.end);
1560 if (head.end > head.start)
1562 before_truncation = 0;
1563 head_truncation = (truncation_string
1564 && head.start > left_context_start);
1567 head_truncation = 0;
1569 SKIP_WHITE (head.start, head.end);
1574 /* No place left for a head field. */
1578 head_truncation = 0;
1584 /* Construct the reference text in preallocated space from the file
1585 name and the line number. Find out in which file the reference
1586 occurred. Standard input yields an empty file name. Insure line
1587 numbers are one based, even if they are computed zero based. */
1590 while (file_line_count[file_index] < occurs->reference)
1593 file_name = input_file_name[file_index];
1597 line_ordinal = occurs->reference + 1;
1599 line_ordinal -= file_line_count[file_index - 1];
1601 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1602 reference.end = reference.start + strlen (reference.start);
1604 else if (input_reference)
1607 /* Reference starts at saved position for reference and extends right
1608 until some white space is met. */
1610 reference.start = keyafter.start + (DELTA) occurs->reference;
1611 reference.end = reference.start;
1612 SKIP_NON_WHITE (reference.end, right_context_end);
1616 /* Formatting and actual output - control routines. */
1618 /*----------------------------------------------------------------------.
1619 | Output the current output fields as one line for `troff' or `nroff'. |
1620 `----------------------------------------------------------------------*/
1623 output_one_roff_line (void)
1625 /* Output the `tail' field. */
1627 printf (".%s \"", macro_name);
1629 if (tail_truncation)
1630 fputs (truncation_string, stdout);
1633 /* Output the `before' field. */
1635 fputs (" \"", stdout);
1636 if (before_truncation)
1637 fputs (truncation_string, stdout);
1638 print_field (before);
1641 /* Output the `keyafter' field. */
1643 fputs (" \"", stdout);
1644 print_field (keyafter);
1645 if (keyafter_truncation)
1646 fputs (truncation_string, stdout);
1649 /* Output the `head' field. */
1651 fputs (" \"", stdout);
1652 if (head_truncation)
1653 fputs (truncation_string, stdout);
1657 /* Conditionally output the `reference' field. */
1659 if (auto_reference || input_reference)
1661 fputs (" \"", stdout);
1662 print_field (reference);
1669 /*---------------------------------------------------------.
1670 | Output the current output fields as one line for `TeX'. |
1671 `---------------------------------------------------------*/
1674 output_one_tex_line (void)
1676 BLOCK key; /* key field, isolated */
1677 BLOCK after; /* after field, isolated */
1678 char *cursor; /* running cursor in source text */
1680 printf ("\\%s ", macro_name);
1681 fputs ("{", stdout);
1683 fputs ("}{", stdout);
1684 print_field (before);
1685 fputs ("}{", stdout);
1686 key.start = keyafter.start;
1687 after.end = keyafter.end;
1688 cursor = keyafter.start;
1689 SKIP_SOMETHING (cursor, keyafter.end);
1691 after.start = cursor;
1693 fputs ("}{", stdout);
1694 print_field (after);
1695 fputs ("}{", stdout);
1697 fputs ("}", stdout);
1698 if (auto_reference || input_reference)
1700 fputs ("{", stdout);
1701 print_field (reference);
1702 fputs ("}", stdout);
1704 fputs ("\n", stdout);
1707 /*-------------------------------------------------------------------.
1708 | Output the current output fields as one line for a dumb terminal. |
1709 `-------------------------------------------------------------------*/
1712 output_one_dumb_line (void)
1714 if (!right_reference)
1719 /* Output the `reference' field, in such a way that GNU emacs
1720 next-error will handle it. The ending colon is taken from the
1721 gap which follows. */
1723 print_field (reference);
1725 print_spaces (reference_max_width
1727 - (reference.end - reference.start)
1733 /* Output the `reference' field and its following gap. */
1735 print_field (reference);
1736 print_spaces (reference_max_width
1738 - (reference.end - reference.start));
1742 if (tail.start < tail.end)
1744 /* Output the `tail' field. */
1747 if (tail_truncation)
1748 fputs (truncation_string, stdout);
1750 print_spaces (half_line_width - gap_size
1751 - (before.end - before.start)
1752 - (before_truncation ? truncation_string_length : 0)
1753 - (tail.end - tail.start)
1754 - (tail_truncation ? truncation_string_length : 0));
1757 print_spaces (half_line_width - gap_size
1758 - (before.end - before.start)
1759 - (before_truncation ? truncation_string_length : 0));
1761 /* Output the `before' field. */
1763 if (before_truncation)
1764 fputs (truncation_string, stdout);
1765 print_field (before);
1767 print_spaces (gap_size);
1769 /* Output the `keyafter' field. */
1771 print_field (keyafter);
1772 if (keyafter_truncation)
1773 fputs (truncation_string, stdout);
1775 if (head.start < head.end)
1777 /* Output the `head' field. */
1779 print_spaces (half_line_width
1780 - (keyafter.end - keyafter.start)
1781 - (keyafter_truncation ? truncation_string_length : 0)
1782 - (head.end - head.start)
1783 - (head_truncation ? truncation_string_length : 0));
1784 if (head_truncation)
1785 fputs (truncation_string, stdout);
1790 if ((auto_reference || input_reference) && right_reference)
1791 print_spaces (half_line_width
1792 - (keyafter.end - keyafter.start)
1793 - (keyafter_truncation ? truncation_string_length : 0));
1795 if ((auto_reference || input_reference) && right_reference)
1797 /* Output the `reference' field. */
1799 print_spaces (gap_size);
1800 print_field (reference);
1803 fputs ("\n", stdout);
1806 /*------------------------------------------------------------------------.
1807 | Scan the whole occurs table and, for each entry, output one line in the |
1808 | appropriate format. |
1809 `------------------------------------------------------------------------*/
1812 generate_all_output (void)
1814 int occurs_index; /* index of keyword entry being processed */
1815 OCCURS *occurs_cursor; /* current keyword entry being processed */
1817 /* The following assignments are useful to provide default values in case
1818 line contexts or references are not used, in which case these variables
1819 would never be computed. */
1823 tail_truncation = 0;
1827 head_truncation = 0;
1829 /* Loop over all keyword occurrences. */
1831 occurs_cursor = occurs_table[0];
1833 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1835 /* Compute the exact size of every field and whenever truncation flags
1836 are present or not. */
1838 define_all_fields (occurs_cursor);
1840 /* Produce one output line according to selected format. */
1842 switch (output_format)
1844 case UNKNOWN_FORMAT:
1845 /* Should never happen. */
1848 output_one_dumb_line ();
1852 output_one_roff_line ();
1856 output_one_tex_line ();
1860 /* Advance the cursor into the occurs table. */
1866 /* Option decoding and main program. */
1868 /*------------------------------------------------------.
1869 | Print program identification and options, then exit. |
1870 `------------------------------------------------------*/
1875 if (status != EXIT_SUCCESS)
1876 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1881 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1882 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1883 program_name, program_name);
1885 Mandatory arguments to long options are mandatory for short options too.\n\
1887 -A, --auto-reference output automatically generated references\n\
1888 -C, --copyright display Copyright and copying conditions\n\
1889 -G, --traditional behave more like System V `ptx'\n\
1890 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1891 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1892 -O, --format=roff generate output as roff directives\n\
1893 -R, --right-side-refs put references at right, not counted in -w\n\
1894 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1895 -T, --format=tex generate output as TeX directives\n\
1896 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1897 -b, --break-file=FILE word break characters in this FILE\n\
1898 -f, --ignore-case fold lower case to upper case for sorting\n\
1899 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1900 -i, --ignore-file=FILE read ignore word list from FILE\n\
1901 -o, --only-file=FILE read only word list from this FILE\n\
1902 -r, --references first field of each line is a reference\n\
1903 -t, --typeset-mode - not implemented -\n\
1904 -w, --width=NUMBER output width in columns, reference excluded\n\
1905 --help display this help and exit\n\
1906 --version output version information and exit\n\
1908 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n"),
1914 /*----------------------------------------------------------------------.
1915 | Main program. Decode ARGC arguments passed through the ARGV array of |
1916 | strings, then launch execution. |
1917 `----------------------------------------------------------------------*/
1919 /* Long options equivalences. */
1920 static const struct option long_options[] =
1922 {"auto-reference", no_argument, NULL, 'A'},
1923 {"break-file", required_argument, NULL, 'b'},
1924 {"copyright", no_argument, NULL, 'C'},
1925 {"flag-truncation", required_argument, NULL, 'F'},
1926 {"ignore-case", no_argument, NULL, 'f'},
1927 {"gap-size", required_argument, NULL, 'g'},
1928 {"ignore-file", required_argument, NULL, 'i'},
1929 {"macro-name", required_argument, NULL, 'M'},
1930 {"only-file", required_argument, NULL, 'o'},
1931 {"references", no_argument, NULL, 'r'},
1932 {"right-side-refs", no_argument, NULL, 'R'},
1933 {"format", required_argument, NULL, 10},
1934 {"sentence-regexp", required_argument, NULL, 'S'},
1935 {"traditional", no_argument, NULL, 'G'},
1936 {"typeset-mode", no_argument, NULL, 't'},
1937 {"width", required_argument, NULL, 'w'},
1938 {"word-regexp", required_argument, NULL, 'W'},
1942 static char const* const format_args[] =
1947 static enum Format const format_vals[] =
1949 ROFF_FORMAT, TEX_FORMAT
1953 main (int argc, char **argv)
1955 int optchar; /* argument character */
1956 int file_index; /* index in text input file arrays */
1958 /* Decode program options. */
1960 program_name = argv[0];
1961 setlocale (LC_ALL, "");
1962 bindtextdomain (PACKAGE, LOCALEDIR);
1963 textdomain (PACKAGE);
1965 #if HAVE_SETCHRCLASS
1969 parse_long_options (argc, argv, "ptx", GNU_PACKAGE, VERSION,
1970 "François Pinard", usage);
1972 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1973 long_options, NULL),
1979 usage (EXIT_FAILURE);
1986 This program is free software; you can redistribute it and/or modify\n\
1987 it under the terms of the GNU General Public License as published by\n\
1988 the Free Software Foundation; either version 2, or (at your option)\n\
1989 any later version.\n\
1991 This program is distributed in the hope that it will be useful,\n\
1992 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
1993 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
1994 GNU General Public License for more details.\n\
1996 You should have received a copy of the GNU General Public License\n\
1997 along with this program; if not, write to the Free Software Foundation,\n\
1998 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
2001 exit (EXIT_SUCCESS);
2008 break_file = optarg;
2016 gap_size = atoi (optarg);
2020 ignore_file = optarg;
2028 input_reference = 1;
2032 /* Yet to understand... */
2036 line_width = atoi (optarg);
2044 truncation_string = copy_unescaped_string (optarg);
2048 macro_name = optarg;
2052 output_format = ROFF_FORMAT;
2056 right_reference = 1;
2060 context_regex_string = copy_unescaped_string (optarg);
2064 output_format = TEX_FORMAT;
2068 word_regex_string = copy_unescaped_string (optarg);
2072 output_format = XARGMATCH ("--format", optarg,
2073 format_args, format_vals);
2077 /* Change the default Ignore file if one is defined. */
2079 #ifdef DEFAULT_IGNORE_FILE
2081 ignore_file = DEFAULT_IGNORE_FILE;
2084 /* Process remaining arguments. If GNU extensions are enabled, process
2085 all arguments as input parameters. If disabled, accept at most two
2086 arguments, the second of which is an output parameter. */
2091 /* No more argument simply means: read standard input. */
2093 input_file_name = (const char **) xmalloc (sizeof (const char *));
2094 file_line_count = (int *) xmalloc (sizeof (int));
2095 number_input_files = 1;
2096 input_file_name[0] = NULL;
2098 else if (gnu_extensions)
2100 number_input_files = argc - optind;
2102 = (const char **) xmalloc (number_input_files * sizeof (const char *));
2104 = (int *) xmalloc (number_input_files * sizeof (int));
2106 for (file_index = 0; file_index < number_input_files; file_index++)
2108 input_file_name[file_index] = argv[optind];
2109 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2110 input_file_name[0] = NULL;
2112 input_file_name[0] = argv[optind];
2119 /* There is one necessary input file. */
2121 number_input_files = 1;
2122 input_file_name = (const char **) xmalloc (sizeof (const char *));
2123 file_line_count = (int *) xmalloc (sizeof (int));
2124 if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
2125 input_file_name[0] = NULL;
2127 input_file_name[0] = argv[optind];
2130 /* Redirect standard output, only if requested. */
2135 if (fopen (argv[optind], "w") == NULL)
2136 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2140 /* Diagnose any other argument as an error. */
2143 usage (EXIT_FAILURE);
2146 /* If the output format has not been explicitly selected, choose dumb
2147 terminal format if GNU extensions are enabled, else `roff' format. */
2149 if (output_format == UNKNOWN_FORMAT)
2150 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2152 /* Initialize the main tables. */
2154 initialize_regex ();
2156 /* Read `Break character' file, if any. */
2159 digest_break_file (break_file);
2161 /* Read `Ignore words' file and `Only words' files, if any. If any of
2162 these files is empty, reset the name of the file to NULL, to avoid
2163 unnecessary calls to search_table. */
2167 digest_word_file (ignore_file, &ignore_table);
2168 if (ignore_table.length == 0)
2174 digest_word_file (only_file, &only_table);
2175 if (only_table.length == 0)
2179 /* Prepare to study all the input files. */
2181 number_of_occurs[0] = 0;
2182 total_line_count = 0;
2183 maximum_word_length = 0;
2184 reference_max_width = 0;
2186 for (file_index = 0; file_index < number_input_files; file_index++)
2189 /* Read the file in core, than study it. */
2191 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2192 find_occurs_in_text ();
2194 /* Maintain for each file how many lines has been read so far when its
2195 end is reached. Incrementing the count first is a simple kludge to
2196 handle a possible incomplete line at end of file. */
2199 file_line_count[file_index] = total_line_count;
2202 /* Do the output process phase. */
2204 sort_found_occurs ();
2205 fix_output_parameters ();
2206 generate_all_output ();
2210 exit (EXIT_SUCCESS);