File: | gotadmin/../lib/reference.c |
Warning: | line 955, column 10 Potential leak of memory pointed to by 'path_refs' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.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 <sys/types.h> | |||
18 | #include <sys/queue.h> | |||
19 | #include <sys/stat.h> | |||
20 | ||||
21 | #include <errno(*__errno()).h> | |||
22 | #include <ctype.h> | |||
23 | #include <dirent.h> | |||
24 | #include <limits.h> | |||
25 | #include <sha1.h> | |||
26 | #include <stdio.h> | |||
27 | #include <stdlib.h> | |||
28 | #include <string.h> | |||
29 | #include <unistd.h> | |||
30 | #include <util.h> | |||
31 | #include <zlib.h> | |||
32 | #include <time.h> | |||
33 | #include <libgen.h> | |||
34 | ||||
35 | #include "got_error.h" | |||
36 | #include "got_object.h" | |||
37 | #include "got_repository.h" | |||
38 | #include "got_reference.h" | |||
39 | #include "got_opentemp.h" | |||
40 | #include "got_path.h" | |||
41 | ||||
42 | #include "got_lib_sha1.h" | |||
43 | #include "got_lib_delta.h" | |||
44 | #include "got_lib_inflate.h" | |||
45 | #include "got_lib_object.h" | |||
46 | #include "got_lib_object_idset.h" | |||
47 | #include "got_lib_lockfile.h" | |||
48 | ||||
49 | #ifndef nitems | |||
50 | #define nitems(_a)(sizeof(_a) / sizeof((_a)[0])) (sizeof(_a) / sizeof((_a)[0])) | |||
51 | #endif | |||
52 | ||||
53 | #define GOT_REF_HEADS"heads" "heads" | |||
54 | #define GOT_REF_TAGS"tags" "tags" | |||
55 | #define GOT_REF_REMOTES"remotes" "remotes" | |||
56 | ||||
57 | /* | |||
58 | * We do not resolve tags yet, and don't yet care about sorting refs either, | |||
59 | * so packed-refs files we write contain a minimal header which disables all | |||
60 | * packed-refs "traits" supported by Git. | |||
61 | */ | |||
62 | #define GOT_PACKED_REFS_HEADER"# pack-refs with:" "# pack-refs with:" | |||
63 | ||||
64 | /* A symbolic reference. */ | |||
65 | struct got_symref { | |||
66 | char *name; | |||
67 | char *ref; | |||
68 | }; | |||
69 | ||||
70 | #define GOT_REF_RECURSE_MAX20 20 | |||
71 | ||||
72 | /* A non-symbolic reference (there is no better designation). */ | |||
73 | struct got_ref { | |||
74 | char *name; | |||
75 | u_int8_t sha1[SHA1_DIGEST_LENGTH20]; | |||
76 | }; | |||
77 | ||||
78 | /* A reference which points to an arbitrary object. */ | |||
79 | struct got_reference { | |||
80 | unsigned int flags; | |||
81 | #define GOT_REF_IS_SYMBOLIC0x01 0x01 | |||
82 | #define GOT_REF_IS_PACKED0x02 0x02 | |||
83 | ||||
84 | union { | |||
85 | struct got_ref ref; | |||
86 | struct got_symref symref; | |||
87 | } ref; | |||
88 | ||||
89 | struct got_lockfile *lf; | |||
90 | }; | |||
91 | ||||
92 | static const struct got_error * | |||
93 | alloc_ref(struct got_reference **ref, const char *name, | |||
94 | struct got_object_id *id, int flags) | |||
95 | { | |||
96 | const struct got_error *err = NULL((void *)0); | |||
97 | ||||
98 | *ref = calloc(1, sizeof(**ref)); | |||
99 | if (*ref == NULL((void *)0)) | |||
100 | return got_error_from_errno("calloc"); | |||
101 | ||||
102 | memcpy((*ref)->ref.ref.sha1, id->sha1, sizeof((*ref)->ref.ref.sha1)); | |||
103 | (*ref)->flags = flags; | |||
104 | (*ref)->ref.ref.name = strdup(name); | |||
105 | if ((*ref)->ref.ref.name == NULL((void *)0)) { | |||
106 | err = got_error_from_errno("strdup"); | |||
107 | got_ref_close(*ref); | |||
108 | *ref = NULL((void *)0); | |||
109 | } | |||
110 | return err; | |||
111 | } | |||
112 | ||||
113 | static const struct got_error * | |||
114 | alloc_symref(struct got_reference **ref, const char *name, | |||
115 | const char *target_ref, int flags) | |||
116 | { | |||
117 | const struct got_error *err = NULL((void *)0); | |||
118 | ||||
119 | *ref = calloc(1, sizeof(**ref)); | |||
120 | if (*ref == NULL((void *)0)) | |||
121 | return got_error_from_errno("calloc"); | |||
122 | ||||
123 | (*ref)->flags = GOT_REF_IS_SYMBOLIC0x01 | flags; | |||
124 | (*ref)->ref.symref.name = strdup(name); | |||
125 | if ((*ref)->ref.symref.name == NULL((void *)0)) { | |||
126 | err = got_error_from_errno("strdup"); | |||
127 | got_ref_close(*ref); | |||
128 | *ref = NULL((void *)0); | |||
129 | return err; | |||
130 | } | |||
131 | (*ref)->ref.symref.ref = strdup(target_ref); | |||
132 | if ((*ref)->ref.symref.ref == NULL((void *)0)) { | |||
133 | err = got_error_from_errno("strdup"); | |||
134 | got_ref_close(*ref); | |||
135 | *ref = NULL((void *)0); | |||
136 | } | |||
137 | return err; | |||
138 | } | |||
139 | ||||
140 | static const struct got_error * | |||
141 | parse_symref(struct got_reference **ref, const char *name, const char *line) | |||
142 | { | |||
143 | if (line[0] == '\0') | |||
144 | return got_error(GOT_ERR_BAD_REF_DATA57); | |||
145 | ||||
146 | return alloc_symref(ref, name, line, 0); | |||
147 | } | |||
148 | ||||
149 | static const struct got_error * | |||
150 | parse_ref_line(struct got_reference **ref, const char *name, const char *line) | |||
151 | { | |||
152 | struct got_object_id id; | |||
153 | ||||
154 | if (strncmp(line, "ref: ", 5) == 0) { | |||
155 | line += 5; | |||
156 | return parse_symref(ref, name, line); | |||
157 | } | |||
158 | ||||
159 | if (!got_parse_sha1_digest(id.sha1, line)) | |||
160 | return got_error(GOT_ERR_BAD_REF_DATA57); | |||
161 | ||||
162 | return alloc_ref(ref, name, &id, 0); | |||
163 | } | |||
164 | ||||
165 | static const struct got_error * | |||
166 | parse_ref_file(struct got_reference **ref, const char *name, | |||
167 | const char *absname, const char *abspath, int lock) | |||
168 | { | |||
169 | const struct got_error *err = NULL((void *)0); | |||
170 | FILE *f; | |||
171 | char *line = NULL((void *)0); | |||
172 | size_t linesize = 0; | |||
173 | ssize_t linelen; | |||
174 | struct got_lockfile *lf = NULL((void *)0); | |||
175 | ||||
176 | if (lock) { | |||
177 | err = got_lockfile_lock(&lf, abspath); | |||
178 | if (err) { | |||
179 | if (err->code == GOT_ERR_ERRNO1 && errno(*__errno()) == ENOENT2) | |||
180 | err = got_error_not_ref(name); | |||
181 | return err; | |||
182 | } | |||
183 | } | |||
184 | ||||
185 | f = fopen(abspath, "rb"); | |||
186 | if (f == NULL((void *)0)) { | |||
187 | if (errno(*__errno()) != ENOTDIR20 && errno(*__errno()) != ENOENT2) | |||
188 | err = got_error_from_errno2("fopen", abspath); | |||
189 | else | |||
190 | err = got_error_not_ref(name); | |||
191 | if (lock) | |||
192 | got_lockfile_unlock(lf); | |||
193 | return err; | |||
194 | } | |||
195 | ||||
196 | linelen = getline(&line, &linesize, f); | |||
197 | if (linelen == -1) { | |||
198 | if (feof(f)(!__isthreaded ? (((f)->_flags & 0x0020) != 0) : (feof )(f))) | |||
199 | err = NULL((void *)0); /* ignore empty files (could be locks) */ | |||
200 | else { | |||
201 | if (errno(*__errno()) == EISDIR21) | |||
202 | err = got_error(GOT_ERR_NOT_REF5); | |||
203 | else if (ferror(f)(!__isthreaded ? (((f)->_flags & 0x0040) != 0) : (ferror )(f))) | |||
204 | err = got_ferror(f, GOT_ERR_IO6); | |||
205 | else | |||
206 | err = got_error_from_errno2("getline", abspath); | |||
207 | } | |||
208 | if (lock) | |||
209 | got_lockfile_unlock(lf); | |||
210 | goto done; | |||
211 | } | |||
212 | while (linelen > 0 && line[linelen - 1] == '\n') { | |||
213 | line[linelen - 1] = '\0'; | |||
214 | linelen--; | |||
215 | } | |||
216 | ||||
217 | err = parse_ref_line(ref, absname, line); | |||
218 | if (lock) { | |||
219 | if (err) | |||
220 | got_lockfile_unlock(lf); | |||
221 | else { | |||
222 | if (*ref) | |||
223 | (*ref)->lf = lf; | |||
224 | else | |||
225 | got_lockfile_unlock(lf); | |||
226 | } | |||
227 | } | |||
228 | done: | |||
229 | free(line); | |||
230 | if (fclose(f) == EOF(-1) && err == NULL((void *)0)) { | |||
231 | err = got_error_from_errno("fclose"); | |||
232 | if (*ref) { | |||
233 | if (lock) | |||
234 | got_ref_unlock(*ref); | |||
235 | got_ref_close(*ref); | |||
236 | *ref = NULL((void *)0); | |||
237 | } | |||
238 | } | |||
239 | return err; | |||
240 | } | |||
241 | ||||
242 | static int | |||
243 | is_well_known_ref(const char *refname) | |||
244 | { | |||
245 | return (strcmp(refname, GOT_REF_HEAD"HEAD") == 0 || | |||
246 | strcmp(refname, GOT_REF_ORIG_HEAD"ORIG_HEAD") == 0 || | |||
247 | strcmp(refname, GOT_REF_MERGE_HEAD"MERGE_HEAD") == 0 || | |||
248 | strcmp(refname, GOT_REF_FETCH_HEAD"FETCH_HEAD") == 0); | |||
249 | } | |||
250 | ||||
251 | static char * | |||
252 | get_refs_dir_path(struct got_repository *repo, const char *refname) | |||
253 | { | |||
254 | if (is_well_known_ref(refname) || strncmp(refname, "refs/", 5) == 0) | |||
255 | return strdup(got_repo_get_path_git_dir(repo)); | |||
256 | ||||
257 | return got_repo_get_path_refs(repo); | |||
258 | } | |||
259 | ||||
260 | static int | |||
261 | is_valid_ref_name(const char *name) | |||
262 | { | |||
263 | const char *s, *seg; | |||
264 | const char forbidden[] = { ' ', '~', '^', ':', '?', '*', '[' , '\\' }; | |||
265 | const char *forbidden_seq[] = { "//", "..", "@{" }; | |||
266 | const char *lfs = GOT_LOCKFILE_SUFFIX".lock"; | |||
267 | const size_t lfs_len = sizeof(GOT_LOCKFILE_SUFFIX".lock") - 1; | |||
268 | size_t i; | |||
269 | ||||
270 | if (name[0] == '@' && name[1] == '\0') | |||
271 | return 0; | |||
272 | ||||
273 | s = name; | |||
274 | seg = s; | |||
275 | if (seg[0] == '\0' || seg[0] == '.' || seg[0] == '/') | |||
276 | return 0; | |||
277 | while (*s) { | |||
278 | for (i = 0; i < nitems(forbidden)(sizeof(forbidden) / sizeof((forbidden)[0])); i++) { | |||
279 | if (*s == forbidden[i]) | |||
280 | return 0; | |||
281 | } | |||
282 | for (i = 0; i < nitems(forbidden_seq)(sizeof(forbidden_seq) / sizeof((forbidden_seq)[0])); i++) { | |||
283 | if (s[0] == forbidden_seq[i][0] && | |||
284 | s[1] == forbidden_seq[i][1]) | |||
285 | return 0; | |||
286 | } | |||
287 | if (iscntrl((unsigned char)s[0])) | |||
288 | return 0; | |||
289 | if (s[0] == '.' && s[1] == '\0') | |||
290 | return 0; | |||
291 | if (*s == '/') { | |||
292 | const char *nextseg = s + 1; | |||
293 | if (nextseg[0] == '\0' || nextseg[0] == '.' || | |||
294 | nextseg[0] == '/') | |||
295 | return 0; | |||
296 | if (seg <= s - lfs_len && | |||
297 | strncmp(s - lfs_len, lfs, lfs_len) == 0) | |||
298 | return 0; | |||
299 | seg = nextseg; | |||
300 | } | |||
301 | s++; | |||
302 | } | |||
303 | ||||
304 | if (seg <= s - lfs_len && | |||
305 | strncmp(s - lfs_len, lfs, lfs_len) == 0) | |||
306 | return 0; | |||
307 | ||||
308 | return 1; | |||
309 | } | |||
310 | ||||
311 | const struct got_error * | |||
312 | got_ref_alloc(struct got_reference **ref, const char *name, | |||
313 | struct got_object_id *id) | |||
314 | { | |||
315 | if (!is_valid_ref_name(name)) | |||
316 | return got_error_path(name, GOT_ERR_BAD_REF_NAME65); | |||
317 | ||||
318 | return alloc_ref(ref, name, id, 0); | |||
319 | } | |||
320 | ||||
321 | const struct got_error * | |||
322 | got_ref_alloc_symref(struct got_reference **ref, const char *name, | |||
323 | struct got_reference *target_ref) | |||
324 | { | |||
325 | if (!is_valid_ref_name(name)) | |||
326 | return got_error_path(name, GOT_ERR_BAD_REF_NAME65); | |||
327 | ||||
328 | return alloc_symref(ref, name, got_ref_get_name(target_ref), 0); | |||
329 | } | |||
330 | ||||
331 | static const struct got_error * | |||
332 | parse_packed_ref_line(struct got_reference **ref, const char *abs_refname, | |||
333 | const char *line) | |||
334 | { | |||
335 | struct got_object_id id; | |||
336 | const char *name; | |||
337 | ||||
338 | *ref = NULL((void *)0); | |||
339 | ||||
340 | if (line[0] == '#' || line[0] == '^') | |||
341 | return NULL((void *)0); | |||
342 | ||||
343 | if (!got_parse_sha1_digest(id.sha1, line)) | |||
344 | return got_error(GOT_ERR_BAD_REF_DATA57); | |||
345 | ||||
346 | if (abs_refname) { | |||
347 | if (strcmp(line + SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1), abs_refname) != 0) | |||
348 | return NULL((void *)0); | |||
349 | name = abs_refname; | |||
350 | } else | |||
351 | name = line + SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1); | |||
352 | ||||
353 | return alloc_ref(ref, name, &id, GOT_REF_IS_PACKED0x02); | |||
354 | } | |||
355 | ||||
356 | static const struct got_error * | |||
357 | open_packed_ref(struct got_reference **ref, FILE *f, const char **subdirs, | |||
358 | int nsubdirs, const char *refname) | |||
359 | { | |||
360 | const struct got_error *err = NULL((void *)0); | |||
361 | char *abs_refname; | |||
362 | char *line = NULL((void *)0); | |||
363 | size_t linesize = 0; | |||
364 | ssize_t linelen; | |||
365 | int i, ref_is_absolute = (strncmp(refname, "refs/", 5) == 0); | |||
366 | ||||
367 | *ref = NULL((void *)0); | |||
368 | ||||
369 | if (ref_is_absolute) | |||
370 | abs_refname = (char *)refname; | |||
371 | do { | |||
372 | linelen = getline(&line, &linesize, f); | |||
373 | if (linelen == -1) { | |||
374 | if (feof(f)(!__isthreaded ? (((f)->_flags & 0x0020) != 0) : (feof )(f))) | |||
375 | break; | |||
376 | err = got_ferror(f, GOT_ERR_BAD_REF_DATA57); | |||
377 | break; | |||
378 | } | |||
379 | if (linelen > 0 && line[linelen - 1] == '\n') | |||
380 | line[linelen - 1] = '\0'; | |||
381 | for (i = 0; i < nsubdirs; i++) { | |||
382 | if (!ref_is_absolute && | |||
383 | asprintf(&abs_refname, "refs/%s/%s", subdirs[i], | |||
384 | refname) == -1) | |||
385 | return got_error_from_errno("asprintf"); | |||
386 | err = parse_packed_ref_line(ref, abs_refname, line); | |||
387 | if (!ref_is_absolute) | |||
388 | free(abs_refname); | |||
389 | if (err || *ref != NULL((void *)0)) | |||
390 | break; | |||
391 | } | |||
392 | if (err) | |||
393 | break; | |||
394 | } while (*ref == NULL((void *)0)); | |||
395 | free(line); | |||
396 | ||||
397 | return err; | |||
398 | } | |||
399 | ||||
400 | static const struct got_error * | |||
401 | open_ref(struct got_reference **ref, const char *path_refs, const char *subdir, | |||
402 | const char *name, int lock) | |||
403 | { | |||
404 | const struct got_error *err = NULL((void *)0); | |||
405 | char *path = NULL((void *)0); | |||
406 | char *absname = NULL((void *)0); | |||
407 | int ref_is_absolute = (strncmp(name, "refs/", 5) == 0); | |||
408 | int ref_is_well_known = (subdir[0] == '\0' && is_well_known_ref(name)); | |||
409 | ||||
410 | *ref = NULL((void *)0); | |||
411 | ||||
412 | if (!is_valid_ref_name(name)) | |||
413 | return got_error_path(name, GOT_ERR_BAD_REF_NAME65); | |||
414 | ||||
415 | if (ref_is_absolute || ref_is_well_known) { | |||
416 | if (asprintf(&path, "%s/%s", path_refs, name) == -1) | |||
417 | return got_error_from_errno("asprintf"); | |||
418 | absname = (char *)name; | |||
419 | } else { | |||
420 | if (asprintf(&path, "%s/%s%s%s", path_refs, subdir, | |||
421 | subdir[0] ? "/" : "", name) == -1) | |||
422 | return got_error_from_errno("asprintf"); | |||
423 | ||||
424 | if (asprintf(&absname, "refs/%s%s%s", | |||
425 | subdir, subdir[0] ? "/" : "", name) == -1) { | |||
426 | err = got_error_from_errno("asprintf"); | |||
427 | goto done; | |||
428 | } | |||
429 | } | |||
430 | ||||
431 | err = parse_ref_file(ref, name, absname, path, lock); | |||
432 | done: | |||
433 | if (!ref_is_absolute && !ref_is_well_known) | |||
434 | free(absname); | |||
435 | free(path); | |||
436 | return err; | |||
437 | } | |||
438 | ||||
439 | const struct got_error * | |||
440 | got_ref_open(struct got_reference **ref, struct got_repository *repo, | |||
441 | const char *refname, int lock) | |||
442 | { | |||
443 | const struct got_error *err = NULL((void *)0); | |||
444 | char *path_refs = NULL((void *)0); | |||
445 | const char *subdirs[] = { | |||
446 | GOT_REF_HEADS"heads", GOT_REF_TAGS"tags", GOT_REF_REMOTES"remotes" | |||
447 | }; | |||
448 | size_t i; | |||
449 | int well_known = is_well_known_ref(refname); | |||
450 | struct got_lockfile *lf = NULL((void *)0); | |||
451 | ||||
452 | *ref = NULL((void *)0); | |||
453 | ||||
454 | path_refs = get_refs_dir_path(repo, refname); | |||
455 | if (path_refs == NULL((void *)0)) { | |||
456 | err = got_error_from_errno2("get_refs_dir_path", refname); | |||
457 | goto done; | |||
458 | } | |||
459 | ||||
460 | if (well_known) { | |||
461 | err = open_ref(ref, path_refs, "", refname, lock); | |||
462 | } else { | |||
463 | char *packed_refs_path; | |||
464 | FILE *f; | |||
465 | ||||
466 | /* Search on-disk refs before packed refs! */ | |||
467 | for (i = 0; i < nitems(subdirs)(sizeof(subdirs) / sizeof((subdirs)[0])); i++) { | |||
468 | err = open_ref(ref, path_refs, subdirs[i], refname, | |||
469 | lock); | |||
470 | if ((err && err->code != GOT_ERR_NOT_REF5) || *ref) | |||
471 | goto done; | |||
472 | } | |||
473 | ||||
474 | packed_refs_path = got_repo_get_path_packed_refs(repo); | |||
475 | if (packed_refs_path == NULL((void *)0)) { | |||
476 | err = got_error_from_errno( | |||
477 | "got_repo_get_path_packed_refs"); | |||
478 | goto done; | |||
479 | } | |||
480 | ||||
481 | if (lock) { | |||
482 | err = got_lockfile_lock(&lf, packed_refs_path); | |||
483 | if (err) | |||
484 | goto done; | |||
485 | } | |||
486 | f = fopen(packed_refs_path, "rb"); | |||
487 | free(packed_refs_path); | |||
488 | if (f != NULL((void *)0)) { | |||
489 | err = open_packed_ref(ref, f, subdirs, nitems(subdirs)(sizeof(subdirs) / sizeof((subdirs)[0])), | |||
490 | refname); | |||
491 | if (!err) { | |||
492 | if (fclose(f) == EOF(-1)) { | |||
493 | err = got_error_from_errno("fclose"); | |||
494 | got_ref_close(*ref); | |||
495 | *ref = NULL((void *)0); | |||
496 | } else if (*ref) | |||
497 | (*ref)->lf = lf; | |||
498 | } | |||
499 | } | |||
500 | } | |||
501 | done: | |||
502 | if (!err && *ref == NULL((void *)0)) | |||
503 | err = got_error_not_ref(refname); | |||
504 | if (err && lf) | |||
505 | got_lockfile_unlock(lf); | |||
506 | free(path_refs); | |||
507 | return err; | |||
508 | } | |||
509 | ||||
510 | void | |||
511 | got_ref_close(struct got_reference *ref) | |||
512 | { | |||
513 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) { | |||
514 | free(ref->ref.symref.name); | |||
515 | free(ref->ref.symref.ref); | |||
516 | } else | |||
517 | free(ref->ref.ref.name); | |||
518 | free(ref); | |||
519 | } | |||
520 | ||||
521 | struct got_reference * | |||
522 | got_ref_dup(struct got_reference *ref) | |||
523 | { | |||
524 | struct got_reference *ret; | |||
525 | ||||
526 | ret = calloc(1, sizeof(*ret)); | |||
527 | if (ret == NULL((void *)0)) | |||
528 | return NULL((void *)0); | |||
529 | ||||
530 | ret->flags = ref->flags; | |||
531 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) { | |||
532 | ret->ref.symref.name = strdup(ref->ref.symref.name); | |||
533 | if (ret->ref.symref.name == NULL((void *)0)) { | |||
534 | free(ret); | |||
535 | return NULL((void *)0); | |||
536 | } | |||
537 | ret->ref.symref.ref = strdup(ref->ref.symref.ref); | |||
538 | if (ret->ref.symref.ref == NULL((void *)0)) { | |||
539 | free(ret->ref.symref.name); | |||
540 | free(ret); | |||
541 | return NULL((void *)0); | |||
542 | } | |||
543 | } else { | |||
544 | ret->ref.ref.name = strdup(ref->ref.ref.name); | |||
545 | if (ret->ref.ref.name == NULL((void *)0)) { | |||
546 | free(ret); | |||
547 | return NULL((void *)0); | |||
548 | } | |||
549 | memcpy(ret->ref.ref.sha1, ref->ref.ref.sha1, | |||
550 | sizeof(ret->ref.ref.sha1)); | |||
551 | } | |||
552 | ||||
553 | return ret; | |||
554 | } | |||
555 | ||||
556 | const struct got_error * | |||
557 | got_reflist_entry_dup(struct got_reflist_entry **newp, | |||
558 | struct got_reflist_entry *re) | |||
559 | { | |||
560 | const struct got_error *err = NULL((void *)0); | |||
561 | struct got_reflist_entry *new; | |||
562 | ||||
563 | *newp = NULL((void *)0); | |||
564 | ||||
565 | new = malloc(sizeof(*new)); | |||
566 | if (new == NULL((void *)0)) | |||
567 | return got_error_from_errno("malloc"); | |||
568 | ||||
569 | new->ref = got_ref_dup(re->ref); | |||
570 | if (new->ref == NULL((void *)0)) { | |||
571 | err = got_error_from_errno("got_ref_dup"); | |||
572 | free(new); | |||
573 | return err; | |||
574 | } | |||
575 | ||||
576 | *newp = new; | |||
577 | return NULL((void *)0); | |||
578 | } | |||
579 | ||||
580 | static const struct got_error * | |||
581 | resolve_symbolic_ref(struct got_reference **resolved, | |||
582 | struct got_repository *repo, struct got_reference *ref) | |||
583 | { | |||
584 | struct got_reference *nextref; | |||
585 | const struct got_error *err; | |||
586 | ||||
587 | err = got_ref_open(&nextref, repo, ref->ref.symref.ref, 0); | |||
588 | if (err) | |||
589 | return err; | |||
590 | ||||
591 | if (nextref->flags & GOT_REF_IS_SYMBOLIC0x01) | |||
592 | err = resolve_symbolic_ref(resolved, repo, nextref); | |||
593 | else | |||
594 | *resolved = got_ref_dup(nextref); | |||
595 | ||||
596 | got_ref_close(nextref); | |||
597 | return err; | |||
598 | } | |||
599 | ||||
600 | static const struct got_error * | |||
601 | ref_resolve(struct got_object_id **id, struct got_repository *repo, | |||
602 | struct got_reference *ref, int recursion) | |||
603 | { | |||
604 | const struct got_error *err; | |||
605 | ||||
606 | if (recursion <= 0) | |||
607 | return got_error_msg(GOT_ERR_RECURSION32, | |||
608 | "reference recursion limit reached"); | |||
609 | ||||
610 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) { | |||
611 | struct got_reference *resolved = NULL((void *)0); | |||
612 | err = resolve_symbolic_ref(&resolved, repo, ref); | |||
613 | if (err == NULL((void *)0)) | |||
614 | err = ref_resolve(id, repo, resolved, --recursion); | |||
615 | if (resolved) | |||
616 | got_ref_close(resolved); | |||
617 | return err; | |||
618 | } | |||
619 | ||||
620 | *id = calloc(1, sizeof(**id)); | |||
621 | if (*id == NULL((void *)0)) | |||
622 | return got_error_from_errno("calloc"); | |||
623 | memcpy((*id)->sha1, ref->ref.ref.sha1, sizeof((*id)->sha1)); | |||
624 | return NULL((void *)0); | |||
625 | } | |||
626 | ||||
627 | const struct got_error * | |||
628 | got_ref_resolve(struct got_object_id **id, struct got_repository *repo, | |||
629 | struct got_reference *ref) | |||
630 | { | |||
631 | return ref_resolve(id, repo, ref, GOT_REF_RECURSE_MAX20); | |||
632 | } | |||
633 | ||||
634 | char * | |||
635 | got_ref_to_str(struct got_reference *ref) | |||
636 | { | |||
637 | char *str; | |||
638 | ||||
639 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) | |||
640 | return strdup(ref->ref.symref.ref); | |||
641 | ||||
642 | str = malloc(SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)); | |||
643 | if (str == NULL((void *)0)) | |||
644 | return NULL((void *)0); | |||
645 | ||||
646 | if (got_sha1_digest_to_str(ref->ref.ref.sha1, str, | |||
647 | SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)) == NULL((void *)0)) { | |||
648 | free(str); | |||
649 | return NULL((void *)0); | |||
650 | } | |||
651 | ||||
652 | return str; | |||
653 | } | |||
654 | ||||
655 | const char * | |||
656 | got_ref_get_name(struct got_reference *ref) | |||
657 | { | |||
658 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) | |||
659 | return ref->ref.symref.name; | |||
660 | ||||
661 | return ref->ref.ref.name; | |||
662 | } | |||
663 | ||||
664 | const char * | |||
665 | got_ref_get_symref_target(struct got_reference *ref) | |||
666 | { | |||
667 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) | |||
668 | return ref->ref.symref.ref; | |||
669 | ||||
670 | return NULL((void *)0); | |||
671 | } | |||
672 | ||||
673 | const struct got_error * | |||
674 | got_ref_cmp_by_name(void *arg, int *cmp, struct got_reference *re1, | |||
675 | struct got_reference* re2) | |||
676 | { | |||
677 | const char *name1 = got_ref_get_name(re1); | |||
678 | const char *name2 = got_ref_get_name(re2); | |||
679 | ||||
680 | *cmp = got_path_cmp(name1, name2, strlen(name1), strlen(name2)); | |||
681 | return NULL((void *)0); | |||
682 | } | |||
683 | ||||
684 | const struct got_error * | |||
685 | got_ref_cmp_tags(void *arg, int *cmp, struct got_reference *ref1, | |||
686 | struct got_reference *ref2) | |||
687 | { | |||
688 | const struct got_error *err = NULL((void *)0); | |||
689 | struct got_repository *repo = arg; | |||
690 | struct got_object_id *id1, *id2 = NULL((void *)0); | |||
691 | struct got_tag_object *tag1 = NULL((void *)0), *tag2 = NULL((void *)0); | |||
692 | struct got_commit_object *commit1 = NULL((void *)0), *commit2 = NULL((void *)0); | |||
693 | time_t time1, time2; | |||
694 | ||||
695 | *cmp = 0; | |||
696 | ||||
697 | err = got_ref_resolve(&id1, repo, ref1); | |||
698 | if (err) | |||
699 | return err; | |||
700 | err = got_object_open_as_tag(&tag1, repo, id1); | |||
701 | if (err) { | |||
702 | if (err->code != GOT_ERR_OBJ_TYPE11) | |||
703 | goto done; | |||
704 | /* "lightweight" tag */ | |||
705 | err = got_object_open_as_commit(&commit1, repo, id1); | |||
706 | if (err) | |||
707 | goto done; | |||
708 | time1 = got_object_commit_get_committer_time(commit1); | |||
709 | } else | |||
710 | time1 = got_object_tag_get_tagger_time(tag1); | |||
711 | ||||
712 | err = got_ref_resolve(&id2, repo, ref2); | |||
713 | if (err) | |||
714 | goto done; | |||
715 | err = got_object_open_as_tag(&tag2, repo, id2); | |||
716 | if (err) { | |||
717 | if (err->code != GOT_ERR_OBJ_TYPE11) | |||
718 | goto done; | |||
719 | /* "lightweight" tag */ | |||
720 | err = got_object_open_as_commit(&commit2, repo, id2); | |||
721 | if (err) | |||
722 | goto done; | |||
723 | time2 = got_object_commit_get_committer_time(commit2); | |||
724 | } else | |||
725 | time2 = got_object_tag_get_tagger_time(tag2); | |||
726 | ||||
727 | /* Put latest tags first. */ | |||
728 | if (time1 < time2) | |||
729 | *cmp = 1; | |||
730 | else if (time1 > time2) | |||
731 | *cmp = -1; | |||
732 | else | |||
733 | err = got_ref_cmp_by_name(NULL((void *)0), cmp, ref2, ref1); | |||
734 | done: | |||
735 | free(id1); | |||
736 | free(id2); | |||
737 | if (tag1) | |||
738 | got_object_tag_close(tag1); | |||
739 | if (tag2) | |||
740 | got_object_tag_close(tag2); | |||
741 | if (commit1) | |||
742 | got_object_commit_close(commit1); | |||
743 | if (commit2) | |||
744 | got_object_commit_close(commit2); | |||
745 | return err; | |||
746 | } | |||
747 | ||||
748 | const struct got_error * | |||
749 | got_ref_cmp_by_commit_timestamp_descending(void *arg, int *cmp, | |||
750 | struct got_reference *ref1, struct got_reference *ref2) | |||
751 | { | |||
752 | const struct got_error *err; | |||
753 | struct got_repository *repo = arg; | |||
754 | struct got_object_id *id1, *id2 = NULL((void *)0); | |||
755 | struct got_commit_object *commit1 = NULL((void *)0), *commit2 = NULL((void *)0); | |||
756 | time_t time1, time2; | |||
757 | ||||
758 | *cmp = 0; | |||
759 | ||||
760 | err = got_ref_resolve(&id1, repo, ref1); | |||
761 | if (err) | |||
762 | return err; | |||
763 | err = got_ref_resolve(&id2, repo, ref2); | |||
764 | if (err) | |||
765 | goto done; | |||
766 | ||||
767 | err = got_object_open_as_commit(&commit1, repo, id1); | |||
768 | if (err) | |||
769 | goto done; | |||
770 | err = got_object_open_as_commit(&commit2, repo, id2); | |||
771 | if (err) | |||
772 | goto done; | |||
773 | ||||
774 | time1 = got_object_commit_get_committer_time(commit1); | |||
775 | time2 = got_object_commit_get_committer_time(commit2); | |||
776 | if (time1 < time2) | |||
777 | *cmp = 1; | |||
778 | else if (time2 < time1) | |||
779 | *cmp = -1; | |||
780 | done: | |||
781 | free(id1); | |||
782 | free(id2); | |||
783 | if (commit1) | |||
784 | got_object_commit_close(commit1); | |||
785 | if (commit2) | |||
786 | got_object_commit_close(commit2); | |||
787 | return err; | |||
788 | } | |||
789 | ||||
790 | static const struct got_error * | |||
791 | insert_ref(struct got_reflist_entry **newp, struct got_reflist_head *refs, | |||
792 | struct got_reference *ref, struct got_repository *repo, | |||
793 | got_ref_cmp_cb cmp_cb, void *cmp_arg) | |||
794 | { | |||
795 | const struct got_error *err; | |||
796 | struct got_reflist_entry *new, *re; | |||
797 | int cmp; | |||
798 | ||||
799 | *newp = NULL((void *)0); | |||
800 | ||||
801 | new = malloc(sizeof(*new)); | |||
802 | if (new == NULL((void *)0)) | |||
803 | return got_error_from_errno("malloc"); | |||
804 | new->ref = ref; | |||
805 | *newp = new; | |||
806 | ||||
807 | /* | |||
808 | * We must de-duplicate entries on insert because packed-refs may | |||
809 | * contain redundant entries. On-disk refs take precedence. | |||
810 | * This code assumes that on-disk revs are read before packed-refs. | |||
811 | * We're iterating the list anyway, so insert elements sorted by name. | |||
812 | * | |||
813 | * Many callers will provide paths in a somewhat sorted order. | |||
814 | * Iterating backwards from the tail of the list should be more | |||
815 | * efficient than traversing through the entire list each time | |||
816 | * an element is inserted. | |||
817 | */ | |||
818 | re = TAILQ_LAST(refs, got_reflist_head)(*(((struct got_reflist_head *)((refs)->tqh_last))->tqh_last )); | |||
819 | while (re) { | |||
820 | err = (*cmp_cb)(cmp_arg, &cmp, re->ref, new->ref); | |||
821 | if (err) | |||
822 | return err; | |||
823 | if (cmp == 0) { | |||
824 | /* duplicate */ | |||
825 | free(new); | |||
826 | *newp = NULL((void *)0); | |||
827 | return NULL((void *)0); | |||
828 | } else if (cmp < 0) { | |||
829 | TAILQ_INSERT_AFTER(refs, re, new, entry)do { if (((new)->entry.tqe_next = (re)->entry.tqe_next) != ((void *)0)) (new)->entry.tqe_next->entry.tqe_prev = &(new)->entry.tqe_next; else (refs)->tqh_last = & (new)->entry.tqe_next; (re)->entry.tqe_next = (new); (new )->entry.tqe_prev = &(re)->entry.tqe_next; } while ( 0); | |||
830 | return NULL((void *)0); | |||
831 | } | |||
832 | re = TAILQ_PREV(re, got_reflist_head, entry)(*(((struct got_reflist_head *)((re)->entry.tqe_prev))-> tqh_last)); | |||
833 | } | |||
834 | ||||
835 | TAILQ_INSERT_HEAD(refs, new, entry)do { if (((new)->entry.tqe_next = (refs)->tqh_first) != ((void *)0)) (refs)->tqh_first->entry.tqe_prev = & (new)->entry.tqe_next; else (refs)->tqh_last = &(new )->entry.tqe_next; (refs)->tqh_first = (new); (new)-> entry.tqe_prev = &(refs)->tqh_first; } while (0); | |||
836 | return NULL((void *)0); | |||
837 | } | |||
838 | ||||
839 | static const struct got_error * | |||
840 | gather_on_disk_refs(struct got_reflist_head *refs, const char *path_refs, | |||
841 | const char *subdir, struct got_repository *repo, | |||
842 | got_ref_cmp_cb cmp_cb, void *cmp_arg) | |||
843 | { | |||
844 | const struct got_error *err = NULL((void *)0); | |||
845 | DIR *d = NULL((void *)0); | |||
846 | char *path_subdir; | |||
847 | ||||
848 | while (subdir[0] == '/') | |||
849 | subdir++; | |||
850 | ||||
851 | if (asprintf(&path_subdir, "%s/%s", path_refs, subdir) == -1) | |||
852 | return got_error_from_errno("asprintf"); | |||
853 | ||||
854 | d = opendir(path_subdir); | |||
855 | if (d == NULL((void *)0)) | |||
856 | goto done; | |||
857 | ||||
858 | for (;;) { | |||
859 | struct dirent *dent; | |||
860 | struct got_reference *ref; | |||
861 | char *child; | |||
862 | int type; | |||
863 | ||||
864 | dent = readdir(d); | |||
865 | if (dent == NULL((void *)0)) | |||
866 | break; | |||
867 | ||||
868 | if (strcmp(dent->d_name, ".") == 0 || | |||
869 | strcmp(dent->d_name, "..") == 0) | |||
870 | continue; | |||
871 | ||||
872 | err = got_path_dirent_type(&type, path_subdir, dent); | |||
873 | if (err) | |||
874 | break; | |||
875 | ||||
876 | switch (type) { | |||
877 | case DT_REG8: | |||
878 | err = open_ref(&ref, path_refs, subdir, dent->d_name, | |||
879 | 0); | |||
880 | if (err) | |||
881 | goto done; | |||
882 | if (ref) { | |||
883 | struct got_reflist_entry *new; | |||
884 | err = insert_ref(&new, refs, ref, repo, | |||
885 | cmp_cb, cmp_arg); | |||
886 | if (err || new == NULL((void *)0) /* duplicate */) | |||
887 | got_ref_close(ref); | |||
888 | if (err) | |||
889 | goto done; | |||
890 | } | |||
891 | break; | |||
892 | case DT_DIR4: | |||
893 | if (asprintf(&child, "%s%s%s", subdir, | |||
894 | subdir[0] == '\0' ? "" : "/", dent->d_name) == -1) { | |||
895 | err = got_error_from_errno("asprintf"); | |||
896 | break; | |||
897 | } | |||
898 | err = gather_on_disk_refs(refs, path_refs, child, repo, | |||
899 | cmp_cb, cmp_arg); | |||
900 | free(child); | |||
901 | break; | |||
902 | default: | |||
903 | break; | |||
904 | } | |||
905 | } | |||
906 | done: | |||
907 | if (d) | |||
908 | closedir(d); | |||
909 | free(path_subdir); | |||
910 | return err; | |||
911 | } | |||
912 | ||||
913 | const struct got_error * | |||
914 | got_ref_list(struct got_reflist_head *refs, struct got_repository *repo, | |||
915 | const char *ref_namespace, got_ref_cmp_cb cmp_cb, void *cmp_arg) | |||
916 | { | |||
917 | const struct got_error *err; | |||
918 | char *packed_refs_path, *path_refs = NULL((void *)0); | |||
919 | char *abs_namespace = NULL((void *)0); | |||
920 | char *buf = NULL((void *)0), *ondisk_ref_namespace = NULL((void *)0); | |||
921 | char *line = NULL((void *)0); | |||
922 | FILE *f = NULL((void *)0); | |||
923 | struct got_reference *ref; | |||
924 | struct got_reflist_entry *new; | |||
925 | ||||
926 | if (ref_namespace == NULL((void *)0) || ref_namespace[0] == '\0') { | |||
| ||||
927 | path_refs = get_refs_dir_path(repo, GOT_REF_HEAD"HEAD"); | |||
928 | if (path_refs == NULL((void *)0)) { | |||
929 | err = got_error_from_errno("get_refs_dir_path"); | |||
930 | goto done; | |||
931 | } | |||
932 | err = open_ref(&ref, path_refs, "", GOT_REF_HEAD"HEAD", 0); | |||
933 | if (err) | |||
934 | goto done; | |||
935 | err = insert_ref(&new, refs, ref, repo, | |||
936 | cmp_cb, cmp_arg); | |||
937 | if (err || new == NULL((void *)0) /* duplicate */) | |||
938 | got_ref_close(ref); | |||
939 | if (err && err->code != GOT_ERR_NOT_REF5) | |||
940 | goto done; | |||
941 | } else { | |||
942 | /* Try listing a single reference. */ | |||
943 | const char *refname = ref_namespace; | |||
944 | path_refs = get_refs_dir_path(repo, refname); | |||
945 | if (path_refs == NULL((void *)0)) { | |||
946 | err = got_error_from_errno("get_refs_dir_path"); | |||
947 | goto done; | |||
948 | } | |||
949 | err = open_ref(&ref, path_refs, "", refname, 0); | |||
950 | if (err) { | |||
951 | if (err->code != GOT_ERR_NOT_REF5) | |||
952 | goto done; | |||
953 | /* Try to look up references in a given namespace. */ | |||
954 | } else { | |||
955 | err = insert_ref(&new, refs, ref, repo, | |||
| ||||
956 | cmp_cb, cmp_arg); | |||
957 | if (err || new == NULL((void *)0) /* duplicate */) | |||
958 | got_ref_close(ref); | |||
959 | return err; | |||
960 | } | |||
961 | } | |||
962 | ||||
963 | if (ref_namespace) { | |||
964 | size_t len; | |||
965 | /* Canonicalize the path to eliminate double-slashes if any. */ | |||
966 | if (asprintf(&abs_namespace, "/%s", ref_namespace) == -1) { | |||
967 | err = got_error_from_errno("asprintf"); | |||
968 | goto done; | |||
969 | } | |||
970 | len = strlen(abs_namespace) + 1; | |||
971 | buf = malloc(len); | |||
972 | if (buf == NULL((void *)0)) { | |||
973 | err = got_error_from_errno("malloc"); | |||
974 | goto done; | |||
975 | } | |||
976 | err = got_canonpath(abs_namespace, buf, len); | |||
977 | if (err) | |||
978 | goto done; | |||
979 | ondisk_ref_namespace = buf; | |||
980 | while (ondisk_ref_namespace[0] == '/') | |||
981 | ondisk_ref_namespace++; | |||
982 | if (strncmp(ondisk_ref_namespace, "refs/", 5) == 0) | |||
983 | ondisk_ref_namespace += 5; | |||
984 | else if (strcmp(ondisk_ref_namespace, "refs") == 0) | |||
985 | ondisk_ref_namespace = ""; | |||
986 | } | |||
987 | ||||
988 | /* Gather on-disk refs before parsing packed-refs. */ | |||
989 | free(path_refs); | |||
990 | path_refs = get_refs_dir_path(repo, ""); | |||
991 | if (path_refs == NULL((void *)0)) { | |||
992 | err = got_error_from_errno("get_refs_dir_path"); | |||
993 | goto done; | |||
994 | } | |||
995 | err = gather_on_disk_refs(refs, path_refs, | |||
996 | ondisk_ref_namespace ? ondisk_ref_namespace : "", repo, | |||
997 | cmp_cb, cmp_arg); | |||
998 | if (err) | |||
999 | goto done; | |||
1000 | ||||
1001 | /* | |||
1002 | * The packed-refs file may contain redundant entries, in which | |||
1003 | * case on-disk refs take precedence. | |||
1004 | */ | |||
1005 | packed_refs_path = got_repo_get_path_packed_refs(repo); | |||
1006 | if (packed_refs_path == NULL((void *)0)) { | |||
1007 | err = got_error_from_errno("got_repo_get_path_packed_refs"); | |||
1008 | goto done; | |||
1009 | } | |||
1010 | ||||
1011 | f = fopen(packed_refs_path, "r"); | |||
1012 | free(packed_refs_path); | |||
1013 | if (f) { | |||
1014 | size_t linesize = 0; | |||
1015 | ssize_t linelen; | |||
1016 | for (;;) { | |||
1017 | linelen = getline(&line, &linesize, f); | |||
1018 | if (linelen == -1) { | |||
1019 | if (feof(f)(!__isthreaded ? (((f)->_flags & 0x0020) != 0) : (feof )(f))) | |||
1020 | break; | |||
1021 | err = got_ferror(f, GOT_ERR_BAD_REF_DATA57); | |||
1022 | goto done; | |||
1023 | } | |||
1024 | if (linelen > 0 && line[linelen - 1] == '\n') | |||
1025 | line[linelen - 1] = '\0'; | |||
1026 | err = parse_packed_ref_line(&ref, NULL((void *)0), line); | |||
1027 | if (err) | |||
1028 | goto done; | |||
1029 | if (ref) { | |||
1030 | if (ref_namespace) { | |||
1031 | const char *name; | |||
1032 | name = got_ref_get_name(ref); | |||
1033 | if (!got_path_is_child(name, | |||
1034 | ref_namespace, | |||
1035 | strlen(ref_namespace))) { | |||
1036 | got_ref_close(ref); | |||
1037 | continue; | |||
1038 | } | |||
1039 | } | |||
1040 | err = insert_ref(&new, refs, ref, repo, | |||
1041 | cmp_cb, cmp_arg); | |||
1042 | if (err || new == NULL((void *)0) /* duplicate */) | |||
1043 | got_ref_close(ref); | |||
1044 | if (err) | |||
1045 | goto done; | |||
1046 | } | |||
1047 | } | |||
1048 | } | |||
1049 | done: | |||
1050 | free(abs_namespace); | |||
1051 | free(buf); | |||
1052 | free(line); | |||
1053 | free(path_refs); | |||
1054 | if (f && fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
1055 | err = got_error_from_errno("fclose"); | |||
1056 | return err; | |||
1057 | } | |||
1058 | ||||
1059 | void | |||
1060 | got_ref_list_free(struct got_reflist_head *refs) | |||
1061 | { | |||
1062 | struct got_reflist_entry *re; | |||
1063 | ||||
1064 | while ((re = TAILQ_FIRST(refs)((refs)->tqh_first))) { | |||
1065 | TAILQ_REMOVE(refs, re, entry)do { if (((re)->entry.tqe_next) != ((void *)0)) (re)->entry .tqe_next->entry.tqe_prev = (re)->entry.tqe_prev; else ( refs)->tqh_last = (re)->entry.tqe_prev; *(re)->entry .tqe_prev = (re)->entry.tqe_next; ; ; } while (0); | |||
1066 | got_ref_close(re->ref); | |||
1067 | free(re); | |||
1068 | } | |||
1069 | ||||
1070 | } | |||
1071 | ||||
1072 | int | |||
1073 | got_ref_is_symbolic(struct got_reference *ref) | |||
1074 | { | |||
1075 | return (ref->flags & GOT_REF_IS_SYMBOLIC0x01); | |||
1076 | } | |||
1077 | ||||
1078 | const struct got_error * | |||
1079 | got_ref_change_ref(struct got_reference *ref, struct got_object_id *id) | |||
1080 | { | |||
1081 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) | |||
1082 | return got_error(GOT_ERR_BAD_REF_TYPE70); | |||
1083 | ||||
1084 | memcpy(ref->ref.ref.sha1, id->sha1, sizeof(ref->ref.ref.sha1)); | |||
1085 | return NULL((void *)0); | |||
1086 | } | |||
1087 | ||||
1088 | const struct got_error * | |||
1089 | got_ref_change_symref(struct got_reference *ref, const char *refname) | |||
1090 | { | |||
1091 | char *new_name; | |||
1092 | ||||
1093 | if ((ref->flags & GOT_REF_IS_SYMBOLIC0x01) == 0) | |||
1094 | return got_error(GOT_ERR_BAD_REF_TYPE70); | |||
1095 | ||||
1096 | new_name = strdup(refname); | |||
1097 | if (new_name == NULL((void *)0)) | |||
1098 | return got_error_from_errno("strdup"); | |||
1099 | ||||
1100 | free(ref->ref.symref.ref); | |||
1101 | ref->ref.symref.ref = new_name; | |||
1102 | return NULL((void *)0); | |||
1103 | } | |||
1104 | ||||
1105 | const struct got_error * | |||
1106 | got_ref_change_symref_to_ref(struct got_reference *symref, | |||
1107 | struct got_object_id *id) | |||
1108 | { | |||
1109 | if ((symref->flags & GOT_REF_IS_SYMBOLIC0x01) == 0) | |||
1110 | return got_error(GOT_ERR_BAD_REF_TYPE70); | |||
1111 | ||||
1112 | symref->ref.ref.name = symref->ref.symref.name; | |||
1113 | memcpy(symref->ref.ref.sha1, id->sha1, SHA1_DIGEST_LENGTH20); | |||
1114 | symref->flags &= ~GOT_REF_IS_SYMBOLIC0x01; | |||
1115 | return NULL((void *)0); | |||
1116 | } | |||
1117 | ||||
1118 | const struct got_error * | |||
1119 | got_ref_write(struct got_reference *ref, struct got_repository *repo) | |||
1120 | { | |||
1121 | const struct got_error *err = NULL((void *)0), *unlock_err = NULL((void *)0); | |||
1122 | const char *name = got_ref_get_name(ref); | |||
1123 | char *path_refs = NULL((void *)0), *path = NULL((void *)0), *tmppath = NULL((void *)0); | |||
1124 | struct got_lockfile *lf = NULL((void *)0); | |||
1125 | FILE *f = NULL((void *)0); | |||
1126 | size_t n; | |||
1127 | struct stat sb; | |||
1128 | ||||
1129 | path_refs = get_refs_dir_path(repo, name); | |||
1130 | if (path_refs == NULL((void *)0)) { | |||
1131 | err = got_error_from_errno2("get_refs_dir_path", name); | |||
1132 | goto done; | |||
1133 | } | |||
1134 | ||||
1135 | if (asprintf(&path, "%s/%s", path_refs, name) == -1) { | |||
1136 | err = got_error_from_errno("asprintf"); | |||
1137 | goto done; | |||
1138 | } | |||
1139 | ||||
1140 | err = got_opentemp_named(&tmppath, &f, path); | |||
1141 | if (err) { | |||
1142 | char *parent; | |||
1143 | if (!(err->code == GOT_ERR_ERRNO1 && errno(*__errno()) == ENOENT2)) | |||
1144 | goto done; | |||
1145 | err = got_path_dirname(&parent, path); | |||
1146 | if (err) | |||
1147 | goto done; | |||
1148 | err = got_path_mkdir(parent); | |||
1149 | free(parent); | |||
1150 | if (err) | |||
1151 | goto done; | |||
1152 | err = got_opentemp_named(&tmppath, &f, path); | |||
1153 | if (err) | |||
1154 | goto done; | |||
1155 | } | |||
1156 | ||||
1157 | if (ref->flags & GOT_REF_IS_SYMBOLIC0x01) { | |||
1158 | n = fprintf(f, "ref: %s\n", ref->ref.symref.ref); | |||
1159 | if (n != strlen(ref->ref.symref.ref) + 6) { | |||
1160 | err = got_ferror(f, GOT_ERR_IO6); | |||
1161 | goto done; | |||
1162 | } | |||
1163 | } else { | |||
1164 | char hex[SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)]; | |||
1165 | if (got_sha1_digest_to_str(ref->ref.ref.sha1, hex, | |||
1166 | sizeof(hex)) == NULL((void *)0)) { | |||
1167 | err = got_error(GOT_ERR_BAD_REF_DATA57); | |||
1168 | goto done; | |||
1169 | } | |||
1170 | n = fprintf(f, "%s\n", hex); | |||
1171 | if (n != sizeof(hex)) { | |||
1172 | err = got_ferror(f, GOT_ERR_IO6); | |||
1173 | goto done; | |||
1174 | } | |||
1175 | } | |||
1176 | ||||
1177 | if (ref->lf == NULL((void *)0)) { | |||
1178 | err = got_lockfile_lock(&lf, path); | |||
1179 | if (err) | |||
1180 | goto done; | |||
1181 | } | |||
1182 | ||||
1183 | /* XXX: check if old content matches our expectations? */ | |||
1184 | ||||
1185 | if (stat(path, &sb) != 0) { | |||
1186 | if (errno(*__errno()) != ENOENT2) { | |||
1187 | err = got_error_from_errno2("stat", path); | |||
1188 | goto done; | |||
1189 | } | |||
1190 | sb.st_mode = GOT_DEFAULT_FILE_MODE(0100000 | 0000400|0000200 | 0000040 | 0000004); | |||
1191 | } | |||
1192 | ||||
1193 | if (fchmod(fileno(f)(!__isthreaded ? ((f)->_file) : (fileno)(f)), sb.st_mode) != 0) { | |||
1194 | err = got_error_from_errno2("fchmod", tmppath); | |||
1195 | goto done; | |||
1196 | } | |||
1197 | ||||
1198 | if (rename(tmppath, path) != 0) { | |||
1199 | err = got_error_from_errno3("rename", tmppath, path); | |||
1200 | goto done; | |||
1201 | } | |||
1202 | free(tmppath); | |||
1203 | tmppath = NULL((void *)0); | |||
1204 | done: | |||
1205 | if (ref->lf == NULL((void *)0) && lf) | |||
1206 | unlock_err = got_lockfile_unlock(lf); | |||
1207 | if (f) { | |||
1208 | if (fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
1209 | err = got_error_from_errno("fclose"); | |||
1210 | } | |||
1211 | free(path_refs); | |||
1212 | free(path); | |||
1213 | if (tmppath) { | |||
1214 | if (unlink(tmppath) != 0 && err == NULL((void *)0)) | |||
1215 | err = got_error_from_errno2("unlink", tmppath); | |||
1216 | free(tmppath); | |||
1217 | } | |||
1218 | return err ? err : unlock_err; | |||
1219 | } | |||
1220 | ||||
1221 | static const struct got_error * | |||
1222 | delete_packed_ref(struct got_reference *delref, struct got_repository *repo) | |||
1223 | { | |||
1224 | const struct got_error *err = NULL((void *)0), *unlock_err = NULL((void *)0); | |||
1225 | struct got_lockfile *lf = NULL((void *)0); | |||
1226 | FILE *f = NULL((void *)0), *tmpf = NULL((void *)0); | |||
1227 | char *line = NULL((void *)0), *packed_refs_path, *tmppath = NULL((void *)0); | |||
1228 | size_t linesize = 0; | |||
1229 | struct got_reflist_head refs; | |||
1230 | int found_delref = 0; | |||
1231 | ||||
1232 | /* The packed-refs file does not cotain symbolic references. */ | |||
1233 | if (delref->flags & GOT_REF_IS_SYMBOLIC0x01) | |||
1234 | return got_error(GOT_ERR_BAD_REF_DATA57); | |||
1235 | ||||
1236 | TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)-> tqh_last = &(&refs)->tqh_first; } while (0); | |||
1237 | ||||
1238 | packed_refs_path = got_repo_get_path_packed_refs(repo); | |||
1239 | if (packed_refs_path == NULL((void *)0)) | |||
1240 | return got_error_from_errno("got_repo_get_path_packed_refs"); | |||
1241 | ||||
1242 | err = got_opentemp_named(&tmppath, &tmpf, packed_refs_path); | |||
1243 | if (err) | |||
1244 | goto done; | |||
1245 | ||||
1246 | if (delref->lf == NULL((void *)0)) { | |||
1247 | err = got_lockfile_lock(&lf, packed_refs_path); | |||
1248 | if (err) | |||
1249 | goto done; | |||
1250 | } | |||
1251 | ||||
1252 | f = fopen(packed_refs_path, "r"); | |||
1253 | if (f == NULL((void *)0)) { | |||
1254 | err = got_error_from_errno2("fopen", packed_refs_path); | |||
1255 | goto done; | |||
1256 | } | |||
1257 | for (;;) { | |||
1258 | ssize_t linelen; | |||
1259 | struct got_reference *ref; | |||
1260 | struct got_reflist_entry *new; | |||
1261 | ||||
1262 | linelen = getline(&line, &linesize, f); | |||
1263 | if (linelen == -1) { | |||
1264 | if (feof(f)(!__isthreaded ? (((f)->_flags & 0x0020) != 0) : (feof )(f))) | |||
1265 | break; | |||
1266 | err = got_ferror(f, GOT_ERR_BAD_REF_DATA57); | |||
1267 | goto done; | |||
1268 | } | |||
1269 | if (linelen > 0 && line[linelen - 1] == '\n') | |||
1270 | line[linelen - 1] = '\0'; | |||
1271 | err = parse_packed_ref_line(&ref, NULL((void *)0), line); | |||
1272 | if (err) | |||
1273 | goto done; | |||
1274 | if (ref == NULL((void *)0)) | |||
1275 | continue; | |||
1276 | ||||
1277 | if (strcmp(ref->ref.ref.name, delref->ref.ref.name) == 0 && | |||
1278 | memcmp(ref->ref.ref.sha1, delref->ref.ref.sha1, | |||
1279 | sizeof(delref->ref.ref.sha1)) == 0) { | |||
1280 | found_delref = 1; | |||
1281 | got_ref_close(ref); | |||
1282 | continue; | |||
1283 | } | |||
1284 | ||||
1285 | err = insert_ref(&new, &refs, ref, repo, | |||
1286 | got_ref_cmp_by_name, NULL((void *)0)); | |||
1287 | if (err || new == NULL((void *)0) /* duplicate */) | |||
1288 | got_ref_close(ref); | |||
1289 | if (err) | |||
1290 | goto done; | |||
1291 | } | |||
1292 | ||||
1293 | if (found_delref) { | |||
1294 | struct got_reflist_entry *re; | |||
1295 | size_t n; | |||
1296 | struct stat sb; | |||
1297 | ||||
1298 | n = fprintf(tmpf, "%s\n", GOT_PACKED_REFS_HEADER"# pack-refs with:"); | |||
1299 | if (n != sizeof(GOT_PACKED_REFS_HEADER"# pack-refs with:")) { | |||
1300 | err = got_ferror(f, GOT_ERR_IO6); | |||
1301 | goto done; | |||
1302 | } | |||
1303 | ||||
1304 | TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); ( re) = ((re)->entry.tqe_next)) { | |||
1305 | uint8_t hex[SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)]; | |||
1306 | ||||
1307 | if (got_sha1_digest_to_str(re->ref->ref.ref.sha1, hex, | |||
1308 | sizeof(hex)) == NULL((void *)0)) { | |||
1309 | err = got_error(GOT_ERR_BAD_REF_DATA57); | |||
1310 | goto done; | |||
1311 | } | |||
1312 | n = fprintf(tmpf, "%s ", hex); | |||
1313 | if (n != sizeof(hex)) { | |||
1314 | err = got_ferror(f, GOT_ERR_IO6); | |||
1315 | goto done; | |||
1316 | } | |||
1317 | n = fprintf(tmpf, "%s\n", re->ref->ref.ref.name); | |||
1318 | if (n != strlen(re->ref->ref.ref.name) + 1) { | |||
1319 | err = got_ferror(f, GOT_ERR_IO6); | |||
1320 | goto done; | |||
1321 | } | |||
1322 | } | |||
1323 | ||||
1324 | if (fflush(tmpf) != 0) { | |||
1325 | err = got_error_from_errno("fflush"); | |||
1326 | goto done; | |||
1327 | } | |||
1328 | ||||
1329 | if (fstat(fileno(f)(!__isthreaded ? ((f)->_file) : (fileno)(f)), &sb) != 0) { | |||
1330 | if (errno(*__errno()) != ENOENT2) { | |||
1331 | err = got_error_from_errno2("fstat", | |||
1332 | packed_refs_path); | |||
1333 | goto done; | |||
1334 | } | |||
1335 | sb.st_mode = GOT_DEFAULT_FILE_MODE(0100000 | 0000400|0000200 | 0000040 | 0000004); | |||
1336 | } | |||
1337 | ||||
1338 | if (fchmod(fileno(tmpf)(!__isthreaded ? ((tmpf)->_file) : (fileno)(tmpf)), sb.st_mode) != 0) { | |||
1339 | err = got_error_from_errno2("fchmod", tmppath); | |||
1340 | goto done; | |||
1341 | } | |||
1342 | ||||
1343 | if (rename(tmppath, packed_refs_path) != 0) { | |||
1344 | err = got_error_from_errno3("rename", tmppath, | |||
1345 | packed_refs_path); | |||
1346 | goto done; | |||
1347 | } | |||
1348 | } | |||
1349 | done: | |||
1350 | if (delref->lf == NULL((void *)0) && lf) | |||
1351 | unlock_err = got_lockfile_unlock(lf); | |||
1352 | if (f) { | |||
1353 | if (fclose(f) == EOF(-1) && err == NULL((void *)0)) | |||
1354 | err = got_error_from_errno("fclose"); | |||
1355 | } | |||
1356 | if (tmpf) { | |||
1357 | unlink(tmppath); | |||
1358 | if (fclose(tmpf) == EOF(-1) && err == NULL((void *)0)) | |||
1359 | err = got_error_from_errno("fclose"); | |||
1360 | } | |||
1361 | free(tmppath); | |||
1362 | free(packed_refs_path); | |||
1363 | free(line); | |||
1364 | got_ref_list_free(&refs); | |||
1365 | return err ? err : unlock_err; | |||
1366 | } | |||
1367 | ||||
1368 | static const struct got_error * | |||
1369 | delete_loose_ref(struct got_reference *ref, struct got_repository *repo) | |||
1370 | { | |||
1371 | const struct got_error *err = NULL((void *)0), *unlock_err = NULL((void *)0); | |||
1372 | const char *name = got_ref_get_name(ref); | |||
1373 | char *path_refs = NULL((void *)0), *path = NULL((void *)0); | |||
1374 | struct got_lockfile *lf = NULL((void *)0); | |||
1375 | ||||
1376 | path_refs = get_refs_dir_path(repo, name); | |||
1377 | if (path_refs == NULL((void *)0)) { | |||
1378 | err = got_error_from_errno2("get_refs_dir_path", name); | |||
1379 | goto done; | |||
1380 | } | |||
1381 | ||||
1382 | if (asprintf(&path, "%s/%s", path_refs, name) == -1) { | |||
1383 | err = got_error_from_errno("asprintf"); | |||
1384 | goto done; | |||
1385 | } | |||
1386 | ||||
1387 | if (ref->lf == NULL((void *)0)) { | |||
1388 | err = got_lockfile_lock(&lf, path); | |||
1389 | if (err) | |||
1390 | goto done; | |||
1391 | } | |||
1392 | ||||
1393 | /* XXX: check if old content matches our expectations? */ | |||
1394 | ||||
1395 | if (unlink(path) != 0) | |||
1396 | err = got_error_from_errno2("unlink", path); | |||
1397 | done: | |||
1398 | if (ref->lf == NULL((void *)0) && lf) | |||
1399 | unlock_err = got_lockfile_unlock(lf); | |||
1400 | ||||
1401 | free(path_refs); | |||
1402 | free(path); | |||
1403 | return err ? err : unlock_err; | |||
1404 | } | |||
1405 | ||||
1406 | const struct got_error * | |||
1407 | got_ref_delete(struct got_reference *ref, struct got_repository *repo) | |||
1408 | { | |||
1409 | const struct got_error *err = NULL((void *)0); | |||
1410 | struct got_reference *ref2; | |||
1411 | ||||
1412 | if (ref->flags & GOT_REF_IS_PACKED0x02) { | |||
1413 | err = delete_packed_ref(ref, repo); | |||
1414 | if (err) | |||
1415 | return err; | |||
1416 | ||||
1417 | err = got_ref_open(&ref2, repo, got_ref_get_name(ref), 0); | |||
1418 | if (err) { | |||
1419 | if (err->code == GOT_ERR_NOT_REF5) | |||
1420 | return NULL((void *)0); | |||
1421 | return err; | |||
1422 | } | |||
1423 | ||||
1424 | err = delete_loose_ref(ref2, repo); | |||
1425 | got_ref_close(ref2); | |||
1426 | return err; | |||
1427 | } else { | |||
1428 | err = delete_loose_ref(ref, repo); | |||
1429 | if (err) | |||
1430 | return err; | |||
1431 | ||||
1432 | err = got_ref_open(&ref2, repo, got_ref_get_name(ref), 0); | |||
1433 | if (err) { | |||
1434 | if (err->code == GOT_ERR_NOT_REF5) | |||
1435 | return NULL((void *)0); | |||
1436 | return err; | |||
1437 | } | |||
1438 | ||||
1439 | err = delete_packed_ref(ref2, repo); | |||
1440 | got_ref_close(ref2); | |||
1441 | return err; | |||
1442 | } | |||
1443 | } | |||
1444 | ||||
1445 | const struct got_error * | |||
1446 | got_ref_unlock(struct got_reference *ref) | |||
1447 | { | |||
1448 | const struct got_error *err; | |||
1449 | err = got_lockfile_unlock(ref->lf); | |||
1450 | ref->lf = NULL((void *)0); | |||
1451 | return err; | |||
1452 | } | |||
1453 | ||||
1454 | struct got_reflist_object_id_map { | |||
1455 | struct got_object_idset *idset; | |||
1456 | }; | |||
1457 | ||||
1458 | struct got_reflist_object_id_map_entry { | |||
1459 | struct got_reflist_head refs; | |||
1460 | }; | |||
1461 | ||||
1462 | static const struct got_error * | |||
1463 | add_object_id_map_entry(struct got_object_idset *idset, | |||
1464 | struct got_object_id *id, struct got_reflist_entry *re) | |||
1465 | { | |||
1466 | const struct got_error *err = NULL((void *)0); | |||
1467 | struct got_reflist_object_id_map_entry *ent; | |||
1468 | struct got_reflist_entry *new; | |||
1469 | ||||
1470 | ent = got_object_idset_get(idset, id); | |||
1471 | if (ent == NULL((void *)0)) { | |||
1472 | ent = malloc(sizeof(*ent)); | |||
1473 | if (ent == NULL((void *)0)) | |||
1474 | return got_error_from_errno("malloc"); | |||
1475 | ||||
1476 | TAILQ_INIT(&ent->refs)do { (&ent->refs)->tqh_first = ((void *)0); (&ent ->refs)->tqh_last = &(&ent->refs)->tqh_first ; } while (0); | |||
1477 | err = got_object_idset_add(idset, id, ent); | |||
1478 | if (err) | |||
1479 | return err; | |||
1480 | } | |||
1481 | ||||
1482 | err = got_reflist_entry_dup(&new, re); | |||
1483 | if (err) | |||
1484 | return err; | |||
1485 | ||||
1486 | TAILQ_INSERT_TAIL(&ent->refs, new, entry)do { (new)->entry.tqe_next = ((void *)0); (new)->entry. tqe_prev = (&ent->refs)->tqh_last; *(&ent->refs )->tqh_last = (new); (&ent->refs)->tqh_last = & (new)->entry.tqe_next; } while (0); | |||
1487 | return NULL((void *)0); | |||
1488 | } | |||
1489 | ||||
1490 | const struct got_error * | |||
1491 | got_reflist_object_id_map_create(struct got_reflist_object_id_map **map, | |||
1492 | struct got_reflist_head *refs, struct got_repository *repo) | |||
1493 | { | |||
1494 | const struct got_error *err = NULL((void *)0); | |||
1495 | struct got_object_idset *idset; | |||
1496 | struct got_object_id *id = NULL((void *)0); | |||
1497 | struct got_reflist_entry *re; | |||
1498 | ||||
1499 | idset = got_object_idset_alloc(); | |||
1500 | if (idset == NULL((void *)0)) | |||
1501 | return got_error_from_errno("got_object_idset_alloc"); | |||
1502 | ||||
1503 | *map = malloc(sizeof(**map)); | |||
1504 | if (*map == NULL((void *)0)) { | |||
1505 | got_object_idset_free(idset); | |||
1506 | return got_error_from_errno("malloc"); | |||
1507 | } | |||
1508 | (*map)->idset = idset; | |||
1509 | ||||
1510 | TAILQ_FOREACH(re, refs, entry)for((re) = ((refs)->tqh_first); (re) != ((void *)0); (re) = ((re)->entry.tqe_next)) { | |||
1511 | struct got_tag_object *tag = NULL((void *)0); | |||
1512 | ||||
1513 | err = got_ref_resolve(&id, repo, re->ref); | |||
1514 | if (err) | |||
1515 | goto done; | |||
1516 | ||||
1517 | err = add_object_id_map_entry(idset, id, re); | |||
1518 | if (err) | |||
1519 | goto done; | |||
1520 | ||||
1521 | if (strstr(got_ref_get_name(re->ref), "/tags/") == NULL((void *)0)) { | |||
1522 | free(id); | |||
1523 | id = NULL((void *)0); | |||
1524 | continue; | |||
1525 | } | |||
1526 | ||||
1527 | err = got_object_open_as_tag(&tag, repo, id); | |||
1528 | if (err) { | |||
1529 | if (err->code != GOT_ERR_OBJ_TYPE11) | |||
1530 | goto done; | |||
1531 | /* Ref points at something other than a tag. */ | |||
1532 | err = NULL((void *)0); | |||
1533 | tag = NULL((void *)0); | |||
1534 | free(id); | |||
1535 | id = NULL((void *)0); | |||
1536 | continue; | |||
1537 | } | |||
1538 | ||||
1539 | err = add_object_id_map_entry(idset, | |||
1540 | got_object_tag_get_object_id(tag), re); | |||
1541 | got_object_tag_close(tag); | |||
1542 | if (err) | |||
1543 | goto done; | |||
1544 | ||||
1545 | free(id); | |||
1546 | id = NULL((void *)0); | |||
1547 | } | |||
1548 | done: | |||
1549 | free(id); | |||
1550 | if (err) { | |||
1551 | got_reflist_object_id_map_free(*map); | |||
1552 | *map = NULL((void *)0); | |||
1553 | } | |||
1554 | return err; | |||
1555 | } | |||
1556 | ||||
1557 | struct got_reflist_head * | |||
1558 | got_reflist_object_id_map_lookup(struct got_reflist_object_id_map *map, | |||
1559 | struct got_object_id *id) | |||
1560 | { | |||
1561 | struct got_reflist_object_id_map_entry *ent; | |||
1562 | ent = got_object_idset_get(map->idset, id); | |||
1563 | if (ent) | |||
1564 | return &ent->refs; | |||
1565 | return NULL((void *)0); | |||
1566 | } | |||
1567 | ||||
1568 | static const struct got_error * | |||
1569 | free_id_map_entry(struct got_object_id *id, void *data, void *arg) | |||
1570 | { | |||
1571 | struct got_reflist_object_id_map_entry *ent = data; | |||
1572 | ||||
1573 | got_ref_list_free(&ent->refs); | |||
1574 | free(ent); | |||
1575 | return NULL((void *)0); | |||
1576 | } | |||
1577 | ||||
1578 | void | |||
1579 | got_reflist_object_id_map_free(struct got_reflist_object_id_map *map) | |||
1580 | { | |||
1581 | got_object_idset_for_each(map->idset, free_id_map_entry, NULL((void *)0)); | |||
1582 | got_object_idset_free(map->idset); | |||
1583 | free(map); | |||
1584 | } |