]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/sh-script.el
(gdb-current-frame, gud-watch)
[gnu-emacs] / lisp / progmodes / sh-script.el
index fd225cd164ab4a76aaa3258a65c8adedf8e5dde8..f47ca3a73d4dff2e1e959050eb4ae4ad353551d2 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, 03, 2004
+;;  Free Software Foundation, Inc.
 
 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Version: 2.0f
 ;;             ===========
 ;; Indentation for rc and es modes is very limited, but for Bourne shells
 ;; and its derivatives it is quite customizable.
-;; 
+;;
 ;; The following description applies to sh and derived shells (bash,
 ;; zsh, ...).
-;; 
+;;
 ;; There are various customization variables which allow tailoring to
 ;; a wide variety of styles.  Most of these variables are named
 ;; sh-indent-for-XXX and sh-indent-after-XXX.  For example.
 ;; sh-indent-after-if controls the indenting of a line following
 ;; an if statement, and sh-indent-for-fi controls the indentation
 ;; of the line containing the fi.
-;; 
+;;
 ;; You can set each to a numeric value, but it is often more convenient
 ;; to a symbol such as `+' which uses the value of variable `sh-basic-offset'.
 ;; By changing this one variable you can increase or decrease how much
 ;; indentation there is.  Valid symbols:
-;; 
+;;
 ;;     +   Indent right by sh-basic-offset
 ;;     -   Indent left  by sh-basic-offset
 ;;     ++  Indent right twice sh-basic-offset
 ;;     --  Indent left  twice sh-basic-offset
 ;;     *   Indent right half sh-basic-offset
 ;;     /   Indent left  half sh-basic-offset.
-;; 
+;;
 ;; There are 4 commands to help set the indentation variables:
-;; 
+;;
 ;; `sh-show-indent'
 ;;    This shows what variable controls the indentation of the current
 ;;    line and its value.
-;; 
+;;
 ;; `sh-set-indent'
 ;;    This allows you to set the value of the variable controlling the
 ;;    current line's indentation.  You can enter a number or one of a
 ;;    or its negative, or half it, or twice it, etc.  If you've used
 ;;    cc-mode this should be familiar.  If you forget which symbols are
 ;;    valid simply press C-h at the prompt.
-;; 
+;;
 ;; `sh-learn-line-indent'
 ;;    Simply make the line look the way you want it, then invoke this
 ;;    command.  It will set the variable to the value that makes the line
 ;;    indent like that.  If called with a prefix argument then it will set
 ;;    the value to one of the symbols if applicable.
-;;    
+;;
 ;; `sh-learn-buffer-indent'
 ;;    This is the deluxe function!  It "learns" the whole buffer (use
 ;;    narrowing if you want it to process only part).  It outputs to a
@@ -97,7 +98,7 @@
 ;;    pattern;  if they don't it will be set to nil.
 ;;    Whether `sh-basic-offset' is set is determined by variable
 ;;    `sh-learn-basic-offset'.
-;; 
+;;
 ;;    Unfortunately, `sh-learn-buffer-indent' can take a long time to run
 ;;    (e.g. if there are large case statements).  Perhaps it does not make
 ;;    sense to run it on large buffers: if lots of lines have different
 ;;    *indent* buffer; if there is a consistent style then running
 ;;    `sh-learn-buffer-indent' on a small region of the buffer should
 ;;    suffice.
-;;   
+;;
 ;;     Saving indentation values
 ;;     -------------------------
 ;; After you've learned the values in a buffer, how to you remember
 ;; the buffer.
 ;; You can do this automatically like this:
 ;;   (add-hook 'sh-set-shell-hook 'sh-learn-buffer-indent)
-;; 
-;; However...   `sh-learn-buffer-indent' is extremely slow,
+;;
+;; However...  `sh-learn-buffer-indent' is extremely slow,
 ;; especially on large-ish buffer.  Also, if there are conflicts the
 ;; "last one wins" which may not produce the desired setting.
-;; 
+;;
 ;; So...There is a minimal way of being able to save indentation values and
 ;; to reload them in another buffer or at another point in time.
-;; 
+;;
 ;; Use `sh-name-style' to give a name to the indentation settings of
 ;;     the current buffer.
 ;; Use `sh-load-style' to load indentation settings for the current
 ;; Use `sh-save-styles-to-buffer' to write all the styles to a buffer
 ;;     in lisp code.  You can then store it in a file and later use
 ;;     `load-file' to load it.
-;; 
+;;
 ;;     Indentation variables - buffer local or global?
 ;;     ----------------------------------------------
 ;; I think that often having them buffer-local makes sense,
 ;; especially if one is using `sh-learn-buffer-indent'.  However, if
 ;; a user sets values using customization, these changes won't appear
 ;; to work if the variables are already local!
-;; 
+;;
 ;; To get round this, there is a variable `sh-make-vars-local' and 2
 ;; functions: `sh-make-vars-local' and `sh-reset-indent-vars-to-global-values'.
-;; 
+;;
 ;; If `sh-make-vars-local' is non-nil, then these variables become
 ;; buffer local when the mode is established.
 ;; If this is nil, then the variables are global.  At any time you
 ;; can make them local with the command `sh-make-vars-local'.
 ;; Conversely, to update with the global values you can use the
 ;; command `sh-reset-indent-vars-to-global-values'.
-;; 
+;;
 ;; This may be awkward, but the intent is to cover all cases.
-;; 
+;;
 ;;     Awkward things, pitfalls
 ;;     ------------------------
 ;; Indentation for a sh script is complicated for a number of reasons:
-;; 
+;;
 ;; 1. You can't format by simply looking at symbols, you need to look
 ;;    at keywords.  [This is not the case for rc and es shells.]
 ;; 2. The character ")" is used both as a matched pair "(" ... ")" and
 ;; 4. A line may be continued using the "\".
 ;; 5. The character "#" (outside a string) normally starts a comment,
 ;;    but it doesn't in the sequence "$#"!
-;; 
+;;
 ;; To try and address points 2 3 and 5 I used a feature that cperl mode
 ;; uses, that of a text's syntax property.  This, however, has 2
 ;; disadvantages:
 ;;    buffer is read-only buffer we have to cheat and bypass the read-only
 ;;    status.  This is for cases where the buffer started read-only buffer
 ;;    but the user issued `toggle-read-only'.
-;; 
+;;
 ;;     Bugs
 ;;     ----
-;; - Here-documents are marked with text properties face and syntax
-;;   table.  This serves 2 purposes: stopping indentation while inside
-;;   them, and moving over them when finding the previous line to
-;;   indent to.  However, if font-lock mode is active when there is
-;;   any change inside the here-document font-lock clears that
-;;   property.  This causes several problems: lines after the here-doc
-;;   will not be re-indented properly, words in the here-doc region
-;;   may be fontified, and indentation may occur within the
-;;   here-document.
-;;   I'm not sure how to fix this, perhaps using the point-entered
-;;   property.  Anyway, if you use font lock and change a
-;;   here-document, I recommend using M-x sh-rescan-buffer after the
-;;   changes are made.  Similarly, when using highlight-changes-mode,
-;;   changes inside a here-document may confuse shell indenting, but again
-;;   using `sh-rescan-buffer' should fix them.
-;; 
 ;; - Indenting many lines is slow.  It currently does each line
 ;;   independently, rather than saving state information.
-;; 
+;;
 ;; - `sh-learn-buffer-indent' is extremely slow.
-;; 
+;;
 ;; Richard Sharman <rsharman@pobox.com>  June 1999.
 
 ;;; Code:
 ;; page 4:     statement syntax-commands for various shells
 ;; page 5:     various other commands
 
+(eval-when-compile
+  (require 'skeleton)
+  (require 'cl)
+  (require 'comint))
 (require 'executable)
 
 
@@ -248,8 +237,8 @@ By default we have the following three hierarchies:
 
 csh            C Shell
   jcsh         C Shell with Job Control
-  tcsh         Toronto C Shell
-    itcsh      ? Toronto C Shell
+  tcsh         Turbo C Shell
+    itcsh      ? Turbo C Shell
 rc             Plan 9 Shell
   es           Extensible Shell
 sh             Bourne Shell
@@ -270,7 +259,7 @@ sh          Bourne Shell
 
 
 (defcustom sh-alias-alist
-  (nconc (if (eq system-type 'gnu/linux)
+  (append (if (eq system-type 'gnu/linux)
             '((csh . tcsh)
               (ksh . pdksh)))
         ;; for the time being
@@ -326,18 +315,20 @@ shell it really is."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (const :tag "No Arguments" nil)
                               (string :tag "Arguments")
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 (defcustom sh-imenu-generic-expression
   `((sh
      . ((nil "^\\s-*\\(function\\s-+\\)?\\([A-Za-z_][A-Za-z_0-9]+\\)\\s-*()" 2))))
-  "*Regular expression for recognizing shell function definitions.
-See `sh-feature'."
-  :type '(repeat (cons (symbol :tag "Shell")
-                      regexp))
+  "*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")
 
@@ -362,48 +353,58 @@ 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 sh-abbrevs shell
+;;      "switch" 'sh-case
+;;      "getopts" 'sh-while-getopts)
+
+;;    (es sh-abbrevs shell
+;;     "function" 'sh-function)
+
+;;    (ksh88 sh-abbrevs sh
+;;        "select" 'sh-select)
+
+;;    (rc sh-abbrevs shell
+;;     "case" 'sh-case
+;;     "function" 'sh-function)
+
+;;    (sh sh-abbrevs shell
+;;     "case" 'sh-case
+;;     "function" 'sh-function
+;;     "until" 'sh-until
+;;     "getopts" 'sh-while-getopts)
+
+;;    ;; The next entry is only used for defining the others
+;;    (shell "for" sh-for
+;;        "loop" sh-indexed-loop
+;;        "if" sh-if
+;;        "tmpfile" sh-tmp-file
+;;        "while" sh-while)
+
+;;    (zsh sh-abbrevs ksh88
+;;      "repeat" 'sh-repeat))
+;;  "Abbrev-table used in Shell-Script mode.  See `sh-feature'.
 ;;;Due to the internal workings of abbrev tables, the shell name symbol is
 ;;;actually defined as the table for the like of \\[edit-abbrevs].")
 
 
 
-(defvar sh-mode-syntax-table
-  '((sh eval sh-mode-syntax-table ()
+(defun sh-mode-syntax-table (table &rest list)
+  "Copy TABLE and set syntax for successive CHARs according to strings S."
+  (setq table (copy-syntax-table table))
+  (while list
+    (modify-syntax-entry (pop list) (pop list) table))
+  table)
+
+(defvar sh-mode-syntax-table nil
+  "The syntax table to use for Shell-Script mode.
+This is buffer-local in every such buffer.")
+
+(defvar sh-mode-default-syntax-table
+  (sh-mode-syntax-table ()
        ?\# "<"
-       ?\^l ">#"
        ?\n ">#"
        ?\" "\"\""
        ?\' "\"'"
@@ -414,13 +415,14 @@ the car and cdr are the same symbol.")
        ?. "_"
        ?^ "_"
        ?~ "_"
+       ?, "_"
        ?< "."
        ?> ".")
-    (csh eval identity sh)
-    (rc eval identity sh))
-  "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
-
+  "Default syntax table for shell mode.")
 
+(defvar sh-mode-syntax-table-input
+  '((sh . nil))
+  "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
 
 (defvar sh-mode-map
   (let ((map (make-sparse-keymap))
@@ -452,25 +454,14 @@ the car and cdr are the same symbol.")
     (define-key map "'" 'skeleton-pair-insert-maybe)
     (define-key map "`" 'skeleton-pair-insert-maybe)
     (define-key map "\"" 'skeleton-pair-insert-maybe)
-    (define-key map  ")" 'sh-electric-rparen)
-    (define-key map  "<" 'sh-electric-less)
-    (define-key map  "#" 'sh-electric-hash)
-
-    (substitute-key-definition 'complete-tag 'comint-dynamic-complete
-                              map (current-global-map))
-    (substitute-key-definition 'newline-and-indent 'sh-newline-and-indent
-                              map (current-global-map))
-    (substitute-key-definition 'delete-backward-char
-                              'backward-delete-char-untabify
-                              map (current-global-map))
+
+    (define-key map [remap complete-tag] 'comint-dynamic-complete)
+    (define-key map [remap newline-and-indent] 'sh-newline-and-indent)
+    (define-key map [remap delete-backward-char]
+      'backward-delete-char-untabify)
     (define-key map "\C-c:" 'sh-set-shell)
-    (substitute-key-definition 'beginning-of-defun
-                              'sh-beginning-of-compound-command
-                              map (current-global-map))
-    (substitute-key-definition 'backward-sentence 'sh-beginning-of-command
-                              map (current-global-map))
-    (substitute-key-definition 'forward-sentence 'sh-end-of-command
-                              map (current-global-map))
+    (define-key map [remap backward-sentence] 'sh-beginning-of-command)
+    (define-key map [remap forward-sentence] 'sh-end-of-command)
     (define-key map [menu-bar insert] (cons "Insert" menu-map))
     (define-key menu-map [sh-while]    '("While Loop" . sh-while))
     (define-key menu-map [sh-until]    '("Until Loop" . sh-until))
@@ -499,15 +490,13 @@ the car and cdr are the same symbol.")
 (defcustom sh-require-final-newline
   '((csh . t)
     (pdksh . t)
-    (rc eval . require-final-newline)
-    (sh eval . require-final-newline))
+    (rc . require-final-newline)
+    (sh . require-final-newline))
   "*Value of `require-final-newline' in Shell-Script mode buffers.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (const :tag "require" t)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
@@ -522,9 +511,7 @@ First grouping matches the variable name.  This is upto and including the `='
 sign.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice regexp
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
@@ -563,7 +550,13 @@ The actual command ends at the end of the first \\(grouping\\)."
 
 
 (defvar sh-here-document-word "EOF"
-  "Word to delimit here documents.")
+  "Word to delimit here documents.
+If the first character of this string is \"-\", this character will
+be removed from the string when it is used to close the here document.
+This convention is used by the Bash shell, for example, to indicate
+that leading tabs inside the here document should be ignored.
+Note that Emacs currently has no support for indenting inside here
+documents - you must insert literal tabs by hand.")
 
 (defvar sh-test
   '((sh "[  ]" . 3)
@@ -574,60 +567,61 @@ The actual command ends at the end of the first \\(grouping\\)."
 ;; customized this out of sheer bravado.  not for the faint of heart.
 ;; but it *did* have an asterisk in the docstring!
 (defcustom sh-builtins
-  '((bash eval sh-append posix
-         "alias" "bg" "bind" "builtin" "declare" "dirs" "enable" "fc" "fg"
-         "help" "history" "jobs" "kill" "let" "local" "popd" "pushd" "source"
-         "suspend" "typeset" "unalias")
+  '((bash sh-append posix
+         "." "alias" "bg" "bind" "builtin" "compgen" "complete"
+          "declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
+          "jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
+          "source" "suspend" "typeset" "unalias")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append shell
+    (bourne sh-append shell
            "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
            "times" "ulimit")
 
-    (csh eval sh-append shell
+    (csh sh-append shell
         "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
         "setenv" "source" "time" "unalias" "unhash")
 
-    (dtksh eval identity wksh)
+    (dtksh sh-append wksh)
 
     (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
        "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
 
-    (jsh eval sh-append sh
+    (jsh sh-append sh
         "bg" "fg" "jobs" "kill" "stop" "suspend")
 
-    (jcsh eval sh-append csh
+    (jcsh sh-append csh
          "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
 
-    (ksh88 eval sh-append bourne
+    (ksh88 sh-append bourne
           "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
           "typeset" "unalias" "whence")
 
-    (oash eval sh-append sh
+    (oash sh-append sh
          "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit"
          "oaed" "oahelp" "oainit" "pp" "ppfile" "scan" "scrollok" "wattr"
          "wclear" "werase" "win" "wmclose" "wmmessage" "wmopen" "wmove"
          "wmtitle" "wrefresh")
 
-    (pdksh eval sh-append ksh88
+    (pdksh sh-append ksh88
           "bind")
 
-    (posix eval sh-append sh
+    (posix sh-append sh
           "command")
 
     (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
        "whatis")
 
-    (sh eval sh-append bourne
+    (sh sh-append bourne
        "hash" "test" "type")
 
     ;; The next entry is only used for defining the others
     (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
 
-    (wksh eval sh-append ksh88
+    (wksh sh-append ksh88
          "Xt[A-Z][A-Za-z]*")
 
-    (zsh eval sh-append ksh88
+    (zsh sh-append ksh88
         "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs"
         "disable" "disown" "echotc" "enable" "functions" "getln" "hash"
         "history" "integer" "limit" "local" "log" "popd" "pushd" "r"
@@ -639,109 +633,112 @@ Note that on some systems not all builtins are available or some are
 implemented as aliases.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 
 (defcustom sh-leading-keywords
-  '((csh "else")
+  '((bash sh-append sh
+          "time")
+
+    (csh "else")
 
     (es "true" "unwind-protect" "whatis")
 
     (rc "else")
 
-    (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
+    (sh "!" "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
   "*List of keywords that may be immediately followed by a builtin or keyword.
 Given some confusion between keywords and builtins depending on shell and
 system, the distinction here has been based on whether they influence the
 flow of control or syntax.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 (defcustom sh-other-keywords
-  '((bash eval sh-append bourne
-         "bye" "logout")
+  '((bash sh-append bourne
+         "bye" "logout" "select")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append sh
+    (bourne sh-append sh
            "function")
 
-    (csh eval sh-append shell
+    (csh sh-append shell
         "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
         "if" "logout" "onintr" "repeat" "switch" "then" "while")
 
     (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
        "return" "throw" "while")
 
-    (ksh88 eval sh-append bourne
+    (ksh88 sh-append bourne
           "select")
 
     (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
        "while")
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        "done" "esac" "fi" "for" "in" "return")
 
     ;; The next entry is only used for defining the others
     (shell "break" "case" "continue" "exec" "exit")
 
-    (zsh eval sh-append bash
+    (zsh sh-append bash
         "select"))
   "*List of keywords not in `sh-leading-keywords'.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 
 (defvar sh-variables
-  '((bash eval sh-append sh
-         "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_VERSION"
-         "cdable_vars" "ENV" "EUID" "FCEDIT" "FIGNORE" "glob_dot_filenames"
-         "histchars" "HISTFILE" "HISTFILESIZE" "history_control" "HISTSIZE"
-         "hostname_completion_file" "HOSTTYPE" "IGNOREEOF" "ignoreeof"
-         "LINENO" "MAIL_WARNING" "noclobber" "nolinks" "notify"
-         "no_exit_on_failed_exec" "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "PPID"
-         "PROMPT_COMMAND" "PS4" "pushd_silent" "PWD" "RANDOM" "REPLY"
-         "SECONDS" "SHLVL" "TMOUT" "UID")
-
-    (csh eval sh-append shell
+  '((bash sh-append sh
+         "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_ENV"
+         "BASH_VERSINFO" "BASH_VERSION" "cdable_vars" "COMP_CWORD"
+         "COMP_LINE" "COMP_POINT" "COMP_WORDS" "COMPREPLY" "DIRSTACK"
+         "ENV" "EUID" "FCEDIT" "FIGNORE" "FUNCNAME"
+         "glob_dot_filenames" "GLOBIGNORE" "GROUPS" "histchars"
+         "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
+         "HISTIGNORE" "history_control" "HISTSIZE"
+         "hostname_completion_file" "HOSTFILE" "HOSTTYPE" "IGNOREEOF"
+         "ignoreeof" "INPUTRC" "LINENO" "MACHTYPE" "MAIL_WARNING"
+         "noclobber" "nolinks" "notify" "no_exit_on_failed_exec"
+         "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
+         "PPID" "POSIXLY_CORRECT" "PROMPT_COMMAND" "PS3" "PS4"
+         "pushd_silent" "PWD" "RANDOM" "REPLY" "SECONDS" "SHELLOPTS"
+         "SHLVL" "TIMEFORMAT" "TMOUT" "UID")
+
+    (csh sh-append shell
         "argv" "cdpath" "child" "echo" "histchars" "history" "home"
         "ignoreeof" "mail" "noclobber" "noglob" "nonomatch" "path" "prompt"
         "shell" "status" "time" "verbose")
 
-    (es eval sh-append shell
+    (es sh-append shell
        "apid" "cdpath" "CDPATH" "history" "home" "ifs" "noexport" "path"
        "pid" "prompt" "signals")
 
-    (jcsh eval sh-append csh
+    (jcsh sh-append csh
          "notify")
 
-    (ksh88 eval sh-append sh
+    (ksh88 sh-append sh
           "ENV" "ERRNO" "FCEDIT" "FPATH" "HISTFILE" "HISTSIZE" "LINENO"
           "OLDPWD" "PPID" "PS3" "PS4" "PWD" "RANDOM" "REPLY" "SECONDS"
           "TMOUT")
 
-    (oash eval sh-append sh
+    (oash sh-append sh
          "FIELD" "FIELD_MAX" "LAST_KEY" "OALIB" "PP_ITEM" "PP_NUM")
 
-    (rc eval sh-append shell
+    (rc sh-append shell
        "apid" "apids" "cdpath" "CDPATH" "history" "home" "ifs" "path" "pid"
        "prompt" "status")
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        "CDPATH" "IFS" "OPTARG" "OPTIND" "PS1" "PS2")
 
     ;; The next entry is only used for defining the others
@@ -750,7 +747,7 @@ See `sh-feature'."
           "LINES" "LOGNAME" "MAIL" "MAILCHECK" "MAILPATH" "PAGER" "PATH"
           "SHELL" "TERM" "TERMCAP" "TERMINFO" "VISUAL")
 
-    (tcsh eval sh-append csh
+    (tcsh sh-append csh
          "addsuffix" "ampm" "autocorrect" "autoexpand" "autolist"
          "autologout" "chase_symlinks" "correct" "dextract" "edit" "el"
          "fignore" "gid" "histlit" "HOST" "HOSTTYPE" "HPATH"
@@ -761,7 +758,7 @@ See `sh-feature'."
          "tperiod" "tty" "uid" "version" "visiblebell" "watch" "who"
          "wordchars")
 
-    (zsh eval sh-append ksh88
+    (zsh sh-append ksh88
         "BAUD" "bindcmds" "cdpath" "DIRSTACKSIZE" "fignore" "FIGNORE" "fpath"
         "HISTCHARS" "hostcmds" "hosts" "HOSTS" "LISTMAX" "LITHISTSIZE"
         "LOGCHECK" "mailpath" "manpath" "NULLCMD" "optcmds" "path" "POSTEDIT"
@@ -772,37 +769,54 @@ See `sh-feature'."
   "List of all shell variables available for completing read.
 See `sh-feature'.")
 
+\f
+;; Font-Lock support
+
+(defface sh-heredoc-face
+  '((((class color)
+      (background dark))
+     (:foreground "yellow" :weight bold))
+    (((class color)
+      (background light))
+     (:foreground "tan" ))
+    (t
+     (:weight bold)))
+  "Face to show a here-document"
+  :group 'sh-indentation)
+(defvar sh-heredoc-face 'sh-heredoc-face)
 
 
 (defvar sh-font-lock-keywords
-  '((csh eval sh-append shell
-        '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
-          font-lock-variable-name-face))
+  '((csh sh-append shell
+        ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
+          font-lock-variable-name-face))
 
-    (es eval sh-append executable-font-lock-keywords
-       '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
-         font-lock-variable-name-face))
+    (es sh-append executable-font-lock-keywords
+       ("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
+         font-lock-variable-name-face))
 
-    (rc eval identity es)
+    (rc sh-append es)
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        ;; Variable names.
-       '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
+       ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
          font-lock-variable-name-face)
        ;; Function names.
-       '("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
-       '("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
+       ("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
+       ("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
          (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))
 
     ;; The next entry is only used for defining the others
-    (shell eval sh-append executable-font-lock-keywords
-          '("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
-          '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
+    (shell sh-append executable-font-lock-keywords
+           ;; Using font-lock-string-face here confuses sh-get-indent-info.
+           ("\\\\$" 0 font-lock-warning-face)
+          ("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
+          ("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
             font-lock-variable-name-face))
-    (rpm eval sh-append rpm2
-        '("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
-    (rpm2 eval sh-append shell
-         '("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
+    (rpm sh-append rpm2
+        ("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
+    (rpm2 sh-append shell
+         ("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
   "Default expressions to highlight in Shell Script modes.  See `sh-feature'.")
 
 (defvar sh-font-lock-keywords-1
@@ -812,16 +826,132 @@ See `sh-feature'.")
 (defvar sh-font-lock-keywords-2 ()
   "Gaudy level highlighting for Shell Script modes.")
 
+;; These are used for the syntax table stuff (derived from cperl-mode).
+;; Note: parse-sexp-lookup-properties must be set to t for it to work.
+(defconst sh-st-punc (string-to-syntax "."))
+(defconst sh-st-symbol (string-to-syntax "_"))
+(defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
+
+(defconst sh-here-doc-open-re "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|\\s_\\)+\\).*\\(\n\\)")
+
+(defvar sh-here-doc-markers nil)
+(make-variable-buffer-local 'sh-here-doc-markers)
+(defvar sh-here-doc-re sh-here-doc-open-re)
+(make-variable-buffer-local 'sh-here-doc-re)
+
+(defun sh-font-lock-close-heredoc (bol eof indented)
+  "Determine the syntax of the \\n after an EOF.
+If non-nil INDENTED indicates that the EOF was indented."
+  (let* ((eof-re (if eof (regexp-quote eof) ""))
+         ;; A rough regexp that should find the opening <<EOF back.
+        (sre (concat "<<\\(-?\\)\\s-*['\"\\]?"
+                     ;; Use \s| to cheaply check it's an open-heredoc.
+                     eof-re "['\"]?\\([ \t|;&)<>].*\\)?\\s|"))
+        ;; A regexp that will find other EOFs.
+        (ere (concat "^" (if indented "[ \t]*") eof-re "\n"))
+        (start (save-excursion
+                 (goto-char bol)
+                 (re-search-backward (concat sre "\\|" ere) nil t))))
+    ;; If subgroup 1 matched, we found an open-heredoc, otherwise we first
+    ;; found a close-heredoc which makes the current close-heredoc inoperant.
+    (cond
+     ((when (and start (match-end 1)
+                (not (and indented (= (match-beginning 1) (match-end 1))))
+                (not (sh-in-comment-or-string (match-beginning 0))))
+       ;; Make sure our `<<' is not the EOF1 of a `cat <<EOF1 <<EOF2'.
+       (save-excursion
+         (goto-char start)
+         (setq start (line-beginning-position 2))
+         (while
+             (progn
+               (re-search-forward "<<") ; Skip ourselves.
+               (and (re-search-forward sh-here-doc-open-re start 'move)
+                    (goto-char (match-beginning 0))
+                    (sh-in-comment-or-string (point)))))
+         ;; No <<EOF2 found after our <<.
+         (= (point) start)))
+      sh-here-doc-syntax)
+     ((not (or start (save-excursion (re-search-forward sre nil t))))
+      ;; There's no <<EOF either before or after us,
+      ;; so we should remove ourselves from font-lock's keywords.
+      (setq sh-here-doc-markers (delete eof sh-here-doc-markers))
+      (setq sh-here-doc-re
+           (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
+                   (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))
+      nil))))
+
+(defun sh-font-lock-open-heredoc (start string)
+  "Determine the syntax of the \\n after a <<EOF.
+START is the position of <<.
+STRING is the actual word used as delimiter (f.ex. \"EOF\").
+INDENTED is non-nil if the here document's content (and the EOF mark) can
+be indented (i.e. a <<- was used rather than just <<)."
+  (unless (or (memq (char-before start) '(?< ?>))
+             (sh-in-comment-or-string start))
+    ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic
+    ;; font-lock keywords to detect the end of this here document.
+    (let ((str (replace-regexp-in-string "['\"]" "" string)))
+      (unless (member str sh-here-doc-markers)
+       (push str sh-here-doc-markers)
+       (setq sh-here-doc-re
+             (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
+                     (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))))
+    sh-here-doc-syntax))
+
+(defun sh-font-lock-here-doc (limit)
+  "Search for a heredoc marker."
+  ;; This looks silly, but it's because `sh-here-doc-re' keeps changing.
+  (re-search-forward sh-here-doc-re limit t))
+
+(defun sh-is-quoted-p (pos)
+  (and (eq (char-before pos) ?\\)
+       (not (sh-is-quoted-p (1- pos)))))
+
+(defun sh-font-lock-paren (start)
+  (save-excursion
+    (goto-char start)
+    ;; Skip through all patterns
+    (while
+       (progn
+         (forward-comment (- (point-max)))
+         ;; Skip through one pattern
+         (while
+             (or (/= 0 (skip-syntax-backward "w_"))
+                 (/= 0 (skip-chars-backward "?[]*@/\\"))
+                 (and (sh-is-quoted-p (1- (point)))
+                      (goto-char (- (point) 2)))
+                 (when (memq (char-before) '(?\" ?\'))
+                   (condition-case nil (progn (backward-sexp 1) t)
+                     (error nil)))))
+         (forward-comment (- (point-max)))
+         (when (eq (char-before) ?|)
+           (backward-char 1) t)))
+    (when (save-excursion (backward-char 2) (looking-at ";;\\|in"))
+      sh-st-punc)))
+
 (defconst sh-font-lock-syntactic-keywords
-  ;; Mark a `#' character as having punctuation syntax in a variable reference.
-  ;; Really we should do this properly.  From Chet Ramey and Brian Fox:
-  ;; "A `#' begins a comment when it is unquoted and at the beginning of a
-  ;; word.  In the shell, words are separated by metacharacters."
-  ;; To do this in a regexp would be slow as it would be anchored to the right.
-  ;; But I can't be bothered to write a function to do it properly and
-  ;; efficiently.  So we only do it properly for `#' in variable references and
-  ;; do it efficiently by anchoring the regexp to the left.
-  '(("\\${?[^}#\n\t ]*\\(##?\\)" 1 (1 . nil))))
+  ;; A `#' begins a comment when it is unquoted and at the beginning of a
+  ;; word.  In the shell, words are separated by metacharacters.
+  ;; The list of special chars is taken from the single-unix spec
+  ;; of the shell command language (under `quoting') but with `$' removed.
+  `(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 ,sh-st-symbol)
+    ;; Find HEREDOC starters and add a corresponding rule for the ender.
+    (sh-font-lock-here-doc
+     (2 (sh-font-lock-open-heredoc
+        (match-beginning 0) (match-string 1)) nil t)
+     (5 (sh-font-lock-close-heredoc
+        (match-beginning 0) (match-string 4)
+         (and (match-beginning 3) (/= (match-beginning 3) (match-end 3))))
+      nil t))
+    ;; Distinguish the special close-paren in `case'.
+    (")" 0 (sh-font-lock-paren (match-beginning 0)))))
+
+(defun sh-font-lock-syntactic-face-function (state)
+  (if (nth 3 state)
+      (if (char-valid-p (nth 3 state))
+         font-lock-string-face
+       sh-heredoc-face)
+    font-lock-comment-face))
 
 (defgroup sh-indentation nil
   "Variables controlling indentation in shell scripts.
@@ -945,12 +1075,12 @@ a number means align to that column, e.g. 0 means fist column."
          sh-symbol-list))
 
 (defcustom sh-indent-for-fi 0
-  "*How much to indent a fi relative to an if.   Usually 0."
+  "*How much to indent a fi relative to an if.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-done '0
-  "*How much to indent a done relative to its matching stmt.   Usually 0."
+  "*How much to indent a done relative to its matching stmt.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
@@ -967,7 +1097,7 @@ does not affect then else elif or fi statements themselves."
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-then '+
-  "*How much to indent an then relative to an if."
+  "*How much to indent a then relative to an if."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
@@ -1048,51 +1178,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"))
 
 
@@ -1113,13 +1206,6 @@ or preceded by punctuation characters like '-'."
 (defvar sh-indent-supported-here nil
   "Non-nil if we support indentation for the current buffer's shell type.")
 
-(defconst sh-electric-rparen-needed
-  '((sh . t))
-  "Non-nil if the shell type needs an electric handling of case alternatives.")
-
-(defvar sh-electric-rparen-needed-here nil
-  "Non-nil if the buffer needs an electric handling of case alternatives.")
-
 (defconst sh-var-list
   '(
     sh-basic-offset sh-first-lines-indent sh-indent-after-case
@@ -1153,9 +1239,6 @@ frequently editing existing scripts with different styles.")
 \f
 ;; mode-command and utility functions
 
-;;;###autoload
-(put 'sh-mode 'mode-class 'special)
-
 ;;;###autoload
 (defun sh-mode ()
   "Major mode for editing shell scripts.
@@ -1201,7 +1284,7 @@ buffer indents as it currently is indented.
 \\[sh-execute-region]   Have optional header and region be executed in a subshell.
 
 \\[sh-maybe-here-document]      Without prefix, following an unquoted < inserts here document.
-{, (, [, ', \", `
+\{, (, [, ', \", `
        Unless quoted with \\, insert the pairs {}, (), [], or '', \"\", ``.
 
 If you generally program a shell different from your login shell you can
@@ -1212,6 +1295,8 @@ If your shell gives error messages with line numbers, you can use \\[executable-
 with your script for an edit-interpret-debug cycle."
   (interactive)
   (kill-all-local-variables)
+  (setq major-mode 'sh-mode
+       mode-name "Shell-script")
   (use-local-map sh-mode-map)
   (make-local-variable 'skeleton-end-hook)
   (make-local-variable 'paragraph-start)
@@ -1233,19 +1318,7 @@ with your script for an edit-interpret-debug cycle."
   (make-local-variable 'sh-shell-variables-initialized)
   (make-local-variable 'imenu-generic-expression)
   (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
@@ -1254,13 +1327,13 @@ 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
@@ -1279,10 +1352,9 @@ with your script for an edit-interpret-debug cycle."
                 ((and buffer-file-name
                       (string-match "\\.m?spec$" buffer-file-name))
                  "rpm")))))
-    (if interpreter
-       (sh-set-shell interpreter nil nil)
-      (sh-set-shell sh-shell-file nil nil))
-    (run-hooks 'sh-mode-hook)))
+    (sh-set-shell (or interpreter sh-shell-file) nil nil))
+  (run-hooks 'sh-mode-hook))
+
 ;;;###autoload
 (defalias 'shell-script-mode 'sh-mode)
 
@@ -1300,21 +1372,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)))
@@ -1384,12 +1452,21 @@ This adds rules for comments and assignments."
 
 (defun sh-set-shell (shell &optional no-query-flag insert-flag)
   "Set this buffer's shell to SHELL (a string).
-Makes this script executable via `executable-set-magic', and sets up the
-proper starting #!-line, if INSERT-FLAG is non-nil.
+When used interactively, insert the proper starting #!-line,
+and make the visited file executable via `executable-set-magic',
+perhaps querying depending on the value of `executable-query'.
+
+When this function is called noninteractively, INSERT-FLAG (the third
+argument) controls whether to insert a #!-line and think about making
+the visited file executable, and NO-QUERY-FLAG (the second argument)
+controls whether to query about making the visited file executable.
+
 Calls the value of `sh-set-shell-hook' if set."
-  (interactive (list (completing-read "Name or path of shell: "
-                                     interpreter-mode-alist
-                                     (lambda (x) (eq (cdr x) 'sh-mode)))
+  (interactive (list (completing-read (format "Shell \(default %s\): "
+                                             sh-shell-file)
+                                     interpreter-mode-alist
+                                     (lambda (x) (eq (cdr x) 'sh-mode))
+                                     nil nil nil sh-shell-file)
                     (eq executable-query 'function)
                     t))
   (if (string-match "\\.exe\\'" shell)
@@ -1401,26 +1478,30 @@ Calls the value of `sh-set-shell-hook' if set."
       (setq sh-shell-file
            (executable-set-magic shell (sh-feature sh-shell-arg)
                                  no-query-flag insert-flag)))
-  (setq require-final-newline (sh-feature sh-require-final-newline)
-;;;    local-abbrev-table (sh-feature sh-abbrevs)
+  (let ((tem (sh-feature sh-require-final-newline)))
+    (unless (eq tem 'require-final-newline)
+      (setq require-final-newline tem)))
+  (setq
        comment-start-skip "#+[\t ]*"
+;;;    local-abbrev-table (sh-feature sh-abbrevs)
        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)))
+  (make-local-variable 'sh-mode-syntax-table)
+  (let ((tem (sh-feature sh-mode-syntax-table-input)))
+    (setq sh-mode-syntax-table
+         (if tem (apply 'sh-mode-syntax-table tem)
+           sh-mode-default-syntax-table)))
+  (set-syntax-table sh-mode-syntax-table)
   (dolist (var (sh-feature sh-variables))
     (sh-remember-variable var))
   (make-local-variable 'indent-line-function)
   (if (setq sh-indent-supported-here (sh-feature sh-indent-supported))
       (progn
        (message "Setting up indent for shell type %s" sh-shell)
-       (set (make-local-variable 'sh-electric-rparen-needed-here)
-            (sh-feature sh-electric-rparen-needed))
        (set (make-local-variable 'parse-sexp-lookup-properties) t)
-       (sh-scan-buffer)
        (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw))
        (let ((regexp (sh-feature sh-kws-for-done)))
          (if regexp
@@ -1439,7 +1520,7 @@ Calls the value of `sh-set-shell-hook' if set."
 
 
 
-(defun sh-feature (list &optional function)
+(defun sh-feature (alist &optional function)
   "Index ALIST by the current shell.
 If ALIST isn't a list where every element is a cons, it is returned as is.
 Else indexing follows an inheritance logic which works in two ways:
@@ -1448,46 +1529,43 @@ Else indexing follows an inheritance logic which works in two ways:
     the alist contains no value for the current shell.
     The ultimate default is always `sh'.
 
-  - If the value thus looked up is a list starting with `eval' its `cdr' is
-    first evaluated.  If that is also a list and the first argument is a
-    symbol in ALIST it is not evaluated, but rather recursively looked up in
-    ALIST to allow the function called to define the value for one shell to be
-    derived from another shell.  While calling the function, is the car of the
-    alist element is the current shell.
+  - If the value thus looked up is a list starting with `sh-append',
+    we call the function `sh-append' with the rest of the list as
+    arguments, and use the value.  However, the next element of the
+    list is not used as-is; instead, we look it up recursively
+    in ALIST to allow the function called to define the value for
+    one shell to be derived from another shell.
     The value thus determined is physically replaced into the alist.
 
 Optional FUNCTION is applied to the determined value and the result is cached
 in ALIST."
-  (or (if (consp list)
-         (let ((l list))
+  (or (if (consp alist)
+         (let ((l alist))
            (while (and l (consp (car l)))
              (setq l (cdr l)))
-           (if l list)))
+           (if l alist)))
       (if function
-         (cdr (assoc (setq function (cons sh-shell function)) list)))
+         (cdr (assoc (setq function (cons sh-shell function)) alist)))
       (let ((sh-shell sh-shell)
            elt val)
        (while (and sh-shell
-                   (not (setq elt (assq sh-shell list))))
+                   (not (setq elt (assq sh-shell alist))))
          (setq sh-shell (cdr (assq sh-shell sh-ancestor-alist))))
        ;; If the shell is not known, treat it as sh.
        (unless elt
-         (setq elt (assq 'sh list)))
+         (setq elt (assq 'sh alist)))
        (if (and (consp (setq val (cdr elt)))
-                (eq (car val) 'eval))
+                (memq (car val) '(sh-append sh-modify)))
            (setcdr elt
                    (setq val
-                         (eval (if (consp (setq val (cdr val)))
-                                   (let ((sh-shell (car (cdr val)))
-                                         function)
-                                     (if (assq sh-shell list)
-                                         (setcar (cdr val)
-                                                 (list 'quote
-                                                       (sh-feature list))))
-                                     val)
-                                 val)))))
+                         (apply (car val)
+                                (let ((sh-shell (car (cdr val))))
+                                   (if (assq sh-shell alist)
+                                       (sh-feature alist)
+                                     (eval sh-shell)))
+                                (cddr val)))))
        (if function
-           (nconc list
+           (nconc alist
                   (list (cons function
                               (setq sh-shell (car function)
                                     val (funcall (cdr function) val))))))
@@ -1495,45 +1573,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 (pop list) (pop list) table))
-  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)
@@ -1624,17 +1694,11 @@ region, clear header."
   (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2)))
 \f
 ;; Indentation stuff.
-(defun sh-must-be-shell-mode ()
-  "Signal an error if not in Shell-script mode."
-  (unless (eq major-mode 'sh-mode)
-    (error "This buffer is not in Shell-script mode")))
-
 (defun sh-must-support-indent ()
   "*Signal an error if the shell type for this buffer is not supported.
 Also, the buffer must be in Shell-script mode."
-  (sh-must-be-shell-mode)
   (unless sh-indent-supported-here
-    (error "This buffer's shell type is not supported for this command")))
+    (error "This buffer's shell does not support indentation through Emacs")))
 
 (defun sh-make-vars-local ()
   "Make the indentation variables local to this buffer.
@@ -1644,7 +1708,6 @@ variable `sh-make-vars-local' has been set to nil.
 To revert all these variables to the global values, use
 command `sh-reset-indent-vars-to-global-values'."
   (interactive)
-  (sh-must-be-shell-mode)
   (mapcar 'make-local-variable sh-var-list)
   (message "Indentation variable are now local."))
 
@@ -1652,7 +1715,6 @@ command `sh-reset-indent-vars-to-global-values'."
   "Reset local indentation variables to the global values.
 Then, if variable `sh-make-vars-local' is non-nil, make them local."
   (interactive)
-  (sh-must-be-shell-mode)
   (mapcar 'kill-local-variable sh-var-list)
   (if sh-make-vars-local
       (mapcar 'make-local-variable sh-var-list)))
@@ -1678,9 +1740,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))
@@ -1704,10 +1766,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.
@@ -1900,27 +1960,21 @@ STRING       This is ignored for the purposes of calculating
             what the indentation is based on."
   ;; See comments before `sh-kw'.
   (save-excursion
-    (let ((prev-kw nil)
-         (prev-stmt nil)
-         (have-result nil)
-         depth-bol depth-eol
+    (let ((have-result nil)
          this-kw
-         (state nil)
-         state-bol
-         (depth-prev-bol nil)
          start
-         func val
+         val
          (result nil)
-         prev-lines-indent
-         (prev-list nil)
-         (this-list nil)
          (align-point nil)
          prev-line-end x)
       (beginning-of-line)
       ;; Note: setting result to t means we are done and will return nil.
       ;;(This function never returns just t.)
       (cond
-       ((equal (get-text-property (point) 'syntax-table) sh-here-doc-syntax)
+       ((or (and (boundp 'font-lock-string-face) (not (bobp))
+                (eq (get-text-property (1- (point)) 'face)
+                    font-lock-string-face))
+           (eq (get-text-property (point) 'face) sh-heredoc-face))
        (setq result t)
        (setq have-result t))
        ((looking-at "\\s-*#")          ; was (equal this-kw "#")
@@ -1933,7 +1987,7 @@ STRING         This is ignored for the purposes of calculating
          (setq have-result nil)
          ))
        ) ;; cond
-      
+
       (unless have-result
        ;; Continuation lines are handled specially
        (if (sh-this-is-a-continuation)
@@ -1979,14 +2033,15 @@ STRING       This is ignored for the purposes of calculating
              (cond
               ((and (equal x ")")
                     (equal (get-text-property (1- (point)) 'syntax-table)
-                           sh-special-syntax))
+                           sh-st-punc))
                (sh-debug "Case label) here")
                (setq x 'case-label)
                (if (setq val (sh-check-rule 2 x))
                    (progn
                      (setq result (append result val))
                      (setq align-point (point))))
-               (forward-char -1)
+               (or (bobp)
+                   (forward-char -1))
                (skip-chars-forward "[a-z0-9]*?")
                )
               ((string-match "[])}]" x)
@@ -2026,19 +2081,19 @@ STRING       This is ignored for the purposes of calculating
        (sh-debug "No prev line!")
        (sh-debug "result: %s  align-point: %s" result align-point)
        )
-      
+
       (if align-point
          ;; was: (setq result (append result (list (list t align-point))))
          (setq result (append  (list (list t align-point)) result))
        )
       (sh-debug "result is now: %s" result)
-       
+
       (or result
          (if prev-line-end
              (setq result (list (list t prev-line-end)))
            (setq result (list (list '= 'sh-first-lines-indent)))
            ))
-       
+
       (if (eq result t)
          (setq result nil))
       (sh-debug  "result is: %s" result)
@@ -2111,19 +2166,19 @@ If INFO is supplied it is used, else it is calculated."
   "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)
   (save-excursion
     (beginning-of-line)
     (forward-comment (- (point-max)))
     (unless end (beginning-of-line))
     (when (and (not (bobp))
-              (equal (get-text-property (1- (point)) 'syntax-table)
-                     sh-here-doc-syntax))
-      (let ((p1 (previous-single-property-change (1- (point)) 'syntax-table)))
+              (equal (get-text-property (1- (point)) 'face)
+                     sh-heredoc-face))
+      (let ((p1 (previous-single-property-change (1- (point)) 'face)))
        (when p1
          (goto-char p1)
-         (forward-line -1)
-         (if end (end-of-line)))))
+         (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
@@ -2184,8 +2239,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 ()
@@ -2214,14 +2268,11 @@ we go to the end of the previous line and do not check for continuations."
   ;; Possible return values:
   ;;  nil  -  nothing
   ;; a string - possibly a keyword
-  ;; 
+  ;;
   (if (bolp)
       nil
-    (let ((going t)
-         c n
-         min-point
-         (start (point))
-         (found nil))
+    (let (c min-point
+         (start (point)))
       (save-restriction
        (narrow-to-region
        (if (sh-this-is-a-continuation)
@@ -2280,8 +2331,7 @@ If AND-MOVE is non-nil then move to end of word."
        (buffer-substring (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.
@@ -2443,17 +2493,12 @@ If INFO is supplied it is used, else it is calculated from current line."
 (defun sh-indent-line ()
   "Indent the current line."
   (interactive)
-  (sh-must-be-shell-mode)
-  (let ((indent (sh-calculate-indent)) shift-amt beg end
+  (let ((indent (sh-calculate-indent))
        (pos (- (point-max) (point))))
     (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))
+      (indent-line-to indent)
       ;; If initial point was within line's indentation,
       ;; position after the indentation.  Else stay at same point in text.
       (if (> (- (point-max) pos) (point))
@@ -2516,7 +2561,7 @@ for a new value for it."
   (sh-must-support-indent)
   (let* ((info (sh-get-indent-info))
         (var (sh-get-indent-var-for-line info))
-        val val0 new-val old-val indent-val)
+        val old-val indent-val)
     (if (stringp var)
        (message (format "Cannot set indent - %s" var))
       (setq old-val (symbol-value var))
@@ -2589,7 +2634,7 @@ unless optional argument ARG (the prefix when interactive) is non-nil."
        ((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))
@@ -2609,13 +2654,11 @@ unless optional argument ARG (the prefix when interactive) is non-nil."
 
 (defun sh-mark-init (buffer)
   "Initialize a BUFFER to be used by `sh-mark-line'."
-  (let ((main-buffer (current-buffer)))
-    (save-excursion
-      (set-buffer (get-buffer-create buffer))
-      (erase-buffer)
-      (occur-mode)
-      (setq occur-buffer main-buffer)
-      )))
+  (save-excursion
+    (set-buffer (get-buffer-create buffer))
+    (erase-buffer)
+    (occur-mode)
+    ))
 
 
 (defun sh-mark-line (message point buffer &optional add-linenum occur-point)
@@ -2625,7 +2668,6 @@ If ADD-LINENUM is non-nil the message is preceded by the line number.
 If OCCUR-POINT is non-nil then the line is marked as a new occurrence
 so that `occur-next' and `occur-prev' will work."
   (let ((m1 (make-marker))
-       (main-buffer (current-buffer))
        start
        (line ""))
     (when point
@@ -2637,7 +2679,6 @@ so that `occur-next' and `occur-prev' will work."
          (set-buffer (get-buffer buffer))
        (set-buffer (get-buffer-create buffer))
        (occur-mode)
-       (setq occur-buffer main-buffer)
        )
       (goto-char (point-max))
       (setq start (point))
@@ -2646,14 +2687,17 @@ so that `occur-next' and `occur-prev' will work."
          (setq occur-point (point)))
       (insert message)
       (if point
-         (put-text-property start (point) 'mouse-face 'highlight))
+         (add-text-properties
+          start (point)
+          '(mouse-face highlight
+            help-echo "mouse-2: go to the line where I learned this")))
       (insert "\n")
       (if point
          (progn
-           (put-text-property start (point) 'occur m1)
+           (put-text-property start (point) 'occur-target m1)
            (if occur-point
-               (put-text-property occur-point (1+ occur-point)
-                                  'occur-point t))
+               (put-text-property start occur-point
+                                  'occur-match t))
            ))
       )))
 
@@ -2662,8 +2706,8 @@ so that `occur-next' and `occur-prev' will work."
 ;; Is this really worth having?
 (defvar sh-learned-buffer-hook nil
   "*An abnormal hook, called with an alist of learned variables.")
-;;; Example of how to use sh-learned-buffer-hook
-;; 
+;; Example of how to use sh-learned-buffer-hook
+;;
 ;; (defun what-i-learned (list)
 ;;   (let ((p list))
 ;;     (save-excursion
@@ -2676,7 +2720,7 @@ so that `occur-next' and `occur-prev' will work."
 ;;     (setq p (cdr p)))
 ;;       (insert ")\n")
 ;;       )))
-;; 
+;;
 ;; (add-hook 'sh-learned-buffer-hook 'what-i-learned)
 
 
@@ -2712,7 +2756,6 @@ This command can often take a long time to run."
     (let ((learned-var-list nil)
          (out-buffer "*indent*")
          (num-diffs 0)
-         last-pos
          previous-set-info
          (max 17)
          vec
@@ -2868,19 +2911,12 @@ This command can often take a long time to run."
                 (format "Suggested sh-basic-offset:  %d" suggested))
               nil out-buffer))))
 
-      
+
       (setq learned-var-list
            (append (list (list 'sh-indent-comment comment-col (point-max)))
                    learned-var-list))
       (setq sh-indent-comment comment-col)
-      (let ((name (buffer-name))
-           (lines (if (and (eq (point-min) 1)
-                           (eq (point-max) (1+ (buffer-size))))
-                      ""
-                    (format "lines %d to %d of "
-                            (1+ (count-lines 1 (point-min)))
-                            (1+ (count-lines 1 (point-max))))))
-           )
+      (let ((name (buffer-name)))
        (sh-mark-line  "\nLearned variable settings:" nil out-buffer)
        (if arg
            ;; Set learned variables to symbolic rather than numeric
@@ -2925,12 +2961,9 @@ Return values:
   nil            - we couldn't find a reasonable one."
   (let* ((max (1- (length vec)))
         (i 1)
-        (totals (make-vector max 0))
-        (return nil)
-        j)
+        (totals (make-vector max 0)))
     (while (< i max)
       (aset totals i (+ (aref totals i) (* 4 (aref vec i))))
-      (setq j (/ i 2))
       (if (zerop (% i 2))
          (aset totals i (+ (aref totals i) (aref vec (/ i 2)))))
       (if (< (* i 2) max)
@@ -2978,337 +3011,7 @@ Return values:
                 (car (car x)))
        ;; result is nil here
        ))
-      result
-      )))
-
-
-;; 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 ((inhibit-modification-hooks t))
-    (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 ((inhibit-modification-hooks t))
-    (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)))
 
 ;; ========================================================================
 
@@ -3369,6 +3072,11 @@ This is always added to the end of the buffer."
 ;; You are welcome to add the syntax or even completely new statements as
 ;; appropriate for your favorite shell.
 
+(defconst sh-non-closing-paren
+  ;; If we leave it rear-sticky, calling `newline' ends up inserting a \n
+  ;; that inherits this property, which then confuses the indentation.
+  (propertize ")" 'syntax-table sh-st-punc 'rear-nonsticky t))
+
 (define-skeleton sh-case
   "Insert a case/switch statement.  See `sh-feature'."
   (csh "expression: "
@@ -3383,7 +3091,7 @@ This is always added to the end of the buffer."
        < "default:" \n
        > _ \n
        resume:
-       < < "endsw")
+       < < "endsw" \n)
   (es)
   (rc "expression: "
       > "switch( " str " ) {" \n
@@ -3395,56 +3103,50 @@ This is always added to the end of the buffer."
       "case *" > \n
       > _ \n
       resume:
-      ?} > )
+      ?\} > \n)
   (sh "expression: "
       > "case " str " in" \n
-      > (read-string "pattern: ")
-      (propertize ")" 'syntax-table sh-st-punc)
-      \n
-      > _ \n
-      ";;" \n
-      ( "other pattern, %s: "
-       > str (propertize ")" 'syntax-table sh-st-punc) \n
+      ( "pattern, %s: "
+       > str sh-non-closing-paren \n
        > _ \n
        ";;" \n)
-      > "*" (propertize ")" 'syntax-table sh-st-punc) \n
+      > "*" sh-non-closing-paren \n
       > _ \n
       resume:
-      "esac" > ))
+      "esac" > \n))
 
 (define-skeleton sh-for
   "Insert a for loop.  See `sh-feature'."
-  (csh eval sh-modify sh
+  (csh sh-modify sh
        1 ""
        2 "foreach "
        4 " ( "
        6 " )"
        15 '<
-       16 "end"
-       )
-  (es eval sh-modify rc
+       16 "end")
+  (es sh-modify rc
       4 " = ")
-  (rc eval sh-modify sh
+  (rc sh-modify sh
       2 "for( "
       6 " ) {"
-      15 ?} )
+      15 ?\} )
   (sh "Index variable: "
       > "for " str " in " _ "; do" \n
       > _ | ?$ & (sh-remember-variable str) \n
-      "done" > ))
+      "done" > \n))
 
 
 
 (define-skeleton sh-indexed-loop
   "Insert an indexed loop from 1 to n.  See `sh-feature'."
-  (bash eval identity posix)
+  (bash sh-modify posix)
   (csh "Index variable: "
        "@ " str " = 1" \n
        "while( $" str " <= " (read-string "upper limit: ") " )" \n
        > _ ?$ str \n
        "@ " str "++" \n
-       < "end")
-  (es eval sh-modify rc
+       < "end" \n)
+  (es sh-modify rc
       4 " =")
   (ksh88 "Index variable: "
         > "integer " str "=0" \n
@@ -3452,7 +3154,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 "
@@ -3460,19 +3162,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 ()
@@ -3511,12 +3213,10 @@ t means to return a list of all possible completions of STRING.
                              (cons name name)))
                          process-environment)
                  sh-shell-variables))))
-    (cond ((null code)
-          (try-completion string sh-shell-variables predicate))
-         ((eq code t)
-          (all-completions string sh-shell-variables predicate))
-         ((eq code 'lambda)
-          (assoc string sh-shell-variables)))))
+    (case code
+      ((nil) (try-completion string sh-shell-variables predicate))
+      (lambda (test-completion string sh-shell-variables predicate))
+      (t (all-completions string sh-shell-variables predicate)))))
 
 (defun sh-add (var delta)
   "Insert an addition of VAR and prefix DELTA for Bourne (type) shell."
@@ -3544,18 +3244,18 @@ t means to return a list of all possible completions of STRING.
 
 (define-skeleton sh-function
   "Insert a function definition.  See `sh-feature'."
-  (bash eval sh-modify ksh88
+  (bash sh-modify ksh88
        3 "() {")
   (ksh88 "name: "
         "function " str " {" \n
         > _ \n
-        < "}")
-  (rc eval sh-modify ksh88
+        < "}" \n)
+  (rc sh-modify ksh88
       1 "fn ")
   (sh ()
       "() {" \n
       > _ \n
-      < "}"))
+      < "}" \n))
 
 
 
@@ -3570,7 +3270,7 @@ 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
@@ -3580,7 +3280,7 @@ t means to return a list of all possible completions of STRING.
       "} {" > \n
       > _ \n
       resume:
-      ?} > )
+      ?\} > \n)
   (rc "condition: "
       > "if( " str " ) {" \n
       > _ \n
@@ -3590,8 +3290,7 @@ t means to return a list of all possible completions of STRING.
       "} else {" > \n
       > _ \n
       resume:
-      ?} >
-      )
+      ?\} > \n)
   (sh "condition: "
       '(setq input (sh-feature sh-test))
       > "if " str "; then" \n
@@ -3602,7 +3301,7 @@ t means to return a list of all possible completions of STRING.
       "else" > \n
       > \n
       resume:
-      "fi" > ))
+      "fi" > \n))
 
 
 
@@ -3611,11 +3310,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" > ))
+       "done" > \n))
 
 ;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
 
@@ -3626,16 +3325,15 @@ t means to return a list of all possible completions of STRING.
   (ksh88 "Index variable: "
         > "select " str " in " _ "; do" \n
         > ?$ str \n
-        "done" > )
-  (bash eval sh-append ksh88)
-  )
+        "done" > \n)
+  (bash sh-append ksh88))
 ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
 
 
 
 (define-skeleton sh-tmp-file
   "Insert code to setup temporary file handling.  See `sh-feature'."
-  (bash eval identity ksh88)
+  (bash sh-append ksh88)
   (csh (file-name-nondirectory (buffer-file-name))
        "set tmp = /tmp/" str ".$$" \n
        "onintr exit" \n _
@@ -3643,7 +3341,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
@@ -3652,16 +3350,16 @@ t means to return a list of all possible completions of STRING.
       "throw $e" \n
       "} {" > \n
       _ \n
-      ?} > \n
-      ?} > )
-  (ksh88 eval sh-modify sh
+      ?\} > \n
+      ?\} > \n)
+  (ksh88 sh-modify sh
         7 "EXIT")
   (rc (file-name-nondirectory (buffer-file-name))
       > "tmp = /tmp/" str ".$pid" \n
-      "fn sigexit { rm $tmp^* >[2]/dev/null }")
+      "fn sigexit { rm $tmp^* >[2]/dev/null }" \n)
   (sh (file-name-nondirectory (buffer-file-name))
       > "TMP=${TMPDIR:-/tmp}/" str ".$$" \n
-      "trap \"rm $TMP* 2>/dev/null\" " ?0))
+      "trap \"rm $TMP* 2>/dev/null\" " ?0 \n))
 
 
 
@@ -3671,32 +3369,32 @@ t means to return a list of all possible completions of STRING.
       '(setq input (sh-feature sh-test))
       > "until " str "; do" \n
       > _ \n
-      "done" > ))
+      "done" > \n))
 ;;;(put 'sh-until 'menu-enable '(sh-feature sh-until))
 
 
 
 (define-skeleton sh-while
   "Insert a while loop.  See `sh-feature'."
-  (csh eval sh-modify sh
+  (csh sh-modify sh
        2 ""
        3 "while( "
        5 " )"
        10 '<
-       11 "end" )
-  (es eval sh-modify sh
+       11 "end")
+  (es sh-modify sh
       3 "while { "
       5 " } {"
-      10 ?} )
-  (rc eval sh-modify sh
+      10 ?\} )
+  (rc sh-modify sh
       3 "while( "
       5 " ) {"
-      10 ?} )
+      10 ?\} )
   (sh "condition: "
       '(setq input (sh-feature sh-test))
       > "while " str "; do" \n
       > _ \n
-      "done" > ))
+      "done" > \n))
 
 
 
@@ -3704,7 +3402,7 @@ t means to return a list of all possible completions of STRING.
   "Insert a while getopts loop.  See `sh-feature'.
 Prompts for an options string which consists of letters for each recognized
 option followed by a colon `:' if the option accepts an argument."
-  (bash eval sh-modify sh
+  (bash sh-modify sh
        18 "${0##*/}")
   (csh nil
        "while( 1 )" \n
@@ -3728,12 +3426,12 @@ option followed by a colon `:' if the option accepts an argument."
        resume:
        < < "endsw" \n
        "shift" \n
-       < "end")
-  (ksh88 eval sh-modify sh
+       < "end" \n)
+  (ksh88 sh-modify sh
         16 "print"
         18 "${0##*/}"
-        36 "OPTIND-1")
-  (posix eval sh-modify sh
+        37 "OPTIND-1")
+  (posix sh-modify sh
         18 "$(basename $0)")
   (sh "optstring: "
       > "while getopts :" str " OPT; do" \n
@@ -3745,10 +3443,10 @@ option followed by a colon `:' if the option accepts an argument."
                    v2 "\"$OPTARG\"")
            (setq v1 (cdr v1)
                  v2 nil)))
-       > str "|+" str (propertize ")" 'syntax-table sh-st-punc) \n
+       > str "|+" str sh-non-closing-paren \n
        > _ v2 \n
        > ";;" \n)
-      > "*" (propertize ")" 'syntax-table sh-st-punc) \n
+      > "*" sh-non-closing-paren \n
       > "echo" " \"usage: " "`basename $0`"
       " [+-" '(setq v1 (point)) str
       '(save-excursion
@@ -3761,7 +3459,7 @@ option followed by a colon `:' if the option accepts an argument."
       "esac" >
       \n "done"
       > \n
-      "shift " (sh-add "OPTIND" -1)))
+      "shift " (sh-add "OPTIND" -1) \n))
 
 
 
@@ -3798,7 +3496,10 @@ The document is bounded by `sh-here-document-word'."
            (sh-quoted-p)
          (end-of-line 2))
        (newline)
-       (save-excursion (insert ?\n sh-here-document-word)))))
+       (save-excursion
+          (insert ?\n (substring
+                       sh-here-document-word
+                       (if (string-match "^-" sh-here-document-word) 1 0)))))))
 
 \f
 ;; various other commands
@@ -3843,4 +3544,5 @@ The document is bounded by `sh-here-document-word'."
 
 (provide 'sh-script)
 
+;;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
 ;;; sh-script.el ends here