32d2fceaff2a3fb8e3e52d4708946e205ab41d76
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7
   8static char *diff_cmd = "diff -L 'a/%s' -L 'b/%s' -p -u - '%s'";
   9
  10/* Help to copy the thing properly quoted for the shell safety.
  11 * any single quote is replaced with '\'', and the caller is
  12 * expected to enclose the result within a single quote pair.
  13 *
  14 * E.g.
  15 *  original     sq_expand     result
  16 *  name     ==> name      ==> 'name'
  17 *  a b      ==> a b       ==> 'a b'
  18 *  a'b      ==> a'\''b    ==> 'a'\''b'
  19 *
  20 * NOTE! The returned memory belongs to this function so
  21 * do not free it.
  22 */
  23static char *sq_expand(char *src)
  24{
  25        static char *buf = NULL;
  26        int cnt, c;
  27        char *cp;
  28
  29        /* count bytes needed to store the quoted string. */ 
  30        for (cnt = 1, cp = src; *cp; cnt++, cp++)
  31                if (*cp == '\'')
  32                        cnt += 3;
  33
  34        if (! (buf = malloc(cnt)))
  35            return buf;
  36        cp = buf;
  37        while ((c = *src++)) {
  38                if (c != '\'')
  39                        *cp++ = c;
  40                else {
  41                        cp = strcpy(cp, "'\\''");
  42                        cp += 4;
  43                }
  44        }
  45        *cp = 0;
  46        return buf;
  47}
  48
  49static void show_differences(char *name, char *label, void *old_contents,
  50                             unsigned long long old_size)
  51{
  52        FILE *f;
  53        char *name_sq = sq_expand(name);
  54        char *label_sq = (name != label) ? sq_expand(label) : name_sq;
  55        int cmd_size = strlen(name_sq) +
  56                strlen(label_sq) * 2 + strlen(diff_cmd);
  57        char *cmd = malloc(cmd_size);
  58
  59        fflush(stdout);
  60        snprintf(cmd, cmd_size, diff_cmd, label_sq, label_sq, name_sq);
  61        f = popen(cmd, "w");
  62        if (old_size)
  63                fwrite(old_contents, old_size, 1, f);
  64        pclose(f);
  65        if (label_sq != name_sq)
  66                free(label_sq);
  67        free(name_sq);
  68        free(cmd);
  69}
  70
  71static void show_diff_empty(struct cache_entry *ce)
  72{
  73        char *old;
  74        unsigned long int size;
  75        unsigned char type[20];
  76
  77        old = read_sha1_file(ce->sha1, type, &size);
  78        if (! old) {
  79                error("unable to read blob object for %s (%s)", ce->name,
  80                      sha1_to_hex(ce->sha1));
  81                return;
  82        }
  83        show_differences("/dev/null", ce->name, old, size);
  84}
  85
  86static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]";
  87
  88static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
  89{
  90        int i;
  91        int namelen = ce_namelen(ce);
  92        for (i = 0; i < cnt; i++) {
  93                int speclen = strlen(spec[i]);
  94                if (! strncmp(spec[i], ce->name, speclen) &&
  95                    speclen <= namelen &&
  96                    (ce->name[speclen] == 0 ||
  97                     ce->name[speclen] == '/'))
  98                        return 1;
  99        }
 100        return 0;
 101}
 102
 103int main(int argc, char **argv)
 104{
 105        int silent = 0;
 106        int silent_on_nonexisting_files = 0;
 107        int machine_readable = 0;
 108        int entries = read_cache();
 109        int i;
 110
 111        while (1 < argc && argv[1][0] == '-') {
 112                if (!strcmp(argv[1], "-s"))
 113                        silent_on_nonexisting_files = silent = 1;
 114                else if (!strcmp(argv[1], "-q"))
 115                        silent_on_nonexisting_files = 1;
 116                else if (!strcmp(argv[1], "-z"))
 117                        machine_readable = 1;
 118                else
 119                        usage(show_diff_usage);
 120                argv++; argc--;
 121        }
 122
 123        /* At this point, if argc == 1, then we are doing everything.
 124         * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
 125         */
 126        if (entries < 0) {
 127                perror("read_cache");
 128                exit(1);
 129        }
 130        for (i = 0; i < entries; i++) {
 131                struct stat st;
 132                struct cache_entry *ce = active_cache[i];
 133                int changed;
 134                unsigned long size;
 135                char type[20];
 136                void *old;
 137
 138                if (1 < argc &&
 139                    ! matches_pathspec(ce, argv+1, argc-1))
 140                        continue;
 141
 142                if (ce_stage(ce)) {
 143                        if (machine_readable)
 144                                printf("U %s%c", ce->name, 0);
 145                        else
 146                                printf("%s: Unmerged\n",
 147                                       ce->name);
 148                        while (i < entries &&
 149                               !strcmp(ce->name, active_cache[i]->name))
 150                                i++;
 151                        i--; /* compensate for loop control increments */
 152                        continue;
 153                }
 154 
 155                if (stat(ce->name, &st) < 0) {
 156                        if (errno == ENOENT && silent_on_nonexisting_files)
 157                                continue;
 158                        if (machine_readable)
 159                                printf("X %s%c", ce->name, 0);
 160                        else {
 161                                printf("%s: %s\n", ce->name, strerror(errno));
 162                                if (errno == ENOENT)
 163                                        show_diff_empty(ce);
 164                        }
 165                        continue;
 166                }
 167                changed = cache_match_stat(ce, &st);
 168                if (!changed)
 169                        continue;
 170                if (!machine_readable)
 171                        printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1));
 172                else {
 173                        printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0);
 174                        continue;
 175                }
 176                if (silent)
 177                        continue;
 178
 179                old = read_sha1_file(ce->sha1, type, &size);
 180                if (! old)
 181                        error("unable to read blob object for %s (%s)",
 182                              ce->name, sha1_to_hex(ce->sha1));
 183                else
 184                        show_differences(ce->name, ce->name, old, size);
 185                free(old);
 186        }
 187        return 0;
 188}