+static void run_diff(struct diff_filepair *p)
+{
+ const char *pgm = external_diff();
+ char msg_[PATH_MAX*2+200], *xfrm_msg;
+ struct diff_filespec *one;
+ struct diff_filespec *two;
+ const char *name;
+ const char *other;
+ int complete_rewrite = 0;
+
+ if (DIFF_PAIR_UNMERGED(p)) {
+ /* unmerged */
+ run_external_diff(pgm, p->one->path, NULL, NULL, NULL, NULL,
+ 0);
+ return;
+ }
+
+ name = p->one->path;
+ other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+ one = p->one; two = p->two;
+ switch (p->status) {
+ case DIFF_STATUS_COPIED:
+ sprintf(msg_,
+ "similarity index %d%%\n"
+ "copy from %s\n"
+ "copy to %s",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name, other);
+ xfrm_msg = msg_;
+ break;
+ case DIFF_STATUS_RENAMED:
+ sprintf(msg_,
+ "similarity index %d%%\n"
+ "rename from %s\n"
+ "rename to %s",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name, other);
+ xfrm_msg = msg_;
+ break;
+ case DIFF_STATUS_MODIFIED:
+ if (p->score) {
+ sprintf(msg_,
+ "dissimilarity index %d%%",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ xfrm_msg = msg_;
+ complete_rewrite = 1;
+ break;
+ }
+ /* fallthru */
+ default:
+ xfrm_msg = NULL;
+ }
+
+ if (!pgm &&
+ DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
+ (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
+ /* a filepair that changes between file and symlink
+ * needs to be split into deletion and creation.
+ */
+ struct diff_filespec *null = alloc_filespec(two->path);
+ run_external_diff(NULL, name, other, one, null, xfrm_msg, 0);
+ free(null);
+ null = alloc_filespec(one->path);
+ run_external_diff(NULL, name, other, null, two, xfrm_msg, 0);
+ free(null);
+ }
+ else
+ run_external_diff(pgm, name, other, one, two, xfrm_msg,
+ complete_rewrite);
+}
+
+void diff_setup(struct diff_options *options)
+{
+ memset(options, 0, sizeof(*options));
+ options->output_format = DIFF_FORMAT_RAW;
+ options->line_termination = '\n';
+ options->break_opt = -1;
+ options->rename_limit = -1;
+}
+
+int diff_setup_done(struct diff_options *options)
+{
+ if ((options->find_copies_harder || 0 <= options->rename_limit) &&
+ options->detect_rename != DIFF_DETECT_COPY)
+ return -1;
+ if (options->setup & DIFF_SETUP_USE_CACHE) {
+ if (!active_cache)
+ /* read-cache does not die even when it fails
+ * so it is safe for us to do this here. Also
+ * it does not smudge active_cache or active_nr
+ * when it fails, so we do not have to worry about
+ * cleaning it up oufselves either.
+ */
+ read_cache();
+ }
+ if (options->setup & DIFF_SETUP_USE_SIZE_CACHE)
+ use_size_cache = 1;
+
+ return 0;
+}
+
+int diff_opt_parse(struct diff_options *options, const char **av, int ac)
+{
+ const char *arg = av[0];
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+ options->output_format = DIFF_FORMAT_PATCH;
+ else if (!strcmp(arg, "-z"))
+ options->line_termination = 0;
+ else if (!strncmp(arg, "-l", 2))
+ options->rename_limit = strtoul(arg+2, NULL, 10);
+ else if (!strcmp(arg, "--name-only"))
+ options->output_format = DIFF_FORMAT_NAME;
+ else if (!strcmp(arg, "--name-status"))
+ options->output_format = DIFF_FORMAT_NAME_STATUS;
+ else if (!strcmp(arg, "-R"))
+ options->reverse_diff = 1;
+ else if (!strncmp(arg, "-S", 2))
+ options->pickaxe = arg + 2;
+ else if (!strcmp(arg, "-s"))
+ options->output_format = DIFF_FORMAT_NO_OUTPUT;
+ else if (!strncmp(arg, "-O", 2))
+ options->orderfile = arg + 2;
+ else if (!strncmp(arg, "--diff-filter=", 14))
+ options->filter = arg + 14;
+ else if (!strcmp(arg, "--pickaxe-all"))
+ options->pickaxe_opts = DIFF_PICKAXE_ALL;
+ else if (!strncmp(arg, "-B", 2)) {
+ if ((options->break_opt =
+ diff_scoreopt_parse(arg)) == -1)
+ return -1;
+ }
+ else if (!strncmp(arg, "-M", 2)) {
+ if ((options->rename_score =
+ diff_scoreopt_parse(arg)) == -1)
+ return -1;
+ options->detect_rename = DIFF_DETECT_RENAME;
+ }
+ else if (!strncmp(arg, "-C", 2)) {
+ if ((options->rename_score =
+ diff_scoreopt_parse(arg)) == -1)
+ return -1;
+ options->detect_rename = DIFF_DETECT_COPY;
+ }
+ else if (!strcmp(arg, "--find-copies-harder"))
+ options->find_copies_harder = 1;
+ else
+ return 0;
+ return 1;
+}
+
+static int parse_num(const char **cp_p)