emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] Changes to emacs/lisp/progmodes/sh-script.el [gnus-5_10-br


From: Miles Bader
Subject: [Emacs-diffs] Changes to emacs/lisp/progmodes/sh-script.el [gnus-5_10-branch]
Date: Sat, 04 Sep 2004 08:46:28 -0400

Index: emacs/lisp/progmodes/sh-script.el
diff -c /dev/null emacs/lisp/progmodes/sh-script.el:1.139.2.1
*** /dev/null   Sat Sep  4 12:02:16 2004
--- emacs/lisp/progmodes/sh-script.el   Sat Sep  4 12:01:12 2004
***************
*** 0 ****
--- 1,3553 ----
+ ;;; sh-script.el --- shell-script editing commands for Emacs
+ 
+ ;; Copyright (C) 1993, 94, 95, 96, 97, 1999, 2001, 03, 2004
+ ;;  Free Software Foundation, Inc.
+ 
+ ;; Author: Daniel Pfeiffer <address@hidden>
+ ;; Version: 2.0f
+ ;; Maintainer: FSF
+ ;; Keywords: languages, unix
+ 
+ ;; This file is part of GNU Emacs.
+ 
+ ;; 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 2, 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
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ;; 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., 59 Temple Place - Suite 330,
+ ;; Boston, MA 02111-1307, USA.
+ 
+ ;;; Commentary:
+ 
+ ;; Major mode for editing shell scripts.  Bourne, C and rc shells as well
+ ;; as various derivatives are supported and easily derived from.  Structured
+ ;; statements can be inserted with one command or abbrev.  Completion is
+ ;; available for filenames, variables known from the script, the shell and
+ ;; the environment as well as commands.
+ 
+ ;;; Known Bugs:
+ 
+ ;; - In Bourne the keyword `in' is not anchored to case, for, select ...
+ ;; - Variables in `"' strings aren't fontified because there's no way of
+ ;;   syntactically distinguishing those from `'' strings.
+ 
+ ;;            Indentation
+ ;;            ===========
+ ;; Indentation for rc and es modes is very limited, but for Bourne shells
+ ;; and its derivatives it is quite customizable.
+ ;;
+ ;; The following description applies to sh and derived shells (bash,
+ ;; zsh, ...).
+ ;;
+ ;; There are various customization variables which allow tailoring to
+ ;; a wide variety of styles.  Most of these variables are named
+ ;; sh-indent-for-XXX and sh-indent-after-XXX.  For example.
+ ;; sh-indent-after-if controls the indenting of a line following
+ ;; an if statement, and sh-indent-for-fi controls the indentation
+ ;; of the line containing the fi.
+ ;;
+ ;; You can set each to a numeric value, but it is often more convenient
+ ;; to a symbol such as `+' which uses the value of variable `sh-basic-offset'.
+ ;; By changing this one variable you can increase or decrease how much
+ ;; indentation there is.  Valid symbols:
+ ;;
+ ;;    +   Indent right by sh-basic-offset
+ ;;    -   Indent left  by sh-basic-offset
+ ;;    ++  Indent right twice sh-basic-offset
+ ;;    --  Indent left  twice sh-basic-offset
+ ;;    *   Indent right half sh-basic-offset
+ ;;    /   Indent left  half sh-basic-offset.
+ ;;
+ ;; There are 4 commands to help set the indentation variables:
+ ;;
+ ;; `sh-show-indent'
+ ;;    This shows what variable controls the indentation of the current
+ ;;    line and its value.
+ ;;
+ ;; `sh-set-indent'
+ ;;    This allows you to set the value of the variable controlling the
+ ;;    current line's indentation.  You can enter a number or one of a
+ ;;    number of special symbols to denote the value of sh-basic-offset,
+ ;;    or its negative, or half it, or twice it, etc.  If you've used
+ ;;    cc-mode this should be familiar.  If you forget which symbols are
+ ;;    valid simply press C-h at the prompt.
+ ;;
+ ;; `sh-learn-line-indent'
+ ;;    Simply make the line look the way you want it, then invoke this
+ ;;    command.  It will set the variable to the value that makes the line
+ ;;    indent like that.  If called with a prefix argument then it will set
+ ;;    the value to one of the symbols if applicable.
+ ;;
+ ;; `sh-learn-buffer-indent'
+ ;;    This is the deluxe function!  It "learns" the whole buffer (use
+ ;;    narrowing if you want it to process only part).  It outputs to a
+ ;;    buffer *indent* any conflicts it finds, and all the variables it has
+ ;;    learned.  This buffer is a sort of Occur mode buffer, allowing you to
+ ;;    easily find where something was set.  It is popped to automatically
+ ;;    if there are any conflicts found or if `sh-popup-occur-buffer' is
+ ;;    non-nil.
+ ;;    `sh-indent-comment' will be set if all comments follow  the same
+ ;;    pattern;  if they don't it will be set to nil.
+ ;;    Whether `sh-basic-offset' is set is determined by variable
+ ;;    `sh-learn-basic-offset'.
+ ;;
+ ;;    Unfortunately, `sh-learn-buffer-indent' can take a long time to run
+ ;;    (e.g. if there are large case statements).  Perhaps it does not make
+ ;;    sense to run it on large buffers: if lots of lines have different
+ ;;    indentation styles it will produce a lot of diagnostics in the
+ ;;    *indent* buffer; if there is a consistent style then running
+ ;;    `sh-learn-buffer-indent' on a small region of the buffer should
+ ;;    suffice.
+ ;;
+ ;;    Saving indentation values
+ ;;    -------------------------
+ ;; After you've learned the values in a buffer, how to you remember
+ ;; them?   Originally I had hoped that `sh-learn-buffer-indent'
+ ;; would make this unnecessary;  simply learn the values when you visit
+ ;; the buffer.
+ ;; You can do this automatically like this:
+ ;;   (add-hook 'sh-set-shell-hook 'sh-learn-buffer-indent)
+ ;;
+ ;; However...  `sh-learn-buffer-indent' is extremely slow,
+ ;; especially on large-ish buffer.  Also, if there are conflicts the
+ ;; "last one wins" which may not produce the desired setting.
+ ;;
+ ;; So...There is a minimal way of being able to save indentation values and
+ ;; to reload them in another buffer or at another point in time.
+ ;;
+ ;; Use `sh-name-style' to give a name to the indentation settings of
+ ;;    the current buffer.
+ ;; Use `sh-load-style' to load indentation settings for the current
+ ;;    buffer from a specific style.
+ ;; Use `sh-save-styles-to-buffer' to write all the styles to a buffer
+ ;;    in lisp code.  You can then store it in a file and later use
+ ;;    `load-file' to load it.
+ ;;
+ ;;    Indentation variables - buffer local or global?
+ ;;    ----------------------------------------------
+ ;; I think that often having them buffer-local makes sense,
+ ;; especially if one is using `sh-learn-buffer-indent'.  However, if
+ ;; a user sets values using customization, these changes won't appear
+ ;; to work if the variables are already local!
+ ;;
+ ;; To get round this, there is a variable `sh-make-vars-local' and 2
+ ;; functions: `sh-make-vars-local' and 
`sh-reset-indent-vars-to-global-values'.
+ ;;
+ ;; If `sh-make-vars-local' is non-nil, then these variables become
+ ;; buffer local when the mode is established.
+ ;; If this is nil, then the variables are global.  At any time you
+ ;; can make them local with the command `sh-make-vars-local'.
+ ;; Conversely, to update with the global values you can use the
+ ;; command `sh-reset-indent-vars-to-global-values'.
+ ;;
+ ;; This may be awkward, but the intent is to cover all cases.
+ ;;
+ ;;    Awkward things, pitfalls
+ ;;    ------------------------
+ ;; Indentation for a sh script is complicated for a number of reasons:
+ ;;
+ ;; 1. You can't format by simply looking at symbols, you need to look
+ ;;    at keywords.  [This is not the case for rc and es shells.]
+ ;; 2. The character ")" is used both as a matched pair "(" ... ")" and
+ ;;    as a stand-alone symbol (in a case alternative).  This makes
+ ;;    things quite tricky!
+ ;; 3. Here-documents in a script should be treated "as is", and when
+ ;;    they terminate we want to revert to the indentation of the line
+ ;;    containing the "<<" symbol.
+ ;; 4. A line may be continued using the "\".
+ ;; 5. The character "#" (outside a string) normally starts a comment,
+ ;;    but it doesn't in the sequence "$#"!
+ ;;
+ ;; To try and address points 2 3 and 5 I used a feature that cperl mode
+ ;; uses, that of a text's syntax property.  This, however, has 2
+ ;; disadvantages:
+ ;; 1. We need to scan the buffer to find which ")" symbols belong to a
+ ;;    case alternative, to find any here documents, and handle "$#".
+ ;; 2. Setting the text property makes the buffer modified.  If the
+ ;;    buffer is read-only buffer we have to cheat and bypass the read-only
+ ;;    status.  This is for cases where the buffer started read-only buffer
+ ;;    but the user issued `toggle-read-only'.
+ ;;
+ ;;    Bugs
+ ;;    ----
+ ;; - Indenting many lines is slow.  It currently does each line
+ ;;   independently, rather than saving state information.
+ ;;
+ ;; - `sh-learn-buffer-indent' is extremely slow.
+ ;;
+ ;; Richard Sharman <address@hidden>  June 1999.
+ 
+ ;;; Code:
+ 
+ ;; page 1:    variables and settings
+ ;; page 2:    indentation stuff
+ ;; page 3:    mode-command and utility functions
+ ;; page 4:    statement syntax-commands for various shells
+ ;; page 5:    various other commands
+ 
+ (eval-when-compile
+   (require 'skeleton)
+   (require 'cl)
+   (require 'comint))
+ (require 'executable)
+ 
+ 
+ 
+ (defgroup sh nil
+   "Shell programming utilities"
+   :group 'unix
+   :group 'languages)
+ 
+ (defgroup sh-script nil
+   "Shell script mode"
+   :group 'sh
+   :prefix "sh-")
+ 
+ 
+ (defcustom sh-ancestor-alist
+   '((ash . sh)
+     (bash . jsh)
+     (bash2 . jsh)
+     (dtksh . ksh)
+     (es . rc)
+     (itcsh . tcsh)
+     (jcsh . csh)
+     (jsh . sh)
+     (ksh . ksh88)
+     (ksh88 . jsh)
+     (oash . sh)
+     (pdksh . ksh88)
+     (posix . sh)
+     (tcsh . csh)
+     (wksh . ksh88)
+     (wsh . sh)
+     (zsh . ksh88)
+     (rpm . sh))
+   "*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:
+ 
+ csh           C Shell
+   jcsh                C Shell with Job Control
+   tcsh                Turbo C Shell
+     itcsh     ? Turbo C Shell
+ rc            Plan 9 Shell
+   es          Extensible Shell
+ sh            Bourne Shell
+   ash         ? Shell
+   jsh         Bourne Shell with Job Control
+     bash      GNU Bourne Again Shell
+     ksh88     Korn Shell '88
+       ksh     Korn Shell '93
+       dtksh   CDE Desktop Korn Shell
+       pdksh   Public Domain Korn Shell
+       wksh    Window Korn Shell
+       zsh     Z Shell
+   oash                SCO OA (curses) Shell
+   posix               IEEE 1003.2 Shell Standard
+   wsh         ? Shell"
+   :type '(repeat (cons symbol symbol))
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-alias-alist
+   (append (if (eq system-type 'gnu/linux)
+            '((csh . tcsh)
+              (ksh . pdksh)))
+        ;; for the time being
+        '((ksh . ksh88)
+            (bash2 . bash)
+          (sh5 . sh)))
+   "*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))
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-shell-file
+   (or
+    ;; On MSDOS and Windows, collapse $SHELL to lower-case and remove
+    ;; the executable extension, so comparisons with the list of
+    ;; known shells work.
+    (and (memq system-type '(ms-dos windows-nt))
+       (let* ((shell (getenv "SHELL"))
+              (shell-base
+               (and shell (file-name-nondirectory shell))))
+         ;; shell-script mode doesn't support DOS/Windows shells,
+         ;; so use the default instead.
+         (if (or (null shell)
+                 (member (downcase shell-base)
+                         '("command.com" "cmd.exe" "4dos.com" "ndos.com"
+                           "cmdproxy.exe")))
+             "/bin/sh"
+           (file-name-sans-extension (downcase shell)))))
+    (getenv "SHELL")
+    "/bin/sh")
+   "*The executable file name for the shell being programmed."
+   :type 'string
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-shell-arg
+   ;; bash does not need any options when run in a shell script,
+   '((bash)
+     (csh . "-f")
+     (pdksh)
+     ;; address@hidden says -p with ksh can do harm.
+     (ksh88)
+     ;; -p means don't initialize functions from the environment.
+     (rc . "-p")
+     ;; Someone proposed -motif, but we don't want to encourage
+     ;; use of a non-free widget set.
+     (wksh)
+     ;; -f means don't run .zshrc.
+     (zsh . "-f"))
+   "*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")
+                              (sexp :format "Evaluate: %v"))))
+   :group 'sh-script)
+ 
+ (defcustom sh-imenu-generic-expression
+   `((sh
+      . ((nil "^\\s-*\\(function\\s-+\\)?\\([A-Za-z_][A-Za-z_0-9]+\\)\\s-*()" 
2))))
+   "*Alist of regular expressions for recognizing shell function definitions.
+ See `sh-feature' and `imenu-generic-expression'."
+   :type '(alist :key-type (symbol :tag "Shell")
+               :value-type (alist :key-type (choice :tag "Title"
+                                                    string
+                                                    (const :tag "None" nil))
+                                  :value-type
+                                  (repeat :tag "Regexp, index..." sexp)))
+   :group 'sh-script
+   :version "20.4")
+ 
+ (defvar sh-shell-variables nil
+   "Alist of shell variable names that should be included in completion.
+ These are used for completion in addition to all the variables named
+ in `process-environment'.  Each element looks like (VAR . VAR), where
+ the car and cdr are the same symbol.")
+ 
+ (defvar sh-shell-variables-initialized nil
+   "Non-nil if `sh-shell-variables' is initialized.")
+ 
+ (defun sh-canonicalize-shell (shell)
+   "Convert a shell name SHELL to the one we should handle it as."
+   (if (string-match "\\.exe\\'" shell)
+       (setq shell (substring shell 0 (match-beginning 0))))
+   (or (symbolp shell)
+       (setq shell (intern shell)))
+   (or (cdr (assq shell sh-alias-alist))
+       shell))
+ 
+ (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 sh-abbrevs shell
+ ;;     "switch" 'sh-case
+ ;;     "getopts" 'sh-while-getopts)
+ 
+ ;;    (es sh-abbrevs shell
+ ;;    "function" 'sh-function)
+ 
+ ;;    (ksh88 sh-abbrevs sh
+ ;;       "select" 'sh-select)
+ 
+ ;;    (rc sh-abbrevs shell
+ ;;    "case" 'sh-case
+ ;;    "function" 'sh-function)
+ 
+ ;;    (sh sh-abbrevs shell
+ ;;    "case" 'sh-case
+ ;;    "function" 'sh-function
+ ;;    "until" 'sh-until
+ ;;    "getopts" 'sh-while-getopts)
+ 
+ ;;    ;; The next entry is only used for defining the others
+ ;;    (shell "for" sh-for
+ ;;       "loop" sh-indexed-loop
+ ;;       "if" sh-if
+ ;;       "tmpfile" sh-tmp-file
+ ;;       "while" sh-while)
+ 
+ ;;    (zsh sh-abbrevs ksh88
+ ;;     "repeat" 'sh-repeat))
+ ;;  "Abbrev-table used in Shell-Script mode.  See `sh-feature'.
+ ;;;Due to the internal workings of abbrev tables, the shell name symbol is
+ ;;;actually defined as the table for the like of \\[edit-abbrevs].")
+ 
+ 
+ 
+ (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 ">#"
+       ?\" "\"\""
+       ?\' "\"'"
+       ?\` "\"`"
+       ?! "_"
+       ?% "_"
+       ?: "_"
+       ?. "_"
+       ?^ "_"
+       ?~ "_"
+       ?, "_"
+       ?< "."
+       ?> ".")
+   "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))
+       (menu-map (make-sparse-keymap "Insert")))
+     (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)
+     (define-key map "\C-c\C-t" 'sh-tmp-file)
+     (define-key map "\C-c\C-s" 'sh-select)
+     (define-key map "\C-c\C-r" 'sh-repeat)
+     (define-key map "\C-c\C-o" 'sh-while-getopts)
+     (define-key map "\C-c\C-l" 'sh-indexed-loop)
+     (define-key map "\C-c\C-i" 'sh-if)
+     (define-key map "\C-c\C-f" 'sh-for)
+     (define-key map "\C-c\C-c" 'sh-case)
+     (define-key map "\C-c?" 'sh-show-indent)
+     (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 "=" 'sh-assignment)
+     (define-key map "\C-c+" 'sh-add)
+     (define-key map "\C-\M-x" 'sh-execute-region)
+     (define-key map "\C-c\C-x" 'executable-interpret)
+     (define-key map "<" 'sh-maybe-here-document)
+     (define-key map "(" 'skeleton-pair-insert-maybe)
+     (define-key map "{" 'skeleton-pair-insert-maybe)
+     (define-key map "[" 'skeleton-pair-insert-maybe)
+     (define-key map "'" 'skeleton-pair-insert-maybe)
+     (define-key map "`" 'skeleton-pair-insert-maybe)
+     (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))
+     map)
+   "Keymap used in Shell-Script mode.")
+ 
+ 
+ 
+ (defcustom sh-dynamic-complete-functions
+   '(shell-dynamic-complete-environment-variable
+     shell-dynamic-complete-command
+     comint-dynamic-complete-filename)
+   "*Functions for doing TAB dynamic completion."
+   :type '(repeat function)
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-require-final-newline
+   '((csh . t)
+     (pdksh . t)
+     (rc . require-final-newline)
+     (sh . require-final-newline))
+   "*Value of `require-final-newline' in Shell-Script mode buffers.
+ See `sh-feature'."
+   :type '(repeat (cons (symbol :tag "Shell")
+                      (choice (const :tag "require" t)
+                              (sexp :format "Evaluate: %v"))))
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-assignment-regexp
+   '((csh . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \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.
+ First grouping matches the variable name.  This is upto and including the `='
+ sign.  See `sh-feature'."
+   :type '(repeat (cons (symbol :tag "Shell")
+                      (choice regexp
+                              (sexp :format "Evaluate: %v"))))
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-indentation 4
+   "The width for further indentation in Shell-Script mode."
+   :type 'integer
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-remember-variable-min 3
+   "*Don't remember variables less than this length for completing reads."
+   :type 'integer
+   :group 'sh-script)
+ 
+ 
+ (defvar sh-header-marker nil
+   "When non-nil is the end of header for prepending by \\[sh-execute-region].
+ 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.
+ 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.
+ The actual command ends at the end of the first \\(grouping\\)."
+   :type 'regexp
+   :group 'sh-script)
+ 
+ 
+ 
+ (defvar sh-here-document-word "EOF"
+   "Word to delimit here documents.
+ If the first character of this string is \"-\", this character will
+ be removed from the string when it is used to close the here document.
+ This convention is used by the Bash shell, for example, to indicate
+ that leading tabs inside the here document should be ignored.
+ Note that Emacs currently has no support for indenting inside here
+ documents - you must insert literal tabs by hand.")
+ 
+ (defvar sh-test
+   '((sh "[  ]" . 3)
+     (ksh88 "[[  ]]" . 4))
+   "Initial input in Bourne if, while and until skeletons.  See `sh-feature'.")
+ 
+ 
+ ;; 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 sh-append posix
+         "." "alias" "bg" "bind" "builtin" "compgen" "complete"
+           "declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
+           "jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
+           "source" "suspend" "typeset" "unalias")
+ 
+     ;; The next entry is only used for defining the others
+     (bourne sh-append shell
+           "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
+           "times" "ulimit")
+ 
+     (csh sh-append shell
+        "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
+        "setenv" "source" "time" "unalias" "unhash")
+ 
+     (dtksh sh-append wksh)
+ 
+     (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
+       "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
+ 
+     (jsh sh-append sh
+        "bg" "fg" "jobs" "kill" "stop" "suspend")
+ 
+     (jcsh sh-append csh
+         "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
+ 
+     (ksh88 sh-append bourne
+          "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
+          "typeset" "unalias" "whence")
+ 
+     (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 sh-append ksh88
+          "bind")
+ 
+     (posix sh-append sh
+          "command")
+ 
+     (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
+       "whatis")
+ 
+     (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 sh-append ksh88
+         "Xt[A-Z][A-Za-z]*")
+ 
+     (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"
+        "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.
+ 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)
+                              (sexp :format "Evaluate: %v"))))
+   :group 'sh-script)
+ 
+ 
+ 
+ (defcustom sh-leading-keywords
+   '((bash sh-append sh
+           "time")
+ 
+     (csh "else")
+ 
+     (es "true" "unwind-protect" "whatis")
+ 
+     (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.
+ 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)
+                              (sexp :format "Evaluate: %v"))))
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-other-keywords
+   '((bash sh-append bourne
+         "bye" "logout" "select")
+ 
+     ;; The next entry is only used for defining the others
+     (bourne sh-append sh
+           "function")
+ 
+     (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 sh-append bourne
+          "select")
+ 
+     (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
+       "while")
+ 
+     (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 sh-append bash
+        "select"))
+   "*List of keywords not in `sh-leading-keywords'.
+ See `sh-feature'."
+   :type '(repeat (cons (symbol :tag "Shell")
+                      (choice (repeat string)
+                              (sexp :format "Evaluate: %v"))))
+   :group 'sh-script)
+ 
+ 
+ 
+ (defvar sh-variables
+   '((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 sh-append shell
+       "apid" "cdpath" "CDPATH" "history" "home" "ifs" "noexport" "path"
+       "pid" "prompt" "signals")
+ 
+     (jcsh sh-append csh
+         "notify")
+ 
+     (ksh88 sh-append sh
+          "ENV" "ERRNO" "FCEDIT" "FPATH" "HISTFILE" "HISTSIZE" "LINENO"
+          "OLDPWD" "PPID" "PS3" "PS4" "PWD" "RANDOM" "REPLY" "SECONDS"
+          "TMOUT")
+ 
+     (oash sh-append sh
+         "FIELD" "FIELD_MAX" "LAST_KEY" "OALIB" "PP_ITEM" "PP_NUM")
+ 
+     (rc sh-append shell
+       "apid" "apids" "cdpath" "CDPATH" "history" "home" "ifs" "path" "pid"
+       "prompt" "status")
+ 
+     (sh sh-append shell
+       "CDPATH" "IFS" "OPTARG" "OPTIND" "PS1" "PS2")
+ 
+     ;; The next entry is only used for defining the others
+     (shell "COLUMNS" "EDITOR" "HOME" "HUSHLOGIN" "LANG" "LC_COLLATE"
+          "LC_CTYPE" "LC_MESSAGES" "LC_MONETARY" "LC_NUMERIC" "LC_TIME"
+          "LINES" "LOGNAME" "MAIL" "MAILCHECK" "MAILPATH" "PAGER" "PATH"
+          "SHELL" "TERM" "TERMCAP" "TERMINFO" "VISUAL")
+ 
+     (tcsh sh-append csh
+         "addsuffix" "ampm" "autocorrect" "autoexpand" "autolist"
+         "autologout" "chase_symlinks" "correct" "dextract" "edit" "el"
+         "fignore" "gid" "histlit" "HOST" "HOSTTYPE" "HPATH"
+         "ignore_symlinks" "listjobs" "listlinks" "listmax" "matchbeep"
+         "nobeep" "NOREBIND" "oid" "printexitvalue" "prompt2" "prompt3"
+         "pushdsilent" "pushdtohome" "recexact" "recognize_only_executables"
+         "rmstar" "savehist" "SHLVL" "showdots" "sl" "SYSTYPE" "tcsh" "term"
+         "tperiod" "tty" "uid" "version" "visiblebell" "watch" "who"
+         "wordchars")
+ 
+     (zsh sh-append ksh88
+        "BAUD" "bindcmds" "cdpath" "DIRSTACKSIZE" "fignore" "FIGNORE" "fpath"
+        "HISTCHARS" "hostcmds" "hosts" "HOSTS" "LISTMAX" "LITHISTSIZE"
+        "LOGCHECK" "mailpath" "manpath" "NULLCMD" "optcmds" "path" "POSTEDIT"
+        "prompt" "PROMPT" "PROMPT2" "PROMPT3" "PROMPT4" "psvar" "PSVAR"
+        "READNULLCMD" "REPORTTIME" "RPROMPT" "RPS1" "SAVEHIST" "SPROMPT"
+        "STTY" "TIMEFMT" "TMOUT" "TMPPREFIX" "varcmds" "watch" "WATCH"
+        "WATCHFMT" "WORDCHARS" "ZDOTDIR"))
+   "List of all shell variables available for completing read.
+ See `sh-feature'.")
+ 
+ 
+ ;; Font-Lock support
+ 
+ (defface sh-heredoc-face
+   '((((class color)
+       (background dark))
+      (:foreground "yellow" :weight bold))
+     (((class color)
+       (background light))
+      (:foreground "tan" ))
+     (t
+      (:weight bold)))
+   "Face to show a here-document"
+   :group 'sh-indentation)
+ (defvar sh-heredoc-face 'sh-heredoc-face)
+ 
+ 
+ (defvar sh-font-lock-keywords
+   '((csh sh-append shell
+        ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 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 sh-append es)
+ 
+     (sh sh-append shell
+       ;; Variable names.
+       ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|address@hidden)" 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)))
+ 
+     ;; The next entry is only used for defining the others
+     (shell sh-append executable-font-lock-keywords
+            ;; Using font-lock-string-face here confuses sh-get-indent-info.
+            ("\\\\$" 0 font-lock-warning-face)
+          ("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
+          ("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
+            font-lock-variable-name-face))
+     (rpm 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
+   '((sh "[ \t]in\\>"))
+   "Subdued level highlighting for Shell Script modes.")
+ 
+ (defvar sh-font-lock-keywords-2 ()
+   "Gaudy level highlighting for Shell Script modes.")
+ 
+ ;; These are used for the syntax table stuff (derived from cperl-mode).
+ ;; Note: parse-sexp-lookup-properties must be set to t for it to work.
+ (defconst sh-st-punc (string-to-syntax "."))
+ (defconst sh-st-symbol (string-to-syntax "_"))
+ (defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
+ 
+ (defconst sh-here-doc-open-re 
"<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|\\s_\\)+\\).*\\(\n\\)")
+ 
+ (defvar sh-here-doc-markers nil)
+ (make-variable-buffer-local 'sh-here-doc-markers)
+ (defvar sh-here-doc-re sh-here-doc-open-re)
+ (make-variable-buffer-local 'sh-here-doc-re)
+ 
+ (defun sh-font-lock-close-heredoc (bol eof indented)
+   "Determine the syntax of the \\n after an EOF.
+ If non-nil INDENTED indicates that the EOF was indented."
+   (let* ((eof-re (if eof (regexp-quote eof) ""))
+          ;; A rough regexp that should find the opening <<EOF back.
+        (sre (concat "<<\\(-?\\)\\s-*['\"\\]?"
+                     ;; Use \s| to cheaply check it's an open-heredoc.
+                     eof-re "['\"]?\\([ \t|;&)<>].*\\)?\\s|"))
+        ;; A regexp that will find other EOFs.
+        (ere (concat "^" (if indented "[ \t]*") eof-re "\n"))
+        (start (save-excursion
+                 (goto-char bol)
+                 (re-search-backward (concat sre "\\|" ere) nil t))))
+     ;; If subgroup 1 matched, we found an open-heredoc, otherwise we first
+     ;; found a close-heredoc which makes the current close-heredoc inoperant.
+     (cond
+      ((when (and start (match-end 1)
+                (not (and indented (= (match-beginning 1) (match-end 1))))
+                (not (sh-in-comment-or-string (match-beginning 0))))
+       ;; Make sure our `<<' is not the EOF1 of a `cat <<EOF1 <<EOF2'.
+       (save-excursion
+         (goto-char start)
+         (setq start (line-beginning-position 2))
+         (while
+             (progn
+               (re-search-forward "<<") ; Skip ourselves.
+               (and (re-search-forward sh-here-doc-open-re start 'move)
+                    (goto-char (match-beginning 0))
+                    (sh-in-comment-or-string (point)))))
+         ;; No <<EOF2 found after our <<.
+         (= (point) start)))
+       sh-here-doc-syntax)
+      ((not (or start (save-excursion (re-search-forward sre nil t))))
+       ;; There's no <<EOF either before or after us,
+       ;; so we should remove ourselves from font-lock's keywords.
+       (setq sh-here-doc-markers (delete eof sh-here-doc-markers))
+       (setq sh-here-doc-re
+           (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
+                   (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))
+       nil))))
+ 
+ (defun sh-font-lock-open-heredoc (start string)
+   "Determine the syntax of the \\n after a <<EOF.
+ START is the position of <<.
+ STRING is the actual word used as delimiter (f.ex. \"EOF\").
+ INDENTED is non-nil if the here document's content (and the EOF mark) can
+ be indented (i.e. a <<- was used rather than just <<)."
+   (unless (or (memq (char-before start) '(?< ?>))
+             (sh-in-comment-or-string start))
+     ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic
+     ;; font-lock keywords to detect the end of this here document.
+     (let ((str (replace-regexp-in-string "['\"]" "" string)))
+       (unless (member str sh-here-doc-markers)
+       (push str sh-here-doc-markers)
+       (setq sh-here-doc-re
+             (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
+                     (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))))
+     sh-here-doc-syntax))
+ 
+ (defun sh-font-lock-here-doc (limit)
+   "Search for a heredoc marker."
+   ;; This looks silly, but it's because `sh-here-doc-re' keeps changing.
+   (re-search-forward sh-here-doc-re limit t))
+ 
+ (defun sh-is-quoted-p (pos)
+   (and (eq (char-before pos) ?\\)
+        (not (sh-is-quoted-p (1- pos)))))
+ 
+ (defun sh-font-lock-paren (start)
+   (save-excursion
+     (goto-char start)
+     ;; Skip through all patterns
+     (while
+       (progn
+         (forward-comment (- (point-max)))
+         ;; Skip through one pattern
+         (while
+             (or (/= 0 (skip-syntax-backward "w_"))
+                 (/= 0 (skip-chars-backward "?[]*@/\\"))
+                 (and (sh-is-quoted-p (1- (point)))
+                      (goto-char (- (point) 2)))
+                 (when (memq (char-before) '(?\" ?\'))
+                   (condition-case nil (progn (backward-sexp 1) t)
+                     (error nil)))))
+         (forward-comment (- (point-max)))
+         (when (eq (char-before) ?|)
+           (backward-char 1) t)))
+     (when (save-excursion (backward-char 2) (looking-at ";;\\|in"))
+       sh-st-punc)))
+ 
+ (defconst sh-font-lock-syntactic-keywords
+   ;; A `#' begins a comment when it is unquoted and at the beginning of a
+   ;; word.  In the shell, words are separated by metacharacters.
+   ;; The list of special chars is taken from the single-unix spec
+   ;; of the shell command language (under `quoting') but with `$' removed.
+   `(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 ,sh-st-symbol)
+     ;; Find HEREDOC starters and add a corresponding rule for the ender.
+     (sh-font-lock-here-doc
+      (2 (sh-font-lock-open-heredoc
+        (match-beginning 0) (match-string 1)) nil t)
+      (5 (sh-font-lock-close-heredoc
+        (match-beginning 0) (match-string 4)
+          (and (match-beginning 3) (/= (match-beginning 3) (match-end 3))))
+       nil t))
+     ;; Distinguish the special close-paren in `case'.
+     (")" 0 (sh-font-lock-paren (match-beginning 0)))))
+ 
+ (defun sh-font-lock-syntactic-face-function (state)
+   (if (nth 3 state)
+       (if (char-valid-p (nth 3 state))
+         font-lock-string-face
+       sh-heredoc-face)
+     font-lock-comment-face))
+ 
+ (defgroup sh-indentation nil
+   "Variables controlling indentation in shell scripts.
+ 
+ Note: customizing these variables will not affect existing buffers if
+ `sh-make-vars-local' is no-nil.  See the documentation for
+ variable `sh-make-vars-local', command `sh-make-vars-local'
+ and command `sh-reset-indent-vars-to-global-values'."
+   :group 'sh-script)
+ 
+ 
+ (defcustom sh-set-shell-hook nil
+   "*Hook run by `sh-set-shell'."
+   :type 'hook
+   :group 'sh-script)
+ 
+ (defcustom sh-mode-hook nil
+   "*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'.
+ 
+ nil mean:              never.
+ t means:               only if there seems to be an obvious value.
+ Anything else means:   whenever we have a \"good guess\" as to the value."
+   :type '(choice
+         (const :tag "Never" nil)
+         (const :tag "Only if sure"  t)
+         (const :tag "If have a good guess" usually))
+   :group 'sh-indentation)
+ 
+ (defcustom sh-popup-occur-buffer nil
+   "*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
+         (const :tag "Only when there are conflicts." nil)
+         (const :tag "Always"  t))
+   :group 'sh-indentation)
+ 
+ (defcustom sh-blink t
+   "*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."
+   :type 'boolean
+   :group 'sh-indentation)
+ 
+ (defcustom sh-first-lines-indent 0
+   "*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
+         (const :tag "Leave as is"     nil)
+         (integer :tag "Column number"
+                  :menu-tag "Indent to this col (0 means first col)" ))
+   :group 'sh-indentation)
+ 
+ 
+ (defcustom sh-basic-offset 4
+   "*The default indentation increment.
+ This value is used for the + and - symbols in an indentation variable."
+   :type 'integer
+   :group 'sh-indentation)
+ 
+ (defcustom sh-indent-comment nil
+   "*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."
+   :type '(choice
+         (const :tag "Leave as is." nil)
+         (const :tag "Indent as a normal line."  t)
+         (integer :menu-tag "Indent to this col (0 means first col)."
+                  :tag "Indent to column number.") )
+   :group 'sh-indentation)
+ 
+ 
+ (defvar sh-debug nil
+   "Enable lots of debug messages - if function `sh-debug' is enabled.")
+ 
+ 
+ ;; Uncomment this defun and comment the defmacro for debugging.
+ ;; (defun sh-debug (&rest args)
+ ;;   "For debugging:  display message ARGS if variable SH-DEBUG is non-nil."
+ ;;   (if sh-debug
+ ;;       (apply 'message args)))
+ (defmacro sh-debug (&rest args))
+ 
+ (defconst sh-symbol-list
+   '((const :tag "+ "  :value +
+          :menu-tag "+   Indent right by sh-basic-offset")
+     (const :tag "- "  :value -
+          :menu-tag "-   Indent left  by sh-basic-offset")
+     (const :tag "++"  :value  ++
+          :menu-tag "++  Indent right twice sh-basic-offset")
+     (const :tag "--"  :value --
+          :menu-tag "--  Indent left  twice sh-basic-offset")
+     (const :tag "* " :value *
+          :menu-tag "*   Indent right half sh-basic-offset")
+     (const :tag "/ " :value /
+          :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."
+   :type `(choice
+         (integer :menu-tag "A number (positive=>indent right)"
+                  :tag "A number")
+         (const :tag "--") ;; separator!
+         ,@ sh-symbol-list
+         )
+   :group 'sh-indentation)
+ 
+ (defconst sh-number-or-symbol-list
+   (append '((integer :menu-tag "A number (positive=>indent right)"
+                    :tag "A number")
+           (const :tag "--"))          ; separator
+         sh-symbol-list))
+ 
+ (defcustom sh-indent-for-fi 0
+   "*How much to indent a fi relative to an if.  Usually 0."
+   :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."
+   :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."
+   :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."
+   :type `(choice ,@ sh-number-or-symbol-list )
+   :group 'sh-indentation)
+ 
+ (defcustom sh-indent-for-then '+
+   "*How much to indent a then relative to an 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."
+   :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."
+   :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."
+   :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
+ looping construct 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."
+   :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."
+   :type `(choice ,@ sh-number-or-symbol-list)
+   :group 'sh-indentation)
+ 
+ 
+ (defcustom sh-indent-for-continuation '+
+   "*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.
+ 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."
+   :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.
+ 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.
+ This is for the rc shell."
+   :type `(choice ,@ sh-number-or-symbol-list)
+   :group 'sh-indentation)
+ 
+ ;; Internal use - not designed to be changed by the user:
+ 
+ (defun sh-mkword-regexpr (word)
+   "Make a regexp which matches WORD as a word.
+ This specifically excludes an occurrence of WORD followed by
+ punctuation characters like '-'."
+   (concat word "\\([^-a-z0-9_]\\|$\\)"))
+ 
+ (defconst sh-re-done (sh-mkword-regexpr "done"))
+ 
+ 
+ (defconst sh-kws-for-done
+   '((sh .  ( "while" "until" "for" ) )
+     (bash . ( "while" "until" "for" "select"  ) )
+     (ksh88 . ( "while" "until" "for" "select"  ) )
+     (zsh .  ( "while" "until" "for" "repeat" "select" ) ) )
+   "Which keywords can match the word `done' in this shell.")
+ 
+ 
+ (defconst sh-indent-supported
+   '((sh . t)
+     (csh . nil)
+     (rc . t))
+   "Shell types that shell indenting can do something with.")
+ 
+ (defvar sh-indent-supported-here nil
+   "Non-nil if we support indentation for the current buffer's shell type.")
+ 
+ (defconst sh-var-list
+   '(
+     sh-basic-offset sh-first-lines-indent sh-indent-after-case
+     sh-indent-after-do sh-indent-after-done
+     sh-indent-after-else
+     sh-indent-after-if
+     sh-indent-after-loop-construct
+     sh-indent-after-open
+     sh-indent-comment
+     sh-indent-for-case-alt
+     sh-indent-for-case-label
+     sh-indent-for-continuation
+     sh-indent-for-do
+     sh-indent-for-done
+     sh-indent-for-else
+     sh-indent-for-fi
+     sh-indent-for-then
+     )
+   "A list of variables used by script mode to control indentation.
+ This list is used when switching between buffer-local and global
+ values of variables, and for the commands using indentation styles.")
+ 
+ (defvar sh-make-vars-local t
+   "*Controls whether indentation variables are local to the buffer.
+ If non-nil, indentation variables are made local initially.
+ If nil, you can later make the variables local by invoking
+ command `sh-make-vars-local'.
+ The default is t because I assume that in one Emacs session one is
+ frequently editing existing scripts with different styles.")
+ 
+ 
+ ;; mode-command and utility functions
+ 
+ ;;;###autoload
+ (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.
+ Unless the file's magic number indicates the shell, your usual shell is
+ assumed.  Since filenames rarely give a clue, they are not further analyzed.
+ 
+ This mode adapts to the variations between shells (see `sh-set-shell') by
+ means of an inheritance based feature lookup (see `sh-feature').  This
+ mechanism applies to all variables (including skeletons) that pertain to
+ shell-specific features.
+ 
+ The default style of this mode is that of Rosenblatt's Korn shell book.
+ The syntax of the statements varies with the shell being used.  The
+ following commands are available, based on the current shell's syntax:
+ 
+ \\[sh-case]    case statement
+ \\[sh-for]     for loop
+ \\[sh-function]        function definition
+ \\[sh-if]      if statement
+ \\[sh-indexed-loop]    indexed loop from 1 to n
+ \\[sh-while-getopts]   while getopts loop
+ \\[sh-repeat]  repeat loop
+ \\[sh-select]  select loop
+ \\[sh-until]   until loop
+ \\[sh-while]   while loop
+ 
+ For sh and rc shells indentation commands are:
+ \\[sh-show-indent]    Show the variable controlling this line's indentation.
+ \\[sh-set-indent]     Set then variable controlling this line's indentation.
+ \\[sh-learn-line-indent]      Change the indentation variable so this line
+ would indent to the way it currently is.
+ \\[sh-learn-buffer-indent]  Set the indentation variables so the
+ 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.
+ \\[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.
+ \\[sh-execute-region]  Have optional header and region be executed in a 
subshell.
+ 
+ \\[sh-maybe-here-document]     Without prefix, following an unquoted < 
inserts here document.
+ \{, (, [, ', \", `
+       Unless quoted with \\, insert the pairs {}, (), [], or '', \"\", ``.
+ 
+ If you generally program a shell different from your login shell you can
+ set `sh-shell-file' accordingly.  If your shell's file name doesn't correctly
+ 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)
+   (make-local-variable 'comment-start)
+   (make-local-variable 'comment-start-skip)
+   (make-local-variable 'require-final-newline)
+   (make-local-variable 'sh-header-marker)
+   (make-local-variable 'sh-shell-file)
+   (make-local-variable 'sh-shell)
+   (make-local-variable 'skeleton-pair-alist)
+   (make-local-variable 'skeleton-pair-filter)
+   (make-local-variable 'comint-dynamic-complete-functions)
+   (make-local-variable 'comint-prompt-regexp)
+   (make-local-variable 'font-lock-defaults)
+   (make-local-variable 'skeleton-filter)
+   (make-local-variable 'skeleton-newline-indent-rigidly)
+   (make-local-variable 'sh-shell-variables)
+   (make-local-variable 'sh-shell-variables-initialized)
+   (make-local-variable 'imenu-generic-expression)
+   (make-local-variable 'sh-indent-supported-here)
+   (setq skeleton-end-hook (lambda ()
+                           (or (eolp) (newline) (indent-relative)))
+       paragraph-start (concat page-delimiter "\\|$")
+       paragraph-separate paragraph-start
+       comment-start "# "
+       comint-dynamic-complete-functions sh-dynamic-complete-functions
+       ;; we can't look if previous line ended with `\'
+       comint-prompt-regexp "^[ \t]*"
+       font-lock-defaults
+       `((sh-font-lock-keywords
+          sh-font-lock-keywords-1 sh-font-lock-keywords-2)
+         nil nil
+         ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
+         (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords)
+         (font-lock-syntactic-face-function
+          . sh-font-lock-syntactic-face-function))
+       skeleton-pair-alist '((?` _ ?`))
+       skeleton-pair-filter 'sh-quoted-p
+       skeleton-further-elements '((< '(- (min sh-indentation
+                                               (current-column)))))
+       skeleton-filter 'sh-feature
+       skeleton-newline-indent-rigidly t
+       sh-indent-supported-here nil)
+   (set (make-local-variable 'parse-sexp-ignore-comments) t)
+   ;; Parse or insert magic number for exec, and set all variables depending
+   ;; on the shell thus determined.
+   (let ((interpreter
+        (save-excursion
+          (goto-char (point-min))
+          (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))
+                 "rpm")))))
+     (sh-set-shell (or interpreter sh-shell-file) nil nil))
+   (run-hooks 'sh-mode-hook))
+ 
+ ;;;###autoload
+ (defalias 'shell-script-mode 'sh-mode)
+ 
+ 
+ (defun sh-font-lock-keywords (&optional keywords)
+   "Function to get simple fontification based on `sh-font-lock-keywords'.
+ This adds rules for comments and assignments."
+   (sh-feature sh-font-lock-keywords
+             (when (stringp (sh-feature sh-assignment-regexp))
+               (lambda (list)
+                 `((,(sh-feature sh-assignment-regexp)
+                    1 font-lock-variable-name-face)
+                   ,@keywords
+                   ,@list)))))
+ 
+ (defun sh-font-lock-keywords-1 (&optional builtins)
+   "Function to get better fontification including keywords."
+   (let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\("
+                         (regexp-opt (sh-feature sh-leading-keywords) t)
+                         "[ \t]+\\)?"
+                         (regexp-opt (append (sh-feature sh-leading-keywords)
+                                             (sh-feature sh-other-keywords))
+                                     t))))
+     (sh-font-lock-keywords
+      `(,@(if builtins
+            `((,(concat keywords "[ \t]+\\)?"
+                        (regexp-opt (sh-feature sh-builtins) t)
+                        "\\>")
+               (2 font-lock-keyword-face nil t)
+               (6 font-lock-builtin-face))
+              ,@(sh-feature sh-font-lock-keywords-2)))
+        (,(concat keywords "\\)\\>")
+         2 font-lock-keyword-face)
+        ,@(sh-feature sh-font-lock-keywords-1)))))
+ 
+ (defun sh-font-lock-keywords-2 ()
+   "Function to get better fontification including keywords and builtins."
+   (sh-font-lock-keywords-1 t))
+ 
+ 
+ (defvar sh-regexp-for-done nil
+   "A buffer-local regexp to match opening keyword for done.")
+ 
+ (defvar sh-kw-alist nil
+   "A buffer-local, since it is shell-type dependent, list of keywords.")
+ 
+ ;; ( key-word  first-on-this  on-prev-line )
+ ;; This is used to set `sh-kw-alist' which is a list of sublists each
+ ;; having 3 elements:
+ ;;   a keyword
+ ;;   a rule to check when the keyword appears on "this" line
+ ;;   a rule to check when the keyword appears on "the previous" line
+ ;; The keyword is usually a string and is the first word on a line.
+ ;; If this keyword appears on the line whose indentation is to be
+ ;; calculated, the rule in element 2 is called.  If this returns
+ ;; non-zero, the resulting point (which may be changed by the rule)
+ ;; is used as the default indentation.
+ ;; If it returned false or the keyword was not found in the table,
+ ;; then the keyword from the previous line is looked up and the rule
+ ;; in element 3 is called.  In this case, however,
+ ;; `sh-get-indent-info' does not stop but may keep going and test
+ ;; other keywords against rules in element 3.  This is because the
+ ;; preceding line could have, for example, an opening "if" and an
+ ;; opening "while" keyword and we need to add the indentation offsets
+ ;; for both.
+ ;;
+ (defconst sh-kw
+   '((sh
+      ("if" nil sh-handle-prev-if)
+      ("elif" sh-handle-this-else sh-handle-prev-else)
+      ("else" sh-handle-this-else sh-handle-prev-else)
+      ("fi" sh-handle-this-fi sh-handle-prev-fi)
+      ("then" sh-handle-this-then sh-handle-prev-then)
+      ("(" nil sh-handle-prev-open)
+      ("{" nil sh-handle-prev-open)
+      ("[" nil sh-handle-prev-open)
+      ("}" sh-handle-this-close nil)
+      (")" sh-handle-this-close nil)
+      ("]" sh-handle-this-close nil)
+      ("case" nil sh-handle-prev-case)
+      ("esac" sh-handle-this-esac sh-handle-prev-esac)
+      (case-label nil sh-handle-after-case-label) ;; ???
+      (";;" nil sh-handle-prev-case-alt-end) ;; ???
+      ("done" sh-handle-this-done sh-handle-prev-done)
+      ("do" sh-handle-this-do sh-handle-prev-do))
+ 
+     ;; Note: we don't need specific stuff for bash and zsh shells;
+     ;; the regexp `sh-regexp-for-done' handles the extra keywords
+     ;; these shells use.
+     (rc
+      ("{" nil sh-handle-prev-open)
+      ("}" sh-handle-this-close nil)
+      ("case" sh-handle-this-rc-case sh-handle-prev-rc-case))))
+ 
+ 
+ (defun sh-set-shell (shell &optional no-query-flag insert-flag)
+   "Set this buffer's shell to SHELL (a string).
+ 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 (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)
+       (setq shell (substring shell 0 (match-beginning 0))))
+   (setq sh-shell (intern (file-name-nondirectory shell))
+       sh-shell (or (cdr (assq sh-shell sh-alias-alist))
+                    sh-shell))
+   (if insert-flag
+       (setq sh-shell-file
+           (executable-set-magic shell (sh-feature sh-shell-arg)
+                                 no-query-flag insert-flag)))
+   (let ((tem (sh-feature sh-require-final-newline)))
+     (unless (eq tem 'require-final-newline)
+       (setq require-final-newline tem)))
+   (setq
+       comment-start-skip "#+[\t ]*"
+       local-abbrev-table sh-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)
+   (if (setq sh-indent-supported-here (sh-feature sh-indent-supported))
+       (progn
+       (message "Setting up indent for shell type %s" sh-shell)
+       (set (make-local-variable 'parse-sexp-lookup-properties) t)
+       (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw))
+       (let ((regexp (sh-feature sh-kws-for-done)))
+         (if regexp
+             (set (make-local-variable 'sh-regexp-for-done)
+                  (sh-mkword-regexpr (regexp-opt regexp t)))))
+       (message "setting up indent stuff")
+       ;; sh-mode has already made indent-line-function local
+       ;; but do it in case this is called before that.
+       (setq indent-line-function 'sh-indent-line)
+       (if sh-make-vars-local
+           (sh-make-vars-local))
+       (message "Indentation setup for shell type %s" sh-shell))
+     (message "No indentation for this shell type.")
+     (setq indent-line-function 'sh-basic-indent-line))
+   (run-hooks 'sh-set-shell-hook))
+ 
+ 
+ 
+ (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:
+ 
+   - Fall back on successive ancestors (see `sh-ancestor-alist') as long as
+     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 `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 alist)
+         (let ((l alist))
+           (while (and l (consp (car l)))
+             (setq l (cdr l)))
+           (if l alist)))
+       (if function
+         (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 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 alist)))
+       (if (and (consp (setq val (cdr elt)))
+                (memq (car val) '(sh-append sh-modify)))
+           (setcdr elt
+                   (setq 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 alist
+                  (list (cons function
+                              (setq sh-shell (car function)
+                                    val (funcall (cdr function) val))))))
+       val)))
+ 
+ 
+ 
+ ;; I commented this out because nobody calls it -- rms.
+ ;;(defun sh-abbrevs (ancestor &rest list)
+ ;;  "Iff it isn't, define the current shell as abbrev table and fill that.
+ ;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an 
abbrev
+ ;;table or a list of (NAME1 EXPANSION1 ...).  In addition it will define 
abbrevs
+ ;;according to the remaining arguments NAMEi EXPANSIONi ...
+ ;;EXPANSION may be either a string or a skeleton command."
+ ;;  (or (if (boundp sh-shell)
+ ;;      (symbol-value sh-shell))
+ ;;      (progn
+ ;;    (if (listp ancestor)
+ ;;        (nconc list ancestor))
+ ;;    (define-abbrev-table sh-shell ())
+ ;;    (if (vectorp ancestor)
+ ;;        (mapatoms (lambda (atom)
+ ;;                    (or (eq atom 0)
+ ;;                        (define-abbrev (symbol-value sh-shell)
+ ;;                          (symbol-name atom)
+ ;;                          (symbol-value atom)
+ ;;                          (symbol-function atom))))
+ ;;                  ancestor))
+ ;;    (while list
+ ;;      (define-abbrev (symbol-value sh-shell)
+ ;;        (car list)
+ ;;        (if (stringp (car (cdr list)))
+ ;;            (car (cdr list))
+ ;;          "")
+ ;;        (if (symbolp (car (cdr list)))
+ ;;            (car (cdr list))))
+ ;;      (setq list (cdr (cdr list)))))
+ ;;      (symbol-value sh-shell)))
+ 
+ 
+ (defun sh-append (ancestor &rest list)
+   "Return list composed of first argument (a list) physically appended to 
rest."
+   (nconc list ancestor))
+ 
+ 
+ (defun sh-modify (skeleton &rest list)
+   "Modify a copy of SKELETON by replacing I1 with REPL1, I2 with REPL2 ..."
+   (setq skeleton (copy-sequence skeleton))
+   (while list
+     (setcar (or (nthcdr (car list) skeleton)
+               (error "Index %d out of bounds" (car list)))
+           (car (cdr list)))
+     (setq list (nthcdr 2 list)))
+   skeleton)
+ 
+ 
+ (defun sh-basic-indent-line ()
+   "Indent a line for Sh mode (shell script mode).
+ Indent as far as preceding non-empty line, then by steps of `sh-indentation'.
+ Lines containing only comments are considered empty."
+   (interactive)
+   (let ((previous (save-excursion
+                   (while (and (progn (beginning-of-line)
+                                      (not (bobp)))
+                               (progn
+                                 (forward-line -1)
+                                 (back-to-indentation)
+                                 (or (eolp)
+                                     (eq (following-char) ?#)))))
+                   (current-column)))
+       current)
+     (save-excursion
+       (indent-to (if (eq this-command 'newline-and-indent)
+                    previous
+                  (if (< (current-column)
+                         (setq current (progn (back-to-indentation)
+                                              (current-column))))
+                      (if (eolp) previous 0)
+                    (delete-region (point)
+                                   (progn (beginning-of-line) (point)))
+                    (if (eolp)
+                        (max previous (* (1+ (/ current sh-indentation))
+                                         sh-indentation))
+                      (* (1+ (/ current sh-indentation)) sh-indentation))))))
+     (if (< (current-column) (current-indentation))
+       (skip-chars-forward " \t"))))
+ 
+ 
+ (defun sh-execute-region (start end &optional flag)
+   "Pass optional header and region to a subshell for noninteractive execution.
+ The working directory is that of the buffer, and only environment variables
+ are already set which is why you can mark a header within the script.
+ 
+ With a positive prefix ARG, instead of sending region, define header from
+ beginning of buffer to point.  With a negative prefix ARG, instead of sending
+ region, clear header."
+   (interactive "r\nP")
+   (if flag
+       (setq sh-header-marker (if (> (prefix-numeric-value flag) 0)
+                                (point-marker)))
+     (if sh-header-marker
+       (save-excursion
+         (let (buffer-undo-list)
+           (goto-char sh-header-marker)
+           (append-to-buffer (current-buffer) start end)
+           (shell-command-on-region (point-min)
+                                    (setq end (+ sh-header-marker
+                                                 (- end start)))
+                                    sh-shell-file)
+           (delete-region sh-header-marker end)))
+       (shell-command-on-region start end (concat sh-shell-file " -")))))
+ 
+ 
+ (defun sh-remember-variable (var)
+   "Make VARIABLE available for future completing reads in this buffer."
+   (or (< (length var) sh-remember-variable-min)
+       (getenv var)
+       (assoc var sh-shell-variables)
+       (push (cons var var) sh-shell-variables))
+   var)
+ 
+ 
+ 
+ (defun sh-quoted-p ()
+   "Is point preceded by an odd number of backslashes?"
+   (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2)))
+ 
+ ;; Indentation stuff.
+ (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."
+   (unless sh-indent-supported-here
+     (error "This buffer's shell does not support indentation through Emacs")))
+ 
+ (defun sh-make-vars-local ()
+   "Make the indentation variables local to this buffer.
+ Normally they already are local.  This command is provided in case
+ 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)
+   (message "Indentation variable 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)
+   (if sh-make-vars-local
+       (mapcar 'make-local-variable sh-var-list)))
+ 
+ 
+ ;; Theoretically these are only needed in shell and derived modes.
+ ;; However, the routines which use them are only called in those modes.
+ (defconst sh-special-keywords "then\\|do")
+ 
+ (defun sh-help-string-for-variable (var)
+   "Construct a string for `sh-read-variable' when changing variable VAR ."
+   (let ((msg (documentation-property var 'variable-documentation))
+       (msg2 ""))
+     (unless (memq var '(sh-first-lines-indent sh-indent-comment))
+       (setq msg2
+           (format "\n
+ You can enter a number (positive to increase indentation,
+ negative to decrease indentation, zero for no change to indentation).
+ 
+ Or, you can enter one of the following symbols which are relative to
+ the value of variable `sh-basic-offset'
+ which in this buffer is currently %s.
+ 
+ \t%s."
+                   sh-basic-offset
+                   (mapconcat (lambda (x)
+                                (nth (1- (length x)) x))
+                              sh-symbol-list  "\n\t"))))
+     (concat
+      ;; The following shows the global not the local value!
+      ;; (format "Current value of %s is %s\n\n" var (symbol-value var))
+      msg msg2)))
+ 
+ (defun sh-read-variable (var)
+   "Read a new value for indentation variable VAR."
+   (interactive "*variable? ") ;; to test
+   (let ((minibuffer-help-form `(sh-help-string-for-variable
+                               (quote ,var)))
+       val)
+     (setq val (read-from-minibuffer
+              (format "New value for %s (press %s for help): "
+                      var  (single-key-description help-char))
+              (format "%s" (symbol-value var))
+              nil t))
+     val))
+ 
+ 
+ 
+ (defun sh-in-comment-or-string (start)
+   "Return non-nil if START is in a comment or string."
+   (save-excursion
+     (let ((state (syntax-ppss start)))
+       (or (nth 3 state) (nth 4 state)))))
+ 
+ (defun sh-goto-matching-if ()
+   "Go to the matching if for a fi.
+ This handles nested if..fi pairs."
+   (let ((found (sh-find-prev-matching "\\bif\\b" "\\bfi\\b" 1)))
+     (if found
+       (goto-char found))))
+ 
+ 
+ ;; Functions named sh-handle-this-XXX are called when the keyword on the
+ ;; line whose indentation is being handled contain XXX;
+ ;; those named sh-handle-prev-XXX are when XXX appears on the previous line.
+ 
+ (defun sh-handle-prev-if ()
+   (list '(+ sh-indent-after-if)))
+ 
+ (defun sh-handle-this-else ()
+   (if (sh-goto-matching-if)
+       ;; (list "aligned to if")
+       (list "aligned to if" '(+ sh-indent-for-else))
+     nil
+     ))
+ 
+ (defun sh-handle-prev-else ()
+   (if (sh-goto-matching-if)
+       (list  '(+ sh-indent-after-if))
+     ))
+ 
+ (defun sh-handle-this-fi ()
+   (if (sh-goto-matching-if)
+       (list "aligned to if" '(+ sh-indent-for-fi))
+     nil
+     ))
+ 
+ (defun sh-handle-prev-fi ()
+   ;; Why do we have this rule?  Because we must go back to the if
+   ;; to get its indent.  We may continue back from there.
+   ;; We return nil because we don't have anything to add to result,
+   ;; the side affect of setting align-point is all that matters.
+   ;; we could return a comment (a string) but I can't think of a good one...
+   (sh-goto-matching-if)
+   nil)
+ 
+ (defun sh-handle-this-then ()
+   (let ((p (sh-goto-matching-if)))
+     (if p
+       (list '(+ sh-indent-for-then))
+       )))
+ 
+ (defun sh-handle-prev-then ()
+   (let ((p (sh-goto-matching-if)))
+     (if p
+       (list '(+ sh-indent-after-if))
+       )))
+ 
+ (defun sh-handle-prev-open ()
+   (save-excursion
+     (let ((x (sh-prev-stmt)))
+       (if (and x
+              (progn
+                (goto-char x)
+                (or
+                 (looking-at "function\\b")
+                 (looking-at "\\s-*\\S-+\\s-*()")
+                 )))
+         (list '(+ sh-indent-after-function))
+       (list '(+ sh-indent-after-open)))
+       )))
+ 
+ (defun sh-handle-this-close ()
+   (forward-char 1) ;; move over ")"
+   (if (sh-safe-forward-sexp -1)
+       (list "aligned to opening paren")))
+ 
+ (defun sh-goto-matching-case ()
+   (let ((found (sh-find-prev-matching "\\bcase\\b" "\\besac\\b" 1)))
+     (if found (goto-char found))))
+ 
+ (defun sh-handle-prev-case ()
+   ;; This is typically called when point is on same line as a case
+   ;; we shouldn't -- and can't find prev-case
+   (if (looking-at ".*\\<case\\>")
+       (list '(+ sh-indent-for-case-label))
+     (error "We don't seem to be on a line with a case"))) ;; debug
+ 
+ (defun sh-handle-this-esac ()
+   (if (sh-goto-matching-case)
+       (list "aligned to matching case")))
+ 
+ (defun sh-handle-prev-esac ()
+   (if (sh-goto-matching-case)
+       (list "matching case")))
+ 
+ (defun sh-handle-after-case-label ()
+   (if (sh-goto-matching-case)
+       (list '(+ sh-indent-for-case-alt))))
+ 
+ (defun sh-handle-prev-case-alt-end ()
+   (if (sh-goto-matching-case)
+       (list '(+ sh-indent-for-case-label))))
+ 
+ (defun sh-safe-forward-sexp (&optional arg)
+   "Try and do a `forward-sexp', but do not error.
+ Return new point if successful, nil if an error occurred."
+   (condition-case nil
+       (progn
+       (forward-sexp (or arg 1))
+       (point))        ;; return point if successful
+     (error
+      (sh-debug "oops!(1) %d" (point))
+      nil))) ;; return nil if fail
+ 
+ (defun sh-goto-match-for-done ()
+   (let ((found (sh-find-prev-matching sh-regexp-for-done sh-re-done 1)))
+     (if found
+       (goto-char found))))
+ 
+ (defun sh-handle-this-done ()
+   (if (sh-goto-match-for-done)
+       (list  "aligned to do stmt"  '(+ sh-indent-for-done))))
+ 
+ (defun sh-handle-prev-done ()
+   (if (sh-goto-match-for-done)
+       (list "previous done")))
+ 
+ (defun sh-handle-this-do ()
+   (if (sh-goto-match-for-done)
+       (list '(+ sh-indent-for-do))))
+ 
+ (defun sh-handle-prev-do ()
+   (cond
+    ((save-restriction
+       (narrow-to-region
+        (point)
+        (save-excursion
+        (beginning-of-line)
+        (point)))
+       (sh-goto-match-for-done))
+     (sh-debug "match for done found on THIS line")
+     (list '(+ sh-indent-after-loop-construct)))
+    ((sh-goto-match-for-done)
+     (sh-debug "match for done found on PREV line")
+     (list '(+ sh-indent-after-do)))
+    (t
+     (message "match for done NOT found")
+     nil)))
+ 
+ ;; for rc:
+ (defun sh-find-prev-switch ()
+   "Find the line for the switch keyword matching this line's case keyword."
+   (re-search-backward "\\<switch\\>" nil t))
+ 
+ (defun sh-handle-this-rc-case ()
+   (if (sh-find-prev-switch)
+       (list  '(+ sh-indent-after-switch))
+     ;; (list  '(+ sh-indent-for-case-label))
+     nil))
+ 
+ (defun sh-handle-prev-rc-case ()
+   (list '(+ sh-indent-after-case)))
+ 
+ (defun sh-check-rule (n thing)
+   (let ((rule (nth n (assoc thing sh-kw-alist)))
+       (val nil))
+     (if rule
+       (progn
+         (setq val (funcall rule))
+         (sh-debug "rule (%d) for %s at %d is %s\n-> returned %s"
+                   n thing (point) rule val)))
+     val))
+ 
+ 
+ (defun sh-get-indent-info ()
+   "Return indent-info for this line.
+ This is a list.  nil means the line is to be left as is.
+ Otherwise it contains one or more of the following sublists:
+ \(t NUMBER\)   NUMBER is the base location in the buffer that indentation is
+            relative to.  If present, this is always the first of the
+            sublists.  The indentation of the line in question is
+            derived from the indentation of this point, possibly
+            modified by subsequent sublists.
+ \(+ VAR\)
+ \(- VAR\)      Get the value of variable VAR and add to or subtract from
+            the indentation calculated so far.
+ \(= VAR\)          Get the value of variable VAR and *replace* the
+            indentation with its value.  This only occurs for
+            special variables such as `sh-indent-comment'.
+ STRING             This is ignored for the purposes of calculating
+            indentation, it is printed in certain cases to help show
+            what the indentation is based on."
+   ;; See comments before `sh-kw'.
+   (save-excursion
+     (let ((have-result nil)
+         this-kw
+         start
+         val
+         (result 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) (not (bobp))
+                (eq (get-text-property (1- (point)) 'face)
+                    font-lock-string-face))
+           (eq (get-text-property (point) 'face) sh-heredoc-face))
+       (setq result t)
+       (setq have-result t))
+        ((looking-at "\\s-*#")         ; was (equal this-kw "#")
+       (if (bobp)
+           (setq result t) ;; return nil if 1st line!
+         (setq result (list '(= sh-indent-comment)))
+         ;; we still need to get previous line in case
+         ;; sh-indent-comment is t (indent as normal)
+         (setq align-point (sh-prev-line nil))
+         (setq have-result nil)
+         ))
+        ) ;; cond
+ 
+       (unless have-result
+       ;; Continuation lines are handled specially
+       (if (sh-this-is-a-continuation)
+           (progn
+             ;; We assume the line being continued is already
+             ;; properly indented...
+             ;; (setq prev-line-end (sh-prev-line))
+             (setq align-point (sh-prev-line nil))
+             (setq result (list '(+ sh-indent-for-continuation)))
+             (setq have-result t))
+         (beginning-of-line)
+         (skip-chars-forward " \t")
+         (setq this-kw (sh-get-kw)))
+ 
+         ;; Handle "this" keyword:  first word on the line we're
+       ;; calculating indentation info for.
+       (if this-kw
+           (if (setq val (sh-check-rule 1 this-kw))
+               (progn
+                 (setq align-point (point))
+                 (sh-debug
+                  "this - setting align-point to %d" align-point)
+                 (setq result (append result val))
+                 (setq have-result t)
+                 ;; set prev-line to continue processing remainder
+                 ;; of this line as a previous line
+                 (setq prev-line-end (point))
+                 ))))
+ 
+       (unless have-result
+       (setq prev-line-end (sh-prev-line 'end)))
+ 
+       (if prev-line-end
+         (save-excursion
+           ;; We start off at beginning of this line.
+           ;; Scan previous statements while this is <=
+           ;; start of previous line.
+           (setq start (point)) ;; for debug only
+           (goto-char prev-line-end)
+           (setq x t)
+           (while (and x (setq x  (sh-prev-thing)))
+             (sh-debug "at %d x is: %s  result is: %s" (point) x result)
+             (cond
+              ((and (equal x ")")
+                    (equal (get-text-property (1- (point)) 'syntax-table)
+                           sh-st-punc))
+               (sh-debug "Case label) here")
+               (setq x 'case-label)
+               (if (setq val (sh-check-rule 2 x))
+                   (progn
+                     (setq result (append result val))
+                     (setq align-point (point))))
+               (or (bobp)
+                   (forward-char -1))
+               (skip-chars-forward "[a-z0-9]*?")
+               )
+              ((string-match "[])}]" x)
+               (setq x (sh-safe-forward-sexp -1))
+               (if x
+                   (progn
+                     (setq align-point (point))
+                     (setq result (append result
+                                          (list "aligned to opening paren")))
+                     )))
+              ((string-match "[[({]" x)
+               (sh-debug "Checking special thing: %s" x)
+               (if (setq val (sh-check-rule 2 x))
+                   (setq result (append result val)))
+               (forward-char -1)
+               (setq align-point (point)))
+              ((string-match "[\"'`]" x)
+               (sh-debug "Skipping back for %s" x)
+               ;; this was oops-2
+               (setq x (sh-safe-forward-sexp -1)))
+              ((stringp x)
+               (sh-debug "Checking string %s at %s" x (point))
+               (if (setq val (sh-check-rule 2 x))
+                   ;; (or (eq t (car val))
+                   ;; (eq t (car (car val))))
+                   (setq result (append result val)))
+               ;; not sure about this test Wed Jan 27 23:48:35 1999
+               (setq align-point (point))
+               (unless (bolp)
+                 (forward-char -1)))
+              (t
+               (error "Don't know what to do with %s" x))
+              )
+             ) ;; while
+           (sh-debug "result is %s" result)
+           )
+       (sh-debug "No prev line!")
+       (sh-debug "result: %s  align-point: %s" result align-point)
+       )
+ 
+       (if align-point
+         ;; was: (setq result (append result (list (list t align-point))))
+         (setq result (append  (list (list t align-point)) result))
+       )
+       (sh-debug "result is now: %s" result)
+ 
+       (or result
+         (if prev-line-end
+             (setq result (list (list t prev-line-end)))
+           (setq result (list (list '= 'sh-first-lines-indent)))
+           ))
+ 
+       (if (eq result t)
+         (setq result nil))
+       (sh-debug  "result is: %s" result)
+       result
+       )       ;; let
+     ))
+ 
+ 
+ (defun sh-get-indent-var-for-line (&optional info)
+   "Return the variable controlling indentation for this line.
+ If there is not [just] one such variable, return a string
+ indicating the problem.
+ If INFO is supplied it is used, else it is calculated."
+   (let ((var nil)
+       (result nil)
+       (reason nil)
+       sym elt)
+     (or info
+       (setq info (sh-get-indent-info)))
+     (if (null info)
+       (setq result "this line to be left as is")
+       (while (and info (null result))
+       (setq elt (car info))
+       (cond
+        ((stringp elt)
+         (setq reason elt)
+         )
+        ((not (listp elt))
+         (error "sh-get-indent-var-for-line invalid elt: %s" elt))
+        ;; so it is a list
+        ((eq t (car elt))
+         ) ;; nothing
+        ((symbolp  (setq sym (nth 1 elt)))
+         ;; A bit of a kludge - when we see the sh-indent-comment
+         ;; ignore other variables.  Otherwise it is tricky to
+         ;; "learn" the comment indentation.
+         (if (eq var 'sh-indent-comment)
+             (setq result var)
+           (if var
+               (setq result
+                     "this line is controlled by more than 1 variable.")
+             (setq var sym))))
+        (t
+         (error "sh-get-indent-var-for-line invalid list elt: %s" elt)))
+       (setq info (cdr info))
+       ))
+     (or result
+       (setq result var))
+     (or result
+       (setq result reason))
+     (if (null result)
+       ;; e.g. just had (t POS)
+       (setq result "line has default indentation"))
+     result))
+ 
+ 
+ 
+ ;; Finding the previous line isn't trivial.
+ ;; We must *always* go back one more and see if that is a continuation
+ ;; line -- it is the PREVIOUS line which is continued, not the one
+ ;; we are going to!
+ ;; Also, we want to treat a whole "here document" as one big line,
+ ;; because we may want to a align to the beginning of it.
+ ;;
+ ;; What we do:
+ ;; - go back to previous non-empty line
+ ;; - if this is in a here-document, go to the beginning of it
+ ;; - while previous line is continued, go back one line
+ (defun sh-prev-line (&optional end)
+   "Back to end of previous non-comment non-empty line.
+ Go to beginning of logical line unless END is non-nil, in which case
+ we go to the end of the previous line and do not check for continuations."
+   (save-excursion
+     (beginning-of-line)
+     (forward-comment (- (point-max)))
+     (unless end (beginning-of-line))
+     (when (and (not (bobp))
+              (equal (get-text-property (1- (point)) 'face)
+                     sh-heredoc-face))
+       (let ((p1 (previous-single-property-change (1- (point)) 'face)))
+       (when p1
+         (goto-char p1)
+         (if end
+             (end-of-line)
+           (beginning-of-line)))))
+     (unless end
+       ;; we must check previous lines to see if they are continuation lines
+       ;; if so, we must return position of first of them
+       (while (and (sh-this-is-a-continuation)
+                 (>= 0 (forward-line -1))))
+       (beginning-of-line)
+       (skip-chars-forward " \t"))
+     (point)))
+ 
+ 
+ (defun sh-prev-stmt ()
+   "Return the address of the previous stmt or nil."
+   ;; This is used when we are trying to find a matching keyword.
+   ;; Searching backward for the keyword would certainly be quicker, but
+   ;; it is hard to remove "false matches" -- such as if the keyword
+   ;; appears in a string or quote.  This way is slower, but (I think) safer.
+   (interactive)
+   (save-excursion
+     (let ((going t)
+         (start (point))
+         (found nil)
+         (prev nil))
+       (skip-chars-backward " \t;|&({[")
+       (while (and (not found)
+                 (not (bobp))
+                 going)
+       ;; Do a backward-sexp if possible, else backup bit by bit...
+       (if (sh-safe-forward-sexp -1)
+           (progn
+             (if (looking-at sh-special-keywords)
+                 (progn
+                   (setq found prev))
+               (setq prev (point))
+               ))
+         ;; backward-sexp failed
+         (if (zerop (skip-chars-backward " \t()[\]{};`'"))
+             (forward-char -1))
+         (if (bolp)
+             (let ((back (sh-prev-line nil)))
+               (if back
+                   (goto-char back)
+                 (setq going nil)))))
+       (unless found
+         (skip-chars-backward " \t")
+         (if (or (and (bolp) (not (sh-this-is-a-continuation)))
+                 (eq (char-before) ?\;)
+                 (looking-at "\\s-*[|&]"))
+             (setq found (point)))))
+       (if found
+         (goto-char found))
+       (if found
+         (progn
+           (skip-chars-forward " \t|&({[")
+           (setq found (point))))
+       (if (>= (point) start)
+         (progn
+           (debug "We didn't move!")
+           (setq found nil))
+       (or found
+           (sh-debug "Did not find prev stmt.")))
+       found)))
+ 
+ 
+ (defun sh-get-word ()
+   "Get a shell word skipping whitespace from point."
+   (interactive)
+   (skip-chars-forward "\t ")
+   (let ((start (point)))
+     (while
+       (if (looking-at "[\"'`]")
+           (sh-safe-forward-sexp)
+         ;; (> (skip-chars-forward "^ \t\n\"'`") 0)
+         (> (skip-chars-forward "-_a-zA-Z\$0-9") 0)
+         ))
+     (buffer-substring start (point))
+     ))
+ 
+ (defun sh-prev-thing ()
+   "Return the previous thing this logical line."
+   ;; This is called when `sh-get-indent-info' is working backwards on
+   ;; the previous line(s) finding what keywords may be relevant for
+   ;; indenting.  It moves over sexps if possible, and will stop
+   ;; on a ; and at the beginning of a line if it is not a continuation
+   ;; line.
+   ;;
+   ;; Added a kludge for ";;"
+   ;; Possible return values:
+   ;;  nil  -  nothing
+   ;; a string - possibly a keyword
+   ;;
+   (if (bolp)
+       nil
+     (let (c min-point
+         (start (point)))
+       (save-restriction
+       (narrow-to-region
+       (if (sh-this-is-a-continuation)
+           (setq min-point (sh-prev-line nil))
+         (save-excursion
+           (beginning-of-line)
+           (setq min-point (point))))
+       (point))
+       (skip-chars-backward " \t;")
+       (unless (looking-at "\\s-*;;")
+       (skip-chars-backward "^)}];\"'`({[")
+       (setq c (char-before))))
+       (sh-debug "stopping at %d c is %s  start=%d min-point=%d"
+               (point) c start min-point)
+       (if (< (point) min-point)
+         (error "point %d < min-point %d" (point) min-point))
+       (cond
+        ((looking-at "\\s-*;;")
+       ;; (message "Found ;; !")
+        ";;")
+        ((or (eq c ?\n)
+           (eq c nil)
+           (eq c ?\;))
+        (save-excursion
+        ;; skip forward over white space newline and \ at eol
+        (skip-chars-forward " \t\n\\\\")
+        (sh-debug "Now at %d   start=%d" (point) start)
+        (if (>= (point) start)
+            (progn
+              (sh-debug "point: %d >= start: %d" (point) start)
+              nil)
+          (sh-get-word))
+        ))
+        (t
+       ;; c    -- return a string
+        (char-to-string c)
+        ))
+       )))
+ 
+ 
+ (defun sh-this-is-a-continuation ()
+   "Return non-nil if current line is a continuation of previous line."
+   (save-excursion
+     (and (zerop (forward-line -1))
+        (looking-at ".*\\\\$")
+        (not (nth 4 (parse-partial-sexp (match-beginning 0) (match-end 0)
+                                        nil nil nil t))))))
+ 
+ (defun sh-get-kw (&optional where and-move)
+   "Return first word of line from WHERE.
+ If AND-MOVE is non-nil then move to end of word."
+   (let ((start (point)))
+     (if where
+       (goto-char where))
+     (prog1
+       (buffer-substring (point)
+                         (progn (skip-chars-forward "^ \t\n;")(point)))
+       (unless and-move
+       (goto-char start)))))
+ 
+ (defun sh-find-prev-matching (open close &optional depth)
+   "Find a matching token for a set of opening and closing keywords.
+ This takes into account that there may be nested open..close pairings.
+ OPEN and CLOSE are regexps denoting the tokens to be matched.
+ Optional parameter DEPTH (usually 1) says how many to look for."
+   (let ((parse-sexp-ignore-comments t)
+       prev)
+     (setq depth (or depth 1))
+     (save-excursion
+       (condition-case nil
+         (while (and
+                 (/= 0  depth)
+                 (not (bobp))
+                 (setq prev (sh-prev-stmt)))
+           (goto-char prev)
+           (save-excursion
+             (if (looking-at "\\\\\n")
+                 (progn
+                   (forward-char 2)
+                   (skip-chars-forward " \t")))
+             (cond
+              ((looking-at open)
+               (setq depth (1- depth))
+               (sh-debug "found open at %d - depth = %d" (point) depth))
+              ((looking-at close)
+               (setq depth (1+ depth))
+               (sh-debug "found close - depth = %d" depth))
+              (t
+               ))))
+       (error nil))
+       (if (eq depth 0)
+         prev ;; (point)
+       nil)
+       )))
+ 
+ 
+ (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
+ IGNORE-ERROR is non-nil."
+   (let ((val (symbol-value var)))
+     (cond
+      ((numberp val)
+       val)
+      ((eq val t)
+       val)
+      ((null val)
+       val)
+      ((eq val '+)
+       sh-basic-offset)
+      ((eq val '-)
+       (- sh-basic-offset))
+      ((eq val '++)
+       (* 2 sh-basic-offset))
+      ((eq val '--)
+       (* 2 (- sh-basic-offset)))
+      ((eq val '*)
+       (/ sh-basic-offset 2))
+      ((eq val '/)
+       (/ (- sh-basic-offset) 2))
+      (t
+       (if ignore-error
+       (progn
+       (message "Don't know how to handle %s's value of %s" var val)
+       0)
+       (error "Don't know how to handle %s's value of %s" var val))
+       ))))
+ 
+ (defun sh-set-var-value (var value &optional no-symbol)
+   "Set variable VAR to VALUE.
+ Unless optional argument NO-SYMBOL is non-nil, then if VALUE is
+ can be represented by a symbol then do so."
+   (cond
+    (no-symbol
+     (set var value))
+    ((= value sh-basic-offset)
+     (set var '+))
+    ((= value (- sh-basic-offset))
+     (set var '-))
+    ((eq value (* 2 sh-basic-offset))
+     (set var  '++))
+    ((eq value (* 2 (- sh-basic-offset)))
+     (set var  '--))
+    ((eq value (/ sh-basic-offset 2))
+     (set var  '*))
+    ((eq value (/ (- sh-basic-offset) 2))
+     (set var  '/))
+    (t
+     (set var value)))
+   )
+ 
+ 
+ (defun sh-calculate-indent (&optional info)
+   "Return the indentation for the current line.
+ If INFO is supplied it is used, else it is calculated from current line."
+   (let ((ofs 0)
+       (base-value 0)
+       elt a b var val)
+     (or info
+       (setq info (sh-get-indent-info)))
+     (when info
+       (while info
+       (sh-debug "info: %s  ofs=%s" info ofs)
+       (setq elt (car info))
+       (cond
+        ((stringp elt)) ;; do nothing?
+        ((listp elt)
+         (setq a (car (car info)))
+         (setq b (nth 1 (car info)))
+         (cond
+          ((eq a t)
+           (save-excursion
+             (goto-char b)
+             (setq val (current-indentation)))
+           (setq base-value val))
+          ((symbolp b)
+           (setq val (sh-var-value b))
+           (cond
+            ((eq a '=)
+             (cond
+              ((null val)
+               ;; no indentation
+               ;; set info to nil so  we stop immediately
+               (setq base-value nil  ofs nil  info nil))
+              ((eq val t) (setq ofs 0)) ;; indent as normal line
+              (t
+               ;; The following assume the (t POS) come first!
+               (setq ofs val  base-value 0)
+               (setq info nil))))      ;; ? stop now
+            ((eq a '+) (setq ofs (+ ofs val)))
+            ((eq a '-) (setq ofs (- ofs val)))
+            (t
+             (error "sh-calculate-indent invalid a a=%s b=%s" a b))))
+          (t
+           (error "sh-calculate-indent invalid elt: a=%s b=%s" a b))))
+        (t
+         (error "sh-calculate-indent invalid elt %s" elt)))
+       (sh-debug "a=%s b=%s val=%s base-value=%s ofs=%s"
+                 a b val base-value ofs)
+       (setq info (cdr info)))
+       ;; return value:
+       (sh-debug "at end:  base-value: %s    ofs: %s" base-value ofs)
+ 
+       (cond
+        ((or (null base-value)(null ofs))
+       nil)
+        ((and (numberp base-value)(numberp ofs))
+       (sh-debug "base (%d) + ofs (%d) = %d"
+                 base-value ofs (+ base-value ofs))
+       (+ base-value ofs)) ;; return value
+        (t
+       (error "sh-calculate-indent:  Help.  base-value=%s ofs=%s"
+              base-value ofs)
+       nil)))))
+ 
+ 
+ (defun sh-indent-line ()
+   "Indent the current line."
+   (interactive)
+   (let ((indent (sh-calculate-indent))
+       (pos (- (point-max) (point))))
+     (when indent
+       (beginning-of-line)
+       (skip-chars-forward " \t")
+       (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))
+         (goto-char (- (point-max) pos))))))
+ 
+ 
+ (defun sh-blink (blinkpos &optional msg)
+   "Move cursor momentarily to BLINKPOS and display MSG."
+   ;; We can get here without it being a number on first line
+   (if (numberp blinkpos)
+       (save-excursion
+       (goto-char blinkpos)
+       (message msg)
+       (sit-for blink-matching-delay))
+     (message msg)))
+ 
+ (defun sh-show-indent (arg)
+   "Show the how the currently 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),
+ a pop up window describes this variable.
+ If variable `sh-blink' is non-nil then momentarily go to the line
+ we are indenting relative to, if applicable."
+   (interactive "P")
+   (sh-must-support-indent)
+   (let* ((info (sh-get-indent-info))
+        (var (sh-get-indent-var-for-line info))
+        (curr-indent (current-indentation))
+        val msg)
+     (if (stringp var)
+       (message (setq msg var))
+       (setq val (sh-calculate-indent info))
+ 
+       (if (eq curr-indent val)
+         (setq msg (format "%s is %s" var (symbol-value var)))
+       (setq msg
+             (if val
+                 (format "%s (%s) would change indent from %d to: %d"
+                         var (symbol-value var) curr-indent val)
+               (format "%s (%s) would leave line as is"
+                       var (symbol-value var)))
+             ))
+       (if (and arg var)
+         (describe-variable var)))
+     (if sh-blink
+       (let ((info (sh-get-indent-info)))
+         (if (and info (listp (car info))
+                  (eq (car (car info)) t))
+             (sh-blink (nth 1 (car info))  msg)
+           (message msg)))
+       (message msg))
+     ))
+ 
+ (defun sh-set-indent ()
+   "Set the indentation for the current line.
+ If the current line is controlled by an indentation variable, prompt
+ for a new value for it."
+   (interactive)
+   (sh-must-support-indent)
+   (let* ((info (sh-get-indent-info))
+        (var (sh-get-indent-var-for-line info))
+        val old-val indent-val)
+     (if (stringp var)
+       (message (format "Cannot set indent - %s" var))
+       (setq old-val (symbol-value var))
+       (setq val (sh-read-variable var))
+       (condition-case nil
+         (progn
+           (set var val)
+           (setq indent-val (sh-calculate-indent info))
+           (if indent-val
+               (message "Variable: %s  Value: %s  would indent to: %d"
+                        var (symbol-value var) indent-val)
+             (message "Variable: %s  Value: %s  would leave line as is."
+                      var (symbol-value var)))
+           ;; I'm not sure about this, indenting it now?
+           ;; No.  Because it would give the impression that an undo would
+           ;; restore thing, but the value has been altered.
+           ;; (sh-indent-line)
+           )
+       (error
+        (set var old-val)
+        (message "Bad value for %s, restoring to previous value %s"
+                 var old-val)
+        (sit-for 1)
+        nil))
+       )))
+ 
+ 
+ (defun sh-learn-line-indent (arg)
+   "Learn how to indent a line as it currently is indented.
+ 
+ If there is an indentation variable which controls this line's indentation,
+ then set it to a value which would indent the line the way it
+ presently is.
+ 
+ If the value can be represented by one of the symbols then do so
+ unless optional argument ARG (the prefix when interactive) is non-nil."
+   (interactive "*P")
+   (sh-must-support-indent)
+   ;; I'm not sure if we show allow learning on an empty line.
+   ;; Though it might occasionally be useful I think it usually
+   ;; would just be confusing.
+   (if (save-excursion
+       (beginning-of-line)
+       (looking-at "\\s-*$"))
+       (message "sh-learn-line-indent ignores empty lines.")
+     (let* ((info (sh-get-indent-info))
+          (var (sh-get-indent-var-for-line info))
+          ival sval diff new-val
+          (no-symbol arg)
+          (curr-indent (current-indentation)))
+       (cond
+        ((stringp var)
+       (message (format "Cannot learn line - %s" var)))
+        ((eq var 'sh-indent-comment)
+       ;; This is arbitrary...
+       ;; - if curr-indent is 0, set to curr-indent
+       ;; - else if it has the indentation of a "normal" line,
+       ;;   then set to t
+       ;; - else set to curr-indent.
+       (setq sh-indent-comment
+             (if (= curr-indent 0)
+                 0
+               (let* ((sh-indent-comment t)
+                      (val2 (sh-calculate-indent info)))
+                 (if (= val2 curr-indent)
+                     t
+                   curr-indent))))
+       (message "%s set to %s" var (symbol-value var))
+       )
+        ((numberp (setq sval (sh-var-value var)))
+       (setq ival (sh-calculate-indent info))
+       (setq diff (- curr-indent ival))
+ 
+       (sh-debug "curr-indent: %d   ival: %d  diff: %d  var:%s  sval %s"
+                 curr-indent ival diff  var sval)
+       (setq new-val (+ sval diff))
+ ;;;     I commented out this because someone might want to replace
+ ;;;     a value of `+' with the current value of sh-basic-offset
+ ;;;     or vice-versa.
+ ;;;     (if (= 0 diff)
+ ;;;         (message "No change needed!")
+       (sh-set-var-value var new-val no-symbol)
+       (message "%s set to %s" var (symbol-value var))
+       )
+        (t
+       (debug)
+       (message "Cannot change %s" var))))))
+ 
+ 
+ 
+ (defun sh-mark-init (buffer)
+   "Initialize a BUFFER to be used by `sh-mark-line'."
+   (save-excursion
+     (set-buffer (get-buffer-create buffer))
+     (erase-buffer)
+     (occur-mode)
+     ))
+ 
+ 
+ (defun sh-mark-line (message point buffer &optional add-linenum occur-point)
+   "Insert MESSAGE referring to location POINT in current buffer into BUFFER.
+ Buffer BUFFER is in `occur-mode'.
+ 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))
+       start
+       (line ""))
+     (when point
+       (set-marker m1 point (current-buffer))
+       (if add-linenum
+         (setq line (format "%d: " (1+ (count-lines 1 point))))))
+     (save-excursion
+       (if (get-buffer buffer)
+         (set-buffer (get-buffer buffer))
+       (set-buffer (get-buffer-create buffer))
+       (occur-mode)
+       )
+       (goto-char (point-max))
+       (setq start (point))
+       (insert line)
+       (if occur-point
+         (setq occur-point (point)))
+       (insert message)
+       (if point
+         (add-text-properties
+          start (point)
+          '(mouse-face highlight
+            help-echo "mouse-2: go to the line where I learned this")))
+       (insert "\n")
+       (if point
+         (progn
+           (put-text-property start (point) 'occur-target m1)
+           (if occur-point
+               (put-text-property start occur-point
+                                  'occur-match t))
+           ))
+       )))
+ 
+ 
+ 
+ ;; Is this really worth having?
+ (defvar sh-learned-buffer-hook nil
+   "*An abnormal hook, called with an alist of learned variables.")
+ ;; Example of how to use sh-learned-buffer-hook
+ ;;
+ ;; (defun what-i-learned (list)
+ ;;   (let ((p list))
+ ;;     (save-excursion
+ ;;       (set-buffer "*scratch*")
+ ;;       (goto-char (point-max))
+ ;;       (insert "(setq\n")
+ ;;       (while p
+ ;;    (insert (format "  %s %s \n"
+ ;;                    (nth 0 (car p)) (nth 1 (car p))))
+ ;;    (setq p (cdr p)))
+ ;;       (insert ")\n")
+ ;;       )))
+ ;;
+ ;; (add-hook 'sh-learned-buffer-hook 'what-i-learned)
+ 
+ 
+ ;; Originally this was sh-learn-region-indent (beg end)
+ ;; However, in practice this was awkward so I changed it to
+ ;; use the whole buffer.  Use narrowing if needbe.
+ (defun sh-learn-buffer-indent (&optional arg)
+   "Learn how to indent the buffer the way it currently is.
+ 
+ Output in buffer \"*indent*\" shows any lines which have conflicting
+ values of a variable, and the final value of all variables learned.
+ This buffer is popped to automatically if there are any discrepancies.
+ 
+ If no prefix ARG is given, then variables are set to numbers.
+ If a prefix arg is given, then variables are set to symbols when
+ applicable -- e.g. to symbol `+' if the value is that of the
+ basic indent.
+ If a positive numerical prefix is given, then  `sh-basic-offset'
+ is set to the prefix's numerical value.
+ Otherwise, sh-basic-offset may or may not be changed, according
+ to the value of variable `sh-learn-basic-offset'.
+ 
+ Abnormal hook `sh-learned-buffer-hook' if non-nil is called when the
+ function completes.  The function is abnormal because it is called
+ with an alist of variables learned.  This feature may be changed or
+ removed in the future.
+ 
+ This command can often take a long time to run."
+   (interactive "P")
+   (sh-must-support-indent)
+   (save-excursion
+     (goto-char (point-min))
+     (let ((learned-var-list nil)
+         (out-buffer "*indent*")
+         (num-diffs 0)
+         previous-set-info
+         (max 17)
+         vec
+         msg
+         (comment-col nil) ;; number if all same, t if seen diff values
+         (comments-always-default t) ;; nil if we see one not default
+         initial-msg
+         (specified-basic-offset (and arg (numberp arg)
+                                      (> arg 0)))
+         (linenum 0)
+         suggested)
+       (setq vec (make-vector max 0))
+       (sh-mark-init out-buffer)
+ 
+       (if specified-basic-offset
+         (progn
+           (setq sh-basic-offset arg)
+           (setq initial-msg
+                 (format "Using specified sh-basic-offset of %d"
+                         sh-basic-offset)))
+       (setq initial-msg
+             (format "Initial value of sh-basic-offset: %s"
+                     sh-basic-offset)))
+ 
+       (while (< (point) (point-max))
+       (setq linenum (1+ linenum))
+       ;; (if (zerop (% linenum 10))
+       (message "line %d" linenum)
+       ;; )
+       (unless (looking-at "\\s-*$") ;; ignore empty lines!
+         (let* ((sh-indent-comment t) ;; info must return default indent
+                (info (sh-get-indent-info))
+                (var (sh-get-indent-var-for-line info))
+                sval ival diff new-val
+                (curr-indent (current-indentation)))
+           (cond
+            ((null var)
+             nil)
+            ((stringp var)
+             nil)
+            ((numberp (setq sval (sh-var-value var 'no-error)))
+             ;; the numberp excludes comments since sval will be t.
+             (setq ival (sh-calculate-indent))
+             (setq diff (- curr-indent ival))
+             (setq new-val (+ sval diff))
+             (sh-set-var-value var new-val 'no-symbol)
+             (unless (looking-at "\\s-*#") ;; don't learn from comments
+               (if (setq previous-set-info (assoc var learned-var-list))
+                   (progn
+                     ;; it was already there, is it same value ?
+                     (unless (eq (symbol-value var)
+                                 (nth 1 previous-set-info))
+                       (sh-mark-line
+                        (format "Variable %s was set to %s"
+                                var (symbol-value var))
+                        (point) out-buffer t t)
+                       (sh-mark-line
+                        (format "  but was previously set to %s"
+                                (nth 1 previous-set-info))
+                        (nth 2 previous-set-info) out-buffer t)
+                       (setq num-diffs (1+ num-diffs))
+                       ;; (delete previous-set-info  learned-var-list)
+                       (setcdr previous-set-info
+                               (list (symbol-value var) (point)))
+                       )
+                     )
+                 (setq learned-var-list
+                       (append (list (list var (symbol-value var)
+                                           (point)))
+                               learned-var-list)))
+               (if (numberp new-val)
+                   (progn
+                     (sh-debug
+                      "This line's indent value: %d"  new-val)
+                     (if (< new-val 0)
+                         (setq new-val (- new-val)))
+                     (if (< new-val max)
+                         (aset vec new-val (1+ (aref vec new-val))))))
+               ))
+            ((eq var 'sh-indent-comment)
+             (unless (= curr-indent (sh-calculate-indent info))
+               ;; this is not the default indentation
+               (setq comments-always-default nil)
+               (if comment-col ;; then we have see one before
+                   (or (eq comment-col curr-indent)
+                       (setq comment-col t)) ;; seen a different one
+                 (setq comment-col curr-indent))
+               ))
+            (t
+             (sh-debug "Cannot learn this line!!!")
+             ))
+           (sh-debug
+            "at %s learned-var-list is %s" (point) learned-var-list)
+           ))
+       (forward-line 1)
+       ) ;; while
+       (if sh-debug
+         (progn
+           (setq msg (format
+                      "comment-col = %s  comments-always-default = %s"
+                      comment-col comments-always-default))
+           ;; (message msg)
+           (sh-mark-line  msg nil out-buffer)))
+       (cond
+        ((eq comment-col 0)
+       (setq msg  "\nComments are all in 1st column.\n"))
+        (comments-always-default
+       (setq msg  "\nComments follow default indentation.\n")
+       (setq comment-col t))
+        ((numberp comment-col)
+       (setq msg  (format "\nComments are in col %d." comment-col)))
+        (t
+       (setq msg  "\nComments seem to be mixed, leaving them as is.\n")
+       (setq comment-col nil)
+       ))
+       (sh-debug msg)
+       (sh-mark-line  msg nil out-buffer)
+ 
+       (sh-mark-line initial-msg nil out-buffer t t)
+ 
+       (setq suggested (sh-guess-basic-offset vec))
+ 
+       (if (and suggested (not specified-basic-offset))
+         (let ((new-value
+                (cond
+                 ;; t => set it if we have a single value as a number
+                 ((and (eq sh-learn-basic-offset t) (numberp suggested))
+                  suggested)
+                 ;; other non-nil => set it if only one value was found
+                 (sh-learn-basic-offset
+                  (if (numberp suggested)
+                      suggested
+                    (if (= (length suggested) 1)
+                        (car suggested))))
+                 (t
+                  nil))))
+           (if new-value
+               (progn
+                 (setq learned-var-list
+                       (append (list (list 'sh-basic-offset
+                                           (setq sh-basic-offset new-value)
+                                           (point-max)))
+                               learned-var-list))
+                 ;; Not sure if we need to put this line in, since
+                 ;; it will appear in the "Learned variable settings".
+                 (sh-mark-line
+                  (format "Changed sh-basic-offset to: %d" sh-basic-offset)
+                  nil out-buffer))
+             (sh-mark-line
+              (if (listp suggested)
+                  (format "Possible value(s) for sh-basic-offset:  %s"
+                          (mapconcat 'int-to-string suggested " "))
+                (format "Suggested sh-basic-offset:  %d" suggested))
+              nil out-buffer))))
+ 
+ 
+       (setq learned-var-list
+           (append (list (list 'sh-indent-comment comment-col (point-max)))
+                   learned-var-list))
+       (setq sh-indent-comment comment-col)
+       (let ((name (buffer-name)))
+       (sh-mark-line  "\nLearned variable settings:" nil out-buffer)
+       (if arg
+           ;; Set learned variables to symbolic rather than numeric
+           ;; values where possible.
+           (dolist (learned-var (reverse learned-var-list))
+             (let ((var (car learned-var))
+                   (val (nth 1 learned-var)))
+               (when (and (not (eq var 'sh-basic-offset))
+                          (numberp val))
+                 (sh-set-var-value var val)))))
+       (dolist (learned-var (reverse learned-var-list))
+         (let ((var (car learned-var)))
+           (sh-mark-line (format "  %s %s" var (symbol-value var))
+                         (nth 2 learned-var) out-buffer)))
+       (save-excursion
+         (set-buffer out-buffer)
+         (goto-char (point-min))
+         (insert
+          (format "Indentation values for buffer %s.\n" name)
+          (format "%d indentation variable%s different values%s\n\n"
+                  num-diffs
+                  (if (= num-diffs 1)
+                      " has"   "s have")
+                  (if (zerop num-diffs)
+                      "." ":"))
+          )))
+       ;; Are abnormal hooks considered bad form?
+       (run-hook-with-args 'sh-learned-buffer-hook learned-var-list)
+       (if (or sh-popup-occur-buffer (> num-diffs 0))
+         (pop-to-buffer out-buffer))
+       )))
+ 
+ (defun sh-guess-basic-offset (vec)
+   "See if we can determine a reasonable value for `sh-basic-offset'.
+ This is experimental, heuristic and arbitrary!
+ Argument VEC is a vector of information collected by
+ `sh-learn-buffer-indent'.
+ Return values:
+   number          - there appears to be a good single value
+   list of numbers - no obvious one, here is a list of one or more
+                   reasonable choices
+   nil           - we couldn't find a reasonable one."
+   (let* ((max (1- (length vec)))
+        (i 1)
+        (totals (make-vector max 0)))
+     (while (< i max)
+       (aset totals i (+ (aref totals i) (* 4 (aref vec i))))
+       (if (zerop (% i 2))
+         (aset totals i (+ (aref totals i) (aref vec (/ i 2)))))
+       (if (< (* i 2) max)
+         (aset totals i (+ (aref totals i) (aref vec (* i 2)))))
+       (setq i (1+ i)))
+ 
+     (let ((x nil)
+         (result nil)
+         tot sum p)
+       (setq i 1)
+       (while (< i max)
+       (if (/= (aref totals i) 0)
+           (setq x (append x (list (cons i (aref totals i))))))
+       (setq i (1+ i)))
+ 
+       (setq x (sort x (lambda (a b) (> (cdr a) (cdr b)))))
+       (setq tot (apply '+ (append totals nil)))
+       (sh-debug (format "vec: %s\ntotals: %s\ntot: %d"
+                       vec totals tot))
+       (cond
+        ((zerop (length x))
+       (message "no values!")) ;; we return nil
+        ((= (length x) 1)
+       (message "only value is %d" (car (car x)))
+       (setq result (car (car x)))) ;; return single value
+        ((> (cdr (car x)) (/ tot 2))
+       ;; 1st is > 50%
+       (message "basic-offset is probably %d" (car (car x)))
+       (setq result (car (car x)))) ;;   again, return a single value
+        ((>=  (cdr (car x)) (* 2 (cdr (car (cdr x)))))
+       ;; 1st is >= 2 * 2nd
+       (message "basic-offset could be %d" (car (car x)))
+       (setq result (car (car x))))
+        ((>= (+ (cdr (car x))(cdr (car (cdr x)))) (/ tot 2))
+       ;; 1st & 2nd together >= 50%  - return a list
+       (setq p x  sum 0 result nil)
+       (while  (and p
+                    (<= (setq sum (+ sum (cdr (car p)))) (/ tot 2)))
+         (setq result (append result (list (car (car p)))))
+         (setq p (cdr p)))
+       (message "Possible choices for sh-basic-offset: %s"
+                (mapconcat 'int-to-string result " ")))
+        (t
+       (message "No obvious value for sh-basic-offset.  Perhaps %d"
+                (car (car x)))
+       ;; result is nil here
+       ))
+       result)))
+ 
+ ;; ========================================================================
+ 
+ ;; Styles -- a quick and dirty way of saving the indentation settings.
+ 
+ (defvar sh-styles-alist nil
+   "A list of all known shell indentation styles.")
+ 
+ (defun sh-name-style (name &optional confirm-overwrite)
+   "Name the current indentation settings as a style called NAME.
+ If this name exists, the command will prompt whether it should be
+ overwritten if
+ - - it was called interactively with a prefix argument, or
+ - - called non-interactively with optional CONFIRM-OVERWRITE non-nil."
+   ;; (interactive "sName for this style: ")
+   (interactive
+    (list
+     (read-from-minibuffer "Name for this style? " )
+     (not current-prefix-arg)))
+   (let ((slist (cons name
+                    (mapcar (lambda (var) (cons var (symbol-value var)))
+                            sh-var-list)))
+       (style (assoc name sh-styles-alist)))
+     (if style
+       (if (and confirm-overwrite
+                (not (y-or-n-p "This style exists.  Overwrite it? ")))
+           (message "Not changing style %s" name)
+         (message "Updating style %s" name)
+         (setcdr style (cdr slist)))
+       (message "Creating new style %s" name)
+       (push slist sh-styles-alist))))
+ 
+ (defun sh-load-style (name)
+   "Set shell indentation values for this buffer from those in style NAME."
+   (interactive (list (completing-read
+                     "Which style to use for this buffer? "
+                     sh-styles-alist nil t)))
+   (let ((sl (assoc name  sh-styles-alist)))
+     (if (null sl)
+       (error "sh-load-style - style %s not known" name)
+       (dolist (var (cdr sl))
+       (set (car var) (cdr var))))))
+ 
+ (defun sh-save-styles-to-buffer (buff)
+   "Save all current styles in elisp to buffer BUFF.
+ This is always added to the end of the buffer."
+   (interactive (list
+               (read-from-minibuffer "Buffer to save styles in? " 
"*scratch*")))
+   (with-current-buffer (get-buffer-create buff)
+     (goto-char (point-max))
+     (insert "\n")
+     (pp `(setq sh-styles-alist ',sh-styles-alist) (current-buffer))))
+ 
+ 
+ 
+ ;; statement syntax-commands for various shells
+ 
+ ;; 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: "
+        "switch( " str " )" \n
+        > "case " (read-string "pattern: ") ?: \n
+        > _ \n
+        "breaksw" \n
+        ( "other pattern, %s: "
+        < "case " str ?: \n
+        > _ \n
+        "breaksw" \n)
+        < "default:" \n
+        > _ \n
+        resume:
+        < < "endsw" \n)
+   (es)
+   (rc "expression: "
+       > "switch( " str " ) {" \n
+       > "case " (read-string "pattern: ") \n
+       > _ \n
+       ( "other pattern, %s: "
+       "case " str > \n
+       > _ \n)
+       "case *" > \n
+       > _ \n
+       resume:
+       ?\} > \n)
+   (sh "expression: "
+       > "case " str " in" \n
+       ( "pattern, %s: "
+       > str sh-non-closing-paren \n
+       > _ \n
+       ";;" \n)
+       > "*" sh-non-closing-paren \n
+       > _ \n
+       resume:
+       "esac" > \n))
+ 
+ (define-skeleton sh-for
+   "Insert a for loop.  See `sh-feature'."
+   (csh sh-modify sh
+        1 ""
+        2 "foreach "
+        4 " ( "
+        6 " )"
+        15 '<
+        16 "end")
+   (es sh-modify rc
+       4 " = ")
+   (rc sh-modify sh
+       2 "for( "
+       6 " ) {"
+       15 ?\} )
+   (sh "Index variable: "
+       > "for " str " in " _ "; do" \n
+       > _ | ?$ & (sh-remember-variable str) \n
+       "done" > \n))
+ 
+ 
+ 
+ (define-skeleton sh-indexed-loop
+   "Insert an indexed loop from 1 to n.  See `sh-feature'."
+   (bash sh-modify posix)
+   (csh "Index variable: "
+        "@ " str " = 1" \n
+        "while( $" str " <= " (read-string "upper limit: ") " )" \n
+        > _ ?$ str \n
+        "@ " str "++" \n
+        < "end" \n)
+   (es sh-modify rc
+       4 " =")
+   (ksh88 "Index variable: "
+        > "integer " str "=0" \n
+        > "while (( ( " str " += 1 ) <= "
+        (read-string "upper limit: ")
+        " )); do" \n
+        > _ ?$ (sh-remember-variable str) > \n
+        "done" > \n)
+   (posix "Index variable: "
+        > str "=1" \n
+        "while [ $" str " -le "
+        (read-string "upper limit: ")
+        " ]; do" \n
+        > _ ?$ str \n
+        str ?= (sh-add (sh-remember-variable str) 1) \n
+        "done" > \n)
+   (rc "Index variable: "
+       > "for( " str " in" " `{awk 'BEGIN { for( i=1; i<="
+       (read-string "upper limit: ")
+       "; i++ ) print i }'`}) {" \n
+       > _ ?$ (sh-remember-variable str) \n
+       ?\} > \n)
+   (sh "Index variable: "
+       > "for " str " in `awk 'BEGIN { for( i=1; i<="
+       (read-string "upper limit: ")
+       "; i++ ) print i }'`; do" \n
+       > _ ?$ (sh-remember-variable str) \n
+       "done" > \n))
+ 
+ 
+ (defun sh-shell-initialize-variables ()
+   "Scan the buffer for variable assignments.
+ Add these variables to `sh-shell-variables'."
+   (message "Scanning buffer `%s' for variable assignments..." (buffer-name))
+   (save-excursion
+     (goto-char (point-min))
+     (setq sh-shell-variables-initialized t)
+     (while (search-forward "=" nil t)
+       (sh-assignment 0)))
+   (message "Scanning buffer `%s' for variable assignments...done"
+          (buffer-name)))
+ 
+ (defvar sh-add-buffer)
+ 
+ (defun sh-add-completer (string predicate code)
+   "Do completion using `sh-shell-variables', but initialize it first.
+ This function is designed for use as the \"completion table\",
+ so it takes three arguments:
+   STRING, the current buffer contents;
+   PREDICATE, the predicate for filtering possible matches;
+   CODE, which says what kind of things to do.
+ CODE can be nil, t or `lambda'.
+ nil means to return the best completion of STRING, or nil if there is none.
+ t means to return a list of all possible completions of STRING.
+ `lambda' means to return t if STRING is a valid completion as it stands."
+   (let ((sh-shell-variables
+        (save-excursion
+          (set-buffer sh-add-buffer)
+          (or sh-shell-variables-initialized
+              (sh-shell-initialize-variables))
+          (nconc (mapcar (lambda (var)
+                           (let ((name
+                                  (substring var 0 (string-match "=" var))))
+                             (cons name name)))
+                         process-environment)
+                 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."
+   (interactive
+    (let ((sh-add-buffer (current-buffer)))
+      (list (completing-read "Variable: " 'sh-add-completer)
+          (prefix-numeric-value current-prefix-arg))))
+   (insert (sh-feature '((bash . "$[ ")
+                       (ksh88 . "$(( ")
+                       (posix . "$(( ")
+                       (rc . "`{expr $")
+                       (sh . "`expr $")
+                       (zsh . "$[ ")))
+         (sh-remember-variable var)
+         (if (< delta 0) " - " " + ")
+         (number-to-string (abs delta))
+         (sh-feature '((bash . " ]")
+                       (ksh88 . " ))")
+                       (posix . " ))")
+                       (rc . "}")
+                       (sh . "`")
+                       (zsh . " ]")))))
+ 
+ 
+ 
+ (define-skeleton sh-function
+   "Insert a function definition.  See `sh-feature'."
+   (bash sh-modify ksh88
+       3 "() {")
+   (ksh88 "name: "
+        "function " str " {" \n
+        > _ \n
+        < "}" \n)
+   (rc sh-modify ksh88
+       1 "fn ")
+   (sh ()
+       "() {" \n
+       > _ \n
+       < "}" \n))
+ 
+ 
+ 
+ (define-skeleton sh-if
+   "Insert an if statement.  See `sh-feature'."
+   (csh "condition: "
+        "if( " str " ) then" \n
+        > _ \n
+        ( "other condition, %s: "
+        < "else if( " str " ) then" \n
+        > _ \n)
+        < "else" \n
+        > _ \n
+        resume:
+        < "endif" \n)
+   (es "condition: "
+       > "if { " str " } {" \n
+       > _ \n
+       ( "other condition, %s: "
+       "} { " str " } {" > \n
+       > _ \n)
+       "} {" > \n
+       > _ \n
+       resume:
+       ?\} > \n)
+   (rc "condition: "
+       > "if( " str " ) {" \n
+       > _ \n
+       ( "other condition, %s: "
+       "} else if( " str " ) {"  > \n
+       > _ \n)
+       "} else {" > \n
+       > _ \n
+       resume:
+       ?\} > \n)
+   (sh "condition: "
+       '(setq input (sh-feature sh-test))
+       > "if " str "; then" \n
+       > _ \n
+       ( "other condition, %s: "
+       >  "elif " str "; then" > \n
+       > \n)
+       "else" > \n
+       > \n
+       resume:
+       "fi" > \n))
+ 
+ 
+ 
+ (define-skeleton sh-repeat
+   "Insert a repeat loop definition.  See `sh-feature'."
+   (es nil
+       > "forever {" \n
+       > _ \n
+       ?\} > \n)
+   (zsh "factor: "
+        > "repeat " str "; do" > \n
+        >  \n
+        "done" > \n))
+ 
+ ;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
+ 
+ 
+ 
+ (define-skeleton sh-select
+   "Insert a select statement.  See `sh-feature'."
+   (ksh88 "Index variable: "
+        > "select " str " in " _ "; do" \n
+        > ?$ str \n
+        "done" > \n)
+   (bash sh-append ksh88))
+ ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
+ 
+ 
+ 
+ (define-skeleton sh-tmp-file
+   "Insert code to setup temporary file handling.  See `sh-feature'."
+   (bash sh-append ksh88)
+   (csh (file-name-nondirectory (buffer-file-name))
+        "set tmp = /tmp/" str ".$$" \n
+        "onintr exit" \n _
+        (and (goto-char (point-max))
+           (not (bolp))
+           ?\n)
+        "exit:\n"
+        "rm $tmp* >&/dev/null" > \n)
+   (es (file-name-nondirectory (buffer-file-name))
+       > "local( signals = $signals sighup sigint; tmp = /tmp/" str
+       ".$pid ) {" \n
+       > "catch @ e {" \n
+       > "rm $tmp^* >[2]/dev/null" \n
+       "throw $e" \n
+       "} {" > \n
+       _ \n
+       ?\} > \n
+       ?\} > \n)
+   (ksh88 sh-modify sh
+        7 "EXIT")
+   (rc (file-name-nondirectory (buffer-file-name))
+       > "tmp = /tmp/" str ".$pid" \n
+       "fn sigexit { rm $tmp^* >[2]/dev/null }" \n)
+   (sh (file-name-nondirectory (buffer-file-name))
+       > "TMP=${TMPDIR:-/tmp}/" str ".$$" \n
+       "trap \"rm $TMP* 2>/dev/null\" " ?0 \n))
+ 
+ 
+ 
+ (define-skeleton sh-until
+   "Insert an until loop.  See `sh-feature'."
+   (sh "condition: "
+       '(setq input (sh-feature sh-test))
+       > "until " str "; do" \n
+       > _ \n
+       "done" > \n))
+ ;;;(put 'sh-until 'menu-enable '(sh-feature sh-until))
+ 
+ 
+ 
+ (define-skeleton sh-while
+   "Insert a while loop.  See `sh-feature'."
+   (csh sh-modify sh
+        2 ""
+        3 "while( "
+        5 " )"
+        10 '<
+        11 "end")
+   (es sh-modify sh
+       3 "while { "
+       5 " } {"
+       10 ?\} )
+   (rc sh-modify sh
+       3 "while( "
+       5 " ) {"
+       10 ?\} )
+   (sh "condition: "
+       '(setq input (sh-feature sh-test))
+       > "while " str "; do" \n
+       > _ \n
+       "done" > \n))
+ 
+ 
+ 
+ (define-skeleton sh-while-getopts
+   "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 sh-modify sh
+       18 "${0##*/}")
+   (csh nil
+        "while( 1 )" \n
+        > "switch( \"$1\" )" \n
+        '(setq input '("- x" . 2))
+        > >
+        ( "option, %s: "
+        < "case " '(eval str)
+        '(if (string-match " +" str)
+             (setq v1 (substring str (match-end 0))
+                   str (substring str 0 (match-beginning 0)))
+           (setq v1 nil))
+        str ?: \n
+        > "set " v1 & " = $2" | -4 & _ \n
+        (if v1 "shift") & \n
+        "breaksw" \n)
+        < "case --:" \n
+        > "shift" \n
+        < "default:" \n
+        > "break" \n
+        resume:
+        < < "endsw" \n
+        "shift" \n
+        < "end" \n)
+   (ksh88 sh-modify sh
+        16 "print"
+        18 "${0##*/}"
+        37 "OPTIND-1")
+   (posix sh-modify sh
+        18 "$(basename $0)")
+   (sh "optstring: "
+       > "while getopts :" str " OPT; do" \n
+       > "case $OPT in" \n
+       '(setq v1 (append (vconcat str) nil))
+       ( (prog1 (if v1 (char-to-string (car v1)))
+         (if (eq (nth 1 v1) ?:)
+             (setq v1 (nthcdr 2 v1)
+                   v2 "\"$OPTARG\"")
+           (setq v1 (cdr v1)
+                 v2 nil)))
+       > str "|+" str sh-non-closing-paren \n
+       > _ v2 \n
+       > ";;" \n)
+       > "*" sh-non-closing-paren \n
+       > "echo" " \"usage: " "`basename $0`"
+       " [+-" '(setq v1 (point)) str
+       '(save-excursion
+        (while (search-backward ":" v1 t)
+          (replace-match " ARG] [+-" t t)))
+       (if (eq (preceding-char) ?-) -5)
+       (if (and (sequencep v1) (length v1)) "] " "} ")
+       "[--] ARGS...\"" \n
+       "exit 2"  > \n
+       "esac" >
+       \n "done"
+       > \n
+       "shift " (sh-add "OPTIND" -1) \n))
+ 
+ 
+ 
+ (defun sh-assignment (arg)
+   "Remember preceding identifier for future completion and do self-insert."
+   (interactive "p")
+   (self-insert-command arg)
+   (if (<= arg 1)
+       (sh-remember-variable
+        (save-excursion
+        (if (re-search-forward (sh-feature sh-assignment-regexp)
+                               (prog1 (point)
+                                 (beginning-of-line 1))
+                               t)
+            (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'."
+   (interactive "*P")
+   (self-insert-command (prefix-numeric-value arg))
+   (or arg
+       (not (eq (char-after (- (point) 2)) last-command-char))
+       (save-excursion
+       (backward-char 2)
+       (sh-quoted-p))
+       (progn
+       (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 (substring
+                        sh-here-document-word
+                        (if (string-match "^-" sh-here-document-word) 1 
0)))))))
+ 
+ 
+ ;; various other commands
+ 
+ (autoload 'comint-dynamic-complete "comint"
+   "Dynamically perform completion at point." t)
+ 
+ (autoload 'shell-dynamic-complete-command "shell"
+   "Dynamically complete the command at point." t)
+ 
+ (autoload 'comint-dynamic-complete-filename "comint"
+   "Dynamically complete the filename at point." t)
+ 
+ (autoload 'shell-dynamic-complete-environment-variable "shell"
+   "Dynamically complete the environment variable at point." t)
+ 
+ 
+ 
+ (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)
+   (if (re-search-backward sh-beginning-of-command nil t)
+       (goto-char (match-beginning 2))))
+ 
+ (defun sh-end-of-command ()
+   "Move point to successive ends of commands."
+   (interactive)
+   (if (re-search-forward sh-end-of-command nil t)
+       (goto-char (match-end 1))))
+ 
+ (provide 'sh-script)
+ 
+ ;;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
+ ;;; sh-script.el ends here




reply via email to

[Prev in Thread] Current Thread [Next in Thread]