main.c revision 96175
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 96175 2002-05-07 18:32:18Z jmallett $"); 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; 991590Srgrimes 1001590Srgrimes/* 1011590Srgrimes * Current file and line number; line numbers restart across compilation 1021590Srgrimes * units, but span across input files. 1031590Srgrimes */ 10487766Smarkmconst char *fname; /* File name. */ 10596175Sjmallettconst char *inplace; /* Inplace edit file extension. */ 1061590Srgrimesu_long linenum; 1071590Srgrimesint lastline; /* TRUE on the last line of the last file */ 1081590Srgrimes 10992922Simpstatic void add_compunit(enum e_cut, char *); 11092922Simpstatic void add_file(char *); 11196175Sjmallettstatic int inplace_edit(char **); 11292922Simpstatic void usage(void); 1131590Srgrimes 1141590Srgrimesint 1151590Srgrimesmain(argc, argv) 1161590Srgrimes int argc; 1171590Srgrimes char *argv[]; 1181590Srgrimes{ 1191590Srgrimes int c, fflag; 12060394Snsayer char *temp_arg; 1211590Srgrimes 12217522Sache (void) setlocale(LC_ALL, ""); 12317522Sache 1241590Srgrimes fflag = 0; 12596175Sjmallett inplace = NULL; 12696175Sjmallett 12796175Sjmallett while ((c = getopt(argc, argv, "Eae:f:i:n")) != -1) 1281590Srgrimes switch (c) { 12958309Sgreen case 'E': 13058309Sgreen rflags = REG_EXTENDED; 13158309Sgreen break; 1321590Srgrimes case 'a': 1331590Srgrimes aflag = 1; 1341590Srgrimes break; 1351590Srgrimes case 'e': 1361590Srgrimes eflag = 1; 13780286Sobrien if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL) 13880286Sobrien err(1, "malloc"); 13960394Snsayer strcpy(temp_arg, optarg); 14060394Snsayer strcat(temp_arg, "\n"); 14160394Snsayer add_compunit(CU_STRING, temp_arg); 1421590Srgrimes break; 1431590Srgrimes case 'f': 1441590Srgrimes fflag = 1; 1451590Srgrimes add_compunit(CU_FILE, optarg); 1461590Srgrimes break; 14796175Sjmallett case 'i': 14896175Sjmallett inplace = optarg; 14996175Sjmallett break; 1501590Srgrimes case 'n': 1511590Srgrimes nflag = 1; 1521590Srgrimes break; 1531590Srgrimes default: 1541590Srgrimes case '?': 15528066Scharnier usage(); 1561590Srgrimes } 1571590Srgrimes argc -= optind; 1581590Srgrimes argv += optind; 1591590Srgrimes 1601590Srgrimes /* First usage case; script is the first arg */ 1611590Srgrimes if (!eflag && !fflag && *argv) { 1621590Srgrimes add_compunit(CU_STRING, *argv); 1631590Srgrimes argv++; 1641590Srgrimes } 1651590Srgrimes 1661590Srgrimes compile(); 1671590Srgrimes 1681590Srgrimes /* Continue with first and start second usage */ 1691590Srgrimes if (*argv) 1701590Srgrimes for (; *argv; argv++) 1711590Srgrimes add_file(*argv); 1721590Srgrimes else 1731590Srgrimes add_file(NULL); 1741590Srgrimes process(); 1751590Srgrimes cfclose(prog, NULL); 1761590Srgrimes if (fclose(stdout)) 17728066Scharnier err(1, "stdout"); 1781590Srgrimes exit (0); 1791590Srgrimes} 1801590Srgrimes 18128066Scharnierstatic void 18228066Scharnierusage() 18328066Scharnier{ 18428066Scharnier (void)fprintf(stderr, "%s\n%s\n", 18558309Sgreen "usage: sed script [-Ean] [file ...]", 18628066Scharnier " sed [-an] [-e script] ... [-f script_file] ... [file ...]"); 18728066Scharnier exit(1); 18828066Scharnier} 18928066Scharnier 1901590Srgrimes/* 1911590Srgrimes * Like fgets, but go through the chain of compilation units chaining them 1921590Srgrimes * together. Empty strings and files are ignored. 1931590Srgrimes */ 1941590Srgrimeschar * 19541602Sarchiecu_fgets(buf, n, more) 1961590Srgrimes char *buf; 1971590Srgrimes int n; 19841602Sarchie int *more; 1991590Srgrimes{ 2001590Srgrimes static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; 2011590Srgrimes static FILE *f; /* Current open file */ 2021590Srgrimes static char *s; /* Current pointer inside string */ 2031590Srgrimes static char string_ident[30]; 2041590Srgrimes char *p; 2051590Srgrimes 2061590Srgrimesagain: 2071590Srgrimes switch (state) { 2081590Srgrimes case ST_EOF: 20941602Sarchie if (script == NULL) { 21041602Sarchie if (more != NULL) 21141602Sarchie *more = 0; 2121590Srgrimes return (NULL); 21341602Sarchie } 2141590Srgrimes linenum = 0; 2151590Srgrimes switch (script->type) { 2161590Srgrimes case CU_FILE: 2171590Srgrimes if ((f = fopen(script->s, "r")) == NULL) 21828066Scharnier err(1, "%s", script->s); 2191590Srgrimes fname = script->s; 2201590Srgrimes state = ST_FILE; 2211590Srgrimes goto again; 2221590Srgrimes case CU_STRING: 2231590Srgrimes if ((snprintf(string_ident, 2241590Srgrimes sizeof(string_ident), "\"%s\"", script->s)) >= 2251590Srgrimes sizeof(string_ident) - 1) 2261590Srgrimes (void)strcpy(string_ident + 2271590Srgrimes sizeof(string_ident) - 6, " ...\""); 2281590Srgrimes fname = string_ident; 2291590Srgrimes s = script->s; 2301590Srgrimes state = ST_STRING; 2311590Srgrimes goto again; 2321590Srgrimes } 2331590Srgrimes case ST_FILE: 2341590Srgrimes if ((p = fgets(buf, n, f)) != NULL) { 2351590Srgrimes linenum++; 2361590Srgrimes if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') 2371590Srgrimes nflag = 1; 23841602Sarchie if (more != NULL) 23941602Sarchie *more = !feof(f); 2401590Srgrimes return (p); 2411590Srgrimes } 2421590Srgrimes script = script->next; 2431590Srgrimes (void)fclose(f); 2441590Srgrimes state = ST_EOF; 2451590Srgrimes goto again; 2461590Srgrimes case ST_STRING: 2471590Srgrimes if (linenum == 0 && s[0] == '#' && s[1] == 'n') 2481590Srgrimes nflag = 1; 2491590Srgrimes p = buf; 2501590Srgrimes for (;;) { 2511590Srgrimes if (n-- <= 1) { 2521590Srgrimes *p = '\0'; 2531590Srgrimes linenum++; 25441602Sarchie if (more != NULL) 25541602Sarchie *more = 1; 2561590Srgrimes return (buf); 2571590Srgrimes } 2581590Srgrimes switch (*s) { 2591590Srgrimes case '\0': 2601590Srgrimes state = ST_EOF; 2611590Srgrimes if (s == script->s) { 2621590Srgrimes script = script->next; 2631590Srgrimes goto again; 2641590Srgrimes } else { 2651590Srgrimes script = script->next; 2661590Srgrimes *p = '\0'; 2671590Srgrimes linenum++; 26841602Sarchie if (more != NULL) 26941602Sarchie *more = 0; 2701590Srgrimes return (buf); 2711590Srgrimes } 2721590Srgrimes case '\n': 2731590Srgrimes *p++ = '\n'; 2741590Srgrimes *p = '\0'; 2751590Srgrimes s++; 2761590Srgrimes linenum++; 27741602Sarchie if (more != NULL) 27841602Sarchie *more = 0; 2791590Srgrimes return (buf); 2801590Srgrimes default: 2811590Srgrimes *p++ = *s++; 2821590Srgrimes } 2831590Srgrimes } 2841590Srgrimes } 2851590Srgrimes /* NOTREACHED */ 28628066Scharnier return (NULL); 2871590Srgrimes} 2881590Srgrimes 2891590Srgrimes/* 2901590Srgrimes * Like fgets, but go through the list of files chaining them together. 2911590Srgrimes * Set len to the length of the line. 2921590Srgrimes */ 2931590Srgrimesint 2941590Srgrimesmf_fgets(sp, spflag) 2951590Srgrimes SPACE *sp; 2961590Srgrimes enum e_spflag spflag; 2971590Srgrimes{ 2981590Srgrimes static FILE *f; /* Current open file */ 2991590Srgrimes size_t len; 30019829Swosch char *p; 30119829Swosch int c; 3021590Srgrimes 3031590Srgrimes if (f == NULL) 3041590Srgrimes /* Advance to first non-empty file */ 3051590Srgrimes for (;;) { 3061590Srgrimes if (files == NULL) { 3071590Srgrimes lastline = 1; 3081590Srgrimes return (0); 3091590Srgrimes } 3101590Srgrimes if (files->fname == NULL) { 31196175Sjmallett if (inplace != NULL) 31296175Sjmallett errx(1, "-i may not be used with stdin"); 3131590Srgrimes f = stdin; 3141590Srgrimes fname = "stdin"; 3151590Srgrimes } else { 31696175Sjmallett if (inplace != NULL) { 31796175Sjmallett if (inplace_edit(&files->fname) == -1) 31896175Sjmallett continue; 31996175Sjmallett } 3201590Srgrimes fname = files->fname; 3211590Srgrimes if ((f = fopen(fname, "r")) == NULL) 32228066Scharnier err(1, "%s", fname); 3231590Srgrimes } 32419829Swosch if ((c = getc(f)) != EOF) { 32519829Swosch (void)ungetc(c, f); 3261590Srgrimes break; 3271590Srgrimes } 3281590Srgrimes (void)fclose(f); 3291590Srgrimes files = files->next; 3301590Srgrimes } 3311590Srgrimes 3321590Srgrimes if (lastline) { 3331590Srgrimes sp->len = 0; 3341590Srgrimes return (0); 3351590Srgrimes } 3361590Srgrimes 3371590Srgrimes /* 3381590Srgrimes * Use fgetln so that we can handle essentially infinite input data. 33919829Swosch * Can't use the pointer into the stdio buffer as the process space 34019829Swosch * because the ungetc() can cause it to move. 3411590Srgrimes */ 3421590Srgrimes p = fgetln(f, &len); 3431590Srgrimes if (ferror(f)) 34428066Scharnier errx(1, "%s: %s", fname, strerror(errno ? errno : EIO)); 3451590Srgrimes cspace(sp, p, len, spflag); 3461590Srgrimes 3471590Srgrimes linenum++; 3481590Srgrimes /* Advance to next non-empty file */ 34919829Swosch while ((c = getc(f)) == EOF) { 3501590Srgrimes (void)fclose(f); 3511590Srgrimes files = files->next; 3521590Srgrimes if (files == NULL) { 3531590Srgrimes lastline = 1; 3541590Srgrimes return (1); 3551590Srgrimes } 3561590Srgrimes if (files->fname == NULL) { 35796175Sjmallett if (inplace != NULL) 35896175Sjmallett errx(1, "-i may not be used with stdin"); 3591590Srgrimes f = stdin; 3601590Srgrimes fname = "stdin"; 3611590Srgrimes } else { 36296175Sjmallett if (inplace != NULL) { 36396175Sjmallett if (inplace_edit(&files->fname) == -1) 36496175Sjmallett continue; 36596175Sjmallett } 3661590Srgrimes fname = files->fname; 3671590Srgrimes if ((f = fopen(fname, "r")) == NULL) 36828066Scharnier err(1, "%s", fname); 3691590Srgrimes } 3701590Srgrimes } 37119829Swosch (void)ungetc(c, f); 3721590Srgrimes return (1); 3731590Srgrimes} 3741590Srgrimes 3751590Srgrimes/* 3761590Srgrimes * Add a compilation unit to the linked list 3771590Srgrimes */ 3781590Srgrimesstatic void 3791590Srgrimesadd_compunit(type, s) 3801590Srgrimes enum e_cut type; 3811590Srgrimes char *s; 3821590Srgrimes{ 3831590Srgrimes struct s_compunit *cu; 3841590Srgrimes 38580286Sobrien if ((cu = malloc(sizeof(struct s_compunit))) == NULL) 38680286Sobrien err(1, "malloc"); 3871590Srgrimes cu->type = type; 3881590Srgrimes cu->s = s; 3891590Srgrimes cu->next = NULL; 3901590Srgrimes *cu_nextp = cu; 3911590Srgrimes cu_nextp = &cu->next; 3921590Srgrimes} 3931590Srgrimes 3941590Srgrimes/* 3951590Srgrimes * Add a file to the linked list 3961590Srgrimes */ 3971590Srgrimesstatic void 3981590Srgrimesadd_file(s) 3991590Srgrimes char *s; 4001590Srgrimes{ 4011590Srgrimes struct s_flist *fp; 4021590Srgrimes 40380286Sobrien if ((fp = malloc(sizeof(struct s_flist))) == NULL) 40480286Sobrien err(1, "malloc"); 4051590Srgrimes fp->next = NULL; 4061590Srgrimes *fl_nextp = fp; 4071590Srgrimes fp->fname = s; 4081590Srgrimes fl_nextp = &fp->next; 4091590Srgrimes} 41096175Sjmallett 41196175Sjmallett/* 41296175Sjmallett * Modify a pointer to a filename for inplace editing and reopen stdout 41396175Sjmallett */ 41496175Sjmallettstatic int 41596175Sjmallettinplace_edit(fname) 41696175Sjmallett char **fname; 41796175Sjmallett{ 41896175Sjmallett struct stat orig; 41996175Sjmallett int input, output; 42096175Sjmallett char backup[MAXPATHLEN]; 42196175Sjmallett char *buffer; 42296175Sjmallett 42396175Sjmallett if (lstat(*fname, &orig) == -1) 42496175Sjmallett err(1, "lstat"); 42596175Sjmallett if ((orig.st_mode & S_IFREG) == 0) { 42696175Sjmallett warnx("cannot inplace edit %s, not a regular file", fname); 42796175Sjmallett return -1; 42896175Sjmallett } 42996175Sjmallett 43096175Sjmallett strlcpy(backup, *fname, MAXPATHLEN); 43196175Sjmallett strlcat(backup, inplace, MAXPATHLEN); 43296175Sjmallett 43396175Sjmallett input = open(*fname, O_RDONLY); 43496175Sjmallett if (input == -1) 43596175Sjmallett err(1, "open(%s)", fname); 43696175Sjmallett output = open(backup, O_WRONLY|O_CREAT); 43796175Sjmallett if (output == -1) 43896175Sjmallett err(1, "open(%s)", backup); 43996175Sjmallett if (fchmod(output, orig.st_mode & ~S_IFMT) == -1) 44096175Sjmallett err(1, "chmod"); 44196175Sjmallett buffer = malloc(orig.st_size); 44296175Sjmallett if (buffer == NULL) 44396175Sjmallett errx(1, "malloc failed"); 44496175Sjmallett if (read(input, buffer, orig.st_size) == -1) 44596175Sjmallett err(1, "read"); 44696175Sjmallett if (write(output, buffer, orig.st_size) == -1) 44796175Sjmallett err(1, "write"); 44896175Sjmallett close(input); 44996175Sjmallett close(output); 45096175Sjmallett freopen(*fname, "w", stdout); 45196175Sjmallett *fname = strdup(backup); 45296175Sjmallett return 0; 45396175Sjmallett} 454