// // aegis - project change supervisor // Copyright (C) 2004-2009, 2011, 2012, 2014 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include #include #include #include #include #include #include static int name_has_numeric_suffix(string_ty *name, string_ty **left, long *right) { char *ep; ep = name->str_text + name->str_length; while (ep > name->str_text && isdigit((unsigned char)ep[-1])) --ep; if (!*ep) // still pointing to end of string return 0; if (ep < name->str_text + 2 || !ispunct((unsigned char)ep[-1])) return 0; *left = str_n_from_c(name->str_text, ep - 1 - name->str_text); *right = magic_zero_encode(atol(ep)); return 1; } void project::bind_existing(void) { string_ty *s; string_ty *parent_name; int alias_retry_count; // // make sure project exists // trace(("project::bind_existing(this = %p)\n{\n", this)); assert(!home_path); alias_retry_count = 0; alias_retry: s = gonzo_project_home_path_from_name(name); // // Look to see if there is an alias. // if (!s) { s = gonzo_alias_to_actual(name); if (s) { str_free(name); name = str_copy(s); s = gonzo_project_home_path_from_name(name); } } // // If the named project was not found, and it has a numeric suffix, // then assume that it is a project.branch combination, // and try to find the deeper project. // if (!s && name_has_numeric_suffix(name, &parent_name, &parent_bn)) { trace(("mark\n")); project *ppp = project_alloc(parent_name); ppp->bind_existing(); int err = project_is_readable(ppp); if (err != 0) { off_limits = true; ppp->free(); ppp = 0; } else { parent = ppp; ppp = 0; pcp = change::create(parent, parent_bn); pcp->bind_existing(); if (!pcp->was_a_branch()) change_fatal(pcp, 0, i18n("not a branch")); // // rebuild the project name // ...eventually, use the remembered punctuation // str_free(name); name = str_format ( "%s.%ld", project_name_get(parent).c_str(), magic_zero_decode(parent_bn) ); changes_path = str_format ( "%s.branch", parent->change_path_get(parent_bn).c_str() ); // // This project's user will be the same as the parent's user. // up = project_user(parent); uid = up->get_uid(); gid = up->get_gid(); return; } } // // If the name was not found, try to find an alias match for it. // Loop if an alias is found. // if (!s) { string_ty *other; other = gonzo_alias_to_actual(name); if (other) { if (++alias_retry_count > 5) { project_fatal(this, 0, i18n("alias loop detected")); } str_free(name); name = str_copy(other); goto alias_retry; } } // // If the name was not found, try to find a fuzzy match for it. // In general, this results in more informative error messages. // if (!s) { string_list_ty wl; string_ty *best; double best_weight; size_t j; gonzo_project_list(&wl); best = 0; best_weight = 0.6; for (j = 0; j < wl.size(); ++j) { double w; s = wl[j]; w = fstrcmp(name->str_text, s->str_text); if (w > best_weight) { best = s; best_weight = w; } } if (best) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "Name", name); sub_var_set_string(scp, "Guess", best); fatal_intl(scp, i18n("no $name project, closest is $guess")); } else { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "Name", name); fatal_intl(scp, i18n("no $name project")); // NOTREACHED sub_context_delete(scp); } } // // To cope with automounters, directories are stored as given, // or are derived from the home directory in the passwd file. // Within aegis, pathnames have their symbolic links resolved, // and any comparison of paths is done on this "system idea" // of the pathname. // home_path = str_copy(s); // // Create the project user from details of the project path. // get_the_owner(); up = user_ty::create(uid, gid); // // set the umask from the project state data. // up->umask_set(umask_get()); trace(("}\n")); } bool project::bind_existing_errok(void) { string_ty *s; string_ty *parent_name; int alias_retry_count; // // make sure project exists // trace(("project::bind_existing_errok(this = %p)\n{\n", this)); assert(!home_path); alias_retry_count = 0; alias_retry: s = gonzo_project_home_path_from_name(name); if (s) { nstring root(s); nstring info_state_path = root + "/info/state"; os_become_orig(); int err = os_readable(info_state_path); os_become_undo(); if (err != 0) return false; } // // If the named project was not found, and it has a numeric suffix, // then assume that it is a project.branch combination, // and try to find the deeper project. // if (!s && name_has_numeric_suffix(name, &parent_name, &parent_bn)) { parent = project_alloc(parent_name); if (!parent->bind_existing_errok()) { parent->free(); trace(("return false;\n")); trace(("}\n")); return false; } pcp = change::create(parent, parent_bn); if (!pcp->bind_existing_errok()) { pcp.reset(); parent->free(); trace(("return false;\n")); trace(("}\n")); return false; } if (!pcp->was_a_branch()) { pcp.reset(); parent->free(); trace(("return false;\n")); trace(("}\n")); return false; } // // rebuild the project name // ...eventually, use the remembered punctuation // str_free(name); name = str_format ( "%s.%ld", project_name_get(parent).c_str(), magic_zero_decode(parent_bn) ); changes_path = str_format ( "%s.branch", parent->change_path_get(parent_bn).c_str() ); // // This project's user will be the same as the parent's user. // up = project_user(parent); uid = up->get_uid(); gid = up->get_gid(); trace(("return true;\n")); trace(("}\n")); return true; } // // If the name was not found, try to find an alias match for it. // Loop if an alias is found. // if (!s) { string_ty *other; other = gonzo_alias_to_actual(name); if (other) { if (++alias_retry_count > 5) { trace(("return false;\n")); trace(("}\n")); return false; } str_free(name); name = str_copy(other); goto alias_retry; } } // // If the name was not found, try to find a fuzzy match for it. // In general, this results in more informative error messages. // if (!s) { trace(("return false;\n")); trace(("}\n")); return false; } // // To cope with automounters, directories are stored as given, // or are derived from the home directory in the passwd file. // Within aegis, pathnames have their symbolic links resolved, // and any comparison of paths is done on this "system idea" // of the pathname. // home_path = str_copy(s); // // Create the project user from the details of the project directory. // get_the_owner(); up = user_ty::create(uid, gid); // // set the umask from the project state data. // up->umask_set(umask_get()); trace(("return true\n")); trace(("}\n")); return true; } // vim: set ts=8 sw=4 et :