(browse-url-browser-function)
[bpt/emacs.git] / lisp / progmodes / sh-script.el
index e26bd48..23d8374 100644 (file)
@@ -1,6 +1,7 @@
 ;;; sh-script.el --- shell-script editing commands for Emacs
 
-;; Copyright (C) 1993, 94, 95, 96, 97, 1999 by Free Software Foundation, Inc.
+;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 1999, 2001, 2003, 2004, 2005
+;;  Free Software Foundation, Inc.
 
 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Version: 2.0f
 ;;             ===========
 ;; Indentation for rc and es modes is very limited, but for Bourne shells
 ;; and its derivatives it is quite customizable.
-;; 
+;;
 ;; The following description applies to sh and derived shells (bash,
 ;; zsh, ...).
-;; 
+;;
 ;; There are various customization variables which allow tailoring to
 ;; a wide variety of styles.  Most of these variables are named
 ;; sh-indent-for-XXX and sh-indent-after-XXX.  For example.
 ;; sh-indent-after-if controls the indenting of a line following
-;; an if statement,  and sh-indent-for-fi controls the indentation
+;; an if statement, and sh-indent-for-fi controls the indentation
 ;; of the line containing the fi.
-;; 
+;;
 ;; You can set each to a numeric value, but it is often more convenient
 ;; to a symbol such as `+' which uses the value of variable `sh-basic-offset'.
 ;; By changing this one variable you can increase or decrease how much
 ;; indentation there is.  Valid symbols:
-;; 
+;;
 ;;     +   Indent right by sh-basic-offset
 ;;     -   Indent left  by sh-basic-offset
 ;;     ++  Indent right twice sh-basic-offset
 ;;     --  Indent left  twice sh-basic-offset
 ;;     *   Indent right half sh-basic-offset
 ;;     /   Indent left  half sh-basic-offset.
-;; 
+;;
 ;; There are 4 commands to help set the indentation variables:
-;; 
+;;
 ;; `sh-show-indent'
 ;;    This shows what variable controls the indentation of the current
 ;;    line and its value.
-;; 
+;;
 ;; `sh-set-indent'
 ;;    This allows you to set the value of the variable controlling the
 ;;    current line's indentation.  You can enter a number or one of a
 ;;    or its negative, or half it, or twice it, etc.  If you've used
 ;;    cc-mode this should be familiar.  If you forget which symbols are
 ;;    valid simply press C-h at the prompt.
-;; 
+;;
 ;; `sh-learn-line-indent'
 ;;    Simply make the line look the way you want it, then invoke this
 ;;    command.  It will set the variable to the value that makes the line
 ;;    indent like that.  If called with a prefix argument then it will set
 ;;    the value to one of the symbols if applicable.
-;;    
+;;
 ;; `sh-learn-buffer-indent'
 ;;    This is the deluxe function!  It "learns" the whole buffer (use
 ;;    narrowing if you want it to process only part).  It outputs to a
@@ -97,7 +98,7 @@
 ;;    pattern;  if they don't it will be set to nil.
 ;;    Whether `sh-basic-offset' is set is determined by variable
 ;;    `sh-learn-basic-offset'.
-;; 
+;;
 ;;    Unfortunately, `sh-learn-buffer-indent' can take a long time to run
 ;;    (e.g. if there are large case statements).  Perhaps it does not make
 ;;    sense to run it on large buffers: if lots of lines have different
 ;;    *indent* buffer; if there is a consistent style then running
 ;;    `sh-learn-buffer-indent' on a small region of the buffer should
 ;;    suffice.
-;;   
+;;
 ;;     Saving indentation values
 ;;     -------------------------
 ;; After you've learned the values in a buffer, how to you remember
 ;; would make this unnecessary;  simply learn the values when you visit
 ;; the buffer.
 ;; You can do this automatically like this:
-;   (add-hook 'sh-set-shell-hook 'sh-learn-buffer-indent)
-;; 
-;; However...   `sh-learn-buffer-indent' is extremely slow,
-;; especially on large-ish buffer.  Also,  if there are conflicts the
+;;   (add-hook 'sh-set-shell-hook 'sh-learn-buffer-indent)
+;;
+;; However...  `sh-learn-buffer-indent' is extremely slow,
+;; especially on large-ish buffer.  Also, if there are conflicts the
 ;; "last one wins" which may not produce the desired setting.
-;; 
+;;
 ;; So...There is a minimal way of being able to save indentation values and
 ;; to reload them in another buffer or at another point in time.
-;; 
+;;
 ;; Use `sh-name-style' to give a name to the indentation settings of
 ;;     the current buffer.
 ;; Use `sh-load-style' to load indentation settings for the current
 ;; Use `sh-save-styles-to-buffer' to write all the styles to a buffer
 ;;     in lisp code.  You can then store it in a file and later use
 ;;     `load-file' to load it.
-;; 
+;;
 ;;     Indentation variables - buffer local or global?
 ;;     ----------------------------------------------
 ;; I think that often having them buffer-local makes sense,
 ;; especially if one is using `sh-learn-buffer-indent'.  However, if
-;; a user sets values using customization,  these changes won't appear
+;; a user sets values using customization, these changes won't appear
 ;; to work if the variables are already local!
-;; 
-;; To get round this,  there is a variable `sh-make-vars-local' and 2
+;;
+;; To get round this, there is a variable `sh-make-vars-local' and 2
 ;; functions: `sh-make-vars-local' and `sh-reset-indent-vars-to-global-values'.
-;; 
-;; If `sh-make-vars-local' is non-nil,  then these variables become
+;;
+;; If `sh-make-vars-local' is non-nil, then these variables become
 ;; buffer local when the mode is established.
-;; If this is nil,  then the variables are global.  At any time you
+;; If this is nil, then the variables are global.  At any time you
 ;; can make them local with the command `sh-make-vars-local'.
-;; Conversely,  to update with the global values you can use the
+;; Conversely, to update with the global values you can use the
 ;; command `sh-reset-indent-vars-to-global-values'.
-;; 
-;; This may be awkward,  but the intent is to cover all cases.
-;; 
+;;
+;; This may be awkward, but the intent is to cover all cases.
+;;
 ;;     Awkward things, pitfalls
 ;;     ------------------------
 ;; Indentation for a sh script is complicated for a number of reasons:
-;; 
-;; 1. You can't format by simply looking at symbols,  you need to look
+;;
+;; 1. You can't format by simply looking at symbols, you need to look
 ;;    at keywords.  [This is not the case for rc and es shells.]
 ;; 2. The character ")" is used both as a matched pair "(" ... ")" and
 ;;    as a stand-alone symbol (in a case alternative).  This makes
 ;;    things quite tricky!
-;; 3. Here-documents in a script should be treated "as is",  and when
+;; 3. Here-documents in a script should be treated "as is", and when
 ;;    they terminate we want to revert to the indentation of the line
 ;;    containing the "<<" symbol.
 ;; 4. A line may be continued using the "\".
 ;; 5. The character "#" (outside a string) normally starts a comment,
 ;;    but it doesn't in the sequence "$#"!
-;; 
+;;
 ;; To try and address points 2 3 and 5 I used a feature that cperl mode
-;; uses,  that of a text's syntax property.  This, however, has 2
+;; uses, that of a text's syntax property.  This, however, has 2
 ;; disadvantages:
 ;; 1. We need to scan the buffer to find which ")" symbols belong to a
 ;;    case alternative, to find any here documents, and handle "$#".
 ;;    buffer is read-only buffer we have to cheat and bypass the read-only
 ;;    status.  This is for cases where the buffer started read-only buffer
 ;;    but the user issued `toggle-read-only'.
-;; 
+;;
 ;;     Bugs
 ;;     ----
-;; - Here-documents are marked with text properties face and syntax
-;;   table.  This serves 2 purposes: stopping indentation while inside
-;;   them, and moving over them when finding the previous line to
-;;   indent to.  However, if font-lock mode is active when there is
-;;   any change inside the here-document font-lock clears that
-;;   property.  This causes several problems: lines after the here-doc
-;;   will not be re-indentation properly,  words in the here-doc region
-;;   may be fontified,  and indentation may occur within the
-;;   here-document.
-;;   I'm not sure how to fix this, perhaps using the point-entered
-;;   property.  Anyway, if you use font lock and change a
-;;   here-document,  I recommend using M-x sh-rescan-buffer after the
-;;   changes are made.  Similarly, when using higlight-changes-mode,
-;;   changes inside a here-document may confuse shell indenting,  but again
-;;   using `sh-rescan-buffer' should fix them.
-;; 
 ;; - Indenting many lines is slow.  It currently does each line
 ;;   independently, rather than saving state information.
-;; 
+;;
 ;; - `sh-learn-buffer-indent' is extremely slow.
-;; 
+;;
 ;; Richard Sharman <rsharman@pobox.com>  June 1999.
 
 ;;; Code:
 ;; page 4:     statement syntax-commands for various shells
 ;; page 5:     various other commands
 
+(eval-when-compile
+  (require 'skeleton)
+  (require 'cl)
+  (require 'comint))
 (require 'executable)
 
 
 (defcustom sh-ancestor-alist
   '((ash . sh)
     (bash . jsh)
+    (bash2 . jsh)
     (dtksh . ksh)
     (es . rc)
     (itcsh . tcsh)
@@ -247,8 +237,8 @@ By default we have the following three hierarchies:
 
 csh            C Shell
   jcsh         C Shell with Job Control
-  tcsh         Toronto C Shell
-    itcsh      ? Toronto C Shell
+  tcsh         Turbo C Shell
+    itcsh      ? Turbo C Shell
 rc             Plan 9 Shell
   es           Extensible Shell
 sh             Bourne Shell
@@ -269,11 +259,12 @@ sh                Bourne Shell
 
 
 (defcustom sh-alias-alist
-  (nconc (if (eq system-type 'gnu/linux)
+  (append (if (eq system-type 'gnu/linux)
             '((csh . tcsh)
               (ksh . pdksh)))
         ;; for the time being
         '((ksh . ksh88)
+           (bash2 . bash)
           (sh5 . sh)))
   "*Alist for transforming shell names to what they really are.
 Use this where the name of the executable doesn't correspond to the type of
@@ -324,22 +315,20 @@ shell it really is."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (const :tag "No Arguments" nil)
                               (string :tag "Arguments")
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 (defcustom sh-imenu-generic-expression
-  (list
-   (cons 'sh
-        (concat
-         "\\(^\\s-*function\\s-+[A-Za-z_][A-Za-z_0-9]*\\)"
-         "\\|"
-         "\\(^\\s-*[A-Za-z_][A-Za-z_0-9]*\\s-*()\\)")))
-  "*Regular expression for recognizing shell function definitions.
-See `sh-feature'."
-  :type '(repeat (cons (symbol :tag "Shell")
-                      regexp))
+  `((sh
+     . ((nil "^\\s-*\\(function\\s-+\\)?\\([A-Za-z_][A-Za-z_0-9]+\\)\\s-*()" 2))))
+  "*Alist of regular expressions for recognizing shell function definitions.
+See `sh-feature' and `imenu-generic-expression'."
+  :type '(alist :key-type (symbol :tag "Shell")
+               :value-type (alist :key-type (choice :tag "Title"
+                                                    string
+                                                    (const :tag "None" nil))
+                                  :value-type
+                                  (repeat :tag "Regexp, index..." sexp)))
   :group 'sh-script
   :version "20.4")
 
@@ -364,64 +353,87 @@ the car and cdr are the same symbol.")
 (defvar sh-shell (sh-canonicalize-shell (file-name-nondirectory sh-shell-file))
   "The shell being programmed.  This is set by \\[sh-set-shell].")
 
-;;; I turned off this feature because it doesn't permit typing commands
-;;; in the usual way without help.
-;;;(defvar sh-abbrevs
-;;;  '((csh eval sh-abbrevs shell
-;;;     "switch" 'sh-case
-;;;     "getopts" 'sh-while-getopts)
-
-;;;    (es eval sh-abbrevs shell
-;;;    "function" 'sh-function)
-
-;;;    (ksh88 eval sh-abbrevs sh
-;;;       "select" 'sh-select)
-
-;;;    (rc eval sh-abbrevs shell
-;;;    "case" 'sh-case
-;;;    "function" 'sh-function)
-
-;;;    (sh eval sh-abbrevs shell
-;;;    "case" 'sh-case
-;;;    "function" 'sh-function
-;;;    "until" 'sh-until
-;;;    "getopts" 'sh-while-getopts)
-
-;;;    ;; The next entry is only used for defining the others
-;;;    (shell "for" sh-for
-;;;       "loop" sh-indexed-loop
-;;;       "if" sh-if
-;;;       "tmpfile" sh-tmp-file
-;;;       "while" sh-while)
-
-;;;    (zsh eval sh-abbrevs ksh88
-;;;     "repeat" 'sh-repeat))
-;;;  "Abbrev-table used in Shell-Script mode.  See `sh-feature'.
+(defvar sh-mode-abbrev-table nil)
+
+(define-abbrev-table 'sh-mode-abbrev-table ())
+
+
+;; I turned off this feature because it doesn't permit typing commands
+;; in the usual way without help.
+;;(defvar sh-abbrevs
+;;  '((csh sh-abbrevs shell
+;;      "switch" 'sh-case
+;;      "getopts" 'sh-while-getopts)
+
+;;    (es sh-abbrevs shell
+;;     "function" 'sh-function)
+
+;;    (ksh88 sh-abbrevs sh
+;;        "select" 'sh-select)
+
+;;    (rc sh-abbrevs shell
+;;     "case" 'sh-case
+;;     "function" 'sh-function)
+
+;;    (sh sh-abbrevs shell
+;;     "case" 'sh-case
+;;     "function" 'sh-function
+;;     "until" 'sh-until
+;;     "getopts" 'sh-while-getopts)
+
+;;    ;; The next entry is only used for defining the others
+;;    (shell "for" sh-for
+;;        "loop" sh-indexed-loop
+;;        "if" sh-if
+;;        "tmpfile" sh-tmp-file
+;;        "while" sh-while)
+
+;;    (zsh sh-abbrevs ksh88
+;;      "repeat" 'sh-repeat))
+;;  "Abbrev-table used in Shell-Script mode.  See `sh-feature'.
 ;;;Due to the internal workings of abbrev tables, the shell name symbol is
 ;;;actually defined as the table for the like of \\[edit-abbrevs].")
 
 
 
-(defvar sh-mode-syntax-table
-  '((sh eval sh-mode-syntax-table ()
+(defun sh-mode-syntax-table (table &rest list)
+  "Copy TABLE and set syntax for successive CHARs according to strings S."
+  (setq table (copy-syntax-table table))
+  (while list
+    (modify-syntax-entry (pop list) (pop list) table))
+  table)
+
+(defvar sh-mode-syntax-table nil
+  "The syntax table to use for Shell-Script mode.
+This is buffer-local in every such buffer.")
+
+(defvar sh-mode-default-syntax-table
+  (sh-mode-syntax-table ()
        ?\# "<"
-       ?\^l ">#"
        ?\n ">#"
        ?\" "\"\""
        ?\' "\"'"
        ?\` "\"`"
+       ;; ?$ might also have a ". p" syntax. Both "'" and ". p" seem
+       ;; to work fine. This is needed so that dabbrev-expand
+       ;; $VARNAME works.
+       ?$ "'"
        ?! "_"
        ?% "_"
        ?: "_"
        ?. "_"
        ?^ "_"
-       ?~ "_")
-    (csh eval identity sh)
-    (rc eval identity sh))
+       ?~ "_"
+       ?, "_"
+       ?= "."
+       ?< "."
+       ?> ".")
+  "Default syntax table for shell mode.")
+
+(defvar sh-mode-syntax-table-input
+  '((sh . nil))
   "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
 
-
-
 (defvar sh-mode-map
   (let ((map (make-sparse-keymap))
        (menu-map (make-sparse-keymap "Insert")))
@@ -440,6 +452,7 @@ the car and cdr are the same symbol.")
     (define-key map "\C-c=" 'sh-set-indent)
     (define-key map "\C-c<" 'sh-learn-line-indent)
     (define-key map "\C-c>" 'sh-learn-buffer-indent)
+    (define-key map "\C-c\C-\\" 'sh-backslash-region)
 
     (define-key map "=" 'sh-assignment)
     (define-key map "\C-c+" 'sh-add)
@@ -452,35 +465,22 @@ the car and cdr are the same symbol.")
     (define-key map "'" 'skeleton-pair-insert-maybe)
     (define-key map "`" 'skeleton-pair-insert-maybe)
     (define-key map "\"" 'skeleton-pair-insert-maybe)
-    (define-key map  ")" 'sh-electric-rparen)
-    (define-key map  "<" 'sh-electric-less)
-    (define-key map  "#" 'sh-electric-hash)
-
-    (substitute-key-definition 'complete-tag 'comint-dynamic-complete
-                              map (current-global-map))
-    (substitute-key-definition 'newline-and-indent 'sh-newline-and-indent
-                              map (current-global-map))
-    (substitute-key-definition 'delete-backward-char
-                              'backward-delete-char-untabify
-                              map (current-global-map))
+
+    (define-key map [remap complete-tag] 'comint-dynamic-complete)
+    (define-key map [remap newline-and-indent] 'sh-newline-and-indent)
+    (define-key map [remap delete-backward-char]
+      'backward-delete-char-untabify)
     (define-key map "\C-c:" 'sh-set-shell)
-    (substitute-key-definition 'beginning-of-defun
-                              'sh-beginning-of-compound-command
-                              map (current-global-map))
-    (substitute-key-definition 'backward-sentence 'sh-beginning-of-command
-                              map (current-global-map))
-    (substitute-key-definition 'forward-sentence 'sh-end-of-command
-                              map (current-global-map))
+    (define-key map [remap backward-sentence] 'sh-beginning-of-command)
+    (define-key map [remap forward-sentence] 'sh-end-of-command)
     (define-key map [menu-bar insert] (cons "Insert" menu-map))
     (define-key menu-map [sh-while]    '("While Loop" . sh-while))
     (define-key menu-map [sh-until]    '("Until Loop" . sh-until))
     (define-key menu-map [sh-tmp-file] '("Temporary File" . sh-tmp-file))
     (define-key menu-map [sh-select]   '("Select Statement" . sh-select))
     (define-key menu-map [sh-repeat]   '("Repeat Loop" . sh-repeat))
-    (define-key menu-map [sh-while-getopts]
-                                       '("Options Loop" . sh-while-getopts))
-    (define-key menu-map [sh-indexed-loop]
-                                       '("Indexed Loop" . sh-indexed-loop))
+    (define-key menu-map [sh-getopts]  '("Options Loop" . sh-while-getopts))
+    (define-key menu-map [sh-indexed-loop] '("Indexed Loop" . sh-indexed-loop))
     (define-key menu-map [sh-if]       '("If Statement" . sh-if))
     (define-key menu-map [sh-for]      '("For Loop" . sh-for))
     (define-key menu-map [sh-case]     '("Case Statement" . sh-case))
@@ -500,16 +500,13 @@ the car and cdr are the same symbol.")
 
 (defcustom sh-require-final-newline
   '((csh . t)
-    (pdksh . t)
-    (rc eval . require-final-newline)
-    (sh eval . require-final-newline))
+    (pdksh . t))
   "*Value of `require-final-newline' in Shell-Script mode buffers.
+\(SHELL . t) means use the value of `mode-require-final-newline' for SHELL.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (const :tag "require" t)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
@@ -524,9 +521,7 @@ First grouping matches the variable name.  This is upto and including the `='
 sign.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice regexp
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
@@ -564,8 +559,24 @@ The actual command ends at the end of the first \\(grouping\\)."
 
 
 
-(defvar sh-here-document-word "EOF"
-  "Word to delimit here documents.")
+(defcustom sh-here-document-word "EOF"
+  "Word to delimit here documents.
+If the first character of this string is \"-\", this is taken as
+part of the redirection operator, rather than part of the
+word (that is, \"<<-\" instead of \"<<\").  This is a feature
+used by some shells (for example Bash) to indicate that leading
+tabs inside the here document should be ignored.  In this case,
+Emacs indents the initial body and end of the here document with
+tabs, to the same level as the start (note that apart from this
+there is no support for indentation of here documents).  This
+will only work correctly if `sh-basic-offset' is a multiple of
+`tab-width'.
+
+Any quote characters or leading whitespace in the word are
+removed when closing the here document."
+  :type 'string
+  :group 'sh-script)
+
 
 (defvar sh-test
   '((sh "[  ]" . 3)
@@ -576,60 +587,61 @@ The actual command ends at the end of the first \\(grouping\\)."
 ;; customized this out of sheer bravado.  not for the faint of heart.
 ;; but it *did* have an asterisk in the docstring!
 (defcustom sh-builtins
-  '((bash eval sh-append posix
-         "alias" "bg" "bind" "builtin" "declare" "dirs" "enable" "fc" "fg"
-         "help" "history" "jobs" "kill" "let" "local" "popd" "pushd" "source"
-         "suspend" "typeset" "unalias")
+  '((bash sh-append posix
+         "." "alias" "bg" "bind" "builtin" "caller" "compgen" "complete"
+          "declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
+          "jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
+          "source" "suspend" "typeset" "unalias")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append shell
+    (bourne sh-append shell
            "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
            "times" "ulimit")
 
-    (csh eval sh-append shell
+    (csh sh-append shell
         "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
         "setenv" "source" "time" "unalias" "unhash")
 
-    (dtksh eval identity wksh)
+    (dtksh sh-append wksh)
 
     (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
        "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
 
-    (jsh eval sh-append sh
+    (jsh sh-append sh
         "bg" "fg" "jobs" "kill" "stop" "suspend")
 
-    (jcsh eval sh-append csh
-        "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
+    (jcsh sh-append csh
+         "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
 
-    (ksh88 eval sh-append bourne
+    (ksh88 sh-append bourne
           "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
           "typeset" "unalias" "whence")
 
-    (oash eval sh-append sh
+    (oash sh-append sh
          "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit"
          "oaed" "oahelp" "oainit" "pp" "ppfile" "scan" "scrollok" "wattr"
          "wclear" "werase" "win" "wmclose" "wmmessage" "wmopen" "wmove"
          "wmtitle" "wrefresh")
 
-    (pdksh eval sh-append ksh88
+    (pdksh sh-append ksh88
           "bind")
 
-    (posix eval sh-append sh
+    (posix sh-append sh
           "command")
 
     (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
        "whatis")
 
-    (sh eval sh-append bourne
+    (sh sh-append bourne
        "hash" "test" "type")
 
     ;; The next entry is only used for defining the others
     (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
 
-    (wksh eval sh-append ksh88
+    (wksh sh-append ksh88
          "Xt[A-Z][A-Za-z]*")
 
-    (zsh eval sh-append ksh88
+    (zsh sh-append ksh88
         "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs"
         "disable" "disown" "echotc" "enable" "functions" "getln" "hash"
         "history" "integer" "limit" "local" "log" "popd" "pushd" "r"
@@ -641,109 +653,112 @@ Note that on some systems not all builtins are available or some are
 implemented as aliases.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 
 (defcustom sh-leading-keywords
-  '((csh "else")
+  '((bash sh-append sh
+          "time")
+
+    (csh "else")
 
     (es "true" "unwind-protect" "whatis")
 
     (rc "else")
 
-    (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
+    (sh "!" "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
   "*List of keywords that may be immediately followed by a builtin or keyword.
 Given some confusion between keywords and builtins depending on shell and
 system, the distinction here has been based on whether they influence the
 flow of control or syntax.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 (defcustom sh-other-keywords
-  '((bash eval sh-append bourne
-         "bye" "logout")
+  '((bash sh-append bourne
+         "bye" "logout" "select")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append sh
+    (bourne sh-append sh
            "function")
 
-    (csh eval sh-append shell
+    (csh sh-append shell
         "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
         "if" "logout" "onintr" "repeat" "switch" "then" "while")
 
     (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
        "return" "throw" "while")
 
-    (ksh88 eval sh-append bourne
+    (ksh88 sh-append bourne
           "select")
 
     (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
        "while")
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        "done" "esac" "fi" "for" "in" "return")
 
     ;; The next entry is only used for defining the others
     (shell "break" "case" "continue" "exec" "exit")
 
-    (zsh eval sh-append bash
+    (zsh sh-append bash
         "select"))
   "*List of keywords not in `sh-leading-keywords'.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 
 (defvar sh-variables
-  '((bash eval sh-append sh
-         "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_VERSION"
-         "cdable_vars" "ENV" "EUID" "FCEDIT" "FIGNORE" "glob_dot_filenames"
-         "histchars" "HISTFILE" "HISTFILESIZE" "history_control" "HISTSIZE"
-         "hostname_completion_file" "HOSTTYPE" "IGNOREEOF" "ignoreeof"
-         "LINENO" "MAIL_WARNING" "noclobber" "nolinks" "notify"
-         "no_exit_on_failed_exec" "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "PPID"
-         "PROMPT_COMMAND" "PS4" "pushd_silent" "PWD" "RANDOM" "REPLY"
-         "SECONDS" "SHLVL" "TMOUT" "UID")
-
-    (csh eval sh-append shell
+  '((bash sh-append sh
+         "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_ENV"
+         "BASH_VERSINFO" "BASH_VERSION" "cdable_vars" "COMP_CWORD"
+         "COMP_LINE" "COMP_POINT" "COMP_WORDS" "COMPREPLY" "DIRSTACK"
+         "ENV" "EUID" "FCEDIT" "FIGNORE" "FUNCNAME"
+         "glob_dot_filenames" "GLOBIGNORE" "GROUPS" "histchars"
+         "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
+         "HISTIGNORE" "history_control" "HISTSIZE"
+         "hostname_completion_file" "HOSTFILE" "HOSTTYPE" "IGNOREEOF"
+         "ignoreeof" "INPUTRC" "LINENO" "MACHTYPE" "MAIL_WARNING"
+         "noclobber" "nolinks" "notify" "no_exit_on_failed_exec"
+         "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
+         "PPID" "POSIXLY_CORRECT" "PROMPT_COMMAND" "PS3" "PS4"
+         "pushd_silent" "PWD" "RANDOM" "REPLY" "SECONDS" "SHELLOPTS"
+         "SHLVL" "TIMEFORMAT" "TMOUT" "UID")
+
+    (csh sh-append shell
         "argv" "cdpath" "child" "echo" "histchars" "history" "home"
         "ignoreeof" "mail" "noclobber" "noglob" "nonomatch" "path" "prompt"
         "shell" "status" "time" "verbose")
 
-    (es eval sh-append shell
+    (es sh-append shell
        "apid" "cdpath" "CDPATH" "history" "home" "ifs" "noexport" "path"
        "pid" "prompt" "signals")
 
-    (jcsh eval sh-append csh
-        "notify")
+    (jcsh sh-append csh
+         "notify")
 
-    (ksh88 eval sh-append sh
+    (ksh88 sh-append sh
           "ENV" "ERRNO" "FCEDIT" "FPATH" "HISTFILE" "HISTSIZE" "LINENO"
           "OLDPWD" "PPID" "PS3" "PS4" "PWD" "RANDOM" "REPLY" "SECONDS"
           "TMOUT")
 
-    (oash eval sh-append sh
+    (oash sh-append sh
          "FIELD" "FIELD_MAX" "LAST_KEY" "OALIB" "PP_ITEM" "PP_NUM")
 
-    (rc eval sh-append shell
+    (rc sh-append shell
        "apid" "apids" "cdpath" "CDPATH" "history" "home" "ifs" "path" "pid"
        "prompt" "status")
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        "CDPATH" "IFS" "OPTARG" "OPTIND" "PS1" "PS2")
 
     ;; The next entry is only used for defining the others
@@ -752,7 +767,7 @@ See `sh-feature'."
           "LINES" "LOGNAME" "MAIL" "MAILCHECK" "MAILPATH" "PAGER" "PATH"
           "SHELL" "TERM" "TERMCAP" "TERMINFO" "VISUAL")
 
-    (tcsh eval sh-append csh
+    (tcsh sh-append csh
          "addsuffix" "ampm" "autocorrect" "autoexpand" "autolist"
          "autologout" "chase_symlinks" "correct" "dextract" "edit" "el"
          "fignore" "gid" "histlit" "HOST" "HOSTTYPE" "HPATH"
@@ -763,7 +778,7 @@ See `sh-feature'."
          "tperiod" "tty" "uid" "version" "visiblebell" "watch" "who"
          "wordchars")
 
-    (zsh eval sh-append ksh88
+    (zsh sh-append ksh88
         "BAUD" "bindcmds" "cdpath" "DIRSTACKSIZE" "fignore" "FIGNORE" "fpath"
         "HISTCHARS" "hostcmds" "hosts" "HOSTS" "LISTMAX" "LITHISTSIZE"
         "LOGCHECK" "mailpath" "manpath" "NULLCMD" "optcmds" "path" "POSTEDIT"
@@ -774,37 +789,65 @@ See `sh-feature'."
   "List of all shell variables available for completing read.
 See `sh-feature'.")
 
+\f
+;; Font-Lock support
 
+(defface sh-heredoc
+  '((((min-colors 88) (class color)
+      (background dark))
+     (:foreground "yellow1" :weight bold))
+    (((class color)
+      (background dark))
+     (:foreground "yellow" :weight bold))
+    (((class color)
+      (background light))
+     (:foreground "tan" ))
+    (t
+     (:weight bold)))
+  "Face to show a here-document"
+  :group 'sh-indentation)
+;; backward-compatibility alias
+(put 'sh-heredoc-face 'face-alias 'sh-heredoc)
+(defvar sh-heredoc-face 'sh-heredoc)
+
+(defface sh-escaped-newline '((t :inherit font-lock-string-face))
+  "Face used for (non-escaped) backslash at end of a line in Shell-script mode."
+  :group 'sh-script
+  :version "22.1")
 
 (defvar sh-font-lock-keywords
-  '((csh eval sh-append shell
-        '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
-          font-lock-variable-name-face))
+  '((csh sh-append shell
+        ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
+          font-lock-variable-name-face))
 
-    (es eval sh-append executable-font-lock-keywords
-       '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
-         font-lock-variable-name-face))
+    (es sh-append executable-font-lock-keywords
+       ("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
+         font-lock-variable-name-face))
 
-    (rc eval identity es)
+    (rc sh-append es)
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        ;; Variable names.
-       '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
+       ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
          font-lock-variable-name-face)
        ;; Function names.
-       '("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
-       '("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
-         (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))
+       ("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
+       ("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
+         (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t))
+       ("\\(?:^\\s *\\|[[();&|]\\s *\\|\\(?:\\s +-[ao]\\|if\\|else\\|then\\|while\\|do\\)\\s +\\)\\(!\\)"
+        1 font-lock-negation-char-face))
 
     ;; The next entry is only used for defining the others
-    (shell eval sh-append executable-font-lock-keywords
-          '("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
-          '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
+    (shell sh-append executable-font-lock-keywords
+           ;; Using font-lock-string-face here confuses sh-get-indent-info.
+           ("\\(^\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\)$" 3 'sh-escaped-newline)
+          ("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
+          ("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
             font-lock-variable-name-face))
-    (rpm eval sh-append rpm2
-        '("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
-    (rpm2 eval sh-append shell
-         '("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
+    (rpm sh-append rpm2
+        ("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
+    (rpm2 sh-append shell
+         ("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
   "Default expressions to highlight in Shell Script modes.  See `sh-feature'.")
 
 (defvar sh-font-lock-keywords-1
@@ -814,16 +857,132 @@ See `sh-feature'.")
 (defvar sh-font-lock-keywords-2 ()
   "Gaudy level highlighting for Shell Script modes.")
 
+;; These are used for the syntax table stuff (derived from cperl-mode).
+;; Note: parse-sexp-lookup-properties must be set to t for it to work.
+(defconst sh-st-punc (string-to-syntax "."))
+(defconst sh-st-symbol (string-to-syntax "_"))
+(defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
+
+(defconst sh-here-doc-open-re "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\)+\\).*\\(\n\\)")
+
+(defvar sh-here-doc-markers nil)
+(make-variable-buffer-local 'sh-here-doc-markers)
+(defvar sh-here-doc-re sh-here-doc-open-re)
+(make-variable-buffer-local 'sh-here-doc-re)
+
+(defun sh-font-lock-close-heredoc (bol eof indented)
+  "Determine the syntax of the \\n after an EOF.
+If non-nil INDENTED indicates that the EOF was indented."
+  (let* ((eof-re (if eof (regexp-quote eof) ""))
+         ;; A rough regexp that should find the opening <<EOF back.
+        (sre (concat "<<\\(-?\\)\\s-*['\"\\]?"
+                     ;; Use \s| to cheaply check it's an open-heredoc.
+                     eof-re "['\"]?\\([ \t|;&)<>].*\\)?\\s|"))
+        ;; A regexp that will find other EOFs.
+        (ere (concat "^" (if indented "[ \t]*") eof-re "\n"))
+        (start (save-excursion
+                 (goto-char bol)
+                 (re-search-backward (concat sre "\\|" ere) nil t))))
+    ;; If subgroup 1 matched, we found an open-heredoc, otherwise we first
+    ;; found a close-heredoc which makes the current close-heredoc inoperant.
+    (cond
+     ((when (and start (match-end 1)
+                (not (and indented (= (match-beginning 1) (match-end 1))))
+                (not (sh-in-comment-or-string (match-beginning 0))))
+       ;; Make sure our `<<' is not the EOF1 of a `cat <<EOF1 <<EOF2'.
+       (save-excursion
+         (goto-char start)
+         (setq start (line-beginning-position 2))
+         (while
+             (progn
+               (re-search-forward "<<") ; Skip ourselves.
+               (and (re-search-forward sh-here-doc-open-re start 'move)
+                    (goto-char (match-beginning 0))
+                    (sh-in-comment-or-string (point)))))
+         ;; No <<EOF2 found after our <<.
+         (= (point) start)))
+      sh-here-doc-syntax)
+     ((not (or start (save-excursion (re-search-forward sre nil t))))
+      ;; There's no <<EOF either before or after us,
+      ;; so we should remove ourselves from font-lock's keywords.
+      (setq sh-here-doc-markers (delete eof sh-here-doc-markers))
+      (setq sh-here-doc-re
+           (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
+                   (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))
+      nil))))
+
+(defun sh-font-lock-open-heredoc (start string)
+  "Determine the syntax of the \\n after a <<EOF.
+START is the position of <<.
+STRING is the actual word used as delimiter (f.ex. \"EOF\").
+INDENTED is non-nil if the here document's content (and the EOF mark) can
+be indented (i.e. a <<- was used rather than just <<)."
+  (unless (or (memq (char-before start) '(?< ?>))
+             (sh-in-comment-or-string start))
+    ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic
+    ;; font-lock keywords to detect the end of this here document.
+    (let ((str (replace-regexp-in-string "['\"]" "" string)))
+      (unless (member str sh-here-doc-markers)
+       (push str sh-here-doc-markers)
+       (setq sh-here-doc-re
+             (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
+                     (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))))
+    sh-here-doc-syntax))
+
+(defun sh-font-lock-here-doc (limit)
+  "Search for a heredoc marker."
+  ;; This looks silly, but it's because `sh-here-doc-re' keeps changing.
+  (re-search-forward sh-here-doc-re limit t))
+
+(defun sh-is-quoted-p (pos)
+  (and (eq (char-before pos) ?\\)
+       (not (sh-is-quoted-p (1- pos)))))
+
+(defun sh-font-lock-paren (start)
+  (save-excursion
+    (goto-char start)
+    ;; Skip through all patterns
+    (while
+       (progn
+         (forward-comment (- (point-max)))
+         ;; Skip through one pattern
+         (while
+             (or (/= 0 (skip-syntax-backward "w_"))
+                 (/= 0 (skip-chars-backward "?[]*@/\\"))
+                 (and (sh-is-quoted-p (1- (point)))
+                      (goto-char (- (point) 2)))
+                 (when (memq (char-before) '(?\" ?\'))
+                   (condition-case nil (progn (backward-sexp 1) t)
+                     (error nil)))))
+         (forward-comment (- (point-max)))
+         (when (eq (char-before) ?|)
+           (backward-char 1) t)))
+    (when (save-excursion (backward-char 2) (looking-at ";;\\|in"))
+      sh-st-punc)))
+
 (defconst sh-font-lock-syntactic-keywords
-  ;; Mark a `#' character as having punctuation syntax in a variable reference.
-  ;; Really we should do this properly.  From Chet Ramey and Brian Fox:
-  ;; "A `#' begins a comment when it is unquoted and at the beginning of a
-  ;; word.  In the shell, words are separated by metacharacters."
-  ;; To do this in a regexp would be slow as it would be anchored to the right.
-  ;; But I can't be bothered to write a function to do it properly and
-  ;; efficiently.  So we only do it properly for `#' in variable references and
-  ;; do it efficiently by anchoring the regexp to the left.
-  '(("\\${?[^}#\n\t ]*\\(##?\\)" 1 (1 . nil))))
+  ;; A `#' begins a comment when it is unquoted and at the beginning of a
+  ;; word.  In the shell, words are separated by metacharacters.
+  ;; The list of special chars is taken from the single-unix spec
+  ;; of the shell command language (under `quoting') but with `$' removed.
+  `(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 ,sh-st-symbol)
+    ;; Find HEREDOC starters and add a corresponding rule for the ender.
+    (sh-font-lock-here-doc
+     (2 (sh-font-lock-open-heredoc
+        (match-beginning 0) (match-string 1)) nil t)
+     (5 (sh-font-lock-close-heredoc
+        (match-beginning 0) (match-string 4)
+         (and (match-beginning 3) (/= (match-beginning 3) (match-end 3))))
+      nil t))
+    ;; Distinguish the special close-paren in `case'.
+    (")" 0 (sh-font-lock-paren (match-beginning 0)))))
+
+(defun sh-font-lock-syntactic-face-function (state)
+  (if (nth 3 state)
+      (if (char-valid-p (nth 3 state))
+         font-lock-string-face
+       sh-heredoc-face)
+    font-lock-comment-face))
 
 (defgroup sh-indentation nil
   "Variables controlling indentation in shell scripts.
@@ -837,12 +996,12 @@ and command `sh-reset-indent-vars-to-global-values'."
 
 (defcustom sh-set-shell-hook nil
   "*Hook run by `sh-set-shell'."
-   :type 'hook
+  :type 'hook
   :group 'sh-script)
 
 (defcustom sh-mode-hook nil
   "*Hook run by `sh-mode'."
-   :type 'hook
+  :type 'hook
   :group 'sh-script)
 
 (defcustom sh-learn-basic-offset nil
@@ -854,22 +1013,20 @@ Anything else means:   whenever we have a \"good guess\" as to the value."
   :type '(choice
          (const :tag "Never" nil)
          (const :tag "Only if sure"  t)
-         (const :tag "If have a good guess" usually)
-         )
+         (const :tag "If have a good guess" usually))
   :group 'sh-indentation)
 
 (defcustom sh-popup-occur-buffer nil
-  "*Controls when  `sh-learn-buffer-indent' poos the *indent* buffer.
-If t it is always shown.  If nil,  it is shown only when there
+  "*Controls when  `sh-learn-buffer-indent' pops the `*indent*' buffer.
+If t it is always shown.  If nil, it is shown only when there
 are conflicts."
   :type '(choice
          (const :tag "Only when there are conflicts." nil)
-         (const :tag "Always"  t)
-         )
+         (const :tag "Always"  t))
   :group 'sh-indentation)
 
 (defcustom sh-blink t
-  "*If non-nil,  `sh-show-indent' shows the line indentation is relative to.
+  "*If non-nil, `sh-show-indent' shows the line indentation is relative to.
 The position on the line is not necessarily meaningful.
 In some cases the line will be the matching keyword, but this is not
 always the case."
@@ -879,32 +1036,31 @@ always the case."
 (defcustom sh-first-lines-indent 0
   "*The indentation of the first non-blank non-comment line.
 Usually 0 meaning first column.
-Can be set to a number,  or to nil which means leave it as is."
+Can be set to a number, or to nil which means leave it as is."
   :type '(choice
          (const :tag "Leave as is"     nil)
          (integer :tag "Column number"
-                  :menu-tag "Indent to this col (0 means first col)" )
-         )
+                  :menu-tag "Indent to this col (0 means first col)" ))
   :group 'sh-indentation)
 
 
 (defcustom sh-basic-offset 4
-  "*The default indentation incrementation.
-This value is used for the + and - symbols in an indentation variable."
+  "*The default indentation increment.
+This value is used for the `+' and `-' symbols in an indentation variable."
   :type 'integer
   :group 'sh-indentation)
 
 (defcustom sh-indent-comment nil
   "*How a comment line is to be indented.
 nil means leave it as it is;
-t  means indent it as a normal line,  aligning it to previous non-blank
+t  means indent it as a normal line, aligning it to previous non-blank
    non-comment line;
-a number means align to that column,  e.g. 0 means fist column."
+a number means align to that column, e.g. 0 means fist column."
   :type '(choice
          (const :tag "Leave as is." nil)
          (const :tag "Indent as a normal line."  t)
          (integer :menu-tag "Indent to this col (0 means first col)."
-          :tag "Indent to column number.") )
+                  :tag "Indent to column number.") )
   :group 'sh-indentation)
 
 
@@ -919,24 +1075,22 @@ a number means align to that column,  e.g. 0 means fist column."
 ;;       (apply 'message args)))
 (defmacro sh-debug (&rest args))
 
-(setq sh-symbol-list
- '(
-   (const :tag "+ "  :value +
-         :menu-tag "+   Indent right by sh-basic-offset")
-   (const :tag "- "  :value -
-         :menu-tag "-   Indent left  by sh-basic-offset")
-   (const :tag "++"  :value  ++
-         :menu-tag "++  Indent right twice sh-basic-offset")
-   (const :tag "--"  :value --
-         :menu-tag "--  Indent left  twice sh-basic-offset")
-   (const :tag "* " :value *
-         :menu-tag "*   Indent right half sh-basic-offset")
-   (const :tag "/ " :value /
-         :menu-tag "/   Indent left  half sh-basic-offset")
-   ))
+(defconst sh-symbol-list
+  '((const :tag "+ "  :value +
+          :menu-tag "+   Indent right by sh-basic-offset")
+    (const :tag "- "  :value -
+          :menu-tag "-   Indent left  by sh-basic-offset")
+    (const :tag "++"  :value  ++
+          :menu-tag "++  Indent right twice sh-basic-offset")
+    (const :tag "--"  :value --
+          :menu-tag "--  Indent left  twice sh-basic-offset")
+    (const :tag "* " :value *
+          :menu-tag "*   Indent right half sh-basic-offset")
+    (const :tag "/ " :value /
+          :menu-tag "/   Indent left  half sh-basic-offset")))
 
 (defcustom sh-indent-for-else 0
-  "*How much to indent an else relative to an if.  Usually 0."
+  "*How much to indent an `else' relative to its `if'.  Usually 0."
   :type `(choice
          (integer :menu-tag "A number (positive=>indent right)"
                   :tag "A number")
@@ -945,84 +1099,82 @@ a number means align to that column,  e.g. 0 means fist column."
          )
   :group 'sh-indentation)
 
-(setq sh-number-or-symbol-list
-      (append (list '(
-                     integer :menu-tag "A number (positive=>indent right)"
-                             :tag "A number")
-                   '(const :tag "--") ;; separator
-                   )
-             sh-symbol-list))
+(defconst sh-number-or-symbol-list
+  (append '((integer :menu-tag "A number (positive=>indent right)"
+                    :tag "A number")
+           (const :tag "--"))          ; separator
+         sh-symbol-list))
 
 (defcustom sh-indent-for-fi 0
-  "*How much to indent a fi relative to an if.   Usually 0."
+  "*How much to indent a `fi' relative to its `if'.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
-(defcustom sh-indent-for-done '0
-  "*How much to indent a done relative to its matching stmt.   Usually 0."
+(defcustom sh-indent-for-done 0
+  "*How much to indent a `done' relative to its matching stmt.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-else '+
-  "*How much to indent a statement after an else statement."
+  "*How much to indent a statement after an `else' statement."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-if '+
-  "*How much to indent a statement after an if statement.
-This includes lines after else and elif statements, too, but
-does not affect then else elif or fi statements themselves."
+  "*How much to indent a statement after an `if' statement.
+This includes lines after `else' and `elif' statements, too, but
+does not affect the `else', `elif' or `fi' statements themselves."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
-(defcustom sh-indent-for-then '+
-  "*How much to indent an then relative to an if."
+(defcustom sh-indent-for-then 0
+  "*How much to indent a `then' relative to its `if'."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
-(defcustom sh-indent-for-do '*
-  "*How much to indent a do statement.
-This is relative to the statement before the do,  i.e. the
-while until or for statement."
+(defcustom sh-indent-for-do 0
+  "*How much to indent a `do' statement.
+This is relative to the statement before the `do', typically a
+`while', `until', `for', `repeat' or `select' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
-(defcustom sh-indent-after-do '*
-"*How much to indent a line after a do statement.
-This is used when the do is the first word of the line.
-This is relative to the statement before the do,  e.g. a
-while for repeat or select statement."
+(defcustom sh-indent-after-do '+
+  "*How much to indent a line after a `do' statement.
+This is used when the `do' is the first word of the line.
+This is relative to the statement before the `do', typically a
+`while', `until', `for', `repeat' or `select' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-loop-construct '+
   "*How much to indent a statement after a loop construct.
 
-This variable is used when the keyword \"do\" is on the same line as the
-loop statement (e.g.  \"until\", \"while\" or \"for\").
-If the do is on a line by itself, then `sh-indent-after-do' is used instead."
+This variable is used when the keyword `do' is on the same line as the
+loop statement (e.g., `until', `while' or `for').
+If the `do' is on a line by itself, then `sh-indent-after-do' is used instead."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 
 (defcustom sh-indent-after-done 0
-  "*How much to indent a statement after a \"done\" keyword.
-Normally this is 0, which aligns the \"done\" to the matching
+  "*How much to indent a statement after a `done' keyword.
+Normally this is 0, which aligns the `done' to the matching
 looping construct line.
-Setting it non-zero allows you to have the \"do\" statement on a line
+Setting it non-zero allows you to have the `do' statement on a line
 by itself and align the done under to do."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-case-label '+
   "*How much to indent a case label statement.
-This is relative to the line containing the case statement."
+This is relative to the line containing the `case' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-case-alt '++
   "*How much to indent statements after the case label.
-This is relative to the line containing the case statement."
+This is relative to the line containing the `case' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
@@ -1034,7 +1186,7 @@ This is relative to the line containing the case statement."
 
 (defcustom sh-indent-after-open '+
   "*How much to indent after a line with an opening parenthesis or brace.
-For an open paren after a function `sh-indent-after-function' is used."
+For an open paren after a function, `sh-indent-after-function' is used."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
@@ -1046,90 +1198,54 @@ For an open paren after a function `sh-indent-after-function' is used."
 ;; These 2 are for the rc shell:
 
 (defcustom sh-indent-after-switch '+
-  "*How much to indent a case statement relative to the switch statement.
+  "*How much to indent a `case' statement relative to the `switch' statement.
 This is for the rc shell."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-case '+
-  "*How much to indent a statement relative to the case statement.
+  "*How much to indent a statement relative to the `case' statement.
 This is for the rc shell."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
-(defface sh-heredoc-face
-  '((((class color)
-      (background dark))
-     (:foreground "yellow" :bold t))
-    (((class color)
-      (background light))
-     (:foreground "tan" ))
-    (t
-     (:bold t)))
-  "Face to show a here-document"
-  :group 'sh-indentation)
-
-(defface sh-st-face
-  '((((class color)
-      (background dark))
-     (:foreground "yellow" :bold t))
-    (((class color)
-      (background light))
-     (:foreground "tan" ))
-    (t
-     (:bold t)))
-  "Face to show characters with special syntax properties."
-  :group 'sh-indentation)
+(defcustom sh-backslash-column 48
+  "*Column in which `sh-backslash-region' inserts backslashes."
+  :type 'integer
+  :group 'sh)
 
+(defcustom sh-backslash-align t
+  "*If non-nil, `sh-backslash-region' will align backslashes."
+  :type 'boolean
+  :group 'sh)
 
 ;; Internal use - not designed to be changed by the user:
 
-;; These are used for the syntax table stuff (derived from cperl-mode).
-;; Note: parse-sexp-lookup-properties must be set to t for it to work.
-(defconst sh-here-doc-syntax '(15))    ;; generic string
-(defconst sh-st-punc '(1))
-(defconst sh-special-syntax sh-st-punc)
-
 (defun sh-mkword-regexpr (word)
   "Make a regexp which matches WORD as a word.
-This specifically excludes an occurance of WORD followed by
+This specifically excludes an occurrence of WORD followed by
 punctuation characters like '-'."
   (concat word "\\([^-a-z0-9_]\\|$\\)"))
 
-(defun sh-mkword-regexp (word)
-  "Make a regexp which matches WORD as a word.
-This specifically excludes an occurance of WORD followed by
-or preceded by punctuation characters like '-'."
-  (concat "\\(^\\|[^-a-z0-9_]\\)" word "\\([^-a-z0-9_]\\|$\\)"))
-
-(setq sh-re-done (sh-mkword-regexpr "done"))
+(defconst sh-re-done (sh-mkword-regexpr "done"))
 
 
 (defconst sh-kws-for-done
-  '(
-    (sh .  ( "while" "until" "for" ) )
+  '((sh .  ( "while" "until" "for" ) )
     (bash . ( "while" "until" "for" "select"  ) )
     (ksh88 . ( "while" "until" "for" "select"  ) )
-    (zsh .  ( "while" "until" "for" "repeat" "select" ) )
-    )
-  "Which keywords can match the word `done' in this shell."
-  )
+    (zsh .  ( "while" "until" "for" "repeat" "select" ) ) )
+  "Which keywords can match the word `done' in this shell.")
 
 
 (defconst sh-indent-supported
-  '(
-    (sh . t)
+  '((sh . t)
     (csh . nil)
-    (rc . t)
-    )
-  "Shell types that shell indenting can do something with."
-  )
+    (rc . t))
+  "Shell types that shell indenting can do something with.")
 
-(defconst sh-electric-rparen-needed
-  '(
-    (sh . t))
-  "Non-nil if the shell type needs an electric handling of case alternatives."
-  )
+(defvar sh-indent-supported-here nil
+  "Non-nil if we support indentation for the current buffer's shell type.")
 
 (defconst sh-var-list
   '(
@@ -1151,12 +1267,12 @@ or preceded by punctuation characters like '-'."
     )
   "A list of variables used by script mode to control indentation.
 This list is used when switching between buffer-local and global
-values of variables,  and for the commands using indenation styles.")
+values of variables, and for the commands using indentation styles.")
 
 (defvar sh-make-vars-local t
   "*Controls whether indentation variables are local to the buffer.
-If non-nil,  indentation variables are made local initially.
-If nil,  you can later make the variables local by invoking
+If non-nil, indentation variables are made local initially.
+If nil, you can later make the variables local by invoking
 command `sh-make-vars-local'.
 The default is t because I assume that in one Emacs session one is
 frequently editing existing scripts with different styles.")
@@ -1164,9 +1280,6 @@ frequently editing existing scripts with different styles.")
 \f
 ;; mode-command and utility functions
 
-;;;###autoload
-(put 'sh-mode 'mode-class 'special)
-
 ;;;###autoload
 (defun sh-mode ()
   "Major mode for editing shell scripts.
@@ -1201,7 +1314,7 @@ For sh and rc shells indentation commands are:
 \\[sh-learn-line-indent]       Change the indentation variable so this line
 would indent to the way it currently is.
 \\[sh-learn-buffer-indent]  Set the indentation variables so the
-buffer indents as it currently is indendeted.
+buffer indents as it currently is indented.
 
 
 \\[backward-delete-char-untabify]       Delete backward one position, even if it was a tab.
@@ -1212,7 +1325,7 @@ buffer indents as it currently is indendeted.
 \\[sh-execute-region]   Have optional header and region be executed in a subshell.
 
 \\[sh-maybe-here-document]      Without prefix, following an unquoted < inserts here document.
-{, (, [, ', \", `
+\{, (, [, ', \", `
        Unless quoted with \\, insert the pairs {}, (), [], or '', \"\", ``.
 
 If you generally program a shell different from your login shell you can
@@ -1223,9 +1336,9 @@ If your shell gives error messages with line numbers, you can use \\[executable-
 with your script for an edit-interpret-debug cycle."
   (interactive)
   (kill-all-local-variables)
+  (setq major-mode 'sh-mode
+       mode-name "Shell-script")
   (use-local-map sh-mode-map)
-  (make-local-variable 'indent-line-function)
-  (make-local-variable 'indent-region-function)
   (make-local-variable 'skeleton-end-hook)
   (make-local-variable 'paragraph-start)
   (make-local-variable 'paragraph-separate)
@@ -1245,21 +1358,8 @@ with your script for an edit-interpret-debug cycle."
   (make-local-variable 'sh-shell-variables)
   (make-local-variable 'sh-shell-variables-initialized)
   (make-local-variable 'imenu-generic-expression)
-  (make-local-variable 'sh-electric-rparen-needed-here)
   (make-local-variable 'sh-indent-supported-here)
-  (make-local-variable 'font-lock-unfontify-region-function)
-  (setq major-mode 'sh-mode
-       mode-name "Shell-script"
-       ;; not very clever, but enables wrapping skeletons around regions
-       indent-region-function (lambda (b e)
-                                (save-excursion
-                                  (goto-char b)
-                                  (skip-syntax-backward "-")
-                                  (setq b (point))
-                                  (goto-char e)
-                                  (skip-syntax-backward "-")
-                                  (indent-rigidly b (point) sh-indentation)))
-       skeleton-end-hook (lambda ()
+  (setq skeleton-end-hook (lambda ()
                            (or (eolp) (newline) (indent-relative)))
        paragraph-start (concat page-delimiter "\\|$")
        paragraph-separate paragraph-start
@@ -1268,23 +1368,21 @@ with your script for an edit-interpret-debug cycle."
        ;; we can't look if previous line ended with `\'
        comint-prompt-regexp "^[ \t]*"
        font-lock-defaults
-       '((sh-font-lock-keywords
+       `((sh-font-lock-keywords
           sh-font-lock-keywords-1 sh-font-lock-keywords-2)
          nil nil
          ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
-         (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords))
-       font-lock-unfontify-region-function 
-               'sh-font-lock-unfontify-region-function
+         (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords)
+         (font-lock-syntactic-face-function
+          . sh-font-lock-syntactic-face-function))
        skeleton-pair-alist '((?` _ ?`))
        skeleton-pair-filter 'sh-quoted-p
        skeleton-further-elements '((< '(- (min sh-indentation
                                                (current-column)))))
        skeleton-filter 'sh-feature
        skeleton-newline-indent-rigidly t
-       sh-electric-rparen-needed-here nil
        sh-indent-supported-here nil)
-  (make-local-variable 'parse-sexp-ignore-comments)
-  (setq parse-sexp-ignore-comments t)
+  (set (make-local-variable 'parse-sexp-ignore-comments) t)
   ;; Parse or insert magic number for exec, and set all variables depending
   ;; on the shell thus determined.
   (let ((interpreter
@@ -1293,12 +1391,11 @@ with your script for an edit-interpret-debug cycle."
           (cond ((looking-at "#![ \t]?\\([^ \t\n]*/bin/env[ \t]\\)?\\([^ \t\n]+\\)")
                  (match-string 2))
                 ((and buffer-file-name
-                      (string-match "\\.m?spec$" buffer-file-name))
+                      (string-match "\\.m?spec\\'" buffer-file-name))
                  "rpm")))))
-    (if interpreter
-       (sh-set-shell interpreter nil nil)
-      (sh-set-shell sh-shell-file nil t))
-  (run-hooks 'sh-mode-hook))
+    (sh-set-shell (or interpreter sh-shell-file) nil nil))
+  (run-mode-hooks 'sh-mode-hook))
+
 ;;;###autoload
 (defalias 'shell-script-mode 'sh-mode)
 
@@ -1316,21 +1413,17 @@ This adds rules for comments and assignments."
 
 (defun sh-font-lock-keywords-1 (&optional builtins)
   "Function to get better fontification including keywords."
-  (let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\(\\("
-                         (mapconcat 'identity
-                                    (sh-feature sh-leading-keywords)
-                                    "\\|")
-                         "\\)[ \t]+\\)?\\("
-                         (mapconcat 'identity
-                                    (append (sh-feature sh-leading-keywords)
-                                            (sh-feature sh-other-keywords))
-                                    "\\|")
-                         "\\)")))
+  (let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\("
+                         (regexp-opt (sh-feature sh-leading-keywords) t)
+                         "[ \t]+\\)?"
+                         (regexp-opt (append (sh-feature sh-leading-keywords)
+                                             (sh-feature sh-other-keywords))
+                                     t))))
     (sh-font-lock-keywords
      `(,@(if builtins
-            `((,(concat keywords "[ \t]+\\)?\\("
-                        (mapconcat 'identity (sh-feature sh-builtins) "\\|")
-                        "\\)\\>")
+            `((,(concat keywords "[ \t]+\\)?"
+                        (regexp-opt (sh-feature sh-builtins) t)
+                        "\\>")
                (2 font-lock-keyword-face nil t)
                (6 font-lock-builtin-face))
               ,@(sh-feature sh-font-lock-keywords-2)))
@@ -1343,14 +1436,78 @@ This adds rules for comments and assignments."
   (sh-font-lock-keywords-1 t))
 
 
+(defvar sh-regexp-for-done nil
+  "A buffer-local regexp to match opening keyword for done.")
+
+(defvar sh-kw-alist nil
+  "A buffer-local, since it is shell-type dependent, list of keywords.")
+
+;; ( key-word  first-on-this  on-prev-line )
+;; This is used to set `sh-kw-alist' which is a list of sublists each
+;; having 3 elements:
+;;   a keyword
+;;   a rule to check when the keyword appears on "this" line
+;;   a rule to check when the keyword appears on "the previous" line
+;; The keyword is usually a string and is the first word on a line.
+;; If this keyword appears on the line whose indentation is to be
+;; calculated, the rule in element 2 is called.  If this returns
+;; non-zero, the resulting point (which may be changed by the rule)
+;; is used as the default indentation.
+;; If it returned false or the keyword was not found in the table,
+;; then the keyword from the previous line is looked up and the rule
+;; in element 3 is called.  In this case, however,
+;; `sh-get-indent-info' does not stop but may keep going and test
+;; other keywords against rules in element 3.  This is because the
+;; preceding line could have, for example, an opening "if" and an
+;; opening "while" keyword and we need to add the indentation offsets
+;; for both.
+;;
+(defconst sh-kw
+  '((sh
+     ("if" nil sh-handle-prev-if)
+     ("elif" sh-handle-this-else sh-handle-prev-else)
+     ("else" sh-handle-this-else sh-handle-prev-else)
+     ("fi" sh-handle-this-fi sh-handle-prev-fi)
+     ("then" sh-handle-this-then sh-handle-prev-then)
+     ("(" nil sh-handle-prev-open)
+     ("{" nil sh-handle-prev-open)
+     ("[" nil sh-handle-prev-open)
+     ("}" sh-handle-this-close nil)
+     (")" sh-handle-this-close nil)
+     ("]" sh-handle-this-close nil)
+     ("case" nil sh-handle-prev-case)
+     ("esac" sh-handle-this-esac sh-handle-prev-esac)
+     (case-label nil sh-handle-after-case-label) ;; ???
+     (";;" nil sh-handle-prev-case-alt-end) ;; ???
+     ("done" sh-handle-this-done sh-handle-prev-done)
+     ("do" sh-handle-this-do sh-handle-prev-do))
+
+    ;; Note: we don't need specific stuff for bash and zsh shells;
+    ;; the regexp `sh-regexp-for-done' handles the extra keywords
+    ;; these shells use.
+    (rc
+     ("{" nil sh-handle-prev-open)
+     ("}" sh-handle-this-close nil)
+     ("case" sh-handle-this-rc-case sh-handle-prev-rc-case))))
+
+
 (defun sh-set-shell (shell &optional no-query-flag insert-flag)
   "Set this buffer's shell to SHELL (a string).
-Makes this script executable via `executable-set-magic', and sets up the
-proper starting #!-line, if INSERT-FLAG is non-nil.
+When used interactively, insert the proper starting #!-line,
+and make the visited file executable via `executable-set-magic',
+perhaps querying depending on the value of `executable-query'.
+
+When this function is called noninteractively, INSERT-FLAG (the third
+argument) controls whether to insert a #!-line and think about making
+the visited file executable, and NO-QUERY-FLAG (the second argument)
+controls whether to query about making the visited file executable.
+
 Calls the value of `sh-set-shell-hook' if set."
-  (interactive (list (completing-read "Name or path of shell: "
-                                     interpreter-mode-alist
-                                     (lambda (x) (eq (cdr x) 'sh-mode)))
+  (interactive (list (completing-read (format "Shell \(default %s\): "
+                                             sh-shell-file)
+                                     interpreter-mode-alist
+                                     (lambda (x) (eq (cdr x) 'sh-mode))
+                                     nil nil nil sh-shell-file)
                     (eq executable-query 'function)
                     t))
   (if (string-match "\\.exe\\'" shell)
@@ -1362,50 +1519,39 @@ Calls the value of `sh-set-shell-hook' if set."
       (setq sh-shell-file
            (executable-set-magic shell (sh-feature sh-shell-arg)
                                  no-query-flag insert-flag)))
-  (setq require-final-newline (sh-feature sh-require-final-newline)
-;;;    local-abbrev-table (sh-feature sh-abbrevs)
-;; Packages should not need to set these variables directly.  sm.
-;      font-lock-keywords nil          ; force resetting
-;      font-lock-syntax-table nil
+  (let ((tem (sh-feature sh-require-final-newline)))
+    (if (eq tem t)
+       (setq require-final-newline mode-require-final-newline)))
+  (setq
        comment-start-skip "#+[\t ]*"
+       local-abbrev-table sh-mode-abbrev-table
        mode-line-process (format "[%s]" sh-shell)
        sh-shell-variables nil
        sh-shell-variables-initialized nil
        imenu-generic-expression (sh-feature sh-imenu-generic-expression)
        imenu-case-fold-search nil)
-  (set-syntax-table (or (sh-feature sh-mode-syntax-table)
-                       (standard-syntax-table)))
-  (let ((vars (sh-feature sh-variables)))
-    (while vars
-      (sh-remember-variable (car vars))
-      (setq vars (cdr vars))))
-;; Packages should not need to toggle Font Lock mode.  sm.
-;  (and (boundp 'font-lock-mode)
-;       font-lock-mode
-;       (font-lock-mode (font-lock-mode 0)))
+  (make-local-variable 'sh-mode-syntax-table)
+  (let ((tem (sh-feature sh-mode-syntax-table-input)))
+    (setq sh-mode-syntax-table
+         (if tem (apply 'sh-mode-syntax-table tem)
+           sh-mode-default-syntax-table)))
+  (set-syntax-table sh-mode-syntax-table)
+  (dolist (var (sh-feature sh-variables))
+    (sh-remember-variable var))
+  (make-local-variable 'indent-line-function)
   (if (setq sh-indent-supported-here (sh-feature sh-indent-supported))
       (progn
        (message "Setting up indent for shell type %s" sh-shell)
-       (make-local-variable 'sh-kw-alist)
-       (make-local-variable 'sh-regexp-for-done)
-       (make-local-variable 'parse-sexp-lookup-properties)
-       (setq sh-electric-rparen-needed-here
-             (sh-feature sh-electric-rparen-needed))
-       (setq parse-sexp-lookup-properties t)
-       (sh-scan-buffer)
-       (setq sh-kw-alist (sh-feature sh-kw))
+       (set (make-local-variable 'parse-sexp-lookup-properties) t)
+       (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw))
        (let ((regexp (sh-feature sh-kws-for-done)))
          (if regexp
-             (setq sh-regexp-for-done
-                   (sh-mkword-regexpr (regexp-opt regexp t)))))
+             (set (make-local-variable 'sh-regexp-for-done)
+                  (sh-mkword-regexpr (regexp-opt regexp t)))))
        (message "setting up indent stuff")
        ;; sh-mode has already made indent-line-function local
        ;; but do it in case this is called before that.
-       (make-local-variable 'indent-line-function)
        (setq indent-line-function 'sh-indent-line)
-       ;; This is very inefficient,  but this at least makes indent-region work:
-       (make-local-variable 'indent-region-function)
-       (setq indent-region-function nil)
        (if sh-make-vars-local
            (sh-make-vars-local))
        (message "Indentation setup for shell type %s" sh-shell))
@@ -1415,7 +1561,7 @@ Calls the value of `sh-set-shell-hook' if set."
 
 
 
-(defun sh-feature (list &optional function)
+(defun sh-feature (alist &optional function)
   "Index ALIST by the current shell.
 If ALIST isn't a list where every element is a cons, it is returned as is.
 Else indexing follows an inheritance logic which works in two ways:
@@ -1424,46 +1570,43 @@ Else indexing follows an inheritance logic which works in two ways:
     the alist contains no value for the current shell.
     The ultimate default is always `sh'.
 
-  - If the value thus looked up is a list starting with `eval' its `cdr' is
-    first evaluated.  If that is also a list and the first argument is a
-    symbol in ALIST it is not evaluated, but rather recursively looked up in
-    ALIST to allow the function called to define the value for one shell to be
-    derived from another shell.  While calling the function, is the car of the
-    alist element is the current shell.
+  - If the value thus looked up is a list starting with `sh-append',
+    we call the function `sh-append' with the rest of the list as
+    arguments, and use the value.  However, the next element of the
+    list is not used as-is; instead, we look it up recursively
+    in ALIST to allow the function called to define the value for
+    one shell to be derived from another shell.
     The value thus determined is physically replaced into the alist.
 
 Optional FUNCTION is applied to the determined value and the result is cached
 in ALIST."
-  (or (if (consp list)
-         (let ((l list))
+  (or (if (consp alist)
+         (let ((l alist))
            (while (and l (consp (car l)))
              (setq l (cdr l)))
-           (if l list)))
+           (if l alist)))
       (if function
-         (cdr (assoc (setq function (cons sh-shell function)) list)))
+         (cdr (assoc (setq function (cons sh-shell function)) alist)))
       (let ((sh-shell sh-shell)
            elt val)
        (while (and sh-shell
-                   (not (setq elt (assq sh-shell list))))
+                   (not (setq elt (assq sh-shell alist))))
          (setq sh-shell (cdr (assq sh-shell sh-ancestor-alist))))
        ;; If the shell is not known, treat it as sh.
        (unless elt
-         (setq elt (assq 'sh list)))
+         (setq elt (assq 'sh alist)))
        (if (and (consp (setq val (cdr elt)))
-                (eq (car val) 'eval))
+                (memq (car val) '(sh-append sh-modify)))
            (setcdr elt
                    (setq val
-                         (eval (if (consp (setq val (cdr val)))
-                                   (let ((sh-shell (car (cdr val)))
-                                         function)
-                                     (if (assq sh-shell list)
-                                         (setcar (cdr val)
-                                                 (list 'quote
-                                                       (sh-feature list))))
-                                     val)
-                                 val)))))
+                         (apply (car val)
+                                (let ((sh-shell (car (cdr val))))
+                                   (if (assq sh-shell alist)
+                                       (sh-feature alist)
+                                     (eval sh-shell)))
+                                (cddr val)))))
        (if function
-           (nconc list
+           (nconc alist
                   (list (cons function
                               (setq sh-shell (car function)
                                     val (funcall (cdr function) val))))))
@@ -1471,46 +1614,37 @@ in ALIST."
 
 
 
-;;; I commented this out because nobody calls it -- rms.
-;;;(defun sh-abbrevs (ancestor &rest list)
-;;;  "Iff it isn't, define the current shell as abbrev table and fill that.
-;;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an abbrev
-;;;table or a list of (NAME1 EXPANSION1 ...).  In addition it will define abbrevs
-;;;according to the remaining arguments NAMEi EXPANSIONi ...
-;;;EXPANSION may be either a string or a skeleton command."
-;;;  (or (if (boundp sh-shell)
-;;;      (symbol-value sh-shell))
-;;;      (progn
-;;;    (if (listp ancestor)
-;;;        (nconc list ancestor))
-;;;    (define-abbrev-table sh-shell ())
-;;;    (if (vectorp ancestor)
-;;;        (mapatoms (lambda (atom)
-;;;                    (or (eq atom 0)
-;;;                        (define-abbrev (symbol-value sh-shell)
-;;;                          (symbol-name atom)
-;;;                          (symbol-value atom)
-;;;                          (symbol-function atom))))
-;;;                  ancestor))
-;;;    (while list
-;;;      (define-abbrev (symbol-value sh-shell)
-;;;        (car list)
-;;;        (if (stringp (car (cdr list)))
-;;;            (car (cdr list))
-;;;          "")
-;;;        (if (symbolp (car (cdr list)))
-;;;            (car (cdr list))))
-;;;      (setq list (cdr (cdr list)))))
-;;;      (symbol-value sh-shell)))
-
-
-(defun sh-mode-syntax-table (table &rest list)
-  "Copy TABLE and set syntax for successive CHARs according to strings S."
-  (setq table (copy-syntax-table table))
-  (while list
-    (modify-syntax-entry (car list) (car (cdr list)) table)
-    (setq list (cdr (cdr list))))
-  table)
+;; I commented this out because nobody calls it -- rms.
+;;(defun sh-abbrevs (ancestor &rest list)
+;;  "Iff it isn't, define the current shell as abbrev table and fill that.
+;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an abbrev
+;;table or a list of (NAME1 EXPANSION1 ...).  In addition it will define abbrevs
+;;according to the remaining arguments NAMEi EXPANSIONi ...
+;;EXPANSION may be either a string or a skeleton command."
+;;  (or (if (boundp sh-shell)
+;;       (symbol-value sh-shell))
+;;      (progn
+;;     (if (listp ancestor)
+;;         (nconc list ancestor))
+;;     (define-abbrev-table sh-shell ())
+;;     (if (vectorp ancestor)
+;;         (mapatoms (lambda (atom)
+;;                     (or (eq atom 0)
+;;                         (define-abbrev (symbol-value sh-shell)
+;;                           (symbol-name atom)
+;;                           (symbol-value atom)
+;;                           (symbol-function atom))))
+;;                   ancestor))
+;;     (while list
+;;       (define-abbrev (symbol-value sh-shell)
+;;         (car list)
+;;         (if (stringp (car (cdr list)))
+;;             (car (cdr list))
+;;           "")
+;;         (if (symbolp (car (cdr list)))
+;;             (car (cdr list))))
+;;       (setq list (cdr (cdr list)))))
+;;      (symbol-value sh-shell)))
 
 
 (defun sh-append (ancestor &rest list)
@@ -1591,7 +1725,7 @@ region, clear header."
   (or (< (length var) sh-remember-variable-min)
       (getenv var)
       (assoc var sh-shell-variables)
-      (setq sh-shell-variables (cons (cons var var) sh-shell-variables)))
+      (push (cons var var) sh-shell-variables))
   var)
 
 
@@ -1601,167 +1735,55 @@ region, clear header."
   (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2)))
 \f
 ;; Indentation stuff.
-(defun sh-must-be-shell-mode ()
-  "Signal an error if not in Shell-script mode."
-  (unless (eq major-mode 'sh-mode)
-    (error "This buffer is not in Shell-script mode")))
-
 (defun sh-must-support-indent ()
   "*Signal an error if the shell type for this buffer is not supported.
-Also,  the buffer must be in Shell-script mode."
-  (sh-must-be-shell-mode)
+Also, the buffer must be in Shell-script mode."
   (unless sh-indent-supported-here
-    (error "This buffer's shell type is not supported for this command")))
+    (error "This buffer's shell does not support indentation through Emacs")))
 
 (defun sh-make-vars-local ()
   "Make the indentation variables local to this buffer.
 Normally they already are local.  This command is provided in case
 variable `sh-make-vars-local' has been set to nil.
 
-To revert all these variables to the global values,  use
+To revert all these variables to the global values, use
 command `sh-reset-indent-vars-to-global-values'."
   (interactive)
-  (sh-must-be-shell-mode)
   (mapcar 'make-local-variable sh-var-list)
   (message "Indentation variable are now local."))
 
 (defun sh-reset-indent-vars-to-global-values ()
-  "Reset local indenatation variables to the global values.
-Then, if variable `sh-make-vars-local' is non-nil,  make them local."
+  "Reset local indentation variables to the global values.
+Then, if variable `sh-make-vars-local' is non-nil, make them local."
   (interactive)
-  (sh-must-be-shell-mode)
   (mapcar 'kill-local-variable sh-var-list)
   (if sh-make-vars-local
       (mapcar 'make-local-variable sh-var-list)))
 
 
-(defvar sh-kw-alist nil
-  "A buffer-local, since it is shell-type dependent, list of keywords.")
-
-(defvar sh-regexp-for-done nil
-  "A buffer-local regexp to match opening keyword for done.")
-
 ;; Theoretically these are only needed in shell and derived modes.
 ;; However, the routines which use them are only called in those modes.
 (defconst sh-special-keywords "then\\|do")
 
-;; ( key-word  first-on-this  on-prev-line )
-;; This is used to set `sh-kw-alist' which is a list of sublists each
-;; having 3 elements:
-;;   a keyword
-;;   a rule to check when the keyword apepars on "this" line
-;;   a rule to check when the keyword apepars on "the previous" line
-;; The keyword is usually a string and is the first word on a line.
-;; If this keyword appears on the line whose indenation is to be
-;; calculated,  the rule in element 2 is called.  If this returns
-;; non-zero,  the resulting point (which may be changed by the rule)
-;; is used as the default indentation.
-;; If it returned false or the keyword was not found in the table,
-;; then the keyword from the previous line is looked up and the rule
-;; in element 3 is called.  In this case, however,
-;; `sh-get-indent-info' does not stop but may keepp going and test
-;; other keywords against rules in element 3.  This is because the
-;; precending line could have, for example, an opening "if" and an
-;; opening "while" keyword and we need to add the indentation offsets
-;; for both.
-;;
-(defconst sh-kw
-  '(
-    (sh
-       ( "if"
-         nil
-         sh-handle-prev-if   )
-       ( "elif"
-         sh-handle-this-else
-         sh-handle-prev-else )
-       ( "else"
-         sh-handle-this-else
-         sh-handle-prev-else )
-       ( "fi"
-         sh-handle-this-fi
-         sh-handle-prev-fi )
-       ( "then"
-         sh-handle-this-then
-         sh-handle-prev-then )
-       ( "("
-         nil
-         sh-handle-prev-open  )
-       ( "{"
-         nil
-         sh-handle-prev-open  )
-       ( "["
-         nil
-         sh-handle-prev-open  )
-       ( "}"
-         sh-handle-this-close
-         nil  )
-       ( ")"
-         sh-handle-this-close
-         nil  )
-       ( "]"
-         sh-handle-this-close
-         nil  )
-       ( "case"
-         nil
-         sh-handle-prev-case   )
-       ( "esac"
-         sh-handle-this-esac
-         sh-handle-prev-esac )
-       ( case-label
-         nil   ;; ???
-         sh-handle-after-case-label )
-       ( ";;"
-         nil   ;; ???
-         sh-handle-prev-case-alt-end  ;; ??
-         )
-       ( "done"
-         sh-handle-this-done
-         sh-handle-prev-done )
-       ( "do"
-         sh-handle-this-do
-         sh-handle-prev-do )
-       ) ;; end of sh
-
-    ;; Note: we don't need specific stuff for bash and zsh shells;
-    ;; the regexp `sh-regexp-for-done' handles the extra keywords
-    ;; these shells use.
-    (rc
-     ( "{"
-         nil
-         sh-handle-prev-open  )
-     ( "}"
-         sh-handle-this-close
-         nil  )
-     ( "case"
-       sh-handle-this-rc-case
-       sh-handle-prev-rc-case   )
-     ) ;; end of rc
-    ))
-
-
 (defun sh-help-string-for-variable (var)
   "Construct a string for `sh-read-variable' when changing variable VAR ."
   (let ((msg (documentation-property var 'variable-documentation))
        (msg2 ""))
-    (unless (or
-            (eq var 'sh-first-lines-indent)
-            (eq var 'sh-indent-comment))
+    (unless (memq var '(sh-first-lines-indent sh-indent-comment))
       (setq msg2
            (format "\n
-You can enter a number (positive to increase indentenation,
-negative to decrease indentation,  zero for no change to  indentnation).
+You can enter a number (positive to increase indentation,
+negative to decrease indentation, zero for no change to indentation).
 
-Or,  you can enter one of the following symbols which are relative to
+Or, you can enter one of the following symbols which are relative to
 the value of variable `sh-basic-offset'
 which in this buffer is currently %s.
 
 \t%s."
                    sh-basic-offset
-                   (mapconcat  '(lambda (x)
-                                  (nth (1- (length x))  x) )
-                               sh-symbol-list  "\n\t")
-                   )))
-
+                   (mapconcat (lambda (x)
+                                (nth (1- (length x)) x))
+                              sh-symbol-list  "\n\t"))))
     (concat
      ;; The following shows the global not the local value!
      ;; (format "Current value of %s is %s\n\n" var (symbol-value var))
@@ -1774,10 +1796,10 @@ which in this buffer is currently %s.
                                (quote ,var)))
        val)
     (setq val (read-from-minibuffer
-                (format "New value for %s (press %s for help): "
-                        var  (single-key-description help-char))
-                (format "%s" (symbol-value var))
-                nil t))
+              (format "New value for %s (press %s for help): "
+                      var  (single-key-description help-char))
+              (format "%s" (symbol-value var))
+              nil t))
     val))
 
 
@@ -1785,10 +1807,8 @@ which in this buffer is currently %s.
 (defun sh-in-comment-or-string (start)
   "Return non-nil if START is in a comment or string."
   (save-excursion
-    (let (state)
-      (beginning-of-line)
-      (setq state (parse-partial-sexp (point) start nil nil nil t))
-      (or (nth 3 state)(nth 4 state)))))
+    (let ((state (syntax-ppss start)))
+      (or (nth 3 state) (nth 4 state)))))
 
 (defun sh-goto-matching-if ()
   "Go to the matching if for a fi.
@@ -1800,7 +1820,7 @@ This handles nested if..fi pairs."
 
 ;; Functions named sh-handle-this-XXX are called when the keyword on the
 ;; line whose indentation is being handled contain XXX;
-;; those named sh-handle-prev-XXX are when XXX appears on the prevoius line.
+;; those named sh-handle-prev-XXX are when XXX appears on the previous line.
 
 (defun sh-handle-prev-if ()
   (list '(+ sh-indent-after-if)))
@@ -1860,79 +1880,46 @@ This handles nested if..fi pairs."
 
 (defun sh-handle-this-close ()
   (forward-char 1) ;; move over ")"
-  (let ((p (sh-safe-backward-sexp)))
-    (if p
-       (list "aligned to opening paren")
-      nil
-      )))
+  (if (sh-safe-forward-sexp -1)
+      (list "aligned to opening paren")))
 
 (defun sh-goto-matching-case ()
   (let ((found (sh-find-prev-matching "\\bcase\\b" "\\besac\\b" 1)))
-    (if found
-       (goto-char found))))
+    (if found (goto-char found))))
 
 (defun sh-handle-prev-case ()
   ;; This is typically called when point is on same line as a case
   ;; we shouldn't -- and can't find prev-case
-  (if (looking-at ".*\\bcase\\b")
+  (if (looking-at ".*\\<case\\>")
       (list '(+ sh-indent-for-case-label))
-    (error "We don't see to be on a line with a case") ;; debug
-    ))
+    (error "We don't seem to be on a line with a case"))) ;; debug
 
 (defun sh-handle-this-esac ()
-  (let ((p (sh-goto-matching-case)))
-    (if p
-       (list "aligned to matching case")
-      nil
-      )))
-
+  (if (sh-goto-matching-case)
+      (list "aligned to matching case")))
 
 (defun sh-handle-prev-esac ()
-  (let ((p (sh-goto-matching-case)))
-    (if p
-       (list "matching case")
-      nil
-    )))
+  (if (sh-goto-matching-case)
+      (list "matching case")))
 
 (defun sh-handle-after-case-label ()
-  (let ((p (sh-goto-matching-case)))
-    (if p
-       (list '( + sh-indent-for-case-alt ))
-      nil
-    )))
+  (if (sh-goto-matching-case)
+      (list '(+ sh-indent-for-case-alt))))
 
 (defun sh-handle-prev-case-alt-end ()
-  (let ((p (sh-goto-matching-case)))
-    (if p
-       (list '( + sh-indent-for-case-label ))
-      nil
-      )))
-
-(defun sh-safe-backward-sexp ()
-  "Try and do a `backward-sexp', but do not error.
-Return new point if successful,  nil if an error occurred."
-  (condition-case nil
-      (progn
-       (backward-sexp 1)
-       (point) ;; return point if successful
-       )
-    (error
-     (sh-debug "oops!(0) %d" (point))
-     nil ;; return nil if fail
-     )))
+  (if (sh-goto-matching-case)
+      (list '(+ sh-indent-for-case-label))))
 
-(defun sh-safe-forward-sexp ()
+(defun sh-safe-forward-sexp (&optional arg)
   "Try and do a `forward-sexp', but do not error.
-Return new point if successful,  nil if an error occurred."
+Return new point if successful, nil if an error occurred."
   (condition-case nil
       (progn
-       (forward-sexp 1)
-       (point) ;; return point if successful
-       )
+       (forward-sexp (or arg 1))
+       (point))        ;; return point if successful
     (error
      (sh-debug "oops!(1) %d" (point))
-     nil ;; return nil if fail
-     )))
+     nil))) ;; return nil if fail
 
 (defun sh-goto-match-for-done ()
   (let ((found (sh-find-prev-matching sh-regexp-for-done sh-re-done 1)))
@@ -1941,51 +1928,43 @@ Return new point if successful,  nil if an error occurred."
 
 (defun sh-handle-this-done ()
   (if (sh-goto-match-for-done)
-      (list  "aligned to do stmt"  '(+ sh-indent-for-done))
-    nil
-    ))
+      (list  "aligned to do stmt"  '(+ sh-indent-for-done))))
 
 (defun sh-handle-prev-done ()
   (if (sh-goto-match-for-done)
-      (list "previous done")
-    nil
-    ))
+      (list "previous done")))
 
 (defun sh-handle-this-do ()
-  (let ( (p (sh-goto-match-for-done))  )
-    (if p
-       (list  '(+ sh-indent-for-do))
-      nil
-      )))
+  (if (sh-goto-match-for-done)
+      (list '(+ sh-indent-for-do))))
 
 (defun sh-handle-prev-do ()
-  (let ( (p) )
-    (cond
-     ((save-restriction
-       (narrow-to-region
-        (point)
-        (save-excursion
-          (beginning-of-line)
-          (point)))
-       (sh-goto-match-for-done))
-      (sh-debug "match for done found on THIS line")
-      (list '(+ sh-indent-after-loop-construct)))
-     ((sh-goto-match-for-done)
-      (sh-debug "match for done found on PREV line")
-      (list '(+ sh-indent-after-do)))
-     (t
-      (message "match for done NOT found")
-      nil))))
+  (cond
+   ((save-restriction
+      (narrow-to-region
+       (point)
+       (save-excursion
+        (beginning-of-line)
+        (point)))
+      (sh-goto-match-for-done))
+    (sh-debug "match for done found on THIS line")
+    (list '(+ sh-indent-after-loop-construct)))
+   ((sh-goto-match-for-done)
+    (sh-debug "match for done found on PREV line")
+    (list '(+ sh-indent-after-do)))
+   (t
+    (message "match for done NOT found")
+    nil)))
 
 ;; for rc:
 (defun sh-find-prev-switch ()
   "Find the line for the switch keyword matching this line's case keyword."
-  (re-search-backward "\\bswitch\\b" nil t))
+  (re-search-backward "\\<switch\\>" nil t))
 
 (defun sh-handle-this-rc-case ()
   (if (sh-find-prev-switch)
       (list  '(+ sh-indent-after-switch))
-      ;; (list  '(+ sh-indent-for-case-label))
+    ;; (list  '(+ sh-indent-for-case-label))
     nil))
 
 (defun sh-handle-prev-rc-case ()
@@ -2006,56 +1985,50 @@ Return new point if successful,  nil if an error occurred."
   "Return indent-info for this line.
 This is a list.  nil means the line is to be left as is.
 Otherwise it contains one or more of the following sublists:
-\(t NUMBER\)   NUMBER is the base location in the buffer that indendation is
+\(t NUMBER\)   NUMBER is the base location in the buffer that indentation is
             relative to.  If present, this is always the first of the
             sublists.  The indentation of the line in question is
-            derived from the indentation of this point,  possibly
+            derived from the indentation of this point, possibly
             modified by subsequent sublists.
 \(+ VAR\)
 \(- VAR\)      Get the value of variable VAR and add to or subtract from
             the indentation calculated so far.
 \(= VAR\)           Get the value of variable VAR and *replace* the
-            indentation with itss value.  This only occurs for
+            indentation with its value.  This only occurs for
             special variables such as `sh-indent-comment'.
 STRING      This is ignored for the purposes of calculating
-            indentation,  it is printed in certain cases to help show
+            indentation, it is printed in certain cases to help show
             what the indentation is based on."
   ;; See comments before `sh-kw'.
   (save-excursion
-    (let ((prev-kw nil)
-         (prev-stmt nil)
-         (have-result nil)
-         depth-bol depth-eol
+    (let ((have-result nil)
          this-kw
-         (state nil)
-         state-bol
-         (depth-prev-bol nil)
          start
-         func val
+         val
          (result nil)
-         prev-lines-indent
-         (prev-list nil)
-         (this-list nil)
          (align-point nil)
          prev-line-end x)
       (beginning-of-line)
       ;; Note: setting result to t means we are done and will return nil.
-      ;;( This function never returns just t.)
+      ;;(This function never returns just t.)
       (cond
-       ((equal (get-text-property (point) 'syntax-table) sh-here-doc-syntax)
+       ((or (and (boundp 'font-lock-string-face) (not (bobp))
+                (eq (get-text-property (1- (point)) 'face)
+                    font-lock-string-face))
+           (eq (get-text-property (point) 'face) sh-heredoc-face))
        (setq result t)
        (setq have-result t))
        ((looking-at "\\s-*#")          ; was (equal this-kw "#")
        (if (bobp)
-           (setq result t);; return nil if 1st line!
+           (setq result t) ;; return nil if 1st line!
          (setq result (list '(= sh-indent-comment)))
          ;; we still need to get previous line in case
-         ;; sh-indent-comnent is t (indent as normal)
+         ;; sh-indent-comment is t (indent as normal)
          (setq align-point (sh-prev-line nil))
          (setq have-result nil)
          ))
-       );; cond
-      
+       ) ;; cond
+
       (unless have-result
        ;; Continuation lines are handled specially
        (if (sh-this-is-a-continuation)
@@ -2081,7 +2054,7 @@ STRING         This is ignored for the purposes of calculating
                  (setq result (append result val))
                  (setq have-result t)
                  ;; set prev-line to continue processing remainder
-                 ;; of this line as a previous l ine
+                 ;; of this line as a previous line
                  (setq prev-line-end (point))
                  ))))
 
@@ -2093,7 +2066,7 @@ STRING         This is ignored for the purposes of calculating
            ;; We start off at beginning of this line.
            ;; Scan previous statements while this is <=
            ;; start of previous line.
-           (setq start (point));; for debug only
+           (setq start (point)) ;; for debug only
            (goto-char prev-line-end)
            (setq x t)
            (while (and x (setq x  (sh-prev-thing)))
@@ -2101,18 +2074,19 @@ STRING       This is ignored for the purposes of calculating
              (cond
               ((and (equal x ")")
                     (equal (get-text-property (1- (point)) 'syntax-table)
-                           sh-special-syntax))
+                           sh-st-punc))
                (sh-debug "Case label) here")
                (setq x 'case-label)
                (if (setq val (sh-check-rule 2 x))
                    (progn
                      (setq result (append result val))
                      (setq align-point (point))))
-               (forward-char -1)
+               (or (bobp)
+                   (forward-char -1))
                (skip-chars-forward "[a-z0-9]*?")
                )
               ((string-match "[])}]" x)
-               (setq x (sh-safe-backward-sexp))
+               (setq x (sh-safe-forward-sexp -1))
                (if x
                    (progn
                      (setq align-point (point))
@@ -2128,7 +2102,7 @@ STRING         This is ignored for the purposes of calculating
               ((string-match "[\"'`]" x)
                (sh-debug "Skipping back for %s" x)
                ;; this was oops-2
-               (setq x (sh-safe-backward-sexp)))
+               (setq x (sh-safe-forward-sexp -1)))
               ((stringp x)
                (sh-debug "Checking string %s at %s" x (point))
                (if (setq val (sh-check-rule 2 x))
@@ -2142,30 +2116,30 @@ STRING       This is ignored for the purposes of calculating
               (t
                (error "Don't know what to do with %s" x))
               )
-             );; while
+             ) ;; while
            (sh-debug "result is %s" result)
            )
        (sh-debug "No prev line!")
        (sh-debug "result: %s  align-point: %s" result align-point)
        )
-      
+
       (if align-point
          ;; was: (setq result (append result (list (list t align-point))))
          (setq result (append  (list (list t align-point)) result))
        )
       (sh-debug "result is now: %s" result)
-       
+
       (or result
          (if prev-line-end
              (setq result (list (list t prev-line-end)))
            (setq result (list (list '= 'sh-first-lines-indent)))
            ))
-       
+
       (if (eq result t)
          (setq result nil))
       (sh-debug  "result is: %s" result)
       result
-      );; let
+      )        ;; let
     ))
 
 
@@ -2192,7 +2166,7 @@ If INFO is supplied it is used, else it is calculated."
          (error "sh-get-indent-var-for-line invalid elt: %s" elt))
         ;; so it is a list
         ((eq t (car elt))
-         );; nothing
+         ) ;; nothing
         ((symbolp  (setq sym (nth 1 elt)))
          ;; A bit of a kludge - when we see the sh-indent-comment
          ;; ignore other variables.  Otherwise it is tricky to
@@ -2220,87 +2194,46 @@ If INFO is supplied it is used, else it is calculated."
 
 ;; Finding the previous line isn't trivial.
 ;; We must *always* go back one more and see if that is a continuation
-;; line -- it is the PREVIOUS line which is continued,  not the one
+;; line -- it is the PREVIOUS line which is continued, not the one
 ;; we are going to!
 ;; Also, we want to treat a whole "here document" as one big line,
 ;; because we may want to a align to the beginning of it.
 ;;
 ;; What we do:
-;; - go back a line,  if empty repeat
-;; - (we are now at a previous non empty line)
-;; - save this
-;; - if this is in a here-document,  go to the beginning of it
-;;   and save that
-;; - go back one more physcial line and see if it is a continuation line
-;; - if yes,  save it and repeat
-;; - if no,  go back to where we last saved.
+;; - go back to previous non-empty line
+;; - if this is in a here-document, go to the beginning of it
+;; - while previous line is continued, go back one line
 (defun sh-prev-line (&optional end)
   "Back to end of previous non-comment non-empty line.
-Go to beginning of logical line unless END is non-nil,  in which case
+Go to beginning of logical line unless END is non-nil, in which case
 we go to the end of the previous line and do not check for continuations."
-  (sh-must-be-shell-mode)
-  (let ((going t)
-         (last-contin-line nil)
-         (result nil)
-         bol eol state)
-    (save-excursion
+  (save-excursion
+    (beginning-of-line)
+    (forward-comment (- (point-max)))
+    (unless end (beginning-of-line))
+    (when (and (not (bobp))
+              (equal (get-text-property (1- (point)) 'face)
+                     sh-heredoc-face))
+      (let ((p1 (previous-single-property-change (1- (point)) 'face)))
+       (when p1
+         (goto-char p1)
+         (if end
+             (end-of-line)
+           (beginning-of-line)))))
+    (unless end
+      ;; we must check previous lines to see if they are continuation lines
+      ;; if so, we must return position of first of them
+      (while (and (sh-this-is-a-continuation)
+                 (>= 0 (forward-line -1))))
       (beginning-of-line)
-      (while (and going
-                 (not (bobp))
-                 (>= 0  (forward-line -1))
-                 )
-       (setq bol (point))
-       (end-of-line)
-       (setq eol (point))
-       (save-restriction
-         (setq state (parse-partial-sexp bol eol nil nil nil t))
-         (if (nth 4 state)
-             (setq eol (nth 8 state)))
-         (narrow-to-region bol eol)
-         (goto-char bol)
-         (cond
-          ((looking-at "\\s-*$"))
-          (t
-           (if end
-               (setq result eol)
-             (setq result bol))
-           (setq going nil))
-          )))
-      (if (and result
-              (equal (get-text-property (1- result) 'syntax-table)
-                  sh-here-doc-syntax))
-         (let ((p1 (previous-single-property-change
-                    (1- result) 'syntax-table)))
-           (if p1
-               (progn
-                 (goto-char p1)
-                 (forward-line -1)
-                 (if end
-                     (end-of-line))
-                 (setq result (point)))
-             )))
-      (unless end
-       ;; we must check previous lines to see if they are continuation lines
-       ;; if so, we must return position of first of them
-       (while (and (sh-this-is-a-continuation)
-                   (>= 0  (forward-line -1)))
-         (setq result (point)))
-       (if result
-           (progn
-             (goto-char result)
-             (beginning-of-line)
-             (skip-chars-forward " \t")
-             (setq result (point))
-             )))
-      )  ;; save-excursion
-    result
-    ))
+      (skip-chars-forward " \t"))
+    (point)))
 
 
 (defun sh-prev-stmt ()
   "Return the address of the previous stmt or nil."
   ;; This is used when we are trying to find a matching keyword.
-  ;; Searching backward for the keyword would certainly be quicker,  but
+  ;; Searching backward for the keyword would certainly be quicker, but
   ;; it is hard to remove "false matches" -- such as if the keyword
   ;; appears in a string or quote.  This way is slower, but (I think) safer.
   (interactive)
@@ -2313,8 +2246,8 @@ we go to the end of the previous line and do not check for continuations."
       (while (and (not found)
                  (not (bobp))
                  going)
-       ;; Do a backward-sexp if possible,  else backup bit by bit...
-       (if (sh-safe-backward-sexp)
+       ;; Do a backward-sexp if possible, else backup bit by bit...
+       (if (sh-safe-forward-sexp -1)
            (progn
              (if (looking-at sh-special-keywords)
                  (progn
@@ -2347,8 +2280,7 @@ we go to the end of the previous line and do not check for continuations."
            (setq found nil))
        (or found
            (sh-debug "Did not find prev stmt.")))
-      found
-      )))
+      found)))
 
 
 (defun sh-get-word ()
@@ -2360,7 +2292,7 @@ we go to the end of the previous line and do not check for continuations."
        (if (looking-at "[\"'`]")
            (sh-safe-forward-sexp)
          ;; (> (skip-chars-forward "^ \t\n\"'`") 0)
-         (> (skip-chars-forward "-_a-zA-Z\$0-9") 0)
+         (> (skip-chars-forward "-_a-zA-Z$0-9") 0)
          ))
     (buffer-substring start (point))
     ))
@@ -2369,7 +2301,7 @@ we go to the end of the previous line and do not check for continuations."
   "Return the previous thing this logical line."
   ;; This is called when `sh-get-indent-info' is working backwards on
   ;; the previous line(s) finding what keywords may be relevant for
-  ;; indenting.  It moves over sexps if possible,  and will stop
+  ;; indenting.  It moves over sexps if possible, and will stop
   ;; on a ; and at the beginning of a line if it is not a continuation
   ;; line.
   ;;
@@ -2377,69 +2309,58 @@ we go to the end of the previous line and do not check for continuations."
   ;; Possible return values:
   ;;  nil  -  nothing
   ;; a string - possibly a keyword
-  ;; 
+  ;;
   (if (bolp)
       nil
-    (let ((going t)
-         c n
-         min-point
-         (start (point))
-         (found nil))
+    (let (c min-point
+         (start (point)))
       (save-restriction
        (narrow-to-region
-        (if (sh-this-is-a-continuation)
-            (setq min-point (sh-prev-line nil))
-          (save-excursion
-            (beginning-of-line)
-            (setq min-point (point))))
-        (point))
+       (if (sh-this-is-a-continuation)
+           (setq min-point (sh-prev-line nil))
+         (save-excursion
+           (beginning-of-line)
+           (setq min-point (point))))
+       (point))
        (skip-chars-backward " \t;")
        (unless (looking-at "\\s-*;;")
-         (skip-chars-backward "^)}];\"'`({[")
-         (setq c (char-before))))
+       (skip-chars-backward "^)}];\"'`({[")
+       (setq c (char-before))))
       (sh-debug "stopping at %d c is %s  start=%d min-point=%d"
-                (point) c start min-point)
+               (point) c start min-point)
       (if (< (point) min-point)
          (error "point %d < min-point %d" (point) min-point))
       (cond
        ((looking-at "\\s-*;;")
        ;; (message "Found ;; !")
-       ";;")
+       ";;")
        ((or (eq c ?\n)
            (eq c nil)
            (eq c ?\;))
-       (save-excursion
-         ;; skip forward over white space newline and \ at eol
-         (skip-chars-forward " \t\n\\\\")
-         (sh-debug "Now at %d   start=%d" (point) start)
-         (if (>= (point) start)
-             (progn
-               (sh-debug "point: %d >= start: %d" (point) start)
-               nil)
-           (sh-get-word))
-         ))
+       (save-excursion
+        ;; skip forward over white space newline and \ at eol
+        (skip-chars-forward " \t\n\\\\")
+        (sh-debug "Now at %d   start=%d" (point) start)
+        (if (>= (point) start)
+            (progn
+              (sh-debug "point: %d >= start: %d" (point) start)
+              nil)
+          (sh-get-word))
+        ))
        (t
        ;; c    -- return a string
-       (char-to-string c)
-       ))
+       (char-to-string c)
+       ))
       )))
 
 
 (defun sh-this-is-a-continuation ()
   "Return non-nil if current line is a continuation of previous line."
-  (let ((result nil)
-       bol eol state)
-    (save-excursion
-      (if (and (zerop (forward-line -1))
-              (looking-at ".*\\\\$"))
-         (progn
-           (setq bol (point))
-           (end-of-line)
-           (setq eol (point))
-           (setq state (parse-partial-sexp bol eol nil nil nil t))
-           (unless (nth 4 state)
-             (setq result t))
-           )))))
+  (save-excursion
+    (and (zerop (forward-line -1))
+        (looking-at ".*\\\\$")
+        (not (nth 4 (parse-partial-sexp (match-beginning 0) (match-end 0)
+                                        nil nil nil t))))))
 
 (defun sh-get-kw (&optional where and-move)
   "Return first word of line from WHERE.
@@ -2449,10 +2370,9 @@ If AND-MOVE is non-nil then move to end of word."
        (goto-char where))
     (prog1
        (buffer-substring (point)
-       (progn (skip-chars-forward "^ \t\n;")(point)))
+                         (progn (skip-chars-forward "^ \t\n;")(point)))
       (unless and-move
-       (goto-char start)))
-    ))
+       (goto-char start)))))
 
 (defun sh-find-prev-matching (open close &optional depth)
   "Find a matching token for a set of opening and closing keywords.
@@ -2483,7 +2403,7 @@ Optional parameter DEPTH (usually 1) says how many to look for."
                (sh-debug "found close - depth = %d" depth))
               (t
                ))))
-               (error nil))
+       (error nil))
       (if (eq depth 0)
          prev ;; (point)
        nil)
@@ -2493,7 +2413,7 @@ Optional parameter DEPTH (usually 1) says how many to look for."
 (defun sh-var-value (var &optional ignore-error)
   "Return the value of variable VAR, interpreting symbols.
 It can also return t or nil.
-If an illegal value is found,  throw an error unless Optional argument
+If an invalid value is found, throw an error unless Optional argument
 IGNORE-ERROR is non-nil."
   (let ((val (symbol-value var)))
     (cond
@@ -2517,15 +2437,15 @@ IGNORE-ERROR is non-nil."
       (/ (- sh-basic-offset) 2))
      (t
       (if ignore-error
-         (progn
-           (message "Don't konw how to handle %s's value of %s" var val)
-           0)
-       (error "Don't know how to handle %s's value of %s" var val))
+      (progn
+       (message "Don't know how to handle %s's value of %s" var val)
+       0)
+      (error "Don't know how to handle %s's value of %s" var val))
       ))))
 
 (defun sh-set-var-value (var value &optional no-symbol)
   "Set variable VAR to VALUE.
-Unless optional argument NO-SYMBOL is non-nil,  then if VALUE is
+Unless optional argument NO-SYMBOL is non-nil, then if VALUE is
 can be represented by a symbol then do so."
   (cond
    (no-symbol
@@ -2550,21 +2470,17 @@ can be represented by a symbol then do so."
 (defun sh-calculate-indent (&optional info)
   "Return the indentation for the current line.
 If INFO is supplied it is used, else it is calculated from current line."
-  (let (
-       (ofs 0)
+  (let ((ofs 0)
        (base-value 0)
        elt a b var val)
     (or info
        (setq info (sh-get-indent-info)))
-    (if (null info)
-       nil
+    (when info
       (while info
        (sh-debug "info: %s  ofs=%s" info ofs)
        (setq elt (car info))
        (cond
-        ((stringp elt)
-         ;; do nothing?
-         )
+        ((stringp elt)) ;; do nothing?
         ((listp elt)
          (setq a (car (car info)))
          (setq b (nth 1 (car info)))
@@ -2583,31 +2499,22 @@ If INFO is supplied it is used, else it is calculated from current line."
                ;; no indentation
                ;; set info to nil so  we stop immediately
                (setq base-value nil  ofs nil  info nil))
-              ((eq val t)
-               ;; indent as normal line
-               (setq ofs 0))
+              ((eq val t) (setq ofs 0)) ;; indent as normal line
               (t
                ;; The following assume the (t POS) come first!
                (setq ofs val  base-value 0)
-               (setq info nil) ;; ? stop now
-               ))
-             )
-            ((eq a '+)
-             (setq ofs (+ ofs val)))
-            ((eq a '-)
-             (setq ofs (- ofs val)))
+               (setq info nil))))      ;; ? stop now
+            ((eq a '+) (setq ofs (+ ofs val)))
+            ((eq a '-) (setq ofs (- ofs val)))
             (t
              (error "sh-calculate-indent invalid a a=%s b=%s" a b))))
           (t
-           (error "sh-calculate-indent invalid elt: a=%s b=%s" a b)))
-         )
+           (error "sh-calculate-indent invalid elt: a=%s b=%s" a b))))
         (t
-         (error "sh-calculate-indent invalid elt %s" elt))
-        )
-        (sh-debug "a=%s b=%s val=%s base-value=%s ofs=%s"
-                   a b val base-value ofs)
-        (setq info (cdr info))
-        )
+         (error "sh-calculate-indent invalid elt %s" elt)))
+       (sh-debug "a=%s b=%s val=%s base-value=%s ofs=%s"
+                 a b val base-value ofs)
+       (setq info (cdr info)))
       ;; return value:
       (sh-debug "at end:  base-value: %s    ofs: %s" base-value ofs)
 
@@ -2616,36 +2523,27 @@ If INFO is supplied it is used, else it is calculated from current line."
        nil)
        ((and (numberp base-value)(numberp ofs))
        (sh-debug "base (%d) + ofs (%d) = %d"
-                  base-value ofs (+ base-value ofs))
+                 base-value ofs (+ base-value ofs))
        (+ base-value ofs)) ;; return value
        (t
        (error "sh-calculate-indent:  Help.  base-value=%s ofs=%s"
               base-value ofs)
-       nil))
-      )))
+       nil)))))
 
 
 (defun sh-indent-line ()
   "Indent the current line."
   (interactive)
-  (sh-must-be-shell-mode)
-  (let ((indent (sh-calculate-indent)) shift-amt beg end
+  (let ((indent (sh-calculate-indent))
        (pos (- (point-max) (point))))
-    (if indent
-      (progn
-       (beginning-of-line)
-       (setq beg (point))
-       (skip-chars-forward " \t")
-       (setq shift-amt (- indent (current-column)))
-       (if (zerop shift-amt)
-           nil
-         (delete-region beg (point))
-         (indent-to indent))
-       ;; If initial point was within line's indentation,
-       ;; position after the indentation.  Else stay at same point in text.
-       (if (> (- (point-max) pos) (point))
-         (goto-char (- (point-max) pos)))
-       ))))
+    (when indent
+      (beginning-of-line)
+      (skip-chars-forward " \t")
+      (indent-line-to indent)
+      ;; If initial point was within line's indentation,
+      ;; position after the indentation.  Else stay at same point in text.
+      (if (> (- (point-max) pos) (point))
+         (goto-char (- (point-max) pos))))))
 
 
 (defun sh-blink (blinkpos &optional msg)
@@ -2656,8 +2554,7 @@ If INFO is supplied it is used, else it is calculated from current line."
        (goto-char blinkpos)
        (message msg)
        (sit-for blink-matching-delay))
-    (message msg)
-    ))
+    (message msg)))
 
 (defun sh-show-indent (arg)
   "Show the how the currently line would be indented.
@@ -2671,9 +2568,8 @@ we are indenting relative to, if applicable."
   (sh-must-support-indent)
   (let* ((info (sh-get-indent-info))
         (var (sh-get-indent-var-for-line info))
-       val msg
-       (curr-indent (current-indentation))
-       )
+        (curr-indent (current-indentation))
+        val msg)
     (if (stringp var)
        (message (setq msg var))
       (setq val (sh-calculate-indent info))
@@ -2706,7 +2602,7 @@ for a new value for it."
   (sh-must-support-indent)
   (let* ((info (sh-get-indent-info))
         (var (sh-get-indent-var-for-line info))
-        val val0 new-val old-val indent-val)
+        val old-val indent-val)
     (if (stringp var)
        (message (format "Cannot set indent - %s" var))
       (setq old-val (symbol-value var))
@@ -2720,14 +2616,14 @@ for a new value for it."
                         var (symbol-value var) indent-val)
              (message "Variable: %s  Value: %s  would leave line as is."
                       var (symbol-value var)))
-           ;; I'm not sure about this,  indenting it now?
+           ;; I'm not sure about this, indenting it now?
            ;; No.  Because it would give the impression that an undo would
-           ;; restore thing,  but the value has been altered.
+           ;; restore thing, but the value has been altered.
            ;; (sh-indent-line)
            )
        (error
         (set var old-val)
-        (message "Bad value for %s,  restoring to previous value %s"
+        (message "Bad value for %s, restoring to previous value %s"
                  var old-val)
         (sit-for 1)
         nil))
@@ -2757,79 +2653,73 @@ unless optional argument ARG (the prefix when interactive) is non-nil."
           ival sval diff new-val
           (no-symbol arg)
           (curr-indent (current-indentation)))
-    (cond
-     ((stringp var)
-      (message (format "Cannot learn line - %s" var)))
-     ((eq var 'sh-indent-comment)
-      ;; This is arbitrary...
-      ;; - if curr-indent is 0,  set to curr-indent
-      ;; - else if it has the indentation of a "normal" line,
-      ;;   then set to t
-      ;; - else set to curr-indent.
-      (setq sh-indent-comment
-           (if (= curr-indent 0)
-               0
-             (let* ((sh-indent-comment t)
-                    (val2 (sh-calculate-indent info)))
-               (if (= val2 curr-indent)
-                   t
-                 curr-indent))))
-      (message "%s set to %s" var (symbol-value var))
-      )
-     ((numberp (setq sval (sh-var-value var)))
-      (setq ival (sh-calculate-indent info))
-      (setq diff (- curr-indent ival))
-      
-      (sh-debug "curr-indent: %d   ival: %d  diff: %d  var:%s  sval %s"
-              curr-indent ival diff  var sval)
-      (setq new-val (+ sval diff))
+      (cond
+       ((stringp var)
+       (message (format "Cannot learn line - %s" var)))
+       ((eq var 'sh-indent-comment)
+       ;; This is arbitrary...
+       ;; - if curr-indent is 0, set to curr-indent
+       ;; - else if it has the indentation of a "normal" line,
+       ;;   then set to t
+       ;; - else set to curr-indent.
+       (setq sh-indent-comment
+             (if (= curr-indent 0)
+                 0
+               (let* ((sh-indent-comment t)
+                      (val2 (sh-calculate-indent info)))
+                 (if (= val2 curr-indent)
+                     t
+                   curr-indent))))
+       (message "%s set to %s" var (symbol-value var))
+       )
+       ((numberp (setq sval (sh-var-value var)))
+       (setq ival (sh-calculate-indent info))
+       (setq diff (- curr-indent ival))
+
+       (sh-debug "curr-indent: %d   ival: %d  diff: %d  var:%s  sval %s"
+                 curr-indent ival diff  var sval)
+       (setq new-val (+ sval diff))
 ;;;      I commented out this because someone might want to replace
 ;;;      a value of `+' with the current value of sh-basic-offset
 ;;;      or vice-versa.
 ;;;      (if (= 0 diff)
 ;;;          (message "No change needed!")
-      (sh-set-var-value var new-val no-symbol)
-      (message "%s set to %s" var (symbol-value var))
-      )
-     (t
-      (debug)
-      (message "Cannot change %s" var))
-     ))))
+       (sh-set-var-value var new-val no-symbol)
+       (message "%s set to %s" var (symbol-value var))
+       )
+       (t
+       (debug)
+       (message "Cannot change %s" var))))))
 
 
 
 (defun sh-mark-init (buffer)
   "Initialize a BUFFER to be used by `sh-mark-line'."
-  (let ((main-buffer (current-buffer)))
-    (save-excursion
-      (set-buffer (get-buffer-create buffer))
-      (erase-buffer)
-      (occur-mode)
-      (setq occur-buffer main-buffer)
-      )))
+  (save-excursion
+    (set-buffer (get-buffer-create buffer))
+    (erase-buffer)
+    (occur-mode)
+    ))
 
 
 (defun sh-mark-line (message point buffer &optional add-linenum occur-point)
   "Insert MESSAGE referring to location POINT in current buffer into BUFFER.
 Buffer BUFFER is in `occur-mode'.
 If ADD-LINENUM is non-nil the message is preceded by the line number.
-If OCCUR-POINT is non-nil then the line is marked as a new occurence
+If OCCUR-POINT is non-nil then the line is marked as a new occurrence
 so that `occur-next' and `occur-prev' will work."
   (let ((m1 (make-marker))
-       (main-buffer (current-buffer))
        start
-       (line "") )
-    (if point
-       (progn
-         (set-marker m1 point (current-buffer))
-         (if add-linenum
-             (setq line (format "%d: " (1+ (count-lines 1 point)))))))
+       (line ""))
+    (when point
+      (set-marker m1 point (current-buffer))
+      (if add-linenum
+         (setq line (format "%d: " (1+ (count-lines 1 point))))))
     (save-excursion
       (if (get-buffer buffer)
          (set-buffer (get-buffer buffer))
        (set-buffer (get-buffer-create buffer))
        (occur-mode)
-       (setq occur-buffer main-buffer)
        )
       (goto-char (point-max))
       (setq start (point))
@@ -2838,14 +2728,17 @@ so that `occur-next' and `occur-prev' will work."
          (setq occur-point (point)))
       (insert message)
       (if point
-         (put-text-property start (point) 'mouse-face 'highlight))
+         (add-text-properties
+          start (point)
+          '(mouse-face highlight
+            help-echo "mouse-2: go to the line where I learned this")))
       (insert "\n")
       (if point
          (progn
-           (put-text-property start (point) 'occur m1)
+           (put-text-property start (point) 'occur-target m1)
            (if occur-point
-               (put-text-property occur-point (1+ occur-point)
-                                  'occur-point t))
+               (put-text-property start occur-point
+                                  'occur-match t))
            ))
       )))
 
@@ -2853,9 +2746,9 @@ so that `occur-next' and `occur-prev' will work."
 
 ;; Is this really worth having?
 (defvar sh-learned-buffer-hook nil
-  "*An abnormal hook,  called with an alist of leared variables.")
-;;; Example of how to use sh-learned-buffer-hook
-;; 
+  "*An abnormal hook, called with an alist of learned variables.")
+;; Example of how to use sh-learned-buffer-hook
+;;
 ;; (defun what-i-learned (list)
 ;;   (let ((p list))
 ;;     (save-excursion
@@ -2868,32 +2761,32 @@ so that `occur-next' and `occur-prev' will work."
 ;;     (setq p (cdr p)))
 ;;       (insert ")\n")
 ;;       )))
-;; 
+;;
 ;; (add-hook 'sh-learned-buffer-hook 'what-i-learned)
 
 
 ;; Originally this was sh-learn-region-indent (beg end)
-;; However, in practise this was awkward so I changed it to
+;; However, in practice this was awkward so I changed it to
 ;; use the whole buffer.  Use narrowing if needbe.
 (defun sh-learn-buffer-indent (&optional arg)
   "Learn how to indent the buffer the way it currently is.
 
 Output in buffer \"*indent*\" shows any lines which have conflicting
-values of a variable, and the final value of all variables learnt.
-This buffer is popped to automatically if there are any discrepencies.
+values of a variable, and the final value of all variables learned.
+This buffer is popped to automatically if there are any discrepancies.
 
-If no prefix ARG is given,  then variables are set to numbers.
-If a prefix arg is given,  then variables are set to symbols when
+If no prefix ARG is given, then variables are set to numbers.
+If a prefix arg is given, then variables are set to symbols when
 applicable -- e.g. to symbol `+' if the value is that of the
 basic indent.
 If a positive numerical prefix is given, then  `sh-basic-offset'
 is set to the prefix's numerical value.
-Otherwise,  sh-basic-offset may or may not be changed,  according
+Otherwise, sh-basic-offset may or may not be changed, according
 to the value of variable `sh-learn-basic-offset'.
 
 Abnormal hook `sh-learned-buffer-hook' if non-nil is called when the
 function completes.  The function is abnormal because it is called
-with an alist of variables learnt.  This feature may be changed or
+with an alist of variables learned.  This feature may be changed or
 removed in the future.
 
 This command can often take a long time to run."
@@ -2904,12 +2797,11 @@ This command can often take a long time to run."
     (let ((learned-var-list nil)
          (out-buffer "*indent*")
          (num-diffs 0)
-         last-pos
          previous-set-info
          (max 17)
          vec
          msg
-         (comment-col nil) ;; number if all same,  t if seen diff values
+         (comment-col nil) ;; number if all same, t if seen diff values
          (comments-always-default t) ;; nil if we see one not default
          initial-msg
          (specified-basic-offset (and arg (numberp arg)
@@ -2931,9 +2823,9 @@ This command can often take a long time to run."
 
       (while (< (point) (point-max))
        (setq linenum (1+ linenum))
-;;     (if (zerop (% linenum 10))
-           (message "line %d" linenum)
-;;       )
+       ;; (if (zerop (% linenum 10))
+       (message "line %d" linenum)
+       ;; )
        (unless (looking-at "\\s-*$") ;; ignore empty lines!
          (let* ((sh-indent-comment t) ;; info must return default indent
                 (info (sh-get-indent-info))
@@ -2951,10 +2843,10 @@ This command can often take a long time to run."
              (setq diff (- curr-indent ival))
              (setq new-val (+ sval diff))
              (sh-set-var-value var new-val 'no-symbol)
-             (unless (looking-at "\\s-*#");; don't learn from comments
+             (unless (looking-at "\\s-*#") ;; don't learn from comments
                (if (setq previous-set-info (assoc var learned-var-list))
                    (progn
-                     ;; it was already there,  is it same value ?
+                     ;; it was already there, is it same value ?
                      (unless (eq (symbol-value var)
                                  (nth 1 previous-set-info))
                        (sh-mark-line
@@ -2988,16 +2880,16 @@ This command can often take a long time to run."
              (unless (= curr-indent (sh-calculate-indent info))
                ;; this is not the default indentation
                (setq comments-always-default nil)
-               (if comment-col;; then we have see one before
+               (if comment-col ;; then we have see one before
                    (or (eq comment-col curr-indent)
-                       (setq comment-col t));; seen a different one
+                       (setq comment-col t)) ;; seen a different one
                  (setq comment-col curr-indent))
-                   ))
-             (t
+               ))
+            (t
              (sh-debug "Cannot learn this line!!!")
              ))
            (sh-debug
-               "at %s learned-var-list is %s" (point) learned-var-list)
+            "at %s learned-var-list is %s" (point) learned-var-list)
            ))
        (forward-line 1)
        ) ;; while
@@ -3017,7 +2909,7 @@ This command can often take a long time to run."
        ((numberp comment-col)
        (setq msg  (format "\nComments are in col %d." comment-col)))
        (t
-       (setq msg  "\nComments seem to be mixed,  leaving them as is.\n")
+       (setq msg  "\nComments seem to be mixed, leaving them as is.\n")
        (setq comment-col nil)
        ))
       (sh-debug msg)
@@ -3060,57 +2952,38 @@ This command can often take a long time to run."
                 (format "Suggested sh-basic-offset:  %d" suggested))
               nil out-buffer))))
 
-      
+
       (setq learned-var-list
            (append (list (list 'sh-indent-comment comment-col (point-max)))
-                               learned-var-list))
+                   learned-var-list))
       (setq sh-indent-comment comment-col)
-      (let ((name (buffer-name))
-               (lines (if (and (eq (point-min) 1)
-                               (eq (point-max) (1+ (buffer-size))))
-                          ""
-                        (format "lines %d to %d of "
-                                (1+ (count-lines 1 (point-min)))
-                                (1+ (count-lines 1 (point-max))))))
-               )
+      (let ((name (buffer-name)))
        (sh-mark-line  "\nLearned variable settings:" nil out-buffer)
        (if arg
            ;; Set learned variables to symbolic rather than numeric
            ;; values where possible.
-           (progn
-             (let ((p (reverse learned-var-list))
-                   var val)
-               (while p
-                 (setq var (car (car p)))
-                 (setq val (nth 1 (car p)))
-                 (cond
-                  ((eq var 'sh-basic-offset)
-                   )
-                 ((numberp val)
-                  (sh-set-var-value var val))
-                 (t
-                  ))
-                 (setq p (cdr p))
-                 ))))
-       (let ((p (reverse learned-var-list))
-             var)
-         (while p
-           (setq var (car (car p)))
+           (dolist (learned-var (reverse learned-var-list))
+             (let ((var (car learned-var))
+                   (val (nth 1 learned-var)))
+               (when (and (not (eq var 'sh-basic-offset))
+                          (numberp val))
+                 (sh-set-var-value var val)))))
+       (dolist (learned-var (reverse learned-var-list))
+         (let ((var (car learned-var)))
            (sh-mark-line (format "  %s %s" var (symbol-value var))
-                          (nth 2 (car p)) out-buffer)
-           (setq p (cdr p))))
+                         (nth 2 learned-var) out-buffer)))
        (save-excursion
-             (set-buffer out-buffer)
-             (goto-char (point-min))
-             (insert
-              (format "Indentation values for buffer %s.\n" name)
-              (format "%d indentation variable%s different values%s\n\n"
-                      num-diffs
-                      (if (= num-diffs 1)
-                          " has"   "s have")
-                      (if (zerop num-diffs)
-                          "." ":"))
-              )))
+         (set-buffer out-buffer)
+         (goto-char (point-min))
+         (insert
+          (format "Indentation values for buffer %s.\n" name)
+          (format "%d indentation variable%s different values%s\n\n"
+                  num-diffs
+                  (if (= num-diffs 1)
+                      " has"   "s have")
+                  (if (zerop num-diffs)
+                      "." ":"))
+          )))
       ;; Are abnormal hooks considered bad form?
       (run-hook-with-args 'sh-learned-buffer-hook learned-var-list)
       (if (or sh-popup-occur-buffer (> num-diffs 0))
@@ -3118,29 +2991,26 @@ This command can often take a long time to run."
       )))
 
 (defun sh-guess-basic-offset (vec)
-  "See if we can determine a reasonbable value for `sh-basic-offset'.
+  "See if we can determine a reasonable value for `sh-basic-offset'.
 This is experimental, heuristic and arbitrary!
 Argument VEC is a vector of information collected by
 `sh-learn-buffer-indent'.
 Return values:
   number          - there appears to be a good single value
-  list of numbers - no obvious one,  here is a list of one or more
+  list of numbers - no obvious one, here is a list of one or more
                    reasonable choices
   nil            - we couldn't find a reasonable one."
   (let* ((max (1- (length vec)))
-       (i 1)
-       (totals (make-vector max 0))
-       (return nil)
-       j)
+        (i 1)
+        (totals (make-vector max 0)))
     (while (< i max)
       (aset totals i (+ (aref totals i) (* 4 (aref vec i))))
-      (setq j (/ i 2))
       (if (zerop (% i 2))
          (aset totals i (+ (aref totals i) (aref vec (/ i 2)))))
       (if (< (* i 2) max)
          (aset totals i (+ (aref totals i) (aref vec (* i 2)))))
-      (setq i (1+ i))
-      )
+      (setq i (1+ i)))
+
     (let ((x nil)
          (result nil)
          tot sum p)
@@ -3150,17 +3020,16 @@ Return values:
            (setq x (append x (list (cons i (aref totals i))))))
        (setq i (1+ i)))
 
-      (setq x (sort x '(lambda (a b)
-                        (> (cdr a)(cdr b)))))
+      (setq x (sort x (lambda (a b) (> (cdr a) (cdr b)))))
       (setq tot (apply '+ (append totals nil)))
       (sh-debug (format "vec: %s\ntotals: %s\ntot: %d"
-                        vec totals tot))
+                       vec totals tot))
       (cond
        ((zerop (length x))
        (message "no values!")) ;; we return nil
        ((= (length x) 1)
        (message "only value is %d" (car (car x)))
-       (setq result (car (car x))))    ;; return single value
+       (setq result (car (car x)))) ;; return single value
        ((> (cdr (car x)) (/ tot 2))
        ;; 1st is > 50%
        (message "basic-offset is probably %d" (car (car x)))
@@ -3183,332 +3052,38 @@ Return values:
                 (car (car x)))
        ;; result is nil here
        ))
-      result
-      )))
-
-
-(defun sh-do-nothing (a b c)
-  ;; checkdoc-params: (a b c)
-  "A dummy function to prevent font-lock from re-fontifying a change.
-Otherwise,  we fontify something and font-lock overwrites it."
-  )
-
-;; The default font-lock-unfontify-region-function removes 
-;; syntax-table properties,  and so removes our information.
-(defun sh-font-lock-unfontify-region-function (beg end)
-  (let* ((modified (buffer-modified-p)) (buffer-undo-list t)
-        (inhibit-read-only t) (inhibit-point-motion-hooks t)
-        before-change-functions after-change-functions
-        deactivate-mark buffer-file-name buffer-file-truename)
-    (remove-text-properties beg end '(face nil))
-    (when (and (not modified) (buffer-modified-p))
-      (set-buffer-modified-p nil))))
-
-(defun sh-set-char-syntax (where new-prop)
-  "Set the character's syntax table property at WHERE to be NEW-PROP."
-  (or where
-      (setq where (point)))
-  (let ((font-lock-fontify-region-function 'sh-do-nothing))
-    (put-text-property where (1+ where) 'syntax-table new-prop)
-    (add-text-properties where (1+ where)
-                        '(face sh-st-face rear-nonsticky t))
-    ))
-
-
-(defun sh-check-paren-in-case ()
-  "Make syntax class of case label's right parenthesis not close parenthesis.
-If this parenthesis is a case alternative,  set its syntax class to a word."
-  (let ((start (point))
-       state prev-line)
-    ;; First test if this is a possible candidate,  the first "(" or ")"
-    ;; on the line;  then, if go, check prev line is ;; or case.
-    (save-excursion
-      (beginning-of-line)
-      ;; stop at comment or when depth becomes -1
-      (setq state (parse-partial-sexp (point) start -1 nil nil t))
-      (if (and
-          (= (car state) -1)
-          (= (point) start)
-          (setq prev-line (sh-prev-line nil)))
-         (progn
-           (goto-char prev-line)
-           (beginning-of-line)
-           ;; (setq case-stmt-start (point))
-           ;; (if (looking-at "\\(^\\s-*case[^-a-z0-9_]\\|[^#]*;;\\s-*$\\)")
-           (if (sh-search-word "\\(case\\|;;\\)" start)
-               (sh-set-char-syntax (1- start) sh-special-syntax)
-             ))))))
-
-(defun sh-electric-rparen ()
-  "Insert a right parethese,  and check if it is a case alternative.
-If so, its syntax class is set to word, and its text proerty
-is set to have face `sh-st-face'."
-  (interactive)
-  (insert ")")
-  (if sh-electric-rparen-needed-here
-      (sh-check-paren-in-case)))
-
-(defun sh-electric-hash ()
-  "Insert a hash, but check it is preceded by \"$\".
-If so, it is given a syntax type of comment.
-Its text proerty has face `sh-st-face'."
-  (interactive)
-  (let ((pos (point)))
-    (insert "#")
-    (if (eq (char-before pos) ?$)
-      (sh-set-char-syntax pos sh-st-punc))))
-
-(defun sh-electric-less (arg)
-  "Insert a \"<\" and see if this is the start of a here-document.
-If so, the syntax class is set so that it will not be automatically
-reindented.
-Argument ARG if non-nil disables this test."
-  (interactive "*P")
-  (let ((p1 (point)) p2 p3)
-    (sh-maybe-here-document arg) ;; call the original fn in sh-script.el.
-    (setq p2 (point))
-    (if (/= (+ p1 (prefix-numeric-value arg)) p2)
-       (save-excursion
-         (forward-line 1)
-         (end-of-line)
-         (setq p3 (point))
-         (sh-set-here-doc-region p2 p3))
-      )))
-
-(defun sh-set-here-doc-region (start end)
-  "Mark a here-document from START to END so that it will not be reindented."
-  (interactive "r")
-  ;; Make the whole thing have syntax type word...
-  ;; That way sexp movement doens't worry about any parentheses.
-  ;; A disadvantage of this is we can't use forward-word within a
-  ;; here-doc, which is annoying.
-  (let ((font-lock-fontify-region-function 'sh-do-nothing))
-    (put-text-property start end 'syntax-table sh-here-doc-syntax)
-    (put-text-property start end 'face 'sh-heredoc-face)
-    (put-text-property (1- end) end  'rear-nonsticky t)
-    (put-text-property start (1+ start)  'front-sticky t)
-    ))
-
-(defun sh-remove-our-text-properties ()
-  "Remove text properties relating to right parentheses and here documents."
-  (interactive)
-  (save-excursion
-    (goto-char (point-min))
-    (while (not (eobp))
-      (let ((plist (text-properties-at (point)))
-           (next-change
-            (or (next-single-property-change (point) 'syntax-table
-                                             (current-buffer) )
-                (point-max))))
-       ;; Process text from point to NEXT-CHANGE...
-       (if (get-text-property (point) 'syntax-table)
-           (progn
-             (sh-debug "-- removing props from %d to %d --"
-                        (point) next-change)
-             (remove-text-properties (point) next-change
-                                     '(syntax-table nil))
-             (remove-text-properties (point) next-change '(face nil))
-             ))
-       (goto-char next-change)))
-    ))
-
-(defun sh-search-word (word &optional limit)
-  "Search forward for regexp WORD occuring as a word not in string nor comment.
-If found, returns non nil with the match available in  \(match-string 2\).
-Yes 2, not 1, since we build a regexp to guard against false matches
-such as matching \"a-case\" when we are searching for \"case\".
-If not found, it returns nil.
-The search maybe limited by optional argument LIMIT."
-  (interactive "sSearch for: ")
-  (let ((found nil)
-       ;; Cannot use \\b here since it matches "-" and "_"
-       (regexp (sh-mkword-regexp word))
-       start state where)
-    (setq start (point))
-    (while (and (setq start (point))
-               (not found)
-               (re-search-forward regexp limit t))
-      ;; Found str;  check it is not in a comment or string.
-      (setq state
-           ;; Stop on comment:
-           (parse-partial-sexp start (point) nil nil nil 'syntax_table))
-      (if (setq where (nth 8 state))
-         ;; in comment or string
-         (if (= where -1)
-             (setq found (point))
-           (if (eq (char-after where) ?#)
-               (end-of-line)
-             (goto-char where)
-             (unless (sh-safe-forward-sexp)
-               ;; If the above fails we must either give up or
-               ;; move forward and try again.
-               (forward-line 1))
-             ))
-       ;; not in comment or string, so accept it
-       (setq found (point))
-       ))
-    found
-    ))
-
-(defun sh-scan-case ()
-  "Scan a case statement for right parens belonging to case alternatives.
-Mark each as having syntax `sh-special-syntax'.
-Called from scan-buff.  If ok, return non-nil."
-  (let (end
-       state
-       (depth 1) ;; we are called at a "case"
-       (start (point))
-       (return t))
-    ;; We enter here at a case statement
-    ;; First, find limits of the case.
-    (while (and (> depth 0)
-               (sh-search-word "\\(case\\|esac\\)"))
-      (if (equal (match-string 2) "case")
-         (setq depth (1+ depth))
-       (setq depth (1- depth))))
-    ;; (message "end of search for esac  at %d depth=%d" (point) depth)
-    (setq end (point))
-    (goto-char start)
-    ;; if we found the esac,  then fix all appropriate ')'s in the region
-    (if (zerop depth)
-       (progn
-         (while (< (point) end)
-           ;; search for targetdepth of -1 meaning extra right paren
-           (setq state (parse-partial-sexp (point) end -1 nil nil nil))
-           (if (and (= (car state) -1)
-                    (= (char-before) ?\)))
-               (progn
-                 ;; (message "At %d  state is %s" (point) state)
-                 ;; (message "Fixing %d" (point))
-                 (sh-set-char-syntax (1- (point)) sh-special-syntax)
-                 ;; we could advance to the next ";;" perhaps
-                 )
-             ;; (message "? Not found at %d" (point)) ; ok, could be "]"
-             ))
-         (goto-char end))
-      (message "No matching esac for case at %d" start)
-      (setq return nil)
-      )
-    return
-    ))
-
-
-(defun sh-scan-buffer ()
-  "Scan a sh buffer for case statements and here-documents.
-
-For each case alternative found, mark its \")\" with a text property
-so that its syntax class is no longer a close parenthesis character.
-
-Each here-document is also marked so that it is effectively immune
-from indenation changes."
-  ;; Do not call this interactively, call `sh-rescan-buffer' instead.
-  (sh-must-be-shell-mode)
-  (let ((n 0)
-       (initial-buffer-modified-p (buffer-modified-p))
-       start end where label ws)
-      (save-excursion
-       (goto-char (point-min))
-       ;; 1. Scan for ")" in case statements.
-       (while (and ;; (re-search-forward "^[^#]*\\bcase\\b" nil t)
-               (sh-search-word "\\(case\\|esac\\)")
-               ;; (progn (message "Found a case at %d" (point)) t)
-               (sh-scan-case)))
-       ;; 2. Scan for here docs
-       (goto-char (point-min))
-       ;;  while (re-search-forward "<<\\(-?\\)\\(\\s-*\\)\\(.*\\)$" nil t)
-       (while (re-search-forward "<<\\(-?\\)" nil t)
-         (unless (sh-in-comment-or-string (match-beginning 0))
-           ;; (setq label (match-string 3))
-           (setq label (sh-get-word))
-           (if (string= (match-string 1) "-")
-               ;; if <<- then we allow whitespace
-               (setq ws "\\s-*")
-             ;; otherwise we don't
-             (setq ws ""))
-           (while (string-match "['\"\\]" label)
-             (setq label (replace-match "" nil nil label)))
-           (if (setq n (string-match "\\s-+$" label))
-               (setq label (substring label 0 n)))
-           (forward-line 1)
-           ;; the line containing the << could be continued...
-           (while (sh-this-is-a-continuation)
-             (forward-line 1))
-           (setq start (point))
-           (if (re-search-forward (concat "^" ws (regexp-quote label)
-                                          "\\s-*$")
-                                  nil t)
-               (sh-set-here-doc-region start (point))
-             (sh-debug "missing here-doc delimiter `%s'" label))))
-       ;; 3. Scan for $# -- make the "#" a punctuation not a comment
-       (goto-char (point-min))
-       (let (state)
-         (while (and (not (eobp))
-                     (setq state (parse-partial-sexp
-                                  (1+ (point))(point-max) nil nil nil t))
-                     (nth 4 state))
-           (goto-char (nth 8 state))
-           (sh-debug "At %d  %s" (point) (eq (char-before) ?$))
-           (if (eq (char-before) ?$)
-               (sh-set-char-syntax (point) sh-st-punc) ;; not a comment!
-             (end-of-line) ;; if this *was* a comment, ignore rest of line!
-             )))
-       ;; 4. Hide these changes from making a previously unmodified
-       ;; buffer into a modified buffer.
-       (if sh-debug
-           (if initial-buffer-modified-p
-               (message "buffer was initially modified")
-             (message
-              "buffer not initially modified - so clearing modified flag")))
-       (set-buffer-modified-p initial-buffer-modified-p)
-       )))
-
-(defun sh-rescan-buffer ()
-  "Rescan the buffer for case alternative parentheses and here documents."
-  (interactive)
-  (if (eq major-mode 'sh-mode)
-      (let ((inhibit-read-only t))
-       (sh-remove-our-text-properties)
-       (message "Re-scanning buffer...")
-       (sh-scan-buffer)
-       (message "Re-scanning buffer...done")
-       )))
+      result)))
 
 ;; ========================================================================
 
-;; Styles -- a quick and dirty way of saving the indenation settings.
+;; Styles -- a quick and dirty way of saving the indentation settings.
 
 (defvar sh-styles-alist nil
   "A list of all known shell indentation styles.")
 
 (defun sh-name-style (name &optional confirm-overwrite)
   "Name the current indentation settings as a style called NAME.
-If this name exists,  the command will prompt whether it should be
+If this name exists, the command will prompt whether it should be
 overwritten if
-- - it was called interactively with a prefix argument,  or
+- - it was called interactively with a prefix argument, or
 - - called non-interactively with optional CONFIRM-OVERWRITE non-nil."
   ;; (interactive "sName for this style: ")
   (interactive
    (list
     (read-from-minibuffer "Name for this style? " )
     (not current-prefix-arg)))
-  (let ((slist (list name))
-       (p sh-var-list)
-       var style)
-    (while p
-      (setq var (car p))
-       (setq slist (append slist (list (cons var (symbol-value var)))))
-       (setq p (cdr p)))
-    (if (setq style (assoc name sh-styles-alist))
-       (if (or (not confirm-overwrite)
-               (y-or-n-p "This style exists.  Overwrite it? "))
-           (progn
-             (message "Updating style %s" name)
-             (setcdr style (cdr slist)))
-         (message "Not changing style %s" name))
+  (let ((slist (cons name
+                    (mapcar (lambda (var) (cons var (symbol-value var)))
+                            sh-var-list)))
+       (style (assoc name sh-styles-alist)))
+    (if style
+       (if (and confirm-overwrite
+                (not (y-or-n-p "This style exists.  Overwrite it? ")))
+           (message "Not changing style %s" name)
+         (message "Updating style %s" name)
+         (setcdr style (cdr slist)))
       (message "Creating new style %s" name)
-      (setq sh-styles-alist (append sh-styles-alist
-                                (list   slist)))
-      )))
+      (push slist sh-styles-alist))))
 
 (defun sh-load-style (name)
   "Set shell indentation values for this buffer from those in style NAME."
@@ -3518,37 +3093,18 @@ overwritten if
   (let ((sl (assoc name  sh-styles-alist)))
     (if (null sl)
        (error "sh-load-style - style %s not known" name)
-      (setq sl (cdr sl))
-      (while sl
-       (set (car (car sl)) (cdr (car sl)))
-       (setq sl (cdr sl))
-       ))))
+      (dolist (var (cdr sl))
+       (set (car var) (cdr var))))))
 
 (defun sh-save-styles-to-buffer (buff)
   "Save all current styles in elisp to buffer BUFF.
 This is always added to the end of the buffer."
   (interactive (list
-    (read-from-minibuffer "Buffer to save styles in? " "*scratch*")))
-  ;; This is an attempt to sort of pretty print it...
-  (save-excursion
-    (set-buffer (get-buffer-create buff))
+               (read-from-minibuffer "Buffer to save styles in? " "*scratch*")))
+  (with-current-buffer (get-buffer-create buff)
     (goto-char (point-max))
     (insert "\n")
-    (let ((p sh-styles-alist) q)
-      (insert "(setq sh-styles-alist '(\n")
-      (while p
-       (setq q (car p))
-       (insert "  ( " (prin1-to-string (car q)) "\n")
-       (setq q (cdr q))
-       (while q
-         (insert "    "(prin1-to-string (car q)) "\n")
-         (setq q (cdr q)))
-       (insert "    )\n")
-       (setq p (cdr p))
-       )
-      (insert "))\n")
-      )))
-       
+    (pp `(setq sh-styles-alist ',sh-styles-alist) (current-buffer))))
 
 
 \f
@@ -3557,6 +3113,11 @@ This is always added to the end of the buffer."
 ;; You are welcome to add the syntax or even completely new statements as
 ;; appropriate for your favorite shell.
 
+(defconst sh-non-closing-paren
+  ;; If we leave it rear-sticky, calling `newline' ends up inserting a \n
+  ;; that inherits this property, which then confuses the indentation.
+  (propertize ")" 'syntax-table sh-st-punc 'rear-nonsticky t))
+
 (define-skeleton sh-case
   "Insert a case/switch statement.  See `sh-feature'."
   (csh "expression: "
@@ -3571,7 +3132,7 @@ This is always added to the end of the buffer."
        < "default:" \n
        > _ \n
        resume:
-       < < "endsw")
+       < < "endsw" \n)
   (es)
   (rc "expression: "
       > "switch( " str " ) {" \n
@@ -3583,56 +3144,50 @@ This is always added to the end of the buffer."
       "case *" > \n
       > _ \n
       resume:
-       ?} > )
+      ?\} > \n)
   (sh "expression: "
       > "case " str " in" \n
-      > (read-string "pattern: ")
-      '(sh-electric-rparen)
-      \n
-      > _ \n
-      ";;" \n
-      ( "other pattern, %s: "
-       > str  '(sh-electric-rparen) \n
+      ( "pattern, %s: "
+       > str sh-non-closing-paren \n
        > _ \n
        ";;" \n)
-      > "*"  '(sh-electric-rparen) \n
+      > "*" sh-non-closing-paren \n
       > _ \n
       resume:
-      "esac" > ))
+      "esac" > \n))
 
 (define-skeleton sh-for
   "Insert a for loop.  See `sh-feature'."
-  (csh eval sh-modify sh
+  (csh sh-modify sh
        1 ""
        2 "foreach "
        4 " ( "
        6 " )"
        15 '<
-       16 "end"
-       )
-  (es eval sh-modify rc
+       16 "end")
+  (es sh-modify rc
       4 " = ")
-  (rc eval sh-modify sh
+  (rc sh-modify sh
       2 "for( "
       6 " ) {"
-      15 ?} )
+      15 ?\} )
   (sh "Index variable: "
       > "for " str " in " _ "; do" \n
       > _ | ?$ & (sh-remember-variable str) \n
-      "done" > ))
+      "done" > \n))
 
 
 
 (define-skeleton sh-indexed-loop
   "Insert an indexed loop from 1 to n.  See `sh-feature'."
-  (bash eval identity posix)
+  (bash sh-modify posix)
   (csh "Index variable: "
        "@ " str " = 1" \n
        "while( $" str " <= " (read-string "upper limit: ") " )" \n
        > _ ?$ str \n
        "@ " str "++" \n
-       < "end")
-  (es eval sh-modify rc
+       < "end" \n)
+  (es sh-modify rc
       4 " =")
   (ksh88 "Index variable: "
         > "integer " str "=0" \n
@@ -3640,7 +3195,7 @@ This is always added to the end of the buffer."
         (read-string "upper limit: ")
         " )); do" \n
         > _ ?$ (sh-remember-variable str) > \n
-        "done" > )
+        "done" > \n)
   (posix "Index variable: "
         > str "=1" \n
         "while [ $" str " -le "
@@ -3648,19 +3203,19 @@ This is always added to the end of the buffer."
         " ]; do" \n
         > _ ?$ str \n
         str ?= (sh-add (sh-remember-variable str) 1) \n
-        "done" > )
+        "done" > \n)
   (rc "Index variable: "
       > "for( " str " in" " `{awk 'BEGIN { for( i=1; i<="
       (read-string "upper limit: ")
       "; i++ ) print i }'`}) {" \n
       > _ ?$ (sh-remember-variable str) \n
-       ?} >)
+      ?\} > \n)
   (sh "Index variable: "
       > "for " str " in `awk 'BEGIN { for( i=1; i<="
       (read-string "upper limit: ")
       "; i++ ) print i }'`; do" \n
       > _ ?$ (sh-remember-variable str) \n
-      "done" > ))
+      "done" > \n))
 
 
 (defun sh-shell-initialize-variables ()
@@ -3699,12 +3254,10 @@ t means to return a list of all possible completions of STRING.
                              (cons name name)))
                          process-environment)
                  sh-shell-variables))))
-    (cond ((null code)
-          (try-completion string sh-shell-variables predicate))
-         ((eq code t)
-          (all-completions string sh-shell-variables predicate))
-         ((eq code 'lambda)
-          (assoc string sh-shell-variables)))))
+    (case code
+      ((nil) (try-completion string sh-shell-variables predicate))
+      (lambda (test-completion string sh-shell-variables predicate))
+      (t (all-completions string sh-shell-variables predicate)))))
 
 (defun sh-add (var delta)
   "Insert an addition of VAR and prefix DELTA for Bourne (type) shell."
@@ -3712,7 +3265,7 @@ t means to return a list of all possible completions of STRING.
    (let ((sh-add-buffer (current-buffer)))
      (list (completing-read "Variable: " 'sh-add-completer)
           (prefix-numeric-value current-prefix-arg))))
-  (insert (sh-feature '((bash . "$[ ")
+  (insert (sh-feature '((bash . "$(( ")
                        (ksh88 . "$(( ")
                        (posix . "$(( ")
                        (rc . "`{expr $")
@@ -3721,7 +3274,7 @@ t means to return a list of all possible completions of STRING.
          (sh-remember-variable var)
          (if (< delta 0) " - " " + ")
          (number-to-string (abs delta))
-         (sh-feature '((bash . " ]")
+         (sh-feature '((bash . " ))")
                        (ksh88 . " ))")
                        (posix . " ))")
                        (rc . "}")
@@ -3732,18 +3285,18 @@ t means to return a list of all possible completions of STRING.
 
 (define-skeleton sh-function
   "Insert a function definition.  See `sh-feature'."
-  (bash eval sh-modify ksh88
+  (bash sh-modify ksh88
        3 "() {")
   (ksh88 "name: "
         "function " str " {" \n
         > _ \n
-        < "}")
-  (rc eval sh-modify ksh88
-       1 "fn ")
+        < "}" \n)
+  (rc sh-modify ksh88
+      1 "fn ")
   (sh ()
       "() {" \n
       > _ \n
-      < "}"))
+      < "}" \n))
 
 
 
@@ -3758,39 +3311,38 @@ t means to return a list of all possible completions of STRING.
        < "else" \n
        > _ \n
        resume:
-       < "endif")
+       < "endif" \n)
   (es "condition: "
-       > "if { " str " } {" \n
-       > _ \n
-       ( "other condition, %s: "
-        "} { " str " } {" > \n
-        > _ \n)
-       "} {" > \n
-       > _ \n
-       resume:
-       ?} > )
+      > "if { " str " } {" \n
+      > _ \n
+      ( "other condition, %s: "
+       "} { " str " } {" > \n
+       > _ \n)
+      "} {" > \n
+      > _ \n
+      resume:
+      ?\} > \n)
   (rc "condition: "
-       > "if( " str " ) {" \n
-       > _ \n
-       ( "other condition, %s: "
-          "} else if( " str " ) {"  > \n
-          > _ \n)
-       "} else {" > \n
-       > _ \n
-       resume:
-        ?} >
-       )
+      > "if( " str " ) {" \n
+      > _ \n
+      ( "other condition, %s: "
+       "} else if( " str " ) {"  > \n
+       > _ \n)
+      "} else {" > \n
+      > _ \n
+      resume:
+      ?\} > \n)
   (sh "condition: "
       '(setq input (sh-feature sh-test))
       > "if " str "; then" \n
       > _ \n
       ( "other condition, %s: "
-      >  "elif " str "; then" > \n
-      > \n)
-       "else" > \n
+       >  "elif " str "; then" > \n
+       > \n)
+      "else" > \n
       > \n
       resume:
-      "fi" > ))
+      "fi" > \n))
 
 
 
@@ -3799,11 +3351,11 @@ t means to return a list of all possible completions of STRING.
   (es nil
       > "forever {" \n
       > _ \n
-       ?} > )
+      ?\} > \n)
   (zsh "factor: "
        > "repeat " str "; do" > \n
-      >  \n
-       "done" > ))
+       >  \n
+       "done" > \n))
 
 ;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
 
@@ -3814,16 +3366,15 @@ t means to return a list of all possible completions of STRING.
   (ksh88 "Index variable: "
         > "select " str " in " _ "; do" \n
         > ?$ str \n
-        "done" > )
-  (bash eval sh-append ksh88)
-  )
+        "done" > \n)
+  (bash sh-append ksh88))
 ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
 
 
 
 (define-skeleton sh-tmp-file
   "Insert code to setup temporary file handling.  See `sh-feature'."
-  (bash eval identity ksh88)
+  (bash sh-append ksh88)
   (csh (file-name-nondirectory (buffer-file-name))
        "set tmp = /tmp/" str ".$$" \n
        "onintr exit" \n _
@@ -3831,7 +3382,7 @@ t means to return a list of all possible completions of STRING.
            (not (bolp))
            ?\n)
        "exit:\n"
-       "rm $tmp* >&/dev/null" >)
+       "rm $tmp* >&/dev/null" > \n)
   (es (file-name-nondirectory (buffer-file-name))
       > "local( signals = $signals sighup sigint; tmp = /tmp/" str
       ".$pid ) {" \n
@@ -3839,17 +3390,17 @@ t means to return a list of all possible completions of STRING.
       > "rm $tmp^* >[2]/dev/null" \n
       "throw $e" \n
       "} {" > \n
-       _ \n
-      ?} > \n
-      ?} > )
-  (ksh88 eval sh-modify sh
+      _ \n
+      ?\} > \n
+      ?\} > \n)
+  (ksh88 sh-modify sh
         7 "EXIT")
   (rc (file-name-nondirectory (buffer-file-name))
       > "tmp = /tmp/" str ".$pid" \n
-       "fn sigexit { rm $tmp^* >[2]/dev/null }")
+      "fn sigexit { rm $tmp^* >[2]/dev/null }" \n)
   (sh (file-name-nondirectory (buffer-file-name))
       > "TMP=${TMPDIR:-/tmp}/" str ".$$" \n
-      "trap \"rm $TMP* 2>/dev/null\" " ?0))
+      "trap \"rm $TMP* 2>/dev/null\" " ?0 \n))
 
 
 
@@ -3859,32 +3410,32 @@ t means to return a list of all possible completions of STRING.
       '(setq input (sh-feature sh-test))
       > "until " str "; do" \n
       > _ \n
-       "done" > ))
+      "done" > \n))
 ;;;(put 'sh-until 'menu-enable '(sh-feature sh-until))
 
 
 
 (define-skeleton sh-while
   "Insert a while loop.  See `sh-feature'."
-  (csh eval sh-modify sh
+  (csh sh-modify sh
        2 ""
        3 "while( "
        5 " )"
        10 '<
-       11 "end" )
-  (es eval sh-modify sh
+       11 "end")
+  (es sh-modify sh
       3 "while { "
       5 " } {"
-      10 ?} )
-  (rc eval sh-modify sh
+      10 ?\} )
+  (rc sh-modify sh
       3 "while( "
       5 " ) {"
-      10 ?} )
+      10 ?\} )
   (sh "condition: "
       '(setq input (sh-feature sh-test))
       > "while " str "; do" \n
       > _ \n
-      "done" > ))
+      "done" > \n))
 
 
 
@@ -3892,7 +3443,7 @@ t means to return a list of all possible completions of STRING.
   "Insert a while getopts loop.  See `sh-feature'.
 Prompts for an options string which consists of letters for each recognized
 option followed by a colon `:' if the option accepts an argument."
-  (bash eval sh-modify sh
+  (bash sh-modify sh
        18 "${0##*/}")
   (csh nil
        "while( 1 )" \n
@@ -3916,12 +3467,12 @@ option followed by a colon `:' if the option accepts an argument."
        resume:
        < < "endsw" \n
        "shift" \n
-       < "end")
-  (ksh88 eval sh-modify sh
+       < "end" \n)
+  (ksh88 sh-modify sh
         16 "print"
         18 "${0##*/}"
-        36 "OPTIND-1")
-  (posix eval sh-modify sh
+        37 "OPTIND-1")
+  (posix sh-modify sh
         18 "$(basename $0)")
   (sh "optstring: "
       > "while getopts :" str " OPT; do" \n
@@ -3933,22 +3484,24 @@ option followed by a colon `:' if the option accepts an argument."
                    v2 "\"$OPTARG\"")
            (setq v1 (cdr v1)
                  v2 nil)))
-       > str "|+" str '(sh-electric-rparen) \n
+       > str "|+" str sh-non-closing-paren \n
        > _ v2 \n
        > ";;" \n)
-      > "*"  '(sh-electric-rparen) \n
+      > "*" sh-non-closing-paren \n
       > "echo" " \"usage: " "`basename $0`"
       " [+-" '(setq v1 (point)) str
       '(save-excursion
         (while (search-backward ":" v1 t)
           (replace-match " ARG] [+-" t t)))
       (if (eq (preceding-char) ?-) -5)
-      "] [--] ARGS...\"" \n
+      (if (and (sequencep v1) (length v1)) "] " "} ")
+      "[--] ARGS...\"" \n
       "exit 2"  > \n
-        "esac" >
-        \n "done"
-        > \n
-      "shift " (sh-add "OPTIND" -1)))
+      "esac" >
+      \n "done"
+      > \n
+      "shift " (sh-add "OPTIND" -1) \n
+      "OPTIND=1" \n))
 
 
 
@@ -3966,9 +3519,8 @@ option followed by a colon `:' if the option accepts an argument."
             (match-string 1))))))
 
 
-
 (defun sh-maybe-here-document (arg)
-  "Inserts self.  Without prefix, following unquoted `<' inserts here document.
+  "Insert self.  Without prefix, following unquoted `<' inserts here document.
 The document is bounded by `sh-here-document-word'."
   (interactive "*P")
   (self-insert-command (prefix-numeric-value arg))
@@ -3977,15 +3529,21 @@ The document is bounded by `sh-here-document-word'."
       (save-excursion
        (backward-char 2)
        (sh-quoted-p))
-      (progn
+      (let ((tabs (if (string-match "\\`-" sh-here-document-word)
+                      (make-string (/ (current-indentation) tab-width) ?\t)
+                    ""))
+            (delim (replace-regexp-in-string "['\"]" ""
+                                            sh-here-document-word)))
        (insert sh-here-document-word)
        (or (eolp) (looking-at "[ \t]") (insert ? ))
        (end-of-line 1)
        (while
            (sh-quoted-p)
          (end-of-line 2))
-       (newline)
-       (save-excursion (insert ?\n sh-here-document-word)))))
+       (insert ?\n tabs)
+       (save-excursion
+          (insert ?\n tabs (replace-regexp-in-string
+                            "\\`-?[ \t]*" "" delim))))))
 
 \f
 ;; various other commands
@@ -4028,6 +3586,78 @@ The document is bounded by `sh-here-document-word'."
   (if (re-search-forward sh-end-of-command nil t)
       (goto-char (match-end 1))))
 
+;; Backslashification.  Stolen from make-mode.el.
+
+(defun sh-backslash-region (from to delete-flag)
+  "Insert, align, or delete end-of-line backslashes on the lines in the region.
+With no argument, inserts backslashes and aligns existing backslashes.
+With an argument, deletes the backslashes.
+
+This function does not modify the last line of the region if the region ends
+right at the start of the following line; it does not modify blank lines
+at the start of the region.  So you can put the region around an entire
+shell command and conveniently use this command."
+  (interactive "r\nP")
+  (save-excursion
+    (goto-char from)
+    (let ((column sh-backslash-column)
+          (endmark (make-marker)))
+      (move-marker endmark to)
+      ;; Compute the smallest column number past the ends of all the lines.
+      (if sh-backslash-align
+         (progn
+           (if (not delete-flag)
+               (while (< (point) to)
+                 (end-of-line)
+                 (if (= (preceding-char) ?\\)
+                     (progn (forward-char -1)
+                            (skip-chars-backward " \t")))
+                 (setq column (max column (1+ (current-column))))
+                 (forward-line 1)))
+           ;; Adjust upward to a tab column, if that doesn't push
+           ;; past the margin.
+           (if (> (% column tab-width) 0)
+               (let ((adjusted (* (/ (+ column tab-width -1) tab-width)
+                                  tab-width)))
+                 (if (< adjusted (window-width))
+                     (setq column adjusted))))))
+      ;; Don't modify blank lines at start of region.
+      (goto-char from)
+      (while (and (< (point) endmark) (eolp))
+        (forward-line 1))
+      ;; Add or remove backslashes on all the lines.
+      (while (and (< (point) endmark)
+                  ;; Don't backslashify the last line
+                  ;; if the region ends right at the start of the next line.
+                  (save-excursion
+                    (forward-line 1)
+                    (< (point) endmark)))
+        (if (not delete-flag)
+            (sh-append-backslash column)
+          (sh-delete-backslash))
+        (forward-line 1))
+      (move-marker endmark nil))))
+
+(defun sh-append-backslash (column)
+  (end-of-line)
+  ;; Note that "\\\\" is needed to get one backslash.
+  (if (= (preceding-char) ?\\)
+      (progn (forward-char -1)
+             (delete-horizontal-space)
+             (indent-to column (if sh-backslash-align nil 1)))
+    (indent-to column (if sh-backslash-align nil 1))
+    (insert "\\")))
+
+(defun sh-delete-backslash ()
+  (end-of-line)
+  (or (bolp)
+      (progn
+       (forward-char -1)
+       (if (looking-at "\\\\")
+           (delete-region (1+ (point))
+                          (progn (skip-chars-backward " \t") (point)))))))
+
 (provide 'sh-script)
 
+;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
 ;;; sh-script.el ends here