* src/dircolors.hin: Add .flv. Move .svgz to "image formats".
[coreutils.git] / src / ptx.c
blob8f7ae95a496ed9daa345f436568c4b00d0614349
1 /* Permuted index for GNU, with keywords in their context.
2 Copyright (C) 1990, 1991, 1993, 1998-2008 Free Software Foundation, Inc.
3 François Pinard <pinard@iro.umontreal.ca>, 1988.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 François Pinard <pinard@iro.umontreal.ca> */
20 #include <config.h>
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/types.h>
25 #include "system.h"
26 #include "argmatch.h"
27 #include "diacrit.h"
28 #include "error.h"
29 #include "quote.h"
30 #include "quotearg.h"
31 #include "regex.h"
32 #include "xstrtol.h"
34 /* The official name of this program (e.g., no `g' prefix). */
35 #define PROGRAM_NAME "ptx"
37 /* Note to translator: Please translate "F. Pinard" to "François
38 Pinard" if "ç" (c-with-cedilla) is available in the
39 translation's character set and encoding. */
40 #define AUTHORS _("F. Pinard")
42 /* Number of possible characters in a byte. */
43 #define CHAR_SET_SIZE 256
45 #define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
46 #define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
47 : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
48 #define OCTTOBIN(C) ((C) - '0')
50 /* Debugging the memory allocator. */
52 #if WITH_DMALLOC
53 # define MALLOC_FUNC_CHECK 1
54 # include <dmalloc.h>
55 #endif
57 /* Global definitions. */
59 /* FIXME: There are many unchecked integer overflows in this file,
60 that will cause this command to misbehave given large inputs or
61 options. Many of the "int" values below should be "size_t" or
62 something else like that. */
64 /* Reallocation step when swallowing non regular files. The value is not
65 the actual reallocation step, but its base two logarithm. */
66 #define SWALLOW_REALLOC_LOG 12
68 /* Imported from "regex.c". */
69 #define Sword 1
71 /* The name this program was run with. */
72 char *program_name;
74 /* Program options. */
76 enum Format
78 UNKNOWN_FORMAT, /* output format still unknown */
79 DUMB_FORMAT, /* output for a dumb terminal */
80 ROFF_FORMAT, /* output for `troff' or `nroff' */
81 TEX_FORMAT /* output for `TeX' or `LaTeX' */
84 static bool gnu_extensions = true; /* trigger all GNU extensions */
85 static bool auto_reference = false; /* refs are `file_name:line_number:' */
86 static bool input_reference = false; /* refs at beginning of input lines */
87 static bool right_reference = false; /* output refs after right context */
88 static int line_width = 72; /* output line width in characters */
89 static int gap_size = 3; /* number of spaces between output fields */
90 static const char *truncation_string = "/";
91 /* string used to mark line truncations */
92 static const char *macro_name = "xx"; /* macro name for roff or TeX output */
93 static enum Format output_format = UNKNOWN_FORMAT;
94 /* output format */
96 static bool ignore_case = false; /* fold lower to upper for sorting */
97 static const char *break_file = NULL; /* name of the `Break characters' file */
98 static const char *only_file = NULL; /* name of the `Only words' file */
99 static const char *ignore_file = NULL; /* name of the `Ignore words' file */
101 /* Options that use regular expressions. */
102 struct regex_data
104 /* The original regular expression, as a string. */
105 char const *string;
107 /* The compiled regular expression, and its fastmap. */
108 struct re_pattern_buffer pattern;
109 char fastmap[UCHAR_MAX + 1];
112 static struct regex_data context_regex; /* end of context */
113 static struct regex_data word_regex; /* keyword */
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. */
119 typedef struct
121 char *start; /* pointer to beginning of region */
122 char *end; /* pointer to end + 1 of region */
124 BLOCK;
126 typedef struct
128 char *start; /* pointer to beginning of region */
129 short int size; /* length of the region */
131 WORD;
133 typedef struct
135 WORD *start; /* array of WORDs */
136 size_t alloc; /* allocated length */
137 size_t length; /* number of used entries */
139 WORD_TABLE;
141 /* Pattern description tables. */
143 /* For each character, provide its folded equivalent. */
144 static unsigned char folded_chars[CHAR_SET_SIZE];
146 /* End of context pattern register indices. */
147 static struct re_registers context_regs;
149 /* Keyword pattern register indices. */
150 static struct re_registers word_regs;
152 /* A word characters fastmap is used only when no word regexp has been
153 provided. A word is then made up of a sequence of one or more characters
154 allowed by the fastmap. Contains !0 if character allowed in word. Not
155 only this is faster in most cases, but it simplifies the implementation
156 of the Break files. */
157 static char word_fastmap[CHAR_SET_SIZE];
159 /* Maximum length of any word read. */
160 static int maximum_word_length;
162 /* Maximum width of any reference used. */
163 static int reference_max_width;
165 /* Ignore and Only word tables. */
167 static WORD_TABLE ignore_table; /* table of words to ignore */
168 static WORD_TABLE only_table; /* table of words to select */
170 /* Source text table, and scanning macros. */
172 static int number_input_files; /* number of text input files */
173 static int total_line_count; /* total number of lines seen so far */
174 static const char **input_file_name; /* array of text input file names */
175 static int *file_line_count; /* array of `total_line_count' values at end */
177 static BLOCK text_buffer; /* file to study */
179 /* SKIP_NON_WHITE used only for getting or skipping the reference. */
181 #define SKIP_NON_WHITE(cursor, limit) \
182 while (cursor < limit && ! isspace (to_uchar (*cursor))) \
183 cursor++
185 #define SKIP_WHITE(cursor, limit) \
186 while (cursor < limit && isspace (to_uchar (*cursor))) \
187 cursor++
189 #define SKIP_WHITE_BACKWARDS(cursor, start) \
190 while (cursor > start && isspace (to_uchar (cursor[-1]))) \
191 cursor--
193 #define SKIP_SOMETHING(cursor, limit) \
194 if (word_regex.string) \
196 regoff_t count; \
197 count = re_match (&word_regex.pattern, cursor, limit - cursor, 0, NULL); \
198 if (count == -2) \
199 matcher_error (); \
200 cursor += count == -1 ? 1 : count; \
202 else if (word_fastmap[to_uchar (*cursor)]) \
203 while (cursor < limit && word_fastmap[to_uchar (*cursor)]) \
204 cursor++; \
205 else \
206 cursor++
208 /* Occurrences table.
210 The `keyword' pointer provides the central word, which is surrounded
211 by a left context and a right context. The `keyword' and `length'
212 field allow full 8-bit characters keys, even including NULs. At other
213 places in this program, the name `keyafter' refers to the keyword
214 followed by its right context.
216 The left context does not extend, towards the beginning of the file,
217 further than a distance given by the `left' value. This value is
218 relative to the keyword beginning, it is usually negative. This
219 insures that, except for white space, we will never have to backward
220 scan the source text, when it is time to generate the final output
221 lines.
223 The right context, indirectly attainable through the keyword end, does
224 not extend, towards the end of the file, further than a distance given
225 by the `right' value. This value is relative to the keyword
226 beginning, it is usually positive.
228 When automatic references are used, the `reference' value is the
229 overall line number in all input files read so far, in this case, it
230 is of type (int). When input references are used, the `reference'
231 value indicates the distance between the keyword beginning and the
232 start of the reference field, it is of type (DELTA) and usually
233 negative. */
235 typedef short int DELTA; /* to hold displacement within one context */
237 typedef struct
239 WORD key; /* description of the keyword */
240 DELTA left; /* distance to left context start */
241 DELTA right; /* distance to right context end */
242 int reference; /* reference descriptor */
244 OCCURS;
246 /* The various OCCURS tables are indexed by the language. But the time
247 being, there is no such multiple language support. */
249 static OCCURS *occurs_table[1]; /* all words retained from the read text */
250 static size_t occurs_alloc[1]; /* allocated size of occurs_table */
251 static size_t number_of_occurs[1]; /* number of used slots in occurs_table */
254 /* Communication among output routines. */
256 /* Indicate if special output processing is requested for each character. */
257 static char edited_flag[CHAR_SET_SIZE];
259 static int half_line_width; /* half of line width, reference excluded */
260 static int before_max_width; /* maximum width of before field */
261 static int keyafter_max_width; /* maximum width of keyword-and-after field */
262 static int truncation_string_length;/* length of string used to flag truncation */
264 /* When context is limited by lines, wraparound may happen on final output:
265 the `head' pointer gives access to some supplementary left context which
266 will be seen at the end of the output line, the `tail' pointer gives
267 access to some supplementary right context which will be seen at the
268 beginning of the output line. */
270 static BLOCK tail; /* tail field */
271 static int tail_truncation; /* flag truncation after the tail field */
273 static BLOCK before; /* before field */
274 static int before_truncation; /* flag truncation before the before field */
276 static BLOCK keyafter; /* keyword-and-after field */
277 static int keyafter_truncation; /* flag truncation after the keyafter field */
279 static BLOCK head; /* head field */
280 static int head_truncation; /* flag truncation before the head field */
282 static BLOCK reference; /* reference field for input reference mode */
284 /* Miscellaneous routines. */
286 /* Diagnose an error in the regular expression matcher. Then exit. */
288 static void ATTRIBUTE_NORETURN
289 matcher_error (void)
291 error (0, errno, _("error in regular expression matcher"));
292 exit (EXIT_FAILURE);
295 /*------------------------------------------------------.
296 | Duplicate string STRING, while evaluating \-escapes. |
297 `------------------------------------------------------*/
299 /* Loosely adapted from GNU sh-utils printf.c code. */
301 static char *
302 copy_unescaped_string (const char *string)
304 char *result; /* allocated result */
305 char *cursor; /* cursor in result */
306 int value; /* value of \nnn escape */
307 int length; /* length of \nnn escape */
309 result = xmalloc (strlen (string) + 1);
310 cursor = result;
312 while (*string)
314 if (*string == '\\')
316 string++;
317 switch (*string)
319 case 'x': /* \xhhh escape, 3 chars maximum */
320 value = 0;
321 for (length = 0, string++;
322 length < 3 && isxdigit (to_uchar (*string));
323 length++, string++)
324 value = value * 16 + HEXTOBIN (*string);
325 if (length == 0)
327 *cursor++ = '\\';
328 *cursor++ = 'x';
330 else
331 *cursor++ = value;
332 break;
334 case '0': /* \0ooo escape, 3 chars maximum */
335 value = 0;
336 for (length = 0, string++;
337 length < 3 && ISODIGIT (*string);
338 length++, string++)
339 value = value * 8 + OCTTOBIN (*string);
340 *cursor++ = value;
341 break;
343 case 'a': /* alert */
344 #if __STDC__
345 *cursor++ = '\a';
346 #else
347 *cursor++ = 7;
348 #endif
349 string++;
350 break;
352 case 'b': /* backspace */
353 *cursor++ = '\b';
354 string++;
355 break;
357 case 'c': /* cancel the rest of the output */
358 while (*string)
359 string++;
360 break;
362 case 'f': /* form feed */
363 *cursor++ = '\f';
364 string++;
365 break;
367 case 'n': /* new line */
368 *cursor++ = '\n';
369 string++;
370 break;
372 case 'r': /* carriage return */
373 *cursor++ = '\r';
374 string++;
375 break;
377 case 't': /* horizontal tab */
378 *cursor++ = '\t';
379 string++;
380 break;
382 case 'v': /* vertical tab */
383 #if __STDC__
384 *cursor++ = '\v';
385 #else
386 *cursor++ = 11;
387 #endif
388 string++;
389 break;
391 case '\0': /* lone backslash at end of string */
392 /* ignore it */
393 break;
395 default:
396 *cursor++ = '\\';
397 *cursor++ = *string++;
398 break;
401 else
402 *cursor++ = *string++;
405 *cursor = '\0';
406 return result;
409 /*--------------------------------------------------------------------------.
410 | Compile the regex represented by REGEX, diagnose and abort if any error. |
411 `--------------------------------------------------------------------------*/
413 static void
414 compile_regex (struct regex_data *regex)
416 struct re_pattern_buffer *pattern = &regex->pattern;
417 char const *string = regex->string;
418 char const *message;
420 pattern->buffer = NULL;
421 pattern->allocated = 0;
422 pattern->fastmap = regex->fastmap;
423 pattern->translate = ignore_case ? folded_chars : NULL;
425 message = re_compile_pattern (string, strlen (string), pattern);
426 if (message)
427 error (EXIT_FAILURE, 0, _("%s (for regexp %s)"), message, quote (string));
429 /* The fastmap should be compiled before `re_match'. The following
430 call is not mandatory, because `re_search' is always called sooner,
431 and it compiles the fastmap if this has not been done yet. */
433 re_compile_fastmap (pattern);
436 /*------------------------------------------------------------------------.
437 | This will initialize various tables for pattern match and compiles some |
438 | regexps. |
439 `------------------------------------------------------------------------*/
441 static void
442 initialize_regex (void)
444 int character; /* character value */
446 /* Initialize the case folding table. */
448 if (ignore_case)
449 for (character = 0; character < CHAR_SET_SIZE; character++)
450 folded_chars[character] = toupper (character);
452 /* Unless the user already provided a description of the end of line or
453 end of sentence sequence, select an end of line sequence to compile.
454 If the user provided an empty definition, thus disabling end of line
455 or sentence feature, make it NULL to speed up tests. If GNU
456 extensions are enabled, use end of sentence like in GNU emacs. If
457 disabled, use end of lines. */
459 if (context_regex.string)
461 if (!*context_regex.string)
462 context_regex.string = NULL;
464 else if (gnu_extensions & !input_reference)
465 context_regex.string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
466 else
467 context_regex.string = "\n";
469 if (context_regex.string)
470 compile_regex (&context_regex);
472 /* If the user has already provided a non-empty regexp to describe
473 words, compile it. Else, unless this has already been done through
474 a user provided Break character file, construct a fastmap of
475 characters that may appear in a word. If GNU extensions enabled,
476 include only letters of the underlying character set. If disabled,
477 include almost everything, even punctuations; stop only on white
478 space. */
480 if (word_regex.string)
481 compile_regex (&word_regex);
482 else if (!break_file)
484 if (gnu_extensions)
487 /* Simulate \w+. */
489 for (character = 0; character < CHAR_SET_SIZE; character++)
490 word_fastmap[character] = !! isalpha (character);
492 else
495 /* Simulate [^ \t\n]+. */
497 memset (word_fastmap, 1, CHAR_SET_SIZE);
498 word_fastmap[' '] = 0;
499 word_fastmap['\t'] = 0;
500 word_fastmap['\n'] = 0;
505 /*------------------------------------------------------------------------.
506 | This routine will attempt to swallow a whole file name FILE_NAME into a |
507 | contiguous region of memory and return a description of it into BLOCK. |
508 | Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
510 | Previously, in some cases, white space compression was attempted while |
511 | inputting text. This was defeating some regexps like default end of |
512 | sentence, which checks for two consecutive spaces. If white space |
513 | compression is ever reinstated, it should be in output routines. |
514 `------------------------------------------------------------------------*/
516 static void
517 swallow_file_in_memory (const char *file_name, BLOCK *block)
519 int file_handle; /* file descriptor number */
520 struct stat stat_block; /* stat block for file */
521 size_t allocated_length; /* allocated length of memory buffer */
522 size_t used_length; /* used length in memory buffer */
523 int read_length; /* number of character gotten on last read */
525 /* As special cases, a file name which is NULL or "-" indicates standard
526 input, which is already opened. In all other cases, open the file from
527 its name. */
528 bool using_stdin = !file_name || !*file_name || STREQ (file_name, "-");
529 if (using_stdin)
530 file_handle = STDIN_FILENO;
531 else
532 if ((file_handle = open (file_name, O_RDONLY)) < 0)
533 error (EXIT_FAILURE, errno, "%s", file_name);
535 /* If the file is a plain, regular file, allocate the memory buffer all at
536 once and swallow the file in one blow. In other cases, read the file
537 repeatedly in smaller chunks until we have it all, reallocating memory
538 once in a while, as we go. */
540 if (fstat (file_handle, &stat_block) < 0)
541 error (EXIT_FAILURE, errno, "%s", file_name);
543 if (S_ISREG (stat_block.st_mode))
545 size_t in_memory_size;
547 block->start = xmalloc ((size_t) stat_block.st_size);
549 if ((in_memory_size = read (file_handle,
550 block->start, (size_t) stat_block.st_size))
551 != stat_block.st_size)
553 #if MSDOS
554 /* On MSDOS, in memory size may be smaller than the file
555 size, because of end of line conversions. But it can
556 never be smaller than half the file size, because the
557 minimum is when all lines are empty and terminated by
558 CR+LF. */
559 if (in_memory_size != (size_t)-1
560 && in_memory_size >= stat_block.st_size / 2)
561 block->start = xrealloc (block->start, in_memory_size);
562 else
563 #endif /* not MSDOS */
565 error (EXIT_FAILURE, errno, "%s", file_name);
567 block->end = block->start + in_memory_size;
569 else
571 block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
572 used_length = 0;
573 allocated_length = (1 << SWALLOW_REALLOC_LOG);
575 while (read_length = read (file_handle,
576 block->start + used_length,
577 allocated_length - used_length),
578 read_length > 0)
580 used_length += read_length;
581 if (used_length == allocated_length)
583 allocated_length += (1 << SWALLOW_REALLOC_LOG);
584 block->start
585 = xrealloc (block->start, allocated_length);
589 if (read_length < 0)
590 error (EXIT_FAILURE, errno, "%s", file_name);
592 block->end = block->start + used_length;
595 /* Close the file, but only if it was not the standard input. */
597 if (! using_stdin && close (file_handle) != 0)
598 error (EXIT_FAILURE, errno, "%s", file_name);
601 /* Sort and search routines. */
603 /*--------------------------------------------------------------------------.
604 | Compare two words, FIRST and SECOND, and return 0 if they are identical. |
605 | Return less than 0 if the first word goes before the second; return |
606 | greater than 0 if the first word goes after the second. |
608 | If a word is indeed a prefix of the other, the shorter should go first. |
609 `--------------------------------------------------------------------------*/
611 static int
612 compare_words (const void *void_first, const void *void_second)
614 #define first ((const WORD *) void_first)
615 #define second ((const WORD *) void_second)
616 int length; /* minimum of two lengths */
617 int counter; /* cursor in words */
618 int value; /* value of comparison */
620 length = first->size < second->size ? first->size : second->size;
622 if (ignore_case)
624 for (counter = 0; counter < length; counter++)
626 value = (folded_chars [to_uchar (first->start[counter])]
627 - folded_chars [to_uchar (second->start[counter])]);
628 if (value != 0)
629 return value;
632 else
634 for (counter = 0; counter < length; counter++)
636 value = (to_uchar (first->start[counter])
637 - to_uchar (second->start[counter]));
638 if (value != 0)
639 return value;
643 return first->size - second->size;
644 #undef first
645 #undef second
648 /*-----------------------------------------------------------------------.
649 | Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
650 | go first. In case of a tie, preserve the original order through a |
651 | pointer comparison. |
652 `-----------------------------------------------------------------------*/
654 static int
655 compare_occurs (const void *void_first, const void *void_second)
657 #define first ((const OCCURS *) void_first)
658 #define second ((const OCCURS *) void_second)
659 int value;
661 value = compare_words (&first->key, &second->key);
662 return value == 0 ? first->key.start - second->key.start : value;
663 #undef first
664 #undef second
667 /*------------------------------------------------------------.
668 | Return !0 if WORD appears in TABLE. Uses a binary search. |
669 `------------------------------------------------------------*/
671 static int
672 search_table (WORD *word, WORD_TABLE *table)
674 int lowest; /* current lowest possible index */
675 int highest; /* current highest possible index */
676 int middle; /* current middle index */
677 int value; /* value from last comparison */
679 lowest = 0;
680 highest = table->length - 1;
681 while (lowest <= highest)
683 middle = (lowest + highest) / 2;
684 value = compare_words (word, table->start + middle);
685 if (value < 0)
686 highest = middle - 1;
687 else if (value > 0)
688 lowest = middle + 1;
689 else
690 return 1;
692 return 0;
695 /*---------------------------------------------------------------------.
696 | Sort the whole occurs table in memory. Presumably, `qsort' does not |
697 | take intermediate copies or table elements, so the sort will be |
698 | stabilized throughout the comparison routine. |
699 `---------------------------------------------------------------------*/
701 static void
702 sort_found_occurs (void)
705 /* Only one language for the time being. */
707 qsort (occurs_table[0], number_of_occurs[0], sizeof **occurs_table,
708 compare_occurs);
711 /* Parameter files reading routines. */
713 /*----------------------------------------------------------------------.
714 | Read a file named FILE_NAME, containing a set of break characters. |
715 | Build a content to the array word_fastmap in which all characters are |
716 | allowed except those found in the file. Characters may be repeated. |
717 `----------------------------------------------------------------------*/
719 static void
720 digest_break_file (const char *file_name)
722 BLOCK file_contents; /* to receive a copy of the file */
723 char *cursor; /* cursor in file copy */
725 swallow_file_in_memory (file_name, &file_contents);
727 /* Make the fastmap and record the file contents in it. */
729 memset (word_fastmap, 1, CHAR_SET_SIZE);
730 for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
731 word_fastmap[to_uchar (*cursor)] = 0;
733 if (!gnu_extensions)
736 /* If GNU extensions are enabled, the only way to avoid newline as
737 a break character is to write all the break characters in the
738 file with no newline at all, not even at the end of the file.
739 If disabled, spaces, tabs and newlines are always considered as
740 break characters even if not included in the break file. */
742 word_fastmap[' '] = 0;
743 word_fastmap['\t'] = 0;
744 word_fastmap['\n'] = 0;
747 /* Return the space of the file, which is no more required. */
749 free (file_contents.start);
752 /*-----------------------------------------------------------------------.
753 | Read a file named FILE_NAME, containing one word per line, then |
754 | construct in TABLE a table of WORD descriptors for them. The routine |
755 | swallows the whole file in memory; this is at the expense of space |
756 | needed for newlines, which are useless; however, the reading is fast. |
757 `-----------------------------------------------------------------------*/
759 static void
760 digest_word_file (const char *file_name, WORD_TABLE *table)
762 BLOCK file_contents; /* to receive a copy of the file */
763 char *cursor; /* cursor in file copy */
764 char *word_start; /* start of the current word */
766 swallow_file_in_memory (file_name, &file_contents);
768 table->start = NULL;
769 table->alloc = 0;
770 table->length = 0;
772 /* Read the whole file. */
774 cursor = file_contents.start;
775 while (cursor < file_contents.end)
778 /* Read one line, and save the word in contains. */
780 word_start = cursor;
781 while (cursor < file_contents.end && *cursor != '\n')
782 cursor++;
784 /* Record the word in table if it is not empty. */
786 if (cursor > word_start)
788 if (table->length == table->alloc)
790 if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
791 xalloc_die ();
792 table->alloc = table->alloc * 2 + 1;
793 table->start = xrealloc (table->start,
794 table->alloc * sizeof *table->start);
797 table->start[table->length].start = word_start;
798 table->start[table->length].size = cursor - word_start;
799 table->length++;
802 /* This test allows for an incomplete line at end of file. */
804 if (cursor < file_contents.end)
805 cursor++;
808 /* Finally, sort all the words read. */
810 qsort (table->start, table->length, sizeof table->start[0], compare_words);
813 /* Keyword recognition and selection. */
815 /*----------------------------------------------------------------------.
816 | For each keyword in the source text, constructs an OCCURS structure. |
817 `----------------------------------------------------------------------*/
819 static void
820 find_occurs_in_text (void)
822 char *cursor; /* for scanning the source text */
823 char *scan; /* for scanning the source text also */
824 char *line_start; /* start of the current input line */
825 char *line_scan; /* newlines scanned until this point */
826 int reference_length; /* length of reference in input mode */
827 WORD possible_key; /* possible key, to ease searches */
828 OCCURS *occurs_cursor; /* current OCCURS under construction */
830 char *context_start; /* start of left context */
831 char *context_end; /* end of right context */
832 char *word_start; /* start of word */
833 char *word_end; /* end of word */
834 char *next_context_start; /* next start of left context */
836 /* reference_length is always used within `if (input_reference)'.
837 However, GNU C diagnoses that it may be used uninitialized. The
838 following assignment is merely to shut it up. */
840 reference_length = 0;
842 /* Tracking where lines start is helpful for reference processing. In
843 auto reference mode, this allows counting lines. In input reference
844 mode, this permits finding the beginning of the references.
846 The first line begins with the file, skip immediately this very first
847 reference in input reference mode, to help further rejection any word
848 found inside it. Also, unconditionally assigning these variable has
849 the happy effect of shutting up lint. */
851 line_start = text_buffer.start;
852 line_scan = line_start;
853 if (input_reference)
855 SKIP_NON_WHITE (line_scan, text_buffer.end);
856 reference_length = line_scan - line_start;
857 SKIP_WHITE (line_scan, text_buffer.end);
860 /* Process the whole buffer, one line or one sentence at a time. */
862 for (cursor = text_buffer.start;
863 cursor < text_buffer.end;
864 cursor = next_context_start)
867 /* `context_start' gets initialized before the processing of each
868 line, or once for the whole buffer if no end of line or sentence
869 sequence separator. */
871 context_start = cursor;
873 /* If a end of line or end of sentence sequence is defined and
874 non-empty, `next_context_start' will be recomputed to be the end of
875 each line or sentence, before each one is processed. If no such
876 sequence, then `next_context_start' is set at the end of the whole
877 buffer, which is then considered to be a single line or sentence.
878 This test also accounts for the case of an incomplete line or
879 sentence at the end of the buffer. */
881 next_context_start = text_buffer.end;
882 if (context_regex.string)
883 switch (re_search (&context_regex.pattern, cursor,
884 text_buffer.end - cursor,
885 0, text_buffer.end - cursor, &context_regs))
887 case -2:
888 matcher_error ();
890 case -1:
891 break;
893 default:
894 next_context_start = cursor + context_regs.end[0];
895 break;
898 /* Include the separator into the right context, but not any suffix
899 white space in this separator; this insures it will be seen in
900 output and will not take more space than necessary. */
902 context_end = next_context_start;
903 SKIP_WHITE_BACKWARDS (context_end, context_start);
905 /* Read and process a single input line or sentence, one word at a
906 time. */
908 while (1)
910 if (word_regex.string)
912 /* If a word regexp has been compiled, use it to skip at the
913 beginning of the next word. If there is no such word, exit
914 the loop. */
917 regoff_t r = re_search (&word_regex.pattern, cursor,
918 context_end - cursor,
919 0, context_end - cursor, &word_regs);
920 if (r == -2)
921 matcher_error ();
922 if (r == -1)
923 break;
924 word_start = cursor + word_regs.start[0];
925 word_end = cursor + word_regs.end[0];
927 else
929 /* Avoid re_search and use the fastmap to skip to the
930 beginning of the next word. If there is no more word in
931 the buffer, exit the loop. */
934 scan = cursor;
935 while (scan < context_end
936 && !word_fastmap[to_uchar (*scan)])
937 scan++;
939 if (scan == context_end)
940 break;
942 word_start = scan;
944 while (scan < context_end
945 && word_fastmap[to_uchar (*scan)])
946 scan++;
948 word_end = scan;
951 /* Skip right to the beginning of the found word. */
953 cursor = word_start;
955 /* Skip any zero length word. Just advance a single position,
956 then go fetch the next word. */
958 if (word_end == word_start)
960 cursor++;
961 continue;
964 /* This is a genuine, non empty word, so save it as a possible
965 key. Then skip over it. Also, maintain the maximum length of
966 all words read so far. It is mandatory to take the maximum
967 length of all words in the file, without considering if they
968 are actually kept or rejected, because backward jumps at output
969 generation time may fall in *any* word. */
971 possible_key.start = cursor;
972 possible_key.size = word_end - word_start;
973 cursor += possible_key.size;
975 if (possible_key.size > maximum_word_length)
976 maximum_word_length = possible_key.size;
978 /* In input reference mode, update `line_start' from its previous
979 value. Count the lines just in case auto reference mode is
980 also selected. If it happens that the word just matched is
981 indeed part of a reference; just ignore it. */
983 if (input_reference)
985 while (line_scan < possible_key.start)
986 if (*line_scan == '\n')
988 total_line_count++;
989 line_scan++;
990 line_start = line_scan;
991 SKIP_NON_WHITE (line_scan, text_buffer.end);
992 reference_length = line_scan - line_start;
994 else
995 line_scan++;
996 if (line_scan > possible_key.start)
997 continue;
1000 /* Ignore the word if an `Ignore words' table exists and if it is
1001 part of it. Also ignore the word if an `Only words' table and
1002 if it is *not* part of it.
1004 It is allowed that both tables be used at once, even if this
1005 may look strange for now. Just ignore a word that would appear
1006 in both. If regexps are eventually implemented for these
1007 tables, the Ignore table could then reject words that would
1008 have been previously accepted by the Only table. */
1010 if (ignore_file && search_table (&possible_key, &ignore_table))
1011 continue;
1012 if (only_file && !search_table (&possible_key, &only_table))
1013 continue;
1015 /* A non-empty word has been found. First of all, insure
1016 proper allocation of the next OCCURS, and make a pointer to
1017 where it will be constructed. */
1019 if (number_of_occurs[0] == occurs_alloc[0])
1021 if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
1022 < occurs_alloc[0])
1023 xalloc_die ();
1024 occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
1025 occurs_table[0] = xrealloc (occurs_table[0],
1026 occurs_alloc[0] * sizeof *occurs_table[0]);
1029 occurs_cursor = occurs_table[0] + number_of_occurs[0];
1031 /* Define the refence field, if any. */
1033 if (auto_reference)
1036 /* While auto referencing, update `line_start' from its
1037 previous value, counting lines as we go. If input
1038 referencing at the same time, `line_start' has been
1039 advanced earlier, and the following loop is never really
1040 executed. */
1042 while (line_scan < possible_key.start)
1043 if (*line_scan == '\n')
1045 total_line_count++;
1046 line_scan++;
1047 line_start = line_scan;
1048 SKIP_NON_WHITE (line_scan, text_buffer.end);
1050 else
1051 line_scan++;
1053 occurs_cursor->reference = total_line_count;
1055 else if (input_reference)
1058 /* If only input referencing, `line_start' has been computed
1059 earlier to detect the case the word matched would be part
1060 of the reference. The reference position is simply the
1061 value of `line_start'. */
1063 occurs_cursor->reference
1064 = (DELTA) (line_start - possible_key.start);
1065 if (reference_length > reference_max_width)
1066 reference_max_width = reference_length;
1069 /* Exclude the reference from the context in simple cases. */
1071 if (input_reference && line_start == context_start)
1073 SKIP_NON_WHITE (context_start, context_end);
1074 SKIP_WHITE (context_start, context_end);
1077 /* Completes the OCCURS structure. */
1079 occurs_cursor->key = possible_key;
1080 occurs_cursor->left = context_start - possible_key.start;
1081 occurs_cursor->right = context_end - possible_key.start;
1083 number_of_occurs[0]++;
1088 /* Formatting and actual output - service routines. */
1090 /*-----------------------------------------.
1091 | Prints some NUMBER of spaces on stdout. |
1092 `-----------------------------------------*/
1094 static void
1095 print_spaces (int number)
1097 int counter;
1099 for (counter = number; counter > 0; counter--)
1100 putchar (' ');
1103 /*-------------------------------------.
1104 | Prints the field provided by FIELD. |
1105 `-------------------------------------*/
1107 static void
1108 print_field (BLOCK field)
1110 char *cursor; /* Cursor in field to print */
1111 int base; /* Base character, without diacritic */
1112 int diacritic; /* Diacritic code for the character */
1114 /* Whitespace is not really compressed. Instead, each white space
1115 character (tab, vt, ht etc.) is printed as one single space. */
1117 for (cursor = field.start; cursor < field.end; cursor++)
1119 unsigned char character = *cursor;
1120 if (edited_flag[character])
1123 /* First check if this is a diacriticized character.
1125 This works only for TeX. I do not know how diacriticized
1126 letters work with `roff'. Please someone explain it to me! */
1128 diacritic = todiac (character);
1129 if (diacritic != 0 && output_format == TEX_FORMAT)
1131 base = tobase (character);
1132 switch (diacritic)
1135 case 1: /* Latin diphthongs */
1136 switch (base)
1138 case 'o':
1139 fputs ("\\oe{}", stdout);
1140 break;
1142 case 'O':
1143 fputs ("\\OE{}", stdout);
1144 break;
1146 case 'a':
1147 fputs ("\\ae{}", stdout);
1148 break;
1150 case 'A':
1151 fputs ("\\AE{}", stdout);
1152 break;
1154 default:
1155 putchar (' ');
1157 break;
1159 case 2: /* Acute accent */
1160 printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
1161 break;
1163 case 3: /* Grave accent */
1164 printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
1165 break;
1167 case 4: /* Circumflex accent */
1168 printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
1169 break;
1171 case 5: /* Diaeresis */
1172 printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
1173 break;
1175 case 6: /* Tilde accent */
1176 printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
1177 break;
1179 case 7: /* Cedilla */
1180 printf ("\\c{%c}", base);
1181 break;
1183 case 8: /* Small circle beneath */
1184 switch (base)
1186 case 'a':
1187 fputs ("\\aa{}", stdout);
1188 break;
1190 case 'A':
1191 fputs ("\\AA{}", stdout);
1192 break;
1194 default:
1195 putchar (' ');
1197 break;
1199 case 9: /* Strike through */
1200 switch (base)
1202 case 'o':
1203 fputs ("\\o{}", stdout);
1204 break;
1206 case 'O':
1207 fputs ("\\O{}", stdout);
1208 break;
1210 default:
1211 putchar (' ');
1213 break;
1216 else
1218 /* This is not a diacritic character, so handle cases which are
1219 really specific to `roff' or TeX. All white space processing
1220 is done as the default case of this switch. */
1222 switch (character)
1224 case '"':
1225 /* In roff output format, double any quote. */
1226 putchar ('"');
1227 putchar ('"');
1228 break;
1230 case '$':
1231 case '%':
1232 case '&':
1233 case '#':
1234 case '_':
1235 /* In TeX output format, precede these with a backslash. */
1236 putchar ('\\');
1237 putchar (character);
1238 break;
1240 case '{':
1241 case '}':
1242 /* In TeX output format, precede these with a backslash and
1243 force mathematical mode. */
1244 printf ("$\\%c$", character);
1245 break;
1247 case '\\':
1248 /* In TeX output mode, request production of a backslash. */
1249 fputs ("\\backslash{}", stdout);
1250 break;
1252 default:
1253 /* Any other flagged character produces a single space. */
1254 putchar (' ');
1257 else
1258 putchar (*cursor);
1262 /* Formatting and actual output - planning routines. */
1264 /*--------------------------------------------------------------------.
1265 | From information collected from command line options and input file |
1266 | readings, compute and fix some output parameter values. |
1267 `--------------------------------------------------------------------*/
1269 static void
1270 fix_output_parameters (void)
1272 int file_index; /* index in text input file arrays */
1273 int line_ordinal; /* line ordinal value for reference */
1274 char ordinal_string[12]; /* edited line ordinal for reference */
1275 int reference_width; /* width for the whole reference */
1276 int character; /* character ordinal */
1277 const char *cursor; /* cursor in some constant strings */
1279 /* In auto reference mode, the maximum width of this field is
1280 precomputed and subtracted from the overall line width. Add one for
1281 the column which separate the file name from the line number. */
1283 if (auto_reference)
1285 reference_max_width = 0;
1286 for (file_index = 0; file_index < number_input_files; file_index++)
1288 line_ordinal = file_line_count[file_index] + 1;
1289 if (file_index > 0)
1290 line_ordinal -= file_line_count[file_index - 1];
1291 sprintf (ordinal_string, "%d", line_ordinal);
1292 reference_width = strlen (ordinal_string);
1293 if (input_file_name[file_index])
1294 reference_width += strlen (input_file_name[file_index]);
1295 if (reference_width > reference_max_width)
1296 reference_max_width = reference_width;
1298 reference_max_width++;
1299 reference.start = xmalloc ((size_t) reference_max_width + 1);
1302 /* If the reference appears to the left of the output line, reserve some
1303 space for it right away, including one gap size. */
1305 if ((auto_reference | input_reference) & !right_reference)
1306 line_width -= reference_max_width + gap_size;
1308 /* The output lines, minimally, will contain from left to right a left
1309 context, a gap, and a keyword followed by the right context with no
1310 special intervening gap. Half of the line width is dedicated to the
1311 left context and the gap, the other half is dedicated to the keyword
1312 and the right context; these values are computed once and for all here.
1313 There also are tail and head wrap around fields, used when the keyword
1314 is near the beginning or the end of the line, or when some long word
1315 cannot fit in, but leave place from wrapped around shorter words. The
1316 maximum width of these fields are recomputed separately for each line,
1317 on a case by case basis. It is worth noting that it cannot happen that
1318 both the tail and head fields are used at once. */
1320 half_line_width = line_width / 2;
1321 before_max_width = half_line_width - gap_size;
1322 keyafter_max_width = half_line_width;
1324 /* If truncation_string is the empty string, make it NULL to speed up
1325 tests. In this case, truncation_string_length will never get used, so
1326 there is no need to set it. */
1328 if (truncation_string && *truncation_string)
1329 truncation_string_length = strlen (truncation_string);
1330 else
1331 truncation_string = NULL;
1333 if (gnu_extensions)
1336 /* When flagging truncation at the left of the keyword, the
1337 truncation mark goes at the beginning of the before field,
1338 unless there is a head field, in which case the mark goes at the
1339 left of the head field. When flagging truncation at the right
1340 of the keyword, the mark goes at the end of the keyafter field,
1341 unless there is a tail field, in which case the mark goes at the
1342 end of the tail field. Only eight combination cases could arise
1343 for truncation marks:
1345 . None.
1346 . One beginning the before field.
1347 . One beginning the head field.
1348 . One ending the keyafter field.
1349 . One ending the tail field.
1350 . One beginning the before field, another ending the keyafter field.
1351 . One ending the tail field, another beginning the before field.
1352 . One ending the keyafter field, another beginning the head field.
1354 So, there is at most two truncation marks, which could appear both
1355 on the left side of the center of the output line, both on the
1356 right side, or one on either side. */
1358 before_max_width -= 2 * truncation_string_length;
1359 keyafter_max_width -= 2 * truncation_string_length;
1361 else
1364 /* I never figured out exactly how UNIX' ptx plans the output width
1365 of its various fields. If GNU extensions are disabled, do not
1366 try computing the field widths correctly; instead, use the
1367 following formula, which does not completely imitate UNIX' ptx,
1368 but almost. */
1370 keyafter_max_width -= 2 * truncation_string_length + 1;
1373 /* Compute which characters need special output processing. Initialize
1374 by flagging any white space character. Some systems do not consider
1375 form feed as a space character, but we do. */
1377 for (character = 0; character < CHAR_SET_SIZE; character++)
1378 edited_flag[character] = !! isspace (character);
1379 edited_flag['\f'] = 1;
1381 /* Complete the special character flagging according to selected output
1382 format. */
1384 switch (output_format)
1386 case UNKNOWN_FORMAT:
1387 /* Should never happen. */
1389 case DUMB_FORMAT:
1390 break;
1392 case ROFF_FORMAT:
1394 /* `Quote' characters should be doubled. */
1396 edited_flag['"'] = 1;
1397 break;
1399 case TEX_FORMAT:
1401 /* Various characters need special processing. */
1403 for (cursor = "$%&#_{}\\"; *cursor; cursor++)
1404 edited_flag[to_uchar (*cursor)] = 1;
1406 /* Any character with 8th bit set will print to a single space, unless
1407 it is diacriticized. */
1409 for (character = 0200; character < CHAR_SET_SIZE; character++)
1410 edited_flag[character] = todiac (character) != 0;
1411 break;
1415 /*------------------------------------------------------------------.
1416 | Compute the position and length of all the output fields, given a |
1417 | pointer to some OCCURS. |
1418 `------------------------------------------------------------------*/
1420 static void
1421 define_all_fields (OCCURS *occurs)
1423 int tail_max_width; /* allowable width of tail field */
1424 int head_max_width; /* allowable width of head field */
1425 char *cursor; /* running cursor in source text */
1426 char *left_context_start; /* start of left context */
1427 char *right_context_end; /* end of right context */
1428 char *left_field_start; /* conservative start for `head'/`before' */
1429 int file_index; /* index in text input file arrays */
1430 const char *file_name; /* file name for reference */
1431 int line_ordinal; /* line ordinal for reference */
1433 /* Define `keyafter', start of left context and end of right context.
1434 `keyafter' starts at the saved position for keyword and extend to the
1435 right from the end of the keyword, eating separators or full words, but
1436 not beyond maximum allowed width for `keyafter' field or limit for the
1437 right context. Suffix spaces will be removed afterwards. */
1439 keyafter.start = occurs->key.start;
1440 keyafter.end = keyafter.start + occurs->key.size;
1441 left_context_start = keyafter.start + occurs->left;
1442 right_context_end = keyafter.start + occurs->right;
1444 cursor = keyafter.end;
1445 while (cursor < right_context_end
1446 && cursor <= keyafter.start + keyafter_max_width)
1448 keyafter.end = cursor;
1449 SKIP_SOMETHING (cursor, right_context_end);
1451 if (cursor <= keyafter.start + keyafter_max_width)
1452 keyafter.end = cursor;
1454 keyafter_truncation = truncation_string && keyafter.end < right_context_end;
1456 SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
1458 /* When the left context is wide, it might take some time to catch up from
1459 the left context boundary to the beginning of the `head' or `before'
1460 fields. So, in this case, to speed the catchup, we jump back from the
1461 keyword, using some secure distance, possibly falling in the middle of
1462 a word. A secure backward jump would be at least half the maximum
1463 width of a line, plus the size of the longest word met in the whole
1464 input. We conclude this backward jump by a skip forward of at least
1465 one word. In this manner, we should not inadvertently accept only part
1466 of a word. From the reached point, when it will be time to fix the
1467 beginning of `head' or `before' fields, we will skip forward words or
1468 delimiters until we get sufficiently near. */
1470 if (-occurs->left > half_line_width + maximum_word_length)
1472 left_field_start
1473 = keyafter.start - (half_line_width + maximum_word_length);
1474 SKIP_SOMETHING (left_field_start, keyafter.start);
1476 else
1477 left_field_start = keyafter.start + occurs->left;
1479 /* `before' certainly ends at the keyword, but not including separating
1480 spaces. It starts after than the saved value for the left context, by
1481 advancing it until it falls inside the maximum allowed width for the
1482 before field. There will be no prefix spaces either. `before' only
1483 advances by skipping single separators or whole words. */
1485 before.start = left_field_start;
1486 before.end = keyafter.start;
1487 SKIP_WHITE_BACKWARDS (before.end, before.start);
1489 while (before.start + before_max_width < before.end)
1490 SKIP_SOMETHING (before.start, before.end);
1492 if (truncation_string)
1494 cursor = before.start;
1495 SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
1496 before_truncation = cursor > left_context_start;
1498 else
1499 before_truncation = 0;
1501 SKIP_WHITE (before.start, text_buffer.end);
1503 /* The tail could not take more columns than what has been left in the
1504 left context field, and a gap is mandatory. It starts after the
1505 right context, and does not contain prefixed spaces. It ends at
1506 the end of line, the end of buffer or when the tail field is full,
1507 whichever comes first. It cannot contain only part of a word, and
1508 has no suffixed spaces. */
1510 tail_max_width
1511 = before_max_width - (before.end - before.start) - gap_size;
1513 if (tail_max_width > 0)
1515 tail.start = keyafter.end;
1516 SKIP_WHITE (tail.start, text_buffer.end);
1518 tail.end = tail.start;
1519 cursor = tail.end;
1520 while (cursor < right_context_end
1521 && cursor < tail.start + tail_max_width)
1523 tail.end = cursor;
1524 SKIP_SOMETHING (cursor, right_context_end);
1527 if (cursor < tail.start + tail_max_width)
1528 tail.end = cursor;
1530 if (tail.end > tail.start)
1532 keyafter_truncation = 0;
1533 tail_truncation = truncation_string && tail.end < right_context_end;
1535 else
1536 tail_truncation = 0;
1538 SKIP_WHITE_BACKWARDS (tail.end, tail.start);
1540 else
1543 /* No place left for a tail field. */
1545 tail.start = NULL;
1546 tail.end = NULL;
1547 tail_truncation = 0;
1550 /* `head' could not take more columns than what has been left in the right
1551 context field, and a gap is mandatory. It ends before the left
1552 context, and does not contain suffixed spaces. Its pointer is advanced
1553 until the head field has shrunk to its allowed width. It cannot
1554 contain only part of a word, and has no suffixed spaces. */
1556 head_max_width
1557 = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
1559 if (head_max_width > 0)
1561 head.end = before.start;
1562 SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
1564 head.start = left_field_start;
1565 while (head.start + head_max_width < head.end)
1566 SKIP_SOMETHING (head.start, head.end);
1568 if (head.end > head.start)
1570 before_truncation = 0;
1571 head_truncation = (truncation_string
1572 && head.start > left_context_start);
1574 else
1575 head_truncation = 0;
1577 SKIP_WHITE (head.start, head.end);
1579 else
1582 /* No place left for a head field. */
1584 head.start = NULL;
1585 head.end = NULL;
1586 head_truncation = 0;
1589 if (auto_reference)
1592 /* Construct the reference text in preallocated space from the file
1593 name and the line number. Find out in which file the reference
1594 occurred. Standard input yields an empty file name. Insure line
1595 numbers are one based, even if they are computed zero based. */
1597 file_index = 0;
1598 while (file_line_count[file_index] < occurs->reference)
1599 file_index++;
1601 file_name = input_file_name[file_index];
1602 if (!file_name)
1603 file_name = "";
1605 line_ordinal = occurs->reference + 1;
1606 if (file_index > 0)
1607 line_ordinal -= file_line_count[file_index - 1];
1609 sprintf (reference.start, "%s:%d", file_name, line_ordinal);
1610 reference.end = reference.start + strlen (reference.start);
1612 else if (input_reference)
1615 /* Reference starts at saved position for reference and extends right
1616 until some white space is met. */
1618 reference.start = keyafter.start + (DELTA) occurs->reference;
1619 reference.end = reference.start;
1620 SKIP_NON_WHITE (reference.end, right_context_end);
1624 /* Formatting and actual output - control routines. */
1626 /*----------------------------------------------------------------------.
1627 | Output the current output fields as one line for `troff' or `nroff'. |
1628 `----------------------------------------------------------------------*/
1630 static void
1631 output_one_roff_line (void)
1633 /* Output the `tail' field. */
1635 printf (".%s \"", macro_name);
1636 print_field (tail);
1637 if (tail_truncation)
1638 fputs (truncation_string, stdout);
1639 putchar ('"');
1641 /* Output the `before' field. */
1643 fputs (" \"", stdout);
1644 if (before_truncation)
1645 fputs (truncation_string, stdout);
1646 print_field (before);
1647 putchar ('"');
1649 /* Output the `keyafter' field. */
1651 fputs (" \"", stdout);
1652 print_field (keyafter);
1653 if (keyafter_truncation)
1654 fputs (truncation_string, stdout);
1655 putchar ('"');
1657 /* Output the `head' field. */
1659 fputs (" \"", stdout);
1660 if (head_truncation)
1661 fputs (truncation_string, stdout);
1662 print_field (head);
1663 putchar ('"');
1665 /* Conditionally output the `reference' field. */
1667 if (auto_reference | input_reference)
1669 fputs (" \"", stdout);
1670 print_field (reference);
1671 putchar ('"');
1674 putchar ('\n');
1677 /*---------------------------------------------------------.
1678 | Output the current output fields as one line for `TeX'. |
1679 `---------------------------------------------------------*/
1681 static void
1682 output_one_tex_line (void)
1684 BLOCK key; /* key field, isolated */
1685 BLOCK after; /* after field, isolated */
1686 char *cursor; /* running cursor in source text */
1688 printf ("\\%s ", macro_name);
1689 putchar ('{');
1690 print_field (tail);
1691 fputs ("}{", stdout);
1692 print_field (before);
1693 fputs ("}{", stdout);
1694 key.start = keyafter.start;
1695 after.end = keyafter.end;
1696 cursor = keyafter.start;
1697 SKIP_SOMETHING (cursor, keyafter.end);
1698 key.end = cursor;
1699 after.start = cursor;
1700 print_field (key);
1701 fputs ("}{", stdout);
1702 print_field (after);
1703 fputs ("}{", stdout);
1704 print_field (head);
1705 putchar ('}');
1706 if (auto_reference | input_reference)
1708 putchar ('{');
1709 print_field (reference);
1710 putchar ('}');
1712 putchar ('\n');
1715 /*-------------------------------------------------------------------.
1716 | Output the current output fields as one line for a dumb terminal. |
1717 `-------------------------------------------------------------------*/
1719 static void
1720 output_one_dumb_line (void)
1722 if (!right_reference)
1724 if (auto_reference)
1727 /* Output the `reference' field, in such a way that GNU emacs
1728 next-error will handle it. The ending colon is taken from the
1729 gap which follows. */
1731 print_field (reference);
1732 putchar (':');
1733 print_spaces (reference_max_width
1734 + gap_size
1735 - (reference.end - reference.start)
1736 - 1);
1738 else
1741 /* Output the `reference' field and its following gap. */
1743 print_field (reference);
1744 print_spaces (reference_max_width
1745 + gap_size
1746 - (reference.end - reference.start));
1750 if (tail.start < tail.end)
1752 /* Output the `tail' field. */
1754 print_field (tail);
1755 if (tail_truncation)
1756 fputs (truncation_string, stdout);
1758 print_spaces (half_line_width - gap_size
1759 - (before.end - before.start)
1760 - (before_truncation ? truncation_string_length : 0)
1761 - (tail.end - tail.start)
1762 - (tail_truncation ? truncation_string_length : 0));
1764 else
1765 print_spaces (half_line_width - gap_size
1766 - (before.end - before.start)
1767 - (before_truncation ? truncation_string_length : 0));
1769 /* Output the `before' field. */
1771 if (before_truncation)
1772 fputs (truncation_string, stdout);
1773 print_field (before);
1775 print_spaces (gap_size);
1777 /* Output the `keyafter' field. */
1779 print_field (keyafter);
1780 if (keyafter_truncation)
1781 fputs (truncation_string, stdout);
1783 if (head.start < head.end)
1785 /* Output the `head' field. */
1787 print_spaces (half_line_width
1788 - (keyafter.end - keyafter.start)
1789 - (keyafter_truncation ? truncation_string_length : 0)
1790 - (head.end - head.start)
1791 - (head_truncation ? truncation_string_length : 0));
1792 if (head_truncation)
1793 fputs (truncation_string, stdout);
1794 print_field (head);
1796 else
1798 if ((auto_reference | input_reference) & right_reference)
1799 print_spaces (half_line_width
1800 - (keyafter.end - keyafter.start)
1801 - (keyafter_truncation ? truncation_string_length : 0));
1803 if ((auto_reference | input_reference) & right_reference)
1805 /* Output the `reference' field. */
1807 print_spaces (gap_size);
1808 print_field (reference);
1811 putchar ('\n');
1814 /*------------------------------------------------------------------------.
1815 | Scan the whole occurs table and, for each entry, output one line in the |
1816 | appropriate format. |
1817 `------------------------------------------------------------------------*/
1819 static void
1820 generate_all_output (void)
1822 size_t occurs_index; /* index of keyword entry being processed */
1823 OCCURS *occurs_cursor; /* current keyword entry being processed */
1825 /* The following assignments are useful to provide default values in case
1826 line contexts or references are not used, in which case these variables
1827 would never be computed. */
1829 tail.start = NULL;
1830 tail.end = NULL;
1831 tail_truncation = 0;
1833 head.start = NULL;
1834 head.end = NULL;
1835 head_truncation = 0;
1837 /* Loop over all keyword occurrences. */
1839 occurs_cursor = occurs_table[0];
1841 for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
1843 /* Compute the exact size of every field and whenever truncation flags
1844 are present or not. */
1846 define_all_fields (occurs_cursor);
1848 /* Produce one output line according to selected format. */
1850 switch (output_format)
1852 case UNKNOWN_FORMAT:
1853 /* Should never happen. */
1855 case DUMB_FORMAT:
1856 output_one_dumb_line ();
1857 break;
1859 case ROFF_FORMAT:
1860 output_one_roff_line ();
1861 break;
1863 case TEX_FORMAT:
1864 output_one_tex_line ();
1865 break;
1868 /* Advance the cursor into the occurs table. */
1870 occurs_cursor++;
1874 /* Option decoding and main program. */
1876 /*------------------------------------------------------.
1877 | Print program identification and options, then exit. |
1878 `------------------------------------------------------*/
1880 void
1881 usage (int status)
1883 if (status != EXIT_SUCCESS)
1884 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1885 program_name);
1886 else
1888 printf (_("\
1889 Usage: %s [OPTION]... [INPUT]... (without -G)\n\
1890 or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
1891 program_name, program_name);
1892 fputs (_("\
1893 Output a permuted index, including context, of the words in the input files.\n\
1895 "), stdout);
1896 fputs (_("\
1897 Mandatory arguments to long options are mandatory for short options too.\n\
1898 "), stdout);
1899 fputs (_("\
1900 -A, --auto-reference output automatically generated references\n\
1901 -G, --traditional behave more like System V `ptx'\n\
1902 -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
1903 "), stdout);
1904 fputs (_("\
1905 -M, --macro-name=STRING macro name to use instead of `xx'\n\
1906 -O, --format=roff generate output as roff directives\n\
1907 -R, --right-side-refs put references at right, not counted in -w\n\
1908 -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
1909 -T, --format=tex generate output as TeX directives\n\
1910 "), stdout);
1911 fputs (_("\
1912 -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
1913 -b, --break-file=FILE word break characters in this FILE\n\
1914 -f, --ignore-case fold lower case to upper case for sorting\n\
1915 -g, --gap-size=NUMBER gap size in columns between output fields\n\
1916 -i, --ignore-file=FILE read ignore word list from FILE\n\
1917 -o, --only-file=FILE read only word list from this FILE\n\
1918 "), stdout);
1919 fputs (_("\
1920 -r, --references first field of each line is a reference\n\
1921 -t, --typeset-mode - not implemented -\n\
1922 -w, --width=NUMBER output width in columns, reference excluded\n\
1923 "), stdout);
1924 fputs (HELP_OPTION_DESCRIPTION, stdout);
1925 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1926 fputs (_("\
1928 With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
1929 "), stdout);
1930 emit_bug_reporting_address ();
1932 exit (status);
1935 /*----------------------------------------------------------------------.
1936 | Main program. Decode ARGC arguments passed through the ARGV array of |
1937 | strings, then launch execution. |
1938 `----------------------------------------------------------------------*/
1940 /* Long options equivalences. */
1941 static const struct option long_options[] =
1943 {"auto-reference", no_argument, NULL, 'A'},
1944 {"break-file", required_argument, NULL, 'b'},
1945 {"flag-truncation", required_argument, NULL, 'F'},
1946 {"ignore-case", no_argument, NULL, 'f'},
1947 {"gap-size", required_argument, NULL, 'g'},
1948 {"ignore-file", required_argument, NULL, 'i'},
1949 {"macro-name", required_argument, NULL, 'M'},
1950 {"only-file", required_argument, NULL, 'o'},
1951 {"references", no_argument, NULL, 'r'},
1952 {"right-side-refs", no_argument, NULL, 'R'},
1953 {"format", required_argument, NULL, 10},
1954 {"sentence-regexp", required_argument, NULL, 'S'},
1955 {"traditional", no_argument, NULL, 'G'},
1956 {"typeset-mode", no_argument, NULL, 't'},
1957 {"width", required_argument, NULL, 'w'},
1958 {"word-regexp", required_argument, NULL, 'W'},
1959 {GETOPT_HELP_OPTION_DECL},
1960 {GETOPT_VERSION_OPTION_DECL},
1961 {NULL, 0, NULL, 0},
1964 static char const* const format_args[] =
1966 "roff", "tex", NULL
1969 static enum Format const format_vals[] =
1971 ROFF_FORMAT, TEX_FORMAT
1975 main (int argc, char **argv)
1977 int optchar; /* argument character */
1978 int file_index; /* index in text input file arrays */
1980 /* Decode program options. */
1982 initialize_main (&argc, &argv);
1983 program_name = argv[0];
1984 setlocale (LC_ALL, "");
1985 bindtextdomain (PACKAGE, LOCALEDIR);
1986 textdomain (PACKAGE);
1988 atexit (close_stdout);
1990 #if HAVE_SETCHRCLASS
1991 setchrclass (NULL);
1992 #endif
1994 while (optchar = getopt_long (argc, argv, "AF:GM:ORS:TW:b:i:fg:o:trw:",
1995 long_options, NULL),
1996 optchar != EOF)
1998 switch (optchar)
2000 default:
2001 usage (EXIT_FAILURE);
2003 case 'G':
2004 gnu_extensions = false;
2005 break;
2007 case 'b':
2008 break_file = optarg;
2009 break;
2011 case 'f':
2012 ignore_case = true;
2013 break;
2015 case 'g':
2017 unsigned long int tmp_ulong;
2018 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2019 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2020 error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
2021 quotearg (optarg));
2022 gap_size = tmp_ulong;
2023 break;
2026 case 'i':
2027 ignore_file = optarg;
2028 break;
2030 case 'o':
2031 only_file = optarg;
2032 break;
2034 case 'r':
2035 input_reference = true;
2036 break;
2038 case 't':
2039 /* Yet to understand... */
2040 break;
2042 case 'w':
2044 unsigned long int tmp_ulong;
2045 if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
2046 || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
2047 error (EXIT_FAILURE, 0, _("invalid line width: %s"),
2048 quotearg (optarg));
2049 line_width = tmp_ulong;
2050 break;
2053 case 'A':
2054 auto_reference = true;
2055 break;
2057 case 'F':
2058 truncation_string = copy_unescaped_string (optarg);
2059 break;
2061 case 'M':
2062 macro_name = optarg;
2063 break;
2065 case 'O':
2066 output_format = ROFF_FORMAT;
2067 break;
2069 case 'R':
2070 right_reference = true;
2071 break;
2073 case 'S':
2074 context_regex.string = copy_unescaped_string (optarg);
2075 break;
2077 case 'T':
2078 output_format = TEX_FORMAT;
2079 break;
2081 case 'W':
2082 word_regex.string = copy_unescaped_string (optarg);
2083 if (!*word_regex.string)
2084 word_regex.string = NULL;
2085 break;
2087 case 10:
2088 output_format = XARGMATCH ("--format", optarg,
2089 format_args, format_vals);
2090 case_GETOPT_HELP_CHAR;
2092 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
2096 /* Process remaining arguments. If GNU extensions are enabled, process
2097 all arguments as input parameters. If disabled, accept at most two
2098 arguments, the second of which is an output parameter. */
2100 if (optind == argc)
2103 /* No more argument simply means: read standard input. */
2105 input_file_name = xmalloc (sizeof *input_file_name);
2106 file_line_count = xmalloc (sizeof *file_line_count);
2107 number_input_files = 1;
2108 input_file_name[0] = NULL;
2110 else if (gnu_extensions)
2112 number_input_files = argc - optind;
2113 input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
2114 file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
2116 for (file_index = 0; file_index < number_input_files; file_index++)
2118 input_file_name[file_index] = argv[optind];
2119 if (!*argv[optind] || STREQ (argv[optind], "-"))
2120 input_file_name[0] = NULL;
2121 else
2122 input_file_name[0] = argv[optind];
2123 optind++;
2126 else
2129 /* There is one necessary input file. */
2131 number_input_files = 1;
2132 input_file_name = xmalloc (sizeof *input_file_name);
2133 file_line_count = xmalloc (sizeof *file_line_count);
2134 if (!*argv[optind] || STREQ (argv[optind], "-"))
2135 input_file_name[0] = NULL;
2136 else
2137 input_file_name[0] = argv[optind];
2138 optind++;
2140 /* Redirect standard output, only if requested. */
2142 if (optind < argc)
2144 if (! freopen (argv[optind], "w", stdout))
2145 error (EXIT_FAILURE, errno, "%s", argv[optind]);
2146 optind++;
2149 /* Diagnose any other argument as an error. */
2151 if (optind < argc)
2153 error (0, 0, _("extra operand %s"), quote (argv[optind]));
2154 usage (EXIT_FAILURE);
2158 /* If the output format has not been explicitly selected, choose dumb
2159 terminal format if GNU extensions are enabled, else `roff' format. */
2161 if (output_format == UNKNOWN_FORMAT)
2162 output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
2164 /* Initialize the main tables. */
2166 initialize_regex ();
2168 /* Read `Break character' file, if any. */
2170 if (break_file)
2171 digest_break_file (break_file);
2173 /* Read `Ignore words' file and `Only words' files, if any. If any of
2174 these files is empty, reset the name of the file to NULL, to avoid
2175 unnecessary calls to search_table. */
2177 if (ignore_file)
2179 digest_word_file (ignore_file, &ignore_table);
2180 if (ignore_table.length == 0)
2181 ignore_file = NULL;
2184 if (only_file)
2186 digest_word_file (only_file, &only_table);
2187 if (only_table.length == 0)
2188 only_file = NULL;
2191 /* Prepare to study all the input files. */
2193 number_of_occurs[0] = 0;
2194 total_line_count = 0;
2195 maximum_word_length = 0;
2196 reference_max_width = 0;
2198 for (file_index = 0; file_index < number_input_files; file_index++)
2201 /* Read the file in core, than study it. */
2203 swallow_file_in_memory (input_file_name[file_index], &text_buffer);
2204 find_occurs_in_text ();
2206 /* Maintain for each file how many lines has been read so far when its
2207 end is reached. Incrementing the count first is a simple kludge to
2208 handle a possible incomplete line at end of file. */
2210 total_line_count++;
2211 file_line_count[file_index] = total_line_count;
2214 /* Do the output process phase. */
2216 sort_found_occurs ();
2217 fix_output_parameters ();
2218 generate_all_output ();
2220 /* All done. */
2222 exit (EXIT_SUCCESS);