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