1 ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
6 (in-package :iolib.pathnames
)
8 ;;;-------------------------------------------------------------------------
10 ;;;-------------------------------------------------------------------------
12 (defclass unix-path
(file-path)
14 (:default-initargs
:host
:unspecific
18 ;;;-------------------------------------------------------------------------
20 ;;;-------------------------------------------------------------------------
22 (defmethod initialize-instance :after
((path unix-path
) &key
)
23 (with-slots (host device
)
25 (unless device
(setf device
:unspecific
))
26 (check-type host
(eql :unspecific
))
27 (check-type device
(eql :unspecific
))))
30 ;;;-------------------------------------------------------------------------
32 ;;;-------------------------------------------------------------------------
34 (defun unix-path-p (thing)
35 (typep thing
'unix-path
))
38 ;;;-------------------------------------------------------------------------
40 ;;;-------------------------------------------------------------------------
42 (defun make-file-path (&key host device
(components nil componentsp
)
43 (defaults nil defaultsp
))
44 (declare (ignore host device
))
45 (let ((defaults (and defaultsp
(file-path defaults
))))
46 (make-instance 'unix-path
47 :components
(cond (componentsp components
)
49 (file-path-components defaults
))))))
51 (defun merge-file-paths (pathspec &optional
52 (defaults *default-file-path-defaults
*))
53 (let ((path (file-path pathspec
))
54 (defaults (file-path defaults
)))
55 (if (absolute-file-path-p path
)
57 (make-instance 'unix-path
58 :components
(append (file-path-components defaults
)
59 (file-path-components path
))))))
61 (defun enough-file-path (pathspec &optional
62 (defaults *default-file-path-defaults
*))
63 (let ((path (file-path pathspec
))
64 (defaults (file-path defaults
)))
66 ((or (relative-file-path-p path
)
67 (relative-file-path-p defaults
))
70 (let* ((dir (cdr (slot-value path
'components
)))
72 (mismatch dir
(cdr (slot-value defaults
'components
))
75 (make-instance 'unix-path
:components
(subseq dir mismatch
))
76 (make-instance 'unix-path
:components
(list :root
))))))))
78 (defun %file-path-host-namestring
(path)
79 (declare (ignore path
))
82 (defun %file-path-device-namestring
(path)
83 (declare (ignore path
))
86 (defun %components-namestring
(components)
87 (multiple-value-bind (root dirs
)
88 (split-root/nodes components
)
89 (let ((delimstr (string +directory-delimiter
+))
97 (apply #'join
+directory-delimiter
+ dirs
)))))
99 (defun %file-path-components-namestring
(path)
100 (%components-namestring
(slot-value path
'components
)))
102 (defun %file-path-directory-namestring
(path)
103 (%components-namestring
(%file-path-directory path
)))
105 (defmethod file-path-namestring ((path unix-path
))
106 (%components-namestring
(slot-value path
'components
)))
108 (defmethod file-path-namestring (pathspec)
109 (file-path-namestring (file-path pathspec
)))
111 (defun split-directory-namestring (namestring)
112 (split-sequence-if (lambda (c) (char= c
+directory-delimiter
+))
114 :remove-empty-subseqs t
))
116 (defun absolute-namestring-p (namestring)
117 (char= +directory-delimiter
+ (aref namestring
0)))
119 (defun parse-file-path (pathspec &key
(start 0) end
(expand-user t
))
120 (check-type pathspec string
)
121 (when (zerop (length pathspec
))
122 (error 'invalid-file-path
124 :reason
"Null paths are not valid"))
125 (let* ((actual-namestring (subseq pathspec start end
))
126 (expansion (or (when expand-user
127 (ignore-some-conditions (isys:syscall-error
)
128 (%expand-user-directory actual-namestring
)))
130 (components (split-directory-namestring expansion
)))
131 (make-instance 'unix-path
132 :components
(if (absolute-namestring-p expansion
)
133 (cons :root components
)
136 (defun %expand-user-directory
(pathspec)
137 (flet ((user-homedir (user)
138 (nth-value 5 (isys:getpwnam user
)))
140 (nth-value 5 (isys:getpwuid uid
))))
141 (unless (char= #\~
(aref pathspec
0))
143 (destructuring-bind (first &rest rest
)
144 (split-directory-namestring pathspec
)
148 (or (isys:getenv
"HOME")
150 (or (isys:getenv
"USER")
151 (isys:getenv
"LOGIN"))))
153 (user-homedir username
)
154 (uid-homedir (isys:getuid
))))))
155 ((char= #\~
(aref first
0))
156 (user-homedir (subseq first
1)))
158 (bug "The pathspec is suppose to start with a ~S" #\~
)))))
160 (apply #'join
+directory-delimiter
+ homedir rest
)
164 ;;;-------------------------------------------------------------------------
166 ;;;-------------------------------------------------------------------------
168 (defparameter *default-file-path-defaults
*
169 (or (ignore-some-conditions (isys:syscall-error
)
170 (parse-file-path (isys:getcwd
)))
171 (ignore-some-conditions (isys:syscall-error
)
172 (parse-file-path "~"))
173 (parse-file-path "/")))
175 (defparameter *default-execution-path
*
176 (mapcar #'parse-file-path
177 (split-sequence +execution-path-delimiter
+
179 :remove-empty-subseqs t
)))