1;;; w32shell.el --- Helpers for inferior shells on w32
3;; Copyright (C) 2005, 2006 by Lennart Borgman
5;; Author: Lennart Borgman
6;; Created: Tue Nov 22 01:07:13 2005
8;; Last-Updated: Thu Dec 28 15:34:00 2006 (3600 +0100)
10;; Compatibility: Emacs 22
13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
17;; On w32 you sometimes need an inferior shell like GNU bash for
18;; installations and other purposes. (Note: An "inferior" process in
19;; Emacs is a subprocess.) You may also need to have the Emacs bin
20;; directory in path at the same time. The main purpose of this
21;; package is to make this easier. There are however also some helper
22;; functions for manipulating paths on w32.
24;; To choose shell customize group the `w32shell'.
26;; To run interactive shells you can use `cygwin-shell', `msys-shell'
29;; Notice that you can get the paths to cygwin and MSYS automatically
30;; if you use EmacsW32. From the menu bar choose Options - Customize
31;; EmacsW32... and then let Emacs search for those paths.
33;; Put this in your .emacs:
38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
42;; 2005-12-15 More careful check of shell exec.
43;; 2005-12-21 (w32shell-delayed-customize): No dialog box.
44;; 2005-12-22 (w32shell-set-shell): Call cygwin-mount-activate later.
45;; 2006-12-13 Added support for gnuwin32 progs that comes with EmacsW32
48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
52(eval-when-compile (require 'cl))
54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
56(defun w32shell-in-exec-path(path)
57 (let ((dc-exec-path (mapcar (lambda(elt)
60 (member (downcase path) dc-exec-path)))
62(defun w32shell-add-exec-path(path &optional append)
63 (unless (w32shell-in-exec-path path)
64 (add-to-list 'exec-path path append)))
66(defun w32shell-remove-exec-path(path)
68 (dcpath (downcase path)))
70 (when (equal dcpath (downcase elt))
74 (setq exec-path (delete inpath exec-path)))))
77(defun w32shell-in-envpath(path)
78 (let ((envpath (replace-regexp-in-string "\\\\" "/" (getenv "PATH")))
79 (norpath (replace-regexp-in-string "\\\\" "/" path))
81 (string-match (concat "\\(?:^\\|;\\)" (regexp-quote norpath) "\\($\\|;\\)") envpath)))
83(defun w32shell-add-envpath(path &optional append)
84 (unless (w32shell-in-envpath path)
85 (let ((bslash-path (replace-regexp-in-string "/" "\\\\" path)))
87 (setenv "PATH" (concat (getenv "PATH") ";" bslash-path))
88 (setenv "PATH" (concat bslash-path ";" (getenv "PATH")))))))
90(defun w32shell-remove-envpath(path)
91 (let ((envpath (replace-regexp-in-string "\\\\" "/" (getenv "PATH")))
92 (pos (w32shell-in-envpath path))
96 (sub1 (if (= 0 pos) "" (substring envpath 0 pos)))
97 (sub2 (substring envpath (+ pos 1 (length path))))
98 (newenvpath (replace-regexp-in-string "/" "\\\\" (concat sub1 sub2))))
99 (setenv "PATH" newenvpath))
100 (setq envpath (replace-regexp-in-string "\\\\" "/" (getenv "PATH")))
101 (setq pos (w32shell-in-envpath path))
106;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
107;;; Emacs itself in path
108;; FIx-me: is this needed?
109;; (defun w32shell-emacs-path()
110;; (file-name-as-directory exec-directory))
111;; (if (string= "/" (substring exec-directory -1))
112;; (setq emacs-path (substring exec-directory 0 -1))
114(defun w32shell-emacsw32-gnuwin32-bindir()
115 ;;(lwarn '(w32shell-emacsw32-gnuwin32-bindir) :warning "exec-directory=%s" exec-directory)
116 (let* ((top (directory-file-name
122 (emacsw32 (directory-file-name
123 (expand-file-name "EmacsW32" top)))
124 (gnuwin32 (directory-file-name
125 (expand-file-name "gnuwin32" emacsw32)))
126 (bin (expand-file-name "bin" gnuwin32)))
127 ;;(lwarn '(w32shell-emacsw32-gnuwin32-bindir) :warning "top=%s" top)
128 ;;(lwarn '(w32shell-emacsw32-gnuwin32-bindir) :warning "emacsw32=%s" emacsw32)
129 ;;(lwarn '(w32shell-emacsw32-gnuwin32-bindir) :warning "bin=%s" bin)
130 (when (file-directory-p bin)
131 (file-name-as-directory bin))))
133(defun w32shell-add-emacs(&optional append)
134 "Add Emacs itself to the path of inferior shells."
136 (w32shell-add-envpath exec-directory)
137 (w32shell-add-exec-path exec-directory))
138(defun w32shell-remove-emacs()
139 "Remove Emacs itself from the path of inferior shells."
141 (w32shell-remove-envpath exec-directory)
142 (w32shell-remove-exec-path exec-directory))
145;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
146;;; Choosing a w32 shell for Emacs
148(defun w32shell-delayed-customize(msg symbol)
149 (let ((use-dialog-box nil)
150 (is-group (get symbol 'custom-group)))
151 (when (y-or-n-p (format "%s. Do you want to customize %s now? " msg symbol))
153 (customize-group symbol)
154 (customize-option symbol)))))
156(defun w32shell-verify-bindir(bin-sym shexe)
157 "If BIN-SYM value is a sh bin directory name return it.
158Otherwise offer the user at idle time to customize it.
160Helper for `w32shell-set-shell'."
161 (let ((standard-value (car (get bin-sym 'standard-value)))
162 (bindir (symbol-value bin-sym))
164 (if (equal bindir standard-value)
165 (setq errmsg (concat "You must set " (symbol-name bin-sym)))
166 (if (file-directory-p bindir)
168 ;;(setq exefile (concat bindir "/" shexe))
169 (setq exefile (expand-file-name shexe (file-name-as-directory bindir)))
170 (unless (file-executable-p exefile)
171 (setq errmsg (concat "Can't find file " exefile))))
172 (setq errmsg (concat "Can't find directory " bindir))))
174 (let ((use-dialog-box nil))
175 (lwarn '(w32shell) :warning errmsg)
176 (unless (eq major-mode 'custom-mode)
177 (with-timeout (6 (progn
178 (lwarn '(w32shell) :warning "Ok, please customize w32shell later!")
179 (message "Time out, continuing")))
180 (w32shell-delayed-customize errmsg 'w32shell)))
184(defvar w32shell-current-shell-path nil)
186(defcustom w32shell-wanted-progs
187 '("grep" "find" "xargs" "cmp" "diff" "diff3" "cmp" "patch" "locate")
188 "List of programs that are checked for availability.
189This list of programs are searched for in your path by
190`executable-find' when calling `w32shell-set-shell'. If any of
191them is not found a warning is given."
192 :type '(repeat string)
195;; Fix-me: write a function that checks consistency against this!
196(defun w32shell-set-shell(shellname)
197 "Set shell to use for inferior shells.
198This sets `shell-file-name' and the environment variable SHELL.
200Accepted values for SHELLNAME are \"cmd\", \"cygwin\" and
203If SHELLNAME is \"cygwin\" then it calls `cygwin-mount-activate'.
205If SHELLNAME is \"cygwin\" or \"msys\" then the corresponding bin
206directory is added to path.
208This function checks if the programs in `w32shell-wanted-progs'
209that may be used from the inferior shells are available. If they
210are not a warning will be given. It also checks if 'find' is the
211unix style find or not.
213Returns non-nil if success."
216 (let* ( (history '("cmd" "msys" "cygwin"))
217 (history-length (length history)) )
218 (completing-read "Choose shell: " '("cmd" "msys" "cygwin") nil t "cygwin" 'history))))
219 ;;(lwarn '(w32shell) :warning "Calling w32shell-set-shell %s" shellname)
221 (cond ( (equal shellname "cygwin")
222 (setq bin (w32shell-verify-bindir 'w32shell-cygwin-bin "bash.exe"))
225 (setenv "PS1" "Cygwin \\w > "))
227 ( (equal shellname "msys")
228 (setq bin (w32shell-verify-bindir 'w32shell-msys-bin "sh.exe"))
231 (setenv "PS1" "MSYS \\w > "))
233 ( (equal shellname "cmd")
234 (setq bin (w32shell-emacsw32-gnuwin32-bindir))
235 ;;(lwarn '(w32shell) :warning "cmd cond, bin=%s" bin)
236 (setq shell (expand-file-name "cmdproxy.exe" exec-directory))
239 (error "Unrecognized shell name: %s" shellname)))
242 (unless (file-directory-p bin)
243 (error "Can't find directory %s" bin)))
244 (when w32shell-current-shell-path
245 (w32shell-remove-exec-path w32shell-current-shell-path)
246 (w32shell-remove-envpath w32shell-current-shell-path)
247 (setq w32shell-current-shell-path nil))
248 (cond ( (equal shellname "cmd")
249 (setq process-coding-system-alist nil)
250 (setq w32-process-args nil)
251 (remove-hook 'comint-output-filter-functions
252 'comint-strip-ctrl-m)
254 ( (or (equal shellname "cygwin") (equal shellname "msys"))
255 ;;(setq process-coding-system-alist '((shell-file-name . undecided-unix)))
256 ;;(setq process-coding-system-alist (list (cons shell-file-name 'undecided-unix)))
257 (setq w32-process-args ?\")
259 (add-hook 'comint-output-filter-functions 'comint-strip-ctrl-m)
263 ;;(lwarn '(w32shell) :warning "bin=%s, shell-name=%s" bin shellname)
265 (w32shell-add-exec-path bin)
266 (w32shell-add-envpath bin))
267 (setq w32shell-current-shell-path bin)
268 ;; Call cygwin-mount. After an idea by Ismael Valladolid Torres:
269 (when (equal shellname "cygwin")
270 (when (require 'cygwin-mount nil t)
271 (cygwin-mount-activate)))
272 (setq shell-file-name shell) ; Single shell
273 ;;(setenv "SHELL" shell-file-name) ; Single shell
274 ;;(setq explicit-shell-file-name shell-file-name)
275 ;; Some sanity checks:
276 (w32shell-check-wanted-progs)
280(defun w32shell-get-missing-progs()
282 (dolist (prog w32shell-wanted-progs)
283 (unless (executable-find prog)
284 (add-to-list 'missing prog)))
287(defun w32shell-find-is-unix-find()
288 (let ((find-prog (executable-find "find"))
289 (findstr-prog (executable-find "findstr")))
290 (not (string= (file-name-directory find-prog)
291 (file-name-directory findstr-prog)))))
293(defun w32shell-check-wanted-progs()
294 "Checks if `w32shell-wanted-progs' are available.
295This depends on `w32shell'."
297 (dolist (prog (w32shell-get-missing-progs))
298 (lwarn '(w32shell) :warning
299 (concat "When using '" shellname "' program '" prog "' can't be found")))
300 (unless (w32shell-find-is-unix-find)
301 (lwarn '(w32shell) :warning
302 (concat "When using '" shellname "' program 'find'"
303 " will be Windows' find, should be unix' find"))))
305(defun w32shell-quote-argument (argument)
306 "Like `shell-quote-argument' but knows about w32shell."
307 (unless (eq system-type 'windows-nt) (error "You can only use this on w32"))
308 (let ((system-type (if (string= "cmd" shell-file-name)
311 (shell-quote-argument argument)))
314;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
317(defgroup w32shell nil
318 "Customization group for w32shell"
321(defcustom w32shell-cygwin-bin ""
322 "Path to Cygwin bin directory.
323Note that you can set this automatically from the menus through
324Options - Customize EmacsW32 if you are using EmacsW32."
328(defcustom w32shell-msys-bin ""
329 "Path to MSYS bin directory.
330Note that you can set this automatically from the menus through
331Options - Customize EmacsW32 if you are using EmacsW32."
335(defvar w32shell-old nil)
336(defcustom w32shell-shell nil
337 "Shell to use for `shell' command.
338Value should be 'cmd, 'cygwin or 'msys. If it is cygwin or msys
339those utilities bin path are put first in path.
341Setting is done with `w32shell-set-shell'."
342 ;; Make sure emacsw32 is loaded:
343 ;;:set-after '(emacsw32-style-frame-title)
345 (const :tag "(unset)" nil)
346 (const :tag "Windows cmd.exe - uses unix progs from EmacsW32" cmd)
347 (const :tag "Cygwin" cygwin)
348 (const :tag "MSYS" msys)
350 :set (lambda (symbol value)
351 (set-default symbol value)
353 (unless (eq w32shell-old value)
354 (setq w32shell-old value)
355 (w32shell-set-shell (format "%s" value))
360(defcustom w32shell-add-emacs-to-path t
361 "Add Emacs bin directory to path when non-nil."
363 :set (lambda (symbol value)
364 (set-default symbol value)
366 (unless (w32shell-in-exec-path exec-directory)
367 (w32shell-add-emacs))
368 (w32shell-remove-emacs)))
371(defvar w32shell-with-shell-internal nil)
373(defmacro w32shell-with-shell (use-shell &rest body)
374 "Execute the BODY forms with shell temporary set to USE-SHELL."
375 (declare (indent 0) (debug t))
376 `(let ((shell-file-name)
377 (exec-path exec-path)
378 (envpath (getenv "PATH")))
380 (when (w32shell-set-shell ,use-shell)
382 (error (message "%s" (error-message-string err))))
383 (setenv "PATH" envpath)))
387 "Run `shell' with Cygwin as the shell.
388Does not affect the setting of `w32shell-shell' but otherwise
389works as if you had set this to 'cygwin.
391See also `msys-shell' and `cmd-shell'."
392 "Run `shell' with MSYS as the shell.
393Is otherwise similar to `cygwin-shell'."
395 (w32shell-with-shell "cygwin" (shell "*cygwin shell*")))
398 "Run `shell' with MSYS as the shell.
399Is otherwise similar to `cygwin-shell'."
401 (w32shell-with-shell "msys" (shell "*msys shell*")))
404 "Run `shell' with Windows Command Prompt as the shell.
405File name completion with Tab/Shift-Tab is done in the style that
406Windows Command Prompt does it.
408Is otherwise similar to `cygwin-shell'."
413 (shell "*cmd shell*")
414 ;; fix-me: Temporary, until removed from viper
415 (when (and (boundp 'viper-insert-basic-map)
416 (keymapp viper-insert-basic-map))
417 (define-key viper-insert-basic-map
418 (if viper-xemacs-p [(shift tab)] [S-tab]) nil))
419 (local-set-key [tab] 'w32shell-dynamic-complete-filename-like-cmd-fw)
420 (local-set-key [(shift tab)]
421 'w32shell-dynamic-complete-filename-like-cmd-bw))))
425;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
426(defun w32shell-dynamic-complete-filename-like-cmd-fw ()
427 "Tab style file name completion like cmd.exe.
428Tries to do Tab style file name completion like cmd.exe on w32
431See also `w32shell-dynamic-complete-filename-like-cmd-bw'."
433 (w32shell-dynamic-complete-filename-like-cmd t))
435(defun w32shell-dynamic-complete-filename-like-cmd-bw ()
436 "Shift-Tab style file name completion like cmd.exe.
437Tries to do Shift-Tab style file name completion like cmd.exe on
440See also `w32shell-dynamic-complete-filename-like-cmd-fw'."
442 (w32shell-dynamic-complete-filename-like-cmd nil))
444(defconst w32shell-dynamic-complete-state nil)
446(defcustom w32shell-dynamic-complete-sync-dirs t
447 "Synchronize process directory and `default-directory' if non-nil.
448If non-nil then `w32shell-dynamic-complete-filename-like-cmd-fw' (and
449dito -bw) will send an invisible \"cd\" to the process running
450cmd.exe to find out what directory the cmd.exe process
451uses. `default-directory' is then set to this directory."
455(defcustom w32shell-dynamic-complete-only-dirs '("cd" "pushd")
456 "Commands for which only directories should be shown.
457When doing file name completion the commands in this list will
458only get directory names.
460This is used in `w32shell-dynamic-complete-filename-like-cmd-fw' (and
462 :type '(repeat string)
465(defun w32shell-dynamic-complete-filename-like-cmd (forward)
466 (let* ((proc (get-buffer-process (current-buffer)))
467 (pmark (process-mark proc))
469 (cmdstr (buffer-substring-no-properties pmark point))
470 (argv (w32shell-get-argv cmdstr))
471 (first-arg (car argv))
472 (last-arg (car (reverse argv)))
473 (only-dirs (member (car first-arg) w32shell-dynamic-complete-only-dirs))
474 (prev-cmdstr (nth 0 w32shell-dynamic-complete-state))
475 (prev-completion (nth 1 w32shell-dynamic-complete-state))
476 (prev-begin-filename (nth 2 w32shell-dynamic-complete-state))
477 (in-completion (string= cmdstr prev-cmdstr))
478 (begin-filename prev-begin-filename)
488 (setq w32shell-dynamic-complete-state nil)
490 (setq completion-dir-given (file-name-directory (car last-arg))))
491 (if completion-dir-given
492 (setq completion-dir-given
493 (file-name-as-directory completion-dir-given))
494 (setq completion-dir-given ""))
495 ;; Not continuing completion set up for completion
496 (unless in-completion
497 (setq prev-completion nil)
500 (concat "^" (file-name-nondirectory (car last-arg))))
501 (setq begin-filename nil))
502 ;; Sync process directory and default-directory
503 (when w32shell-dynamic-complete-sync-dirs
504 (let ((old-out-filter (process-filter proc)))
510 (let ((lstr (split-string str "[\r\n]+")))
511 (setq default-directory
512 (file-name-as-directory (nth 1 lstr))))))
513 (process-send-string proc "cd\n")
514 (accept-process-output proc))
515 (error (message "%s" (error-message-string err))))
516 (set-process-filter proc old-out-filter))))
518 (setq completion-dir (expand-file-name completion-dir-given))
519 (setq dir-files (directory-files completion-dir nil begin-filename))
521 (setq dir-files (reverse dir-files)))
522 (dolist (f dir-files)
523 (when (and (not (member f '("." "..")))
525 (file-directory-p (expand-file-name f completion-dir))))
526 (unless new-completion
527 (setq new-completion f))
528 (if (string= f prev-completion)
532 (setq new-completion f)))))
533 (setq new-full-completion
534 (convert-standard-filename
535 (concat completion-dir-given new-completion)))
536 ;; Replase last argument
537 (setq beginning-last (nth 1 last-arg))
538 (unless beginning-last
539 (setq beginning-last 0))
540 (goto-char (+ pmark beginning-last))
541 (unless (eolp) (kill-line))
542 ;; The code below should probably use shell-quote-argument, but
543 ;; because of trouble with this function I am using a more
544 ;; w32 specific quoting here at the moment.
545 (if (not (memq ?\ (append new-full-completion nil)))
546 (insert new-full-completion)
548 (insert new-full-completion)
550 ;; Save completion state
552 ;; return non-nil to show completion has been done!
553 (setq w32shell-dynamic-complete-state
555 (buffer-substring-no-properties pmark (point))
559(defun w32shell-get-argv(cmdline)
560 "Split CMDLINE into args.
561The splitting is done using the syntax used on MS Windows.
563Return a list where each element is a list in the form
565 \(arg arg-begin arg-end)
567where ARG is the argument stripped from any \". ARG-BEGIN and
568ARG-END are the beginning and end of the argument in cmdline.
570If CMDLINE ends with a space or is \"\" a list consisting of
571\(\"\" LEN nil) is added. LEN is the length of CMDLINE."
572 (let ((lcmd (append cmdline nil))
573 (len (length cmdline))
583 (setq lcmd (cdr lcmd))
586 (when arg (error "arg not nil"))
590 (setq arg-begin (- len 1 (length lcmd)))
591 (setq state 'state-qarg))
593 (setq arg-begin (- len 1 (length lcmd)))
594 (setq state 'state-arg)
595 (setq arg (cons c arg)))))
596 ( (eq state 'state-arg)
600 (setq arg-end (- len 1 (length lcmd)))
602 (list (concat (nreverse arg))
608 (setq state 'state-arg-q))
610 (setq arg (cons c arg)))))
611 ( (eq state 'state-arg-q)
614 (setq state 'state-arg))
616 (setq arg (cons c arg)))))
617 ( (eq state 'state-qarg)
620 (setq state 'state-qarg-q))
622 (setq arg (cons c arg)))))
623 ( (eq state 'state-qarg-q)
627 (setq arg-end (- len 1 (length lcmd)))
629 (list (concat (nreverse arg))
635 (setq arg (cons c arg))
636 (setq state 'state-qarg))
638 (setq arg (cons c arg)))))
640 (error "unknown state=%s" state))
644 (setq arg-end (- len 0 (length lcmd)))
647 (concat (nreverse arg))
651 (when (or (not c) (= c ?\ ))
652 (setq argv (cons (list "" (length cmdstr) nil) argv))))
657 (global-set-key [f9] 'w32shell-dynamic-complete-filename-like-cmd-fw)
658 (global-set-key [(shift f9)] 'w32shell-dynamic-complete-filename-like-cmd-bw)
662 (let* ((cmd "cd \\\"hej\"\\du \"sista\"")
663 (argv (w32shell-get-argv cmd)))
665 (message "%s %s %s (%s)"
669 (substring cmd (nth 1 a) (nth 2 a)))))
672(defun w32shell-explorer(dir)
673 "Open Windows Explorer in directory DIR."
674 (interactive "DStart in directory: ")
675 (setq dir (expand-file-name dir))
676 (w32shell-shell-execute nil dir))
678(defun w32shell-explorer-here()
679 "Open Windows Explorer in current directory."
681 (w32shell-explorer default-directory))
683(defun w32shell-cmd(dir)
684 "Open a Windows command prompt in directory DIR.
685Emacs bin dir is added to path in the started command window."
686 (interactive "DStart in directory: ")
687 (let ((default-directory (expand-file-name dir))
688 (old-path (getenv "PATH")))
689 (w32shell-add-envpath exec-directory)
690 ;;(w32shell-remove-envpath exec-directory)
693 ;;(call-process "cmd.exe" nil 0 nil "/c" "start" (concat '(?\") "hej4" '(?\")) "cmd.exe")
694 ;; Bug in call-process quoting, use this instead
695 (w32-shell-execute nil "cmd.exe" "/c start \"Command Prompt with Emacs in PATH\"")
697 (error (message "%s" (error-message-string err))))
698 (setenv "PATH" old-path)))
700(defun w32shell-cmd-here()
701 "Open a Windows command prompt in current directory.
702Emacs bin dir is added to path in the started command window."
704 (w32shell-cmd default-directory))
708;;; w32shell.el ends here