mylisp/cygpath.el
author Oleksandr Gavenko <gavenkoa@gmail.com>
Wed, 14 Dec 2022 00:58:57 +0200
changeset 1767 fd3589f24170
parent 1731 mylisp/cygwin-winpath.el@fec5d1fffe8c
permissions -rw-r--r--
In addition to interpreting Windows paths in Cygwin a handler is added for interpreting MSYS paths in NT Emacs.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Handling MSYS paths in NT Emacs.

(defvar cygpath-msys-root nil
  "Installation root for MSYS.")

(defconst cygpath-msys-drive-regex "\\`/\\([a-z]\\)/"
  "Regex for identifying MSYS(2) drive-like paths.")
(defconst cygpath-msys-root-dirs (list "bin" "clang32" "clang64" "clangarm64" "etc" "home" "mingw32" "mingw64" "opt" "srv" "tmp" "ucrt64" "usr" "var")
  "Root directories to be recognized as MSYS paths.")
(defun cygpath-msys-root--regex ()
    "Build regex for matching MSYS root dirs."
    (format "\\`/%s" (regexp-opt cygpath-msys-root-dirs)))
(defconst cygpath-msys-root--regex nil
    "Regex for matching MSYS root dirs. Automatically regenerated by
the function `cygpath-msys-root--regex'.")

(defun cygpath-msys-to-nt (msyspath)
  "Coverts MSYS style path to NT path."
  (cond
   ((string-match cygpath-msys-drive-regex msyspath)
    (concat (match-string 1 msyspath) ":\\" (string-replace "/" "\\" (substring msyspath (match-end 0)))))
   ((string-match cygpath-msys-root--regex msyspath)
    (concat cygpath-msys-root (string-replace "/" "\\" msyspath)))
   (t msyspath)))

;; (cygpath-msys-to-nt "/c/")
;; (cygpath-msys-to-nt "/c/Users")
;; (setq cygpath-msys-root--regex (cygpath-msys-root--regex))
;; (cygpath-msys-to-nt "/etc/fstab")

(defun cygpath-msys--mapping-function (op msyspath &rest args)
  (let ((inhibit-file-name-handlers (cons #'cygpath-msys--mapping-function inhibit-file-name-handlers))
        (inhibit-file-name-operation op))
    (apply op (cygpath-msys-to-nt msyspath) args)))

(defvar cygpath-msys-activated nil)

(defun cygpath-msys--fail-if-not-nt ()
  (unless (eq system-type 'windows-nt)
    (error "`cygpath-msys' mode makes sense only in NT Emacs."))
  (unless cygpath-msys-root
    (error "`cygpath-msys-root' is not defined."))
  (unless (file-directory-p cygpath-msys-root)
    (error "`cygpath-msys-root' is a directory.")))

;;;###autoload
(defun cygpath-msys-activate ()
  "Start interpret MSYS(2) paths inside NT Emacs."
  (interactive)
  (cygpath-msys--fail-if-not-nt)
  (unless cygpath-msys-activated
    (setq cygpath-msys-root--regex (cygpath-msys-root--regex))
    (mapc (lambda (regex)
            (add-to-list 'file-name-handler-alist (cons regex #'cygpath-msys--mapping-function)))
          (list cygpath-msys-drive-regex (cygpath-msys-root--regex)))
    (setq cygpath-msys-activated t)))

;;;###autoload
(defun cygpath-msys-deactivate ()
  "Stop interpret MSYS(2) paths inside NT Emacs."
  (interactive)
  (cygpath-msys--fail-if-not-nt)
  (when cygpath-msys-activated
    (setf file-name-handler-alist (cl-delete #'cygpath-msys--mapping-function file-name-handler-alist :key #'cdr :test #'eq))
    (setq cygpath-msys-activated nil)))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Handling Windows paths in Cygwin Emacs.

(defvar cygpath-nt-cygdrive-prefix "/cygdrive"
  "For MSYS set to empty string!")

(defconst cygpath-nt-regex-forward-slash "\\`\\([a-zA-Z]\\):/")
(defconst cygpath-nt-regex-backward-slash "\\`\\([a-zA-Z]\\):\\\\")
(defconst cygpath-nt-regex-list (list cygpath-nt-regex-forward-slash cygpath-nt-regex-backward-slash))

(defun cygpath-nt-to-cygwin (winpath)
  (cond
   ((string-match cygpath-nt-regex-forward-slash winpath)
    (concat cygpath-nt-cygdrive-prefix "/" (downcase (match-string 1 winpath)) "/" (substring winpath (match-end 0))))
   ((string-match cygpath-nt-regex-backward-slash winpath)
    (concat cygpath-nt-cygdrive-prefix "/" (downcase (match-string 1 winpath)) "/" (replace-regexp-in-string "\\\\" "/" (substring winpath (match-end 0)))))
   (t winpath)))

;; (cygpath-nt-to-cygwin "c:/home/my.txt")
;; (cygpath-nt-to-cygwin "c:\\home\\my.txt")

(defun cygpath-nt--mapping-function (op winname &rest args)
  (let ((inhibit-file-name-handlers (cons #'cygpath-nt--mapping-function inhibit-file-name-handlers))
        (inhibit-file-name-operation op))
    (apply op (cygpath-nt-to-cygwin winname) args)))

(defvar cygpath-nt-activated nil)

(defun cygpath-nt--fail-if-not-cygwin ()
  (unless (eq system-type 'cygwin)
    (error "Mode is suppose to work only inside Cygwin")))

;;;###autoload
(defun cygpath-nt-activate ()
  "Start interpret Windows paths inside Cygwin Emacs."
  (interactive)
  (cygpath-nt--fail-if-not-cygwin)
  (unless cygpath-nt-activated
    (mapc (lambda (regex)
            (add-to-list 'file-name-handler-alist (cons regex #'cygpath-nt--mapping-function)))
          cygpath-nt-regex-list)
    (setq cygpath-nt-activated t)))

;;;###autoload
(defun cygpath-nt-deactivate ()
  "Stop interpret Windows paths inside Cygwin Emacs."
  (interactive)
  (cygpath-nt--fail-if-not-cygwin)
  (when cygpath-nt-activated
    (mapc (lambda (regex)
            (setf file-name-handler-alist (cl-delete regex file-name-handler-alist :key #'car :test #'equal)))
          cygpath-nt-regex-list)
    (setq cygpath-nt-activated t)))


(provide 'cygpath-msys)
(provide 'cygpath-nt)

(provide 'cygpath)