/* namespace.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/fmt/cvt.h" #include "hackerlab/char/char-class.h" #include "hackerlab/char/str.h" #include "hackerlab/sort/qsort.h" #include "hackerlab/vu/safe.h" #include "libfsutils/safety.h" #include "libarch/my.h" #include "libarch/namespace.h" /* __STDC__ prototypes for static functions */ static int version_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l); static int ver_order (t_uchar c); static int ver_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l); static int arch_cmp_by_field (void * va, void * vb, void * vdata); static int arch_lvl_cmp_by_field (void * va, void * vb, void * vdata); static t_uchar const * over_opt_archive_prefix (t_uchar const * name); static t_uchar const * over_archive_name (t_uchar const * in); static t_uchar const * over_basename (t_uchar const * in); static t_uchar const * over_separator (t_uchar const * in); static t_uchar const * over_version (t_uchar const * in); static t_uchar const * over_patch_level (t_uchar const * in); static t_uchar const * find_version_start (t_uchar const * name); static t_uchar const * find_version_end (t_uchar const * version_start); int arch_valid_id (t_uchar * id) { t_uchar * non_empty_marker; /* This isn't exactly the same as larch's version -- * it's a superset. Both are wrong, really. :-) */ while (char_is_alnum (*id) || char_is_space (*id) || (char_is_punct (*id) && (*id != '<'))) ++id; if (*id != '<') return 0; ++id; non_empty_marker = id; while (char_is_alnum (*id) || (*id == '-') || (*id == '+') || (*id == '_') || (*id == '.')) ++id; if (id == non_empty_marker) return 0; if (*id != '@') return 0; ++id; non_empty_marker = id; while (char_is_alnum (*id) || (*id == '-') || (*id == '_') || (*id == '.')) ++id; if (*id != '>') return 0; ++id; if (*id) return 0; return 1; } int arch_valid_archive_name (t_uchar const * name) { t_uchar const * end; if (!name) return 0; end = over_archive_name (name); if (!end || *end) return 0; else return 1; } int arch_valid_patch_level_name (t_uchar const * const name) { t_uchar const * lvl; if (!str_cmp (name, "base-0")) return 1; if (str_cmp_prefix ("patch-", name) && str_cmp_prefix ("version-", name) && str_cmp_prefix ("versionfix-", name)) return 0; lvl = str_chr_index (name, '-'); if (!lvl) return 0; ++lvl; if (!*lvl) return 0; while (char_is_digit (*lvl)) ++lvl; if (*lvl) return 0; else return 1; } int arch_valid_config_name (t_uchar * name) { return is_non_upwards_relative_path (name); } /*(c arch_valid_package_name) * int arch_valid_package_name (t_uchar * name, * enum arch_valid_package_name_archive archive_disposition, * enum arch_valid_package_name_types type, * int tolerant); * * Return non-0 if `name' is a valid arch name of the sort described by * the other arguments. * * `archive_disposition' may be any of the values: * * arch_no_archive the name must not be fully qualified (must have no * no archive name component) * * arch_maybe_archive the name _may_ be fully qualified * * arch_req_archive the name _must_ be fully qualified * * * `type' may be any of the values: * * arch_req_category the name must have at least a category * * arch_req_package the name must have a category and may * have a branch label * * arch_req_version the name must have a version id * (and therefore must have a category and * may have a branch label) * * arch_req_patch_level the name must have a category, may have a * branch label, must have a version id, and * must have a revision name * * `tolerant': * * if 0, then the name may not have any components beyond those * required by `type'. For example, if type is `arch_req_category', * then the name may not have a branch label or version id. * * if 1, then the name may have additional components */ int arch_valid_package_name (t_uchar const * name, enum arch_valid_package_name_archive archive_disposition, enum arch_valid_package_name_types type, int tolerant) { int has_archive; int has_category; int has_branch; int has_version; int has_patch_level; t_uchar const * next; if (!name) return 0; has_archive = 0; has_category = 0; has_branch = 0; has_version = 0; has_patch_level = 0; next = over_opt_archive_prefix (name); has_archive = (next && (next != name)); switch (archive_disposition) { case arch_maybe_archive: { if (!next) return 0; break; } case arch_req_archive: { if (!next || (next == name)) return 0; break; } case arch_no_archive: { if (!next || (next != name)) return 0; break; } } name = next; name = over_basename (name); if (!name) return 0; else has_category = 1; if (*name) { name = over_separator (name); if (!name) return 0; next = over_basename (name); if (next) { has_branch = 1; name = next; if (*name) { name = over_separator (name); if (!name) return 0; } } if (*name) { name = over_version (name); if (!name) return 0; has_version = 1; if (*name) { name = over_separator (name); if (!name) return 0; name = over_patch_level (name); if (!name || *name) return 0; has_patch_level = 1; } } } switch (type) { case arch_req_category: { if (tolerant) return 1; else return !has_branch && !has_version; break; } case arch_req_package: { if (tolerant) return 1; else return !has_version; break; } case arch_req_version: { if (tolerant) return has_version; else return has_version && !has_patch_level; break; } case arch_req_patch_level: { return has_patch_level; } default: { panic ("arch_valid_package_name: bad argument."); return 0; } } } /*(c arch_is_system_package_name) * int arch_is_system_package_name (t_uchar * name); * * Return non-0 if `name' is a system package name. */ int arch_is_system_package_name (t_uchar * name) { t_uchar * package = 0; int answer; package = arch_parse_package_name (arch_ret_package, 0, name); answer = !!str_chr_index (name, '%'); lim_free (0, package); return answer; } /*(c arch_parse_package_name) * t_uchar * arch_parse_package_name (enum arch_parse_package_name_type type, * t_uchar * default_archive, * t_uchar * name); * * Parse a package name. * * `type' may be any of the values: * * arch_ret_archive Return the archive component of the name, * or the value `default_archive' if the name * is not fully qualified. * * arch_ret_non_archive Return all of the name except its (optional) * archive component. * * arch_ret_category Return just the category name. * * arch_ret_package Return the category--branch if the name has a * branch label, just category otherwise. * * arch_ret_version Return just the version id of the name. * * arch_ret_package_version Return the category(--branch)?--version * of the name * * arch_ret_patch_level Return just the revision id of the name * * arch_ret_fqversion Return the archive/categry--[branch--]version. * Panic if no version is provided. * * Note that there is no `arch_ret_branch' (since not all names have branch labels) * and no `arch_ret_package_patch_level' (since `arch_ret_non_archive' will do that * job). * * `default_archive' is the archive name to use for `arch_ret_archive' if the * name is not fully qualified. Typically, the value of `default_archive' * is taken from a command-line -A argument or the user's .arch-params/=default-archive * file. * * `name' is the name to parse. * */ /* helpers for parse_package_name */ t_uchar const * find_version_start (t_uchar const * name) { t_uchar const * version_start; t_uchar const * version_end; t_uchar const * t; name = over_opt_archive_prefix (name); version_end = over_basename (name); /* over category */ version_end = over_separator (version_end); invariant (!!version_end); version_start = version_end; t = over_basename (version_start); /* maybe over explicit branch */ if (t) { version_end = over_separator (t); invariant (!!version_end); version_start = version_end; } return version_start; } t_uchar const * find_version_end (t_uchar const * version_start) { t_uchar const * version_end = over_version (version_start); invariant (!!version_end); return version_end; } /** * \brief parse a package name * \param type what parts of the package to acquire * \param default_archive override a default if the package does not have an archive component * \param name the package name to parse * \return t_uchar * parsed, heap allocated value */ t_uchar * arch_parse_package_name (enum arch_parse_package_name_type type, t_uchar const * default_archive, t_uchar const * name) { invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1)); return arch_parse_name (type, default_archive, name); } /** * \brief parse a package name * \param type what parts of the package to acquire * \param default_archive override a default if the package does not have an archive component * \param name the package name to parse * \return t_uchar * parsed, heap allocated value */ t_uchar * arch_parse_name (enum arch_parse_package_name_type type, t_uchar const * default_archive, t_uchar const * name) { invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1) || (type == arch_ret_archive && arch_valid_archive_name (name))); switch (type) { default: panic ("bad argument to arch_parse_package_name"); return 0; /* notreached */ break; case arch_ret_archive: { t_uchar * slash; if (arch_valid_archive_name (name)) return str_save (0, name); slash = str_chr_index (name, '/'); if (!slash) { t_uchar * answer = 0; answer = arch_my_default_archive (default_archive); if (!answer) { safe_printfmt (2, "arch: no default archive set\n"); exit (2); } return answer; } else { return str_save_n (0, name, slash - name); } break; } case arch_ret_non_archive: { t_uchar * slash; slash = str_chr_index (name, '/'); if (!slash) return str_save (0, name); else return str_save (0, slash + 1); break; } case arch_ret_category: { t_uchar const * cat_end; name = over_opt_archive_prefix (name); cat_end = over_basename (name); return str_save_n (0, name, cat_end - name); break; } case arch_ret_branch: { t_uchar const * branch_end; name = over_opt_archive_prefix (name); name = over_basename (name); /* over category */ name = over_separator (name); if (!name) return str_save (0, ""); branch_end = over_basename (name); if (!branch_end) return str_save (0, ""); return str_save_n (0, name, branch_end - name); break; } case arch_ret_package: { t_uchar const * branch_end; t_uchar const * t; name = over_opt_archive_prefix (name); branch_end = over_basename (name); t = over_separator (branch_end); if (!t) return str_save (0, name); /* only category provided */ branch_end = over_basename (t); /* over category */ if (!branch_end) return str_save_n (0, name, (t - 2) - name); /* category--version */ else return str_save_n (0, name, branch_end - name); break; } case arch_ret_version: { t_uchar const * version_start = find_version_start (name); t_uchar const * version_end = find_version_end (version_start); return str_save_n (0, version_start, version_end - version_start); break; } case arch_ret_patch_level: { t_uchar const * t; name = over_opt_archive_prefix (name); invariant (!!name); name = over_basename (name); invariant (!!name); name = over_separator (name); invariant (!!name); t = over_basename (name); /* maybe over explicit branch */ if (t) { name = over_separator (t); invariant (!!name); } name = over_version (name); invariant (!!name); invariant (name[0]); name = over_separator (name); invariant (!!name); invariant (name[0]); return str_save (0, name); break; } case arch_ret_package_version: { t_uchar const * version_start; t_uchar const * version_end; t_uchar const * t; name = over_opt_archive_prefix (name); version_end = over_basename (name); /* over category */ version_end = over_separator (version_end); invariant (!!version_end); version_start = version_end; t = over_basename (version_start); /* maybe over explicit branch */ if (t) { version_end = over_separator (t); invariant (!!version_end); version_start = version_end; } version_end = over_version (version_end); invariant (!!version_end); return str_save_n (0, name, version_end - name); break; } case arch_ret_fqversion: { t_uchar const * version_end = find_version_end (find_version_start (name)); return str_save_n (0, name, version_end - name); break; } } } t_uchar * arch_fully_qualify (t_uchar const * const archive, t_uchar const * const name) { invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1)); if (archive) invariant (arch_valid_archive_name (archive)); if (str_chr_index (name, '/')) return str_save (0, name); else { t_uchar * default_archive = str_save (0, archive); if (!str_length (default_archive)) default_archive = arch_my_default_archive (NULL); if (!default_archive) panic ("arch: no default archive set"); return str_realloc_cat_many (0, default_archive, "/", name, str_end); } } int arch_names_cmp (t_uchar const * a, t_uchar const * b) { t_uchar const * a_nxt; t_uchar const * b_nxt; int res; /* compare archive name */ a_nxt = over_opt_archive_prefix (a); b_nxt = over_opt_archive_prefix (b); res = str_cmp_n (a, a_nxt - a, b, b_nxt - b); if (res) return res; a = a_nxt; b = b_nxt; /* compare category name */ a_nxt = over_basename (a); b_nxt = over_basename (b); res = str_cmp_n (a, a_nxt - a, b, b_nxt - b); if (res) return res; if (!*a_nxt && !*b_nxt) return 0; if (!*a_nxt) return -1; if (!*b_nxt) return 1; a = over_separator (a_nxt); b = over_separator (b_nxt); /* compare branch names */ a_nxt = over_basename (a); b_nxt = over_basename (b); if (a_nxt && !b_nxt) { /* a has a branch, b does not */ return 1; } else if (!a_nxt && b_nxt) { /* b has a branch, a does not */ return -1; } else if (a_nxt && b_nxt) { /* both have branch names */ res = str_cmp_n (a, a_nxt - a, b, b_nxt - b); if (res) return res; if (!*a_nxt && !*b_nxt) return 0; if (!*a_nxt) return -1; if (!*b_nxt) return 1; a = over_separator (a_nxt); b = over_separator (b_nxt); } /* compare version names */ a_nxt = over_version (a); b_nxt = over_version (b); res = version_cmp_n (a, a_nxt - a, b, b_nxt - b); if (res < 0) return -1; else if (res > 0) return 1; a = over_separator (a_nxt); b = over_separator (b_nxt); if (!a && !b) return 0; else if (a && !b) return 1; else if (!a && b) return -1; /* compare patch level phase */ { t_uchar * a_dash; t_uchar * b_dash; a_dash = str_chr_index (a, '-'); b_dash = str_chr_index (b, '-'); invariant (a && b); res = str_cmp_n (a, a_dash - a, b, b_dash - b); if (res) return res; a = a_dash + 1; b = b_dash + 1; } /* compare patch level numbers */ { int errn; t_ulong a_lvl; t_ulong b_lvl; invariant (!cvt_decimal_to_ulong (&errn, &a_lvl, a, str_length (a))); invariant (!cvt_decimal_to_ulong (&errn, &b_lvl, b, str_length (b))); if (a_lvl < b_lvl) return -1; else if (a_lvl > b_lvl) return 1; else return 0; } } static int version_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l) { t_uchar * a_colon; t_uchar * b_colon; t_ulong a_epoch; t_ulong b_epoch; int errn; a_colon = str_chr_index_n (a, a_l, ':'); if (a_colon) { invariant (!cvt_decimal_to_ulong (&errn, &a_epoch, a, a_colon - a)); a_l -= (a_colon - a) - 1; a = a_colon + 1; } else a_epoch = 0; b_colon = str_chr_index (b, ':'); if (b_colon) { invariant (!cvt_decimal_to_ulong (&errn, &b_epoch, b, b_colon - b)); b_l -= (b_colon - b) - 1; b = b_colon + 1; } else b_epoch = 0; if (a_epoch < b_epoch) return -1; else if (a_epoch > b_epoch) return 1; else return ver_cmp_n (a, a_l, b, b_l); } static int ver_order (t_uchar c) { if (c == '~') return -1; else if (char_is_digit (c)) return 0; else if (char_is_alpha (c)) return c; else return c + 256; } static int ver_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l) { while ((*a && a_l) || (*b && b_l)) { int first_diff = 0; while ((*a && a_l && !char_is_digit (*a)) || (*b && b_l && !char_is_digit (*b))) { int ac, bc; ac = (*a && a_l) ? ver_order (*a) : 0; bc = (*b && b_l) ? ver_order (*b) : 0; if (ac != bc) return ac - bc; ++a, --a_l; ++b, --b_l; } while ((*a == '0') && a_l) ++a, --a_l; while ((*b == '0') && b_l) ++b, --b_l; while (a_l && char_is_digit (*a) && b_l && char_is_digit (*b)) { if (!first_diff) first_diff = *a - *b; ++a, --a_l; ++b, --b_l; } if (a_l && char_is_digit (*a)) return 1; else if (b_l && char_is_digit (*b)) return -1; else if (first_diff) return first_diff; } return 0; } struct arch_sort_spec { int reverse_p; int field; }; void arch_sort_table_by_name_field (int reverse_p, rel_table table, int field) { struct arch_sort_spec spec; spec.reverse_p = reverse_p; spec.field = field; quicksort ((void *)table, rel_n_records (table), sizeof (rel_record), arch_cmp_by_field, (void *)&spec); } static int arch_cmp_by_field (void * va, void * vb, void * vdata) { rel_record * a; rel_record * b; struct arch_sort_spec * spec; a = (rel_record *)va; b = (rel_record *)vb; spec = (struct arch_sort_spec *)vdata; if (spec->reverse_p) { return -arch_names_cmp ((*a)[spec->field], (*b)[spec->field]); } else { return arch_names_cmp ((*a)[spec->field], (*b)[spec->field]); } } int arch_patch_lvl_cmp (t_uchar * a, t_uchar * b) { t_uchar * a_dash; t_uchar * b_dash; int res; t_ulong a_lvl; t_ulong b_lvl; int errn; a_dash = str_chr_index (a, '-'); b_dash = str_chr_index (b, '-'); invariant (a_dash && b_dash); res = str_cmp_n (a, a_dash - a, b, b_dash - b); if (res) return res; invariant (!cvt_decimal_to_ulong (&errn, &a_lvl, a_dash + 1, str_length (a_dash + 1))); invariant (!cvt_decimal_to_ulong (&errn, &b_lvl, b_dash + 1, str_length (b_dash + 1))); if (a_lvl < b_lvl) return -1; else if (a_lvl > b_lvl) return 1; else return 0; } void arch_sort_table_by_patch_level_field (int reverse_p, rel_table table, int field) { struct arch_sort_spec spec; spec.reverse_p = reverse_p; spec.field = field; quicksort ((void *)table, rel_n_records (table), sizeof (rel_record), arch_lvl_cmp_by_field, (void *)&spec); } static int arch_lvl_cmp_by_field (void * va, void * vb, void * vdata) { rel_record * a; rel_record * b; struct arch_sort_spec * spec; a = (rel_record *)va; b = (rel_record *)vb; spec = (struct arch_sort_spec *)vdata; if (spec->reverse_p) { return -arch_patch_lvl_cmp ((*a)[spec->field], (*b)[spec->field]); } else { return arch_patch_lvl_cmp ((*a)[spec->field], (*b)[spec->field]); } } rel_table arch_pick_archives_by_field (rel_table in, int field) { rel_table answer = 0; int x; for (x = 0; x < rel_n_records (in); ++x) { if (arch_valid_archive_name (in[x][field])) rel_add_records (&answer, rel_copy_record (in[x]), 0); } return answer; } rel_table arch_pick_categories_by_field (rel_table in, int field) { rel_table answer = 0; int x; for (x = 0; x < rel_n_records (in); ++x) { if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_category, 0)) rel_add_records (&answer, rel_copy_record (in[x]), 0); } return answer; } rel_table arch_pick_branches_by_field (rel_table in, int field) { rel_table answer = 0; int x; for (x = 0; x < rel_n_records (in); ++x) { if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_package, 0)) rel_add_records (&answer, rel_copy_record (in[x]), 0); } return answer; } rel_table arch_pick_versions_by_field (rel_table in, int field) { rel_table answer = 0; int x; for (x = 0; x < rel_n_records (in); ++x) { if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_version, 0)) rel_add_records (&answer, rel_copy_record (in[x]), 0); } return answer; } rel_table arch_pick_revisions_by_field (rel_table in, int field) { rel_table answer = 0; int x; for (x = 0; x < rel_n_records (in); ++x) { if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_patch_level, 0)) rel_add_records (&answer, rel_copy_record (in[x]), 0); } return answer; } rel_table arch_pick_patch_levels_by_field (rel_table in, int field) { rel_table answer = 0; int x; for (x = 0; x < rel_n_records (in); ++x) { if (arch_valid_patch_level_name (in[x][field])) rel_add_records (&answer, rel_copy_record (in[x]), 0); } return answer; } enum arch_patch_level_type arch_analyze_patch_level (t_ulong * n, t_uchar * patch_level) { int ign; enum arch_patch_level_type type; t_uchar * n_str; if (!str_cmp ("base-0", patch_level)) { *n = 0; return arch_is_base0_level; } else if (!str_cmp_prefix ("patch-", patch_level)) { type = arch_is_patch_level; n_str = patch_level + sizeof ("patch-") - 1; } else if (!str_cmp_prefix ("version-", patch_level)) { type = arch_is_version_level; n_str = patch_level + sizeof ("version-") - 1; } else if (!str_cmp_prefix ("versionfix-", patch_level)) { type = arch_is_versionfix_level; n_str = patch_level + sizeof ("versionfix-") - 1; } else panic ("illegal patch_level in arch_analyze_patch_level"); if (cvt_decimal_to_ulong (&ign, n, n_str, str_length (n_str))) panic ("illegal patch_level in arch_analyze_patch_level"); return type; } int arch_cmp_revision (t_uchar *a, t_uchar *b) { enum arch_patch_level_type a_type; enum arch_patch_level_type b_type; t_ulong a_level; t_ulong b_level; a_type = arch_analyze_patch_level (&a_level, a); b_type = arch_analyze_patch_level (&b_level, b); if (a_type != b_type) return arch_cmp_patch_level_type (a_type, b_type); else if (a_level < b_level) return -1; if (b_level == a_level) return 0; else return 1; } int arch_cmp_patch_level_type (enum arch_patch_level_type a, enum arch_patch_level_type b) { if (a == b) return 0; else if (a < b) return -1; else return 1; } t_uchar * arch_form_patch_level (enum arch_patch_level_type type, t_ulong n) { t_uchar n_buf[64]; cvt_ulong_to_decimal (n_buf, n); switch (type) { case arch_is_base0_level: { return str_save (0, "base-0"); } case arch_is_patch_level: { return str_alloc_cat (0, "patch-", n_buf); } case arch_is_version_level: { return str_alloc_cat (0, "version-", n_buf); } case arch_is_versionfix_level: { return str_alloc_cat (0, "versionfix-", n_buf); } default: { panic ("not reached in arch_form_patch_level"); return 0; } } } static t_uchar const * over_opt_archive_prefix (t_uchar const * name) { t_uchar * slash; t_uchar const * archive_name_end; slash = str_chr_index (name, '/'); if (!slash) return name; archive_name_end = over_archive_name (name); if (archive_name_end != slash) return 0; return slash + 1; } #define char_is_alnum_or_dash(c) (char_is_alnum (c) || (c == '-')) static t_uchar const * over_archive_name (t_uchar const * in) { if (!char_is_alnum_or_dash (*in)) return 0; while (1) { while (char_is_alnum_or_dash (*in)) ++in; if (*in != '.' && *in != '_') break; if (!char_is_alnum_or_dash (in[1])) return 0; ++in; } if (*in != '@') return 0; ++in; while (char_is_alnum_or_dash(*in) || (*in == '.')) { if ((*in == '-') && (in[1] == '-')) break; ++in; } if (!*in || (*in == '/')) return in; if ((in[0] != '-') || (in[1] != '-')) return 0; in += 2; while (char_is_alnum_or_dash(*in) || (*in == '.')) ++in; if (*in && (*in != '/')) return 0; return in; } static t_uchar const * over_basename (t_uchar const * in) { if (!char_is_alpha (*in)) return 0; ++in; while (1) { if (!*in) return in; else if ((char_is_alnum (*in)) || (*in == '%') || (*in == ',')) ++in; else if (*in == '-') { if (in[1] == '-') return in; else ++in; } else break; } return 0; } static t_uchar const * over_separator (t_uchar const * in) { if ((*in == '-') && (in[1] == '-')) return in + 2; else return 0; } static t_uchar const * over_version (t_uchar const * in) { if (!char_is_digit (*in)) return 0; ++in; while (1) { if (!*in) return in; else if ((char_is_alnum (*in)) || (*in == '.') || (*in == '+') || (*in == ':') || (*in == '~')) ++in; else if (*in == '-') { if (in[1] == '-') return in; else ++in; } else break; } return 0; } static t_uchar const * over_patch_level (t_uchar const * in) { if (!str_cmp (in, "base-0")) return in + sizeof ("base-0") - 1; else { int prefix_len; if (!str_cmp_prefix ("patch-", in)) prefix_len = sizeof ("patch-") - 1; else if (!str_cmp_prefix ("version-", in)) prefix_len = sizeof ("version-") - 1; else if (!str_cmp_prefix ("versionfix-", in)) prefix_len = sizeof ("versionfix-") - 1; else return 0; in += prefix_len; if (!char_is_digit (*in)) return 0; while (char_is_digit (*in)) ++in; if (*in) return 0; return in; } } /* tag: Tom Lord Mon May 12 10:17:47 2003 (namespace.c) */