GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: join.c,v 1.27 2015/10/09 01:37:07 deraadt Exp $ */ |
||
2 |
|||
3 |
/*- |
||
4 |
* Copyright (c) 1991, 1993, 1994 |
||
5 |
* The Regents of the University of California. All rights reserved. |
||
6 |
* |
||
7 |
* This code is derived from software contributed to Berkeley by |
||
8 |
* Steve Hayman of the Computer Science Department, Indiana University, |
||
9 |
* Michiro Hikida and David Goodenough. |
||
10 |
* |
||
11 |
* Redistribution and use in source and binary forms, with or without |
||
12 |
* modification, are permitted provided that the following conditions |
||
13 |
* are met: |
||
14 |
* 1. Redistributions of source code must retain the above copyright |
||
15 |
* notice, this list of conditions and the following disclaimer. |
||
16 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
17 |
* notice, this list of conditions and the following disclaimer in the |
||
18 |
* documentation and/or other materials provided with the distribution. |
||
19 |
* 3. Neither the name of the University nor the names of its contributors |
||
20 |
* may be used to endorse or promote products derived from this software |
||
21 |
* without specific prior written permission. |
||
22 |
* |
||
23 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
24 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
25 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
26 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
27 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
28 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
29 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
30 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
31 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
32 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
33 |
* SUCH DAMAGE. |
||
34 |
*/ |
||
35 |
|||
36 |
#include <err.h> |
||
37 |
#include <stdio.h> |
||
38 |
#include <stdlib.h> |
||
39 |
#include <string.h> |
||
40 |
#include <unistd.h> |
||
41 |
|||
42 |
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
||
43 |
|||
44 |
/* |
||
45 |
* There's a structure per input file which encapsulates the state of the |
||
46 |
* file. We repeatedly read lines from each file until we've read in all |
||
47 |
* the consecutive lines from the file with a common join field. Then we |
||
48 |
* compare the set of lines with an equivalent set from the other file. |
||
49 |
*/ |
||
50 |
typedef struct { |
||
51 |
char *line; /* line */ |
||
52 |
u_long linealloc; /* line allocated count */ |
||
53 |
char **fields; /* line field(s) */ |
||
54 |
u_long fieldcnt; /* line field(s) count */ |
||
55 |
u_long fieldalloc; /* line field(s) allocated count */ |
||
56 |
u_long cfieldc; /* current field count */ |
||
57 |
long fpos; /* fpos of start of field */ |
||
58 |
} LINE; |
||
59 |
|||
60 |
typedef struct { |
||
61 |
FILE *fp; /* file descriptor */ |
||
62 |
u_long joinf; /* join field (-1, -2, -j) */ |
||
63 |
int unpair; /* output unpairable lines (-a) */ |
||
64 |
u_long number; /* 1 for file 1, 2 for file 2 */ |
||
65 |
LINE *set; /* set of lines with same field */ |
||
66 |
int pushbool; /* if pushback is set */ |
||
67 |
u_long pushback; /* line on the stack */ |
||
68 |
u_long setcnt; /* set count */ |
||
69 |
u_long setalloc; /* set allocated count */ |
||
70 |
u_long setusedc; /* sets used */ |
||
71 |
} INPUT; |
||
72 |
INPUT input1 = { NULL, 0, 0, 1, NULL, 0, 0, 0, 0, 0 }, |
||
73 |
input2 = { NULL, 0, 0, 2, NULL, 0, 0, 0, 0, 0 }; |
||
74 |
|||
75 |
typedef struct { |
||
76 |
u_long filenum; /* file number */ |
||
77 |
u_long fieldno; /* field number */ |
||
78 |
} OLIST; |
||
79 |
OLIST *olist; /* output field list */ |
||
80 |
u_long olistcnt; /* output field list count */ |
||
81 |
u_long olistalloc; /* output field allocated count */ |
||
82 |
|||
83 |
int joinout = 1; /* show lines with matched join fields (-v) */ |
||
84 |
int needsep; /* need separator character */ |
||
85 |
int spans = 1; /* span multiple delimiters (-t) */ |
||
86 |
char *empty; /* empty field replacement string (-e) */ |
||
87 |
char *tabchar = " \t"; /* delimiter characters (-t) */ |
||
88 |
|||
89 |
int cmp(LINE *, u_long, LINE *, u_long); |
||
90 |
void fieldarg(char *); |
||
91 |
void joinlines(INPUT *, INPUT *); |
||
92 |
void obsolete(char **); |
||
93 |
void outfield(LINE *, u_long, int); |
||
94 |
void outoneline(INPUT *, LINE *); |
||
95 |
void outtwoline(INPUT *, LINE *, INPUT *, LINE *); |
||
96 |
void slurp(INPUT *); |
||
97 |
void slurpit(INPUT *); |
||
98 |
void usage(void); |
||
99 |
|||
100 |
int |
||
101 |
main(int argc, char *argv[]) |
||
102 |
{ |
||
103 |
INPUT *F1, *F2; |
||
104 |
int aflag, ch, cval, vflag; |
||
105 |
244 |
char *end; |
|
106 |
|||
107 |
✗✓ | 122 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
108 |
err(1, "pledge"); |
||
109 |
|||
110 |
F1 = &input1; |
||
111 |
F2 = &input2; |
||
112 |
|||
113 |
aflag = vflag = 0; |
||
114 |
122 |
obsolete(argv); |
|
115 |
✓✓ | 732 |
while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != -1) { |
116 |
✗✗✗✗ ✗✓✓✗ ✗✗ |
244 |
switch (ch) { |
117 |
case '\01': /* See comment in obsolete(). */ |
||
118 |
aflag = 1; |
||
119 |
F1->unpair = F2->unpair = 1; |
||
120 |
break; |
||
121 |
case '1': |
||
122 |
if ((F1->joinf = strtol(optarg, &end, 10)) < 1) |
||
123 |
errx(1, "-1 option field number less than 1"); |
||
124 |
if (*end) |
||
125 |
errx(1, "illegal field number -- %s", optarg); |
||
126 |
--F1->joinf; |
||
127 |
break; |
||
128 |
case '2': |
||
129 |
if ((F2->joinf = strtol(optarg, &end, 10)) < 1) |
||
130 |
errx(1, "-2 option field number less than 1"); |
||
131 |
if (*end) |
||
132 |
errx(1, "illegal field number -- %s", optarg); |
||
133 |
--F2->joinf; |
||
134 |
break; |
||
135 |
case 'a': |
||
136 |
aflag = 1; |
||
137 |
switch(strtol(optarg, &end, 10)) { |
||
138 |
case 1: |
||
139 |
F1->unpair = 1; |
||
140 |
break; |
||
141 |
case 2: |
||
142 |
F2->unpair = 1; |
||
143 |
break; |
||
144 |
default: |
||
145 |
errx(1, "-a option file number not 1 or 2"); |
||
146 |
break; |
||
147 |
} |
||
148 |
if (*end) |
||
149 |
errx(1, "illegal file number -- %s", optarg); |
||
150 |
break; |
||
151 |
case 'e': |
||
152 |
empty = optarg; |
||
153 |
break; |
||
154 |
case 'j': |
||
155 |
✗✓ | 122 |
if ((F1->joinf = F2->joinf = strtol(optarg, &end, 10)) < 1) |
156 |
errx(1, "-j option field number less than 1"); |
||
157 |
✗✓ | 122 |
if (*end) |
158 |
errx(1, "illegal field number -- %s", optarg); |
||
159 |
122 |
--F1->joinf; |
|
160 |
122 |
--F2->joinf; |
|
161 |
122 |
break; |
|
162 |
case 'o': |
||
163 |
122 |
fieldarg(optarg); |
|
164 |
122 |
break; |
|
165 |
case 't': |
||
166 |
spans = 0; |
||
167 |
if (strlen(tabchar = optarg) != 1) |
||
168 |
errx(1, "illegal tab character specification"); |
||
169 |
break; |
||
170 |
case 'v': |
||
171 |
vflag = 1; |
||
172 |
joinout = 0; |
||
173 |
switch (strtol(optarg, &end, 10)) { |
||
174 |
case 1: |
||
175 |
F1->unpair = 1; |
||
176 |
break; |
||
177 |
case 2: |
||
178 |
F2->unpair = 1; |
||
179 |
break; |
||
180 |
default: |
||
181 |
errx(1, "-v option file number not 1 or 2"); |
||
182 |
break; |
||
183 |
} |
||
184 |
if (*end) |
||
185 |
errx(1, "illegal file number -- %s", optarg); |
||
186 |
break; |
||
187 |
case '?': |
||
188 |
default: |
||
189 |
usage(); |
||
190 |
} |
||
191 |
} |
||
192 |
122 |
argc -= optind; |
|
193 |
122 |
argv += optind; |
|
194 |
|||
195 |
✗✓ | 122 |
if (aflag && vflag) |
196 |
errx(1, "the -a and -v options are mutually exclusive"); |
||
197 |
|||
198 |
✗✓ | 122 |
if (argc != 2) |
199 |
usage(); |
||
200 |
|||
201 |
/* Open the files; "-" means stdin. */ |
||
202 |
✗✓ | 122 |
if (!strcmp(*argv, "-")) |
203 |
F1->fp = stdin; |
||
204 |
✗✓ | 122 |
else if ((F1->fp = fopen(*argv, "r")) == NULL) |
205 |
err(1, "%s", *argv); |
||
206 |
122 |
++argv; |
|
207 |
✗✓ | 122 |
if (!strcmp(*argv, "-")) |
208 |
F2->fp = stdin; |
||
209 |
✗✓ | 122 |
else if ((F2->fp = fopen(*argv, "r")) == NULL) |
210 |
err(1, "%s", *argv); |
||
211 |
✗✓✗✗ |
122 |
if (F1->fp == stdin && F2->fp == stdin) |
212 |
errx(1, "only one input file may be stdin"); |
||
213 |
|||
214 |
✗✓ | 122 |
if (pledge("stdio flock rpath cpath wpath", NULL) == -1) |
215 |
err(1, "pledge"); |
||
216 |
|||
217 |
122 |
F1->setusedc = 0; |
|
218 |
122 |
F2->setusedc = 0; |
|
219 |
122 |
slurp(F1); |
|
220 |
122 |
slurp(F2); |
|
221 |
122 |
F1->set->cfieldc = 0; |
|
222 |
122 |
F2->set->cfieldc = 0; |
|
223 |
|||
224 |
/* |
||
225 |
* We try to let the files have the same field value, advancing |
||
226 |
* whoever falls behind and always advancing the file(s) we output |
||
227 |
* from. |
||
228 |
*/ |
||
229 |
✓✓✓✓ |
196828 |
while (F1->setcnt && F2->setcnt) { |
230 |
49094 |
cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf); |
|
231 |
✓✓ | 49094 |
if (cval == 0) { |
232 |
/* Oh joy, oh rapture, oh beauty divine! */ |
||
233 |
✓✗ | 16062 |
if (joinout) |
234 |
16062 |
joinlines(F1, F2); |
|
235 |
16062 |
slurp(F1); |
|
236 |
16062 |
slurp(F2); |
|
237 |
16062 |
} |
|
238 |
else { |
||
239 |
✗✗ | 33032 |
if (F1->unpair |
240 |
✗✓✗✗ |
33032 |
&& (cval < 0 || F2->set->cfieldc == F2->setusedc -1)) { |
241 |
joinlines(F1, NULL); |
||
242 |
slurp(F1); |
||
243 |
} |
||
244 |
✓✓ | 33032 |
else if (cval < 0) |
245 |
/* File 1 takes the lead... */ |
||
246 |
7530 |
slurp(F1); |
|
247 |
✗✗ | 33032 |
if (F2->unpair |
248 |
✗✓✗✗ |
33032 |
&& (cval > 0 || F1->set->cfieldc == F1->setusedc -1)) { |
249 |
joinlines(F2, NULL); |
||
250 |
slurp(F2); |
||
251 |
} |
||
252 |
✓✓ | 33032 |
else if (cval > 0) |
253 |
/* File 2 takes the lead... */ |
||
254 |
25502 |
slurp(F2); |
|
255 |
} |
||
256 |
} |
||
257 |
|||
258 |
/* |
||
259 |
* Now that one of the files is used up, optionally output any |
||
260 |
* remaining lines from the other file. |
||
261 |
*/ |
||
262 |
✗✓ | 122 |
if (F1->unpair) |
263 |
while (F1->setcnt) { |
||
264 |
joinlines(F1, NULL); |
||
265 |
slurp(F1); |
||
266 |
} |
||
267 |
✗✓ | 122 |
if (F2->unpair) |
268 |
while (F2->setcnt) { |
||
269 |
joinlines(F2, NULL); |
||
270 |
slurp(F2); |
||
271 |
} |
||
272 |
|||
273 |
122 |
return 0; |
|
274 |
122 |
} |
|
275 |
|||
276 |
/* wrapper around slurpit() to keep track of what field we are on */ |
||
277 |
void slurp(INPUT *F) |
||
278 |
{ |
||
279 |
long fpos; |
||
280 |
u_long cfieldc; |
||
281 |
|||
282 |
✓✓ | 130800 |
if (F->set == NULL) { |
283 |
fpos = 0; |
||
284 |
cfieldc = 0; |
||
285 |
244 |
} |
|
286 |
else { |
||
287 |
65156 |
fpos = F->set->fpos; |
|
288 |
65156 |
cfieldc = F->set->cfieldc; |
|
289 |
} |
||
290 |
65400 |
slurpit(F); |
|
291 |
✗✓ | 65400 |
if (F->set == NULL) |
292 |
return; |
||
293 |
✓✓ | 65400 |
else if (fpos != F->set->fpos) |
294 |
65034 |
F->set->cfieldc = cfieldc+1; |
|
295 |
130800 |
} |
|
296 |
|||
297 |
void |
||
298 |
slurpit(INPUT *F) |
||
299 |
{ |
||
300 |
130800 |
LINE *lp, *lastlp, tmp; |
|
301 |
65400 |
size_t len; |
|
302 |
u_long cnt; |
||
303 |
65400 |
char *bp, *fieldp; |
|
304 |
long fpos; |
||
305 |
/* |
||
306 |
* Read all of the lines from an input file that have the same |
||
307 |
* join field. |
||
308 |
*/ |
||
309 |
|||
310 |
65400 |
F->setcnt = 0; |
|
311 |
231975 |
for (lastlp = NULL; ; ++F->setcnt, lastlp = lp) { |
|
312 |
/* |
||
313 |
* If we're out of space to hold line structures, allocate |
||
314 |
* more. Initialize the structure so that we know that this |
||
315 |
* is new space. |
||
316 |
*/ |
||
317 |
✓✓ | 231975 |
if (F->setcnt == F->setalloc) { |
318 |
LINE *p; |
||
319 |
378 |
u_long newsize = F->setalloc + 50; |
|
320 |
cnt = F->setalloc; |
||
321 |
✗✓ | 756 |
if ((p = reallocarray(F->set, newsize, sizeof(LINE))) |
322 |
378 |
== NULL) |
|
323 |
err(1, NULL); |
||
324 |
378 |
F->set = p; |
|
325 |
378 |
F->setalloc = newsize; |
|
326 |
378 |
memset(F->set + cnt, 0, 50 * sizeof(LINE)); |
|
327 |
/* re-set lastlp in case it moved */ |
||
328 |
✓✓ | 378 |
if (lastlp != NULL) |
329 |
134 |
lastlp = &F->set[F->setcnt - 1]; |
|
330 |
378 |
} |
|
331 |
/* |
||
332 |
* Get any pushed back line, else get the next line. Allocate |
||
333 |
* space as necessary. If taking the line from the stack swap |
||
334 |
* the two structures so that we don't lose space allocated to |
||
335 |
* either structure. This could be avoided by doing another |
||
336 |
* level of indirection, but it's probably okay as is. |
||
337 |
*/ |
||
338 |
231975 |
lp = &F->set[F->setcnt]; |
|
339 |
✓✓ | 231975 |
if (F->pushbool) { |
340 |
65034 |
tmp = F->set[F->setcnt]; |
|
341 |
65034 |
F->set[F->setcnt] = F->set[F->pushback]; |
|
342 |
65034 |
F->set[F->pushback] = tmp; |
|
343 |
65034 |
F->pushbool = 0; |
|
344 |
65034 |
continue; |
|
345 |
} |
||
346 |
✓✓ | 166941 |
if ((bp = fgetln(F->fp, &len)) == NULL) |
347 |
274 |
return; |
|
348 |
/* |
||
349 |
* we depend on knowing on what field we are, one safe way is |
||
350 |
* the file position. |
||
351 |
*/ |
||
352 |
166667 |
fpos = ftell(F->fp) - len; |
|
353 |
✓✓ | 166667 |
if (lp->linealloc <= len + 1) { |
354 |
char *p; |
||
355 |
8777 |
u_long newsize = lp->linealloc + |
|
356 |
✗✓ | 17554 |
MAXIMUM(100, len + 1 - lp->linealloc); |
357 |
✗✓ | 8777 |
if ((p = realloc(lp->line, newsize)) == NULL) |
358 |
err(1, NULL); |
||
359 |
8777 |
lp->line = p; |
|
360 |
8777 |
lp->linealloc = newsize; |
|
361 |
8777 |
} |
|
362 |
166667 |
F->setusedc++; |
|
363 |
166667 |
memmove(lp->line, bp, len); |
|
364 |
166667 |
lp->fpos = fpos; |
|
365 |
/* Replace trailing newline, if it exists. */ |
||
366 |
166667 |
if (bp[len - 1] == '\n') |
|
367 |
lp->line[len - 1] = '\0'; |
||
368 |
else |
||
369 |
lp->line[len] = '\0'; |
||
370 |
166667 |
bp = lp->line; |
|
371 |
|||
372 |
/* Split the line into fields, allocate space as necessary. */ |
||
373 |
166667 |
lp->fieldcnt = 0; |
|
374 |
✓✓ | 1000002 |
while ((fieldp = strsep(&bp, tabchar)) != NULL) { |
375 |
✓✗✗✓ |
666668 |
if (spans && *fieldp == '\0') |
376 |
continue; |
||
377 |
✓✓ | 333334 |
if (lp->fieldcnt == lp->fieldalloc) { |
378 |
char **p; |
||
379 |
8745 |
u_long newsize = lp->fieldalloc + 50; |
|
380 |
✗✓ | 17490 |
if ((p = reallocarray(lp->fields, newsize, |
381 |
8745 |
sizeof(char *))) == NULL) |
|
382 |
err(1, NULL); |
||
383 |
8745 |
lp->fields = p; |
|
384 |
8745 |
lp->fieldalloc = newsize; |
|
385 |
8745 |
} |
|
386 |
333334 |
lp->fields[lp->fieldcnt++] = fieldp; |
|
387 |
} |
||
388 |
|||
389 |
/* See if the join field value has changed. */ |
||
390 |
✓✓✓✓ |
333090 |
if (lastlp != NULL && cmp(lp, F->joinf, lastlp, F->joinf)) { |
391 |
65126 |
F->pushbool = 1; |
|
392 |
65126 |
F->pushback = F->setcnt; |
|
393 |
break; |
||
394 |
} |
||
395 |
} |
||
396 |
130526 |
} |
|
397 |
|||
398 |
int |
||
399 |
cmp(LINE *lp1, u_long fieldno1, LINE *lp2, u_long fieldno2) |
||
400 |
{ |
||
401 |
✗✓ | 431034 |
if (lp1->fieldcnt <= fieldno1) |
402 |
return (-1); |
||
403 |
✗✓ | 215517 |
else if (lp2->fieldcnt <= fieldno2) |
404 |
return (1); |
||
405 |
215517 |
return (strcmp(lp1->fields[fieldno1], lp2->fields[fieldno2])); |
|
406 |
215517 |
} |
|
407 |
|||
408 |
void |
||
409 |
joinlines(INPUT *F1, INPUT *F2) |
||
410 |
{ |
||
411 |
u_long cnt1, cnt2; |
||
412 |
|||
413 |
/* |
||
414 |
* Output the results of a join comparison. The output may be from |
||
415 |
* either file 1 or file 2 (in which case the first argument is the |
||
416 |
* file from which to output) or from both. |
||
417 |
*/ |
||
418 |
✗✓ | 32124 |
if (F2 == NULL) { |
419 |
for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1) |
||
420 |
outoneline(F1, &F1->set[cnt1]); |
||
421 |
return; |
||
422 |
} |
||
423 |
✓✓ | 134980 |
for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1) |
424 |
✓✓ | 205712 |
for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2) |
425 |
51428 |
outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]); |
|
426 |
32124 |
} |
|
427 |
|||
428 |
void |
||
429 |
outoneline(INPUT *F, LINE *lp) |
||
430 |
{ |
||
431 |
u_long cnt; |
||
432 |
|||
433 |
/* |
||
434 |
* Output a single line from one of the files, according to the |
||
435 |
* join rules. This happens when we are writing unmatched single |
||
436 |
* lines. Output empty fields in the right places. |
||
437 |
*/ |
||
438 |
if (olist) |
||
439 |
for (cnt = 0; cnt < olistcnt; ++cnt) { |
||
440 |
if (olist[cnt].filenum == F->number) |
||
441 |
outfield(lp, olist[cnt].fieldno, 0); |
||
442 |
else if (olist[cnt].filenum == 0) |
||
443 |
outfield(lp, F->joinf, 0); |
||
444 |
else |
||
445 |
outfield(lp, 0, 1); |
||
446 |
} |
||
447 |
else { |
||
448 |
/* |
||
449 |
* Output the join field, then the remaining fields from F |
||
450 |
*/ |
||
451 |
outfield(lp, F->joinf, 0); |
||
452 |
for (cnt = 0; cnt < lp->fieldcnt; ++cnt) |
||
453 |
if (F->joinf != cnt) |
||
454 |
outfield(lp, cnt, 0); |
||
455 |
} |
||
456 |
|||
457 |
putchar('\n'); |
||
458 |
if (ferror(stdout)) |
||
459 |
err(1, "stdout"); |
||
460 |
needsep = 0; |
||
461 |
} |
||
462 |
|||
463 |
void |
||
464 |
outtwoline(INPUT *F1, LINE *lp1, INPUT *F2, LINE *lp2) |
||
465 |
{ |
||
466 |
u_long cnt; |
||
467 |
|||
468 |
/* Output a pair of lines according to the join list (if any). */ |
||
469 |
✓✗ | 102856 |
if (olist) { |
470 |
✓✓ | 308568 |
for (cnt = 0; cnt < olistcnt; ++cnt) |
471 |
✗✓ | 102856 |
if (olist[cnt].filenum == 0) { |
472 |
if (lp1->fieldcnt >= F1->joinf) |
||
473 |
outfield(lp1, F1->joinf, 0); |
||
474 |
else |
||
475 |
outfield(lp2, F2->joinf, 0); |
||
476 |
✓✓ | 102856 |
} else if (olist[cnt].filenum == 1) |
477 |
51428 |
outfield(lp1, olist[cnt].fieldno, 0); |
|
478 |
else /* if (olist[cnt].filenum == 2) */ |
||
479 |
51428 |
outfield(lp2, olist[cnt].fieldno, 0); |
|
480 |
} else { |
||
481 |
/* |
||
482 |
* Output the join field, then the remaining fields from F1 |
||
483 |
* and F2. |
||
484 |
*/ |
||
485 |
outfield(lp1, F1->joinf, 0); |
||
486 |
for (cnt = 0; cnt < lp1->fieldcnt; ++cnt) |
||
487 |
if (F1->joinf != cnt) |
||
488 |
outfield(lp1, cnt, 0); |
||
489 |
for (cnt = 0; cnt < lp2->fieldcnt; ++cnt) |
||
490 |
if (F2->joinf != cnt) |
||
491 |
outfield(lp2, cnt, 0); |
||
492 |
} |
||
493 |
✓✗ | 102856 |
putchar('\n'); |
494 |
✓✗✗✓ ✗✗ |
102856 |
if (ferror(stdout)) |
495 |
err(1, "stdout"); |
||
496 |
51428 |
needsep = 0; |
|
497 |
51428 |
} |
|
498 |
|||
499 |
void |
||
500 |
outfield(LINE *lp, u_long fieldno, int out_empty) |
||
501 |
{ |
||
502 |
✓✓ | 205712 |
if (needsep++) |
503 |
✓✗ | 102856 |
putchar((int)*tabchar); |
504 |
✓✗✓✗ ✗✗ |
205712 |
if (!ferror(stdout)) { |
505 |
✗✓ | 102856 |
if (lp->fieldcnt <= fieldno || out_empty) { |
506 |
if (empty != NULL) |
||
507 |
fputs(empty, stdout); |
||
508 |
} else { |
||
509 |
✓✗ | 102856 |
if (*lp->fields[fieldno] == '\0') |
510 |
return; |
||
511 |
102856 |
fputs(lp->fields[fieldno], stdout); |
|
512 |
} |
||
513 |
} |
||
514 |
✓✗✗✓ ✗✗ |
205712 |
if (ferror(stdout)) |
515 |
err(1, "stdout"); |
||
516 |
102856 |
} |
|
517 |
|||
518 |
/* |
||
519 |
* Convert an output list argument "2.1, 1.3, 2.4" into an array of output |
||
520 |
* fields. |
||
521 |
*/ |
||
522 |
void |
||
523 |
fieldarg(char *option) |
||
524 |
{ |
||
525 |
u_long fieldno, filenum; |
||
526 |
122 |
char *end, *token; |
|
527 |
|||
528 |
✓✓ | 732 |
while ((token = strsep(&option, ", \t")) != NULL) { |
529 |
✗✓ | 244 |
if (*token == '\0') |
530 |
continue; |
||
531 |
✗✓ | 244 |
if (token[0] == '0') |
532 |
filenum = fieldno = 0; |
||
533 |
✓✓✓✗ ✓✗ |
610 |
else if ((token[0] == '1' || token[0] == '2') && |
534 |
244 |
token[1] == '.') { |
|
535 |
244 |
filenum = token[0] - '0'; |
|
536 |
244 |
fieldno = strtol(token + 2, &end, 10); |
|
537 |
✗✓ | 244 |
if (*end) |
538 |
errx(1, "malformed -o option field"); |
||
539 |
✗✓ | 244 |
if (fieldno == 0) |
540 |
errx(1, "field numbers are 1 based"); |
||
541 |
244 |
--fieldno; |
|
542 |
} else |
||
543 |
errx(1, "malformed -o option field"); |
||
544 |
✓✓ | 244 |
if (olistcnt == olistalloc) { |
545 |
OLIST *p; |
||
546 |
122 |
u_long newsize = olistalloc + 50; |
|
547 |
✗✓ | 244 |
if ((p = reallocarray(olist, newsize, sizeof(OLIST))) |
548 |
122 |
== NULL) |
|
549 |
err(1, NULL); |
||
550 |
122 |
olist = p; |
|
551 |
122 |
olistalloc = newsize; |
|
552 |
122 |
} |
|
553 |
244 |
olist[olistcnt].filenum = filenum; |
|
554 |
244 |
olist[olistcnt].fieldno = fieldno; |
|
555 |
244 |
++olistcnt; |
|
556 |
} |
||
557 |
122 |
} |
|
558 |
|||
559 |
void |
||
560 |
obsolete(char **argv) |
||
561 |
{ |
||
562 |
size_t len; |
||
563 |
char **p, *ap, *t; |
||
564 |
|||
565 |
✓✓ | 1220 |
while ((ap = *++argv) != NULL) { |
566 |
/* Return if "--". */ |
||
567 |
✓✓✗✓ |
854 |
if (ap[0] == '-' && ap[1] == '-') |
568 |
return; |
||
569 |
/* skip if not an option */ |
||
570 |
✓✓ | 610 |
if (ap[0] != '-') |
571 |
366 |
continue; |
|
572 |
✗✓✓✓ |
366 |
switch (ap[1]) { |
573 |
case 'a': |
||
574 |
/* |
||
575 |
* The original join allowed "-a", which meant the |
||
576 |
* same as -a1 plus -a2. POSIX 1003.2, Draft 11.2 |
||
577 |
* only specifies this as "-a 1" and "a -2", so we |
||
578 |
* have to use another option flag, one that is |
||
579 |
* unlikely to ever be used or accidentally entered |
||
580 |
* on the command line. (Well, we could reallocate |
||
581 |
* the argv array, but that hardly seems worthwhile.) |
||
582 |
*/ |
||
583 |
if (ap[2] == '\0' && (argv[1] == NULL || |
||
584 |
(strcmp(argv[1], "1") != 0 && |
||
585 |
strcmp(argv[1], "2") != 0))) { |
||
586 |
ap[1] = '\01'; |
||
587 |
warnx("-a option used without an argument; " |
||
588 |
"reverting to historical behavior"); |
||
589 |
} |
||
590 |
break; |
||
591 |
case 'j': |
||
592 |
/* |
||
593 |
* The original join allowed "-j[12] arg" and "-j arg". |
||
594 |
* Convert the former to "-[12] arg". Don't convert |
||
595 |
* the latter since getopt(3) can handle it. |
||
596 |
*/ |
||
597 |
✗✗✗✓ |
122 |
switch(ap[2]) { |
598 |
case '1': |
||
599 |
case '2': |
||
600 |
if (ap[3] != '\0') |
||
601 |
goto jbad; |
||
602 |
ap[1] = ap[2]; |
||
603 |
ap[2] = '\0'; |
||
604 |
break; |
||
605 |
case '\0': |
||
606 |
break; |
||
607 |
default: |
||
608 |
jbad: warnx("unknown option -- %s", ap + 1); |
||
609 |
usage(); |
||
610 |
} |
||
611 |
break; |
||
612 |
case 'o': |
||
613 |
/* |
||
614 |
* The original join allowed "-o arg arg". |
||
615 |
* Convert to "-o arg -o arg". |
||
616 |
*/ |
||
617 |
✓✗✓✗ |
244 |
if (ap[2] != '\0' || argv[1] == NULL) |
618 |
break; |
||
619 |
✓✗ | 244 |
for (p = argv + 2; *p != NULL; ++p) { |
620 |
✓✗✓✗ ✗✗ |
244 |
if (p[0][0] == '0' || ((p[0][0] != '1' && |
621 |
✗✓ | 122 |
p[0][0] != '2') || p[0][1] != '.')) |
622 |
break; |
||
623 |
len = strlen(*p); |
||
624 |
if (len - 2 != strspn(*p + 2, "0123456789")) |
||
625 |
break; |
||
626 |
if ((t = malloc(len + 3)) == NULL) |
||
627 |
err(1, NULL); |
||
628 |
t[0] = '-'; |
||
629 |
t[1] = 'o'; |
||
630 |
memmove(t + 2, *p, len + 1); |
||
631 |
*p = t; |
||
632 |
} |
||
633 |
122 |
argv = p - 1; |
|
634 |
122 |
break; |
|
635 |
} |
||
636 |
} |
||
637 |
244 |
} |
|
638 |
|||
639 |
void |
||
640 |
usage(void) |
||
641 |
{ |
||
642 |
int len; |
||
643 |
extern char *__progname; |
||
644 |
|||
645 |
len = strlen(__progname) + sizeof("usage: "); |
||
646 |
(void)fprintf(stderr, "usage: %s [-1 field] [-2 field] " |
||
647 |
"[-a file_number | -v file_number] [-e string]\n" |
||
648 |
"%*s[-o list] [-t char] file1 file2\n", |
||
649 |
__progname, len, ""); |
||
650 |
exit(1); |
||
651 |
} |
Generated by: GCOVR (Version 3.3) |