1/*
2* GIT - The information manager from hell
3*
4* Copyright (C) Linus Torvalds, 2005
5*/
6#include "cache.h"
7#include <ctype.h>
89
static char *diff_cmd = "diff -L '%s' -u -N - '%s'";
1011
/* 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{
26static char *buf = NULL;
27static int buf_size = -1;
28int cnt, c;
29char *cp;
3031
/* count single quote characters */
32for (cnt = 0, cp = src; *cp; cnt++, cp++)
33if (*cp == '\'')
34cnt += 3;
3536
if (buf_size < cnt) {
37free(buf);
38buf_size = cnt;
39buf = malloc(cnt);
40}
4142
cp = buf;
43while ((c = *src++)) {
44if (c != '\'')
45*cp++ = c;
46else {
47cp = strcpy(cp, "'\\''");
48cp += 4;
49}
50}
51*cp = 0;
52return buf;
53}
5455
static void show_differences(char *name, void *old_contents,
56unsigned long long old_size)
57{
58FILE *f;
59static char *cmd = NULL;
60static int cmd_size = -1;
6162
char *name_sq = sq_expand(name);
63int cmd_required_length = strlen(name_sq) * 2 + strlen(diff_cmd);
6465
if (cmd_size < cmd_required_length) {
66free(cmd);
67cmd_size = cmd_required_length;
68cmd = malloc(cmd_required_length);
69}
70snprintf(cmd, cmd_size, diff_cmd, name_sq, name_sq);
71f = popen(cmd, "w");
72if (old_size)
73fwrite(old_contents, old_size, 1, f);
74pclose(f);
75}
7677
static void show_diff_empty(struct cache_entry *ce)
78{
79char *old;
80unsigned long int size;
81int lines=0;
82unsigned char type[20], *p, *end;
8384
old = read_sha1_file(ce->sha1, type, &size);
85if (size > 0) {
86int startline = 1;
87int c = 0;
8889
printf("--- %s\n", ce->name);
90printf("+++ /dev/null\n");
91p = old;
92end = old + size;
93while (p < end)
94if (*p++ == '\n')
95lines ++;
96printf("@@ -1,%d +0,0 @@\n", lines);
97p = old;
98while (p < end) {
99c = *p++;
100if (startline) {
101putchar('-');
102startline = 0;
103}
104putchar(c);
105if (c == '\n')
106startline = 1;
107}
108if (c!='\n')
109printf("\n");
110fflush(stdout);
111}
112}
113114
static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]";
115116
static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
117{
118int i;
119int namelen = ce_namelen(ce);
120for (i = 0; i < cnt; i++) {
121int speclen = strlen(spec[i]);
122if (! strncmp(spec[i], ce->name, speclen) &&
123speclen <= namelen &&
124(ce->name[speclen] == 0 ||
125ce->name[speclen] == '/'))
126return 1;
127}
128return 0;
129}
130131
int main(int argc, char **argv)
132{
133int silent = 0;
134int silent_on_nonexisting_files = 0;
135int machine_readable = 0;
136int entries = read_cache();
137int i;
138139
while (1 < argc && argv[1][0] == '-') {
140if (!strcmp(argv[1], "-s"))
141silent_on_nonexisting_files = silent = 1;
142else if (!strcmp(argv[1], "-q"))
143silent_on_nonexisting_files = 1;
144else if (!strcmp(argv[1], "-z"))
145machine_readable = 1;
146else
147usage(show_diff_usage);
148argv++; argc--;
149}
150151
/* At this point, if argc == 1, then we are doing everything.
152* Otherwise argv[1] .. argv[argc-1] have the explicit paths.
153*/
154if (entries < 0) {
155perror("read_cache");
156exit(1);
157}
158for (i = 0; i < entries; i++) {
159struct stat st;
160struct cache_entry *ce = active_cache[i];
161int changed;
162unsigned long size;
163char type[20];
164void *old;
165166
if (1 < argc &&
167! matches_pathspec(ce, argv+1, argc-1))
168continue;
169170
if (stat(ce->name, &st) < 0) {
171if (errno == ENOENT && silent_on_nonexisting_files)
172continue;
173if (machine_readable)
174printf("X %s%c", ce->name, 0);
175else {
176printf("%s: %s\n", ce->name, strerror(errno));
177if (errno == ENOENT)
178show_diff_empty(ce);
179}
180continue;
181}
182changed = cache_match_stat(ce, &st);
183if (!changed)
184continue;
185if (!machine_readable)
186printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1));
187else {
188printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0);
189continue;
190}
191fflush(stdout);
192if (silent)
193continue;
194195
old = read_sha1_file(ce->sha1, type, &size);
196show_differences(ce->name, old, size);
197free(old);
198}
199return 0;
200}