home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / modes / sh-script.el.z / sh-script.el
Encoding:
Text File  |  1998-05-21  |  46.7 KB  |  1,533 lines

  1. ;;; sh-script.el --- shell-script editing commands for Emacs
  2.  
  3. ;; Copyright (C) 1993, 1994, 1995, 1996 by Free Software Foundation, Inc.
  4.  
  5. ;; Author: Daniel.Pfeiffer@Informatik.START.dbp.de, fax (+49 69) 7588-2389
  6. ;; Version: 2.0e
  7. ;; Maintainer: FSF
  8. ;; Keywords: languages, unix
  9.  
  10. ;; This file is part of XEmacs.
  11.  
  12. ;; XEmacs is free software; you can redistribute it and/or modify it
  13. ;; under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 2, or (at your option)
  15. ;; any later version.
  16.  
  17. ;; XEmacs is distributed in the hope that it will be useful, but
  18. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20. ;; General Public License for more details.
  21.  
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with XEmacs; see the file COPYING.  If not, write to the Free
  24. ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  25. ;; 02111-1307, USA.
  26.  
  27. ;;; Synched up with: FSF 19.34.
  28.  
  29. ;;; Commentary:
  30.  
  31. ;; Major mode for editing shell scripts.  Bourne, C and rc shells as well
  32. ;; as various derivatives are supported and easily derived from.  Structured
  33. ;; statements can be inserted with one command or abbrev.  Completion is
  34. ;; available for filenames, variables known from the script, the shell and
  35. ;; the environment as well as commands.
  36.  
  37. ;;; Known Bugs:
  38.  
  39. ;; - In Bourne the keyword `in' is not anchored to case, for, select ...
  40. ;; - Variables in `"' strings aren't fontified because there's no way of
  41. ;;   syntactically distinguishing those from `'' strings.
  42.  
  43. ;;; Code:
  44.  
  45. ;; page 1:    variables and settings
  46. ;; page 2:    mode-command and utility functions
  47. ;; page 3:    statement syntax-commands for various shells
  48. ;; page 4:    various other commands
  49.  
  50. (require 'executable)
  51.  
  52. (defgroup sh nil
  53.   "Shell programming mode."
  54.   :group 'unix
  55.   :group 'languages)
  56.  
  57.  
  58. ;;; interpreter-mode-alist is not compatible between Emacs and XEmacs.
  59. ;;; So fake it.
  60.  
  61. (defvar sh-interpreter-mode-alist
  62.   '(("perl" . perl-mode)
  63.     ("perl5" . perl-mode)
  64.     ("wish" . tcl-mode)
  65.     ("wishx" . tcl-mode)
  66.     ("tcl" . tcl-mode)
  67.     ("tclsh" . tcl-mode)
  68.     ("awk" . awk-mode)
  69.     ("mawk" . awk-mode)
  70.     ("nawk" . awk-mode)
  71.     ("gawk" . awk-mode)
  72.     ("scm" . scheme-mode)
  73.     ("ash" . sh-mode)
  74.     ("bash" . sh-mode)
  75.     ("csh" . sh-mode)
  76.     ("dtksh" . sh-mode)
  77.     ("es" . sh-mode)
  78.     ("itcsh" . sh-mode)
  79.     ("jsh" . sh-mode)
  80.     ("ksh" . sh-mode)
  81.     ("oash" . sh-mode)
  82.     ("pdksh" . sh-mode)
  83.     ("rc" . sh-mode)
  84.     ("sh" . sh-mode)
  85.     ("sh5" . sh-mode)
  86.     ("tcsh" . sh-mode)
  87.     ("wksh" . sh-mode)
  88.     ("wsh" . sh-mode)
  89.     ("zsh" . sh-mode)
  90.     ("tail" . text-mode)
  91.     ("more" . text-mode)
  92.     ("less" . text-mode)
  93.     ("pg" . text-mode))
  94.   "Alist mapping interpreter names to major modes.
  95. This alist applies to files whose first line starts with `#!'.
  96. Each element looks like (INTERPRETER . MODE).
  97. The car of each element is compared with
  98. the name of the interpreter specified in the first line.
  99. If it matches, mode MODE is selected.")
  100.  
  101. (defcustom sh-mode-hook nil
  102.   "*Hook run by `sh-mode'."
  103.   :type 'hook
  104.   :group 'sh)
  105.  
  106. (defcustom sh-set-shell-hook nil
  107.   "*Hook run by `sh-set-shell'."
  108.   :type 'hook
  109.   :group 'sh)
  110.  
  111. (defcustom sh-ancestor-alist
  112.   '((ash . sh)
  113.     (bash . jsh)
  114.     (dtksh . ksh)
  115.     (es . rc)
  116.     (itcsh . tcsh)
  117.     (jcsh . csh)
  118.     (jsh . sh)
  119.     (ksh . ksh88)
  120.     (ksh88 . jsh)
  121.     (oash . sh)
  122.     (pdksh . ksh88)
  123.     (posix . sh)
  124.     (tcsh . csh)
  125.     (wksh . ksh88)
  126.     (wsh . sh)
  127.     (zsh . ksh88))
  128.   "*Alist showing the direct ancestor of various shells.
  129. This is the basis for `sh-feature'.  See also `sh-alias-alist'.
  130. By default we have the following three hierarchies:
  131.  
  132. csh        C Shell
  133.   jcsh        C Shell with Job Control
  134.   tcsh        Toronto C Shell
  135.     itcsh    ? Toronto C Shell
  136. rc        Plan 9 Shell
  137.   es        Extensible Shell
  138. sh        Bourne Shell
  139.   ash        ? Shell
  140.   jsh        Bourne Shell with Job Control
  141.     bash    GNU Bourne Again Shell
  142.     ksh88    Korn Shell '88
  143.       ksh    Korn Shell '93
  144.     dtksh    CDE Desktop Korn Shell
  145.       pdksh    Public Domain Korn Shell
  146.       wksh    Window Korn Shell
  147.       zsh    Z Shell
  148.   oash        SCO OA (curses) Shell
  149.   posix        IEEE 1003.2 Shell Standard
  150.   wsh        ? Shell"
  151.   :type '(repeat (cons symbol symbol))
  152.   :group 'sh)
  153.  
  154.  
  155. (defcustom sh-alias-alist
  156.   ;; XEmacs: Linux is spelled `linux'
  157.   (nconc (if (eq system-type 'linux)
  158.          '((csh . tcsh)
  159.            (ksh . pdksh)))
  160.      ;; for the time being
  161.      '((ksh . ksh88)
  162.        (sh5 . sh)))
  163.   "*Alist for transforming shell names to what they really are.
  164. Use this where the name of the executable doesn't correspond to the type of
  165. shell it really is."
  166.   :type '(repeat (cons symbol symbol))
  167.   :group 'sh)
  168.  
  169.  
  170. (defcustom sh-shell-file (or (getenv "SHELL") "/bin/sh")
  171.   "*The executable file name for the shell being programmed."
  172.   :type 'string
  173.   :group 'sh)
  174.  
  175.  
  176. (defcustom sh-shell-arg
  177.   ;; bash does not need any options when run in a shell script,
  178.   '((bash)
  179.     (csh . "-f")
  180.     (pdksh)
  181.     ;; Bill_Mann@praxisint.com says -p with ksh can do harm.
  182.     (ksh88)
  183.     ;; -p means don't initialize functions from the environment.
  184.     (rc . "-p")
  185.     ;; Someone proposed -motif, but we don't want to encourage
  186.     ;; use of a non-free widget set.
  187.     (wksh)
  188.     ;; -f means don't run .zshrc.
  189.     (zsh . "-f"))
  190.   "*Single argument string for the magic number.  See `sh-feature'."
  191.   :type '(repeat (cons (symbol :tag "Shell")
  192.                (choice (const :tag "No Arguments" nil)
  193.                    (string :tag "Arguments")
  194.                    (cons :format "Evaluate: %v"
  195.                      (const :format "" eval)
  196.                      sexp))))
  197.   :group 'sh)
  198.  
  199. (defvar sh-shell-variables nil
  200.   "Alist of shell variable names that should be included in completion.
  201. These are used for completion in addition to all the variables named
  202. in `process-environment'.  Each element looks like (VAR . VAR), where
  203. the car and cdr are the same symbol.")
  204.  
  205. (defvar sh-shell-variables-initialized nil
  206.   "Non-nil if `sh-shell-variables' is initialized.")
  207.  
  208. (defun sh-canonicalize-shell (shell)
  209.   "Convert a shell name SHELL to the one we should handle it as."
  210.   (or (symbolp shell)
  211.       (setq shell (intern shell)))
  212.   (or (cdr (assq shell sh-alias-alist))
  213.       shell))
  214.  
  215. (defvar sh-shell (sh-canonicalize-shell (file-name-nondirectory sh-shell-file))
  216.   "The shell being programmed.  This is set by \\[sh-set-shell].")
  217.  
  218. ;;; I turned off this feature because it doesn't permit typing commands
  219. ;;; in the usual way without help.
  220. ;;;(defvar sh-abbrevs
  221. ;;;  '((csh eval sh-abbrevs shell
  222. ;;;     "switch" 'sh-case
  223. ;;;     "getopts" 'sh-while-getopts)
  224.  
  225. ;;;    (es eval sh-abbrevs shell
  226. ;;;    "function" 'sh-function)
  227.  
  228. ;;;    (ksh88 eval sh-abbrevs sh
  229. ;;;       "select" 'sh-select)
  230.  
  231. ;;;    (rc eval sh-abbrevs shell
  232. ;;;    "case" 'sh-case
  233. ;;;    "function" 'sh-function)
  234.  
  235. ;;;    (sh eval sh-abbrevs shell
  236. ;;;    "case" 'sh-case
  237. ;;;    "function" 'sh-function
  238. ;;;    "until" 'sh-until
  239. ;;;    "getopts" 'sh-while-getopts)
  240.  
  241. ;;;    ;; The next entry is only used for defining the others
  242. ;;;    (shell "for" sh-for
  243. ;;;       "loop" sh-indexed-loop
  244. ;;;       "if" sh-if
  245. ;;;       "tmpfile" sh-tmp-file
  246. ;;;       "while" sh-while)
  247.  
  248. ;;;    (zsh eval sh-abbrevs ksh88
  249. ;;;     "repeat" 'sh-repeat))
  250. ;;;  "Abbrev-table used in Shell-Script mode.  See `sh-feature'.
  251. ;;;Due to the internal workings of abbrev tables, the shell name symbol is
  252. ;;;actually defined as the table for the like of \\[edit-abbrevs].")
  253.  
  254.  
  255.  
  256. (defvar sh-mode-syntax-table
  257.   '((csh eval identity sh)
  258.     (sh eval sh-mode-syntax-table ()
  259.     ;; #'s meanings depend on context which can't be expressed here
  260.     ;; ?\# "<"
  261.     ;; ?\^l ">#"
  262.     ;; ?\n ">#"
  263.     ?\" "\"\""
  264.     ?\' "\"'"
  265.     ?\` ".`"
  266.     ?$ "_"
  267.     ?! "_"
  268.     ?% "_"
  269.     ?: "_"
  270.     ?. "_"
  271.     ?^ "_"
  272.     ?~ "_")
  273.     (rc eval sh-mode-syntax-table sh
  274.     ?\" "_"
  275.     ?\` "."))
  276.   "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
  277.  
  278.  
  279.  
  280. (defvar sh-mode-map
  281.   (let ((map (make-sparse-keymap))
  282.     (menu-map (make-sparse-keymap "Insert")))
  283.     (define-key map "\C-c(" 'sh-function)
  284.     (define-key map "\C-c\C-w" 'sh-while)
  285.     (define-key map "\C-c\C-u" 'sh-until)
  286.     (define-key map "\C-c\C-t" 'sh-tmp-file)
  287.     (define-key map "\C-c\C-s" 'sh-select)
  288.     (define-key map "\C-c\C-r" 'sh-repeat)
  289.     (define-key map "\C-c\C-o" 'sh-while-getopts)
  290.     (define-key map "\C-c\C-l" 'sh-indexed-loop)
  291.     (define-key map "\C-c\C-i" 'sh-if)
  292.     (define-key map "\C-c\C-f" 'sh-for)
  293.     (define-key map "\C-c\C-c" 'sh-case)
  294.  
  295.     (define-key map "=" 'sh-assignment)
  296.     (define-key map "\C-c+" 'sh-add)
  297.     (define-key map "\C-\M-x" 'sh-execute-region)
  298.     (define-key map "\C-c\C-x" 'executable-interpret)
  299.     (define-key map "<" 'sh-maybe-here-document)
  300.     (define-key map "(" 'skeleton-pair-insert-maybe)
  301.     (define-key map "{" 'skeleton-pair-insert-maybe)
  302.     (define-key map "[" 'skeleton-pair-insert-maybe)
  303.     (define-key map "'" 'skeleton-pair-insert-maybe)
  304.     (define-key map "`" 'skeleton-pair-insert-maybe)
  305.     (define-key map "\"" 'skeleton-pair-insert-maybe)
  306.  
  307.     (define-key map "\t" 'sh-indent-line)
  308.     (substitute-key-definition 'complete-tag 'comint-dynamic-complete
  309.                    map (current-global-map))
  310.     (substitute-key-definition 'newline-and-indent 'sh-newline-and-indent
  311.                    map (current-global-map))
  312. ;; GDF - Don't mess around with the DEL bindings    
  313. ;;    (substitute-key-definition 'delete-backward-char
  314. ;;                   'backward-delete-char-untabify
  315. ;;                   map (current-global-map))
  316.     (define-key map "\C-c:" 'sh-set-shell)
  317.     (substitute-key-definition 'beginning-of-defun
  318.                    'sh-beginning-of-compound-command
  319.                    map (current-global-map))
  320.     (substitute-key-definition 'backward-sentence 'sh-beginning-of-command
  321.                    map (current-global-map))
  322.     (substitute-key-definition 'forward-sentence 'sh-end-of-command
  323.                    map (current-global-map))
  324.     (define-key map [menu-bar insert] (cons "Insert" menu-map))
  325.     (define-key menu-map [sh-while]    '("While Loop" . sh-while))
  326.     (define-key menu-map [sh-until]    '("Until Loop" . sh-until))
  327.     (define-key menu-map [sh-tmp-file]    '("Temporary File" . sh-tmp-file))
  328.     (define-key menu-map [sh-select]    '("Select Statement" . sh-select))
  329.     (define-key menu-map [sh-repeat]    '("Repeat Loop" . sh-repeat))
  330.     (define-key menu-map [sh-while-getopts]
  331.                     '("Options Loop" . sh-while-getopts))
  332.     (define-key menu-map [sh-indexed-loop]
  333.                     '("Indexed Loop" . sh-indexed-loop))
  334.     (define-key menu-map [sh-if]    '("If Statement" . sh-if))
  335.     (define-key menu-map [sh-for]    '("For Loop" . sh-for))
  336.     (define-key menu-map [sh-case]    '("Case Statement" . sh-case))
  337.     map)
  338.   "Keymap used in Shell-Script mode.")
  339.  
  340.  
  341.  
  342. (defcustom sh-dynamic-complete-functions
  343.   '(shell-dynamic-complete-environment-variable
  344.     shell-dynamic-complete-command
  345.     comint-dynamic-complete-filename)
  346.   "*Functions for doing TAB dynamic completion."
  347.   :type '(repeat function)
  348.   :group 'sh)
  349.  
  350.  
  351. (defcustom sh-require-final-newline
  352.   '((csh . t)
  353.     (pdksh . t)
  354.     (rc eval . require-final-newline)
  355.     (sh eval . require-final-newline))
  356.   "*Value of `require-final-newline' in Shell-Script mode buffers.
  357. See `sh-feature'."
  358.   :type '(repeat (cons (symbol :tag "Shell")
  359.                (choice (const :tag "require" t)
  360.                    (cons :format "Evaluate: %v"
  361.                      (const :format "" eval)
  362.                      sexp))))
  363.   :group 'sh)
  364.  
  365.  
  366. (defcustom sh-comment-prefix
  367.   '((csh . "\\(^\\|[^$]\\|\\$[^{]\\)")
  368.     (rc eval identity csh)
  369.     (sh . "\\(^\\|[ \t|&;()]\\)"))
  370.   "*Regexp matching what may come before a comment `#'.
  371. This must contain one \\(grouping\\) since it is the basis for fontifying
  372. comments as well as for `comment-start-skip'.
  373. See `sh-feature'."
  374.   :type '(repeat (cons (symbol :tag "Shell")
  375.                (choice regexp
  376.                    (cons :format "Evaluate: %v"
  377.                      (const :format "" eval)
  378.                      sexp))))
  379.   :group 'sh)
  380.  
  381.  
  382. (defcustom sh-assignment-regexp
  383.   '((csh . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \t]*[-+*/%^]?=")
  384.     ;; actually spaces are only supported in let/(( ... ))
  385.     (ksh88 . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \t]*\\([-+*/%&|~^]\\|<<\\|>>\\)?=")
  386.     (rc . "\\<\\([a-zA-Z0-9_*]+\\)[ \t]*=")
  387.     (sh . "\\<\\([a-zA-Z0-9_]+\\)="))
  388.   "*Regexp for the variable name and what may follow in an assignment.
  389. First grouping matches the variable name.  This is upto and including the `='
  390. sign.  See `sh-feature'."
  391.   :type '(repeat (cons (symbol :tag "Shell")
  392.                (choice regexp
  393.                    (cons :format "Evaluate: %v"
  394.                      (const :format "" eval)
  395.                      sexp))))
  396.   :group 'sh)
  397.  
  398.  
  399. (defcustom sh-indentation 4
  400.   "The width for further indentation in Shell-Script mode."
  401.   :type 'integer
  402.   :group 'sh)
  403.  
  404.  
  405. (defcustom sh-remember-variable-min 3
  406.   "*Don't remember variables less than this length for completing reads."
  407.   :type 'integer
  408.   :group 'sh)
  409.  
  410.  
  411. (defvar sh-header-marker nil
  412.   "When non-`nil' is the end of header for prepending by \\[sh-execute-region].
  413. That command is also used for setting this variable.")
  414.  
  415.  
  416. (defcustom sh-beginning-of-command
  417.   "\\([;({`|&]\\|\\`\\|[^\\]\n\\)[ \t]*\\([/~a-zA-Z0-9:]\\)"
  418.   "*Regexp to determine the beginning of a shell command.
  419. The actual command starts at the beginning of the second \\(grouping\\)."
  420.   :type 'regexp
  421.   :group 'sh)
  422.  
  423.  
  424. (defcustom sh-end-of-command
  425.   "\\([/~a-zA-Z0-9:]\\)[ \t]*\\([;#)}`|&]\\|$\\)"
  426.   "*Regexp to determine the end of a shell command.
  427. The actual command ends at the end of the first \\(grouping\\)."
  428.   :type 'regexp
  429.   :group 'sh)
  430.  
  431.  
  432.  
  433. (defvar sh-here-document-word "EOF"
  434.   "Word to delimit here documents.")
  435.  
  436. (defvar sh-test
  437.   '((sh "[  ]" . 2)
  438.     (ksh88 "[[  ]]" . 3))
  439.   "Initial input in Bourne if, while and until skeletons.  See `sh-feature'.")
  440.  
  441.  
  442. ;; customized this out of sheer bravado.  not for the faint of heart.
  443. ;; but it *did* have an asterisk in the docstring!
  444. (defcustom sh-builtins
  445.   '((bash eval sh-append posix
  446.       "alias" "bg" "bind" "builtin" "declare" "dirs" "enable" "fc" "fg"
  447.       "help" "history" "jobs" "kill" "let" "local" "popd" "pushd" "source"
  448.       "suspend" "typeset" "unalias")
  449.  
  450.     ;; The next entry is only used for defining the others
  451.     (bourne eval sh-append shell
  452.         "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
  453.         "times" "ulimit")
  454.  
  455.     (csh eval sh-append shell
  456.      "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
  457.      "setenv" "source" "time" "unalias" "unhash")
  458.  
  459.     (dtksh eval identity wksh)
  460.  
  461.     (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
  462.     "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
  463.  
  464.     (jsh eval sh-append sh
  465.      "bg" "fg" "jobs" "kill" "stop" "suspend")
  466.  
  467.     (jcsh eval sh-append csh
  468.      "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
  469.  
  470.     (ksh88 eval sh-append bourne
  471.        "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
  472.        "typeset" "unalias" "whence")
  473.  
  474.     (oash eval sh-append sh
  475.       "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit"
  476.       "oaed" "oahelp" "oainit" "pp" "ppfile" "scan" "scrollok" "wattr"
  477.       "wclear" "werase" "win" "wmclose" "wmmessage" "wmopen" "wmove"
  478.       "wmtitle" "wrefresh")
  479.  
  480.     (pdksh eval sh-append ksh88
  481.        "bind")
  482.  
  483.     (posix eval sh-append sh
  484.        "command")
  485.  
  486.     (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
  487.     "whatis")
  488.  
  489.     (sh eval sh-append bourne
  490.     "hash" "test" "type")
  491.  
  492.     ;; The next entry is only used for defining the others
  493.     (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
  494.  
  495.     (wksh eval sh-append ksh88
  496.       "Xt[A-Z][A-Za-z]*")
  497.  
  498.     (zsh eval sh-append ksh88
  499.      "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs"
  500.      "disable" "disown" "echotc" "enable" "functions" "getln" "hash"
  501.      "history" "integer" "limit" "local" "log" "popd" "pushd" "r"
  502.      "readonly" "rehash" "sched" "setopt" "source" "suspend" "true"
  503.      "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" "vared"
  504.      "which"))
  505.   "*List of all shell builtins for completing read and fontification.
  506. Note that on some systems not all builtins are available or some are
  507. implemented as aliases.  See `sh-feature'."
  508.   :type '(repeat (cons (symbol :tag "Shell")
  509.                (choice (repeat string)
  510.                    (cons :format "Evaluate: %v"
  511.                      (const :format "" eval)
  512.                      sexp))))
  513.   :group 'sh)
  514.  
  515.  
  516.  
  517. (defcustom sh-leading-keywords
  518.   '((csh "else")
  519.  
  520.     (es "true" "unwind-protect" "whatis")
  521.  
  522.     (rc "else")
  523.  
  524.     (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
  525.   "*List of keywords that may be immediately followed by a builtin or keyword.
  526. Given some confusion between keywords and builtins depending on shell and
  527. system, the distinction here has been based on whether they influence the
  528. flow of control or syntax.  See `sh-feature'."
  529.   :type '(repeat (cons (symbol :tag "Shell")
  530.                (choice (repeat string)
  531.                    (cons :format "Evaluate: %v"
  532.                      (const :format "" eval)
  533.                      sexp))))
  534.   :group 'sh)
  535.  
  536.  
  537. (defcustom sh-other-keywords
  538.   '((bash eval sh-append bourne
  539.       "bye" "logout")
  540.  
  541.     ;; The next entry is only used for defining the others
  542.     (bourne eval sh-append shell
  543.         "done" "esac" "fi" "for" "function" "in" "return")
  544.  
  545.     (csh eval sh-append shell
  546.      "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
  547.      "if" "logout" "onintr" "repeat" "switch" "then" "while")
  548.  
  549.     (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
  550.     "return" "throw" "while")
  551.  
  552.     (ksh88 eval sh-append bourne
  553.        "select")
  554.  
  555.     (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
  556.     "while")
  557.  
  558.     ;; The next entry is only used for defining the others
  559.     (shell "break" "case" "continue" "exec" "exit")
  560.  
  561.     (zsh eval sh-append bash
  562.      "select"))
  563.   "*List of keywords not in `sh-leading-keywords'.
  564. See `sh-feature'."
  565.   :type '(repeat (cons (symbol :tag "Shell")
  566.                (choice (repeat string)
  567.                    (cons :format "Evaluate: %v"
  568.                      (const :format "" eval)
  569.                      sexp))))
  570.   :group 'sh)
  571.  
  572.  
  573.  
  574. (defvar sh-variables
  575.   '((bash eval sh-append sh
  576.       "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_VERSION"
  577.       "cdable_vars" "ENV" "EUID" "FCEDIT" "FIGNORE" "glob_dot_filenames"
  578.       "histchars" "HISTFILE" "HISTFILESIZE" "history_control" "HISTSIZE"
  579.       "hostname_completion_file" "HOSTTYPE" "IGNOREEOF" "ignoreeof"
  580.       "LINENO" "MAIL_WARNING" "noclobber" "nolinks" "notify"
  581.       "no_exit_on_failed_exec" "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "PPID"
  582.       "PROMPT_COMMAND" "PS4" "pushd_silent" "PWD" "RANDOM" "REPLY"
  583.       "SECONDS" "SHLVL" "TMOUT" "UID")
  584.  
  585.     (csh eval sh-append shell
  586.      "argv" "cdpath" "child" "echo" "histchars" "history" "home"
  587.      "ignoreeof" "mail" "noclobber" "noglob" "nonomatch" "path" "prompt"
  588.      "shell" "status" "time" "verbose")
  589.  
  590.     (es eval sh-append shell
  591.     "apid" "cdpath" "CDPATH" "history" "home" "ifs" "noexport" "path"
  592.     "pid" "prompt" "signals")
  593.  
  594.     (jcsh eval sh-append csh
  595.      "notify")
  596.  
  597.     (ksh88 eval sh-append sh
  598.        "ENV" "ERRNO" "FCEDIT" "FPATH" "HISTFILE" "HISTSIZE" "LINENO"
  599.        "OLDPWD" "PPID" "PS3" "PS4" "PWD" "RANDOM" "REPLY" "SECONDS"
  600.        "TMOUT")
  601.  
  602.     (oash eval sh-append sh
  603.       "FIELD" "FIELD_MAX" "LAST_KEY" "OALIB" "PP_ITEM" "PP_NUM")
  604.  
  605.     (rc eval sh-append shell
  606.     "apid" "apids" "cdpath" "CDPATH" "history" "home" "ifs" "path" "pid"
  607.     "prompt" "status")
  608.  
  609.     (sh eval sh-append shell
  610.     "CDPATH" "IFS" "OPTARG" "OPTIND" "PS1" "PS2")
  611.  
  612.     ;; The next entry is only used for defining the others
  613.     (shell "COLUMNS" "EDITOR" "HOME" "HUSHLOGIN" "LANG" "LC_COLLATE"
  614.        "LC_CTYPE" "LC_MESSAGES" "LC_MONETARY" "LC_NUMERIC" "LC_TIME"
  615.        "LINES" "LOGNAME" "MAIL" "MAILCHECK" "MAILPATH" "PAGER" "PATH"
  616.        "SHELL" "TERM" "TERMCAP" "TERMINFO" "VISUAL")
  617.  
  618.     (tcsh eval sh-append csh
  619.       "addsuffix" "ampm" "autocorrect" "autoexpand" "autolist"
  620.       "autologout" "chase_symlinks" "correct" "dextract" "edit" "el"
  621.       "fignore" "gid" "histlit" "HOST" "HOSTTYPE" "HPATH"
  622.       "ignore_symlinks" "listjobs" "listlinks" "listmax" "matchbeep"
  623.       "nobeep" "NOREBIND" "oid" "printexitvalue" "prompt2" "prompt3"
  624.       "pushdsilent" "pushdtohome" "recexact" "recognize_only_executables"
  625.       "rmstar" "savehist" "SHLVL" "showdots" "sl" "SYSTYPE" "tcsh" "term"
  626.       "tperiod" "tty" "uid" "version" "visiblebell" "watch" "who"
  627.       "wordchars")
  628.  
  629.     (zsh eval sh-append ksh88
  630.      "BAUD" "bindcmds" "cdpath" "DIRSTACKSIZE" "fignore" "FIGNORE" "fpath"
  631.      "HISTCHARS" "hostcmds" "hosts" "HOSTS" "LISTMAX" "LITHISTSIZE"
  632.      "LOGCHECK" "mailpath" "manpath" "NULLCMD" "optcmds" "path" "POSTEDIT"
  633.      "prompt" "PROMPT" "PROMPT2" "PROMPT3" "PROMPT4" "psvar" "PSVAR"
  634.      "READNULLCMD" "REPORTTIME" "RPROMPT" "RPS1" "SAVEHIST" "SPROMPT"
  635.      "STTY" "TIMEFMT" "TMOUT" "TMPPREFIX" "varcmds" "watch" "WATCH"
  636.      "WATCHFMT" "WORDCHARS" "ZDOTDIR"))
  637.   "List of all shell variables available for completing read.
  638. See `sh-feature'.")
  639.  
  640.  
  641.  
  642. (defvar sh-font-lock-keywords
  643.   '((csh eval sh-append shell
  644.      '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
  645.        font-lock-variable-name-face))
  646.  
  647.     (es eval sh-append executable-font-lock-keywords
  648.     '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
  649.       font-lock-variable-name-face))
  650.  
  651.     (rc eval identity es)
  652.  
  653.     (sh eval sh-append shell
  654.     '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
  655.       font-lock-variable-name-face))
  656.  
  657.     ;; The next entry is only used for defining the others
  658.     (shell eval sh-append executable-font-lock-keywords
  659.        '("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
  660.        '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
  661.          font-lock-variable-name-face)))
  662.   "*Rules for highlighting shell scripts.  See `sh-feature'.")
  663.  
  664. (defvar sh-font-lock-keywords-1
  665.   '((sh "[ \t]in\\>"))
  666.   "*Additional rules for highlighting shell scripts.  See `sh-feature'.")
  667.  
  668. (defvar sh-font-lock-keywords-2 ()
  669.   "*Yet more rules for highlighting shell scripts.  See `sh-feature'.")
  670.  
  671. (defvar sh-font-lock-keywords-only t
  672.   "*Value of `font-lock-keywords-only' for highlighting shell scripts.
  673. Default value is `t' because Emacs' syntax is not expressive enough to
  674. detect that $# does not start a comment.  Thus comments are fontified by
  675. regexp which means that a single apostrophe in a comment turns everything
  676. upto the next one or end of buffer into a string.")
  677.  
  678. ;; mode-command and utility functions
  679.  
  680. ;;;###autoload
  681. (put 'sh-mode 'mode-class 'special)
  682.  
  683. ;;;###autoload
  684. (defun sh-mode ()
  685.   "Major mode for editing shell scripts.
  686. This mode works for many shells, since they all have roughly the same syntax,
  687. as far as commands, arguments, variables, pipes, comments etc. are concerned.
  688. Unless the file's magic number indicates the shell, your usual shell is
  689. assumed.  Since filenames rarely give a clue, they are not further analyzed.
  690.  
  691. This mode adapts to the variations between shells (see `sh-set-shell') by
  692. means of an inheritance based feature lookup (see `sh-feature').  This
  693. mechanism applies to all variables (including skeletons) that pertain to
  694. shell-specific features.
  695.  
  696. The default style of this mode is that of Rosenblatt's Korn shell book.
  697. The syntax of the statements varies with the shell being used.  The
  698. following commands are available, based on the current shell's syntax:
  699.  
  700. \\[sh-case]     case statement
  701. \\[sh-for]     for loop
  702. \\[sh-function]     function definition
  703. \\[sh-if]     if statement
  704. \\[sh-indexed-loop]     indexed loop from 1 to n
  705. \\[sh-while-getopts]     while getopts loop
  706. \\[sh-repeat]     repeat loop
  707. \\[sh-select]     select loop
  708. \\[sh-until]     until loop
  709. \\[sh-while]     while loop
  710.  
  711. \\[backward-delete-char-untabify]     Delete backward one position, even if it was a tab.
  712. \\[sh-newline-and-indent]     Delete unquoted space and indent new line same as this one.
  713. \\[sh-end-of-command]     Go to end of successive commands.
  714. \\[sh-beginning-of-command]     Go to beginning of successive commands.
  715. \\[sh-set-shell]     Set this buffer's shell, and maybe its magic number.
  716. \\[sh-execute-region]     Have optional header and region be executed in a subshell.
  717.  
  718. \\[sh-maybe-here-document]     Without prefix, following an unquoted < inserts here document.
  719. {, (, [, ', \", `
  720.     Unless quoted with \\, insert the pairs {}, (), [], or '', \"\", ``.
  721.  
  722. If you generally program a shell different from your login shell you can
  723. set `sh-shell-file' accordingly.  If your shell's file name doesn't correctly
  724. indicate what shell it is use `sh-alias-alist' to translate.
  725.  
  726. If your shell gives error messages with line numbers, you can use \\[executable-interpret]
  727. with your script for an edit-interpret-debug cycle."
  728.   (interactive)
  729.   (kill-all-local-variables)
  730.   (use-local-map sh-mode-map)
  731.   (make-local-variable 'indent-line-function)
  732.   (make-local-variable 'indent-region-function)
  733.   (make-local-variable 'skeleton-end-hook)
  734.   (make-local-variable 'paragraph-start)
  735.   (make-local-variable 'paragraph-separate)
  736.   (make-local-variable 'comment-start)
  737.   (make-local-variable 'comment-start-skip)
  738.   (make-local-variable 'require-final-newline)
  739.   (make-local-variable 'sh-header-marker)
  740.   (make-local-variable 'sh-shell-file)
  741.   (make-local-variable 'sh-shell)
  742.   (make-local-variable 'skeleton-pair-alist)
  743.   (make-local-variable 'skeleton-pair-filter)
  744.   (make-local-variable 'comint-dynamic-complete-functions)
  745.   (make-local-variable 'comint-prompt-regexp)
  746.   (make-local-variable 'font-lock-keywords)
  747.   (make-local-variable 'font-lock-defaults)
  748.   (make-local-variable 'skeleton-filter)
  749.   (make-local-variable 'skeleton-newline-indent-rigidly)
  750.   (make-local-variable 'sh-shell-variables)
  751.   (make-local-variable 'sh-shell-variables-initialized)
  752.   (setq major-mode 'sh-mode
  753.     mode-name "Shell-script"
  754.     indent-line-function 'sh-indent-line
  755.     ;; not very clever, but enables wrapping skeletons around regions
  756.     indent-region-function (lambda (b e)
  757.                  (save-excursion
  758.                    (goto-char b)
  759.                    (skip-syntax-backward "-")
  760.                    (setq b (point))
  761.                    (goto-char e)
  762.                    (skip-syntax-backward "-")
  763.                    (indent-rigidly b (point) sh-indentation)))
  764.     skeleton-end-hook (lambda ()
  765.                 (or (eolp) (newline) (indent-relative)))
  766.     paragraph-start (concat page-delimiter "\\|$")
  767.     paragraph-separate paragraph-start
  768.     comment-start "# "
  769.     comint-dynamic-complete-functions sh-dynamic-complete-functions
  770.     ;; we can't look if previous line ended with `\'
  771.     comint-prompt-regexp "^[ \t]*"
  772.     font-lock-defaults
  773.       `((sh-font-lock-keywords
  774.          sh-font-lock-keywords-1
  775.          sh-font-lock-keywords-2)
  776.         ,sh-font-lock-keywords-only
  777.         nil
  778.         ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")))
  779.     skeleton-pair-alist '((?` _ ?`))
  780.     skeleton-pair-filter 'sh-quoted-p
  781.     skeleton-further-elements '((< '(- (min sh-indentation
  782.                         (current-column)))))
  783.     skeleton-filter 'sh-feature
  784.     skeleton-newline-indent-rigidly t)
  785.   (save-excursion
  786.     ;; parse or insert magic number for exec() and set all variables depending
  787.     ;; on the shell thus determined
  788.     (goto-char (point-min))
  789.     (and (zerop (buffer-size))
  790.      (not buffer-read-only)
  791.      (sh-set-shell sh-shell-file)))
  792.   (run-hooks 'sh-mode-hook))
  793. ;;;###autoload
  794. (defalias 'shell-script-mode 'sh-mode)
  795.  
  796. ;;; XEmacs
  797. (put 'sh-mode 'font-lock-defaults
  798.      `((sh-font-lock-keywords
  799.     sh-font-lock-keywords-1
  800.     sh-font-lock-keywords-2)
  801.        ,sh-font-lock-keywords-only
  802.        nil
  803.        ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w"))))
  804.      
  805.  
  806. (defun sh-font-lock-keywords (&optional keywords)
  807.   "Function to get simple fontification based on `sh-font-lock-keywords'.
  808. This adds rules for comments and assignments."
  809.   (sh-feature sh-font-lock-keywords
  810.           (lambda (list)
  811.         `((,(concat (sh-feature sh-comment-prefix) "\\(#.*\\)")
  812.            2 font-lock-comment-face t)
  813.           (,(sh-feature sh-assignment-regexp)
  814.            1 font-lock-variable-name-face)
  815.           ,@keywords
  816.           ,@list))))
  817.  
  818. (defun sh-font-lock-keywords-1 (&optional builtins)
  819.   "Function to get better fontification including keywords."
  820.   (let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\(\\("
  821.               (mapconcat 'identity
  822.                      (sh-feature sh-leading-keywords)
  823.                      "\\|")
  824.               "\\)[ \t]+\\)?\\("
  825.               (mapconcat 'identity
  826.                      (append (sh-feature sh-leading-keywords)
  827.                          (sh-feature sh-other-keywords))
  828.                      "\\|")
  829.               "\\)")))
  830.     (sh-font-lock-keywords
  831.      `(,@(if builtins
  832.          `((,(concat keywords "[ \t]+\\)?\\("
  833.              (mapconcat 'identity (sh-feature sh-builtins) "\\|")
  834.              "\\)\\>")
  835.         (2 font-lock-keyword-face nil t)
  836.         (6 font-lock-function-name-face))
  837.            ,@(sh-feature sh-font-lock-keywords-2)))
  838.      (,(concat keywords "\\)\\>")
  839.       2 font-lock-keyword-face)
  840.      ,@(sh-feature sh-font-lock-keywords-1)))))
  841.  
  842. (defun sh-font-lock-keywords-2 ()
  843.   "Function to get better fontification including keywords and builtins."
  844.   (sh-font-lock-keywords-1 t))
  845.  
  846.  
  847. (defun sh-set-shell (shell &optional no-query-flag insert-flag)
  848.   "Set this buffer's shell to SHELL (a string).
  849. Makes this script executable via `executable-set-magic'.
  850. Calls the value of `sh-set-shell-hook' if set."
  851.   (interactive (list (completing-read "Name or path of shell: "
  852.                       ;; XEmacs change
  853.                       sh-interpreter-mode-alist
  854.                       (lambda (x) (eq (cdr x) 'sh-mode)))
  855.              (eq executable-query 'function)
  856.              t))
  857.   (setq sh-shell (intern (file-name-nondirectory shell))
  858.     sh-shell (or (cdr (assq sh-shell sh-alias-alist))
  859.              sh-shell))
  860.   (setq sh-shell-file (executable-set-magic shell (sh-feature sh-shell-arg)))
  861.   (setq require-final-newline (sh-feature sh-require-final-newline)
  862. ;;;    local-abbrev-table (sh-feature sh-abbrevs)
  863.     font-lock-defaults-computed nil
  864.     ;; Next two lines kill XEmacs
  865.     ;font-lock-keywords nil        ; force resetting
  866.     ;font-lock-syntax-table nil
  867.     comment-start-skip (concat (sh-feature sh-comment-prefix) "#+[\t ]*")
  868.     mode-line-process (format "[%s]" sh-shell)
  869.     sh-shell-variables nil
  870.     sh-shell-variables-initialized nil
  871.     shell (sh-feature sh-variables))
  872.   (set-syntax-table (sh-feature sh-mode-syntax-table))
  873.   (while shell
  874.     (sh-remember-variable (car shell))
  875.     (setq shell (cdr shell)))
  876.   (and (boundp 'font-lock-mode)
  877.        font-lock-mode
  878.        ;; Gnu Emacs, doesn't work
  879.        (font-lock-mode (font-lock-mode 0)))
  880.        ;; (font-lock-fontify-buffer))
  881.   (run-hooks 'sh-set-shell-hook))
  882.  
  883.  
  884.  
  885. (defun sh-feature (list &optional function)
  886.   "Index ALIST by the current shell.
  887. If ALIST isn't a list where every element is a cons, it is returned as is.
  888. Else indexing follows an inheritance logic which works in two ways:
  889.  
  890.   - Fall back on successive ancestors (see `sh-ancestor-alist') as long as
  891.     the alist contains no value for the current shell.
  892.  
  893.   - If the value thus looked up is a list starting with `eval' its `cdr' is
  894.     first evaluated.  If that is also a list and the first argument is a
  895.     symbol in ALIST it is not evaluated, but rather recursively looked up in
  896.     ALIST to allow the function called to define the value for one shell to be
  897.     derived from another shell.  While calling the function, is the car of the
  898.     alist element is the current shell.
  899.     The value thus determined is physically replaced into the alist.
  900.  
  901. Optional FUNCTION is applied to the determined value and the result is cached
  902. in ALIST."
  903.   (or (if (consp list)
  904.       (let ((l list))
  905.         (while (and l (consp (car l)))
  906.           (setq l (cdr l)))
  907.         (if l list)))
  908.       (if function
  909.       (cdr (assoc (setq function (cons sh-shell function)) list)))
  910.       (let ((sh-shell sh-shell)
  911.         elt val)
  912.     (while (and sh-shell
  913.             (not (setq elt (assq sh-shell list))))
  914.       (setq sh-shell (cdr (assq sh-shell sh-ancestor-alist))))
  915.     (if (and (consp (setq val (cdr elt)))
  916.          (eq (car val) 'eval))
  917.         (setcdr elt
  918.             (setq val
  919.               (eval (if (consp (setq val (cdr val)))
  920.                     (let ((sh-shell (car (cdr val)))
  921.                       function)
  922.                       (if (assq sh-shell list)
  923.                       (setcar (cdr val)
  924.                           (list 'quote
  925.                             (sh-feature list))))
  926.                       val)
  927.                   val)))))
  928.     (if function
  929.         (nconc list
  930.            (list (cons function
  931.                    (setq sh-shell (car function)
  932.                      val (funcall (cdr function) val))))))
  933.     val)))
  934.  
  935.  
  936.  
  937. ;;; I commented this out because nobody calls it -- rms.
  938. ;;;(defun sh-abbrevs (ancestor &rest list)
  939. ;;;  "Iff it isn't, define the current shell as abbrev table and fill that.
  940. ;;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an abbrev
  941. ;;;table or a list of (NAME1 EXPANSION1 ...).  In addition it will define abbrevs
  942. ;;;according to the remaining arguments NAMEi EXPANSIONi ...
  943. ;;;EXPANSION may be either a string or a skeleton command."
  944. ;;;  (or (if (boundp sh-shell)
  945. ;;;      (symbol-value sh-shell))
  946. ;;;      (progn
  947. ;;;    (if (listp ancestor)
  948. ;;;        (nconc list ancestor))
  949. ;;;    (define-abbrev-table sh-shell ())
  950. ;;;    (if (vectorp ancestor)
  951. ;;;        (mapatoms (lambda (atom)
  952. ;;;            (or (eq atom 0)
  953. ;;;                (define-abbrev (symbol-value sh-shell)
  954. ;;;                  (symbol-name atom)
  955. ;;;                  (symbol-value atom)
  956. ;;;                  (symbol-function atom))))
  957. ;;;              ancestor))
  958. ;;;    (while list
  959. ;;;      (define-abbrev (symbol-value sh-shell)
  960. ;;;        (car list)
  961. ;;;        (if (stringp (car (cdr list)))
  962. ;;;        (car (cdr list))
  963. ;;;          "")
  964. ;;;        (if (symbolp (car (cdr list)))
  965. ;;;        (car (cdr list))))
  966. ;;;      (setq list (cdr (cdr list)))))
  967. ;;;      (symbol-value sh-shell)))
  968.  
  969.  
  970. (defun sh-mode-syntax-table (table &rest list)
  971.   "Copy TABLE and set syntax for successive CHARs according to strings S."
  972.   (setq table (copy-syntax-table table))
  973.   (while list
  974.     (modify-syntax-entry (car list) (car (cdr list)) table)
  975.     (setq list (cdr (cdr list))))
  976.   table)
  977.  
  978.  
  979. (defun sh-append (ancestor &rest list)
  980.   "Return list composed of first argument (a list) physically appended to rest."
  981.   (nconc list ancestor))
  982.  
  983.  
  984. (defun sh-modify (skeleton &rest list)
  985.   "Modify a copy of SKELETON by replacing I1 with REPL1, I2 with REPL2 ..."
  986.   (setq skeleton (copy-sequence skeleton))
  987.   (while list
  988.     (setcar (or (nthcdr (car list) skeleton)
  989.         (error "Index %d out of bounds" (car list)))
  990.         (car (cdr list)))
  991.     (setq list (nthcdr 2 list)))
  992.   skeleton)
  993.  
  994.  
  995. (defun sh-indent-line ()
  996.   "Indent as far as preceding non-empty line, then by steps of `sh-indentation'.
  997. Lines containing only comments are considered empty."
  998.   (interactive)
  999.   (let ((previous (save-excursion
  1000.             (while (and (not (bobp))
  1001.                 (not (eq (point-min) (point-at-bol)))
  1002.                 (progn
  1003.                   (forward-line -1)
  1004.                   (back-to-indentation)
  1005.                   (or (eolp)
  1006.                       (eq (following-char) ?#)))))
  1007.             (current-column)))
  1008.     current)
  1009.     (save-excursion
  1010.       (indent-to (if (eq this-command 'newline-and-indent)
  1011.              previous
  1012.            (if (< (current-column)
  1013.               (setq current (progn (back-to-indentation)
  1014.                            (current-column))))
  1015.                (if (eolp) previous 0)
  1016.              (delete-region (point)
  1017.                     (progn (beginning-of-line) (point)))
  1018.              (if (eolp)
  1019.              (max previous (* (1+ (/ current sh-indentation))
  1020.                       sh-indentation))
  1021.                (* (1+ (/ current sh-indentation)) sh-indentation))))))
  1022.     (if (< (current-column) (current-indentation))
  1023.     (skip-chars-forward " \t"))))
  1024.  
  1025.  
  1026. (defun sh-execute-region (start end &optional flag)
  1027.   "Pass optional header and region to a subshell for noninteractive execution.
  1028. The working directory is that of the buffer, and only environment variables
  1029. are already set which is why you can mark a header within the script.
  1030.  
  1031. With a positive prefix ARG, instead of sending region, define header from
  1032. beginning of buffer to point.  With a negative prefix ARG, instead of sending
  1033. region, clear header."
  1034.   (interactive "r\nP")
  1035.   (if flag
  1036.       (setq sh-header-marker (if (> (prefix-numeric-value flag) 0)
  1037.                  (point-marker)))
  1038.     (if sh-header-marker
  1039.     (save-excursion
  1040.       (let (buffer-undo-list)
  1041.         (goto-char sh-header-marker)
  1042.         (append-to-buffer (current-buffer) start end)
  1043.         (shell-command-on-region (point-min)
  1044.                      (setq end (+ sh-header-marker
  1045.                           (- end start)))
  1046.                      sh-shell-file)
  1047.         (delete-region sh-header-marker end)))
  1048.       (shell-command-on-region start end (concat sh-shell-file " -")))))
  1049.  
  1050.  
  1051. (defun sh-remember-variable (var)
  1052.   "Make VARIABLE available for future completing reads in this buffer."
  1053.   (or (< (length var) sh-remember-variable-min)
  1054.       (getenv var)
  1055.       (assoc var sh-shell-variables)
  1056.       (setq sh-shell-variables (cons (cons var var) sh-shell-variables)))
  1057.   var)
  1058.  
  1059.  
  1060.  
  1061. (defun sh-quoted-p ()
  1062.   "Is point preceded by an odd number of backslashes?"
  1063.   (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2)))
  1064.  
  1065. ;; statement syntax-commands for various shells
  1066.  
  1067. ;; You are welcome to add the syntax or even completely new statements as
  1068. ;; appropriate for your favorite shell.
  1069.  
  1070. (define-skeleton sh-case
  1071.   "Insert a case/switch statement.  See `sh-feature'."
  1072.   (csh "expression: "
  1073.        "switch( " str " )" \n
  1074.        > "case " (read-string "pattern: ") ?: \n
  1075.        > _ \n
  1076.        "breaksw" \n
  1077.        ( "other pattern, %s: "
  1078.      < "case " str ?: \n
  1079.      > _ \n
  1080.      "breaksw" \n)
  1081.        < "default:" \n
  1082.        > _ \n
  1083.        resume:
  1084.        < < "endsw")
  1085.   (es)
  1086.   (rc "expression: "
  1087.       "switch( " str " ) {" \n
  1088.       > "case " (read-string "pattern: ") \n
  1089.       > _ \n
  1090.       ( "other pattern, %s: "
  1091.     < "case " str \n
  1092.     > _ \n)
  1093.       < "case *" \n
  1094.       > _ \n
  1095.       resume:
  1096.       < < ?})
  1097.   (sh "expression: "
  1098.       "case " str " in" \n
  1099.       > (read-string "pattern: ") ?\) \n
  1100.       > _ \n
  1101.       ";;" \n
  1102.       ( "other pattern, %s: "
  1103.     < str ?\) \n
  1104.     > _ \n
  1105.     ";;" \n)
  1106.       < "*)" \n
  1107.       > _ \n
  1108.       resume:
  1109.       < < "esac"))
  1110. (put 'sh-case 'menu-enable '(sh-feature sh-case))
  1111.  
  1112.  
  1113.  
  1114. (define-skeleton sh-for
  1115.   "Insert a for loop.  See `sh-feature'."
  1116.   (csh eval sh-modify sh
  1117.        1 "foreach "
  1118.        3 " ( "
  1119.        5 " )"
  1120.        15 "end")
  1121.   (es eval sh-modify rc
  1122.       3 " = ")
  1123.   (rc eval sh-modify sh
  1124.       1 "for( "
  1125.       5 " ) {"
  1126.       15 ?})
  1127.   (sh "Index variable: "
  1128.       "for " str " in " _ "; do" \n
  1129.       > _ | ?$ & (sh-remember-variable str) \n
  1130.       < "done"))
  1131.  
  1132.  
  1133.  
  1134. (define-skeleton sh-indexed-loop
  1135.   "Insert an indexed loop from 1 to n.  See `sh-feature'."
  1136.   (bash eval identity posix)
  1137.   (csh "Index variable: "
  1138.        "@ " str " = 1" \n
  1139.        "while( $" str " <= " (read-string "upper limit: ") " )" \n
  1140.        > _ ?$ str \n
  1141.        "@ " str "++" \n
  1142.        < "end")
  1143.   (es eval sh-modify rc
  1144.       3 " =")
  1145.   (ksh88 "Index variable: "
  1146.      "integer " str "=0" \n
  1147.      "while (( ( " str " += 1 ) <= "
  1148.      (read-string "upper limit: ")
  1149.      " )); do" \n
  1150.      > _ ?$ (sh-remember-variable str) \n
  1151.      < "done")
  1152.   (posix "Index variable: "
  1153.      str "=1" \n
  1154.      "while [ $" str " -le "
  1155.      (read-string "upper limit: ")
  1156.      " ]; do" \n
  1157.      > _ ?$ str \n
  1158.      str ?= (sh-add (sh-remember-variable str) 1) \n
  1159.      < "done")
  1160.   (rc "Index variable: "
  1161.       "for( " str " in" " `{awk 'BEGIN { for( i=1; i<="
  1162.       (read-string "upper limit: ")
  1163.       "; i++ ) print i }'}) {" \n
  1164.       > _ ?$ (sh-remember-variable str) \n
  1165.       < ?})
  1166.   (sh "Index variable: "
  1167.       "for " str " in `awk 'BEGIN { for( i=1; i<="
  1168.       (read-string "upper limit: ")
  1169.       "; i++ ) print i }'`; do" \n
  1170.       > _ ?$ (sh-remember-variable str) \n
  1171.       < "done"))
  1172.  
  1173.  
  1174. (defun sh-shell-initialize-variables ()
  1175.   "Scan the buffer for variable assignments.
  1176. Add these variables to `sh-shell-variables'."
  1177.   (message "Scanning buffer `%s' for variable assignments..." (buffer-name))
  1178.   (save-excursion
  1179.     (goto-char (point-min))
  1180.     (setq sh-shell-variables-initialized t)
  1181.     (while (search-forward "=" nil t)
  1182.       (sh-assignment 0)))
  1183.   (message "Scanning buffer `%s' for variable assignments...done"
  1184.        (buffer-name)))
  1185.  
  1186. (defvar sh-add-buffer)
  1187.  
  1188. (defun sh-add-completer (string predicate code)
  1189.   "Do completion using `sh-shell-variables', but initialize it first.
  1190. This function is designed for use as the \"completion table\",
  1191. so it takes three arguments:
  1192.   STRING, the current buffer contents;
  1193.   PREDICATE, the predicate for filtering possible matches;
  1194.   CODE, which says what kind of things to do.
  1195. CODE can be nil, t or `lambda'.
  1196. nil means to return the best completion of STRING, or nil if there is none.
  1197. t means to return a list of all possible completions of STRING.
  1198. `lambda' means to return t if STRING is a valid completion as it stands."
  1199.   (let ((sh-shell-variables
  1200.      (save-excursion
  1201.        (set-buffer sh-add-buffer)
  1202.        (or sh-shell-variables-initialized
  1203.            (sh-shell-initialize-variables))
  1204.        (nconc (mapcar (lambda (var)
  1205.                 (let ((name
  1206.                    (substring var 0 (string-match "=" var))))
  1207.                   (cons name name)))
  1208.               process-environment)
  1209.           sh-shell-variables))))
  1210.     (cond ((null code)
  1211.        (try-completion string sh-shell-variables predicate))
  1212.       ((eq code t)
  1213.        (all-completions string sh-shell-variables predicate))
  1214.       ((eq code 'lambda)
  1215.        (assoc string sh-shell-variables)))))
  1216.  
  1217. (defun sh-add (var delta)
  1218.   "Insert an addition of VAR and prefix DELTA for Bourne (type) shell."
  1219.   (interactive
  1220.    (let ((sh-add-buffer (current-buffer)))
  1221.      (list (completing-read "Variable: " 'sh-add-completer)
  1222.        (prefix-numeric-value current-prefix-arg))))
  1223.   (insert (sh-feature '((bash . "$[ ")
  1224.             (ksh88 . "$(( ")
  1225.             (posix . "$(( ")
  1226.             (rc . "`{expr $")
  1227.             (sh . "`expr $")
  1228.             (zsh . "$[ ")))
  1229.       (sh-remember-variable var)
  1230.       (if (< delta 0) " - " " + ")
  1231.       (number-to-string (abs delta))
  1232.       (sh-feature '((bash . " ]")
  1233.             (ksh88 . " ))")
  1234.             (posix . " ))")
  1235.             (rc . "}")
  1236.             (sh . "`")
  1237.             (zsh . " ]")))))
  1238.  
  1239.  
  1240.  
  1241. (define-skeleton sh-function
  1242.   "Insert a function definition.  See `sh-feature'."
  1243.   (bash eval sh-modify ksh88
  1244.     3 "() {")
  1245.   (ksh88 "name: "
  1246.      "function " str " {" \n
  1247.      > _ \n
  1248.      < "}")
  1249.   (rc eval sh-modify ksh88
  1250.     1 "fn ")
  1251.   (sh ()
  1252.       "() {" \n
  1253.       > _ \n
  1254.       < "}"))
  1255.  
  1256.  
  1257.  
  1258. (define-skeleton sh-if
  1259.   "Insert an if statement.  See `sh-feature'."
  1260.   (csh "condition: "
  1261.        "if( " str " ) then" \n
  1262.        > _ \n
  1263.        ( "other condition, %s: "
  1264.      < "else if( " str " ) then" \n
  1265.      > _ \n)
  1266.        < "else" \n
  1267.        > _ \n
  1268.        resume:
  1269.        < "endif")
  1270.   (es "condition: "
  1271.       "if { " str " } {" \n
  1272.        > _ \n
  1273.        ( "other condition, %s: "
  1274.      < "} { " str " } {" \n
  1275.      > _ \n)
  1276.        < "} {" \n
  1277.        > _ \n
  1278.        resume:
  1279.        < ?})
  1280.   (rc eval sh-modify csh
  1281.       3 " ) {"
  1282.       8 '( "other condition, %s: "
  1283.        < "} else if( " str " ) {" \n
  1284.        > _ \n)
  1285.       10 "} else {"
  1286.       17 ?})
  1287.   (sh "condition: "
  1288.       '(setq input (sh-feature sh-test))
  1289.       "if " str "; then" \n
  1290.       > _ \n
  1291.       ( "other condition, %s: "
  1292.     < "elif " str "; then" \n
  1293.     > _ \n)
  1294.       < "else" \n
  1295.       > _ \n
  1296.       resume:
  1297.       < "fi"))
  1298.  
  1299.  
  1300.  
  1301. (define-skeleton sh-repeat
  1302.   "Insert a repeat loop definition.  See `sh-feature'."
  1303.   (es nil
  1304.       "forever {" \n
  1305.       > _ \n
  1306.       < ?})
  1307.   (zsh "factor: "
  1308.       "repeat " str "; do"\n
  1309.       > _ \n
  1310.       < "done"))
  1311. (put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
  1312.  
  1313.  
  1314.  
  1315. (define-skeleton sh-select
  1316.   "Insert a select statement.  See `sh-feature'."
  1317.   (ksh88 "Index variable: "
  1318.      "select " str " in " _ "; do" \n
  1319.      > ?$ str \n
  1320.      < "done"))
  1321. (put 'sh-select 'menu-enable '(sh-feature sh-select))
  1322.  
  1323.  
  1324.  
  1325. (define-skeleton sh-tmp-file
  1326.   "Insert code to setup temporary file handling.  See `sh-feature'."
  1327.   (bash eval identity ksh88)
  1328.   (csh (file-name-nondirectory (buffer-file-name))
  1329.        "set tmp = /tmp/" str ".$$" \n
  1330.        "onintr exit" \n _
  1331.        (and (goto-char (point-max))
  1332.         (not (bolp))
  1333.         ?\n)
  1334.        "exit:\n"
  1335.        "rm $tmp* >&/dev/null" >)
  1336.   (es (file-name-nondirectory (buffer-file-name))
  1337.       "local( signals = $signals sighup sigint; tmp = /tmp/" str ".$pid ) {" \n
  1338.       > "catch @ e {" \n
  1339.       > "rm $tmp^* >[2]/dev/null" \n
  1340.       "throw $e" \n
  1341.       < "} {" \n
  1342.       > _ \n
  1343.       < ?} \n
  1344.       < ?})
  1345.   (ksh88 eval sh-modify sh
  1346.      6 "EXIT")
  1347.   (rc (file-name-nondirectory (buffer-file-name))
  1348.        "tmp = /tmp/" str ".$pid" \n
  1349.        "fn sigexit { rm $tmp^* >[2]/dev/null }")
  1350.   (sh (file-name-nondirectory (buffer-file-name))
  1351.       "TMP=/tmp/" str ".$$" \n
  1352.       "trap \"rm $TMP* 2>/dev/null\" " ?0))
  1353.  
  1354.  
  1355.  
  1356. (define-skeleton sh-until
  1357.   "Insert an until loop.  See `sh-feature'."
  1358.   (sh "condition: "
  1359.       '(setq input (sh-feature sh-test))
  1360.       "until " str "; do" \n
  1361.       > _ \n
  1362.       < "done"))
  1363. (put 'sh-until 'menu-enable '(sh-feature sh-until))
  1364.  
  1365.  
  1366.  
  1367. (define-skeleton sh-while
  1368.   "Insert a while loop.  See `sh-feature'."
  1369.   (csh eval sh-modify sh
  1370.        2 "while( "
  1371.        4 " )"
  1372.        10 "end")
  1373.   (es eval sh-modify rc
  1374.       2 "while { "
  1375.       4 " } {")
  1376.   (rc eval sh-modify csh
  1377.       4 " ) {"
  1378.       10 ?})
  1379.   (sh "condition: "
  1380.       '(setq input (sh-feature sh-test))
  1381.       "while " str "; do" \n
  1382.       > _ \n
  1383.       < "done"))
  1384.  
  1385.  
  1386.  
  1387. (define-skeleton sh-while-getopts
  1388.   "Insert a while getopts loop.  See `sh-feature'.
  1389. Prompts for an options string which consists of letters for each recognized
  1390. option followed by a colon `:' if the option accepts an argument."
  1391.   (bash eval sh-modify sh
  1392.     18 "${0##*/}")
  1393.   (csh nil
  1394.        "while( 1 )" \n
  1395.        > "switch( \"$1\" )" \n
  1396.        '(setq input '("- x" . 2))
  1397.        > >
  1398.        ( "option, %s: "
  1399.      < "case " '(eval str)
  1400.      '(if (string-match " +" str)
  1401.           (setq v1 (substring str (match-end 0))
  1402.             str (substring str 0 (match-beginning 0)))
  1403.         (setq v1 nil))
  1404.      str ?: \n
  1405.      > "set " v1 & " = $2" | -4 & _ \n
  1406.      (if v1 "shift") & \n
  1407.      "breaksw" \n)
  1408.        < "case --:" \n
  1409.        > "shift" \n
  1410.        < "default:" \n
  1411.        > "break" \n
  1412.        resume:
  1413.        < < "endsw" \n
  1414.        "shift" \n
  1415.        < "end")
  1416.   (ksh88 eval sh-modify sh
  1417.      16 "print"
  1418.      18 "${0##*/}"
  1419.      36 "OPTIND-1")
  1420.   (posix eval sh-modify sh
  1421.      18 "$(basename $0)")
  1422.   (sh "optstring: "
  1423.       "while getopts :" str " OPT; do" \n
  1424.       > "case $OPT in" \n
  1425.       > >
  1426.       '(setq v1 (append (vconcat str) nil))
  1427.       ( (prog1 (if v1 (char-to-string (car v1)))
  1428.       (if (eq (nth 1 v1) ?:)
  1429.           (setq v1 (nthcdr 2 v1)
  1430.             v2 "\"$OPTARG\"")
  1431.         (setq v1 (cdr v1)
  1432.           v2 nil)))
  1433.     < str "|+" str ?\) \n
  1434.     > _ v2 \n
  1435.     ";;" \n)
  1436.       < "*)" \n
  1437.       > "echo" " \"usage: " "`basename $0`"
  1438.       " [+-" '(setq v1 (point)) str
  1439.       '(save-excursion
  1440.      (while (search-backward ":" v1 t)
  1441.        (replace-match " ARG] [+-" t t)))
  1442.       (if (eq (preceding-char) ?-) -5)
  1443.       "] [--] ARGS...\"" \n
  1444.       "exit 2" \n
  1445.       < < "esac" \n
  1446.       < "done" \n
  1447.       "shift " (sh-add "OPTIND" -1)))
  1448. (put 'sh-while-getopts 'menu-enable '(sh-feature sh-while-getopts))
  1449.  
  1450.  
  1451.  
  1452. (defun sh-assignment (arg)
  1453.   "Remember preceding identifier for future completion and do self-insert."
  1454.   (interactive "p")
  1455.   (self-insert-command arg)
  1456.   (if (<= arg 1)
  1457.       (sh-remember-variable
  1458.        (save-excursion
  1459.      (if (re-search-forward (sh-feature sh-assignment-regexp)
  1460.                 (prog1 (point)
  1461.                   (beginning-of-line 1))
  1462.                 t)
  1463.          (match-string 1))))))
  1464.  
  1465.  
  1466.  
  1467. (defun sh-maybe-here-document (arg)
  1468.   "Inserts self.  Without prefix, following unquoted `<' inserts here document.
  1469. The document is bounded by `sh-here-document-word'."
  1470.   (interactive "*P")
  1471.   (self-insert-command (prefix-numeric-value arg))
  1472.   (or arg
  1473.       (not (eq (char-after (- (point) 2)) last-command-char))
  1474.       (save-excursion
  1475.     (backward-char 2)
  1476.     (sh-quoted-p))
  1477.       (progn
  1478.     (insert sh-here-document-word)
  1479.     (or (eolp) (looking-at "[ \t]") (insert ? ))
  1480.     (end-of-line 1)
  1481.     (while
  1482.         (sh-quoted-p)
  1483.       (end-of-line 2))
  1484.     (newline)
  1485.     (save-excursion (insert ?\n sh-here-document-word)))))
  1486.  
  1487.  
  1488. ;; various other commands
  1489.  
  1490. (autoload 'comint-dynamic-complete "comint"
  1491.   "Dynamically perform completion at point." t)
  1492.  
  1493. (autoload 'shell-dynamic-complete-command "shell"
  1494.   "Dynamically complete the command at point." t)
  1495.  
  1496. (autoload 'comint-dynamic-complete-filename "comint"
  1497.   "Dynamically complete the filename at point." t)
  1498.  
  1499. (autoload 'shell-dynamic-complete-environment-variable "shell"
  1500.   "Dynamically complete the environment variable at point." t)
  1501.  
  1502.  
  1503.  
  1504. (defun sh-newline-and-indent ()
  1505.   "Strip unquoted whitespace, insert newline, and indent like current line."
  1506.   (interactive "*")
  1507.   (indent-to (prog1 (current-indentation)
  1508.            (delete-region (point)
  1509.                   (progn
  1510.                 (or (zerop (skip-chars-backward " \t"))
  1511.                     (if (sh-quoted-p)
  1512.                     (forward-char)))
  1513.                 (point)))
  1514.            (newline))))
  1515.  
  1516.  
  1517.  
  1518. (defun sh-beginning-of-command ()
  1519.   "Move point to successive beginnings of commands."
  1520.   (interactive)
  1521.   (if (re-search-backward sh-beginning-of-command nil t)
  1522.       (goto-char (match-beginning 2))))
  1523.  
  1524.  
  1525. (defun sh-end-of-command ()
  1526.   "Move point to successive ends of commands."
  1527.   (interactive)
  1528.   (if (re-search-forward sh-end-of-command nil t)
  1529.       (goto-char (match-end 1))))
  1530.  
  1531. (provide 'sh-script)
  1532. ;;; sh-script.el ends here
  1533.