2bd789c261851e899bdf653c1fa59d39412c6f51
   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 '%s' -u -N  - '%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, void *old_contents,
  50                             unsigned long long old_size)
  51{
  52        FILE *f;
  53        char *name_sq = sq_expand(name);
  54        int cmd_size = strlen(name_sq) * 2 + strlen(diff_cmd);
  55        char *cmd = malloc(cmd_size);
  56
  57        snprintf(cmd, cmd_size, diff_cmd, name_sq, name_sq);
  58        f = popen(cmd, "w");
  59        if (old_size)
  60                fwrite(old_contents, old_size, 1, f);
  61        pclose(f);
  62        free(name_sq);
  63        free(cmd);
  64}
  65
  66static void show_diff_empty(struct cache_entry *ce)
  67{
  68        char *old;
  69        unsigned long int size;
  70        int lines=0;
  71        unsigned char type[20], *p, *end;
  72
  73        old = read_sha1_file(ce->sha1, type, &size);
  74        if (size > 0) {
  75                int startline = 1;
  76                int c = 0;
  77
  78                printf("--- %s\n", ce->name);
  79                printf("+++ /dev/null\n");
  80                p = old;
  81                end = old + size;
  82                while (p < end)
  83                        if (*p++ == '\n')
  84                                lines ++;
  85                printf("@@ -1,%d +0,0 @@\n", lines);
  86                p = old;
  87                while (p < end) {
  88                        c = *p++;
  89                        if (startline) {
  90                                putchar('-');
  91                                startline = 0;
  92                        }
  93                        putchar(c);
  94                        if (c == '\n')
  95                                startline = 1;
  96                }
  97                if (c!='\n')
  98                        printf("\n");
  99                fflush(stdout);
 100        }
 101}
 102
 103static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]";
 104
 105static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
 106{
 107        int i;
 108        int namelen = ce_namelen(ce);
 109        for (i = 0; i < cnt; i++) {
 110                int speclen = strlen(spec[i]);
 111                if (! strncmp(spec[i], ce->name, speclen) &&
 112                    speclen <= namelen &&
 113                    (ce->name[speclen] == 0 ||
 114                     ce->name[speclen] == '/'))
 115                        return 1;
 116        }
 117        return 0;
 118}
 119
 120int main(int argc, char **argv)
 121{
 122        int silent = 0;
 123        int silent_on_nonexisting_files = 0;
 124        int machine_readable = 0;
 125        int entries = read_cache();
 126        int i;
 127
 128        while (1 < argc && argv[1][0] == '-') {
 129                if (!strcmp(argv[1], "-s"))
 130                        silent_on_nonexisting_files = silent = 1;
 131                else if (!strcmp(argv[1], "-q"))
 132                        silent_on_nonexisting_files = 1;
 133                else if (!strcmp(argv[1], "-z"))
 134                        machine_readable = 1;
 135                else
 136                        usage(show_diff_usage);
 137                argv++; argc--;
 138        }
 139
 140        /* At this point, if argc == 1, then we are doing everything.
 141         * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
 142         */
 143        if (entries < 0) {
 144                perror("read_cache");
 145                exit(1);
 146        }
 147        for (i = 0; i < entries; i++) {
 148                struct stat st;
 149                struct cache_entry *ce = active_cache[i];
 150                int changed;
 151                unsigned long size;
 152                char type[20];
 153                void *old;
 154
 155                if (1 < argc &&
 156                    ! matches_pathspec(ce, argv+1, argc-1))
 157                        continue;
 158
 159                if (ce_stage(ce)) {
 160                        if (machine_readable)
 161                                printf("U %s%c", ce->name, 0);
 162                        else
 163                                printf("%s: Unmerged\n",
 164                                       ce->name);
 165                        while (i < entries &&
 166                               !strcmp(ce->name, active_cache[i]->name))
 167                                i++;
 168                        i--; /* compensate for loop control increments */
 169                        continue;
 170                }
 171 
 172                if (stat(ce->name, &st) < 0) {
 173                        if (errno == ENOENT && silent_on_nonexisting_files)
 174                                continue;
 175                        if (machine_readable)
 176                                printf("X %s%c", ce->name, 0);
 177                        else {
 178                                printf("%s: %s\n", ce->name, strerror(errno));
 179                                if (errno == ENOENT)
 180                                        show_diff_empty(ce);
 181                        }
 182                        continue;
 183                }
 184                changed = cache_match_stat(ce, &st);
 185                if (!changed)
 186                        continue;
 187                if (!machine_readable)
 188                        printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1));
 189                else {
 190                        printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0);
 191                        continue;
 192                }
 193                fflush(stdout);
 194                if (silent)
 195                        continue;
 196
 197                old = read_sha1_file(ce->sha1, type, &size);
 198                show_differences(ce->name, old, size);
 199                free(old);
 200        }
 201        return 0;
 202}