Compute the ultimate fallback for exec_path from the program invocation.
[git/mingw.git] / exec_cmd.c
bloba9886b87fd943efae6406781f391ab15c0fb08b9
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 #ifdef __MINGW32__
84 if (*exec_dir != '/' && exec_dir[1] != ':') {
85 #else
86 if (*exec_dir != '/') {
87 #endif
88 if (!getcwd(git_command, sizeof(git_command))) {
89 fprintf(stderr, "git: cannot determine "
90 "current directory: %s\n",
91 strerror(errno));
92 break;
94 len = strlen(git_command);
96 /* Trivial cleanup */
97 while (!prefixcmp(exec_dir, "./")) {
98 exec_dir += 2;
99 while (*exec_dir == '/')
100 exec_dir++;
103 rc = snprintf(git_command + len,
104 sizeof(git_command) - len, "/%s",
105 exec_dir);
106 if (rc < 0 || rc >= sizeof(git_command) - len) {
107 fprintf(stderr, "git: command name given "
108 "is too long.\n");
109 break;
111 } else {
112 if (strlen(exec_dir) + 1 > sizeof(git_command)) {
113 fprintf(stderr, "git: command name given "
114 "is too long.\n");
115 break;
117 strcpy(git_command, exec_dir);
120 len = strlen(git_command);
121 rc = snprintf(git_command + len, sizeof(git_command) - len,
122 "/git-%s", argv[0]);
123 if (rc < 0 || rc >= sizeof(git_command) - len) {
124 fprintf(stderr,
125 "git: command name given is too long.\n");
126 break;
129 /* argv[0] must be the git command, but the argv array
130 * belongs to the caller, and my be reused in
131 * subsequent loop iterations. Save argv[0] and
132 * restore it on error.
135 tmp = argv[0];
136 argv[0] = git_command;
138 trace_argv_printf(argv, -1, "trace: exec:");
140 /* execve() can only ever return if it fails */
141 execve(git_command, (char **)argv, environ);
143 trace_printf("trace: exec failed: %s\n", strerror(errno));
145 argv[0] = tmp;
147 return -1;
152 int execl_git_cmd(const char *cmd,...)
154 int argc;
155 const char *argv[MAX_ARGS + 1];
156 const char *arg;
157 va_list param;
159 va_start(param, cmd);
160 argv[0] = cmd;
161 argc = 1;
162 while (argc < MAX_ARGS) {
163 arg = argv[argc++] = va_arg(param, char *);
164 if (!arg)
165 break;
167 va_end(param);
168 if (MAX_ARGS <= argc)
169 return error("too many args to run %s", cmd);
171 argv[argc] = NULL;
172 return execv_git_cmd(argv);
175 int spawnv_git_cmd(const char **argv, int pin[2], int pout[2])
177 char cmd[100];
178 int i, rc;
179 pid_t pid;
180 const char *paths[] = { current_exec_path,
181 getenv(EXEC_PATH_ENVIRONMENT),
182 builtin_exec_path() };
183 char p[3][PATH_MAX + 1];
184 char *usedpaths[4], **up = usedpaths;
185 const char *tmp;
187 for (i = 0; i < ARRAY_SIZE(paths); ++i) {
188 size_t len;
189 const char *exec_dir = paths[i];
191 if (!exec_dir || !*exec_dir) continue;
193 #ifdef __MINGW32__
194 if (*exec_dir != '/' && exec_dir[1] != ':') {
195 #else
196 if (*exec_dir != '/') {
197 #endif
198 if (!getcwd(p[i], sizeof(p[i]))) {
199 fprintf(stderr, "git: cannot determine "
200 "current directory: %s\n",
201 strerror(errno));
202 return -1;
204 len = strlen(p[i]);
206 /* Trivial cleanup */
207 while (!strncmp(exec_dir, "./", 2)) {
208 exec_dir += 2;
209 while (*exec_dir == '/')
210 exec_dir++;
213 rc = snprintf(p[i] + len,
214 sizeof(p[i]) - len, "/%s",
215 exec_dir);
216 if (rc < 0 || rc >= sizeof(p[i]) - len) {
217 fprintf(stderr, "git: command name given "
218 "is too long.\n");
219 return -1;
221 } else {
222 if (strlen(exec_dir) + 1 > sizeof(p[i])) {
223 fprintf(stderr, "git: command name given "
224 "is too long.\n");
225 return -1;
227 strcpy(p[i], exec_dir);
229 *up++ = p[i];
231 *up = NULL;
233 rc = snprintf(cmd, sizeof(cmd), "git-%s", argv[0]);
234 if (rc < 0 || rc >= sizeof(cmd)) {
235 fprintf(stderr,
236 "git: command name given is too long.\n");
237 return -1;
240 /* argv[0] must be the git command, but the argv array
241 * belongs to the caller. Save argv[0] and
242 * restore it later.
245 tmp = argv[0];
246 argv[0] = cmd;
248 trace_argv_printf(argv, -1, "trace: exec:");
250 pid = spawnvppe_pipe(cmd, argv, environ, usedpaths,
251 pin, pout);
253 argv[0] = tmp;
254 return pid;