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 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 * 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 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 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 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