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