Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
-
bc8cb405
by Raymond Toy at 2023-12-07T23:09:31+00:00
-
81f0d53c
by Raymond Toy at 2023-12-07T23:09:36+00:00
7 changed files:
- src/code/exports.lisp
- src/code/filesys.lisp
- src/code/os.lisp
- src/general-info/release-21f.md
- src/i18n/locale/cmucl-os.pot
- tests/os.lisp
- tests/pathname.lisp
Changes:
... | ... | @@ -2074,7 +2074,7 @@ |
2074 | 2074 | "FD-STREAM-P" "FIND-IF-IN-CLOSURE" "FOREIGN-SYMBOL-ADDRESS"
|
2075 | 2075 | "FOREIGN-SYMBOL-CODE-ADDRESS" "FOREIGN-SYMBOL-DATA-ADDRESS"
|
2076 | 2076 | "GET-PAGE-SIZE" "GET-SYSTEM-INFO"
|
2077 | - "GET-USER-HOMEDIR-PATHNAME"
|
|
2077 | + "GET-USER-HOMEDIR-NAMESTRING"
|
|
2078 | 2078 | "IGNORE-INTERRUPT"
|
2079 | 2079 | "INT-SAP" "INVALIDATE-DESCRIPTOR" "IO-TIMEOUT"
|
2080 | 2080 | "LISP-STREAM" "LONG-FLOAT-RADIX" "LONG-WORDS"
|
... | ... | @@ -301,105 +301,136 @@ |
301 | 301 | (return (values (remove-backslashes namestr start index)
|
302 | 302 | (1+ index)))))))))
|
303 | 303 | |
304 | +(defun expand-tilde-user-name (str start end)
|
|
305 | + ;; Quick exit if STR doesn't start with ~ or we have an empty string.
|
|
306 | + (when (or (= start end)
|
|
307 | + (char/= (schar str start) #\~))
|
|
308 | + (return-from expand-tilde-user-name
|
|
309 | + (values str start end)))
|
|
310 | +
|
|
311 | + (let ((end-user (position #\/ str :start start :end end)))
|
|
312 | + ;; Quick exit if we can't find a "/" to terminate the user name.
|
|
313 | + (unless end-user
|
|
314 | + (return-from expand-tilde-user-name
|
|
315 | + (values str start end)))
|
|
316 | + (let* ((user-name (subseq str (1+ start) end-user))
|
|
317 | + (homedir (get-user-homedir-namestring user-name)))
|
|
318 | + (unless homedir
|
|
319 | + (error "Unknown user ~S in namestring ~S" user-name (subseq str start end)))
|
|
320 | + ;; Replace the ~user part with the home directory, adjusting END
|
|
321 | + ;; because of the replacement.
|
|
322 | + (values (concatenate 'simple-base-string
|
|
323 | + (subseq str 0 start)
|
|
324 | + homedir
|
|
325 | + (subseq str end-user))
|
|
326 | + start
|
|
327 | + (+ end (- (length homedir)
|
|
328 | + (length user-name)
|
|
329 | + 1))))))
|
|
330 | +
|
|
304 | 331 | (defun parse-unix-namestring (namestr start end)
|
305 | 332 | (declare (type simple-base-string namestr)
|
306 | 333 | (type index start end))
|
307 | - (multiple-value-bind
|
|
308 | - (absolute pieces)
|
|
309 | - (split-at-slashes namestr start end)
|
|
310 | - (let ((search-list
|
|
311 | - (if absolute
|
|
312 | - nil
|
|
313 | - (let ((first (car pieces)))
|
|
314 | - (multiple-value-bind
|
|
315 | - (search-list new-start)
|
|
316 | - (maybe-extract-search-list namestr
|
|
317 | - (car first) (cdr first))
|
|
318 | - (when search-list
|
|
319 | - ;; Lose if this search-list is already defined as
|
|
320 | - ;; a logical host. Since the syntax for
|
|
321 | - ;; search-lists and logical pathnames are the
|
|
322 | - ;; same, we can't allow the creation of one when
|
|
323 | - ;; the other is defined.
|
|
324 | - (when (find-logical-host search-list nil)
|
|
325 | - (error (intl:gettext "~A already names a logical host") search-list))
|
|
326 | - (setf absolute t)
|
|
327 | - (setf (car first) new-start))
|
|
328 | - search-list)))))
|
|
329 | - (multiple-value-bind (name type version)
|
|
330 | - (let* ((tail (car (last pieces)))
|
|
331 | - (tail-start (car tail))
|
|
332 | - (tail-end (cdr tail)))
|
|
333 | - (unless (= tail-start tail-end)
|
|
334 | - (setf pieces (butlast pieces))
|
|
335 | - (cond ((string= namestr ".." :start1 tail-start :end1 tail-end)
|
|
336 | - ;; ".." is a directory. Add this piece to the
|
|
337 | - ;; list of pieces, and make the name/type/version
|
|
338 | - ;; nil.
|
|
339 | - (setf pieces (append pieces (list (cons tail-start tail-end))))
|
|
340 | - (values nil nil nil))
|
|
341 | - ((string= namestr "." :start1 tail-start :end1 tail-end)
|
|
342 | - ;; "." is a directory as well.
|
|
343 | - (setf pieces (append pieces (list (cons tail-start tail-end))))
|
|
344 | - (values nil nil nil))
|
|
345 | - ((not (find-if-not #'(lambda (c)
|
|
346 | - (char= c #\.))
|
|
347 | - namestr :start tail-start :end tail-end))
|
|
348 | - ;; Got a bunch of dots. Make it a file of the
|
|
349 | - ;; same name, and type the empty string.
|
|
350 | - (values (subseq namestr tail-start (1- tail-end)) "" nil))
|
|
351 | - (t
|
|
352 | - (extract-name-type-and-version namestr tail-start tail-end)))))
|
|
353 | - ;; PVE: Make sure there are no illegal characters in the name
|
|
354 | - ;; such as #\Null and #\/.
|
|
355 | - (when (and (stringp name)
|
|
356 | - (find-if #'(lambda (x)
|
|
357 | - (or (char= x #\Null) (char= x #\/)))
|
|
358 | - name))
|
|
359 | - (error 'parse-error))
|
|
360 | - ;; Now we have everything we want. So return it.
|
|
361 | - (values nil ; no host for unix namestrings.
|
|
362 | - nil ; no devices for unix namestrings.
|
|
363 | - (collect ((dirs))
|
|
364 | - (when search-list
|
|
365 | - (dirs (intern-search-list search-list)))
|
|
366 | - (dolist (piece pieces)
|
|
367 | - (let ((piece-start (car piece))
|
|
368 | - (piece-end (cdr piece)))
|
|
369 | - (unless (= piece-start piece-end)
|
|
370 | - (cond ((string= namestr ".." :start1 piece-start
|
|
371 | - :end1 piece-end)
|
|
372 | - (dirs :up))
|
|
373 | - ((string= namestr "**" :start1 piece-start
|
|
374 | - :end1 piece-end)
|
|
375 | - (dirs :wild-inferiors))
|
|
376 | - (t
|
|
377 | - (dirs (maybe-make-pattern namestr
|
|
378 | - piece-start
|
|
379 | - piece-end)))))))
|
|
380 | - (cond (absolute
|
|
381 | - (cons :absolute (dirs)))
|
|
382 | - ((dirs)
|
|
383 | - ;; "." in a :relative directory is the same
|
|
384 | - ;; as if it weren't there, so remove them.
|
|
385 | - (cons :relative (delete "." (dirs) :test #'equal)))
|
|
386 | - (t
|
|
387 | - ;; If there is no directory and the name is
|
|
388 | - ;; "." and the type is NIL, we really got
|
|
389 | - ;; directory ".", so make it so.
|
|
390 | - (if (and (equal name ".")
|
|
391 | - (null type))
|
|
392 | - (list :relative)
|
|
393 | - nil))))
|
|
394 | - ;; A file with name "." and type NIL can't be the name
|
|
395 | - ;; of file on Unix because it's a directory. This was
|
|
396 | - ;; handled above, so we can just set the name to nil.
|
|
397 | - (if (and (equal name ".")
|
|
398 | - (null type))
|
|
399 | - nil
|
|
400 | - name)
|
|
401 | - type
|
|
402 | - version)))))
|
|
334 | + ;; Look for "~user/" (or "~/"). If found replace it with the user's
|
|
335 | + ;; home directory
|
|
336 | + (multiple-value-bind (namestr start end)
|
|
337 | + (expand-tilde-user-name namestr start end)
|
|
338 | + (multiple-value-bind
|
|
339 | + (absolute pieces)
|
|
340 | + (split-at-slashes namestr start end)
|
|
341 | + (let ((search-list
|
|
342 | + (if absolute
|
|
343 | + nil
|
|
344 | + (let ((first (car pieces)))
|
|
345 | + (multiple-value-bind
|
|
346 | + (search-list new-start)
|
|
347 | + (maybe-extract-search-list namestr
|
|
348 | + (car first) (cdr first))
|
|
349 | + (when search-list
|
|
350 | + ;; Lose if this search-list is already defined as
|
|
351 | + ;; a logical host. Since the syntax for
|
|
352 | + ;; search-lists and logical pathnames are the
|
|
353 | + ;; same, we can't allow the creation of one when
|
|
354 | + ;; the other is defined.
|
|
355 | + (when (find-logical-host search-list nil)
|
|
356 | + (error (intl:gettext "~A already names a logical host") search-list))
|
|
357 | + (setf absolute t)
|
|
358 | + (setf (car first) new-start))
|
|
359 | + search-list)))))
|
|
360 | + (multiple-value-bind (name type version)
|
|
361 | + (let* ((tail (car (last pieces)))
|
|
362 | + (tail-start (car tail))
|
|
363 | + (tail-end (cdr tail)))
|
|
364 | + (unless (= tail-start tail-end)
|
|
365 | + (setf pieces (butlast pieces))
|
|
366 | + (cond ((string= namestr ".." :start1 tail-start :end1 tail-end)
|
|
367 | + ;; ".." is a directory. Add this piece to the
|
|
368 | + ;; list of pieces, and make the name/type/version
|
|
369 | + ;; nil.
|
|
370 | + (setf pieces (append pieces (list (cons tail-start tail-end))))
|
|
371 | + (values nil nil nil))
|
|
372 | + ((string= namestr "." :start1 tail-start :end1 tail-end)
|
|
373 | + ;; "." is a directory as well.
|
|
374 | + (setf pieces (append pieces (list (cons tail-start tail-end))))
|
|
375 | + (values nil nil nil))
|
|
376 | + ((not (find-if-not #'(lambda (c)
|
|
377 | + (char= c #\.))
|
|
378 | + namestr :start tail-start :end tail-end))
|
|
379 | + ;; Got a bunch of dots. Make it a file of the
|
|
380 | + ;; same name, and type the empty string.
|
|
381 | + (values (subseq namestr tail-start (1- tail-end)) "" nil))
|
|
382 | + (t
|
|
383 | + (extract-name-type-and-version namestr tail-start tail-end)))))
|
|
384 | + ;; PVE: Make sure there are no illegal characters in the name
|
|
385 | + ;; such as #\Null and #\/.
|
|
386 | + (when (and (stringp name)
|
|
387 | + (find-if #'(lambda (x)
|
|
388 | + (or (char= x #\Null) (char= x #\/)))
|
|
389 | + name))
|
|
390 | + (error 'parse-error))
|
|
391 | + ;; Now we have everything we want. So return it.
|
|
392 | + (values nil ; no host for unix namestrings.
|
|
393 | + nil ; no devices for unix namestrings.
|
|
394 | + (collect ((dirs))
|
|
395 | + (when search-list
|
|
396 | + (dirs (intern-search-list search-list)))
|
|
397 | + (dolist (piece pieces)
|
|
398 | + (let ((piece-start (car piece))
|
|
399 | + (piece-end (cdr piece)))
|
|
400 | + (unless (= piece-start piece-end)
|
|
401 | + (cond ((string= namestr ".." :start1 piece-start
|
|
402 | + :end1 piece-end)
|
|
403 | + (dirs :up))
|
|
404 | + ((string= namestr "**" :start1 piece-start
|
|
405 | + :end1 piece-end)
|
|
406 | + (dirs :wild-inferiors))
|
|
407 | + (t
|
|
408 | + (dirs (maybe-make-pattern namestr
|
|
409 | + piece-start
|
|
410 | + piece-end)))))))
|
|
411 | + (cond (absolute
|
|
412 | + (cons :absolute (dirs)))
|
|
413 | + ((dirs)
|
|
414 | + ;; "." in a :relative directory is the same
|
|
415 | + ;; as if it weren't there, so remove them.
|
|
416 | + (cons :relative (delete "." (dirs) :test #'equal)))
|
|
417 | + (t
|
|
418 | + ;; If there is no directory and the name is
|
|
419 | + ;; "." and the type is NIL, we really got
|
|
420 | + ;; directory ".", so make it so.
|
|
421 | + (if (and (equal name ".")
|
|
422 | + (null type))
|
|
423 | + (list :relative)
|
|
424 | + nil))))
|
|
425 | + ;; A file with name "." and type NIL can't be the name
|
|
426 | + ;; of file on Unix because it's a directory. This was
|
|
427 | + ;; handled above, so we can just set the name to nil.
|
|
428 | + (if (and (equal name ".")
|
|
429 | + (null type))
|
|
430 | + nil
|
|
431 | + name)
|
|
432 | + type
|
|
433 | + version))))))
|
|
403 | 434 | |
404 | 435 | (defun unparse-unix-host (pathname)
|
405 | 436 | (declare (type pathname pathname)
|
... | ... | @@ -58,32 +58,39 @@ |
58 | 58 | (unix:get-unix-error-msg utime)))
|
59 | 59 | (values utime stime major-fault))))
|
60 | 60 | |
61 | -;;; GET-USER-HOMEDIR-PATHNAME -- Public
|
|
61 | +;;; GET-USER-HOMEDIR-NAMESTRING -- Public
|
|
62 | 62 | ;;;
|
63 | -(defun get-user-homedir-pathname (name)
|
|
64 | - _N"Get the user home directory for user named NAME. Two values are
|
|
65 | - returned: the pathname of the home directory and a status code. If
|
|
66 | - the home directory does not exist NIL is returned. The status is 0
|
|
67 | - if no errors occurred. Otherwise a non-zero value is returned.
|
|
68 | - Examining errno may give information about what failed."
|
|
69 | - (alien:with-alien ((status c-call:int))
|
|
70 | - (let (result)
|
|
71 | - (unwind-protect
|
|
72 | - (progn
|
|
73 | - (setf result
|
|
74 | - (alien:alien-funcall
|
|
75 | - (alien:extern-alien "os_get_user_homedir"
|
|
76 | - (function (alien:* c-call:c-string)
|
|
77 | - c-call:c-string
|
|
78 | - (* c-call:int)))
|
|
79 | - name
|
|
80 | - (alien:addr status)))
|
|
81 | - (if (and (zerop status)
|
|
82 | - (not (alien:null-alien result)))
|
|
83 | - (values (pathname
|
|
84 | - (concatenate 'string
|
|
85 | - (alien:cast result c-call:c-string)
|
|
86 | - "/"))
|
|
87 | - status)
|
|
88 | - (values nil status)))
|
|
89 | - (alien:free-alien result))))) |
|
63 | +(defun get-user-homedir-namestring (name)
|
|
64 | + _N"Get the user home directory for user named NAME. If NAME is the empty
|
|
65 | + string, the home directory of the current user is returned.
|
|
66 | + |
|
67 | + Two values are returned: the pathname of the home directory and a
|
|
68 | + status code. If the home directory does not exist NIL is returned.
|
|
69 | + The status is 0 if no errors occurred. Otherwise a non-zero value
|
|
70 | + is returned. Examining errno may give information about what failed."
|
|
71 | + (cond
|
|
72 | + ((zerop (length name))
|
|
73 | + (multiple-value-bind (user-info status)
|
|
74 | + (unix:unix-getpwuid (unix:unix-getuid))
|
|
75 | + (values (when user-info
|
|
76 | + (unix:user-info-dir user-info))
|
|
77 | + status)))
|
|
78 | + (t
|
|
79 | + (alien:with-alien ((status c-call:int))
|
|
80 | + (let (result)
|
|
81 | + (unwind-protect
|
|
82 | + (progn
|
|
83 | + (setf result
|
|
84 | + (alien:alien-funcall
|
|
85 | + (alien:extern-alien "os_get_user_homedir"
|
|
86 | + (function (alien:* c-call:c-string)
|
|
87 | + c-call:c-string
|
|
88 | + (* c-call:int)))
|
|
89 | + name
|
|
90 | + (alien:addr status)))
|
|
91 | + (if (and (zerop status)
|
|
92 | + (not (alien:null-alien result)))
|
|
93 | + (values (alien:cast result c-call:c-string)
|
|
94 | + status)
|
|
95 | + (values nil status)))
|
|
96 | + (alien:free-alien result))))))) |
... | ... | @@ -39,6 +39,7 @@ public domain. |
39 | 39 | * ~~#253~~ Block-compile list-to-hashtable and callers
|
40 | 40 | * ~~#258~~ Remove `get-page-size` from linux-os.lisp
|
41 | 41 | * ~~#269~~ Add function to get user's home directory
|
42 | + * ~~#266~~ Support "~user" in namestrings
|
|
42 | 43 | * Other changes:
|
43 | 44 | * Improvements to the PCL implementation of CLOS:
|
44 | 45 | * Changes to building procedure:
|
... | ... | @@ -35,10 +35,12 @@ msgstr "" |
35 | 35 | |
36 | 36 | #: src/code/os.lisp
|
37 | 37 | msgid ""
|
38 | -"Get the user home directory for user named NAME. Two values are\n"
|
|
39 | -" returned: the pathname of the home directory and a status code. If\n"
|
|
40 | -" the home directory does not exist NIL is returned. The status is 0\n"
|
|
41 | -" if no errors occurred. Otherwise a non-zero value is returned.\n"
|
|
42 | -" Examining errno may give information about what failed."
|
|
38 | +"Get the user home directory for user named NAME. If NAME is the empty\n"
|
|
39 | +" string, the home directory of the current user is returned.\n"
|
|
40 | +"\n"
|
|
41 | +" Two values are returned: the pathname of the home directory and a\n"
|
|
42 | +" status code. If the home directory does not exist NIL is returned.\n"
|
|
43 | +" The status is 0 if no errors occurred. Otherwise a non-zero value\n"
|
|
44 | +" is returned. Examining errno may give information about what failed."
|
|
43 | 45 | msgstr ""
|
44 | 46 |
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | (define-test user-homedir.1
|
8 | 8 | "Test user-homedir"
|
9 | 9 | (:tag :issues)
|
10 | - ;; Simple test to see if get-user-homedir-pathname returns the
|
|
10 | + ;; Simple test to see if get-user-homedir-namestring returns the
|
|
11 | 11 | ;; expected value. Use getuid and getpwuid to figure out what the
|
12 | 12 | ;; name and home directory should be.
|
13 | 13 | (let* ((uid (unix:unix-getuid))
|
... | ... | @@ -15,15 +15,13 @@ |
15 | 15 | (assert-true uid)
|
16 | 16 | (assert-true user-info)
|
17 | 17 | (let* ((info-dir (unix:user-info-dir user-info))
|
18 | - (info-name (unix:user-info-name user-info))
|
|
19 | - (expected-home-pathname (pathname
|
|
20 | - (concatenate 'string info-dir "/"))))
|
|
21 | - (multiple-value-bind (home-pathname status)
|
|
22 | - (system:get-user-homedir-pathname info-name)
|
|
18 | + (info-name (unix:user-info-name user-info)))
|
|
19 | + (multiple-value-bind (home-namestring status)
|
|
20 | + (system:get-user-homedir-namestring info-name)
|
|
23 | 21 | (assert-true info-dir)
|
24 | 22 | (assert-true info-name)
|
25 | 23 | |
26 | - (assert-equal home-pathname expected-home-pathname)
|
|
24 | + (assert-equal home-namestring info-dir)
|
|
27 | 25 | (assert-eql status 0)))))
|
28 | 26 | |
29 | 27 | (define-test user-homedir.2
|
... | ... | @@ -33,6 +31,6 @@ |
33 | 31 | ;; value for a user that does not exist. Well, we assume such a
|
34 | 32 | ;; user doesn't exist.
|
35 | 33 | (multiple-value-bind (home-pathname status)
|
36 | - (system:get-user-homedir-pathname "zotuserunknown")
|
|
34 | + (system:get-user-homedir-namestring "zotuserunknown")
|
|
37 | 35 | (assert-eql home-pathname nil)
|
38 | 36 | (assert-eql status 0))) |
... | ... | @@ -111,3 +111,34 @@ |
111 | 111 | test
|
112 | 112 | (assert-equal printed-value (output pathname))
|
113 | 113 | (assert-equal namestring (namestring pathname))))))
|
114 | + |
|
115 | +(define-test issue.266.pathname-tilde.unknown-user
|
|
116 | + (:tag :issues)
|
|
117 | + ;; This assumes that there's no user named "zotunknown".
|
|
118 | + (assert-error 'simple-error (parse-namestring "~zotunknown/*.*")))
|
|
119 | + |
|
120 | +(define-test issue.266.pathname-tilde.1
|
|
121 | + (:tag :issues)
|
|
122 | + ;; Simple test for ~ in pathnames. Get a directory list using
|
|
123 | + ;; #P"~/*.*". This should produce exactly the same list as the
|
|
124 | + ;; #search-list P"home:*.*".
|
|
125 | + (let ((dir-home (directory #p"home:*.*" :truenamep nil :follow-links nil))
|
|
126 | + (dir-tilde (directory #p"~/*.*" :truenamep nil :follow-links nil)))
|
|
127 | + (assert-equal dir-tilde dir-home)))
|
|
128 | + |
|
129 | +(define-test issue.266.pathname-tilde.2
|
|
130 | + (:tag :issues)
|
|
131 | + ;; Simple test for ~ in pathnames. Get a directory list using
|
|
132 | + ;; #P"~user/*.*". This should produce exactly the same list as the
|
|
133 | + ;; #search-list P"home:*.*". We determine the user name via getuid
|
|
134 | + ;; #and getpwuid.
|
|
135 | + (let ((user-name (unix:user-info-name (unix:unix-getpwuid (unix:unix-getuid)))))
|
|
136 | + (assert-true user-name)
|
|
137 | + (let* ((dir-home (directory #p"home:*.*" :truenamep nil :follow-links nil))
|
|
138 | +
|
|
139 | + (dir-tilde (directory (concatenate 'string
|
|
140 | + "~"
|
|
141 | + user-name
|
|
142 | + "/*.*")
|
|
143 | + :truenamep nil :follow-links nil)))
|
|
144 | + (assert-equal dir-tilde dir-home)))) |