// // aegis - project change supervisor // Copyright (C) 2003-2008 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 #include static inline char safe_toupper(char c) { if (islower((unsigned char)c)) return toupper((unsigned char)c); return c; } static inline bool safe_isdigit(char c) { return isdigit((unsigned char)c); } static inline bool safe_ispunct(char c) { return ispunct((unsigned char)c); } static bool extract_change_number(string_ty **project_name_p, long *change_number_p) { string_ty *project_name = *project_name_p; if (safe_toupper(project_name->str_text[0]) == 'C') { const char *cp = project_name->str_text + 1; char *end = 0; long change_number = strtol(cp, &end, 10); if (end == cp || *end) return false; *change_number_p = change_number; *project_name_p = str_from_c(""); } else { const char *cp = strstr(project_name->str_text, ".C"); if (!cp) cp = strstr(project_name->str_text, ".c"); if (!cp) return false; char *end = 0; long change_number = strtol(cp + 2, &end, 10); if (end == cp + 2 || *end) return false; *change_number_p = change_number; string_ty *new_project_name = str_n_from_c(project_name->str_text, cp - project_name->str_text); *project_name_p = new_project_name; } str_free(project_name); return true; } static bool extract_delta_number(string_ty **project_name_p, long *delta_number_p) { string_ty *project_name = *project_name_p; if (safe_toupper(project_name->str_text[0]) == 'D') { const char *cp = project_name->str_text + 1; char *end = 0; long delta_number = strtol(cp, &end, 10); if (end == cp || *end) return false; if (delta_number <= 0) return false; *delta_number_p = delta_number; *project_name_p = str_from_c(""); } else { const char *cp = strstr(project_name->str_text, ".D"); if (!cp) cp = strstr(project_name->str_text, ".d"); if (!cp) return false; char *end = 0; long delta_number = strtol(cp + 2, &end, 10); if (end == cp + 2 || *end) return false; if (delta_number <= 0) return false; *delta_number_p = delta_number; *project_name_p = str_n_from_c(project_name->str_text, cp - project_name->str_text); } str_free(project_name); return true; } static bool is_a_branch_number(string_ty *s) { if (!s->str_length) return true; bool digit_required = true; const char *cp = s->str_text; for (;;) { if (digit_required) { if (!*cp) return false; if (!safe_isdigit(*cp)) return false; digit_required = false; } else { if (!*cp) return true; if (safe_isdigit(*cp)) digit_required = false; else if (safe_ispunct(*cp)) digit_required = true; else return false; } ++cp; } } static void no_such_uuid(project_ty *pp, string_ty *uuid) { // // Build the message we actually want to send. // sub_context_ty *scp = sub_context_new(); string_ty *msg = subst_intl(scp, i18n("unknown change")); // // Get ready to pass the message to the project error function. // We are going to re-use the substitution context. // sub_var_set_string(scp, "MeSsaGe", msg); str_free(msg); // // Wrap the project name and chnage "number" around the error message. // sub_var_set_string(scp, "Change", uuid); sub_var_override(scp, "Change"); project_fatal(pp, scp, i18n("change $change: $message")); // NOTREACHED } /** * The arglex_parse_change_tok is used to parse a complete --change * command line option and any succeeding arguments. When finished, * the parse point will be placed at the begining of the next command * line option. */ static void arglex_parse_change_tok(string_ty **project_name_p, long *change_number_p, int token, void (*usage)(void)) { string_ty *project_name; long change_number; string_ty *s; long delta_number; change_number = *change_number_p; project_name = *project_name_p; if (change_number) duplicate_option_by_name(token, usage); switch (arglex_token) { case arglex_token_number: change_number = arglex_value.alv_number; range_check: if (change_number == 0) change_number = MAGIC_ZERO; else if (change_number < 1) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_long(scp, "Number", change_number); if (token == arglex_token_branch) fatal_intl(scp, i18n("branch $number out of range")); else fatal_intl(scp, i18n("change $number out of range")); // NOTREACHED } break; case arglex_token_string: s = str_from_c(arglex_value.alv_string); if (universal_unique_identifier_valid_partial(s)) { // // Hunt for the change set with the given UUID within the // project. Complain if there is no change set with the // given UUID if the UUID is exact. Move on if there is no // match, or too many matches, for a partial (prefix) UUID. // if (!project_name) { nstring n = user_ty::create()->default_project(); project_name = str_copy(n.get_ref()); } project_ty *pp = project_alloc(project_name); pp->bind_existing(); change::pointer cp = project_uuid_find(pp, s); if (!cp) { if (s->str_length == 36) { no_such_uuid(pp, s); // NOTREACHED } project_free(pp); } else { if (!*project_name_p) *project_name_p = str_copy(project_name_get(cp->pp)); else if (!str_equal(project_name, project_name_get(cp->pp))) duplicate_option_by_name(arglex_token_project, usage); change_number = cp->number; if (change_number == 0) change_number = MAGIC_ZERO; change_free(cp); project_free(pp); project_name = *project_name_p; break; } } if (extract_change_number(&s, &change_number)) { if (is_a_branch_number(s) && !project_name) { project_ty *pp; project_ty *pp2; nstring n = user_ty::create()->default_project(); project_name = str_copy(n.get_ref()); pp = project_alloc(project_name); pp->bind_existing(); pp2 = pp->find_branch(s->str_text); project_name = str_copy(project_name_get(pp2)); project_free(pp2); project_free(pp); str_free(s); } else { if (project_name && !str_equal(project_name, s)) duplicate_option_by_name(arglex_token_project, usage); project_name = s; } goto range_check; } if (extract_delta_number(&s, &delta_number)) { if (is_a_branch_number(s) && !project_name) { project_ty *pp; project_ty *pp2; nstring n = user_ty::create()->default_project(); project_name = str_copy(n.get_ref()); pp = project_alloc(project_name); pp->bind_existing(); pp2 = pp->find_branch(s->str_text); project_name = str_copy(project_name_get(pp2)); change_number = project_delta_number_to_change_number(pp2, delta_number); project_free(pp2); project_free(pp); str_free(s); } else { project_ty *pp; if (project_name && !str_equal(project_name, s)) duplicate_option_by_name(arglex_token_project, usage); project_name = s; pp = project_alloc(project_name); pp->bind_existing(); change_number = project_delta_number_to_change_number(pp, delta_number); project_free(pp); } break; } // fall through... default: option_needs_number(token, usage); // NOTREACHED } // advance past change number arglex(); *change_number_p = change_number; *project_name_p = project_name; } void arglex_parse_change(string_ty **project_name_p, long *change_number_p, void (*usage)(void)) { arglex_parse_change_tok ( project_name_p, change_number_p, arglex_token_change, usage ); } void arglex_parse_branch(string_ty **project_name_p, long *change_number_p, void (*usage)(void)) { arglex_parse_change_tok ( project_name_p, change_number_p, arglex_token_branch, usage ); } void arglex_parse_change_with_branch(string_ty **project_name_p, long *change_number_p, const char **branch_p, void (*usage)(void)) { string_ty *project_name; long change_number; const char *branch; string_ty *s; long delta_number; project_name = *project_name_p; change_number = *change_number_p; branch = *branch_p; switch (arglex_token) { case arglex_token_number: if (change_number) duplicate_option_by_name(arglex_token_change, usage); change_number = arglex_value.alv_number; range_check: if (change_number == 0) change_number = MAGIC_ZERO; else if (change_number < 1) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_long(scp, "Number", change_number); fatal_intl(scp, i18n("change $number out of range")); // NOTREACHED } break; case arglex_token_string: s = str_from_c(arglex_value.alv_string); if (universal_unique_identifier_valid_partial(s)) { // // Hunt for the change set with the given UUID within the // project. Complain if there is no change set with the // given UUID if the UUID is exact. Move on if there is no // match, or too many matches, for a partial (prefix) UUID. // if (!project_name) { nstring n = user_ty::create()->default_project(); project_name = str_copy(n.get_ref()); } project_ty *pp = project_alloc(project_name); pp->bind_existing(); change::pointer cp = project_uuid_find(pp, s); if (!cp) { if (s->str_length == 36) { no_such_uuid(pp, s); // NOTREACHED } project_free(pp); pp = 0; } else { if (!*project_name_p) *project_name_p = str_copy(project_name_get(cp->pp)); else if (!str_equal(project_name, project_name_get(cp->pp))) duplicate_option_by_name(arglex_token_project, usage); change_number = cp->number; if (change_number == 0) change_number = MAGIC_ZERO; change_free(cp); cp = 0; project_free(pp); pp = 0; project_name = *project_name_p; break; } } if (extract_change_number(&s, &change_number)) { if (is_a_branch_number(s)) { if (branch && 0 != strcmp(branch, s->str_text)) duplicate_option_by_name(arglex_token_branch, usage); branch = mem_copy_string(s->str_text); str_free(s); } else { if (project_name && !str_equal(project_name, s)) duplicate_option_by_name(arglex_token_project, usage); project_name = s; } goto range_check; } if (extract_delta_number(&s, &delta_number)) { if (is_a_branch_number(s)) { project_ty *pp; project_ty *pp2; if (branch && 0 != strcmp(branch, s->str_text)) duplicate_option_by_name(arglex_token_branch, usage); branch = mem_copy_string(s->str_text); str_free(s); // // Bind to the project. // if (!project_name) { nstring n = user_ty::create()->default_project(); project_name = str_copy(n.get_ref()); } pp = project_alloc(project_name); pp->bind_existing(); pp2 = pp->find_branch(branch); // // Find the change number. // change_number = project_delta_number_to_change_number(pp2, delta_number); project_free(pp2); project_free(pp); } else { project_ty *pp; if (project_name && !str_equal(project_name, s)) duplicate_option_by_name(arglex_token_project, usage); project_name = s; pp = project_alloc(project_name); pp->bind_existing(); change_number = project_delta_number_to_change_number(pp, delta_number); project_free(pp); } break; } // fall through... default: option_needs_number(arglex_token_change, usage); // NOTREACHED } // advance past change number arglex(); *branch_p = branch; *change_number_p = change_number; *project_name_p = project_name; }