GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/patch/ed.c Lines: 110 137 80.3 %
Date: 2017-11-07 Branches: 76 108 70.4 %

Line Branch Exec Source
1
/*	$OpenBSD: ed.c,v 1.3 2016/09/02 21:39:51 tobias Exp $ */
2
3
/*
4
 * Copyright (c) 2015 Tobias Stoeckmann <tobias@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/queue.h>
20
#include <sys/stat.h>
21
22
#include <ctype.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "common.h"
28
#include "util.h"
29
#include "pch.h"
30
#include "inp.h"
31
32
/* states of finite state machine */
33
#define FSM_CMD		1
34
#define FSM_A		2
35
#define FSM_C		3
36
#define FSM_D		4
37
#define FSM_I		5
38
#define FSM_S		6
39
40
#define SRC_INP		1	/* line's origin is input file */
41
#define SRC_PCH		2	/* line's origin is patch file */
42
43
#define S_PATTERN	"/.//"
44
45
static void		init_lines(void);
46
static void		free_lines(void);
47
static struct ed_line	*get_line(LINENUM);
48
static struct ed_line	*create_line(off_t);
49
static int		valid_addr(LINENUM, LINENUM);
50
static int		get_command(void);
51
static void		write_lines(char *);
52
53
LIST_HEAD(ed_head, ed_line) head;
54
struct ed_line {
55
	LIST_ENTRY(ed_line)	entries;
56
	int			src;
57
	unsigned long		subst;
58
	union {
59
		LINENUM		lineno;
60
		off_t		seek;
61
	} pos;
62
};
63
64
static LINENUM		first_addr;
65
static LINENUM		second_addr;
66
static LINENUM		line_count;
67
static struct ed_line	*cline;		/* current line */
68
69
void
70
do_ed_script(void)
71
{
72
	off_t linepos;
73
	struct ed_line *nline;
74
	LINENUM i, range;
75
	int fsm;
76
77
234
	init_lines();
78
117
	cline = NULL;
79
	fsm = FSM_CMD;
80
81
22329
	for (;;) {
82
32130
		linepos = ftello(pfp);
83
32130
		if (pgets(buf, sizeof buf, pfp) == NULL)
84
			break;
85
32013
		p_input_line++;
86
87
32013
		if (fsm == FSM_CMD) {
88
4995
			if ((fsm = get_command()) == -1)
89
				break;
90
91

35892
			switch (fsm) {
92
			case FSM_C:
93
			case FSM_D:
94
				/* delete lines in specified range */
95
3870
				if (second_addr == -1)
96
2241
					range = 1;
97
				else
98
1629
					range = second_addr - first_addr + 1;
99
28764
				for (i = 0; i < range; i++) {
100
10512
					nline = LIST_NEXT(cline, entries);
101
31482
					LIST_REMOVE(cline, entries);
102
10512
					free(cline);
103
10512
					cline = nline;
104
10512
					line_count--;
105
				}
106
3870
				cline = get_line(first_addr - 1);
107
3870
				fsm = (fsm == FSM_C) ? FSM_A : FSM_CMD;
108
3870
				break;
109
			case FSM_S:
110
9
				cline->subst++;
111
				fsm = FSM_CMD;
112
9
				break;
113
			default:
114
				break;
115
			}
116
117
			continue;
118
		}
119
120
27018
		if (strcmp(buf, ".\n") == 0) {
121
			fsm = FSM_CMD;
122
4806
			continue;
123
		}
124
125
22212
		nline = create_line(linepos);
126
22212
		if (cline == NULL)
127
144
			LIST_INSERT_HEAD(&head, nline, entries);
128
22158
		else if (fsm == FSM_A)
129
60849
			LIST_INSERT_AFTER(cline, nline, entries);
130
		else
131
			LIST_INSERT_BEFORE(cline, nline, entries);
132
22212
		cline = nline;
133
22212
		line_count++;
134
		fsm = FSM_A;
135
	}
136
137
117
	next_intuit_at(linepos, p_input_line);
138
139
117
	if (skip_rest_of_patch) {
140
		free_lines();
141
		return;
142
	}
143
144
117
	write_lines(TMPOUTNAME);
145
117
	free_lines();
146
147
117
	ignore_signals();
148
117
	if (!check_only) {
149
117
		if (move_file(TMPOUTNAME, outname) < 0) {
150
			toutkeep = true;
151
			chmod(TMPOUTNAME, filemode);
152
		} else
153
117
			chmod(outname, filemode);
154
	}
155
117
	set_signals(1);
156
234
}
157
158
static int
159
get_command(void)
160
{
161
9990
	char *p;
162
	LINENUM min_addr;
163
	int fsm;
164
165
	min_addr = 0;
166
	fsm = -1;
167
4995
	p = buf;
168
169
	/* maybe garbage encountered at end of patch */
170
4995
	if (!isdigit((unsigned char)*p))
171
		return -1;
172
173
4995
	first_addr = strtolinenum(buf, &p);
174
11619
	second_addr = (*p == ',') ? strtolinenum(p + 1, &p) : -1;
175
176

4995
	switch (*p++) {
177
	case 'a':
178
1116
		if (second_addr != -1)
179
			fatal("invalid address at line %ld: %s",
180
			    p_input_line, buf);
181
		fsm = FSM_A;
182
1116
		break;
183
	case 'c':
184
		fsm = FSM_C;
185
		min_addr = 1;
186
3708
		break;
187
	case 'd':
188
		fsm = FSM_D;
189
		min_addr = 1;
190
162
		break;
191
	case 'i':
192
		if (second_addr != -1)
193
			fatal("invalid address at line %ld: %s",
194
			    p_input_line, buf);
195
		fsm = FSM_I;
196
		break;
197
	case 's':
198
9
		if (second_addr != -1)
199
			fatal("unsupported address range at line %ld: %s",
200
			    p_input_line, buf);
201
9
		if (strncmp(p, S_PATTERN, sizeof(S_PATTERN) - 1) != 0)
202
			fatal("unsupported substitution at "
203
			    "line %ld: %s", p_input_line, buf);
204
9
		p += sizeof(S_PATTERN) - 1;
205
		fsm = FSM_S;
206
		min_addr = 1;
207
9
		break;
208
	default:
209
		return -1;
210
		/* NOTREACHED */
211
	}
212
213
4995
	if (*p != '\n')
214
		return -1;
215
216

6624
	if (!valid_addr(first_addr, min_addr) ||
217
6624
	    (second_addr != -1 && !valid_addr(second_addr, first_addr)))
218
		fatal("invalid address at line %ld: %s", p_input_line, buf);
219
220
4995
	cline = get_line(first_addr);
221
222
4995
	return fsm;
223
4995
}
224
225
static void
226
write_lines(char *filename)
227
{
228
	FILE *ofp;
229
	char *p;
230
	struct ed_line *line;
231
	off_t linepos;
232
233
234
	linepos = ftello(pfp);
234
117
	ofp = fopen(filename, "w");
235
117
	if (ofp == NULL)
236
		pfatal("can't create %s", filename);
237
238
87264
	LIST_FOREACH(line, &head, entries) {
239
43515
		if (line->src == SRC_INP) {
240
21303
			p = ifetch(line->pos.lineno, 0);
241
			/* Note: string is not NUL terminated. */
242
1007892
			for (; *p != '\n'; p++)
243
482643
				if (line->subst != 0)
244
					line->subst--;
245
				else
246
965286
					putc(*p, ofp);
247
42606
			putc('\n', ofp);
248
22212
		} else if (line->src == SRC_PCH) {
249
22212
			fseeko(pfp, line->pos.seek, SEEK_SET);
250
22212
			if (pgets(buf, sizeof buf, pfp) == NULL)
251
				fatal("unexpected end of file");
252
			p = buf;
253
22212
			if (line->subst != 0)
254

45
				for (; *p != '\0' && *p != '\n'; p++)
255
18
					if (line->subst-- == 0)
256
						break;
257
22212
			fputs(p, ofp);
258
22212
			if (strchr(p, '\n') == NULL)
259
				putc('\n', ofp);
260
		}
261
	}
262
117
	fclose(ofp);
263
264
	/* restore patch file position to match p_input_line */
265
117
	fseeko(pfp, linepos, SEEK_SET);
266
117
}
267
268
/* initialize list with input file */
269
static void
270
init_lines(void)
271
{
272
	struct ed_line *line;
273
	LINENUM i;
274
275
234
	LIST_INIT(&head);
276
63864
	for (i = input_lines; i > 0; i--) {
277
31815
		line = malloc(sizeof(*line));
278
31815
		if (line == NULL)
279
			fatal("cannot allocate memory");
280
31815
		line->src = SRC_INP;
281
31815
		line->subst = 0;
282
31815
		line->pos.lineno = i;
283
95337
		LIST_INSERT_HEAD(&head, line, entries);
284
	}
285
117
	line_count = input_lines;
286
117
}
287
288
static void
289
free_lines(void)
290
{
291
	struct ed_line *line;
292
293
87381
	while (!LIST_EMPTY(&head)) {
294
		line = LIST_FIRST(&head);
295
130437
		LIST_REMOVE(line, entries);
296
43515
		free(line);
297
	}
298
117
}
299
300
static struct ed_line *
301
get_line(LINENUM lineno)
302
{
303
	struct ed_line *line;
304
	LINENUM i;
305
306
17730
	if (lineno == 0)
307
63
		return NULL;
308
309
	i = 0;
310
12307176
	LIST_FOREACH(line, &head, entries)
311
6153588
		if (++i == lineno)
312
8802
			return line;
313
314
	return NULL;
315
8865
}
316
317
static struct ed_line *
318
create_line(off_t seek)
319
{
320
	struct ed_line *line;
321
322
44424
	line = malloc(sizeof(*line));
323
22212
	if (line == NULL)
324
		fatal("cannot allocate memory");
325
22212
	line->src = SRC_PCH;
326
22212
	line->subst = 0;
327
22212
	line->pos.seek = seek;
328
329
22212
	return line;
330
}
331
332
static int
333
valid_addr(LINENUM lineno, LINENUM min)
334
{
335
26496
	return lineno >= min && lineno <= line_count;
336
}