// // aegis - project change supervisor // Copyright (C) 1999, 2001-2008 Peter Miller // Copyright (C) 2006 Walter Franzini // // 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 // for assert #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void showtime(output::pointer fp, time_t when, bool exempt) { if (when) { struct tm *the_time = localtime(&when); char buffer[100]; strftime(buffer, sizeof(buffer), "%H:%M:%S %d-%b-%Y", the_time); fp->fputs(buffer); } else if (exempt) fp->fputs("exempt"); else fp->fputs("required"); } list_change_details_columns::~list_change_details_columns() { } list_change_details_columns::list_change_details_columns() : colp(col::open((string_ty *)0)), head_col(colp->create(0, 0, (const char *)0)), body_col(colp->create(INDENT_WIDTH, 0, (const char *)0)) { } void list_change_details_columns::list(change::pointer cp, bool recurse) { trace(("list_change_details_columns::list()\n{\n")); cstate_ty *cstate_data = cp->cstate_get(); // // Set the page heading. // if (cstate_data->branch) { if (cp->number == TRUNK_CHANGE_NUMBER) { string_ty *line1 = str_format ( "Project \"%s\"", project_name_get(cp->pp)->str_text ); colp->title(line1->str_text, "Project Details"); str_free(line1); } else { string_ty *line1 = str_format ( "Project \"%s.%ld\"", project_name_get(cp->pp)->str_text, magic_zero_decode(cp->number) ); colp->title(line1->str_text, "Branch Details"); str_free(line1); } } else { string_ty *line1 = str_format ( "Project \"%s\", Change %ld", project_name_get(cp->pp)->str_text, magic_zero_decode(cp->number) ); colp->title(line1->str_text, "Change Details"); str_free(line1); } // // Identification. // head_col->fputs("NAME"); colp->eoln(); body_col->fprintf("Project \"%s\"", project_name_get(cp->pp)->str_text); if (cstate_data->delta_number) body_col->fprintf(", Delta %ld", cstate_data->delta_number); if ( cp->number != TRUNK_CHANGE_NUMBER && (cstate_data->state < cstate_state_completed || option_verbose_get()) ) { body_col->fprintf(", Change %ld", magic_zero_decode(cp->number)); } if (cstate_data->uuid && option_verbose_get()) { body_col->fputs(",\n"); body_col->fputs(cstate_data->uuid); } else body_col->fputc('.'); colp->eoln(); // // synopsis // colp->need(5); head_col->fputs("SUMMARY"); colp->eoln(); body_col->fputs(cstate_data->brief_description); colp->eoln(); // // description // colp->need(5); head_col->fputs("DESCRIPTION"); colp->eoln(); body_col->fputs(cstate_data->description); if (cstate_data->test_exempt || cstate_data->test_baseline_exempt) { body_col->end_of_line(); body_col->fputc('\n'); if (!cstate_data->regression_test_exempt) { body_col->fputs("This change must pass a full regression test. "); } if (cstate_data->test_exempt) { body_col->fputs ( "This change is exempt from testing against the " "development directory. " ); } if (cstate_data->test_baseline_exempt) { body_col->fputs ( "This change is exempt from testing against the baseline." ); } } colp->eoln(); // // List attributes // if (cstate_data->attribute && cstate_data->attribute->length) { colp->need(5); head_col->fputs("ATTRIBUTES"); colp->eoln(); // // create the columns // int left = INDENT_WIDTH; output::pointer name_col = colp->create(left, left + ATTR_WIDTH, "Name\n-------"); left += ATTR_WIDTH + 1; output::pointer value_col = colp->create(left, 0, "Value\n-------"); // // list the attributes // attributes_list_ty *alp = cstate_data->attribute; for (size_t j = 0; j < alp->length; ++j) { attributes_ty *ap = alp->list[j]; if (ap->name) name_col->fputs(ap->name); if (ap->value) value_col->fputs(ap->value); colp->eoln(); } colp->forget(name_col); colp->forget(value_col); } // // show the sub-changes of a branch // if (cstate_data->branch && (recurse || option_verbose_get())) { colp->need(5); head_col->fputs("BRANCH CONTENTS"); colp->eoln(); // // create the columns // int left = INDENT_WIDTH; output::pointer number_col = colp->create(left, left + CHANGE_WIDTH, "Change\n-------"); left += CHANGE_WIDTH + 1; output::pointer state_col = colp->create(left, left + STATE_WIDTH, "State\n-------"); left += STATE_WIDTH + 1; output::pointer description_col = colp->create(left, 0, "Description\n-------------"); // // list the sub changes // project_ty *sub_pp = cp->pp->bind_branch(cp); for (size_t j = 0;; ++j) { long sub_cn; if (!project_change_nth(sub_pp, j, &sub_cn)) break; change::pointer sub_cp = change_alloc(sub_pp, sub_cn); change_bind_existing(sub_cp); cstate_ty *sub_cstate_data = sub_cp->cstate_get(); number_col->fprintf("%4ld", magic_zero_decode(sub_cn)); state_col->fputs(cstate_state_ename(sub_cstate_data->state)); if (cstate_data->brief_description) { description_col->fputs(sub_cstate_data->brief_description); } colp->eoln(); change_free(sub_cp); } colp->forget(number_col); colp->forget(state_col); colp->forget(description_col); } colp->eoln(); // // architecture // colp->need(7); head_col->fprintf ( "ARCHITECTURE%s", (cstate_data->architecture->length == 1 ? "" : "S") ); colp->eoln(); body_col->fputs("This change must "); bool build_required = change_build_required(cp, !change_was_a_branch(cp)); if (build_required) body_col->fputs("build and "); body_col->fputs("test in"); if (cstate_data->architecture->length > 1) body_col->fputs(" each of"); body_col->fputs(" the"); for (size_t k = 0; k < cstate_data->architecture->length; ++k) { string_ty *s = cstate_data->architecture->list[k]; if (k) { if (k == cstate_data->architecture->length - 1) body_col->fputs(" and"); else body_col->fputc(','); } body_col->fprintf(" \"%s\"", s->str_text); } body_col->fprintf ( " architecture%s.", (cstate_data->architecture->length == 1 ? "" : "s") ); colp->eoln(); if ( cstate_data->state == cstate_state_being_developed || cstate_data->state == cstate_state_being_integrated ) { string_list_ty done; colp->need(5); int left = INDENT_WIDTH; output::pointer arch_col = colp->create(left, left + ARCH_WIDTH, "arch.\n--------"); left += ARCH_WIDTH + 1; output::pointer host_col = colp->create(left, left + HOST_WIDTH, "host\n--------"); left += HOST_WIDTH + 1; output::pointer build_col = colp->create(left, left + TIME_WIDTH, "aeb\n---------"); left += TIME_WIDTH + 1; output::pointer test_col = colp->create(left, left + TIME_WIDTH, "aet\n---------"); left += TIME_WIDTH + 1; output::pointer test_bl_col = colp->create(left, left + TIME_WIDTH, "aet -bl\n---------"); left += TIME_WIDTH + 1; output::pointer test_reg_col = colp->create(left, left + TIME_WIDTH, "aet -reg\n---------"); for (size_t j = 0; j < cstate_data->architecture->length; ++j) { cstate_architecture_times_ty *tp; string_ty *s; s = cstate_data->architecture->list[j]; tp = change_architecture_times_find(cp, s); arch_col->fputs(tp->variant); done.push_back(tp->variant); if (tp->node) host_col->fputs(tp->node); showtime(build_col, tp->build_time, !build_required); showtime(test_col, tp->test_time, cstate_data->test_exempt); showtime ( test_bl_col, tp->test_baseline_time, cstate_data->test_baseline_exempt ); showtime ( test_reg_col, tp->regression_test_time, cstate_data->regression_test_exempt ); colp->eoln(); } for (size_t k = 0; k < cstate_data->architecture_times->length; ++k) { cstate_architecture_times_ty *tp = cstate_data->architecture_times->list[k]; if (done.member(tp->variant)) continue; arch_col->fputs(tp->variant); done.push_back(tp->variant); if (tp->node) host_col->fputs(tp->node); showtime(build_col, tp->build_time, true); showtime(test_col, tp->test_time, true); showtime(test_bl_col, tp->test_baseline_time, true); showtime(test_reg_col, tp->regression_test_time, true); colp->eoln(); } if (cstate_data->architecture->length > 1) { build_col->fputs("---------\n"); test_col->fputs("---------\n"); test_bl_col->fputs("---------\n"); test_reg_col->fputs("---------\n"); showtime ( build_col, cstate_data->build_time, !build_required ); showtime ( test_col, cstate_data->test_time, cstate_data->test_exempt ); showtime ( test_bl_col, cstate_data->test_baseline_time, cstate_data->test_baseline_exempt ); showtime ( test_reg_col, cstate_data->regression_test_time, cstate_data->regression_test_exempt ); colp->eoln(); } colp->forget(arch_col); colp->forget(host_col); colp->forget(build_col); colp->forget(test_col); colp->forget(test_bl_col); colp->forget(test_reg_col); } // // cause // colp->need(5); head_col->fputs("CAUSE"); colp->eoln(); body_col->fprintf ( "This change was caused by %s.", change_cause_ename(cstate_data->cause) ); colp->eoln(); // // state // if (cstate_data->state != cstate_state_completed) { colp->need(5); head_col->fputs("STATE"); colp->eoln(); body_col->fprintf ( "This change is in the '%s' state.", cstate_state_ename(cstate_data->state) ); colp->eoln(); } // // files // colp->need(5); head_col->fputs("FILES"); colp->eoln(); if (change_file_nth(cp, (size_t)0, view_path_first)) { int left = INDENT_WIDTH; output::pointer usage_col = colp->create(left, left + USAGE_WIDTH, "Type\n-------"); left += USAGE_WIDTH + 1; output::pointer action_col = colp->create(left, left + ACTION_WIDTH, "Action\n--------"); left += ACTION_WIDTH + 1; output::pointer edit_col = colp->create(left, left + EDIT_WIDTH, "Edit\n-------"); left += EDIT_WIDTH + 1; output::pointer file_name_col = colp->create(left, 0, "File Name\n-----------"); for (size_t j = 0;; ++j) { fstate_src_ty *src_data; src_data = change_file_nth(cp, j, view_path_first); if (!src_data) break; assert(src_data->file_name); usage_col->fputs(file_usage_ename(src_data->usage)); action_col->fputs(file_action_ename(src_data->action)); list_format_edit_number(edit_col, src_data); if ( cstate_data->state == cstate_state_being_developed && !change_file_up_to_date(cp->pp, src_data) ) { fstate_src_ty *psrc_data; // // The current head revision of the branch may not equal // the version "originally" copied. // psrc_data = project_file_find (cp->pp, src_data, view_path_extreme); if (psrc_data && psrc_data->edit) { assert(psrc_data->edit->revision); edit_col->fprintf ( " (%s)", psrc_data->edit->revision->str_text ); } } if (src_data->edit_origin_new) { // // The "cross branch merge" version. // assert(src_data->edit_origin_new->revision); edit_col->end_of_line(); edit_col->fprintf ( "{cross %4s}", src_data->edit_origin_new->revision->str_text ); } file_name_col->fputs(src_data->file_name); if (src_data->move) { switch (src_data->action) { case file_action_create: file_name_col->end_of_line(); file_name_col->fputs("Moved from "); file_name_col->fputs(src_data->move); break; case file_action_remove: file_name_col->end_of_line(); file_name_col->fputs("Moved to "); file_name_col->fputs(src_data->move); break; case file_action_modify: case file_action_insulate: case file_action_transparent: break; } } colp->eoln(); } colp->forget(usage_col); colp->forget(action_col); colp->forget(edit_col); colp->forget(file_name_col); } else { body_col->fputs("This change has no files."); colp->eoln(); } // // history // colp->need(5); head_col->fputs("HISTORY"); colp->eoln(); if (option_verbose_get()) { int left = INDENT_WIDTH; output::pointer what_col = colp->create(left, left + WHAT_WIDTH, "What\n------"); left += WHAT_WIDTH + 1; output::pointer when_col = colp->create(left, left + WHEN_WIDTH, "When\n------"); left += WHEN_WIDTH + 1; output::pointer who_col = colp->create(left, left + WHO_WIDTH, "Who\n-----"); left += WHO_WIDTH + 1; output::pointer why_col = colp->create(left, 0, "Comment\n---------"); output::pointer wide_why_col = colp->create(12, 0, 0); for (size_t j = 0; j < cstate_data->history->length; ++j) { cstate_history_ty *history_data; time_t t; history_data = cstate_data->history->list[j]; what_col->fputs(cstate_history_what_ename(history_data->what)); t = history_data->when; when_col->fputs(ctime(&t)); who_col->fputs(history_data->who); if (history_data->why && history_data->why->str_length <= 40) why_col->fputs(history_data->why); if (history_data->what != cstate_history_what_integrate_pass) { time_t finish; if (j + 1 < cstate_data->history->length) finish = cstate_data->history->list[j + 1]->when; else finish = now(); if (finish - t >= ELAPSED_TIME_THRESHOLD) { why_col->end_of_line(); why_col->fprintf ( "Elapsed time: %5.3f days.\n", working_days(t, finish) ); } } colp->eoln(); if (history_data->why && history_data->why->str_length > 40) { wide_why_col->fputs(history_data->why); colp->eoln(); } } colp->forget(what_col); colp->forget(when_col); colp->forget(who_col); colp->forget(why_col); colp->forget(wide_why_col); } else { if (cstate_data->state >= cstate_state_being_developed) { body_col->fprintf ( "Developed by %s.", change_developer_name(cp)->str_text ); } if (cstate_data->state >= cstate_state_awaiting_integration) { body_col->fprintf ( " Reviewed by %s.", change_reviewer_name(cp)->str_text ); } if (cstate_data->state >= cstate_state_being_integrated) { body_col->fprintf ( " Integrated by %s.", change_integrator_name(cp)->str_text ); } colp->eoln(); } // // Recurse on branches. // if (cstate_data->branch && recurse) { project_ty *sub_pp = cp->pp->bind_branch(cp); for (size_t j = 0;; ++j) { long sub_cn; if (!project_change_nth(sub_pp, j, &sub_cn)) break; change::pointer sub_cp = change_alloc(sub_pp, sub_cn); change_bind_existing(sub_cp); colp->eject(); list(sub_cp, true); change_free(sub_cp); } } trace(("}\n")); } void list_change_details(change_identifier &cid, string_list_ty *) { trace(("list_change_details()\n{\n")); list_change_details_columns process; process.list(cid.get_cp(), false); trace(("}\n")); }