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