Mercurial > hg > toybox
comparison toys/posix/patch.c @ 694:786841fdb1e0
Reindent to two spaces per level. Remove vi: directives that haven't worked right in years (ubuntu broke its' vim implementation). Remove trailing spaces. Add/remove blank lines. Re-wordwrap in places. Update documentation with new coding style.
The actual code should be the same afterward, this is just cosmetic refactoring.
author | Rob Landley <rob@landley.net> |
---|---|
date | Tue, 13 Nov 2012 17:14:08 -0600 |
parents | a81cf364764d |
children | 98c396477e0b |
comparison
equal
deleted
inserted
replaced
693:4a5a250e0633 | 694:786841fdb1e0 |
---|---|
1 /* vi: set sw=4 ts=4: | 1 /* patch.c - Apply a "universal" diff. |
2 * | |
3 * patch.c - Apply a "universal" diff. | |
4 * | 2 * |
5 * Copyright 2007 Rob Landley <rob@landley.net> | 3 * Copyright 2007 Rob Landley <rob@landley.net> |
6 * | 4 * |
7 * see http://opengroup.org/onlinepubs/9699919799/utilities/patch.html | 5 * see http://opengroup.org/onlinepubs/9699919799/utilities/patch.html |
8 * (But only does -u, because who still cares about "ed"?) | 6 * (But only does -u, because who still cares about "ed"?) |
22 * [file] which file to patch | 20 * [file] which file to patch |
23 | 21 |
24 USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN)) | 22 USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN)) |
25 | 23 |
26 config PATCH | 24 config PATCH |
27 bool "patch" | 25 bool "patch" |
28 default y | 26 default y |
29 help | 27 help |
30 usage: patch [-i file] [-p depth] [-Ru] | 28 usage: patch [-i file] [-p depth] [-Ru] |
31 | 29 |
32 Apply a unified diff to one or more files. | 30 Apply a unified diff to one or more files. |
33 | 31 |
34 -i Input file (defaults=stdin) | 32 -i Input file (defaults=stdin) |
35 -l Loose match (ignore whitespace) | 33 -l Loose match (ignore whitespace) |
36 -p Number of '/' to strip from start of file paths (default=all) | 34 -p Number of '/' to strip from start of file paths (default=all) |
37 -R Reverse patch. | 35 -R Reverse patch. |
38 -u Ignored (only handles "unified" diffs) | 36 -u Ignored (only handles "unified" diffs) |
39 | 37 |
40 This version of patch only handles unified diffs, and only modifies | 38 This version of patch only handles unified diffs, and only modifies |
41 a file when all all hunks to that file apply. Patch prints failed | 39 a file when all all hunks to that file apply. Patch prints failed |
42 hunks to stderr, and exits with nonzero status if any hunks fail. | 40 hunks to stderr, and exits with nonzero status if any hunks fail. |
43 | 41 |
44 A file compared against /dev/null (or with a date <= the epoch) is | 42 A file compared against /dev/null (or with a date <= the epoch) is |
45 created/deleted as appropriate. | 43 created/deleted as appropriate. |
46 */ | 44 */ |
47 | 45 |
48 #define FOR_patch | 46 #define FOR_patch |
49 #include "toys.h" | 47 #include "toys.h" |
50 | 48 |
51 GLOBALS( | 49 GLOBALS( |
52 char *infile; | 50 char *infile; |
53 long prefix; | 51 long prefix; |
54 | 52 |
55 struct double_list *current_hunk; | 53 struct double_list *current_hunk; |
56 long oldline, oldlen, newline, newlen; | 54 long oldline, oldlen, newline, newlen; |
57 long linenum; | 55 long linenum; |
58 int context, state, filein, fileout, filepatch, hunknum; | 56 int context, state, filein, fileout, filepatch, hunknum; |
59 char *tempname; | 57 char *tempname; |
60 ) | 58 ) |
61 | 59 |
62 // Dispose of a line of input, either by writing it out or discarding it. | 60 // Dispose of a line of input, either by writing it out or discarding it. |
63 | 61 |
64 // state < 2: just free | 62 // state < 2: just free |
68 | 66 |
69 #define PATCH_DEBUG (CFG_TOYBOX_DEBUG && (toys.optflags & 32)) | 67 #define PATCH_DEBUG (CFG_TOYBOX_DEBUG && (toys.optflags & 32)) |
70 | 68 |
71 static void do_line(void *data) | 69 static void do_line(void *data) |
72 { | 70 { |
73 struct double_list *dlist = (struct double_list *)data; | 71 struct double_list *dlist = (struct double_list *)data; |
74 | 72 |
75 if (TT.state>1 && *dlist->data != TT.state) { | 73 if (TT.state>1 && *dlist->data != TT.state) { |
76 char *s = dlist->data+(TT.state>3 ? 1 : 0); | 74 char *s = dlist->data+(TT.state>3 ? 1 : 0); |
77 int i = TT.state == 2 ? 2 : TT.fileout; | 75 int i = TT.state == 2 ? 2 : TT.fileout; |
78 | 76 |
79 xwrite(i, s, strlen(s)); | 77 xwrite(i, s, strlen(s)); |
80 xwrite(i, "\n", 1); | 78 xwrite(i, "\n", 1); |
81 } | 79 } |
82 | 80 |
83 if (PATCH_DEBUG) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data); | 81 if (PATCH_DEBUG) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data); |
84 | 82 |
85 free(dlist->data); | 83 free(dlist->data); |
86 free(data); | 84 free(data); |
87 } | 85 } |
88 | 86 |
89 static void finish_oldfile(void) | 87 static void finish_oldfile(void) |
90 { | 88 { |
91 if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); | 89 if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); |
92 TT.fileout = TT.filein = -1; | 90 TT.fileout = TT.filein = -1; |
93 } | 91 } |
94 | 92 |
95 static void fail_hunk(void) | 93 static void fail_hunk(void) |
96 { | 94 { |
97 if (!TT.current_hunk) return; | 95 if (!TT.current_hunk) return; |
98 TT.current_hunk->prev->next = 0; | 96 TT.current_hunk->prev->next = 0; |
99 | 97 |
100 fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n", | 98 fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n", |
101 TT.hunknum, TT.oldline, TT.newline); | 99 TT.hunknum, TT.oldline, TT.newline); |
102 toys.exitval = 1; | 100 toys.exitval = 1; |
103 | 101 |
104 // If we got to this point, we've seeked to the end. Discard changes to | 102 // If we got to this point, we've seeked to the end. Discard changes to |
105 // this file and advance to next file. | 103 // this file and advance to next file. |
106 | 104 |
107 TT.state = 2; | 105 TT.state = 2; |
108 llist_traverse(TT.current_hunk, do_line); | 106 llist_traverse(TT.current_hunk, do_line); |
109 TT.current_hunk = NULL; | 107 TT.current_hunk = NULL; |
110 delete_tempfile(TT.filein, TT.fileout, &TT.tempname); | 108 delete_tempfile(TT.filein, TT.fileout, &TT.tempname); |
111 TT.state = 0; | 109 TT.state = 0; |
112 } | 110 } |
113 | 111 |
114 // Compare ignoring whitespace. Just returns | 112 // Compare ignoring whitespace. Just returns |
115 static int loosecmp(char *aa, char *bb) | 113 static int loosecmp(char *aa, char *bb) |
116 { | 114 { |
117 int a = 0, b = 0; | 115 int a = 0, b = 0; |
118 | 116 |
119 for (;;) { | 117 for (;;) { |
120 while (isspace(aa[a])) a++; | 118 while (isspace(aa[a])) a++; |
121 while (isspace(bb[b])) b++; | 119 while (isspace(bb[b])) b++; |
122 if (aa[a] != bb[b]) return 1; | 120 if (aa[a] != bb[b]) return 1; |
123 if (!aa[a]) return 0; | 121 if (!aa[a]) return 0; |
124 a++, b++; | 122 a++, b++; |
125 } | 123 } |
126 } | 124 } |
127 | 125 |
128 // Given a hunk of a unified diff, make the appropriate change to the file. | 126 // Given a hunk of a unified diff, make the appropriate change to the file. |
129 // This does not use the location information, but instead treats a hunk | 127 // This does not use the location information, but instead treats a hunk |
130 // as a sort of regex. Copies data from input to output until it finds | 128 // as a sort of regex. Copies data from input to output until it finds |
132 // (Finding EOF first is an error.) This is a single pass operation, so | 130 // (Finding EOF first is an error.) This is a single pass operation, so |
133 // multiple hunks must occur in order in the file. | 131 // multiple hunks must occur in order in the file. |
134 | 132 |
135 static int apply_one_hunk(void) | 133 static int apply_one_hunk(void) |
136 { | 134 { |
137 struct double_list *plist, *buf = NULL, *check; | 135 struct double_list *plist, *buf = NULL, *check; |
138 int matcheof = 0, reverse = toys.optflags & FLAG_R, backwarn = 0; | 136 int matcheof = 0, reverse = toys.optflags & FLAG_R, backwarn = 0; |
139 int (*lcmp)(char *aa, char *bb); | 137 int (*lcmp)(char *aa, char *bb); |
140 | 138 |
141 lcmp = (toys.optflags & FLAG_l) ? (void *)loosecmp : (void *)strcmp; | 139 lcmp = (toys.optflags & FLAG_l) ? (void *)loosecmp : (void *)strcmp; |
142 | 140 |
143 // Break doubly linked list so we can use singly linked traversal function. | 141 // Break doubly linked list so we can use singly linked traversal function. |
144 TT.current_hunk->prev->next = NULL; | 142 TT.current_hunk->prev->next = NULL; |
145 | 143 |
146 // Match EOF if there aren't as many ending context lines as beginning | 144 // Match EOF if there aren't as many ending context lines as beginning |
147 for (plist = TT.current_hunk; plist; plist = plist->next) { | 145 for (plist = TT.current_hunk; plist; plist = plist->next) { |
148 if (plist->data[0]==' ') matcheof++; | 146 if (plist->data[0]==' ') matcheof++; |
149 else matcheof = 0; | 147 else matcheof = 0; |
150 if (PATCH_DEBUG) fprintf(stderr, "HUNK:%s\n", plist->data); | 148 if (PATCH_DEBUG) fprintf(stderr, "HUNK:%s\n", plist->data); |
151 } | 149 } |
152 matcheof = matcheof < TT.context; | 150 matcheof = matcheof < TT.context; |
153 | 151 |
154 if (PATCH_DEBUG) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); | 152 if (PATCH_DEBUG) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); |
155 | 153 |
156 // Loop through input data searching for this hunk. Match all context | 154 // Loop through input data searching for this hunk. Match all context |
157 // lines and all lines to be removed until we've found the end of a | 155 // lines and all lines to be removed until we've found the end of a |
158 // complete hunk. | 156 // complete hunk. |
159 plist = TT.current_hunk; | 157 plist = TT.current_hunk; |
160 buf = NULL; | 158 buf = NULL; |
161 if (TT.context) for (;;) { | 159 if (TT.context) for (;;) { |
162 char *data = get_line(TT.filein); | 160 char *data = get_line(TT.filein); |
163 | 161 |
164 TT.linenum++; | 162 TT.linenum++; |
165 | 163 |
166 // Figure out which line of hunk to compare with next. (Skip lines | 164 // Figure out which line of hunk to compare with next. (Skip lines |
167 // of the hunk we'd be adding.) | 165 // of the hunk we'd be adding.) |
168 while (plist && *plist->data == "+-"[reverse]) { | 166 while (plist && *plist->data == "+-"[reverse]) { |
169 if (data && !lcmp(data, plist->data+1)) { | 167 if (data && !lcmp(data, plist->data+1)) { |
170 if (!backwarn) backwarn = TT.linenum; | 168 if (!backwarn) backwarn = TT.linenum; |
171 } | 169 } |
172 plist = plist->next; | 170 plist = plist->next; |
173 } | 171 } |
174 | 172 |
175 // Is this EOF? | 173 // Is this EOF? |
176 if (!data) { | 174 if (!data) { |
177 if (PATCH_DEBUG) fprintf(stderr, "INEOF\n"); | 175 if (PATCH_DEBUG) fprintf(stderr, "INEOF\n"); |
178 | 176 |
179 // Does this hunk need to match EOF? | 177 // Does this hunk need to match EOF? |
180 if (!plist && matcheof) break; | 178 if (!plist && matcheof) break; |
181 | 179 |
182 if (backwarn) | 180 if (backwarn) |
183 fprintf(stderr, "Possibly reversed hunk %d at %ld\n", | 181 fprintf(stderr, "Possibly reversed hunk %d at %ld\n", |
184 TT.hunknum, TT.linenum); | 182 TT.hunknum, TT.linenum); |
185 | 183 |
186 // File ended before we found a place for this hunk. | 184 // File ended before we found a place for this hunk. |
187 fail_hunk(); | 185 fail_hunk(); |
188 goto done; | 186 goto done; |
189 } else if (PATCH_DEBUG) fprintf(stderr, "IN: %s\n", data); | 187 } else if (PATCH_DEBUG) fprintf(stderr, "IN: %s\n", data); |
190 check = dlist_add(&buf, data); | 188 check = dlist_add(&buf, data); |
191 | 189 |
192 // Compare this line with next expected line of hunk. | 190 // Compare this line with next expected line of hunk. |
193 | 191 |
194 // A match can fail because the next line doesn't match, or because | 192 // A match can fail because the next line doesn't match, or because |
195 // we hit the end of a hunk that needed EOF, and this isn't EOF. | 193 // we hit the end of a hunk that needed EOF, and this isn't EOF. |
196 | 194 |
197 // If match failed, flush first line of buffered data and | 195 // If match failed, flush first line of buffered data and |
198 // recheck buffered data for a new match until we find one or run | 196 // recheck buffered data for a new match until we find one or run |
199 // out of buffer. | 197 // out of buffer. |
200 | 198 |
201 for (;;) { | 199 for (;;) { |
202 if (!plist || lcmp(check->data, plist->data+1)) { | 200 if (!plist || lcmp(check->data, plist->data+1)) { |
203 // Match failed. Write out first line of buffered data and | 201 // Match failed. Write out first line of buffered data and |
204 // recheck remaining buffered data for a new match. | 202 // recheck remaining buffered data for a new match. |
205 | 203 |
206 if (PATCH_DEBUG) | 204 if (PATCH_DEBUG) fprintf(stderr, "NOT: %s\n", plist->data); |
207 fprintf(stderr, "NOT: %s\n", plist->data); | 205 |
208 | 206 TT.state = 3; |
209 TT.state = 3; | 207 check = llist_pop(&buf); |
210 check = llist_pop(&buf); | 208 check->prev->next = buf; |
211 check->prev->next = buf; | 209 buf->prev = check->prev; |
212 buf->prev = check->prev; | 210 do_line(check); |
213 do_line(check); | 211 plist = TT.current_hunk; |
214 plist = TT.current_hunk; | 212 |
215 | 213 // If we've reached the end of the buffer without confirming a |
216 // If we've reached the end of the buffer without confirming a | 214 // match, read more lines. |
217 // match, read more lines. | 215 if (check==buf) { |
218 if (check==buf) { | 216 buf = 0; |
219 buf = 0; | 217 break; |
220 break; | 218 } |
221 } | 219 check = buf; |
222 check = buf; | 220 } else { |
223 } else { | 221 if (PATCH_DEBUG) fprintf(stderr, "MAYBE: %s\n", plist->data); |
224 if (PATCH_DEBUG) | 222 // This line matches. Advance plist, detect successful match. |
225 fprintf(stderr, "MAYBE: %s\n", plist->data); | 223 plist = plist->next; |
226 // This line matches. Advance plist, detect successful match. | 224 if (!plist && !matcheof) goto out; |
227 plist = plist->next; | 225 check = check->next; |
228 if (!plist && !matcheof) goto out; | 226 if (check == buf) break; |
229 check = check->next; | 227 } |
230 if (check == buf) break; | 228 } |
231 } | 229 } |
232 } | |
233 } | |
234 out: | 230 out: |
235 // We have a match. Emit changed data. | 231 // We have a match. Emit changed data. |
236 TT.state = "-+"[reverse]; | 232 TT.state = "-+"[reverse]; |
237 llist_traverse(TT.current_hunk, do_line); | 233 llist_traverse(TT.current_hunk, do_line); |
238 TT.current_hunk = NULL; | 234 TT.current_hunk = NULL; |
239 TT.state = 1; | 235 TT.state = 1; |
240 done: | 236 done: |
241 if (buf) { | 237 if (buf) { |
242 buf->prev->next = NULL; | 238 buf->prev->next = NULL; |
243 llist_traverse(buf, do_line); | 239 llist_traverse(buf, do_line); |
244 } | 240 } |
245 | 241 |
246 return TT.state; | 242 return TT.state; |
247 } | 243 } |
248 | 244 |
249 // Read a patch file and find hunks, opening/creating/deleting files. | 245 // Read a patch file and find hunks, opening/creating/deleting files. |
250 // Call apply_one_hunk() on each hunk. | 246 // Call apply_one_hunk() on each hunk. |
251 | 247 |
254 // state 2: In hunk: counting initial context lines | 250 // state 2: In hunk: counting initial context lines |
255 // state 3: In hunk: getting body | 251 // state 3: In hunk: getting body |
256 | 252 |
257 void patch_main(void) | 253 void patch_main(void) |
258 { | 254 { |
259 int reverse = toys.optflags&FLAG_R, state = 0, patchlinenum = 0, | 255 int reverse = toys.optflags&FLAG_R, state = 0, patchlinenum = 0, |
260 strip = 0; | 256 strip = 0; |
261 char *oldname = NULL, *newname = NULL; | 257 char *oldname = NULL, *newname = NULL; |
262 | 258 |
263 if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY); | 259 if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY); |
264 TT.filein = TT.fileout = -1; | 260 TT.filein = TT.fileout = -1; |
265 | 261 |
266 // Loop through the lines in the patch | 262 // Loop through the lines in the patch |
267 for (;;) { | 263 for (;;) { |
268 char *patchline; | 264 char *patchline; |
269 | 265 |
270 patchline = get_line(TT.filepatch); | 266 patchline = get_line(TT.filepatch); |
271 if (!patchline) break; | 267 if (!patchline) break; |
272 | 268 |
273 // Other versions of patch accept damaged patches, | 269 // Other versions of patch accept damaged patches, |
274 // so we need to also. | 270 // so we need to also. |
275 if (strip || !patchlinenum++) { | 271 if (strip || !patchlinenum++) { |
276 int len = strlen(patchline); | 272 int len = strlen(patchline); |
277 if (patchline[len-1] == '\r') { | 273 if (patchline[len-1] == '\r') { |
278 if (!strip) fprintf(stderr, "Removing DOS newlines\n"); | 274 if (!strip) fprintf(stderr, "Removing DOS newlines\n"); |
279 strip = 1; | 275 strip = 1; |
280 patchline[len-1]=0; | 276 patchline[len-1]=0; |
281 } | 277 } |
282 } | 278 } |
283 if (!*patchline) { | 279 if (!*patchline) { |
284 free(patchline); | 280 free(patchline); |
285 patchline = xstrdup(" "); | 281 patchline = xstrdup(" "); |
286 } | 282 } |
287 | 283 |
288 // Are we assembling a hunk? | 284 // Are we assembling a hunk? |
289 if (state >= 2) { | 285 if (state >= 2) { |
290 if (*patchline==' ' || *patchline=='+' || *patchline=='-') { | 286 if (*patchline==' ' || *patchline=='+' || *patchline=='-') { |
291 dlist_add(&TT.current_hunk, patchline); | 287 dlist_add(&TT.current_hunk, patchline); |
292 | 288 |
293 if (*patchline != '+') TT.oldlen--; | 289 if (*patchline != '+') TT.oldlen--; |
294 if (*patchline != '-') TT.newlen--; | 290 if (*patchline != '-') TT.newlen--; |
295 | 291 |
296 // Context line? | 292 // Context line? |
297 if (*patchline==' ' && state==2) TT.context++; | 293 if (*patchline==' ' && state==2) TT.context++; |
298 else state=3; | 294 else state=3; |
299 | 295 |
300 // If we've consumed all expected hunk lines, apply the hunk. | 296 // If we've consumed all expected hunk lines, apply the hunk. |
301 | 297 |
302 if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); | 298 if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); |
303 continue; | 299 continue; |
304 } | 300 } |
305 fail_hunk(); | 301 fail_hunk(); |
306 state = 0; | 302 state = 0; |
307 continue; | 303 continue; |
308 } | 304 } |
309 | 305 |
310 // Open a new file? | 306 // Open a new file? |
311 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { | 307 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { |
312 char *s, **name = &oldname; | 308 char *s, **name = &oldname; |
313 int i; | 309 int i; |
314 | 310 |
315 if (*patchline == '+') { | 311 if (*patchline == '+') { |
316 name = &newname; | 312 name = &newname; |
317 state = 1; | 313 state = 1; |
318 } | 314 } |
319 | 315 |
320 free(*name); | 316 free(*name); |
321 finish_oldfile(); | 317 finish_oldfile(); |
322 | 318 |
323 // Trim date from end of filename (if any). We don't care. | 319 // Trim date from end of filename (if any). We don't care. |
324 for (s = patchline+4; *s && *s!='\t'; s++) | 320 for (s = patchline+4; *s && *s!='\t'; s++) |
325 if (*s=='\\' && s[1]) s++; | 321 if (*s=='\\' && s[1]) s++; |
326 i = atoi(s); | 322 i = atoi(s); |
327 if (i>1900 && i<=1970) | 323 if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); |
328 *name = xstrdup("/dev/null"); | 324 else { |
329 else { | 325 *s = 0; |
330 *s = 0; | 326 *name = xstrdup(patchline+4); |
331 *name = xstrdup(patchline+4); | 327 } |
332 } | 328 |
333 | 329 // We defer actually opening the file because svn produces broken |
334 // We defer actually opening the file because svn produces broken | 330 // patches that don't signal they want to create a new file the |
335 // patches that don't signal they want to create a new file the | 331 // way the patch man page says, so you have to read the first hunk |
336 // way the patch man page says, so you have to read the first hunk | 332 // and _guess_. |
337 // and _guess_. | 333 |
338 | 334 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ |
339 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ | 335 // but a missing ,value means the value is 1. |
340 // but a missing ,value means the value is 1. | 336 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { |
341 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { | 337 int i; |
342 int i; | 338 char *s = patchline+4; |
343 char *s = patchline+4; | 339 |
344 | 340 // Read oldline[,oldlen] +newline[,newlen] |
345 // Read oldline[,oldlen] +newline[,newlen] | 341 |
346 | 342 TT.oldlen = TT.newlen = 1; |
347 TT.oldlen = TT.newlen = 1; | 343 TT.oldline = strtol(s, &s, 10); |
348 TT.oldline = strtol(s, &s, 10); | 344 if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); |
349 if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); | 345 TT.newline = strtol(s+2, &s, 10); |
350 TT.newline = strtol(s+2, &s, 10); | 346 if (*s == ',') TT.newlen = strtol(s+1, &s, 10); |
351 if (*s == ',') TT.newlen = strtol(s+1, &s, 10); | 347 |
352 | 348 TT.context = 0; |
353 TT.context = 0; | 349 state = 2; |
354 state = 2; | 350 |
355 | 351 // If this is the first hunk, open the file. |
356 // If this is the first hunk, open the file. | 352 if (TT.filein == -1) { |
357 if (TT.filein == -1) { | 353 int oldsum, newsum, del = 0; |
358 int oldsum, newsum, del = 0; | 354 char *name; |
359 char *name; | 355 |
360 | 356 oldsum = TT.oldline + TT.oldlen; |
361 oldsum = TT.oldline + TT.oldlen; | 357 newsum = TT.newline + TT.newlen; |
362 newsum = TT.newline + TT.newlen; | 358 |
363 | 359 name = reverse ? oldname : newname; |
364 name = reverse ? oldname : newname; | 360 |
365 | 361 // We're deleting oldname if new file is /dev/null (before -p) |
366 // We're deleting oldname if new file is /dev/null (before -p) | 362 // or if new hunk is empty (zero context) after patching |
367 // or if new hunk is empty (zero context) after patching | 363 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) |
368 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) | 364 { |
369 { | 365 name = reverse ? newname : oldname; |
370 name = reverse ? newname : oldname; | 366 del++; |
371 del++; | 367 } |
372 } | 368 |
373 | 369 // handle -p path truncation. |
374 // handle -p path truncation. | 370 for (i = 0, s = name; *s;) { |
375 for (i = 0, s = name; *s;) { | 371 if ((toys.optflags & FLAG_p) && TT.prefix == i) break; |
376 if ((toys.optflags & FLAG_p) && TT.prefix == i) break; | 372 if (*s++ != '/') continue; |
377 if (*s++ != '/') continue; | 373 while (*s == '/') s++; |
378 while (*s == '/') s++; | 374 name = s; |
379 name = s; | 375 i++; |
380 i++; | 376 } |
381 } | 377 |
382 | 378 if (del) { |
383 if (del) { | 379 printf("removing %s\n", name); |
384 printf("removing %s\n", name); | 380 xunlink(name); |
385 xunlink(name); | 381 state = 0; |
386 state = 0; | 382 // If we've got a file to open, do so. |
387 // If we've got a file to open, do so. | 383 } else if (!(toys.optflags & FLAG_p) || i <= TT.prefix) { |
388 } else if (!(toys.optflags & FLAG_p) || i <= TT.prefix) { | 384 // If the old file was null, we're creating a new one. |
389 // If the old file was null, we're creating a new one. | 385 if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK)) |
390 if ((!strcmp(oldname, "/dev/null") || !oldsum) | 386 { |
391 && access(name, F_OK)) | 387 printf("creating %s\n", name); |
392 { | 388 s = strrchr(name, '/'); |
393 printf("creating %s\n", name); | 389 if (s) { |
394 s = strrchr(name, '/'); | 390 *s = 0; |
395 if (s) { | 391 xmkpath(name, -1); |
396 *s = 0; | 392 *s = '/'; |
397 xmkpath(name, -1); | 393 } |
398 *s = '/'; | 394 TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); |
399 } | 395 } else { |
400 TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); | 396 printf("patching %s\n", name); |
401 } else { | 397 TT.filein = xopen(name, O_RDWR); |
402 printf("patching %s\n", name); | 398 } |
403 TT.filein = xopen(name, O_RDWR); | 399 TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); |
404 } | 400 TT.linenum = 0; |
405 TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); | 401 TT.hunknum = 0; |
406 TT.linenum = 0; | 402 } |
407 TT.hunknum = 0; | 403 } |
408 } | 404 |
409 } | 405 TT.hunknum++; |
410 | 406 |
411 TT.hunknum++; | 407 continue; |
412 | 408 } |
413 continue; | 409 |
414 } | 410 // If we didn't continue above, discard this line. |
415 | 411 free(patchline); |
416 // If we didn't continue above, discard this line. | 412 } |
417 free(patchline); | 413 |
418 } | 414 finish_oldfile(); |
419 | 415 |
420 finish_oldfile(); | 416 if (CFG_TOYBOX_FREE) { |
421 | 417 close(TT.filepatch); |
422 if (CFG_TOYBOX_FREE) { | 418 free(oldname); |
423 close(TT.filepatch); | 419 free(newname); |
424 free(oldname); | 420 } |
425 free(newname); | 421 } |
426 } | |
427 } |