xref: /csrg-svn/usr.bin/sed/main.c (revision 55995)
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.1 (Berkeley) 08/23/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[20];
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 			/* Have better handling here */
167 			(void)strncpy(string_ident,
168 			    script->s, sizeof(string_ident));
169 			(void)strcpy(string_ident + sizeof(string_ident) - 5,
170 			    "...");
171 			fname = string_ident;
172 			s = script->s;
173 			state = ST_STRING;
174 			goto again;
175 		}
176 	case ST_FILE:
177 		if ((p = fgets(buf, n, f)) != NULL) {
178 			linenum++;
179 			if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
180 				nflag = 1;
181 			return (p);
182 		}
183 		script = script->next;
184 		(void)fclose(f);
185 		state = ST_EOF;
186 		goto again;
187 	case ST_STRING:
188 		if (linenum == 0 && s[0] == '#' && s[1] == 'n')
189 			nflag = 1;
190 		p = buf;
191 		for (;;) {
192 			if (n-- <= 1) {
193 				*p = '\0';
194 				linenum++;
195 				return (buf);
196 			}
197 			switch (*s) {
198 			case '\0':
199 				state = ST_EOF;
200 				if (s == script->s) {
201 					script = script->next;
202 					goto again;
203 				} else {
204 					script = script->next;
205 					*p = '\0';
206 					linenum++;
207 					return (buf);
208 				}
209 			case '\n':
210 				*p++ = '\n';
211 				*p = '\0';
212 				s++;
213 				linenum++;
214 				return (buf);
215 			default:
216 				*p++ = *s++;
217 			}
218 		}
219 	}
220 	/* NOTREACHED */
221 }
222 
223 /*
224  * Like fgets, but go through the list of files chaining them together.
225  * Set len to the length of the line.
226  */
227 char *
228 mf_fgets(lenp)
229 	size_t *lenp;
230 {
231 	static FILE *f;		/* Current open file */
232 	char c, *p;
233 
234 	if (f == NULL)
235 		/* Advance to first non-empty file */
236 		for (;;) {
237 			if (files == NULL) {
238 				lastline = 1;
239 				return (NULL);
240 			}
241 			if (files->fname == NULL) {
242 				f = stdin;
243 				fname = "stdin";
244 			} else {
245 				fname = files->fname;
246 				if ((f = fopen(fname, "r")) == NULL)
247 					err(FATAL, "%s: %s",
248 					    fname, strerror(errno));
249 			}
250 			if ((c = getc(f)) != EOF) {
251 				(void)ungetc(c, f);
252 				break;
253 			}
254 			(void)fclose(f);
255 			files = files->next;
256 		}
257 
258 	if (lastline) {
259 		*lenp = 0;
260 		return (NULL);
261 	}
262 
263 	p = fgetline(f, lenp);
264 	if (ferror(f))
265 		err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO));
266 
267 	linenum++;
268 	/* Advance to next non-empty file */
269 	while ((c = getc(f)) == EOF) {
270 		(void)fclose(f);
271 		files = files->next;
272 		if (files == NULL) {
273 			lastline = 1;
274 			return (p);
275 		}
276 		if (files->fname == NULL) {
277 			f = stdin;
278 			fname = "stdin";
279 		} else {
280 			fname = files->fname;
281 			if ((f = fopen(fname, "r")) == NULL)
282 				err(FATAL, "%s: %s", fname, strerror(errno));
283 		}
284 	}
285 	(void)ungetc(c, f);
286 	return (p);
287 }
288 
289 /*
290  * Add a compilation unit to the linked list
291  */
292 static void
293 add_compunit(type, s)
294 	enum e_cut type;
295 	char *s;
296 {
297 	struct s_compunit *cu;
298 
299 	cu = xmalloc(sizeof(struct s_compunit));
300 	cu->type = type;
301 	cu->s = s;
302 	cu->next = NULL;
303 	*cu_nextp = cu;
304 	cu_nextp = &cu->next;
305 }
306 
307 /*
308  * Add a file to the linked list
309  */
310 static void
311 add_file(s)
312 	char *s;
313 {
314 	struct s_flist *fp;
315 
316 	fp = xmalloc(sizeof(struct s_flist));
317 	fp->next = NULL;
318 	*fl_nextp = fp;
319 	fp->fname = s;
320 	fl_nextp = &fp->next;
321 }
322