]>
Commit | Line | Data |
---|---|---|
f8758044 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
f8758044 | 3 | * |
bb742ede VM |
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. | |
f8758044 VM |
6 | */ |
7 | ||
8 | #include "common.h" | |
9 | #include "commit.h" | |
10 | #include "tag.h" | |
638c2ca4 | 11 | #include "signature.h" |
458b9450 | 12 | #include "message.h" |
44908fe7 VM |
13 | #include "git2/object.h" |
14 | #include "git2/repository.h" | |
638c2ca4 | 15 | #include "git2/signature.h" |
83cc70d9 | 16 | #include "git2/odb_backend.h" |
f8758044 | 17 | |
78606263 | 18 | void git_tag__free(void *_tag) |
f8758044 | 19 | { |
78606263 | 20 | git_tag *tag = _tag; |
638c2ca4 | 21 | git_signature_free(tag->tagger); |
3286c408 VM |
22 | git__free(tag->message); |
23 | git__free(tag->tag_name); | |
24 | git__free(tag); | |
f8758044 VM |
25 | } |
26 | ||
d9023dbe | 27 | int git_tag_target(git_object **target, const git_tag *t) |
f8758044 | 28 | { |
58519018 | 29 | assert(t); |
6b2a1941 | 30 | return git_object_lookup(target, t->object.repo, &t->target, t->type); |
f8758044 VM |
31 | } |
32 | ||
d9023dbe | 33 | const git_oid *git_tag_target_id(const git_tag *t) |
ec25391d | 34 | { |
6b2a1941 VM |
35 | assert(t); |
36 | return &t->target; | |
37 | } | |
38 | ||
d9023dbe | 39 | git_otype git_tag_target_type(const git_tag *t) |
f8758044 | 40 | { |
58519018 | 41 | assert(t); |
f8758044 VM |
42 | return t->type; |
43 | } | |
44 | ||
d9023dbe | 45 | const char *git_tag_name(const git_tag *t) |
f8758044 | 46 | { |
58519018 | 47 | assert(t); |
f8758044 VM |
48 | return t->tag_name; |
49 | } | |
50 | ||
d9023dbe | 51 | const git_signature *git_tag_tagger(const git_tag *t) |
f8758044 | 52 | { |
f8758044 VM |
53 | return t->tagger; |
54 | } | |
55 | ||
d9023dbe | 56 | const char *git_tag_message(const git_tag *t) |
f8758044 | 57 | { |
58519018 | 58 | assert(t); |
f8758044 VM |
59 | return t->message; |
60 | } | |
61 | ||
3aa351ea CMN |
62 | static int tag_error(const char *str) |
63 | { | |
64 | giterr_set(GITERR_TAG, "Failed to parse tag. %s", str); | |
65 | return -1; | |
66 | } | |
67 | ||
3f27127d | 68 | static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) |
f8758044 VM |
69 | { |
70 | static const char *tag_types[] = { | |
71 | NULL, "commit\n", "tree\n", "blob\n", "tag\n" | |
72 | }; | |
73 | ||
44ef8b1b | 74 | unsigned int i; |
f1453c59 | 75 | size_t text_len, alloc_len; |
f8758044 VM |
76 | char *search; |
77 | ||
3aa351ea CMN |
78 | if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0) |
79 | return tag_error("Object field invalid"); | |
f8758044 VM |
80 | |
81 | if (buffer + 5 >= buffer_end) | |
3aa351ea | 82 | return tag_error("Object too short"); |
f8758044 VM |
83 | |
84 | if (memcmp(buffer, "type ", 5) != 0) | |
3aa351ea | 85 | return tag_error("Type field not found"); |
f8758044 VM |
86 | buffer += 5; |
87 | ||
88 | tag->type = GIT_OBJ_BAD; | |
89 | ||
90 | for (i = 1; i < ARRAY_SIZE(tag_types); ++i) { | |
91 | size_t type_length = strlen(tag_types[i]); | |
92 | ||
93 | if (buffer + type_length >= buffer_end) | |
3aa351ea | 94 | return tag_error("Object too short"); |
f8758044 VM |
95 | |
96 | if (memcmp(buffer, tag_types[i], type_length) == 0) { | |
97 | tag->type = i; | |
98 | buffer += type_length; | |
99 | break; | |
100 | } | |
101 | } | |
102 | ||
103 | if (tag->type == GIT_OBJ_BAD) | |
3aa351ea | 104 | return tag_error("Invalid object type"); |
f8758044 | 105 | |
f8758044 | 106 | if (buffer + 4 >= buffer_end) |
3aa351ea | 107 | return tag_error("Object too short"); |
f8758044 VM |
108 | |
109 | if (memcmp(buffer, "tag ", 4) != 0) | |
3aa351ea | 110 | return tag_error("Tag field not found"); |
44dc0d26 | 111 | |
f8758044 VM |
112 | buffer += 4; |
113 | ||
114 | search = memchr(buffer, '\n', buffer_end - buffer); | |
115 | if (search == NULL) | |
3aa351ea | 116 | return tag_error("Object too short"); |
f8758044 VM |
117 | |
118 | text_len = search - buffer; | |
119 | ||
f1453c59 ET |
120 | GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1); |
121 | tag->tag_name = git__malloc(alloc_len); | |
3aa351ea | 122 | GITERR_CHECK_ALLOC(tag->tag_name); |
076141a1 | 123 | |
f8758044 VM |
124 | memcpy(tag->tag_name, buffer, text_len); |
125 | tag->tag_name[text_len] = '\0'; | |
126 | ||
127 | buffer = search + 1; | |
128 | ||
15b0bed2 | 129 | tag->tagger = NULL; |
24cb87e2 | 130 | if (buffer < buffer_end && *buffer != '\n') { |
15b0bed2 | 131 | tag->tagger = git__malloc(sizeof(git_signature)); |
3aa351ea | 132 | GITERR_CHECK_ALLOC(tag->tagger); |
15b0bed2 | 133 | |
3aa351ea CMN |
134 | if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0) |
135 | return -1; | |
15b0bed2 | 136 | } |
f8758044 | 137 | |
6bb9fea1 EZ |
138 | tag->message = NULL; |
139 | if (buffer < buffer_end) { | |
140 | if( *buffer != '\n' ) | |
141 | return tag_error("No new line before message"); | |
fbfc7580 | 142 | |
6bb9fea1 | 143 | text_len = buffer_end - ++buffer; |
f8758044 | 144 | |
f1453c59 ET |
145 | GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1); |
146 | tag->message = git__malloc(alloc_len); | |
6bb9fea1 | 147 | GITERR_CHECK_ALLOC(tag->message); |
076141a1 | 148 | |
6bb9fea1 EZ |
149 | memcpy(tag->message, buffer, text_len); |
150 | tag->message[text_len] = '\0'; | |
151 | } | |
f8758044 | 152 | |
3aa351ea | 153 | return 0; |
f8758044 VM |
154 | } |
155 | ||
3f27127d RB |
156 | int git_tag__parse(void *_tag, git_odb_object *odb_obj) |
157 | { | |
158 | git_tag *tag = _tag; | |
159 | const char *buffer = git_odb_object_data(odb_obj); | |
160 | const char *buffer_end = buffer + git_odb_object_size(odb_obj); | |
161 | ||
162 | return tag_parse(tag, buffer, buffer_end); | |
163 | } | |
164 | ||
97769280 RB |
165 | static int retrieve_tag_reference( |
166 | git_reference **tag_reference_out, | |
167 | git_buf *ref_name_out, | |
168 | git_repository *repo, | |
169 | const char *tag_name) | |
ec25391d | 170 | { |
9e680bcc | 171 | git_reference *tag_ref; |
172 | int error; | |
173 | ||
75abd2b9 MS |
174 | *tag_reference_out = NULL; |
175 | ||
3aa351ea CMN |
176 | if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0) |
177 | return -1; | |
97769280 RB |
178 | |
179 | error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr); | |
e172cf08 | 180 | if (error < 0) |
3aa351ea | 181 | return error; /* Be it not foundo or corrupted */ |
9e680bcc | 182 | |
3e3e4631 | 183 | *tag_reference_out = tag_ref; |
9e680bcc | 184 | |
3aa351ea CMN |
185 | return 0; |
186 | } | |
187 | ||
188 | static int retrieve_tag_reference_oid( | |
189 | git_oid *oid, | |
190 | git_buf *ref_name_out, | |
191 | git_repository *repo, | |
192 | const char *tag_name) | |
193 | { | |
194 | if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0) | |
195 | return -1; | |
196 | ||
2508cc66 | 197 | return git_reference_name_to_id(oid, repo, ref_name_out->ptr); |
72a3fe42 | 198 | } |
ec25391d | 199 | |
bfbb5562 | 200 | static int write_tag_annotation( |
201 | git_oid *oid, | |
202 | git_repository *repo, | |
203 | const char *tag_name, | |
204 | const git_object *target, | |
205 | const git_signature *tagger, | |
206 | const char *message) | |
207 | { | |
743a4b3b | 208 | git_buf tag = GIT_BUF_INIT; |
9462c471 | 209 | git_odb *odb; |
bfbb5562 | 210 | |
211 | git_oid__writebuf(&tag, "object ", git_object_id(target)); | |
212 | git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target))); | |
213 | git_buf_printf(&tag, "tag %s\n", tag_name); | |
214 | git_signature__writebuf(&tag, "tagger ", tagger); | |
215 | git_buf_putc(&tag, '\n'); | |
bfbb5562 | 216 | |
743a4b3b | 217 | if (git_buf_puts(&tag, message) < 0) |
3aa351ea | 218 | goto on_error; |
bfbb5562 | 219 | |
3aa351ea CMN |
220 | if (git_repository_odb__weakptr(&odb, repo) < 0) |
221 | goto on_error; | |
9462c471 | 222 | |
3aa351ea CMN |
223 | if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0) |
224 | goto on_error; | |
bfbb5562 | 225 | |
3aa351ea CMN |
226 | git_buf_free(&tag); |
227 | return 0; | |
458b9450 | 228 | |
3aa351ea CMN |
229 | on_error: |
230 | git_buf_free(&tag); | |
458b9450 | 231 | giterr_set(GITERR_OBJECT, "Failed to create tag annotation."); |
3aa351ea | 232 | return -1; |
bfbb5562 | 233 | } |
234 | ||
235 | static int git_tag_create__internal( | |
72a3fe42 VM |
236 | git_oid *oid, |
237 | git_repository *repo, | |
238 | const char *tag_name, | |
d5afc039 | 239 | const git_object *target, |
72a3fe42 | 240 | const git_signature *tagger, |
a50c1458 | 241 | const char *message, |
bfbb5562 | 242 | int allow_ref_overwrite, |
243 | int create_tag_annotation) | |
72a3fe42 | 244 | { |
d5afc039 | 245 | git_reference *new_ref = NULL; |
97769280 | 246 | git_buf ref_name = GIT_BUF_INIT; |
ec25391d | 247 | |
3aa351ea | 248 | int error; |
72a3fe42 | 249 | |
bfbb5562 | 250 | assert(repo && tag_name && target); |
251 | assert(!create_tag_annotation || (tagger && message)); | |
252 | ||
3aa351ea CMN |
253 | if (git_object_owner(target) != repo) { |
254 | giterr_set(GITERR_INVALID, "The given target does not belong to this repository"); | |
255 | return -1; | |
256 | } | |
d5afc039 | 257 | |
3aa351ea | 258 | error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name); |
904b67e6 | 259 | if (error < 0 && error != GIT_ENOTFOUND) |
18d6f120 | 260 | goto cleanup; |
d5afc039 | 261 | |
932d1baf | 262 | /** Ensure the tag name doesn't conflict with an already existing |
b874629b | 263 | * reference unless overwriting has explicitly been requested **/ |
3aa351ea CMN |
264 | if (error == 0 && !allow_ref_overwrite) { |
265 | git_buf_free(&ref_name); | |
266 | giterr_set(GITERR_TAG, "Tag already exists"); | |
904b67e6 | 267 | return GIT_EEXISTS; |
81234673 CMN |
268 | } |
269 | ||
bfbb5562 | 270 | if (create_tag_annotation) { |
3aa351ea CMN |
271 | if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0) |
272 | return -1; | |
bfbb5562 | 273 | } else |
274 | git_oid_cpy(oid, git_object_id(target)); | |
72a3fe42 | 275 | |
659cf202 | 276 | error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL); |
932d1baf | 277 | |
18d6f120 | 278 | cleanup: |
75abd2b9 | 279 | git_reference_free(new_ref); |
97769280 | 280 | git_buf_free(&ref_name); |
97769280 | 281 | return error; |
ec25391d VM |
282 | } |
283 | ||
bfbb5562 | 284 | int git_tag_create( |
3f27127d RB |
285 | git_oid *oid, |
286 | git_repository *repo, | |
287 | const char *tag_name, | |
288 | const git_object *target, | |
289 | const git_signature *tagger, | |
290 | const char *message, | |
291 | int allow_ref_overwrite) | |
bfbb5562 | 292 | { |
293 | return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1); | |
294 | } | |
295 | ||
b81cc1d6 | 296 | int git_tag_annotation_create( |
297 | git_oid *oid, | |
298 | git_repository *repo, | |
299 | const char *tag_name, | |
300 | const git_object *target, | |
301 | const git_signature *tagger, | |
302 | const char *message) | |
303 | { | |
304 | assert(oid && repo && tag_name && target && tagger && message); | |
305 | ||
306 | return write_tag_annotation(oid, repo, tag_name, target, tagger, message); | |
307 | } | |
308 | ||
bfbb5562 | 309 | int git_tag_create_lightweight( |
3f27127d RB |
310 | git_oid *oid, |
311 | git_repository *repo, | |
312 | const char *tag_name, | |
313 | const git_object *target, | |
314 | int allow_ref_overwrite) | |
bfbb5562 | 315 | { |
316 | return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0); | |
317 | } | |
318 | ||
d5afc039 | 319 | int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite) |
7b4a16e2 CMN |
320 | { |
321 | git_tag tag; | |
3aa351ea | 322 | int error; |
9462c471 | 323 | git_odb *odb; |
ac4fcf17 | 324 | git_odb_stream *stream; |
d5afc039 | 325 | git_odb_object *target_obj; |
932d1baf | 326 | |
75abd2b9 | 327 | git_reference *new_ref = NULL; |
97769280 | 328 | git_buf ref_name = GIT_BUF_INIT; |
932d1baf | 329 | |
7b4a16e2 | 330 | assert(oid && buffer); |
932d1baf | 331 | |
7b4a16e2 | 332 | memset(&tag, 0, sizeof(tag)); |
932d1baf | 333 | |
3aa351ea CMN |
334 | if (git_repository_odb__weakptr(&odb, repo) < 0) |
335 | return -1; | |
9462c471 | 336 | |
ac4fcf17 | 337 | /* validate the buffer */ |
3f27127d | 338 | if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0) |
3aa351ea | 339 | return -1; |
d5afc039 VM |
340 | |
341 | /* validate the target */ | |
3aa351ea CMN |
342 | if (git_odb_read(&target_obj, odb, &tag.target) < 0) |
343 | goto on_error; | |
d5afc039 | 344 | |
8842c75f | 345 | if (tag.type != target_obj->cached.type) { |
3aa351ea CMN |
346 | giterr_set(GITERR_TAG, "The type for the given target is invalid"); |
347 | goto on_error; | |
97769280 | 348 | } |
d5afc039 | 349 | |
3aa351ea | 350 | error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name); |
904b67e6 | 351 | if (error < 0 && error != GIT_ENOTFOUND) |
3aa351ea CMN |
352 | goto on_error; |
353 | ||
354 | /* We don't need these objects after this */ | |
355 | git_signature_free(tag.tagger); | |
356 | git__free(tag.tag_name); | |
357 | git__free(tag.message); | |
358 | git_odb_object_free(target_obj); | |
d5afc039 VM |
359 | |
360 | /** Ensure the tag name doesn't conflict with an already existing | |
87d9869f | 361 | * reference unless overwriting has explictly been requested **/ |
3aa351ea CMN |
362 | if (error == 0 && !allow_ref_overwrite) { |
363 | giterr_set(GITERR_TAG, "Tag already exists"); | |
904b67e6 | 364 | return GIT_EEXISTS; |
ac4fcf17 | 365 | } |
932d1baf | 366 | |
ac4fcf17 | 367 | /* write the buffer */ |
a37aa82e RB |
368 | if ((error = git_odb_open_wstream( |
369 | &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0) | |
370 | return error; | |
932d1baf | 371 | |
a37aa82e RB |
372 | if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer)))) |
373 | error = git_odb_stream_finalize_write(oid, stream); | |
932d1baf | 374 | |
376e6c9f | 375 | git_odb_stream_free(stream); |
932d1baf | 376 | |
3aa351ea CMN |
377 | if (error < 0) { |
378 | git_buf_free(&ref_name); | |
a37aa82e | 379 | return error; |
3aa351ea | 380 | } |
d5afc039 | 381 | |
a37aa82e | 382 | error = git_reference_create( |
659cf202 | 383 | &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL); |
932d1baf | 384 | |
75abd2b9 | 385 | git_reference_free(new_ref); |
97769280 RB |
386 | git_buf_free(&ref_name); |
387 | ||
97769280 | 388 | return error; |
3aa351ea CMN |
389 | |
390 | on_error: | |
391 | git_signature_free(tag.tagger); | |
392 | git__free(tag.tag_name); | |
393 | git__free(tag.message); | |
394 | git_odb_object_free(target_obj); | |
395 | return -1; | |
7b4a16e2 CMN |
396 | } |
397 | ||
9e680bcc | 398 | int git_tag_delete(git_repository *repo, const char *tag_name) |
399 | { | |
9e680bcc | 400 | git_reference *tag_ref; |
97769280 | 401 | git_buf ref_name = GIT_BUF_INIT; |
d00d5464 | 402 | int error; |
97769280 RB |
403 | |
404 | error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name); | |
405 | ||
406 | git_buf_free(&ref_name); | |
9e680bcc | 407 | |
3aa351ea | 408 | if (error < 0) |
18d6f120 | 409 | return error; |
9e680bcc | 410 | |
1ad15540 POL |
411 | error = git_reference_delete(tag_ref); |
412 | ||
413 | git_reference_free(tag_ref); | |
9e680bcc | 414 | |
78606263 | 415 | return error; |
f8758044 VM |
416 | } |
417 | ||
2b5af615 | 418 | typedef struct { |
2f05339e MS |
419 | git_repository *repo; |
420 | git_tag_foreach_cb cb; | |
421 | void *cb_data; | |
422 | } tag_cb_data; | |
423 | ||
424 | static int tags_cb(const char *ref, void *data) | |
425 | { | |
dab89f9b | 426 | int error; |
2f05339e MS |
427 | git_oid oid; |
428 | tag_cb_data *d = (tag_cb_data *)data; | |
429 | ||
430 | if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0) | |
431 | return 0; /* no tag */ | |
432 | ||
dab89f9b | 433 | if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { |
26c1cb91 RB |
434 | if ((error = d->cb(ref, &oid, d->cb_data)) != 0) |
435 | giterr_set_after_callback_function(error, "git_tag_foreach"); | |
dab89f9b | 436 | } |
2f05339e | 437 | |
25e0b157 | 438 | return error; |
2f05339e MS |
439 | } |
440 | ||
441 | int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) | |
442 | { | |
443 | tag_cb_data data; | |
444 | ||
445 | assert(repo && cb); | |
446 | ||
447 | data.cb = cb; | |
448 | data.cb_data = cb_data; | |
449 | data.repo = repo; | |
dab89f9b | 450 | |
25e0b157 | 451 | return git_reference_foreach_name(repo, &tags_cb, &data); |
2f05339e MS |
452 | } |
453 | ||
454 | typedef struct { | |
455 | git_vector *taglist; | |
456 | const char *pattern; | |
2b5af615 | 457 | } tag_filter_data; |
458 | ||
932669b8 | 459 | #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR) |
2b5af615 | 460 | |
2f05339e | 461 | static int tag_list_cb(const char *tag_name, git_oid *oid, void *data) |
def3fef1 | 462 | { |
2f05339e MS |
463 | tag_filter_data *filter = (tag_filter_data *)data; |
464 | GIT_UNUSED(oid); | |
2b5af615 | 465 | |
dab89f9b RB |
466 | if (!*filter->pattern || |
467 | p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) | |
468 | { | |
469 | char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN); | |
25e0b157 RB |
470 | GITERR_CHECK_ALLOC(matched); |
471 | ||
dab89f9b RB |
472 | return git_vector_insert(filter->taglist, matched); |
473 | } | |
def3fef1 | 474 | |
3aa351ea | 475 | return 0; |
def3fef1 VM |
476 | } |
477 | ||
2b5af615 | 478 | int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo) |
def3fef1 VM |
479 | { |
480 | int error; | |
2b5af615 | 481 | tag_filter_data filter; |
def3fef1 VM |
482 | git_vector taglist; |
483 | ||
2b5af615 | 484 | assert(tag_names && repo && pattern); |
485 | ||
dab89f9b RB |
486 | if ((error = git_vector_init(&taglist, 8, NULL)) < 0) |
487 | return error; | |
def3fef1 | 488 | |
2b5af615 | 489 | filter.taglist = &taglist; |
490 | filter.pattern = pattern; | |
491 | ||
2f05339e | 492 | error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter); |
dab89f9b | 493 | |
25e0b157 | 494 | if (error < 0) |
def3fef1 | 495 | git_vector_free(&taglist); |
def3fef1 | 496 | |
25e0b157 RB |
497 | tag_names->strings = |
498 | (char **)git_vector_detach(&tag_names->count, NULL, &taglist); | |
499 | ||
3aa351ea | 500 | return 0; |
def3fef1 | 501 | } |
2b5af615 | 502 | |
503 | int git_tag_list(git_strarray *tag_names, git_repository *repo) | |
504 | { | |
505 | return git_tag_list_match(tag_names, "", repo); | |
d483a911 | 506 | } |
3f46f313 | 507 | |
d9023dbe | 508 | int git_tag_peel(git_object **tag_target, const git_tag *tag) |
3f46f313 | 509 | { |
d9023dbe | 510 | return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJ_ANY); |
3f46f313 | 511 | } |