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