Tizen 2.0 Release
[external/tizen-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 /* Record a field in LINE, with location FIELD and size LEN.  */
178
179 static void
180 extract_field (struct line *line, char *field, size_t len)
181 {
182   if (line->nfields >= line->nfields_allocated)
183     {
184       line->fields = X2NREALLOC (line->fields, &line->nfields_allocated);
185     }
186   line->fields[line->nfields].beg = field;
187   line->fields[line->nfields].len = len;
188   ++(line->nfields);
189 }
190
191 /* Fill in the `fields' structure in LINE.  */
192
193 static void
194 xfields (struct line *line)
195 {
196   char *ptr = line->buf.buffer;
197   char const *lim = ptr + line->buf.length - 1;
198
199   if (ptr == lim)
200     return;
201
202   if (0 <= tab)
203     {
204       char *sep;
205       for (; (sep = memchr (ptr, tab, lim - ptr)) != NULL; ptr = sep + 1)
206         extract_field (line, ptr, sep - ptr);
207     }
208   else
209     {
210       /* Skip leading blanks before the first field.  */
211       while (isblank (to_uchar (*ptr)))
212         if (++ptr == lim)
213           return;
214
215       do
216         {
217           char *sep;
218           for (sep = ptr + 1; sep != lim && ! isblank (to_uchar (*sep)); sep++)
219             continue;
220           extract_field (line, ptr, sep - ptr);
221           if (sep == lim)
222             return;
223           for (ptr = sep + 1; ptr != lim && isblank (to_uchar (*ptr)); ptr++)
224             continue;
225         }
226       while (ptr != lim);
227     }
228
229   extract_field (line, ptr, lim - ptr);
230 }
231
232 /* Read a line from FP into LINE and split it into fields.
233    Return true if successful.  */
234
235 static bool
236 get_line (FILE *fp, struct line *line)
237 {
238   initbuffer (&line->buf);
239
240   if (! readlinebuffer (&line->buf, fp))
241     {
242       if (ferror (fp))
243         error (EXIT_FAILURE, errno, _("read error"));
244       free (line->buf.buffer);
245       line->buf.buffer = NULL;
246       return false;
247     }
248
249   line->nfields_allocated = 0;
250   line->nfields = 0;
251   line->fields = NULL;
252   xfields (line);
253   return true;
254 }
255
256 static void
257 freeline (struct line *line)
258 {
259   free (line->fields);
260   free (line->buf.buffer);
261   line->buf.buffer = NULL;
262 }
263
264 static void
265 initseq (struct seq *seq)
266 {
267   seq->count = 0;
268   seq->alloc = 0;
269   seq->lines = NULL;
270 }
271
272 /* Read a line from FP and add it to SEQ.  Return true if successful.  */
273
274 static bool
275 getseq (FILE *fp, struct seq *seq)
276 {
277   if (seq->count == seq->alloc)
278     seq->lines = X2NREALLOC (seq->lines, &seq->alloc);
279
280   if (get_line (fp, &seq->lines[seq->count]))
281     {
282       ++seq->count;
283       return true;
284     }
285   return false;
286 }
287
288 static void
289 delseq (struct seq *seq)
290 {
291   size_t i;
292   for (i = 0; i < seq->count; i++)
293     if (seq->lines[i].buf.buffer)
294       freeline (&seq->lines[i]);
295   free (seq->lines);
296 }
297
298 /* Return <0 if the join field in LINE1 compares less than the one in LINE2;
299    >0 if it compares greater; 0 if it compares equal.
300    Report an error and exit if the comparison fails.  */
301
302 static int
303 keycmp (struct line const *line1, struct line const *line2)
304 {
305   /* Start of field to compare in each file.  */
306   char *beg1;
307   char *beg2;
308
309   size_t len1;
310   size_t len2;          /* Length of fields to compare.  */
311   int diff;
312
313   if (join_field_1 < line1->nfields)
314     {
315       beg1 = line1->fields[join_field_1].beg;
316       len1 = line1->fields[join_field_1].len;
317     }
318   else
319     {
320       beg1 = NULL;
321       len1 = 0;
322     }
323
324   if (join_field_2 < line2->nfields)
325     {
326       beg2 = line2->fields[join_field_2].beg;
327       len2 = line2->fields[join_field_2].len;
328     }
329   else
330     {
331       beg2 = NULL;
332       len2 = 0;
333     }
334
335   if (len1 == 0)
336     return len2 == 0 ? 0 : -1;
337   if (len2 == 0)
338     return 1;
339
340   if (ignore_case)
341     {
342       /* FIXME: ignore_case does not work with NLS (in particular,
343          with multibyte chars).  */
344       diff = memcasecmp (beg1, beg2, MIN (len1, len2));
345     }
346   else
347     {
348       if (hard_LC_COLLATE)
349         return xmemcoll (beg1, len1, beg2, len2);
350       diff = memcmp (beg1, beg2, MIN (len1, len2));
351     }
352
353   if (diff)
354     return diff;
355   return len1 < len2 ? -1 : len1 != len2;
356 }
357
358 /* Print field N of LINE if it exists and is nonempty, otherwise
359    `empty_filler' if it is nonempty.  */
360
361 static void
362 prfield (size_t n, struct line const *line)
363 {
364   size_t len;
365
366   if (n < line->nfields)
367     {
368       len = line->fields[n].len;
369       if (len)
370         fwrite (line->fields[n].beg, 1, len, stdout);
371       else if (empty_filler)
372         fputs (empty_filler, stdout);
373     }
374   else if (empty_filler)
375     fputs (empty_filler, stdout);
376 }
377
378 /* Print the join of LINE1 and LINE2.  */
379
380 static void
381 prjoin (struct line const *line1, struct line const *line2)
382 {
383   const struct outlist *outlist;
384   char output_separator = tab < 0 ? ' ' : tab;
385
386   outlist = outlist_head.next;
387   if (outlist)
388     {
389       const struct outlist *o;
390
391       o = outlist;
392       while (1)
393         {
394           size_t field;
395           struct line const *line;
396
397           if (o->file == 0)
398             {
399               if (line1 == &uni_blank)
400                 {
401                   line = line2;
402                   field = join_field_2;
403                 }
404               else
405                 {
406                   line = line1;
407                   field = join_field_1;
408                 }
409             }
410           else
411             {
412               line = (o->file == 1 ? line1 : line2);
413               field = o->field;
414             }
415           prfield (field, line);
416           o = o->next;
417           if (o == NULL)
418             break;
419           putchar (output_separator);
420         }
421       putchar ('\n');
422     }
423   else
424     {
425       size_t i;
426
427       if (line1 == &uni_blank)
428         {
429           struct line const *t;
430           t = line1;
431           line1 = line2;
432           line2 = t;
433         }
434       prfield (join_field_1, line1);
435       for (i = 0; i < join_field_1 && i < line1->nfields; ++i)
436         {
437           putchar (output_separator);
438           prfield (i, line1);
439         }
440       for (i = join_field_1 + 1; i < line1->nfields; ++i)
441         {
442           putchar (output_separator);
443           prfield (i, line1);
444         }
445
446       for (i = 0; i < join_field_2 && i < line2->nfields; ++i)
447         {
448           putchar (output_separator);
449           prfield (i, line2);
450         }
451       for (i = join_field_2 + 1; i < line2->nfields; ++i)
452         {
453           putchar (output_separator);
454           prfield (i, line2);
455         }
456       putchar ('\n');
457     }
458 }
459
460 /* Print the join of the files in FP1 and FP2.  */
461
462 static void
463 join (FILE *fp1, FILE *fp2)
464 {
465   struct seq seq1, seq2;
466   struct line line;
467   int diff;
468   bool eof1, eof2;
469
470   /* Read the first line of each file.  */
471   initseq (&seq1);
472   getseq (fp1, &seq1);
473   initseq (&seq2);
474   getseq (fp2, &seq2);
475
476   while (seq1.count && seq2.count)
477     {
478       size_t i;
479       diff = keycmp (&seq1.lines[0], &seq2.lines[0]);
480       if (diff < 0)
481         {
482           if (print_unpairables_1)
483             prjoin (&seq1.lines[0], &uni_blank);
484           freeline (&seq1.lines[0]);
485           seq1.count = 0;
486           getseq (fp1, &seq1);
487           continue;
488         }
489       if (diff > 0)
490         {
491           if (print_unpairables_2)
492             prjoin (&uni_blank, &seq2.lines[0]);
493           freeline (&seq2.lines[0]);
494           seq2.count = 0;
495           getseq (fp2, &seq2);
496           continue;
497         }
498
499       /* Keep reading lines from file1 as long as they continue to
500          match the current line from file2.  */
501       eof1 = false;
502       do
503         if (!getseq (fp1, &seq1))
504           {
505             eof1 = true;
506             ++seq1.count;
507             break;
508           }
509       while (!keycmp (&seq1.lines[seq1.count - 1], &seq2.lines[0]));
510
511       /* Keep reading lines from file2 as long as they continue to
512          match the current line from file1.  */
513       eof2 = false;
514       do
515         if (!getseq (fp2, &seq2))
516           {
517             eof2 = true;
518             ++seq2.count;
519             break;
520           }
521       while (!keycmp (&seq1.lines[0], &seq2.lines[seq2.count - 1]));
522
523       if (print_pairables)
524         {
525           for (i = 0; i < seq1.count - 1; ++i)
526             {
527               size_t j;
528               for (j = 0; j < seq2.count - 1; ++j)
529                 prjoin (&seq1.lines[i], &seq2.lines[j]);
530             }
531         }
532
533       for (i = 0; i < seq1.count - 1; ++i)
534         freeline (&seq1.lines[i]);
535       if (!eof1)
536         {
537           seq1.lines[0] = seq1.lines[seq1.count - 1];
538           seq1.count = 1;
539         }
540       else
541         seq1.count = 0;
542
543       for (i = 0; i < seq2.count - 1; ++i)
544         freeline (&seq2.lines[i]);
545       if (!eof2)
546         {
547           seq2.lines[0] = seq2.lines[seq2.count - 1];
548           seq2.count = 1;
549         }
550       else
551         seq2.count = 0;
552     }
553
554   if (print_unpairables_1 && seq1.count)
555     {
556       prjoin (&seq1.lines[0], &uni_blank);
557       freeline (&seq1.lines[0]);
558       while (get_line (fp1, &line))
559         {
560           prjoin (&line, &uni_blank);
561           freeline (&line);
562         }
563     }
564
565   if (print_unpairables_2 && seq2.count)
566     {
567       prjoin (&uni_blank, &seq2.lines[0]);
568       freeline (&seq2.lines[0]);
569       while (get_line (fp2, &line))
570         {
571           prjoin (&uni_blank, &line);
572           freeline (&line);
573         }
574     }
575
576   delseq (&seq1);
577   delseq (&seq2);
578 }
579
580 /* Add a field spec for field FIELD of file FILE to `outlist'.  */
581
582 static void
583 add_field (int file, size_t field)
584 {
585   struct outlist *o;
586
587   assert (file == 0 || file == 1 || file == 2);
588   assert (file != 0 || field == 0);
589
590   o = xmalloc (sizeof *o);
591   o->file = file;
592   o->field = field;
593   o->next = NULL;
594
595   /* Add to the end of the list so the fields are in the right order.  */
596   outlist_end->next = o;
597   outlist_end = o;
598 }
599
600 /* Convert a string of decimal digits, STR (the 1-based join field number),
601    to an integral value.  Upon successful conversion, return one less
602    (the zero-based field number).  Silently convert too-large values
603    to SIZE_MAX - 1.  Otherwise, if a value cannot be converted, give a
604    diagnostic and exit.  */
605
606 static size_t
607 string_to_join_field (char const *str)
608 {
609   size_t result;
610   unsigned long int val;
611   verify (SIZE_MAX <= ULONG_MAX);
612
613   strtol_error s_err = xstrtoul (str, NULL, 10, &val, "");
614   if (s_err == LONGINT_OVERFLOW || (s_err == LONGINT_OK && SIZE_MAX < val))
615     val = SIZE_MAX;
616   else if (s_err != LONGINT_OK || val == 0)
617     error (EXIT_FAILURE, 0, _("invalid field number: %s"), quote (str));
618
619   result = val - 1;
620
621   return result;
622 }
623
624 /* Convert a single field specifier string, S, to a *FILE_INDEX, *FIELD_INDEX
625    pair.  In S, the field index string is 1-based; *FIELD_INDEX is zero-based.
626    If S is valid, return true.  Otherwise, give a diagnostic and exit.  */
627
628 static void
629 decode_field_spec (const char *s, int *file_index, size_t *field_index)
630 {
631   /* The first character must be 0, 1, or 2.  */
632   switch (s[0])
633     {
634     case '0':
635       if (s[1])
636         {
637           /* `0' must be all alone -- no `.FIELD'.  */
638           error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
639         }
640       *file_index = 0;
641       *field_index = 0;
642       break;
643
644     case '1':
645     case '2':
646       if (s[1] != '.')
647         error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
648       *file_index = s[0] - '0';
649       *field_index = string_to_join_field (s + 2);
650       break;
651
652     default:
653       error (EXIT_FAILURE, 0,
654              _("invalid file number in field spec: %s"), quote (s));
655
656       /* Tell gcc -W -Wall that we can't get beyond this point.
657          This avoids a warning (otherwise legit) that the caller's copies
658          of *file_index and *field_index might be used uninitialized.  */
659       abort ();
660
661       break;
662     }
663 }
664
665 /* Add the comma or blank separated field spec(s) in STR to `outlist'.  */
666
667 static void
668 add_field_list (char *str)
669 {
670   char *p = str;
671
672   do
673     {
674       int file_index;
675       size_t field_index;
676       char const *spec_item = p;
677
678       p = strpbrk (p, ", \t");
679       if (p)
680         *p++ = '\0';
681       decode_field_spec (spec_item, &file_index, &field_index);
682       add_field (file_index, field_index);
683     }
684   while (p);
685 }
686
687 /* Set the join field *VAR to VAL, but report an error if *VAR is set
688    more than once to incompatible values.  */
689
690 static void
691 set_join_field (size_t *var, size_t val)
692 {
693   if (*var != SIZE_MAX && *var != val)
694     {
695       unsigned long int var1 = *var + 1;
696       unsigned long int val1 = val + 1;
697       error (EXIT_FAILURE, 0, _("incompatible join fields %lu, %lu"),
698              var1, val1);
699     }
700   *var = val;
701 }
702
703 /* Status of command-line arguments.  */
704
705 enum operand_status
706   {
707     /* This argument must be an operand, i.e., one of the files to be
708        joined.  */
709     MUST_BE_OPERAND,
710
711     /* This might be the argument of the preceding -j1 or -j2 option,
712        or it might be an operand.  */
713     MIGHT_BE_J1_ARG,
714     MIGHT_BE_J2_ARG,
715
716     /* This might be the argument of the preceding -o option, or it might be
717        an operand.  */
718     MIGHT_BE_O_ARG
719   };
720
721 /* Add NAME to the array of input file NAMES with operand statuses
722    OPERAND_STATUS; currently there are NFILES names in the list.  */
723
724 static void
725 add_file_name (char *name, char *names[2],
726                int operand_status[2], int joption_count[2], int *nfiles,
727                int *prev_optc_status, int *optc_status)
728 {
729   int n = *nfiles;
730
731   if (n == 2)
732     {
733       bool op0 = (operand_status[0] == MUST_BE_OPERAND);
734       char *arg = names[op0];
735       switch (operand_status[op0])
736         {
737         case MUST_BE_OPERAND:
738           error (0, 0, _("extra operand %s"), quote (name));
739           usage (EXIT_FAILURE);
740
741         case MIGHT_BE_J1_ARG:
742           joption_count[0]--;
743           set_join_field (&join_field_1, string_to_join_field (arg));
744           break;
745
746         case MIGHT_BE_J2_ARG:
747           joption_count[1]--;
748           set_join_field (&join_field_2, string_to_join_field (arg));
749           break;
750
751         case MIGHT_BE_O_ARG:
752           add_field_list (arg);
753           break;
754         }
755       if (!op0)
756         {
757           operand_status[0] = operand_status[1];
758           names[0] = names[1];
759         }
760       n = 1;
761     }
762
763   operand_status[n] = *prev_optc_status;
764   names[n] = name;
765   *nfiles = n + 1;
766   if (*prev_optc_status == MIGHT_BE_O_ARG)
767     *optc_status = MIGHT_BE_O_ARG;
768 }
769
770 int
771 main (int argc, char **argv)
772 {
773   int optc_status;
774   int prev_optc_status = MUST_BE_OPERAND;
775   int operand_status[2];
776   int joption_count[2] = { 0, 0 };
777   char *names[2];
778   FILE *fp1, *fp2;
779   int optc;
780   int nfiles = 0;
781   int i;
782
783   initialize_main (&argc, &argv);
784   program_name = argv[0];
785   setlocale (LC_ALL, "");
786   bindtextdomain (PACKAGE, LOCALEDIR);
787   textdomain (PACKAGE);
788   hard_LC_COLLATE = hard_locale (LC_COLLATE);
789
790   atexit (close_stdout);
791
792   print_pairables = true;
793
794   while ((optc = getopt_long (argc, argv, "-a:e:i1:2:j:o:t:v:",
795                               longopts, NULL))
796          != -1)
797     {
798       optc_status = MUST_BE_OPERAND;
799
800       switch (optc)
801         {
802         case 'v':
803             print_pairables = false;
804             /* Fall through.  */
805
806         case 'a':
807           {
808             unsigned long int val;
809             if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
810                 || (val != 1 && val != 2))
811               error (EXIT_FAILURE, 0,
812                      _("invalid field number: %s"), quote (optarg));
813             if (val == 1)
814               print_unpairables_1 = true;
815             else
816               print_unpairables_2 = true;
817           }
818           break;
819
820         case 'e':
821           if (empty_filler && ! STREQ (empty_filler, optarg))
822             error (EXIT_FAILURE, 0,
823                    _("conflicting empty-field replacement strings"));
824           empty_filler = optarg;
825           break;
826
827         case 'i':
828           ignore_case = true;
829           break;
830
831         case '1':
832           set_join_field (&join_field_1, string_to_join_field (optarg));
833           break;
834
835         case '2':
836           set_join_field (&join_field_2, string_to_join_field (optarg));
837           break;
838
839         case 'j':
840           if ((optarg[0] == '1' || optarg[0] == '2') && !optarg[1]
841               && optarg == argv[optind - 1] + 2)
842             {
843               /* The argument was either "-j1" or "-j2".  */
844               bool is_j2 = (optarg[0] == '2');
845               joption_count[is_j2]++;
846               optc_status = MIGHT_BE_J1_ARG + is_j2;
847             }
848           else
849             {
850               set_join_field (&join_field_1, string_to_join_field (optarg));
851               set_join_field (&join_field_2, join_field_1);
852             }
853           break;
854
855         case 'o':
856           add_field_list (optarg);
857           optc_status = MIGHT_BE_O_ARG;
858           break;
859
860         case 't':
861           {
862             unsigned char newtab = optarg[0];
863             if (! newtab)
864               error (EXIT_FAILURE, 0, _("empty tab"));
865             if (optarg[1])
866               {
867                 if (STREQ (optarg, "\\0"))
868                   newtab = '\0';
869                 else
870                   error (EXIT_FAILURE, 0, _("multi-character tab %s"),
871                          quote (optarg));
872               }
873             if (0 <= tab && tab != newtab)
874               error (EXIT_FAILURE, 0, _("incompatible tabs"));
875             tab = newtab;
876           }
877           break;
878
879         case 1:         /* Non-option argument.  */
880           add_file_name (optarg, names, operand_status, joption_count,
881                          &nfiles, &prev_optc_status, &optc_status);
882           break;
883
884         case_GETOPT_HELP_CHAR;
885
886         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
887
888         default:
889           usage (EXIT_FAILURE);
890         }
891
892       prev_optc_status = optc_status;
893     }
894
895   /* Process any operands after "--".  */
896   prev_optc_status = MUST_BE_OPERAND;
897   while (optind < argc)
898     add_file_name (argv[optind++], names, operand_status, joption_count,
899                    &nfiles, &prev_optc_status, &optc_status);
900
901   if (nfiles != 2)
902     {
903       if (nfiles == 0)
904         error (0, 0, _("missing operand"));
905       else
906         error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
907       usage (EXIT_FAILURE);
908     }
909
910   /* If "-j1" was specified and it turns out not to have had an argument,
911      treat it as "-j 1".  Likewise for -j2.  */
912   for (i = 0; i < 2; i++)
913     if (joption_count[i] != 0)
914       {
915         set_join_field (&join_field_1, i);
916         set_join_field (&join_field_2, i);
917       }
918
919   if (join_field_1 == SIZE_MAX)
920     join_field_1 = 0;
921   if (join_field_2 == SIZE_MAX)
922     join_field_2 = 0;
923
924   fp1 = STREQ (names[0], "-") ? stdin : fopen (names[0], "r");
925   if (!fp1)
926     error (EXIT_FAILURE, errno, "%s", names[0]);
927   fp2 = STREQ (names[1], "-") ? stdin : fopen (names[1], "r");
928   if (!fp2)
929     error (EXIT_FAILURE, errno, "%s", names[1]);
930   if (fp1 == fp2)
931     error (EXIT_FAILURE, errno, _("both files cannot be standard input"));
932   join (fp1, fp2);
933
934   if (fclose (fp1) != 0)
935     error (EXIT_FAILURE, errno, "%s", names[0]);
936   if (fclose (fp2) != 0)
937     error (EXIT_FAILURE, errno, "%s", names[1]);
938
939   exit (EXIT_SUCCESS);
940 }