]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/sed/main.c
atrtc: Add a required include
[FreeBSD/FreeBSD.git] / usr.bin / sed / main.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson.
5  * Copyright (c) 1992 Diomidis Spinellis.
6  * Copyright (c) 1992, 1993
7  *      The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Diomidis Spinellis of Imperial College, University of London.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include <sys/cdefs.h>
38 #ifndef lint
39 static const char copyright[] =
40 "@(#) Copyright (c) 1992, 1993\n\
41         The Regents of the University of California.  All rights reserved.\n";
42 #endif
43
44 #ifndef lint
45 static const char sccsid[] = "@(#)main.c        8.2 (Berkeley) 1/3/94";
46 #endif
47
48 #include <sys/types.h>
49 #include <sys/mman.h>
50 #include <sys/param.h>
51 #include <sys/stat.h>
52
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <libgen.h>
57 #include <limits.h>
58 #include <locale.h>
59 #include <regex.h>
60 #include <stddef.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65
66 #include "defs.h"
67 #include "extern.h"
68
69 /*
70  * Linked list of units (strings and files) to be compiled
71  */
72 struct s_compunit {
73         struct s_compunit *next;
74         enum e_cut {CU_FILE, CU_STRING} type;
75         char *s;                        /* Pointer to string or fname */
76 };
77
78 /*
79  * Linked list pointer to compilation units and pointer to current
80  * next pointer.
81  */
82 static struct s_compunit *script, **cu_nextp = &script;
83
84 /*
85  * Linked list of files to be processed
86  */
87 struct s_flist {
88         char *fname;
89         struct s_flist *next;
90 };
91
92 /*
93  * Linked list pointer to files and pointer to current
94  * next pointer.
95  */
96 static struct s_flist *files, **fl_nextp = &files;
97
98 FILE *infile;                   /* Current input file */
99 FILE *outfile;                  /* Current output file */
100
101 int aflag, eflag, nflag;
102 int rflags = 0;
103 int quit = 0;
104 static int rval;                /* Exit status */
105
106 static int ispan;               /* Whether inplace editing spans across files */
107
108 /*
109  * Current file and line number; line numbers restart across compilation
110  * units, but span across input files.  The latter is optional if editing
111  * in place.
112  */
113 const char *fname;              /* File name. */
114 const char *outfname;           /* Output file name */
115 static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */
116 static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */
117 const char *inplace;            /* Inplace edit file extension. */
118 u_long linenum;
119
120 static void add_compunit(enum e_cut, char *);
121 static void add_file(char *);
122 static void usage(void) __dead2;
123
124 int
125 main(int argc, char *argv[])
126 {
127         int c, fflag, fflagstdin;
128         char *temp_arg;
129
130         (void) setlocale(LC_ALL, "");
131
132         fflag = 0;
133         fflagstdin = 0;
134         inplace = NULL;
135
136         while ((c = getopt(argc, argv, "EI:ae:f:i:lnru")) != -1)
137                 switch (c) {
138                 case 'r':               /* Gnu sed compat */
139                 case 'E':
140                         rflags = REG_EXTENDED;
141                         break;
142                 case 'I':
143                         inplace = optarg;
144                         ispan = 1;      /* span across input files */
145                         break;
146                 case 'a':
147                         aflag = 1;
148                         break;
149                 case 'e':
150                         eflag = 1;
151                         if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL)
152                                 err(1, "malloc");
153                         strcpy(temp_arg, optarg);
154                         strcat(temp_arg, "\n");
155                         add_compunit(CU_STRING, temp_arg);
156                         break;
157                 case 'f':
158                         fflag = 1;
159                         if (strcmp(optarg, "-") == 0)
160                                 fflagstdin = 1;
161                         add_compunit(CU_FILE, optarg);
162                         break;
163                 case 'i':
164                         inplace = optarg;
165                         ispan = 0;      /* don't span across input files */
166                         break;
167                 case 'l':
168                         if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
169                                 warnx("setting line buffered output failed");
170                         break;
171                 case 'n':
172                         nflag = 1;
173                         break;
174                 case 'u':
175                         if(setvbuf(stdout, NULL, _IONBF, 0) != 0)
176                                 warnx("setting unbuffered output failed");
177                         break;
178                 default:
179                 case '?':
180                         usage();
181                 }
182         argc -= optind;
183         argv += optind;
184
185         /* First usage case; script is the first arg */
186         if (!eflag && !fflag && *argv) {
187                 add_compunit(CU_STRING, *argv);
188                 argv++;
189         }
190
191         compile();
192
193         /* Continue with first and start second usage */
194         if (*argv)
195                 for (; *argv; argv++)
196                         add_file(*argv);
197         else if (fflagstdin)
198                 exit(rval);
199         else
200                 add_file(NULL);
201         process();
202         cfclose(prog, NULL);
203         if (fclose(stdout))
204                 err(1, "stdout");
205         exit(rval);
206 }
207
208 static void
209 usage(void)
210 {
211         (void)fprintf(stderr,
212             "usage: %s script [-Ealnru] [-i extension] [file ...]\n"
213             "\t%s [-Ealnu] [-i extension] [-e script] ... [-f script_file]"
214             " ... [file ...]\n", getprogname(), getprogname());
215         exit(1);
216 }
217
218 /*
219  * Like fgets, but go through the chain of compilation units chaining them
220  * together.  Empty strings and files are ignored.
221  */
222 char *
223 cu_fgets(char *buf, int n, int *more)
224 {
225         static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
226         static FILE *f;         /* Current open file */
227         static char *s;         /* Current pointer inside string */
228         static char string_ident[30];
229         char *p;
230
231 again:
232         switch (state) {
233         case ST_EOF:
234                 if (script == NULL) {
235                         if (more != NULL)
236                                 *more = 0;
237                         return (NULL);
238                 }
239                 linenum = 0;
240                 switch (script->type) {
241                 case CU_FILE:
242                         if (strcmp(script->s, "-") == 0) {
243                                 f = stdin;
244                                 fname = "stdin";
245                         } else {
246                                 if ((f = fopen(script->s, "r")) == NULL)
247                                         err(1, "%s", script->s);
248                                 fname = script->s;
249                         }
250                         state = ST_FILE;
251                         goto again;
252                 case CU_STRING:
253                         if (((size_t)snprintf(string_ident,
254                             sizeof(string_ident), "\"%s\"", script->s)) >=
255                             sizeof(string_ident) - 1)
256                                 (void)strcpy(string_ident +
257                                     sizeof(string_ident) - 6, " ...\"");
258                         fname = string_ident;
259                         s = script->s;
260                         state = ST_STRING;
261                         goto again;
262                 default:
263                         __unreachable();
264                 }
265         case ST_FILE:
266                 if ((p = fgets(buf, n, f)) != NULL) {
267                         linenum++;
268                         if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
269                                 nflag = 1;
270                         if (more != NULL)
271                                 *more = !feof(f);
272                         return (p);
273                 }
274                 script = script->next;
275                 (void)fclose(f);
276                 state = ST_EOF;
277                 goto again;
278         case ST_STRING:
279                 if (linenum == 0 && s[0] == '#' && s[1] == 'n')
280                         nflag = 1;
281                 p = buf;
282                 for (;;) {
283                         if (n-- <= 1) {
284                                 *p = '\0';
285                                 linenum++;
286                                 if (more != NULL)
287                                         *more = 1;
288                                 return (buf);
289                         }
290                         switch (*s) {
291                         case '\0':
292                                 state = ST_EOF;
293                                 if (s == script->s) {
294                                         script = script->next;
295                                         goto again;
296                                 } else {
297                                         script = script->next;
298                                         *p = '\0';
299                                         linenum++;
300                                         if (more != NULL)
301                                                 *more = 0;
302                                         return (buf);
303                                 }
304                         case '\n':
305                                 *p++ = '\n';
306                                 *p = '\0';
307                                 s++;
308                                 linenum++;
309                                 if (more != NULL)
310                                         *more = 0;
311                                 return (buf);
312                         default:
313                                 *p++ = *s++;
314                         }
315                 }
316         }
317         /* NOTREACHED */
318         return (NULL);
319 }
320
321 /*
322  * Like fgets, but go through the list of files chaining them together.
323  * Set len to the length of the line.
324  */
325 int
326 mf_fgets(SPACE *sp, enum e_spflag spflag)
327 {
328         struct stat sb;
329         ssize_t len;
330         char *dirbuf, *basebuf;
331         static char *p = NULL;
332         static size_t plen = 0;
333         int c;
334         static int firstfile;
335
336         if (infile == NULL) {
337                 /* stdin? */
338                 if (files->fname == NULL) {
339                         if (inplace != NULL)
340                                 errx(1, "-I or -i may not be used with stdin");
341                         infile = stdin;
342                         fname = "stdin";
343                         outfile = stdout;
344                         outfname = "stdout";
345                 }
346                 firstfile = 1;
347         }
348
349         for (;;) {
350                 if (infile != NULL && (c = getc(infile)) != EOF && !quit) {
351                         (void)ungetc(c, infile);
352                         break;
353                 }
354                 /* If we are here then either eof or no files are open yet */
355                 if (infile == stdin) {
356                         sp->len = 0;
357                         return (0);
358                 }
359                 if (infile != NULL) {
360                         fclose(infile);
361                         if (*oldfname != '\0') {
362                                 /* if there was a backup file, remove it */
363                                 unlink(oldfname);
364                                 /*
365                                  * Backup the original.  Note that hard links
366                                  * are not supported on all filesystems.
367                                  */
368                                 if ((link(fname, oldfname) != 0) &&
369                                    (rename(fname, oldfname) != 0)) {
370                                         warn("rename()");
371                                         if (*tmpfname)
372                                                 unlink(tmpfname);
373                                         exit(1);
374                                 }
375                                 *oldfname = '\0';
376                         }
377                         if (*tmpfname != '\0') {
378                                 if (outfile != NULL && outfile != stdout)
379                                         if (fclose(outfile) != 0) {
380                                                 warn("fclose()");
381                                                 unlink(tmpfname);
382                                                 exit(1);
383                                         }
384                                 outfile = NULL;
385                                 if (rename(tmpfname, fname) != 0) {
386                                         /* this should not happen really! */
387                                         warn("rename()");
388                                         unlink(tmpfname);
389                                         exit(1);
390                                 }
391                                 *tmpfname = '\0';
392                         }
393                         outfname = NULL;
394                 }
395                 if (firstfile == 0)
396                         files = files->next;
397                 else
398                         firstfile = 0;
399                 if (files == NULL) {
400                         sp->len = 0;
401                         return (0);
402                 }
403                 fname = files->fname;
404                 if (inplace != NULL) {
405                         if (lstat(fname, &sb) != 0)
406                                 err(1, "%s", fname);
407                         if (!S_ISREG(sb.st_mode))
408                                 errx(1, "%s: %s %s", fname,
409                                     "in-place editing only",
410                                     "works for regular files");
411                         if (*inplace != '\0') {
412                                 strlcpy(oldfname, fname,
413                                     sizeof(oldfname));
414                                 len = strlcat(oldfname, inplace,
415                                     sizeof(oldfname));
416                                 if (len > (ssize_t)sizeof(oldfname))
417                                         errx(1, "%s: name too long", fname);
418                         }
419                         if ((dirbuf = strdup(fname)) == NULL ||
420                             (basebuf = strdup(fname)) == NULL)
421                                 err(1, "strdup");
422                         len = snprintf(tmpfname, sizeof(tmpfname),
423                             "%s/.!%ld!%s", dirname(dirbuf), (long)getpid(),
424                             basename(basebuf));
425                         free(dirbuf);
426                         free(basebuf);
427                         if (len >= (ssize_t)sizeof(tmpfname))
428                                 errx(1, "%s: name too long", fname);
429                         unlink(tmpfname);
430                         if (outfile != NULL && outfile != stdout)
431                                 fclose(outfile);
432                         if ((outfile = fopen(tmpfname, "w")) == NULL)
433                                 err(1, "%s", fname);
434                         fchown(fileno(outfile), sb.st_uid, sb.st_gid);
435                         fchmod(fileno(outfile), sb.st_mode & ALLPERMS);
436                         outfname = tmpfname;
437                         if (!ispan) {
438                                 linenum = 0;
439                                 resetstate();
440                         }
441                 } else {
442                         outfile = stdout;
443                         outfname = "stdout";
444                 }
445                 if ((infile = fopen(fname, "r")) == NULL) {
446                         warn("%s", fname);
447                         rval = 1;
448                         continue;
449                 }
450         }
451         /*
452          * We are here only when infile is open and we still have something
453          * to read from it.
454          *
455          * Use getline() so that we can handle essentially infinite input
456          * data.  The p and plen are static so each invocation gives
457          * getline() the same buffer which is expanded as needed.
458          */
459         len = getline(&p, &plen, infile);
460         if (len == -1)
461                 err(1, "%s", fname);
462         if (len != 0 && p[len - 1] == '\n') {
463                 sp->append_newline = 1;
464                 len--;
465         } else if (!lastline()) {
466                 sp->append_newline = 1;
467         } else {
468                 sp->append_newline = 0;
469         }
470         cspace(sp, p, len, spflag);
471
472         linenum++;
473
474         return (1);
475 }
476
477 /*
478  * Add a compilation unit to the linked list
479  */
480 static void
481 add_compunit(enum e_cut type, char *s)
482 {
483         struct s_compunit *cu;
484
485         if ((cu = malloc(sizeof(struct s_compunit))) == NULL)
486                 err(1, "malloc");
487         cu->type = type;
488         cu->s = s;
489         cu->next = NULL;
490         *cu_nextp = cu;
491         cu_nextp = &cu->next;
492 }
493
494 /*
495  * Add a file to the linked list
496  */
497 static void
498 add_file(char *s)
499 {
500         struct s_flist *fp;
501
502         if ((fp = malloc(sizeof(struct s_flist))) == NULL)
503                 err(1, "malloc");
504         fp->next = NULL;
505         *fl_nextp = fp;
506         fp->fname = s;
507         fl_nextp = &fp->next;
508 }
509
510 static int
511 next_files_have_lines(void)
512 {
513         struct s_flist *file;
514         FILE *file_fd;
515         int ch;
516
517         file = files;
518         while ((file = file->next) != NULL) {
519                 if ((file_fd = fopen(file->fname, "r")) == NULL)
520                         continue;
521
522                 if ((ch = getc(file_fd)) != EOF) {
523                         /*
524                          * This next file has content, therefore current
525                          * file doesn't contains the last line.
526                          */
527                         ungetc(ch, file_fd);
528                         fclose(file_fd);
529                         return (1);
530                 }
531
532                 fclose(file_fd);
533         }
534
535         return (0);
536 }
537
538 int
539 lastline(void)
540 {
541         int ch;
542
543         if (feof(infile)) {
544                 return !(
545                     (inplace == NULL || ispan) &&
546                     next_files_have_lines());
547         }
548         if ((ch = getc(infile)) == EOF) {
549                 return !(
550                     (inplace == NULL || ispan) &&
551                     next_files_have_lines());
552         }
553         ungetc(ch, infile);
554         return (0);
555 }