/* cmdutils.c: * **************************************************************** * Copyright (C) 2004 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/vu/safe.h" #include "hackerlab/vu/vu.h" #include "hackerlab/os/errno.h" #include "hackerlab/os/errno-to-string.h" #include "hackerlab/char/str.h" #include "tla/libarch/cmdutils.h" #include "tla/libarch/libraries.h" #include "tla/libarch/project-tree.h" #include "tla/libarch/patch-logs.h" #include "tla/libarch/pfs.h" #include "tla/libfsutils/dir-as-cwd.h" /* __STDC__ prototypes for static functions */ static int category_exists (struct arch_archive *arch, t_uchar * package); static int package_exists (struct arch_archive * arch, t_uchar * package); static int version_exists (struct arch_archive * arch, t_uchar * version_spec); static t_uchar * missing_string (enum arch_valid_package_name_types type); static t_uchar * name_string (enum arch_valid_package_name_types type); static enum arch_parse_package_name_type valid_to_parse (enum arch_valid_package_name_types type); /* Gather a fully-qualified revision name from the current tree * and input (e.g. patch-10) */ t_uchar * arch_fqrvsn_from_tree_and_input (t_uchar *cmd, t_uchar * string, t_uchar * dir) { t_uchar * tree_root = 0; t_uchar * tree_version = 0; t_uchar * tree_arch = 0; t_uchar * ret = 0; if (arch_valid_package_name (string, arch_req_archive, arch_req_patch_level, 0)) return str_save (0, string); tree_root = arch_tree_root (0, dir, 0); if (!tree_root) { safe_printfmt (2, "%s: not in a project tree\n dir: %s\n", cmd, directory_as_cwd (dir)); exit (2); } tree_version = arch_tree_version (tree_root); if (!tree_version) { safe_printfmt (2, "%s: no tree-version set\n tree: %s\n", cmd, tree_root); exit (2); } tree_arch = arch_parse_package_name (arch_ret_archive, 0, tree_version); if (arch_valid_package_name (string, arch_maybe_archive, arch_req_patch_level, 0)) { ret = arch_fully_qualify (tree_arch, string); } else if (arch_valid_package_name (string, arch_maybe_archive, arch_req_version, 0)) { t_uchar * archive = arch_parse_package_name (arch_ret_archive, tree_arch, string); t_uchar * version = arch_parse_package_name (arch_ret_package_version, 0, string); t_uchar * patch_level = arch_highest_patch_level (tree_root, archive, version); t_uchar * revision = str_alloc_cat_many (0, version, "--", patch_level, str_end); ret = arch_fully_qualify (archive, revision); lim_free (0, archive); lim_free (0, version); lim_free (0, patch_level); lim_free (0, revision); } else { ret = str_alloc_cat_many (0, tree_version, "--", string, str_end); if (!arch_valid_package_name (ret, arch_req_archive, arch_req_patch_level, 0)) { safe_printfmt (2, "%s: invalid revision or patch name (%s)\n", cmd, string); exit (2); } } lim_free (0, tree_root); lim_free (0, tree_version); lim_free (0, tree_arch); return ret; } static int category_exists (struct arch_archive *arch, t_uchar * package) { t_uchar * category = 0; rel_table categories = rel_table_nil; int x; category = arch_parse_package_name (arch_ret_category, 0, package); categories = arch_archive_categories (arch); for (x = 0; x < rel_n_records (categories); ++x) { if (!str_cmp (category, rel_peek_str (categories, x, 0))) { lim_free (0, category); rel_free_table (categories); return 1; } } return 0; } static int package_exists (struct arch_archive * arch, t_uchar * package) { t_uchar * category = 0; t_uchar * branch = 0; rel_table categories = rel_table_nil; rel_table branches = rel_table_nil; int x; int found_it = 0; category = arch_parse_package_name (arch_ret_category, 0, package); branch = arch_parse_package_name (arch_ret_package, 0, package); categories = arch_archive_categories (arch); for (x = 0; x < rel_n_records (categories); ++x) { if (!str_cmp (category, rel_peek_str (categories, x, 0))) break; } if (x < rel_n_records (categories)) { branches = arch_archive_branches (arch, category); for (x = 0; x < rel_n_records (branches); ++x) { if (!str_cmp (branch, rel_peek_str (branches, x, 0))) { found_it = 1; break; } } } lim_free (0, category); lim_free (0, branch); rel_free_table (categories); rel_free_table (branches); return found_it; } static int version_exists (struct arch_archive * arch, t_uchar * version_spec) { t_uchar * branch = 0; t_uchar * version = 0; rel_table versions = rel_table_nil; int x; int found_it = 0; branch = arch_parse_package_name (arch_ret_package, 0, version_spec); version = arch_parse_package_name (arch_ret_package_version, 0, version_spec); versions = arch_archive_versions (arch, branch); for (x = 0; x < rel_n_records (versions); ++x) { if (!str_cmp (version, rel_peek_str (versions, x, 0))) { found_it = 1; break; } } lim_free (0, branch); lim_free (0, version); rel_free_table (versions); return found_it; } /* Checks whether the specified item (and all its components) exist */ void arch_check_for (struct arch_archive * arch, enum arch_valid_package_name_types type, t_uchar * package) { t_uchar * spec = arch_parse_package_name (arch_ret_non_archive, "", package); int missing = -1; /* note fall-through behavior */ switch (type) { case arch_req_patch_level: if (arch_revision_exists (arch, spec)) break; else missing = arch_req_patch_level; default: if (!category_exists (arch, spec)) { missing=arch_req_category; break; } if (type == arch_req_category) break; if (!package_exists (arch, spec)) { missing = arch_req_package; break; } if (type == arch_req_package) break; if (!version_exists (arch, spec)) missing = arch_req_version; }; if (missing != -1) { arch_print_missing (arch, missing, type, spec); exit (2); } lim_free (0, spec); } void arch_print_missing (struct arch_archive * arch, enum arch_valid_package_name_types type, enum arch_valid_package_name_types supplied_type, t_uchar * spec) { t_uchar * name = missing_string (type); t_uchar * supplied_name = name_string (supplied_type); t_uchar * portion = arch_parse_package_name (valid_to_parse(type), arch->official_name, spec); t_uchar * nonarch = arch_parse_package_name (arch_ret_non_archive, "", spec); safe_printfmt (2, "No such %s (%s)\n", name, portion); safe_printfmt (2, " name: %s\n location: %s\n %s: %s\n", arch->name, arch->location, supplied_name, nonarch); lim_free (0, portion); lim_free (0, nonarch); } static t_uchar * missing_string (enum arch_valid_package_name_types type) { switch (type) { case arch_req_category: return "category"; break; case arch_req_package: return "package"; break; case arch_req_version: return "version"; break; case arch_req_patch_level: return "patchlevel"; break; default: panic ("missing_string: bad argument."); return 0; }; } static t_uchar * name_string (enum arch_valid_package_name_types type) { switch (type) { case arch_req_category: return "category"; break; case arch_req_package: return "package"; break; case arch_req_version: return "package-version"; break; case arch_req_patch_level: return "revision"; break; default: panic ("name_string: bad argument."); return 0; }; } static enum arch_parse_package_name_type valid_to_parse (enum arch_valid_package_name_types type) { switch (type) { case arch_req_category: return arch_ret_category; break; case arch_req_package: return arch_ret_package; break; case arch_req_version: return arch_ret_version; break; case arch_req_patch_level: return arch_ret_patch_level; break; default: panic ("valid_to_parse: bad argument."); return 0; }; } void arch_check_library_for_revision (t_uchar * archive, t_uchar * revision) { t_uchar * loc = arch_library_find (rel_table_nil, archive, revision, 0); if (loc) { lim_free (0, loc); } else { safe_printfmt (2, "Could not find revision in any library:\n%s/%s\n", archive, revision); exit (2); } } extern t_uchar * safe_tree_version (t_uchar * cmd_name) { t_uchar * tree_version = 0; t_uchar * tree_root = arch_tree_root (0, ".", 0); if (!tree_root) { safe_printfmt (2, "%s: not in a project tree\n dir: %s\n", cmd_name, directory_as_cwd (".")); exit (2); } tree_version = arch_tree_version (tree_root); if (!tree_version) { safe_printfmt (2, "%s: no tree-version set\n tree: %s\n", cmd_name, tree_root); exit (2); } lim_free (0, tree_root); return tree_version; } t_uchar * arch_determine_revision (struct arch_archive ** arch, t_uchar * default_archive, t_uchar * revision_spec, t_uchar * cmd_name) { t_uchar * revision = 0; t_uchar * archive = 0; t_uchar * new_revision_spec = 0; if (!arch_valid_package_name (revision_spec, arch_maybe_archive, arch_req_version, 1)) { if (!arch_valid_patch_level_name (revision_spec)) { safe_printfmt (2, "%s: illegal revision spec (%s)\n", cmd_name, revision_spec); exit (1); } else { t_uchar * tree_version = safe_tree_version (cmd_name); new_revision_spec = str_alloc_cat_many (0, tree_version, "--", revision_spec, str_end); revision_spec = new_revision_spec; } } archive = arch_parse_package_name (arch_ret_archive, default_archive, revision_spec); *arch = arch_archive_connect (archive, 0); lim_free (0, archive); if (arch_valid_package_name (revision_spec, arch_maybe_archive, arch_req_patch_level, 0)) { arch_check_for (*arch, arch_req_patch_level, revision_spec); revision = arch_parse_package_name (arch_ret_non_archive, 0, revision_spec); } else { t_uchar * version = 0; arch_check_for (*arch, arch_req_version, revision_spec); version = arch_parse_package_name (arch_ret_package_version, 0, revision_spec); revision = arch_archive_latest_revision (*arch, version, 2); if (!revision) { safe_printfmt (1, "%s: version has no revisions (%s)\n", cmd_name, version); exit (1); } lim_free (0, version); } lim_free (0, new_revision_spec); return revision; } void arch_check_directory (t_uchar *path, int check_write) { int errn; int mode = R_OK+X_OK; struct stat statb; if (check_write) mode+=W_OK; if (vu_stat (&errn, path, &statb) == -1) { if (errn == ENOENT) safe_printfmt (2, "Specified directory does not exist\nPath: %s\n", path); else safe_printfmt (2, "Error encountered accessing directory (%s)\nPath: %s\n", errno_to_string (errn), path); exit(2); } if (!S_ISDIR(statb.st_mode)) { safe_printfmt (2, "Specified path is not a directory\nPath: %s\n", path); exit (2); } if (access (path, mode) == -1) { safe_printfmt (2, "Error accessing specified directory (%s)\nDirectory: %s\n", errno_to_string (errno), path); exit (2); } } void arch_check_uri (t_uchar * uri) { if (arch_valid_uri (uri)) return; safe_printfmt (2, "URL invalid or unsupported: %s\n", uri); exit (2); } /** * Check whether the revision exists, using local data first, before * trying the archive * archive: the archive containg the revision * revision: the unqualified revision */ extern void arch_check_revision_local (t_uchar * archive, t_uchar * revision) { struct arch_archive * arch = 0; t_uchar * loc = arch_library_find (rel_table_nil, archive, revision, 0); if (loc) { lim_free (0, loc); return; } arch = arch_archive_connect (archive, 0); arch_check_for (arch, arch_req_patch_level, revision); arch_archive_close (arch); } /** * Check the archive to make sure it's suitable for normal use, and not just * for mirroring. */ extern int arch_check_arch (struct arch_archive * arch) { if (str_cmp (arch->name, arch->official_name)) { safe_printfmt (2, ("This archive should only be used as a mirror source or target.\n" "registered with the wrong name. To use it for other purposes, you should\n" "register it using the official name.\n" " name: %s\n" " official name: %s\n" " location: %s\n"), arch->name, arch->official_name, arch->location); return 1; } return 0; } /** * \brief free current and return replacement * * Factors out blocks of temporary variables into one shot. * \return replacement * \param current the string to be freed. * \param replacement the string to be returned */ t_uchar * str_replace (t_uchar *current, t_uchar *replacement) { lim_free (0, current); return replacement; } int arch_separate_arch_name_from_fqrvsn(const t_uchar *name, t_uchar **archive, t_uchar **fqrvsn) { t_uchar *slash = 0; t_uchar *tmp = str_save(0, name); lim_free(0, *archive); lim_free(0, *fqrvsn); if (0 == (slash = str_chr_index (tmp, '/'))) { *archive = str_save(0, ""); *fqrvsn = str_save(0, name); lim_free(0, tmp); return 1; } else { *slash = 0; *archive = str_save(0, tmp); *fqrvsn = str_save(0, slash + 1); lim_free(0, tmp); return 1; } return 0; } extern t_uchar * arch_interpret_delta_path (t_uchar ** arch, t_uchar ** rev, t_uchar * scratch_dir, t_uchar * default_archive, t_uchar * spec, t_uchar * cache_dir) { t_uchar * answer = 0; if (arch_valid_package_name (spec, arch_maybe_archive, arch_req_patch_level, 0)) { t_uchar * archive = 0; t_uchar * revision = 0; struct arch_archive *arch_struct = 0; archive = arch_parse_package_name (arch_ret_archive, default_archive, spec); revision = arch_parse_package_name (arch_ret_non_archive, 0, spec); arch_struct = arch_archive_connect (archive, 0); arch_check_for (arch_struct, arch_req_patch_level, revision); safe_printfmt (1, "* finding or making %s\n", spec); answer = arch_find_or_make_tmp_local_copy (1, scratch_dir, 0, cache_dir, arch_struct, archive, revision); arch_archive_close (arch_struct); if (arch) *arch = archive; else lim_free (0, archive); if (rev) *rev = revision; else lim_free (0, revision); } else if (arch_valid_patch_level_name (spec)) { t_uchar * archive = 0; t_uchar * revision = 0; t_uchar * fqvsn = 0; t_uchar * tree_version = 0; t_uchar * tree_root = 0; tree_root = arch_tree_root (0, ".", 0); if (!tree_root) { safe_printfmt (2, "tla delta: not in a project tree\n dir: %s\n", directory_as_cwd (".")); exit (2); } fqvsn = arch_tree_version (tree_root); if (!fqvsn) { safe_printfmt (2, "tla delta: no tree-version set\n tree: %s\n", tree_root); exit (2); } archive = arch_parse_package_name (arch_ret_archive, 0, fqvsn); tree_version = arch_parse_package_name (arch_ret_non_archive, 0, fqvsn); revision = str_alloc_cat_many (0, tree_version, "--", spec, str_end); safe_printfmt (1, "* finding or making %s\n", spec); safe_flush (1); answer = arch_find_or_make_tmp_local_copy (1, scratch_dir, 0, cache_dir, 0, archive, revision); if (arch) *arch = archive; else lim_free (0, archive); if (rev) *rev = revision; else lim_free (0, revision); lim_free (0, fqvsn); lim_free (0, tree_version); lim_free (0, tree_root); } else { arch_check_directory (spec, 0); answer = directory_as_cwd (spec); } return answer; } /* tag: Aaron Bentley Tue Dec 23 10:46:00 2003 (cmdutils.c) */