1/* texindex -- sort TeX index dribble output into an actual index.
2   $Id: texindex.c,v 1.11 2004/04/11 17:56:47 karl Exp $
3
4   Copyright (C) 1987, 1991, 1992, 1996, 1997, 1998, 1999, 2000, 2001,
5   2002, 2003, 2004 Free Software Foundation, Inc.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307. */
20
21#include "system.h"
22#include <getopt.h>
23
24static char *program_name = "texindex";
25
26#if defined (emacs)
27#  include "../src/config.h"
28/* Some s/os.h files redefine these. */
29#  undef read
30#  undef close
31#  undef write
32#  undef open
33#endif
34
35#if !defined (HAVE_MEMSET)
36#undef memset
37#define memset(ptr, ignore, count) bzero (ptr, count)
38#endif
39
40char *mktemp (char *);
41
42#if !defined (SEEK_SET)
43#  define SEEK_SET 0
44#  define SEEK_CUR 1
45#  define SEEK_END 2
46#endif /* !SEEK_SET */
47
48struct linebuffer;
49
50/* When sorting in core, this structure describes one line
51   and the position and length of its first keyfield.  */
52struct lineinfo
53{
54  char *text;           /* The actual text of the line. */
55  union {
56    char *text;         /* The start of the key (for textual comparison). */
57    long number;        /* The numeric value (for numeric comparison). */
58  } key;
59  long keylen;          /* Length of KEY field. */
60};
61
62/* This structure describes a field to use as a sort key. */
63struct keyfield
64{
65  int startwords;       /* Number of words to skip. */
66  int startchars;       /* Number of additional chars to skip. */
67  int endwords;         /* Number of words to ignore at end. */
68  int endchars;         /* Ditto for characters of last word. */
69  char ignore_blanks;   /* Non-zero means ignore spaces and tabs. */
70  char fold_case;       /* Non-zero means case doesn't matter. */
71  char reverse;         /* Non-zero means compare in reverse order. */
72  char numeric;         /* Non-zeros means field is ASCII numeric. */
73  char positional;      /* Sort according to file position. */
74  char braced;          /* Count balanced-braced groupings as fields. */
75};
76
77/* Vector of keyfields to use. */
78struct keyfield keyfields[3];
79
80/* Number of keyfields stored in that vector.  */
81int num_keyfields = 3;
82
83/* Vector of input file names, terminated with a null pointer. */
84char **infiles;
85
86/* Vector of corresponding output file names, or NULL, meaning default it
87   (add an `s' to the end). */
88char **outfiles;
89
90/* Length of `infiles'. */
91int num_infiles;
92
93/* Pointer to the array of pointers to lines being sorted. */
94char **linearray;
95
96/* The allocated length of `linearray'. */
97long nlines;
98
99/* Directory to use for temporary files.  On Unix, it ends with a slash.  */
100char *tempdir;
101
102/* Number of last temporary file.  */
103int tempcount;
104
105/* Number of last temporary file already deleted.
106   Temporary files are deleted by `flush_tempfiles' in order of creation.  */
107int last_deleted_tempcount;
108
109/* During in-core sort, this points to the base of the data block
110   which contains all the lines of data.  */
111char *text_base;
112
113/* Initially 0; changed to 1 if we want initials in this index.  */
114int need_initials;
115
116/* Remembers the first initial letter seen in this index, so we can
117   determine whether we need initials in the sorted form.  */
118char first_initial;
119
120/* Additional command switches .*/
121
122/* Nonzero means do not delete tempfiles -- for debugging. */
123int keep_tempfiles;
124
125/* Forward declarations of functions in this file. */
126void decode_command (int argc, char **argv);
127void sort_in_core (char *infile, int total, char *outfile);
128void sort_offline (char *infile, off_t total, char *outfile);
129char **parsefile (char *filename, char **nextline, char *data, long int size);
130char *find_field (struct keyfield *keyfield, char *str, long int *lengthptr);
131char *find_pos (char *str, int words, int chars, int ignore_blanks);
132long find_value (char *start, long int length);
133char *find_braced_pos (char *str, int words, int chars, int ignore_blanks);
134char *find_braced_end (char *str);
135void writelines (char **linearray, int nlines, FILE *ostream);
136int compare_field (struct keyfield *keyfield, char *start1,
137                   long int length1, long int pos1, char *start2,
138                   long int length2, long int pos2);
139int compare_full (const void *, const void *);
140long readline (struct linebuffer *linebuffer, FILE *stream);
141int merge_files (char **infiles, int nfiles, char *outfile);
142int merge_direct (char **infiles, int nfiles, char *outfile);
143void pfatal_with_name (const char *name);
144void fatal (const char *format, const char *arg);
145void error (const char *format, const char *arg);
146void *xmalloc (), *xrealloc ();
147char *concat (char *s1, char *s2);
148void flush_tempfiles (int to_count);
149
150#define MAX_IN_CORE_SORT 500000
151
152int
153main (int argc, char **argv)
154{
155  int i;
156
157  tempcount = 0;
158  last_deleted_tempcount = 0;
159
160#ifdef HAVE_SETLOCALE
161  /* Set locale via LC_ALL.  */
162  setlocale (LC_ALL, "");
163#endif
164
165  /* Set the text message domain.  */
166  bindtextdomain (PACKAGE, LOCALEDIR);
167  textdomain (PACKAGE);
168
169  /* In case we write to a redirected stdout that fails.  */
170  /* not ready atexit (close_stdout); */
171
172  /* Describe the kind of sorting to do. */
173  /* The first keyfield uses the first braced field and folds case. */
174  keyfields[0].braced = 1;
175  keyfields[0].fold_case = 1;
176  keyfields[0].endwords = -1;
177  keyfields[0].endchars = -1;
178
179  /* The second keyfield uses the second braced field, numerically. */
180  keyfields[1].braced = 1;
181  keyfields[1].numeric = 1;
182  keyfields[1].startwords = 1;
183  keyfields[1].endwords = -1;
184  keyfields[1].endchars = -1;
185
186  /* The third keyfield (which is ignored while discarding duplicates)
187     compares the whole line. */
188  keyfields[2].endwords = -1;
189  keyfields[2].endchars = -1;
190
191  decode_command (argc, argv);
192
193  /* Process input files completely, one by one.  */
194
195  for (i = 0; i < num_infiles; i++)
196    {
197      int desc;
198      off_t ptr;
199      char *outfile;
200      struct stat instat;
201
202      desc = open (infiles[i], O_RDONLY, 0);
203      if (desc < 0)
204        pfatal_with_name (infiles[i]);
205
206      if (stat (infiles[i], &instat))
207        pfatal_with_name (infiles[i]);
208      if (S_ISDIR (instat.st_mode))
209        {
210#ifdef EISDIR
211          errno = EISDIR;
212#endif
213          pfatal_with_name (infiles[i]);
214        }
215
216      lseek (desc, (off_t) 0, SEEK_END);
217      ptr = (off_t) lseek (desc, (off_t) 0, SEEK_CUR);
218
219      close (desc);
220
221      outfile = outfiles[i];
222      if (!outfile)
223        outfile = concat (infiles[i], "s");
224
225      need_initials = 0;
226      first_initial = '\0';
227
228      if (ptr < MAX_IN_CORE_SORT)
229        /* Sort a small amount of data. */
230        sort_in_core (infiles[i], (int)ptr, outfile);
231      else
232        sort_offline (infiles[i], ptr, outfile);
233    }
234
235  flush_tempfiles (tempcount);
236  xexit (0);
237  return 0; /* Avoid bogus warnings.  */
238}
239
240typedef struct
241{
242  char *long_name;
243  char *short_name;
244  int *variable_ref;
245  int variable_value;
246  char *arg_name;
247  char *doc_string;
248} TEXINDEX_OPTION;
249
250TEXINDEX_OPTION texindex_options[] = {
251  { "--help", "-h", (int *)NULL, 0, (char *)NULL,
252      N_("display this help and exit") },
253  { "--keep", "-k", &keep_tempfiles, 1, (char *)NULL,
254      N_("keep temporary files around after processing") },
255  { "--no-keep", 0, &keep_tempfiles, 0, (char *)NULL,
256      N_("do not keep temporary files around after processing (default)") },
257  { "--output", "-o", (int *)NULL, 0, "FILE",
258      N_("send output to FILE") },
259  { "--version", (char *)NULL, (int *)NULL, 0, (char *)NULL,
260      N_("display version information and exit") },
261  { (char *)NULL, (char *)NULL, (int *)NULL, 0, (char *)NULL }
262};
263
264void
265usage (int result_value)
266{
267  register int i;
268  FILE *f = result_value ? stderr : stdout;
269
270  fprintf (f, _("Usage: %s [OPTION]... FILE...\n"), program_name);
271  fprintf (f, _("Generate a sorted index for each TeX output FILE.\n"));
272  /* Avoid trigraph nonsense.  */
273  fprintf (f,
274_("Usually FILE... is specified as `foo.%c%c\' for a document `foo.texi'.\n"),
275           '?', '?'); /* avoid trigraph in cat-id-tbl.c */
276  fprintf (f, _("\nOptions:\n"));
277
278  for (i = 0; texindex_options[i].long_name; i++)
279    {
280      putc (' ', f);
281
282      if (texindex_options[i].short_name)
283        fprintf (f, "%s, ", texindex_options[i].short_name);
284
285      fprintf (f, "%s %s",
286               texindex_options[i].long_name,
287               texindex_options[i].arg_name
288               ? texindex_options[i].arg_name : "");
289
290      fprintf (f, "\t%s\n", _(texindex_options[i].doc_string));
291    }
292  fputs (_("\n\
293Email bug reports to bug-texinfo@gnu.org,\n\
294general questions and discussion to help-texinfo@gnu.org.\n\
295Texinfo home page: http://www.gnu.org/software/texinfo/"), f);
296  fputs ("\n", f);
297
298  xexit (result_value);
299}
300
301/* Decode the command line arguments to set the parameter variables
302   and set up the vector of keyfields and the vector of input files. */
303
304void
305decode_command (int argc, char **argv)
306{
307  int arg_index = 1;
308  char **ip;
309  char **op;
310
311  /* Store default values into parameter variables. */
312
313  tempdir = getenv ("TMPDIR");
314  if (tempdir == NULL)
315    tempdir = getenv ("TEMP");
316  if (tempdir == NULL)
317    tempdir = getenv ("TMP");
318  if (tempdir == NULL)
319    tempdir = DEFAULT_TMPDIR;
320  else
321    tempdir = concat (tempdir, "/");
322
323  keep_tempfiles = 0;
324
325  /* Allocate ARGC input files, which must be enough.  */
326
327  infiles = (char **) xmalloc (argc * sizeof (char *));
328  outfiles = (char **) xmalloc (argc * sizeof (char *));
329  ip = infiles;
330  op = outfiles;
331
332  while (arg_index < argc)
333    {
334      char *arg = argv[arg_index++];
335
336      if (*arg == '-')
337        {
338          if (strcmp (arg, "--version") == 0)
339            {
340              printf ("texindex (GNU %s) %s\n", PACKAGE, VERSION);
341              puts ("");
342              puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
343              printf (_("There is NO warranty.  You may redistribute this software\n\
344under the terms of the GNU General Public License.\n\
345For more information about these matters, see the files named COPYING.\n"));
346              xexit (0);
347            }
348          else if ((strcmp (arg, "--keep") == 0) ||
349                   (strcmp (arg, "-k") == 0))
350            {
351              keep_tempfiles = 1;
352            }
353          else if ((strcmp (arg, "--help") == 0) ||
354                   (strcmp (arg, "-h") == 0))
355            {
356              usage (0);
357            }
358          else if ((strcmp (arg, "--output") == 0) ||
359                   (strcmp (arg, "-o") == 0))
360            {
361              if (argv[arg_index] != (char *)NULL)
362                {
363                  arg_index++;
364                  if (op > outfiles)
365                    *(op - 1) = argv[arg_index];
366                }
367              else
368                usage (1);
369            }
370          else
371            usage (1);
372        }
373      else
374        {
375          *ip++ = arg;
376          *op++ = (char *)NULL;
377        }
378    }
379
380  /* Record number of keyfields and terminate list of filenames. */
381  num_infiles = ip - infiles;
382  *ip = (char *)NULL;
383  if (num_infiles == 0)
384    usage (1);
385}
386
387static char **tv;
388static int tv_alloc;
389static int tv_used;
390
391static int
392findtempname (char *tempname)
393{
394  int i;
395
396  for (i = 0; i < tv_used; i++)
397    if (strcmp (tv[i], tempname) == 0)
398	return (1);
399  return (0);
400}
401
402/* Return a name for temporary file COUNT. */
403
404static char *
405maketempname (int count)
406{
407  static char *tempbase = NULL;
408  char *tempname;
409  char tempsuffix[10];
410  int fd;
411
412  if (!tempbase)
413    {
414      tempbase = concat (tempdir, "txidxXXXXXX");
415
416      fd = mkstemp (tempbase);
417      if (fd == -1)
418        pfatal_with_name (tempbase);
419    }
420
421  sprintf (tempsuffix, ".%d", count);
422  tempname = concat (tempbase, tempsuffix);
423  /*
424   * The open logic becomes a bit convoluted. If open(2) fails due to EEXIST,
425   * it's likely because somebody attempted to race us, or because we have
426   * already created this file.
427   */
428  fd = open (tempname, O_CREAT|O_EXCL|O_WRONLY, 0600);
429  if (fd == -1)
430    {
431	/*
432	 * If errno is not EEXIST, then open failed for some other reason, so
433	 * we should terminate. If errno == EEXIST AND we didn't create this
434	 * file, terminate. Otherwise, it's safe to say that errno == EEXIST
435	 * because we already created it, in this event, we can just return.
436	 */
437	if (errno != EEXIST ||
438	  (errno == EEXIST && findtempname (tempname) == 0))
439	  pfatal_with_name (tempname);
440	return (tempname);
441    }
442  else if (fd > 0)
443    {
444	close (fd);
445    }
446  if (tv == NULL)
447    {
448	tv_alloc = 16;
449	tv = calloc (tv_alloc, sizeof (char *));
450	if (tv == NULL)
451	  {
452	    fprintf (stderr, "calloc failed\n");
453	    exit (1);
454	  }
455    }
456  else if (tv_used == tv_alloc)
457    {
458	tv_alloc += 4;
459	tv = realloc (tv, tv_alloc * sizeof (char *));
460	if (tv == NULL)
461	  {
462	    fprintf (stderr, "realloc failed");
463	    exit (1);
464	  }
465    }
466  tv[tv_used++] = strdup (tempname);
467  return tempname;
468}
469
470
471/* Delete all temporary files up to TO_COUNT. */
472
473void
474flush_tempfiles (int to_count)
475{
476  if (keep_tempfiles)
477    return;
478  while (last_deleted_tempcount < to_count)
479    unlink (maketempname (++last_deleted_tempcount));
480}
481
482
483/* Compare LINE1 and LINE2 according to the specified set of keyfields. */
484
485int
486compare_full (const void *p1, const void *p2)
487{
488  char **line1 = (char **) p1;
489  char **line2 = (char **) p2;
490  int i;
491
492  /* Compare using the first keyfield;
493     if that does not distinguish the lines, try the second keyfield;
494     and so on. */
495
496  for (i = 0; i < num_keyfields; i++)
497    {
498      long length1, length2;
499      char *start1 = find_field (&keyfields[i], *line1, &length1);
500      char *start2 = find_field (&keyfields[i], *line2, &length2);
501      int tem = compare_field (&keyfields[i], start1, length1,
502                               *line1 - text_base,
503                               start2, length2, *line2 - text_base);
504      if (tem)
505        {
506          if (keyfields[i].reverse)
507            return -tem;
508          return tem;
509        }
510    }
511
512  return 0;                     /* Lines match exactly. */
513}
514
515/* Compare LINE1 and LINE2, described by structures
516   in which the first keyfield is identified in advance.
517   For positional sorting, assumes that the order of the lines in core
518   reflects their nominal order.  */
519int
520compare_prepared (const void *p1, const void *p2)
521{
522  struct lineinfo *line1 = (struct lineinfo *) p1;
523  struct lineinfo *line2 = (struct lineinfo *) p2;
524  int i;
525  int tem;
526  char *text1, *text2;
527
528  /* Compare using the first keyfield, which has been found for us already. */
529  if (keyfields->positional)
530    {
531      if (line1->text - text_base > line2->text - text_base)
532        tem = 1;
533      else
534        tem = -1;
535    }
536  else if (keyfields->numeric)
537    tem = line1->key.number - line2->key.number;
538  else
539    tem = compare_field (keyfields, line1->key.text, line1->keylen, 0,
540                         line2->key.text, line2->keylen, 0);
541  if (tem)
542    {
543      if (keyfields->reverse)
544        return -tem;
545      return tem;
546    }
547
548  text1 = line1->text;
549  text2 = line2->text;
550
551  /* Compare using the second keyfield;
552     if that does not distinguish the lines, try the third keyfield;
553     and so on. */
554
555  for (i = 1; i < num_keyfields; i++)
556    {
557      long length1, length2;
558      char *start1 = find_field (&keyfields[i], text1, &length1);
559      char *start2 = find_field (&keyfields[i], text2, &length2);
560      int tem = compare_field (&keyfields[i], start1, length1,
561                               text1 - text_base,
562                               start2, length2, text2 - text_base);
563      if (tem)
564        {
565          if (keyfields[i].reverse)
566            return -tem;
567          return tem;
568        }
569    }
570
571  return 0;                     /* Lines match exactly. */
572}
573
574/* Like compare_full but more general.
575   You can pass any strings, and you can say how many keyfields to use.
576   POS1 and POS2 should indicate the nominal positional ordering of
577   the two lines in the input.  */
578
579int
580compare_general (char *str1, char *str2, long int pos1, long int pos2, int use_keyfields)
581{
582  int i;
583
584  /* Compare using the first keyfield;
585     if that does not distinguish the lines, try the second keyfield;
586     and so on. */
587
588  for (i = 0; i < use_keyfields; i++)
589    {
590      long length1, length2;
591      char *start1 = find_field (&keyfields[i], str1, &length1);
592      char *start2 = find_field (&keyfields[i], str2, &length2);
593      int tem = compare_field (&keyfields[i], start1, length1, pos1,
594                               start2, length2, pos2);
595      if (tem)
596        {
597          if (keyfields[i].reverse)
598            return -tem;
599          return tem;
600        }
601    }
602
603  return 0;                     /* Lines match exactly. */
604}
605
606/* Find the start and length of a field in STR according to KEYFIELD.
607   A pointer to the starting character is returned, and the length
608   is stored into the int that LENGTHPTR points to.  */
609
610char *
611find_field (struct keyfield *keyfield, char *str, long int *lengthptr)
612{
613  char *start;
614  char *end;
615  char *(*fun) ();
616
617  if (keyfield->braced)
618    fun = find_braced_pos;
619  else
620    fun = find_pos;
621
622  start = (*fun) (str, keyfield->startwords, keyfield->startchars,
623                  keyfield->ignore_blanks);
624  if (keyfield->endwords < 0)
625    {
626      if (keyfield->braced)
627        end = find_braced_end (start);
628      else
629        {
630          end = start;
631          while (*end && *end != '\n')
632            end++;
633        }
634    }
635  else
636    {
637      end = (*fun) (str, keyfield->endwords, keyfield->endchars, 0);
638      if (end - str < start - str)
639        end = start;
640    }
641  *lengthptr = end - start;
642  return start;
643}
644
645/* Return a pointer to a specified place within STR,
646   skipping (from the beginning) WORDS words and then CHARS chars.
647   If IGNORE_BLANKS is nonzero, we skip all blanks
648   after finding the specified word.  */
649
650char *
651find_pos (char *str, int words, int chars, int ignore_blanks)
652{
653  int i;
654  char *p = str;
655
656  for (i = 0; i < words; i++)
657    {
658      char c;
659      /* Find next bunch of nonblanks and skip them. */
660      while ((c = *p) == ' ' || c == '\t')
661        p++;
662      while ((c = *p) && c != '\n' && !(c == ' ' || c == '\t'))
663        p++;
664      if (!*p || *p == '\n')
665        return p;
666    }
667
668  while (*p == ' ' || *p == '\t')
669    p++;
670
671  for (i = 0; i < chars; i++)
672    {
673      if (!*p || *p == '\n')
674        break;
675      p++;
676    }
677  return p;
678}
679
680/* Like find_pos but assumes that each field is surrounded by braces
681   and that braces within fields are balanced. */
682
683char *
684find_braced_pos (char *str, int words, int chars, int ignore_blanks)
685{
686  int i;
687  int bracelevel;
688  char *p = str;
689  char c;
690
691  for (i = 0; i < words; i++)
692    {
693      bracelevel = 1;
694      while ((c = *p++) != '{' && c != '\n' && c)
695        /* Do nothing. */ ;
696      if (c != '{')
697        return p - 1;
698      while (bracelevel)
699        {
700          c = *p++;
701          if (c == '{')
702            bracelevel++;
703          if (c == '}')
704            bracelevel--;
705          if (c == 0 || c == '\n')
706            return p - 1;
707        }
708    }
709
710  while ((c = *p++) != '{' && c != '\n' && c)
711    /* Do nothing. */ ;
712
713  if (c != '{')
714    return p - 1;
715
716  if (ignore_blanks)
717    while ((c = *p) == ' ' || c == '\t')
718      p++;
719
720  for (i = 0; i < chars; i++)
721    {
722      if (!*p || *p == '\n')
723        break;
724      p++;
725    }
726  return p;
727}
728
729/* Find the end of the balanced-brace field which starts at STR.
730   The position returned is just before the closing brace. */
731
732char *
733find_braced_end (char *str)
734{
735  int bracelevel;
736  char *p = str;
737  char c;
738
739  bracelevel = 1;
740  while (bracelevel)
741    {
742      c = *p++;
743      if (c == '{')
744        bracelevel++;
745      if (c == '}')
746        bracelevel--;
747      if (c == 0 || c == '\n')
748        return p - 1;
749    }
750  return p - 1;
751}
752
753long
754find_value (char *start, long int length)
755{
756  while (length != 0L)
757    {
758      if (isdigit (*start))
759        return atol (start);
760      length--;
761      start++;
762    }
763  return 0l;
764}
765
766/* Vector used to translate characters for comparison.
767   This is how we make all alphanumerics follow all else,
768   and ignore case in the first sorting.  */
769int char_order[256];
770
771void
772init_char_order (void)
773{
774  int i;
775  for (i = 1; i < 256; i++)
776    char_order[i] = i;
777
778  for (i = '0'; i <= '9'; i++)
779    char_order[i] += 512;
780
781  for (i = 'a'; i <= 'z'; i++)
782    {
783      char_order[i] = 512 + i;
784      char_order[i + 'A' - 'a'] = 512 + i;
785    }
786}
787
788/* Compare two fields (each specified as a start pointer and a character count)
789   according to KEYFIELD.
790   The sign of the value reports the relation between the fields. */
791
792int
793compare_field (struct keyfield *keyfield, char *start1, long int length1,
794               long int pos1, char *start2, long int length2, long int pos2)
795{
796  if (keyfields->positional)
797    {
798      if (pos1 > pos2)
799        return 1;
800      else
801        return -1;
802    }
803  if (keyfield->numeric)
804    {
805      long value = find_value (start1, length1) - find_value (start2, length2);
806      if (value > 0)
807        return 1;
808      if (value < 0)
809        return -1;
810      return 0;
811    }
812  else
813    {
814      char *p1 = start1;
815      char *p2 = start2;
816      char *e1 = start1 + length1;
817      char *e2 = start2 + length2;
818
819      while (1)
820        {
821          int c1, c2;
822
823          if (p1 == e1)
824            c1 = 0;
825          else
826            c1 = *p1++;
827          if (p2 == e2)
828            c2 = 0;
829          else
830            c2 = *p2++;
831
832          if (char_order[c1] != char_order[c2])
833            return char_order[c1] - char_order[c2];
834          if (!c1)
835            break;
836        }
837
838      /* Strings are equal except possibly for case.  */
839      p1 = start1;
840      p2 = start2;
841      while (1)
842        {
843          int c1, c2;
844
845          if (p1 == e1)
846            c1 = 0;
847          else
848            c1 = *p1++;
849          if (p2 == e2)
850            c2 = 0;
851          else
852            c2 = *p2++;
853
854          if (c1 != c2)
855            /* Reverse sign here so upper case comes out last.  */
856            return c2 - c1;
857          if (!c1)
858            break;
859        }
860
861      return 0;
862    }
863}
864
865/* A `struct linebuffer' is a structure which holds a line of text.
866   `readline' reads a line from a stream into a linebuffer
867   and works regardless of the length of the line.  */
868
869struct linebuffer
870{
871  long size;
872  char *buffer;
873};
874
875/* Initialize LINEBUFFER for use. */
876
877void
878initbuffer (struct linebuffer *linebuffer)
879{
880  linebuffer->size = 200;
881  linebuffer->buffer = (char *) xmalloc (200);
882}
883
884/* Read a line of text from STREAM into LINEBUFFER.
885   Return the length of the line.  */
886
887long
888readline (struct linebuffer *linebuffer, FILE *stream)
889{
890  char *buffer = linebuffer->buffer;
891  char *p = linebuffer->buffer;
892  char *end = p + linebuffer->size;
893
894  while (1)
895    {
896      int c = getc (stream);
897      if (p == end)
898        {
899          buffer = (char *) xrealloc (buffer, linebuffer->size *= 2);
900          p += buffer - linebuffer->buffer;
901          end += buffer - linebuffer->buffer;
902          linebuffer->buffer = buffer;
903        }
904      if (c < 0 || c == '\n')
905        {
906          *p = 0;
907          break;
908        }
909      *p++ = c;
910    }
911
912  return p - buffer;
913}
914
915/* Sort an input file too big to sort in core.  */
916
917void
918sort_offline (char *infile, off_t total, char *outfile)
919{
920  /* More than enough. */
921  int ntemps = 2 * (total + MAX_IN_CORE_SORT - 1) / MAX_IN_CORE_SORT;
922  char **tempfiles = (char **) xmalloc (ntemps * sizeof (char *));
923  FILE *istream = fopen (infile, "r");
924  int i;
925  struct linebuffer lb;
926  long linelength;
927  int failure = 0;
928
929  initbuffer (&lb);
930
931  /* Read in one line of input data.  */
932
933  linelength = readline (&lb, istream);
934
935  if (lb.buffer[0] != '\\' && lb.buffer[0] != '@')
936    {
937      error (_("%s: not a texinfo index file"), infile);
938      return;
939    }
940
941  /* Split up the input into `ntemps' temporary files, or maybe fewer,
942     and put the new files' names into `tempfiles' */
943
944  for (i = 0; i < ntemps; i++)
945    {
946      char *outname = maketempname (++tempcount);
947      FILE *ostream = fopen (outname, "w");
948      long tempsize = 0;
949
950      if (!ostream)
951        pfatal_with_name (outname);
952      tempfiles[i] = outname;
953
954      /* Copy lines into this temp file as long as it does not make file
955         "too big" or until there are no more lines.  */
956
957      while (tempsize + linelength + 1 <= MAX_IN_CORE_SORT)
958        {
959          tempsize += linelength + 1;
960          fputs (lb.buffer, ostream);
961          putc ('\n', ostream);
962
963          /* Read another line of input data.  */
964
965          linelength = readline (&lb, istream);
966          if (!linelength && feof (istream))
967            break;
968
969          if (lb.buffer[0] != '\\' && lb.buffer[0] != '@')
970            {
971              error (_("%s: not a texinfo index file"), infile);
972              failure = 1;
973              goto fail;
974            }
975        }
976      fclose (ostream);
977      if (feof (istream))
978        break;
979    }
980
981  free (lb.buffer);
982
983fail:
984  /* Record number of temp files we actually needed.  */
985
986  ntemps = i;
987
988  /* Sort each tempfile into another tempfile.
989    Delete the first set of tempfiles and put the names of the second
990    into `tempfiles'. */
991
992  for (i = 0; i < ntemps; i++)
993    {
994      char *newtemp = maketempname (++tempcount);
995      sort_in_core (tempfiles[i], MAX_IN_CORE_SORT, newtemp);
996      if (!keep_tempfiles)
997        unlink (tempfiles[i]);
998      tempfiles[i] = newtemp;
999    }
1000
1001  if (failure)
1002    return;
1003
1004  /* Merge the tempfiles together and indexify. */
1005
1006  merge_files (tempfiles, ntemps, outfile);
1007}
1008
1009/* Sort INFILE, whose size is TOTAL,
1010   assuming that is small enough to be done in-core,
1011   then indexify it and send the output to OUTFILE (or to stdout).  */
1012
1013void
1014sort_in_core (char *infile, int total, char *outfile)
1015{
1016  char **nextline;
1017  char *data = (char *) xmalloc (total + 1);
1018  char *file_data;
1019  long file_size;
1020  int i;
1021  FILE *ostream = stdout;
1022  struct lineinfo *lineinfo;
1023
1024  /* Read the contents of the file into the moby array `data'. */
1025
1026  int desc = open (infile, O_RDONLY, 0);
1027
1028  if (desc < 0)
1029    fatal (_("failure reopening %s"), infile);
1030  for (file_size = 0;;)
1031    {
1032      i = read (desc, data + file_size, total - file_size);
1033      if (i <= 0)
1034        break;
1035      file_size += i;
1036    }
1037  file_data = data;
1038  data[file_size] = 0;
1039
1040  close (desc);
1041
1042  if (file_size > 0 && data[0] != '\\' && data[0] != '@')
1043    {
1044      error (_("%s: not a texinfo index file"), infile);
1045      return;
1046    }
1047
1048  init_char_order ();
1049
1050  /* Sort routines want to know this address. */
1051
1052  text_base = data;
1053
1054  /* Create the array of pointers to lines, with a default size
1055     frequently enough.  */
1056
1057  nlines = total / 50;
1058  if (!nlines)
1059    nlines = 2;
1060  linearray = (char **) xmalloc (nlines * sizeof (char *));
1061
1062  /* `nextline' points to the next free slot in this array.
1063     `nlines' is the allocated size.  */
1064
1065  nextline = linearray;
1066
1067  /* Parse the input file's data, and make entries for the lines.  */
1068
1069  nextline = parsefile (infile, nextline, file_data, file_size);
1070  if (nextline == 0)
1071    {
1072      error (_("%s: not a texinfo index file"), infile);
1073      return;
1074    }
1075
1076  /* Sort the lines. */
1077
1078  /* If we have enough space, find the first keyfield of each line in advance.
1079     Make a `struct lineinfo' for each line, which records the keyfield
1080     as well as the line, and sort them.  */
1081
1082  lineinfo = malloc ((nextline - linearray) * sizeof (struct lineinfo));
1083
1084  if (lineinfo)
1085    {
1086      struct lineinfo *lp;
1087      char **p;
1088
1089      for (lp = lineinfo, p = linearray; p != nextline; lp++, p++)
1090        {
1091          lp->text = *p;
1092          lp->key.text = find_field (keyfields, *p, &lp->keylen);
1093          if (keyfields->numeric)
1094            lp->key.number = find_value (lp->key.text, lp->keylen);
1095        }
1096
1097      qsort (lineinfo, nextline - linearray, sizeof (struct lineinfo),
1098             compare_prepared);
1099
1100      for (lp = lineinfo, p = linearray; p != nextline; lp++, p++)
1101        *p = lp->text;
1102
1103      free (lineinfo);
1104    }
1105  else
1106    qsort (linearray, nextline - linearray, sizeof (char *), compare_full);
1107
1108  /* Open the output file. */
1109
1110  if (outfile)
1111    {
1112      ostream = fopen (outfile, "w");
1113      if (!ostream)
1114        pfatal_with_name (outfile);
1115    }
1116
1117  writelines (linearray, nextline - linearray, ostream);
1118  if (outfile)
1119    fclose (ostream);
1120
1121  free (linearray);
1122  free (data);
1123}
1124
1125/* Parse an input string in core into lines.
1126   DATA is the input string, and SIZE is its length.
1127   Data goes in LINEARRAY starting at NEXTLINE.
1128   The value returned is the first entry in LINEARRAY still unused.
1129   Value 0 means input file contents are invalid.  */
1130
1131char **
1132parsefile (char *filename, char **nextline, char *data, long int size)
1133{
1134  char *p, *end;
1135  char **line = nextline;
1136
1137  p = data;
1138  end = p + size;
1139  *end = 0;
1140
1141  while (p != end)
1142    {
1143      if (p[0] != '\\' && p[0] != '@')
1144        return 0;
1145
1146      *line = p;
1147
1148      /* Find the first letter of the first field of this line.  If it
1149         is different from the first letter of the first field of the
1150         first line, we need initial headers in the output index.  */
1151      while (*p && *p != '{')
1152        p++;
1153      if (p == end)
1154        return 0;
1155      p++;
1156      if (first_initial)
1157        {
1158          if (first_initial != toupper (*p))
1159            need_initials = 1;
1160        }
1161      else
1162        first_initial = toupper (*p);
1163
1164      while (*p && *p != '\n')
1165        p++;
1166      if (p != end)
1167        p++;
1168
1169      line++;
1170      if (line == linearray + nlines)
1171        {
1172          char **old = linearray;
1173          linearray = xrealloc (linearray, sizeof (char *) * (nlines *= 4));
1174          line += linearray - old;
1175        }
1176    }
1177
1178  return line;
1179}
1180
1181/* Indexification is a filter applied to the sorted lines
1182   as they are being written to the output file.
1183   Multiple entries for the same name, with different page numbers,
1184   get combined into a single entry with multiple page numbers.
1185   The first braced field, which is used for sorting, is discarded.
1186   However, its first character is examined, folded to lower case,
1187   and if it is different from that in the previous line fed to us
1188   a \initial line is written with one argument, the new initial.
1189
1190   If an entry has four braced fields, then the second and third
1191   constitute primary and secondary names.
1192   In this case, each change of primary name
1193   generates a \primary line which contains only the primary name,
1194   and in between these are \secondary lines which contain
1195   just a secondary name and page numbers. */
1196
1197/* The last primary name we wrote a \primary entry for.
1198   If only one level of indexing is being done, this is the last name seen. */
1199char *lastprimary;
1200/* Length of storage allocated for lastprimary. */
1201int lastprimarylength;
1202
1203/* Similar, for the secondary name. */
1204char *lastsecondary;
1205int lastsecondarylength;
1206
1207/* Zero if we are not in the middle of writing an entry.
1208   One if we have written the beginning of an entry but have not
1209   yet written any page numbers into it.
1210   Greater than one if we have written the beginning of an entry
1211   plus at least one page number. */
1212int pending;
1213
1214/* The initial (for sorting purposes) of the last primary entry written.
1215   When this changes, a \initial {c} line is written */
1216
1217char *lastinitial;
1218
1219int lastinitiallength;
1220
1221/* When we need a string of length 1 for the value of lastinitial,
1222   store it here.  */
1223
1224char lastinitial1[2];
1225
1226/* Initialize static storage for writing an index. */
1227
1228void
1229init_index (void)
1230{
1231  pending = 0;
1232  lastinitial = lastinitial1;
1233  lastinitial1[0] = 0;
1234  lastinitial1[1] = 0;
1235  lastinitiallength = 0;
1236  lastprimarylength = 100;
1237  lastprimary = (char *) xmalloc (lastprimarylength + 1);
1238  memset (lastprimary, '\0', lastprimarylength + 1);
1239  lastsecondarylength = 100;
1240  lastsecondary = (char *) xmalloc (lastsecondarylength + 1);
1241  memset (lastsecondary, '\0', lastsecondarylength + 1);
1242}
1243
1244/* Indexify.  Merge entries for the same name,
1245   insert headers for each initial character, etc.  */
1246
1247void
1248indexify (char *line, FILE *ostream)
1249{
1250  char *primary, *secondary, *pagenumber;
1251  int primarylength, secondarylength = 0, pagelength;
1252  int nosecondary;
1253  int initiallength;
1254  char *initial;
1255  char initial1[2];
1256  register char *p;
1257
1258  /* First, analyze the parts of the entry fed to us this time. */
1259
1260  p = find_braced_pos (line, 0, 0, 0);
1261  if (*p == '{')
1262    {
1263      initial = p;
1264      /* Get length of inner pair of braces starting at `p',
1265         including that inner pair of braces.  */
1266      initiallength = find_braced_end (p + 1) + 1 - p;
1267    }
1268  else
1269    {
1270      initial = initial1;
1271      initial1[0] = toupper (*p);
1272      initial1[1] = 0;
1273      initiallength = 1;
1274    }
1275
1276  pagenumber = find_braced_pos (line, 1, 0, 0);
1277  pagelength = find_braced_end (pagenumber) - pagenumber;
1278  if (pagelength == 0)
1279    fatal (_("No page number in %s"), line);
1280
1281  primary = find_braced_pos (line, 2, 0, 0);
1282  primarylength = find_braced_end (primary) - primary;
1283
1284  secondary = find_braced_pos (line, 3, 0, 0);
1285  nosecondary = !*secondary;
1286  if (!nosecondary)
1287    secondarylength = find_braced_end (secondary) - secondary;
1288
1289  /* If the primary is different from before, make a new primary entry. */
1290  if (strncmp (primary, lastprimary, primarylength))
1291    {
1292      /* Close off current secondary entry first, if one is open. */
1293      if (pending)
1294        {
1295          fputs ("}\n", ostream);
1296          pending = 0;
1297        }
1298
1299      /* If this primary has a different initial, include an entry for
1300         the initial. */
1301      if (need_initials &&
1302          (initiallength != lastinitiallength ||
1303           strncmp (initial, lastinitial, initiallength)))
1304        {
1305          fprintf (ostream, "\\initial {");
1306          fwrite (initial, 1, initiallength, ostream);
1307          fputs ("}\n", ostream);
1308          if (initial == initial1)
1309            {
1310              lastinitial = lastinitial1;
1311              *lastinitial1 = *initial1;
1312            }
1313          else
1314            {
1315              lastinitial = initial;
1316            }
1317          lastinitiallength = initiallength;
1318        }
1319
1320      /* Make the entry for the primary.  */
1321      if (nosecondary)
1322        fputs ("\\entry {", ostream);
1323      else
1324        fputs ("\\primary {", ostream);
1325      fwrite (primary, primarylength, 1, ostream);
1326      if (nosecondary)
1327        {
1328          fputs ("}{", ostream);
1329          pending = 1;
1330        }
1331      else
1332        fputs ("}\n", ostream);
1333
1334      /* Record name of most recent primary. */
1335      if (lastprimarylength < primarylength)
1336        {
1337          lastprimarylength = primarylength + 100;
1338          lastprimary = (char *) xrealloc (lastprimary,
1339                                           1 + lastprimarylength);
1340        }
1341      strncpy (lastprimary, primary, primarylength);
1342      lastprimary[primarylength] = 0;
1343
1344      /* There is no current secondary within this primary, now. */
1345      lastsecondary[0] = 0;
1346    }
1347
1348  /* Should not have an entry with no subtopic following one with a
1349     subtopic. */
1350
1351  if (nosecondary && *lastsecondary)
1352    error (_("entry %s follows an entry with a secondary name"), line);
1353
1354  /* Start a new secondary entry if necessary. */
1355  if (!nosecondary && strncmp (secondary, lastsecondary, secondarylength))
1356    {
1357      if (pending)
1358        {
1359          fputs ("}\n", ostream);
1360          pending = 0;
1361        }
1362
1363      /* Write the entry for the secondary.  */
1364      fputs ("\\secondary {", ostream);
1365      fwrite (secondary, secondarylength, 1, ostream);
1366      fputs ("}{", ostream);
1367      pending = 1;
1368
1369      /* Record name of most recent secondary. */
1370      if (lastsecondarylength < secondarylength)
1371        {
1372          lastsecondarylength = secondarylength + 100;
1373          lastsecondary = (char *) xrealloc (lastsecondary,
1374                                             1 + lastsecondarylength);
1375        }
1376      strncpy (lastsecondary, secondary, secondarylength);
1377      lastsecondary[secondarylength] = 0;
1378    }
1379
1380  /* Here to add one more page number to the current entry. */
1381  if (pending++ != 1)
1382    fputs (", ", ostream);  /* Punctuate first, if this is not the first. */
1383  fwrite (pagenumber, pagelength, 1, ostream);
1384}
1385
1386/* Close out any unfinished output entry. */
1387
1388void
1389finish_index (FILE *ostream)
1390{
1391  if (pending)
1392    fputs ("}\n", ostream);
1393  free (lastprimary);
1394  free (lastsecondary);
1395}
1396
1397/* Copy the lines in the sorted order.
1398   Each line is copied out of the input file it was found in. */
1399
1400void
1401writelines (char **linearray, int nlines, FILE *ostream)
1402{
1403  char **stop_line = linearray + nlines;
1404  char **next_line;
1405
1406  init_index ();
1407
1408  /* Output the text of the lines, and free the buffer space. */
1409
1410  for (next_line = linearray; next_line != stop_line; next_line++)
1411    {
1412      /* If -u was specified, output the line only if distinct from
1413         previous one.  */
1414      if (next_line == linearray
1415      /* Compare previous line with this one, using only the
1416         explicitly specd keyfields. */
1417          || compare_general (*(next_line - 1), *next_line, 0L, 0L,
1418                              num_keyfields - 1))
1419        {
1420          char *p = *next_line;
1421          char c;
1422
1423          while ((c = *p++) && c != '\n')
1424            /* Do nothing. */ ;
1425          *(p - 1) = 0;
1426          indexify (*next_line, ostream);
1427        }
1428    }
1429
1430  finish_index (ostream);
1431}
1432
1433/* Assume (and optionally verify) that each input file is sorted;
1434   merge them and output the result.
1435   Returns nonzero if any input file fails to be sorted.
1436
1437   This is the high-level interface that can handle an unlimited
1438   number of files.  */
1439
1440#define MAX_DIRECT_MERGE 10
1441
1442int
1443merge_files (char **infiles, int nfiles, char *outfile)
1444{
1445  char **tempfiles;
1446  int ntemps;
1447  int i;
1448  int value = 0;
1449  int start_tempcount = tempcount;
1450
1451  if (nfiles <= MAX_DIRECT_MERGE)
1452    return merge_direct (infiles, nfiles, outfile);
1453
1454  /* Merge groups of MAX_DIRECT_MERGE input files at a time,
1455     making a temporary file to hold each group's result.  */
1456
1457  ntemps = (nfiles + MAX_DIRECT_MERGE - 1) / MAX_DIRECT_MERGE;
1458  tempfiles = (char **) xmalloc (ntemps * sizeof (char *));
1459  for (i = 0; i < ntemps; i++)
1460    {
1461      int nf = MAX_DIRECT_MERGE;
1462      if (i + 1 == ntemps)
1463        nf = nfiles - i * MAX_DIRECT_MERGE;
1464      tempfiles[i] = maketempname (++tempcount);
1465      value |= merge_direct (&infiles[i * MAX_DIRECT_MERGE], nf, tempfiles[i]);
1466    }
1467
1468  /* All temporary files that existed before are no longer needed
1469     since their contents have been merged into our new tempfiles.
1470     So delete them.  */
1471  flush_tempfiles (start_tempcount);
1472
1473  /* Now merge the temporary files we created.  */
1474
1475  merge_files (tempfiles, ntemps, outfile);
1476
1477  free (tempfiles);
1478
1479  return value;
1480}
1481
1482/* Assume (and optionally verify) that each input file is sorted;
1483   merge them and output the result.
1484   Returns nonzero if any input file fails to be sorted.
1485
1486   This version of merging will not work if the number of
1487   input files gets too high.  Higher level functions
1488   use it only with a bounded number of input files.  */
1489
1490int
1491merge_direct (char **infiles, int nfiles, char *outfile)
1492{
1493  struct linebuffer *lb1, *lb2;
1494  struct linebuffer **thisline, **prevline;
1495  FILE **streams;
1496  int i;
1497  int nleft;
1498  int lossage = 0;
1499  int *file_lossage;
1500  struct linebuffer *prev_out = 0;
1501  FILE *ostream = stdout;
1502
1503  if (outfile)
1504    {
1505      ostream = fopen (outfile, "w");
1506    }
1507  if (!ostream)
1508    pfatal_with_name (outfile);
1509
1510  init_index ();
1511
1512  if (nfiles == 0)
1513    {
1514      if (outfile)
1515        fclose (ostream);
1516      return 0;
1517    }
1518
1519  /* For each file, make two line buffers.  Also, for each file, there
1520     is an element of `thisline' which points at any time to one of the
1521     file's two buffers, and an element of `prevline' which points to
1522     the other buffer.  `thisline' is supposed to point to the next
1523     available line from the file, while `prevline' holds the last file
1524     line used, which is remembered so that we can verify that the file
1525     is properly sorted. */
1526
1527  /* lb1 and lb2 contain one buffer each per file. */
1528  lb1 = (struct linebuffer *) xmalloc (nfiles * sizeof (struct linebuffer));
1529  lb2 = (struct linebuffer *) xmalloc (nfiles * sizeof (struct linebuffer));
1530
1531  /* thisline[i] points to the linebuffer holding the next available
1532     line in file i, or is zero if there are no lines left in that file.  */
1533  thisline = (struct linebuffer **)
1534    xmalloc (nfiles * sizeof (struct linebuffer *));
1535  /* prevline[i] points to the linebuffer holding the last used line
1536     from file i.  This is just for verifying that file i is properly
1537     sorted.  */
1538  prevline = (struct linebuffer **)
1539    xmalloc (nfiles * sizeof (struct linebuffer *));
1540  /* streams[i] holds the input stream for file i.  */
1541  streams = (FILE **) xmalloc (nfiles * sizeof (FILE *));
1542  /* file_lossage[i] is nonzero if we already know file i is not
1543     properly sorted.  */
1544  file_lossage = (int *) xmalloc (nfiles * sizeof (int));
1545
1546  /* Allocate and initialize all that storage. */
1547
1548  for (i = 0; i < nfiles; i++)
1549    {
1550      initbuffer (&lb1[i]);
1551      initbuffer (&lb2[i]);
1552      thisline[i] = &lb1[i];
1553      prevline[i] = &lb2[i];
1554      file_lossage[i] = 0;
1555      streams[i] = fopen (infiles[i], "r");
1556      if (!streams[i])
1557        pfatal_with_name (infiles[i]);
1558
1559      readline (thisline[i], streams[i]);
1560    }
1561
1562  /* Keep count of number of files not at eof. */
1563  nleft = nfiles;
1564
1565  while (nleft)
1566    {
1567      struct linebuffer *best = 0;
1568      struct linebuffer *exch;
1569      int bestfile = -1;
1570      int i;
1571
1572      /* Look at the next avail line of each file; choose the least one.  */
1573
1574      for (i = 0; i < nfiles; i++)
1575        {
1576          if (thisline[i] &&
1577              (!best ||
1578               0 < compare_general (best->buffer, thisline[i]->buffer,
1579                                 (long) bestfile, (long) i, num_keyfields)))
1580            {
1581              best = thisline[i];
1582              bestfile = i;
1583            }
1584        }
1585
1586      /* Output that line, unless it matches the previous one and we
1587         don't want duplicates. */
1588
1589      if (!(prev_out &&
1590            !compare_general (prev_out->buffer,
1591                              best->buffer, 0L, 1L, num_keyfields - 1)))
1592        indexify (best->buffer, ostream);
1593      prev_out = best;
1594
1595      /* Now make the line the previous of its file, and fetch a new
1596         line from that file.  */
1597
1598      exch = prevline[bestfile];
1599      prevline[bestfile] = thisline[bestfile];
1600      thisline[bestfile] = exch;
1601
1602      while (1)
1603        {
1604          /* If the file has no more, mark it empty. */
1605
1606          if (feof (streams[bestfile]))
1607            {
1608              thisline[bestfile] = 0;
1609              /* Update the number of files still not empty. */
1610              nleft--;
1611              break;
1612            }
1613          readline (thisline[bestfile], streams[bestfile]);
1614          if (thisline[bestfile]->buffer[0] || !feof (streams[bestfile]))
1615            break;
1616        }
1617    }
1618
1619  finish_index (ostream);
1620
1621  /* Free all storage and close all input streams. */
1622
1623  for (i = 0; i < nfiles; i++)
1624    {
1625      fclose (streams[i]);
1626      free (lb1[i].buffer);
1627      free (lb2[i].buffer);
1628    }
1629  free (file_lossage);
1630  free (lb1);
1631  free (lb2);
1632  free (thisline);
1633  free (prevline);
1634  free (streams);
1635
1636  if (outfile)
1637    fclose (ostream);
1638
1639  return lossage;
1640}
1641
1642/* Print error message and exit.  */
1643
1644void
1645fatal (const char *format, const char *arg)
1646{
1647  error (format, arg);
1648  xexit (1);
1649}
1650
1651/* Print error message.  FORMAT is printf control string, ARG is arg for it. */
1652void
1653error (const char *format, const char *arg)
1654{
1655  printf ("%s: ", program_name);
1656  printf (format, arg);
1657  if (format[strlen (format) -1] != '\n')
1658    printf ("\n");
1659}
1660
1661void
1662perror_with_name (const char *name)
1663{
1664  fprintf (stderr, "%s: ", program_name);
1665  perror (name);
1666}
1667
1668void
1669pfatal_with_name (const char *name)
1670{
1671  perror_with_name (name);
1672  xexit (1);
1673}
1674
1675
1676/* Return a newly-allocated string concatenating S1 and S2.  */
1677
1678char *
1679concat (char *s1, char *s2)
1680{
1681  int len1 = strlen (s1), len2 = strlen (s2);
1682  char *result = (char *) xmalloc (len1 + len2 + 1);
1683
1684  strcpy (result, s1);
1685  strcpy (result + len1, s2);
1686  *(result + len1 + len2) = 0;
1687
1688  return result;
1689}
1690