diff options
Diffstat (limited to 'conf-update.c')
-rw-r--r-- | conf-update.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/conf-update.c b/conf-update.c new file mode 100644 index 0000000..f987501 --- /dev/null +++ b/conf-update.c @@ -0,0 +1,438 @@ +#include "conf-update.h" + +int main(int argc, char **argv) { + bool cont, menu_changed, firstrun, doit; + bool *tmp_index; + char *config_protect, *config_protect_mask,*cmd, *myfile, *highest; + char *esc_highest, *esc_myfile; + char **result, **envvars, **protected, **masked, **md5_cache; + char **md5sum_cache, **myupdate, **merged_updates_report = NULL; + char **removed_updates_report = NULL; + const char *name, *myname; + int indent, myindent, i, j, file_count, c, item_ct, cur; + int merged_updates_ct = 0, removed_updates_ct = 0, arglen; + ITEM **items_list; + MENU *mymenu; + WINDOW *inner, *menu_win; + + read_config(); + sanity_checks(); + + if (argc == 1) { + fprintf(stderr, ">>> Getting CONFIG_PROTECT* variables from portage...\n"); + #ifdef DEBUG + // sandboxing is useful for debugging, believe me + envvars = get_listing("portageq envvar CONFIG_PROTECT CONFIG_PROTECT_MASK | sed -e \"s:^/:${SANDBOX}/:\" -e \"s: /: ${SANDBOX}/:g\"", "\n"); + #else + envvars = get_listing("portageq envvar CONFIG_PROTECT CONFIG_PROTECT_MASK", "\n"); + #endif + + if (is_valid_entry(envvars[0]) && is_valid_entry(envvars[1])) { + config_protect = strdup(envvars[0]); + config_protect_mask = strdup(envvars[1]); + free(envvars[0]); + free(envvars[1]); + free(envvars); + } else { + fprintf(stderr, "!!! failed. Aborting.\n"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, ">>> Automerging updates in CONFIG_PROTECT_MASK...\n"); + masked = find_updates(config_protect_mask); + free(config_protect_mask); + for (i=0;!is_last_entry(masked[i]);i++) { + if (is_valid_entry(masked[i])) { + merged_updates_ct++; + merged_updates_report = (char **)realloc(merged_updates_report, merged_updates_ct * sizeof(char *)); + merged_updates_report[merged_updates_ct-1] = get_real_filename(masked[i]); + merge(get_highest_update(masked, masked[i]), masked); + } + } + free(masked); + fprintf(stderr, ">>> Searching for updates in CONFIG_PROTECT...\n"); + } else { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + display_help(); + } else { + arglen = 0; + for (i=1;i<argc;i++) { + arglen += strlen(argv[i]) + 1; + } + config_protect = (char *)calloc(sizeof(char), arglen); + for (i=1;i<argc;i++) { + if (argv[i][0] != '/') { + // relative paths screw our indentation. maybe this should be a TODO? + fprintf(stderr, "!!! ERROR: Non-absolute path given as argument\n"); + exit(EXIT_FAILURE); + } + strcat(config_protect, argv[i]); + if (i < argc-1) { + strcat(config_protect, " "); + } + } + fprintf(stderr, ">>> Searching for updates in specified directories...\n"); + } + } + + protected = find_updates(config_protect); + + // it's important that we do this first + if (config.automerge_unmodified) { + fprintf(stderr, ">>> Automerging unmodified files...\n"); + file_count = 1; + md5_cache = (char **) malloc(sizeof(char *) * file_count); + md5sum_cache = (char **) malloc(sizeof(char *) * file_count); + md5_cache[0] = LAST_ENTRY; + md5sum_cache[0] = LAST_ENTRY; + for (i=0;!is_last_entry(protected[i]);i++) { + if (is_valid_entry(protected[i])) { + highest = get_highest_update(protected, protected[i]); + if (!strcmp(protected[i], highest)) { + md5_cache = (char **) realloc(md5_cache, sizeof(char *) * (file_count + 1)); + md5sum_cache = (char **) realloc(md5sum_cache, sizeof(char *) * (file_count + 1)); + md5_cache[file_count-1] = strdup(highest); + md5sum_cache[file_count-1] = (char *)malloc(sizeof(char) * 32); + calc_md5(md5_cache[file_count-1], md5sum_cache[file_count-1]); + md5_cache[file_count] = LAST_ENTRY; + md5sum_cache[file_count] = LAST_ENTRY; + file_count++; + } + } + } + for (i=0;!is_last_entry(protected[i]);i++) { + if (is_valid_entry(protected[i])) { + myfile = get_real_filename(protected[i]); + + if (!user_modified(myfile)) { + merged_updates_ct++; + merged_updates_report = (char **)realloc(merged_updates_report, merged_updates_ct * sizeof(char *)); + merged_updates_report[merged_updates_ct-1] = get_real_filename(protected[i]); + merge(get_highest_update(protected, protected[i]), protected); + } + + free(myfile); + } + } + for (i=0;!is_last_entry(md5_cache[i]);i++) { + myfile = get_real_filename(md5_cache[i]); + md5sum_update(myfile, md5sum_cache[i]); + free(myfile); + free(md5_cache[i]); + free(md5sum_cache[i]); + } + free(md5_cache); + free(md5sum_cache); + } + + if (config.automerge_trivial) { + fprintf(stderr, ">>> Automerging trivial changes...\n"); + for (i=0;!is_last_entry(protected[i]);i++) { + if (is_valid_entry(protected[i])) { + myfile = get_real_filename(protected[i]); + esc_myfile = g_shell_quote(myfile); + highest = get_highest_update(protected, protected[i]); + esc_highest = g_shell_quote(highest); + cmd = (char *)calloc(strlen("diff -Nu % % | grep \"^[+-][^+-]\" | grep -v \"^[-+]#\" | grep -v \"^[-+][:space:]*$\" " ) + strlen(esc_highest) + strlen(esc_myfile), sizeof(char)); + strcpy(cmd, "diff -Nu "); + strcat(cmd, esc_myfile); + strcat(cmd, " "); + strcat(cmd, esc_highest); + strcat(cmd, " | grep \"^[+-][^+-]\" | grep -v \"^[-+]#\" | grep -v \"^[-+][:space:]*$\""); + + free(myfile); + free(esc_myfile); + free(esc_highest); + + result = get_listing(cmd, "\n"); + free(cmd); + if (is_last_entry(result[0])) { + merged_updates_ct++; + merged_updates_report = (char **)realloc(merged_updates_report, merged_updates_ct * sizeof(char *)); + merged_updates_report[merged_updates_ct-1] = get_real_filename(highest); + merge(highest, protected); + } + for (j=0;!is_last_entry(result[j]);j++) { + free(result[j]); + } + free(result); + } + } + } + /***/ + // ncurses n'stuff + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + start_color(); + init_pair(1, COLOR_CYAN, COLOR_BLUE); + init_pair(2, COLOR_WHITE, COLOR_WHITE); + init_pair(3, COLOR_BLACK, COLOR_WHITE); + init_pair(4, COLOR_RED, COLOR_WHITE); + init_pair(5, COLOR_WHITE, COLOR_BLACK); + + draw_background(); + + inner = newwin(LINES - 4, COLS - 4, 2, 2); + keypad(inner, TRUE); + + draw_legend(inner); + + menu_win = subwin(inner, LINES - 7 - 6, COLS - 4 - 3, 8, 3); + + mymenu = create_menu(protected); + items_list = menu_items(mymenu); + set_menu_win(mymenu, inner); + set_menu_sub(mymenu, menu_win); + + post_menu(mymenu); + touchwin(inner); + wrefresh(inner); + menu_changed = FALSE; + while ((item_count(mymenu) > 1) && (c = wgetch(inner)) != 'q' && c != 'Q') { + switch(c) { + // navigation 1up/down + case KEY_DOWN: + menu_driver(mymenu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(mymenu, REQ_UP_ITEM); + break; + //navigation 1 page up/down + case KEY_PPAGE: + menu_driver(mymenu, REQ_SCR_UPAGE); + break; + case KEY_NPAGE: + menu_driver(mymenu, REQ_SCR_DPAGE); + break; + // navigation top/bottom + case KEY_HOME: + menu_driver(mymenu, REQ_FIRST_ITEM); + break; + case KEY_END: + menu_driver(mymenu, REQ_LAST_ITEM); + break; + + // select single + case ' ': + if ((strrchr(item_name(current_item(mymenu)), '/'))) { + // it's a dir, select all subdirs + files + name = item_name(current_item(mymenu)); + indent = 0; + while (name[indent] == INDENT_CHAR) { + indent++; + } + cont = TRUE; + while (cont) { + menu_driver(mymenu, REQ_DOWN_ITEM); + myname = item_name(current_item(mymenu)); + myindent = 0; + while (myname[myindent] == INDENT_CHAR) { + myindent++; + } + if (myindent > indent) { + if ((!strrchr(myname, '/'))) { + set_item_value(current_item(mymenu), TRUE); + } + } else { + menu_driver(mymenu, REQ_UP_ITEM); + cont = FALSE; + } + } + } else { + menu_driver(mymenu, REQ_TOGGLE_ITEM); + } + break; + // select all + case 'a': + case 'A': + menu_driver(mymenu, REQ_LAST_ITEM); + for (i=0;i<item_count(mymenu);i++) { + if ((!strrchr(item_name(current_item(mymenu)), '/'))) { + set_item_value(current_item(mymenu), TRUE); + } + menu_driver(mymenu, REQ_UP_ITEM); + } + menu_driver(mymenu, REQ_FIRST_ITEM); + break; + // deselect all + case 'u': + case 'U': + menu_driver(mymenu, REQ_LAST_ITEM); + for (i=0;i<item_count(mymenu);i++) { + menu_driver(mymenu, REQ_UP_ITEM); + set_item_value(current_item(mymenu), FALSE); + } + menu_driver(mymenu, REQ_FIRST_ITEM); + break; + // disp diff + case '\n': + case KEY_ENTER: + if (item_userptr(current_item(mymenu))) { + endwin(); + show_diff(*((char **)item_userptr(current_item(mymenu)))); + reset_prog_mode(); + } + break; + // edit update + case 'e': + case 'E': + if (item_userptr(current_item(mymenu))) { + endwin(); + edit_update(*((char **)item_userptr(current_item(mymenu)))); + reset_prog_mode(); + } + break; + // merge interactively + case 'm': + case 'M': + if (item_userptr(current_item(mymenu))) { + endwin(); + protected = merge_interactively(*((char **)item_userptr(current_item(mymenu))), protected); + reset_prog_mode(); + menu_changed = TRUE; + } + break; + + // merge/replace update + case 'r': + case 'R': + /* it is important that we go from last to first: + * if e.g. both 0000 and 0001 are selected for merging, this + * assures (given a sorted list), that 0001 gets merged before + * 0000 and therefore 0000 gets removed + */ + firstrun = config.check_actions; + doit = TRUE; + for (i=item_count(mymenu)-1;i>=0;i--) { + if (item_value(items_list[i]) == TRUE || (current_item(mymenu) == items_list[i] && item_userptr(items_list[i]))) { + if (firstrun) { + doit = get_confirmation(inner, "replace"); + firstrun = false; + } + if (doit) { + myupdate = (char **)item_userptr(items_list[i]); + if (is_valid_entry(*myupdate)) { + merged_updates_ct++; + merged_updates_report = (char **)realloc(merged_updates_report, merged_updates_ct * sizeof(char *)); + merged_updates_report[merged_updates_ct-1] = get_real_filename(*myupdate); + menu_changed = TRUE; + merge(*myupdate, protected); + } + } + } + } + break; + // delete update + case 'd': + case 'D': + firstrun = config.check_actions; + doit = TRUE; + for (i=0;i<item_count(mymenu);i++) { + if (item_value(items_list[i]) == TRUE || (current_item(mymenu) == items_list[i] && item_userptr(items_list[i]))) { + if (firstrun) { + doit = get_confirmation(inner, "delete"); + firstrun = false; + } + if (doit) { + myupdate = (char **)item_userptr(items_list[i]); + exit_error(!unlink(*(myupdate)), *(myupdate)); + removed_updates_ct++; + removed_updates_report = (char**)realloc(removed_updates_report, removed_updates_ct * sizeof(char *)); + removed_updates_report[removed_updates_ct-1] = get_real_filename(*myupdate); + free(*myupdate); + *myupdate = SKIP_ENTRY; + menu_changed = TRUE; + } + } + } + break; + case KEY_RESIZE: + if (LINES > 13 && COLS > 55) { + // we don't want to loose the selection just because of a window resize + item_ct = item_count(mymenu); + cur = item_index(current_item(mymenu)); + tmp_index = malloc(sizeof(bool) * item_ct); + for (i=0;i<item_ct;i++) { + if (item_value(items_list[i]) == TRUE || (cur == i && item_userptr(items_list[i]))) { + tmp_index[i] = TRUE; + } else { + tmp_index[i] = FALSE; + } + } + remove_menu(mymenu); + delwin(menu_win); + delwin(inner); + draw_background(); + inner = newwin(LINES - 4, COLS - 4, 2, 2); + keypad(inner, TRUE); + draw_legend(inner); + menu_win = subwin(inner, LINES - 7 - 6, COLS - 4 - 5, 8, 5); + mymenu = create_menu(protected); + items_list = menu_items(mymenu); + set_menu_win(mymenu, inner); + set_menu_sub(mymenu, menu_win); + post_menu(mymenu); + + for (i=0;i<item_ct;i++) { + set_item_value(items_list[i], tmp_index[i]); + } + set_current_item(mymenu, items_list[cur]); + free(tmp_index); + } + break; + } + if (menu_changed) { + remove_menu(mymenu); + draw_legend(inner); + mymenu = create_menu(protected); + items_list = menu_items(mymenu); + set_menu_win(mymenu, inner); + set_menu_sub(mymenu, menu_win); + post_menu(mymenu); + menu_changed = FALSE; + } + touchwin(inner); + wrefresh(inner); + } + endwin(); + remove_menu(mymenu); + + if (merged_updates_ct > 0) { + fprintf(stdout, ">>> Merged updates for the following files:\n"); + for (i=0;i<merged_updates_ct;i++) { + fprintf(stdout, "\t%s\n", merged_updates_report[i]); + free(merged_updates_report[i]); + } + free(merged_updates_report); + } + if (removed_updates_ct > 0) { + fprintf(stdout, ">>> Deleted updates for the following files:\n"); + for (i=0;i<removed_updates_ct;i++) { + fprintf(stdout, "\t%s\n", removed_updates_report[i]); + free(removed_updates_report[i]); + } + free(removed_updates_report); + } + + for (i=0;!is_last_entry(protected[i]);i++) { + if (is_valid_entry(protected[i])) { + free(protected[i]); + } + } + free(protected); + if (config.pager) { + free(config.pager); + } + if (config.diff_tool) { + free(config.diff_tool); + } + if (config.merge_tool) { + free(config.merge_tool); + } + free(config.edit_tool); + free(config_protect); + fprintf(stderr, ">>> Nothing left to do... Bye!\n"); + exit(EXIT_SUCCESS); +} |