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
main(argc,argv)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 *
cu_fgets(buf,n)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
mf_fgets(sp,spflag)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
add_compunit(type,s)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
add_file(s)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