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