GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/patch/ed.c Lines: 111 138 80.4 %
Date: 2017-11-13 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
130
	init_lines();
78
65
	cline = NULL;
79
	fsm = FSM_CMD;
80
81
12405
	for (;;) {
82
17850
		linepos = ftello(pfp);
83
17850
		if (pgets(buf, sizeof buf, pfp) == NULL)
84
			break;
85
17785
		p_input_line++;
86
87
17785
		if (fsm == FSM_CMD) {
88
2775
			if ((fsm = get_command()) == -1)
89
				break;
90
91

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

2775
	switch (*p++) {
177
	case 'a':
178
620
		if (second_addr != -1)
179
			fatal("invalid address at line %ld: %s",
180
			    p_input_line, buf);
181
		fsm = FSM_A;
182
620
		break;
183
	case 'c':
184
		fsm = FSM_C;
185
		min_addr = 1;
186
2060
		break;
187
	case 'd':
188
		fsm = FSM_D;
189
		min_addr = 1;
190
90
		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
5
		if (second_addr != -1)
199
			fatal("unsupported address range at line %ld: %s",
200
			    p_input_line, buf);
201
5
		if (strncmp(p, S_PATTERN, sizeof(S_PATTERN) - 1) != 0)
202
			fatal("unsupported substitution at "
203
			    "line %ld: %s", p_input_line, buf);
204
5
		p += sizeof(S_PATTERN) - 1;
205
		fsm = FSM_S;
206
		min_addr = 1;
207
5
		break;
208
	default:
209
		return -1;
210
		/* NOTREACHED */
211
	}
212
213
2775
	if (*p != '\n')
214
		return -1;
215
216

3680
	if (!valid_addr(first_addr, min_addr) ||
217
3680
	    (second_addr != -1 && !valid_addr(second_addr, first_addr)))
218
		fatal("invalid address at line %ld: %s", p_input_line, buf);
219
220
2775
	cline = get_line(first_addr);
221
222
2775
	return fsm;
223
2775
}
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
130
	linepos = ftello(pfp);
234
65
	ofp = fopen(filename, "w");
235
65
	if (ofp == NULL)
236
		pfatal("can't create %s", filename);
237
238
48480
	LIST_FOREACH(line, &head, entries) {
239
24175
		if (line->src == SRC_INP) {
240
11835
			p = ifetch(line->pos.lineno, 0);
241
			/* Note: string is not NUL terminated. */
242
559940
			for (; *p != '\n'; p++)
243
268135
				if (line->subst != 0)
244
					line->subst--;
245
				else
246
536270
					putc(*p, ofp);
247
23670
			putc('\n', ofp);
248
12340
		} else if (line->src == SRC_PCH) {
249
12340
			fseeko(pfp, line->pos.seek, SEEK_SET);
250
12340
			if (pgets(buf, sizeof buf, pfp) == NULL)
251
				fatal("unexpected end of file");
252
			p = buf;
253
12340
			if (line->subst != 0)
254

30
				for (; *p != '\0' && *p != '\n'; p++)
255
10
					if (line->subst-- == 0)
256
						break;
257
12340
			fputs(p, ofp);
258
12340
			if (strchr(p, '\n') == NULL)
259
				putc('\n', ofp);
260
		}
261
	}
262
65
	fclose(ofp);
263
264
	/* restore patch file position to match p_input_line */
265
65
	fseeko(pfp, linepos, SEEK_SET);
266
65
}
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
130
	LIST_INIT(&head);
276
35480
	for (i = input_lines; i > 0; i--) {
277
17675
		line = malloc(sizeof(*line));
278
17675
		if (line == NULL)
279
			fatal("cannot allocate memory");
280
17675
		line->src = SRC_INP;
281
17675
		line->subst = 0;
282
17675
		line->pos.lineno = i;
283
52965
		LIST_INSERT_HEAD(&head, line, entries);
284
	}
285
65
	line_count = input_lines;
286
65
}
287
288
static void
289
free_lines(void)
290
{
291
	struct ed_line *line;
292
293
48545
	while (!LIST_EMPTY(&head)) {
294
		line = LIST_FIRST(&head);
295
72465
		LIST_REMOVE(line, entries);
296
24175
		free(line);
297
	}
298
65
}
299
300
static struct ed_line *
301
get_line(LINENUM lineno)
302
{
303
	struct ed_line *line;
304
	LINENUM i;
305
306
9850
	if (lineno == 0)
307
35
		return NULL;
308
309
	i = 0;
310
6837320
	LIST_FOREACH(line, &head, entries)
311
3418660
		if (++i == lineno)
312
4890
			return line;
313
314
	return NULL;
315
4925
}
316
317
static struct ed_line *
318
create_line(off_t seek)
319
{
320
	struct ed_line *line;
321
322
24680
	line = malloc(sizeof(*line));
323
12340
	if (line == NULL)
324
		fatal("cannot allocate memory");
325
12340
	line->src = SRC_PCH;
326
12340
	line->subst = 0;
327
12340
	line->pos.seek = seek;
328
329
12340
	return line;
330
}
331
332
static int
333
valid_addr(LINENUM lineno, LINENUM min)
334
{
335
14720
	return lineno >= min && lineno <= line_count;
336
}