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