xref: /original-bsd/usr.bin/sed/main.c (revision 333da485)
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  * %sccs.include.redist.c%
10  */
11 
12 #ifndef lint
13 static char copyright[] =
14 "@(#) Copyright (c) 1992, 1993\n\
15 	The Regents of the University of California.  All rights reserved.\n";
16 #endif /* not lint */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 01/03/94";
20 #endif /* not lint */
21 
22 #include <sys/types.h>
23 
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <regex.h>
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "defs.h"
35 #include "extern.h"
36 
37 /*
38  * Linked list of units (strings and files) to be compiled
39  */
40 struct s_compunit {
41 	struct s_compunit *next;
42 	enum e_cut {CU_FILE, CU_STRING} type;
43 	char *s;			/* Pointer to string or fname */
44 };
45 
46 /*
47  * Linked list pointer to compilation units and pointer to current
48  * next pointer.
49  */
50 static struct s_compunit *script, **cu_nextp = &script;
51 
52 /*
53  * Linked list of files to be processed
54  */
55 struct s_flist {
56 	char *fname;
57 	struct s_flist *next;
58 };
59 
60 /*
61  * Linked list pointer to files and pointer to current
62  * next pointer.
63  */
64 static struct s_flist *files, **fl_nextp = &files;
65 
66 int aflag, eflag, nflag;
67 
68 /*
69  * Current file and line number; line numbers restart across compilation
70  * units, but span across input files.
71  */
72 char *fname;			/* File name. */
73 u_long linenum;
74 int lastline;			/* TRUE on the last line of the last file */
75 
76 static void add_compunit __P((enum e_cut, char *));
77 static void add_file __P((char *));
78 
79 int
80 main(argc, argv)
81 	int argc;
82 	char *argv[];
83 {
84 	int c, fflag;
85 
86 	fflag = 0;
87 	while ((c = getopt(argc, argv, "ae:f:n")) != EOF)
88 		switch (c) {
89 		case 'a':
90 			aflag = 1;
91 			break;
92 		case 'e':
93 			eflag = 1;
94 			add_compunit(CU_STRING, optarg);
95 			break;
96 		case 'f':
97 			fflag = 1;
98 			add_compunit(CU_FILE, optarg);
99 			break;
100 		case 'n':
101 			nflag = 1;
102 			break;
103 		default:
104 		case '?':
105 			(void)fprintf(stderr,
106 "usage:\tsed script [-an] [file ...]\n\tsed [-an] [-e script] ... [-f scipt_file] ... [file ...]\n");
107 			exit(1);
108 		}
109 	argc -= optind;
110 	argv += optind;
111 
112 	/* First usage case; script is the first arg */
113 	if (!eflag && !fflag && *argv) {
114 		add_compunit(CU_STRING, *argv);
115 		argv++;
116 	}
117 
118 	compile();
119 
120 	/* Continue with first and start second usage */
121 	if (*argv)
122 		for (; *argv; argv++)
123 			add_file(*argv);
124 	else
125 		add_file(NULL);
126 	process();
127 	cfclose(prog, NULL);
128 	if (fclose(stdout))
129 		err(FATAL, "stdout: %s", strerror(errno));
130 	exit (0);
131 }
132 
133 /*
134  * Like fgets, but go through the chain of compilation units chaining them
135  * together.  Empty strings and files are ignored.
136  */
137 char *
138 cu_fgets(buf, n)
139 	char *buf;
140 	int n;
141 {
142 	static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
143 	static FILE *f;		/* Current open file */
144 	static char *s;		/* Current pointer inside string */
145 	static char string_ident[30];
146 	char *p;
147 
148 again:
149 	switch (state) {
150 	case ST_EOF:
151 		if (script == NULL)
152 			return (NULL);
153 		linenum = 0;
154 		switch (script->type) {
155 		case CU_FILE:
156 			if ((f = fopen(script->s, "r")) == NULL)
157 				err(FATAL,
158 				    "%s: %s", script->s, strerror(errno));
159 			fname = script->s;
160 			state = ST_FILE;
161 			goto again;
162 		case CU_STRING:
163 			if ((snprintf(string_ident,
164 			    sizeof(string_ident), "\"%s\"", script->s)) >=
165 			    sizeof(string_ident) - 1)
166 				(void)strcpy(string_ident +
167 				    sizeof(string_ident) - 6, " ...\"");
168 			fname = string_ident;
169 			s = script->s;
170 			state = ST_STRING;
171 			goto again;
172 		}
173 	case ST_FILE:
174 		if ((p = fgets(buf, n, f)) != NULL) {
175 			linenum++;
176 			if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
177 				nflag = 1;
178 			return (p);
179 		}
180 		script = script->next;
181 		(void)fclose(f);
182 		state = ST_EOF;
183 		goto again;
184 	case ST_STRING:
185 		if (linenum == 0 && s[0] == '#' && s[1] == 'n')
186 			nflag = 1;
187 		p = buf;
188 		for (;;) {
189 			if (n-- <= 1) {
190 				*p = '\0';
191 				linenum++;
192 				return (buf);
193 			}
194 			switch (*s) {
195 			case '\0':
196 				state = ST_EOF;
197 				if (s == script->s) {
198 					script = script->next;
199 					goto again;
200 				} else {
201 					script = script->next;
202 					*p = '\0';
203 					linenum++;
204 					return (buf);
205 				}
206 			case '\n':
207 				*p++ = '\n';
208 				*p = '\0';
209 				s++;
210 				linenum++;
211 				return (buf);
212 			default:
213 				*p++ = *s++;
214 			}
215 		}
216 	}
217 	/* NOTREACHED */
218 }
219 
220 /*
221  * Like fgets, but go through the list of files chaining them together.
222  * Set len to the length of the line.
223  */
224 int
225 mf_fgets(sp, spflag)
226 	SPACE *sp;
227 	enum e_spflag spflag;
228 {
229 	static FILE *f;		/* Current open file */
230 	size_t len;
231 	char c, *p;
232 
233 	if (f == NULL)
234 		/* Advance to first non-empty file */
235 		for (;;) {
236 			if (files == NULL) {
237 				lastline = 1;
238 				return (0);
239 			}
240 			if (files->fname == NULL) {
241 				f = stdin;
242 				fname = "stdin";
243 			} else {
244 				fname = files->fname;
245 				if ((f = fopen(fname, "r")) == NULL)
246 					err(FATAL, "%s: %s",
247 					    fname, strerror(errno));
248 			}
249 			if ((c = getc(f)) != EOF) {
250 				(void)ungetc(c, f);
251 				break;
252 			}
253 			(void)fclose(f);
254 			files = files->next;
255 		}
256 
257 	if (lastline) {
258 		sp->len = 0;
259 		return (0);
260 	}
261 
262 	/*
263 	 * Use fgetln so that we can handle essentially infinite input data.
264 	 * Can't use the pointer into the stdio buffer as the process space
265 	 * because the ungetc() can cause it to move.
266 	 */
267 	p = fgetln(f, &len);
268 	if (ferror(f))
269 		err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO));
270 	cspace(sp, p, len, spflag);
271 
272 	linenum++;
273 	/* Advance to next non-empty file */
274 	while ((c = getc(f)) == EOF) {
275 		(void)fclose(f);
276 		files = files->next;
277 		if (files == NULL) {
278 			lastline = 1;
279 			return (1);
280 		}
281 		if (files->fname == NULL) {
282 			f = stdin;
283 			fname = "stdin";
284 		} else {
285 			fname = files->fname;
286 			if ((f = fopen(fname, "r")) == NULL)
287 				err(FATAL, "%s: %s", fname, strerror(errno));
288 		}
289 	}
290 	(void)ungetc(c, f);
291 	return (1);
292 }
293 
294 /*
295  * Add a compilation unit to the linked list
296  */
297 static void
298 add_compunit(type, s)
299 	enum e_cut type;
300 	char *s;
301 {
302 	struct s_compunit *cu;
303 
304 	cu = xmalloc(sizeof(struct s_compunit));
305 	cu->type = type;
306 	cu->s = s;
307 	cu->next = NULL;
308 	*cu_nextp = cu;
309 	cu_nextp = &cu->next;
310 }
311 
312 /*
313  * Add a file to the linked list
314  */
315 static void
316 add_file(s)
317 	char *s;
318 {
319 	struct s_flist *fp;
320 
321 	fp = xmalloc(sizeof(struct s_flist));
322 	fp->next = NULL;
323 	*fl_nextp = fp;
324 	fp->fname = s;
325 	fl_nextp = &fp->next;
326 }
327