+
+ if (one->sha1_valid &&
+ !memcmp(one->u.sha1, null_sha1, sizeof(null_sha1))) {
+ one->sha1_valid = 0;
+ one->u.name = name;
+ }
+
+ if (!one->sha1_valid) {
+ struct stat st;
+ temp->name = one->u.name;
+ if (stat(temp->name, &st) < 0) {
+ if (errno == ENOENT)
+ goto not_a_valid_file;
+ die("stat(%s): %s", temp->name, strerror(errno));
+ }
+ strcpy(temp->hex, ".");
+ sprintf(temp->mode, "%06o",
+ S_IFREG |ce_permissions(st.st_mode));
+ }
+ else {
+ int fd;
+ void *blob;
+ char type[20];
+ unsigned long size;
+
+ blob = read_sha1_file(one->u.sha1, type, &size);
+ if (!blob || strcmp(type, "blob"))
+ die("unable to read blob object for %s (%s)",
+ name, sha1_to_hex(one->u.sha1));
+
+ strcpy(temp->tmp_path, ".diff_XXXXXX");
+ fd = mkstemp(temp->tmp_path);
+ if (fd < 0)
+ die("unable to create temp-file");
+ if (write(fd, blob, size) != size)
+ die("unable to write temp-file");
+ close(fd);
+ free(blob);
+ temp->name = temp->tmp_path;
+ strcpy(temp->hex, sha1_to_hex(one->u.sha1));
+ temp->hex[40] = 0;
+ sprintf(temp->mode, "%06o", one->mode);
+ }
+}
+
+static void remove_tempfile(void)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (diff_temp[i].name == diff_temp[i].tmp_path) {
+ unlink(diff_temp[i].name);
+ diff_temp[i].name = NULL;
+ }
+}
+
+/* An external diff command takes:
+ *
+ * diff-cmd name infile1 infile1-sha1 infile1-mode \
+ * infile2 infile2-sha1 infile2-mode.
+ *
+ */
+void run_external_diff(const char *name,
+ struct diff_spec *one,
+ struct diff_spec *two)
+{
+ struct diff_tempfile *temp = diff_temp;
+ int pid, status;
+ static int atexit_asked = 0;
+
+ prepare_temp_file(name, &temp[0], one);
+ prepare_temp_file(name, &temp[1], two);
+ if (! atexit_asked &&
+ (temp[0].name == temp[0].tmp_path ||
+ temp[1].name == temp[1].tmp_path)) {
+ atexit_asked = 1;
+ atexit(remove_tempfile);
+ }
+
+ fflush(NULL);
+ pid = fork();
+ if (pid < 0)
+ die("unable to fork");
+ if (!pid) {
+ const char *pgm = external_diff();
+ if (pgm)
+ execlp(pgm, pgm,
+ name,
+ temp[0].name, temp[0].hex, temp[0].mode,
+ temp[1].name, temp[1].hex, temp[1].mode,
+ NULL);
+ /*
+ * otherwise we use the built-in one.
+ */
+ builtin_diff(name, temp);
+ exit(0);
+ }
+ if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status))
+ die("diff program failed");
+
+ remove_tempfile();
+}
+
+void show_diff_empty(const struct cache_entry *ce, int reverse)
+{
+ struct diff_spec spec[2], *one, *two;
+
+ memcpy(spec[0].u.sha1, ce->sha1, 20);
+ spec[0].mode = ntohl(ce->ce_mode);
+ spec[0].sha1_valid = spec[0].file_valid = 1;
+ spec[1].file_valid = 0;
+
+ if (reverse) {
+ one = spec + 1; two = spec;
+ } else {
+ one = spec; two = one + 1;
+ }
+
+ run_external_diff(ce->name, one, two);
+}
+
+void show_differences(const struct cache_entry *ce, int reverse)
+{
+ struct diff_spec spec[2], *one, *two;
+
+ memcpy(spec[0].u.sha1, ce->sha1, 20);
+ spec[0].mode = ntohl(ce->ce_mode);
+ spec[0].sha1_valid = spec[0].file_valid = 1;
+
+ spec[1].u.name = ce->name; /* the name we stated */
+ spec[1].sha1_valid = 0;
+ spec[1].file_valid = 1;
+
+ if (reverse) {
+ one = spec + 1; two = spec;
+ } else {
+ one = spec; two = one + 1;
+ }
+
+ run_external_diff(ce->name, one, two);