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