rugged 0.24.6.1 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/ext/rugged/extconf.rb +9 -2
  4. data/ext/rugged/rugged.c +85 -21
  5. data/ext/rugged/rugged.h +7 -21
  6. data/ext/rugged/rugged_backend.c +3 -20
  7. data/ext/rugged/rugged_blame.c +7 -24
  8. data/ext/rugged/rugged_blob.c +136 -59
  9. data/ext/rugged/rugged_branch.c +3 -20
  10. data/ext/rugged/rugged_branch_collection.c +3 -20
  11. data/ext/rugged/rugged_commit.c +251 -101
  12. data/ext/rugged/rugged_config.c +3 -20
  13. data/ext/rugged/rugged_cred.c +3 -20
  14. data/ext/rugged/rugged_diff.c +3 -20
  15. data/ext/rugged/rugged_diff_delta.c +3 -20
  16. data/ext/rugged/rugged_diff_hunk.c +3 -20
  17. data/ext/rugged/rugged_diff_line.c +3 -20
  18. data/ext/rugged/rugged_index.c +46 -229
  19. data/ext/rugged/rugged_note.c +3 -20
  20. data/ext/rugged/rugged_object.c +3 -20
  21. data/ext/rugged/rugged_patch.c +192 -34
  22. data/ext/rugged/rugged_rebase.c +90 -48
  23. data/ext/rugged/rugged_reference.c +4 -21
  24. data/ext/rugged/rugged_reference_collection.c +3 -20
  25. data/ext/rugged/rugged_remote.c +70 -42
  26. data/ext/rugged/rugged_remote_collection.c +3 -20
  27. data/ext/rugged/rugged_repo.c +50 -59
  28. data/ext/rugged/rugged_revwalk.c +4 -21
  29. data/ext/rugged/rugged_settings.c +3 -20
  30. data/ext/rugged/rugged_signature.c +3 -20
  31. data/ext/rugged/rugged_submodule.c +4 -21
  32. data/ext/rugged/rugged_submodule_collection.c +3 -20
  33. data/ext/rugged/rugged_tag.c +3 -20
  34. data/ext/rugged/rugged_tag_collection.c +3 -20
  35. data/ext/rugged/rugged_tree.c +189 -184
  36. data/lib/rugged/attributes.rb +5 -0
  37. data/lib/rugged/blob.rb +5 -0
  38. data/lib/rugged/branch.rb +6 -1
  39. data/lib/rugged/commit.rb +5 -0
  40. data/lib/rugged/console.rb +5 -0
  41. data/lib/rugged/credentials.rb +5 -0
  42. data/lib/rugged/diff/delta.rb +5 -0
  43. data/lib/rugged/diff/hunk.rb +5 -0
  44. data/lib/rugged/diff/line.rb +5 -0
  45. data/lib/rugged/diff.rb +5 -0
  46. data/lib/rugged/index.rb +120 -0
  47. data/lib/rugged/object.rb +5 -0
  48. data/lib/rugged/patch.rb +5 -0
  49. data/lib/rugged/reference.rb +5 -0
  50. data/lib/rugged/remote.rb +5 -0
  51. data/lib/rugged/repository.rb +9 -4
  52. data/lib/rugged/submodule_collection.rb +5 -0
  53. data/lib/rugged/tag.rb +5 -0
  54. data/lib/rugged/tree.rb +156 -1
  55. data/lib/rugged/version.rb +6 -1
  56. data/lib/rugged/walker.rb +5 -0
  57. data/lib/rugged.rb +5 -0
  58. data/vendor/libgit2/CMakeLists.txt +12 -2
  59. data/vendor/libgit2/include/git2/blob.h +39 -28
  60. data/vendor/libgit2/include/git2/commit.h +76 -0
  61. data/vendor/libgit2/include/git2/common.h +21 -1
  62. data/vendor/libgit2/include/git2/describe.h +5 -2
  63. data/vendor/libgit2/include/git2/diff.h +62 -7
  64. data/vendor/libgit2/include/git2/errors.h +2 -1
  65. data/vendor/libgit2/include/git2/index.h +25 -0
  66. data/vendor/libgit2/include/git2/merge.h +10 -1
  67. data/vendor/libgit2/include/git2/odb.h +47 -1
  68. data/vendor/libgit2/include/git2/pack.h +4 -4
  69. data/vendor/libgit2/include/git2/patch.h +1 -1
  70. data/vendor/libgit2/include/git2/proxy.h +92 -0
  71. data/vendor/libgit2/include/git2/refs.h +11 -0
  72. data/vendor/libgit2/include/git2/remote.h +21 -8
  73. data/vendor/libgit2/include/git2/repository.h +20 -1
  74. data/vendor/libgit2/include/git2/revwalk.h +4 -6
  75. data/vendor/libgit2/include/git2/signature.h +13 -0
  76. data/vendor/libgit2/include/git2/submodule.h +11 -3
  77. data/vendor/libgit2/include/git2/sys/merge.h +177 -0
  78. data/vendor/libgit2/include/git2/sys/odb_backend.h +11 -0
  79. data/vendor/libgit2/include/git2/sys/remote.h +16 -0
  80. data/vendor/libgit2/include/git2/sys/stream.h +2 -1
  81. data/vendor/libgit2/include/git2/sys/time.h +31 -0
  82. data/vendor/libgit2/include/git2/sys/transport.h +3 -1
  83. data/vendor/libgit2/include/git2/tag.h +9 -0
  84. data/vendor/libgit2/include/git2/transaction.h +9 -0
  85. data/vendor/libgit2/include/git2/tree.h +55 -0
  86. data/vendor/libgit2/include/git2/version.h +4 -4
  87. data/vendor/libgit2/include/git2.h +1 -0
  88. data/vendor/libgit2/src/annotated_commit.c +99 -80
  89. data/vendor/libgit2/src/annotated_commit.h +5 -2
  90. data/vendor/libgit2/src/apply.c +377 -0
  91. data/vendor/libgit2/src/apply.h +21 -0
  92. data/vendor/libgit2/src/array.h +0 -1
  93. data/vendor/libgit2/src/blob.c +71 -39
  94. data/vendor/libgit2/src/branch.c +7 -5
  95. data/vendor/libgit2/src/buffer.c +252 -20
  96. data/vendor/libgit2/src/buffer.h +8 -0
  97. data/vendor/libgit2/src/checkout.c +69 -42
  98. data/vendor/libgit2/src/clone.c +0 -8
  99. data/vendor/libgit2/src/commit.c +193 -49
  100. data/vendor/libgit2/src/commit_list.c +8 -3
  101. data/vendor/libgit2/src/commit_list.h +1 -0
  102. data/vendor/libgit2/src/common.h +2 -1
  103. data/vendor/libgit2/src/config.c +3 -3
  104. data/vendor/libgit2/src/config_file.c +20 -10
  105. data/vendor/libgit2/src/crlf.c +1 -0
  106. data/vendor/libgit2/src/curl_stream.c +106 -6
  107. data/vendor/libgit2/src/delta.c +238 -62
  108. data/vendor/libgit2/src/delta.h +79 -58
  109. data/vendor/libgit2/src/describe.c +1 -1
  110. data/vendor/libgit2/src/diff.c +32 -1554
  111. data/vendor/libgit2/src/diff.h +14 -122
  112. data/vendor/libgit2/src/diff_driver.c +4 -6
  113. data/vendor/libgit2/src/diff_file.c +3 -0
  114. data/vendor/libgit2/src/diff_generate.c +1613 -0
  115. data/vendor/libgit2/src/diff_generate.h +123 -0
  116. data/vendor/libgit2/src/diff_parse.c +101 -0
  117. data/vendor/libgit2/src/diff_parse.h +18 -0
  118. data/vendor/libgit2/src/diff_print.c +263 -144
  119. data/vendor/libgit2/src/diff_stats.c +21 -12
  120. data/vendor/libgit2/src/diff_tform.c +1 -0
  121. data/vendor/libgit2/src/diff_tform.h +22 -0
  122. data/vendor/libgit2/src/diff_xdiff.c +9 -9
  123. data/vendor/libgit2/src/diff_xdiff.h +5 -5
  124. data/vendor/libgit2/src/fetchhead.c +8 -8
  125. data/vendor/libgit2/src/filebuf.c +6 -1
  126. data/vendor/libgit2/src/filebuf.h +1 -0
  127. data/vendor/libgit2/src/fileops.c +22 -1
  128. data/vendor/libgit2/src/fileops.h +8 -2
  129. data/vendor/libgit2/src/fnmatch.c +18 -5
  130. data/vendor/libgit2/src/global.c +21 -4
  131. data/vendor/libgit2/src/global.h +6 -0
  132. data/vendor/libgit2/src/graph.c +1 -1
  133. data/vendor/libgit2/src/index.c +159 -46
  134. data/vendor/libgit2/src/index.h +2 -0
  135. data/vendor/libgit2/src/iterator.c +1573 -1468
  136. data/vendor/libgit2/src/iterator.h +52 -69
  137. data/vendor/libgit2/src/merge.c +163 -64
  138. data/vendor/libgit2/src/merge.h +61 -2
  139. data/vendor/libgit2/src/merge_driver.c +397 -0
  140. data/vendor/libgit2/src/merge_driver.h +60 -0
  141. data/vendor/libgit2/src/merge_file.c +11 -49
  142. data/vendor/libgit2/src/netops.c +12 -10
  143. data/vendor/libgit2/src/object_api.c +19 -1
  144. data/vendor/libgit2/src/odb.c +228 -52
  145. data/vendor/libgit2/src/odb_loose.c +19 -1
  146. data/vendor/libgit2/src/odb_mempack.c +1 -1
  147. data/vendor/libgit2/src/odb_pack.c +27 -1
  148. data/vendor/libgit2/src/openssl_stream.c +4 -5
  149. data/vendor/libgit2/src/pack-objects.c +105 -76
  150. data/vendor/libgit2/src/pack-objects.h +13 -12
  151. data/vendor/libgit2/src/pack.c +16 -10
  152. data/vendor/libgit2/src/pack.h +2 -0
  153. data/vendor/libgit2/src/patch.c +216 -0
  154. data/vendor/libgit2/src/patch.h +66 -0
  155. data/vendor/libgit2/src/{diff_patch.c → patch_generate.c} +203 -376
  156. data/vendor/libgit2/src/patch_generate.h +68 -0
  157. data/vendor/libgit2/src/patch_parse.c +1159 -0
  158. data/vendor/libgit2/src/patch_parse.h +56 -0
  159. data/vendor/libgit2/src/path.c +38 -2
  160. data/vendor/libgit2/src/path.h +18 -0
  161. data/vendor/libgit2/src/pathspec.c +1 -1
  162. data/vendor/libgit2/src/pool.h +5 -0
  163. data/vendor/libgit2/src/pqueue.c +12 -5
  164. data/vendor/libgit2/src/pqueue.h +1 -0
  165. data/vendor/libgit2/src/proxy.c +32 -0
  166. data/vendor/libgit2/src/proxy.h +14 -0
  167. data/vendor/libgit2/src/push.c +1 -1
  168. data/vendor/libgit2/src/rebase.c +63 -36
  169. data/vendor/libgit2/src/refdb.c +4 -2
  170. data/vendor/libgit2/src/refdb_fs.c +82 -54
  171. data/vendor/libgit2/src/refs.c +13 -1
  172. data/vendor/libgit2/src/remote.c +20 -81
  173. data/vendor/libgit2/src/repository.c +212 -29
  174. data/vendor/libgit2/src/reset.c +1 -1
  175. data/vendor/libgit2/src/revparse.c +1 -1
  176. data/vendor/libgit2/src/revwalk.c +260 -184
  177. data/vendor/libgit2/src/settings.c +11 -3
  178. data/vendor/libgit2/src/signature.c +27 -2
  179. data/vendor/libgit2/src/sortedcache.c +14 -5
  180. data/vendor/libgit2/src/stash.c +1 -0
  181. data/vendor/libgit2/src/status.c +1 -0
  182. data/vendor/libgit2/src/stransport_stream.c +4 -2
  183. data/vendor/libgit2/src/stream.h +2 -2
  184. data/vendor/libgit2/src/submodule.c +16 -4
  185. data/vendor/libgit2/src/sysdir.c +1 -1
  186. data/vendor/libgit2/src/transport.c +3 -5
  187. data/vendor/libgit2/src/transports/http.c +38 -13
  188. data/vendor/libgit2/src/transports/local.c +4 -1
  189. data/vendor/libgit2/src/transports/smart.c +6 -0
  190. data/vendor/libgit2/src/transports/smart.h +1 -0
  191. data/vendor/libgit2/src/transports/smart_pkt.c +5 -13
  192. data/vendor/libgit2/src/transports/smart_protocol.c +22 -7
  193. data/vendor/libgit2/src/transports/winhttp.c +144 -11
  194. data/vendor/libgit2/src/tree.c +267 -2
  195. data/vendor/libgit2/src/unix/posix.h +10 -0
  196. data/vendor/libgit2/src/unix/pthread.h +2 -0
  197. data/vendor/libgit2/src/util.c +25 -2
  198. data/vendor/libgit2/src/util.h +10 -0
  199. data/vendor/libgit2/src/varint.c +44 -0
  200. data/vendor/libgit2/src/varint.h +15 -0
  201. data/vendor/libgit2/src/vector.c +58 -0
  202. data/vendor/libgit2/src/vector.h +8 -0
  203. data/vendor/libgit2/src/win32/posix.h +3 -0
  204. data/vendor/libgit2/src/win32/thread.c +18 -0
  205. data/vendor/libgit2/src/win32/thread.h +2 -0
  206. data/vendor/libgit2/src/win32/w32_util.h +1 -1
  207. data/vendor/libgit2/src/zstream.c +37 -8
  208. data/vendor/libgit2/src/zstream.h +8 -1
  209. metadata +100 -82
  210. data/vendor/libgit2/Makefile.embed +0 -60
  211. data/vendor/libgit2/src/delta-apply.c +0 -166
  212. data/vendor/libgit2/src/delta-apply.h +0 -62
  213. data/vendor/libgit2/src/diff_patch.h +0 -83
@@ -0,0 +1,1159 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #include "git2/patch.h"
8
+ #include "patch.h"
9
+ #include "patch_parse.h"
10
+ #include "diff_parse.h"
11
+ #include "path.h"
12
+
13
+ #define parse_err(...) \
14
+ ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
15
+
16
+ typedef struct {
17
+ git_patch base;
18
+
19
+ git_patch_parse_ctx *ctx;
20
+
21
+ /* the paths from the `diff --git` header, these will be used if this is not
22
+ * a rename (and rename paths are specified) or if no `+++`/`---` line specify
23
+ * the paths.
24
+ */
25
+ char *header_old_path, *header_new_path;
26
+
27
+ /* renamed paths are precise and are not prefixed */
28
+ char *rename_old_path, *rename_new_path;
29
+
30
+ /* the paths given in `---` and `+++` lines */
31
+ char *old_path, *new_path;
32
+
33
+ /* the prefixes from the old/new paths */
34
+ char *old_prefix, *new_prefix;
35
+ } git_patch_parsed;
36
+
37
+
38
+ GIT_INLINE(bool) parse_ctx_contains(
39
+ git_patch_parse_ctx *ctx, const char *str, size_t len)
40
+ {
41
+ return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
42
+ }
43
+
44
+ #define parse_ctx_contains_s(ctx, str) \
45
+ parse_ctx_contains(ctx, str, sizeof(str) - 1)
46
+
47
+ static void parse_advance_line(git_patch_parse_ctx *ctx)
48
+ {
49
+ ctx->line += ctx->line_len;
50
+ ctx->remain_len -= ctx->line_len;
51
+ ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
52
+ ctx->line_num++;
53
+ }
54
+
55
+ static void parse_advance_chars(git_patch_parse_ctx *ctx, size_t char_cnt)
56
+ {
57
+ ctx->line += char_cnt;
58
+ ctx->remain_len -= char_cnt;
59
+ ctx->line_len -= char_cnt;
60
+ }
61
+
62
+ static int parse_advance_expected(
63
+ git_patch_parse_ctx *ctx,
64
+ const char *expected,
65
+ size_t expected_len)
66
+ {
67
+ if (ctx->line_len < expected_len)
68
+ return -1;
69
+
70
+ if (memcmp(ctx->line, expected, expected_len) != 0)
71
+ return -1;
72
+
73
+ parse_advance_chars(ctx, expected_len);
74
+ return 0;
75
+ }
76
+
77
+ #define parse_advance_expected_str(ctx, str) \
78
+ parse_advance_expected(ctx, str, strlen(str))
79
+
80
+ static int parse_advance_ws(git_patch_parse_ctx *ctx)
81
+ {
82
+ int ret = -1;
83
+
84
+ while (ctx->line_len > 0 &&
85
+ ctx->line[0] != '\n' &&
86
+ git__isspace(ctx->line[0])) {
87
+ ctx->line++;
88
+ ctx->line_len--;
89
+ ctx->remain_len--;
90
+ ret = 0;
91
+ }
92
+
93
+ return ret;
94
+ }
95
+
96
+ static int parse_advance_nl(git_patch_parse_ctx *ctx)
97
+ {
98
+ if (ctx->line_len != 1 || ctx->line[0] != '\n')
99
+ return -1;
100
+
101
+ parse_advance_line(ctx);
102
+ return 0;
103
+ }
104
+
105
+ static int header_path_len(git_patch_parse_ctx *ctx)
106
+ {
107
+ bool inquote = 0;
108
+ bool quoted = (ctx->line_len > 0 && ctx->line[0] == '"');
109
+ size_t len;
110
+
111
+ for (len = quoted; len < ctx->line_len; len++) {
112
+ if (!quoted && git__isspace(ctx->line[len]))
113
+ break;
114
+ else if (quoted && !inquote && ctx->line[len] == '"') {
115
+ len++;
116
+ break;
117
+ }
118
+
119
+ inquote = (!inquote && ctx->line[len] == '\\');
120
+ }
121
+
122
+ return len;
123
+ }
124
+
125
+ static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx)
126
+ {
127
+ int path_len, error = 0;
128
+
129
+ path_len = header_path_len(ctx);
130
+
131
+ if ((error = git_buf_put(path, ctx->line, path_len)) < 0)
132
+ goto done;
133
+
134
+ parse_advance_chars(ctx, path_len);
135
+
136
+ git_buf_rtrim(path);
137
+
138
+ if (path->size > 0 && path->ptr[0] == '"')
139
+ error = git_buf_unquote(path);
140
+
141
+ if (error < 0)
142
+ goto done;
143
+
144
+ git_path_squash_slashes(path);
145
+
146
+ done:
147
+ return error;
148
+ }
149
+
150
+ static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
151
+ {
152
+ git_buf path = GIT_BUF_INIT;
153
+ int error = parse_header_path_buf(&path, ctx);
154
+
155
+ *out = git_buf_detach(&path);
156
+
157
+ return error;
158
+ }
159
+
160
+ static int parse_header_git_oldpath(
161
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
162
+ {
163
+ return parse_header_path(&patch->old_path, ctx);
164
+ }
165
+
166
+ static int parse_header_git_newpath(
167
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
168
+ {
169
+ return parse_header_path(&patch->new_path, ctx);
170
+ }
171
+
172
+ static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
173
+ {
174
+ const char *end;
175
+ int32_t m;
176
+ int ret;
177
+
178
+ if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
179
+ return parse_err("invalid file mode at line %"PRIuZ, ctx->line_num);
180
+
181
+ if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)
182
+ return ret;
183
+
184
+ if (m > UINT16_MAX)
185
+ return -1;
186
+
187
+ *mode = (uint16_t)m;
188
+
189
+ parse_advance_chars(ctx, (end - ctx->line));
190
+
191
+ return ret;
192
+ }
193
+
194
+ static int parse_header_oid(
195
+ git_oid *oid,
196
+ uint16_t *oid_len,
197
+ git_patch_parse_ctx *ctx)
198
+ {
199
+ size_t len;
200
+
201
+ for (len = 0; len < ctx->line_len && len < GIT_OID_HEXSZ; len++) {
202
+ if (!git__isxdigit(ctx->line[len]))
203
+ break;
204
+ }
205
+
206
+ if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
207
+ git_oid_fromstrn(oid, ctx->line, len) < 0)
208
+ return parse_err("invalid hex formatted object id at line %"PRIuZ,
209
+ ctx->line_num);
210
+
211
+ parse_advance_chars(ctx, len);
212
+
213
+ *oid_len = (uint16_t)len;
214
+
215
+ return 0;
216
+ }
217
+
218
+ static int parse_header_git_index(
219
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
220
+ {
221
+ if (parse_header_oid(&patch->base.delta->old_file.id,
222
+ &patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
223
+ parse_advance_expected_str(ctx, "..") < 0 ||
224
+ parse_header_oid(&patch->base.delta->new_file.id,
225
+ &patch->base.delta->new_file.id_abbrev, ctx) < 0)
226
+ return -1;
227
+
228
+ if (ctx->line_len > 0 && ctx->line[0] == ' ') {
229
+ uint16_t mode;
230
+
231
+ parse_advance_chars(ctx, 1);
232
+
233
+ if (parse_header_mode(&mode, ctx) < 0)
234
+ return -1;
235
+
236
+ if (!patch->base.delta->new_file.mode)
237
+ patch->base.delta->new_file.mode = mode;
238
+
239
+ if (!patch->base.delta->old_file.mode)
240
+ patch->base.delta->old_file.mode = mode;
241
+ }
242
+
243
+ return 0;
244
+ }
245
+
246
+ static int parse_header_git_oldmode(
247
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
248
+ {
249
+ return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
250
+ }
251
+
252
+ static int parse_header_git_newmode(
253
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
254
+ {
255
+ return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
256
+ }
257
+
258
+ static int parse_header_git_deletedfilemode(
259
+ git_patch_parsed *patch,
260
+ git_patch_parse_ctx *ctx)
261
+ {
262
+ git__free((char *)patch->base.delta->old_file.path);
263
+
264
+ patch->base.delta->old_file.path = NULL;
265
+ patch->base.delta->status = GIT_DELTA_DELETED;
266
+ patch->base.delta->nfiles = 1;
267
+
268
+ return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
269
+ }
270
+
271
+ static int parse_header_git_newfilemode(
272
+ git_patch_parsed *patch,
273
+ git_patch_parse_ctx *ctx)
274
+ {
275
+ git__free((char *)patch->base.delta->new_file.path);
276
+
277
+ patch->base.delta->new_file.path = NULL;
278
+ patch->base.delta->status = GIT_DELTA_ADDED;
279
+ patch->base.delta->nfiles = 1;
280
+
281
+ return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
282
+ }
283
+
284
+ static int parse_header_rename(
285
+ char **out,
286
+ git_patch_parse_ctx *ctx)
287
+ {
288
+ git_buf path = GIT_BUF_INIT;
289
+
290
+ if (parse_header_path_buf(&path, ctx) < 0)
291
+ return -1;
292
+
293
+ /* Note: the `rename from` and `rename to` lines include the literal
294
+ * filename. They do *not* include the prefix. (Who needs consistency?)
295
+ */
296
+ *out = git_buf_detach(&path);
297
+ return 0;
298
+ }
299
+
300
+ static int parse_header_renamefrom(
301
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
302
+ {
303
+ patch->base.delta->status = GIT_DELTA_RENAMED;
304
+ return parse_header_rename(&patch->rename_old_path, ctx);
305
+ }
306
+
307
+ static int parse_header_renameto(
308
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
309
+ {
310
+ patch->base.delta->status = GIT_DELTA_RENAMED;
311
+ return parse_header_rename(&patch->rename_new_path, ctx);
312
+ }
313
+
314
+ static int parse_header_copyfrom(
315
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
316
+ {
317
+ patch->base.delta->status = GIT_DELTA_COPIED;
318
+ return parse_header_rename(&patch->rename_old_path, ctx);
319
+ }
320
+
321
+ static int parse_header_copyto(
322
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
323
+ {
324
+ patch->base.delta->status = GIT_DELTA_COPIED;
325
+ return parse_header_rename(&patch->rename_new_path, ctx);
326
+ }
327
+
328
+ static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
329
+ {
330
+ int32_t val;
331
+ const char *end;
332
+
333
+ if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]) ||
334
+ git__strntol32(&val, ctx->line, ctx->line_len, &end, 10) < 0)
335
+ return -1;
336
+
337
+ parse_advance_chars(ctx, (end - ctx->line));
338
+
339
+ if (parse_advance_expected_str(ctx, "%") < 0)
340
+ return -1;
341
+
342
+ if (val > 100)
343
+ return -1;
344
+
345
+ *out = val;
346
+ return 0;
347
+ }
348
+
349
+ static int parse_header_similarity(
350
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
351
+ {
352
+ if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
353
+ return parse_err("invalid similarity percentage at line %"PRIuZ,
354
+ ctx->line_num);
355
+
356
+ return 0;
357
+ }
358
+
359
+ static int parse_header_dissimilarity(
360
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
361
+ {
362
+ uint16_t dissimilarity;
363
+
364
+ if (parse_header_percent(&dissimilarity, ctx) < 0)
365
+ return parse_err("invalid similarity percentage at line %"PRIuZ,
366
+ ctx->line_num);
367
+
368
+ patch->base.delta->similarity = 100 - dissimilarity;
369
+
370
+ return 0;
371
+ }
372
+
373
+ typedef struct {
374
+ const char *str;
375
+ int(*fn)(git_patch_parsed *, git_patch_parse_ctx *);
376
+ } header_git_op;
377
+
378
+ static const header_git_op header_git_ops[] = {
379
+ { "diff --git ", NULL },
380
+ { "@@ -", NULL },
381
+ { "GIT binary patch", NULL },
382
+ { "Binary files ", NULL },
383
+ { "--- ", parse_header_git_oldpath },
384
+ { "+++ ", parse_header_git_newpath },
385
+ { "index ", parse_header_git_index },
386
+ { "old mode ", parse_header_git_oldmode },
387
+ { "new mode ", parse_header_git_newmode },
388
+ { "deleted file mode ", parse_header_git_deletedfilemode },
389
+ { "new file mode ", parse_header_git_newfilemode },
390
+ { "rename from ", parse_header_renamefrom },
391
+ { "rename to ", parse_header_renameto },
392
+ { "rename old ", parse_header_renamefrom },
393
+ { "rename new ", parse_header_renameto },
394
+ { "copy from ", parse_header_copyfrom },
395
+ { "copy to ", parse_header_copyto },
396
+ { "similarity index ", parse_header_similarity },
397
+ { "dissimilarity index ", parse_header_dissimilarity },
398
+ };
399
+
400
+ static int parse_header_git(
401
+ git_patch_parsed *patch,
402
+ git_patch_parse_ctx *ctx)
403
+ {
404
+ size_t i;
405
+ int error = 0;
406
+
407
+ /* Parse the diff --git line */
408
+ if (parse_advance_expected_str(ctx, "diff --git ") < 0)
409
+ return parse_err("corrupt git diff header at line %"PRIuZ, ctx->line_num);
410
+
411
+ if (parse_header_path(&patch->header_old_path, ctx) < 0)
412
+ return parse_err("corrupt old path in git diff header at line %"PRIuZ,
413
+ ctx->line_num);
414
+
415
+ if (parse_advance_ws(ctx) < 0 ||
416
+ parse_header_path(&patch->header_new_path, ctx) < 0)
417
+ return parse_err("corrupt new path in git diff header at line %"PRIuZ,
418
+ ctx->line_num);
419
+
420
+ /* Parse remaining header lines */
421
+ for (parse_advance_line(ctx);
422
+ ctx->remain_len > 0;
423
+ parse_advance_line(ctx)) {
424
+
425
+ bool found = false;
426
+
427
+ if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n')
428
+ break;
429
+
430
+ for (i = 0; i < ARRAY_SIZE(header_git_ops); i++) {
431
+ const header_git_op *op = &header_git_ops[i];
432
+ size_t op_len = strlen(op->str);
433
+
434
+ if (memcmp(ctx->line, op->str, min(op_len, ctx->line_len)) != 0)
435
+ continue;
436
+
437
+ /* Do not advance if this is the patch separator */
438
+ if (op->fn == NULL)
439
+ goto done;
440
+
441
+ parse_advance_chars(ctx, op_len);
442
+
443
+ if ((error = op->fn(patch, ctx)) < 0)
444
+ goto done;
445
+
446
+ parse_advance_ws(ctx);
447
+ parse_advance_expected_str(ctx, "\n");
448
+
449
+ if (ctx->line_len > 0) {
450
+ error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
451
+ goto done;
452
+ }
453
+
454
+ found = true;
455
+ break;
456
+ }
457
+
458
+ if (!found) {
459
+ error = parse_err("invalid patch header at line %"PRIuZ,
460
+ ctx->line_num);
461
+ goto done;
462
+ }
463
+ }
464
+
465
+ done:
466
+ return error;
467
+ }
468
+
469
+ static int parse_number(git_off_t *out, git_patch_parse_ctx *ctx)
470
+ {
471
+ const char *end;
472
+ int64_t num;
473
+
474
+ if (!git__isdigit(ctx->line[0]))
475
+ return -1;
476
+
477
+ if (git__strntol64(&num, ctx->line, ctx->line_len, &end, 10) < 0)
478
+ return -1;
479
+
480
+ if (num < 0)
481
+ return -1;
482
+
483
+ *out = num;
484
+ parse_advance_chars(ctx, (end - ctx->line));
485
+
486
+ return 0;
487
+ }
488
+
489
+ static int parse_int(int *out, git_patch_parse_ctx *ctx)
490
+ {
491
+ git_off_t num;
492
+
493
+ if (parse_number(&num, ctx) < 0 || !git__is_int(num))
494
+ return -1;
495
+
496
+ *out = (int)num;
497
+ return 0;
498
+ }
499
+
500
+ static int parse_hunk_header(
501
+ git_patch_hunk *hunk,
502
+ git_patch_parse_ctx *ctx)
503
+ {
504
+ const char *header_start = ctx->line;
505
+
506
+ hunk->hunk.old_lines = 1;
507
+ hunk->hunk.new_lines = 1;
508
+
509
+ if (parse_advance_expected_str(ctx, "@@ -") < 0 ||
510
+ parse_int(&hunk->hunk.old_start, ctx) < 0)
511
+ goto fail;
512
+
513
+ if (ctx->line_len > 0 && ctx->line[0] == ',') {
514
+ if (parse_advance_expected_str(ctx, ",") < 0 ||
515
+ parse_int(&hunk->hunk.old_lines, ctx) < 0)
516
+ goto fail;
517
+ }
518
+
519
+ if (parse_advance_expected_str(ctx, " +") < 0 ||
520
+ parse_int(&hunk->hunk.new_start, ctx) < 0)
521
+ goto fail;
522
+
523
+ if (ctx->line_len > 0 && ctx->line[0] == ',') {
524
+ if (parse_advance_expected_str(ctx, ",") < 0 ||
525
+ parse_int(&hunk->hunk.new_lines, ctx) < 0)
526
+ goto fail;
527
+ }
528
+
529
+ if (parse_advance_expected_str(ctx, " @@") < 0)
530
+ goto fail;
531
+
532
+ parse_advance_line(ctx);
533
+
534
+ if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
535
+ goto fail;
536
+
537
+ hunk->hunk.header_len = ctx->line - header_start;
538
+ if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
539
+ return parse_err("oversized patch hunk header at line %"PRIuZ,
540
+ ctx->line_num);
541
+
542
+ memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
543
+ hunk->hunk.header[hunk->hunk.header_len] = '\0';
544
+
545
+ return 0;
546
+
547
+ fail:
548
+ giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ,
549
+ ctx->line_num);
550
+ return -1;
551
+ }
552
+
553
+ static int parse_hunk_body(
554
+ git_patch_parsed *patch,
555
+ git_patch_hunk *hunk,
556
+ git_patch_parse_ctx *ctx)
557
+ {
558
+ git_diff_line *line;
559
+ int error = 0;
560
+
561
+ int oldlines = hunk->hunk.old_lines;
562
+ int newlines = hunk->hunk.new_lines;
563
+
564
+ for (;
565
+ ctx->remain_len > 4 && (oldlines || newlines) &&
566
+ memcmp(ctx->line, "@@ -", 4) != 0;
567
+ parse_advance_line(ctx)) {
568
+
569
+ int origin;
570
+ int prefix = 1;
571
+
572
+ if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') {
573
+ error = parse_err("invalid patch instruction at line %"PRIuZ,
574
+ ctx->line_num);
575
+ goto done;
576
+ }
577
+
578
+ switch (ctx->line[0]) {
579
+ case '\n':
580
+ prefix = 0;
581
+
582
+ case ' ':
583
+ origin = GIT_DIFF_LINE_CONTEXT;
584
+ oldlines--;
585
+ newlines--;
586
+ break;
587
+
588
+ case '-':
589
+ origin = GIT_DIFF_LINE_DELETION;
590
+ oldlines--;
591
+ break;
592
+
593
+ case '+':
594
+ origin = GIT_DIFF_LINE_ADDITION;
595
+ newlines--;
596
+ break;
597
+
598
+ default:
599
+ error = parse_err("invalid patch hunk at line %"PRIuZ, ctx->line_num);
600
+ goto done;
601
+ }
602
+
603
+ line = git_array_alloc(patch->base.lines);
604
+ GITERR_CHECK_ALLOC(line);
605
+
606
+ memset(line, 0x0, sizeof(git_diff_line));
607
+
608
+ line->content = ctx->line + prefix;
609
+ line->content_len = ctx->line_len - prefix;
610
+ line->content_offset = ctx->content_len - ctx->remain_len;
611
+ line->origin = origin;
612
+
613
+ hunk->line_count++;
614
+ }
615
+
616
+ if (oldlines || newlines) {
617
+ error = parse_err(
618
+ "invalid patch hunk, expected %d old lines and %d new lines",
619
+ hunk->hunk.old_lines, hunk->hunk.new_lines);
620
+ goto done;
621
+ }
622
+
623
+ /* Handle "". Only expect the leading
624
+ * backslash, though, because the rest of the string could be
625
+ * localized. Because `diff` optimizes for the case where you
626
+ * want to apply the patch by hand.
627
+ */
628
+ if (parse_ctx_contains_s(ctx, "\\ ") &&
629
+ git_array_size(patch->base.lines) > 0) {
630
+
631
+ line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
632
+
633
+ if (line->content_len < 1) {
634
+ error = parse_err("cannot trim trailing newline of empty line");
635
+ goto done;
636
+ }
637
+
638
+ line->content_len--;
639
+
640
+ parse_advance_line(ctx);
641
+ }
642
+
643
+ done:
644
+ return error;
645
+ }
646
+
647
+ static int parse_patch_header(
648
+ git_patch_parsed *patch,
649
+ git_patch_parse_ctx *ctx)
650
+ {
651
+ int error = 0;
652
+
653
+ for (ctx->line = ctx->remain;
654
+ ctx->remain_len > 0;
655
+ parse_advance_line(ctx)) {
656
+
657
+ /* This line is too short to be a patch header. */
658
+ if (ctx->line_len < 6)
659
+ continue;
660
+
661
+ /* This might be a hunk header without a patch header, provide a
662
+ * sensible error message. */
663
+ if (parse_ctx_contains_s(ctx, "@@ -")) {
664
+ size_t line_num = ctx->line_num;
665
+ git_patch_hunk hunk;
666
+
667
+ /* If this cannot be parsed as a hunk header, it's just leading
668
+ * noise, continue.
669
+ */
670
+ if (parse_hunk_header(&hunk, ctx) < 0) {
671
+ giterr_clear();
672
+ continue;
673
+ }
674
+
675
+ error = parse_err("invalid hunk header outside patch at line %"PRIuZ,
676
+ line_num);
677
+ goto done;
678
+ }
679
+
680
+ /* This buffer is too short to contain a patch. */
681
+ if (ctx->remain_len < ctx->line_len + 6)
682
+ break;
683
+
684
+ /* A proper git patch */
685
+ if (parse_ctx_contains_s(ctx, "diff --git ")) {
686
+ error = parse_header_git(patch, ctx);
687
+ goto done;
688
+ }
689
+
690
+ error = 0;
691
+ continue;
692
+ }
693
+
694
+ giterr_set(GITERR_PATCH, "no patch found");
695
+ error = GIT_ENOTFOUND;
696
+
697
+ done:
698
+ return error;
699
+ }
700
+
701
+ static int parse_patch_binary_side(
702
+ git_diff_binary_file *binary,
703
+ git_patch_parse_ctx *ctx)
704
+ {
705
+ git_diff_binary_t type = GIT_DIFF_BINARY_NONE;
706
+ git_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT;
707
+ git_off_t len;
708
+ int error = 0;
709
+
710
+ if (parse_ctx_contains_s(ctx, "literal ")) {
711
+ type = GIT_DIFF_BINARY_LITERAL;
712
+ parse_advance_chars(ctx, 8);
713
+ } else if (parse_ctx_contains_s(ctx, "delta ")) {
714
+ type = GIT_DIFF_BINARY_DELTA;
715
+ parse_advance_chars(ctx, 6);
716
+ } else {
717
+ error = parse_err(
718
+ "unknown binary delta type at line %"PRIuZ, ctx->line_num);
719
+ goto done;
720
+ }
721
+
722
+ if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {
723
+ error = parse_err("invalid binary size at line %"PRIuZ, ctx->line_num);
724
+ goto done;
725
+ }
726
+
727
+ while (ctx->line_len) {
728
+ char c = ctx->line[0];
729
+ size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;
730
+
731
+ if (c == '\n')
732
+ break;
733
+ else if (c >= 'A' && c <= 'Z')
734
+ decoded_len = c - 'A' + 1;
735
+ else if (c >= 'a' && c <= 'z')
736
+ decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
737
+
738
+ if (!decoded_len) {
739
+ error = parse_err("invalid binary length at line %"PRIuZ, ctx->line_num);
740
+ goto done;
741
+ }
742
+
743
+ parse_advance_chars(ctx, 1);
744
+
745
+ encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
746
+
747
+ if (encoded_len > ctx->line_len - 1) {
748
+ error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
749
+ goto done;
750
+ }
751
+
752
+ if ((error = git_buf_decode_base85(
753
+ &decoded, ctx->line, encoded_len, decoded_len)) < 0)
754
+ goto done;
755
+
756
+ if (decoded.size - decoded_orig != decoded_len) {
757
+ error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
758
+ goto done;
759
+ }
760
+
761
+ parse_advance_chars(ctx, encoded_len);
762
+
763
+ if (parse_advance_nl(ctx) < 0) {
764
+ error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
765
+ goto done;
766
+ }
767
+ }
768
+
769
+ binary->type = type;
770
+ binary->inflatedlen = (size_t)len;
771
+ binary->datalen = decoded.size;
772
+ binary->data = git_buf_detach(&decoded);
773
+
774
+ done:
775
+ git_buf_free(&base85);
776
+ git_buf_free(&decoded);
777
+ return error;
778
+ }
779
+
780
+ static int parse_patch_binary(
781
+ git_patch_parsed *patch,
782
+ git_patch_parse_ctx *ctx)
783
+ {
784
+ int error;
785
+
786
+ if (parse_advance_expected_str(ctx, "GIT binary patch") < 0 ||
787
+ parse_advance_nl(ctx) < 0)
788
+ return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
789
+
790
+ /* parse old->new binary diff */
791
+ if ((error = parse_patch_binary_side(
792
+ &patch->base.binary.new_file, ctx)) < 0)
793
+ return error;
794
+
795
+ if (parse_advance_nl(ctx) < 0)
796
+ return parse_err("corrupt git binary separator at line %"PRIuZ,
797
+ ctx->line_num);
798
+
799
+ /* parse new->old binary diff */
800
+ if ((error = parse_patch_binary_side(
801
+ &patch->base.binary.old_file, ctx)) < 0)
802
+ return error;
803
+
804
+ if (parse_advance_nl(ctx) < 0)
805
+ return parse_err("corrupt git binary patch separator at line %"PRIuZ,
806
+ ctx->line_num);
807
+
808
+ patch->base.binary.contains_data = 1;
809
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
810
+ return 0;
811
+ }
812
+
813
+ static int parse_patch_binary_nodata(
814
+ git_patch_parsed *patch,
815
+ git_patch_parse_ctx *ctx)
816
+ {
817
+ if (parse_advance_expected_str(ctx, "Binary files ") < 0 ||
818
+ parse_advance_expected_str(ctx, patch->header_old_path) < 0 ||
819
+ parse_advance_expected_str(ctx, " and ") < 0 ||
820
+ parse_advance_expected_str(ctx, patch->header_new_path) < 0 ||
821
+ parse_advance_expected_str(ctx, " differ") < 0 ||
822
+ parse_advance_nl(ctx) < 0)
823
+ return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
824
+
825
+ patch->base.binary.contains_data = 0;
826
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
827
+ return 0;
828
+ }
829
+
830
+ static int parse_patch_hunks(
831
+ git_patch_parsed *patch,
832
+ git_patch_parse_ctx *ctx)
833
+ {
834
+ git_patch_hunk *hunk;
835
+ int error = 0;
836
+
837
+ while (parse_ctx_contains_s(ctx, "@@ -")) {
838
+ hunk = git_array_alloc(patch->base.hunks);
839
+ GITERR_CHECK_ALLOC(hunk);
840
+
841
+ memset(hunk, 0, sizeof(git_patch_hunk));
842
+
843
+ hunk->line_start = git_array_size(patch->base.lines);
844
+ hunk->line_count = 0;
845
+
846
+ if ((error = parse_hunk_header(hunk, ctx)) < 0 ||
847
+ (error = parse_hunk_body(patch, hunk, ctx)) < 0)
848
+ goto done;
849
+ }
850
+
851
+ patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
852
+
853
+ done:
854
+ return error;
855
+ }
856
+
857
+ static int parse_patch_body(
858
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
859
+ {
860
+ if (parse_ctx_contains_s(ctx, "GIT binary patch"))
861
+ return parse_patch_binary(patch, ctx);
862
+ else if (parse_ctx_contains_s(ctx, "Binary files "))
863
+ return parse_patch_binary_nodata(patch, ctx);
864
+ else
865
+ return parse_patch_hunks(patch, ctx);
866
+ }
867
+
868
+ int check_header_names(
869
+ const char *one,
870
+ const char *two,
871
+ const char *old_or_new,
872
+ bool two_null)
873
+ {
874
+ if (!one || !two)
875
+ return 0;
876
+
877
+ if (two_null && strcmp(two, "/dev/null") != 0)
878
+ return parse_err("expected %s path of '/dev/null'", old_or_new);
879
+
880
+ else if (!two_null && strcmp(one, two) != 0)
881
+ return parse_err("mismatched %s path names", old_or_new);
882
+
883
+ return 0;
884
+ }
885
+
886
+ static int check_prefix(
887
+ char **out,
888
+ size_t *out_len,
889
+ git_patch_parsed *patch,
890
+ const char *path_start)
891
+ {
892
+ const char *path = path_start;
893
+ size_t prefix_len = patch->ctx->opts.prefix_len;
894
+ size_t remain_len = prefix_len;
895
+
896
+ *out = NULL;
897
+ *out_len = 0;
898
+
899
+ if (prefix_len == 0)
900
+ goto done;
901
+
902
+ /* leading slashes do not count as part of the prefix in git apply */
903
+ while (*path == '/')
904
+ path++;
905
+
906
+ while (*path && remain_len) {
907
+ if (*path == '/')
908
+ remain_len--;
909
+
910
+ path++;
911
+ }
912
+
913
+ if (remain_len || !*path)
914
+ return parse_err(
915
+ "header filename does not contain %"PRIuZ" path components",
916
+ prefix_len);
917
+
918
+ done:
919
+ *out_len = (path - path_start);
920
+ *out = git__strndup(path_start, *out_len);
921
+
922
+ return (*out == NULL) ? -1 : 0;
923
+ }
924
+
925
+ static int check_filenames(git_patch_parsed *patch)
926
+ {
927
+ const char *prefixed_new, *prefixed_old;
928
+ size_t old_prefixlen = 0, new_prefixlen = 0;
929
+ bool added = (patch->base.delta->status == GIT_DELTA_ADDED);
930
+ bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
931
+
932
+ if (patch->old_path && !patch->new_path)
933
+ return parse_err("missing new path");
934
+
935
+ if (!patch->old_path && patch->new_path)
936
+ return parse_err("missing old path");
937
+
938
+ /* Ensure (non-renamed) paths match */
939
+ if (check_header_names(
940
+ patch->header_old_path, patch->old_path, "old", added) < 0 ||
941
+ check_header_names(
942
+ patch->header_new_path, patch->new_path, "new", deleted) < 0)
943
+ return -1;
944
+
945
+ prefixed_old = (!added && patch->old_path) ? patch->old_path :
946
+ patch->header_old_path;
947
+ prefixed_new = (!deleted && patch->new_path) ? patch->new_path :
948
+ patch->header_new_path;
949
+
950
+ if (check_prefix(
951
+ &patch->old_prefix, &old_prefixlen, patch, prefixed_old) < 0 ||
952
+ check_prefix(
953
+ &patch->new_prefix, &new_prefixlen, patch, prefixed_new) < 0)
954
+ return -1;
955
+
956
+ /* Prefer the rename filenames as they are unambiguous and unprefixed */
957
+ if (patch->rename_old_path)
958
+ patch->base.delta->old_file.path = patch->rename_old_path;
959
+ else
960
+ patch->base.delta->old_file.path = prefixed_old + old_prefixlen;
961
+
962
+ if (patch->rename_new_path)
963
+ patch->base.delta->new_file.path = patch->rename_new_path;
964
+ else
965
+ patch->base.delta->new_file.path = prefixed_new + new_prefixlen;
966
+
967
+ if (!patch->base.delta->old_file.path &&
968
+ !patch->base.delta->new_file.path)
969
+ return parse_err("git diff header lacks old / new paths");
970
+
971
+ return 0;
972
+ }
973
+
974
+ static int check_patch(git_patch_parsed *patch)
975
+ {
976
+ git_diff_delta *delta = patch->base.delta;
977
+
978
+ if (check_filenames(patch) < 0)
979
+ return -1;
980
+
981
+ if (delta->old_file.path &&
982
+ delta->status != GIT_DELTA_DELETED &&
983
+ !delta->new_file.mode)
984
+ delta->new_file.mode = delta->old_file.mode;
985
+
986
+ if (delta->status == GIT_DELTA_MODIFIED &&
987
+ !(delta->flags & GIT_DIFF_FLAG_BINARY) &&
988
+ delta->new_file.mode == delta->old_file.mode &&
989
+ git_array_size(patch->base.hunks) == 0)
990
+ return parse_err("patch with no hunks");
991
+
992
+ if (delta->status == GIT_DELTA_ADDED) {
993
+ memset(&delta->old_file.id, 0x0, sizeof(git_oid));
994
+ delta->old_file.id_abbrev = 0;
995
+ }
996
+
997
+ if (delta->status == GIT_DELTA_DELETED) {
998
+ memset(&delta->new_file.id, 0x0, sizeof(git_oid));
999
+ delta->new_file.id_abbrev = 0;
1000
+ }
1001
+
1002
+ return 0;
1003
+ }
1004
+
1005
+ git_patch_parse_ctx *git_patch_parse_ctx_init(
1006
+ const char *content,
1007
+ size_t content_len,
1008
+ const git_patch_options *opts)
1009
+ {
1010
+ git_patch_parse_ctx *ctx;
1011
+ git_patch_options default_opts = GIT_PATCH_OPTIONS_INIT;
1012
+
1013
+ if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
1014
+ return NULL;
1015
+
1016
+ if (content_len) {
1017
+ if ((ctx->content = git__malloc(content_len)) == NULL) {
1018
+ git__free(ctx);
1019
+ return NULL;
1020
+ }
1021
+
1022
+ memcpy((char *)ctx->content, content, content_len);
1023
+ }
1024
+
1025
+ ctx->content_len = content_len;
1026
+ ctx->remain = ctx->content;
1027
+ ctx->remain_len = ctx->content_len;
1028
+
1029
+ if (opts)
1030
+ memcpy(&ctx->opts, opts, sizeof(git_patch_options));
1031
+ else
1032
+ memcpy(&ctx->opts, &default_opts, sizeof(git_patch_options));
1033
+
1034
+ GIT_REFCOUNT_INC(ctx);
1035
+ return ctx;
1036
+ }
1037
+
1038
+ static void patch_parse_ctx_free(git_patch_parse_ctx *ctx)
1039
+ {
1040
+ if (!ctx)
1041
+ return;
1042
+
1043
+ git__free((char *)ctx->content);
1044
+ git__free(ctx);
1045
+ }
1046
+
1047
+ void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx)
1048
+ {
1049
+ GIT_REFCOUNT_DEC(ctx, patch_parse_ctx_free);
1050
+ }
1051
+
1052
+ int git_patch_parsed_from_diff(git_patch **out, git_diff *d, size_t idx)
1053
+ {
1054
+ git_diff_parsed *diff = (git_diff_parsed *)d;
1055
+ git_patch *p;
1056
+
1057
+ if ((p = git_vector_get(&diff->patches, idx)) == NULL)
1058
+ return -1;
1059
+
1060
+ GIT_REFCOUNT_INC(p);
1061
+ *out = p;
1062
+
1063
+ return 0;
1064
+ }
1065
+
1066
+ static void patch_parsed__free(git_patch *p)
1067
+ {
1068
+ git_patch_parsed *patch = (git_patch_parsed *)p;
1069
+
1070
+ if (!patch)
1071
+ return;
1072
+
1073
+ git_patch_parse_ctx_free(patch->ctx);
1074
+
1075
+ git__free((char *)patch->base.binary.old_file.data);
1076
+ git__free((char *)patch->base.binary.new_file.data);
1077
+ git_array_clear(patch->base.hunks);
1078
+ git_array_clear(patch->base.lines);
1079
+ git__free(patch->base.delta);
1080
+
1081
+ git__free(patch->old_prefix);
1082
+ git__free(patch->new_prefix);
1083
+ git__free(patch->header_old_path);
1084
+ git__free(patch->header_new_path);
1085
+ git__free(patch->rename_old_path);
1086
+ git__free(patch->rename_new_path);
1087
+ git__free(patch->old_path);
1088
+ git__free(patch->new_path);
1089
+ git__free(patch);
1090
+ }
1091
+
1092
+ int git_patch_parse(
1093
+ git_patch **out,
1094
+ git_patch_parse_ctx *ctx)
1095
+ {
1096
+ git_patch_parsed *patch;
1097
+ size_t start, used;
1098
+ int error = 0;
1099
+
1100
+ assert(out && ctx);
1101
+
1102
+ *out = NULL;
1103
+
1104
+ patch = git__calloc(1, sizeof(git_patch_parsed));
1105
+ GITERR_CHECK_ALLOC(patch);
1106
+
1107
+ patch->ctx = ctx;
1108
+ GIT_REFCOUNT_INC(patch->ctx);
1109
+
1110
+ patch->base.free_fn = patch_parsed__free;
1111
+
1112
+ patch->base.delta = git__calloc(1, sizeof(git_diff_delta));
1113
+ GITERR_CHECK_ALLOC(patch->base.delta);
1114
+
1115
+ patch->base.delta->status = GIT_DELTA_MODIFIED;
1116
+ patch->base.delta->nfiles = 2;
1117
+
1118
+ start = ctx->remain_len;
1119
+
1120
+ if ((error = parse_patch_header(patch, ctx)) < 0 ||
1121
+ (error = parse_patch_body(patch, ctx)) < 0 ||
1122
+ (error = check_patch(patch)) < 0)
1123
+ goto done;
1124
+
1125
+ used = start - ctx->remain_len;
1126
+ ctx->remain += used;
1127
+
1128
+ patch->base.diff_opts.old_prefix = patch->old_prefix;
1129
+ patch->base.diff_opts.new_prefix = patch->new_prefix;
1130
+ patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY;
1131
+
1132
+ GIT_REFCOUNT_INC(patch);
1133
+ *out = &patch->base;
1134
+
1135
+ done:
1136
+ if (error < 0)
1137
+ patch_parsed__free(&patch->base);
1138
+
1139
+ return error;
1140
+ }
1141
+
1142
+ int git_patch_from_buffer(
1143
+ git_patch **out,
1144
+ const char *content,
1145
+ size_t content_len,
1146
+ const git_patch_options *opts)
1147
+ {
1148
+ git_patch_parse_ctx *ctx;
1149
+ int error;
1150
+
1151
+ ctx = git_patch_parse_ctx_init(content, content_len, opts);
1152
+ GITERR_CHECK_ALLOC(ctx);
1153
+
1154
+ error = git_patch_parse(out, ctx);
1155
+
1156
+ git_patch_parse_ctx_free(ctx);
1157
+ return error;
1158
+ }
1159
+