+ int fd;
+
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = mkstemp(path);
+ if (fd < 0)
+ die("unable to create temp-file");
+ if (write_in_full(fd, src->ptr, src->size) != src->size)
+ die("unable to write temp-file");
+ close(fd);
+}
+
+static int ll_ext_merge(const char *cmd,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ char temp[3][50];
+ char cmdbuf[2048];
+ struct interp table[] = {
+ { "%O" },
+ { "%A" },
+ { "%B" },
+ };
+ struct child_process child;
+ const char *args[20];
+ int status, fd, i;
+ struct stat st;
+
+ result->ptr = NULL;
+ result->size = 0;
+ create_temp(orig, temp[0]);
+ create_temp(src1, temp[1]);
+ create_temp(src2, temp[2]);
+
+ interp_set_entry(table, 0, temp[0]);
+ interp_set_entry(table, 1, temp[1]);
+ interp_set_entry(table, 2, temp[2]);
+
+ interpolate(cmdbuf, sizeof(cmdbuf), cmd, table, 3);
+
+ memset(&child, 0, sizeof(child));
+ child.argv = args;
+ args[0] = "sh";
+ args[1] = "-c";
+ args[2] = cmdbuf;
+ args[3] = NULL;
+
+ status = run_command(&child);
+ if (status < -ERR_RUN_COMMAND_FORK)
+ ; /* failure in run-command */
+ else
+ status = -status;
+ fd = open(temp[1], O_RDONLY);
+ if (fd < 0)
+ goto bad;
+ if (fstat(fd, &st))
+ goto close_bad;
+ result->size = st.st_size;
+ result->ptr = xmalloc(result->size + 1);
+ if (read_in_full(fd, result->ptr, result->size) != result->size) {
+ free(result->ptr);
+ result->ptr = NULL;
+ result->size = 0;
+ }
+ close_bad:
+ close(fd);
+ bad:
+ for (i = 0; i < 3; i++)
+ unlink(temp[i]);
+ return status;
+}
+
+/*
+ * merge.default and merge.driver configuration items
+ */
+static struct user_merge_fn {
+ struct user_merge_fn *next;
+ const char *name;
+ char *cmdline;
+ char b_[1];
+} *ll_user_merge_fns, **ll_user_merge_fns_tail;
+
+static int read_merge_config(const char *var, const char *value)
+{
+ struct user_merge_fn *fn;
+ int blen, nlen;
+
+ if (strcmp(var, "merge.driver"))
+ return 0;
+ if (!value)
+ return error("%s: lacks value", var);
+ /*
+ * merge.driver is a multi-valued configuration, whose value is
+ * of form:
+ *
+ * name command-line
+ *
+ * The command-line will be interpolated with the following
+ * tokens and is given to the shell:
+ *
+ * %O - temporary file name for the merge base.
+ * %A - temporary file name for our version.
+ * %B - temporary file name for the other branches' version.
+ *
+ * The external merge driver should write the results in the file
+ * named by %A, and signal that it has done with exit status 0.
+ */
+ for (nlen = -1, blen = 0; value[blen]; blen++)
+ if (nlen < 0 && isspace(value[blen]))
+ nlen = blen;
+ if (nlen < 0)
+ return error("%s '%s': lacks command line", var, value);
+ fn = xcalloc(1, sizeof(struct user_merge_fn) + blen + 1);
+ memcpy(fn->b_, value, blen + 1);
+ fn->name = fn->b_;
+ fn->b_[nlen] = 0;
+ fn->cmdline = fn->b_ + nlen + 1;
+ fn->next = *ll_user_merge_fns_tail;
+ *ll_user_merge_fns_tail = fn;
+ return 0;
+}
+
+static void initialize_ll_merge(void)
+{
+ if (ll_user_merge_fns_tail)
+ return;
+ ll_user_merge_fns_tail = &ll_user_merge_fns;
+ git_config(read_merge_config);
+}
+
+static ll_merge_fn find_ll_merge_fn(void *merge_attr, const char **cmdline)
+{
+ struct user_merge_fn *fn;