/* * Back-end sub module * Execute diff3(1) command, and create backend-data structure from this output. * See "diff.h" for the details of data structure. * This module should be independent from GUI frontend. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #if defined(HAVE_STRING_H) #include #elif defined(HAVE_STRINGS_H) #include #endif #include #include "diff.h" #include "misc.h" /* Constant strings */ #ifndef DIFF3_PATH /* normally defined in config.h */ # define DIFF3_PATH "/usr/bin/diff3" #endif /* Private function declarations */ static DiffType parse_diff3_line(const char *buf, int *fn, int *n1, int *n2, char *t); static DiffType determine_dtype(int uniq_file, const char *type); /* XXX: Now, run_diff3() can't accept directories */ /** * run_diff3: * Create back-end data structure from diff3(1) output. * See "diff.h" for the details of data structures. * Input: * DiffDir *diffdir; * const char *filename1; * const char *filename2; * const char *filename3; * const char *args; Argument string to diff(1). * DiffFiles *cur_files; The current DiffFiles. * Specified only if update existing diffdir. * Output: * DiffDir *diffdir; GSList *dfiles_list is updated. * DiffFiles *cur_files; GList *dlines_list can be updated. **/ void run_diff3(DiffDir *diffdir, const char *filename1, const char *filename2, const char *filename3, const char *args, DiffFiles *cur_files) { FILE *fpdiff; char buf[BUFSIZ]; char *prog = DIFF3_PATH; char *diff_args; int uniq_file = -1; /* A unique file among three files. 3 means all are different */ int begin[MAX_NUM_COMPARE_FILES]; int end[MAX_NUM_COMPARE_FILES]; char type[MAX_NUM_COMPARE_FILES];/* 'a' or 'c' */ int num_fn = 0;/* the number of found files in diff3 outout */ gboolean b_update = FALSE; /* If args is defined as a const string, some compiler might place it on read-only memory. I explicitly copy the string, because it would be passed to strtok(). */ diff_args = g_strdup(args); if (cur_files) { b_update = TRUE; g_error("Not implemented\n"); } else /* A new diffdir, so use the first node as the current DiffFiles */ cur_files = g_slist_nth_data(diffdir->dfiles_list, 0); fpdiff = spawn_prog(prog, diff_args, (char*)filename1, (char*)filename2, (char*)filename3); while (fgets(buf, sizeof(buf), fpdiff) != NULL) { DiffType dtype = IGNORE; if (buf[0] == '=') { g_assert(buf[1] == '=' && buf[2] == '=' && buf[3] == '='); if (buf[4] == '1' || buf[4] == '2' || buf[4] == '3') { uniq_file = buf[4] - '1'; /* zero base */ } else { uniq_file = 3; /* all different */ } } else { DiffType dt_ret;/* a tentative value */ int fn, n1, n2; char t; dt_ret = parse_diff3_line(buf, &fn, &n1, &n2, &t); if (dt_ret == CHANGE) { begin[fn] = n1; end[fn] = n2; type[fn] = t; num_fn++; if (num_fn == 3) { /* Have parsed three files */ /* Strict checks */ g_assert(uniq_file != -1); g_assert(begin[0] != -1 && begin[1] != -1 && begin[2] != -1); /* A true DiffType is determined */ dtype = determine_dtype(uniq_file, type); } } else if (dt_ret == FINDBINFILE) { dtype = FINDBINFILE; } } switch (dtype) { case IGNORE: break; case FINDBINFILE: cur_files->binary = TRUE; break; case ERROR: g_warning("diff3 error?\n %s", buf); break; default: dfiles_add_dlines(cur_files, dtype, begin, end); /* reset */ num_fn = 0; /* Illegal values for strict check */ uniq_file = -1; begin[0] = begin[1] = begin[2] = -1; break; } } fclose(fpdiff); g_free(diff_args); } /* ---The followings are private functions--- */ /** * parse_diff3_line: * Need more works... * Input: * const char *buf; diff3 output. * Output: * int *fn; Return WhichFile's value. * int *n1; The begin line number of the different portion. * int *n2; The end line number of the different portion. * char *t; Retrun 'a' or 'c'. * Return value; Not a complete DiffType. A complete DiffType is determined later. **/ static DiffType parse_diff3_line(const char *buf, int *fn, int *n1, int *n2, char *t) { #define FINDBIN_MSG "diff3: diff error: Binary files" int tn1, tn2;/* tmp vals */ char tt;/* tmp vals */ if ((buf[0] == '1') || (buf[0] == '2') || (buf[0] == '3')) { g_assert(buf[1] == ':'); *fn = buf[0] - '1'; /* zero base */ if (sscanf(&buf[2], "%d,%dc\n", &tn1, &tn2) == 2) { *n1 = tn1; *n2 = tn2; *t = 'c'; return CHANGE;/* tentative value */ } else if (sscanf(&buf[2], "%d%c\n", &tn1, &tt) == 2) { /* %c is 'a' or 'c' */ *n1 = *n2 = tn1; *t = tt; return CHANGE;/* tentative value */ } else { g_assert_not_reached(); } } else if (strncmp(buf, FINDBIN_MSG, sizeof(FINDBIN_MSG)-1) == 0) { return FINDBINFILE; } return IGNORE; } /** * determine_dtype: * Input: * int uniq_file; The value is WhichFile or 3(all different). * const char *type; An array of types. Each value is 'a' or 'c'. **/ static DiffType determine_dtype(int uniq_file, const char *type) { DiffType dtype = ERROR; if (uniq_file == 3) {/* all different */ if (type[FIRST_FILE] == 'c' && type[SECOND_FILE] == 'c' && type[THIRD_FILE] == 'c') { dtype = CHANGE; } else { int num_uniq_file = 0;/* for a strict check */ int n; /* strict check */ for (n = 0; n < MAX_NUM_COMPARE_FILES; n++) { if (type[n] == 'a') num_uniq_file++; } g_assert(num_uniq_file == 1); if (type[FIRST_FILE] == 'a') dtype = F23ADD; else if (type[SECOND_FILE] == 'a') dtype = F31ADD; else if (type[THIRD_FILE] == 'a') dtype = F12ADD; } } else { WhichFile same_file[2]; if (uniq_file == FIRST_FILE) { same_file[0] = SECOND_FILE; same_file[1] = THIRD_FILE; dtype = F1ONLY; } else if (uniq_file == SECOND_FILE) { same_file[0] = FIRST_FILE; same_file[1] = THIRD_FILE; dtype = F2ONLY; } else if (uniq_file == THIRD_FILE) { same_file[0] = FIRST_FILE; same_file[1] = SECOND_FILE; dtype = F3ONLY; } if (type[same_file[0]] == 'a' && type[same_file[1]] == 'a') { g_assert(type[uniq_file] == 'c'); dtype |= ONLY_ADD; } else if (type[same_file[0]] == 'c' && type[same_file[1]] == 'c') { if (type[uniq_file] == 'c') { dtype |= ONLY_CHANGE; } else if (type[uniq_file] == 'a') { /* XXX what's difference with the above? */ dtype = 0; if (uniq_file == FIRST_FILE) dtype = F23ADD; else if (uniq_file == SECOND_FILE) dtype = F31ADD; else if (uniq_file == THIRD_FILE) dtype = F12ADD; } else { g_assert_not_reached(); } } else { g_assert_not_reached(); } } #ifdef DEBUG g_print("dtype(hex) = %x\n", dtype); #endif return dtype; }