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