GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/patch/ed.c Lines: 129 152 84.9 %
Date: 2016-12-06 Branches: 79 109 72.5 %

Line Branch Exec Source
1
/*	$OpenBSD: ed.c,v 1.2 2016/02/22 19:31:38 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
13
{
72
	off_t linepos;
73
	struct ed_line *nline;
74
	LINENUM i, range;
75
	int fsm;
76
77
13
	init_lines();
78
13
	cline = NULL;
79
13
	fsm = FSM_CMD;
80
81
	for (;;) {
82
3570
		linepos = ftello(pfp);
83
3570
		if (pgets(buf, sizeof buf, pfp) == NULL)
84
13
			break;
85
3557
		p_input_line++;
86
87
3557
		if (fsm == FSM_CMD) {
88
555
			if ((fsm = get_command()) == -1)
89
				break;
90
91
555
			switch (fsm) {
92
			case FSM_C:
93
			case FSM_D:
94
				/* delete lines in specified range */
95
430
				if (second_addr == -1)
96
249
					range = 1;
97
				else
98
181
					range = second_addr - first_addr + 1;
99
1598
				for (i = 0; i < range; i++) {
100
1168
					nline = LIST_NEXT(cline, entries);
101
1168
					LIST_REMOVE(cline, entries);
102
1168
					free(cline);
103
1168
					cline = nline;
104
1168
					line_count--;
105
				}
106
430
				fsm = (fsm == FSM_C) ? FSM_I : FSM_CMD;
107
430
				break;
108
			case FSM_S:
109
1
				cline->subst++;
110
1
				fsm = FSM_CMD;
111
				break;
112
			default:
113
				break;
114
			}
115
116
			continue;
117
		}
118
119
3002
		if (strcmp(buf, ".\n") == 0) {
120
534
			fsm = FSM_CMD;
121
534
			continue;
122
		}
123
124
2468
		nline = create_line(linepos);
125
2468
		if (cline == NULL)
126
6
			LIST_INSERT_HEAD(&head, nline, entries);
127
2462
		else if (fsm == FSM_A)
128
2054
			LIST_INSERT_AFTER(cline, nline, entries);
129
		else
130
408
			LIST_INSERT_BEFORE(cline, nline, entries);
131
2468
		cline = nline;
132
2468
		line_count++;
133
2468
		fsm = FSM_A;
134
	}
135
136
13
	next_intuit_at(linepos, p_input_line);
137
138
13
	if (skip_rest_of_patch) {
139
		free_lines();
140
		return;
141
	}
142
143
13
	write_lines(TMPOUTNAME);
144
13
	free_lines();
145
146
13
	ignore_signals();
147
13
	if (!check_only) {
148
13
		if (move_file(TMPOUTNAME, outname) < 0) {
149
			toutkeep = true;
150
			chmod(TMPOUTNAME, filemode);
151
		} else
152
13
			chmod(outname, filemode);
153
	}
154
13
	set_signals(1);
155
}
156
157
static int
158
get_command(void)
159
555
{
160
	char *p;
161
	LINENUM min_addr;
162
	int fsm;
163
164
555
	min_addr = 0;
165
555
	fsm = -1;
166
555
	p = buf;
167
168
	/* maybe garbage encountered at end of patch */
169
555
	if (!isdigit((unsigned char)*p))
170
		return -1;
171
172
555
	first_addr = strtolinenum(buf, &p);
173
555
	second_addr = (*p == ',') ? strtolinenum(p + 1, &p) : -1;
174
175

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

555
	if (!valid_addr(first_addr, min_addr) ||
216
	    (second_addr != -1 && !valid_addr(second_addr, first_addr)))
217
		fatal("invalid address at line %ld: %s", p_input_line, buf);
218
219
555
	cline = get_line(first_addr);
220
221
555
	return fsm;
222
}
223
224
static void
225
write_lines(char *filename)
226
13
{
227
	FILE *ofp;
228
	char *p;
229
	struct ed_line *line;
230
	off_t linepos;
231
232
13
	linepos = ftello(pfp);
233
13
	ofp = fopen(filename, "w");
234
13
	if (ofp == NULL)
235
		pfatal("can't create %s", filename);
236
237
4848
	LIST_FOREACH(line, &head, entries) {
238
4835
		if (line->src == SRC_INP) {
239
2367
			p = ifetch(line->pos.lineno, 0);
240
			/* Note: string is not NUL terminated. */
241
55994
			for (; *p != '\n'; p++)
242
53627
				if (line->subst != 0)
243
					line->subst--;
244
				else
245
53627
					putc(*p, ofp);
246
2367
			putc('\n', ofp);
247
2468
		} else if (line->src == SRC_PCH) {
248
2468
			fseeko(pfp, line->pos.seek, SEEK_SET);
249
2468
			if (pgets(buf, sizeof buf, pfp) == NULL)
250
				fatal("unexpected end of file");
251
2468
			p = buf;
252
2468
			if (line->subst != 0)
253
1
				for (; *p != '\0' && *p != '\n'; p++)
254
2
					if (line->subst-- == 0)
255
1
						break;
256
2468
			fputs(p, ofp);
257
2468
			if (strchr(p, '\n') == NULL)
258
				putc('\n', ofp);
259
		}
260
	}
261
13
	fclose(ofp);
262
263
	/* restore patch file position to match p_input_line */
264
13
	fseeko(pfp, linepos, SEEK_SET);
265
13
}
266
267
/* initialize list with input file */
268
static void
269
init_lines(void)
270
13
{
271
	struct ed_line *line;
272
	LINENUM i;
273
274
13
	LIST_INIT(&head);
275
3548
	for (i = input_lines; i > 0; i--) {
276
3535
		line = malloc(sizeof(*line));
277
3535
		if (line == NULL)
278
			fatal("cannot allocate memory");
279
3535
		line->src = SRC_INP;
280
3535
		line->subst = 0;
281
3535
		line->pos.lineno = i;
282
3535
		LIST_INSERT_HEAD(&head, line, entries);
283
	}
284
13
	line_count = input_lines;
285
13
}
286
287
static void
288
free_lines(void)
289
13
{
290
	struct ed_line *line;
291
292
4861
	while (!LIST_EMPTY(&head)) {
293
4835
		line = LIST_FIRST(&head);
294
4835
		LIST_REMOVE(line, entries);
295
4835
		free(line);
296
	}
297
13
}
298
299
static struct ed_line *
300
get_line(LINENUM lineno)
301
555
{
302
	struct ed_line *line;
303
	LINENUM i;
304
305
555
	if (lineno == 0)
306
2
		return NULL;
307
308
553
	i = 0;
309
396164
	LIST_FOREACH(line, &head, entries)
310
396164
		if (++i == lineno)
311
553
			return line;
312
313
	return NULL;
314
}
315
316
static struct ed_line *
317
create_line(off_t seek)
318
2468
{
319
	struct ed_line *line;
320
321
2468
	line = malloc(sizeof(*line));
322
2468
	if (line == NULL)
323
		fatal("cannot allocate memory");
324
2468
	line->src = SRC_PCH;
325
2468
	line->subst = 0;
326
2468
	line->pos.seek = seek;
327
328
2468
	return line;
329
}
330
331
static int
332
valid_addr(LINENUM lineno, LINENUM min)
333
736
{
334

736
	return lineno >= min && lineno <= line_count;
335
}