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