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 | } |