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