main.c revision 252231
11590Srgrimes/*- 2252231Spfg * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson. 31590Srgrimes * Copyright (c) 1992 Diomidis Spinellis. 41590Srgrimes * Copyright (c) 1992, 1993 51590Srgrimes * The Regents of the University of California. All rights reserved. 61590Srgrimes * 71590Srgrimes * This code is derived from software contributed to Berkeley by 81590Srgrimes * Diomidis Spinellis of Imperial College, University of London. 91590Srgrimes * 101590Srgrimes * Redistribution and use in source and binary forms, with or without 111590Srgrimes * modification, are permitted provided that the following conditions 121590Srgrimes * are met: 131590Srgrimes * 1. Redistributions of source code must retain the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer. 151590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer in the 171590Srgrimes * documentation and/or other materials provided with the distribution. 181590Srgrimes * 4. Neither the name of the University nor the names of its contributors 191590Srgrimes * may be used to endorse or promote products derived from this software 201590Srgrimes * without specific prior written permission. 211590Srgrimes * 221590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321590Srgrimes * SUCH DAMAGE. 331590Srgrimes */ 341590Srgrimes 3587766Smarkm#include <sys/cdefs.h> 3687766Smarkm__FBSDID("$FreeBSD: head/usr.bin/sed/main.c 252231 2013-06-26 04:14:19Z pfg $"); 3787766Smarkm 381590Srgrimes#ifndef lint 3928066Scharnierstatic const char copyright[] = 401590Srgrimes"@(#) Copyright (c) 1992, 1993\n\ 411590Srgrimes The Regents of the University of California. All rights reserved.\n"; 4287766Smarkm#endif 431590Srgrimes 441590Srgrimes#ifndef lint 4587766Smarkmstatic const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; 4628066Scharnier#endif 471590Srgrimes 4898200Ssobomax#include <sys/types.h> 4998200Ssobomax#include <sys/mman.h> 5096175Sjmallett#include <sys/param.h> 5196175Sjmallett#include <sys/stat.h> 521590Srgrimes 5328066Scharnier#include <err.h> 541590Srgrimes#include <errno.h> 55200462Sdelphij#include <fcntl.h> 56141563Sstefanf#include <libgen.h> 57132145Stjr#include <limits.h> 5817522Sache#include <locale.h> 591590Srgrimes#include <regex.h> 60200462Sdelphij#include <stddef.h> 61252231Spfg#define _WITH_GETLINE 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 99122049SdesFILE *infile; /* Current input file */ 100122049SdesFILE *outfile; /* Current output file */ 10199352Stjr 1021590Srgrimesint aflag, eflag, nflag; 10358309Sgreenint rflags = 0; 10497238Stjrstatic int rval; /* Exit status */ 1051590Srgrimes 106168921Syarstatic int ispan; /* Whether inplace editing spans across files */ 107168921Syar 1081590Srgrimes/* 1091590Srgrimes * Current file and line number; line numbers restart across compilation 110168921Syar * units, but span across input files. The latter is optional if editing 111168921Syar * in place. 1121590Srgrimes */ 11387766Smarkmconst char *fname; /* File name. */ 114122049Sdesconst char *outfname; /* Output file name */ 115122049Sdesstatic char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */ 116122049Sdesstatic char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */ 117173437Sddsstatic const char *inplace; /* Inplace edit file extension. */ 1181590Srgrimesu_long linenum; 1191590Srgrimes 12092922Simpstatic void add_compunit(enum e_cut, char *); 12192922Simpstatic void add_file(char *); 12292922Simpstatic void usage(void); 1231590Srgrimes 1241590Srgrimesint 125122044Sdesmain(int argc, char *argv[]) 1261590Srgrimes{ 1271590Srgrimes int c, fflag; 12860394Snsayer char *temp_arg; 1291590Srgrimes 13017522Sache (void) setlocale(LC_ALL, ""); 13117522Sache 1321590Srgrimes fflag = 0; 13396175Sjmallett inplace = NULL; 13496175Sjmallett 135205992Simp while ((c = getopt(argc, argv, "EI:ae:f:i:lnr")) != -1) 1361590Srgrimes switch (c) { 137205992Simp case 'r': /* Gnu sed compat */ 13858309Sgreen case 'E': 13958309Sgreen rflags = REG_EXTENDED; 14058309Sgreen break; 141168921Syar case 'I': 142168921Syar inplace = optarg; 143168921Syar ispan = 1; /* span across input files */ 144168921Syar break; 1451590Srgrimes case 'a': 1461590Srgrimes aflag = 1; 1471590Srgrimes break; 1481590Srgrimes case 'e': 1491590Srgrimes eflag = 1; 15080286Sobrien if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL) 15180286Sobrien err(1, "malloc"); 15260394Snsayer strcpy(temp_arg, optarg); 15360394Snsayer strcat(temp_arg, "\n"); 15460394Snsayer add_compunit(CU_STRING, temp_arg); 1551590Srgrimes break; 1561590Srgrimes case 'f': 1571590Srgrimes fflag = 1; 1581590Srgrimes add_compunit(CU_FILE, optarg); 1591590Srgrimes break; 16096175Sjmallett case 'i': 16196175Sjmallett inplace = optarg; 162168921Syar ispan = 0; /* don't span across input files */ 16396175Sjmallett break; 164146055Sglebius case 'l': 165146055Sglebius if(setlinebuf(stdout) != 0) 166146055Sglebius warnx("setlinebuf() failed"); 167146055Sglebius break; 1681590Srgrimes case 'n': 1691590Srgrimes nflag = 1; 1701590Srgrimes break; 1711590Srgrimes default: 1721590Srgrimes case '?': 17328066Scharnier usage(); 1741590Srgrimes } 1751590Srgrimes argc -= optind; 1761590Srgrimes argv += optind; 1771590Srgrimes 1781590Srgrimes /* First usage case; script is the first arg */ 1791590Srgrimes if (!eflag && !fflag && *argv) { 1801590Srgrimes add_compunit(CU_STRING, *argv); 1811590Srgrimes argv++; 1821590Srgrimes } 1831590Srgrimes 1841590Srgrimes compile(); 1851590Srgrimes 1861590Srgrimes /* Continue with first and start second usage */ 1871590Srgrimes if (*argv) 1881590Srgrimes for (; *argv; argv++) 1891590Srgrimes add_file(*argv); 1901590Srgrimes else 1911590Srgrimes add_file(NULL); 1921590Srgrimes process(); 1931590Srgrimes cfclose(prog, NULL); 1941590Srgrimes if (fclose(stdout)) 19528066Scharnier err(1, "stdout"); 19697238Stjr exit(rval); 1971590Srgrimes} 1981590Srgrimes 19928066Scharnierstatic void 200122044Sdesusage(void) 20128066Scharnier{ 20228066Scharnier (void)fprintf(stderr, "%s\n%s\n", 203146055Sglebius "usage: sed script [-Ealn] [-i extension] [file ...]", 204146055Sglebius " sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]"); 20528066Scharnier exit(1); 20628066Scharnier} 20728066Scharnier 2081590Srgrimes/* 2091590Srgrimes * Like fgets, but go through the chain of compilation units chaining them 2101590Srgrimes * together. Empty strings and files are ignored. 2111590Srgrimes */ 2121590Srgrimeschar * 213122044Sdescu_fgets(char *buf, int n, int *more) 2141590Srgrimes{ 2151590Srgrimes static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; 2161590Srgrimes static FILE *f; /* Current open file */ 2171590Srgrimes static char *s; /* Current pointer inside string */ 2181590Srgrimes static char string_ident[30]; 2191590Srgrimes char *p; 2201590Srgrimes 2211590Srgrimesagain: 2221590Srgrimes switch (state) { 2231590Srgrimes case ST_EOF: 22441602Sarchie if (script == NULL) { 22541602Sarchie if (more != NULL) 22641602Sarchie *more = 0; 2271590Srgrimes return (NULL); 22841602Sarchie } 2291590Srgrimes linenum = 0; 2301590Srgrimes switch (script->type) { 2311590Srgrimes case CU_FILE: 2321590Srgrimes if ((f = fopen(script->s, "r")) == NULL) 23328066Scharnier err(1, "%s", script->s); 2341590Srgrimes fname = script->s; 2351590Srgrimes state = ST_FILE; 2361590Srgrimes goto again; 2371590Srgrimes case CU_STRING: 238176126Sdwmalone if (((size_t)snprintf(string_ident, 2391590Srgrimes sizeof(string_ident), "\"%s\"", script->s)) >= 2401590Srgrimes sizeof(string_ident) - 1) 2411590Srgrimes (void)strcpy(string_ident + 2421590Srgrimes sizeof(string_ident) - 6, " ...\""); 2431590Srgrimes fname = string_ident; 2441590Srgrimes s = script->s; 2451590Srgrimes state = ST_STRING; 2461590Srgrimes goto again; 2471590Srgrimes } 2481590Srgrimes case ST_FILE: 2491590Srgrimes if ((p = fgets(buf, n, f)) != NULL) { 2501590Srgrimes linenum++; 2511590Srgrimes if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') 2521590Srgrimes nflag = 1; 25341602Sarchie if (more != NULL) 25441602Sarchie *more = !feof(f); 2551590Srgrimes return (p); 2561590Srgrimes } 2571590Srgrimes script = script->next; 2581590Srgrimes (void)fclose(f); 2591590Srgrimes state = ST_EOF; 2601590Srgrimes goto again; 2611590Srgrimes case ST_STRING: 2621590Srgrimes if (linenum == 0 && s[0] == '#' && s[1] == 'n') 2631590Srgrimes nflag = 1; 2641590Srgrimes p = buf; 2651590Srgrimes for (;;) { 2661590Srgrimes if (n-- <= 1) { 2671590Srgrimes *p = '\0'; 2681590Srgrimes linenum++; 26941602Sarchie if (more != NULL) 27041602Sarchie *more = 1; 2711590Srgrimes return (buf); 2721590Srgrimes } 2731590Srgrimes switch (*s) { 2741590Srgrimes case '\0': 2751590Srgrimes state = ST_EOF; 2761590Srgrimes if (s == script->s) { 2771590Srgrimes script = script->next; 2781590Srgrimes goto again; 2791590Srgrimes } else { 2801590Srgrimes script = script->next; 2811590Srgrimes *p = '\0'; 2821590Srgrimes linenum++; 28341602Sarchie if (more != NULL) 28441602Sarchie *more = 0; 2851590Srgrimes return (buf); 2861590Srgrimes } 2871590Srgrimes case '\n': 2881590Srgrimes *p++ = '\n'; 2891590Srgrimes *p = '\0'; 2901590Srgrimes s++; 2911590Srgrimes linenum++; 29241602Sarchie if (more != NULL) 29341602Sarchie *more = 0; 2941590Srgrimes return (buf); 2951590Srgrimes default: 2961590Srgrimes *p++ = *s++; 2971590Srgrimes } 2981590Srgrimes } 2991590Srgrimes } 3001590Srgrimes /* NOTREACHED */ 30128066Scharnier return (NULL); 3021590Srgrimes} 3031590Srgrimes 3041590Srgrimes/* 3051590Srgrimes * Like fgets, but go through the list of files chaining them together. 3061590Srgrimes * Set len to the length of the line. 3071590Srgrimes */ 3081590Srgrimesint 309122044Sdesmf_fgets(SPACE *sp, enum e_spflag spflag) 3101590Srgrimes{ 311122049Sdes struct stat sb; 312252231Spfg ssize_t len; 313252231Spfg static char *p = NULL; 314252231Spfg static size_t plen = 0; 31519829Swosch int c; 31698200Ssobomax static int firstfile; 3171590Srgrimes 318122049Sdes if (infile == NULL) { 31998200Ssobomax /* stdin? */ 32098200Ssobomax if (files->fname == NULL) { 32198200Ssobomax if (inplace != NULL) 322168921Syar errx(1, "-I or -i may not be used with stdin"); 323122049Sdes infile = stdin; 32498200Ssobomax fname = "stdin"; 325122049Sdes outfile = stdout; 326122049Sdes outfname = "stdout"; 32798200Ssobomax } 32898200Ssobomax firstfile = 1; 32998200Ssobomax } 33098200Ssobomax 33198200Ssobomax for (;;) { 332122049Sdes if (infile != NULL && (c = getc(infile)) != EOF) { 333122049Sdes (void)ungetc(c, infile); 33498200Ssobomax break; 33598200Ssobomax } 33698200Ssobomax /* If we are here then either eof or no files are open yet */ 337122049Sdes if (infile == stdin) { 33898294Stjr sp->len = 0; 33998200Ssobomax return (0); 34098200Ssobomax } 341122049Sdes if (infile != NULL) { 342122049Sdes fclose(infile); 343130039Sbrian if (*oldfname != '\0') { 344217133Sjilles /* if there was a backup file, remove it */ 345217133Sjilles unlink(oldfname); 346217133Sjilles /* 347217133Sjilles * Backup the original. Note that hard links 348217133Sjilles * are not supported on all filesystems. 349217133Sjilles */ 350217133Sjilles if ((link(fname, oldfname) != 0) && 351217133Sjilles (rename(fname, oldfname) != 0)) { 352130039Sbrian warn("rename()"); 353217133Sjilles if (*tmpfname) 354217133Sjilles unlink(tmpfname); 355130039Sbrian exit(1); 356130039Sbrian } 357130039Sbrian *oldfname = '\0'; 358122049Sdes } 359130039Sbrian if (*tmpfname != '\0') { 360130039Sbrian if (outfile != NULL && outfile != stdout) 361217133Sjilles if (fclose(outfile) != 0) { 362217133Sjilles warn("fclose()"); 363217133Sjilles unlink(tmpfname); 364217133Sjilles exit(1); 365217133Sjilles } 366130039Sbrian outfile = NULL; 367217133Sjilles if (rename(tmpfname, fname) != 0) { 368217133Sjilles /* this should not happen really! */ 369217133Sjilles warn("rename()"); 370217133Sjilles unlink(tmpfname); 371217133Sjilles exit(1); 372217133Sjilles } 373130039Sbrian *tmpfname = '\0'; 374130039Sbrian } 375122049Sdes outfname = NULL; 37698200Ssobomax } 377122049Sdes if (firstfile == 0) 3781590Srgrimes files = files->next; 379122049Sdes else 38098200Ssobomax firstfile = 0; 38198200Ssobomax if (files == NULL) { 38298294Stjr sp->len = 0; 38398200Ssobomax return (0); 3841590Srgrimes } 385122049Sdes fname = files->fname; 38698200Ssobomax if (inplace != NULL) { 387122049Sdes if (lstat(fname, &sb) != 0) 388122049Sdes err(1, "%s", fname); 389122049Sdes if (!(sb.st_mode & S_IFREG)) 390122049Sdes errx(1, "%s: %s %s", fname, 391122049Sdes "in-place editing only", 392122049Sdes "works for regular files"); 393122049Sdes if (*inplace != '\0') { 394122049Sdes strlcpy(oldfname, fname, 395122049Sdes sizeof(oldfname)); 396122049Sdes len = strlcat(oldfname, inplace, 397122049Sdes sizeof(oldfname)); 398122049Sdes if (len > sizeof(oldfname)) 399122049Sdes errx(1, "%s: name too long", fname); 400122049Sdes } 401122049Sdes len = snprintf(tmpfname, sizeof(tmpfname), 402122084Sdes "%s/.!%ld!%s", dirname(fname), (long)getpid(), 403122084Sdes basename(fname)); 404122049Sdes if (len >= sizeof(tmpfname)) 405122049Sdes errx(1, "%s: name too long", fname); 406122049Sdes unlink(tmpfname); 407122049Sdes if ((outfile = fopen(tmpfname, "w")) == NULL) 408122049Sdes err(1, "%s", fname); 409122049Sdes fchown(fileno(outfile), sb.st_uid, sb.st_gid); 410122049Sdes fchmod(fileno(outfile), sb.st_mode & ALLPERMS); 411122049Sdes outfname = tmpfname; 412168921Syar if (!ispan) { 413168921Syar linenum = 0; 414170608Syar resetstate(); 415168921Syar } 416122049Sdes } else { 417122049Sdes outfile = stdout; 418122049Sdes outfname = "stdout"; 41998200Ssobomax } 420122049Sdes if ((infile = fopen(fname, "r")) == NULL) { 42198200Ssobomax warn("%s", fname); 42298201Stjr rval = 1; 42398200Ssobomax continue; 42498200Ssobomax } 4251590Srgrimes } 4261590Srgrimes /* 427122049Sdes * We are here only when infile is open and we still have something 42899352Stjr * to read from it. 42998200Ssobomax * 430252231Spfg * Use getline() so that we can handle essentially infinite input 431252231Spfg * data. The p and plen are static so each invocation gives 432252231Spfg * getline() the same buffer which is expanded as needed. 4331590Srgrimes */ 434252231Spfg len = getline(&p, &plen, infile); 435252231Spfg if (len == -1) 436252231Spfg err(1, "%s", fname); 43798601Stjr if (len != 0 && p[len - 1] == '\n') 43898601Stjr len--; 4391590Srgrimes cspace(sp, p, len, spflag); 4401590Srgrimes 4411590Srgrimes linenum++; 44298200Ssobomax 4431590Srgrimes return (1); 4441590Srgrimes} 4451590Srgrimes 4461590Srgrimes/* 4471590Srgrimes * Add a compilation unit to the linked list 4481590Srgrimes */ 4491590Srgrimesstatic void 450122044Sdesadd_compunit(enum e_cut type, char *s) 4511590Srgrimes{ 4521590Srgrimes struct s_compunit *cu; 4531590Srgrimes 45480286Sobrien if ((cu = malloc(sizeof(struct s_compunit))) == NULL) 45580286Sobrien err(1, "malloc"); 4561590Srgrimes cu->type = type; 4571590Srgrimes cu->s = s; 4581590Srgrimes cu->next = NULL; 4591590Srgrimes *cu_nextp = cu; 4601590Srgrimes cu_nextp = &cu->next; 4611590Srgrimes} 4621590Srgrimes 4631590Srgrimes/* 4641590Srgrimes * Add a file to the linked list 4651590Srgrimes */ 4661590Srgrimesstatic void 467122044Sdesadd_file(char *s) 4681590Srgrimes{ 4691590Srgrimes struct s_flist *fp; 4701590Srgrimes 47180286Sobrien if ((fp = malloc(sizeof(struct s_flist))) == NULL) 47280286Sobrien err(1, "malloc"); 4731590Srgrimes fp->next = NULL; 4741590Srgrimes *fl_nextp = fp; 4751590Srgrimes fp->fname = s; 4761590Srgrimes fl_nextp = &fp->next; 4771590Srgrimes} 47896175Sjmallett 47999352Stjrint 48099352Stjrlastline(void) 48199352Stjr{ 48299352Stjr int ch; 48399352Stjr 484168921Syar if (files->next != NULL && (inplace == NULL || ispan)) 48599352Stjr return (0); 486122049Sdes if ((ch = getc(infile)) == EOF) 48799352Stjr return (1); 488122049Sdes ungetc(ch, infile); 48999352Stjr return (0); 49099352Stjr} 491