Download
jcs
/amend
/patch.c
(View History)
jcs *: Lots of little fixes and dead variable removal | Latest amendment: 110 on 2023-02-06 |
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 = -1; |
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 | } |