| File: | src/usr.bin/sed/main.c |
| Warning: | line 152, column 2 Value stored to 'argc' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: main.c,v 1.42 2021/01/31 14:23:05 naddy Exp $ */ |
| 2 | |
| 3 | /*- |
| 4 | * Copyright (c) 1992 Diomidis Spinellis. |
| 5 | * Copyright (c) 1992, 1993 |
| 6 | * The Regents of the University of California. All rights reserved. |
| 7 | * |
| 8 | * This code is derived from software contributed to Berkeley by |
| 9 | * Diomidis Spinellis of Imperial College, University of London. |
| 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 <sys/types.h> |
| 37 | #include <sys/ioctl.h> |
| 38 | #include <sys/stat.h> |
| 39 | |
| 40 | #include <ctype.h> |
| 41 | #include <errno(*__errno()).h> |
| 42 | #include <fcntl.h> |
| 43 | #include <limits.h> |
| 44 | #include <regex.h> |
| 45 | #include <stddef.h> |
| 46 | #include <stdio.h> |
| 47 | #include <stdlib.h> |
| 48 | #include <string.h> |
| 49 | #include <unistd.h> |
| 50 | #include <libgen.h> |
| 51 | |
| 52 | #include "defs.h" |
| 53 | #include "extern.h" |
| 54 | |
| 55 | /* |
| 56 | * Linked list of units (strings and files) to be compiled |
| 57 | */ |
| 58 | struct s_compunit { |
| 59 | struct s_compunit *next; |
| 60 | enum e_cut {CU_FILE, CU_STRING} type; |
| 61 | char *s; /* Pointer to string or fname */ |
| 62 | }; |
| 63 | |
| 64 | /* |
| 65 | * Linked list pointer to compilation units and pointer to current |
| 66 | * next pointer. |
| 67 | */ |
| 68 | static struct s_compunit *script, **cu_nextp = &script; |
| 69 | |
| 70 | /* |
| 71 | * Linked list of files to be processed |
| 72 | */ |
| 73 | struct s_flist { |
| 74 | char *fname; |
| 75 | struct s_flist *next; |
| 76 | }; |
| 77 | |
| 78 | /* |
| 79 | * Linked list pointer to files and pointer to current |
| 80 | * next pointer. |
| 81 | */ |
| 82 | static struct s_flist *files, **fl_nextp = &files; |
| 83 | |
| 84 | FILE *infile; /* Current input file */ |
| 85 | FILE *outfile; /* Current output file */ |
| 86 | |
| 87 | int Eflag, aflag, eflag, nflag; |
| 88 | static int rval; /* Exit status */ |
| 89 | |
| 90 | /* |
| 91 | * Current file and line number; line numbers restart across compilation |
| 92 | * units, but span across input files. The latter is optional if editing |
| 93 | * in place. |
| 94 | */ |
| 95 | const char *fname; /* File name. */ |
| 96 | const char *outfname; /* Output file name */ |
| 97 | static char oldfname[PATH_MAX1024]; /* Old file name (for in-place editing) */ |
| 98 | static char tmpfname[PATH_MAX1024]; /* Temporary file name (for in-place editing) */ |
| 99 | char *inplace; /* Inplace edit file extension */ |
| 100 | u_long linenum; |
| 101 | |
| 102 | static void add_compunit(enum e_cut, char *); |
| 103 | static void add_file(char *); |
| 104 | static int next_files_have_lines(void); |
| 105 | |
| 106 | int termwidth; |
| 107 | |
| 108 | int pledge_wpath, pledge_rpath; |
| 109 | |
| 110 | int |
| 111 | main(int argc, char *argv[]) |
| 112 | { |
| 113 | struct winsize win; |
| 114 | int c, fflag; |
| 115 | char *p; |
| 116 | |
| 117 | fflag = 0; |
| 118 | inplace = NULL((void*)0); |
| 119 | while ((c = getopt(argc, argv, "Eae:f:i::nru")) != -1) |
| 120 | switch (c) { |
| 121 | case 'E': |
| 122 | case 'r': |
| 123 | Eflag = 1; |
| 124 | break; |
| 125 | case 'a': |
| 126 | aflag = 1; |
| 127 | break; |
| 128 | case 'e': |
| 129 | eflag = 1; |
| 130 | add_compunit(CU_STRING, optarg); |
| 131 | break; |
| 132 | case 'f': |
| 133 | fflag = 1; |
| 134 | add_compunit(CU_FILE, optarg); |
| 135 | break; |
| 136 | case 'i': |
| 137 | inplace = optarg ? optarg : ""; |
| 138 | break; |
| 139 | case 'n': |
| 140 | nflag = 1; |
| 141 | break; |
| 142 | case 'u': |
| 143 | setvbuf(stdout(&__sF[1]), NULL((void*)0), _IOLBF1, 0); |
| 144 | break; |
| 145 | default: |
| 146 | case '?': |
| 147 | (void)fprintf(stderr(&__sF[2]), |
| 148 | "usage: sed [-aEnru] [-i[extension]] command [file ...]\n" |
| 149 | " sed [-aEnru] [-e command] [-f command_file] [-i[extension]] [file ...]\n"); |
| 150 | exit(1); |
| 151 | } |
| 152 | argc -= optind; |
Value stored to 'argc' is never read | |
| 153 | argv += optind; |
| 154 | |
| 155 | termwidth = 0; |
| 156 | if ((p = getenv("COLUMNS")) != NULL((void*)0)) |
| 157 | termwidth = strtonum(p, 0, INT_MAX2147483647, NULL((void*)0)); |
| 158 | if (termwidth == 0 && ioctl(STDOUT_FILENO1, TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff ) << 16) | ((('t')) << 8) | ((104))), &win) == 0 && |
| 159 | win.ws_col > 0) |
| 160 | termwidth = win.ws_col; |
| 161 | if (termwidth == 0) |
| 162 | termwidth = 80; |
| 163 | if (termwidth <= 8) |
| 164 | termwidth = 1; |
| 165 | else |
| 166 | termwidth -= 8; |
| 167 | |
| 168 | if (inplace != NULL((void*)0)) { |
| 169 | if (pledge("stdio rpath wpath cpath fattr chown", NULL((void*)0)) == -1) |
| 170 | error(FATAL1, "pledge: %s", strerror(errno(*__errno()))); |
| 171 | } else { |
| 172 | if (pledge("stdio rpath wpath cpath", NULL((void*)0)) == -1) |
| 173 | error(FATAL1, "pledge: %s", strerror(errno(*__errno()))); |
| 174 | } |
| 175 | |
| 176 | /* First usage case; script is the first arg */ |
| 177 | if (!eflag && !fflag && *argv) { |
| 178 | add_compunit(CU_STRING, *argv); |
| 179 | argv++; |
| 180 | } |
| 181 | |
| 182 | compile(); |
| 183 | |
| 184 | /* Continue with first and start second usage */ |
| 185 | if (*argv) { |
| 186 | if (!pledge_wpath && inplace == NULL((void*)0)) { |
| 187 | if (pledge("stdio rpath", NULL((void*)0)) == -1) |
| 188 | error(FATAL1, "pledge: %s", strerror(errno(*__errno()))); |
| 189 | } |
| 190 | for (; *argv; argv++) |
| 191 | add_file(*argv); |
| 192 | } else { |
| 193 | if (!pledge_wpath && !pledge_rpath) { |
| 194 | if (pledge("stdio", NULL((void*)0)) == -1) |
| 195 | error(FATAL1, "pledge: %s", strerror(errno(*__errno()))); |
| 196 | } else if (pledge_rpath) { |
| 197 | if (pledge("stdio rpath", NULL((void*)0)) == -1) |
| 198 | error(FATAL1, "pledge: %s", strerror(errno(*__errno()))); |
| 199 | } else if (pledge_wpath) { |
| 200 | if (pledge("stdio wpath cpath", NULL((void*)0)) == -1) |
| 201 | error(FATAL1, "pledge: %s", strerror(errno(*__errno()))); |
| 202 | } |
| 203 | add_file(NULL((void*)0)); |
| 204 | } |
| 205 | process(); |
| 206 | cfclose(prog, NULL((void*)0)); |
| 207 | if (fclose(stdout(&__sF[1]))) |
| 208 | error(FATAL1, "stdout: %s", strerror(errno(*__errno()))); |
| 209 | exit (rval); |
| 210 | } |
| 211 | |
| 212 | /* |
| 213 | * Like fgets, but go through the chain of compilation units chaining them |
| 214 | * together. Empty strings and files are ignored. |
| 215 | */ |
| 216 | char * |
| 217 | cu_fgets(char **outbuf, size_t *outsize) |
| 218 | { |
| 219 | static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; |
| 220 | static FILE *f; /* Current open file */ |
| 221 | static char *s; /* Current pointer inside string */ |
| 222 | static char string_ident[30]; |
| 223 | size_t len; |
| 224 | char *p; |
| 225 | |
| 226 | if (*outbuf == NULL((void*)0)) |
| 227 | *outsize = 0; |
| 228 | |
| 229 | again: |
| 230 | switch (state) { |
| 231 | case ST_EOF: |
| 232 | if (script == NULL((void*)0)) |
| 233 | return (NULL((void*)0)); |
| 234 | linenum = 0; |
| 235 | switch (script->type) { |
| 236 | case CU_FILE: |
| 237 | if ((f = fopen(script->s, "r")) == NULL((void*)0)) |
| 238 | error(FATAL1, |
| 239 | "%s: %s", script->s, strerror(errno(*__errno()))); |
| 240 | fname = script->s; |
| 241 | state = ST_FILE; |
| 242 | goto again; |
| 243 | case CU_STRING: |
| 244 | len = snprintf(string_ident, sizeof(string_ident), |
| 245 | "\"%s\"", script->s); |
| 246 | if (len >= sizeof(string_ident)) |
| 247 | strlcpy(string_ident + |
| 248 | sizeof(string_ident) - 6, " ...\"", 5); |
| 249 | fname = string_ident; |
| 250 | s = script->s; |
| 251 | state = ST_STRING; |
| 252 | goto again; |
| 253 | } |
| 254 | case ST_FILE: |
| 255 | if (getline(outbuf, outsize, f) != -1) { |
| 256 | p = *outbuf; |
| 257 | linenum++; |
| 258 | if (linenum == 1 && p[0] == '#' && p[1] == 'n') |
| 259 | nflag = 1; |
| 260 | return (*outbuf); |
| 261 | } |
| 262 | script = script->next; |
| 263 | (void)fclose(f); |
| 264 | state = ST_EOF; |
| 265 | goto again; |
| 266 | case ST_STRING: |
| 267 | if (linenum == 0 && s[0] == '#' && s[1] == 'n') |
| 268 | nflag = 1; |
| 269 | p = *outbuf; |
| 270 | len = *outsize; |
| 271 | for (;;) { |
| 272 | if (len <= 1) { |
| 273 | *outbuf = xrealloc(*outbuf, |
| 274 | *outsize + _POSIX2_LINE_MAX2048); |
| 275 | p = *outbuf + *outsize - len; |
| 276 | len += _POSIX2_LINE_MAX2048; |
| 277 | *outsize += _POSIX2_LINE_MAX2048; |
| 278 | } |
| 279 | switch (*s) { |
| 280 | case '\0': |
| 281 | state = ST_EOF; |
| 282 | if (s == script->s) { |
| 283 | script = script->next; |
| 284 | goto again; |
| 285 | } else { |
| 286 | script = script->next; |
| 287 | *p = '\0'; |
| 288 | linenum++; |
| 289 | return (*outbuf); |
| 290 | } |
| 291 | case '\n': |
| 292 | *p++ = '\n'; |
| 293 | *p = '\0'; |
| 294 | s++; |
| 295 | linenum++; |
| 296 | return (*outbuf); |
| 297 | default: |
| 298 | *p++ = *s++; |
| 299 | len--; |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | return (NULL((void*)0)); |
| 305 | } |
| 306 | |
| 307 | void |
| 308 | finish_file(void) |
| 309 | { |
| 310 | if (infile != NULL((void*)0)) { |
| 311 | fclose(infile); |
| 312 | if (*oldfname != '\0') { |
| 313 | if (rename(fname, oldfname) != 0) { |
| 314 | warning("rename()"); |
| 315 | unlink(tmpfname); |
| 316 | exit(1); |
| 317 | } |
| 318 | *oldfname = '\0'; |
| 319 | } |
| 320 | if (*tmpfname != '\0') { |
| 321 | if (outfile != NULL((void*)0) && outfile != stdout(&__sF[1])) |
| 322 | fclose(outfile); |
| 323 | outfile = NULL((void*)0); |
| 324 | rename(tmpfname, fname); |
| 325 | *tmpfname = '\0'; |
| 326 | } |
| 327 | outfname = NULL((void*)0); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | * Like fgets, but go through the list of files chaining them together. |
| 333 | * Set len to the length of the line. |
| 334 | */ |
| 335 | int |
| 336 | mf_fgets(SPACE *sp, enum e_spflag spflag) |
| 337 | { |
| 338 | struct stat sb; |
| 339 | size_t len; |
| 340 | char dirbuf[PATH_MAX1024]; |
| 341 | static char *p; |
| 342 | static size_t psize; |
| 343 | int c, fd; |
| 344 | static int firstfile; |
| 345 | |
| 346 | if (infile == NULL((void*)0)) { |
| 347 | /* stdin? */ |
| 348 | if (files->fname == NULL((void*)0)) { |
| 349 | if (inplace != NULL((void*)0)) |
| 350 | error(FATAL1, "-i may not be used with stdin"); |
| 351 | infile = stdin(&__sF[0]); |
| 352 | fname = "stdin"; |
| 353 | outfile = stdout(&__sF[1]); |
| 354 | outfname = "stdout"; |
| 355 | } |
| 356 | |
| 357 | firstfile = 1; |
| 358 | } |
| 359 | |
| 360 | for (;;) { |
| 361 | if (infile != NULL((void*)0) && (c = getc(infile)(!__isthreaded ? (--(infile)->_r < 0 ? __srget(infile) : (int)(*(infile)->_p++)) : (getc)(infile))) != EOF(-1)) { |
| 362 | (void)ungetc(c, infile); |
| 363 | break; |
| 364 | } |
| 365 | /* If we are here then either eof or no files are open yet */ |
| 366 | if (infile == stdin(&__sF[0])) { |
| 367 | sp->len = 0; |
| 368 | return (0); |
| 369 | } |
| 370 | finish_file(); |
| 371 | if (firstfile == 0) |
| 372 | files = files->next; |
| 373 | else |
| 374 | firstfile = 0; |
| 375 | if (files == NULL((void*)0)) { |
| 376 | sp->len = 0; |
| 377 | return (0); |
| 378 | } |
| 379 | fname = files->fname; |
| 380 | if (inplace != NULL((void*)0)) { |
| 381 | if (lstat(fname, &sb) != 0) |
| 382 | error(FATAL1, "%s: %s", fname, |
| 383 | strerror(errno(*__errno()) ? errno(*__errno()) : EIO5)); |
| 384 | if (!S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000)) |
| 385 | error(FATAL1, "%s: %s %s", fname, |
| 386 | "in-place editing only", |
| 387 | "works for regular files"); |
| 388 | if (*inplace != '\0') { |
| 389 | strlcpy(oldfname, fname, |
| 390 | sizeof(oldfname)); |
| 391 | len = strlcat(oldfname, inplace, |
| 392 | sizeof(oldfname)); |
| 393 | if (len > sizeof(oldfname)) |
| 394 | error(FATAL1, "%s: name too long", fname); |
| 395 | } |
| 396 | strlcpy(dirbuf, fname, sizeof(dirbuf)); |
| 397 | len = snprintf(tmpfname, sizeof(tmpfname), |
| 398 | "%s/sedXXXXXXXXXX", dirname(dirbuf)); |
| 399 | if (len >= sizeof(tmpfname)) |
| 400 | error(FATAL1, "%s: name too long", fname); |
| 401 | if ((fd = mkstemp(tmpfname)) == -1) |
| 402 | error(FATAL1, "%s: %s", fname, strerror(errno(*__errno()))); |
| 403 | if ((outfile = fdopen(fd, "w")) == NULL((void*)0)) { |
| 404 | unlink(tmpfname); |
| 405 | error(FATAL1, "%s", fname); |
| 406 | } |
| 407 | fchown(fileno(outfile)(!__isthreaded ? ((outfile)->_file) : (fileno)(outfile)), sb.st_uid, sb.st_gid); |
| 408 | fchmod(fileno(outfile)(!__isthreaded ? ((outfile)->_file) : (fileno)(outfile)), sb.st_mode & ALLPERMS(0004000|0002000|0001000|0000700|0000070|0000007)); |
| 409 | outfname = tmpfname; |
| 410 | linenum = 0; |
| 411 | resetstate(); |
| 412 | } else { |
| 413 | outfile = stdout(&__sF[1]); |
| 414 | outfname = "stdout"; |
| 415 | } |
| 416 | if ((infile = fopen(fname, "r")) == NULL((void*)0)) { |
| 417 | warning("%s", strerror(errno(*__errno()))); |
| 418 | rval = 1; |
| 419 | continue; |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | /* |
| 424 | * We are here only when infile is open and we still have something |
| 425 | * to read from it. |
| 426 | * |
| 427 | * Use getline() so that we can handle essentially infinite input |
| 428 | * data. The p and psize are static so each invocation gives |
| 429 | * getline() the same buffer which is expanded as needed. |
| 430 | */ |
| 431 | len = getline(&p, &psize, infile); |
| 432 | if ((ssize_t)len == -1) |
| 433 | error(FATAL1, "%s: %s", fname, strerror(errno(*__errno()))); |
| 434 | if (len != 0 && p[len - 1] == '\n') { |
| 435 | sp->append_newline = 1; |
| 436 | len--; |
| 437 | } else if (!lastline()) { |
| 438 | sp->append_newline = 1; |
| 439 | } else { |
| 440 | sp->append_newline = 0; |
| 441 | } |
| 442 | cspace(sp, p, len, spflag); |
| 443 | |
| 444 | linenum++; |
| 445 | |
| 446 | return (1); |
| 447 | } |
| 448 | |
| 449 | /* |
| 450 | * Add a compilation unit to the linked list |
| 451 | */ |
| 452 | static void |
| 453 | add_compunit(enum e_cut type, char *s) |
| 454 | { |
| 455 | struct s_compunit *cu; |
| 456 | |
| 457 | cu = xmalloc(sizeof(struct s_compunit)); |
| 458 | cu->type = type; |
| 459 | cu->s = s; |
| 460 | cu->next = NULL((void*)0); |
| 461 | *cu_nextp = cu; |
| 462 | cu_nextp = &cu->next; |
| 463 | } |
| 464 | |
| 465 | /* |
| 466 | * Add a file to the linked list |
| 467 | */ |
| 468 | static void |
| 469 | add_file(char *s) |
| 470 | { |
| 471 | struct s_flist *fp; |
| 472 | |
| 473 | fp = xmalloc(sizeof(struct s_flist)); |
| 474 | fp->next = NULL((void*)0); |
| 475 | *fl_nextp = fp; |
| 476 | fp->fname = s; |
| 477 | fl_nextp = &fp->next; |
| 478 | } |
| 479 | |
| 480 | |
| 481 | static int |
| 482 | next_files_have_lines() |
| 483 | { |
| 484 | struct s_flist *file; |
| 485 | FILE *file_fd; |
| 486 | int ch; |
| 487 | |
| 488 | file = files; |
| 489 | while ((file = file->next) != NULL((void*)0)) { |
| 490 | if ((file_fd = fopen(file->fname, "r")) == NULL((void*)0)) |
| 491 | continue; |
| 492 | |
| 493 | if ((ch = getc(file_fd)(!__isthreaded ? (--(file_fd)->_r < 0 ? __srget(file_fd ) : (int)(*(file_fd)->_p++)) : (getc)(file_fd))) != EOF(-1)) { |
| 494 | /* |
| 495 | * This next file has content, therefore current |
| 496 | * file doesn't contains the last line. |
| 497 | */ |
| 498 | ungetc(ch, file_fd); |
| 499 | fclose(file_fd); |
| 500 | return (1); |
| 501 | } |
| 502 | fclose(file_fd); |
| 503 | } |
| 504 | return (0); |
| 505 | } |
| 506 | |
| 507 | int |
| 508 | lastline(void) |
| 509 | { |
| 510 | int ch; |
| 511 | |
| 512 | if (feof(infile)(!__isthreaded ? (((infile)->_flags & 0x0020) != 0) : ( feof)(infile))) { |
| 513 | return !( |
| 514 | (inplace == NULL((void*)0)) && |
| 515 | next_files_have_lines()); |
| 516 | } |
| 517 | if ((ch = getc(infile)(!__isthreaded ? (--(infile)->_r < 0 ? __srget(infile) : (int)(*(infile)->_p++)) : (getc)(infile))) == EOF(-1)) { |
| 518 | return !( |
| 519 | (inplace == NULL((void*)0)) && |
| 520 | next_files_have_lines()); |
| 521 | } |
| 522 | ungetc(ch, infile); |
| 523 | return (0); |
| 524 | } |