Annotation of src/usr.bin/patch/ed.c, Revision 1.4
1.4 ! jca 1: /* $OpenBSD: ed.c,v 1.3 2016/09/02 21:39:51 tobias Exp $ */
1.1 tobias 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: init_lines();
78: cline = NULL;
79: fsm = FSM_CMD;
80:
81: for (;;) {
82: linepos = ftello(pfp);
1.4 ! jca 83: if (pgetline(&buf, &bufsz, pfp) == -1)
1.1 tobias 84: break;
85: p_input_line++;
86:
87: if (fsm == FSM_CMD) {
88: if ((fsm = get_command()) == -1)
89: break;
90:
91: switch (fsm) {
92: case FSM_C:
93: case FSM_D:
94: /* delete lines in specified range */
95: if (second_addr == -1)
96: range = 1;
97: else
98: range = second_addr - first_addr + 1;
99: for (i = 0; i < range; i++) {
100: nline = LIST_NEXT(cline, entries);
101: LIST_REMOVE(cline, entries);
102: free(cline);
103: cline = nline;
104: line_count--;
105: }
1.3 tobias 106: cline = get_line(first_addr - 1);
107: fsm = (fsm == FSM_C) ? FSM_A : FSM_CMD;
1.1 tobias 108: break;
109: case FSM_S:
110: cline->subst++;
111: fsm = FSM_CMD;
112: break;
113: default:
114: break;
115: }
116:
117: continue;
118: }
119:
120: if (strcmp(buf, ".\n") == 0) {
121: fsm = FSM_CMD;
122: continue;
123: }
124:
1.2 tobias 125: nline = create_line(linepos);
126: if (cline == NULL)
127: LIST_INSERT_HEAD(&head, nline, entries);
128: else if (fsm == FSM_A)
129: LIST_INSERT_AFTER(cline, nline, entries);
130: else
131: LIST_INSERT_BEFORE(cline, nline, entries);
132: cline = nline;
133: line_count++;
134: fsm = FSM_A;
1.1 tobias 135: }
136:
137: next_intuit_at(linepos, p_input_line);
138:
139: if (skip_rest_of_patch) {
140: free_lines();
141: return;
142: }
143:
144: write_lines(TMPOUTNAME);
145: free_lines();
146:
147: ignore_signals();
148: if (!check_only) {
149: if (move_file(TMPOUTNAME, outname) < 0) {
150: toutkeep = true;
151: chmod(TMPOUTNAME, filemode);
152: } else
153: chmod(outname, filemode);
154: }
155: set_signals(1);
156: }
157:
158: static int
159: get_command(void)
160: {
161: char *p;
162: LINENUM min_addr;
163: int fsm;
164:
165: min_addr = 0;
166: fsm = -1;
167: p = buf;
168:
169: /* maybe garbage encountered at end of patch */
170: if (!isdigit((unsigned char)*p))
171: return -1;
172:
173: first_addr = strtolinenum(buf, &p);
174: second_addr = (*p == ',') ? strtolinenum(p + 1, &p) : -1;
175:
176: switch (*p++) {
177: case 'a':
178: if (second_addr != -1)
179: fatal("invalid address at line %ld: %s",
180: p_input_line, buf);
181: fsm = FSM_A;
182: break;
183: case 'c':
184: fsm = FSM_C;
185: min_addr = 1;
186: break;
187: case 'd':
188: fsm = FSM_D;
189: min_addr = 1;
190: 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: if (second_addr != -1)
199: fatal("unsupported address range at line %ld: %s",
200: p_input_line, buf);
201: if (strncmp(p, S_PATTERN, sizeof(S_PATTERN) - 1) != 0)
202: fatal("unsupported substitution at "
203: "line %ld: %s", p_input_line, buf);
204: p += sizeof(S_PATTERN) - 1;
205: fsm = FSM_S;
206: min_addr = 1;
207: break;
208: default:
209: return -1;
210: /* NOTREACHED */
211: }
212:
213: if (*p != '\n')
214: return -1;
215:
216: if (!valid_addr(first_addr, min_addr) ||
217: (second_addr != -1 && !valid_addr(second_addr, first_addr)))
218: fatal("invalid address at line %ld: %s", p_input_line, buf);
219:
220: cline = get_line(first_addr);
221:
222: return fsm;
223: }
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: linepos = ftello(pfp);
234: ofp = fopen(filename, "w");
235: if (ofp == NULL)
236: pfatal("can't create %s", filename);
237:
238: LIST_FOREACH(line, &head, entries) {
239: if (line->src == SRC_INP) {
240: p = ifetch(line->pos.lineno, 0);
241: /* Note: string is not NUL terminated. */
242: for (; *p != '\n'; p++)
243: if (line->subst != 0)
244: line->subst--;
245: else
246: putc(*p, ofp);
247: putc('\n', ofp);
248: } else if (line->src == SRC_PCH) {
249: fseeko(pfp, line->pos.seek, SEEK_SET);
1.4 ! jca 250: if (pgetline(&buf, &bufsz, pfp) == -1)
1.1 tobias 251: fatal("unexpected end of file");
252: p = buf;
253: if (line->subst != 0)
254: for (; *p != '\0' && *p != '\n'; p++)
255: if (line->subst-- == 0)
256: break;
257: fputs(p, ofp);
258: if (strchr(p, '\n') == NULL)
259: putc('\n', ofp);
260: }
261: }
262: fclose(ofp);
263:
264: /* restore patch file position to match p_input_line */
265: fseeko(pfp, linepos, SEEK_SET);
266: }
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: LIST_INIT(&head);
276: for (i = input_lines; i > 0; i--) {
277: line = malloc(sizeof(*line));
278: if (line == NULL)
279: fatal("cannot allocate memory");
280: line->src = SRC_INP;
281: line->subst = 0;
282: line->pos.lineno = i;
283: LIST_INSERT_HEAD(&head, line, entries);
284: }
285: line_count = input_lines;
286: }
287:
288: static void
289: free_lines(void)
290: {
291: struct ed_line *line;
292:
293: while (!LIST_EMPTY(&head)) {
294: line = LIST_FIRST(&head);
295: LIST_REMOVE(line, entries);
296: free(line);
297: }
298: }
299:
300: static struct ed_line *
301: get_line(LINENUM lineno)
302: {
303: struct ed_line *line;
304: LINENUM i;
305:
306: if (lineno == 0)
307: return NULL;
308:
309: i = 0;
310: LIST_FOREACH(line, &head, entries)
311: if (++i == lineno)
312: return line;
313:
314: return NULL;
315: }
316:
317: static struct ed_line *
318: create_line(off_t seek)
319: {
320: struct ed_line *line;
321:
322: line = malloc(sizeof(*line));
323: if (line == NULL)
324: fatal("cannot allocate memory");
325: line->src = SRC_PCH;
326: line->subst = 0;
327: line->pos.seek = seek;
328:
329: return line;
330: }
331:
332: static int
333: valid_addr(LINENUM lineno, LINENUM min)
334: {
335: return lineno >= min && lineno <= line_count;
336: }