main.c revision 87766
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: head/usr.bin/sed/main.c 87766 2001-12-12 23:20:16Z markm $"); 40 41#ifndef lint 42static 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 48static 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 */ 70struct 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 */ 80static struct s_compunit *script, **cu_nextp = &script; 81 82/* 83 * Linked list of files to be processed 84 */ 85struct 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 */ 94static struct s_flist *files, **fl_nextp = &files; 95 96int aflag, eflag, nflag; 97int rflags = 0; 98 99/* 100 * Current file and line number; line numbers restart across compilation 101 * units, but span across input files. 102 */ 103const char *fname; /* File name. */ 104u_long linenum; 105int lastline; /* TRUE on the last line of the last file */ 106 107static void add_compunit __P((enum e_cut, char *)); 108static void add_file __P((char *)); 109static void usage __P((void)); 110 111int 112main(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 173static void 174usage() 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 */ 186char * 187cu_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 198again: 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 */ 285int 286mf_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 */ 358static void 359add_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 */ 377static void 378add_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