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