]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/sh-script.el
Add new maintainer (deego).
[gnu-emacs] / lisp / progmodes / sh-script.el
index 5f111fa3b9a99c48725a2e7e017478c54ba48553..283fe09cea26daf0117739c18f1a2017cfcf2382 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, 94, 95, 96, 97, 1999, 2001, 2003
+;;  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
 ;; 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,
+;;   (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
 ;; to work if the variables are already local!
-;; 
+;;
 ;; 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
 ;; buffer local when the mode is established.
 ;; 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
 ;; command `sh-reset-indent-vars-to-global-values'.
-;; 
+;;
 ;; 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
 ;;    at keywords.  [This is not the case for rc and es shells.]
 ;; 2. The character ")" is used both as a matched pair "(" ... ")" and
 ;; 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
 ;; disadvantages:
 ;;    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-indented 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 highlight-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 'comint))
 (require 'executable)
 
 
@@ -248,8 +236,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
@@ -270,7 +258,7 @@ 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
@@ -332,16 +320,16 @@ shell it really is."
   :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")
 
@@ -366,39 +354,39 @@ 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'.
+;; 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'.
 ;;;Due to the internal workings of abbrev tables, the shell name symbol is
 ;;;actually defined as the table for the like of \\[edit-abbrevs].")
 
@@ -407,7 +395,6 @@ the car and cdr are the same symbol.")
 (defvar sh-mode-syntax-table
   '((sh eval sh-mode-syntax-table ()
        ?\# "<"
-       ?\^l ">#"
        ?\n ">#"
        ?\" "\"\""
        ?\' "\"'"
@@ -417,12 +404,13 @@ the car and cdr are the same symbol.")
        ?: "_"
        ?. "_"
        ?^ "_"
-       ?~ "_")
+       ?~ "_"
+       ?< "."
+       ?> ".")
     (csh eval identity sh)
     (rc eval identity sh))
-  "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
-
 
+  "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
 
 (defvar sh-mode-map
   (let ((map (make-sparse-keymap))
@@ -454,35 +442,24 @@ 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 beginning-of-defun]
+      'sh-beginning-of-compound-command)
+    (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))
@@ -601,7 +578,7 @@ The actual command ends at the end of the first \\(grouping\\)."
         "bg" "fg" "jobs" "kill" "stop" "suspend")
 
     (jcsh eval sh-append csh
-        "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
+         "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
 
     (ksh88 eval sh-append bourne
           "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
@@ -731,7 +708,7 @@ See `sh-feature'."
        "pid" "prompt" "signals")
 
     (jcsh eval sh-append csh
-        "notify")
+         "notify")
 
     (ksh88 eval sh-append sh
           "ENV" "ERRNO" "FCEDIT" "FPATH" "HISTFILE" "HISTSIZE" "LINENO"
@@ -776,6 +753,21 @@ See `sh-feature'."
   "List of all shell variables available for completing read.
 See `sh-feature'.")
 
+\f
+;; Font-Lock support
+
+(defface sh-heredoc-face
+  '((((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)
+(defvar sh-heredoc-face 'sh-heredoc-face)
 
 
 (defvar sh-font-lock-keywords
@@ -816,16 +808,126 @@ 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\\|\\s_\\)+\\).*\\(\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-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 "?*/"))
+                 (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.
@@ -839,12 +941,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
@@ -903,7 +1005,7 @@ a number means align to that column, e.g. 0 means fist column."
          (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)
 
 
@@ -943,18 +1045,18 @@ a number means align to that column, e.g. 0 means fist column."
   :group 'sh-indentation)
 
 (defconst sh-number-or-symbol-list
-  (append (list '(integer :menu-tag "A number (positive=>indent right)"
-                         :tag "A number")
-               '(const :tag "--"))     ; separator
+  (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 an 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."
+  "*How much to indent a done relative to its matching stmt.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
@@ -971,7 +1073,7 @@ does not affect then else elif or fi statements themselves."
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-then '+
-  "*How much to indent an then relative to an if."
+  "*How much to indent a then relative to an if."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
@@ -983,7 +1085,7 @@ while until or for statement."
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-do '*
-"*How much to indent a line after a do statement.
+  "*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."
@@ -1052,51 +1154,14 @@ 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)
-
-
 ;; 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 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 occurrence of WORD followed by
-or preceded by punctuation characters like '-'."
-  (concat "\\(^\\|[^-a-z0-9_]\\)" word "\\([^-a-z0-9_]\\|$\\)"))
-
 (defconst sh-re-done (sh-mkword-regexpr "done"))
 
 
@@ -1117,13 +1182,6 @@ or preceded by punctuation characters like '-'."
 (defvar sh-indent-supported-here nil
   "Non-nil if we support indentation for the current buffer's shell type.")
 
-(defconst sh-electric-rparen-needed
-  '((sh . t))
-  "Non-nil if the shell type needs an electric handling of case alternatives.")
-
-(defvar sh-electric-rparen-needed-here nil
-  "Non-nil if the buffer needs an electric handling of case alternatives.")
-
 (defconst sh-var-list
   '(
     sh-basic-offset sh-first-lines-indent sh-indent-after-case
@@ -1205,7 +1263,7 @@ buffer indents as it currently is indented.
 \\[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
@@ -1216,9 +1274,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)
@@ -1238,21 +1296,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
@@ -1261,23 +1306,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
@@ -1288,10 +1331,9 @@ with your script for an edit-interpret-debug cycle."
                 ((and 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 nil))
-    (run-hooks 'sh-mode-hook)))
+    (sh-set-shell (or interpreter sh-shell-file) nil nil))
+  (run-hooks 'sh-mode-hook))
+
 ;;;###autoload
 (defalias 'shell-script-mode 'sh-mode)
 
@@ -1309,21 +1351,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)))
@@ -1363,87 +1401,51 @@ This adds rules for comments and assignments."
 ;; 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
+  '((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   )
-     ) ;; end of 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)
@@ -1457,9 +1459,6 @@ Calls the value of `sh-set-shell-hook' if set."
                                  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
        comment-start-skip "#+[\t ]*"
        mode-line-process (format "[%s]" sh-shell)
        sh-shell-variables nil
@@ -1468,37 +1467,22 @@ Calls the value of `sh-set-shell-hook' if set."
        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)))
+  (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))
@@ -1564,48 +1548,46 @@ 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)))
+;; 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))))
+    (modify-syntax-entry (pop list) (pop list) table))
   table)
 
-
 (defun sh-append (ancestor &rest list)
   "Return list composed of first argument (a list) physically appended to rest."
   (nconc list ancestor))
@@ -1684,7 +1666,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)
 
 
@@ -1694,17 +1676,11 @@ 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)
   (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.
@@ -1714,7 +1690,6 @@ variable `sh-make-vars-local' has been set to nil.
 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."))
 
@@ -1722,7 +1697,6 @@ command `sh-reset-indent-vars-to-global-values'."
   "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)))
@@ -1736,9 +1710,7 @@ Then, if variable `sh-make-vars-local' is non-nil, make them local."
   "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 indentation,
@@ -1750,11 +1722,9 @@ 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))
@@ -1767,10 +1737,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))
 
 
@@ -1778,10 +1748,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.
@@ -1853,79 +1821,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 seem 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
-      )))
+  (if (sh-goto-matching-case)
+      (list '(+ sh-indent-for-case-label))))
 
-(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
-     )))
-
-(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."
   (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)))
@@ -1934,41 +1869,33 @@ 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 ()
@@ -1978,7 +1905,7 @@ Return new point if successful, nil if an error occurred."
 (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 ()
@@ -2033,22 +1960,24 @@ STRING       This is ignored for the purposes of calculating
          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)
+                (eq (get-text-property (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-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)
@@ -2086,7 +2015,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)))
@@ -2094,7 +2023,7 @@ 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))
@@ -2105,7 +2034,7 @@ STRING         This is ignored for the purposes of calculating
                (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))
@@ -2121,7 +2050,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))
@@ -2135,30 +2064,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
     ))
 
 
@@ -2185,7 +2114,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
@@ -2219,75 +2148,34 @@ If INFO is supplied it is used, else it is calculated."
 ;; 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
+;; - go back to previous non-empty line
 ;; - if this is in a here-document, go to the beginning of it
-;;   and save that
-;; - go back one more physical line and see if it is a continuation line
-;; - if yes, save it and repeat
-;; - if no, go back to where we last saved.
+;; - 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
 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 ()
@@ -2307,7 +2195,7 @@ we go to the end of the previous line and do not check for continuations."
                  (not (bobp))
                  going)
        ;; Do a backward-sexp if possible, else backup bit by bit...
-       (if (sh-safe-backward-sexp)
+       (if (sh-safe-forward-sexp -1)
            (progn
              (if (looking-at sh-special-keywords)
                  (progn
@@ -2340,8 +2228,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 ()
@@ -2370,7 +2257,7 @@ 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)
@@ -2380,59 +2267,51 @@ we go to the end of the previous line and do not check for continuations."
          (found nil))
       (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.
@@ -2442,10 +2321,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.
@@ -2476,7 +2354,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)
@@ -2510,10 +2388,10 @@ IGNORE-ERROR is non-nil."
       (/ (- sh-basic-offset) 2))
      (t
       (if ignore-error
-         (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))
+      (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)
@@ -2543,21 +2421,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)))
@@ -2576,31 +2450,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)
 
@@ -2609,36 +2474,31 @@ 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
        (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)
+      (setq beg (point))
+      (skip-chars-forward " \t")
+      (setq shift-amt (- indent (current-column)))
+      (unless (zerop shift-amt)
+       (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))))))
 
 
 (defun sh-blink (blinkpos &optional msg)
@@ -2649,8 +2509,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.
@@ -2664,9 +2523,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))
@@ -2750,56 +2608,53 @@ 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)
@@ -2809,20 +2664,17 @@ 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 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))
@@ -2831,14 +2683,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))
            ))
       )))
 
@@ -2847,8 +2702,8 @@ 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 learned variables.")
-;;; Example of how to use sh-learned-buffer-hook
-;; 
+;; Example of how to use sh-learned-buffer-hook
+;;
 ;; (defun what-i-learned (list)
 ;;   (let ((p list))
 ;;     (save-excursion
@@ -2861,7 +2716,7 @@ so that `occur-next' and `occur-prev' will work."
 ;;     (setq p (cdr p)))
 ;;       (insert ")\n")
 ;;       )))
-;; 
+;;
 ;; (add-hook 'sh-learned-buffer-hook 'what-i-learned)
 
 
@@ -2924,9 +2779,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))
@@ -2944,7 +2799,7 @@ 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 ?
@@ -2981,16 +2836,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
@@ -3053,57 +2908,45 @@ 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))))))
-               )
+           (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))))))
+           )
        (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))
@@ -3121,10 +2964,10 @@ Return values:
                    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))
+        (return nil)
+        j)
     (while (< i max)
       (aset totals i (+ (aref totals i) (* 4 (aref vec i))))
       (setq j (/ i 2))
@@ -3132,8 +2975,8 @@ Return values:
          (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)
@@ -3143,17 +2986,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)))
@@ -3176,344 +3018,7 @@ 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 parenthesis and check if it is a case alternative.
-If so, its syntax class is set to word, and its text property
-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 property 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 occurring 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-search-word (word &optional limit)
-  "Search forward for regexp WORD occurring as a word not in string nor comment.
-If found, returns non-nil, with the match available in  \(match-string 2\).
-Yes, that is 2, not 1.
-If not found, it returns nil.
-The search may be limited by optional argument LIMIT."
-  (interactive "sSearch for: ")
-  (let ((found nil)
-       start state where match)
-    (setq start (point))
-    (debug)
-    (while (and (not found)
-               (re-search-forward word limit t))
-      (setq match (match-data))
-      ;; Found the word as a string; check it occurs as a word.
-      (when (and (or (= (match-beginning 0) (point-min))
-                    (save-excursion
-                      (goto-char (1- (match-beginning 0)))
-                      (looking-at "[^-a-z0-9_]")))
-                (or (= (point) (point-max))
-                    (looking-at "[^-a-z0-9_]")))
-       ;; 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)))
-       (setq start (point))))
-    (when found
-      (set-match-data match)
-      (goto-char (1- (match-beginning 0)))
-      (looking-at (sh-mkword-regexp word))
-      (goto-char found))
-    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
-    ))
-
-
-;; FIXME: This loses big time on very large files (such as CVS' sanity.sh).
-(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 indentation 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)))
 
 ;; ========================================================================
 
@@ -3533,24 +3038,18 @@ overwritten if
    (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."
@@ -3560,37 +3059,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
@@ -3613,7 +3093,7 @@ This is always added to the end of the buffer."
        < "default:" \n
        > _ \n
        resume:
-       < < "endsw")
+       < < "endsw" \n)
   (es)
   (rc "expression: "
       > "switch( " str " ) {" \n
@@ -3625,22 +3105,22 @@ 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)
+      (propertize ")" 'syntax-table sh-st-punc)
       \n
       > _ \n
       ";;" \n
       ( "other pattern, %s: "
-       > str  '(sh-electric-rparen) \n
+       > str (propertize ")" 'syntax-table sh-st-punc) \n
        > _ \n
        ";;" \n)
-      > "*"  '(sh-electric-rparen) \n
+      > "*" (propertize ")" 'syntax-table sh-st-punc) \n
       > _ \n
       resume:
-      "esac" > ))
+      "esac" > \n))
 
 (define-skeleton sh-for
   "Insert a for loop.  See `sh-feature'."
@@ -3650,18 +3130,17 @@ This is always added to the end of the buffer."
        4 " ( "
        6 " )"
        15 '<
-       16 "end"
-       )
+       16 "end")
   (es eval sh-modify rc
       4 " = ")
   (rc eval sh-modify sh
       2 "for( "
       6 " ) {"
-      15 ?} )
+      15 ?\} )
   (sh "Index variable: "
       > "for " str " in " _ "; do" \n
       > _ | ?$ & (sh-remember-variable str) \n
-      "done" > ))
+      "done" > \n))
 
 
 
@@ -3673,7 +3152,7 @@ This is always added to the end of the buffer."
        "while( $" str " <= " (read-string "upper limit: ") " )" \n
        > _ ?$ str \n
        "@ " str "++" \n
-       < "end")
+       < "end" \n)
   (es eval sh-modify rc
       4 " =")
   (ksh88 "Index variable: "
@@ -3682,7 +3161,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 "
@@ -3690,19 +3169,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 ()
@@ -3779,13 +3258,13 @@ t means to return a list of all possible completions of STRING.
   (ksh88 "name: "
         "function " str " {" \n
         > _ \n
-        < "}")
+        < "}" \n)
   (rc eval sh-modify ksh88
-       1 "fn ")
+      1 "fn ")
   (sh ()
       "() {" \n
       > _ \n
-      < "}"))
+      < "}" \n))
 
 
 
@@ -3800,39 +3279,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))
 
 
 
@@ -3841,11 +3319,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))
 
@@ -3856,9 +3334,8 @@ 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 eval sh-append ksh88))
 ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
 
 
@@ -3873,7 +3350,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
@@ -3881,17 +3358,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
-      ?} > )
+      _ \n
+      ?\} > \n
+      ?\} > \n)
   (ksh88 eval 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))
 
 
 
@@ -3901,7 +3378,7 @@ 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))
 
 
@@ -3913,20 +3390,20 @@ t means to return a list of all possible completions of STRING.
        3 "while( "
        5 " )"
        10 '<
-       11 "end" )
+       11 "end")
   (es eval sh-modify sh
       3 "while { "
       5 " } {"
-      10 ?} )
+      10 ?\} )
   (rc eval sh-modify sh
       3 "while( "
       5 " ) {"
-      10 ?} )
+      10 ?\} )
   (sh "condition: "
       '(setq input (sh-feature sh-test))
       > "while " str "; do" \n
       > _ \n
-      "done" > ))
+      "done" > \n))
 
 
 
@@ -3958,7 +3435,7 @@ option followed by a colon `:' if the option accepts an argument."
        resume:
        < < "endsw" \n
        "shift" \n
-       < "end")
+       < "end" \n)
   (ksh88 eval sh-modify sh
         16 "print"
         18 "${0##*/}"
@@ -3975,10 +3452,10 @@ 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 (propertize ")" 'syntax-table sh-st-punc) \n
        > _ v2 \n
        > ";;" \n)
-      > "*"  '(sh-electric-rparen) \n
+      > "*" (propertize ")" 'syntax-table sh-st-punc) \n
       > "echo" " \"usage: " "`basename $0`"
       " [+-" '(setq v1 (point)) str
       '(save-excursion
@@ -3988,10 +3465,10 @@ option followed by a colon `:' if the option accepts an argument."
       (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))
 
 
 
@@ -4011,7 +3488,7 @@ option followed by a colon `:' if the option accepts an argument."
 
 
 (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))