Fix memory leak in prepend_to_path (git.c).
[git] / merge-index.c
1 #include <sys/types.h>
2 #include <sys/wait.h>
3 #include <signal.h>
4
5 #include "cache.h"
6
7 static const char *pgm;
8 static const char *arguments[8];
9 static int one_shot, quiet;
10 static int err;
11
12 static void run_program(void)
13 {
14         pid_t pid = fork();
15         int status;
16
17         if (pid < 0)
18                 die("unable to fork");
19         if (!pid) {
20                 execlp(pgm, arguments[0],
21                             arguments[1],
22                             arguments[2],
23                             arguments[3],
24                             arguments[4],
25                             arguments[5],
26                             arguments[6],
27                             arguments[7],
28                             NULL);
29                 die("unable to execute '%s'", pgm);
30         }
31         if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status)) {
32                 if (one_shot) {
33                         err++;
34                 } else {
35                         if (!quiet)
36                                 die("merge program failed");
37                         exit(1);
38                 }
39         }
40 }
41
42 static int merge_entry(int pos, const char *path)
43 {
44         int found;
45         
46         if (pos >= active_nr)
47                 die("git-merge-index: %s not in the cache", path);
48         arguments[0] = pgm;
49         arguments[1] = "";
50         arguments[2] = "";
51         arguments[3] = "";
52         arguments[4] = path;
53         arguments[5] = "";
54         arguments[6] = "";
55         arguments[7] = "";
56         found = 0;
57         do {
58                 static char hexbuf[4][60];
59                 static char ownbuf[4][60];
60                 struct cache_entry *ce = active_cache[pos];
61                 int stage = ce_stage(ce);
62
63                 if (strcmp(ce->name, path))
64                         break;
65                 found++;
66                 strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
67                 sprintf(ownbuf[stage], "%o", ntohl(ce->ce_mode) & (~S_IFMT));
68                 arguments[stage] = hexbuf[stage];
69                 arguments[stage + 4] = ownbuf[stage];
70         } while (++pos < active_nr);
71         if (!found)
72                 die("git-merge-index: %s not in the cache", path);
73         run_program();
74         return found;
75 }
76
77 static void merge_file(const char *path)
78 {
79         int pos = cache_name_pos(path, strlen(path));
80
81         /*
82          * If it already exists in the cache as stage0, it's
83          * already merged and there is nothing to do.
84          */
85         if (pos < 0)
86                 merge_entry(-pos-1, path);
87 }
88
89 static void merge_all(void)
90 {
91         int i;
92         for (i = 0; i < active_nr; i++) {
93                 struct cache_entry *ce = active_cache[i];
94                 if (!ce_stage(ce))
95                         continue;
96                 i += merge_entry(i, ce->name)-1;
97         }
98 }
99
100 int main(int argc, char **argv)
101 {
102         int i, force_file = 0;
103
104         /* Without this we cannot rely on waitpid() to tell
105          * what happened to our children.
106          */
107         signal(SIGCHLD, SIG_DFL);
108
109         if (argc < 3)
110                 usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
111
112         setup_git_directory();
113         read_cache();
114
115         i = 1;
116         if (!strcmp(argv[i], "-o")) {
117                 one_shot = 1;
118                 i++;
119         }
120         if (!strcmp(argv[i], "-q")) {
121                 quiet = 1;
122                 i++;
123         }
124         pgm = argv[i++];
125         for (; i < argc; i++) {
126                 char *arg = argv[i];
127                 if (!force_file && *arg == '-') {
128                         if (!strcmp(arg, "--")) {
129                                 force_file = 1;
130                                 continue;
131                         }
132                         if (!strcmp(arg, "-a")) {
133                                 merge_all();
134                                 continue;
135                         }
136                         die("git-merge-index: unknown option %s", arg);
137                 }
138                 merge_file(arg);
139         }
140         if (err && !quiet)
141                 die("merge program failed");
142         return err;
143 }