]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/remote-ext.c
Merge branch 'js/update-index-ignore-removal-for-skip-worktree'
[thirdparty/git.git] / builtin / remote-ext.c
CommitLineData
c2e86add 1#include "builtin.h"
7f3ecebf
IL
2#include "transport.h"
3#include "run-command.h"
df1ed03a 4#include "pkt-line.h"
7f3ecebf 5
bb246590
JK
6static const char usage_msg[] =
7 "git remote-ext <remote> <url>";
8
7f3ecebf
IL
9/*
10 * URL syntax:
11 * 'command [arg1 [arg2 [...]]]' Invoke command with given arguments.
12 * Special characters:
13 * '% ': Literal space in argument.
14 * '%%': Literal percent sign.
15 * '%S': Name of service (git-upload-pack/git-upload-archive/
16 * git-receive-pack.
17 * '%s': Same as \s, but with possible git- prefix stripped.
18 * '%G': Only allowed as first 'character' of argument. Do not pass this
19 * Argument to command, instead send this as name of repository
20 * in in-line git://-style request (also activates sending this
21 * style of request).
22 * '%V': Only allowed as first 'character' of argument. Used in
23 * conjunction with '%G': Do not pass this argument to command,
24 * instead send this as vhost in git://-style request (note: does
25 * not activate sending git:// style request).
26 */
27
28static char *git_req;
29static char *git_req_vhost;
30
31static char *strip_escapes(const char *str, const char *service,
32 const char **next)
33{
34 size_t rpos = 0;
35 int escape = 0;
36 char special = 0;
e3f1da98 37 const char *service_noprefix = service;
7f3ecebf
IL
38 struct strbuf ret = STRBUF_INIT;
39
e3f1da98 40 skip_prefix(service_noprefix, "git-", &service_noprefix);
7f3ecebf
IL
41
42 /* Pass the service to command. */
43 setenv("GIT_EXT_SERVICE", service, 1);
e3f1da98 44 setenv("GIT_EXT_SERVICE_NOPREFIX", service_noprefix, 1);
7f3ecebf
IL
45
46 /* Scan the length of argument. */
47 while (str[rpos] && (escape || str[rpos] != ' ')) {
48 if (escape) {
49 switch (str[rpos]) {
50 case ' ':
51 case '%':
52 case 's':
53 case 'S':
54 break;
55 case 'G':
56 case 'V':
57 special = str[rpos];
58 if (rpos == 1)
59 break;
1cf01a34 60 /* fallthrough */
7f3ecebf
IL
61 default:
62 die("Bad remote-ext placeholder '%%%c'.",
63 str[rpos]);
64 }
65 escape = 0;
66 } else
67 escape = (str[rpos] == '%');
68 rpos++;
69 }
70 if (escape && !str[rpos])
71 die("remote-ext command has incomplete placeholder");
72 *next = str + rpos;
73 if (**next == ' ')
74 ++*next; /* Skip over space */
75
76 /*
77 * Do the actual placeholder substitution. The string will be short
78 * enough not to overflow integers.
79 */
80 rpos = special ? 2 : 0; /* Skip first 2 bytes in specials. */
81 escape = 0;
82 while (str[rpos] && (escape || str[rpos] != ' ')) {
83 if (escape) {
84 switch (str[rpos]) {
85 case ' ':
86 case '%':
87 strbuf_addch(&ret, str[rpos]);
88 break;
89 case 's':
e3f1da98 90 strbuf_addstr(&ret, service_noprefix);
7f3ecebf
IL
91 break;
92 case 'S':
93 strbuf_addstr(&ret, service);
94 break;
95 }
96 escape = 0;
97 } else
98 switch (str[rpos]) {
99 case '%':
100 escape = 1;
101 break;
102 default:
103 strbuf_addch(&ret, str[rpos]);
104 break;
105 }
106 rpos++;
107 }
108 switch (special) {
109 case 'G':
110 git_req = strbuf_detach(&ret, NULL);
111 return NULL;
112 case 'V':
113 git_req_vhost = strbuf_detach(&ret, NULL);
114 return NULL;
115 default:
116 return strbuf_detach(&ret, NULL);
117 }
118}
119
850d2fec 120static void parse_argv(struct argv_array *out, const char *arg, const char *service)
7f3ecebf 121{
7f3ecebf 122 while (*arg) {
850d2fec 123 char *expanded = strip_escapes(arg, service, &arg);
7f3ecebf 124 if (expanded)
850d2fec
JK
125 argv_array_push(out, expanded);
126 free(expanded);
7f3ecebf 127 }
7f3ecebf
IL
128}
129
130static void send_git_request(int stdin_fd, const char *serv, const char *repo,
131 const char *vhost)
132{
df1ed03a 133 if (!vhost)
81c634e9 134 packet_write_fmt(stdin_fd, "%s %s%c", serv, repo, 0);
7f3ecebf 135 else
81c634e9 136 packet_write_fmt(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0,
df1ed03a 137 vhost, 0);
7f3ecebf
IL
138}
139
140static int run_child(const char *arg, const char *service)
141{
142 int r;
d3180279 143 struct child_process child = CHILD_PROCESS_INIT;
7f3ecebf 144
7f3ecebf
IL
145 child.in = -1;
146 child.out = -1;
147 child.err = 0;
850d2fec 148 parse_argv(&child.args, arg, service);
7f3ecebf
IL
149
150 if (start_command(&child) < 0)
151 die("Can't run specified command");
152
153 if (git_req)
154 send_git_request(child.in, service, git_req, git_req_vhost);
155
156 r = bidirectional_transfer_loop(child.out, child.in);
157 if (!r)
158 r = finish_command(&child);
159 else
160 finish_command(&child);
161 return r;
162}
163
164#define MAXCOMMAND 4096
165
166static int command_loop(const char *child)
167{
168 char buffer[MAXCOMMAND];
169
170 while (1) {
60a2e332 171 size_t i;
7f3ecebf
IL
172 if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
173 if (ferror(stdin))
832c0e5e 174 die("Command input error");
7f3ecebf
IL
175 exit(0);
176 }
177 /* Strip end of line characters. */
60a2e332
JN
178 i = strlen(buffer);
179 while (i > 0 && isspace(buffer[i - 1]))
180 buffer[--i] = 0;
7f3ecebf
IL
181
182 if (!strcmp(buffer, "capabilities")) {
183 printf("*connect\n\n");
184 fflush(stdout);
185 } else if (!strncmp(buffer, "connect ", 8)) {
186 printf("\n");
187 fflush(stdout);
188 return run_child(child, buffer + 8);
189 } else {
190 fprintf(stderr, "Bad command");
191 return 1;
192 }
193 }
194}
195
196int cmd_remote_ext(int argc, const char **argv, const char *prefix)
197{
7851b1e6 198 if (argc != 3)
bb246590 199 usage(usage_msg);
7f3ecebf
IL
200
201 return command_loop(argv[2]);
202}