1 /* project-tree.c:
2 *
3 ****************************************************************
4 * Copyright (C) 2003 Tom Lord
5 *
6 * See the file "COPYING" for further information about
7 * the copyright and warranty status of this work.
8 */
9
10
11 #include "hackerlab/bugs/panic.h"
12 #include "hackerlab/os/errno.h"
13 #include "hackerlab/vu/safe.h"
14 #include "hackerlab/fs/cwd.h"
15 #include "hackerlab/fs/file-names.h"
16 #include "hackerlab/char/str.h"
17 #include "tla/libarch/archive.h"
18 #include "tla/libarch/namespace.h"
19 #include "tla/libarch/patch-logs.h"
20 #include "tla/libarch/changelogs.h"
21 #include "tla/libarch/inv-ids.h"
22 #include "tla/libarch/my.h"
23 #include "tla/libarch/project-tree.h"
24
25
26
27 /* These must agree about the format version number.
28 */
29 static const int arch_tree_format_vsn = 1;
30 static const char arch_tree_format_vsn_id[] = "1";
31 static char arch_tree_format_str[] = "Hackerlab arch project directory, format version 1.";
32
33
34
35
36 t_uchar *
arch_tree_format_string(void)37 arch_tree_format_string (void)
38 {
39 return (t_uchar *)arch_tree_format_str;
40 }
41
42
43 void
arch_init_tree(const t_uchar * tree_root)44 arch_init_tree (const t_uchar * tree_root)
45 {
46 t_uchar * arch_dir = 0;
47 t_uchar * arch_vsn_file = 0;
48 t_uchar * id_tagging_method_file = 0;
49 t_uchar * id_tagging_method_defaults = 0;
50 int out_fd;
51
52 arch_dir = file_name_in_vicinity (0, tree_root, "{arch}");
53 arch_vsn_file = file_name_in_vicinity (0, arch_dir, ".arch-project-tree");
54 id_tagging_method_file = file_name_in_vicinity (0, arch_dir, "=tagging-method");
55
56 safe_mkdir (arch_dir, 0777);
57 out_fd = safe_open (arch_vsn_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
58 safe_printfmt (out_fd, "%s\n", arch_tree_format_string ());
59 safe_close (out_fd);
60
61 id_tagging_method_defaults = arch_default_id_tagging_method_contents (arch_unspecified_id_tagging);
62 out_fd = safe_open (id_tagging_method_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
63 safe_printfmt (out_fd, "%s", id_tagging_method_defaults);
64 safe_close (out_fd);
65
66 lim_free (0, arch_dir);
67 lim_free (0, arch_vsn_file);
68 lim_free (0, id_tagging_method_defaults);
69 lim_free (0, id_tagging_method_file);
70 }
71
72
73 t_uchar *
arch_tree_root(enum arch_tree_state * state,const t_uchar * input_dir,int accurate)74 arch_tree_root (enum arch_tree_state * state,
75 const t_uchar * input_dir,
76 int accurate)
77 {
78 int here_fd;
79 int errn;
80 t_uchar * dir;
81 t_uchar * answer;
82
83 answer = 0;
84
85 if (input_dir)
86 {
87 here_fd = safe_open (".", O_RDONLY, 0);
88 safe_chdir (input_dir);
89 }
90
91 dir = (t_uchar *)current_working_directory (&errn, 0);
92 if (!dir)
93 panic ("unable to compute current working directory");
94
95 if (input_dir)
96 {
97 safe_fchdir (here_fd);
98 safe_close (here_fd);
99 }
100
101 while (1)
102 {
103 t_uchar * arch_dir;
104 t_uchar * arch_version_file;
105 t_uchar * next_dir;
106 int found_it;
107
108 arch_dir = file_name_in_vicinity (0, dir, "{arch}");
109 arch_version_file = file_name_in_vicinity (0, arch_dir, ".arch-project-tree");
110
111 found_it = (!safe_access (arch_dir, F_OK) && !safe_access (arch_version_file, F_OK));
112
113 lim_free (0, arch_dir);
114 lim_free (0, arch_version_file);
115
116 if (found_it)
117 {
118 answer = str_save (0, dir);
119 break;
120 }
121
122 if (!str_cmp (dir, "/"))
123 break;
124
125 next_dir = file_name_directory_file (0, dir);
126 lim_free (0, dir);
127 dir = next_dir;
128 next_dir = 0;
129 }
130
131 if (answer && accurate)
132 {
133 t_uchar * rc_name;
134 t_uchar * cd_name;
135 t_uchar * mc_name;
136
137 rc_name = file_name_in_vicinity (0, answer, "{arch}/++resolve-conflicts");
138 cd_name = file_name_in_vicinity (0, answer, "{arch}/++commit-definite");
139 mc_name = file_name_in_vicinity (0, answer, "{arch}/++mid-commit");
140
141 if (state)
142 {
143 if (!safe_access (rc_name, F_OK))
144 *state = arch_tree_in_resolve_conflicts;
145 else if (!safe_access (cd_name, F_OK))
146 *state = arch_tree_in_commit_definite;
147 else if (!safe_access (mc_name, F_OK))
148 *state = arch_tree_in_mid_commit;
149 else
150 *state = arch_tree_in_ok_state;
151 }
152
153 lim_free (0, rc_name);
154 lim_free (0, cd_name);
155 lim_free (0, mc_name);
156 }
157
158 lim_free (0, dir);
159 return answer;
160 }
161
162
163 t_uchar *
arch_tree_ctl_dir(const t_uchar * tree_root)164 arch_tree_ctl_dir (const t_uchar * tree_root)
165 {
166 return file_name_in_vicinity (0, tree_root, "{arch}");
167 }
168
169
170 int
arch_tree_has_meta_flag(const t_uchar * const tree_root)171 arch_tree_has_meta_flag (const t_uchar * const tree_root)
172 {
173 t_uchar * ctl_dir = 0;
174 t_uchar * meta_file = 0;
175 struct stat meta_stat;
176 int errn = 0;
177 int tree_answer = -69;
178 int answer = -69;
179
180 ctl_dir = arch_tree_ctl_dir (tree_root);
181 meta_file = file_name_in_vicinity (0, ctl_dir, "=meta");
182
183 if (0 == vu_stat (&errn, meta_file, &meta_stat))
184 {
185 if (meta_stat.st_size != 0)
186 {
187 safe_printfmt (2, "illegal ./{arch}/=meta file (non-empty)\n file: %s\n", meta_file);
188 panic ("abort");
189 }
190
191 tree_answer = 1;
192 }
193 else
194 {
195 if (errn == ENOENT)
196 tree_answer = 0;
197 else
198 panic ("strange i/o error");
199 }
200
201 if (tree_answer != 1)
202 {
203 answer = tree_answer;
204 }
205 else
206 {
207 answer = arch_my_allow_full_meta ();
208
209 if (!answer)
210 {
211 safe_printfmt (2, ("warning: ignoring =meta flag in tree\n"
212 " to enable full metadata patching (of owner,\n"
213 " group owner, and 12 permission bits rather\n"
214 " than 9 when a tree has an =meta file) create\n"
215 " the empty file:\n"
216 " ~/.arch-params/metadata-rules.\n"));
217 }
218 }
219
220 lim_free (0, ctl_dir);
221 lim_free (0, meta_file);
222
223 return answer;
224 }
225
226
227 void
arch_set_tree_version(const t_uchar * tree_root,const t_uchar * archive,const t_uchar * version)228 arch_set_tree_version (const t_uchar * tree_root,
229 const t_uchar * archive,
230 const t_uchar * version)
231 {
232 t_uchar * default_version_file = 0;
233 t_uchar * fqversion = 0;
234 int out_fd;
235
236 invariant (arch_valid_archive_name (archive));
237 invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));
238
239 default_version_file = file_name_in_vicinity (0, tree_root, "{arch}/++default-version");
240
241 fqversion = arch_fully_qualify (archive, version);
242
243 out_fd = safe_open (default_version_file, O_WRONLY | O_CREAT, 0666);
244
245 safe_ftruncate (out_fd, (long)0);
246 safe_printfmt (out_fd, "%s\n", fqversion);
247
248 safe_close (out_fd);
249
250 lim_free (0, default_version_file);
251 lim_free (0, fqversion);
252 }
253
254
255 t_uchar *
arch_tree_version(const t_uchar * tree_root)256 arch_tree_version (const t_uchar * tree_root)
257 {
258 t_uchar * default_version_file;
259 int in_fd;
260 t_uchar * file_contents;
261 size_t file_contents_len;
262 t_uchar * nl;
263
264 default_version_file = file_name_in_vicinity (0, tree_root, "{arch}/++default-version");
265
266 if (safe_access (default_version_file, F_OK))
267 {
268 lim_free (0, default_version_file);
269 return 0;
270 }
271
272 in_fd = safe_open (default_version_file, O_RDONLY, 0666);
273
274 file_contents = 0;
275 file_contents_len = 0;
276 safe_file_to_string (&file_contents, &file_contents_len, in_fd);
277
278 safe_close (in_fd);
279
280 nl = str_chr_index_n (file_contents, file_contents_len, '\n');
281 if (nl)
282 file_contents_len = nl - file_contents;
283
284 file_contents = lim_realloc (0, file_contents, file_contents_len + 1);
285 file_contents[file_contents_len] = 0;
286
287 invariant (arch_valid_package_name (file_contents, arch_req_archive, arch_req_version, 0));
288
289 lim_free (0, default_version_file);
290
291 return file_contents;
292 }
293
294
295 t_uchar *
arch_try_tree_version(const t_uchar * cmd)296 arch_try_tree_version (const t_uchar * cmd)
297 {
298 t_uchar * version_spec = 0;
299 t_uchar * tree_root = 0;
300
301 tree_root = arch_tree_root (0, ".", 0);
302
303 if (!tree_root)
304 {
305 safe_printfmt (2, "%s: not in a project tree\n", cmd);
306 exit (2);
307 }
308
309 version_spec = arch_tree_version (tree_root);
310
311 if (!version_spec)
312 {
313 safe_printfmt (2, "%s: tree has no default version set\n tree: %s\n",
314 cmd, tree_root);
315 exit (2);
316 }
317
318 lim_free (0, tree_root);
319
320 return version_spec;
321 }
322
323
324
325 void
arch_start_tree_commit(const t_uchar * tree_root,const t_uchar * log)326 arch_start_tree_commit (const t_uchar * tree_root,
327 const t_uchar * log)
328 {
329 int ign;
330 t_uchar * mid_commit_file = 0;
331 t_uchar * mid_commit_tmp = 0;
332 int out_fd;
333
334 mid_commit_file = file_name_in_vicinity (0, tree_root, "{arch}/++mid-commit");
335 mid_commit_tmp = file_name_in_vicinity (0, tree_root, "{arch}/,,mid-commit");
336
337 vu_unlink (&ign, mid_commit_tmp);
338 vu_unlink (&ign, mid_commit_file);
339
340 out_fd = safe_open (mid_commit_tmp, O_WRONLY | O_CREAT | O_EXCL, 0666);
341 safe_printfmt (out_fd, "%s", log);
342 safe_close (out_fd);
343
344 safe_rename (mid_commit_tmp, mid_commit_file);
345
346 lim_free (0, mid_commit_file);
347 }
348
349
350 void
arch_finish_tree_commit(const t_uchar * tree_root,const t_uchar * archive,const t_uchar * revision,const t_uchar * changelog_loc,int const full_meta)351 arch_finish_tree_commit (const t_uchar * tree_root,
352 const t_uchar * archive,
353 const t_uchar * revision,
354 const t_uchar * changelog_loc,
355 int const full_meta)
356 {
357 t_uchar * mid_commit_file = 0;
358 t_uchar * commit_definite_file = 0;
359
360 mid_commit_file = file_name_in_vicinity (0, tree_root, "{arch}/++mid-commit");
361 commit_definite_file = file_name_in_vicinity (0, tree_root, "{arch}/++commit-definite");
362
363 safe_rename (mid_commit_file, commit_definite_file);
364
365 if (changelog_loc)
366 {
367 const int full_meta = arch_tree_has_meta_flag (tree_root);
368 struct stat statb;
369 t_uchar * level = 0;
370 t_uchar * version = 0;
371 t_uchar * changelog_path = 0;
372 t_uchar * changelog_dir = 0;
373 t_uchar * changelog_tmp = 0;
374 mode_t mode;
375 int fd;
376
377 level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
378 version = arch_parse_package_name (arch_ret_package_version, 0, revision);
379
380 changelog_path = file_name_in_vicinity (0, tree_root, changelog_loc);
381 changelog_dir = file_name_directory_file (0, changelog_path);
382 changelog_tmp = file_name_in_vicinity (0, changelog_dir, ",,new-changelog");
383
384 safe_stat (changelog_path, &statb);
385 if (full_meta)
386 {
387 mode = statb.st_mode & 07777;
388 }
389 else
390 {
391 mode = statb.st_mode & 0777;
392 }
393
394 fd = safe_open (changelog_tmp, O_WRONLY | O_CREAT | O_TRUNC, mode);
395 safe_fchmod (fd, mode);
396 if (full_meta)
397 safe_fchown (fd, statb.st_uid, statb.st_gid);
398
399 arch_generate_changelog (fd, tree_root, 0, 0, level, commit_definite_file, archive, version);
400
401 safe_close (fd);
402
403 safe_rename (changelog_tmp, changelog_path);
404
405 lim_free (0, level);
406 lim_free (0, version);
407 lim_free (0, changelog_path);
408 lim_free (0, changelog_dir);
409 lim_free (0, changelog_tmp);
410 }
411
412 arch_copy_to_patch_log (tree_root, archive, revision, commit_definite_file);
413
414 safe_unlink (commit_definite_file);
415
416 lim_free (0, mid_commit_file);
417 lim_free (0, commit_definite_file);
418 }
419
420
421 void
arch_abort_tree_commit(const t_uchar * tree_root,const t_uchar * archive,const t_uchar * revision)422 arch_abort_tree_commit (const t_uchar * tree_root,
423 const t_uchar * archive,
424 const t_uchar * revision)
425 {
426 t_uchar * mid_commit_file = 0;
427
428 mid_commit_file = file_name_in_vicinity (0, tree_root, "{arch}/++mid-commit");
429
430 safe_unlink (mid_commit_file);
431
432 lim_free (0, mid_commit_file);
433 }
434
435 t_uchar *
arch_get_tree_fqrevision(const t_uchar * tree_root)436 arch_get_tree_fqrevision(const t_uchar *tree_root)
437 {
438 t_uchar * fqvsn = arch_tree_version(tree_root);
439 t_uchar * latest_log = 0;
440 t_uchar * tree_arch = 0;
441 t_uchar * tree_version = 0;
442 t_uchar * rvsnspec = 0;
443
444
445 tree_arch = arch_parse_package_name (arch_ret_archive, 0, fqvsn);
446 tree_version = arch_parse_package_name (arch_ret_non_archive, 0, fqvsn);
447
448 if (!fqvsn)
449 {
450 safe_printfmt (2, "unable to determine project tree identifier.\n tree: %s\n", tree_root);
451 return 0;
452 }
453
454 latest_log = arch_highest_patch_level (tree_root, tree_arch, tree_version);
455
456 if (!latest_log)
457 {
458 safe_printfmt (2, "tree shows no revisions in version\n version: %s\n", fqvsn);
459
460 }
461 else
462 rvsnspec = str_alloc_cat_many (0, fqvsn, "--", latest_log, str_end);
463
464
465 lim_free (0, tree_arch);
466 lim_free (0, tree_version);
467 lim_free (0, latest_log);
468
469
470 lim_free(0, fqvsn);
471
472 return rvsnspec;
473
474 }
475
476
477 t_uchar *
arch_get_fqrevision(const t_uchar * fqvsn)478 arch_get_fqrevision(const t_uchar *fqvsn)
479 {
480 t_uchar * rvsnspec = 0;
481 t_uchar * tree_arch = 0;
482 t_uchar * tree_version = 0;
483 t_uchar * latest_log = 0;
484
485 tree_arch = arch_parse_package_name (arch_ret_archive, 0, fqvsn);
486 tree_version = arch_parse_package_name (arch_ret_non_archive, 0, fqvsn);
487
488 invariant (!!tree_arch);
489
490 if (!arch_valid_package_name (tree_version, arch_no_archive, arch_req_version, 0))
491 {
492 rvsnspec = str_save (0, fqvsn);
493 }
494 else
495 {
496
497 rel_table archive_levels ;
498 struct arch_archive *arch = arch_archive_connect (tree_arch, 0);
499 invariant (!!arch);
500 archive_levels = arch_archive_revisions (arch, tree_version, 0);
501 if (!rel_n_records (archive_levels))
502 {
503 safe_printfmt (2, "(WEIRD ERROR!) archive has no revisions for version (%s/%s)\n",
504 tree_arch, tree_version);
505 exit (1);
506 }
507 latest_log = str_save (0, rel_peek_str (archive_levels, rel_n_records (archive_levels) - 1, 0));
508 rel_free_table (archive_levels);
509
510 arch_archive_close (arch);
511
512 if (!latest_log)
513 {
514 safe_printfmt (2, "tree shows no revisions in version\n version: %s\n", fqvsn);
515
516 }
517 else
518 rvsnspec = str_alloc_cat_many (0, fqvsn, "--", latest_log, str_end);
519 }
520
521 lim_free (0, tree_arch);
522 lim_free (0, tree_version);
523 lim_free (0, latest_log);
524
525
526 return rvsnspec;
527 }
528
529
530
531
532
533
534
535
536
537 /* tag: Tom Lord Mon May 12 10:12:38 2003 (project-tree.c)
538 */
539