3 ;;;; To use, add this to your ~/.sbclrc:
5 ;;;; (load "/path/to/tilde.lisp")
6 ;;;; (tilde:install-tilde-expander)
8 ;;;; After that, (equal (probe-file "~/") (user-homedir-pathname)) =>
16 (:export
#:expand-tilde-namestring
17 #:install-tilde-expander
18 #:uninstall-tilde-expander
23 (defparameter *desktop-directory-namestring
* "Desktop/")
25 (proclaim '(ftype (function (t) t
) username
))
27 (define-condition no-such-user
(error)
31 (:report
(lambda (condition stream
)
32 (format stream
"No such user ~S" (username condition
)))))
34 (defun posix-passwd (username-designator)
35 (declare (sb-ext:muffle-conditions sb-ext
:compiler-note
))
36 (etypecase username-designator
38 (sb-posix:getpwuid
(sb-posix:getuid
)))
40 (sb-posix:getpwnam username-designator
))))
42 (defun posix-home-directory (username)
43 (let ((passwd (posix-passwd username
)))
45 (concatenate 'string
(sb-posix:passwd-dir passwd
) "/")
46 (error 'no-such-user
:username username
))))
48 (defun home-directory-namestring (username)
49 (posix-home-directory username
))
51 (defun desktop-directory-namestring (username)
52 (concatenate 'string
(home-directory-namestring username
)
53 *desktop-directory-namestring
*))
55 (defun tilde-namestring-parts (string &key
(start 0) (end (length string
)))
56 "Return multiple values: username, suffix, desktopp."
57 (assert (char= (char string start
) #\~
))
58 (let ((pos (1+ start
))
66 (values username suffix desktopp
))
75 (maybe-username char
))))
76 (maybe-username (char)
79 (setf suffix-start pos
)
84 (setf username-start pos
)
89 (setf suffix
(subseq string
(1+ suffix-start
) pos
))
96 (setf suffix-start pos
)
97 (setf username
(subseq string username-start pos
))
100 (setf username
(subseq string username-start pos
))
104 (setf state
#'maybe-desktop
)
107 (return (funcall state
:end
)))
108 (setf state
(funcall state
(char string pos
)))
112 (defun expand-tilde-namestring (string &key
(start 0) end
)
113 "If STRING starts with a tilde \(~), returns an expanded
114 namestring. Namestrings are expanded like so:
116 ~ expands to the home directory for the current user
117 ~<username> expands to the home directory for <username>
118 ~~ expands to the desktop directory for the current user
119 ~~<username> expands to the desktop directory for <username>
121 If a username is not found, an error of type NO-SUCH-USER is raised."
122 (setf end
(or end
(length string
)))
123 (if (and (plusp (length string
))
124 (char= (char string start
) #\~
))
125 (multiple-value-bind (username suffix desktopp
)
126 (tilde-namestring-parts string
:start start
:end end
)
127 (let ((base (if desktopp
128 (desktop-directory-namestring username
)
129 (home-directory-namestring username
))))
130 (concatenate 'string base suffix
)))
133 (defun error-not-installed (&rest rest
)
134 (declare (ignore rest
))
135 (error "Not installed"))
137 (defvar *old-parse-namestring
* 'error-not-installed
)
140 (declare (sb-ext:muffle-conditions style-warning
))
141 (defun new-parse-namestring (thing
144 (defaults *default-pathname-defaults
*)
145 &key
(start 0) end junk-allowed
)
146 (when (stringp thing
)
147 (setf thing
(expand-tilde-namestring thing
)))
148 (funcall *old-parse-namestring
* thing host defaults
151 :junk-allowed junk-allowed
)))
153 (defun install-tilde-expander ()
154 (when (eql *old-parse-namestring
* 'error-not-installed
)
155 (setf *old-parse-namestring
* #'sb-impl
::parse-namestring
)
156 (sb-ext:without-package-locks
157 (setf (fdefinition 'sb-impl
::parse-namestring
) #'new-parse-namestring
))
160 (defun uninstall-tilde-expander ()
161 (unless (eql *old-parse-namestring
* 'error-not-installed
)
162 (sb-ext:without-package-locks
163 (setf (fdefinition 'sb-impl
::parse-namestring
) *old-parse-namestring
*))