bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: declare -f does not output esac pattern correctly


From: Martin D Kealey
Subject: Re: declare -f does not output esac pattern correctly
Date: Tue, 27 Feb 2024 21:23:34 +1300 (NZDT)
User-agent: Alpine 2.21 (DEB 202 2017-01-01)

I've been thinking for a while now that POSIX made a mistake when it
permitted ';;' before the closing 'esac'. If ';;' were prohibited there,
then the parser could be sure that the next word after every ';;' would be a
pattern, even if it looks like 'esac'. But as things stand, there's an
ambiguity which has traditionally been resolved by assuming an unquoted
'esac' occurring in the 'expect-a-pattern' state is actually a
case-statement terminator. (And don't get me started on the stupidity of
intentionally mismatched parentheses.)

But that's all water under the bridge.

Prior to version 5.2 of Bash, even inserting '(' before esac wasn't enough
to hide it:

$ bash-5.1.12p1-release -c 'a () { case $1 in (esac) echo esac ; esac } ; type 
a'
bash-5.1.12p1-release: -c: line 1: syntax error near unexpected token `esac'
bash-5.1.12p1-release: -c: line 1: `a () { case $1 in (esac) echo esac ; esac } 
; type a'
$ bash-5.2.0p1-alpha -c 'a () { case $1 in (esac) echo esac ; esac } ; type a'
a is a function
a ()
{
    case $1 in
        esac)
            echo esac
        ;;
    esac
}

A better approach might be simply to quote 'esac' as a pattern words in the
output of declare or type. Herewith a patch that fixes both annoyances:

$ build/bash
$ ./bash
$ a() { case $1 in "") echo None ;; (esac) echo Esac ; esac }
$ shopt -p balanced_case_parens
shopt -u balanced_case_parens
$ type a
a is a function
a ()
{
    case $1 in
        "")
            echo None
        ;;
        \esac)
            echo Esac
    esac
}
$ shopt -s balanced_case_parens
$ type a
a is a function
a ()
{
    case $1 in
        ("")
            echo None
        ;;
        (esac)
            echo Esac
    esac
}
$ git d devel..@
diff --git a/builtins/shopt.def b/builtins/shopt.def
index b3e1cfe5..0a385a58 100644
--- a/builtins/shopt.def
+++ b/builtins/shopt.def
@@ -75,6 +75,7 @@ $END
 #define OPTFMT         "%-15s\t%s\n"

 extern int allow_null_glob_expansion, fail_glob_expansion, glob_dot_filenames;
+extern int balanced_case_parens;
 extern int cdable_vars, mail_warning, source_uses_path;
 extern int no_exit_on_failed_exec, print_shift_error;
 extern int check_hashed_filenames, promptvars;
@@ -182,6 +183,7 @@ static struct {
   { "array_expand_once", &expand_once_flag, set_array_expand },
   { "assoc_expand_once", &expand_once_flag, set_array_expand },
 #endif
+  { "balanced_case_parens", &balanced_case_parens, (shopt_set_func_t *)NULL },
   { "cdable_vars", &cdable_vars, (shopt_set_func_t *)NULL },
   { "cdspell", &cdspelling, (shopt_set_func_t *)NULL },
   { "checkhash", &check_hashed_filenames, (shopt_set_func_t *)NULL },
diff --git a/print_cmd.c b/print_cmd.c
index 330223d3..892443ab 100644
--- a/print_cmd.c
+++ b/print_cmd.c
@@ -52,6 +52,8 @@ extern int printf (const char *, ...);        /* Yuck.  
Double yuck. */
 static int indentation;
 static int indentation_amount = 4;

+int balanced_case_parens = 0;
+
 typedef void PFUNC (const char *, ...);

 static void cprintf (const char *, ...)  __attribute__((__format__ (printf, 1, 
2)));
@@ -771,6 +773,11 @@ print_case_clauses (PATTERN_LIST *clauses)
       if (printing_comsub == 0 || first == 0)
        newline ("");
       first = 0;
+      if (balanced_case_parens)
+        cprintf("(");
+      else if (!strcmp(clauses->patterns->word->word, "esac"))
+        cprintf("\\");
+
       command_print_word_list (clauses->patterns, " | ");
       cprintf (")\n");
       indentation += indentation_amount;
@@ -781,7 +788,7 @@ print_case_clauses (PATTERN_LIST *clauses)
        newline (";&");
       else if (clauses->flags & CASEPAT_TESTNEXT)
        newline (";;&");
-      else
+      else if (clauses->next) /* be unambiguous: omit last ';;' */
        newline (";;");
       clauses = clauses->next;
     }

-Martin



reply via email to

[Prev in Thread] Current Thread [Next in Thread]