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