AmendHub

Download

jcs

/

amend

/

patch.c

 

(View History)

jcs   browser+committer: Use a null caretHook on read-only TextEdits Latest amendment: 57 on 2022-02-03

1 /*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "patch.h"
21 #include "util.h"
22
23 static short patch_state;
24 static char patch_err[128];
25
26 enum {
27 PATCH_STATE_HEADER_FROM,
28 PATCH_STATE_TO,
29 PATCH_STATE_CHUNK_HEADER,
30 PATCH_STATE_CONTEXT
31 };
32
33 short patch_open_source_file(struct repo *repo, char *filename);
34 short patch_open_temp_dest_file(struct repo *repo, char *filename);
35
36
37 short
38 patch_open_source_file(struct repo *repo, char *filename)
39 {
40 short error, ret, i;
41 Str255 pfilename;
42
43 for (i = 0; i < repo->nfiles; i++) {
44 if (strcmp(repo->files[i]->filename, filename) != 0)
45 continue;
46
47 memcpy(pfilename, filename, sizeof(pfilename));
48 CtoPstr(pfilename);
49
50 error = FSOpen(pfilename, repo->bile->vrefnum, &ret);
51 if (error && error == 1234) {
52 /* TODO: what does it return if file isn't there? */
53 error = Create(pfilename, repo->bile->vrefnum,
54 repo->files[i]->creator, repo->files[i]->type);
55 if (error && error != dupFNErr)
56 err(1, "Failed to create %s: %d", filename, error);
57 error = FSOpen(pfilename, repo->bile->vrefnum, &ret);
58 }
59
60 if (error)
61 err(1, "Failed to open %s: %d", filename, error);
62
63 return ret;
64 }
65
66 return -1;
67 }
68
69 short
70 patch_open_temp_dest_file(struct repo *repo, char *filename)
71 {
72 short error, ret;
73 char tmpfile[256];
74
75 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
76 CtoPstr(tmpfile);
77
78 error = Create(tmpfile, repo->bile->vrefnum, AMEND_CREATOR, 'TEXT');
79 if (error && error != dupFNErr)
80 err(1, "Failed to create %s: %d", PtoCstr(tmpfile), error);
81 error = FSOpen(tmpfile, repo->bile->vrefnum, &ret);
82 if (error)
83 err(1, "Failed to open %s: %d", PtoCstr(tmpfile), error);
84
85 return ret;
86 }
87
88 short
89 patch_process(struct repo *repo, Str255 filename, short vrefnum)
90 {
91 char tofilename[256] = { 0 };
92 char buf[1024];
93 size_t i;
94 long patch_size;
95 short linenum = 0, error, ret;
96 short linelen, patch_frefnum = -1, source_frefnum = -1,
97 dest_frefnum = -1;
98 char *line;
99
100 error = FSOpen(filename, vrefnum, &patch_frefnum);
101 if (error)
102 err(1, "Failed to open patch %s: %d", PtoCstr(filename), error);
103
104 error = GetEOF(patch_frefnum, &patch_size);
105 if (error)
106 err(1, "Failed to get size of patch %s: %d", PtoCstr(filename),
107 error);
108
109 patch_state = PATCH_STATE_HEADER_FROM;
110
111 for (i = 0; i < patch_size; i += linelen) {
112 linelen = FSReadLine(patch_frefnum, buf, sizeof(buf) - 1);
113 if (linelen < 0)
114 break;
115 buf[linelen] = '\0';
116 linenum++;
117 line = buf;
118
119 switch (patch_state) {
120 case PATCH_STATE_HEADER_FROM:
121 if (strncmp(line, "--- ", 4) != 0)
122 break;
123 patch_state = PATCH_STATE_TO;
124 break;
125 case PATCH_STATE_TO:
126 if (strncmp(line, "+++ ", 4) != 0) {
127 snprintf(patch_err, sizeof(patch_err),
128 "Expected '+++ ' on line %d", linenum);
129 ret = -1;
130 goto patch_done;
131 }
132 line += 4;
133 linelen -= 4;
134
135 tofilename[0] = '\0';
136 for (i = 0; i < linelen; i++) {
137 if (line[i] == '\0' || line[i] == '\t') {
138 memcpy(tofilename, line, i);
139 tofilename[i + 1] = '\0';
140 break;
141 }
142 }
143 if (tofilename[0] == '\0') {
144 snprintf(patch_err, sizeof(patch_err),
145 "Failed to parse filename after +++ on line %d",
146 linenum);
147 ret = -1;
148 goto patch_done;
149 }
150
151 source_frefnum = patch_open_source_file(repo, tofilename);
152 if (source_frefnum == -1) {
153 ret = -1;
154 goto patch_done;
155 }
156
157 dest_frefnum = patch_open_temp_dest_file(repo, tofilename);
158 if (dest_frefnum == -1) {
159 ret = -1;
160 goto patch_done;
161 }
162
163 patch_state = PATCH_STATE_CHUNK_HEADER;
164 break;
165 case PATCH_STATE_CHUNK_HEADER: {
166 short source_line, source_delta, dest_line, dest_delta, count;
167
168 if (strncmp(line, "@@ ", 3) != 0) {
169 snprintf(patch_err, sizeof(patch_err),
170 "Expected '@@ ' on line %d", linenum);
171 ret = -1;
172 goto patch_done;
173 }
174 if (sscanf(line, "@@ %d,%d %d,%d @@%n",
175 &source_line, &source_delta, &dest_line, &dest_delta,
176 &count) != 4 || count < 1) {
177 snprintf(patch_err, sizeof(patch_err),
178 "Malformed '@@ ' on line %d", linenum);
179 ret = -1;
180 goto patch_done;
181 }
182 break;
183 }
184 default:
185 err(1, "Invalid patch state %d", patch_state);
186 }
187 }
188
189 patch_done:
190 if (patch_frefnum > -1)
191 FSClose(patch_frefnum);
192 if (source_frefnum > -1)
193 FSClose(source_frefnum);
194 if (dest_frefnum > -1)
195 FSClose(dest_frefnum);
196
197 return ret;
198 }