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