xref: /openbsd-src/usr.bin/patch/ed.c (revision 5fbd5e42b6e8a8eb350fdd243f46cfefbc9d0b49)
1*5fbd5e42Sjca /*	$OpenBSD: ed.c,v 1.4 2019/12/02 22:17:32 jca Exp $ */
26d4b3e9eStobias 
36d4b3e9eStobias /*
46d4b3e9eStobias  * Copyright (c) 2015 Tobias Stoeckmann <tobias@openbsd.org>
56d4b3e9eStobias  *
66d4b3e9eStobias  * Permission to use, copy, modify, and distribute this software for any
76d4b3e9eStobias  * purpose with or without fee is hereby granted, provided that the above
86d4b3e9eStobias  * copyright notice and this permission notice appear in all copies.
96d4b3e9eStobias  *
106d4b3e9eStobias  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116d4b3e9eStobias  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126d4b3e9eStobias  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136d4b3e9eStobias  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146d4b3e9eStobias  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156d4b3e9eStobias  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166d4b3e9eStobias  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176d4b3e9eStobias  */
186d4b3e9eStobias 
196d4b3e9eStobias #include <sys/queue.h>
206d4b3e9eStobias #include <sys/stat.h>
216d4b3e9eStobias 
226d4b3e9eStobias #include <ctype.h>
236d4b3e9eStobias #include <stdio.h>
246d4b3e9eStobias #include <stdlib.h>
256d4b3e9eStobias #include <string.h>
266d4b3e9eStobias 
276d4b3e9eStobias #include "common.h"
286d4b3e9eStobias #include "util.h"
296d4b3e9eStobias #include "pch.h"
306d4b3e9eStobias #include "inp.h"
316d4b3e9eStobias 
326d4b3e9eStobias /* states of finite state machine */
336d4b3e9eStobias #define FSM_CMD		1
346d4b3e9eStobias #define FSM_A		2
356d4b3e9eStobias #define FSM_C		3
366d4b3e9eStobias #define FSM_D		4
376d4b3e9eStobias #define FSM_I		5
386d4b3e9eStobias #define FSM_S		6
396d4b3e9eStobias 
406d4b3e9eStobias #define SRC_INP		1	/* line's origin is input file */
416d4b3e9eStobias #define SRC_PCH		2	/* line's origin is patch file */
426d4b3e9eStobias 
436d4b3e9eStobias #define S_PATTERN	"/.//"
446d4b3e9eStobias 
456d4b3e9eStobias static void		init_lines(void);
466d4b3e9eStobias static void		free_lines(void);
476d4b3e9eStobias static struct ed_line	*get_line(LINENUM);
486d4b3e9eStobias static struct ed_line	*create_line(off_t);
496d4b3e9eStobias static int		valid_addr(LINENUM, LINENUM);
506d4b3e9eStobias static int		get_command(void);
516d4b3e9eStobias static void		write_lines(char *);
526d4b3e9eStobias 
536d4b3e9eStobias LIST_HEAD(ed_head, ed_line) head;
546d4b3e9eStobias struct ed_line {
556d4b3e9eStobias 	LIST_ENTRY(ed_line)	entries;
566d4b3e9eStobias 	int			src;
576d4b3e9eStobias 	unsigned long		subst;
586d4b3e9eStobias 	union {
596d4b3e9eStobias 		LINENUM		lineno;
606d4b3e9eStobias 		off_t		seek;
616d4b3e9eStobias 	} pos;
626d4b3e9eStobias };
636d4b3e9eStobias 
646d4b3e9eStobias static LINENUM		first_addr;
656d4b3e9eStobias static LINENUM		second_addr;
666d4b3e9eStobias static LINENUM		line_count;
676d4b3e9eStobias static struct ed_line	*cline;		/* current line */
686d4b3e9eStobias 
696d4b3e9eStobias void
do_ed_script(void)706d4b3e9eStobias do_ed_script(void)
716d4b3e9eStobias {
726d4b3e9eStobias 	off_t linepos;
736d4b3e9eStobias 	struct ed_line *nline;
746d4b3e9eStobias 	LINENUM i, range;
756d4b3e9eStobias 	int fsm;
766d4b3e9eStobias 
776d4b3e9eStobias 	init_lines();
786d4b3e9eStobias 	cline = NULL;
796d4b3e9eStobias 	fsm = FSM_CMD;
806d4b3e9eStobias 
816d4b3e9eStobias 	for (;;) {
826d4b3e9eStobias 		linepos = ftello(pfp);
83*5fbd5e42Sjca 		if (pgetline(&buf, &bufsz, pfp) == -1)
846d4b3e9eStobias 			break;
856d4b3e9eStobias 		p_input_line++;
866d4b3e9eStobias 
876d4b3e9eStobias 		if (fsm == FSM_CMD) {
886d4b3e9eStobias 			if ((fsm = get_command()) == -1)
896d4b3e9eStobias 				break;
906d4b3e9eStobias 
916d4b3e9eStobias 			switch (fsm) {
926d4b3e9eStobias 			case FSM_C:
936d4b3e9eStobias 			case FSM_D:
946d4b3e9eStobias 				/* delete lines in specified range */
956d4b3e9eStobias 				if (second_addr == -1)
966d4b3e9eStobias 					range = 1;
976d4b3e9eStobias 				else
986d4b3e9eStobias 					range = second_addr - first_addr + 1;
996d4b3e9eStobias 				for (i = 0; i < range; i++) {
1006d4b3e9eStobias 					nline = LIST_NEXT(cline, entries);
1016d4b3e9eStobias 					LIST_REMOVE(cline, entries);
1026d4b3e9eStobias 					free(cline);
1036d4b3e9eStobias 					cline = nline;
1046d4b3e9eStobias 					line_count--;
1056d4b3e9eStobias 				}
106bf6824b1Stobias 				cline = get_line(first_addr - 1);
107bf6824b1Stobias 				fsm = (fsm == FSM_C) ? FSM_A : FSM_CMD;
1086d4b3e9eStobias 				break;
1096d4b3e9eStobias 			case FSM_S:
1106d4b3e9eStobias 				cline->subst++;
1116d4b3e9eStobias 				fsm = FSM_CMD;
1126d4b3e9eStobias 				break;
1136d4b3e9eStobias 			default:
1146d4b3e9eStobias 				break;
1156d4b3e9eStobias 			}
1166d4b3e9eStobias 
1176d4b3e9eStobias 			continue;
1186d4b3e9eStobias 		}
1196d4b3e9eStobias 
1206d4b3e9eStobias 		if (strcmp(buf, ".\n") == 0) {
1216d4b3e9eStobias 			fsm = FSM_CMD;
1226d4b3e9eStobias 			continue;
1236d4b3e9eStobias 		}
1246d4b3e9eStobias 
1256d4b3e9eStobias 		nline = create_line(linepos);
1266d4b3e9eStobias 		if (cline == NULL)
1276d4b3e9eStobias 			LIST_INSERT_HEAD(&head, nline, entries);
1284125338fStobias 		else if (fsm == FSM_A)
1296d4b3e9eStobias 			LIST_INSERT_AFTER(cline, nline, entries);
1304125338fStobias 		else
1316d4b3e9eStobias 			LIST_INSERT_BEFORE(cline, nline, entries);
1324125338fStobias 		cline = nline;
1336d4b3e9eStobias 		line_count++;
1344125338fStobias 		fsm = FSM_A;
1356d4b3e9eStobias 	}
1366d4b3e9eStobias 
1376d4b3e9eStobias 	next_intuit_at(linepos, p_input_line);
1386d4b3e9eStobias 
1396d4b3e9eStobias 	if (skip_rest_of_patch) {
1406d4b3e9eStobias 		free_lines();
1416d4b3e9eStobias 		return;
1426d4b3e9eStobias 	}
1436d4b3e9eStobias 
1446d4b3e9eStobias 	write_lines(TMPOUTNAME);
1456d4b3e9eStobias 	free_lines();
1466d4b3e9eStobias 
1476d4b3e9eStobias 	ignore_signals();
1486d4b3e9eStobias 	if (!check_only) {
1496d4b3e9eStobias 		if (move_file(TMPOUTNAME, outname) < 0) {
1506d4b3e9eStobias 			toutkeep = true;
1516d4b3e9eStobias 			chmod(TMPOUTNAME, filemode);
1526d4b3e9eStobias 		} else
1536d4b3e9eStobias 			chmod(outname, filemode);
1546d4b3e9eStobias 	}
1556d4b3e9eStobias 	set_signals(1);
1566d4b3e9eStobias }
1576d4b3e9eStobias 
1586d4b3e9eStobias static int
get_command(void)1596d4b3e9eStobias get_command(void)
1606d4b3e9eStobias {
1616d4b3e9eStobias 	char *p;
1626d4b3e9eStobias 	LINENUM min_addr;
1636d4b3e9eStobias 	int fsm;
1646d4b3e9eStobias 
1656d4b3e9eStobias 	min_addr = 0;
1666d4b3e9eStobias 	fsm = -1;
1676d4b3e9eStobias 	p = buf;
1686d4b3e9eStobias 
1696d4b3e9eStobias 	/* maybe garbage encountered at end of patch */
1706d4b3e9eStobias 	if (!isdigit((unsigned char)*p))
1716d4b3e9eStobias 		return -1;
1726d4b3e9eStobias 
1736d4b3e9eStobias 	first_addr = strtolinenum(buf, &p);
1746d4b3e9eStobias 	second_addr = (*p == ',') ? strtolinenum(p + 1, &p) : -1;
1756d4b3e9eStobias 
1766d4b3e9eStobias 	switch (*p++) {
1776d4b3e9eStobias 	case 'a':
1786d4b3e9eStobias 		if (second_addr != -1)
1796d4b3e9eStobias 			fatal("invalid address at line %ld: %s",
1806d4b3e9eStobias 			    p_input_line, buf);
1816d4b3e9eStobias 		fsm = FSM_A;
1826d4b3e9eStobias 		break;
1836d4b3e9eStobias 	case 'c':
1846d4b3e9eStobias 		fsm = FSM_C;
1856d4b3e9eStobias 		min_addr = 1;
1866d4b3e9eStobias 		break;
1876d4b3e9eStobias 	case 'd':
1886d4b3e9eStobias 		fsm = FSM_D;
1896d4b3e9eStobias 		min_addr = 1;
1906d4b3e9eStobias 		break;
1916d4b3e9eStobias 	case 'i':
1926d4b3e9eStobias 		if (second_addr != -1)
1936d4b3e9eStobias 			fatal("invalid address at line %ld: %s",
1946d4b3e9eStobias 			    p_input_line, buf);
1956d4b3e9eStobias 		fsm = FSM_I;
1966d4b3e9eStobias 		break;
1976d4b3e9eStobias 	case 's':
1986d4b3e9eStobias 		if (second_addr != -1)
1996d4b3e9eStobias 			fatal("unsupported address range at line %ld: %s",
2006d4b3e9eStobias 			    p_input_line, buf);
2016d4b3e9eStobias 		if (strncmp(p, S_PATTERN, sizeof(S_PATTERN) - 1) != 0)
2026d4b3e9eStobias 			fatal("unsupported substitution at "
2036d4b3e9eStobias 			    "line %ld: %s", p_input_line, buf);
2046d4b3e9eStobias 		p += sizeof(S_PATTERN) - 1;
2056d4b3e9eStobias 		fsm = FSM_S;
2066d4b3e9eStobias 		min_addr = 1;
2076d4b3e9eStobias 		break;
2086d4b3e9eStobias 	default:
2096d4b3e9eStobias 		return -1;
2106d4b3e9eStobias 		/* NOTREACHED */
2116d4b3e9eStobias 	}
2126d4b3e9eStobias 
2136d4b3e9eStobias 	if (*p != '\n')
2146d4b3e9eStobias 		return -1;
2156d4b3e9eStobias 
2166d4b3e9eStobias 	if (!valid_addr(first_addr, min_addr) ||
2176d4b3e9eStobias 	    (second_addr != -1 && !valid_addr(second_addr, first_addr)))
2186d4b3e9eStobias 		fatal("invalid address at line %ld: %s", p_input_line, buf);
2196d4b3e9eStobias 
2206d4b3e9eStobias 	cline = get_line(first_addr);
2216d4b3e9eStobias 
2226d4b3e9eStobias 	return fsm;
2236d4b3e9eStobias }
2246d4b3e9eStobias 
2256d4b3e9eStobias static void
write_lines(char * filename)2266d4b3e9eStobias write_lines(char *filename)
2276d4b3e9eStobias {
2286d4b3e9eStobias 	FILE *ofp;
2296d4b3e9eStobias 	char *p;
2306d4b3e9eStobias 	struct ed_line *line;
2316d4b3e9eStobias 	off_t linepos;
2326d4b3e9eStobias 
2336d4b3e9eStobias 	linepos = ftello(pfp);
2346d4b3e9eStobias 	ofp = fopen(filename, "w");
2356d4b3e9eStobias 	if (ofp == NULL)
2366d4b3e9eStobias 		pfatal("can't create %s", filename);
2376d4b3e9eStobias 
2386d4b3e9eStobias 	LIST_FOREACH(line, &head, entries) {
2396d4b3e9eStobias 		if (line->src == SRC_INP) {
2406d4b3e9eStobias 			p = ifetch(line->pos.lineno, 0);
2416d4b3e9eStobias 			/* Note: string is not NUL terminated. */
2426d4b3e9eStobias 			for (; *p != '\n'; p++)
2436d4b3e9eStobias 				if (line->subst != 0)
2446d4b3e9eStobias 					line->subst--;
2456d4b3e9eStobias 				else
2466d4b3e9eStobias 					putc(*p, ofp);
2476d4b3e9eStobias 			putc('\n', ofp);
2486d4b3e9eStobias 		} else if (line->src == SRC_PCH) {
2496d4b3e9eStobias 			fseeko(pfp, line->pos.seek, SEEK_SET);
250*5fbd5e42Sjca 			if (pgetline(&buf, &bufsz, pfp) == -1)
2516d4b3e9eStobias 				fatal("unexpected end of file");
2526d4b3e9eStobias 			p = buf;
2536d4b3e9eStobias 			if (line->subst != 0)
2546d4b3e9eStobias 				for (; *p != '\0' && *p != '\n'; p++)
2556d4b3e9eStobias 					if (line->subst-- == 0)
2566d4b3e9eStobias 						break;
2576d4b3e9eStobias 			fputs(p, ofp);
2586d4b3e9eStobias 			if (strchr(p, '\n') == NULL)
2596d4b3e9eStobias 				putc('\n', ofp);
2606d4b3e9eStobias 		}
2616d4b3e9eStobias 	}
2626d4b3e9eStobias 	fclose(ofp);
2636d4b3e9eStobias 
2646d4b3e9eStobias 	/* restore patch file position to match p_input_line */
2656d4b3e9eStobias 	fseeko(pfp, linepos, SEEK_SET);
2666d4b3e9eStobias }
2676d4b3e9eStobias 
2686d4b3e9eStobias /* initialize list with input file */
2696d4b3e9eStobias static void
init_lines(void)2706d4b3e9eStobias init_lines(void)
2716d4b3e9eStobias {
2726d4b3e9eStobias 	struct ed_line *line;
2736d4b3e9eStobias 	LINENUM i;
2746d4b3e9eStobias 
2756d4b3e9eStobias 	LIST_INIT(&head);
2766d4b3e9eStobias 	for (i = input_lines; i > 0; i--) {
2776d4b3e9eStobias 		line = malloc(sizeof(*line));
2786d4b3e9eStobias 		if (line == NULL)
2796d4b3e9eStobias 			fatal("cannot allocate memory");
2806d4b3e9eStobias 		line->src = SRC_INP;
2816d4b3e9eStobias 		line->subst = 0;
2826d4b3e9eStobias 		line->pos.lineno = i;
2836d4b3e9eStobias 		LIST_INSERT_HEAD(&head, line, entries);
2846d4b3e9eStobias 	}
2856d4b3e9eStobias 	line_count = input_lines;
2866d4b3e9eStobias }
2876d4b3e9eStobias 
2886d4b3e9eStobias static void
free_lines(void)2896d4b3e9eStobias free_lines(void)
2906d4b3e9eStobias {
2916d4b3e9eStobias 	struct ed_line *line;
2926d4b3e9eStobias 
2936d4b3e9eStobias 	while (!LIST_EMPTY(&head)) {
2946d4b3e9eStobias 		line = LIST_FIRST(&head);
2956d4b3e9eStobias 		LIST_REMOVE(line, entries);
2966d4b3e9eStobias 		free(line);
2976d4b3e9eStobias 	}
2986d4b3e9eStobias }
2996d4b3e9eStobias 
3006d4b3e9eStobias static struct ed_line *
get_line(LINENUM lineno)3016d4b3e9eStobias get_line(LINENUM lineno)
3026d4b3e9eStobias {
3036d4b3e9eStobias 	struct ed_line *line;
3046d4b3e9eStobias 	LINENUM i;
3056d4b3e9eStobias 
3066d4b3e9eStobias 	if (lineno == 0)
3076d4b3e9eStobias 		return NULL;
3086d4b3e9eStobias 
3096d4b3e9eStobias 	i = 0;
3106d4b3e9eStobias 	LIST_FOREACH(line, &head, entries)
3116d4b3e9eStobias 		if (++i == lineno)
3126d4b3e9eStobias 			return line;
3136d4b3e9eStobias 
3146d4b3e9eStobias 	return NULL;
3156d4b3e9eStobias }
3166d4b3e9eStobias 
3176d4b3e9eStobias static struct ed_line *
create_line(off_t seek)3186d4b3e9eStobias create_line(off_t seek)
3196d4b3e9eStobias {
3206d4b3e9eStobias 	struct ed_line *line;
3216d4b3e9eStobias 
3226d4b3e9eStobias 	line = malloc(sizeof(*line));
3236d4b3e9eStobias 	if (line == NULL)
3246d4b3e9eStobias 		fatal("cannot allocate memory");
3256d4b3e9eStobias 	line->src = SRC_PCH;
3266d4b3e9eStobias 	line->subst = 0;
3276d4b3e9eStobias 	line->pos.seek = seek;
3286d4b3e9eStobias 
3296d4b3e9eStobias 	return line;
3306d4b3e9eStobias }
3316d4b3e9eStobias 
3326d4b3e9eStobias static int
valid_addr(LINENUM lineno,LINENUM min)3336d4b3e9eStobias valid_addr(LINENUM lineno, LINENUM min)
3346d4b3e9eStobias {
3356d4b3e9eStobias 	return lineno >= min && lineno <= line_count;
3366d4b3e9eStobias }
337