Print this page
10128 csplit should use strlcpy
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/csplit/csplit.c
+++ new/usr/src/cmd/csplit/csplit.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
↓ open down ↓ |
19 lines elided |
↑ open up ↑ |
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 */
25 25
26 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 27 /* All Rights Reserved */
28 28
29 29 /*
30 + * Copyright (c) 2018, Joyent, Inc.
31 + */
32 +
33 +/*
30 34 * csplit - Context or line file splitter
31 35 * Compile: cc -O -s -o csplit csplit.c
32 36 */
33 37
34 38 #include <stdio.h>
35 39 #include <stdlib.h>
36 40 #include <unistd.h>
37 41 #include <string.h>
38 42 #include <ctype.h>
39 43 #include <errno.h>
40 44 #include <limits.h>
41 45 #include <regexpr.h>
42 46 #include <signal.h>
43 47 #include <locale.h>
44 48 #include <libintl.h>
45 49
46 50 #define LAST 0LL
47 51 #define ERR -1
48 52 #define FALSE 0
49 53 #define TRUE 1
50 54 #define EXPMODE 2
51 55 #define LINMODE 3
52 56 #define LINSIZ LINE_MAX /* POSIX.2 - read lines LINE_MAX long */
53 57
54 58 /* Globals */
55 59
56 60 char linbuf[LINSIZ]; /* Input line buffer */
57 61 char *expbuf;
58 62 char tmpbuf[BUFSIZ]; /* Temporary buffer for stdin */
59 63 char file[8192] = "xx"; /* File name buffer */
60 64 char *targ; /* Arg ptr for error messages */
61 65 char *sptr;
62 66 FILE *infile, *outfile; /* I/O file streams */
63 67 int silent, keep, create; /* Flags: -s(ilent), -k(eep), (create) */
64 68 int errflg;
65 69 int fiwidth = 2; /* file index width (output file names) */
66 70 extern int optind;
67 71 extern char *optarg;
68 72 offset_t offset; /* Regular expression offset value */
69 73 offset_t curline; /* Current line in input file */
70 74
71 75 /*
72 76 * These defines are needed for regexp handling(see regexp(7))
73 77 */
74 78 #define PERROR(x) fatal("%s: Illegal Regular Expression\n", targ);
75 79
76 80 static int asc_to_ll(char *, long long *);
77 81 static void closefile(void);
78 82 static void fatal(char *, char *);
79 83 static offset_t findline(char *, offset_t);
80 84 static void flush(void);
81 85 static FILE *getfile(void);
82 86 static char *getaline(int);
83 87 static void line_arg(char *);
84 88 static void num_arg(char *, int);
85 89 static void re_arg(char *);
86 90 static void sig(int);
87 91 static void to_line(offset_t);
88 92 static void usage(void);
89 93
90 94 int
91 95 main(int argc, char **argv)
92 96 {
93 97 int ch, mode;
94 98 char *ptr;
95 99
96 100 (void) setlocale(LC_ALL, "");
97 101 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
98 102 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
99 103 #endif
100 104 (void) textdomain(TEXT_DOMAIN);
101 105
102 106 while ((ch = getopt(argc, argv, "skf:n:")) != EOF) {
103 107 switch (ch) {
104 108 case 'f':
105 109 (void) strcpy(file, optarg);
106 110 if ((ptr = strrchr(optarg, '/')) == NULL)
107 111 ptr = optarg;
108 112 else
109 113 ptr++;
110 114
111 115 break;
112 116 case 'n': /* POSIX.2 */
113 117 for (ptr = optarg; *ptr != NULL; ptr++)
114 118 if (!isdigit((int)*ptr))
115 119 fatal("-n num\n", NULL);
116 120 fiwidth = atoi(optarg);
117 121 break;
118 122 case 'k':
119 123 keep++;
120 124 break;
121 125 case 's':
122 126 silent++;
123 127 break;
124 128 case '?':
125 129 errflg++;
126 130 }
127 131 }
128 132
129 133 argv = &argv[optind];
130 134 argc -= optind;
131 135 if (argc <= 1 || errflg)
132 136 usage();
133 137
134 138 if (strcmp(*argv, "-") == 0) {
135 139 infile = tmpfile();
136 140
137 141 while (fread(tmpbuf, 1, BUFSIZ, stdin) != 0) {
138 142 if (fwrite(tmpbuf, 1, BUFSIZ, infile) == 0)
139 143 if (errno == ENOSPC) {
140 144 (void) fprintf(stderr, "csplit: ");
141 145 (void) fprintf(stderr, gettext(
142 146 "No space left on device\n"));
143 147 exit(1);
144 148 } else {
145 149 (void) fprintf(stderr, "csplit: ");
146 150 (void) fprintf(stderr, gettext(
147 151 "Bad write to temporary "
148 152 "file\n"));
149 153 exit(1);
150 154 }
151 155
152 156 /* clear the buffer to get correct size when writing buffer */
153 157
154 158 (void) memset(tmpbuf, '\0', sizeof (tmpbuf));
155 159 }
156 160 rewind(infile);
157 161 } else if ((infile = fopen(*argv, "r")) == NULL)
158 162 fatal("Cannot open %s\n", *argv);
159 163 ++argv;
160 164 curline = (offset_t)1;
161 165 (void) signal(SIGINT, sig);
162 166
163 167 /*
164 168 * The following for loop handles the different argument types.
165 169 * A switch is performed on the first character of the argument
166 170 * and each case calls the appropriate argument handling routine.
167 171 */
168 172
169 173 for (; *argv; ++argv) {
170 174 targ = *argv;
171 175 switch (**argv) {
172 176 case '/':
173 177 mode = EXPMODE;
174 178 create = TRUE;
175 179 re_arg(*argv);
176 180 break;
177 181 case '%':
178 182 mode = EXPMODE;
179 183 create = FALSE;
180 184 re_arg(*argv);
181 185 break;
182 186 case '{':
183 187 num_arg(*argv, mode);
184 188 mode = FALSE;
185 189 break;
186 190 default:
187 191 mode = LINMODE;
188 192 create = TRUE;
189 193 line_arg(*argv);
190 194 break;
191 195 }
192 196 }
193 197 create = TRUE;
194 198 to_line(LAST);
195 199 return (0);
196 200 }
197 201
198 202 /*
199 203 * asc_to_ll takes an ascii argument(str) and converts it to a long long(plc)
200 204 * It returns ERR if an illegal character. The reason that asc_to_ll
201 205 * does not return an answer(long long) is that any value for the long
202 206 * long is legal, and this version of asc_to_ll detects error strings.
203 207 */
204 208
205 209 static int
206 210 asc_to_ll(char *str, long long *plc)
207 211 {
208 212 int f;
209 213 *plc = 0;
210 214 f = 0;
211 215 for (; ; str++) {
212 216 switch (*str) {
213 217 case ' ':
214 218 case '\t':
215 219 continue;
216 220 case '-':
217 221 f++;
218 222 /* FALLTHROUGH */
219 223 case '+':
220 224 str++;
221 225 }
222 226 break;
223 227 }
224 228 for (; *str != NULL; str++)
225 229 if (*str >= '0' && *str <= '9')
226 230 *plc = *plc * 10 + *str - '0';
227 231 else
228 232 return (ERR);
229 233 if (f)
230 234 *plc = -(*plc);
231 235 return (TRUE); /* not error */
232 236 }
233 237
234 238 /*
235 239 * Closefile prints the byte count of the file created,(via fseeko
236 240 * and ftello), if the create flag is on and the silent flag is not on.
237 241 * If the create flag is on closefile then closes the file(fclose).
238 242 */
239 243
240 244 static void
241 245 closefile()
242 246 {
243 247 if (!silent && create) {
244 248 (void) fseeko(outfile, (offset_t)0, SEEK_END);
245 249 (void) fprintf(stdout, "%lld\n", (offset_t)ftello(outfile));
246 250 }
247 251 if (create)
248 252 (void) fclose(outfile);
249 253 }
250 254
251 255 /*
252 256 * Fatal handles error messages and cleanup.
253 257 * Because "arg" can be the global file, and the cleanup processing
254 258 * uses the global file, the error message is printed first. If the
255 259 * "keep" flag is not set, fatal unlinks all created files. If the
256 260 * "keep" flag is set, fatal closes the current file(if there is one).
257 261 * Fatal exits with a value of 1.
258 262 */
259 263
260 264 static void
261 265 fatal(char *string, char *arg)
262 266 {
263 267 char *fls;
264 268 int num;
265 269
266 270 (void) fprintf(stderr, "csplit: ");
267 271
268 272 /* gettext dynamically replaces string */
269 273
270 274 (void) fprintf(stderr, gettext(string), arg);
271 275 if (!keep) {
272 276 if (outfile) {
273 277 (void) fclose(outfile);
274 278 for (fls = file; *fls != '\0'; fls++)
275 279 continue;
276 280 fls -= fiwidth;
277 281 for (num = atoi(fls); num >= 0; num--) {
278 282 (void) sprintf(fls, "%.*d", fiwidth, num);
279 283 (void) unlink(file);
280 284 }
281 285 }
282 286 } else
283 287 if (outfile)
284 288 closefile();
285 289 exit(1);
286 290 }
287 291
288 292 /*
289 293 * Findline returns the line number referenced by the current argument.
290 294 * Its arguments are a pointer to the compiled regular expression(expr),
291 295 * and an offset(oset). The variable lncnt is used to count the number
292 296 * of lines searched. First the current stream location is saved via
293 297 * ftello(), and getaline is called so that R.E. searching starts at the
294 298 * line after the previously referenced line. The while loop checks
295 299 * that there are more lines(error if none), bumps the line count, and
296 300 * checks for the R.E. on each line. If the R.E. matches on one of the
297 301 * lines the old stream location is restored, and the line number
298 302 * referenced by the R.E. and the offset is returned.
299 303 */
300 304
301 305 static offset_t
302 306 findline(char *expr, offset_t oset)
303 307 {
304 308 static int benhere = 0;
305 309 offset_t lncnt = 0, saveloc;
306 310
307 311 saveloc = ftello(infile);
308 312 if (curline != (offset_t)1 || benhere) /* If first line, first time, */
309 313 (void) getaline(FALSE); /* then don't skip */
310 314 else
311 315 lncnt--;
312 316 benhere = 1;
313 317 while (getaline(FALSE) != NULL) {
314 318 lncnt++;
315 319 if ((sptr = strrchr(linbuf, '\n')) != NULL)
316 320 *sptr = '\0';
317 321 if (step(linbuf, expr)) {
318 322 (void) fseeko(infile, (offset_t)saveloc, SEEK_SET);
319 323 return (curline+lncnt+oset);
320 324 }
321 325 }
322 326 (void) fseeko(infile, (offset_t)saveloc, SEEK_SET);
323 327 return (curline+lncnt+oset+2);
324 328 }
325 329
326 330 /*
327 331 * Flush uses fputs to put lines on the output file stream(outfile)
328 332 * Since fputs does its own buffering, flush doesn't need to.
329 333 * Flush does nothing if the create flag is not set.
330 334 */
331 335
332 336 static void
333 337 flush()
334 338 {
335 339 if (create)
336 340 (void) fputs(linbuf, outfile);
337 341 }
338 342
339 343 /*
340 344 * Getfile does nothing if the create flag is not set. If the create
341 345 * flag is set, getfile positions the file pointer(fptr) at the end of
342 346 * the file name prefix on the first call(fptr=0). The file counter is
343 347 * stored in the file name and incremented. If the subsequent fopen
344 348 * fails, the file name is copied to tfile for the error message, the
345 349 * previous file name is restored for cleanup, and fatal is called. If
346 350 * the fopen succeeds, the stream(opfil) is returned.
347 351 */
348 352
349 353 FILE *
350 354 getfile()
351 355 {
352 356 static char *fptr;
353 357 static int ctr;
354 358 FILE *opfil;
355 359 char tfile[15];
356 360 char *delim;
357 361 char savedelim;
358 362
359 363 if (create) {
360 364 if (fptr == 0)
361 365 for (fptr = file; *fptr != NULL; fptr++)
362 366 continue;
363 367 (void) sprintf(fptr, "%.*d", fiwidth, ctr++);
364 368
365 369 /* check for suffix length overflow */
366 370 if (strlen(fptr) > fiwidth) {
367 371 fatal("Suffix longer than %ld chars; increase -n\n",
368 372 (char *)fiwidth);
369 373 }
370 374
371 375 /* check for filename length overflow */
372 376
373 377 delim = strrchr(file, '/');
374 378 if (delim == (char *)NULL) {
375 379 if (strlen(file) > pathconf(".", _PC_NAME_MAX)) {
376 380 fatal("Name too long: %s\n", file);
377 381 }
378 382 } else {
379 383 /* truncate file at pathname delim to do pathconf */
380 384 savedelim = *delim;
381 385 *delim = '\0';
382 386 /*
383 387 * file: pppppppp\0fffff\0
↓ open down ↓ |
344 lines elided |
↑ open up ↑ |
384 388 * ..... ^ file
385 389 * ............. ^ delim
386 390 */
387 391 if (strlen(delim + 1) > pathconf(file, _PC_NAME_MAX)) {
388 392 fatal("Name too long: %s\n", delim + 1);
389 393 }
390 394 *delim = savedelim;
391 395 }
392 396
393 397 if ((opfil = fopen(file, "w")) == NULL) {
394 - (void) strcpy(tfile, file);
398 + (void) strlcpy(tfile, file, sizeof (tfile));
395 399 (void) sprintf(fptr, "%.*d", fiwidth, (ctr-2));
396 400 fatal("Cannot create %s\n", tfile);
397 401 }
398 402 return (opfil);
399 403 }
400 404 return (NULL);
401 405 }
402 406
403 407 /*
404 408 * Getline gets a line via fgets from the input stream "infile".
405 409 * The line is put into linbuf and may not be larger than LINSIZ.
406 410 * If getaline is called with a non-zero value, the current line
407 411 * is bumped, otherwise it is not(for R.E. searching).
408 412 */
409 413
410 414 static char *
411 415 getaline(int bumpcur)
412 416 {
413 417 char *ret;
414 418 if (bumpcur)
415 419 curline++;
416 420 ret = fgets(linbuf, LINSIZ, infile);
417 421 return (ret);
418 422 }
419 423
420 424 /*
421 425 * Line_arg handles line number arguments.
422 426 * line_arg takes as its argument a pointer to a character string
423 427 * (assumed to be a line number). If that character string can be
424 428 * converted to a number(long long), to_line is called with that number,
425 429 * otherwise error.
426 430 */
427 431
428 432 static void
429 433 line_arg(char *line)
430 434 {
431 435 long long to;
432 436
433 437 if (asc_to_ll(line, &to) == ERR)
434 438 fatal("%s: bad line number\n", line);
435 439 to_line(to);
436 440 }
437 441
438 442 /*
439 443 * Num_arg handles repeat arguments.
440 444 * Num_arg copies the numeric argument to "rep" (error if number is
441 445 * larger than 20 characters or } is left off). Num_arg then converts
442 446 * the number and checks for validity. Next num_arg checks the mode
443 447 * of the previous argument, and applys the argument the correct number
444 448 * of times. If the mode is not set properly its an error.
445 449 */
446 450
447 451 static void
448 452 num_arg(char *arg, int md)
449 453 {
450 454 offset_t repeat, toline;
451 455 char rep[21];
452 456 char *ptr;
453 457 int len;
454 458
455 459 ptr = rep;
456 460 for (++arg; *arg != '}'; arg += len) {
457 461 if (*arg == NULL)
458 462 fatal("%s: missing '}'\n", targ);
459 463 if ((len = mblen(arg, MB_LEN_MAX)) <= 0)
460 464 len = 1;
461 465 if ((ptr + len) >= &rep[20])
462 466 fatal("%s: Repeat count too large\n", targ);
463 467 (void) memcpy(ptr, arg, len);
464 468 ptr += len;
465 469 }
466 470 *ptr = NULL;
467 471 if ((asc_to_ll(rep, &repeat) == ERR) || repeat < 0L)
468 472 fatal("Illegal repeat count: %s\n", targ);
469 473 if (md == LINMODE) {
470 474 toline = offset = curline;
471 475 for (; repeat > 0LL; repeat--) {
472 476 toline += offset;
473 477 to_line(toline);
474 478 }
475 479 } else if (md == EXPMODE)
476 480 for (; repeat > 0LL; repeat--)
477 481 to_line(findline(expbuf, offset));
478 482 else
479 483 fatal("No operation for %s\n", targ);
480 484 }
481 485
482 486 /*
483 487 * Re_arg handles regular expression arguments.
484 488 * Re_arg takes a csplit regular expression argument. It checks for
485 489 * delimiter balance, computes any offset, and compiles the regular
486 490 * expression. Findline is called with the compiled expression and
487 491 * offset, and returns the corresponding line number, which is used
488 492 * as input to the to_line function.
489 493 */
490 494
491 495 static void
492 496 re_arg(char *string)
493 497 {
494 498 char *ptr;
495 499 char ch;
496 500 int len;
497 501
498 502 ch = *string;
499 503 ptr = string;
500 504 ptr++;
501 505 while (*ptr != ch) {
502 506 if (*ptr == '\\')
503 507 ++ptr;
504 508
505 509 if (*ptr == NULL)
506 510 fatal("%s: missing delimiter\n", targ);
507 511
508 512 if ((len = mblen(ptr, MB_LEN_MAX)) <= 0)
509 513 len = 1;
510 514 ptr += len;
511 515 }
512 516
513 517 /*
514 518 * The line below was added because compile no longer supports
515 519 * the fourth argument being passed. The fourth argument used
516 520 * to be '/' or '%'.
517 521 */
518 522
519 523 *ptr = NULL;
520 524 if (asc_to_ll(++ptr, &offset) == ERR)
521 525 fatal("%s: illegal offset\n", string);
522 526
523 527 /*
524 528 * The line below was added because INIT which did this for us
525 529 * was removed from compile in regexp.h
526 530 */
527 531
528 532 string++;
529 533 expbuf = compile(string, (char *)0, (char *)0);
530 534 if (regerrno)
531 535 PERROR(regerrno);
532 536 to_line(findline(expbuf, offset));
533 537 }
534 538
535 539 /*
536 540 * Sig handles breaks. When a break occurs the signal is reset,
537 541 * and fatal is called to clean up and print the argument which
538 542 * was being processed at the time the interrupt occured.
539 543 */
540 544
541 545 /* ARGSUSED */
542 546 static void
543 547 sig(int s)
544 548 {
545 549 (void) signal(SIGINT, sig);
546 550 fatal("Interrupt - program aborted at arg '%s'\n", targ);
547 551 }
548 552
549 553 /*
550 554 * To_line creates split files.
551 555 * To_line gets as its argument the line which the current argument
552 556 * referenced. To_line calls getfile for a new output stream, which
553 557 * does nothing if create is False. If to_line's argument is not LAST
554 558 * it checks that the current line is not greater than its argument.
555 559 * While the current line is less than the desired line to_line gets
556 560 * lines and flushes(error if EOF is reached).
557 561 * If to_line's argument is LAST, it checks for more lines, and gets
558 562 * and flushes lines till the end of file.
559 563 * Finally, to_line calls closefile to close the output stream.
560 564 */
561 565
562 566 static void
563 567 to_line(offset_t ln)
564 568 {
565 569 outfile = getfile();
566 570 if (ln != LAST) {
567 571 if (curline > ln)
568 572 fatal("%s - out of range\n", targ);
569 573 while (curline < ln) {
570 574 if (getaline(TRUE) == NULL)
571 575 fatal("%s - out of range\n", targ);
572 576 flush();
573 577 }
574 578 } else /* last file */
575 579 if (getaline(TRUE) != NULL) {
576 580 flush();
577 581 for (;;) {
578 582 if (getaline(TRUE) == NULL)
579 583 break;
580 584 flush();
581 585 }
582 586 } else
583 587 fatal("%s - out of range\n", targ);
584 588 closefile();
585 589 }
586 590
587 591 static void
588 592 usage()
589 593 {
590 594 (void) fprintf(stderr, gettext(
591 595 "usage: csplit [-ks] [-f prefix] [-n number] "
592 596 "file arg1 ...argn\n"));
593 597 exit(1);
594 598 }
↓ open down ↓ |
190 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX