Commit 151eb4b9 151eb4b9e00a56a1e4c85e8120403385012eb95a by Sergey Poznyakoff

Fix command expansion in wordsplit

* libmailutils/string/wordsplit.c: Change ordering of expansions so
that command expansion occurs first.  This fixes nested expansions
and command expansions occurring after variable expansions.
* libmailutils/tests/wordsplit.at: Add more tests.
1 parent a923b221
...@@ -1332,7 +1332,6 @@ expcmd (struct mu_wordsplit *wsp, const char *str, size_t len, ...@@ -1332,7 +1332,6 @@ expcmd (struct mu_wordsplit *wsp, const char *str, size_t len,
1332 struct mu_wordsplit ws; 1332 struct mu_wordsplit ws;
1333 1333
1334 rc = _wsplt_subsplit (wsp, &ws, str, j, 1334 rc = _wsplt_subsplit (wsp, &ws, str, j,
1335 MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
1336 MU_WRDSF_WS | MU_WRDSF_QUOTE); 1335 MU_WRDSF_WS | MU_WRDSF_QUOTE);
1337 if (rc) 1336 if (rc)
1338 { 1337 {
...@@ -2065,29 +2064,41 @@ mu_wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex) ...@@ -2065,29 +2064,41 @@ mu_wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex)
2065 } 2064 }
2066 } 2065 }
2067 2066
2067
2068 /* This structure describes a single expansion phase */
2068 struct exptab 2069 struct exptab
2069 { 2070 {
2070 char *descr; 2071 char *descr; /* Textual description (for debugging) */
2071 int flag; 2072 int flag; /* MU_WRDSF_ bit that controls this phase */
2072 int opt; 2073 int opt; /* Entry-specific options (see EXPOPT_ flags below */
2073 int (*expansion) (struct mu_wordsplit *wsp); 2074 int (*expansion) (struct mu_wordsplit *wsp); /* expansion function */
2074 }; 2075 };
2075 2076
2077 /* The following options control expansions: */
2078 /* Normally the exptab entry is run if its flag bit is set in struct
2079 wordsplit. The EXPOPT_NEG option negates this test so that expansion
2080 is performed if its associated flag bit is not set in struct wordsplit. */
2076 #define EXPOPT_NEG 0x01 2081 #define EXPOPT_NEG 0x01
2082 /* Coalesce the input list before running the expansion. */
2077 #define EXPOPT_COALESCE 0x02 2083 #define EXPOPT_COALESCE 0x02
2078 2084
2079 static struct exptab exptab[] = { 2085 static struct exptab exptab[] = {
2080 { N_("WS trimming"), MU_WRDSF_WS, 0, mu_wordsplit_trimws }, 2086 { N_("WS trimming"), MU_WRDSF_WS, 0,
2081 { N_("tilde expansion"), MU_WRDSF_PATHEXPAND, 0, mu_wordsplit_tildexpand }, 2087 mu_wordsplit_trimws },
2088 { N_("command substitution"), MU_WRDSF_NOCMD, EXPOPT_NEG|EXPOPT_COALESCE,
2089 mu_wordsplit_cmdexp },
2090 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE,
2091 NULL },
2092 { N_("tilde expansion"), MU_WRDSF_PATHEXPAND, 0,
2093 mu_wordsplit_tildexpand },
2082 { N_("variable expansion"), MU_WRDSF_NOVAR, EXPOPT_NEG, 2094 { N_("variable expansion"), MU_WRDSF_NOVAR, EXPOPT_NEG,
2083 mu_wordsplit_varexp }, 2095 mu_wordsplit_varexp },
2084 { N_("quote removal"), 0, EXPOPT_NEG, 2096 { N_("quote removal"), 0, EXPOPT_NEG,
2085 wsnode_quoteremoval }, 2097 wsnode_quoteremoval },
2086 { N_("command substitution"), MU_WRDSF_NOCMD, EXPOPT_NEG|EXPOPT_COALESCE,
2087 mu_wordsplit_cmdexp },
2088 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE, 2098 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE,
2089 NULL }, 2099 NULL },
2090 { N_("path expansion"), MU_WRDSF_PATHEXPAND, 0, mu_wordsplit_pathexpand }, 2100 { N_("path expansion"), MU_WRDSF_PATHEXPAND, 0,
2101 mu_wordsplit_pathexpand },
2091 { NULL } 2102 { NULL }
2092 }; 2103 };
2093 2104
......
...@@ -421,4 +421,207 @@ NF: 1 ...@@ -421,4 +421,207 @@ NF: 1
421 [input exhausted 421 [input exhausted
422 ]) 422 ])
423 423
424 dnl Something that doesn't fit into TESTWSP
425
426 AT_SETUP([simple command substitution])
427 AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-1])
428 AT_CHECK([
429 mkdir dir
430 > dir/file
431
432 wsp -nocmd <<'EOT'
433 begin $(find dir) end
434 EOT
435 ],
436 [0],
437 [NF: 4
438 0: begin
439 1: dir
440 2: dir/file
441 3: end
442 ])
443 AT_CLEANUP
444
445 AT_SETUP([quoted command substitution])
446 AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-2])
447 AT_CHECK([
448 mkdir dir
449 > dir/file
450
451 wsp -nocmd <<'EOT'
452 begin "$(find dir)" end
453 EOT
454 ],
455 [0],
456 [NF: 3
457 0: begin
458 1: "dir dir/file"
459 2: end
460 ])
461 AT_CLEANUP
462
463 AT_SETUP([coalesced command substitution])
464 AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-3])
465 AT_CHECK([
466 mkdir dir
467 > dir/file
468
469 wsp -nocmd <<'EOT'
470 begin($(find dir))end
471 EOT
472 ],
473 [0],
474 [NF: 2
475 0: begin(dir
476 1: dir/file)end
477 ])
478 AT_CLEANUP
479
480 AT_SETUP([quoted coalesced command substitution])
481 AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-4])
482 AT_CHECK([
483 mkdir dir
484 > dir/file
485
486 wsp -nocmd <<'EOT'
487 "begin($(find dir))end"
488 EOT
489 ],
490 [0],
491 [NF: 1
492 0: "begin(dir dir/file)end"
493 ])
494 AT_CLEANUP
495
496 AT_SETUP([variable and command substitution])
497 AT_KEYWORDS([wordsplit wsp wsp-var wsp-var24 wsp-cmd wsp-cmd-5])
498 AT_CHECK([
499 mkdir dir
500 > dir/file
501
502 DIR=dir wsp -nocmd -novar<<'EOT'
503 begin $DIR $(find $DIR) end
504 EOT
505 ],
506 [0],
507 [NF: 5
508 0: begin
509 1: dir
510 2: dir
511 3: dir/file
512 4: end
513 ])
514 AT_CLEANUP
515
516 AT_SETUP([variable expansion and command substitution in quotes])
517 AT_KEYWORDS([wordsplit wsp wsp-var wsp-var25 wsp-cmd wsp-cmd-6])
518 AT_CHECK([
519 mkdir dir
520 > dir/file
521
522 DIR=dir BEGIN=begin wsp -nocmd -novar<<'EOT'
523 "${BEGIN}($(find $DIR))end"
524 EOT
525 ],
526 [0],
527 [NF: 1
528 0: "begin(dir dir/file)end"
529 ])
530 AT_CLEANUP
531
532 AT_SETUP([nested commands])
533 AT_KEYWORDS([wordsplit wsp wsp-cmd])
534 AT_CHECK([
535 AT_DATA([input],[foo
536 bar
537 baz
538 ])
539 SUFFIX=put wsp -nocmd -novar <<'EOT'
540 $(echo output $(cat in$SUFFIX))
541 EOT
542 ],
543 [0],
544 [NF: 4
545 0: output
546 1: foo
547 2: bar
548 3: baz
549 ])
550 AT_CLEANUP
551
552 AT_SETUP([pathname expansion])
553 AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-1])
554 AT_CHECK([
555 mkdir dir
556 > dir/1.c
557 > dir/2.c
558 > dir/3.b
559
560 wsp pathexpand<<'EOT'
561 begin dir/*.c end
562 EOT
563 ],
564 [0],
565 [NF: 4
566 0: begin
567 1: dir/1.c
568 2: dir/2.c
569 3: end
570 ])
571 AT_CLEANUP
572
573 AT_SETUP([pathname expansion: no match])
574 AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-2])
575 AT_CHECK([
576 mkdir dir
577 > dir/1.c
578 > dir/2.b
579
580 wsp pathexpand<<'EOT'
581 begin dir/*.d end
582 EOT
583 ],
584 [0],
585 [NF: 3
586 0: begin
587 1: dir/*.d
588 2: end
589 ])
590 AT_CLEANUP
591
592 AT_SETUP([pathname expansion: nullglob])
593 AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-3])
594 AT_CHECK([
595 mkdir dir
596 > dir/1.c
597 > dir/2.b
598
599 wsp pathexpand nullglob<<'EOT'
600 begin dir/*.d end
601 EOT
602 ],
603 [0],
604 [NF: 2
605 0: begin
606 1: end
607 ])
608 AT_CLEANUP
609
610 AT_SETUP([pathname expansion: failglob])
611 AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-4])
612 AT_CHECK([
613 mkdir dir
614 > dir/1.c
615 > dir/2.b
616
617 wsp pathexpand failglob<<'EOT'
618 begin dir/*.d end
619 EOT
620 ],
621 [0],
622 [],
623 [no files match pattern dir/*.d
624 ])
625 AT_CLEANUP
626
424 m4_popdef([TESTWSP]) 627 m4_popdef([TESTWSP])
......