7851b31606188fde109f2faa3cb6b4efc38116aa
[platform/upstream/coreutils.git] / src / join.c
1 /* join - join lines of two files on a common field
2    Copyright (C) 91, 1995-2006 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18    Written by Mike Haertel, mike@gnu.ai.mit.edu.  */
19
20 #include <config.h>
21
22 #include <assert.h>
23 #include <sys/types.h>
24 #include <getopt.h>
25
26 #include "system.h"
27 #include "error.h"
28 #include "hard-locale.h"
29 #include "linebuffer.h"
30 #include "memcasecmp.h"
31 #include "quote.h"
32 #include "stdio--.h"
33 #include "xmemcoll.h"
34 #include "xstrtol.h"
35
36 /* The official name of this program (e.g., no `g' prefix).  */
37 #define PROGRAM_NAME "join"
38
39 #define AUTHORS "Mike Haertel"
40
41 #define join system_join
42
43 /* An element of the list identifying which fields to print for each
44    output line.  */
45 struct outlist
46   {
47     /* File number: 0, 1, or 2.  0 means use the join field.
48        1 means use the first file argument, 2 the second.  */
49     int file;
50
51     /* Field index (zero-based), specified only when FILE is 1 or 2.  */
52     size_t field;
53
54     struct outlist *next;
55   };
56
57 /* A field of a line.  */
58 struct field
59   {
60     char *beg;                  /* First character in field.  */
61     size_t len;                 /* The length of the field.  */
62   };
63
64 /* A line read from an input file.  */
65 struct line
66   {
67     struct linebuffer buf;      /* The line itself.  */
68     size_t nfields;             /* Number of elements in `fields'.  */
69     size_t nfields_allocated;   /* Number of elements allocated for `fields'. */
70     struct field *fields;
71   };
72
73 /* One or more consecutive lines read from a file that all have the
74    same join field value.  */
75 struct seq
76   {
77     size_t count;                       /* Elements used in `lines'.  */
78     size_t alloc;                       /* Elements allocated in `lines'.  */
79     struct line *lines;
80   };
81
82 /* The name this program was run with.  */
83 char *program_name;
84
85 /* True if the LC_COLLATE locale is hard.  */
86 static bool hard_LC_COLLATE;
87
88 /* If nonzero, print unpairable lines in file 1 or 2.  */
89 static bool print_unpairables_1, print_unpairables_2;
90
91 /* If nonzero, print pairable lines.  */
92 static bool print_pairables;
93
94 /* Empty output field filler.  */
95 static char const *empty_filler;
96
97 /* Field to join on; SIZE_MAX means they haven't been determined yet.  */
98 static size_t join_field_1 = SIZE_MAX;
99 static size_t join_field_2 = SIZE_MAX;
100
101 /* List of fields to print.  */
102 static struct outlist outlist_head;
103
104 /* Last element in `outlist', where a new element can be added.  */
105 static struct outlist *outlist_end = &outlist_head;
106
107 /* Tab character separating fields.  If negative, fields are separated
108    by any nonempty string of blanks, otherwise by exactly one
109    tab character whose value (when cast to unsigned char) equals TAB.  */
110 static int tab = -1;
111
112 static struct option const longopts[] =
113 {
114   {"ignore-case", no_argument, NULL, 'i'},
115   {GETOPT_HELP_OPTION_DECL},
116   {GETOPT_VERSION_OPTION_DECL},
117   {NULL, 0, NULL, 0}
118 };
119
120 /* Used to print non-joining lines */
121 static struct line uni_blank;
122
123 /* If nonzero, ignore case when comparing join fields.  */
124 static bool ignore_case;
125
126 void
127 usage (int status)
128 {
129   if (status != EXIT_SUCCESS)
130     fprintf (stderr, _("Try `%s --help' for more information.\n"),
131              program_name);
132   else
133     {
134       printf (_("\
135 Usage: %s [OPTION]... FILE1 FILE2\n\
136 "),
137               program_name);
138       fputs (_("\
139 For each pair of input lines with identical join fields, write a line to\n\
140 standard output.  The default join field is the first, delimited\n\
141 by whitespace.  When FILE1 or FILE2 (not both) is -, read standard input.\n\
142 \n\
143   -a FILENUM        print unpairable lines coming from file FILENUM, where\n\
144                       FILENUM is 1 or 2, corresponding to FILE1 or FILE2\n\
145   -e EMPTY          replace missing input fields with EMPTY\n\
146 "), stdout);
147       fputs (_("\
148   -i, --ignore-case  ignore differences in case when comparing fields\n\
149   -j FIELD          equivalent to `-1 FIELD -2 FIELD'\n\
150   -o FORMAT         obey FORMAT while constructing output line\n\
151   -t CHAR           use CHAR as input and output field separator\n\
152 "), stdout);
153       fputs (_("\
154   -v FILENUM        like -a FILENUM, but suppress joined output lines\n\
155   -1 FIELD          join on this FIELD of file 1\n\
156   -2 FIELD          join on this FIELD of file 2\n\
157 "), stdout);
158       fputs (HELP_OPTION_DESCRIPTION, stdout);
159       fputs (VERSION_OPTION_DESCRIPTION, stdout);
160       fputs (_("\
161 \n\
162 Unless -t CHAR is given, leading blanks separate fields and are ignored,\n\
163 else fields are separated by CHAR.  Any FIELD is a field number counted\n\
164 from 1.  FORMAT is one or more comma or blank separated specifications,\n\
165 each being `FILENUM.FIELD' or `0'.  Default FORMAT outputs the join field,\n\
166 the remaining fields from FILE1, the remaining fields from FILE2, all\n\
167 separated by CHAR.\n\
168 \n\
169 Important: FILE1 and FILE2 must be sorted on the join fields.\n\
170 E.g., use `sort -k 1b,1' if `join' has no options.\n\
171 "), stdout);
172       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
173     }
174   exit (status);
175 }
176
177 /* Return true if C is a blank (a default input field separator).  */
178
179 static inline bool
180 is_blank (unsigned char c)
181 {
182   return ISBLANK (c) != 0;
183 }
184
185 /* Record a field in LINE, with location FIELD and size LEN.  */
186
187 static void
188 extract_field (struct line *line, char *field, size_t len)
189 {
190   if (line->nfields >= line->nfields_allocated)
191     {
192       line->fields = X2NREALLOC (line->fields, &line->nfields_allocated);
193     }
194   line->fields[line->nfields].beg = field;
195   line->fields[line->nfields].len = len;
196   ++(line->nfields);
197 }
198
199 /* Fill in the `fields' structure in LINE.  */
200
201 static void
202 xfields (struct line *line)
203 {
204   char *ptr = line->buf.buffer;
205   char const *lim = ptr + line->buf.length - 1;
206
207   if (ptr == lim)
208     return;
209
210   if (0 <= tab)
211     {
212       char *sep;
213       for (; (sep = memchr (ptr, tab, lim - ptr)) != NULL; ptr = sep + 1)
214         extract_field (line, ptr, sep - ptr);
215     }
216   else
217     {
218       /* Skip leading blanks before the first field.  */
219       while (is_blank (*ptr))
220         if (++ptr == lim)
221           return;
222
223       do
224         {
225           char *sep;
226           for (sep = ptr + 1; sep != lim && ! is_blank (*sep); sep++)
227             continue;
228           extract_field (line, ptr, sep - ptr);
229           if (sep == lim)
230             return;
231           for (ptr = sep + 1; ptr != lim && is_blank (*ptr); ptr++)
232             continue;
233         }
234       while (ptr != lim);
235     }
236
237   extract_field (line, ptr, lim - ptr);
238 }
239
240 /* Read a line from FP into LINE and split it into fields.
241    Return true if successful.  */
242
243 static bool
244 get_line (FILE *fp, struct line *line)
245 {
246   initbuffer (&line->buf);
247
248   if (! readlinebuffer (&line->buf, fp))
249     {
250       if (ferror (fp))
251         error (EXIT_FAILURE, errno, _("read error"));
252       free (line->buf.buffer);
253       line->buf.buffer = NULL;
254       return false;
255     }
256
257   line->nfields_allocated = 0;
258   line->nfields = 0;
259   line->fields = NULL;
260   xfields (line);
261   return true;
262 }
263
264 static void
265 freeline (struct line *line)
266 {
267   free (line->fields);
268   free (line->buf.buffer);
269   line->buf.buffer = NULL;
270 }
271
272 static void
273 initseq (struct seq *seq)
274 {
275   seq->count = 0;
276   seq->alloc = 0;
277   seq->lines = NULL;
278 }
279
280 /* Read a line from FP and add it to SEQ.  Return true if successful.  */
281
282 static bool
283 getseq (FILE *fp, struct seq *seq)
284 {
285   if (seq->count == seq->alloc)
286     seq->lines = X2NREALLOC (seq->lines, &seq->alloc);
287
288   if (get_line (fp, &seq->lines[seq->count]))
289     {
290       ++seq->count;
291       return true;
292     }
293   return false;
294 }
295
296 static void
297 delseq (struct seq *seq)
298 {
299   size_t i;
300   for (i = 0; i < seq->count; i++)
301     if (seq->lines[i].buf.buffer)
302       freeline (&seq->lines[i]);
303   free (seq->lines);
304 }
305
306 /* Return <0 if the join field in LINE1 compares less than the one in LINE2;
307    >0 if it compares greater; 0 if it compares equal.
308    Report an error and exit if the comparison fails.  */
309
310 static int
311 keycmp (struct line const *line1, struct line const *line2)
312 {
313   /* Start of field to compare in each file.  */
314   char *beg1;
315   char *beg2;
316
317   size_t len1;
318   size_t len2;          /* Length of fields to compare.  */
319   int diff;
320
321   if (join_field_1 < line1->nfields)
322     {
323       beg1 = line1->fields[join_field_1].beg;
324       len1 = line1->fields[join_field_1].len;
325     }
326   else
327     {
328       beg1 = NULL;
329       len1 = 0;
330     }
331
332   if (join_field_2 < line2->nfields)
333     {
334       beg2 = line2->fields[join_field_2].beg;
335       len2 = line2->fields[join_field_2].len;
336     }
337   else
338     {
339       beg2 = NULL;
340       len2 = 0;
341     }
342
343   if (len1 == 0)
344     return len2 == 0 ? 0 : -1;
345   if (len2 == 0)
346     return 1;
347
348   if (ignore_case)
349     {
350       /* FIXME: ignore_case does not work with NLS (in particular,
351          with multibyte chars).  */
352       diff = memcasecmp (beg1, beg2, MIN (len1, len2));
353     }
354   else
355     {
356       if (hard_LC_COLLATE)
357         return xmemcoll (beg1, len1, beg2, len2);
358       diff = memcmp (beg1, beg2, MIN (len1, len2));
359     }
360
361   if (diff)
362     return diff;
363   return len1 < len2 ? -1 : len1 != len2;
364 }
365
366 /* Print field N of LINE if it exists and is nonempty, otherwise
367    `empty_filler' if it is nonempty.  */
368
369 static void
370 prfield (size_t n, struct line const *line)
371 {
372   size_t len;
373
374   if (n < line->nfields)
375     {
376       len = line->fields[n].len;
377       if (len)
378         fwrite (line->fields[n].beg, 1, len, stdout);
379       else if (empty_filler)
380         fputs (empty_filler, stdout);
381     }
382   else if (empty_filler)
383     fputs (empty_filler, stdout);
384 }
385
386 /* Print the join of LINE1 and LINE2.  */
387
388 static void
389 prjoin (struct line const *line1, struct line const *line2)
390 {
391   const struct outlist *outlist;
392   char output_separator = tab < 0 ? ' ' : tab;
393
394   outlist = outlist_head.next;
395   if (outlist)
396     {
397       const struct outlist *o;
398
399       o = outlist;
400       while (1)
401         {
402           size_t field;
403           struct line const *line;
404
405           if (o->file == 0)
406             {
407               if (line1 == &uni_blank)
408                 {
409                   line = line2;
410                   field = join_field_2;
411                 }
412               else
413                 {
414                   line = line1;
415                   field = join_field_1;
416                 }
417             }
418           else
419             {
420               line = (o->file == 1 ? line1 : line2);
421               field = o->field;
422             }
423           prfield (field, line);
424           o = o->next;
425           if (o == NULL)
426             break;
427           putchar (output_separator);
428         }
429       putchar ('\n');
430     }
431   else
432     {
433       size_t i;
434
435       if (line1 == &uni_blank)
436         {
437           struct line const *t;
438           t = line1;
439           line1 = line2;
440           line2 = t;
441         }
442       prfield (join_field_1, line1);
443       for (i = 0; i < join_field_1 && i < line1->nfields; ++i)
444         {
445           putchar (output_separator);
446           prfield (i, line1);
447         }
448       for (i = join_field_1 + 1; i < line1->nfields; ++i)
449         {
450           putchar (output_separator);
451           prfield (i, line1);
452         }
453
454       for (i = 0; i < join_field_2 && i < line2->nfields; ++i)
455         {
456           putchar (output_separator);
457           prfield (i, line2);
458         }
459       for (i = join_field_2 + 1; i < line2->nfields; ++i)
460         {
461           putchar (output_separator);
462           prfield (i, line2);
463         }
464       putchar ('\n');
465     }
466 }
467
468 /* Print the join of the files in FP1 and FP2.  */
469
470 static void
471 join (FILE *fp1, FILE *fp2)
472 {
473   struct seq seq1, seq2;
474   struct line line;
475   int diff;
476   bool eof1, eof2;
477
478   /* Read the first line of each file.  */
479   initseq (&seq1);
480   getseq (fp1, &seq1);
481   initseq (&seq2);
482   getseq (fp2, &seq2);
483
484   while (seq1.count && seq2.count)
485     {
486       size_t i;
487       diff = keycmp (&seq1.lines[0], &seq2.lines[0]);
488       if (diff < 0)
489         {
490           if (print_unpairables_1)
491             prjoin (&seq1.lines[0], &uni_blank);
492           freeline (&seq1.lines[0]);
493           seq1.count = 0;
494           getseq (fp1, &seq1);
495           continue;
496         }
497       if (diff > 0)
498         {
499           if (print_unpairables_2)
500             prjoin (&uni_blank, &seq2.lines[0]);
501           freeline (&seq2.lines[0]);
502           seq2.count = 0;
503           getseq (fp2, &seq2);
504           continue;
505         }
506
507       /* Keep reading lines from file1 as long as they continue to
508          match the current line from file2.  */
509       eof1 = false;
510       do
511         if (!getseq (fp1, &seq1))
512           {
513             eof1 = true;
514             ++seq1.count;
515             break;
516           }
517       while (!keycmp (&seq1.lines[seq1.count - 1], &seq2.lines[0]));
518
519       /* Keep reading lines from file2 as long as they continue to
520          match the current line from file1.  */
521       eof2 = false;
522       do
523         if (!getseq (fp2, &seq2))
524           {
525             eof2 = true;
526             ++seq2.count;
527             break;
528           }
529       while (!keycmp (&seq1.lines[0], &seq2.lines[seq2.count - 1]));
530
531       if (print_pairables)
532         {
533           for (i = 0; i < seq1.count - 1; ++i)
534             {
535               size_t j;
536               for (j = 0; j < seq2.count - 1; ++j)
537                 prjoin (&seq1.lines[i], &seq2.lines[j]);
538             }
539         }
540
541       for (i = 0; i < seq1.count - 1; ++i)
542         freeline (&seq1.lines[i]);
543       if (!eof1)
544         {
545           seq1.lines[0] = seq1.lines[seq1.count - 1];
546           seq1.count = 1;
547         }
548       else
549         seq1.count = 0;
550
551       for (i = 0; i < seq2.count - 1; ++i)
552         freeline (&seq2.lines[i]);
553       if (!eof2)
554         {
555           seq2.lines[0] = seq2.lines[seq2.count - 1];
556           seq2.count = 1;
557         }
558       else
559         seq2.count = 0;
560     }
561
562   if (print_unpairables_1 && seq1.count)
563     {
564       prjoin (&seq1.lines[0], &uni_blank);
565       freeline (&seq1.lines[0]);
566       while (get_line (fp1, &line))
567         {
568           prjoin (&line, &uni_blank);
569           freeline (&line);
570         }
571     }
572
573   if (print_unpairables_2 && seq2.count)
574     {
575       prjoin (&uni_blank, &seq2.lines[0]);
576       freeline (&seq2.lines[0]);
577       while (get_line (fp2, &line))
578         {
579           prjoin (&uni_blank, &line);
580           freeline (&line);
581         }
582     }
583
584   delseq (&seq1);
585   delseq (&seq2);
586 }
587
588 /* Add a field spec for field FIELD of file FILE to `outlist'.  */
589
590 static void
591 add_field (int file, size_t field)
592 {
593   struct outlist *o;
594
595   assert (file == 0 || file == 1 || file == 2);
596   assert (file != 0 || field == 0);
597
598   o = xmalloc (sizeof *o);
599   o->file = file;
600   o->field = field;
601   o->next = NULL;
602
603   /* Add to the end of the list so the fields are in the right order.  */
604   outlist_end->next = o;
605   outlist_end = o;
606 }
607
608 /* Convert a string of decimal digits, STR (the 1-based join field number),
609    to an integral value.  Upon successful conversion, return one less
610    (the zero-based field number).  If it cannot be converted, give a
611    diagnostic and exit.  */
612
613 static size_t
614 string_to_join_field (char const *str)
615 {
616   size_t result;
617   unsigned long int val;
618
619   strtol_error s_err = xstrtoul (str, NULL, 10, &val, "");
620   if (s_err == LONGINT_OVERFLOW || (s_err == LONGINT_OK && SIZE_MAX < val))
621     {
622       error (EXIT_FAILURE, 0,
623              _("value %s is so large that it is not representable"),
624              quote (str));
625     }
626
627   if (s_err != LONGINT_OK || val == 0)
628     error (EXIT_FAILURE, 0, _("invalid field number: %s"), quote (str));
629
630   result = val - 1;
631
632   return result;
633 }
634
635 /* Convert a single field specifier string, S, to a *FILE_INDEX, *FIELD_INDEX
636    pair.  In S, the field index string is 1-based; *FIELD_INDEX is zero-based.
637    If S is valid, return true.  Otherwise, give a diagnostic and exit.  */
638
639 static void
640 decode_field_spec (const char *s, int *file_index, size_t *field_index)
641 {
642   /* The first character must be 0, 1, or 2.  */
643   switch (s[0])
644     {
645     case '0':
646       if (s[1])
647         {
648           /* `0' must be all alone -- no `.FIELD'.  */
649           error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
650         }
651       *file_index = 0;
652       *field_index = 0;
653       break;
654
655     case '1':
656     case '2':
657       if (s[1] != '.')
658         error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
659       *file_index = s[0] - '0';
660       *field_index = string_to_join_field (s + 2);
661       break;
662
663     default:
664       error (EXIT_FAILURE, 0,
665              _("invalid file number in field spec: %s"), quote (s));
666
667       /* Tell gcc -W -Wall that we can't get beyond this point.
668          This avoids a warning (otherwise legit) that the caller's copies
669          of *file_index and *field_index might be used uninitialized.  */
670       abort ();
671
672       break;
673     }
674 }
675
676 /* Add the comma or blank separated field spec(s) in STR to `outlist'.  */
677
678 static void
679 add_field_list (char *str)
680 {
681   char *p = str;
682
683   do
684     {
685       int file_index;
686       size_t field_index;
687       char const *spec_item = p;
688
689       p = strpbrk (p, ", \t");
690       if (p)
691         *p++ = '\0';
692       decode_field_spec (spec_item, &file_index, &field_index);
693       add_field (file_index, field_index);
694     }
695   while (p);
696 }
697
698 /* Set the join field *VAR to VAL, but report an error if *VAR is set
699    more than once to incompatible values.  */
700
701 static void
702 set_join_field (size_t *var, size_t val)
703 {
704   if (*var != SIZE_MAX && *var != val)
705     {
706       unsigned long int var1 = *var + 1;
707       unsigned long int val1 = val + 1;
708       error (EXIT_FAILURE, 0, _("incompatible join fields %lu, %lu"),
709              var1, val1);
710     }
711   *var = val;
712 }
713
714 /* Status of command-line arguments.  */
715
716 enum operand_status
717   {
718     /* This argument must be an operand, i.e., one of the files to be
719        joined.  */
720     MUST_BE_OPERAND,
721
722     /* This might be the argument of the preceding -j1 or -j2 option,
723        or it might be an operand.  */
724     MIGHT_BE_J1_ARG,
725     MIGHT_BE_J2_ARG,
726
727     /* This might be the argument of the preceding -o option, or it might be
728        an operand.  */
729     MIGHT_BE_O_ARG
730   };
731
732 /* Add NAME to the array of input file NAMES with operand statuses
733    OPERAND_STATUS; currently there are NFILES names in the list.  */
734
735 static void
736 add_file_name (char *name, char *names[2],
737                int operand_status[2], int joption_count[2], int *nfiles,
738                int *prev_optc_status, int *optc_status)
739 {
740   int n = *nfiles;
741
742   if (n == 2)
743     {
744       bool op0 = (operand_status[0] == MUST_BE_OPERAND);
745       char *arg = names[op0];
746       switch (operand_status[op0])
747         {
748         case MUST_BE_OPERAND:
749           error (0, 0, _("extra operand %s"), quote (name));
750           usage (EXIT_FAILURE);
751
752         case MIGHT_BE_J1_ARG:
753           joption_count[0]--;
754           set_join_field (&join_field_1, string_to_join_field (arg));
755           break;
756
757         case MIGHT_BE_J2_ARG:
758           joption_count[1]--;
759           set_join_field (&join_field_2, string_to_join_field (arg));
760           break;
761
762         case MIGHT_BE_O_ARG:
763           add_field_list (arg);
764           break;
765         }
766       if (!op0)
767         {
768           operand_status[0] = operand_status[1];
769           names[0] = names[1];
770         }
771       n = 1;
772     }
773
774   operand_status[n] = *prev_optc_status;
775   names[n] = name;
776   *nfiles = n + 1;
777   if (*prev_optc_status == MIGHT_BE_O_ARG)
778     *optc_status = MIGHT_BE_O_ARG;
779 }
780
781 int
782 main (int argc, char **argv)
783 {
784   int optc_status;
785   int prev_optc_status = MUST_BE_OPERAND;
786   int operand_status[2];
787   int joption_count[2] = { 0, 0 };
788   char *names[2];
789   FILE *fp1, *fp2;
790   int optc;
791   int nfiles = 0;
792   int i;
793
794   initialize_main (&argc, &argv);
795   program_name = argv[0];
796   setlocale (LC_ALL, "");
797   bindtextdomain (PACKAGE, LOCALEDIR);
798   textdomain (PACKAGE);
799   hard_LC_COLLATE = hard_locale (LC_COLLATE);
800
801   atexit (close_stdout);
802
803   print_pairables = true;
804
805   while ((optc = getopt_long (argc, argv, "-a:e:i1:2:j:o:t:v:",
806                               longopts, NULL))
807          != -1)
808     {
809       optc_status = MUST_BE_OPERAND;
810
811       switch (optc)
812         {
813         case 'v':
814             print_pairables = false;
815             /* Fall through.  */
816
817         case 'a':
818           {
819             unsigned long int val;
820             if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
821                 || (val != 1 && val != 2))
822               error (EXIT_FAILURE, 0,
823                      _("invalid field number: %s"), quote (optarg));
824             if (val == 1)
825               print_unpairables_1 = true;
826             else
827               print_unpairables_2 = true;
828           }
829           break;
830
831         case 'e':
832           if (empty_filler && ! STREQ (empty_filler, optarg))
833             error (EXIT_FAILURE, 0,
834                    _("conflicting empty-field replacement strings"));
835           empty_filler = optarg;
836           break;
837
838         case 'i':
839           ignore_case = true;
840           break;
841
842         case '1':
843           set_join_field (&join_field_1, string_to_join_field (optarg));
844           break;
845
846         case '2':
847           set_join_field (&join_field_2, string_to_join_field (optarg));
848           break;
849
850         case 'j':
851           if ((optarg[0] == '1' || optarg[0] == '2') && !optarg[1]
852               && optarg == argv[optind - 1] + 2)
853             {
854               /* The argument was either "-j1" or "-j2".  */
855               bool is_j2 = (optarg[0] == '2');
856               joption_count[is_j2]++;
857               optc_status = MIGHT_BE_J1_ARG + is_j2;
858             }
859           else
860             {
861               set_join_field (&join_field_1, string_to_join_field (optarg));
862               set_join_field (&join_field_2, join_field_1);
863             }
864           break;
865
866         case 'o':
867           add_field_list (optarg);
868           optc_status = MIGHT_BE_O_ARG;
869           break;
870
871         case 't':
872           {
873             unsigned char newtab = optarg[0];
874             if (! newtab)
875               error (EXIT_FAILURE, 0, _("empty tab"));
876             if (optarg[1])
877               {
878                 if (STREQ (optarg, "\\0"))
879                   newtab = '\0';
880                 else
881                   error (EXIT_FAILURE, 0, _("multi-character tab %s"),
882                          quote (optarg));
883               }
884             if (0 <= tab && tab != newtab)
885               error (EXIT_FAILURE, 0, _("incompatible tabs"));
886             tab = newtab;
887           }
888           break;
889
890         case 1:         /* Non-option argument.  */
891           add_file_name (optarg, names, operand_status, joption_count,
892                          &nfiles, &prev_optc_status, &optc_status);
893           break;
894
895         case_GETOPT_HELP_CHAR;
896
897         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
898
899         default:
900           usage (EXIT_FAILURE);
901         }
902
903       prev_optc_status = optc_status;
904     }
905
906   /* Process any operands after "--".  */
907   prev_optc_status = MUST_BE_OPERAND;
908   while (optind < argc)
909     add_file_name (argv[optind++], names, operand_status, joption_count,
910                    &nfiles, &prev_optc_status, &optc_status);
911
912   if (nfiles != 2)
913     {
914       if (nfiles == 0)
915         error (0, 0, _("missing operand"));
916       else
917         error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
918       usage (EXIT_FAILURE);
919     }
920
921   /* If "-j1" was specified and it turns out not to have had an argument,
922      treat it as "-j 1".  Likewise for -j2.  */
923   for (i = 0; i < 2; i++)
924     if (joption_count[i] != 0)
925       {
926         set_join_field (&join_field_1, i);
927         set_join_field (&join_field_2, i);
928       }
929
930   if (join_field_1 == SIZE_MAX)
931     join_field_1 = 0;
932   if (join_field_2 == SIZE_MAX)
933     join_field_2 = 0;
934
935   fp1 = STREQ (names[0], "-") ? stdin : fopen (names[0], "r");
936   if (!fp1)
937     error (EXIT_FAILURE, errno, "%s", names[0]);
938   fp2 = STREQ (names[1], "-") ? stdin : fopen (names[1], "r");
939   if (!fp2)
940     error (EXIT_FAILURE, errno, "%s", names[1]);
941   if (fp1 == fp2)
942     error (EXIT_FAILURE, errno, _("both files cannot be standard input"));
943   join (fp1, fp2);
944
945   if (fclose (fp1) != 0)
946     error (EXIT_FAILURE, errno, "%s", names[0]);
947   if (fclose (fp2) != 0)
948     error (EXIT_FAILURE, errno, "%s", names[1]);
949
950   exit (EXIT_SUCCESS);
951 }