Added Riece pkg subtree
[packages] / xemacs-packages / sh-script / executable.el
1 ;;; executable.el --- base functionality for executable interpreter scripts -*- byte-compile-dynamic: t -*-
2
3 ;; Copyright (C) 1994, 1995, 1996, 2000, 2003, 2004 by Free Software Foundation, Inc.
4
5 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
6 ;; Keywords: languages, unix
7
8 ;; This file is part of XEmacs.
9
10 ;; XEmacs is free software; you can redistribute it and/or modify it
11 ;; under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; XEmacs is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
22 ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor
23 ;; Boston, MA 02110-1301, USA.
24
25 ;;; Synched up with: FSF 22.0.50.1 (CVS)
26
27 ;;; Commentary:
28
29 ;; executable.el is used by certain major modes to insert a suitable
30 ;; #! line at the beginning of the file, if the file does not already
31 ;; have one.
32
33 ;; Unless it has a magic number, a Unix file with executable mode is passed to
34 ;; a new instance of the running shell (or to a Bourne shell if a csh is
35 ;; running and the file starts with `:').  Only a shell can start such a file,
36 ;; exec() cannot, which is why it is important to have a magic number in every
37 ;; executable script.  Such a magic number is made up by the characters `#!'
38 ;; the filename of an interpreter (in COFF, ELF or somesuch format) and one
39 ;; optional argument.
40
41 ;; This library is for certain major modes like sh-, awk-, perl-, tcl- or
42 ;; makefile-mode to insert or update a suitable #! line at the beginning of
43 ;; the file, if the file does not already have one and the file is not a
44 ;; default file of that interpreter (like .profile or makefile).  It also
45 ;; makes the file executable if it wasn't, as soon as it's saved.
46
47 ;; It also allows debugging scripts, with an adaptation of compile, as far
48 ;; as interpreters give out meaningful error messages.
49
50 ;; Modes that use this should nconc `executable-map' to the end of their own
51 ;; keymap and `executable-font-lock-keywords' to the end of their own font
52 ;; lock keywords.  Their mode-setting commands should call
53 ;; `executable-set-magic'.
54
55 ;;; Code:
56
57 ;; XEmacs change
58 (eval-when-compile
59   (require 'compile))
60
61 (defgroup executable nil
62   "Base functionality for executable interpreter scripts"
63   :group 'processes)
64
65 ;; This used to default to `other', but that doesn't seem to have any
66 ;; significance.  fx 2000-02-11.
67 (defcustom executable-insert t          ; 'other
68   "*Non-nil means offer to add a magic number to a file.
69 This takes effect when you switch to certain major modes,
70 including Shell-script mode (`sh-mode').
71 When you type \\[executable-set-magic], it always offers to add or
72 update the magic number."
73 ;;;   :type '(choice (const :tag "off" nil)
74 ;;;              (const :tag "on" t)
75 ;;;              symbol)
76   :type 'boolean
77   :group 'executable)
78
79
80 (defcustom executable-query 'function
81   "*If non-nil, ask user before changing an existing magic number.
82 When this is `function', only ask when called non-interactively."
83   :type '(choice (const :tag "Don't Ask" nil)
84                  (const :tag "Ask when non-interactive" function)
85                  (other :tag "Ask" t))
86   :group 'executable)
87
88
89 (defcustom executable-magicless-file-regexp "/[Mm]akefile$\\|/\\.\\(z?profile\\|bash_profile\\|z?login\\|bash_login\\|z?logout\\|bash_logout\\|.+shrc\\|esrc\\|rcrc\\|[kz]shenv\\)$"
90   "*On files with this kind of name no magic is inserted or changed."
91   :type 'regexp
92   :group 'executable)
93
94
95 (defcustom executable-prefix "#! "
96   "*Interpreter magic number prefix inserted when there was no magic number."
97   :type 'string
98   :group 'executable)
99
100
101 (defcustom executable-chmod 73
102   "*After saving, if the file is not executable, set this mode.
103 This mode passed to `set-file-modes' is taken absolutely when negative, or
104 relative to the files existing modes.  Do nothing if this is nil.
105 Typical values are 73 (+x) or -493 (rwxr-xr-x)."
106   :type '(choice integer
107                  (const nil))
108   :group 'executable)
109
110
111 (defvar executable-command nil)
112
113 (defcustom executable-self-display "tail"
114   "*Command you use with argument `+2' to make text files self-display.
115 Note that the like of `more' doesn't work too well under Emacs \\[shell]."
116   :type 'string
117   :group 'executable)
118
119
120 (defvar executable-font-lock-keywords
121   '(("\\`#!.*/\\([^ \t\n]+\\)" 1 font-lock-keyword-face t))
122   "*Rules for highlighting executable scripts' magic number.
123 This can be included in `font-lock-keywords' by modes that call `executable'.")
124
125
126 (defvar executable-error-regexp-alist
127   '(;; /bin/xyz: syntax error at line 14: `(' unexpected
128     ;; /bin/xyz[5]: syntax error at line 8 : ``' unmatched
129     ("^\\(.*[^[/]\\)\\(\\[[0-9]+\\]\\)?: .* error .* line \\([0-9]+\\)" 1 3)
130     ;; /bin/xyz[27]: ehco:  not found
131     ("^\\(.*[^/]\\)\\[\\([0-9]+\\)\\]: .*: " 1 2)
132     ;; /bin/xyz: syntax error near unexpected token `)'
133     ;; /bin/xyz: /bin/xyz: line 2: `)'
134     ("^\\(.*[^/]\\): [^0-9\n]+\n\\1: \\1: line \\([0-9]+\\):" 1 2)
135     ;; /usr/bin/awk: syntax error at line 5 of file /bin/xyz
136     (" error .* line \\([0-9]+\\) of file \\(.+\\)$" 2 1)
137     ;; /usr/bin/awk: calling undefined function toto
138     ;;  input record number 3, file awktestdata
139     ;;  source line 4 of file /bin/xyz
140     ("^[^ ].+\n\\( .+\n\\)* line \\([0-9]+\\) of file \\(.+\\)$" 3 2)
141     ;; makefile:1: *** target pattern contains no `%'.  Stop.
142     ("^\\(.+\\):\\([0-9]+\\): " 1 2))
143   "Alist of regexps used to match script errors.
144 See `compilation-error-regexp-alist'.")
145
146 ;; The C function openp slightly modified would do the trick fine
147 (defvar executable-binary-suffixes
148   ;; XEmacs change: added cygwin32
149   (if (memq system-type '(ms-dos windows-nt cygwin32))
150       ;; .btm is for 4DOS and friends, <URL:http://www.jpsoft.com/>
151       '(".exe" ".com" ".bat" ".cmd" ".btm" "")
152     '("")))
153
154 ;;;###autoload
155 (defun executable-command-find-posix-p (&optional program)
156   "Check if PROGRAM handles arguments Posix-style.
157 If PROGRAM is non-nil, use that instead of \"find\"."
158   ;;  Pick file to search from location we know
159   (let* ((dir (file-truename data-directory))
160          (file (car (directory-files dir nil "^[^.]"))))
161     (with-temp-buffer
162       (call-process (or program "find")
163                     nil
164                     (current-buffer)
165                     nil
166                     dir
167                     "-name"
168                     file
169                     "-maxdepth"
170                     "1")
171         (goto-char (point-min))
172         (if (search-forward file nil t)
173             t))))
174
175 ;;;###autoload
176 (defun executable-find (command)
177   "Search for COMMAND in `exec-path' and return the absolute file name.
178 Return nil if COMMAND is not found anywhere in `exec-path'."
179   (let ((list exec-path)
180         file)
181     (while list
182       (setq list
183             (if (and (setq file (expand-file-name command (car list)))
184                      (let ((suffixes executable-binary-suffixes)
185                            candidate)
186                        (while suffixes
187                          (setq candidate (concat file (car suffixes)))
188                          (if (and (file-executable-p candidate)
189                                   (not (file-directory-p candidate)))
190                              (setq suffixes nil)
191                            (setq suffixes (cdr suffixes))
192                            (setq candidate nil)))
193                        (setq file candidate)))
194                 nil
195               (setq file nil)
196               (cdr list))))
197     file))
198
199 (defun executable-chmod ()
200   "This gets called after saving a file to assure that it be executable.
201 You can set the absolute or relative mode in variable `executable-chmod' for
202 non-executable files."
203   (and executable-chmod
204        buffer-file-name
205        (or (file-executable-p buffer-file-name)
206            (set-file-modes buffer-file-name
207                            (if (< executable-chmod 0)
208                                (- executable-chmod)
209                              (logior executable-chmod
210                                      (file-modes buffer-file-name)))))))
211
212
213 ;;;###autoload
214 (defun executable-interpret (command)
215   "Run script with user-specified args, and collect output in a buffer.
216 While script runs asynchronously, you can use the \\[next-error]
217 command to find the next error.  The buffer is also in `comint-mode' and
218 `compilation-shell-minor-mode', so that you can answer any prompts."
219   (interactive (list (read-string "Run script: "
220                                   (or executable-command
221                                       buffer-file-name))))
222   (require 'compile)
223   (save-some-buffers (not compilation-ask-about-save))
224   (set (make-local-variable 'executable-command) command)
225   (let ((compilation-error-regexp-alist executable-error-regexp-alist))
226     ;; XEmacs change: argument to compile-internal differ from Emacs
227     (compile-internal command
228                       "No more errors"
229                       nil
230                       nil
231                       nil
232                       (lambda (x) "*interpretation*"))))
233
234
235
236 ;;;###autoload
237 (defun executable-set-magic (interpreter &optional argument
238                                          no-query-flag insert-flag)
239   "Set this buffer's interpreter to INTERPRETER with optional ARGUMENT.
240 The variables `executable-magicless-file-regexp', `executable-prefix',
241 `executable-insert', `executable-query' and `executable-chmod' control
242 when and how magic numbers are inserted or replaced and scripts made
243 executable."
244   (interactive
245    (let* ((name (read-string "Name or file name of interpreter: "))
246           (arg (read-string (format "Argument for %s: " name))))
247      (list name arg (eq executable-query 'function) t)))
248
249   (setq interpreter (if (file-name-absolute-p interpreter)
250                         interpreter
251                       (or (executable-find interpreter)
252                           (error "Interpreter %s not recognized"
253                                  interpreter))))
254
255   (setq argument (concat (if (string-match "\\`/:" interpreter)
256                              (replace-match "" nil nil interpreter)
257                            interpreter)
258                          (and argument (string< "" argument) " ")
259                          argument))
260
261   (or buffer-read-only
262       (if buffer-file-name
263           (string-match executable-magicless-file-regexp
264                         buffer-file-name))
265       (not (or insert-flag executable-insert))
266       (> (point-min) 1)
267       (save-excursion
268         (goto-char (point-min))
269         (add-hook 'after-save-hook 'executable-chmod nil t)
270         (if (looking-at "#![ \t]*\\(.*\\)$")
271             (and (goto-char (match-beginning 1))
272                  ;; If the line ends in a space,
273                  ;; don't offer to change it.
274                  (not (= (char-after (1- (match-end 1))) ?\ ))
275                  (not (string= argument
276                                (buffer-substring (point) (match-end 1))))
277                  (if (or (not executable-query) no-query-flag
278                          (save-window-excursion
279                            ;; Make buffer visible before question.
280                            (switch-to-buffer (current-buffer))
281                            (y-or-n-p (concat "Replace magic number by `"
282                                              executable-prefix argument "'? "))))
283                      (progn
284                        (replace-match argument t t nil 1)
285                        (message "Magic number changed to `%s'"
286                                 (concat executable-prefix argument)))))
287           (insert executable-prefix argument ?\n)
288           (message "Magic number changed to `%s'"
289                    (concat executable-prefix argument)))))
290     interpreter)
291
292
293
294 ;;;###autoload
295 (defun executable-self-display ()
296   "Turn a text file into a self-displaying Un*x command.
297 The magic number of such a command displays all lines but itself."
298   (interactive)
299   (if (eq this-command 'executable-self-display)
300       (setq this-command 'executable-set-magic))
301   (executable-set-magic executable-self-display "+2"))
302
303 ;;;###autoload
304 (defun executable-make-buffer-file-executable-if-script-p ()
305   "Make file executable according to umask if not already executable.
306 If file already has any execute bits set at all, do not change existing
307 file modes."
308   (and (>= (buffer-size) 2)
309        (save-restriction
310          (widen)
311          (string= "#!" (buffer-substring (point-min) (+ 2 (point-min)))))
312        (let* ((current-mode (file-modes (buffer-file-name)))
313               (add-mode (logand ?\111 (default-file-modes))))
314          (or (/= (logand ?\111 current-mode) 0)
315              (zerop add-mode)
316              (set-file-modes (buffer-file-name)
317                              (logior current-mode add-mode))))))
318
319 (provide 'executable)
320
321 ;;; executable.el ends here