xref: /illumos-gate/usr/src/cmd/sed/main.c (revision c6fc7f7b)
184441f85SGarrett D'Amore /*
2*c6fc7f7bSAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
39384cec6SJohann 'Myrkraverk' Oskarsson  * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.com>
4d15978eaSGary Mills  * Copyright (c) 2011 Gary Mills
5e50226ecSYuri Pankov  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
684441f85SGarrett D'Amore  * Copyright (c) 1992 Diomidis Spinellis.
784441f85SGarrett D'Amore  * Copyright (c) 1992, 1993
884441f85SGarrett D'Amore  *	The Regents of the University of California.  All rights reserved.
984441f85SGarrett D'Amore  *
1084441f85SGarrett D'Amore  * This code is derived from software contributed to Berkeley by
1184441f85SGarrett D'Amore  * Diomidis Spinellis of Imperial College, University of London.
1284441f85SGarrett D'Amore  *
1384441f85SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
1484441f85SGarrett D'Amore  * modification, are permitted provided that the following conditions
1584441f85SGarrett D'Amore  * are met:
1684441f85SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright
1784441f85SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer.
1884441f85SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright
1984441f85SGarrett D'Amore  *    notice, this list of conditions and the following disclaimer in the
2084441f85SGarrett D'Amore  *    documentation and/or other materials provided with the distribution.
2184441f85SGarrett D'Amore  * 4. Neither the name of the University nor the names of its contributors
2284441f85SGarrett D'Amore  *    may be used to endorse or promote products derived from this software
2384441f85SGarrett D'Amore  *    without specific prior written permission.
2484441f85SGarrett D'Amore  *
2584441f85SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2684441f85SGarrett D'Amore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2784441f85SGarrett D'Amore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2884441f85SGarrett D'Amore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2984441f85SGarrett D'Amore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3084441f85SGarrett D'Amore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3184441f85SGarrett D'Amore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3284441f85SGarrett D'Amore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3384441f85SGarrett D'Amore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3484441f85SGarrett D'Amore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3584441f85SGarrett D'Amore  * SUCH DAMAGE.
3684441f85SGarrett D'Amore  */
3784441f85SGarrett D'Amore 
3884441f85SGarrett D'Amore #include <sys/types.h>
3984441f85SGarrett D'Amore #include <sys/mman.h>
4084441f85SGarrett D'Amore #include <sys/param.h>
4184441f85SGarrett D'Amore #include <sys/stat.h>
4284441f85SGarrett D'Amore 
4384441f85SGarrett D'Amore #include <err.h>
4484441f85SGarrett D'Amore #include <errno.h>
4584441f85SGarrett D'Amore #include <fcntl.h>
46e50226ecSYuri Pankov #include <getopt.h>
4784441f85SGarrett D'Amore #include <libgen.h>
48e50226ecSYuri Pankov #include <libintl.h>
4984441f85SGarrett D'Amore #include <limits.h>
5084441f85SGarrett D'Amore #include <locale.h>
5184441f85SGarrett D'Amore #include <regex.h>
5284441f85SGarrett D'Amore #include <stddef.h>
5384441f85SGarrett D'Amore #include <stdio.h>
5484441f85SGarrett D'Amore #include <stdlib.h>
5584441f85SGarrett D'Amore #include <string.h>
5684441f85SGarrett D'Amore #include <unistd.h>
5784441f85SGarrett D'Amore 
5884441f85SGarrett D'Amore #include "defs.h"
5984441f85SGarrett D'Amore #include "extern.h"
6084441f85SGarrett D'Amore 
6184441f85SGarrett D'Amore /*
6284441f85SGarrett D'Amore  * Linked list of units (strings and files) to be compiled
6384441f85SGarrett D'Amore  */
6484441f85SGarrett D'Amore struct s_compunit {
6584441f85SGarrett D'Amore 	struct s_compunit *next;
6684441f85SGarrett D'Amore 	enum e_cut {CU_FILE, CU_STRING} type;
6784441f85SGarrett D'Amore 	char *s;			/* Pointer to string or fname */
6884441f85SGarrett D'Amore };
6984441f85SGarrett D'Amore 
7084441f85SGarrett D'Amore /*
7184441f85SGarrett D'Amore  * Linked list pointer to compilation units and pointer to current
7284441f85SGarrett D'Amore  * next pointer.
7384441f85SGarrett D'Amore  */
7484441f85SGarrett D'Amore static struct s_compunit *script, **cu_nextp = &script;
7584441f85SGarrett D'Amore 
7684441f85SGarrett D'Amore /*
7784441f85SGarrett D'Amore  * Linked list of files to be processed
7884441f85SGarrett D'Amore  */
7984441f85SGarrett D'Amore struct s_flist {
8084441f85SGarrett D'Amore 	char *fname;
8184441f85SGarrett D'Amore 	struct s_flist *next;
8284441f85SGarrett D'Amore };
8384441f85SGarrett D'Amore 
8484441f85SGarrett D'Amore /*
8584441f85SGarrett D'Amore  * Linked list pointer to files and pointer to current
8684441f85SGarrett D'Amore  * next pointer.
8784441f85SGarrett D'Amore  */
8884441f85SGarrett D'Amore static struct s_flist *files, **fl_nextp = &files;
8984441f85SGarrett D'Amore 
9084441f85SGarrett D'Amore FILE *infile;			/* Current input file */
9184441f85SGarrett D'Amore FILE *outfile;			/* Current output file */
9284441f85SGarrett D'Amore 
9384441f85SGarrett D'Amore int aflag, eflag, nflag;
9484441f85SGarrett D'Amore int rflags = 0;
9584441f85SGarrett D'Amore static int rval;		/* Exit status */
9684441f85SGarrett D'Amore 
9784441f85SGarrett D'Amore static int ispan;		/* Whether inplace editing spans across files */
9884441f85SGarrett D'Amore 
9984441f85SGarrett D'Amore /*
10084441f85SGarrett D'Amore  * Current file and line number; line numbers restart across compilation
10184441f85SGarrett D'Amore  * units, but span across input files.  The latter is optional if editing
10284441f85SGarrett D'Amore  * in place.
10384441f85SGarrett D'Amore  */
10484441f85SGarrett D'Amore const char *fname;		/* File name. */
10584441f85SGarrett D'Amore const char *outfname;		/* Output file name */
10684441f85SGarrett D'Amore static char oldfname[PATH_MAX];	/* Old file name (for in-place editing) */
10784441f85SGarrett D'Amore static char tmpfname[PATH_MAX];	/* Temporary file name (for in-place editing) */
10884441f85SGarrett D'Amore static const char *inplace;	/* Inplace edit file extension. */
10984441f85SGarrett D'Amore ulong_t linenum;
11084441f85SGarrett D'Amore 
111e50226ecSYuri Pankov static const struct option lopts[] = {
112e50226ecSYuri Pankov 	{"in-place",	optional_argument,	NULL,	'i'},
113e50226ecSYuri Pankov 	{NULL,		0,			NULL,	0}
114e50226ecSYuri Pankov };
115e50226ecSYuri Pankov 
11684441f85SGarrett D'Amore static void add_compunit(enum e_cut, char *);
11784441f85SGarrett D'Amore static void add_file(char *);
11884441f85SGarrett D'Amore static void usage(void);
11984441f85SGarrett D'Amore 
12084441f85SGarrett D'Amore 
12184441f85SGarrett D'Amore int
main(int argc,char * argv[])12284441f85SGarrett D'Amore main(int argc, char *argv[])
12384441f85SGarrett D'Amore {
12484441f85SGarrett D'Amore 	int c, fflag;
12584441f85SGarrett D'Amore 	char *temp_arg;
12684441f85SGarrett D'Amore 
12784441f85SGarrett D'Amore 	(void) setlocale(LC_ALL, "");
12884441f85SGarrett D'Amore 
12984441f85SGarrett D'Amore #ifndef TEXT_DOMAIN
13084441f85SGarrett D'Amore #define	TEXT_DOMAIN	"SYS_TEST"
13184441f85SGarrett D'Amore #endif
13284441f85SGarrett D'Amore 	(void) textdomain(TEXT_DOMAIN);
13384441f85SGarrett D'Amore 
13484441f85SGarrett D'Amore 	fflag = 0;
13584441f85SGarrett D'Amore 	inplace = NULL;
13684441f85SGarrett D'Amore 
137e50226ecSYuri Pankov 	while ((c = getopt_long(argc, argv, "EI::ae:f:i::lnr", lopts, NULL)) !=
138e50226ecSYuri Pankov 	    -1)
13984441f85SGarrett D'Amore 		switch (c) {
14084441f85SGarrett D'Amore 		case 'r':		/* Gnu sed compat */
14184441f85SGarrett D'Amore 		case 'E':
14284441f85SGarrett D'Amore 			rflags = REG_EXTENDED;
14384441f85SGarrett D'Amore 			break;
14484441f85SGarrett D'Amore 		case 'I':
145e50226ecSYuri Pankov 			if (optarg != NULL)
146e50226ecSYuri Pankov 				inplace = optarg;
147e50226ecSYuri Pankov 			else
148e50226ecSYuri Pankov 				inplace = "";
14984441f85SGarrett D'Amore 			ispan = 1;	/* span across input files */
15084441f85SGarrett D'Amore 			break;
15184441f85SGarrett D'Amore 		case 'a':
15284441f85SGarrett D'Amore 			aflag = 1;
15384441f85SGarrett D'Amore 			break;
15484441f85SGarrett D'Amore 		case 'e':
15584441f85SGarrett D'Amore 			eflag = 1;
156d15978eaSGary Mills 			if (asprintf(&temp_arg, "%s\n", optarg) < 1)
15784441f85SGarrett D'Amore 				err(1, "asprintf");
15884441f85SGarrett D'Amore 			add_compunit(CU_STRING, temp_arg);
15984441f85SGarrett D'Amore 			break;
16084441f85SGarrett D'Amore 		case 'f':
16184441f85SGarrett D'Amore 			fflag = 1;
16284441f85SGarrett D'Amore 			add_compunit(CU_FILE, optarg);
16384441f85SGarrett D'Amore 			break;
16484441f85SGarrett D'Amore 		case 'i':
165e50226ecSYuri Pankov 			if (optarg != NULL)
166e50226ecSYuri Pankov 				inplace = optarg;
167e50226ecSYuri Pankov 			else
168e50226ecSYuri Pankov 				inplace = "";
16984441f85SGarrett D'Amore 			ispan = 0;	/* don't span across input files */
17084441f85SGarrett D'Amore 			break;
17184441f85SGarrett D'Amore 		case 'l':
17284441f85SGarrett D'Amore 			/* On SunOS, setlinebuf "returns no useful value */
17384441f85SGarrett D'Amore 			(void) setlinebuf(stdout);
17484441f85SGarrett D'Amore 			break;
17584441f85SGarrett D'Amore 		case 'n':
17684441f85SGarrett D'Amore 			nflag = 1;
17784441f85SGarrett D'Amore 			break;
17884441f85SGarrett D'Amore 		default:
17984441f85SGarrett D'Amore 		case '?':
18084441f85SGarrett D'Amore 			usage();
18184441f85SGarrett D'Amore 		}
18284441f85SGarrett D'Amore 	argc -= optind;
18384441f85SGarrett D'Amore 	argv += optind;
18484441f85SGarrett D'Amore 
18584441f85SGarrett D'Amore 	/* First usage case; script is the first arg */
18684441f85SGarrett D'Amore 	if (!eflag && !fflag && *argv) {
18784441f85SGarrett D'Amore 		add_compunit(CU_STRING, *argv);
18884441f85SGarrett D'Amore 		argv++;
18984441f85SGarrett D'Amore 	}
19084441f85SGarrett D'Amore 
19184441f85SGarrett D'Amore 	compile();
19284441f85SGarrett D'Amore 
19384441f85SGarrett D'Amore 	/* Continue with first and start second usage */
19484441f85SGarrett D'Amore 	if (*argv)
19584441f85SGarrett D'Amore 		for (; *argv; argv++)
19684441f85SGarrett D'Amore 			add_file(*argv);
19784441f85SGarrett D'Amore 	else
19884441f85SGarrett D'Amore 		add_file(NULL);
19984441f85SGarrett D'Amore 	process();
20084441f85SGarrett D'Amore 	cfclose(prog, NULL);
20184441f85SGarrett D'Amore 	if (fclose(stdout))
20284441f85SGarrett D'Amore 		err(1, "stdout");
20384441f85SGarrett D'Amore 	return (rval);
20484441f85SGarrett D'Amore }
20584441f85SGarrett D'Amore 
20684441f85SGarrett D'Amore static void
usage(void)20784441f85SGarrett D'Amore usage(void)
20884441f85SGarrett D'Amore {
209e50226ecSYuri Pankov 	(void) fputs(_("usage: sed script [-Ealn] [-i[extension]] [file...]\n"
210e50226ecSYuri Pankov 	    "       sed [-Ealn] [-i[extension]] [-e script]... "
211da17b20bSDamian Wojslaw 	    "[-f script_file]... [file...]\n"),
21284441f85SGarrett D'Amore 	    stderr);
21384441f85SGarrett D'Amore 	exit(1);
21484441f85SGarrett D'Amore }
21584441f85SGarrett D'Amore 
21684441f85SGarrett D'Amore /*
21784441f85SGarrett D'Amore  * Like fgets, but go through the chain of compilation units chaining them
21884441f85SGarrett D'Amore  * together.  Empty strings and files are ignored.
21984441f85SGarrett D'Amore  */
22084441f85SGarrett D'Amore char *
cu_fgets(char * buf,int n,int * more)22184441f85SGarrett D'Amore cu_fgets(char *buf, int n, int *more)
22284441f85SGarrett D'Amore {
22384441f85SGarrett D'Amore 	static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
22484441f85SGarrett D'Amore 	static FILE *f;		/* Current open file */
22584441f85SGarrett D'Amore 	static char *s;		/* Current pointer inside string */
22684441f85SGarrett D'Amore 	static char string_ident[30];
22784441f85SGarrett D'Amore 	char *p;
22884441f85SGarrett D'Amore 
22984441f85SGarrett D'Amore again:
23084441f85SGarrett D'Amore 	switch (state) {
23184441f85SGarrett D'Amore 	case ST_EOF:
23284441f85SGarrett D'Amore 		if (script == NULL) {
23384441f85SGarrett D'Amore 			if (more != NULL)
23484441f85SGarrett D'Amore 				*more = 0;
23584441f85SGarrett D'Amore 			return (NULL);
23684441f85SGarrett D'Amore 		}
23784441f85SGarrett D'Amore 		linenum = 0;
23884441f85SGarrett D'Amore 		switch (script->type) {
23984441f85SGarrett D'Amore 		case CU_FILE:
24084441f85SGarrett D'Amore 			if ((f = fopen(script->s, "r")) == NULL)
24184441f85SGarrett D'Amore 				err(1, "%s", script->s);
24284441f85SGarrett D'Amore 			fname = script->s;
24384441f85SGarrett D'Amore 			state = ST_FILE;
24484441f85SGarrett D'Amore 			goto again;
24584441f85SGarrett D'Amore 		case CU_STRING:
24684441f85SGarrett D'Amore 			if (((size_t)snprintf(string_ident,
24784441f85SGarrett D'Amore 			    sizeof (string_ident), "\"%s\"", script->s)) >=
24884441f85SGarrett D'Amore 			    sizeof (string_ident) - 1)
24984441f85SGarrett D'Amore 				(void) strcpy(string_ident +
25084441f85SGarrett D'Amore 				    sizeof (string_ident) - 6, " ...\"");
25184441f85SGarrett D'Amore 			fname = string_ident;
25284441f85SGarrett D'Amore 			s = script->s;
25384441f85SGarrett D'Amore 			state = ST_STRING;
25484441f85SGarrett D'Amore 			goto again;
255941b7e9cSToomas Soome 		default:
256941b7e9cSToomas Soome 			errx(1, "BUG: Unknown script type: %d\n", script->type);
25784441f85SGarrett D'Amore 		}
25884441f85SGarrett D'Amore 		/*NOTREACHED*/
25984441f85SGarrett D'Amore 
26084441f85SGarrett D'Amore 	case ST_FILE:
26184441f85SGarrett D'Amore 		if ((p = fgets(buf, n, f)) != NULL) {
26284441f85SGarrett D'Amore 			linenum++;
26384441f85SGarrett D'Amore 			if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
26484441f85SGarrett D'Amore 				nflag = 1;
26584441f85SGarrett D'Amore 			if (more != NULL)
26684441f85SGarrett D'Amore 				*more = !feof(f);
26784441f85SGarrett D'Amore 			return (p);
26884441f85SGarrett D'Amore 		}
26984441f85SGarrett D'Amore 		script = script->next;
27084441f85SGarrett D'Amore 		(void) fclose(f);
27184441f85SGarrett D'Amore 		state = ST_EOF;
27284441f85SGarrett D'Amore 		goto again;
27384441f85SGarrett D'Amore 	case ST_STRING:
27484441f85SGarrett D'Amore 		if (linenum == 0 && s[0] == '#' && s[1] == 'n')
27584441f85SGarrett D'Amore 			nflag = 1;
27684441f85SGarrett D'Amore 		p = buf;
27784441f85SGarrett D'Amore 		for (;;) {
27884441f85SGarrett D'Amore 			if (n-- <= 1) {
27984441f85SGarrett D'Amore 				*p = '\0';
28084441f85SGarrett D'Amore 				linenum++;
28184441f85SGarrett D'Amore 				if (more != NULL)
28284441f85SGarrett D'Amore 					*more = 1;
28384441f85SGarrett D'Amore 				return (buf);
28484441f85SGarrett D'Amore 			}
28584441f85SGarrett D'Amore 			switch (*s) {
28684441f85SGarrett D'Amore 			case '\0':
28784441f85SGarrett D'Amore 				state = ST_EOF;
28884441f85SGarrett D'Amore 				if (s == script->s) {
28984441f85SGarrett D'Amore 					script = script->next;
29084441f85SGarrett D'Amore 					goto again;
29184441f85SGarrett D'Amore 				} else {
29284441f85SGarrett D'Amore 					script = script->next;
29384441f85SGarrett D'Amore 					*p = '\0';
29484441f85SGarrett D'Amore 					linenum++;
29584441f85SGarrett D'Amore 					if (more != NULL)
29684441f85SGarrett D'Amore 						*more = 0;
29784441f85SGarrett D'Amore 					return (buf);
29884441f85SGarrett D'Amore 				}
29984441f85SGarrett D'Amore 			case '\n':
30084441f85SGarrett D'Amore 				*p++ = '\n';
30184441f85SGarrett D'Amore 				*p = '\0';
30284441f85SGarrett D'Amore 				s++;
30384441f85SGarrett D'Amore 				linenum++;
30484441f85SGarrett D'Amore 				if (more != NULL)
30584441f85SGarrett D'Amore 					*more = 0;
30684441f85SGarrett D'Amore 				return (buf);
30784441f85SGarrett D'Amore 			default:
30884441f85SGarrett D'Amore 				*p++ = *s++;
30984441f85SGarrett D'Amore 			}
31084441f85SGarrett D'Amore 		}
31184441f85SGarrett D'Amore 	}
31284441f85SGarrett D'Amore 	/* NOTREACHED */
31384441f85SGarrett D'Amore 	return (NULL);
31484441f85SGarrett D'Amore }
31584441f85SGarrett D'Amore 
31684441f85SGarrett D'Amore /*
31784441f85SGarrett D'Amore  * Like fgets, but go through the list of files chaining them together.
31884441f85SGarrett D'Amore  * Set len to the length of the line.
31984441f85SGarrett D'Amore  */
32084441f85SGarrett D'Amore int
mf_fgets(SPACE * sp,enum e_spflag spflag)32184441f85SGarrett D'Amore mf_fgets(SPACE *sp, enum e_spflag spflag)
32284441f85SGarrett D'Amore {
323c530934aSGarrett D'Amore 	struct stat sb, nsb;
3249384cec6SJohann 'Myrkraverk' Oskarsson 	ssize_t len;
3259384cec6SJohann 'Myrkraverk' Oskarsson 	static char *p = NULL;
3269384cec6SJohann 'Myrkraverk' Oskarsson 	static size_t plen = 0;
32784441f85SGarrett D'Amore 	int c;
32884441f85SGarrett D'Amore 	static int firstfile;
32984441f85SGarrett D'Amore 
33084441f85SGarrett D'Amore 	if (infile == NULL) {
33184441f85SGarrett D'Amore 		/* stdin? */
33284441f85SGarrett D'Amore 		if (files->fname == NULL) {
33384441f85SGarrett D'Amore 			if (inplace != NULL)
33484441f85SGarrett D'Amore 				errx(1,
33584441f85SGarrett D'Amore 				    _("-I or -i may not be used with stdin"));
33684441f85SGarrett D'Amore 			infile = stdin;
33784441f85SGarrett D'Amore 			fname = "stdin";
33884441f85SGarrett D'Amore 			outfile = stdout;
33984441f85SGarrett D'Amore 			outfname = "stdout";
34084441f85SGarrett D'Amore 		}
34184441f85SGarrett D'Amore 		firstfile = 1;
34284441f85SGarrett D'Amore 	}
34384441f85SGarrett D'Amore 
34484441f85SGarrett D'Amore 	for (;;) {
34584441f85SGarrett D'Amore 		if (infile != NULL && (c = getc(infile)) != EOF) {
34684441f85SGarrett D'Amore 			(void) ungetc(c, infile);
34784441f85SGarrett D'Amore 			break;
34884441f85SGarrett D'Amore 		}
34984441f85SGarrett D'Amore 		/* If we are here then either eof or no files are open yet */
35084441f85SGarrett D'Amore 		if (infile == stdin) {
35184441f85SGarrett D'Amore 			sp->len = 0;
35284441f85SGarrett D'Amore 			return (0);
35384441f85SGarrett D'Amore 		}
35484441f85SGarrett D'Amore 		if (infile != NULL) {
35584441f85SGarrett D'Amore 			(void) fclose(infile);
35684441f85SGarrett D'Amore 			if (*oldfname != '\0') {
357429d0af2SGarrett D'Amore 				/* if there was a backup file, remove it */
358429d0af2SGarrett D'Amore 				(void) unlink(oldfname);
359c530934aSGarrett D'Amore 				/*
360c530934aSGarrett D'Amore 				 * Backup the original.  Note that hard links
361c530934aSGarrett D'Amore 				 * are not supported on all filesystems.
362c530934aSGarrett D'Amore 				 */
363c530934aSGarrett D'Amore 				if ((link(fname, oldfname) != 0) &&
364c530934aSGarrett D'Amore 				    (rename(fname, oldfname) != 0)) {
365c530934aSGarrett D'Amore 					warn("rename()");
366c530934aSGarrett D'Amore 					if (*tmpfname)
367c530934aSGarrett D'Amore 						(void) unlink(tmpfname);
36884441f85SGarrett D'Amore 					exit(1);
36984441f85SGarrett D'Amore 				}
37084441f85SGarrett D'Amore 				*oldfname = '\0';
37184441f85SGarrett D'Amore 			}
37284441f85SGarrett D'Amore 			if (*tmpfname != '\0') {
37384441f85SGarrett D'Amore 				if (outfile != NULL && outfile != stdout)
37484441f85SGarrett D'Amore 					if (fclose(outfile) != 0) {
37584441f85SGarrett D'Amore 						warn("fclose()");
37684441f85SGarrett D'Amore 						(void) unlink(tmpfname);
37784441f85SGarrett D'Amore 						exit(1);
37884441f85SGarrett D'Amore 					}
37984441f85SGarrett D'Amore 				outfile = NULL;
38084441f85SGarrett D'Amore 				if (rename(tmpfname, fname) != 0) {
38184441f85SGarrett D'Amore 					/* this should not happen really! */
38284441f85SGarrett D'Amore 					warn("rename()");
38384441f85SGarrett D'Amore 					(void) unlink(tmpfname);
38484441f85SGarrett D'Amore 					exit(1);
38584441f85SGarrett D'Amore 				}
38684441f85SGarrett D'Amore 				*tmpfname = '\0';
38784441f85SGarrett D'Amore 			}
38884441f85SGarrett D'Amore 			outfname = NULL;
38984441f85SGarrett D'Amore 		}
39084441f85SGarrett D'Amore 		if (firstfile == 0)
39184441f85SGarrett D'Amore 			files = files->next;
39284441f85SGarrett D'Amore 		else
39384441f85SGarrett D'Amore 			firstfile = 0;
39484441f85SGarrett D'Amore 		if (files == NULL) {
39584441f85SGarrett D'Amore 			sp->len = 0;
39684441f85SGarrett D'Amore 			return (0);
39784441f85SGarrett D'Amore 		}
39884441f85SGarrett D'Amore 		fname = files->fname;
39984441f85SGarrett D'Amore 		if (inplace != NULL) {
40084441f85SGarrett D'Amore 			char bn[PATH_MAX];
40184441f85SGarrett D'Amore 			char dn[PATH_MAX];
40284441f85SGarrett D'Amore 			(void) strlcpy(bn, fname, sizeof (bn));
40384441f85SGarrett D'Amore 			(void) strlcpy(dn, fname, sizeof (dn));
40484441f85SGarrett D'Amore 			if (lstat(fname, &sb) != 0)
40584441f85SGarrett D'Amore 				err(1, "%s", fname);
40684441f85SGarrett D'Amore 			if (!(sb.st_mode & S_IFREG))
40784441f85SGarrett D'Amore 				fatal(_("in-place editing only "
40884441f85SGarrett D'Amore 				    "works for regular files"));
40984441f85SGarrett D'Amore 			if (*inplace != '\0') {
41084441f85SGarrett D'Amore 				(void) strlcpy(oldfname, fname,
41184441f85SGarrett D'Amore 				    sizeof (oldfname));
41284441f85SGarrett D'Amore 				len = strlcat(oldfname, inplace,
41384441f85SGarrett D'Amore 				    sizeof (oldfname));
41484441f85SGarrett D'Amore 				if (len > sizeof (oldfname))
41584441f85SGarrett D'Amore 					fatal(_("name too long"));
41684441f85SGarrett D'Amore 			}
41784441f85SGarrett D'Amore 			len = snprintf(tmpfname, sizeof (tmpfname),
41884441f85SGarrett D'Amore 			    "%s/.!%ld!%s", dirname(dn), (long)getpid(),
41984441f85SGarrett D'Amore 			    basename(bn));
42084441f85SGarrett D'Amore 			if (len >= sizeof (tmpfname))
42184441f85SGarrett D'Amore 				fatal(_("name too long"));
42284441f85SGarrett D'Amore 			(void) unlink(tmpfname);
42384441f85SGarrett D'Amore 			if ((outfile = fopen(tmpfname, "w")) == NULL)
42484441f85SGarrett D'Amore 				err(1, "%s", fname);
425c530934aSGarrett D'Amore 			/*
426c530934aSGarrett D'Amore 			 * Some file systems don't support chown or
427c530934aSGarrett D'Amore 			 * chmod fully.  On those, the owner/group and
428c530934aSGarrett D'Amore 			 * permissions will already be set to what
429c530934aSGarrett D'Amore 			 * they need to be.
430c530934aSGarrett D'Amore 			 */
431c530934aSGarrett D'Amore 			if (fstat(fileno(outfile), &nsb) != 0) {
432c530934aSGarrett D'Amore 				warn("fstat()");
433c530934aSGarrett D'Amore 			}
434c530934aSGarrett D'Amore 			if (((sb.st_uid != nsb.st_uid) ||
435c530934aSGarrett D'Amore 			    (sb.st_gid != nsb.st_gid)) &&
436c530934aSGarrett D'Amore 			    (fchown(fileno(outfile), sb.st_uid, sb.st_gid)
437c530934aSGarrett D'Amore 			    != 0))
43884441f85SGarrett D'Amore 				warn("fchown()");
439c530934aSGarrett D'Amore 			if ((sb.st_mode != nsb.st_mode) &&
440c530934aSGarrett D'Amore 			    (fchmod(fileno(outfile), sb.st_mode & 07777) != 0))
44184441f85SGarrett D'Amore 				warn("fchmod()");
44284441f85SGarrett D'Amore 			outfname = tmpfname;
44384441f85SGarrett D'Amore 			if (!ispan) {
44484441f85SGarrett D'Amore 				linenum = 0;
44584441f85SGarrett D'Amore 				resetstate();
44684441f85SGarrett D'Amore 			}
44784441f85SGarrett D'Amore 		} else {
44884441f85SGarrett D'Amore 			outfile = stdout;
44984441f85SGarrett D'Amore 			outfname = "stdout";
45084441f85SGarrett D'Amore 		}
45184441f85SGarrett D'Amore 		if ((infile = fopen(fname, "r")) == NULL) {
45284441f85SGarrett D'Amore 			warn("%s", fname);
45384441f85SGarrett D'Amore 			rval = 1;
45484441f85SGarrett D'Amore 			continue;
45584441f85SGarrett D'Amore 		}
45684441f85SGarrett D'Amore 	}
45784441f85SGarrett D'Amore 	/*
45884441f85SGarrett D'Amore 	 * We are here only when infile is open and we still have something
45984441f85SGarrett D'Amore 	 * to read from it.
46084441f85SGarrett D'Amore 	 *
4619384cec6SJohann 'Myrkraverk' Oskarsson 	 * Use getline() so that we can handle essentially infinite
4629384cec6SJohann 'Myrkraverk' Oskarsson 	 * input data.  The p and plen are static so each invocation gives
4639384cec6SJohann 'Myrkraverk' Oskarsson 	 * getline() the same buffer which is expanded as needed.
46484441f85SGarrett D'Amore 	 */
4659384cec6SJohann 'Myrkraverk' Oskarsson 	len = getline(&p, &plen, infile);
4669384cec6SJohann 'Myrkraverk' Oskarsson 	if (len == -1)
4679384cec6SJohann 'Myrkraverk' Oskarsson 		err(1, "%s", fname);
46884441f85SGarrett D'Amore 	if (len != 0 && p[len - 1] == '\n')
46984441f85SGarrett D'Amore 		len--;
47084441f85SGarrett D'Amore 	cspace(sp, p, len, spflag);
47184441f85SGarrett D'Amore 
47284441f85SGarrett D'Amore 	linenum++;
47384441f85SGarrett D'Amore 
47484441f85SGarrett D'Amore 	return (1);
47584441f85SGarrett D'Amore }
47684441f85SGarrett D'Amore 
47784441f85SGarrett D'Amore /*
47884441f85SGarrett D'Amore  * Add a compilation unit to the linked list
47984441f85SGarrett D'Amore  */
48084441f85SGarrett D'Amore static void
add_compunit(enum e_cut type,char * s)48184441f85SGarrett D'Amore add_compunit(enum e_cut type, char *s)
48284441f85SGarrett D'Amore {
48384441f85SGarrett D'Amore 	struct s_compunit *cu;
48484441f85SGarrett D'Amore 
48584441f85SGarrett D'Amore 	if ((cu = malloc(sizeof (struct s_compunit))) == NULL)
48684441f85SGarrett D'Amore 		err(1, "malloc");
48784441f85SGarrett D'Amore 	cu->type = type;
48884441f85SGarrett D'Amore 	cu->s = s;
48984441f85SGarrett D'Amore 	cu->next = NULL;
49084441f85SGarrett D'Amore 	*cu_nextp = cu;
49184441f85SGarrett D'Amore 	cu_nextp = &cu->next;
49284441f85SGarrett D'Amore }
49384441f85SGarrett D'Amore 
49484441f85SGarrett D'Amore /*
49584441f85SGarrett D'Amore  * Add a file to the linked list
49684441f85SGarrett D'Amore  */
49784441f85SGarrett D'Amore static void
add_file(char * s)49884441f85SGarrett D'Amore add_file(char *s)
49984441f85SGarrett D'Amore {
50084441f85SGarrett D'Amore 	struct s_flist *fp;
50184441f85SGarrett D'Amore 
50284441f85SGarrett D'Amore 	if ((fp = malloc(sizeof (struct s_flist))) == NULL)
50384441f85SGarrett D'Amore 		err(1, "malloc");
50484441f85SGarrett D'Amore 	fp->next = NULL;
50584441f85SGarrett D'Amore 	*fl_nextp = fp;
50684441f85SGarrett D'Amore 	fp->fname = s;
50784441f85SGarrett D'Amore 	fl_nextp = &fp->next;
50884441f85SGarrett D'Amore }
50984441f85SGarrett D'Amore 
51084441f85SGarrett D'Amore int
lastline(void)51184441f85SGarrett D'Amore lastline(void)
51284441f85SGarrett D'Amore {
51384441f85SGarrett D'Amore 	int ch;
51484441f85SGarrett D'Amore 
515*c6fc7f7bSAndy Fiddaman 	if (feof(infile) != 0 || (ch = getc(infile)) == EOF) {
516*c6fc7f7bSAndy Fiddaman 		struct s_flist *f;
517*c6fc7f7bSAndy Fiddaman 
518*c6fc7f7bSAndy Fiddaman 		/*
519*c6fc7f7bSAndy Fiddaman 		 * Reached the end of the current input file.
520*c6fc7f7bSAndy Fiddaman 		 * If there are no more that contain data, then this is the
521*c6fc7f7bSAndy Fiddaman 		 * last line.
522*c6fc7f7bSAndy Fiddaman 		 */
523*c6fc7f7bSAndy Fiddaman 		if (inplace != NULL && ispan == 0)
524*c6fc7f7bSAndy Fiddaman 			return (1);
525*c6fc7f7bSAndy Fiddaman 
526*c6fc7f7bSAndy Fiddaman 		for (f = files->next; f != NULL; f = f->next) {
527*c6fc7f7bSAndy Fiddaman 			struct stat st;
528*c6fc7f7bSAndy Fiddaman 
529*c6fc7f7bSAndy Fiddaman 			if (stat(f->fname, &st) == -1) {
530*c6fc7f7bSAndy Fiddaman 				/* Treat an error here as an empty file */
531*c6fc7f7bSAndy Fiddaman 				continue;
532*c6fc7f7bSAndy Fiddaman 			}
533*c6fc7f7bSAndy Fiddaman 			if (st.st_size > 0)
534*c6fc7f7bSAndy Fiddaman 				return (0);
535*c6fc7f7bSAndy Fiddaman 		}
53684441f85SGarrett D'Amore 		return (1);
537*c6fc7f7bSAndy Fiddaman 	}
538*c6fc7f7bSAndy Fiddaman 
53984441f85SGarrett D'Amore 	(void) ungetc(ch, infile);
54084441f85SGarrett D'Amore 	return (0);
54184441f85SGarrett D'Amore }
542