main.c revision 97238
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1992 Diomidis Spinellis. 31590Srgrimes * Copyright (c) 1992, 1993 41590Srgrimes * The Regents of the University of California. All rights reserved. 51590Srgrimes * 61590Srgrimes * This code is derived from software contributed to Berkeley by 71590Srgrimes * Diomidis Spinellis of Imperial College, University of London. 81590Srgrimes * 91590Srgrimes * Redistribution and use in source and binary forms, with or without 101590Srgrimes * modification, are permitted provided that the following conditions 111590Srgrimes * are met: 121590Srgrimes * 1. Redistributions of source code must retain the above copyright 131590Srgrimes * notice, this list of conditions and the following disclaimer. 141590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151590Srgrimes * notice, this list of conditions and the following disclaimer in the 161590Srgrimes * documentation and/or other materials provided with the distribution. 171590Srgrimes * 3. All advertising materials mentioning features or use of this software 181590Srgrimes * must display the following acknowledgement: 191590Srgrimes * This product includes software developed by the University of 201590Srgrimes * California, Berkeley and its contributors. 211590Srgrimes * 4. Neither the name of the University nor the names of its contributors 221590Srgrimes * may be used to endorse or promote products derived from this software 231590Srgrimes * without specific prior written permission. 241590Srgrimes * 251590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 261590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 271590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 281590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 291590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 301590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 311590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 321590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 331590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 341590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 351590Srgrimes * SUCH DAMAGE. 361590Srgrimes */ 371590Srgrimes 3887766Smarkm#include <sys/cdefs.h> 3987766Smarkm__FBSDID("$FreeBSD: head/usr.bin/sed/main.c 97238 2002-05-24 10:58:21Z tjr $"); 4087766Smarkm 411590Srgrimes#ifndef lint 4228066Scharnierstatic const char copyright[] = 431590Srgrimes"@(#) Copyright (c) 1992, 1993\n\ 441590Srgrimes The Regents of the University of California. All rights reserved.\n"; 4587766Smarkm#endif 461590Srgrimes 471590Srgrimes#ifndef lint 4887766Smarkmstatic const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; 4928066Scharnier#endif 501590Srgrimes 5196175Sjmallett#include <sys/param.h> 5296175Sjmallett#include <sys/stat.h> 531590Srgrimes 5428066Scharnier#include <err.h> 551590Srgrimes#include <errno.h> 561590Srgrimes#include <fcntl.h> 5717522Sache#include <locale.h> 581590Srgrimes#include <regex.h> 591590Srgrimes#include <stddef.h> 601590Srgrimes#include <stdio.h> 611590Srgrimes#include <stdlib.h> 621590Srgrimes#include <string.h> 631590Srgrimes#include <unistd.h> 641590Srgrimes 651590Srgrimes#include "defs.h" 661590Srgrimes#include "extern.h" 671590Srgrimes 681590Srgrimes/* 691590Srgrimes * Linked list of units (strings and files) to be compiled 701590Srgrimes */ 711590Srgrimesstruct s_compunit { 721590Srgrimes struct s_compunit *next; 731590Srgrimes enum e_cut {CU_FILE, CU_STRING} type; 741590Srgrimes char *s; /* Pointer to string or fname */ 751590Srgrimes}; 761590Srgrimes 771590Srgrimes/* 781590Srgrimes * Linked list pointer to compilation units and pointer to current 791590Srgrimes * next pointer. 801590Srgrimes */ 811590Srgrimesstatic struct s_compunit *script, **cu_nextp = &script; 821590Srgrimes 831590Srgrimes/* 841590Srgrimes * Linked list of files to be processed 851590Srgrimes */ 861590Srgrimesstruct s_flist { 871590Srgrimes char *fname; 881590Srgrimes struct s_flist *next; 891590Srgrimes}; 901590Srgrimes 911590Srgrimes/* 921590Srgrimes * Linked list pointer to files and pointer to current 931590Srgrimes * next pointer. 941590Srgrimes */ 951590Srgrimesstatic struct s_flist *files, **fl_nextp = &files; 961590Srgrimes 971590Srgrimesint aflag, eflag, nflag; 9858309Sgreenint rflags = 0; 9997238Stjrstatic int rval; /* Exit status */ 1001590Srgrimes 1011590Srgrimes/* 1021590Srgrimes * Current file and line number; line numbers restart across compilation 1031590Srgrimes * units, but span across input files. 1041590Srgrimes */ 10587766Smarkmconst char *fname; /* File name. */ 10696175Sjmallettconst char *inplace; /* Inplace edit file extension. */ 1071590Srgrimesu_long linenum; 1081590Srgrimesint lastline; /* TRUE on the last line of the last file */ 1091590Srgrimes 11092922Simpstatic void add_compunit(enum e_cut, char *); 11192922Simpstatic void add_file(char *); 11296175Sjmallettstatic int inplace_edit(char **); 11392922Simpstatic void usage(void); 1141590Srgrimes 1151590Srgrimesint 1161590Srgrimesmain(argc, argv) 1171590Srgrimes int argc; 1181590Srgrimes char *argv[]; 1191590Srgrimes{ 1201590Srgrimes int c, fflag; 12160394Snsayer char *temp_arg; 1221590Srgrimes 12317522Sache (void) setlocale(LC_ALL, ""); 12417522Sache 1251590Srgrimes fflag = 0; 12696175Sjmallett inplace = NULL; 12796175Sjmallett 12896175Sjmallett while ((c = getopt(argc, argv, "Eae:f:i:n")) != -1) 1291590Srgrimes switch (c) { 13058309Sgreen case 'E': 13158309Sgreen rflags = REG_EXTENDED; 13258309Sgreen break; 1331590Srgrimes case 'a': 1341590Srgrimes aflag = 1; 1351590Srgrimes break; 1361590Srgrimes case 'e': 1371590Srgrimes eflag = 1; 13880286Sobrien if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL) 13980286Sobrien err(1, "malloc"); 14060394Snsayer strcpy(temp_arg, optarg); 14160394Snsayer strcat(temp_arg, "\n"); 14260394Snsayer add_compunit(CU_STRING, temp_arg); 1431590Srgrimes break; 1441590Srgrimes case 'f': 1451590Srgrimes fflag = 1; 1461590Srgrimes add_compunit(CU_FILE, optarg); 1471590Srgrimes break; 14896175Sjmallett case 'i': 14996175Sjmallett inplace = optarg; 15096175Sjmallett break; 1511590Srgrimes case 'n': 1521590Srgrimes nflag = 1; 1531590Srgrimes break; 1541590Srgrimes default: 1551590Srgrimes case '?': 15628066Scharnier usage(); 1571590Srgrimes } 1581590Srgrimes argc -= optind; 1591590Srgrimes argv += optind; 1601590Srgrimes 1611590Srgrimes /* First usage case; script is the first arg */ 1621590Srgrimes if (!eflag && !fflag && *argv) { 1631590Srgrimes add_compunit(CU_STRING, *argv); 1641590Srgrimes argv++; 1651590Srgrimes } 1661590Srgrimes 1671590Srgrimes compile(); 1681590Srgrimes 1691590Srgrimes /* Continue with first and start second usage */ 1701590Srgrimes if (*argv) 1711590Srgrimes for (; *argv; argv++) 1721590Srgrimes add_file(*argv); 1731590Srgrimes else 1741590Srgrimes add_file(NULL); 1751590Srgrimes process(); 1761590Srgrimes cfclose(prog, NULL); 1771590Srgrimes if (fclose(stdout)) 17828066Scharnier err(1, "stdout"); 17997238Stjr exit(rval); 1801590Srgrimes} 1811590Srgrimes 18228066Scharnierstatic void 18328066Scharnierusage() 18428066Scharnier{ 18528066Scharnier (void)fprintf(stderr, "%s\n%s\n", 18696189Sjmallett "usage: sed script [-Ean] [-i extension] [file ...]", 18796189Sjmallett " sed [-an] [-i extension] [-e script] ... [-f script_file] ... [file ...]"); 18828066Scharnier exit(1); 18928066Scharnier} 19028066Scharnier 1911590Srgrimes/* 1921590Srgrimes * Like fgets, but go through the chain of compilation units chaining them 1931590Srgrimes * together. Empty strings and files are ignored. 1941590Srgrimes */ 1951590Srgrimeschar * 19641602Sarchiecu_fgets(buf, n, more) 1971590Srgrimes char *buf; 1981590Srgrimes int n; 19941602Sarchie int *more; 2001590Srgrimes{ 2011590Srgrimes static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; 2021590Srgrimes static FILE *f; /* Current open file */ 2031590Srgrimes static char *s; /* Current pointer inside string */ 2041590Srgrimes static char string_ident[30]; 2051590Srgrimes char *p; 2061590Srgrimes 2071590Srgrimesagain: 2081590Srgrimes switch (state) { 2091590Srgrimes case ST_EOF: 21041602Sarchie if (script == NULL) { 21141602Sarchie if (more != NULL) 21241602Sarchie *more = 0; 2131590Srgrimes return (NULL); 21441602Sarchie } 2151590Srgrimes linenum = 0; 2161590Srgrimes switch (script->type) { 2171590Srgrimes case CU_FILE: 2181590Srgrimes if ((f = fopen(script->s, "r")) == NULL) 21928066Scharnier err(1, "%s", script->s); 2201590Srgrimes fname = script->s; 2211590Srgrimes state = ST_FILE; 2221590Srgrimes goto again; 2231590Srgrimes case CU_STRING: 2241590Srgrimes if ((snprintf(string_ident, 2251590Srgrimes sizeof(string_ident), "\"%s\"", script->s)) >= 2261590Srgrimes sizeof(string_ident) - 1) 2271590Srgrimes (void)strcpy(string_ident + 2281590Srgrimes sizeof(string_ident) - 6, " ...\""); 2291590Srgrimes fname = string_ident; 2301590Srgrimes s = script->s; 2311590Srgrimes state = ST_STRING; 2321590Srgrimes goto again; 2331590Srgrimes } 2341590Srgrimes case ST_FILE: 2351590Srgrimes if ((p = fgets(buf, n, f)) != NULL) { 2361590Srgrimes linenum++; 2371590Srgrimes if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') 2381590Srgrimes nflag = 1; 23941602Sarchie if (more != NULL) 24041602Sarchie *more = !feof(f); 2411590Srgrimes return (p); 2421590Srgrimes } 2431590Srgrimes script = script->next; 2441590Srgrimes (void)fclose(f); 2451590Srgrimes state = ST_EOF; 2461590Srgrimes goto again; 2471590Srgrimes case ST_STRING: 2481590Srgrimes if (linenum == 0 && s[0] == '#' && s[1] == 'n') 2491590Srgrimes nflag = 1; 2501590Srgrimes p = buf; 2511590Srgrimes for (;;) { 2521590Srgrimes if (n-- <= 1) { 2531590Srgrimes *p = '\0'; 2541590Srgrimes linenum++; 25541602Sarchie if (more != NULL) 25641602Sarchie *more = 1; 2571590Srgrimes return (buf); 2581590Srgrimes } 2591590Srgrimes switch (*s) { 2601590Srgrimes case '\0': 2611590Srgrimes state = ST_EOF; 2621590Srgrimes if (s == script->s) { 2631590Srgrimes script = script->next; 2641590Srgrimes goto again; 2651590Srgrimes } else { 2661590Srgrimes script = script->next; 2671590Srgrimes *p = '\0'; 2681590Srgrimes linenum++; 26941602Sarchie if (more != NULL) 27041602Sarchie *more = 0; 2711590Srgrimes return (buf); 2721590Srgrimes } 2731590Srgrimes case '\n': 2741590Srgrimes *p++ = '\n'; 2751590Srgrimes *p = '\0'; 2761590Srgrimes s++; 2771590Srgrimes linenum++; 27841602Sarchie if (more != NULL) 27941602Sarchie *more = 0; 2801590Srgrimes return (buf); 2811590Srgrimes default: 2821590Srgrimes *p++ = *s++; 2831590Srgrimes } 2841590Srgrimes } 2851590Srgrimes } 2861590Srgrimes /* NOTREACHED */ 28728066Scharnier return (NULL); 2881590Srgrimes} 2891590Srgrimes 2901590Srgrimes/* 2911590Srgrimes * Like fgets, but go through the list of files chaining them together. 2921590Srgrimes * Set len to the length of the line. 2931590Srgrimes */ 2941590Srgrimesint 2951590Srgrimesmf_fgets(sp, spflag) 2961590Srgrimes SPACE *sp; 2971590Srgrimes enum e_spflag spflag; 2981590Srgrimes{ 2991590Srgrimes static FILE *f; /* Current open file */ 3001590Srgrimes size_t len; 30119829Swosch char *p; 30219829Swosch int c; 3031590Srgrimes 3041590Srgrimes if (f == NULL) 3051590Srgrimes /* Advance to first non-empty file */ 3061590Srgrimes for (;;) { 3071590Srgrimes if (files == NULL) { 3081590Srgrimes lastline = 1; 3091590Srgrimes return (0); 3101590Srgrimes } 3111590Srgrimes if (files->fname == NULL) { 31296175Sjmallett if (inplace != NULL) 31396175Sjmallett errx(1, "-i may not be used with stdin"); 3141590Srgrimes f = stdin; 3151590Srgrimes fname = "stdin"; 3161590Srgrimes } else { 31796175Sjmallett if (inplace != NULL) { 31896175Sjmallett if (inplace_edit(&files->fname) == -1) 31996175Sjmallett continue; 32096175Sjmallett } 3211590Srgrimes fname = files->fname; 32297238Stjr if ((f = fopen(fname, "r")) == NULL) { 32397238Stjr warn("%s", fname); 32497238Stjr rval = 1; 32597238Stjr files = files->next; 32697238Stjr continue; 32797238Stjr } 32896188Sjmallett if (inplace != NULL && *inplace == '\0') 32996188Sjmallett unlink(fname); 3301590Srgrimes } 33119829Swosch if ((c = getc(f)) != EOF) { 33219829Swosch (void)ungetc(c, f); 3331590Srgrimes break; 3341590Srgrimes } 3351590Srgrimes (void)fclose(f); 3361590Srgrimes files = files->next; 3371590Srgrimes } 3381590Srgrimes 3391590Srgrimes if (lastline) { 3401590Srgrimes sp->len = 0; 3411590Srgrimes return (0); 3421590Srgrimes } 3431590Srgrimes 3441590Srgrimes /* 3451590Srgrimes * Use fgetln so that we can handle essentially infinite input data. 34619829Swosch * Can't use the pointer into the stdio buffer as the process space 34719829Swosch * because the ungetc() can cause it to move. 3481590Srgrimes */ 3491590Srgrimes p = fgetln(f, &len); 3501590Srgrimes if (ferror(f)) 35128066Scharnier errx(1, "%s: %s", fname, strerror(errno ? errno : EIO)); 3521590Srgrimes cspace(sp, p, len, spflag); 3531590Srgrimes 3541590Srgrimes linenum++; 3551590Srgrimes /* Advance to next non-empty file */ 35619829Swosch while ((c = getc(f)) == EOF) { 3571590Srgrimes (void)fclose(f); 35897238Stjrnext: files = files->next; 3591590Srgrimes if (files == NULL) { 3601590Srgrimes lastline = 1; 3611590Srgrimes return (1); 3621590Srgrimes } 3631590Srgrimes if (files->fname == NULL) { 36496175Sjmallett if (inplace != NULL) 36596175Sjmallett errx(1, "-i may not be used with stdin"); 3661590Srgrimes f = stdin; 3671590Srgrimes fname = "stdin"; 3681590Srgrimes } else { 36996175Sjmallett if (inplace != NULL) { 37096175Sjmallett if (inplace_edit(&files->fname) == -1) 37196175Sjmallett continue; 37296175Sjmallett } 3731590Srgrimes fname = files->fname; 37497238Stjr if ((f = fopen(fname, "r")) == NULL) { 37597238Stjr warn("%s", fname); 37697238Stjr rval = 1; 37797238Stjr goto next; 37897238Stjr } 37996188Sjmallett if (inplace != NULL && *inplace == '\0') 38096188Sjmallett unlink(fname); 3811590Srgrimes } 3821590Srgrimes } 38319829Swosch (void)ungetc(c, f); 3841590Srgrimes return (1); 3851590Srgrimes} 3861590Srgrimes 3871590Srgrimes/* 3881590Srgrimes * Add a compilation unit to the linked list 3891590Srgrimes */ 3901590Srgrimesstatic void 3911590Srgrimesadd_compunit(type, s) 3921590Srgrimes enum e_cut type; 3931590Srgrimes char *s; 3941590Srgrimes{ 3951590Srgrimes struct s_compunit *cu; 3961590Srgrimes 39780286Sobrien if ((cu = malloc(sizeof(struct s_compunit))) == NULL) 39880286Sobrien err(1, "malloc"); 3991590Srgrimes cu->type = type; 4001590Srgrimes cu->s = s; 4011590Srgrimes cu->next = NULL; 4021590Srgrimes *cu_nextp = cu; 4031590Srgrimes cu_nextp = &cu->next; 4041590Srgrimes} 4051590Srgrimes 4061590Srgrimes/* 4071590Srgrimes * Add a file to the linked list 4081590Srgrimes */ 4091590Srgrimesstatic void 4101590Srgrimesadd_file(s) 4111590Srgrimes char *s; 4121590Srgrimes{ 4131590Srgrimes struct s_flist *fp; 4141590Srgrimes 41580286Sobrien if ((fp = malloc(sizeof(struct s_flist))) == NULL) 41680286Sobrien err(1, "malloc"); 4171590Srgrimes fp->next = NULL; 4181590Srgrimes *fl_nextp = fp; 4191590Srgrimes fp->fname = s; 4201590Srgrimes fl_nextp = &fp->next; 4211590Srgrimes} 42296175Sjmallett 42396175Sjmallett/* 42496175Sjmallett * Modify a pointer to a filename for inplace editing and reopen stdout 42596175Sjmallett */ 42696175Sjmallettstatic int 42796185Sjmallettinplace_edit(filename) 42896185Sjmallett char **filename; 42996175Sjmallett{ 43096175Sjmallett struct stat orig; 43196175Sjmallett int input, output; 43296175Sjmallett char backup[MAXPATHLEN]; 43396175Sjmallett char *buffer; 43496175Sjmallett 43596185Sjmallett if (lstat(*filename, &orig) == -1) 43696175Sjmallett err(1, "lstat"); 43796175Sjmallett if ((orig.st_mode & S_IFREG) == 0) { 43896185Sjmallett warnx("cannot inplace edit %s, not a regular file", *filename); 43996175Sjmallett return -1; 44096175Sjmallett } 44196175Sjmallett 44296188Sjmallett if (*inplace == '\0') { 44396188Sjmallett char template[] = "/tmp/sed.XXXXXXXXXX"; 44496175Sjmallett 44596621Sgreen output = mkstemp(template); 44696621Sgreen if (output == -1) 44796621Sgreen err(1, "mkstemp"); 44896188Sjmallett strlcpy(backup, template, MAXPATHLEN); 44996188Sjmallett } else { 45096188Sjmallett strlcpy(backup, *filename, MAXPATHLEN); 45196188Sjmallett strlcat(backup, inplace, MAXPATHLEN); 45296621Sgreen output = open(backup, O_WRONLY | O_CREAT | O_EXCL); 45396621Sgreen if (output == -1) 45496621Sgreen err(1, "open(%s)", backup); 45596188Sjmallett } 45696188Sjmallett 45796185Sjmallett input = open(*filename, O_RDONLY); 45896175Sjmallett if (input == -1) 45996185Sjmallett err(1, "open(%s)", *filename); 46096175Sjmallett if (fchmod(output, orig.st_mode & ~S_IFMT) == -1) 46196175Sjmallett err(1, "chmod"); 46296175Sjmallett buffer = malloc(orig.st_size); 46396175Sjmallett if (buffer == NULL) 46496175Sjmallett errx(1, "malloc failed"); 46596175Sjmallett if (read(input, buffer, orig.st_size) == -1) 46696175Sjmallett err(1, "read"); 46796175Sjmallett if (write(output, buffer, orig.st_size) == -1) 46896175Sjmallett err(1, "write"); 46996175Sjmallett close(input); 47096175Sjmallett close(output); 47196185Sjmallett freopen(*filename, "w", stdout); 47296185Sjmallett *filename = strdup(backup); 47396175Sjmallett return 0; 47496175Sjmallett} 475