remove redundant const directives
[platform/upstream/coreutils.git] / src / ptx.c
1 /* Permuted index for GNU, with keywords in their context.
2    Copyright (C) 1990, 1991, 1993, 1998-2008 Free Software Foundation, Inc.
3    François Pinard <pinard@iro.umontreal.ca>, 1988.
4
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.
9
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.
14
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/>.
17
18    François Pinard <pinard@iro.umontreal.ca> */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/types.h>
25 #include "system.h"
26 #include "argmatch.h"
27 #include "diacrit.h"
28 #include "error.h"
29 #include "quote.h"
30 #include "quotearg.h"
31 #include "regex.h"
32 #include "xstrtol.h"
33
34 /* The official name of this program (e.g., no `g' prefix).  */
35 #define PROGRAM_NAME "ptx"
36
37 /* TRANSLATORS: Please translate "F. Pinard" to "François Pinard"
38    if "ç" (c-with-cedilla) is available in the translation's character
39    set and encoding.  */
40 #define AUTHORS proper_name_utf8 ("F. Pinard", "Fran\xc3\xa7ois Pinard")
41
42 /* Number of possible characters in a byte.  */
43 #define CHAR_SET_SIZE 256
44
45 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
46 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
47                      : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
48 #define OCTTOBIN(C) ((C) - '0')
49
50 /* Debugging the memory allocator.  */
51
52 #if WITH_DMALLOC
53 # define MALLOC_FUNC_CHECK 1
54 # include <dmalloc.h>
55 #endif
56 \f
57 /* Global definitions.  */
58
59 /* FIXME: There are many unchecked integer overflows in this file,
60    that will cause this command to misbehave given large inputs or
61    options.  Many of the "int" values below should be "size_t" or
62    something else like that.  */
63
64 /* Reallocation step when swallowing non regular files.  The value is not
65    the actual reallocation step, but its base two logarithm.  */
66 #define SWALLOW_REALLOC_LOG 12
67
68 /* Imported from "regex.c".  */
69 #define Sword 1
70
71 /* Program options.  */
72
73 enum Format
74 {
75   UNKNOWN_FORMAT,               /* output format still unknown */
76   DUMB_FORMAT,                  /* output for a dumb terminal */
77   ROFF_FORMAT,                  /* output for `troff' or `nroff' */
78   TEX_FORMAT                    /* output for `TeX' or `LaTeX' */
79 };
80
81 static bool gnu_extensions = true;      /* trigger all GNU extensions */
82 static bool auto_reference = false;     /* refs are `file_name:line_number:' */
83 static bool input_reference = false;    /* refs at beginning of input lines */
84 static bool right_reference = false;    /* output refs after right context  */
85 static int line_width = 72;     /* output line width in characters */
86 static int gap_size = 3;        /* number of spaces between output fields */
87 static const char *truncation_string = "/";
88                                 /* string used to mark line truncations */
89 static const char *macro_name = "xx";   /* macro name for roff or TeX output */
90 static enum Format output_format = UNKNOWN_FORMAT;
91                                 /* output format */
92
93 static bool ignore_case = false;        /* fold lower to upper for sorting */
94 static const char *break_file = NULL;   /* name of the `Break characters' file */
95 static const char *only_file = NULL;    /* name of the `Only words' file */
96 static const char *ignore_file = NULL;  /* name of the `Ignore words' file */
97
98 /* Options that use regular expressions.  */
99 struct regex_data
100 {
101   /* The original regular expression, as a string.  */
102   char const *string;
103
104   /* The compiled regular expression, and its fastmap.  */
105   struct re_pattern_buffer pattern;
106   char fastmap[UCHAR_MAX + 1];
107 };
108
109 static struct regex_data context_regex; /* end of context */
110 static struct regex_data word_regex;    /* keyword */
111
112 /* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
113    whole file.  A WORD is something smaller, its length should fit in a
114    short integer.  A WORD_TABLE may contain several WORDs.  */
115
116 typedef struct
117   {
118     char *start;                /* pointer to beginning of region */
119     char *end;                  /* pointer to end + 1 of region */
120   }
121 BLOCK;
122
123 typedef struct
124   {
125     char *start;                /* pointer to beginning of region */
126     short int size;             /* length of the region */
127   }
128 WORD;
129
130 typedef struct
131   {
132     WORD *start;                /* array of WORDs */
133     size_t alloc;               /* allocated length */
134     size_t length;              /* number of used entries */
135   }
136 WORD_TABLE;
137
138 /* Pattern description tables.  */
139
140 /* For each character, provide its folded equivalent.  */
141 static unsigned char folded_chars[CHAR_SET_SIZE];
142
143 /* End of context pattern register indices.  */
144 static struct re_registers context_regs;
145
146 /* Keyword pattern register indices.  */
147 static struct re_registers word_regs;
148
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];
155
156 /* Maximum length of any word read.  */
157 static int maximum_word_length;
158
159 /* Maximum width of any reference used.  */
160 static int reference_max_width;
161
162 /* Ignore and Only word tables.  */
163
164 static WORD_TABLE ignore_table; /* table of words to ignore */
165 static WORD_TABLE only_table;           /* table of words to select */
166
167 /* Source text table, and scanning macros.  */
168
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 */
173
174 static BLOCK text_buffer;       /* file to study */
175
176 /* SKIP_NON_WHITE used only for getting or skipping the reference.  */
177
178 #define SKIP_NON_WHITE(cursor, limit) \
179   while (cursor < limit && ! isspace (to_uchar (*cursor)))              \
180     cursor++
181
182 #define SKIP_WHITE(cursor, limit) \
183   while (cursor < limit && isspace (to_uchar (*cursor)))                \
184     cursor++
185
186 #define SKIP_WHITE_BACKWARDS(cursor, start) \
187   while (cursor > start && isspace (to_uchar (cursor[-1])))             \
188     cursor--
189
190 #define SKIP_SOMETHING(cursor, limit) \
191   if (word_regex.string)                                                \
192     {                                                                   \
193       regoff_t count;                                                   \
194       count = re_match (&word_regex.pattern, cursor, limit - cursor, 0, NULL); \
195       if (count == -2)                                                  \
196         matcher_error ();                                               \
197       cursor += count == -1 ? 1 : count;                                \
198     }                                                                   \
199   else if (word_fastmap[to_uchar (*cursor)])                            \
200     while (cursor < limit && word_fastmap[to_uchar (*cursor)])          \
201       cursor++;                                                         \
202   else                                                                  \
203     cursor++
204
205 /* Occurrences table.
206
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.
212
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.
219
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.
224
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.  */
231
232 typedef short int DELTA;        /* to hold displacement within one context */
233
234 typedef struct
235   {
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 */
240   }
241 OCCURS;
242
243 /* The various OCCURS tables are indexed by the language.  But the time
244    being, there is no such multiple language support.  */
245
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 */
249
250
251 /* Communication among output routines.  */
252
253 /* Indicate if special output processing is requested for each character.  */
254 static char edited_flag[CHAR_SET_SIZE];
255
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 */
260
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. */
266
267 static BLOCK tail;              /* tail field */
268 static int tail_truncation;     /* flag truncation after the tail field */
269
270 static BLOCK before;            /* before field */
271 static int before_truncation;   /* flag truncation before the before field */
272
273 static BLOCK keyafter;          /* keyword-and-after field */
274 static int keyafter_truncation; /* flag truncation after the keyafter field */
275
276 static BLOCK head;              /* head field */
277 static int head_truncation;     /* flag truncation before the head field */
278
279 static BLOCK reference;         /* reference field for input reference mode */
280 \f
281 /* Miscellaneous routines.  */
282
283 /* Diagnose an error in the regular expression matcher.  Then exit.  */
284
285 static void ATTRIBUTE_NORETURN
286 matcher_error (void)
287 {
288   error (0, errno, _("error in regular expression matcher"));
289   exit (EXIT_FAILURE);
290 }
291
292 /*------------------------------------------------------.
293 | Duplicate string STRING, while evaluating \-escapes.  |
294 `------------------------------------------------------*/
295
296 /* Loosely adapted from GNU sh-utils printf.c code.  */
297
298 static char *
299 copy_unescaped_string (const char *string)
300 {
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 */
305
306   result = xmalloc (strlen (string) + 1);
307   cursor = result;
308
309   while (*string)
310     {
311       if (*string == '\\')
312         {
313           string++;
314           switch (*string)
315             {
316             case 'x':           /* \xhhh escape, 3 chars maximum */
317               value = 0;
318               for (length = 0, string++;
319                    length < 3 && isxdigit (to_uchar (*string));
320                    length++, string++)
321                 value = value * 16 + HEXTOBIN (*string);
322               if (length == 0)
323                 {
324                   *cursor++ = '\\';
325                   *cursor++ = 'x';
326                 }
327               else
328                 *cursor++ = value;
329               break;
330
331             case '0':           /* \0ooo escape, 3 chars maximum */
332               value = 0;
333               for (length = 0, string++;
334                    length < 3 && ISODIGIT (*string);
335                    length++, string++)
336                 value = value * 8 + OCTTOBIN (*string);
337               *cursor++ = value;
338               break;
339
340             case 'a':           /* alert */
341 #if __STDC__
342               *cursor++ = '\a';
343 #else
344               *cursor++ = 7;
345 #endif
346               string++;
347               break;
348
349             case 'b':           /* backspace */
350               *cursor++ = '\b';
351               string++;
352               break;
353
354             case 'c':           /* cancel the rest of the output */
355               while (*string)
356                 string++;
357               break;
358
359             case 'f':           /* form feed */
360               *cursor++ = '\f';
361               string++;
362               break;
363
364             case 'n':           /* new line */
365               *cursor++ = '\n';
366               string++;
367               break;
368
369             case 'r':           /* carriage return */
370               *cursor++ = '\r';
371               string++;
372               break;
373
374             case 't':           /* horizontal tab */
375               *cursor++ = '\t';
376               string++;
377               break;
378
379             case 'v':           /* vertical tab */
380 #if __STDC__
381               *cursor++ = '\v';
382 #else
383               *cursor++ = 11;
384 #endif
385               string++;
386               break;
387
388             case '\0':          /* lone backslash at end of string */
389               /* ignore it */
390               break;
391
392             default:
393               *cursor++ = '\\';
394               *cursor++ = *string++;
395               break;
396             }
397         }
398       else
399         *cursor++ = *string++;
400     }
401
402   *cursor = '\0';
403   return result;
404 }
405
406 /*--------------------------------------------------------------------------.
407 | Compile the regex represented by REGEX, diagnose and abort if any error.  |
408 `--------------------------------------------------------------------------*/
409
410 static void
411 compile_regex (struct regex_data *regex)
412 {
413   struct re_pattern_buffer *pattern = &regex->pattern;
414   char const *string = regex->string;
415   char const *message;
416
417   pattern->buffer = NULL;
418   pattern->allocated = 0;
419   pattern->fastmap = regex->fastmap;
420   pattern->translate = ignore_case ? folded_chars : NULL;
421
422   message = re_compile_pattern (string, strlen (string), pattern);
423   if (message)
424     error (EXIT_FAILURE, 0, _("%s (for regexp %s)"), message, quote (string));
425
426   /* The fastmap should be compiled before `re_match'.  The following
427      call is not mandatory, because `re_search' is always called sooner,
428      and it compiles the fastmap if this has not been done yet.  */
429
430   re_compile_fastmap (pattern);
431 }
432
433 /*------------------------------------------------------------------------.
434 | This will initialize various tables for pattern match and compiles some |
435 | regexps.                                                                |
436 `------------------------------------------------------------------------*/
437
438 static void
439 initialize_regex (void)
440 {
441   int character;                /* character value */
442
443   /* Initialize the case folding table.  */
444
445   if (ignore_case)
446     for (character = 0; character < CHAR_SET_SIZE; character++)
447       folded_chars[character] = toupper (character);
448
449   /* Unless the user already provided a description of the end of line or
450      end of sentence sequence, select an end of line sequence to compile.
451      If the user provided an empty definition, thus disabling end of line
452      or sentence feature, make it NULL to speed up tests.  If GNU
453      extensions are enabled, use end of sentence like in GNU emacs.  If
454      disabled, use end of lines.  */
455
456   if (context_regex.string)
457     {
458       if (!*context_regex.string)
459         context_regex.string = NULL;
460     }
461   else if (gnu_extensions & !input_reference)
462     context_regex.string = "[.?!][]\"')}]*\\($\\|\t\\|  \\)[ \t\n]*";
463   else
464     context_regex.string = "\n";
465
466   if (context_regex.string)
467     compile_regex (&context_regex);
468
469   /* If the user has already provided a non-empty regexp to describe
470      words, compile it.  Else, unless this has already been done through
471      a user provided Break character file, construct a fastmap of
472      characters that may appear in a word.  If GNU extensions enabled,
473      include only letters of the underlying character set.  If disabled,
474      include almost everything, even punctuations; stop only on white
475      space.  */
476
477   if (word_regex.string)
478     compile_regex (&word_regex);
479   else if (!break_file)
480     {
481       if (gnu_extensions)
482         {
483
484           /* Simulate \w+.  */
485
486           for (character = 0; character < CHAR_SET_SIZE; character++)
487             word_fastmap[character] = !! isalpha (character);
488         }
489       else
490         {
491
492           /* Simulate [^ \t\n]+.  */
493
494           memset (word_fastmap, 1, CHAR_SET_SIZE);
495           word_fastmap[' '] = 0;
496           word_fastmap['\t'] = 0;
497           word_fastmap['\n'] = 0;
498         }
499     }
500 }
501
502 /*------------------------------------------------------------------------.
503 | This routine will attempt to swallow a whole file name FILE_NAME into a |
504 | contiguous region of memory and return a description of it into BLOCK.  |
505 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-".     |
506 |                                                                         |
507 | Previously, in some cases, white space compression was attempted while  |
508 | inputting text.  This was defeating some regexps like default end of    |
509 | sentence, which checks for two consecutive spaces.  If white space      |
510 | compression is ever reinstated, it should be in output routines.        |
511 `------------------------------------------------------------------------*/
512
513 static void
514 swallow_file_in_memory (const char *file_name, BLOCK *block)
515 {
516   int file_handle;              /* file descriptor number */
517   struct stat stat_block;       /* stat block for file */
518   size_t allocated_length;      /* allocated length of memory buffer */
519   size_t used_length;           /* used length in memory buffer */
520   int read_length;              /* number of character gotten on last read */
521
522   /* As special cases, a file name which is NULL or "-" indicates standard
523      input, which is already opened.  In all other cases, open the file from
524      its name.  */
525   bool using_stdin = !file_name || !*file_name || STREQ (file_name, "-");
526   if (using_stdin)
527     file_handle = STDIN_FILENO;
528   else
529     if ((file_handle = open (file_name, O_RDONLY)) < 0)
530       error (EXIT_FAILURE, errno, "%s", file_name);
531
532   /* If the file is a plain, regular file, allocate the memory buffer all at
533      once and swallow the file in one blow.  In other cases, read the file
534      repeatedly in smaller chunks until we have it all, reallocating memory
535      once in a while, as we go.  */
536
537   if (fstat (file_handle, &stat_block) < 0)
538     error (EXIT_FAILURE, errno, "%s", file_name);
539
540   if (S_ISREG (stat_block.st_mode))
541     {
542       size_t in_memory_size;
543
544       block->start = xmalloc ((size_t) stat_block.st_size);
545
546       if ((in_memory_size = read (file_handle,
547                                   block->start, (size_t) stat_block.st_size))
548           != stat_block.st_size)
549         {
550 #if MSDOS
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
555              CR+LF.  */
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);
559           else
560 #endif /* not MSDOS */
561
562             error (EXIT_FAILURE, errno, "%s", file_name);
563         }
564       block->end = block->start + in_memory_size;
565     }
566   else
567     {
568       block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
569       used_length = 0;
570       allocated_length = (1 << SWALLOW_REALLOC_LOG);
571
572       while (read_length = read (file_handle,
573                                  block->start + used_length,
574                                  allocated_length - used_length),
575              read_length > 0)
576         {
577           used_length += read_length;
578           if (used_length == allocated_length)
579             {
580               allocated_length += (1 << SWALLOW_REALLOC_LOG);
581               block->start
582                 = xrealloc (block->start, allocated_length);
583             }
584         }
585
586       if (read_length < 0)
587         error (EXIT_FAILURE, errno, "%s", file_name);
588
589       block->end = block->start + used_length;
590     }
591
592   /* Close the file, but only if it was not the standard input.  */
593
594   if (! using_stdin && close (file_handle) != 0)
595     error (EXIT_FAILURE, errno, "%s", file_name);
596 }
597 \f
598 /* Sort and search routines.  */
599
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.                   |
604 |                                                                           |
605 | If a word is indeed a prefix of the other, the shorter should go first.   |
606 `--------------------------------------------------------------------------*/
607
608 static int
609 compare_words (const void *void_first, const void *void_second)
610 {
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 */
616
617   length = first->size < second->size ? first->size : second->size;
618
619   if (ignore_case)
620     {
621       for (counter = 0; counter < length; counter++)
622         {
623           value = (folded_chars [to_uchar (first->start[counter])]
624                    - folded_chars [to_uchar (second->start[counter])]);
625           if (value != 0)
626             return value;
627         }
628     }
629   else
630     {
631       for (counter = 0; counter < length; counter++)
632         {
633           value = (to_uchar (first->start[counter])
634                    - to_uchar (second->start[counter]));
635           if (value != 0)
636             return value;
637         }
638     }
639
640   return first->size - second->size;
641 #undef first
642 #undef second
643 }
644
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 `-----------------------------------------------------------------------*/
650
651 static int
652 compare_occurs (const void *void_first, const void *void_second)
653 {
654 #define first ((const OCCURS *) void_first)
655 #define second ((const OCCURS *) void_second)
656   int value;
657
658   value = compare_words (&first->key, &second->key);
659   return value == 0 ? first->key.start - second->key.start : value;
660 #undef first
661 #undef second
662 }
663
664 /*------------------------------------------------------------.
665 | Return !0 if WORD appears in TABLE.  Uses a binary search.  |
666 `------------------------------------------------------------*/
667
668 static int
669 search_table (WORD *word, WORD_TABLE *table)
670 {
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 */
675
676   lowest = 0;
677   highest = table->length - 1;
678   while (lowest <= highest)
679     {
680       middle = (lowest + highest) / 2;
681       value = compare_words (word, table->start + middle);
682       if (value < 0)
683         highest = middle - 1;
684       else if (value > 0)
685         lowest = middle + 1;
686       else
687         return 1;
688     }
689   return 0;
690 }
691
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 `---------------------------------------------------------------------*/
697
698 static void
699 sort_found_occurs (void)
700 {
701
702   /* Only one language for the time being.  */
703
704   qsort (occurs_table[0], number_of_occurs[0], sizeof **occurs_table,
705          compare_occurs);
706 }
707 \f
708 /* Parameter files reading routines.  */
709
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 `----------------------------------------------------------------------*/
715
716 static void
717 digest_break_file (const char *file_name)
718 {
719   BLOCK file_contents;          /* to receive a copy of the file */
720   char *cursor;                 /* cursor in file copy */
721
722   swallow_file_in_memory (file_name, &file_contents);
723
724   /* Make the fastmap and record the file contents in it.  */
725
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;
729
730   if (!gnu_extensions)
731     {
732
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.  */
738
739       word_fastmap[' '] = 0;
740       word_fastmap['\t'] = 0;
741       word_fastmap['\n'] = 0;
742     }
743
744   /* Return the space of the file, which is no more required.  */
745
746   free (file_contents.start);
747 }
748
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 `-----------------------------------------------------------------------*/
755
756 static void
757 digest_word_file (const char *file_name, WORD_TABLE *table)
758 {
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 */
762
763   swallow_file_in_memory (file_name, &file_contents);
764
765   table->start = NULL;
766   table->alloc = 0;
767   table->length = 0;
768
769   /* Read the whole file.  */
770
771   cursor = file_contents.start;
772   while (cursor < file_contents.end)
773     {
774
775       /* Read one line, and save the word in contains.  */
776
777       word_start = cursor;
778       while (cursor < file_contents.end && *cursor != '\n')
779         cursor++;
780
781       /* Record the word in table if it is not empty.  */
782
783       if (cursor > word_start)
784         {
785           if (table->length == table->alloc)
786             {
787               if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
788                 xalloc_die ();
789               table->alloc = table->alloc * 2 + 1;
790               table->start = xrealloc (table->start,
791                                        table->alloc * sizeof *table->start);
792             }
793
794           table->start[table->length].start = word_start;
795           table->start[table->length].size = cursor - word_start;
796           table->length++;
797         }
798
799       /* This test allows for an incomplete line at end of file.  */
800
801       if (cursor < file_contents.end)
802         cursor++;
803     }
804
805   /* Finally, sort all the words read.  */
806
807   qsort (table->start, table->length, sizeof table->start[0], compare_words);
808 }
809 \f
810 /* Keyword recognition and selection.  */
811
812 /*----------------------------------------------------------------------.
813 | For each keyword in the source text, constructs an OCCURS structure.  |
814 `----------------------------------------------------------------------*/
815
816 static void
817 find_occurs_in_text (void)
818 {
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 */
826
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 */
832
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.  */
836
837   reference_length = 0;
838
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.
842
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.  */
847
848   line_start = text_buffer.start;
849   line_scan = line_start;
850   if (input_reference)
851     {
852       SKIP_NON_WHITE (line_scan, text_buffer.end);
853       reference_length = line_scan - line_start;
854       SKIP_WHITE (line_scan, text_buffer.end);
855     }
856
857   /* Process the whole buffer, one line or one sentence at a time.  */
858
859   for (cursor = text_buffer.start;
860        cursor < text_buffer.end;
861        cursor = next_context_start)
862     {
863
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.  */
867
868       context_start = cursor;
869
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.  */
877
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))
883           {
884           case -2:
885             matcher_error ();
886
887           case -1:
888             break;
889
890           default:
891             next_context_start = cursor + context_regs.end[0];
892             break;
893           }
894
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.  */
898
899       context_end = next_context_start;
900       SKIP_WHITE_BACKWARDS (context_end, context_start);
901
902       /* Read and process a single input line or sentence, one word at a
903          time.  */
904
905       while (1)
906         {
907           if (word_regex.string)
908
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
911                the loop.  */
912
913             {
914               regoff_t r = re_search (&word_regex.pattern, cursor,
915                                       context_end - cursor,
916                                       0, context_end - cursor, &word_regs);
917               if (r == -2)
918                 matcher_error ();
919               if (r == -1)
920                 break;
921               word_start = cursor + word_regs.start[0];
922               word_end = cursor + word_regs.end[0];
923             }
924           else
925
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.  */
929
930             {
931               scan = cursor;
932               while (scan < context_end
933                      && !word_fastmap[to_uchar (*scan)])
934                 scan++;
935
936               if (scan == context_end)
937                 break;
938
939               word_start = scan;
940
941               while (scan < context_end
942                      && word_fastmap[to_uchar (*scan)])
943                 scan++;
944
945               word_end = scan;
946             }
947
948           /* Skip right to the beginning of the found word.  */
949
950           cursor = word_start;
951
952           /* Skip any zero length word.  Just advance a single position,
953              then go fetch the next word.  */
954
955           if (word_end == word_start)
956             {
957               cursor++;
958               continue;
959             }
960
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.  */
967
968           possible_key.start = cursor;
969           possible_key.size = word_end - word_start;
970           cursor += possible_key.size;
971
972           if (possible_key.size > maximum_word_length)
973             maximum_word_length = possible_key.size;
974
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.  */
979
980           if (input_reference)
981             {
982               while (line_scan < possible_key.start)
983                 if (*line_scan == '\n')
984                   {
985                     total_line_count++;
986                     line_scan++;
987                     line_start = line_scan;
988                     SKIP_NON_WHITE (line_scan, text_buffer.end);
989                     reference_length = line_scan - line_start;
990                   }
991                 else
992                   line_scan++;
993               if (line_scan > possible_key.start)
994                 continue;
995             }
996
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.
1000
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.  */
1006
1007           if (ignore_file && search_table (&possible_key, &ignore_table))
1008             continue;
1009           if (only_file && !search_table (&possible_key, &only_table))
1010             continue;
1011
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.  */
1015
1016           if (number_of_occurs[0] == occurs_alloc[0])
1017             {
1018               if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1019                   < occurs_alloc[0])
1020                 xalloc_die ();
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]);
1024             }
1025
1026           occurs_cursor = occurs_table[0] + number_of_occurs[0];
1027
1028           /* Define the refence field, if any.  */
1029
1030           if (auto_reference)
1031             {
1032
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
1037                  executed.  */
1038
1039               while (line_scan < possible_key.start)
1040                 if (*line_scan == '\n')
1041                   {
1042                     total_line_count++;
1043                     line_scan++;
1044                     line_start = line_scan;
1045                     SKIP_NON_WHITE (line_scan, text_buffer.end);
1046                   }
1047                 else
1048                   line_scan++;
1049
1050               occurs_cursor->reference = total_line_count;
1051             }
1052           else if (input_reference)
1053             {
1054
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'.  */
1059
1060               occurs_cursor->reference
1061                 = (DELTA) (line_start - possible_key.start);
1062               if (reference_length > reference_max_width)
1063                 reference_max_width = reference_length;
1064             }
1065
1066           /* Exclude the reference from the context in simple cases.  */
1067
1068           if (input_reference && line_start == context_start)
1069             {
1070               SKIP_NON_WHITE (context_start, context_end);
1071               SKIP_WHITE (context_start, context_end);
1072             }
1073
1074           /* Completes the OCCURS structure.  */
1075
1076           occurs_cursor->key = possible_key;
1077           occurs_cursor->left = context_start - possible_key.start;
1078           occurs_cursor->right = context_end - possible_key.start;
1079
1080           number_of_occurs[0]++;
1081         }
1082     }
1083 }
1084 \f
1085 /* Formatting and actual output - service routines.  */
1086
1087 /*-----------------------------------------.
1088 | Prints some NUMBER of spaces on stdout.  |
1089 `-----------------------------------------*/
1090
1091 static void
1092 print_spaces (int number)
1093 {
1094   int counter;
1095
1096   for (counter = number; counter > 0; counter--)
1097     putchar (' ');
1098 }
1099
1100 /*-------------------------------------.
1101 | Prints the field provided by FIELD.  |
1102 `-------------------------------------*/
1103
1104 static void
1105 print_field (BLOCK field)
1106 {
1107   char *cursor;                 /* Cursor in field to print */
1108   int base;                     /* Base character, without diacritic */
1109   int diacritic;                /* Diacritic code for the character */
1110
1111   /* Whitespace is not really compressed.  Instead, each white space
1112      character (tab, vt, ht etc.) is printed as one single space.  */
1113
1114   for (cursor = field.start; cursor < field.end; cursor++)
1115     {
1116       unsigned char character = *cursor;
1117       if (edited_flag[character])
1118         {
1119
1120           /* First check if this is a diacriticized character.
1121
1122              This works only for TeX.  I do not know how diacriticized
1123              letters work with `roff'.  Please someone explain it to me!  */
1124
1125           diacritic = todiac (character);
1126           if (diacritic != 0 && output_format == TEX_FORMAT)
1127             {
1128               base = tobase (character);
1129               switch (diacritic)
1130                 {
1131
1132                 case 1:         /* Latin diphthongs */
1133                   switch (base)
1134                     {
1135                     case 'o':
1136                       fputs ("\\oe{}", stdout);
1137                       break;
1138
1139                     case 'O':
1140                       fputs ("\\OE{}", stdout);
1141                       break;
1142
1143                     case 'a':
1144                       fputs ("\\ae{}", stdout);
1145                       break;
1146
1147                     case 'A':
1148                       fputs ("\\AE{}", stdout);
1149                       break;
1150
1151                     default:
1152                       putchar (' ');
1153                     }
1154                   break;
1155
1156                 case 2:         /* Acute accent */
1157                   printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1158                   break;
1159
1160                 case 3:         /* Grave accent */
1161                   printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1162                   break;
1163
1164                 case 4:         /* Circumflex accent */
1165                   printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1166                   break;
1167
1168                 case 5:         /* Diaeresis */
1169                   printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1170                   break;
1171
1172                 case 6:         /* Tilde accent */
1173                   printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1174                   break;
1175
1176                 case 7:         /* Cedilla */
1177                   printf ("\\c{%c}", base);
1178                   break;
1179
1180                 case 8:         /* Small circle beneath */
1181                   switch (base)
1182                     {
1183                     case 'a':
1184                       fputs ("\\aa{}", stdout);
1185                       break;
1186
1187                     case 'A':
1188                       fputs ("\\AA{}", stdout);
1189                       break;
1190
1191                     default:
1192                       putchar (' ');
1193                     }
1194                   break;
1195
1196                 case 9:         /* Strike through */
1197                   switch (base)
1198                     {
1199                     case 'o':
1200                       fputs ("\\o{}", stdout);
1201                       break;
1202
1203                     case 'O':
1204                       fputs ("\\O{}", stdout);
1205                       break;
1206
1207                     default:
1208                       putchar (' ');
1209                     }
1210                   break;
1211                 }
1212             }
1213           else
1214
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.  */
1218
1219             switch (character)
1220               {
1221               case '"':
1222                 /* In roff output format, double any quote.  */
1223                 putchar ('"');
1224                 putchar ('"');
1225                 break;
1226
1227               case '$':
1228               case '%':
1229               case '&':
1230               case '#':
1231               case '_':
1232                 /* In TeX output format, precede these with a backslash.  */
1233                 putchar ('\\');
1234                 putchar (character);
1235                 break;
1236
1237               case '{':
1238               case '}':
1239                 /* In TeX output format, precede these with a backslash and
1240                    force mathematical mode.  */
1241                 printf ("$\\%c$", character);
1242                 break;
1243
1244               case '\\':
1245                 /* In TeX output mode, request production of a backslash.  */
1246                 fputs ("\\backslash{}", stdout);
1247                 break;
1248
1249               default:
1250                 /* Any other flagged character produces a single space.  */
1251                 putchar (' ');
1252               }
1253         }
1254       else
1255         putchar (*cursor);
1256     }
1257 }
1258 \f
1259 /* Formatting and actual output - planning routines.  */
1260
1261 /*--------------------------------------------------------------------.
1262 | From information collected from command line options and input file |
1263 | readings, compute and fix some output parameter values.             |
1264 `--------------------------------------------------------------------*/
1265
1266 static void
1267 fix_output_parameters (void)
1268 {
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 */
1275
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.  */
1279
1280   if (auto_reference)
1281     {
1282       reference_max_width = 0;
1283       for (file_index = 0; file_index < number_input_files; file_index++)
1284         {
1285           line_ordinal = file_line_count[file_index] + 1;
1286           if (file_index > 0)
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;
1294         }
1295       reference_max_width++;
1296       reference.start = xmalloc ((size_t) reference_max_width + 1);
1297     }
1298
1299   /* If the reference appears to the left of the output line, reserve some
1300      space for it right away, including one gap size.  */
1301
1302   if ((auto_reference | input_reference) & !right_reference)
1303     line_width -= reference_max_width + gap_size;
1304
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.  */
1316
1317   half_line_width = line_width / 2;
1318   before_max_width = half_line_width - gap_size;
1319   keyafter_max_width = half_line_width;
1320
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.  */
1324
1325   if (truncation_string && *truncation_string)
1326     truncation_string_length = strlen (truncation_string);
1327   else
1328     truncation_string = NULL;
1329
1330   if (gnu_extensions)
1331     {
1332
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:
1341
1342          . None.
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.
1350
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.  */
1354
1355       before_max_width -= 2 * truncation_string_length;
1356       keyafter_max_width -= 2 * truncation_string_length;
1357     }
1358   else
1359     {
1360
1361       /* I never figured out exactly how UNIX' ptx plans the output width
1362          of its various fields.  If GNU extensions are disabled, do not
1363          try computing the field widths correctly; instead, use the
1364          following formula, which does not completely imitate UNIX' ptx,
1365          but almost.  */
1366
1367       keyafter_max_width -= 2 * truncation_string_length + 1;
1368     }
1369
1370   /* Compute which characters need special output processing.  Initialize
1371      by flagging any white space character.  Some systems do not consider
1372      form feed as a space character, but we do.  */
1373
1374   for (character = 0; character < CHAR_SET_SIZE; character++)
1375     edited_flag[character] = !! isspace (character);
1376   edited_flag['\f'] = 1;
1377
1378   /* Complete the special character flagging according to selected output
1379      format.  */
1380
1381   switch (output_format)
1382     {
1383     case UNKNOWN_FORMAT:
1384       /* Should never happen.  */
1385
1386     case DUMB_FORMAT:
1387       break;
1388
1389     case ROFF_FORMAT:
1390
1391       /* `Quote' characters should be doubled.  */
1392
1393       edited_flag['"'] = 1;
1394       break;
1395
1396     case TEX_FORMAT:
1397
1398       /* Various characters need special processing.  */
1399
1400       for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1401         edited_flag[to_uchar (*cursor)] = 1;
1402
1403       /* Any character with 8th bit set will print to a single space, unless
1404          it is diacriticized.  */
1405
1406       for (character = 0200; character < CHAR_SET_SIZE; character++)
1407         edited_flag[character] = todiac (character) != 0;
1408       break;
1409     }
1410 }
1411
1412 /*------------------------------------------------------------------.
1413 | Compute the position and length of all the output fields, given a |
1414 | pointer to some OCCURS.                                           |
1415 `------------------------------------------------------------------*/
1416
1417 static void
1418 define_all_fields (OCCURS *occurs)
1419 {
1420   int tail_max_width;           /* allowable width of tail field */
1421   int head_max_width;           /* allowable width of head field */
1422   char *cursor;                 /* running cursor in source text */
1423   char *left_context_start;     /* start of left context */
1424   char *right_context_end;      /* end of right context */
1425   char *left_field_start;       /* conservative start for `head'/`before' */
1426   int file_index;               /* index in text input file arrays */
1427   const char *file_name;        /* file name for reference */
1428   int line_ordinal;             /* line ordinal for reference */
1429
1430   /* Define `keyafter', start of left context and end of right context.
1431      `keyafter' starts at the saved position for keyword and extend to the
1432      right from the end of the keyword, eating separators or full words, but
1433      not beyond maximum allowed width for `keyafter' field or limit for the
1434      right context.  Suffix spaces will be removed afterwards.  */
1435
1436   keyafter.start = occurs->key.start;
1437   keyafter.end = keyafter.start + occurs->key.size;
1438   left_context_start = keyafter.start + occurs->left;
1439   right_context_end = keyafter.start + occurs->right;
1440
1441   cursor = keyafter.end;
1442   while (cursor < right_context_end
1443          && cursor <= keyafter.start + keyafter_max_width)
1444     {
1445       keyafter.end = cursor;
1446       SKIP_SOMETHING (cursor, right_context_end);
1447     }
1448   if (cursor <= keyafter.start + keyafter_max_width)
1449     keyafter.end = cursor;
1450
1451   keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1452
1453   SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1454
1455   /* When the left context is wide, it might take some time to catch up from
1456      the left context boundary to the beginning of the `head' or `before'
1457      fields.  So, in this case, to speed the catchup, we jump back from the
1458      keyword, using some secure distance, possibly falling in the middle of
1459      a word.  A secure backward jump would be at least half the maximum
1460      width of a line, plus the size of the longest word met in the whole
1461      input.  We conclude this backward jump by a skip forward of at least
1462      one word.  In this manner, we should not inadvertently accept only part
1463      of a word.  From the reached point, when it will be time to fix the
1464      beginning of `head' or `before' fields, we will skip forward words or
1465      delimiters until we get sufficiently near.  */
1466
1467   if (-occurs->left > half_line_width + maximum_word_length)
1468     {
1469       left_field_start
1470         = keyafter.start - (half_line_width + maximum_word_length);
1471       SKIP_SOMETHING (left_field_start, keyafter.start);
1472     }
1473   else
1474     left_field_start = keyafter.start + occurs->left;
1475
1476   /* `before' certainly ends at the keyword, but not including separating
1477      spaces.  It starts after than the saved value for the left context, by
1478      advancing it until it falls inside the maximum allowed width for the
1479      before field.  There will be no prefix spaces either.  `before' only
1480      advances by skipping single separators or whole words. */
1481
1482   before.start = left_field_start;
1483   before.end = keyafter.start;
1484   SKIP_WHITE_BACKWARDS (before.end, before.start);
1485
1486   while (before.start + before_max_width < before.end)
1487     SKIP_SOMETHING (before.start, before.end);
1488
1489   if (truncation_string)
1490     {
1491       cursor = before.start;
1492       SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1493       before_truncation = cursor > left_context_start;
1494     }
1495   else
1496     before_truncation = 0;
1497
1498   SKIP_WHITE (before.start, text_buffer.end);
1499
1500   /* The tail could not take more columns than what has been left in the
1501      left context field, and a gap is mandatory.  It starts after the
1502      right context, and does not contain prefixed spaces.  It ends at
1503      the end of line, the end of buffer or when the tail field is full,
1504      whichever comes first.  It cannot contain only part of a word, and
1505      has no suffixed spaces.  */
1506
1507   tail_max_width
1508     = before_max_width - (before.end - before.start) - gap_size;
1509
1510   if (tail_max_width > 0)
1511     {
1512       tail.start = keyafter.end;
1513       SKIP_WHITE (tail.start, text_buffer.end);
1514
1515       tail.end = tail.start;
1516       cursor = tail.end;
1517       while (cursor < right_context_end
1518              && cursor < tail.start + tail_max_width)
1519         {
1520           tail.end = cursor;
1521           SKIP_SOMETHING (cursor, right_context_end);
1522         }
1523
1524       if (cursor < tail.start + tail_max_width)
1525         tail.end = cursor;
1526
1527       if (tail.end > tail.start)
1528         {
1529           keyafter_truncation = 0;
1530           tail_truncation = truncation_string && tail.end < right_context_end;
1531         }
1532       else
1533         tail_truncation = 0;
1534
1535       SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1536     }
1537   else
1538     {
1539
1540       /* No place left for a tail field.  */
1541
1542       tail.start = NULL;
1543       tail.end = NULL;
1544       tail_truncation = 0;
1545     }
1546
1547   /* `head' could not take more columns than what has been left in the right
1548      context field, and a gap is mandatory.  It ends before the left
1549      context, and does not contain suffixed spaces.  Its pointer is advanced
1550      until the head field has shrunk to its allowed width.  It cannot
1551      contain only part of a word, and has no suffixed spaces.  */
1552
1553   head_max_width
1554     = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1555
1556   if (head_max_width > 0)
1557     {
1558       head.end = before.start;
1559       SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1560
1561       head.start = left_field_start;
1562       while (head.start + head_max_width < head.end)
1563         SKIP_SOMETHING (head.start, head.end);
1564
1565       if (head.end > head.start)
1566         {
1567           before_truncation = 0;
1568           head_truncation = (truncation_string
1569                              && head.start > left_context_start);
1570         }
1571       else
1572         head_truncation = 0;
1573
1574       SKIP_WHITE (head.start, head.end);
1575     }
1576   else
1577     {
1578
1579       /* No place left for a head field.  */
1580
1581       head.start = NULL;
1582       head.end = NULL;
1583       head_truncation = 0;
1584     }
1585
1586   if (auto_reference)
1587     {
1588
1589       /* Construct the reference text in preallocated space from the file
1590          name and the line number.  Find out in which file the reference
1591          occurred.  Standard input yields an empty file name.  Insure line
1592          numbers are one based, even if they are computed zero based.  */
1593
1594       file_index = 0;
1595       while (file_line_count[file_index] < occurs->reference)
1596         file_index++;
1597
1598       file_name = input_file_name[file_index];
1599       if (!file_name)
1600         file_name = "";
1601
1602       line_ordinal = occurs->reference + 1;
1603       if (file_index > 0)
1604         line_ordinal -= file_line_count[file_index - 1];
1605
1606       sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1607       reference.end = reference.start + strlen (reference.start);
1608     }
1609   else if (input_reference)
1610     {
1611
1612       /* Reference starts at saved position for reference and extends right
1613          until some white space is met.  */
1614
1615       reference.start = keyafter.start + (DELTA) occurs->reference;
1616       reference.end = reference.start;
1617       SKIP_NON_WHITE (reference.end, right_context_end);
1618     }
1619 }
1620 \f
1621 /* Formatting and actual output - control routines.  */
1622
1623 /*----------------------------------------------------------------------.
1624 | Output the current output fields as one line for `troff' or `nroff'.  |
1625 `----------------------------------------------------------------------*/
1626
1627 static void
1628 output_one_roff_line (void)
1629 {
1630   /* Output the `tail' field.  */
1631
1632   printf (".%s \"", macro_name);
1633   print_field (tail);
1634   if (tail_truncation)
1635     fputs (truncation_string, stdout);
1636   putchar ('"');
1637
1638   /* Output the `before' field.  */
1639
1640   fputs (" \"", stdout);
1641   if (before_truncation)
1642     fputs (truncation_string, stdout);
1643   print_field (before);
1644   putchar ('"');
1645
1646   /* Output the `keyafter' field.  */
1647
1648   fputs (" \"", stdout);
1649   print_field (keyafter);
1650   if (keyafter_truncation)
1651     fputs (truncation_string, stdout);
1652   putchar ('"');
1653
1654   /* Output the `head' field.  */
1655
1656   fputs (" \"", stdout);
1657   if (head_truncation)
1658     fputs (truncation_string, stdout);
1659   print_field (head);
1660   putchar ('"');
1661
1662   /* Conditionally output the `reference' field.  */
1663
1664   if (auto_reference | input_reference)
1665     {
1666       fputs (" \"", stdout);
1667       print_field (reference);
1668       putchar ('"');
1669     }
1670
1671   putchar ('\n');
1672 }
1673
1674 /*---------------------------------------------------------.
1675 | Output the current output fields as one line for `TeX'.  |
1676 `---------------------------------------------------------*/
1677
1678 static void
1679 output_one_tex_line (void)
1680 {
1681   BLOCK key;                    /* key field, isolated */
1682   BLOCK after;                  /* after field, isolated */
1683   char *cursor;                 /* running cursor in source text */
1684
1685   printf ("\\%s ", macro_name);
1686   putchar ('{');
1687   print_field (tail);
1688   fputs ("}{", stdout);
1689   print_field (before);
1690   fputs ("}{", stdout);
1691   key.start = keyafter.start;
1692   after.end = keyafter.end;
1693   cursor = keyafter.start;
1694   SKIP_SOMETHING (cursor, keyafter.end);
1695   key.end = cursor;
1696   after.start = cursor;
1697   print_field (key);
1698   fputs ("}{", stdout);
1699   print_field (after);
1700   fputs ("}{", stdout);
1701   print_field (head);
1702   putchar ('}');
1703   if (auto_reference | input_reference)
1704     {
1705       putchar ('{');
1706       print_field (reference);
1707       putchar ('}');
1708     }
1709   putchar ('\n');
1710 }
1711
1712 /*-------------------------------------------------------------------.
1713 | Output the current output fields as one line for a dumb terminal.  |
1714 `-------------------------------------------------------------------*/
1715
1716 static void
1717 output_one_dumb_line (void)
1718 {
1719   if (!right_reference)
1720     {
1721       if (auto_reference)
1722         {
1723
1724           /* Output the `reference' field, in such a way that GNU emacs
1725              next-error will handle it.  The ending colon is taken from the
1726              gap which follows.  */
1727
1728           print_field (reference);
1729           putchar (':');
1730           print_spaces (reference_max_width
1731                         + gap_size
1732                         - (reference.end - reference.start)
1733                         - 1);
1734         }
1735       else
1736         {
1737
1738           /* Output the `reference' field and its following gap.  */
1739
1740           print_field (reference);
1741           print_spaces (reference_max_width
1742                         + gap_size
1743                         - (reference.end - reference.start));
1744         }
1745     }
1746
1747   if (tail.start < tail.end)
1748     {
1749       /* Output the `tail' field.  */
1750
1751       print_field (tail);
1752       if (tail_truncation)
1753         fputs (truncation_string, stdout);
1754
1755       print_spaces (half_line_width - gap_size
1756                     - (before.end - before.start)
1757                     - (before_truncation ? truncation_string_length : 0)
1758                     - (tail.end - tail.start)
1759                     - (tail_truncation ? truncation_string_length : 0));
1760     }
1761   else
1762     print_spaces (half_line_width - gap_size
1763                   - (before.end - before.start)
1764                   - (before_truncation ? truncation_string_length : 0));
1765
1766   /* Output the `before' field.  */
1767
1768   if (before_truncation)
1769     fputs (truncation_string, stdout);
1770   print_field (before);
1771
1772   print_spaces (gap_size);
1773
1774   /* Output the `keyafter' field.  */
1775
1776   print_field (keyafter);
1777   if (keyafter_truncation)
1778     fputs (truncation_string, stdout);
1779
1780   if (head.start < head.end)
1781     {
1782       /* Output the `head' field.  */
1783
1784       print_spaces (half_line_width
1785                     - (keyafter.end - keyafter.start)
1786                     - (keyafter_truncation ? truncation_string_length : 0)
1787                     - (head.end - head.start)
1788                     - (head_truncation ? truncation_string_length : 0));
1789       if (head_truncation)
1790         fputs (truncation_string, stdout);
1791       print_field (head);
1792     }
1793   else
1794
1795     if ((auto_reference | input_reference) & right_reference)
1796       print_spaces (half_line_width
1797                     - (keyafter.end - keyafter.start)
1798                     - (keyafter_truncation ? truncation_string_length : 0));
1799
1800   if ((auto_reference | input_reference) & right_reference)
1801     {
1802       /* Output the `reference' field.  */
1803
1804       print_spaces (gap_size);
1805       print_field (reference);
1806     }
1807
1808   putchar ('\n');
1809 }
1810
1811 /*------------------------------------------------------------------------.
1812 | Scan the whole occurs table and, for each entry, output one line in the |
1813 | appropriate format.                                                     |
1814 `------------------------------------------------------------------------*/
1815
1816 static void
1817 generate_all_output (void)
1818 {
1819   size_t occurs_index;          /* index of keyword entry being processed */
1820   OCCURS *occurs_cursor;        /* current keyword entry being processed */
1821
1822   /* The following assignments are useful to provide default values in case
1823      line contexts or references are not used, in which case these variables
1824      would never be computed.  */
1825
1826   tail.start = NULL;
1827   tail.end = NULL;
1828   tail_truncation = 0;
1829
1830   head.start = NULL;
1831   head.end = NULL;
1832   head_truncation = 0;
1833
1834   /* Loop over all keyword occurrences.  */
1835
1836   occurs_cursor = occurs_table[0];
1837
1838   for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1839     {
1840       /* Compute the exact size of every field and whenever truncation flags
1841          are present or not.  */
1842
1843       define_all_fields (occurs_cursor);
1844
1845       /* Produce one output line according to selected format.  */
1846
1847       switch (output_format)
1848         {
1849         case UNKNOWN_FORMAT:
1850           /* Should never happen.  */
1851
1852         case DUMB_FORMAT:
1853           output_one_dumb_line ();
1854           break;
1855
1856         case ROFF_FORMAT:
1857           output_one_roff_line ();
1858           break;
1859
1860         case TEX_FORMAT:
1861           output_one_tex_line ();
1862           break;
1863         }
1864
1865       /* Advance the cursor into the occurs table.  */
1866
1867       occurs_cursor++;
1868     }
1869 }
1870 \f
1871 /* Option decoding and main program.  */
1872
1873 /*------------------------------------------------------.
1874 | Print program identification and options, then exit.  |
1875 `------------------------------------------------------*/
1876
1877 void
1878 usage (int status)
1879 {
1880   if (status != EXIT_SUCCESS)
1881     fprintf (stderr, _("Try `%s --help' for more information.\n"),
1882              program_name);
1883   else
1884     {
1885       printf (_("\
1886 Usage: %s [OPTION]... [INPUT]...   (without -G)\n\
1887   or:  %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1888               program_name, program_name);
1889       fputs (_("\
1890 Output a permuted index, including context, of the words in the input files.\n\
1891 \n\
1892 "), stdout);
1893       fputs (_("\
1894 Mandatory arguments to long options are mandatory for short options too.\n\
1895 "), stdout);
1896       fputs (_("\
1897   -A, --auto-reference           output automatically generated references\n\
1898   -G, --traditional              behave more like System V `ptx'\n\
1899   -F, --flag-truncation=STRING   use STRING for flagging line truncations\n\
1900 "), stdout);
1901       fputs (_("\
1902   -M, --macro-name=STRING        macro name to use instead of `xx'\n\
1903   -O, --format=roff              generate output as roff directives\n\
1904   -R, --right-side-refs          put references at right, not counted in -w\n\
1905   -S, --sentence-regexp=REGEXP   for end of lines or end of sentences\n\
1906   -T, --format=tex               generate output as TeX directives\n\
1907 "), stdout);
1908       fputs (_("\
1909   -W, --word-regexp=REGEXP       use REGEXP to match each keyword\n\
1910   -b, --break-file=FILE          word break characters in this FILE\n\
1911   -f, --ignore-case              fold lower case to upper case for sorting\n\
1912   -g, --gap-size=NUMBER          gap size in columns between output fields\n\
1913   -i, --ignore-file=FILE         read ignore word list from FILE\n\
1914   -o, --only-file=FILE           read only word list from this FILE\n\
1915 "), stdout);
1916       fputs (_("\
1917   -r, --references               first field of each line is a reference\n\
1918   -t, --typeset-mode               - not implemented -\n\
1919   -w, --width=NUMBER             output width in columns, reference excluded\n\
1920 "), stdout);
1921       fputs (HELP_OPTION_DESCRIPTION, stdout);
1922       fputs (VERSION_OPTION_DESCRIPTION, stdout);
1923       fputs (_("\
1924 \n\
1925 With no FILE or if FILE is -, read Standard Input.  `-F /' by default.\n\
1926 "), stdout);
1927       emit_bug_reporting_address ();
1928     }
1929   exit (status);
1930 }
1931
1932 /*----------------------------------------------------------------------.
1933 | Main program.  Decode ARGC arguments passed through the ARGV array of |
1934 | strings, then launch execution.                                       |
1935 `----------------------------------------------------------------------*/
1936
1937 /* Long options equivalences.  */
1938 static struct option const long_options[] =
1939 {
1940   {"auto-reference", no_argument, NULL, 'A'},
1941   {"break-file", required_argument, NULL, 'b'},
1942   {"flag-truncation", required_argument, NULL, 'F'},
1943   {"ignore-case", no_argument, NULL, 'f'},
1944   {"gap-size", required_argument, NULL, 'g'},
1945   {"ignore-file", required_argument, NULL, 'i'},
1946   {"macro-name", required_argument, NULL, 'M'},
1947   {"only-file", required_argument, NULL, 'o'},
1948   {"references", no_argument, NULL, 'r'},
1949   {"right-side-refs", no_argument, NULL, 'R'},
1950   {"format", required_argument, NULL, 10},
1951   {"sentence-regexp", required_argument, NULL, 'S'},
1952   {"traditional", no_argument, NULL, 'G'},
1953   {"typeset-mode", no_argument, NULL, 't'},
1954   {"width", required_argument, NULL, 'w'},
1955   {"word-regexp", required_argument, NULL, 'W'},
1956   {GETOPT_HELP_OPTION_DECL},
1957   {GETOPT_VERSION_OPTION_DECL},
1958   {NULL, 0, NULL, 0},
1959 };
1960
1961 static char const* const format_args[] =
1962 {
1963   "roff", "tex", NULL
1964 };
1965
1966 static enum Format const format_vals[] =
1967 {
1968   ROFF_FORMAT, TEX_FORMAT
1969 };
1970
1971 int
1972 main (int argc, char **argv)
1973 {
1974   int optchar;                  /* argument character */
1975   int file_index;               /* index in text input file arrays */
1976
1977   /* Decode program options.  */
1978
1979   initialize_main (&argc, &argv);
1980   set_program_name (argv[0]);
1981   setlocale (LC_ALL, "");
1982   bindtextdomain (PACKAGE, LOCALEDIR);
1983   textdomain (PACKAGE);
1984
1985   atexit (close_stdout);
1986
1987 #if HAVE_SETCHRCLASS
1988   setchrclass (NULL);
1989 #endif
1990
1991   while (optchar = getopt_long (argc, argv, "AF:GM:ORS:TW:b:i:fg:o:trw:",
1992                                 long_options, NULL),
1993          optchar != EOF)
1994     {
1995       switch (optchar)
1996         {
1997         default:
1998           usage (EXIT_FAILURE);
1999
2000         case 'G':
2001           gnu_extensions = false;
2002           break;
2003
2004         case 'b':
2005           break_file = optarg;
2006           break;
2007
2008         case 'f':
2009           ignore_case = true;
2010           break;
2011
2012         case 'g':
2013           {
2014             unsigned long int tmp_ulong;
2015             if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2016                 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2017               error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
2018                      quotearg (optarg));
2019             gap_size = tmp_ulong;
2020             break;
2021           }
2022
2023         case 'i':
2024           ignore_file = optarg;
2025           break;
2026
2027         case 'o':
2028           only_file = optarg;
2029           break;
2030
2031         case 'r':
2032           input_reference = true;
2033           break;
2034
2035         case 't':
2036           /* Yet to understand...  */
2037           break;
2038
2039         case 'w':
2040           {
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 line width: %s"),
2045                      quotearg (optarg));
2046             line_width = tmp_ulong;
2047             break;
2048           }
2049
2050         case 'A':
2051           auto_reference = true;
2052           break;
2053
2054         case 'F':
2055           truncation_string = copy_unescaped_string (optarg);
2056           break;
2057
2058         case 'M':
2059           macro_name = optarg;
2060           break;
2061
2062         case 'O':
2063           output_format = ROFF_FORMAT;
2064           break;
2065
2066         case 'R':
2067           right_reference = true;
2068           break;
2069
2070         case 'S':
2071           context_regex.string = copy_unescaped_string (optarg);
2072           break;
2073
2074         case 'T':
2075           output_format = TEX_FORMAT;
2076           break;
2077
2078         case 'W':
2079           word_regex.string = copy_unescaped_string (optarg);
2080           if (!*word_regex.string)
2081             word_regex.string = NULL;
2082           break;
2083
2084         case 10:
2085           output_format = XARGMATCH ("--format", optarg,
2086                                      format_args, format_vals);
2087         case_GETOPT_HELP_CHAR;
2088
2089         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2090         }
2091     }
2092
2093   /* Process remaining arguments.  If GNU extensions are enabled, process
2094      all arguments as input parameters.  If disabled, accept at most two
2095      arguments, the second of which is an output parameter.  */
2096
2097   if (optind == argc)
2098     {
2099
2100       /* No more argument simply means: read standard input.  */
2101
2102       input_file_name = xmalloc (sizeof *input_file_name);
2103       file_line_count = xmalloc (sizeof *file_line_count);
2104       number_input_files = 1;
2105       input_file_name[0] = NULL;
2106     }
2107   else if (gnu_extensions)
2108     {
2109       number_input_files = argc - optind;
2110       input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
2111       file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
2112
2113       for (file_index = 0; file_index < number_input_files; file_index++)
2114         {
2115           input_file_name[file_index] = argv[optind];
2116           if (!*argv[optind] || STREQ (argv[optind], "-"))
2117             input_file_name[0] = NULL;
2118           else
2119             input_file_name[0] = argv[optind];
2120           optind++;
2121         }
2122     }
2123   else
2124     {
2125
2126       /* There is one necessary input file.  */
2127
2128       number_input_files = 1;
2129       input_file_name = xmalloc (sizeof *input_file_name);
2130       file_line_count = xmalloc (sizeof *file_line_count);
2131       if (!*argv[optind] || STREQ (argv[optind], "-"))
2132         input_file_name[0] = NULL;
2133       else
2134         input_file_name[0] = argv[optind];
2135       optind++;
2136
2137       /* Redirect standard output, only if requested.  */
2138
2139       if (optind < argc)
2140         {
2141           if (! freopen (argv[optind], "w", stdout))
2142             error (EXIT_FAILURE, errno, "%s", argv[optind]);
2143           optind++;
2144         }
2145
2146       /* Diagnose any other argument as an error.  */
2147
2148       if (optind < argc)
2149         {
2150           error (0, 0, _("extra operand %s"), quote (argv[optind]));
2151           usage (EXIT_FAILURE);
2152         }
2153     }
2154
2155   /* If the output format has not been explicitly selected, choose dumb
2156      terminal format if GNU extensions are enabled, else `roff' format.  */
2157
2158   if (output_format == UNKNOWN_FORMAT)
2159     output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2160
2161   /* Initialize the main tables.  */
2162
2163   initialize_regex ();
2164
2165   /* Read `Break character' file, if any.  */
2166
2167   if (break_file)
2168     digest_break_file (break_file);
2169
2170   /* Read `Ignore words' file and `Only words' files, if any.  If any of
2171      these files is empty, reset the name of the file to NULL, to avoid
2172      unnecessary calls to search_table. */
2173
2174   if (ignore_file)
2175     {
2176       digest_word_file (ignore_file, &ignore_table);
2177       if (ignore_table.length == 0)
2178         ignore_file = NULL;
2179     }
2180
2181   if (only_file)
2182     {
2183       digest_word_file (only_file, &only_table);
2184       if (only_table.length == 0)
2185         only_file = NULL;
2186     }
2187
2188   /* Prepare to study all the input files.  */
2189
2190   number_of_occurs[0] = 0;
2191   total_line_count = 0;
2192   maximum_word_length = 0;
2193   reference_max_width = 0;
2194
2195   for (file_index = 0; file_index < number_input_files; file_index++)
2196     {
2197
2198       /* Read the file in core, than study it.  */
2199
2200       swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2201       find_occurs_in_text ();
2202
2203       /* Maintain for each file how many lines has been read so far when its
2204          end is reached.  Incrementing the count first is a simple kludge to
2205          handle a possible incomplete line at end of file.  */
2206
2207       total_line_count++;
2208       file_line_count[file_index] = total_line_count;
2209     }
2210
2211   /* Do the output process phase.  */
2212
2213   sort_found_occurs ();
2214   fix_output_parameters ();
2215   generate_all_output ();
2216
2217   /* All done.  */
2218
2219   exit (EXIT_SUCCESS);
2220 }