(perform-replace): Move START and END parameters
[bpt/emacs.git] / lisp / progmodes / sh-script.el
index 08e0e1e..9392c3d 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
+;;  Free Software Foundation, Inc.
 
 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Version: 2.0f
 ;; 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)
+;;   (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
 ;; 
 ;;     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.
 ;; 
 ;; 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
@@ -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,62 +354,60 @@ 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].")
 
 
 
-(defvar sh-mode-syntax-table
-  '((sh eval sh-mode-syntax-table ()
-       ?\# "<"
-       ?\^l ">#"
-       ?\n ">#"
-       ?\" "\"\""
-       ?\' "\"'"
-       ?\` "\"`"
-       ?! "_"
-       ?% "_"
-       ?: "_"
-       ?. "_"
-       ?^ "_"
-       ?~ "_")
-    (csh eval identity sh)
-    (rc eval identity sh))
-  "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
-
+(easy-mmode-defsyntax sh-mode-syntax-table
+  '((?\# . "<")
+   (?\^l . ">#")
+   (?\n . ">#")
+   (?\" . "\"\"")
+   (?\' . "\"'")
+   (?\` . "\"`")
+   (?! . "_")
+   (?% . "_")
+   (?: . "_")
+   (?. . "_")
+   (?^ . "_")
+   (?~ . "_")
+   (?< . ".")
+   (?> . "."))
+  "Syntax-table used in Shell-Script mode.")
 
 
 (defvar sh-mode-map
@@ -454,9 +440,6 @@ 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))
@@ -479,10 +462,8 @@ the car and cdr are the same symbol.")
     (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 +582,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 +712,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 +757,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" :bold t))
+    (((class color)
+      (background light))
+     (:foreground "tan" ))
+    (t
+     (:bold t)))
+  "Face to show a here-document"
+  :group 'sh-indentation)
+(defvar sh-heredoc-face 'sh-heredoc-face)
 
 
 (defvar sh-font-lock-keywords
@@ -816,16 +812,124 @@ 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* (;; A rough regexp that should find the opening <<EOF back.
+        (sre (concat "<<\\(-?\\)\\s-*['\"\\]?"
+                     ;; Use \s| to cheaply check it's an open-heredoc.
+                     (regexp-quote eof) "['\"]?\\([ \t|;&)<>].*\\)?\\s|"))
+        ;; A regexp that will find other EOFs.
+        (ere (concat "^" (if indented "[ \t]*") (regexp-quote eof) "\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)
+        (/= (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 +943,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 +1007,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,9 +1047,9 @@ 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
@@ -983,7 +1087,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 +1156,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"))
 
 
@@ -1121,9 +1188,6 @@ or preceded by punctuation characters like '-'."
   '((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
@@ -1161,7 +1225,7 @@ frequently editing existing scripts with different styles.")
 (put 'sh-mode 'mode-class 'special)
 
 ;;;###autoload
-(defun sh-mode ()
+(define-derived-mode sh-mode nil "Shell-script"
   "Major mode for editing shell scripts.
 This mode works for many shells, since they all have roughly the same syntax,
 as far as commands, arguments, variables, pipes, comments etc. are concerned.
@@ -1214,11 +1278,6 @@ indicate what shell it is use `sh-alias-alist' to translate.
 
 If your shell gives error messages with line numbers, you can use \\[executable-interpret]
 with your script for an edit-interpret-debug cycle."
-  (interactive)
-  (kill-all-local-variables)
-  (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 +1297,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 +1307,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 +1332,8 @@ 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)))
+
 ;;;###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,77 +1401,32 @@ 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)
@@ -1457,48 +1450,28 @@ 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
        sh-shell-variables-initialized nil
        imenu-generic-expression (sh-feature sh-imenu-generic-expression)
        imenu-case-fold-search nil)
-  (set-syntax-table (or (sh-feature sh-mode-syntax-table)
-                       (standard-syntax-table)))
-  (let ((vars (sh-feature sh-variables)))
-    (while vars
-      (sh-remember-variable (car vars))
-      (setq vars (cdr vars))))
-;; Packages should not need to toggle Font Lock mode.  sm.
-;  (and (boundp 'font-lock-mode)
-;       font-lock-mode
-;       (font-lock-mode (font-lock-mode 0)))
+  (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,46 +1537,37 @@ in ALIST."
 
 
 
-;;; I commented this out because nobody calls it -- rms.
-;;;(defun sh-abbrevs (ancestor &rest list)
-;;;  "Iff it isn't, define the current shell as abbrev table and fill that.
-;;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an abbrev
-;;;table or a list of (NAME1 EXPANSION1 ...).  In addition it will define abbrevs
-;;;according to the remaining arguments NAMEi EXPANSIONi ...
-;;;EXPANSION may be either a string or a skeleton command."
-;;;  (or (if (boundp sh-shell)
-;;;      (symbol-value sh-shell))
-;;;      (progn
-;;;    (if (listp ancestor)
-;;;        (nconc list ancestor))
-;;;    (define-abbrev-table sh-shell ())
-;;;    (if (vectorp ancestor)
-;;;        (mapatoms (lambda (atom)
-;;;                    (or (eq atom 0)
-;;;                        (define-abbrev (symbol-value sh-shell)
-;;;                          (symbol-name atom)
-;;;                          (symbol-value atom)
-;;;                          (symbol-function atom))))
-;;;                  ancestor))
-;;;    (while list
-;;;      (define-abbrev (symbol-value sh-shell)
-;;;        (car list)
-;;;        (if (stringp (car (cdr list)))
-;;;            (car (cdr list))
-;;;          "")
-;;;        (if (symbolp (car (cdr list)))
-;;;            (car (cdr list))))
-;;;      (setq list (cdr (cdr list)))))
-;;;      (symbol-value sh-shell)))
-
-
-(defun sh-mode-syntax-table (table &rest list)
-  "Copy TABLE and set syntax for successive CHARs according to strings S."
-  (setq table (copy-syntax-table table))
-  (while list
-    (modify-syntax-entry (car list) (car (cdr list)) table)
-    (setq list (cdr (cdr list))))
-  table)
+;; I commented this out because nobody calls it -- rms.
+;;(defun sh-abbrevs (ancestor &rest list)
+;;  "Iff it isn't, define the current shell as abbrev table and fill that.
+;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an abbrev
+;;table or a list of (NAME1 EXPANSION1 ...).  In addition it will define abbrevs
+;;according to the remaining arguments NAMEi EXPANSIONi ...
+;;EXPANSION may be either a string or a skeleton command."
+;;  (or (if (boundp sh-shell)
+;;       (symbol-value sh-shell))
+;;      (progn
+;;     (if (listp ancestor)
+;;         (nconc list ancestor))
+;;     (define-abbrev-table sh-shell ())
+;;     (if (vectorp ancestor)
+;;         (mapatoms (lambda (atom)
+;;                     (or (eq atom 0)
+;;                         (define-abbrev (symbol-value sh-shell)
+;;                           (symbol-name atom)
+;;                           (symbol-value atom)
+;;                           (symbol-function atom))))
+;;                   ancestor))
+;;     (while list
+;;       (define-abbrev (symbol-value sh-shell)
+;;         (car list)
+;;         (if (stringp (car (cdr list)))
+;;             (car (cdr list))
+;;           "")
+;;         (if (symbolp (car (cdr list)))
+;;             (car (cdr list))))
+;;       (setq list (cdr (cdr list)))))
+;;      (symbol-value sh-shell)))
 
 
 (defun sh-append (ancestor &rest list)
@@ -1684,7 +1648,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)
 
 
@@ -1736,9 +1700,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 +1712,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 +1727,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 +1738,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 +1811,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
-      )))
-
-(defun sh-safe-backward-sexp ()
-  "Try and do a `backward-sexp', but do not error.
-Return new point if successful, nil if an error occurred."
-  (condition-case nil
-      (progn
-       (backward-sexp 1)
-       (point) ;; return point if successful
-       )
-    (error
-     (sh-debug "oops!(0) %d" (point))
-     nil ;; return nil if fail
-     )))
+  (if (sh-goto-matching-case)
+      (list '(+ sh-indent-for-case-label))))
 
-(defun sh-safe-forward-sexp ()
+(defun sh-safe-forward-sexp (&optional arg)
   "Try and do a `forward-sexp', but do not error.
 Return new point if successful, nil if an error occurred."
   (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 +1859,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 +1895,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,21 +1950,23 @@ 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
@@ -2086,7 +2005,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 +2013,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 +2024,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 +2040,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,7 +2054,7 @@ 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!")
@@ -2158,7 +2077,7 @@ STRING         This is ignored for the purposes of calculating
          (setq result nil))
       (sh-debug  "result is: %s" result)
       result
-      );; let
+      )        ;; let
     ))
 
 
@@ -2185,7 +2104,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 +2138,35 @@ 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 +2186,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 +2219,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 ()
@@ -2380,59 +2258,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 +2312,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 +2345,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 +2379,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 +2412,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 +2441,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,13 +2465,12 @@ 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 ()
@@ -2624,21 +2479,18 @@ If INFO is supplied it is used, else it is calculated from current line."
   (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 +2501,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 +2515,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,44 +2600,43 @@ 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))
+      (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))
+       (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))))))
 
 
 
@@ -2811,12 +2660,11 @@ 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))
@@ -2831,7 +2679,10 @@ 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
@@ -2847,7 +2698,7 @@ 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))
@@ -2924,9 +2775,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 +2795,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 +2832,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
@@ -3056,54 +2907,42 @@ This command can often take a long time to run."
       
       (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 +2960,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 +2971,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 +2982,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,343 +3014,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))
-    (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)))
 
 ;; ========================================================================
 
@@ -3532,24 +3034,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."
@@ -3559,37 +3055,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
@@ -3612,7 +3089,7 @@ This is always added to the end of the buffer."
        < "default:" \n
        > _ \n
        resume:
-       < < "endsw")
+       < < "endsw" \n)
   (es)
   (rc "expression: "
       > "switch( " str " ) {" \n
@@ -3624,22 +3101,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'."
@@ -3649,8 +3126,7 @@ 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
@@ -3660,7 +3136,7 @@ This is always added to the end of the buffer."
   (sh "Index variable: "
       > "for " str " in " _ "; do" \n
       > _ | ?$ & (sh-remember-variable str) \n
-      "done" > ))
+      "done" > \n))
 
 
 
@@ -3672,7 +3148,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: "
@@ -3681,7 +3157,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 "
@@ -3689,19 +3165,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 ()
@@ -3778,13 +3254,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))
 
 
 
@@ -3799,39 +3275,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))
 
 
 
@@ -3840,11 +3315,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))
 
@@ -3855,9 +3330,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))
 
 
@@ -3872,7 +3346,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
@@ -3880,17 +3354,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)
   (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))
 
 
 
@@ -3900,7 +3374,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))
 
 
@@ -3912,7 +3386,7 @@ 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 " } {"
@@ -3925,7 +3399,7 @@ t means to return a list of all possible completions of STRING.
       '(setq input (sh-feature sh-test))
       > "while " str "; do" \n
       > _ \n
-      "done" > ))
+      "done" > \n))
 
 
 
@@ -3957,7 +3431,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##*/}"
@@ -3974,10 +3448,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
@@ -3987,10 +3461,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))
 
 
 
@@ -4010,7 +3484,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))