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