Skip to content

Commit 4301605

Browse files
committed
Handle embedded %()-substitutions inside %(prompt) and %(sh)
Allowed are e.g.: %(prompt %(commit)), etc. and also recursive like %(sh printf %(sh printf)).
1 parent b536f3f commit 4301605

File tree

1 file changed

+68
-5
lines changed

1 file changed

+68
-5
lines changed

src/argv.c

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ format_expand_arg(struct format_context *format, const char *name, const char *e
300300
const char *value;
301301
const char *msgstart = name + STRING_SIZE("%(prompt");
302302
const int msglen = end - msgstart - 1;
303-
303+
char *tmp;
304304
if (end && msglen > 0 && string_format(msgbuf, "%.*s", msglen, msgstart)) {
305305
const char *msg = msgbuf;
306306

@@ -310,7 +310,9 @@ format_expand_arg(struct format_context *format, const char *name, const char *e
310310
prompt = msg;
311311
}
312312

313-
value = read_prompt(prompt);
313+
tmp=argv_format_arg(&argv_env, prompt);
314+
value = read_prompt(tmp);
315+
free(tmp);
314316
if (value == NULL)
315317
return false;
316318
return string_format_from(format->buf, &format->bufpos, "%s", value);
@@ -322,6 +324,7 @@ format_expand_arg(struct format_context *format, const char *name, const char *e
322324
const char *cstart = name + STRING_SIZE("%(sh");
323325
const int clen = end - cstart - 1;
324326
int size;
327+
char *tmp;
325328
FILE *cp;
326329
if (end && clen > 0 && string_format(cbuf, "sh -c '%.*s'", clen, cstart)) {
327330
c=cbuf;
@@ -330,7 +333,9 @@ format_expand_arg(struct format_context *format, const char *name, const char *e
330333
}
331334
if (!*c || strlen(c)==8)
332335
return false;
333-
cp=popen(c,"r");
336+
tmp=argv_format_arg(&argv_env, c);
337+
cp=popen(tmp,"r");
338+
free(tmp);
334339
size=fread(value,1,SIZEOF_STR-1,cp);
335340
if (value[0]==0 || size==0)
336341
return false;
@@ -352,6 +357,64 @@ format_expand_arg(struct format_context *format, const char *name, const char *e
352357
return false;
353358
}
354359

360+
static const char*
361+
get_closing_brace(const char *input)
362+
{
363+
const char *s=input,*cur, *tmp;
364+
int idx,level=0,pair_idx;
365+
int level_to_pos[3000]={[0 ... 2998]=-1, -2};
366+
int pos_to_level[256]={[0 ... 254]=-1, -2};
367+
int pair_map[3000]={[0 ... 2998]=-1, -2};
368+
int final_pairs[3000]={[0 ... 2998]=-1, -2};
369+
int first=-1;
370+
while ( (cur = strstr(s,"(")), (tmp = strstr(s,")")), (cur&&cur<tmp?cur:(cur=tmp))) {
371+
s=cur+1;
372+
idx=cur-input;
373+
if ( *cur == '(' ) {
374+
pos_to_level[idx]=++level;
375+
level_to_pos[level]=idx;
376+
} else if ( *cur == ')' ) {
377+
if ( level > 0 ) {
378+
pair_idx=level_to_pos[level];
379+
pos_to_level[idx]=level --;
380+
if (input[pair_idx]=='(' && input[idx]==')' ||
381+
input[pair_idx]==')' && input[idx]=='(' ) {
382+
final_pairs[idx]=pair_idx;
383+
final_pairs[pair_idx]=idx;
384+
}
385+
} else {
386+
pos_to_level[idx]=-1;
387+
}
388+
}
389+
}
390+
391+
for (idx=0;pos_to_level[idx]!=-2; idx++) {
392+
bool ok=false;
393+
if (pos_to_level[idx]==-1)
394+
continue;
395+
for (int j=0; final_pairs[j]!=-2;j++) {
396+
if (final_pairs[j]==-1)
397+
continue;
398+
if (final_pairs[j]==idx) {
399+
if (idx==0)
400+
first=j;
401+
ok=true;
402+
break;
403+
}
404+
}
405+
if (!ok)
406+
return NULL;
407+
}
408+
409+
if (pos_to_level[0] < 0)
410+
return NULL;
411+
if (first<0)
412+
return NULL;
413+
414+
return input+first;
415+
}
416+
417+
355418
static bool
356419
format_append_arg(struct format_context *format, const char ***dst_argv, const char *arg)
357420
{
@@ -362,7 +425,7 @@ format_append_arg(struct format_context *format, const char ***dst_argv, const c
362425
const char *var = strstr(arg, "%(");
363426
const char *esc = strstr(arg, "%%");
364427
bool is_escaped = esc && (esc < var || !var);
365-
const char *closing = var && !is_escaped ? strchr(var, ')') : NULL;
428+
const char *closing = var && !is_escaped ? get_closing_brace(var+1) : NULL;
366429
const char *next = is_escaped ? esc + 2 : closing ? closing + 1 : NULL;
367430
int len = var && !is_escaped ? var - arg : esc ? esc - arg + 1 : strlen(arg);
368431

@@ -372,7 +435,7 @@ format_append_arg(struct format_context *format, const char ***dst_argv, const c
372435
if (len && !string_format_from(format->buf, &format->bufpos, "%.*s", len, arg))
373436
return false;
374437

375-
if (var && !is_escaped && !format_expand_arg(format, var, next))
438+
if (var && !is_escaped && !format_expand_arg(format, var, next))
376439
return false;
377440

378441
arg = next;

0 commit comments

Comments
 (0)