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