xref: /csrg-svn/usr.bin/sed/main.c (revision 56003)
1 /*-
2  * Copyright (c) 1992 Diomidis Spinellis.
3  * Copyright (c) 1992 The Regents of the University of California.
4  * 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 char copyright[] =
14 "@(#) Copyright (c) 1992 The Regents of the University of California.\n\
15  All rights reserved.\n";
16 #endif /* not lint */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)main.c	5.2 (Berkeley) 08/24/92";
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 compile_errors;		/* Compile error count. */
67 int aflag, eflag, nflag;
68 
69 /*
70  * Current file and line number; line numbers restart across compilation
71  * units, but span across input files.
72  */
73 char *fname;			/* File name. */
74 u_long linenum;
75 int lastline;			/* TRUE on the last line of the last file */
76 
77 static void add_compunit __P((enum e_cut, char *));
78 static void add_file __P((char *));
79 
80 int
81 main(argc, argv)
82 	int argc;
83 	char *argv[];
84 {
85 	int c, fflag;
86 
87 	fflag = 0;
88 	while ((c = getopt(argc, argv, "ae:f:n")) != EOF)
89 		switch (c) {
90 		case 'a':
91 			aflag = 1;
92 			break;
93 		case 'e':
94 			eflag = 1;
95 			add_compunit(CU_STRING, optarg);
96 			break;
97 		case 'f':
98 			fflag = 1;
99 			add_compunit(CU_FILE, optarg);
100 			break;
101 		case 'n':
102 			nflag = 1;
103 			break;
104 		default:
105 		case '?':
106 			(void)fprintf(stderr,
107 "usage:\tsed script [-an] [file ...]\n\tsed [-an] [-e script] ... [-f scipt_file] ... [file ...]\n");
108 			exit(1);
109 		}
110 	argc -= optind;
111 	argv += optind;
112 
113 	/* First usage case; script is the first arg */
114 	if (!eflag && !fflag && *argv) {
115 		add_compunit(CU_STRING, *argv);
116 		argv++;
117 	}
118 
119 	compile();
120 	if (compile_errors)
121 		exit(1);
122 
123 	/* Continue with first and start second usage */
124 	if (*argv)
125 		for (; *argv; argv++)
126 			add_file(*argv);
127 	else
128 		add_file(NULL);
129 	process();
130 	cfclose(prog);
131 	if (fclose(stdout))
132 		err(FATAL, "stdout: %s", strerror(errno));
133 	exit (0);
134 }
135 
136 /*
137  * Like fgets, but go through the chain of compilation units chaining them
138  * together.  Empty strings and files are ignored.
139  */
140 char *
141 cu_fgets(buf, n)
142 	char *buf;
143 	int n;
144 {
145 	static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
146 	static FILE *f;		/* Current open file */
147 	static char *s;		/* Current pointer inside string */
148 	static char string_ident[60];
149 	char *p;
150 
151 again:
152 	switch (state) {
153 	case ST_EOF:
154 		if (script == NULL)
155 			return (NULL);
156 		linenum = 0;
157 		switch (script->type) {
158 		case CU_FILE:
159 			if ((f = fopen(script->s, "r")) == NULL)
160 				err(FATAL,
161 				    "%s: %s", script->s, strerror(errno));
162 			fname = script->s;
163 			state = ST_FILE;
164 			goto again;
165 		case CU_STRING:
166 			(void)sprintf(string_ident, "\"%.50s%s\"", script->s,
167 			    strlen(script->s) > 50 ? "..." : "");
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 char *
225 mf_fgets(lenp)
226 	size_t *lenp;
227 {
228 	static FILE *f;		/* Current open file */
229 	char c, *p;
230 
231 	if (f == NULL)
232 		/* Advance to first non-empty file */
233 		for (;;) {
234 			if (files == NULL) {
235 				lastline = 1;
236 				return (NULL);
237 			}
238 			if (files->fname == NULL) {
239 				f = stdin;
240 				fname = "stdin";
241 			} else {
242 				fname = files->fname;
243 				if ((f = fopen(fname, "r")) == NULL)
244 					err(FATAL, "%s: %s",
245 					    fname, strerror(errno));
246 			}
247 			if ((c = getc(f)) != EOF) {
248 				(void)ungetc(c, f);
249 				break;
250 			}
251 			(void)fclose(f);
252 			files = files->next;
253 		}
254 
255 	if (lastline) {
256 		*lenp = 0;
257 		return (NULL);
258 	}
259 
260 	p = fgetline(f, lenp);
261 	if (ferror(f))
262 		err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO));
263 
264 	linenum++;
265 	/* Advance to next non-empty file */
266 	while ((c = getc(f)) == EOF) {
267 		(void)fclose(f);
268 		files = files->next;
269 		if (files == NULL) {
270 			lastline = 1;
271 			return (p);
272 		}
273 		if (files->fname == NULL) {
274 			f = stdin;
275 			fname = "stdin";
276 		} else {
277 			fname = files->fname;
278 			if ((f = fopen(fname, "r")) == NULL)
279 				err(FATAL, "%s: %s", fname, strerror(errno));
280 		}
281 	}
282 	(void)ungetc(c, f);
283 	return (p);
284 }
285 
286 /*
287  * Add a compilation unit to the linked list
288  */
289 static void
290 add_compunit(type, s)
291 	enum e_cut type;
292 	char *s;
293 {
294 	struct s_compunit *cu;
295 
296 	cu = xmalloc(sizeof(struct s_compunit));
297 	cu->type = type;
298 	cu->s = s;
299 	cu->next = NULL;
300 	*cu_nextp = cu;
301 	cu_nextp = &cu->next;
302 }
303 
304 /*
305  * Add a file to the linked list
306  */
307 static void
308 add_file(s)
309 	char *s;
310 {
311 	struct s_flist *fp;
312 
313 	fp = xmalloc(sizeof(struct s_flist));
314 	fp->next = NULL;
315 	*fl_nextp = fp;
316 	fp->fname = s;
317 	fl_nextp = &fp->next;
318 }
319