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