Require that this test be run as non-root.
[coreutils.git] / src / ptx.c
blob8de95b22fc875d17ee774fb42a11a4f873befee7
1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990, 1991, 1993, 1998-2005 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)
8 any later version.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 François Pinard <pinard@iro.umontreal.ca> */
21 #include <config.h>
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <sys/types.h>
26 #include "system.h"
27 #include "argmatch.h"
28 #include "diacrit.h"
29 #include "error.h"
30 #include "quote.h"
31 #include "quotearg.h"
32 #include "regex.h"
33 #include "xstrtol.h"
35 /* The official name of this program (e.g., no `g' prefix). */
36 #define PROGRAM_NAME "ptx"
38 /* Note to translator: Please translate "F. Pinard" to "François
39 Pinard" if "ç" (c-with-cedilla) is available in the
40 translation's character set and encoding. */
41 #define AUTHORS _("F. 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. */
53 #if WITH_DMALLOC
54 # define MALLOC_FUNC_CHECK 1
55 # include <dmalloc.h>
56 #endif
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 /* Imported from "regex.c". */
70 #define Sword 1
72 /* The name this program was run with. */
73 char *program_name;
75 /* Program options. */
77 enum Format
79 UNKNOWN_FORMAT, /* output format still unknown */
80 DUMB_FORMAT, /* output for a dumb terminal */
81 ROFF_FORMAT, /* output for `troff' or `nroff' */
82 TEX_FORMAT /* output for `TeX' or `LaTeX' */
85 static bool gnu_extensions = true; /* trigger all GNU extensions */
86 static bool auto_reference = false; /* refs are `file_name:line_number:' */
87 static bool input_reference = false; /* refs at beginning of input lines */
88 static bool right_reference = false; /* output refs after right context */
89 static int line_width = 72; /* output line width in characters */
90 static int gap_size = 3; /* number of spaces between output fields */
91 static const char *truncation_string = "/";
92 /* string used to mark line truncations */
93 static const char *macro_name = "xx"; /* macro name for roff or TeX output */
94 static enum Format output_format = UNKNOWN_FORMAT;
95 /* output format */
97 static bool ignore_case = false; /* fold lower to upper for sorting */
98 static const char *context_regex_string = NULL;
99 /* raw regex for end of context */
100 static const char *word_regex_string = NULL;
101 /* raw regex for a keyword */
102 static const char *break_file = NULL; /* name of the `Break characters' file */
103 static const char *only_file = NULL; /* name of the `Only words' file */
104 static const char *ignore_file = NULL; /* name of the `Ignore words' file */
106 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
107 whole file. A WORD is something smaller, its length should fit in a
108 short integer. A WORD_TABLE may contain several WORDs. */
110 typedef struct
112 char *start; /* pointer to beginning of region */
113 char *end; /* pointer to end + 1 of region */
115 BLOCK;
117 typedef struct
119 char *start; /* pointer to beginning of region */
120 short int size; /* length of the region */
122 WORD;
124 typedef struct
126 WORD *start; /* array of WORDs */
127 size_t alloc; /* allocated length */
128 size_t length; /* number of used entries */
130 WORD_TABLE;
132 /* Pattern description tables. */
134 /* For each character, provide its folded equivalent. */
135 static unsigned char folded_chars[CHAR_SET_SIZE];
137 /* Compiled regex for end of context. */
138 static struct re_pattern_buffer *context_regex;
140 /* End of context pattern register indices. */
141 static struct re_registers context_regs;
143 /* Compiled regex for a keyword. */
144 static struct re_pattern_buffer *word_regex;
146 /* Keyword pattern register indices. */
147 static struct re_registers word_regs;
149 /* A word characters fastmap is used only when no word regexp has been
150 provided. A word is then made up of a sequence of one or more characters
151 allowed by the fastmap. Contains !0 if character allowed in word. Not
152 only this is faster in most cases, but it simplifies the implementation
153 of the Break files. */
154 static char word_fastmap[CHAR_SET_SIZE];
156 /* Maximum length of any word read. */
157 static int maximum_word_length;
159 /* Maximum width of any reference used. */
160 static int reference_max_width;
162 /* Ignore and Only word tables. */
164 static WORD_TABLE ignore_table; /* table of words to ignore */
165 static WORD_TABLE only_table; /* table of words to select */
167 /* Source text table, and scanning macros. */
169 static int number_input_files; /* number of text input files */
170 static int total_line_count; /* total number of lines seen so far */
171 static const char **input_file_name; /* array of text input file names */
172 static int *file_line_count; /* array of `total_line_count' values at end */
174 static BLOCK text_buffer; /* file to study */
176 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
178 #define SKIP_NON_WHITE(cursor, limit) \
179 while (cursor < limit && !ISSPACE(*cursor)) \
180 cursor++
182 #define SKIP_WHITE(cursor, limit) \
183 while (cursor < limit && ISSPACE(*cursor)) \
184 cursor++
186 #define SKIP_WHITE_BACKWARDS(cursor, start) \
187 while (cursor > start && ISSPACE(cursor[-1])) \
188 cursor--
190 #define SKIP_SOMETHING(cursor, limit) \
191 if (word_regex_string) \
193 regoff_t count; \
194 count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
195 if (count == -2) \
196 matcher_error (); \
197 cursor += count == -1 ? 1 : count; \
199 else if (word_fastmap[to_uchar (*cursor)]) \
200 while (cursor < limit && word_fastmap[to_uchar (*cursor)]) \
201 cursor++; \
202 else \
203 cursor++
205 /* Occurrences table.
207 The `keyword' pointer provides the central word, which is surrounded
208 by a left context and a right context. The `keyword' and `length'
209 field allow full 8-bit characters keys, even including NULs. At other
210 places in this program, the name `keyafter' refers to the keyword
211 followed by its right context.
213 The left context does not extend, towards the beginning of the file,
214 further than a distance given by the `left' value. This value is
215 relative to the keyword beginning, it is usually negative. This
216 insures that, except for white space, we will never have to backward
217 scan the source text, when it is time to generate the final output
218 lines.
220 The right context, indirectly attainable through the keyword end, does
221 not extend, towards the end of the file, further than a distance given
222 by the `right' value. This value is relative to the keyword
223 beginning, it is usually positive.
225 When automatic references are used, the `reference' value is the
226 overall line number in all input files read so far, in this case, it
227 is of type (int). When input references are used, the `reference'
228 value indicates the distance between the keyword beginning and the
229 start of the reference field, it is of type (DELTA) and usually
230 negative. */
232 typedef short int DELTA; /* to hold displacement within one context */
234 typedef struct
236 WORD key; /* description of the keyword */
237 DELTA left; /* distance to left context start */
238 DELTA right; /* distance to right context end */
239 int reference; /* reference descriptor */
241 OCCURS;
243 /* The various OCCURS tables are indexed by the language. But the time
244 being, there is no such multiple language support. */
246 static OCCURS *occurs_table[1]; /* all words retained from the read text */
247 static size_t occurs_alloc[1]; /* allocated size of occurs_table */
248 static size_t number_of_occurs[1]; /* number of used slots in occurs_table */
251 /* Communication among output routines. */
253 /* Indicate if special output processing is requested for each character. */
254 static char edited_flag[CHAR_SET_SIZE];
256 static int half_line_width; /* half of line width, reference excluded */
257 static int before_max_width; /* maximum width of before field */
258 static int keyafter_max_width; /* maximum width of keyword-and-after field */
259 static int truncation_string_length;/* length of string used to flag truncation */
261 /* When context is limited by lines, wraparound may happen on final output:
262 the `head' pointer gives access to some supplementary left context which
263 will be seen at the end of the output line, the `tail' pointer gives
264 access to some supplementary right context which will be seen at the
265 beginning of the output line. */
267 static BLOCK tail; /* tail field */
268 static int tail_truncation; /* flag truncation after the tail field */
270 static BLOCK before; /* before field */
271 static int before_truncation; /* flag truncation before the before field */
273 static BLOCK keyafter; /* keyword-and-after field */
274 static int keyafter_truncation; /* flag truncation after the keyafter field */
276 static BLOCK head; /* head field */
277 static int head_truncation; /* flag truncation before the head field */
279 static BLOCK reference; /* reference field for input reference mode */
281 /* Miscellaneous routines. */
283 /* Diagnose an error in the regular expression matcher. Then exit. */
285 static void ATTRIBUTE_NORETURN
286 matcher_error (void)
288 error (0, errno, _("error in regular expression matcher"));
289 exit (EXIT_FAILURE);
292 /*------------------------------------------------------.
293 | Duplicate string STRING, while evaluating \-escapes. |
294 `------------------------------------------------------*/
296 /* Loosely adapted from GNU sh-utils printf.c code. */
298 static char *
299 copy_unescaped_string (const char *string)
301 char *result; /* allocated result */
302 char *cursor; /* cursor in result */
303 int value; /* value of \nnn escape */
304 int length; /* length of \nnn escape */
306 result = xmalloc (strlen (string) + 1);
307 cursor = result;
309 while (*string)
310 if (*string == '\\')
312 string++;
313 switch (*string)
315 case 'x': /* \xhhh escape, 3 chars maximum */
316 value = 0;
317 for (length = 0, string++;
318 length < 3 && ISXDIGIT (*string);
319 length++, string++)
320 value = value * 16 + HEXTOBIN (*string);
321 if (length == 0)
323 *cursor++ = '\\';
324 *cursor++ = 'x';
326 else
327 *cursor++ = value;
328 break;
330 case '0': /* \0ooo escape, 3 chars maximum */
331 value = 0;
332 for (length = 0, string++;
333 length < 3 && ISODIGIT (*string);
334 length++, string++)
335 value = value * 8 + OCTTOBIN (*string);
336 *cursor++ = value;
337 break;
339 case 'a': /* alert */
340 #if __STDC__
341 *cursor++ = '\a';
342 #else
343 *cursor++ = 7;
344 #endif
345 string++;
346 break;
348 case 'b': /* backspace */
349 *cursor++ = '\b';
350 string++;
351 break;
353 case 'c': /* cancel the rest of the output */
354 while (*string)
355 string++;
356 break;
358 case 'f': /* form feed */
359 *cursor++ = '\f';
360 string++;
361 break;
363 case 'n': /* new line */
364 *cursor++ = '\n';
365 string++;
366 break;
368 case 'r': /* carriage return */
369 *cursor++ = '\r';
370 string++;
371 break;
373 case 't': /* horizontal tab */
374 *cursor++ = '\t';
375 string++;
376 break;
378 case 'v': /* vertical tab */
379 #if __STDC__
380 *cursor++ = '\v';
381 #else
382 *cursor++ = 11;
383 #endif
384 string++;
385 break;
387 default:
388 *cursor++ = '\\';
389 *cursor++ = *string++;
390 break;
393 else
394 *cursor++ = *string++;
396 *cursor = '\0';
397 return result;
400 /*-------------------------------------------------------------------.
401 | Compile the regex represented by STRING, diagnose and abort if any |
402 | error. Returns the compiled regex structure. |
403 `-------------------------------------------------------------------*/
405 static struct re_pattern_buffer *
406 alloc_and_compile_regex (const char *string)
408 struct re_pattern_buffer *pattern; /* newly allocated structure */
409 const char *message; /* error message returned by regex.c */
411 pattern = xmalloc (sizeof *pattern);
412 memset (pattern, 0, sizeof *pattern);
414 pattern->buffer = NULL;
415 pattern->allocated = 0;
416 pattern->translate = ignore_case ? (char *) folded_chars : NULL;
417 pattern->fastmap = xmalloc ((size_t) CHAR_SET_SIZE);
419 message = re_compile_pattern (string, (int) strlen (string), pattern);
420 if (message)
421 error (EXIT_FAILURE, 0, _("%s (for regexp %s)"), message, quote (string));
423 /* The fastmap should be compiled before `re_match'. The following
424 call is not mandatory, because `re_search' is always called sooner,
425 and it compiles the fastmap if this has not been done yet. */
427 re_compile_fastmap (pattern);
429 /* Do not waste extra allocated space. */
431 pattern->buffer = xrealloc (pattern->buffer, pattern->used);
432 pattern->allocated = pattern->used;
434 return pattern;
437 /*------------------------------------------------------------------------.
438 | This will initialize various tables for pattern match and compiles some |
439 | regexps. |
440 `------------------------------------------------------------------------*/
442 static void
443 initialize_regex (void)
445 int character; /* character value */
447 /* Initialize the case folding table. */
449 if (ignore_case)
450 for (character = 0; character < CHAR_SET_SIZE; character++)
451 folded_chars[character] = TOUPPER (character);
453 /* Unless the user already provided a description of the end of line or
454 end of sentence sequence, select an end of line sequence to compile.
455 If the user provided an empty definition, thus disabling end of line
456 or sentence feature, make it NULL to speed up tests. If GNU
457 extensions are enabled, use end of sentence like in GNU emacs. If
458 disabled, use end of lines. */
460 if (context_regex_string)
462 if (!*context_regex_string)
463 context_regex_string = NULL;
465 else if (gnu_extensions & !input_reference)
466 context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
467 else
468 context_regex_string = "\n";
470 if (context_regex_string)
471 context_regex = alloc_and_compile_regex (context_regex_string);
473 /* If the user has already provided a non-empty regexp to describe
474 words, compile it. Else, unless this has already been done through
475 a user provided Break character file, construct a fastmap of
476 characters that may appear in a word. If GNU extensions enabled,
477 include only letters of the underlying character set. If disabled,
478 include almost everything, even punctuations; stop only on white
479 space. */
481 if (word_regex_string && *word_regex_string)
482 word_regex = alloc_and_compile_regex (word_regex_string);
483 else if (!break_file)
485 if (gnu_extensions)
488 /* Simulate \w+. */
490 for (character = 0; character < CHAR_SET_SIZE; character++)
491 word_fastmap[character] = ISALPHA (character) ? 1 : 0;
493 else
496 /* Simulate [^ \t\n]+. */
498 memset (word_fastmap, 1, CHAR_SET_SIZE);
499 word_fastmap[' '] = 0;
500 word_fastmap['\t'] = 0;
501 word_fastmap['\n'] = 0;
506 /*------------------------------------------------------------------------.
507 | This routine will attempt to swallow a whole file name FILE_NAME into a |
508 | contiguous region of memory and return a description of it into BLOCK. |
509 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
511 | Previously, in some cases, white space compression was attempted while |
512 | inputting text. This was defeating some regexps like default end of |
513 | sentence, which checks for two consecutive spaces. If white space |
514 | compression is ever reinstated, it should be in output routines. |
515 `------------------------------------------------------------------------*/
517 static void
518 swallow_file_in_memory (const char *file_name, BLOCK *block)
520 int file_handle; /* file descriptor number */
521 struct stat stat_block; /* stat block for file */
522 size_t allocated_length; /* allocated length of memory buffer */
523 size_t used_length; /* used length in memory buffer */
524 int read_length; /* number of character gotten on last read */
526 /* As special cases, a file name which is NULL or "-" indicates standard
527 input, which is already opened. In all other cases, open the file from
528 its name. */
529 bool using_stdin = !file_name || !*file_name || STREQ (file_name, "-");
530 if (using_stdin)
531 file_handle = STDIN_FILENO;
532 else
533 if ((file_handle = open (file_name, O_RDONLY)) < 0)
534 error (EXIT_FAILURE, errno, "%s", file_name);
536 /* If the file is a plain, regular file, allocate the memory buffer all at
537 once and swallow the file in one blow. In other cases, read the file
538 repeatedly in smaller chunks until we have it all, reallocating memory
539 once in a while, as we go. */
541 if (fstat (file_handle, &stat_block) < 0)
542 error (EXIT_FAILURE, errno, "%s", file_name);
544 if (S_ISREG (stat_block.st_mode))
546 size_t in_memory_size;
548 block->start = xmalloc ((size_t) stat_block.st_size);
550 if ((in_memory_size = read (file_handle,
551 block->start, (size_t) stat_block.st_size))
552 != stat_block.st_size)
554 #if MSDOS
555 /* On MSDOS, in memory size may be smaller than the file
556 size, because of end of line conversions. But it can
557 never be smaller than half the file size, because the
558 minimum is when all lines are empty and terminated by
559 CR+LF. */
560 if (in_memory_size != (size_t)-1
561 && in_memory_size >= stat_block.st_size / 2)
562 block->start = xrealloc (block->start, in_memory_size);
563 else
564 #endif /* not MSDOS */
566 error (EXIT_FAILURE, errno, "%s", file_name);
568 block->end = block->start + in_memory_size;
570 else
572 block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
573 used_length = 0;
574 allocated_length = (1 << SWALLOW_REALLOC_LOG);
576 while (read_length = read (file_handle,
577 block->start + used_length,
578 allocated_length - used_length),
579 read_length > 0)
581 used_length += read_length;
582 if (used_length == allocated_length)
584 allocated_length += (1 << SWALLOW_REALLOC_LOG);
585 block->start
586 = xrealloc (block->start, allocated_length);
590 if (read_length < 0)
591 error (EXIT_FAILURE, errno, "%s", file_name);
593 block->end = block->start + used_length;
596 /* Close the file, but only if it was not the standard input. */
598 if (! using_stdin && close (file_handle) != 0)
599 error (EXIT_FAILURE, errno, "%s", file_name);
602 /* Sort and search routines. */
604 /*--------------------------------------------------------------------------.
605 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
606 | Return less than 0 if the first word goes before the second; return |
607 | greater than 0 if the first word goes after the second. |
609 | If a word is indeed a prefix of the other, the shorter should go first. |
610 `--------------------------------------------------------------------------*/
612 static int
613 compare_words (const void *void_first, const void *void_second)
615 #define first ((const WORD *) void_first)
616 #define second ((const WORD *) void_second)
617 int length; /* minimum of two lengths */
618 int counter; /* cursor in words */
619 int value; /* value of comparison */
621 length = first->size < second->size ? first->size : second->size;
623 if (ignore_case)
625 for (counter = 0; counter < length; counter++)
627 value = (folded_chars [to_uchar (first->start[counter])]
628 - folded_chars [to_uchar (second->start[counter])]);
629 if (value != 0)
630 return value;
633 else
635 for (counter = 0; counter < length; counter++)
637 value = (to_uchar (first->start[counter])
638 - to_uchar (second->start[counter]));
639 if (value != 0)
640 return value;
644 return first->size - second->size;
645 #undef first
646 #undef second
649 /*-----------------------------------------------------------------------.
650 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
651 | go first. In case of a tie, preserve the original order through a |
652 | pointer comparison. |
653 `-----------------------------------------------------------------------*/
655 static int
656 compare_occurs (const void *void_first, const void *void_second)
658 #define first ((const OCCURS *) void_first)
659 #define second ((const OCCURS *) void_second)
660 int value;
662 value = compare_words (&first->key, &second->key);
663 return value == 0 ? first->key.start - second->key.start : value;
664 #undef first
665 #undef second
668 /*------------------------------------------------------------.
669 | Return !0 if WORD appears in TABLE. Uses a binary search. |
670 `------------------------------------------------------------*/
672 static int
673 search_table (WORD *word, WORD_TABLE *table)
675 int lowest; /* current lowest possible index */
676 int highest; /* current highest possible index */
677 int middle; /* current middle index */
678 int value; /* value from last comparison */
680 lowest = 0;
681 highest = table->length - 1;
682 while (lowest <= highest)
684 middle = (lowest + highest) / 2;
685 value = compare_words (word, table->start + middle);
686 if (value < 0)
687 highest = middle - 1;
688 else if (value > 0)
689 lowest = middle + 1;
690 else
691 return 1;
693 return 0;
696 /*---------------------------------------------------------------------.
697 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
698 | take intermediate copies or table elements, so the sort will be |
699 | stabilized throughout the comparison routine. |
700 `---------------------------------------------------------------------*/
702 static void
703 sort_found_occurs (void)
706 /* Only one language for the time being. */
708 qsort (occurs_table[0], number_of_occurs[0], sizeof **occurs_table,
709 compare_occurs);
712 /* Parameter files reading routines. */
714 /*----------------------------------------------------------------------.
715 | Read a file named FILE_NAME, containing a set of break characters. |
716 | Build a content to the array word_fastmap in which all characters are |
717 | allowed except those found in the file. Characters may be repeated. |
718 `----------------------------------------------------------------------*/
720 static void
721 digest_break_file (const char *file_name)
723 BLOCK file_contents; /* to receive a copy of the file */
724 char *cursor; /* cursor in file copy */
726 swallow_file_in_memory (file_name, &file_contents);
728 /* Make the fastmap and record the file contents in it. */
730 memset (word_fastmap, 1, CHAR_SET_SIZE);
731 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
732 word_fastmap[to_uchar (*cursor)] = 0;
734 if (!gnu_extensions)
737 /* If GNU extensions are enabled, the only way to avoid newline as
738 a break character is to write all the break characters in the
739 file with no newline at all, not even at the end of the file.
740 If disabled, spaces, tabs and newlines are always considered as
741 break characters even if not included in the break file. */
743 word_fastmap[' '] = 0;
744 word_fastmap['\t'] = 0;
745 word_fastmap['\n'] = 0;
748 /* Return the space of the file, which is no more required. */
750 free (file_contents.start);
753 /*-----------------------------------------------------------------------.
754 | Read a file named FILE_NAME, containing one word per line, then |
755 | construct in TABLE a table of WORD descriptors for them. The routine |
756 | swallows the whole file in memory; this is at the expense of space |
757 | needed for newlines, which are useless; however, the reading is fast. |
758 `-----------------------------------------------------------------------*/
760 static void
761 digest_word_file (const char *file_name, WORD_TABLE *table)
763 BLOCK file_contents; /* to receive a copy of the file */
764 char *cursor; /* cursor in file copy */
765 char *word_start; /* start of the current word */
767 swallow_file_in_memory (file_name, &file_contents);
769 table->start = NULL;
770 table->alloc = 0;
771 table->length = 0;
773 /* Read the whole file. */
775 cursor = file_contents.start;
776 while (cursor < file_contents.end)
779 /* Read one line, and save the word in contains. */
781 word_start = cursor;
782 while (cursor < file_contents.end && *cursor != '\n')
783 cursor++;
785 /* Record the word in table if it is not empty. */
787 if (cursor > word_start)
789 if (table->length == table->alloc)
791 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
792 xalloc_die ();
793 table->alloc = table->alloc * 2 + 1;
794 table->start = xrealloc (table->start,
795 table->alloc * sizeof *table->start);
798 table->start[table->length].start = word_start;
799 table->start[table->length].size = cursor - word_start;
800 table->length++;
803 /* This test allows for an incomplete line at end of file. */
805 if (cursor < file_contents.end)
806 cursor++;
809 /* Finally, sort all the words read. */
811 qsort (table->start, table->length, sizeof table->start[0], compare_words);
814 /* Keyword recognition and selection. */
816 /*----------------------------------------------------------------------.
817 | For each keyword in the source text, constructs an OCCURS structure. |
818 `----------------------------------------------------------------------*/
820 static void
821 find_occurs_in_text (void)
823 char *cursor; /* for scanning the source text */
824 char *scan; /* for scanning the source text also */
825 char *line_start; /* start of the current input line */
826 char *line_scan; /* newlines scanned until this point */
827 int reference_length; /* length of reference in input mode */
828 WORD possible_key; /* possible key, to ease searches */
829 OCCURS *occurs_cursor; /* current OCCURS under construction */
831 char *context_start; /* start of left context */
832 char *context_end; /* end of right context */
833 char *word_start; /* start of word */
834 char *word_end; /* end of word */
835 char *next_context_start; /* next start of left context */
837 /* reference_length is always used within `if (input_reference)'.
838 However, GNU C diagnoses that it may be used uninitialized. The
839 following assignment is merely to shut it up. */
841 reference_length = 0;
843 /* Tracking where lines start is helpful for reference processing. In
844 auto reference mode, this allows counting lines. In input reference
845 mode, this permits finding the beginning of the references.
847 The first line begins with the file, skip immediately this very first
848 reference in input reference mode, to help further rejection any word
849 found inside it. Also, unconditionally assigning these variable has
850 the happy effect of shutting up lint. */
852 line_start = text_buffer.start;
853 line_scan = line_start;
854 if (input_reference)
856 SKIP_NON_WHITE (line_scan, text_buffer.end);
857 reference_length = line_scan - line_start;
858 SKIP_WHITE (line_scan, text_buffer.end);
861 /* Process the whole buffer, one line or one sentence at a time. */
863 for (cursor = text_buffer.start;
864 cursor < text_buffer.end;
865 cursor = next_context_start)
868 /* `context_start' gets initialized before the processing of each
869 line, or once for the whole buffer if no end of line or sentence
870 sequence separator. */
872 context_start = cursor;
874 /* If a end of line or end of sentence sequence is defined and
875 non-empty, `next_context_start' will be recomputed to be the end of
876 each line or sentence, before each one is processed. If no such
877 sequence, then `next_context_start' is set at the end of the whole
878 buffer, which is then considered to be a single line or sentence.
879 This test also accounts for the case of an incomplete line or
880 sentence at the end of the buffer. */
882 next_context_start = text_buffer.end;
883 if (context_regex_string)
884 switch (re_search (context_regex, cursor, text_buffer.end - cursor,
885 0, text_buffer.end - cursor, &context_regs))
887 case -2:
888 matcher_error ();
890 case -1:
891 break;
893 default:
894 next_context_start = cursor + context_regs.end[0];
895 break;
898 /* Include the separator into the right context, but not any suffix
899 white space in this separator; this insures it will be seen in
900 output and will not take more space than necessary. */
902 context_end = next_context_start;
903 SKIP_WHITE_BACKWARDS (context_end, context_start);
905 /* Read and process a single input line or sentence, one word at a
906 time. */
908 while (1)
910 if (word_regex)
912 /* If a word regexp has been compiled, use it to skip at the
913 beginning of the next word. If there is no such word, exit
914 the loop. */
917 regoff_t r = re_search (word_regex, cursor, context_end - cursor,
918 0, context_end - cursor, &word_regs);
919 if (r == -2)
920 matcher_error ();
921 if (r == -1)
922 break;
923 word_start = cursor + word_regs.start[0];
924 word_end = cursor + word_regs.end[0];
926 else
928 /* Avoid re_search and use the fastmap to skip to the
929 beginning of the next word. If there is no more word in
930 the buffer, exit the loop. */
933 scan = cursor;
934 while (scan < context_end
935 && !word_fastmap[to_uchar (*scan)])
936 scan++;
938 if (scan == context_end)
939 break;
941 word_start = scan;
943 while (scan < context_end
944 && word_fastmap[to_uchar (*scan)])
945 scan++;
947 word_end = scan;
950 /* Skip right to the beginning of the found word. */
952 cursor = word_start;
954 /* Skip any zero length word. Just advance a single position,
955 then go fetch the next word. */
957 if (word_end == word_start)
959 cursor++;
960 continue;
963 /* This is a genuine, non empty word, so save it as a possible
964 key. Then skip over it. Also, maintain the maximum length of
965 all words read so far. It is mandatory to take the maximum
966 length of all words in the file, without considering if they
967 are actually kept or rejected, because backward jumps at output
968 generation time may fall in *any* word. */
970 possible_key.start = cursor;
971 possible_key.size = word_end - word_start;
972 cursor += possible_key.size;
974 if (possible_key.size > maximum_word_length)
975 maximum_word_length = possible_key.size;
977 /* In input reference mode, update `line_start' from its previous
978 value. Count the lines just in case auto reference mode is
979 also selected. If it happens that the word just matched is
980 indeed part of a reference; just ignore it. */
982 if (input_reference)
984 while (line_scan < possible_key.start)
985 if (*line_scan == '\n')
987 total_line_count++;
988 line_scan++;
989 line_start = line_scan;
990 SKIP_NON_WHITE (line_scan, text_buffer.end);
991 reference_length = line_scan - line_start;
993 else
994 line_scan++;
995 if (line_scan > possible_key.start)
996 continue;
999 /* Ignore the word if an `Ignore words' table exists and if it is
1000 part of it. Also ignore the word if an `Only words' table and
1001 if it is *not* part of it.
1003 It is allowed that both tables be used at once, even if this
1004 may look strange for now. Just ignore a word that would appear
1005 in both. If regexps are eventually implemented for these
1006 tables, the Ignore table could then reject words that would
1007 have been previously accepted by the Only table. */
1009 if (ignore_file && search_table (&possible_key, &ignore_table))
1010 continue;
1011 if (only_file && !search_table (&possible_key, &only_table))
1012 continue;
1014 /* A non-empty word has been found. First of all, insure
1015 proper allocation of the next OCCURS, and make a pointer to
1016 where it will be constructed. */
1018 if (number_of_occurs[0] == occurs_alloc[0])
1020 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1021 < occurs_alloc[0])
1022 xalloc_die ();
1023 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
1024 occurs_table[0] = xrealloc (occurs_table[0],
1025 occurs_alloc[0] * sizeof *occurs_table[0]);
1028 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1030 /* Define the refence field, if any. */
1032 if (auto_reference)
1035 /* While auto referencing, update `line_start' from its
1036 previous value, counting lines as we go. If input
1037 referencing at the same time, `line_start' has been
1038 advanced earlier, and the following loop is never really
1039 executed. */
1041 while (line_scan < possible_key.start)
1042 if (*line_scan == '\n')
1044 total_line_count++;
1045 line_scan++;
1046 line_start = line_scan;
1047 SKIP_NON_WHITE (line_scan, text_buffer.end);
1049 else
1050 line_scan++;
1052 occurs_cursor->reference = total_line_count;
1054 else if (input_reference)
1057 /* If only input referencing, `line_start' has been computed
1058 earlier to detect the case the word matched would be part
1059 of the reference. The reference position is simply the
1060 value of `line_start'. */
1062 occurs_cursor->reference
1063 = (DELTA) (line_start - possible_key.start);
1064 if (reference_length > reference_max_width)
1065 reference_max_width = reference_length;
1068 /* Exclude the reference from the context in simple cases. */
1070 if (input_reference && line_start == context_start)
1072 SKIP_NON_WHITE (context_start, context_end);
1073 SKIP_WHITE (context_start, context_end);
1076 /* Completes the OCCURS structure. */
1078 occurs_cursor->key = possible_key;
1079 occurs_cursor->left = context_start - possible_key.start;
1080 occurs_cursor->right = context_end - possible_key.start;
1082 number_of_occurs[0]++;
1087 /* Formatting and actual output - service routines. */
1089 /*-----------------------------------------.
1090 | Prints some NUMBER of spaces on stdout. |
1091 `-----------------------------------------*/
1093 static void
1094 print_spaces (int number)
1096 int counter;
1098 for (counter = number; counter > 0; counter--)
1099 putchar (' ');
1102 /*-------------------------------------.
1103 | Prints the field provided by FIELD. |
1104 `-------------------------------------*/
1106 static void
1107 print_field (BLOCK field)
1109 char *cursor; /* Cursor in field to print */
1110 int base; /* Base character, without diacritic */
1111 int diacritic; /* Diacritic code for the character */
1113 /* Whitespace is not really compressed. Instead, each white space
1114 character (tab, vt, ht etc.) is printed as one single space. */
1116 for (cursor = field.start; cursor < field.end; cursor++)
1118 unsigned char character = *cursor;
1119 if (edited_flag[character])
1122 /* First check if this is a diacriticized character.
1124 This works only for TeX. I do not know how diacriticized
1125 letters work with `roff'. Please someone explain it to me! */
1127 diacritic = todiac (character);
1128 if (diacritic != 0 && output_format == TEX_FORMAT)
1130 base = tobase (character);
1131 switch (diacritic)
1134 case 1: /* Latin diphthongs */
1135 switch (base)
1137 case 'o':
1138 fputs ("\\oe{}", stdout);
1139 break;
1141 case 'O':
1142 fputs ("\\OE{}", stdout);
1143 break;
1145 case 'a':
1146 fputs ("\\ae{}", stdout);
1147 break;
1149 case 'A':
1150 fputs ("\\AE{}", stdout);
1151 break;
1153 default:
1154 putchar (' ');
1156 break;
1158 case 2: /* Acute accent */
1159 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1160 break;
1162 case 3: /* Grave accent */
1163 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1164 break;
1166 case 4: /* Circumflex accent */
1167 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1168 break;
1170 case 5: /* Diaeresis */
1171 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1172 break;
1174 case 6: /* Tilde accent */
1175 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1176 break;
1178 case 7: /* Cedilla */
1179 printf ("\\c{%c}", base);
1180 break;
1182 case 8: /* Small circle beneath */
1183 switch (base)
1185 case 'a':
1186 fputs ("\\aa{}", stdout);
1187 break;
1189 case 'A':
1190 fputs ("\\AA{}", stdout);
1191 break;
1193 default:
1194 putchar (' ');
1196 break;
1198 case 9: /* Strike through */
1199 switch (base)
1201 case 'o':
1202 fputs ("\\o{}", stdout);
1203 break;
1205 case 'O':
1206 fputs ("\\O{}", stdout);
1207 break;
1209 default:
1210 putchar (' ');
1212 break;
1215 else
1217 /* This is not a diacritic character, so handle cases which are
1218 really specific to `roff' or TeX. All white space processing
1219 is done as the default case of this switch. */
1221 switch (character)
1223 case '"':
1224 /* In roff output format, double any quote. */
1225 putchar ('"');
1226 putchar ('"');
1227 break;
1229 case '$':
1230 case '%':
1231 case '&':
1232 case '#':
1233 case '_':
1234 /* In TeX output format, precede these with a backslash. */
1235 putchar ('\\');
1236 putchar (character);
1237 break;
1239 case '{':
1240 case '}':
1241 /* In TeX output format, precede these with a backslash and
1242 force mathematical mode. */
1243 printf ("$\\%c$", character);
1244 break;
1246 case '\\':
1247 /* In TeX output mode, request production of a backslash. */
1248 fputs ("\\backslash{}", stdout);
1249 break;
1251 default:
1252 /* Any other flagged character produces a single space. */
1253 putchar (' ');
1256 else
1257 putchar (*cursor);
1261 /* Formatting and actual output - planning routines. */
1263 /*--------------------------------------------------------------------.
1264 | From information collected from command line options and input file |
1265 | readings, compute and fix some output parameter values. |
1266 `--------------------------------------------------------------------*/
1268 static void
1269 fix_output_parameters (void)
1271 int file_index; /* index in text input file arrays */
1272 int line_ordinal; /* line ordinal value for reference */
1273 char ordinal_string[12]; /* edited line ordinal for reference */
1274 int reference_width; /* width for the whole reference */
1275 int character; /* character ordinal */
1276 const char *cursor; /* cursor in some constant strings */
1278 /* In auto reference mode, the maximum width of this field is
1279 precomputed and subtracted from the overall line width. Add one for
1280 the column which separate the file name from the line number. */
1282 if (auto_reference)
1284 reference_max_width = 0;
1285 for (file_index = 0; file_index < number_input_files; file_index++)
1287 line_ordinal = file_line_count[file_index] + 1;
1288 if (file_index > 0)
1289 line_ordinal -= file_line_count[file_index - 1];
1290 sprintf (ordinal_string, "%d", line_ordinal);
1291 reference_width = strlen (ordinal_string);
1292 if (input_file_name[file_index])
1293 reference_width += strlen (input_file_name[file_index]);
1294 if (reference_width > reference_max_width)
1295 reference_max_width = reference_width;
1297 reference_max_width++;
1298 reference.start = xmalloc ((size_t) reference_max_width + 1);
1301 /* If the reference appears to the left of the output line, reserve some
1302 space for it right away, including one gap size. */
1304 if ((auto_reference | input_reference) & !right_reference)
1305 line_width -= reference_max_width + gap_size;
1307 /* The output lines, minimally, will contain from left to right a left
1308 context, a gap, and a keyword followed by the right context with no
1309 special intervening gap. Half of the line width is dedicated to the
1310 left context and the gap, the other half is dedicated to the keyword
1311 and the right context; these values are computed once and for all here.
1312 There also are tail and head wrap around fields, used when the keyword
1313 is near the beginning or the end of the line, or when some long word
1314 cannot fit in, but leave place from wrapped around shorter words. The
1315 maximum width of these fields are recomputed separately for each line,
1316 on a case by case basis. It is worth noting that it cannot happen that
1317 both the tail and head fields are used at once. */
1319 half_line_width = line_width / 2;
1320 before_max_width = half_line_width - gap_size;
1321 keyafter_max_width = half_line_width;
1323 /* If truncation_string is the empty string, make it NULL to speed up
1324 tests. In this case, truncation_string_length will never get used, so
1325 there is no need to set it. */
1327 if (truncation_string && *truncation_string)
1328 truncation_string_length = strlen (truncation_string);
1329 else
1330 truncation_string = NULL;
1332 if (gnu_extensions)
1335 /* When flagging truncation at the left of the keyword, the
1336 truncation mark goes at the beginning of the before field,
1337 unless there is a head field, in which case the mark goes at the
1338 left of the head field. When flagging truncation at the right
1339 of the keyword, the mark goes at the end of the keyafter field,
1340 unless there is a tail field, in which case the mark goes at the
1341 end of the tail field. Only eight combination cases could arise
1342 for truncation marks:
1344 . None.
1345 . One beginning the before field.
1346 . One beginning the head field.
1347 . One ending the keyafter field.
1348 . One ending the tail field.
1349 . One beginning the before field, another ending the keyafter field.
1350 . One ending the tail field, another beginning the before field.
1351 . One ending the keyafter field, another beginning the head field.
1353 So, there is at most two truncation marks, which could appear both
1354 on the left side of the center of the output line, both on the
1355 right side, or one on either side. */
1357 before_max_width -= 2 * truncation_string_length;
1358 keyafter_max_width -= 2 * truncation_string_length;
1360 else
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,
1367 but almost. */
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) != 0;
1378 edited_flag['\f'] = 1;
1380 /* Complete the special character flagging according to selected output
1381 format. */
1383 switch (output_format)
1385 case UNKNOWN_FORMAT:
1386 /* Should never happen. */
1388 case DUMB_FORMAT:
1389 break;
1391 case ROFF_FORMAT:
1393 /* `Quote' characters should be doubled. */
1395 edited_flag['"'] = 1;
1396 break;
1398 case TEX_FORMAT:
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;
1410 break;
1414 /*------------------------------------------------------------------.
1415 | Compute the position and length of all the output fields, given a |
1416 | pointer to some OCCURS. |
1417 `------------------------------------------------------------------*/
1419 static void
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)
1471 left_field_start
1472 = keyafter.start - (half_line_width + maximum_word_length);
1473 SKIP_SOMETHING (left_field_start, keyafter.start);
1475 else
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;
1497 else
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. */
1509 tail_max_width
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;
1518 cursor = tail.end;
1519 while (cursor < right_context_end
1520 && cursor < tail.start + tail_max_width)
1522 tail.end = cursor;
1523 SKIP_SOMETHING (cursor, right_context_end);
1526 if (cursor < tail.start + tail_max_width)
1527 tail.end = cursor;
1529 if (tail.end > tail.start)
1531 keyafter_truncation = 0;
1532 tail_truncation = truncation_string && tail.end < right_context_end;
1534 else
1535 tail_truncation = 0;
1537 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1539 else
1542 /* No place left for a tail field. */
1544 tail.start = NULL;
1545 tail.end = NULL;
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. */
1555 head_max_width
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);
1573 else
1574 head_truncation = 0;
1576 SKIP_WHITE (head.start, head.end);
1578 else
1581 /* No place left for a head field. */
1583 head.start = NULL;
1584 head.end = NULL;
1585 head_truncation = 0;
1588 if (auto_reference)
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. */
1596 file_index = 0;
1597 while (file_line_count[file_index] < occurs->reference)
1598 file_index++;
1600 file_name = input_file_name[file_index];
1601 if (!file_name)
1602 file_name = "";
1604 line_ordinal = occurs->reference + 1;
1605 if (file_index > 0)
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 `----------------------------------------------------------------------*/
1629 static void
1630 output_one_roff_line (void)
1632 /* Output the `tail' field. */
1634 printf (".%s \"", macro_name);
1635 print_field (tail);
1636 if (tail_truncation)
1637 fputs (truncation_string, stdout);
1638 putchar ('"');
1640 /* Output the `before' field. */
1642 fputs (" \"", stdout);
1643 if (before_truncation)
1644 fputs (truncation_string, stdout);
1645 print_field (before);
1646 putchar ('"');
1648 /* Output the `keyafter' field. */
1650 fputs (" \"", stdout);
1651 print_field (keyafter);
1652 if (keyafter_truncation)
1653 fputs (truncation_string, stdout);
1654 putchar ('"');
1656 /* Output the `head' field. */
1658 fputs (" \"", stdout);
1659 if (head_truncation)
1660 fputs (truncation_string, stdout);
1661 print_field (head);
1662 putchar ('"');
1664 /* Conditionally output the `reference' field. */
1666 if (auto_reference | input_reference)
1668 fputs (" \"", stdout);
1669 print_field (reference);
1670 putchar ('"');
1673 putchar ('\n');
1676 /*---------------------------------------------------------.
1677 | Output the current output fields as one line for `TeX'. |
1678 `---------------------------------------------------------*/
1680 static void
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);
1688 putchar ('{');
1689 print_field (tail);
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);
1697 key.end = cursor;
1698 after.start = cursor;
1699 print_field (key);
1700 fputs ("}{", stdout);
1701 print_field (after);
1702 fputs ("}{", stdout);
1703 print_field (head);
1704 putchar ('}');
1705 if (auto_reference | input_reference)
1707 putchar ('{');
1708 print_field (reference);
1709 putchar ('}');
1711 putchar ('\n');
1714 /*-------------------------------------------------------------------.
1715 | Output the current output fields as one line for a dumb terminal. |
1716 `-------------------------------------------------------------------*/
1718 static void
1719 output_one_dumb_line (void)
1721 if (!right_reference)
1723 if (auto_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);
1731 putchar (':');
1732 print_spaces (reference_max_width
1733 + gap_size
1734 - (reference.end - reference.start)
1735 - 1);
1737 else
1740 /* Output the `reference' field and its following gap. */
1742 print_field (reference);
1743 print_spaces (reference_max_width
1744 + gap_size
1745 - (reference.end - reference.start));
1749 if (tail.start < tail.end)
1751 /* Output the `tail' field. */
1753 print_field (tail);
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));
1763 else
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);
1793 print_field (head);
1795 else
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);
1810 putchar ('\n');
1813 /*------------------------------------------------------------------------.
1814 | Scan the whole occurs table and, for each entry, output one line in the |
1815 | appropriate format. |
1816 `------------------------------------------------------------------------*/
1818 static void
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. */
1828 tail.start = NULL;
1829 tail.end = NULL;
1830 tail_truncation = 0;
1832 head.start = NULL;
1833 head.end = NULL;
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. */
1854 case DUMB_FORMAT:
1855 output_one_dumb_line ();
1856 break;
1858 case ROFF_FORMAT:
1859 output_one_roff_line ();
1860 break;
1862 case TEX_FORMAT:
1863 output_one_tex_line ();
1864 break;
1867 /* Advance the cursor into the occurs table. */
1869 occurs_cursor++;
1873 /* Option decoding and main program. */
1875 /*------------------------------------------------------.
1876 | Print program identification and options, then exit. |
1877 `------------------------------------------------------*/
1879 void
1880 usage (int status)
1882 if (status != EXIT_SUCCESS)
1883 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1884 program_name);
1885 else
1887 printf (_("\
1888 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1889 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1890 program_name, program_name);
1891 fputs (_("\
1892 Output a permuted index, including context, of the words in the input files.\n\
1894 "), stdout);
1895 fputs (_("\
1896 Mandatory arguments to long options are mandatory for short options too.\n\
1897 "), stdout);
1898 fputs (_("\
1899 -A, --auto-reference output automatically generated references\n\
1900 -C, --copyright display Copyright and copying conditions\n\
1901 -G, --traditional behave more like System V `ptx'\n\
1902 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1903 "), stdout);
1904 fputs (_("\
1905 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1906 -O, --format=roff generate output as roff directives\n\
1907 -R, --right-side-refs put references at right, not counted in -w\n\
1908 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1909 -T, --format=tex generate output as TeX directives\n\
1910 "), stdout);
1911 fputs (_("\
1912 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1913 -b, --break-file=FILE word break characters in this FILE\n\
1914 -f, --ignore-case fold lower case to upper case for sorting\n\
1915 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1916 -i, --ignore-file=FILE read ignore word list from FILE\n\
1917 -o, --only-file=FILE read only word list from this FILE\n\
1918 "), stdout);
1919 fputs (_("\
1920 -r, --references first field of each line is a reference\n\
1921 -t, --typeset-mode - not implemented -\n\
1922 -w, --width=NUMBER output width in columns, reference excluded\n\
1923 "), stdout);
1924 fputs (HELP_OPTION_DESCRIPTION, stdout);
1925 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1926 fputs (_("\
1928 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1929 "), stdout);
1930 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1932 exit (status);
1935 /*----------------------------------------------------------------------.
1936 | Main program. Decode ARGC arguments passed through the ARGV array of |
1937 | strings, then launch execution. |
1938 `----------------------------------------------------------------------*/
1940 /* Long options equivalences. */
1941 static const struct option long_options[] =
1943 {"auto-reference", no_argument, NULL, 'A'},
1944 {"break-file", required_argument, NULL, 'b'},
1945 {"copyright", no_argument, NULL, 'C'},
1946 {"flag-truncation", required_argument, NULL, 'F'},
1947 {"ignore-case", no_argument, NULL, 'f'},
1948 {"gap-size", required_argument, NULL, 'g'},
1949 {"ignore-file", required_argument, NULL, 'i'},
1950 {"macro-name", required_argument, NULL, 'M'},
1951 {"only-file", required_argument, NULL, 'o'},
1952 {"references", no_argument, NULL, 'r'},
1953 {"right-side-refs", no_argument, NULL, 'R'},
1954 {"format", required_argument, NULL, 10},
1955 {"sentence-regexp", required_argument, NULL, 'S'},
1956 {"traditional", no_argument, NULL, 'G'},
1957 {"typeset-mode", no_argument, NULL, 't'},
1958 {"width", required_argument, NULL, 'w'},
1959 {"word-regexp", required_argument, NULL, 'W'},
1960 {GETOPT_HELP_OPTION_DECL},
1961 {GETOPT_VERSION_OPTION_DECL},
1962 {NULL, 0, NULL, 0},
1965 static char const* const format_args[] =
1967 "roff", "tex", NULL
1970 static enum Format const format_vals[] =
1972 ROFF_FORMAT, TEX_FORMAT
1976 main (int argc, char **argv)
1978 int optchar; /* argument character */
1979 int file_index; /* index in text input file arrays */
1981 /* Decode program options. */
1983 initialize_main (&argc, &argv);
1984 program_name = argv[0];
1985 setlocale (LC_ALL, "");
1986 bindtextdomain (PACKAGE, LOCALEDIR);
1987 textdomain (PACKAGE);
1989 atexit (close_stdout);
1991 #if HAVE_SETCHRCLASS
1992 setchrclass (NULL);
1993 #endif
1995 while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
1996 long_options, NULL),
1997 optchar != EOF)
1999 switch (optchar)
2001 default:
2002 usage (EXIT_FAILURE);
2004 case 'C':
2005 fputs (_("\
2006 This program is free software; you can redistribute it and/or modify\n\
2007 it under the terms of the GNU General Public License as published by\n\
2008 the Free Software Foundation; either version 2, or (at your option)\n\
2009 any later version.\n\
2011 "), stdout);
2012 fputs (_("\
2013 This program is distributed in the hope that it will be useful,\n\
2014 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
2015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
2016 GNU General Public License for more details.\n\
2018 "), stdout);
2019 fputs (_("\
2020 You should have received a copy of the GNU General Public License\n\
2021 along with this program; if not, write to the Free Software Foundation,\n\
2022 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"),
2023 stdout);
2025 exit (EXIT_SUCCESS);
2027 case 'G':
2028 gnu_extensions = false;
2029 break;
2031 case 'b':
2032 break_file = optarg;
2033 break;
2035 case 'f':
2036 ignore_case = true;
2037 break;
2039 case 'g':
2041 unsigned long int tmp_ulong;
2042 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2043 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2044 error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
2045 quotearg (optarg));
2046 gap_size = tmp_ulong;
2047 break;
2050 case 'i':
2051 ignore_file = optarg;
2052 break;
2054 case 'o':
2055 only_file = optarg;
2056 break;
2058 case 'r':
2059 input_reference = true;
2060 break;
2062 case 't':
2063 /* Yet to understand... */
2064 break;
2066 case 'w':
2068 unsigned long int tmp_ulong;
2069 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2070 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2071 error (EXIT_FAILURE, 0, _("invalid line width: %s"),
2072 quotearg (optarg));
2073 line_width = tmp_ulong;
2074 break;
2077 case 'A':
2078 auto_reference = true;
2079 break;
2081 case 'F':
2082 truncation_string = copy_unescaped_string (optarg);
2083 break;
2085 case 'M':
2086 macro_name = optarg;
2087 break;
2089 case 'O':
2090 output_format = ROFF_FORMAT;
2091 break;
2093 case 'R':
2094 right_reference = true;
2095 break;
2097 case 'S':
2098 context_regex_string = copy_unescaped_string (optarg);
2099 break;
2101 case 'T':
2102 output_format = TEX_FORMAT;
2103 break;
2105 case 'W':
2106 word_regex_string = copy_unescaped_string (optarg);
2107 break;
2109 case 10:
2110 output_format = XARGMATCH ("--format", optarg,
2111 format_args, format_vals);
2112 case_GETOPT_HELP_CHAR;
2114 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2118 /* Change the default Ignore file if one is defined. */
2120 #ifdef DEFAULT_IGNORE_FILE
2121 if (!ignore_file)
2122 ignore_file = DEFAULT_IGNORE_FILE;
2123 #endif
2125 /* Process remaining arguments. If GNU extensions are enabled, process
2126 all arguments as input parameters. If disabled, accept at most two
2127 arguments, the second of which is an output parameter. */
2129 if (optind == argc)
2132 /* No more argument simply means: read standard input. */
2134 input_file_name = xmalloc (sizeof *input_file_name);
2135 file_line_count = xmalloc (sizeof *file_line_count);
2136 number_input_files = 1;
2137 input_file_name[0] = NULL;
2139 else if (gnu_extensions)
2141 number_input_files = argc - optind;
2142 input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
2143 file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
2145 for (file_index = 0; file_index < number_input_files; file_index++)
2147 input_file_name[file_index] = argv[optind];
2148 if (!*argv[optind] || STREQ (argv[optind], "-"))
2149 input_file_name[0] = NULL;
2150 else
2151 input_file_name[0] = argv[optind];
2152 optind++;
2155 else
2158 /* There is one necessary input file. */
2160 number_input_files = 1;
2161 input_file_name = xmalloc (sizeof *input_file_name);
2162 file_line_count = xmalloc (sizeof *file_line_count);
2163 if (!*argv[optind] || STREQ (argv[optind], "-"))
2164 input_file_name[0] = NULL;
2165 else
2166 input_file_name[0] = argv[optind];
2167 optind++;
2169 /* Redirect standard output, only if requested. */
2171 if (optind < argc)
2173 if (! freopen (argv[optind], "w", stdout))
2174 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2175 optind++;
2178 /* Diagnose any other argument as an error. */
2180 if (optind < argc)
2182 error (0, 0, _("extra operand %s"), quote (argv[optind]));
2183 usage (EXIT_FAILURE);
2187 /* If the output format has not been explicitly selected, choose dumb
2188 terminal format if GNU extensions are enabled, else `roff' format. */
2190 if (output_format == UNKNOWN_FORMAT)
2191 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2193 /* Initialize the main tables. */
2195 initialize_regex ();
2197 /* Read `Break character' file, if any. */
2199 if (break_file)
2200 digest_break_file (break_file);
2202 /* Read `Ignore words' file and `Only words' files, if any. If any of
2203 these files is empty, reset the name of the file to NULL, to avoid
2204 unnecessary calls to search_table. */
2206 if (ignore_file)
2208 digest_word_file (ignore_file, &ignore_table);
2209 if (ignore_table.length == 0)
2210 ignore_file = NULL;
2213 if (only_file)
2215 digest_word_file (only_file, &only_table);
2216 if (only_table.length == 0)
2217 only_file = NULL;
2220 /* Prepare to study all the input files. */
2222 number_of_occurs[0] = 0;
2223 total_line_count = 0;
2224 maximum_word_length = 0;
2225 reference_max_width = 0;
2227 for (file_index = 0; file_index < number_input_files; file_index++)
2230 /* Read the file in core, than study it. */
2232 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2233 find_occurs_in_text ();
2235 /* Maintain for each file how many lines has been read so far when its
2236 end is reached. Incrementing the count first is a simple kludge to
2237 handle a possible incomplete line at end of file. */
2239 total_line_count++;
2240 file_line_count[file_index] = total_line_count;
2243 /* Do the output process phase. */
2245 sort_found_occurs ();
2246 fix_output_parameters ();
2247 generate_all_output ();
2249 /* All done. */
2251 exit (EXIT_SUCCESS);