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