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