(browse-url-browser-function)
[bpt/emacs.git] / lisp / progmodes / sh-script.el
index d467ed7..23d8374 100644 (file)
@@ -1,6 +1,6 @@
 ;;; sh-script.el --- shell-script editing commands for Emacs
 
-;; Copyright (C) 1993, 94, 95, 96, 97, 1999, 2001
+;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 1999, 2001, 2003, 2004, 2005
 ;;  Free Software Foundation, Inc.
 
 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; 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.
 ;;
 
 (eval-when-compile
   (require 'skeleton)
+  (require 'cl)
   (require 'comint))
 (require 'executable)
 
@@ -258,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
@@ -314,9 +315,7 @@ 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
@@ -354,24 +353,29 @@ 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].")
 
+(defvar sh-mode-abbrev-table nil)
+
+(define-abbrev-table 'sh-mode-abbrev-table ())
+
+
 ;; I turned off this feature because it doesn't permit typing commands
 ;; in the usual way without help.
 ;;(defvar sh-abbrevs
-;;  '((csh eval sh-abbrevs shell
+;;  '((csh sh-abbrevs shell
 ;;      "switch" 'sh-case
 ;;      "getopts" 'sh-while-getopts)
 
-;;    (es eval sh-abbrevs shell
+;;    (es sh-abbrevs shell
 ;;     "function" 'sh-function)
 
-;;    (ksh88 eval sh-abbrevs sh
+;;    (ksh88 sh-abbrevs sh
 ;;        "select" 'sh-select)
 
-;;    (rc eval sh-abbrevs shell
+;;    (rc sh-abbrevs shell
 ;;     "case" 'sh-case
 ;;     "function" 'sh-function)
 
-;;    (sh eval sh-abbrevs shell
+;;    (sh sh-abbrevs shell
 ;;     "case" 'sh-case
 ;;     "function" 'sh-function
 ;;     "until" 'sh-until
@@ -384,7 +388,7 @@ the car and cdr are the same symbol.")
 ;;        "tmpfile" sh-tmp-file
 ;;        "while" sh-while)
 
-;;    (zsh eval sh-abbrevs ksh88
+;;    (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
@@ -392,23 +396,43 @@ the car and cdr are the same symbol.")
 
 
 
-(easy-mmode-defsyntax sh-mode-syntax-table
-  '((?\# . "<")
-   (?\^l . ">#")
-   (?\n . ">#")
-   (?\" . "\"\"")
-   (?\' . "\"'")
-   (?\` . "\"`")
-   (?! . "_")
-   (?% . "_")
-   (?: . "_")
-   (?. . "_")
-   (?^ . "_")
-   (?~ . "_")
-   (?< . ".")
-   (?> . "."))
-  "Syntax-table used in Shell-Script mode.")
-
+(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 ()
+       ?\# "<"
+       ?\n ">#"
+       ?\" "\"\""
+       ?\' "\"'"
+       ?\` "\"`"
+       ;; ?$ might also have a ". p" syntax. Both "'" and ". p" seem
+       ;; to work fine. This is needed so that dabbrev-expand
+       ;; $VARNAME works.
+       ?$ "'"
+       ?! "_"
+       ?% "_"
+       ?: "_"
+       ?. "_"
+       ?^ "_"
+       ?~ "_"
+       ?, "_"
+       ?= "."
+       ?< "."
+       ?> ".")
+  "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))
@@ -428,6 +452,7 @@ the car and cdr are the same symbol.")
     (define-key map "\C-c=" 'sh-set-indent)
     (define-key map "\C-c<" 'sh-learn-line-indent)
     (define-key map "\C-c>" 'sh-learn-buffer-indent)
+    (define-key map "\C-c\C-\\" 'sh-backslash-region)
 
     (define-key map "=" 'sh-assignment)
     (define-key map "\C-c+" 'sh-add)
@@ -441,21 +466,13 @@ the car and cdr are the same symbol.")
     (define-key map "`" 'skeleton-pair-insert-maybe)
     (define-key map "\"" 'skeleton-pair-insert-maybe)
 
-    (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))
@@ -483,16 +500,13 @@ the car and cdr are the same symbol.")
 
 (defcustom sh-require-final-newline
   '((csh . t)
-    (pdksh . t)
-    (rc eval . require-final-newline)
-    (sh eval . require-final-newline))
+    (pdksh . t))
   "*Value of `require-final-newline' in Shell-Script mode buffers.
+\(SHELL . t) means use the value of `mode-require-final-newline' for SHELL.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (const :tag "require" t)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
@@ -507,9 +521,7 @@ First grouping matches the variable name.  This is upto and including the `='
 sign.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice regexp
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
@@ -547,8 +559,24 @@ The actual command ends at the end of the first \\(grouping\\)."
 
 
 
-(defvar sh-here-document-word "EOF"
-  "Word to delimit here documents.")
+(defcustom sh-here-document-word "EOF"
+  "Word to delimit here documents.
+If the first character of this string is \"-\", this is taken as
+part of the redirection operator, rather than part of the
+word (that is, \"<<-\" instead of \"<<\").  This is a feature
+used by some shells (for example Bash) to indicate that leading
+tabs inside the here document should be ignored.  In this case,
+Emacs indents the initial body and end of the here document with
+tabs, to the same level as the start (note that apart from this
+there is no support for indentation of here documents).  This
+will only work correctly if `sh-basic-offset' is a multiple of
+`tab-width'.
+
+Any quote characters or leading whitespace in the word are
+removed when closing the here document."
+  :type 'string
+  :group 'sh-script)
+
 
 (defvar sh-test
   '((sh "[  ]" . 3)
@@ -559,60 +587,61 @@ The actual command ends at the end of the first \\(grouping\\)."
 ;; customized this out of sheer bravado.  not for the faint of heart.
 ;; but it *did* have an asterisk in the docstring!
 (defcustom sh-builtins
-  '((bash eval sh-append posix
-         "alias" "bg" "bind" "builtin" "declare" "dirs" "enable" "fc" "fg"
-         "help" "history" "jobs" "kill" "let" "local" "popd" "pushd" "source"
-         "suspend" "typeset" "unalias")
+  '((bash sh-append posix
+         "." "alias" "bg" "bind" "builtin" "caller" "compgen" "complete"
+          "declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
+          "jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
+          "source" "suspend" "typeset" "unalias")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append shell
+    (bourne sh-append shell
            "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
            "times" "ulimit")
 
-    (csh eval sh-append shell
+    (csh sh-append shell
         "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
         "setenv" "source" "time" "unalias" "unhash")
 
-    (dtksh eval identity wksh)
+    (dtksh sh-append wksh)
 
     (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
        "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
 
-    (jsh eval sh-append sh
+    (jsh sh-append sh
         "bg" "fg" "jobs" "kill" "stop" "suspend")
 
-    (jcsh eval sh-append csh
+    (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"
@@ -624,109 +653,112 @@ Note that on some systems not all builtins are available or some are
 implemented as aliases.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 
 (defcustom sh-leading-keywords
-  '((csh "else")
+  '((bash sh-append sh
+          "time")
+
+    (csh "else")
 
     (es "true" "unwind-protect" "whatis")
 
     (rc "else")
 
-    (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
+    (sh "!" "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
   "*List of keywords that may be immediately followed by a builtin or keyword.
 Given some confusion between keywords and builtins depending on shell and
 system, the distinction here has been based on whether they influence the
 flow of control or syntax.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 (defcustom sh-other-keywords
-  '((bash eval sh-append bourne
-         "bye" "logout")
+  '((bash sh-append bourne
+         "bye" "logout" "select")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append sh
+    (bourne sh-append sh
            "function")
 
-    (csh eval sh-append shell
+    (csh sh-append shell
         "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
         "if" "logout" "onintr" "repeat" "switch" "then" "while")
 
     (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
        "return" "throw" "while")
 
-    (ksh88 eval sh-append bourne
+    (ksh88 sh-append bourne
           "select")
 
     (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
        "while")
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        "done" "esac" "fi" "for" "in" "return")
 
     ;; The next entry is only used for defining the others
     (shell "break" "case" "continue" "exec" "exit")
 
-    (zsh eval sh-append bash
+    (zsh sh-append bash
         "select"))
   "*List of keywords not in `sh-leading-keywords'.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
-                              (cons :format "Evaluate: %v"
-                                    (const :format "" eval)
-                                    sexp))))
+                              (sexp :format "Evaluate: %v"))))
   :group 'sh-script)
 
 
 
 (defvar sh-variables
-  '((bash eval sh-append sh
-         "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_VERSION"
-         "cdable_vars" "ENV" "EUID" "FCEDIT" "FIGNORE" "glob_dot_filenames"
-         "histchars" "HISTFILE" "HISTFILESIZE" "history_control" "HISTSIZE"
-         "hostname_completion_file" "HOSTTYPE" "IGNOREEOF" "ignoreeof"
-         "LINENO" "MAIL_WARNING" "noclobber" "nolinks" "notify"
-         "no_exit_on_failed_exec" "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "PPID"
-         "PROMPT_COMMAND" "PS4" "pushd_silent" "PWD" "RANDOM" "REPLY"
-         "SECONDS" "SHLVL" "TMOUT" "UID")
-
-    (csh eval sh-append shell
+  '((bash sh-append sh
+         "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_ENV"
+         "BASH_VERSINFO" "BASH_VERSION" "cdable_vars" "COMP_CWORD"
+         "COMP_LINE" "COMP_POINT" "COMP_WORDS" "COMPREPLY" "DIRSTACK"
+         "ENV" "EUID" "FCEDIT" "FIGNORE" "FUNCNAME"
+         "glob_dot_filenames" "GLOBIGNORE" "GROUPS" "histchars"
+         "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
+         "HISTIGNORE" "history_control" "HISTSIZE"
+         "hostname_completion_file" "HOSTFILE" "HOSTTYPE" "IGNOREEOF"
+         "ignoreeof" "INPUTRC" "LINENO" "MACHTYPE" "MAIL_WARNING"
+         "noclobber" "nolinks" "notify" "no_exit_on_failed_exec"
+         "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
+         "PPID" "POSIXLY_CORRECT" "PROMPT_COMMAND" "PS3" "PS4"
+         "pushd_silent" "PWD" "RANDOM" "REPLY" "SECONDS" "SHELLOPTS"
+         "SHLVL" "TIMEFORMAT" "TMOUT" "UID")
+
+    (csh sh-append shell
         "argv" "cdpath" "child" "echo" "histchars" "history" "home"
         "ignoreeof" "mail" "noclobber" "noglob" "nonomatch" "path" "prompt"
         "shell" "status" "time" "verbose")
 
-    (es eval sh-append shell
+    (es sh-append shell
        "apid" "cdpath" "CDPATH" "history" "home" "ifs" "noexport" "path"
        "pid" "prompt" "signals")
 
-    (jcsh eval sh-append csh
+    (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
@@ -735,7 +767,7 @@ See `sh-feature'."
           "LINES" "LOGNAME" "MAIL" "MAILCHECK" "MAILPATH" "PAGER" "PATH"
           "SHELL" "TERM" "TERMCAP" "TERMINFO" "VISUAL")
 
-    (tcsh eval sh-append csh
+    (tcsh sh-append csh
          "addsuffix" "ampm" "autocorrect" "autoexpand" "autolist"
          "autologout" "chase_symlinks" "correct" "dextract" "edit" "el"
          "fignore" "gid" "histlit" "HOST" "HOSTTYPE" "HPATH"
@@ -746,7 +778,7 @@ See `sh-feature'."
          "tperiod" "tty" "uid" "version" "visiblebell" "watch" "who"
          "wordchars")
 
-    (zsh eval sh-append ksh88
+    (zsh sh-append ksh88
         "BAUD" "bindcmds" "cdpath" "DIRSTACKSIZE" "fignore" "FIGNORE" "fpath"
         "HISTCHARS" "hostcmds" "hosts" "HOSTS" "LISTMAX" "LITHISTSIZE"
         "LOGCHECK" "mailpath" "manpath" "NULLCMD" "optcmds" "path" "POSTEDIT"
@@ -760,49 +792,62 @@ See `sh-feature'.")
 \f
 ;; Font-Lock support
 
-(defface sh-heredoc-face
-  '((((class color)
+(defface sh-heredoc
+  '((((min-colors 88) (class color)
+      (background dark))
+     (:foreground "yellow1" :weight bold))
+    (((class color)
       (background dark))
-     (:foreground "yellow" :bold t))
+     (:foreground "yellow" :weight bold))
     (((class color)
       (background light))
      (:foreground "tan" ))
     (t
-     (:bold t)))
+     (:weight bold)))
   "Face to show a here-document"
   :group 'sh-indentation)
-(defvar sh-heredoc-face 'sh-heredoc-face)
+;; backward-compatibility alias
+(put 'sh-heredoc-face 'face-alias 'sh-heredoc)
+(defvar sh-heredoc-face 'sh-heredoc)
 
+(defface sh-escaped-newline '((t :inherit font-lock-string-face))
+  "Face used for (non-escaped) backslash at end of a line in Shell-script mode."
+  :group 'sh-script
+  :version "22.1")
 
 (defvar sh-font-lock-keywords
-  '((csh eval sh-append shell
-        '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
-          font-lock-variable-name-face))
+  '((csh sh-append shell
+        ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
+          font-lock-variable-name-face))
 
-    (es eval sh-append executable-font-lock-keywords
-       '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
-         font-lock-variable-name-face))
+    (es sh-append executable-font-lock-keywords
+       ("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
+         font-lock-variable-name-face))
 
-    (rc eval identity es)
+    (rc sh-append es)
 
-    (sh eval sh-append shell
+    (sh sh-append shell
        ;; Variable names.
-       '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
+       ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
          font-lock-variable-name-face)
        ;; Function names.
-       '("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
-       '("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
-         (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))
+       ("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
+       ("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
+         (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t))
+       ("\\(?:^\\s *\\|[[();&|]\\s *\\|\\(?:\\s +-[ao]\\|if\\|else\\|then\\|while\\|do\\)\\s +\\)\\(!\\)"
+        1 font-lock-negation-char-face))
 
     ;; The next entry is only used for defining the others
-    (shell eval sh-append executable-font-lock-keywords
-          '("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
-          '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
+    (shell sh-append executable-font-lock-keywords
+           ;; Using font-lock-string-face here confuses sh-get-indent-info.
+           ("\\(^\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\)$" 3 'sh-escaped-newline)
+          ("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
+          ("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
             font-lock-variable-name-face))
-    (rpm eval sh-append rpm2
-        '("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
-    (rpm2 eval sh-append shell
-         '("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
+    (rpm sh-append rpm2
+        ("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
+    (rpm2 sh-append shell
+         ("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
   "Default expressions to highlight in Shell Script modes.  See `sh-feature'.")
 
 (defvar sh-font-lock-keywords-1
@@ -818,7 +863,7 @@ See `sh-feature'.")
 (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\\)")
+(defconst sh-here-doc-open-re "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\)+\\).*\\(\n\\)")
 
 (defvar sh-here-doc-markers nil)
 (make-variable-buffer-local 'sh-here-doc-markers)
@@ -889,6 +934,10 @@ be indented (i.e. a <<- was used rather than just <<)."
   ;; 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)
@@ -899,7 +948,9 @@ be indented (i.e. a <<- was used rather than just <<)."
          ;; Skip through one pattern
          (while
              (or (/= 0 (skip-syntax-backward "w_"))
-                 (/= 0 (skip-chars-backward "?*/"))
+                 (/= 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)))))
@@ -966,7 +1017,7 @@ Anything else means:   whenever we have a \"good guess\" as to the value."
   :group 'sh-indentation)
 
 (defcustom sh-popup-occur-buffer nil
-  "*Controls when  `sh-learn-buffer-indent' pops the *indent* buffer.
+  "*Controls when  `sh-learn-buffer-indent' pops the `*indent*' buffer.
 If t it is always shown.  If nil, it is shown only when there
 are conflicts."
   :type '(choice
@@ -995,7 +1046,7 @@ Can be set to a number, or to nil which means leave it as is."
 
 (defcustom sh-basic-offset 4
   "*The default indentation increment.
-This value is used for the + and - symbols in an indentation variable."
+This value is used for the `+' and `-' symbols in an indentation variable."
   :type 'integer
   :group 'sh-indentation)
 
@@ -1039,7 +1090,7 @@ a number means align to that column, e.g. 0 means fist column."
           :menu-tag "/   Indent left  half sh-basic-offset")))
 
 (defcustom sh-indent-for-else 0
-  "*How much to indent an else relative to an if.  Usually 0."
+  "*How much to indent an `else' relative to its `if'.  Usually 0."
   :type `(choice
          (integer :menu-tag "A number (positive=>indent right)"
                   :tag "A number")
@@ -1055,75 +1106,75 @@ 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 its `if'.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
-(defcustom sh-indent-for-done '0
-  "*How much to indent a done relative to its matching stmt.   Usually 0."
+(defcustom sh-indent-for-done 0
+  "*How much to indent a `done' relative to its matching stmt.  Usually 0."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-else '+
-  "*How much to indent a statement after an else statement."
+  "*How much to indent a statement after an `else' statement."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-if '+
-  "*How much to indent a statement after an if statement.
-This includes lines after else and elif statements, too, but
-does not affect then else elif or fi statements themselves."
+  "*How much to indent a statement after an `if' statement.
+This includes lines after `else' and `elif' statements, too, but
+does not affect the `else', `elif' or `fi' statements themselves."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
-(defcustom sh-indent-for-then '+
-  "*How much to indent an then relative to an if."
+(defcustom sh-indent-for-then 0
+  "*How much to indent a `then' relative to its `if'."
   :type `(choice ,@ sh-number-or-symbol-list )
   :group 'sh-indentation)
 
-(defcustom sh-indent-for-do '*
-  "*How much to indent a do statement.
-This is relative to the statement before the do, i.e. the
-while until or for statement."
+(defcustom sh-indent-for-do 0
+  "*How much to indent a `do' statement.
+This is relative to the statement before the `do', typically a
+`while', `until', `for', `repeat' or `select' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
-(defcustom sh-indent-after-do '*
-  "*How much to indent a line after a do statement.
-This is used when the do is the first word of the line.
-This is relative to the statement before the do, e.g. a
-while for repeat or select statement."
+(defcustom sh-indent-after-do '+
+  "*How much to indent a line after a `do' statement.
+This is used when the `do' is the first word of the line.
+This is relative to the statement before the `do', typically a
+`while', `until', `for', `repeat' or `select' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-loop-construct '+
   "*How much to indent a statement after a loop construct.
 
-This variable is used when the keyword \"do\" is on the same line as the
-loop statement (e.g.  \"until\", \"while\" or \"for\").
-If the do is on a line by itself, then `sh-indent-after-do' is used instead."
+This variable is used when the keyword `do' is on the same line as the
+loop statement (e.g., `until', `while' or `for').
+If the `do' is on a line by itself, then `sh-indent-after-do' is used instead."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 
 (defcustom sh-indent-after-done 0
-  "*How much to indent a statement after a \"done\" keyword.
-Normally this is 0, which aligns the \"done\" to the matching
+  "*How much to indent a statement after a `done' keyword.
+Normally this is 0, which aligns the `done' to the matching
 looping construct line.
-Setting it non-zero allows you to have the \"do\" statement on a line
+Setting it non-zero allows you to have the `do' statement on a line
 by itself and align the done under to do."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-case-label '+
   "*How much to indent a case label statement.
-This is relative to the line containing the case statement."
+This is relative to the line containing the `case' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-case-alt '++
   "*How much to indent statements after the case label.
-This is relative to the line containing the case statement."
+This is relative to the line containing the `case' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
@@ -1135,7 +1186,7 @@ This is relative to the line containing the case statement."
 
 (defcustom sh-indent-after-open '+
   "*How much to indent after a line with an opening parenthesis or brace.
-For an open paren after a function `sh-indent-after-function' is used."
+For an open paren after a function, `sh-indent-after-function' is used."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
@@ -1147,17 +1198,27 @@ For an open paren after a function `sh-indent-after-function' is used."
 ;; These 2 are for the rc shell:
 
 (defcustom sh-indent-after-switch '+
-  "*How much to indent a case statement relative to the switch statement.
+  "*How much to indent a `case' statement relative to the `switch' statement.
 This is for the rc shell."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-case '+
-  "*How much to indent a statement relative to the case statement.
+  "*How much to indent a statement relative to the `case' statement.
 This is for the rc shell."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
+(defcustom sh-backslash-column 48
+  "*Column in which `sh-backslash-region' inserts backslashes."
+  :type 'integer
+  :group 'sh)
+
+(defcustom sh-backslash-align t
+  "*If non-nil, `sh-backslash-region' will align backslashes."
+  :type 'boolean
+  :group 'sh)
+
 ;; Internal use - not designed to be changed by the user:
 
 (defun sh-mkword-regexpr (word)
@@ -1186,10 +1247,6 @@ 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.")
-
 (defconst sh-var-list
   '(
     sh-basic-offset sh-first-lines-indent sh-indent-after-case
@@ -1224,10 +1281,7 @@ frequently editing existing scripts with different styles.")
 ;; mode-command and utility functions
 
 ;;;###autoload
-(put 'sh-mode 'mode-class 'special)
-
-;;;###autoload
-(define-derived-mode sh-mode nil "Shell-script"
+(defun sh-mode ()
   "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.
@@ -1280,6 +1334,11 @@ 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)
+  (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)
   (make-local-variable 'paragraph-separate)
@@ -1332,9 +1391,10 @@ with your script for an edit-interpret-debug cycle."
           (cond ((looking-at "#![ \t]?\\([^ \t\n]*/bin/env[ \t]\\)?\\([^ \t\n]+\\)")
                  (match-string 2))
                 ((and buffer-file-name
-                      (string-match "\\.m?spec$" buffer-file-name))
+                      (string-match "\\.m?spec\\'" buffer-file-name))
                  "rpm")))))
-    (sh-set-shell (or interpreter sh-shell-file) nil nil)))
+    (sh-set-shell (or interpreter sh-shell-file) nil nil))
+  (run-mode-hooks 'sh-mode-hook))
 
 ;;;###autoload
 (defalias 'shell-script-mode 'sh-mode)
@@ -1433,12 +1493,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)
@@ -1450,14 +1519,23 @@ 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)))
+    (if (eq tem t)
+       (setq require-final-newline mode-require-final-newline)))
+  (setq
        comment-start-skip "#+[\t ]*"
+       local-abbrev-table sh-mode-abbrev-table
        mode-line-process (format "[%s]" sh-shell)
        sh-shell-variables nil
        sh-shell-variables-initialized nil
        imenu-generic-expression (sh-feature sh-imenu-generic-expression)
        imenu-case-fold-search nil)
+  (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)
@@ -1483,7 +1561,7 @@ Calls the value of `sh-set-shell-hook' if set."
 
 
 
-(defun sh-feature (list &optional function)
+(defun sh-feature (alist &optional function)
   "Index ALIST by the current shell.
 If ALIST isn't a list where every element is a cons, it is returned as is.
 Else indexing follows an inheritance logic which works in two ways:
@@ -1492,46 +1570,43 @@ Else indexing follows an inheritance logic which works in two ways:
     the alist contains no value for the current shell.
     The ultimate default is always `sh'.
 
-  - If the value thus looked up is a list starting with `eval' its `cdr' is
-    first evaluated.  If that is also a list and the first argument is a
-    symbol in ALIST it is not evaluated, but rather recursively looked up in
-    ALIST to allow the function called to define the value for one shell to be
-    derived from another shell.  While calling the function, is the car of the
-    alist element is the current shell.
+  - If the value thus looked up is a list starting with `sh-append',
+    we call the function `sh-append' with the rest of the list as
+    arguments, and use the value.  However, the next element of the
+    list is not used as-is; instead, we look it up recursively
+    in ALIST to allow the function called to define the value for
+    one shell to be derived from another shell.
     The value thus determined is physically replaced into the alist.
 
 Optional FUNCTION is applied to the determined value and the result is cached
 in ALIST."
-  (or (if (consp list)
-         (let ((l list))
+  (or (if (consp alist)
+         (let ((l alist))
            (while (and l (consp (car l)))
              (setq l (cdr l)))
-           (if l list)))
+           (if l alist)))
       (if function
-         (cdr (assoc (setq function (cons sh-shell function)) list)))
+         (cdr (assoc (setq function (cons sh-shell function)) alist)))
       (let ((sh-shell sh-shell)
            elt val)
        (while (and sh-shell
-                   (not (setq elt (assq sh-shell list))))
+                   (not (setq elt (assq sh-shell alist))))
          (setq sh-shell (cdr (assq sh-shell sh-ancestor-alist))))
        ;; If the shell is not known, treat it as sh.
        (unless elt
-         (setq elt (assq 'sh list)))
+         (setq elt (assq 'sh alist)))
        (if (and (consp (setq val (cdr elt)))
-                (eq (car val) 'eval))
+                (memq (car val) '(sh-append sh-modify)))
            (setcdr elt
                    (setq val
-                         (eval (if (consp (setq val (cdr val)))
-                                   (let ((sh-shell (car (cdr val)))
-                                         function)
-                                     (if (assq sh-shell list)
-                                         (setcar (cdr val)
-                                                 (list 'quote
-                                                       (sh-feature list))))
-                                     val)
-                                 val)))))
+                         (apply (car val)
+                                (let ((sh-shell (car (cdr val))))
+                                   (if (assq sh-shell alist)
+                                       (sh-feature alist)
+                                     (eval sh-shell)))
+                                (cddr val)))))
        (if function
-           (nconc list
+           (nconc alist
                   (list (cons function
                               (setq sh-shell (car function)
                                     val (funcall (cdr function) val))))))
@@ -1660,15 +1735,9 @@ 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 (derived-mode-p '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 does not support indentation through Emacs")))
 
@@ -1680,7 +1749,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."))
 
@@ -1688,7 +1756,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)))
@@ -1934,28 +2001,20 @@ 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
-       ((or (and (boundp 'font-lock-string-face)
-                (eq (get-text-property (point) 'face) font-lock-string-face))
+       ((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))
@@ -2022,7 +2081,8 @@ STRING         This is ignored for the purposes of calculating
                    (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)
@@ -2147,7 +2207,6 @@ 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)))
@@ -2233,7 +2292,7 @@ we go to the end of the previous line and do not check for continuations."
        (if (looking-at "[\"'`]")
            (sh-safe-forward-sexp)
          ;; (> (skip-chars-forward "^ \t\n\"'`") 0)
-         (> (skip-chars-forward "-_a-zA-Z\$0-9") 0)
+         (> (skip-chars-forward "-_a-zA-Z$0-9") 0)
          ))
     (buffer-substring start (point))
     ))
@@ -2253,11 +2312,8 @@ we go to the end of the previous line and do not check for continuations."
   ;;
   (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)
@@ -2357,7 +2413,7 @@ Optional parameter DEPTH (usually 1) says how many to look for."
 (defun sh-var-value (var &optional ignore-error)
   "Return the value of variable VAR, interpreting symbols.
 It can also return t or nil.
-If an illegal value is found, throw an error unless Optional argument
+If an invalid value is found, throw an error unless Optional argument
 IGNORE-ERROR is non-nil."
   (let ((val (symbol-value var)))
     (cond
@@ -2478,17 +2534,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))
@@ -2551,7 +2602,7 @@ for a new value for it."
   (sh-must-support-indent)
   (let* ((info (sh-get-indent-info))
         (var (sh-get-indent-var-for-line info))
-        val val0 new-val old-val indent-val)
+        val old-val indent-val)
     (if (stringp var)
        (message (format "Cannot set indent - %s" var))
       (setq old-val (symbol-value var))
@@ -2644,13 +2695,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)
@@ -2660,7 +2709,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
@@ -2672,7 +2720,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))
@@ -2688,10 +2735,10 @@ so that `occur-next' and `occur-prev' will work."
       (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))
            ))
       )))
 
@@ -2750,7 +2797,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
@@ -2911,14 +2957,7 @@ This command can often take a long time to run."
            (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
@@ -2963,12 +3002,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)
@@ -3077,6 +3113,11 @@ This is always added to the end of the buffer."
 ;; You are welcome to add the syntax or even completely new statements as
 ;; appropriate for your favorite shell.
 
+(defconst sh-non-closing-paren
+  ;; If we leave it rear-sticky, calling `newline' ends up inserting a \n
+  ;; that inherits this property, which then confuses the indentation.
+  (propertize ")" 'syntax-table sh-st-punc 'rear-nonsticky t))
+
 (define-skeleton sh-case
   "Insert a case/switch statement.  See `sh-feature'."
   (csh "expression: "
@@ -3106,32 +3147,27 @@ This is always added to the end of the buffer."
       ?\} > \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" > \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
+  (es sh-modify rc
       4 " = ")
-  (rc eval sh-modify sh
+  (rc sh-modify sh
       2 "for( "
       6 " ) {"
       15 ?\} )
@@ -3144,14 +3180,14 @@ This is always added to the end of the buffer."
 
 (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" \n)
-  (es eval sh-modify rc
+  (es sh-modify rc
       4 " =")
   (ksh88 "Index variable: "
         > "integer " str "=0" \n
@@ -3218,12 +3254,10 @@ t means to return a list of all possible completions of STRING.
                              (cons name name)))
                          process-environment)
                  sh-shell-variables))))
-    (cond ((null code)
-          (try-completion string sh-shell-variables predicate))
-         ((eq code t)
-          (all-completions string sh-shell-variables predicate))
-         ((eq code 'lambda)
-          (assoc string sh-shell-variables)))))
+    (case code
+      ((nil) (try-completion string sh-shell-variables predicate))
+      (lambda (test-completion string sh-shell-variables predicate))
+      (t (all-completions string sh-shell-variables predicate)))))
 
 (defun sh-add (var delta)
   "Insert an addition of VAR and prefix DELTA for Bourne (type) shell."
@@ -3231,7 +3265,7 @@ t means to return a list of all possible completions of STRING.
    (let ((sh-add-buffer (current-buffer)))
      (list (completing-read "Variable: " 'sh-add-completer)
           (prefix-numeric-value current-prefix-arg))))
-  (insert (sh-feature '((bash . "$[ ")
+  (insert (sh-feature '((bash . "$(( ")
                        (ksh88 . "$(( ")
                        (posix . "$(( ")
                        (rc . "`{expr $")
@@ -3240,7 +3274,7 @@ t means to return a list of all possible completions of STRING.
          (sh-remember-variable var)
          (if (< delta 0) " - " " + ")
          (number-to-string (abs delta))
-         (sh-feature '((bash . " ]")
+         (sh-feature '((bash . " ))")
                        (ksh88 . " ))")
                        (posix . " ))")
                        (rc . "}")
@@ -3251,13 +3285,13 @@ 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
         < "}" \n)
-  (rc eval sh-modify ksh88
+  (rc sh-modify ksh88
       1 "fn ")
   (sh ()
       "() {" \n
@@ -3333,14 +3367,14 @@ t means to return a list of all possible completions of STRING.
         > "select " str " in " _ "; do" \n
         > ?$ str \n
         "done" > \n)
-  (bash eval sh-append ksh88))
+  (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 _
@@ -3359,7 +3393,7 @@ t means to return a list of all possible completions of STRING.
       _ \n
       ?\} > \n
       ?\} > \n)
-  (ksh88 eval sh-modify sh
+  (ksh88 sh-modify sh
         7 "EXIT")
   (rc (file-name-nondirectory (buffer-file-name))
       > "tmp = /tmp/" str ".$pid" \n
@@ -3383,17 +3417,17 @@ t means to return a list of all possible completions of STRING.
 
 (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
+  (es sh-modify sh
       3 "while { "
       5 " } {"
       10 ?\} )
-  (rc eval sh-modify sh
+  (rc sh-modify sh
       3 "while( "
       5 " ) {"
       10 ?\} )
@@ -3409,7 +3443,7 @@ t means to return a list of all possible completions of STRING.
   "Insert a while getopts loop.  See `sh-feature'.
 Prompts for an options string which consists of letters for each recognized
 option followed by a colon `:' if the option accepts an argument."
-  (bash eval sh-modify sh
+  (bash sh-modify sh
        18 "${0##*/}")
   (csh nil
        "while( 1 )" \n
@@ -3434,11 +3468,11 @@ option followed by a colon `:' if the option accepts an argument."
        < < "endsw" \n
        "shift" \n
        < "end" \n)
-  (ksh88 eval sh-modify sh
+  (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
@@ -3450,10 +3484,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
@@ -3466,7 +3500,8 @@ option followed by a colon `:' if the option accepts an argument."
       "esac" >
       \n "done"
       > \n
-      "shift " (sh-add "OPTIND" -1) \n))
+      "shift " (sh-add "OPTIND" -1) \n
+      "OPTIND=1" \n))
 
 
 
@@ -3484,7 +3519,6 @@ option followed by a colon `:' if the option accepts an argument."
             (match-string 1))))))
 
 
-
 (defun sh-maybe-here-document (arg)
   "Insert self.  Without prefix, following unquoted `<' inserts here document.
 The document is bounded by `sh-here-document-word'."
@@ -3495,15 +3529,21 @@ The document is bounded by `sh-here-document-word'."
       (save-excursion
        (backward-char 2)
        (sh-quoted-p))
-      (progn
+      (let ((tabs (if (string-match "\\`-" sh-here-document-word)
+                      (make-string (/ (current-indentation) tab-width) ?\t)
+                    ""))
+            (delim (replace-regexp-in-string "['\"]" ""
+                                            sh-here-document-word)))
        (insert sh-here-document-word)
        (or (eolp) (looking-at "[ \t]") (insert ? ))
        (end-of-line 1)
        (while
            (sh-quoted-p)
          (end-of-line 2))
-       (newline)
-       (save-excursion (insert ?\n sh-here-document-word)))))
+       (insert ?\n tabs)
+       (save-excursion
+          (insert ?\n tabs (replace-regexp-in-string
+                            "\\`-?[ \t]*" "" delim))))))
 
 \f
 ;; various other commands
@@ -3546,6 +3586,78 @@ The document is bounded by `sh-here-document-word'."
   (if (re-search-forward sh-end-of-command nil t)
       (goto-char (match-end 1))))
 
+;; Backslashification.  Stolen from make-mode.el.
+
+(defun sh-backslash-region (from to delete-flag)
+  "Insert, align, or delete end-of-line backslashes on the lines in the region.
+With no argument, inserts backslashes and aligns existing backslashes.
+With an argument, deletes the backslashes.
+
+This function does not modify the last line of the region if the region ends
+right at the start of the following line; it does not modify blank lines
+at the start of the region.  So you can put the region around an entire
+shell command and conveniently use this command."
+  (interactive "r\nP")
+  (save-excursion
+    (goto-char from)
+    (let ((column sh-backslash-column)
+          (endmark (make-marker)))
+      (move-marker endmark to)
+      ;; Compute the smallest column number past the ends of all the lines.
+      (if sh-backslash-align
+         (progn
+           (if (not delete-flag)
+               (while (< (point) to)
+                 (end-of-line)
+                 (if (= (preceding-char) ?\\)
+                     (progn (forward-char -1)
+                            (skip-chars-backward " \t")))
+                 (setq column (max column (1+ (current-column))))
+                 (forward-line 1)))
+           ;; Adjust upward to a tab column, if that doesn't push
+           ;; past the margin.
+           (if (> (% column tab-width) 0)
+               (let ((adjusted (* (/ (+ column tab-width -1) tab-width)
+                                  tab-width)))
+                 (if (< adjusted (window-width))
+                     (setq column adjusted))))))
+      ;; Don't modify blank lines at start of region.
+      (goto-char from)
+      (while (and (< (point) endmark) (eolp))
+        (forward-line 1))
+      ;; Add or remove backslashes on all the lines.
+      (while (and (< (point) endmark)
+                  ;; Don't backslashify the last line
+                  ;; if the region ends right at the start of the next line.
+                  (save-excursion
+                    (forward-line 1)
+                    (< (point) endmark)))
+        (if (not delete-flag)
+            (sh-append-backslash column)
+          (sh-delete-backslash))
+        (forward-line 1))
+      (move-marker endmark nil))))
+
+(defun sh-append-backslash (column)
+  (end-of-line)
+  ;; Note that "\\\\" is needed to get one backslash.
+  (if (= (preceding-char) ?\\)
+      (progn (forward-char -1)
+             (delete-horizontal-space)
+             (indent-to column (if sh-backslash-align nil 1)))
+    (indent-to column (if sh-backslash-align nil 1))
+    (insert "\\")))
+
+(defun sh-delete-backslash ()
+  (end-of-line)
+  (or (bolp)
+      (progn
+       (forward-char -1)
+       (if (looking-at "\\\\")
+           (delete-region (1+ (point))
+                          (progn (skip-chars-backward " \t") (point)))))))
+
 (provide 'sh-script)
 
+;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
 ;;; sh-script.el ends here