xref: /freebsd/usr.bin/sed/main.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
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  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifndef lint
39 static char copyright[] =
40 "@(#) Copyright (c) 1992, 1993\n\
41 	The Regents of the University of California.  All rights reserved.\n";
42 #endif /* not lint */
43 
44 #ifndef lint
45 static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 1/3/94";
46 #endif /* not lint */
47 
48 #include <sys/types.h>
49 
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <locale.h>
53 #include <regex.h>
54 #include <stddef.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 
60 #include "defs.h"
61 #include "extern.h"
62 
63 /*
64  * Linked list of units (strings and files) to be compiled
65  */
66 struct s_compunit {
67 	struct s_compunit *next;
68 	enum e_cut {CU_FILE, CU_STRING} type;
69 	char *s;			/* Pointer to string or fname */
70 };
71 
72 /*
73  * Linked list pointer to compilation units and pointer to current
74  * next pointer.
75  */
76 static struct s_compunit *script, **cu_nextp = &script;
77 
78 /*
79  * Linked list of files to be processed
80  */
81 struct s_flist {
82 	char *fname;
83 	struct s_flist *next;
84 };
85 
86 /*
87  * Linked list pointer to files and pointer to current
88  * next pointer.
89  */
90 static struct s_flist *files, **fl_nextp = &files;
91 
92 int aflag, eflag, nflag;
93 
94 /*
95  * Current file and line number; line numbers restart across compilation
96  * units, but span across input files.
97  */
98 char *fname;			/* File name. */
99 u_long linenum;
100 int lastline;			/* TRUE on the last line of the last file */
101 
102 static void add_compunit __P((enum e_cut, char *));
103 static void add_file __P((char *));
104 
105 int
106 main(argc, argv)
107 	int argc;
108 	char *argv[];
109 {
110 	int c, fflag;
111 
112 	(void) setlocale(LC_ALL, "");
113 
114 	fflag = 0;
115 	while ((c = getopt(argc, argv, "ae:f:n")) != EOF)
116 		switch (c) {
117 		case 'a':
118 			aflag = 1;
119 			break;
120 		case 'e':
121 			eflag = 1;
122 			add_compunit(CU_STRING, optarg);
123 			break;
124 		case 'f':
125 			fflag = 1;
126 			add_compunit(CU_FILE, optarg);
127 			break;
128 		case 'n':
129 			nflag = 1;
130 			break;
131 		default:
132 		case '?':
133 			(void)fprintf(stderr,
134 "usage:\tsed script [-an] [file ...]\n\tsed [-an] [-e script] ... [-f script_file] ... [file ...]\n");
135 			exit(1);
136 		}
137 	argc -= optind;
138 	argv += optind;
139 
140 	/* First usage case; script is the first arg */
141 	if (!eflag && !fflag && *argv) {
142 		add_compunit(CU_STRING, *argv);
143 		argv++;
144 	}
145 
146 	compile();
147 
148 	/* Continue with first and start second usage */
149 	if (*argv)
150 		for (; *argv; argv++)
151 			add_file(*argv);
152 	else
153 		add_file(NULL);
154 	process();
155 	cfclose(prog, NULL);
156 	if (fclose(stdout))
157 		err(FATAL, "stdout: %s", strerror(errno));
158 	exit (0);
159 }
160 
161 /*
162  * Like fgets, but go through the chain of compilation units chaining them
163  * together.  Empty strings and files are ignored.
164  */
165 char *
166 cu_fgets(buf, n)
167 	char *buf;
168 	int n;
169 {
170 	static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
171 	static FILE *f;		/* Current open file */
172 	static char *s;		/* Current pointer inside string */
173 	static char string_ident[30];
174 	char *p;
175 
176 again:
177 	switch (state) {
178 	case ST_EOF:
179 		if (script == NULL)
180 			return (NULL);
181 		linenum = 0;
182 		switch (script->type) {
183 		case CU_FILE:
184 			if ((f = fopen(script->s, "r")) == NULL)
185 				err(FATAL,
186 				    "%s: %s", script->s, strerror(errno));
187 			fname = script->s;
188 			state = ST_FILE;
189 			goto again;
190 		case CU_STRING:
191 			if ((snprintf(string_ident,
192 			    sizeof(string_ident), "\"%s\"", script->s)) >=
193 			    sizeof(string_ident) - 1)
194 				(void)strcpy(string_ident +
195 				    sizeof(string_ident) - 6, " ...\"");
196 			fname = string_ident;
197 			s = script->s;
198 			state = ST_STRING;
199 			goto again;
200 		}
201 	case ST_FILE:
202 		if ((p = fgets(buf, n, f)) != NULL) {
203 			linenum++;
204 			if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
205 				nflag = 1;
206 			return (p);
207 		}
208 		script = script->next;
209 		(void)fclose(f);
210 		state = ST_EOF;
211 		goto again;
212 	case ST_STRING:
213 		if (linenum == 0 && s[0] == '#' && s[1] == 'n')
214 			nflag = 1;
215 		p = buf;
216 		for (;;) {
217 			if (n-- <= 1) {
218 				*p = '\0';
219 				linenum++;
220 				return (buf);
221 			}
222 			switch (*s) {
223 			case '\0':
224 				state = ST_EOF;
225 				if (s == script->s) {
226 					script = script->next;
227 					goto again;
228 				} else {
229 					script = script->next;
230 					*p = '\0';
231 					linenum++;
232 					return (buf);
233 				}
234 			case '\n':
235 				*p++ = '\n';
236 				*p = '\0';
237 				s++;
238 				linenum++;
239 				return (buf);
240 			default:
241 				*p++ = *s++;
242 			}
243 		}
244 	}
245 	/* NOTREACHED */
246 }
247 
248 /*
249  * Like fgets, but go through the list of files chaining them together.
250  * Set len to the length of the line.
251  */
252 int
253 mf_fgets(sp, spflag)
254 	SPACE *sp;
255 	enum e_spflag spflag;
256 {
257 	static FILE *f;		/* Current open file */
258 	size_t len;
259 	char c, *p;
260 
261 	if (f == NULL)
262 		/* Advance to first non-empty file */
263 		for (;;) {
264 			if (files == NULL) {
265 				lastline = 1;
266 				return (0);
267 			}
268 			if (files->fname == NULL) {
269 				f = stdin;
270 				fname = "stdin";
271 			} else {
272 				fname = files->fname;
273 				if ((f = fopen(fname, "r")) == NULL)
274 					err(FATAL, "%s: %s",
275 					    fname, strerror(errno));
276 			}
277 			if ((c = getc(f)) != EOF) {
278 				(void)ungetc(c, f);
279 				break;
280 			}
281 			(void)fclose(f);
282 			files = files->next;
283 		}
284 
285 	if (lastline) {
286 		sp->len = 0;
287 		return (0);
288 	}
289 
290 	/*
291 	 * Use fgetln so that we can handle essentially infinite input data.
292 	 * Can't use the pointer into the stdio buffer as the process space
293 	 * because the ungetc() can cause it to move.
294 	 */
295 	p = fgetln(f, &len);
296 	if (ferror(f))
297 		err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO));
298 	cspace(sp, p, len, spflag);
299 
300 	linenum++;
301 	/* Advance to next non-empty file */
302 	while ((c = getc(f)) == EOF) {
303 		(void)fclose(f);
304 		files = files->next;
305 		if (files == NULL) {
306 			lastline = 1;
307 			return (1);
308 		}
309 		if (files->fname == NULL) {
310 			f = stdin;
311 			fname = "stdin";
312 		} else {
313 			fname = files->fname;
314 			if ((f = fopen(fname, "r")) == NULL)
315 				err(FATAL, "%s: %s", fname, strerror(errno));
316 		}
317 	}
318 	(void)ungetc(c, f);
319 	return (1);
320 }
321 
322 /*
323  * Add a compilation unit to the linked list
324  */
325 static void
326 add_compunit(type, s)
327 	enum e_cut type;
328 	char *s;
329 {
330 	struct s_compunit *cu;
331 
332 	cu = xmalloc(sizeof(struct s_compunit));
333 	cu->type = type;
334 	cu->s = s;
335 	cu->next = NULL;
336 	*cu_nextp = cu;
337 	cu_nextp = &cu->next;
338 }
339 
340 /*
341  * Add a file to the linked list
342  */
343 static void
344 add_file(s)
345 	char *s;
346 {
347 	struct s_flist *fp;
348 
349 	fp = xmalloc(sizeof(struct s_flist));
350 	fp->next = NULL;
351 	*fl_nextp = fp;
352 	fp->fname = s;
353 	fl_nextp = &fp->next;
354 }
355