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