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