git-commit: Implement racy-safe save_index() with ln instead of cp -p.
[git/dscho.git] / exec_cmd.c
blobbad4843113f9064315ef46d36243c6fc6eabd2a8
1 #include "cache.h"
2 #include "exec_cmd.h"
3 #include "quote.h"
4 #include "spawn-pipe.h"
5 #define MAX_ARGS 32
7 extern char **environ;
8 static const char *current_exec_path;
10 static const char *builtin_exec_path(void)
12 #ifndef __MINGW32__
13 return GIT_EXEC_PATH;
14 #else
15 int len;
16 char *p, *q, *sl;
17 static char *ep;
18 if (ep)
19 return ep;
21 len = strlen(_pgmptr);
22 if (len < 2)
23 return ep = ".";
25 p = ep = xmalloc(len+1);
26 q = _pgmptr;
27 sl = NULL;
28 /* copy program name, turn '\\' into '/', skip last part */
29 while ((*p = *q)) {
30 if (*q == '\\' || *q == '/') {
31 *p = '/';
32 sl = p;
34 p++, q++;
36 if (sl)
37 *sl = '\0';
38 else
39 ep[0] = '.', ep[1] = '\0';
40 return ep;
41 #endif
44 void git_set_exec_path(const char *exec_path)
46 current_exec_path = exec_path;
50 /* Returns the highest-priority, location to look for git programs. */
51 const char *git_exec_path(void)
53 const char *env;
55 if (current_exec_path)
56 return current_exec_path;
58 env = getenv(EXEC_PATH_ENVIRONMENT);
59 if (env && *env) {
60 return env;
63 return builtin_exec_path();
67 int execv_git_cmd(const char **argv)
69 char git_command[PATH_MAX + 1];
70 int i;
71 const char *paths[] = { current_exec_path,
72 getenv(EXEC_PATH_ENVIRONMENT),
73 builtin_exec_path() };
75 for (i = 0; i < ARRAY_SIZE(paths); ++i) {
76 size_t len;
77 int rc;
78 const char *exec_dir = paths[i];
79 const char *tmp;
81 if (!exec_dir || !*exec_dir) continue;
83 if (!is_absolute_path(exec_dir)) {
84 if (!getcwd(git_command, sizeof(git_command))) {
85 fprintf(stderr, "git: cannot determine "
86 "current directory: %s\n",
87 strerror(errno));
88 break;
90 len = strlen(git_command);
92 /* Trivial cleanup */
93 while (!prefixcmp(exec_dir, "./")) {
94 exec_dir += 2;
95 while (*exec_dir == '/')
96 exec_dir++;
99 rc = snprintf(git_command + len,
100 sizeof(git_command) - len, "/%s",
101 exec_dir);
102 if (rc < 0 || rc >= sizeof(git_command) - len) {
103 fprintf(stderr, "git: command name given "
104 "is too long.\n");
105 break;
107 } else {
108 if (strlen(exec_dir) + 1 > sizeof(git_command)) {
109 fprintf(stderr, "git: command name given "
110 "is too long.\n");
111 break;
113 strcpy(git_command, exec_dir);
116 len = strlen(git_command);
117 rc = snprintf(git_command + len, sizeof(git_command) - len,
118 "/git-%s", argv[0]);
119 if (rc < 0 || rc >= sizeof(git_command) - len) {
120 fprintf(stderr,
121 "git: command name given is too long.\n");
122 break;
125 /* argv[0] must be the git command, but the argv array
126 * belongs to the caller, and my be reused in
127 * subsequent loop iterations. Save argv[0] and
128 * restore it on error.
131 tmp = argv[0];
132 argv[0] = git_command;
134 trace_argv_printf(argv, -1, "trace: exec:");
136 /* execve() can only ever return if it fails */
137 execve(git_command, (char **)argv, environ);
139 trace_printf("trace: exec failed: %s\n", strerror(errno));
141 argv[0] = tmp;
143 return -1;
148 int execl_git_cmd(const char *cmd,...)
150 int argc;
151 const char *argv[MAX_ARGS + 1];
152 const char *arg;
153 va_list param;
155 va_start(param, cmd);
156 argv[0] = cmd;
157 argc = 1;
158 while (argc < MAX_ARGS) {
159 arg = argv[argc++] = va_arg(param, char *);
160 if (!arg)
161 break;
163 va_end(param);
164 if (MAX_ARGS <= argc)
165 return error("too many args to run %s", cmd);
167 argv[argc] = NULL;
168 return execv_git_cmd(argv);
171 int spawnv_git_cmd(const char **argv, int pin[2], int pout[2])
173 char cmd[100];
174 int i, rc;
175 pid_t pid;
176 const char *paths[] = { current_exec_path,
177 getenv(EXEC_PATH_ENVIRONMENT),
178 builtin_exec_path() };
179 char p[3][PATH_MAX + 1];
180 char *usedpaths[4], **up = usedpaths;
181 const char *tmp;
183 for (i = 0; i < ARRAY_SIZE(paths); ++i) {
184 size_t len;
185 const char *exec_dir = paths[i];
187 if (!exec_dir || !*exec_dir) continue;
189 if (!is_absolute_path(exec_dir)) {
190 if (!getcwd(p[i], sizeof(p[i]))) {
191 fprintf(stderr, "git: cannot determine "
192 "current directory: %s\n",
193 strerror(errno));
194 return -1;
196 len = strlen(p[i]);
198 /* Trivial cleanup */
199 while (!strncmp(exec_dir, "./", 2)) {
200 exec_dir += 2;
201 while (*exec_dir == '/')
202 exec_dir++;
205 rc = snprintf(p[i] + len,
206 sizeof(p[i]) - len, "/%s",
207 exec_dir);
208 if (rc < 0 || rc >= sizeof(p[i]) - len) {
209 fprintf(stderr, "git: command name given "
210 "is too long.\n");
211 return -1;
213 } else {
214 if (strlen(exec_dir) + 1 > sizeof(p[i])) {
215 fprintf(stderr, "git: command name given "
216 "is too long.\n");
217 return -1;
219 strcpy(p[i], exec_dir);
221 *up++ = p[i];
223 *up = NULL;
225 rc = snprintf(cmd, sizeof(cmd), "git-%s", argv[0]);
226 if (rc < 0 || rc >= sizeof(cmd)) {
227 fprintf(stderr,
228 "git: command name given is too long.\n");
229 return -1;
232 /* argv[0] must be the git command, but the argv array
233 * belongs to the caller. Save argv[0] and
234 * restore it later.
237 tmp = argv[0];
238 argv[0] = cmd;
240 trace_argv_printf(argv, -1, "trace: exec:");
242 pid = spawnvppe_pipe(cmd, argv, environ, usedpaths,
243 pin, pout);
245 argv[0] = tmp;
246 return pid;