1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990-1991, 1993, 1998-2010 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 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU 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, see <http://www.gnu.org/licenses/>.
18 François Pinard <pinard@iro.umontreal.ca> */
23 #include <sys/types.h>
35 /* The official name of this program (e.g., no `g' prefix). */
36 #define PROGRAM_NAME "ptx"
38 /* TRANSLATORS: Please translate "F. Pinard" to "François Pinard"
39 if "ç" (c-with-cedilla) is available in the translation's character
41 #define AUTHORS proper_name_utf8 ("F. Pinard", "Fran\xc3\xa7ois Pinard")
43 /* Number of possible characters in a byte. */
44 #define CHAR_SET_SIZE 256
46 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
47 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
48 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
49 #define OCTTOBIN(C) ((C) - '0')
51 /* Debugging the memory allocator. */
54 # define MALLOC_FUNC_CHECK 1
58 /* Global definitions. */
60 /* FIXME: There are many unchecked integer overflows in this file,
61 that will cause this command to misbehave given large inputs or
62 options. Many of the "int" values below should be "size_t" or
63 something else like that. */
65 /* Reallocation step when swallowing non regular files. The value is not
66 the actual reallocation step, but its base two logarithm. */
67 #define SWALLOW_REALLOC_LOG 12
69 /* Program options. */
73 UNKNOWN_FORMAT
, /* output format still unknown */
74 DUMB_FORMAT
, /* output for a dumb terminal */
75 ROFF_FORMAT
, /* output for `troff' or `nroff' */
76 TEX_FORMAT
/* output for `TeX' or `LaTeX' */
79 static bool gnu_extensions
= true; /* trigger all GNU extensions */
80 static bool auto_reference
= false; /* refs are `file_name:line_number:' */
81 static bool input_reference
= false; /* refs at beginning of input lines */
82 static bool right_reference
= false; /* output refs after right context */
83 static int line_width
= 72; /* output line width in characters */
84 static int gap_size
= 3; /* number of spaces between output fields */
85 static const char *truncation_string
= "/";
86 /* string used to mark line truncations */
87 static const char *macro_name
= "xx"; /* macro name for roff or TeX output */
88 static enum Format output_format
= UNKNOWN_FORMAT
;
91 static bool ignore_case
= false; /* fold lower to upper for sorting */
92 static const char *break_file
= NULL
; /* name of the `Break characters' file */
93 static const char *only_file
= NULL
; /* name of the `Only words' file */
94 static const char *ignore_file
= NULL
; /* name of the `Ignore words' file */
96 /* Options that use regular expressions. */
99 /* The original regular expression, as a string. */
102 /* The compiled regular expression, and its fastmap. */
103 struct re_pattern_buffer pattern
;
104 char fastmap
[UCHAR_MAX
+ 1];
107 static struct regex_data context_regex
; /* end of context */
108 static struct regex_data word_regex
; /* keyword */
110 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
111 whole file. A WORD is something smaller, its length should fit in a
112 short integer. A WORD_TABLE may contain several WORDs. */
116 char *start
; /* pointer to beginning of region */
117 char *end
; /* pointer to end + 1 of region */
123 char *start
; /* pointer to beginning of region */
124 short int size
; /* length of the region */
130 WORD
*start
; /* array of WORDs */
131 size_t alloc
; /* allocated length */
132 size_t length
; /* number of used entries */
136 /* Pattern description tables. */
138 /* For each character, provide its folded equivalent. */
139 static unsigned char folded_chars
[CHAR_SET_SIZE
];
141 /* End of context pattern register indices. */
142 static struct re_registers context_regs
;
144 /* Keyword pattern register indices. */
145 static struct re_registers word_regs
;
147 /* A word characters fastmap is used only when no word regexp has been
148 provided. A word is then made up of a sequence of one or more characters
149 allowed by the fastmap. Contains !0 if character allowed in word. Not
150 only this is faster in most cases, but it simplifies the implementation
151 of the Break files. */
152 static char word_fastmap
[CHAR_SET_SIZE
];
154 /* Maximum length of any word read. */
155 static int maximum_word_length
;
157 /* Maximum width of any reference used. */
158 static int reference_max_width
;
160 /* Ignore and Only word tables. */
162 static WORD_TABLE ignore_table
; /* table of words to ignore */
163 static WORD_TABLE only_table
; /* table of words to select */
165 /* Source text table, and scanning macros. */
167 static int number_input_files
; /* number of text input files */
168 static int total_line_count
; /* total number of lines seen so far */
169 static const char **input_file_name
; /* array of text input file names */
170 static int *file_line_count
; /* array of `total_line_count' values at end */
172 static BLOCK text_buffer
; /* file to study */
174 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
176 #define SKIP_NON_WHITE(cursor, limit) \
177 while (cursor < limit && ! isspace (to_uchar (*cursor))) \
180 #define SKIP_WHITE(cursor, limit) \
181 while (cursor < limit && isspace (to_uchar (*cursor))) \
184 #define SKIP_WHITE_BACKWARDS(cursor, start) \
185 while (cursor > start && isspace (to_uchar (cursor[-1]))) \
188 #define SKIP_SOMETHING(cursor, limit) \
189 if (word_regex.string) \
192 count = re_match (&word_regex.pattern, cursor, limit - cursor, 0, NULL); \
195 cursor += count == -1 ? 1 : count; \
197 else if (word_fastmap[to_uchar (*cursor)]) \
198 while (cursor < limit && word_fastmap[to_uchar (*cursor)]) \
203 /* Occurrences table.
205 The `keyword' pointer provides the central word, which is surrounded
206 by a left context and a right context. The `keyword' and `length'
207 field allow full 8-bit characters keys, even including NULs. At other
208 places in this program, the name `keyafter' refers to the keyword
209 followed by its right context.
211 The left context does not extend, towards the beginning of the file,
212 further than a distance given by the `left' value. This value is
213 relative to the keyword beginning, it is usually negative. This
214 insures that, except for white space, we will never have to backward
215 scan the source text, when it is time to generate the final output
218 The right context, indirectly attainable through the keyword end, does
219 not extend, towards the end of the file, further than a distance given
220 by the `right' value. This value is relative to the keyword
221 beginning, it is usually positive.
223 When automatic references are used, the `reference' value is the
224 overall line number in all input files read so far, in this case, it
225 is of type (int). When input references are used, the `reference'
226 value indicates the distance between the keyword beginning and the
227 start of the reference field, it is of type (DELTA) and usually
230 typedef short int DELTA
; /* to hold displacement within one context */
234 WORD key
; /* description of the keyword */
235 DELTA left
; /* distance to left context start */
236 DELTA right
; /* distance to right context end */
237 int reference
; /* reference descriptor */
241 /* The various OCCURS tables are indexed by the language. But the time
242 being, there is no such multiple language support. */
244 static OCCURS
*occurs_table
[1]; /* all words retained from the read text */
245 static size_t occurs_alloc
[1]; /* allocated size of occurs_table */
246 static size_t number_of_occurs
[1]; /* number of used slots in occurs_table */
249 /* Communication among output routines. */
251 /* Indicate if special output processing is requested for each character. */
252 static char edited_flag
[CHAR_SET_SIZE
];
254 static int half_line_width
; /* half of line width, reference excluded */
255 static int before_max_width
; /* maximum width of before field */
256 static int keyafter_max_width
; /* maximum width of keyword-and-after field */
257 static int truncation_string_length
;/* length of string used to flag truncation */
259 /* When context is limited by lines, wraparound may happen on final output:
260 the `head' pointer gives access to some supplementary left context which
261 will be seen at the end of the output line, the `tail' pointer gives
262 access to some supplementary right context which will be seen at the
263 beginning of the output line. */
265 static BLOCK tail
; /* tail field */
266 static int tail_truncation
; /* flag truncation after the tail field */
268 static BLOCK before
; /* before field */
269 static int before_truncation
; /* flag truncation before the before field */
271 static BLOCK keyafter
; /* keyword-and-after field */
272 static int keyafter_truncation
; /* flag truncation after the keyafter field */
274 static BLOCK head
; /* head field */
275 static int head_truncation
; /* flag truncation before the head field */
277 static BLOCK reference
; /* reference field for input reference mode */
279 /* Miscellaneous routines. */
281 /* Diagnose an error in the regular expression matcher. Then exit. */
283 static void ATTRIBUTE_NORETURN
286 error (0, errno
, _("error in regular expression matcher"));
290 /*------------------------------------------------------.
291 | Duplicate string STRING, while evaluating \-escapes. |
292 `------------------------------------------------------*/
294 /* Loosely adapted from GNU sh-utils printf.c code. */
297 copy_unescaped_string (const char *string
)
299 char *result
; /* allocated result */
300 char *cursor
; /* cursor in result */
301 int value
; /* value of \nnn escape */
302 int length
; /* length of \nnn escape */
304 result
= xmalloc (strlen (string
) + 1);
314 case 'x': /* \xhhh escape, 3 chars maximum */
316 for (length
= 0, string
++;
317 length
< 3 && isxdigit (to_uchar (*string
));
319 value
= value
* 16 + HEXTOBIN (*string
);
329 case '0': /* \0ooo escape, 3 chars maximum */
331 for (length
= 0, string
++;
332 length
< 3 && ISODIGIT (*string
);
334 value
= value
* 8 + OCTTOBIN (*string
);
338 case 'a': /* alert */
347 case 'b': /* backspace */
352 case 'c': /* cancel the rest of the output */
357 case 'f': /* form feed */
362 case 'n': /* new line */
367 case 'r': /* carriage return */
372 case 't': /* horizontal tab */
377 case 'v': /* vertical tab */
386 case '\0': /* lone backslash at end of string */
392 *cursor
++ = *string
++;
397 *cursor
++ = *string
++;
404 /*--------------------------------------------------------------------------.
405 | Compile the regex represented by REGEX, diagnose and abort if any error. |
406 `--------------------------------------------------------------------------*/
409 compile_regex (struct regex_data
*regex
)
411 struct re_pattern_buffer
*pattern
= ®ex
->pattern
;
412 char const *string
= regex
->string
;
415 pattern
->buffer
= NULL
;
416 pattern
->allocated
= 0;
417 pattern
->fastmap
= regex
->fastmap
;
418 pattern
->translate
= ignore_case
? folded_chars
: NULL
;
420 message
= re_compile_pattern (string
, strlen (string
), pattern
);
422 error (EXIT_FAILURE
, 0, _("%s (for regexp %s)"), message
, quote (string
));
424 /* The fastmap should be compiled before `re_match'. The following
425 call is not mandatory, because `re_search' is always called sooner,
426 and it compiles the fastmap if this has not been done yet. */
428 re_compile_fastmap (pattern
);
431 /*------------------------------------------------------------------------.
432 | This will initialize various tables for pattern match and compiles some |
434 `------------------------------------------------------------------------*/
437 initialize_regex (void)
439 int character
; /* character value */
441 /* Initialize the case folding table. */
444 for (character
= 0; character
< CHAR_SET_SIZE
; character
++)
445 folded_chars
[character
] = toupper (character
);
447 /* Unless the user already provided a description of the end of line or
448 end of sentence sequence, select an end of line sequence to compile.
449 If the user provided an empty definition, thus disabling end of line
450 or sentence feature, make it NULL to speed up tests. If GNU
451 extensions are enabled, use end of sentence like in GNU emacs. If
452 disabled, use end of lines. */
454 if (context_regex
.string
)
456 if (!*context_regex
.string
)
457 context_regex
.string
= NULL
;
459 else if (gnu_extensions
&& !input_reference
)
460 context_regex
.string
= "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
462 context_regex
.string
= "\n";
464 if (context_regex
.string
)
465 compile_regex (&context_regex
);
467 /* If the user has already provided a non-empty regexp to describe
468 words, compile it. Else, unless this has already been done through
469 a user provided Break character file, construct a fastmap of
470 characters that may appear in a word. If GNU extensions enabled,
471 include only letters of the underlying character set. If disabled,
472 include almost everything, even punctuations; stop only on white
475 if (word_regex
.string
)
476 compile_regex (&word_regex
);
477 else if (!break_file
)
484 for (character
= 0; character
< CHAR_SET_SIZE
; character
++)
485 word_fastmap
[character
] = !! isalpha (character
);
490 /* Simulate [^ \t\n]+. */
492 memset (word_fastmap
, 1, CHAR_SET_SIZE
);
493 word_fastmap
[' '] = 0;
494 word_fastmap
['\t'] = 0;
495 word_fastmap
['\n'] = 0;
500 /*------------------------------------------------------------------------.
501 | This routine will attempt to swallow a whole file name FILE_NAME into a |
502 | contiguous region of memory and return a description of it into BLOCK. |
503 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
505 | Previously, in some cases, white space compression was attempted while |
506 | inputting text. This was defeating some regexps like default end of |
507 | sentence, which checks for two consecutive spaces. If white space |
508 | compression is ever reinstated, it should be in output routines. |
509 `------------------------------------------------------------------------*/
512 swallow_file_in_memory (const char *file_name
, BLOCK
*block
)
514 int file_handle
; /* file descriptor number */
515 struct stat stat_block
; /* stat block for file */
516 size_t allocated_length
; /* allocated length of memory buffer */
517 size_t used_length
; /* used length in memory buffer */
518 int read_length
; /* number of character gotten on last read */
520 /* As special cases, a file name which is NULL or "-" indicates standard
521 input, which is already opened. In all other cases, open the file from
523 bool using_stdin
= !file_name
|| !*file_name
|| STREQ (file_name
, "-");
525 file_handle
= STDIN_FILENO
;
527 if ((file_handle
= open (file_name
, O_RDONLY
)) < 0)
528 error (EXIT_FAILURE
, errno
, "%s", file_name
);
530 /* If the file is a plain, regular file, allocate the memory buffer all at
531 once and swallow the file in one blow. In other cases, read the file
532 repeatedly in smaller chunks until we have it all, reallocating memory
533 once in a while, as we go. */
535 if (fstat (file_handle
, &stat_block
) < 0)
536 error (EXIT_FAILURE
, errno
, "%s", file_name
);
538 if (S_ISREG (stat_block
.st_mode
))
540 size_t in_memory_size
;
542 fdadvise (file_handle
, 0, 0, FADVISE_SEQUENTIAL
);
544 block
->start
= xmalloc ((size_t) stat_block
.st_size
);
546 if ((in_memory_size
= read (file_handle
,
547 block
->start
, (size_t) stat_block
.st_size
))
548 != stat_block
.st_size
)
551 /* On MSDOS, in memory size may be smaller than the file
552 size, because of end of line conversions. But it can
553 never be smaller than half the file size, because the
554 minimum is when all lines are empty and terminated by
556 if (in_memory_size
!= (size_t)-1
557 && in_memory_size
>= stat_block
.st_size
/ 2)
558 block
->start
= xrealloc (block
->start
, in_memory_size
);
560 #endif /* not MSDOS */
562 error (EXIT_FAILURE
, errno
, "%s", file_name
);
564 block
->end
= block
->start
+ in_memory_size
;
568 block
->start
= xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG
);
570 allocated_length
= (1 << SWALLOW_REALLOC_LOG
);
572 while (read_length
= read (file_handle
,
573 block
->start
+ used_length
,
574 allocated_length
- used_length
),
577 used_length
+= read_length
;
578 if (used_length
== allocated_length
)
580 allocated_length
+= (1 << SWALLOW_REALLOC_LOG
);
582 = xrealloc (block
->start
, allocated_length
);
587 error (EXIT_FAILURE
, errno
, "%s", file_name
);
589 block
->end
= block
->start
+ used_length
;
592 /* Close the file, but only if it was not the standard input. */
594 if (! using_stdin
&& close (file_handle
) != 0)
595 error (EXIT_FAILURE
, errno
, "%s", file_name
);
598 /* Sort and search routines. */
600 /*--------------------------------------------------------------------------.
601 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
602 | Return less than 0 if the first word goes before the second; return |
603 | greater than 0 if the first word goes after the second. |
605 | If a word is indeed a prefix of the other, the shorter should go first. |
606 `--------------------------------------------------------------------------*/
609 compare_words (const void *void_first
, const void *void_second
)
611 #define first ((const WORD *) void_first)
612 #define second ((const WORD *) void_second)
613 int length
; /* minimum of two lengths */
614 int counter
; /* cursor in words */
615 int value
; /* value of comparison */
617 length
= first
->size
< second
->size
? first
->size
: second
->size
;
621 for (counter
= 0; counter
< length
; counter
++)
623 value
= (folded_chars
[to_uchar (first
->start
[counter
])]
624 - folded_chars
[to_uchar (second
->start
[counter
])]);
631 for (counter
= 0; counter
< length
; counter
++)
633 value
= (to_uchar (first
->start
[counter
])
634 - to_uchar (second
->start
[counter
]));
640 return first
->size
- second
->size
;
645 /*-----------------------------------------------------------------------.
646 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
647 | go first. In case of a tie, preserve the original order through a |
648 | pointer comparison. |
649 `-----------------------------------------------------------------------*/
652 compare_occurs (const void *void_first
, const void *void_second
)
654 #define first ((const OCCURS *) void_first)
655 #define second ((const OCCURS *) void_second)
658 value
= compare_words (&first
->key
, &second
->key
);
659 return value
== 0 ? first
->key
.start
- second
->key
.start
: value
;
664 /*------------------------------------------------------------.
665 | Return !0 if WORD appears in TABLE. Uses a binary search. |
666 `------------------------------------------------------------*/
669 search_table (WORD
*word
, WORD_TABLE
*table
)
671 int lowest
; /* current lowest possible index */
672 int highest
; /* current highest possible index */
673 int middle
; /* current middle index */
674 int value
; /* value from last comparison */
677 highest
= table
->length
- 1;
678 while (lowest
<= highest
)
680 middle
= (lowest
+ highest
) / 2;
681 value
= compare_words (word
, table
->start
+ middle
);
683 highest
= middle
- 1;
692 /*---------------------------------------------------------------------.
693 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
694 | take intermediate copies or table elements, so the sort will be |
695 | stabilized throughout the comparison routine. |
696 `---------------------------------------------------------------------*/
699 sort_found_occurs (void)
702 /* Only one language for the time being. */
704 qsort (occurs_table
[0], number_of_occurs
[0], sizeof **occurs_table
,
708 /* Parameter files reading routines. */
710 /*----------------------------------------------------------------------.
711 | Read a file named FILE_NAME, containing a set of break characters. |
712 | Build a content to the array word_fastmap in which all characters are |
713 | allowed except those found in the file. Characters may be repeated. |
714 `----------------------------------------------------------------------*/
717 digest_break_file (const char *file_name
)
719 BLOCK file_contents
; /* to receive a copy of the file */
720 char *cursor
; /* cursor in file copy */
722 swallow_file_in_memory (file_name
, &file_contents
);
724 /* Make the fastmap and record the file contents in it. */
726 memset (word_fastmap
, 1, CHAR_SET_SIZE
);
727 for (cursor
= file_contents
.start
; cursor
< file_contents
.end
; cursor
++)
728 word_fastmap
[to_uchar (*cursor
)] = 0;
733 /* If GNU extensions are enabled, the only way to avoid newline as
734 a break character is to write all the break characters in the
735 file with no newline at all, not even at the end of the file.
736 If disabled, spaces, tabs and newlines are always considered as
737 break characters even if not included in the break file. */
739 word_fastmap
[' '] = 0;
740 word_fastmap
['\t'] = 0;
741 word_fastmap
['\n'] = 0;
744 /* Return the space of the file, which is no more required. */
746 free (file_contents
.start
);
749 /*-----------------------------------------------------------------------.
750 | Read a file named FILE_NAME, containing one word per line, then |
751 | construct in TABLE a table of WORD descriptors for them. The routine |
752 | swallows the whole file in memory; this is at the expense of space |
753 | needed for newlines, which are useless; however, the reading is fast. |
754 `-----------------------------------------------------------------------*/
757 digest_word_file (const char *file_name
, WORD_TABLE
*table
)
759 BLOCK file_contents
; /* to receive a copy of the file */
760 char *cursor
; /* cursor in file copy */
761 char *word_start
; /* start of the current word */
763 swallow_file_in_memory (file_name
, &file_contents
);
769 /* Read the whole file. */
771 cursor
= file_contents
.start
;
772 while (cursor
< file_contents
.end
)
775 /* Read one line, and save the word in contains. */
778 while (cursor
< file_contents
.end
&& *cursor
!= '\n')
781 /* Record the word in table if it is not empty. */
783 if (cursor
> word_start
)
785 if (table
->length
== table
->alloc
)
787 if ((SIZE_MAX
/ sizeof *table
->start
- 1) / 2 < table
->alloc
)
789 table
->alloc
= table
->alloc
* 2 + 1;
790 table
->start
= xrealloc (table
->start
,
791 table
->alloc
* sizeof *table
->start
);
794 table
->start
[table
->length
].start
= word_start
;
795 table
->start
[table
->length
].size
= cursor
- word_start
;
799 /* This test allows for an incomplete line at end of file. */
801 if (cursor
< file_contents
.end
)
805 /* Finally, sort all the words read. */
807 qsort (table
->start
, table
->length
, sizeof table
->start
[0], compare_words
);
810 /* Keyword recognition and selection. */
812 /*----------------------------------------------------------------------.
813 | For each keyword in the source text, constructs an OCCURS structure. |
814 `----------------------------------------------------------------------*/
817 find_occurs_in_text (void)
819 char *cursor
; /* for scanning the source text */
820 char *scan
; /* for scanning the source text also */
821 char *line_start
; /* start of the current input line */
822 char *line_scan
; /* newlines scanned until this point */
823 int reference_length
; /* length of reference in input mode */
824 WORD possible_key
; /* possible key, to ease searches */
825 OCCURS
*occurs_cursor
; /* current OCCURS under construction */
827 char *context_start
; /* start of left context */
828 char *context_end
; /* end of right context */
829 char *word_start
; /* start of word */
830 char *word_end
; /* end of word */
831 char *next_context_start
; /* next start of left context */
833 /* reference_length is always used within `if (input_reference)'.
834 However, GNU C diagnoses that it may be used uninitialized. The
835 following assignment is merely to shut it up. */
837 reference_length
= 0;
839 /* Tracking where lines start is helpful for reference processing. In
840 auto reference mode, this allows counting lines. In input reference
841 mode, this permits finding the beginning of the references.
843 The first line begins with the file, skip immediately this very first
844 reference in input reference mode, to help further rejection any word
845 found inside it. Also, unconditionally assigning these variable has
846 the happy effect of shutting up lint. */
848 line_start
= text_buffer
.start
;
849 line_scan
= line_start
;
852 SKIP_NON_WHITE (line_scan
, text_buffer
.end
);
853 reference_length
= line_scan
- line_start
;
854 SKIP_WHITE (line_scan
, text_buffer
.end
);
857 /* Process the whole buffer, one line or one sentence at a time. */
859 for (cursor
= text_buffer
.start
;
860 cursor
< text_buffer
.end
;
861 cursor
= next_context_start
)
864 /* `context_start' gets initialized before the processing of each
865 line, or once for the whole buffer if no end of line or sentence
866 sequence separator. */
868 context_start
= cursor
;
870 /* If a end of line or end of sentence sequence is defined and
871 non-empty, `next_context_start' will be recomputed to be the end of
872 each line or sentence, before each one is processed. If no such
873 sequence, then `next_context_start' is set at the end of the whole
874 buffer, which is then considered to be a single line or sentence.
875 This test also accounts for the case of an incomplete line or
876 sentence at the end of the buffer. */
878 next_context_start
= text_buffer
.end
;
879 if (context_regex
.string
)
880 switch (re_search (&context_regex
.pattern
, cursor
,
881 text_buffer
.end
- cursor
,
882 0, text_buffer
.end
- cursor
, &context_regs
))
891 next_context_start
= cursor
+ context_regs
.end
[0];
895 /* Include the separator into the right context, but not any suffix
896 white space in this separator; this insures it will be seen in
897 output and will not take more space than necessary. */
899 context_end
= next_context_start
;
900 SKIP_WHITE_BACKWARDS (context_end
, context_start
);
902 /* Read and process a single input line or sentence, one word at a
907 if (word_regex
.string
)
909 /* If a word regexp has been compiled, use it to skip at the
910 beginning of the next word. If there is no such word, exit
914 regoff_t r
= re_search (&word_regex
.pattern
, cursor
,
915 context_end
- cursor
,
916 0, context_end
- cursor
, &word_regs
);
921 word_start
= cursor
+ word_regs
.start
[0];
922 word_end
= cursor
+ word_regs
.end
[0];
926 /* Avoid re_search and use the fastmap to skip to the
927 beginning of the next word. If there is no more word in
928 the buffer, exit the loop. */
932 while (scan
< context_end
933 && !word_fastmap
[to_uchar (*scan
)])
936 if (scan
== context_end
)
941 while (scan
< context_end
942 && word_fastmap
[to_uchar (*scan
)])
948 /* Skip right to the beginning of the found word. */
952 /* Skip any zero length word. Just advance a single position,
953 then go fetch the next word. */
955 if (word_end
== word_start
)
961 /* This is a genuine, non empty word, so save it as a possible
962 key. Then skip over it. Also, maintain the maximum length of
963 all words read so far. It is mandatory to take the maximum
964 length of all words in the file, without considering if they
965 are actually kept or rejected, because backward jumps at output
966 generation time may fall in *any* word. */
968 possible_key
.start
= cursor
;
969 possible_key
.size
= word_end
- word_start
;
970 cursor
+= possible_key
.size
;
972 if (possible_key
.size
> maximum_word_length
)
973 maximum_word_length
= possible_key
.size
;
975 /* In input reference mode, update `line_start' from its previous
976 value. Count the lines just in case auto reference mode is
977 also selected. If it happens that the word just matched is
978 indeed part of a reference; just ignore it. */
982 while (line_scan
< possible_key
.start
)
983 if (*line_scan
== '\n')
987 line_start
= line_scan
;
988 SKIP_NON_WHITE (line_scan
, text_buffer
.end
);
989 reference_length
= line_scan
- line_start
;
993 if (line_scan
> possible_key
.start
)
997 /* Ignore the word if an `Ignore words' table exists and if it is
998 part of it. Also ignore the word if an `Only words' table and
999 if it is *not* part of it.
1001 It is allowed that both tables be used at once, even if this
1002 may look strange for now. Just ignore a word that would appear
1003 in both. If regexps are eventually implemented for these
1004 tables, the Ignore table could then reject words that would
1005 have been previously accepted by the Only table. */
1007 if (ignore_file
&& search_table (&possible_key
, &ignore_table
))
1009 if (only_file
&& !search_table (&possible_key
, &only_table
))
1012 /* A non-empty word has been found. First of all, insure
1013 proper allocation of the next OCCURS, and make a pointer to
1014 where it will be constructed. */
1016 if (number_of_occurs
[0] == occurs_alloc
[0])
1018 if ((SIZE_MAX
/ sizeof *occurs_table
[0] - 1) / 2
1021 occurs_alloc
[0] = occurs_alloc
[0] * 2 + 1;
1022 occurs_table
[0] = xrealloc (occurs_table
[0],
1023 occurs_alloc
[0] * sizeof *occurs_table
[0]);
1026 occurs_cursor
= occurs_table
[0] + number_of_occurs
[0];
1028 /* Define the refence field, if any. */
1033 /* While auto referencing, update `line_start' from its
1034 previous value, counting lines as we go. If input
1035 referencing at the same time, `line_start' has been
1036 advanced earlier, and the following loop is never really
1039 while (line_scan
< possible_key
.start
)
1040 if (*line_scan
== '\n')
1044 line_start
= line_scan
;
1045 SKIP_NON_WHITE (line_scan
, text_buffer
.end
);
1050 occurs_cursor
->reference
= total_line_count
;
1052 else if (input_reference
)
1055 /* If only input referencing, `line_start' has been computed
1056 earlier to detect the case the word matched would be part
1057 of the reference. The reference position is simply the
1058 value of `line_start'. */
1060 occurs_cursor
->reference
1061 = (DELTA
) (line_start
- possible_key
.start
);
1062 if (reference_length
> reference_max_width
)
1063 reference_max_width
= reference_length
;
1066 /* Exclude the reference from the context in simple cases. */
1068 if (input_reference
&& line_start
== context_start
)
1070 SKIP_NON_WHITE (context_start
, context_end
);
1071 SKIP_WHITE (context_start
, context_end
);
1074 /* Completes the OCCURS structure. */
1076 occurs_cursor
->key
= possible_key
;
1077 occurs_cursor
->left
= context_start
- possible_key
.start
;
1078 occurs_cursor
->right
= context_end
- possible_key
.start
;
1080 number_of_occurs
[0]++;
1085 /* Formatting and actual output - service routines. */
1087 /*-----------------------------------------.
1088 | Prints some NUMBER of spaces on stdout. |
1089 `-----------------------------------------*/
1092 print_spaces (int number
)
1096 for (counter
= number
; counter
> 0; counter
--)
1100 /*-------------------------------------.
1101 | Prints the field provided by FIELD. |
1102 `-------------------------------------*/
1105 print_field (BLOCK field
)
1107 char *cursor
; /* Cursor in field to print */
1108 int base
; /* Base character, without diacritic */
1109 int diacritic
; /* Diacritic code for the character */
1111 /* Whitespace is not really compressed. Instead, each white space
1112 character (tab, vt, ht etc.) is printed as one single space. */
1114 for (cursor
= field
.start
; cursor
< field
.end
; cursor
++)
1116 unsigned char character
= *cursor
;
1117 if (edited_flag
[character
])
1120 /* First check if this is a diacriticized character.
1122 This works only for TeX. I do not know how diacriticized
1123 letters work with `roff'. Please someone explain it to me! */
1125 diacritic
= todiac (character
);
1126 if (diacritic
!= 0 && output_format
== TEX_FORMAT
)
1128 base
= tobase (character
);
1132 case 1: /* Latin diphthongs */
1136 fputs ("\\oe{}", stdout
);
1140 fputs ("\\OE{}", stdout
);
1144 fputs ("\\ae{}", stdout
);
1148 fputs ("\\AE{}", stdout
);
1156 case 2: /* Acute accent */
1157 printf ("\\'%s%c", (base
== 'i' ? "\\" : ""), base
);
1160 case 3: /* Grave accent */
1161 printf ("\\`%s%c", (base
== 'i' ? "\\" : ""), base
);
1164 case 4: /* Circumflex accent */
1165 printf ("\\^%s%c", (base
== 'i' ? "\\" : ""), base
);
1168 case 5: /* Diaeresis */
1169 printf ("\\\"%s%c", (base
== 'i' ? "\\" : ""), base
);
1172 case 6: /* Tilde accent */
1173 printf ("\\~%s%c", (base
== 'i' ? "\\" : ""), base
);
1176 case 7: /* Cedilla */
1177 printf ("\\c{%c}", base
);
1180 case 8: /* Small circle beneath */
1184 fputs ("\\aa{}", stdout
);
1188 fputs ("\\AA{}", stdout
);
1196 case 9: /* Strike through */
1200 fputs ("\\o{}", stdout
);
1204 fputs ("\\O{}", stdout
);
1215 /* This is not a diacritic character, so handle cases which are
1216 really specific to `roff' or TeX. All white space processing
1217 is done as the default case of this switch. */
1222 /* In roff output format, double any quote. */
1232 /* In TeX output format, precede these with a backslash. */
1234 putchar (character
);
1239 /* In TeX output format, precede these with a backslash and
1240 force mathematical mode. */
1241 printf ("$\\%c$", character
);
1245 /* In TeX output mode, request production of a backslash. */
1246 fputs ("\\backslash{}", stdout
);
1250 /* Any other flagged character produces a single space. */
1259 /* Formatting and actual output - planning routines. */
1261 /*--------------------------------------------------------------------.
1262 | From information collected from command line options and input file |
1263 | readings, compute and fix some output parameter values. |
1264 `--------------------------------------------------------------------*/
1267 fix_output_parameters (void)
1269 int file_index
; /* index in text input file arrays */
1270 int line_ordinal
; /* line ordinal value for reference */
1271 char ordinal_string
[12]; /* edited line ordinal for reference */
1272 int reference_width
; /* width for the whole reference */
1273 int character
; /* character ordinal */
1274 const char *cursor
; /* cursor in some constant strings */
1276 /* In auto reference mode, the maximum width of this field is
1277 precomputed and subtracted from the overall line width. Add one for
1278 the column which separate the file name from the line number. */
1282 reference_max_width
= 0;
1283 for (file_index
= 0; file_index
< number_input_files
; file_index
++)
1285 line_ordinal
= file_line_count
[file_index
] + 1;
1287 line_ordinal
-= file_line_count
[file_index
- 1];
1288 sprintf (ordinal_string
, "%d", line_ordinal
);
1289 reference_width
= strlen (ordinal_string
);
1290 if (input_file_name
[file_index
])
1291 reference_width
+= strlen (input_file_name
[file_index
]);
1292 if (reference_width
> reference_max_width
)
1293 reference_max_width
= reference_width
;
1295 reference_max_width
++;
1296 reference
.start
= xmalloc ((size_t) reference_max_width
+ 1);
1299 /* If the reference appears to the left of the output line, reserve some
1300 space for it right away, including one gap size. */
1302 if ((auto_reference
|| input_reference
) && !right_reference
)
1303 line_width
-= reference_max_width
+ gap_size
;
1305 /* The output lines, minimally, will contain from left to right a left
1306 context, a gap, and a keyword followed by the right context with no
1307 special intervening gap. Half of the line width is dedicated to the
1308 left context and the gap, the other half is dedicated to the keyword
1309 and the right context; these values are computed once and for all here.
1310 There also are tail and head wrap around fields, used when the keyword
1311 is near the beginning or the end of the line, or when some long word
1312 cannot fit in, but leave place from wrapped around shorter words. The
1313 maximum width of these fields are recomputed separately for each line,
1314 on a case by case basis. It is worth noting that it cannot happen that
1315 both the tail and head fields are used at once. */
1317 half_line_width
= line_width
/ 2;
1318 before_max_width
= half_line_width
- gap_size
;
1319 keyafter_max_width
= half_line_width
;
1321 /* If truncation_string is the empty string, make it NULL to speed up
1322 tests. In this case, truncation_string_length will never get used, so
1323 there is no need to set it. */
1325 if (truncation_string
&& *truncation_string
)
1326 truncation_string_length
= strlen (truncation_string
);
1328 truncation_string
= NULL
;
1333 /* When flagging truncation at the left of the keyword, the
1334 truncation mark goes at the beginning of the before field,
1335 unless there is a head field, in which case the mark goes at the
1336 left of the head field. When flagging truncation at the right
1337 of the keyword, the mark goes at the end of the keyafter field,
1338 unless there is a tail field, in which case the mark goes at the
1339 end of the tail field. Only eight combination cases could arise
1340 for truncation marks:
1343 . One beginning the before field.
1344 . One beginning the head field.
1345 . One ending the keyafter field.
1346 . One ending the tail field.
1347 . One beginning the before field, another ending the keyafter field.
1348 . One ending the tail field, another beginning the before field.
1349 . One ending the keyafter field, another beginning the head field.
1351 So, there is at most two truncation marks, which could appear both
1352 on the left side of the center of the output line, both on the
1353 right side, or one on either side. */
1355 before_max_width
-= 2 * truncation_string_length
;
1356 if (before_max_width
< 0)
1357 before_max_width
= 0;
1358 keyafter_max_width
-= 2 * truncation_string_length
;
1363 /* I never figured out exactly how UNIX' ptx plans the output width
1364 of its various fields. If GNU extensions are disabled, do not
1365 try computing the field widths correctly; instead, use the
1366 following formula, which does not completely imitate UNIX' ptx,
1369 keyafter_max_width
-= 2 * truncation_string_length
+ 1;
1372 /* Compute which characters need special output processing. Initialize
1373 by flagging any white space character. Some systems do not consider
1374 form feed as a space character, but we do. */
1376 for (character
= 0; character
< CHAR_SET_SIZE
; character
++)
1377 edited_flag
[character
] = !! isspace (character
);
1378 edited_flag
['\f'] = 1;
1380 /* Complete the special character flagging according to selected output
1383 switch (output_format
)
1385 case UNKNOWN_FORMAT
:
1386 /* Should never happen. */
1393 /* `Quote' characters should be doubled. */
1395 edited_flag
['"'] = 1;
1400 /* Various characters need special processing. */
1402 for (cursor
= "$%&#_{}\\"; *cursor
; cursor
++)
1403 edited_flag
[to_uchar (*cursor
)] = 1;
1405 /* Any character with 8th bit set will print to a single space, unless
1406 it is diacriticized. */
1408 for (character
= 0200; character
< CHAR_SET_SIZE
; character
++)
1409 edited_flag
[character
] = todiac (character
) != 0;
1414 /*------------------------------------------------------------------.
1415 | Compute the position and length of all the output fields, given a |
1416 | pointer to some OCCURS. |
1417 `------------------------------------------------------------------*/
1420 define_all_fields (OCCURS
*occurs
)
1422 int tail_max_width
; /* allowable width of tail field */
1423 int head_max_width
; /* allowable width of head field */
1424 char *cursor
; /* running cursor in source text */
1425 char *left_context_start
; /* start of left context */
1426 char *right_context_end
; /* end of right context */
1427 char *left_field_start
; /* conservative start for `head'/`before' */
1428 int file_index
; /* index in text input file arrays */
1429 const char *file_name
; /* file name for reference */
1430 int line_ordinal
; /* line ordinal for reference */
1432 /* Define `keyafter', start of left context and end of right context.
1433 `keyafter' starts at the saved position for keyword and extend to the
1434 right from the end of the keyword, eating separators or full words, but
1435 not beyond maximum allowed width for `keyafter' field or limit for the
1436 right context. Suffix spaces will be removed afterwards. */
1438 keyafter
.start
= occurs
->key
.start
;
1439 keyafter
.end
= keyafter
.start
+ occurs
->key
.size
;
1440 left_context_start
= keyafter
.start
+ occurs
->left
;
1441 right_context_end
= keyafter
.start
+ occurs
->right
;
1443 cursor
= keyafter
.end
;
1444 while (cursor
< right_context_end
1445 && cursor
<= keyafter
.start
+ keyafter_max_width
)
1447 keyafter
.end
= cursor
;
1448 SKIP_SOMETHING (cursor
, right_context_end
);
1450 if (cursor
<= keyafter
.start
+ keyafter_max_width
)
1451 keyafter
.end
= cursor
;
1453 keyafter_truncation
= truncation_string
&& keyafter
.end
< right_context_end
;
1455 SKIP_WHITE_BACKWARDS (keyafter
.end
, keyafter
.start
);
1457 /* When the left context is wide, it might take some time to catch up from
1458 the left context boundary to the beginning of the `head' or `before'
1459 fields. So, in this case, to speed the catchup, we jump back from the
1460 keyword, using some secure distance, possibly falling in the middle of
1461 a word. A secure backward jump would be at least half the maximum
1462 width of a line, plus the size of the longest word met in the whole
1463 input. We conclude this backward jump by a skip forward of at least
1464 one word. In this manner, we should not inadvertently accept only part
1465 of a word. From the reached point, when it will be time to fix the
1466 beginning of `head' or `before' fields, we will skip forward words or
1467 delimiters until we get sufficiently near. */
1469 if (-occurs
->left
> half_line_width
+ maximum_word_length
)
1472 = keyafter
.start
- (half_line_width
+ maximum_word_length
);
1473 SKIP_SOMETHING (left_field_start
, keyafter
.start
);
1476 left_field_start
= keyafter
.start
+ occurs
->left
;
1478 /* `before' certainly ends at the keyword, but not including separating
1479 spaces. It starts after than the saved value for the left context, by
1480 advancing it until it falls inside the maximum allowed width for the
1481 before field. There will be no prefix spaces either. `before' only
1482 advances by skipping single separators or whole words. */
1484 before
.start
= left_field_start
;
1485 before
.end
= keyafter
.start
;
1486 SKIP_WHITE_BACKWARDS (before
.end
, before
.start
);
1488 while (before
.start
+ before_max_width
< before
.end
)
1489 SKIP_SOMETHING (before
.start
, before
.end
);
1491 if (truncation_string
)
1493 cursor
= before
.start
;
1494 SKIP_WHITE_BACKWARDS (cursor
, text_buffer
.start
);
1495 before_truncation
= cursor
> left_context_start
;
1498 before_truncation
= 0;
1500 SKIP_WHITE (before
.start
, text_buffer
.end
);
1502 /* The tail could not take more columns than what has been left in the
1503 left context field, and a gap is mandatory. It starts after the
1504 right context, and does not contain prefixed spaces. It ends at
1505 the end of line, the end of buffer or when the tail field is full,
1506 whichever comes first. It cannot contain only part of a word, and
1507 has no suffixed spaces. */
1510 = before_max_width
- (before
.end
- before
.start
) - gap_size
;
1512 if (tail_max_width
> 0)
1514 tail
.start
= keyafter
.end
;
1515 SKIP_WHITE (tail
.start
, text_buffer
.end
);
1517 tail
.end
= tail
.start
;
1519 while (cursor
< right_context_end
1520 && cursor
< tail
.start
+ tail_max_width
)
1523 SKIP_SOMETHING (cursor
, right_context_end
);
1526 if (cursor
< tail
.start
+ tail_max_width
)
1529 if (tail
.end
> tail
.start
)
1531 keyafter_truncation
= 0;
1532 tail_truncation
= truncation_string
&& tail
.end
< right_context_end
;
1535 tail_truncation
= 0;
1537 SKIP_WHITE_BACKWARDS (tail
.end
, tail
.start
);
1542 /* No place left for a tail field. */
1546 tail_truncation
= 0;
1549 /* `head' could not take more columns than what has been left in the right
1550 context field, and a gap is mandatory. It ends before the left
1551 context, and does not contain suffixed spaces. Its pointer is advanced
1552 until the head field has shrunk to its allowed width. It cannot
1553 contain only part of a word, and has no suffixed spaces. */
1556 = keyafter_max_width
- (keyafter
.end
- keyafter
.start
) - gap_size
;
1558 if (head_max_width
> 0)
1560 head
.end
= before
.start
;
1561 SKIP_WHITE_BACKWARDS (head
.end
, text_buffer
.start
);
1563 head
.start
= left_field_start
;
1564 while (head
.start
+ head_max_width
< head
.end
)
1565 SKIP_SOMETHING (head
.start
, head
.end
);
1567 if (head
.end
> head
.start
)
1569 before_truncation
= 0;
1570 head_truncation
= (truncation_string
1571 && head
.start
> left_context_start
);
1574 head_truncation
= 0;
1576 SKIP_WHITE (head
.start
, head
.end
);
1581 /* No place left for a head field. */
1585 head_truncation
= 0;
1591 /* Construct the reference text in preallocated space from the file
1592 name and the line number. Find out in which file the reference
1593 occurred. Standard input yields an empty file name. Insure line
1594 numbers are one based, even if they are computed zero based. */
1597 while (file_line_count
[file_index
] < occurs
->reference
)
1600 file_name
= input_file_name
[file_index
];
1604 line_ordinal
= occurs
->reference
+ 1;
1606 line_ordinal
-= file_line_count
[file_index
- 1];
1608 sprintf (reference
.start
, "%s:%d", file_name
, line_ordinal
);
1609 reference
.end
= reference
.start
+ strlen (reference
.start
);
1611 else if (input_reference
)
1614 /* Reference starts at saved position for reference and extends right
1615 until some white space is met. */
1617 reference
.start
= keyafter
.start
+ (DELTA
) occurs
->reference
;
1618 reference
.end
= reference
.start
;
1619 SKIP_NON_WHITE (reference
.end
, right_context_end
);
1623 /* Formatting and actual output - control routines. */
1625 /*----------------------------------------------------------------------.
1626 | Output the current output fields as one line for `troff' or `nroff'. |
1627 `----------------------------------------------------------------------*/
1630 output_one_roff_line (void)
1632 /* Output the `tail' field. */
1634 printf (".%s \"", macro_name
);
1636 if (tail_truncation
)
1637 fputs (truncation_string
, stdout
);
1640 /* Output the `before' field. */
1642 fputs (" \"", stdout
);
1643 if (before_truncation
)
1644 fputs (truncation_string
, stdout
);
1645 print_field (before
);
1648 /* Output the `keyafter' field. */
1650 fputs (" \"", stdout
);
1651 print_field (keyafter
);
1652 if (keyafter_truncation
)
1653 fputs (truncation_string
, stdout
);
1656 /* Output the `head' field. */
1658 fputs (" \"", stdout
);
1659 if (head_truncation
)
1660 fputs (truncation_string
, stdout
);
1664 /* Conditionally output the `reference' field. */
1666 if (auto_reference
|| input_reference
)
1668 fputs (" \"", stdout
);
1669 print_field (reference
);
1676 /*---------------------------------------------------------.
1677 | Output the current output fields as one line for `TeX'. |
1678 `---------------------------------------------------------*/
1681 output_one_tex_line (void)
1683 BLOCK key
; /* key field, isolated */
1684 BLOCK after
; /* after field, isolated */
1685 char *cursor
; /* running cursor in source text */
1687 printf ("\\%s ", macro_name
);
1690 fputs ("}{", stdout
);
1691 print_field (before
);
1692 fputs ("}{", stdout
);
1693 key
.start
= keyafter
.start
;
1694 after
.end
= keyafter
.end
;
1695 cursor
= keyafter
.start
;
1696 SKIP_SOMETHING (cursor
, keyafter
.end
);
1698 after
.start
= cursor
;
1700 fputs ("}{", stdout
);
1701 print_field (after
);
1702 fputs ("}{", stdout
);
1705 if (auto_reference
|| input_reference
)
1708 print_field (reference
);
1714 /*-------------------------------------------------------------------.
1715 | Output the current output fields as one line for a dumb terminal. |
1716 `-------------------------------------------------------------------*/
1719 output_one_dumb_line (void)
1721 if (!right_reference
)
1726 /* Output the `reference' field, in such a way that GNU emacs
1727 next-error will handle it. The ending colon is taken from the
1728 gap which follows. */
1730 print_field (reference
);
1732 print_spaces (reference_max_width
1734 - (reference
.end
- reference
.start
)
1740 /* Output the `reference' field and its following gap. */
1742 print_field (reference
);
1743 print_spaces (reference_max_width
1745 - (reference
.end
- reference
.start
));
1749 if (tail
.start
< tail
.end
)
1751 /* Output the `tail' field. */
1754 if (tail_truncation
)
1755 fputs (truncation_string
, stdout
);
1757 print_spaces (half_line_width
- gap_size
1758 - (before
.end
- before
.start
)
1759 - (before_truncation
? truncation_string_length
: 0)
1760 - (tail
.end
- tail
.start
)
1761 - (tail_truncation
? truncation_string_length
: 0));
1764 print_spaces (half_line_width
- gap_size
1765 - (before
.end
- before
.start
)
1766 - (before_truncation
? truncation_string_length
: 0));
1768 /* Output the `before' field. */
1770 if (before_truncation
)
1771 fputs (truncation_string
, stdout
);
1772 print_field (before
);
1774 print_spaces (gap_size
);
1776 /* Output the `keyafter' field. */
1778 print_field (keyafter
);
1779 if (keyafter_truncation
)
1780 fputs (truncation_string
, stdout
);
1782 if (head
.start
< head
.end
)
1784 /* Output the `head' field. */
1786 print_spaces (half_line_width
1787 - (keyafter
.end
- keyafter
.start
)
1788 - (keyafter_truncation
? truncation_string_length
: 0)
1789 - (head
.end
- head
.start
)
1790 - (head_truncation
? truncation_string_length
: 0));
1791 if (head_truncation
)
1792 fputs (truncation_string
, stdout
);
1797 if ((auto_reference
|| input_reference
) && right_reference
)
1798 print_spaces (half_line_width
1799 - (keyafter
.end
- keyafter
.start
)
1800 - (keyafter_truncation
? truncation_string_length
: 0));
1802 if ((auto_reference
|| input_reference
) && right_reference
)
1804 /* Output the `reference' field. */
1806 print_spaces (gap_size
);
1807 print_field (reference
);
1813 /*------------------------------------------------------------------------.
1814 | Scan the whole occurs table and, for each entry, output one line in the |
1815 | appropriate format. |
1816 `------------------------------------------------------------------------*/
1819 generate_all_output (void)
1821 size_t occurs_index
; /* index of keyword entry being processed */
1822 OCCURS
*occurs_cursor
; /* current keyword entry being processed */
1824 /* The following assignments are useful to provide default values in case
1825 line contexts or references are not used, in which case these variables
1826 would never be computed. */
1830 tail_truncation
= 0;
1834 head_truncation
= 0;
1836 /* Loop over all keyword occurrences. */
1838 occurs_cursor
= occurs_table
[0];
1840 for (occurs_index
= 0; occurs_index
< number_of_occurs
[0]; occurs_index
++)
1842 /* Compute the exact size of every field and whenever truncation flags
1843 are present or not. */
1845 define_all_fields (occurs_cursor
);
1847 /* Produce one output line according to selected format. */
1849 switch (output_format
)
1851 case UNKNOWN_FORMAT
:
1852 /* Should never happen. */
1855 output_one_dumb_line ();
1859 output_one_roff_line ();
1863 output_one_tex_line ();
1867 /* Advance the cursor into the occurs table. */
1873 /* Option decoding and main program. */
1875 /*------------------------------------------------------.
1876 | Print program identification and options, then exit. |
1877 `------------------------------------------------------*/
1882 if (status
!= EXIT_SUCCESS
)
1883 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
1888 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1889 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1890 program_name
, program_name
);
1892 Output a permuted index, including context, of the words in the input files.\n\
1896 Mandatory arguments to long options are mandatory for short options too.\n\
1899 -A, --auto-reference output automatically generated references\n\
1900 -G, --traditional behave more like System V `ptx'\n\
1901 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1904 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1905 -O, --format=roff generate output as roff directives\n\
1906 -R, --right-side-refs put references at right, not counted in -w\n\
1907 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1908 -T, --format=tex generate output as TeX directives\n\
1911 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1912 -b, --break-file=FILE word break characters in this FILE\n\
1913 -f, --ignore-case fold lower case to upper case for sorting\n\
1914 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1915 -i, --ignore-file=FILE read ignore word list from FILE\n\
1916 -o, --only-file=FILE read only word list from this FILE\n\
1919 -r, --references first field of each line is a reference\n\
1920 -t, --typeset-mode - not implemented -\n\
1921 -w, --width=NUMBER output width in columns, reference excluded\n\
1923 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
1924 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
1927 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1929 emit_ancillary_info ();
1934 /*----------------------------------------------------------------------.
1935 | Main program. Decode ARGC arguments passed through the ARGV array of |
1936 | strings, then launch execution. |
1937 `----------------------------------------------------------------------*/
1939 /* Long options equivalences. */
1940 static struct option
const long_options
[] =
1942 {"auto-reference", no_argument
, NULL
, 'A'},
1943 {"break-file", required_argument
, NULL
, 'b'},
1944 {"flag-truncation", required_argument
, NULL
, 'F'},
1945 {"ignore-case", no_argument
, NULL
, 'f'},
1946 {"gap-size", required_argument
, NULL
, 'g'},
1947 {"ignore-file", required_argument
, NULL
, 'i'},
1948 {"macro-name", required_argument
, NULL
, 'M'},
1949 {"only-file", required_argument
, NULL
, 'o'},
1950 {"references", no_argument
, NULL
, 'r'},
1951 {"right-side-refs", no_argument
, NULL
, 'R'},
1952 {"format", required_argument
, NULL
, 10},
1953 {"sentence-regexp", required_argument
, NULL
, 'S'},
1954 {"traditional", no_argument
, NULL
, 'G'},
1955 {"typeset-mode", no_argument
, NULL
, 't'},
1956 {"width", required_argument
, NULL
, 'w'},
1957 {"word-regexp", required_argument
, NULL
, 'W'},
1958 {GETOPT_HELP_OPTION_DECL
},
1959 {GETOPT_VERSION_OPTION_DECL
},
1963 static char const* const format_args
[] =
1968 static enum Format
const format_vals
[] =
1970 ROFF_FORMAT
, TEX_FORMAT
1974 main (int argc
, char **argv
)
1976 int optchar
; /* argument character */
1977 int file_index
; /* index in text input file arrays */
1979 /* Decode program options. */
1981 initialize_main (&argc
, &argv
);
1982 set_program_name (argv
[0]);
1983 setlocale (LC_ALL
, "");
1984 bindtextdomain (PACKAGE
, LOCALEDIR
);
1985 textdomain (PACKAGE
);
1987 atexit (close_stdout
);
1989 #if HAVE_SETCHRCLASS
1993 while (optchar
= getopt_long (argc
, argv
, "AF:GM:ORS:TW:b:i:fg:o:trw:",
1994 long_options
, NULL
),
2000 usage (EXIT_FAILURE
);
2003 gnu_extensions
= false;
2007 break_file
= optarg
;
2016 unsigned long int tmp_ulong
;
2017 if (xstrtoul (optarg
, NULL
, 0, &tmp_ulong
, NULL
) != LONGINT_OK
2018 || ! (0 < tmp_ulong
&& tmp_ulong
<= INT_MAX
))
2019 error (EXIT_FAILURE
, 0, _("invalid gap width: %s"),
2021 gap_size
= tmp_ulong
;
2026 ignore_file
= optarg
;
2034 input_reference
= true;
2038 /* Yet to understand... */
2043 unsigned long int tmp_ulong
;
2044 if (xstrtoul (optarg
, NULL
, 0, &tmp_ulong
, NULL
) != LONGINT_OK
2045 || ! (0 < tmp_ulong
&& tmp_ulong
<= INT_MAX
))
2046 error (EXIT_FAILURE
, 0, _("invalid line width: %s"),
2048 line_width
= tmp_ulong
;
2053 auto_reference
= true;
2057 truncation_string
= copy_unescaped_string (optarg
);
2061 macro_name
= optarg
;
2065 output_format
= ROFF_FORMAT
;
2069 right_reference
= true;
2073 context_regex
.string
= copy_unescaped_string (optarg
);
2077 output_format
= TEX_FORMAT
;
2081 word_regex
.string
= copy_unescaped_string (optarg
);
2082 if (!*word_regex
.string
)
2083 word_regex
.string
= NULL
;
2087 output_format
= XARGMATCH ("--format", optarg
,
2088 format_args
, format_vals
);
2089 case_GETOPT_HELP_CHAR
;
2091 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
2095 /* Process remaining arguments. If GNU extensions are enabled, process
2096 all arguments as input parameters. If disabled, accept at most two
2097 arguments, the second of which is an output parameter. */
2102 /* No more argument simply means: read standard input. */
2104 input_file_name
= xmalloc (sizeof *input_file_name
);
2105 file_line_count
= xmalloc (sizeof *file_line_count
);
2106 number_input_files
= 1;
2107 input_file_name
[0] = NULL
;
2109 else if (gnu_extensions
)
2111 number_input_files
= argc
- optind
;
2112 input_file_name
= xmalloc (number_input_files
* sizeof *input_file_name
);
2113 file_line_count
= xmalloc (number_input_files
* sizeof *file_line_count
);
2115 for (file_index
= 0; file_index
< number_input_files
; file_index
++)
2117 if (!*argv
[optind
] || STREQ (argv
[optind
], "-"))
2118 input_file_name
[file_index
] = NULL
;
2120 input_file_name
[file_index
] = argv
[optind
];
2127 /* There is one necessary input file. */
2129 number_input_files
= 1;
2130 input_file_name
= xmalloc (sizeof *input_file_name
);
2131 file_line_count
= xmalloc (sizeof *file_line_count
);
2132 if (!*argv
[optind
] || STREQ (argv
[optind
], "-"))
2133 input_file_name
[0] = NULL
;
2135 input_file_name
[0] = argv
[optind
];
2138 /* Redirect standard output, only if requested. */
2142 if (! freopen (argv
[optind
], "w", stdout
))
2143 error (EXIT_FAILURE
, errno
, "%s", argv
[optind
]);
2147 /* Diagnose any other argument as an error. */
2151 error (0, 0, _("extra operand %s"), quote (argv
[optind
]));
2152 usage (EXIT_FAILURE
);
2156 /* If the output format has not been explicitly selected, choose dumb
2157 terminal format if GNU extensions are enabled, else `roff' format. */
2159 if (output_format
== UNKNOWN_FORMAT
)
2160 output_format
= gnu_extensions
? DUMB_FORMAT
: ROFF_FORMAT
;
2162 /* Initialize the main tables. */
2164 initialize_regex ();
2166 /* Read `Break character' file, if any. */
2169 digest_break_file (break_file
);
2171 /* Read `Ignore words' file and `Only words' files, if any. If any of
2172 these files is empty, reset the name of the file to NULL, to avoid
2173 unnecessary calls to search_table. */
2177 digest_word_file (ignore_file
, &ignore_table
);
2178 if (ignore_table
.length
== 0)
2184 digest_word_file (only_file
, &only_table
);
2185 if (only_table
.length
== 0)
2189 /* Prepare to study all the input files. */
2191 number_of_occurs
[0] = 0;
2192 total_line_count
= 0;
2193 maximum_word_length
= 0;
2194 reference_max_width
= 0;
2196 for (file_index
= 0; file_index
< number_input_files
; file_index
++)
2199 /* Read the file in core, than study it. */
2201 swallow_file_in_memory (input_file_name
[file_index
], &text_buffer
);
2202 find_occurs_in_text ();
2204 /* Maintain for each file how many lines has been read so far when its
2205 end is reached. Incrementing the count first is a simple kludge to
2206 handle a possible incomplete line at end of file. */
2209 file_line_count
[file_index
] = total_line_count
;
2212 /* Do the output process phase. */
2214 sort_found_occurs ();
2215 fix_output_parameters ();
2216 generate_all_output ();
2220 exit (EXIT_SUCCESS
);