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 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #ifndef lint 42 static const char copyright[] = 43 "@(#) Copyright (c) 1992, 1993\n\ 44 The Regents of the University of California. All rights reserved.\n"; 45 #endif 46 47 #ifndef lint 48 static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; 49 #endif 50 51 #include <sys/types.h> 52 53 #include <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <locale.h> 57 #include <regex.h> 58 #include <stddef.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 #include "defs.h" 65 #include "extern.h" 66 67 /* 68 * Linked list of units (strings and files) to be compiled 69 */ 70 struct s_compunit { 71 struct s_compunit *next; 72 enum e_cut {CU_FILE, CU_STRING} type; 73 char *s; /* Pointer to string or fname */ 74 }; 75 76 /* 77 * Linked list pointer to compilation units and pointer to current 78 * next pointer. 79 */ 80 static struct s_compunit *script, **cu_nextp = &script; 81 82 /* 83 * Linked list of files to be processed 84 */ 85 struct s_flist { 86 char *fname; 87 struct s_flist *next; 88 }; 89 90 /* 91 * Linked list pointer to files and pointer to current 92 * next pointer. 93 */ 94 static struct s_flist *files, **fl_nextp = &files; 95 96 int aflag, eflag, nflag; 97 int rflags = 0; 98 99 /* 100 * Current file and line number; line numbers restart across compilation 101 * units, but span across input files. 102 */ 103 const char *fname; /* File name. */ 104 u_long linenum; 105 int lastline; /* TRUE on the last line of the last file */ 106 107 static void add_compunit(enum e_cut, char *); 108 static void add_file(char *); 109 static void usage(void); 110 111 int 112 main(argc, argv) 113 int argc; 114 char *argv[]; 115 { 116 int c, fflag; 117 char *temp_arg; 118 119 (void) setlocale(LC_ALL, ""); 120 121 fflag = 0; 122 while ((c = getopt(argc, argv, "Eae:f:n")) != -1) 123 switch (c) { 124 case 'E': 125 rflags = REG_EXTENDED; 126 break; 127 case 'a': 128 aflag = 1; 129 break; 130 case 'e': 131 eflag = 1; 132 if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL) 133 err(1, "malloc"); 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 if ((cu = malloc(sizeof(struct s_compunit))) == NULL) 366 err(1, "malloc"); 367 cu->type = type; 368 cu->s = s; 369 cu->next = NULL; 370 *cu_nextp = cu; 371 cu_nextp = &cu->next; 372 } 373 374 /* 375 * Add a file to the linked list 376 */ 377 static void 378 add_file(s) 379 char *s; 380 { 381 struct s_flist *fp; 382 383 if ((fp = malloc(sizeof(struct s_flist))) == NULL) 384 err(1, "malloc"); 385 fp->next = NULL; 386 *fl_nextp = fp; 387 fp->fname = s; 388 fl_nextp = &fp->next; 389 } 390