/* library-txn.c: * * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2 **************************************************************** * Copyright (C) 2003 Tom Lord * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "hackerlab/bugs/panic.h" #include "hackerlab/char/str.h" #include "hackerlab/char/char-class.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/vu/safe.h" #include "libfsutils/rmrf.h" #include "libfsutils/link-tree.h" #include "libfsutils/ensure-dir.h" #include "libfsutils/tmp-files.h" #include "libfsutils/copy-file.h" #include "libfsutils/same.h" #include "libarch/arch.h" #include "libarch/chatter.h" #include "libarch/namespace.h" #include "libarch/patch-logs.h" #include "libarch/build-revision.h" #include "libarch/invent.h" #include "libarch/libraries.h" #include "libarch/project-tree.h" #include "libarch/apply-changeset.h" #include "libarch/inode-sig.h" #include "libarch/replay.h" #include "libarch/my.h" #include "libarch/library-txn.h" /* __STDC__ prototypes for static functions */ static void library_txn_callback (void * vfp, char * fmt, va_list ap); /*(c arch_library_add) * t_uchar * arch_library_add (int chatter_fd, * struct arch_archive * arch, * t_uchar * revision, * t_uchar * opt_library_spec, * t_uchar * opt_same_dev_path) * * Return the path to the _library_ where REVISION from ARCH * should be added by library-add. * * OPT_LIBRARY_SPEC is the (optional) path to a library to which * the user explicitly requested addition. If not 0, we essentially * validate that parameter. * * OPT_SAME_DEV_PATH is the (optional) path to a file or directory * on the device to which we want to library-add. This is for * things like --link: we need to make sure that the revision * is added to a library on the same device. * * Subject to the constraints of the OPT_ parameters, the users * library path is searched (in add order): * * If the revision exists in some library, the first such library * is returned. * * Otherwise, if the revision's version exists in some library, the * first such library is returned. * * Otherwise, if the revision's branch exists in some library, the * first such library is returned. * * Otherwise, if the revision's category exists in some library, the * first such library is returned. * * Otherwise, if the revision's archive exists in some library, the * first such library is returned. * * Otherwise, the first library in the (qualifying part of) the path * is returned. * * This function will not return 0. If no place can be found to add * the revision subject to the constraints of the OPT_* parameters, * an error message is printed and the process exitted with status 2. */ t_uchar * arch_library_add_choice (arch_patch_id * revision, t_uchar const * const opt_library, t_uchar const * const opt_same_dev_path) { rel_table full_user_path = 0; rel_table search_path = 0; t_uchar * target_library = 0; if (opt_library && opt_same_dev_path) { if (!on_same_device (opt_library, opt_same_dev_path)) { safe_printfmt (2, "baz: indicated library is not on the right device for dir\n lib %s\n dir %s\n", opt_library, opt_same_dev_path); exit (2); } } /* For the library path on which to search for * a place to add this revision. * * Note that the path is in general a subsequence of * the user's full path: */ full_user_path = arch_my_library_path (arch_library_path_add_order); if (opt_library) { int x; /* The user wants a specific library. * * Make sure it is really on the user's library path, * then make that one library the entire search path. */ for (x = 0; x < rel_n_records (full_user_path); ++x) { if (names_same_inode (opt_library, full_user_path[x][0])) break; } if (x == rel_n_records (full_user_path)) { safe_printfmt (2, "baz: indicated library is not on library path\n dir %s\n", opt_library); exit (2); } rel_add_records (&search_path, rel_make_record (full_user_path[x][0], 0), 0); } else search_path=make_search_path(full_user_path, opt_same_dev_path, 0); if (!search_path) { if (opt_same_dev_path) safe_printfmt (2, "baz: no library on the same device as dir\n dir %s\n", opt_same_dev_path); else safe_printfmt (2, "baz: no library to add to\n"); exit (2); } /* Now the search path is set. * * If the requested revision is already somewhere on that path, * we're done. */ { arch_project_tree_t * already_there = 0; already_there = arch_library_find (search_path, revision, 1); if (already_there) { t_uchar * there_vsn = 0; t_uchar * there_branch = 0; t_uchar * there_category = 0; t_uchar * there_archive = 0; there_vsn = file_name_directory_file (0, already_there->root); there_branch = file_name_directory_file (0, there_vsn); there_category = file_name_directory_file (0, there_branch); there_archive = file_name_directory_file (0, there_category); target_library = file_name_directory_file (0, there_archive); lim_free (0, there_vsn); lim_free (0, there_branch); lim_free (0, there_category); lim_free (0, there_archive); arch_project_tree_delete (already_there); } /* Not there -- we must decide where to add it */ else { t_uchar *best_guess = arch_find_best_library(arch_patch_id_archive (revision), search_path, arch_patch_id_revision (revision)); target_library = str_save (0, best_guess); } } rel_free_table (full_user_path); rel_free_table (search_path); return target_library; } /** * This function is similar to the above, but it may return NULL * * It finds the best greedy library to add a given revision to * If opt_same_dev_path has a value, matches are restricted to libraries on the * device containing this path */ extern t_uchar * arch_library_greedy_add_choice(arch_patch_id * revision, t_uchar const * const opt_same_dev_path, int require_greedy) { rel_table full_user_path = 0; rel_table search_path = 0; t_uchar * target_library = 0; t_uchar * best_guess = 0; full_user_path = arch_my_library_path (arch_library_path_add_order); search_path = make_search_path (full_user_path, opt_same_dev_path, require_greedy); best_guess = arch_find_best_library (arch_patch_id_archive (revision), search_path, arch_patch_id_revision (revision)); if (best_guess) target_library=str_save(0, best_guess); rel_free_table (full_user_path); rel_free_table (search_path); return target_library; } /* Produces a search path containing only libraries that matches the given * criteria: * if opt_same_dev_path has a value, canditates must be on the same device as * the provided path * if require_greedy is true, candidates must be greedy * returns 0 if there are no matching candidates */ rel_table make_search_path(rel_table full_user_path, t_uchar const *const opt_same_dev_path, int require_greedy) { rel_table search_path = 0; int x; for (x = 0; x < rel_n_records (full_user_path); ++x) { if (opt_same_dev_path && !on_same_device (opt_same_dev_path, full_user_path[x][0])) continue; if (require_greedy && !arch_library_is_greedy(full_user_path[x][0])) continue; rel_add_records (&search_path, rel_make_record (full_user_path[x][0], 0), 0); } return search_path; } t_uchar * arch_library_find_picky (t_uchar * archive, t_uchar * revision, int check_inode_sigs, t_uchar * opt_same_dev_path, int require_greedy) { arch_project_tree_t * answer_tree; t_uchar * answer = 0; rel_table search_path = 0; rel_table full_user_path = arch_my_library_path (arch_library_path_add_order); arch_patch_id patch_id; search_path = make_search_path (full_user_path, opt_same_dev_path, require_greedy); rel_free_table (full_user_path); if (!search_path) return answer; arch_patch_id_init_archive (&patch_id, archive, revision); answer_tree = arch_library_find (search_path, &patch_id, check_inode_sigs); answer = str_save (0, answer_tree->root); arch_project_tree_delete (answer_tree); arch_patch_id_finalise (&patch_id); rel_free_table (search_path); return answer; } /* We search along the path and pick the first revision * with the most specific already-present stuff (same version, * branch, category, or archive) -- defaulting to the first in the * path. */ t_uchar * arch_find_best_library(t_uchar const * const archive, rel_table search_path, t_uchar const * const revision) { int guess_priority; int x; t_uchar * best_guess = 0; t_uchar * category = 0; t_uchar * branch = 0; t_uchar * version = 0; if (rel_n_records (search_path) == 0) return 0; if (rel_n_records (search_path) == 1) return search_path[0][0]; category = arch_parse_package_name (arch_ret_category, 0, revision); branch = arch_parse_package_name (arch_ret_package, 0, revision); version = arch_parse_package_name (arch_ret_package_version, 0, revision); best_guess = search_path[0][0]; guess_priority = 0; for (x = 0; x < rel_n_records (search_path); ++x) { if (arch_library_has_version (search_path[x][0], archive, version)) { best_guess = search_path[x][0]; break; } else if (arch_library_has_branch (search_path[x][0], archive, branch)) { best_guess = search_path[x][0]; guess_priority = 3; } else if ((guess_priority < 2) && arch_library_has_category (search_path[x][0], archive, category)) { best_guess = search_path[x][0]; guess_priority = 2; } else if ((guess_priority < 1) && arch_library_has_archive (search_path[x][0], archive)) { best_guess = search_path[x][0]; guess_priority = 1; } } lim_free (0, category); lim_free (0, branch); lim_free (0, version); return best_guess; } void arch_library_add (int chatter_fd, int no_patch_noise, struct arch_archive * arch, t_uchar const * const revision, t_uchar const * const opt_library, t_uchar const * const opt_same_dev_path, int sparse, int escape_classes) { arch_patch_id * patch_id = arch_patch_id_new_archive (arch->official_name, revision); t_uchar * dest_library = arch_library_add_choice (patch_id, opt_library, opt_same_dev_path); t_uchar * dest_directory = arch_library_revision_dir_in_lib (dest_library, patch_id); if (!safe_access (dest_directory, F_OK)) { lim_free (0, dest_library); lim_free (0, dest_directory); talloc_free (patch_id); return; } if (sparse < 0) sparse = arch_library_is_sparse (dest_library); arch_build_library_revision(no_patch_noise, dest_directory, arch, patch_id, opt_library, opt_same_dev_path, sparse, library_txn_callback, escape_classes); lim_free (0, dest_library); lim_free (0, dest_directory); talloc_free (patch_id); } static void library_txn_callback (void * vfp, char * fmt, va_list ap) { int fd; fd = (int)(t_ulong)vfp; if (fd >= 0) { safe_printfmt_va_list (fd, fmt, ap); safe_flush (1); } } /* tag: Tom Lord Fri May 30 13:50:27 2003 (library-txn.c) */