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