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