]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/sh-script.el
(compilation-start): Resurrect the version for systems that don't support
[gnu-emacs] / lisp / progmodes / sh-script.el
index 786229c4eb9f339ee86bf7c9779c3f24f8ce63ad..72533d887349fee981809957b8b2b29acb19e658 100644 (file)
@@ -1,7 +1,7 @@
 ;;; sh-script.el --- shell-script editing commands for Emacs
 
 ;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 1999, 2001, 2002,
-;;  2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
+;;  2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
 
 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Version: 2.0f
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 
 (defgroup sh nil
   "Shell programming utilities."
-  :group 'unix
   :group 'languages)
 
 (defgroup sh-script nil
     (wsh . sh)
     (zsh . ksh88)
     (rpm . sh))
-  "*Alist showing the direct ancestor of various shells.
+  "Alist showing the direct ancestor of various shells.
 This is the basis for `sh-feature'.  See also `sh-alias-alist'.
 By default we have the following three hierarchies:
 
@@ -276,7 +273,7 @@ sh          Bourne Shell
         '((ksh . ksh88)
            (bash2 . bash)
           (sh5 . sh)))
-  "*Alist for transforming shell names to what they really are.
+  "Alist for transforming shell names to what they really are.
 Use this where the name of the executable doesn't correspond to the type of
 shell it really is."
   :type '(repeat (cons symbol symbol))
@@ -302,7 +299,7 @@ shell it really is."
            (file-name-sans-extension (downcase shell)))))
    (getenv "SHELL")
    "/bin/sh")
-  "*The executable file name for the shell being programmed."
+  "The executable file name for the shell being programmed."
   :type 'string
   :group 'sh-script)
 
@@ -321,7 +318,7 @@ shell it really is."
     (wksh)
     ;; -f means don't run .zshrc.
     (zsh . "-f"))
-  "*Single argument string for the magic number.  See `sh-feature'."
+  "Single argument string for the magic number.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (const :tag "No Arguments" nil)
                               (string :tag "Arguments")
@@ -330,8 +327,8 @@ shell it really is."
 
 (defcustom sh-imenu-generic-expression
   `((sh
-     . ((nil "^\\s-*\\(function\\s-+\\)?\\([A-Za-z_][A-Za-z_0-9]+\\)\\s-*()" 2))))
-  "*Alist of regular expressions for recognizing shell function definitions.
+     . ((nil "^\\s-*\\(function\\s-+\\)?\\([[:alpha:]_][[:alnum:]_]+\\)\\s-*()" 2))))
+  "Alist of regular expressions for recognizing shell function definitions.
 See `sh-feature' and `imenu-generic-expression'."
   :type '(alist :key-type (symbol :tag "Shell")
                :value-type (alist :key-type (choice :tag "Title"
@@ -447,7 +444,7 @@ This is buffer-local in every such buffer.")
 
 (defvar sh-mode-map
   (let ((map (make-sparse-keymap))
-       (menu-map (make-sparse-keymap "Insert")))
+       (menu-map (make-sparse-keymap)))
     (define-key map "\C-c(" 'sh-function)
     (define-key map "\C-c\C-w" 'sh-while)
     (define-key map "\C-c\C-u" 'sh-until)
@@ -478,23 +475,83 @@ This is buffer-local in every such buffer.")
     (define-key map "\"" 'skeleton-pair-insert-maybe)
 
     (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)
     (define-key map [remap backward-sentence] 'sh-beginning-of-command)
     (define-key map [remap forward-sentence] 'sh-end-of-command)
-    (define-key map [menu-bar insert] (cons "Insert" menu-map))
-    (define-key menu-map [sh-while]    '("While Loop" . sh-while))
-    (define-key menu-map [sh-until]    '("Until Loop" . sh-until))
-    (define-key menu-map [sh-tmp-file] '("Temporary File" . sh-tmp-file))
-    (define-key menu-map [sh-select]   '("Select Statement" . sh-select))
-    (define-key menu-map [sh-repeat]   '("Repeat Loop" . sh-repeat))
-    (define-key menu-map [sh-getopts]  '("Options Loop" . sh-while-getopts))
-    (define-key menu-map [sh-indexed-loop] '("Indexed Loop" . sh-indexed-loop))
-    (define-key menu-map [sh-if]       '("If Statement" . sh-if))
-    (define-key menu-map [sh-for]      '("For Loop" . sh-for))
-    (define-key menu-map [sh-case]     '("Case Statement" . sh-case))
+    (define-key map [menu-bar sh-script] (cons "Sh-Script" menu-map))
+    (define-key menu-map [sh-learn-buffer-indent]
+      '(menu-item "Learn buffer indentation" sh-learn-buffer-indent
+                 :help "Learn how to indent the buffer the way it currently is."))
+    (define-key menu-map [sh-learn-line-indent]
+      '(menu-item "Learn line indentation" sh-learn-line-indent
+                 :help "Learn how to indent a line as it currently is indented"))
+    (define-key menu-map [sh-show-indent]
+      '(menu-item "Show indentation" sh-show-indent
+                 :help "Show the how the current line would be indented"))
+    (define-key menu-map [sh-set-indent]
+      '(menu-item "Set indentation" sh-set-indent
+                 :help "Set the indentation for the current line"))
+
+    (define-key menu-map [sh-pair]
+      '(menu-item "Insert braces and quotes in pairs"
+                 (lambda ()
+                   (interactive)
+                   (require 'skeleton)
+                   (setq skeleton-pair (not skeleton-pair)))
+                 :button (:toggle . (and (boundp 'skeleton-pair)
+                                         skeleton-pair))
+                 :help "Inserting a brace or quote automatically inserts the matching pair"))
+
+    (define-key menu-map [sh-s0] '("--"))
+    ;; Insert
+    (define-key menu-map [sh-function]
+      '(menu-item "Function..." sh-function
+                 :help "Insert a function definition"))
+    (define-key menu-map [sh-add]
+      '(menu-item "Addition..." sh-add
+                 :help "Insert an addition of VAR and prefix DELTA for Bourne (type) shell"))
+    (define-key menu-map [sh-until]
+      '(menu-item "Until Loop" sh-until
+                 :help "Insert an until loop"))
+    (define-key menu-map [sh-repeat]
+      '(menu-item "Repeat Loop" sh-repeat
+                 :help "Insert a repeat loop definition"))
+    (define-key menu-map [sh-while]
+      '(menu-item "While Loop" sh-while
+                 :help "Insert a while loop"))
+    (define-key menu-map [sh-getopts]
+      '(menu-item "Options Loop" sh-while-getopts
+                 :help "Insert a while getopts loop."))
+    (define-key menu-map [sh-indexed-loop]
+      '(menu-item "Indexed Loop" sh-indexed-loop
+                 :help "Insert an indexed loop from 1 to n."))
+    (define-key menu-map [sh-select]
+      '(menu-item "Select Statement" sh-select
+                 :help "Insert a select statement "))
+    (define-key menu-map [sh-if]
+      '(menu-item "If Statement" sh-if
+                 :help "Insert an if statement"))
+    (define-key menu-map [sh-for]
+      '(menu-item "For Loop" sh-for
+                 :help "Insert a for loop"))
+    (define-key menu-map [sh-case]
+      '(menu-item "Case Statement" sh-case
+                 :help "Insert a case/switch statement"))
+    (define-key menu-map [sh-s1] '("--"))
+    (define-key menu-map [sh-exec]
+      '(menu-item "Execute region" sh-execute-region
+                 :help "Pass optional header and region to a subshell for noninteractive execution"))
+    (define-key menu-map [sh-exec-interpret]
+      '(menu-item "Execute script..." executable-interpret
+                 :help "Run script with user-specified args, and collect output in a buffer"))
+    (define-key menu-map [sh-set-shell]
+      '(menu-item "Set shell type..." sh-set-shell
+                 :help "Set this buffer's shell to SHELL (a string)"))
+    (define-key menu-map [sh-backslash-region]
+      '(menu-item "Backslash region" sh-backslash-region
+                 :help "Insert, align, or delete end-of-line backslashes on the lines in the region."))
     map)
   "Keymap used in Shell-Script mode.")
 
@@ -507,7 +564,7 @@ This is buffer-local in every such buffer.")
   '(shell-dynamic-complete-environment-variable
     shell-dynamic-complete-command
     comint-dynamic-complete-filename)
-  "*Functions for doing TAB dynamic completion."
+  "Functions for doing TAB dynamic completion."
   :type '(repeat function)
   :group 'sh-script)
 
@@ -515,7 +572,7 @@ This is buffer-local in every such buffer.")
 (defcustom sh-require-final-newline
   '((csh . t)
     (pdksh . t))
-  "*Value of `require-final-newline' in Shell-Script mode buffers.
+  "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")
@@ -525,12 +582,12 @@ See `sh-feature'."
 
 
 (defcustom sh-assignment-regexp
-  '((csh . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \t]*[-+*/%^]?=")
+  '((csh . "\\<\\([[:alnum:]_]+\\)\\(\\[.+\\]\\)?[ \t]*[-+*/%^]?=")
     ;; actually spaces are only supported in let/(( ... ))
-    (ksh88 . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \t]*\\([-+*/%&|~^]\\|<<\\|>>\\)?=")
-    (rc . "\\<\\([a-zA-Z0-9_*]+\\)[ \t]*=")
-    (sh . "\\<\\([a-zA-Z0-9_]+\\)="))
-  "*Regexp for the variable name and what may follow in an assignment.
+    (ksh88 . "\\<\\([[:alnum:]_]+\\)\\(\\[.+\\]\\)?[ \t]*\\([-+*/%&|~^]\\|<<\\|>>\\)?=")
+    (rc . "\\<\\([[:alnum:]_*]+\\)[ \t]*=")
+    (sh . "\\<\\([[:alnum:]_]+\\)="))
+  "Regexp for the variable name and what may follow in an assignment.
 First grouping matches the variable name.  This is upto and including the `='
 sign.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
@@ -546,7 +603,7 @@ sign.  See `sh-feature'."
 (put 'sh-indentation 'safe-local-variable 'integerp)
 
 (defcustom sh-remember-variable-min 3
-  "*Don't remember variables less than this length for completing reads."
+  "Don't remember variables less than this length for completing reads."
   :type 'integer
   :group 'sh-script)
 
@@ -557,16 +614,16 @@ That command is also used for setting this variable.")
 
 
 (defcustom sh-beginning-of-command
-  "\\([;({`|&]\\|\\`\\|[^\\]\n\\)[ \t]*\\([/~a-zA-Z0-9:]\\)"
-  "*Regexp to determine the beginning of a shell command.
+  "\\([;({`|&]\\|\\`\\|[^\\]\n\\)[ \t]*\\([/~[:alnum:]:]\\)"
+  "Regexp to determine the beginning of a shell command.
 The actual command starts at the beginning of the second \\(grouping\\)."
   :type 'regexp
   :group 'sh-script)
 
 
 (defcustom sh-end-of-command
-  "\\([/~a-zA-Z0-9:]\\)[ \t]*\\([;#)}`|&]\\|$\\)"
-  "*Regexp to determine the end of a shell command.
+  "\\([/~[:alnum:]:]\\)[ \t]*\\([;#)}`|&]\\|$\\)"
+  "Regexp to determine the end of a shell command.
 The actual command ends at the end of the first \\(grouping\\)."
   :type 'regexp
   :group 'sh-script)
@@ -653,6 +710,7 @@ removed when closing the here document."
     (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
 
     (wksh sh-append ksh88
+          ;; FIXME: This looks too much like a regexp.  --Stef
          "Xt[A-Z][A-Za-z]*")
 
     (zsh sh-append ksh88
@@ -662,7 +720,7 @@ removed when closing the here document."
         "readonly" "rehash" "sched" "setopt" "source" "suspend" "true"
         "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" "vared"
         "which"))
-  "*List of all shell builtins for completing read and fontification.
+  "List of all shell builtins for completing read and fontification.
 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")
@@ -683,7 +741,7 @@ implemented as aliases.  See `sh-feature'."
     (rc "else")
 
     (sh "!" "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
-  "*List of keywords that may be immediately followed by a builtin or keyword.
+  "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'."
@@ -722,7 +780,7 @@ flow of control or syntax.  See `sh-feature'."
 
     (zsh sh-append bash
         "select"))
-  "*List of keywords not in `sh-leading-keywords'.
+  "List of keywords not in `sh-leading-keywords'.
 See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice (repeat string)
@@ -815,13 +873,13 @@ See `sh-feature'.")
      (:foreground "yellow" :weight bold))
     (((class color)
       (background light))
-     (:foreground "tan" ))
+     (:foreground "tan1" ))
     (t
      (:weight bold)))
   "Face to show a here-document"
   :group 'sh-indentation)
 
-;; These colours are probably icky.  It's just a placeholder though.
+;; These colors are probably icky.  It's just a placeholder though.
 (defface sh-quoted-exec
   '((((class color) (background dark))
      (:foreground "salmon"))
@@ -843,18 +901,18 @@ See `sh-feature'.")
 
 (defvar sh-font-lock-keywords-var
   '((csh sh-append shell
-        ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
+        ("\\${?[#?]?\\([[:alpha:]_][[:alnum:]_]*\\|0\\)" 1
           font-lock-variable-name-face))
 
     (es sh-append executable-font-lock-keywords
-       ("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
+       ("\\$#?\\([[:alpha:]_][[:alnum:]_]*\\|[0-9]+\\)" 1
          font-lock-variable-name-face))
 
     (rc sh-append es)
     (bash sh-append shell ("\\$(\\(\\sw+\\)" (1 'sh-quoted-exec t) ))
     (sh sh-append shell
        ;; Variable names.
-       ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
+       ("\\$\\({#?\\)?\\([[:alpha:]_][[:alnum:]_]*\\|[-#?@!]\\)" 2
          font-lock-variable-name-face)
        ;; Function names.
        ("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
@@ -867,8 +925,8 @@ See `sh-feature'.")
     (shell
            ;; 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
+          ("\\\\[^[:alnum:]]" 0 font-lock-string-face)
+          ("\\${?\\([[:alpha:]_][[:alnum:]_]*\\|[0-9]+\\|[$*_]\\)" 1
             font-lock-variable-name-face))
     (rpm sh-append rpm2
         ("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
@@ -950,7 +1008,7 @@ If non-nil INDENTED indicates that the EOF was indented."
 (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\").
+STRING is the actual word used as delimiter (e.g. \"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 <<).
 Point is at the beginning of the next line."
@@ -985,7 +1043,7 @@ Point is at the beginning of the next line."
   ;; This looks silly, but it's because `sh-here-doc-re' keeps changing.
   (re-search-forward sh-here-doc-re limit t))
 
-(defun sh-quoted-subshell (limit)
+(defun sh-font-lock-quoted-subshell (limit)
   "Search for a subshell embedded in a string.
 Find all the unescaped \" characters within said subshell, remembering that
 subshells can nest."
@@ -997,46 +1055,39 @@ subshells can nest."
             (eq ?\" (nth 3 (syntax-ppss))))
     ;; bingo we have a $( or a ` inside a ""
     (let ((char (char-after (point)))
-          (continue t)
-          (pos (point))
-          (data nil)      ;; value to put into match-data (and return)
-          (last nil)      ;; last char seen
-          (bq  (equal (match-string 1) "`")) ;; ` state flip-flop
-          (seen nil)                         ;; list of important positions
-          (nest 1))                          ;; subshell nesting level
-      (while (and continue char (<= pos limit))
-        ;; unescaped " inside a $( ... ) construct.
-        ;; state machine time...
-        ;; \ => ignore next char;
-        ;; ` => increase or decrease nesting level based on bq flag
-        ;; ) [where nesting > 0] => decrease nesting
-        ;; ( [where nesting > 0] => increase nesting
-        ;; ( [preceeded by $ ]   => increase nesting
-        ;; " [nesting <= 0 ]     => terminate, we're done.
-        ;; " [nesting >  0 ]     => remember this, it's not a proper "
-        ;; FIXME: don't count parens that appear within quotes.
-        (cond
-         ((eq ?\\ last) nil)
-         ((eq ?\` char) (setq nest (+ nest (if bq -1 1)) bq (not bq)))
-         ((and (> nest 0) (eq ?\) char))   (setq nest (1- nest)))
-         ((and (eq ?$ last) (eq ?\( char)) (setq nest (1+ nest)))
-         ((and (> nest 0) (eq ?\( char))   (setq nest (1+ nest)))
-         ((eq char ?\")
-          (if (>= 0 nest) (setq continue nil) (push pos seen))))
-        ;;(message "POS: %d [%d]" pos nest)
-        (setq last char
-              pos  (1+ pos)
-              char (char-after pos)) )
-      ;; FIXME: why construct a costly match data to pass to
-      ;; sh-apply-quoted-subshell rather than apply the highlight
-      ;; directly here?  -- Stef
-      (when seen
-        ;;(message "SEEN: %S" seen)
-        (setq data (list (current-buffer)))
-        (dolist(P seen)
-          (setq data (cons P (cons (1+ P) data))))
-        (store-match-data data))
-      data) ))
+          ;; `state' can be: double-quote, backquote, code.
+          (state (if (eq (char-before) ?`) 'backquote 'code))
+          ;; Stacked states in the context.
+          (states '(double-quote)))
+      (while (and state (progn (skip-chars-forward "^'\\\\\"`$()" limit)
+                               (< (point) limit)))
+        ;; unescape " inside a $( ... ) construct.
+        (case (char-after)
+          (?\' (forward-char 1) (skip-chars-forward "^'" limit))
+          (?\\ (forward-char 1))
+          (?\" (case state
+                 (double-quote (setq state (pop states)))
+                 (t (push state states) (setq state 'double-quote)))
+               (if state (put-text-property (point) (1+ (point))
+                                            'syntax-table '(1))))
+          (?\` (case state
+                 (backquote (setq state (pop states)))
+                 (t (push state states) (setq state 'backquote))))
+          (?\$ (if (not (eq (char-after (1+ (point))) ?\())
+                   nil
+                 (forward-char 1)
+                 (case state
+                   (t (push state states) (setq state 'code)))))
+          (?\( (case state
+                 (double-quote nil)
+                 (t (push state states) (setq state 'code))))
+          (?\) (case state
+                 (double-quote nil)
+                 (t (setq state (pop states)))))
+          (t (error "Internal error in sh-font-lock-quoted-subshell")))
+        (forward-char 1)))
+    t))
+
 
 (defun sh-is-quoted-p (pos)
   (and (eq (char-before pos) ?\\)
@@ -1092,17 +1143,6 @@ subshells can nest."
   (goto-char limit)
   nil)
 
-(defun sh-apply-quoted-subshell ()
-  "Apply the `sh-st-punc' syntax to all the matches in `match-data'.
-This is used to flag quote characters in subshell constructs inside strings
-\(which should therefore not be treated as normal quote characters\)"
-  (let ((m (match-data)) a b)
-    (while m
-      (setq a (car  m)
-            b (cadr m)
-            m (cddr m))
-      (put-text-property a b 'syntax-table sh-st-punc))) sh-st-punc)
-
 (defconst sh-font-lock-syntactic-keywords
   ;; A `#' begins a comment when it is unquoted and at the beginning of a
   ;; word.  In the shell, words are separated by metacharacters.
@@ -1115,7 +1155,7 @@ This is used to flag quote characters in subshell constructs inside strings
     ;; change the syntax, so we have to tell syntax-ppss that the states it
     ;; has just computed will need to be recomputed.
     (sh-font-lock-flush-syntax-ppss-cache)
-    ;; Make sure $@ and @? are correctly recognized as sexps.
+    ;; Make sure $@ and $? are correctly recognized as sexps.
     ("\\$\\([?@]\\)" 1 ,sh-st-symbol)
     ;; Find HEREDOC starters and add a corresponding rule for the ender.
     (sh-font-lock-here-doc
@@ -1129,13 +1169,12 @@ This is used to flag quote characters in subshell constructs inside strings
     (")" 0 (sh-font-lock-paren (match-beginning 0)))
     ;; highlight (possibly nested) subshells inside "" quoted regions correctly.
     ;; This should be at the very end because it uses syntax-ppss.
-    (sh-quoted-subshell
-     (1 (sh-apply-quoted-subshell) t t))))
+    (sh-font-lock-quoted-subshell)))
 
 (defun sh-font-lock-syntactic-face-function (state)
   (let ((q (nth 3 state)))
     (if q
-        (if (char-valid-p q)
+        (if (characterp q)
             (if (eq q ?\`) 'sh-quoted-exec font-lock-string-face)
           sh-heredoc-face)
       font-lock-comment-face)))
@@ -1151,17 +1190,17 @@ and command `sh-reset-indent-vars-to-global-values'."
 
 
 (defcustom sh-set-shell-hook nil
-  "*Hook run by `sh-set-shell'."
+  "Hook run by `sh-set-shell'."
   :type 'hook
   :group 'sh-script)
 
 (defcustom sh-mode-hook nil
-  "*Hook run by `sh-mode'."
+  "Hook run by `sh-mode'."
   :type 'hook
   :group 'sh-script)
 
 (defcustom sh-learn-basic-offset nil
-  "*When `sh-guess-basic-offset' should learn `sh-basic-offset'.
+  "When `sh-guess-basic-offset' should learn `sh-basic-offset'.
 
 nil mean:              never.
 t means:               only if there seems to be an obvious value.
@@ -1173,7 +1212,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
@@ -1182,7 +1221,7 @@ are conflicts."
   :group 'sh-indentation)
 
 (defcustom sh-blink t
-  "*If non-nil, `sh-show-indent' shows the line indentation is relative to.
+  "If non-nil, `sh-show-indent' shows the line indentation is relative to.
 The position on the line is not necessarily meaningful.
 In some cases the line will be the matching keyword, but this is not
 always the case."
@@ -1190,7 +1229,7 @@ always the case."
   :group 'sh-indentation)
 
 (defcustom sh-first-lines-indent 0
-  "*The indentation of the first non-blank non-comment line.
+  "The indentation of the first non-blank non-comment line.
 Usually 0 meaning first column.
 Can be set to a number, or to nil which means leave it as is."
   :type '(choice
@@ -1201,17 +1240,18 @@ Can be set to a number, or to nil which means leave it as is."
 
 
 (defcustom sh-basic-offset 4
-  "*The default indentation increment.
+  "The default indentation increment.
 This value is used for the `+' and `-' symbols in an indentation variable."
   :type 'integer
   :group 'sh-indentation)
+(put 'sh-basic-offset 'safe-local-variable 'integerp)
 
 (defcustom sh-indent-comment nil
-  "*How a comment line is to be indented.
+  "How a comment line is to be indented.
 nil means leave it as it is;
 t  means indent it as a normal line, aligning it to previous non-blank
    non-comment line;
-a number means align to that column, e.g. 0 means fist column."
+a number means align to that column, e.g. 0 means first column."
   :type '(choice
          (const :tag "Leave as is." nil)
          (const :tag "Indent as a normal line."  t)
@@ -1246,7 +1286,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 its `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")
@@ -1262,41 +1302,41 @@ 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 its `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."
+  "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.
+  "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 0
-  "*How much to indent a `then' relative to its `if'."
+  "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 0
-  "*How much to indent a `do' statement.
+  "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.
+  "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."
@@ -1304,7 +1344,7 @@ This is relative to the statement before the `do', typically a
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-loop-construct '+
-  "*How much to indent a statement after a 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').
@@ -1314,7 +1354,7 @@ If the `do' is on a line by itself, then `sh-indent-after-do' is used instead."
 
 
 (defcustom sh-indent-after-done 0
-  "*How much to indent a statement after a `done' keyword.
+  "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
@@ -1323,55 +1363,55 @@ by itself and align the done under to do."
   :group 'sh-indentation)
 
 (defcustom sh-indent-for-case-label '+
-  "*How much to indent a case label statement.
+  "How much to indent a case label 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.
+  "How much to indent statements after the case label.
 This is relative to the line containing the `case' statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 
 (defcustom sh-indent-for-continuation '+
-  "*How much to indent for a continuation statement."
+  "How much to indent for a continuation statement."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-open '+
-  "*How much to indent after a line with an opening parenthesis or brace.
+  "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."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 (defcustom sh-indent-after-function '+
-  "*How much to indent after a function line."
+  "How much to indent after a function line."
   :type `(choice ,@ sh-number-or-symbol-list)
   :group 'sh-indentation)
 
 ;; 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."
+  "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."
+  "If non-nil, `sh-backslash-region' will align backslashes."
   :type 'boolean
   :group 'sh)
 
@@ -1381,7 +1421,7 @@ This is for the rc shell."
   "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_]\\|$\\)"))
+  (concat word "\\([^-[:alnum:]_]\\|$\\)"))
 
 (defconst sh-re-done (sh-mkword-regexpr "done"))
 
@@ -1474,7 +1514,7 @@ buffer indents as it currently is indented.
 
 
 \\[backward-delete-char-untabify]       Delete backward one position, even if it was a tab.
-\\[sh-newline-and-indent]       Delete unquoted space and indent new line same as this one.
+\\[newline-and-indent]  Delete unquoted space and indent new line same as this one.
 \\[sh-end-of-command]   Go to end of successive commands.
 \\[sh-beginning-of-command]     Go to beginning of successive commands.
 \\[sh-set-shell]        Set this buffer's shell, and maybe its magic number.
@@ -1543,6 +1583,8 @@ with your script for an edit-interpret-debug cycle."
        skeleton-filter-function 'sh-feature
        skeleton-newline-indent-rigidly t
        sh-indent-supported-here nil)
+  (set (make-local-variable 'defun-prompt-regexp)
+       (concat "^\\(function[ \t]\\|[[:alnum:]]+[ \t]+()[ \t]+\\)"))
   (set (make-local-variable 'parse-sexp-ignore-comments) t)
   ;; Parse or insert magic number for exec, and set all variables depending
   ;; on the shell thus determined.
@@ -1925,14 +1967,14 @@ 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)
-  (mapcar 'make-local-variable sh-var-list)
+  (mapc 'make-local-variable sh-var-list)
   (message "Indentation variables are now local."))
 
 (defun 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)
-  (mapcar 'kill-local-variable sh-var-list)
+  (mapc 'kill-local-variable sh-var-list)
   (if sh-make-vars-local
       (mapcar 'make-local-variable sh-var-list)))
 
@@ -2268,6 +2310,7 @@ STRING         This is ignored for the purposes of calculating
                      (setq align-point (point))))
                (or (bobp)
                    (forward-char -1))
+                ;; FIXME: This charset looks too much like a regexp.  --Stef
                (skip-chars-forward "[a-z0-9]*?")
                )
               ((string-match "[])}]" x)
@@ -2476,7 +2519,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 "-_$[:alnum:]") 0)
          ))
     (buffer-substring start (point))
     ))
@@ -2553,7 +2596,7 @@ If AND-MOVE is non-nil then move to end of word."
        (goto-char where))
     (prog1
        (buffer-substring (point)
-                         (progn (skip-chars-forward "^ \t\n;&|()")(point)))
+                         (progn (skip-chars-forward "^ \t\n;&|")(point)))
       (unless and-move
        (goto-char start)))))
 
@@ -2740,7 +2783,7 @@ If INFO is supplied it is used, else it is calculated from current line."
     (if msg (message "%s" msg) (message nil))))
 
 (defun sh-show-indent (arg)
-  "Show the how the currently line would be indented.
+  "Show the how the current line would be indented.
 This tells you which variable, if any, controls the indentation of
 this line.
 If optional arg ARG is non-null (called interactively with a prefix),
@@ -3741,18 +3784,6 @@ The document is bounded by `sh-here-document-word'."
 
 
 
-(defun sh-newline-and-indent ()
-  "Strip unquoted whitespace, insert newline, and indent like current line."
-  (interactive "*")
-  (indent-to (prog1 (current-indentation)
-              (delete-region (point)
-                             (progn
-                               (or (zerop (skip-chars-backward " \t"))
-                                   (if (sh-quoted-p)
-                                       (forward-char)))
-                               (point)))
-              (newline))))
-
 (defun sh-beginning-of-command ()
   "Move point to successive beginnings of commands."
   (interactive)