bug-bash
[Top][All Lists]
Advanced

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

(submission) patch to 'parse.y'


From: William Park
Subject: (submission) patch to 'parse.y'
Date: Tue, 5 Jul 2005 23:49:10 -0400
User-agent: Mutt/1.4.2i

Hi Chet,

While the iron is still hot, I am submitting the following modification
that I made to 'parse.y' and any other files related to features added
to 'parse.y'.  I would like to see some of them included in the next
release.

1.  <<<<
    http://home.eol.ca/~parkw/index.html#here_file

    Here-file.  File is read and the content is inserted into where
    here-document would be.  From then on, it's here-document.


2.  <<+
    http://home.eol.ca/~parkw/index.html#here_document

    Similar to '<<-', but keeps relative indentation.  For the first
    line, all indentations (spaces and tabs) are removed.  For the rest
    of lines, all indentations are removed, and if it's greater than the
    first's, then the difference is put back using tabs and spaces.  Tab
    is 8 spaces.


3.  try
        raise xxx
    done in
        xxx) echo x ;;
        yyy) echo y ;;
    esac
    http://home.eol.ca/~parkw/index.html#exception

    This implements exception handling, found in other languages.  New
    construct 'try' and new builtin 'raise' are added.  As you can
    guess, it is concatenation of loop contruct and case statement;
    try-block is like while-loop that runs only once, and 'raise'
    builtin within try-block is like 'break' builtin within loops.  And,
    the exception is caught using 'case' statement.

    When you raise string exception inside try-block, execution breaks
    out, just like breaking out of loops.  But, with exception, it
    breaks upward also, until it is caught by the case statement, which
    is implemented using the same routine as standard case statement.


4.  for a,b,c in ... ; do
        ...
    done
    http://home.eol.ca/~parkw/index.html#for_while_until

    Now that I can read from positional parameters and array, I don't
    use it as often.  But, it's still useful.  The only thing is that
    the variables must be typed explicitly.  They are not expanded.
    Perhaps, it should be... not sure.  I think Zsh has something
    similar.
    

5.  for/while/until ... ; do
        ...
    done then
        echo normal end
    else
        echo used break
    fi
    http://home.eol.ca/~parkw/index.html#for_while_until

    This feature allows you to test how you exited the loop, without
    using flags.  If you used 'break', then else-command will be
    executed.  If you exited normally, then then-command will be
    executed.  Python has something similar.

6.  case ... in
        glob) ... ;;
        regex)) ... ;;
        ...) command1 ;&
        ...) command2 ;;&
        ...) command3 ;;
    esac
    http://home.eol.ca/~parkw/index.html#case

    I added regex support, so that you can specify regex using '))'.  If
    there is any parenthesized group in regex, then it will return the
    subgroups in SUBMATCH array variable, which is the same one used by
    my 'match' builtin.  You can change it to what [[ string ~= regex ]]
    uses.

    Ksh and Zsh has ';&' which will continue on to subsequent command,
    rather than terminating the case statement.  With ';;&', next
    pattern will be tested, rather than terminating the case statement.


Okey, here is the relevant portion of diff file, which can also be found
at
    http://home.eol.ca/~parkw/bashdiff/bashdiff-1.23rc.tar.gz


diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/builtins/break.def ../bash/builtins/break.def
--- ../bash-3.0/builtins/break.def      2003-12-19 17:56:38.000000000 -0500
+++ ../bash/builtins/break.def  2005-06-27 01:36:18.000000000 -0400
@@ -130,3 +130,51 @@
 
   return (loop_level);
 }
+
+
+/*******************************************************************************
+ * Raise string exception for try-block.  --William
+ */
+$BUILTIN raise
+$FUNCTION raise_builtin
+$SHORT_DOC raise [exception]
+Raise string exception (default '') which will be caught in 'try' block.
+$END
+
+char *exception = (char *)NULL;        
+int try_level = 0;             /* similar to loop_level above */
+
+#include "bashgetopt.h"                /* loptend */
+
+
+int
+raise_builtin (list)
+    WORD_LIST *list;
+{
+    if (no_options (list))
+       return (EX_BADUSAGE);
+    list = loptend;             /* skip over possible `--' */
+
+    if (exception) {
+       builtin_error ("exception is already raised.");
+       return (EXECUTION_FAILURE);
+    }
+    if (try_level == 0) {
+#if defined (BREAK_COMPLAINS)
+       if (posixly_correct == 0)
+           builtin_error ("meaningful only in a `try' block");
+#endif
+       return (EXECUTION_SUCCESS);
+    }
+    if (list == 0) {           /* 0 argument */
+       exception = savestring ("");
+       return (EXECUTION_SUCCESS);
+    }
+    if (list->next == 0) {     /* 1 argument */
+       exception = savestring (list->word->word);
+       return (EXECUTION_SUCCESS);
+    }
+    builtin_usage ();          /* 2 or more arguments */
+    return (EX_BADUSAGE);
+}
+/******************************************************************************/
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/command.h ../bash/command.h
--- ../bash-3.0/command.h       2003-09-12 15:13:04.000000000 -0400
+++ ../bash/command.h   2005-07-03 16:18:11.000000000 -0400
@@ -28,7 +28,9 @@
 enum r_instruction {
   r_output_direction, r_input_direction, r_inputa_direction,
   r_appending_to, r_reading_until, r_reading_string,
+  r_reading_herefile,          /* <<<<  --William */
   r_duplicating_input, r_duplicating_output, r_deblank_reading_until,
+  r_deblank_reading_until_using_firstline,             /* <<+  --William */
   r_close_this, r_err_and_out, r_input_output, r_output_force,
   r_duplicating_input_word, r_duplicating_output_word,
   r_move_input, r_move_output, r_move_input_word, r_move_output_word
@@ -63,6 +65,7 @@
 
 /* Command Types: */
 enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
+                   cm_try,             /* --William */
                    cm_connection, cm_function_def, cm_until, cm_group,
                    cm_arith, cm_cond, cm_arith_for, cm_subshell };
 
@@ -157,6 +160,7 @@
     struct for_com *For;
     struct case_com *Case;
     struct while_com *While;
+    struct try_com *Try;               /* --William */
     struct if_com *If;
     struct connection *Connection;
     struct simple_com *Simple;
@@ -193,6 +197,10 @@
   struct pattern_list *next;   /* Clause to try in case this one failed. */
   WORD_LIST *patterns;         /* Linked list of patterns to test. */
   COMMAND *action;             /* Thing to execute if a pattern matches. */
+
+  int glob_or_regex;           /* 0=glob, 1=regex  --William */
+  int goto_next;               /* 0=;; (original) 1=;&  2=;;&  --William */
+
 } PATTERN_LIST;
 
 /* The CASE command. */
@@ -201,6 +209,11 @@
   int line;                    /* line number the `case' keyword appears on */
   WORD_DESC *word;             /* The thing to test. */
   PATTERN_LIST *clauses;       /* The clauses to test against, or NULL. */
+
+  COMMAND *true_case;          /* Commands to run, when there is a match  
--William */
+  COMMAND *false_case;         /* Commands to run, when there is no match  
--William */
+  int total_match;             /* total number of match  --William */
+
 } CASE_COM;
 
 /* FOR command. */
@@ -212,6 +225,10 @@
   COMMAND *action;     /* The action to execute.
                           During execution, NAME is bound to successive
                           members of MAP_LIST. */
+
+  COMMAND *then_action;                /* Commands to run, when loop exits 
normally  --William */
+  COMMAND *else_action;                /* Commands to run, when break is used  
--William */
+
 } FOR_COM;
 
 #if defined (ARITH_FOR_COMMAND)
@@ -251,8 +268,24 @@
   int flags;                   /* See description of CMD flags. */
   COMMAND *test;               /* Thing to test. */
   COMMAND *action;             /* Thing to do while test is non-zero. */
+
+  COMMAND *then_action;                /* Commands to run, when loop exits 
normally  --William */
+  COMMAND *else_action;                /* Commands to run, when break is used  
--William */
+
 } WHILE_COM;
 
+
+/*******************************************************************************
+ * TRY command.  --William
+ */
+typedef struct try_com {
+  int flags;                   /* See description of CMD flags. */
+  COMMAND *action;             /* try-block */
+  PATTERN_LIST *clauses;       /* The clauses to test against, or NULL. */
+} TRY_COM;
+/******************************************************************************/
+
+
 #if defined (DPAREN_ARITHMETIC)
 /* The arithmetic evaluation command, ((...)).  Just a set of flags and
    a WORD_LIST, of which the first element is the only one used, for the
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/copy_cmd.c ../bash/copy_cmd.c
--- ../bash-3.0/copy_cmd.c      2003-10-07 11:43:44.000000000 -0400
+++ ../bash/copy_cmd.c  2005-07-03 03:08:04.000000000 -0400
@@ -85,6 +85,10 @@
   new_clause = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
   new_clause->patterns = copy_word_list (clause->patterns);
   new_clause->action = copy_command (clause->action);
+
+  new_clause->glob_or_regex = clause->glob_or_regex;   /* --William */
+  new_clause->goto_next = clause->goto_next;           /* --William */
+
   return (new_clause);
 }
 
@@ -116,9 +120,11 @@
     {
     case r_reading_until:
     case r_deblank_reading_until:
+    case r_deblank_reading_until_using_firstline:      /* <<+  --William */
       new_redirect->here_doc_eof = savestring (redirect->here_doc_eof);
       /*FALLTHROUGH*/
     case r_reading_string:
+    case r_reading_herefile:           /* <<<<  --William */
     case r_appending_to:
     case r_output_direction:
     case r_input_direction:
@@ -169,6 +175,10 @@
   new_for->name = copy_word (com->name);
   new_for->map_list = copy_word_list (com->map_list);
   new_for->action = copy_command (com->action);
+
+  new_for->then_action = copy_command (com->then_action);      /* --William */
+  new_for->else_action = copy_command (com->else_action);      /* --William */
+
   return (new_for);
 }
 
@@ -224,6 +234,11 @@
   new_case->line = com->line;
   new_case->word = copy_word (com->word);
   new_case->clauses = copy_case_clauses (com->clauses);
+
+  new_case->true_case = copy_command (com->true_case);         /* --William */
+  new_case->false_case = copy_command (com->false_case);       /* --William */
+  new_case->total_match = com->total_match;                    /* --William */
+
   return (new_case);
 }
 
@@ -237,9 +252,29 @@
   new_while->flags = com->flags;
   new_while->test = copy_command (com->test);
   new_while->action = copy_command (com->action);
+
+  new_while->then_action = copy_command (com->then_action);    /* --William */
+  new_while->else_action = copy_command (com->else_action);    /* --William */
+
   return (new_while);
 }
 
+
+/* --William */
+static TRY_COM *
+copy_try_command (com)
+    TRY_COM *com;
+{
+    TRY_COM *new_try;
+
+    new_try = (TRY_COM *) xmalloc (sizeof (TRY_COM));
+    new_try->flags = com->flags;
+    new_try->action = copy_command (com->action);
+    new_try->clauses = copy_case_clauses (com->clauses);
+    return (new_try);
+}
+
+
 static IF_COM *
 copy_if_command (com)
      IF_COM *com;
@@ -382,6 +417,10 @@
        new_command->value.While = copy_while_command (command->value.While);
        break;
 
+      case cm_try:             /* --William */
+       new_command->value.Try = copy_try_command (command->value.Try);
+       break;
+
       case cm_if:
        new_command->value.If = copy_if_command (command->value.If);
        break;
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/dispose_cmd.c ../bash/dispose_cmd.c
--- ../bash-3.0/dispose_cmd.c   2003-02-15 15:48:36.000000000 -0500
+++ ../bash/dispose_cmd.c       2005-07-03 03:08:58.000000000 -0400
@@ -59,6 +59,31 @@
        dispose_word (c->name);
        dispose_words (c->map_list);
        dispose_command (c->action);
+
+       if (command->type == cm_for) {          /* --William */
+           dispose_command (c->then_action);
+           dispose_command (c->else_action);
+       }
+
+       free (c);
+       break;
+      }
+
+    case cm_try:               /* --William */
+    {
+       register TRY_COM *c;
+       PATTERN_LIST *t, *p;
+
+       c = command->value.Try;
+       dispose_command (c->action);
+       for (p = c->clauses; p; )
+         {
+           dispose_words (p->patterns);
+           dispose_command (p->action);
+           t = p;
+           p = p->next;
+           free (t);
+         }
        free (c);
        break;
       }
@@ -100,6 +125,9 @@
        c = command->value.Case;
        dispose_word (c->word);
 
+       dispose_command (c->true_case);         /* --William */
+       dispose_command (c->false_case);        /* --William */
+
        for (p = c->clauses; p; )
          {
            dispose_words (p->patterns);
@@ -120,6 +148,10 @@
        c = command->value.While;
        dispose_command (c->test);
        dispose_command (c->action);
+
+       dispose_command (c->then_action);       /* --William */
+       dispose_command (c->else_action);       /* --William */
+
        free (c);
        break;
       }
@@ -300,9 +332,11 @@
        {
        case r_reading_until:
        case r_deblank_reading_until:
+       case r_deblank_reading_until_using_firstline:   /* <<+  --William */
          free (t->here_doc_eof);
        /*FALLTHROUGH*/
        case r_reading_string:
+       case r_reading_herefile:                /* <<<<  --William */
        case r_output_direction:
        case r_input_direction:
        case r_inputa_direction:
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/execute_cmd.2.c ../bash/execute_cmd.2.c
--- ../bash-3.0/execute_cmd.2.c 1969-12-31 19:00:00.000000000 -0500
+++ ../bash/execute_cmd.2.c     2005-06-10 00:14:52.000000000 -0400
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * try-block with string exception.  New builtin 'raise' is used to raise an
+ * exception.  Once exception is raised, it will break out of all loops and 
skip
+ * all commands, until the exception is "caught" in case-like statement or 
until
+ * there is no more try-block.
+ *
+ *     TRY 
+ *         commands
+ *     DONE
+ *  or 
+ *     TRY 
+ *         commands
+ *     DONE IN
+ *         glob) ... ;;
+ *         regex)) ... ;;
+ *     ESAC
+ *
+ * So, the try-block is like while-loop running only once, and case-like
+ * statement is handled exactly like the normal case statement, where the
+ * exception is now the string to test for.
+ *
+ * --William
+ */
+
+static int
+execute_try_command (try_command)
+    TRY_COM *try_command;
+{
+    int out, ignore_return;
+
+    ignore_return = try_command->flags & CMD_IGNORE_RETURN;
+    if (ignore_return)
+       try_command->action->flags |= CMD_IGNORE_RETURN;
+
+    out = EXECUTION_SUCCESS;
+
+    try_level++;
+    do {
+       REAP ();
+       QUIT;
+       out = execute_command (try_command->action);
+       QUIT;
+    } while (0);       /* run only once */
+    try_level--;
+   
+    if (exception) {
+       CASE_COM *catch;
+       char *old;
+
+       catch = (CASE_COM *)xmalloc (sizeof (CASE_COM));        /* free this */
+       catch->flags = try_command->flags;
+       catch->line = line_number;
+       catch->word = make_bare_word (exception);       /* free this */
+       catch->clauses = try_command->clauses;
+       catch->true_case = (COMMAND *)NULL;
+       catch->false_case = (COMMAND *)NULL;
+       catch->total_match = 0;
+
+       old = exception; 
+       exception = (char *)NULL;       /* so that 'case' statement will run */
+
+       execute_case_command (catch);
+
+       if (exception)          /* new exception raised from 'case' statement */
+           FREE (old);
+       else if (catch->total_match)    /* exception was caught */
+           FREE (old);
+       else if (try_level == 0)        /* we reached top level */
+           FREE (old);
+       else                    /* retore exception, to go further up */
+           exception = old;
+
+       dispose_word (catch->word);
+       FREE (catch);
+    }
+
+    return (out);
+}
+/******************************************************************************/
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/execute_cmd.c ../bash/execute_cmd.c
--- ../bash-3.0/execute_cmd.c   2004-07-04 14:12:58.000000000 -0400
+++ ../bash/execute_cmd.c       2005-06-09 23:32:55.000000000 -0400
@@ -95,6 +95,14 @@
 #  include "bashhist.h"
 #endif
 
+
+/* --William */
+#include "builtins/subroutines.h"
+extern char *exception;
+extern int try_level;
+static int execute_try_command __P((TRY_COM *));
+
+
 extern int posixly_correct;
 extern int breaking, continuing, loop_level;
 extern int expand_aliases;
@@ -382,6 +390,7 @@
     case cm_case:
     case cm_while:
     case cm_until:
+    case cm_try:               /* --William */
     case cm_if:
     case cm_group:
       return (1);
@@ -494,7 +503,7 @@
   volatile pid_t last_pid;
   volatile int save_line_number;
 
-  if (command == 0 || breaking || continuing || read_but_dont_execute)
+  if (command == 0 || breaking || continuing || read_but_dont_execute || 
exception /* --William */)
     return (EXECUTION_SUCCESS);
 
   run_pending_traps ();
@@ -766,6 +775,12 @@
       exec_result = execute_until_command (command->value.While);
       break;
 
+    case cm_try:                       /* --William */
+      if (ignore_return)
+       command->value.Try->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_try_command (command->value.Try);
+      break;
+
     case cm_if:
       if (ignore_return)
        command->value.If->flags |= CMD_IGNORE_RETURN;
@@ -1578,9 +1593,38 @@
   SHELL_VAR *old_value = (SHELL_VAR *)NULL; /* Remember the old value of x. */
 #endif
 
+  WORD_LIST *multi_variables, *mv;             /* --William */
+
   save_line_number = line_number;
+
+  
/*****************************************************************************
+   * Enable multiple loop variables in for-loop, with syntax
+   *   for  a,b,c,...  in list; do
+   *       ...
+   *   done
+   * where no space is allowed around ',' (comma) because only one word is
+   * parsed.  List items are sequentially assigned to the loop variables 'a',
+   * 'b', 'c', etc.  If there is shortage of item, then the last iteration will
+   * run with '' (null) assigned to leftover variables.
+   *
+   * --William Park
+   */
+  multi_variables = (WORD_LIST *)NULL;
+
+  if (xstrchr (for_command->name->word, ',') != NULL) {                /* 
split 'a,b,c,...' */
+      /* word_split() breaks up word, only if it's not quoted.  It's front-end
+       * to list_string() for WORD_DESC variable. */
+      multi_variables = word_split (for_command->name, ",");
+
+      for (mv = multi_variables; mv; mv = mv->next)
+         if (check_identifier (mv->word, 1) == 0) {
+             dispose_words (multi_variables);
+             goto Exit_by_Original_Code;
+         }
+  } else {                     /* original code */
   if (check_identifier (for_command->name, 1) == 0)
     {
+    Exit_by_Original_Code:                     /* --William */
       if (posixly_correct && interactive_shell == 0)
        {
          last_command_exit_value = EX_USAGE;
@@ -1588,6 +1632,8 @@
        }
       return (EXECUTION_FAILURE);
     }
+  }
+  
/****************************************************************************/
 
   loop_level++;
   identifier = for_command->name->word;
@@ -1638,9 +1684,35 @@
 #endif
 
       this_command_name = (char *)NULL;
+     
+      
/*************************************************************************
+       * Assign list items into a, b, c, ...   --William
+       */
+      if (multi_variables) {
+         for (mv = multi_variables; mv; mv = mv->next) {
+             identifier = mv->word->word;
+             if (list) {
+                 /* Goto the next item in the list, only if there are more
+                  * variables to assign.  If finished assigning, then leave the
+                  * incrementing for the next iteration.
+                  */
+                 v = bind_variable (identifier, list->word->word);
+                 if (mv->next)
+                     list = list->next;
+             } else                    /* no more items */
+                 v = bind_variable (identifier, "");
+
+             if (readonly_p (v) || noassign_p (v)) {
+                 dispose_words (multi_variables);
+                 goto Exit_by_Original_Code_2;
+             }
+         }
+      } else {                 /* original code */
+      
/************************************************************************/
       v = bind_variable (identifier, list->word->word);
       if (readonly_p (v) || noassign_p (v))
        {
+Exit_by_Original_Code_2:                       /* --William */
          line_number = save_line_number;
          if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
            {
@@ -1655,6 +1727,8 @@
              return (EXECUTION_FAILURE);
            }
        }
+      }
+
       retval = execute_command (for_command->action);
       REAP ();
       QUIT;
@@ -1671,6 +1745,9 @@
          if (continuing)
            break;
        }
+
+      if (list == 0 || exception)      /* --William */
+         break;
     }
 
   loop_level--;
@@ -1692,6 +1769,26 @@
     }
 #endif
 
+  
/*****************************************************************************
+   * Run THEN commands if for-loop exits normally, and run ELSE commands if
+   * break is used.
+   * --William
+   */
+  if (list == 0 && for_command->then_action) {
+      if (for_command->flags & CMD_IGNORE_RETURN)
+         for_command->then_action->flags |= CMD_IGNORE_RETURN;
+      retval = execute_command (for_command->then_action);
+  }
+  if (list && for_command->else_action) {
+      if (for_command->flags & CMD_IGNORE_RETURN)
+         for_command->else_action->flags |= CMD_IGNORE_RETURN;
+      retval = execute_command (for_command->else_action);
+  }
+
+  if (multi_variables)
+      dispose_words (multi_variables);
+  
/****************************************************************************/
+
   dispose_words (releaser);
   discard_unwind_frame ("for");
   return (retval);
@@ -1826,6 +1923,9 @@
            break;
        }
 
+      if (exception)           /* --William */
+         break;
+
       /* Evaluate the step expression. */
       line_number = arith_lineno;
       expresult = eval_arith_for_expr (arith_for_command->step, &expok);
@@ -2128,6 +2228,9 @@
            break;
        }
 
+      if (exception)           /* --William */
+         break;
+
 #if defined (KSH_COMPATIBLE_SELECT)
       show_menu = 0;
       selection = get_string_value ("REPLY");
@@ -2159,6 +2262,8 @@
   char *word, *pattern;
   int retval, match, ignore_return, save_line_number;
 
+  case_command->total_match = 0;               /* --William */
+
   save_line_number = line_number;
   line_number = case_command->line;
 
@@ -2219,6 +2324,36 @@
              list->word->word = pattern;
            }
 
+#if defined (HAVE_POSIX_REGEXP)
+         /*********************************************************************
+          * If pattern list is terminated by '))', it is regex.  And, if it's
+          * terminated by the usual single ')', it's glob.
+          *    case ... in
+          *        glob ) action ;;
+          *        regex )) action ;;
+          *    esac
+          *
+          * For glob pattern, shell expansions (parameter expansion, command
+          * substitution, arithmetic expansion) are performed, and word
+          * splitting and quote removal are not done.  For regex pattern,
+          * quotes are removed and only word splitting is not done.  Otherwise,
+          * you can't use shell metacharacters.
+          *
+          * If there is a match, SUBMATCH will contain matching string and any
+          * parenthesized subgroup in regex pattern, just like 'match' builtin.
+          *
+          * --William
+          */
+         if (clauses->glob_or_regex == 1) {            /* 0=glob, 1=regex */
+             es = expand_word_unsplit (list->word, 0);
+             if (es && es->word && es->word->word && *(es->word->word))
+                 pattern = savestring (es->word->word);
+             else
+                 pattern = savestring ("");
+
+             match = regex_match (word, pattern, "SUBMATCH", 1) == 
EXECUTION_SUCCESS;
+         } else {              /* original code */
+#endif /* HAVE_POSIX_REGEXP*/
          es = expand_word_leave_quoted (list->word, 0);
 
          if (es && es->word && es->word->word && *(es->word->word))
@@ -2233,6 +2368,8 @@
             Posix.2, section 3.9.4.3), the strmatch () call must be able
             to recognize backslashes as escape characters. */
          match = strmatch (pattern, word, FNMATCH_EXTFLAG) != FNM_NOMATCH;
+         }
+         /********************************************************************/
          free (pattern);
 
          dispose_words (es);
@@ -2242,6 +2379,31 @@
              if (clauses->action && ignore_return)
                clauses->action->flags |= CMD_IGNORE_RETURN;
              retval = execute_command (clauses->action);
+
+             /*****************************************************************
+              * If ';;&', continue on with the next pattern test, instead of
+              * terminating.  --William
+              */
+             case_command->total_match++;
+
+             if (clauses->goto_next == 2)
+                 break;
+
+             /* If ';&', then execute the next commands.  --William
+              */
+             if (clauses->goto_next == 1) {
+                 PATTERN_LIST *c;
+
+                 for (c = clauses->next; c; c = c->next) {
+                     if (c->action && ignore_return)
+                         c->action->flags |= CMD_IGNORE_RETURN;
+                     retval = execute_command (c->action);
+                     if (c->goto_next != 1)
+                         break;
+                 }
+             }
+             /****************************************************************/
+
              EXIT_CASE ();
            }
 
@@ -2250,6 +2412,23 @@
     }
 
 exit_case_command:
+
+  
/*****************************************************************************
+   * Run THEN or ELSE commands if it's specified.
+   * --William
+   */
+  if (case_command->total_match && case_command->true_case) {
+      if (ignore_return)
+         case_command->true_case->flags |= CMD_IGNORE_RETURN;
+      retval = execute_command (case_command->true_case);
+  }
+  if (! case_command->total_match && case_command->false_case) {
+      if (ignore_return)
+         case_command->false_case->flags |= CMD_IGNORE_RETURN;
+      retval = execute_command (case_command->false_case);
+  }
+  
/****************************************************************************/
+
   free (word);
   discard_unwind_frame ("case");
   line_number = save_line_number;
@@ -2319,6 +2498,9 @@
          break;
        }
 
+      if (exception)           /* --William */
+         break;
+
       QUIT;
       body_status = execute_command (while_command->action);
       QUIT;
@@ -2335,12 +2517,38 @@
          if (continuing)
            break;
        }
+
+      if (exception)           /* --William */
+         break;
     }
   loop_level--;
 
+  
/*****************************************************************************
+   * Run THEN commands if while-loop or until-loop exits normally, and run ELSE
+   * commands if break is used.
+   * --William
+   */
+  if ((type == CMD_WHILE && return_value != EXECUTION_SUCCESS && 
while_command->then_action)
+    || (type == CMD_UNTIL && return_value == EXECUTION_SUCCESS && 
while_command->then_action)) {
+      if (while_command->flags & CMD_IGNORE_RETURN)
+         while_command->then_action->flags |= CMD_IGNORE_RETURN;
+      body_status = execute_command (while_command->then_action);
+  }
+  if ((type == CMD_WHILE && return_value == EXECUTION_SUCCESS && 
while_command->else_action)
+    || (type == CMD_UNTIL && return_value != EXECUTION_SUCCESS && 
while_command->else_action)) {
+      if (while_command->flags & CMD_IGNORE_RETURN)
+         while_command->else_action->flags |= CMD_IGNORE_RETURN;
+      body_status = execute_command (while_command->else_action);
+  }
+  
/****************************************************************************/
+
   return (body_status);
 }
 
+
+#include "execute_cmd.2.c"             /* try-block  --William */
+
+
 /* IF test THEN command [ELSE command].
    IF also allows ELIF in the place of ELSE IF, but
    the parser makes *that* stupidity transparent. */
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/make_cmd.c ../bash/make_cmd.c
--- ../bash-3.0/make_cmd.c      2003-12-19 15:46:56.000000000 -0500
+++ ../bash/make_cmd.c  2005-07-05 17:44:25.000000000 -0400
@@ -216,6 +216,10 @@
   temp->line = lineno;
   temp->map_list = map_list;
   temp->action = action;
+
+  temp->then_action = (COMMAND *)NULL;         /* --William */
+  temp->else_action = (COMMAND *)NULL;         /* --William */
+
   return (make_command (type, (SIMPLE_COM *)temp));
 }
 
@@ -355,6 +359,7 @@
 make_case_command (word, clauses, lineno)
      WORD_DESC *word;
      PATTERN_LIST *clauses;
+     int lineno;
 {
   CASE_COM *temp;
 
@@ -363,6 +368,11 @@
   temp->line = lineno;
   temp->word = word;
   temp->clauses = REVERSE_LIST (clauses, PATTERN_LIST *);
+
+  temp->true_case = (COMMAND *)NULL;           /* --William */
+  temp->false_case = (COMMAND *)NULL;          /* --William */
+  temp->total_match = 0;                       /* --William */
+
   return (make_command (cm_case, (SIMPLE_COM *)temp));
 }
 
@@ -376,7 +386,11 @@
   temp = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
   temp->patterns = REVERSE_LIST (patterns, WORD_LIST *);
   temp->action = action;
-  temp->next = NULL;
+
+  temp->next = (PATTERN_LIST *)NULL;   /* Fix: was just NULL  --William */
+  temp->glob_or_regex = 0;             /* --William */
+  temp->goto_next = 0;                 /* --William */
+
   return (temp);
 }
 
@@ -394,6 +408,26 @@
   return (make_command (cm_if, (SIMPLE_COM *)temp));
 }
 
+
+/*******************************************************************************
+ * --William
+ */
+COMMAND *
+make_try_command (action, clauses)
+    COMMAND *action;
+    PATTERN_LIST *clauses;
+{
+    TRY_COM *temp;
+
+    temp = (TRY_COM *)xmalloc (sizeof (TRY_COM));
+    temp->flags = 0;
+    temp->action = action;
+    temp->clauses = REVERSE_LIST (clauses, PATTERN_LIST *);
+    return (make_command (cm_try, (SIMPLE_COM *)temp));
+}
+/******************************************************************************/
+
+
 static COMMAND *
 make_until_or_while (which, test, action)
      enum command_type which;
@@ -405,6 +439,10 @@
   temp->flags = 0;
   temp->test = test;
   temp->action = action;
+
+  temp->then_action = (COMMAND *)NULL;         /* --William */
+  temp->else_action = (COMMAND *)NULL;         /* --William */
+
   return (make_command (which, (SIMPLE_COM *)temp));
 }
 
@@ -554,14 +592,18 @@
   char *redir_word, *document, *full_line;
   int document_index, document_size, delim_unquoted;
 
+  int i, j, first_indentation = -1;            /* <<+  --William */
+
   if (temp->instruction != r_deblank_reading_until &&
+      temp->instruction != r_deblank_reading_until_using_firstline &&  /* <<+  
--William */
       temp->instruction != r_reading_until)
     {
       internal_error (_("make_here_document: bad instruction type %d"), 
temp->instruction);
       return;
     }
 
-  kill_leading = temp->instruction == r_deblank_reading_until;
+  kill_leading = (temp->instruction == r_deblank_reading_until || 
+         temp->instruction == r_deblank_reading_until_using_firstline); /* <<+ 
 --William */
 
   document = (char *)NULL;
   document_index = document_size = 0;
@@ -612,8 +654,48 @@
          if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
            goto document_done;
 
+         /*********************************************************************
+          * Remove leading indentation (spaces and tabs) on the first line.
+          * And, remove the same indentation from the rest of lines.  Tab is
+          * taken as 8 spaces.  So, the last line with delimiter must not have
+          * more indentation than the first line.  --William
+          */
+         if (temp->instruction == r_deblank_reading_until_using_firstline) {
+             if (first_indentation == -1) {
+                 first_indentation = 0;
+                 for ( ; whitespace (*line); line++) {
+                     if (*line == ' ')
+                         first_indentation += 1;
+                     else      /* <Tab> */
+                         first_indentation += (8 - first_indentation % 8);
+                 }
+             } else {
+                 /* strip the leading whitespaces first, then put back
+                  * tabs/spaces. */
+                 i = 0;
+                 for ( ; whitespace (*line); line++) {
+                     if (*line == ' ')
+                         i += 1;
+                     else      /* <Tab> */
+                         i += (8 - i % 8);
+                 }
+                 if (i > first_indentation) {
+                     j = i - first_indentation;
+                     document_size = (document_size)? 2 * (document_size + j) 
: j + 2;
+                     document = (char *)xrealloc (document, document_size);
+                     j = (i - first_indentation) / 8;
+                     while (j-- > 0)
+                         document[document_index++] = '\t';
+                     j = (i - first_indentation) % 8;
+                     while (j-- > 0)
+                         document[document_index++] = ' ';
+                     document[document_index] = '\0';
+                 }
+             }
+         } else                        /* original code */
          while (*line == '\t')
            line++;
+         /********************************************************************/
        }
 
       if (*line == 0)
@@ -692,8 +774,10 @@
       break;
 
     case r_deblank_reading_until:      /* <<-foo */
+    case r_deblank_reading_until_using_firstline:      /* <<+ file  --William 
*/
     case r_reading_until:              /* << foo */
     case r_reading_string:             /* <<< foo */
+    case r_reading_herefile:                           /* <<<< file  --William 
*/
     case r_close_this:                 /* <&- */
     case r_duplicating_input:          /* 1<&2 */
     case r_duplicating_output:         /* 1>&2 */
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/make_cmd.h ../bash/make_cmd.h
--- ../bash-3.0/make_cmd.h      2003-02-15 16:36:06.000000000 -0500
+++ ../bash/make_cmd.h  2004-07-31 06:33:50.000000000 -0400
@@ -37,6 +37,7 @@
 extern COMMAND *make_command __P((enum command_type, SIMPLE_COM *));
 extern COMMAND *command_connect __P((COMMAND *, COMMAND *, int));
 extern COMMAND *make_for_command __P((WORD_DESC *, WORD_LIST *, COMMAND *, 
int));
+extern COMMAND *make_try_command __P((COMMAND *, PATTERN_LIST *));     /* 
--William */
 extern COMMAND *make_group_command __P((COMMAND *));
 extern COMMAND *make_case_command __P((WORD_DESC *, PATTERN_LIST *, int));
 extern PATTERN_LIST *make_pattern_list __P((WORD_LIST *, COMMAND *));
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/parse.y ../bash/parse.y
--- ../bash-3.0/parse.y 2004-05-04 15:09:06.000000000 -0400
+++ ../bash/parse.y     2005-07-03 16:51:53.000000000 -0400
@@ -310,7 +310,9 @@
    in the case that they are preceded by a list_terminator.  Members
    of the second group are for [[...]] commands.  Members of the
    third group are recognized only under special circumstances. */
-%token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION
+
+/* add TRY  --William */
+%token IF THEN ELSE ELIF FI CASE ESAC FOR TRY SELECT WHILE UNTIL DO DONE 
FUNCTION
 %token COND_START COND_END COND_ERROR
 %token IN BANG TIME TIMEOPT
 
@@ -322,6 +324,8 @@
 %token AND_AND OR_OR GREATER_GREATER LESS_LESS LESS_AND LESS_LESS_LESS
 %token GREATER_AND SEMI_SEMI LESS_LESS_MINUS AND_GREATER LESS_GREATER
 %token GREATER_BAR
+%token PAREN_CLOSE_CLOSE SEMI_AND SEMI_SEMI_AND                /* --William */
+%token LESS_LESS_PLUS LESS_LESS_LESS_LESS      /* <<+  <<<<  --William */
 
 /* The types that the various syntactical units return. */
 
@@ -329,6 +333,9 @@
 %type <command> list list0 list1 compound_list simple_list simple_list1
 %type <command> simple_command shell_command
 %type <command> for_command select_command case_command group_command
+
+%type <command> while_command until_command try_command                /* 
--William */
+
 %type <command> arith_command
 %type <command> cond_command
 %type <command> arith_for_command
@@ -446,6 +453,22 @@
                          redir.filename = $3;
                          $$ = make_redirection ($1, r_reading_string, redir);
                        }
+
+       /***********************************************************************
+        * here-file:  [n]<<<<file  --William
+        */
+       |       LESS_LESS_LESS_LESS WORD
+                       {
+                         redir.filename = $2;
+                         $$ = make_redirection (0, r_reading_herefile, redir);
+                       }
+       |       NUMBER LESS_LESS_LESS_LESS WORD
+                       {
+                         redir.filename = $3;
+                         $$ = make_redirection ($1, r_reading_herefile, redir);
+                       }
+       /**********************************************************************/
+
        |       LESS_AND NUMBER
                        {
                          redir.dest = $2;
@@ -500,6 +523,27 @@
                            ($1, r_deblank_reading_until, redir);
                          redir_stack[need_here_doc++] = $$;
                        }
+
+       /***********************************************************************
+        * First input line will determine how many tabs to delete from the rest
+        * of lines.  For <<+ redirection.  --William
+        */
+       |       LESS_LESS_PLUS WORD
+                       {
+                         redir.filename = $2;
+                         $$ = make_redirection
+                           (0, r_deblank_reading_until_using_firstline, redir);
+                         redir_stack[need_here_doc++] = $$;
+                       }
+       |       NUMBER LESS_LESS_PLUS WORD
+                       {
+                         redir.filename = $3;
+                         $$ = make_redirection
+                           ($1, r_deblank_reading_until_using_firstline, 
redir);
+                         redir_stack[need_here_doc++] = $$;
+                       }
+       /**********************************************************************/
+
        |       GREATER_AND '-'
                        {
                          redir.dest = 0;
@@ -604,10 +648,14 @@
                        { $$ = $1; }
        |       case_command
                        { $$ = $1; }
-       |       WHILE compound_list DO compound_list DONE
-                       { $$ = make_while_command ($2, $4); }
-       |       UNTIL compound_list DO compound_list DONE
-                       { $$ = make_until_command ($2, $4); }
+
+       |       while_command
+                       { $$ = $1; }            /* Moved to below  --William */
+       |       until_command
+                       { $$ = $1; }            /* Moved to below  --William */
+       |       try_command
+                       { $$ = $1; }            /* Added  --William */
+
        |       select_command
                        { $$ = $1; }
        |       if_command
@@ -664,6 +712,21 @@
                          $$ = make_for_command ($2, (WORD_LIST *)NULL, $8, 
word_lineno[word_top]);
                          if (word_top > 0) word_top--;
                        }
+
+       /***********************************************************************
+        * Added then-else-fi construct.  --William
+        */
+       |       for_command THEN compound_list FI
+                       { $$ = $1;
+                         $$->value.For->then_action = $3; }
+       |       for_command ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.For->else_action = $3; }
+       |       for_command THEN compound_list ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.For->then_action = $3;
+                         $$->value.For->else_action = $5; }
+       /**********************************************************************/
        ;
 
 arith_for_command:     FOR ARITH_FOR_EXPRS list_terminator newline_list DO 
compound_list DONE
@@ -735,7 +798,65 @@
                          $$ = make_case_command ($2, $5, 
word_lineno[word_top]);
                          if (word_top > 0) word_top--;
                        }
+
+       /***********************************************************************
+        * Added then-else-fi construct.  --William
+        */
+       |       case_command THEN compound_list FI
+                       { $$ = $1;
+                         $$->value.Case->true_case = $3; }
+       |       case_command ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.Case->false_case = $3; }
+       |       case_command THEN compound_list ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.Case->true_case = $3;
+                         $$->value.Case->false_case = $5; }
+       /**********************************************************************/
+       ;
+
+
+/*******************************************************************************
+ * Moved 'while' and 'until' from above, and added then-else-fi contruct.  
Also,
+ * new try-block for raising/catching exception.  --William
+ */
+while_command: WHILE compound_list DO compound_list DONE
+                       { $$ = make_while_command ($2, $4); }
+       |       while_command THEN compound_list FI
+                       { $$ = $1; 
+                         $$->value.While->then_action = $3; }
+       |       while_command ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.While->else_action = $3; }
+       |       while_command THEN compound_list ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.While->then_action = $3;
+                         $$->value.While->else_action = $5; }
+       ;
+
+until_command: UNTIL compound_list DO compound_list DONE
+                       { $$ = make_until_command ($2, $4); }
+       |       until_command THEN compound_list FI
+                       { $$ = $1;
+                         $$->value.While->then_action = $3; }
+       |       until_command ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.While->else_action = $3; }
+       |       until_command THEN compound_list ELSE compound_list FI
+                       { $$ = $1;
+                         $$->value.While->then_action = $3;
+                         $$->value.While->else_action = $5; }
+       ;
+
+try_command:   TRY compound_list DONE
+                       { $$ = make_try_command ($2, (PATTERN_LIST *)NULL); }
+       |       TRY compound_list DONE IN case_clause_sequence newline_list ESAC
+                       { $$ = make_try_command ($2, $5); }
+       |       TRY compound_list DONE IN case_clause ESAC
+                       { $$ = make_try_command ($2, $5); }
        ;
+/******************************************************************************/
+
 
 function_def:  WORD '(' ')' newline_list function_body
                        { $$ = make_function_def ($1, $5, function_dstart, 
function_bstart); }
@@ -830,11 +951,52 @@
                        { $$ = make_pattern_list ($3, $5); }
        |       newline_list '(' pattern ')' newline_list
                        { $$ = make_pattern_list ($3, (COMMAND *)NULL); }
+
+       /***********************************************************************
+        * Use '))' to denote regex pattern in 'case' statement:
+        *      case ... in
+        *          glob ) ... ;;
+        *          regex )) ... ;;
+        *      esac
+        * For the sake of simplicity, there is no optional '((' like glob
+        * counterpart.  --William
+        */
+       |       newline_list pattern PAREN_CLOSE_CLOSE compound_list
+                       { $$ = make_pattern_list ($2, $4);
+                         $$->glob_or_regex = 1; }
+       |       newline_list pattern PAREN_CLOSE_CLOSE newline_list
+                       { $$ = make_pattern_list ($2, (COMMAND *)NULL);
+                         $$->glob_or_regex = 1; }
+       /**********************************************************************/
        ;
 
 case_clause_sequence:  pattern_list SEMI_SEMI
        |       case_clause_sequence pattern_list SEMI_SEMI
                        { $2->next = $1; $$ = $2; }
+
+       /***********************************************************************
+        * Add ';&' (from Ksh/Zsh) and ';;&' for falling through to the next
+        * action:
+        *      case ... in
+        *          pattern1) action1 ;&
+        *          pattern2) action2 ;;
+        *      esac
+        * If 'pattern1' matches, then 'action1' will be run, and then followed
+        * by 'action2'.  This resembles C 'switch' when 'break' is missing.
+        * For ';;&', test next pattern case, instead of terminating the case
+        * statement.  --William
+        */
+       |       pattern_list SEMI_AND
+                       { $$->goto_next = 1; }
+       |       case_clause_sequence pattern_list SEMI_AND
+                       { $2->next = $1; $$ = $2;
+                         $$->goto_next = 1; }
+       |       pattern_list SEMI_SEMI_AND
+                       { $$->goto_next = 2; }
+       |       case_clause_sequence pattern_list SEMI_SEMI_AND
+                       { $2->next = $1; $$ = $2;
+                         $$->goto_next = 2; }
+       /**********************************************************************/
        ;
 
 pattern:       WORD
@@ -1753,6 +1915,7 @@
   { "select", SELECT },
 #endif
   { "while", WHILE },
+  { "try", TRY },              /* --William */
   { "until", UNTIL },
   { "do", DO },
   { "done", DONE },
@@ -1782,8 +1945,13 @@
   { "<&", LESS_AND },
   { ">&", GREATER_AND },
   { ";;", SEMI_SEMI },
+  { ";&", SEMI_AND },                  /* --William */
+  { ";;&", SEMI_SEMI_AND },            /* --William */
+  { "))", PAREN_CLOSE_CLOSE },         /* --William */
+  { "<<+", LESS_LESS_PLUS },           /* <<+  --William */
   { "<<-", LESS_LESS_MINUS },
   { "<<<", LESS_LESS_LESS },
+  { "<<<<", LESS_LESS_LESS_LESS },     /* <<<<  --William */
   { "&>", AND_GREATER },
   { "<>", LESS_GREATER },
   { ">|", GREATER_BAR },
@@ -2210,7 +2378,8 @@
 
 #define command_token_position(token) \
   (((token) == ASSIGNMENT_WORD) || \
-   ((token) != SEMI_SEMI && reserved_word_acceptable(token)))
+   ((token) != SEMI_SEMI && reserved_word_acceptable(token) && \
+    (token) != SEMI_AND && (token) != SEMI_SEMI_AND))          /* --William */
 
 #define assignment_acceptable(token) \
   (command_token_position(token) && ((parser_state & PST_CASEPAT) == 0))
@@ -2591,8 +2760,19 @@
              peek_char = shell_getc (1);
              if (peek_char == '-')
                return (LESS_LESS_MINUS);
-             else if (peek_char == '<')
-               return (LESS_LESS_LESS);
+
+             else if (peek_char == '+')                /* <<+  --William */
+               return (LESS_LESS_PLUS);
+
+             else if (peek_char == '<') {              /* <<<<  --William */
+                 peek_char = shell_getc (1);
+                 if (peek_char == '<') 
+                     return (LESS_LESS_LESS_LESS);
+                 else {
+                     shell_ungetc (peek_char);
+                     return (LESS_LESS_LESS);          /* original code */
+                 }
+             }
              else
                {
                  shell_ungetc (peek_char);
@@ -2607,7 +2787,22 @@
 #if defined (ALIAS)
              parser_state &= ~PST_ALEXPNEXT;
 #endif /* ALIAS */
-             return (SEMI_SEMI);
+
+             /*****************************************************************
+              * It could be either ';;' or ';;&'.  --William
+              */
+             peek_char = shell_getc (1);
+             if (peek_char == '&')
+                 return (SEMI_SEMI_AND);       
+             else {
+                 shell_ungetc (peek_char);
+                 return (SEMI_SEMI);           /* original code */
+             }
+             /****************************************************************/
+
+           case ')':           /* '))' in case statement  --William */
+               parser_state &= ~PST_CASEPAT;   /* end of pattern list */
+               return (PAREN_CLOSE_CLOSE);
 
            case '&':
              return (AND_AND);
@@ -2636,6 +2831,14 @@
       else if MBTEST(peek_char == '>' && character == '&')
        return (AND_GREATER);
 
+      else if MBTEST(character == ';' && peek_char == '&') {   /* --William */
+         parser_state |= PST_CASEPAT;
+#if defined (ALIAS)
+         parser_state &= ~PST_ALEXPNEXT;
+#endif /* ALIAS */
+         return (SEMI_AND);
+      }
+
       shell_ungetc (peek_char);
 
       /* If we look like we are reading the start of a function
@@ -3692,6 +3895,10 @@
     case IF:
     case OR_OR:
     case SEMI_SEMI:
+    case SEMI_AND:             /* --William */
+    case SEMI_SEMI_AND:                /* --William */
+    case PAREN_CLOSE_CLOSE:    /* --William */
+    case TRY:                  /* --William */
     case THEN:
     case TIME:
     case TIMEOPT:
@@ -3753,6 +3960,7 @@
 static int no_semi_successors[] = {
   '\n', '{', '(', ')', ';', '&', '|',
   CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN,
+  PAREN_CLOSE_CLOSE, SEMI_AND, SEMI_SEMI_AND, TRY,     /* --William */
   0
 };
 
Only in ../bash-3.0: parser-built
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/print_cmd.c ../bash/print_cmd.c
--- ../bash-3.0/print_cmd.c     2004-04-13 20:02:38.000000000 -0400
+++ ../bash/print_cmd.c 2005-07-03 16:01:19.000000000 -0400
@@ -88,6 +88,7 @@
 static void print_while_command __P((WHILE_COM *));
 static void print_until_command __P((WHILE_COM *));
 static void print_until_or_while __P((WHILE_COM *, char *));
+static void print_try_command __P((TRY_COM *));                /* --William */
 static void print_if_command __P((IF_COM *));
 #if defined (COND_COMMAND)
 static void print_cond_node __P((COND_COM *));
@@ -189,6 +190,10 @@
          print_until_command (command->value.While);
          break;
 
+       case cm_try:                    /* --William */
+         print_try_command (command->value.Try);
+         break;
+
        case cm_if:
          print_if_command (command->value.If);
          break;
@@ -315,6 +320,40 @@
   _print_word_list (list, separator, xprintf);
 }
 
+
+/*******************************************************************************
+ * Print THEN and ELSE sections, if present, in FOR, WHILE, UNTIL, and CASE
+ * statements.  Copied from print_if_command() below.
+ * --William
+ */
+static void
+print_then_else (then_action, else_action)
+    COMMAND *then_action, *else_action;
+{
+    if (then_action || else_action) {
+       if (then_action) {
+           cprintf (" then\n");
+           indentation += indentation_amount;
+           make_command_string_internal (then_action);
+           indentation -= indentation_amount;
+           semicolon ();
+       }
+       if (else_action) {
+           if (then_action)
+               newline ("else\n");
+           else
+               cprintf (" else\n");
+           indentation += indentation_amount;
+           make_command_string_internal (else_action);
+           indentation -= indentation_amount;
+           semicolon ();
+       }
+       newline ("fi");
+    }
+}
+/******************************************************************************/
+
+
 /* Return a string denoting what our indirection level is. */
 
 char *
@@ -451,6 +490,9 @@
   semicolon ();
   indentation -= indentation_amount;
   newline ("done");
+
+  /* then-else-fi  --William */
+  print_then_else (for_command->then_action, for_command->else_action);
 }
 
 #if defined (ARITH_FOR_COMMAND)
@@ -569,6 +611,9 @@
   if (case_command->clauses)
     print_case_clauses (case_command->clauses);
   newline ("esac");
+
+  /* then-else-fi  --William */
+  print_then_else (case_command->true_case, case_command->false_case);
 }
 
 static void
@@ -580,11 +625,23 @@
     {
       newline ("");
       command_print_word_list (clauses->patterns, " | ");
-      cprintf (")\n");
+
+      if (clauses->glob_or_regex == 0)
+         cprintf (")\n");              /* original code */
+      else
+         cprintf ("))\n");             /* --William */
+
       indentation += indentation_amount;
       make_command_string_internal (clauses->action);
       indentation -= indentation_amount;
-      newline (";;");
+
+      if (clauses->goto_next == 0)
+         newline (";;");                       /* original code */
+      else if (clauses->goto_next == 1)
+         newline (";&");                       /* --William */
+      else if (clauses->goto_next == 2)
+         newline (";&&");                      /* --William */
+
       clauses = clauses->next;
     }
   indentation -= indentation_amount;
@@ -619,8 +676,33 @@
   indentation -= indentation_amount;
   semicolon ();
   newline ("done");
+
+  /* then-else-fi  --William */
+  print_then_else (while_command->then_action, while_command->else_action);
 }
 
+
+/*******************************************************************************
+ * try-block  --William
+ */
+static void
+print_try_command (try_command)
+    TRY_COM *try_command;
+{
+    cprintf ("try ");
+    indentation += indentation_amount;
+    make_command_string_internal (try_command->action);
+    indentation -= indentation_amount;
+    semicolon ();
+    newline ("done");
+
+    if (try_command->clauses)
+       print_case_clauses (try_command->clauses);
+    newline ("esac");
+}
+/******************************************************************************/
+
+
 static void
 print_if_command (if_command)
      IF_COM *if_command;
@@ -795,7 +877,9 @@
     {
       /* Defer printing the here documents until we've printed the
         rest of the redirections. */
-      if (redirects->instruction == r_reading_until || redirects->instruction 
== r_deblank_reading_until)
+      if (redirects->instruction == r_reading_until || 
+         redirects->instruction == r_deblank_reading_until ||
+         redirects->instruction == r_deblank_reading_until_using_firstline)    
/* <<+  --William */
        {
          newredir = copy_redirect (redirects);
          newredir->next = (REDIRECT *)NULL;
@@ -874,6 +958,7 @@
       break;
 
     case r_deblank_reading_until:
+    case r_deblank_reading_until_using_firstline:      /* <<+  --William */
       kill_leading++;
       /* ... */
     case r_reading_until:
@@ -884,11 +969,19 @@
        {
          char *x;
          x = sh_single_quote (redirect->here_doc_eof);
+
+         if (redirect->instruction == r_deblank_reading_until_using_firstline) 
/* --William */
+             cprintf ("<<%s%s\n", kill_leading? "+" : "", x);
+         else                  /* original code */
          cprintf ("<<%s%s\n", kill_leading? "-" : "", x);
          free (x);
        }
       else
+         if (redirect->instruction == r_deblank_reading_until_using_firstline) 
/* --William */
+             cprintf ("<<%s%s\n", kill_leading? "+" : "", 
redirect->here_doc_eof);
+         else                  /* original code */
        cprintf ("<<%s%s\n", kill_leading? "-" : "", redirect->here_doc_eof);
+
       cprintf ("%s%s",
               redirect->redirectee.filename->word, redirect->here_doc_eof);
       break;
@@ -894,17 +987,26 @@
       break;
 
     case r_reading_string:
+    case r_reading_herefile:           /* <<<<  --William */
       if (redirector != 0)
        cprintf ("%d", redirector);
       if (ansic_shouldquote (redirect->redirectee.filename->word))
        {
          char *x;
          x = ansic_quote (redirect->redirectee.filename->word, 0, (int *)0);
+         if (redirect->instruction == r_reading_herefile)      /* --William */
+             cprintf ("<<<< %s", x);
+         else          /* original code */
          cprintf ("<<< %s", x);
          free (x);
        }
       else
+      {
+         if (redirect->instruction == r_reading_herefile)      /* --William */
+             cprintf ("<<<< %s", redirect->redirectee.filename->word);
+         else          /* original code */
        cprintf ("<<< %s", redirect->redirectee.filename->word);
+      }
       break;
 
     case r_duplicating_input:
diff -rubBP -x autom4te.cache -x '*--old' -x doc -x po -x configure -x parse.c 
../bash-3.0/redir.c ../bash/redir.c
--- ../bash-3.0/redir.c 2003-12-19 16:01:29.000000000 -0500
+++ ../bash/redir.c     2005-07-03 18:10:50.000000000 -0400
@@ -54,6 +54,8 @@
 #  include "input.h"
 #endif
 
+#include "builtins/subroutines.h"              /* for 
slurp_filename_into_string()  --William */
+
 int expanding_redir;
 
 extern int posixly_correct;
@@ -707,6 +709,32 @@
       dispose_redirects (new_redirect);
     }
 
+
+  
/*****************************************************************************
+   * Expand WORD just like <WORD, then masquerade as here-document.  As long as
+   * it's not here-string, it will default to here-document below.
+   * --William
+   */
+  if (ri == r_reading_herefile) {
+      if (posixly_correct && interactive_shell == 0) {
+         oflags = redirectee->flags;
+         redirectee->flags |= W_NOGLOB;
+         redirectee_word = redirection_expand (redirectee);
+         redirectee->flags = oflags;
+      } else
+         redirectee_word = redirection_expand (redirectee);
+
+      if (redirectee_word == 0)
+       return (AMBIGUOUS_REDIRECT);
+
+      FREE (redirectee->word);
+      redirectee->word = slurp_filename_into_string (redirectee_word);
+      redirectee->flags = 0;
+      FREE (redirectee_word);
+  }
+  
/****************************************************************************/
+
+
   switch (ri)
     {
     case r_output_direction:
@@ -811,7 +839,9 @@
 
     case r_reading_until:
     case r_deblank_reading_until:
+    case r_deblank_reading_until_using_firstline:      /* <<+  --William */
     case r_reading_string:
+    case r_reading_herefile:           /* <<<<  --William */
       /* REDIRECTEE is a pointer to a WORD_DESC containing the text of
         the new input.  Place it in a temporary file. */
       if (redirectee)
@@ -1020,7 +1050,9 @@
     case r_input_output:
     case r_reading_until:
     case r_deblank_reading_until:
+    case r_deblank_reading_until_using_firstline:      /* <<+  --William */
     case r_reading_string:
+    case r_reading_herefile:           /* <<<<  --William */
       return (1);
     case r_duplicating_input:
     case r_duplicating_input_word:


-- 
William Park <opengeometry@yahoo.ca>, Toronto, Canada
ThinFlash: Linux thin-client on USB key (flash) drive
           http://home.eol.ca/~parkw/thinflash.html
BashDiff: Super Bash shell
          http://freshmeat.net/projects/bashdiff/




reply via email to

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