main.c revision 100970
185909Simp/*-
285909Simp * Copyright (c) 1992 Diomidis Spinellis.
3122116Sbde * Copyright (c) 1992, 1993
4122116Sbde *	The Regents of the University of California.  All rights reserved.
5122116Sbde *
6180012Sru * This code is derived from software contributed to Berkeley by
7240468Sbrooks * Diomidis Spinellis of Imperial College, University of London.
8160440Sobrien *
9210151Simp * Redistribution and use in source and binary forms, with or without
10239272Sgonzo * modification, are permitted provided that the following conditions
11210151Simp * are met:
1285909Simp * 1. Redistributions of source code must retain the above copyright
1385909Simp *    notice, this list of conditions and the following disclaimer.
1485909Simp * 2. Redistributions in binary form must reproduce the above copyright
1585909Simp *    notice, this list of conditions and the following disclaimer in the
16175888Simp *    documentation and/or other materials provided with the distribution.
17175888Simp * 3. All advertising materials mentioning features or use of this software
1885909Simp *    must display the following acknowledgement:
19218538Simp *	This product includes software developed by the University of
2085909Simp *	California, Berkeley and its contributors.
2191512Sobrien * 4. Neither the name of the University nor the names of its contributors
22240451Snp *    may be used to endorse or promote products derived from this software
23116341Smarkm *    without specific prior written permission.
2485909Simp *
2585909Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2685909Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2785909Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28220863Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29140606Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30187103Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31220863Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32224882Snwhitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33224882Snwhitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34224882Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35140606Sobrien * SUCH DAMAGE.
36220863Sdim */
37224882Snwhitehorn
38220863Sdim#include <sys/cdefs.h>
39240468Sbrooks__FBSDID("$FreeBSD: head/usr.bin/sed/main.c 100970 2002-07-30 19:42:18Z fanf $");
40127204Sobrien
41220863Sdim#ifndef lint
42228868Sdimstatic const char copyright[] =
43228868Sdim"@(#) Copyright (c) 1992, 1993\n\
44228868Sdim	The Regents of the University of California.  All rights reserved.\n";
45140606Sobrien#endif
46220863Sdim
47220863Sdim#ifndef lint
48124834Srustatic const char sccsid[] = "@(#)main.c	8.2 (Berkeley) 1/3/94";
49124834Sru#endif
5085909Simp
5185909Simp#include <sys/types.h>
5285909Simp#include <sys/mman.h>
53160043Sobrien#include <sys/param.h>
54126890Strhodes#include <sys/stat.h>
5585909Simp
56192901Sthompsa#include <err.h>
57126890Strhodes#include <errno.h>
58151605Sobrien#include <fcntl.h>
59151605Sobrien#include <locale.h>
60130416Smlaier#include <regex.h>
61130416Smlaier#include <stddef.h>
62149978Sobrien#include <stdio.h>
63149978Sobrien#include <stdlib.h>
64149978Sobrien#include <string.h>
65149978Sobrien#include <unistd.h>
66149978Sobrien
67185522Ssam#include "defs.h"
68185522Ssam#include "extern.h"
69149978Sobrien
70149978Sobrien/*
71149978Sobrien * Linked list of units (strings and files) to be compiled
72149978Sobrien */
73229353Sgjbstruct s_compunit {
74149978Sobrien	struct s_compunit *next;
75149978Sobrien	enum e_cut {CU_FILE, CU_STRING} type;
76218792Snp	char *s;			/* Pointer to string or fname */
77218792Snp};
78183292Skmacy
79149978Sobrien/*
80149978Sobrien * Linked list pointer to compilation units and pointer to current
81160043Sobrien * next pointer.
82150966Sglebius */
83240468Sbrooksstatic struct s_compunit *script, **cu_nextp = &script;
84124834Sru
85210311Sjmallett/*
86132766Skan * Linked list of files to be processed
87132766Skan */
88210311Sjmallettstruct s_flist {
89210311Sjmallett	char *fname;
90215988Sjmallett	struct s_flist *next;
91210311Sjmallett};
92215988Sjmallett
93210311Sjmallett/*
94210394Srpaulo * Linked list pointer to files and pointer to current
95171239Speter * next pointer.
9685909Simp */
9785909Simpstatic struct s_flist *files, **fl_nextp = &files;
9885909Simp
9985909Simpstatic FILE *curfile;		/* Current open file */
100240468Sbrooks
101232263Sdimint aflag, eflag, nflag;
102232263Sdimint rflags = 0;
103232263Sdimstatic int rval;		/* Exit status */
10499923Sbde
105242715Sdim/*
106242715Sdim * Current file and line number; line numbers restart across compilation
107242715Sdim * units, but span across input files.
108242715Sdim */
10999932Sbdeconst char *fname;		/* File name. */
11099932Sbdeconst char *inplace;		/* Inplace edit file extension. */
111242717Sdimu_long linenum;
112242717Sdim
113242717Sdimstatic void add_compunit(enum e_cut, char *);
114242717Sdimstatic void add_file(char *);
11599932Sbdestatic int inplace_edit(char **);
116242717Sdimstatic void usage(void);
11799923Sbde
11899932Sbdeint
11985909Simpmain(argc, argv)
12091002Speter	int argc;
12185909Simp	char *argv[];
12285909Simp{
12385909Simp	int c, fflag;
12485909Simp	char *temp_arg;
125116341Smarkm
126116341Smarkm	(void) setlocale(LC_ALL, "");
127116341Smarkm
12891002Speter	fflag = 0;
12991002Speter	inplace = NULL;
13091002Speter
131105489Smux	while ((c = getopt(argc, argv, "Eae:f:i:n")) != -1)
13285909Simp		switch (c) {
133105462Smux		case 'E':
134105462Smux			rflags = REG_EXTENDED;
13585909Simp			break;
136239956Sjhb		case 'a':
137239957Sjhb			aflag = 1;
138239957Sjhb			break;
139239955Sjhb		case 'e':
140233578Speter			eflag = 1;
141233578Speter			if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL)
142233578Speter				err(1, "malloc");
143233578Speter			strcpy(temp_arg, optarg);
144233578Speter			strcat(temp_arg, "\n");
145233578Speter			add_compunit(CU_STRING, temp_arg);
146228158Sfjoe			break;
147228140Sfjoe		case 'f':
148228158Sfjoe			fflag = 1;
149228158Sfjoe			add_compunit(CU_FILE, optarg);
150228124Sfjoe			break;
151228158Sfjoe		case 'i':
152228124Sfjoe			inplace = optarg;
153179226Sjb			break;
154116341Smarkm		case 'n':
155116341Smarkm			nflag = 1;
156219819Sjeff			break;
157219819Sjeff		default:
158219819Sjeff		case '?':
159242933Sdim			usage();
160219819Sjeff		}
161219819Sjeff	argc -= optind;
162219819Sjeff	argv += optind;
163219819Sjeff
164131210Simp	/* First usage case; script is the first arg */
165144293Sphk	if (!eflag && !fflag && *argv) {
16685909Simp		add_compunit(CU_STRING, *argv);
167111684Sru		argv++;
168111684Sru	}
169111684Sru
170243664Smarcel	compile();
17189180Smsmith
17285909Simp	/* Continue with first and start second usage */
17385909Simp	if (*argv)
174123966Sbde		for (; *argv; argv++)
175175888Simp			add_file(*argv);
17685909Simp	else
17788893Simp		add_file(NULL);
17888893Simp	process();
17988893Simp	cfclose(prog, NULL);
180221265Sbz	if (fclose(stdout))
181210151Simp		err(1, "stdout");
18290789Sphk	exit(rval);
18390789Sphk}
18490789Sphk
18588893Simpstatic void
18688893Simpusage()
18788893Simp{
188216746Scperciva	(void)fprintf(stderr, "%s\n%s\n",
189216746Scperciva		"usage: sed script [-Ean] [-i extension] [file ...]",
190216746Scperciva		"       sed [-an] [-i extension] [-e script] ... [-f script_file] ... [file ...]");
19188893Simp	exit(1);
192125772Sru}
19388893Simp
194240402Sobrien/*
195240402Sobrien * Like fgets, but go through the chain of compilation units chaining them
196240402Sobrien * together.  Empty strings and files are ignored.
197240402Sobrien */
198240402Sobrienchar *
199cu_fgets(buf, n, more)
200	char *buf;
201	int n;
202	int *more;
203{
204	static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
205	static FILE *f;		/* Current open file */
206	static char *s;		/* Current pointer inside string */
207	static char string_ident[30];
208	char *p;
209
210again:
211	switch (state) {
212	case ST_EOF:
213		if (script == NULL) {
214			if (more != NULL)
215				*more = 0;
216			return (NULL);
217		}
218		linenum = 0;
219		switch (script->type) {
220		case CU_FILE:
221			if ((f = fopen(script->s, "r")) == NULL)
222				err(1, "%s", script->s);
223			fname = script->s;
224			state = ST_FILE;
225			goto again;
226		case CU_STRING:
227			if ((snprintf(string_ident,
228			    sizeof(string_ident), "\"%s\"", script->s)) >=
229			    sizeof(string_ident) - 1)
230				(void)strcpy(string_ident +
231				    sizeof(string_ident) - 6, " ...\"");
232			fname = string_ident;
233			s = script->s;
234			state = ST_STRING;
235			goto again;
236		}
237	case ST_FILE:
238		if ((p = fgets(buf, n, f)) != NULL) {
239			linenum++;
240			if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
241				nflag = 1;
242			if (more != NULL)
243				*more = !feof(f);
244			return (p);
245		}
246		script = script->next;
247		(void)fclose(f);
248		state = ST_EOF;
249		goto again;
250	case ST_STRING:
251		if (linenum == 0 && s[0] == '#' && s[1] == 'n')
252			nflag = 1;
253		p = buf;
254		for (;;) {
255			if (n-- <= 1) {
256				*p = '\0';
257				linenum++;
258				if (more != NULL)
259					*more = 1;
260				return (buf);
261			}
262			switch (*s) {
263			case '\0':
264				state = ST_EOF;
265				if (s == script->s) {
266					script = script->next;
267					goto again;
268				} else {
269					script = script->next;
270					*p = '\0';
271					linenum++;
272					if (more != NULL)
273						*more = 0;
274					return (buf);
275				}
276			case '\n':
277				*p++ = '\n';
278				*p = '\0';
279				s++;
280				linenum++;
281				if (more != NULL)
282					*more = 0;
283				return (buf);
284			default:
285				*p++ = *s++;
286			}
287		}
288	}
289	/* NOTREACHED */
290	return (NULL);
291}
292
293/*
294 * Like fgets, but go through the list of files chaining them together.
295 * Set len to the length of the line.
296 */
297int
298mf_fgets(sp, spflag)
299	SPACE *sp;
300	enum e_spflag spflag;
301{
302	size_t len;
303	char *p;
304	int c;
305	static int firstfile;
306
307	if (curfile == NULL) {
308		/* stdin? */
309		if (files->fname == NULL) {
310			if (inplace != NULL)
311				errx(1, "-i may not be used with stdin");
312			curfile = stdin;
313			fname = "stdin";
314		}
315		firstfile = 1;
316	}
317
318	for (;;) {
319		if (curfile != NULL && (c = getc(curfile)) != EOF) {
320			(void)ungetc(c, curfile);
321			break;
322		}
323		/* If we are here then either eof or no files are open yet */
324		if (curfile == stdin) {
325			sp->len = 0;
326			return (0);
327		}
328		if (curfile != NULL) {
329			fclose(curfile);
330		}
331		if (firstfile == 0) {
332			files = files->next;
333		} else
334			firstfile = 0;
335		if (files == NULL) {
336			sp->len = 0;
337			return (0);
338		}
339		if (inplace != NULL) {
340			if (inplace_edit(&files->fname) == -1)
341				continue;
342		}
343		fname = files->fname;
344		if ((curfile = fopen(fname, "r")) == NULL) {
345			warn("%s", fname);
346			rval = 1;
347			continue;
348		}
349		if (inplace != NULL && *inplace == '\0')
350			unlink(fname);
351	}
352	/*
353	 * We are here only when curfile is open and we still have something
354	 * to read from it.
355	 *
356	 * Use fgetln so that we can handle essentially infinite input data.
357	 * Can't use the pointer into the stdio buffer as the process space
358	 * because the ungetc() can cause it to move.
359	 */
360	p = fgetln(curfile, &len);
361	if (ferror(curfile))
362		errx(1, "%s: %s", fname, strerror(errno ? errno : EIO));
363	if (len != 0 && p[len - 1] == '\n')
364		len--;
365	cspace(sp, p, len, spflag);
366
367	linenum++;
368
369	return (1);
370}
371
372/*
373 * Add a compilation unit to the linked list
374 */
375static void
376add_compunit(type, s)
377	enum e_cut type;
378	char *s;
379{
380	struct s_compunit *cu;
381
382	if ((cu = malloc(sizeof(struct s_compunit))) == NULL)
383		err(1, "malloc");
384	cu->type = type;
385	cu->s = s;
386	cu->next = NULL;
387	*cu_nextp = cu;
388	cu_nextp = &cu->next;
389}
390
391/*
392 * Add a file to the linked list
393 */
394static void
395add_file(s)
396	char *s;
397{
398	struct s_flist *fp;
399
400	if ((fp = malloc(sizeof(struct s_flist))) == NULL)
401		err(1, "malloc");
402	fp->next = NULL;
403	*fl_nextp = fp;
404	fp->fname = s;
405	fl_nextp = &fp->next;
406}
407
408/*
409 * Modify a pointer to a filename for inplace editing and reopen stdout
410 */
411static int
412inplace_edit(filename)
413	char **filename;
414{
415	struct stat orig;
416	char backup[MAXPATHLEN];
417
418	if (lstat(*filename, &orig) == -1)
419		err(1, "lstat");
420	if ((orig.st_mode & S_IFREG) == 0) {
421		warnx("cannot inplace edit %s, not a regular file", *filename);
422		return -1;
423	}
424
425	if (*inplace == '\0') {
426		/*
427		 * This is a bit of a hack: we use mkstemp() to avoid the
428		 * mktemp() link-time warning, although mktemp() would fit in
429		 * this context much better. We're only interested in getting
430		 * a name for use in the rename(); there aren't any security
431		 * issues here that don't already exist in relation to the
432		 * original file and its directory.
433		 */
434		int fd;
435		strlcpy(backup, *filename, sizeof(backup));
436		strlcat(backup, ".XXXXXXXXXX", sizeof(backup));
437		fd = mkstemp(backup);
438		if (fd == -1)
439			errx(1, "could not create backup of %s", *filename);
440		else
441			close(fd);
442	} else {
443		strlcpy(backup, *filename, sizeof(backup));
444		strlcat(backup, inplace, sizeof(backup));
445	}
446
447	if (rename(*filename, backup) == -1)
448		err(1, "rename(\"%s\", \"%s\")", *filename, backup);
449	if (freopen(*filename, "w", stdout) == NULL)
450		err(1, "open(\"%s\")", *filename);
451	if (fchmod(fileno(stdout), orig.st_mode) == -1)
452		err(1, "chmod(\"%s\")", *filename);
453	*filename = strdup(backup);
454	if (*filename == NULL)
455		err(1, "malloc");
456	return 0;
457}
458
459int
460lastline(void)
461{
462	int ch;
463
464	if (files->next != NULL)
465		return (0);
466	if ((ch = getc(curfile)) == EOF)
467		return (1);
468	ungetc(ch, curfile);
469	return (0);
470}
471