main.c revision 98601
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 98601 2002-06-22 01:42:26Z 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 5198200Ssobomax#include <sys/types.h> 5298200Ssobomax#include <sys/mman.h> 5396175Sjmallett#include <sys/param.h> 5496175Sjmallett#include <sys/stat.h> 551590Srgrimes 5628066Scharnier#include <err.h> 571590Srgrimes#include <errno.h> 581590Srgrimes#include <fcntl.h> 5917522Sache#include <locale.h> 601590Srgrimes#include <regex.h> 611590Srgrimes#include <stddef.h> 621590Srgrimes#include <stdio.h> 631590Srgrimes#include <stdlib.h> 641590Srgrimes#include <string.h> 651590Srgrimes#include <unistd.h> 661590Srgrimes 671590Srgrimes#include "defs.h" 681590Srgrimes#include "extern.h" 691590Srgrimes 701590Srgrimes/* 711590Srgrimes * Linked list of units (strings and files) to be compiled 721590Srgrimes */ 731590Srgrimesstruct s_compunit { 741590Srgrimes struct s_compunit *next; 751590Srgrimes enum e_cut {CU_FILE, CU_STRING} type; 761590Srgrimes char *s; /* Pointer to string or fname */ 771590Srgrimes}; 781590Srgrimes 791590Srgrimes/* 801590Srgrimes * Linked list pointer to compilation units and pointer to current 811590Srgrimes * next pointer. 821590Srgrimes */ 831590Srgrimesstatic struct s_compunit *script, **cu_nextp = &script; 841590Srgrimes 851590Srgrimes/* 861590Srgrimes * Linked list of files to be processed 871590Srgrimes */ 881590Srgrimesstruct s_flist { 891590Srgrimes char *fname; 901590Srgrimes struct s_flist *next; 911590Srgrimes}; 921590Srgrimes 931590Srgrimes/* 941590Srgrimes * Linked list pointer to files and pointer to current 951590Srgrimes * next pointer. 961590Srgrimes */ 971590Srgrimesstatic struct s_flist *files, **fl_nextp = &files; 981590Srgrimes 991590Srgrimesint aflag, eflag, nflag; 10058309Sgreenint rflags = 0; 10197238Stjrstatic int rval; /* Exit status */ 1021590Srgrimes 1031590Srgrimes/* 1041590Srgrimes * Current file and line number; line numbers restart across compilation 1051590Srgrimes * units, but span across input files. 1061590Srgrimes */ 10787766Smarkmconst char *fname; /* File name. */ 10896175Sjmallettconst char *inplace; /* Inplace edit file extension. */ 1091590Srgrimesu_long linenum; 1101590Srgrimesint lastline; /* TRUE on the last line of the last file */ 1111590Srgrimes 11292922Simpstatic void add_compunit(enum e_cut, char *); 11392922Simpstatic void add_file(char *); 11496175Sjmallettstatic int inplace_edit(char **); 11592922Simpstatic void usage(void); 1161590Srgrimes 1171590Srgrimesint 1181590Srgrimesmain(argc, argv) 1191590Srgrimes int argc; 1201590Srgrimes char *argv[]; 1211590Srgrimes{ 1221590Srgrimes int c, fflag; 12360394Snsayer char *temp_arg; 1241590Srgrimes 12517522Sache (void) setlocale(LC_ALL, ""); 12617522Sache 1271590Srgrimes fflag = 0; 12896175Sjmallett inplace = NULL; 12996175Sjmallett 13096175Sjmallett while ((c = getopt(argc, argv, "Eae:f:i:n")) != -1) 1311590Srgrimes switch (c) { 13258309Sgreen case 'E': 13358309Sgreen rflags = REG_EXTENDED; 13458309Sgreen break; 1351590Srgrimes case 'a': 1361590Srgrimes aflag = 1; 1371590Srgrimes break; 1381590Srgrimes case 'e': 1391590Srgrimes eflag = 1; 14080286Sobrien if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL) 14180286Sobrien err(1, "malloc"); 14260394Snsayer strcpy(temp_arg, optarg); 14360394Snsayer strcat(temp_arg, "\n"); 14460394Snsayer add_compunit(CU_STRING, temp_arg); 1451590Srgrimes break; 1461590Srgrimes case 'f': 1471590Srgrimes fflag = 1; 1481590Srgrimes add_compunit(CU_FILE, optarg); 1491590Srgrimes break; 15096175Sjmallett case 'i': 15196175Sjmallett inplace = optarg; 15296175Sjmallett break; 1531590Srgrimes case 'n': 1541590Srgrimes nflag = 1; 1551590Srgrimes break; 1561590Srgrimes default: 1571590Srgrimes case '?': 15828066Scharnier usage(); 1591590Srgrimes } 1601590Srgrimes argc -= optind; 1611590Srgrimes argv += optind; 1621590Srgrimes 1631590Srgrimes /* First usage case; script is the first arg */ 1641590Srgrimes if (!eflag && !fflag && *argv) { 1651590Srgrimes add_compunit(CU_STRING, *argv); 1661590Srgrimes argv++; 1671590Srgrimes } 1681590Srgrimes 1691590Srgrimes compile(); 1701590Srgrimes 1711590Srgrimes /* Continue with first and start second usage */ 1721590Srgrimes if (*argv) 1731590Srgrimes for (; *argv; argv++) 1741590Srgrimes add_file(*argv); 1751590Srgrimes else 1761590Srgrimes add_file(NULL); 1771590Srgrimes process(); 1781590Srgrimes cfclose(prog, NULL); 1791590Srgrimes if (fclose(stdout)) 18028066Scharnier err(1, "stdout"); 18197238Stjr exit(rval); 1821590Srgrimes} 1831590Srgrimes 18428066Scharnierstatic void 18528066Scharnierusage() 18628066Scharnier{ 18728066Scharnier (void)fprintf(stderr, "%s\n%s\n", 18896189Sjmallett "usage: sed script [-Ean] [-i extension] [file ...]", 18996189Sjmallett " sed [-an] [-i extension] [-e script] ... [-f script_file] ... [file ...]"); 19028066Scharnier exit(1); 19128066Scharnier} 19228066Scharnier 1931590Srgrimes/* 1941590Srgrimes * Like fgets, but go through the chain of compilation units chaining them 1951590Srgrimes * together. Empty strings and files are ignored. 1961590Srgrimes */ 1971590Srgrimeschar * 19841602Sarchiecu_fgets(buf, n, more) 1991590Srgrimes char *buf; 2001590Srgrimes int n; 20141602Sarchie int *more; 2021590Srgrimes{ 2031590Srgrimes static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; 2041590Srgrimes static FILE *f; /* Current open file */ 2051590Srgrimes static char *s; /* Current pointer inside string */ 2061590Srgrimes static char string_ident[30]; 2071590Srgrimes char *p; 2081590Srgrimes 2091590Srgrimesagain: 2101590Srgrimes switch (state) { 2111590Srgrimes case ST_EOF: 21241602Sarchie if (script == NULL) { 21341602Sarchie if (more != NULL) 21441602Sarchie *more = 0; 2151590Srgrimes return (NULL); 21641602Sarchie } 2171590Srgrimes linenum = 0; 2181590Srgrimes switch (script->type) { 2191590Srgrimes case CU_FILE: 2201590Srgrimes if ((f = fopen(script->s, "r")) == NULL) 22128066Scharnier err(1, "%s", script->s); 2221590Srgrimes fname = script->s; 2231590Srgrimes state = ST_FILE; 2241590Srgrimes goto again; 2251590Srgrimes case CU_STRING: 2261590Srgrimes if ((snprintf(string_ident, 2271590Srgrimes sizeof(string_ident), "\"%s\"", script->s)) >= 2281590Srgrimes sizeof(string_ident) - 1) 2291590Srgrimes (void)strcpy(string_ident + 2301590Srgrimes sizeof(string_ident) - 6, " ...\""); 2311590Srgrimes fname = string_ident; 2321590Srgrimes s = script->s; 2331590Srgrimes state = ST_STRING; 2341590Srgrimes goto again; 2351590Srgrimes } 2361590Srgrimes case ST_FILE: 2371590Srgrimes if ((p = fgets(buf, n, f)) != NULL) { 2381590Srgrimes linenum++; 2391590Srgrimes if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') 2401590Srgrimes nflag = 1; 24141602Sarchie if (more != NULL) 24241602Sarchie *more = !feof(f); 2431590Srgrimes return (p); 2441590Srgrimes } 2451590Srgrimes script = script->next; 2461590Srgrimes (void)fclose(f); 2471590Srgrimes state = ST_EOF; 2481590Srgrimes goto again; 2491590Srgrimes case ST_STRING: 2501590Srgrimes if (linenum == 0 && s[0] == '#' && s[1] == 'n') 2511590Srgrimes nflag = 1; 2521590Srgrimes p = buf; 2531590Srgrimes for (;;) { 2541590Srgrimes if (n-- <= 1) { 2551590Srgrimes *p = '\0'; 2561590Srgrimes linenum++; 25741602Sarchie if (more != NULL) 25841602Sarchie *more = 1; 2591590Srgrimes return (buf); 2601590Srgrimes } 2611590Srgrimes switch (*s) { 2621590Srgrimes case '\0': 2631590Srgrimes state = ST_EOF; 2641590Srgrimes if (s == script->s) { 2651590Srgrimes script = script->next; 2661590Srgrimes goto again; 2671590Srgrimes } else { 2681590Srgrimes script = script->next; 2691590Srgrimes *p = '\0'; 2701590Srgrimes linenum++; 27141602Sarchie if (more != NULL) 27241602Sarchie *more = 0; 2731590Srgrimes return (buf); 2741590Srgrimes } 2751590Srgrimes case '\n': 2761590Srgrimes *p++ = '\n'; 2771590Srgrimes *p = '\0'; 2781590Srgrimes s++; 2791590Srgrimes linenum++; 28041602Sarchie if (more != NULL) 28141602Sarchie *more = 0; 2821590Srgrimes return (buf); 2831590Srgrimes default: 2841590Srgrimes *p++ = *s++; 2851590Srgrimes } 2861590Srgrimes } 2871590Srgrimes } 2881590Srgrimes /* NOTREACHED */ 28928066Scharnier return (NULL); 2901590Srgrimes} 2911590Srgrimes 2921590Srgrimes/* 2931590Srgrimes * Like fgets, but go through the list of files chaining them together. 2941590Srgrimes * Set len to the length of the line. 2951590Srgrimes */ 2961590Srgrimesint 2971590Srgrimesmf_fgets(sp, spflag) 2981590Srgrimes SPACE *sp; 2991590Srgrimes enum e_spflag spflag; 3001590Srgrimes{ 3011590Srgrimes static FILE *f; /* Current open file */ 3021590Srgrimes size_t len; 30319829Swosch char *p; 30419829Swosch int c; 30598200Ssobomax static int firstfile; 3061590Srgrimes 30798200Ssobomax if (f == NULL) { 30898200Ssobomax /* stdin? */ 30998200Ssobomax if (files->fname == NULL) { 31098200Ssobomax if (inplace != NULL) 31198200Ssobomax errx(1, "-i may not be used with stdin"); 31298200Ssobomax f = stdin; 31398200Ssobomax fname = "stdin"; 31498200Ssobomax } 31598200Ssobomax firstfile = 1; 31698200Ssobomax } 31798200Ssobomax 31898200Ssobomax for (;;) { 31998200Ssobomax if (f != NULL && (c = getc(f)) != EOF) { 32098200Ssobomax (void)ungetc(c, f); 32198200Ssobomax break; 32298200Ssobomax } 32398200Ssobomax /* If we are here then either eof or no files are open yet */ 32498200Ssobomax if (f == stdin) { 32598294Stjr sp->len = 0; 32698200Ssobomax lastline = 1; 32798200Ssobomax return (0); 32898200Ssobomax } 32998200Ssobomax if (f != NULL) { 33098200Ssobomax fclose(f); 33198200Ssobomax } 33298200Ssobomax if (firstfile == 0) { 3331590Srgrimes files = files->next; 33498200Ssobomax } else 33598200Ssobomax firstfile = 0; 33698200Ssobomax if (files == NULL) { 33798294Stjr sp->len = 0; 33898200Ssobomax lastline = 1; 33998200Ssobomax return (0); 3401590Srgrimes } 34198200Ssobomax if (inplace != NULL) { 34298200Ssobomax if (inplace_edit(&files->fname) == -1) 34398200Ssobomax continue; 34498200Ssobomax } 34598200Ssobomax fname = files->fname; 34698200Ssobomax if ((f = fopen(fname, "r")) == NULL) { 34798200Ssobomax warn("%s", fname); 34898201Stjr rval = 1; 34998200Ssobomax continue; 35098200Ssobomax } 35198200Ssobomax if (inplace != NULL && *inplace == '\0') 35298200Ssobomax unlink(fname); 3531590Srgrimes } 3541590Srgrimes /* 35598200Ssobomax * We are here only when f is open and we still have something to 35698200Ssobomax * read from it. 35798200Ssobomax * 3581590Srgrimes * Use fgetln so that we can handle essentially infinite input data. 35919829Swosch * Can't use the pointer into the stdio buffer as the process space 36019829Swosch * because the ungetc() can cause it to move. 3611590Srgrimes */ 3621590Srgrimes p = fgetln(f, &len); 3631590Srgrimes if (ferror(f)) 36428066Scharnier errx(1, "%s: %s", fname, strerror(errno ? errno : EIO)); 36598601Stjr if (len != 0 && p[len - 1] == '\n') 36698601Stjr len--; 3671590Srgrimes cspace(sp, p, len, spflag); 3681590Srgrimes 3691590Srgrimes linenum++; 37098200Ssobomax if (files->next == NULL) { 37198200Ssobomax if ((c = getc(f)) != EOF) { 37298200Ssobomax (void)ungetc(c, f); 37398200Ssobomax } else { 3741590Srgrimes lastline = 1; 3751590Srgrimes } 3761590Srgrimes } 37798200Ssobomax 3781590Srgrimes return (1); 3791590Srgrimes} 3801590Srgrimes 3811590Srgrimes/* 3821590Srgrimes * Add a compilation unit to the linked list 3831590Srgrimes */ 3841590Srgrimesstatic void 3851590Srgrimesadd_compunit(type, s) 3861590Srgrimes enum e_cut type; 3871590Srgrimes char *s; 3881590Srgrimes{ 3891590Srgrimes struct s_compunit *cu; 3901590Srgrimes 39180286Sobrien if ((cu = malloc(sizeof(struct s_compunit))) == NULL) 39280286Sobrien err(1, "malloc"); 3931590Srgrimes cu->type = type; 3941590Srgrimes cu->s = s; 3951590Srgrimes cu->next = NULL; 3961590Srgrimes *cu_nextp = cu; 3971590Srgrimes cu_nextp = &cu->next; 3981590Srgrimes} 3991590Srgrimes 4001590Srgrimes/* 4011590Srgrimes * Add a file to the linked list 4021590Srgrimes */ 4031590Srgrimesstatic void 4041590Srgrimesadd_file(s) 4051590Srgrimes char *s; 4061590Srgrimes{ 4071590Srgrimes struct s_flist *fp; 4081590Srgrimes 40980286Sobrien if ((fp = malloc(sizeof(struct s_flist))) == NULL) 41080286Sobrien err(1, "malloc"); 4111590Srgrimes fp->next = NULL; 4121590Srgrimes *fl_nextp = fp; 4131590Srgrimes fp->fname = s; 4141590Srgrimes fl_nextp = &fp->next; 4151590Srgrimes} 41696175Sjmallett 41796175Sjmallett/* 41896175Sjmallett * Modify a pointer to a filename for inplace editing and reopen stdout 41996175Sjmallett */ 42096175Sjmallettstatic int 42196185Sjmallettinplace_edit(filename) 42296185Sjmallett char **filename; 42396175Sjmallett{ 42496175Sjmallett struct stat orig; 42596175Sjmallett int input, output; 42696175Sjmallett char backup[MAXPATHLEN]; 42796175Sjmallett char *buffer; 42896175Sjmallett 42996185Sjmallett if (lstat(*filename, &orig) == -1) 43096175Sjmallett err(1, "lstat"); 43196175Sjmallett if ((orig.st_mode & S_IFREG) == 0) { 43296185Sjmallett warnx("cannot inplace edit %s, not a regular file", *filename); 43396175Sjmallett return -1; 43496175Sjmallett } 43596175Sjmallett 43696188Sjmallett if (*inplace == '\0') { 43796188Sjmallett char template[] = "/tmp/sed.XXXXXXXXXX"; 43896175Sjmallett 43996621Sgreen output = mkstemp(template); 44096621Sgreen if (output == -1) 44196621Sgreen err(1, "mkstemp"); 44296188Sjmallett strlcpy(backup, template, MAXPATHLEN); 44396188Sjmallett } else { 44496188Sjmallett strlcpy(backup, *filename, MAXPATHLEN); 44596188Sjmallett strlcat(backup, inplace, MAXPATHLEN); 44698200Ssobomax output = open(backup, O_WRONLY | O_CREAT | O_TRUNC); 44796621Sgreen if (output == -1) 44896621Sgreen err(1, "open(%s)", backup); 44996188Sjmallett } 45096188Sjmallett 45196185Sjmallett input = open(*filename, O_RDONLY); 45296175Sjmallett if (input == -1) 45396185Sjmallett err(1, "open(%s)", *filename); 45496175Sjmallett if (fchmod(output, orig.st_mode & ~S_IFMT) == -1) 45596175Sjmallett err(1, "chmod"); 45698200Ssobomax buffer = (char *)mmap(0, orig.st_size, PROT_READ, MAP_SHARED, input, 0); 45798200Ssobomax if (buffer == MAP_FAILED) 45898200Ssobomax err(1, "mmap(%s)", *filename); 45996175Sjmallett if (write(output, buffer, orig.st_size) == -1) 46098200Ssobomax err(1, "write(%s)", backup); 46198200Ssobomax if (munmap(buffer, orig.st_size) == -1) 46298200Ssobomax err(1, "munmap(%s)", *filename); 46396175Sjmallett close(input); 46496175Sjmallett close(output); 46596185Sjmallett freopen(*filename, "w", stdout); 46696185Sjmallett *filename = strdup(backup); 46796175Sjmallett return 0; 46896175Sjmallett} 469