From: Zelphir Kaltstahl <zelphirkaltstahl@posteo.de>
To: Guile User <guile-user@gnu.org>
Subject: get absolute path of given path
Date: Sun, 6 Sep 2020 17:04:04 +0200 [thread overview]
Message-ID: <53fcdc7a-5c55-3f11-5812-a02729a12295@posteo.de> (raw)
Hi Guile Users!
In my explorations into making examples for web development, I came
across the question of how to get an absolute path from any given path.
This is useful for example when checking, whether a path points to
something inside a static assets directory, or perhaps sneakily tries to
escape that and access things it should not.
I found in Guile's manual the function (canonicalize-path path).
However, this function has one problem, which makes it not sufficient on
its own: It raises an exception, when a path given points to something
that does not exist. I would like to have a function, that gives me the
absolute path of any path I give as argument, not only for existing
paths. So i went ahead and wrote the following code
(https://notabug.org/ZelphirKaltstahl/guile-examples/src/14a76a6aee18a900ac9b9de2b79ede239f8cf9f0/file-system/path-handling.scm):
~~~~START~~~~
(define-module (path-handling)
#:export (path-split
path-join
absolute-path?
absolute-path))
(use-modules
(srfi srfi-1))
;;;
;;; HELPERS
;;;
;;; LOGGING
(define displayln
(lambda* (#:key (output-port (current-output-port)) (verbose #t) . msgs)
(when verbose
(display (string-append
(string-join
(map (lambda (msg) (simple-format #f "~a" msg)) msgs)
" ") "\n")
output-port))))
;; alias for displayln
(define debug displayln)
;;; STRINGS
(use-modules
(ice-9 exceptions))
(define char->string
(λ (c)
(list->string
(list c))))
(define string->char
(λ (str)
"Convert a string, which has only one single character
into a character. This is useful, because some functions
expect a characters as input instead of a string."
(cond
[(= (string-length str) 1)
(car (string->list str))]
[else
(raise-exception
(make-exception
(make-non-continuable-error)
(make-exception-with-message "trying to convert string of more than 1 character to char")
(make-exception-with-irritants (list str))
(make-exception-with-origin 'string->char)))])))
#;(define has-prefix?
(λ (str prefix)
(= (string-prefix-length str prefix)
(string-length prefix))))
;;; LISTS
(define list-prefix?
(λ (lst lst-prefix)
(cond
[(null? lst-prefix) #t]
[(null? lst) #f]
[else
(cond
[(equal? (car lst) (car lst-prefix))
(list-prefix? (cdr lst) (cdr lst-prefix))]
[else #f])])))
;;;
;;; PATH FUNCTIONS
;;;
(define absolute-path?
(λ (path)
"Check, whether the given path is an absolute path."
;; Guile already offers a function for this, but it is a
;; little bit strangely named. We only give it an alias.
(absolute-file-name? path)))
(define path-join
(λ (path1 . other-path-parts)
"Join paths using the system preferred separator."
(debug "joining path parts:" (cons path1 other-path-parts))
(fold
(λ (p2 p1)
(cond
[(null? p2) p1]
[(absolute-path? p2) p2]
[else
(let ([dir-sep (car (string->list file-name-separator-string))])
(string-append
;; Remove any trailing separators to make sure
;; there is only one separator, when the paths
;; are concattenated.
(string-trim-right p1 (λ (char) (char=? char dir-sep)))
;; Concat the paths with the separator in the
;; middle.
(char->string dir-sep)
;; We already know p2 is not an absolute path.
p2))]))
""
(cons path1 other-path-parts))))
(define path-split
(λ (path)
"Split a path by the preferred separator of the system."
(string-split path (string->char file-name-separator-string))))
(define absolute-path
(lambda* (path
#:key
(working-directory
(dirname (or (current-filename)
(canonicalize-path ".")))))
(cond
[(absolute-path? path) path]
[else
;; In case the path is not absolute already, we look
;; for it in the current directory.
(let next-parent ([path-parts
(path-split
(path-join working-directory path))])
(debug "current path-parts:" path-parts)
(cond
;; WARNING: This part is not OS independent. An
;; absolute path does not have to start with the
;; separator string in all OS.
[(null? path-parts) file-name-separator-string]
[else
(let ([path-str (apply path-join path-parts)])
(debug "current path-str:" path-str)
(with-exception-handler
(λ (exception)
(debug "an exception was raised:" exception)
(cond
[(and (eq? (exception-kind exception)
'system-error)
(string=? (car (exception-irritants exception))
"No such file or directory"))
;; Try to check if the path to the
;; parent directory exists and is an
;; absolute path instead.
(debug "the exception is about the path not existing")
(apply path-join
(list (next-parent (drop-right path-parts 1))
(last path-parts)))]
[else
(debug "unexpected exception:" exception)]))
(λ ()
(debug "trying to canonicalize-path" path-str)
(canonicalize-path path-str))
#:unwind? #t))]))])))
~~~~~END~~~~~
But then I thought about it and realized, that this is not OS
independent. Not every OS must have the convention of starting absolute
paths with the separator string.
So I wonder: Is there a function in Guile, which translates a path like
"/a/b/c" into an equivalent on the current OS?
I think in Python 3 the rule is for example to always use "/" as a
separator and Python will take care of translating that to the
underlying OS. Not sure how it handles making absolute paths, but I
could imagine, that one could use this "slash first means absolute path"
kind of path language and then Guile internally translates that to the
underlying OS' absolute path.
Regards,
Zelphir
--
repositories: https://notabug.org/ZelphirKaltstahl
next reply other threads:[~2020-09-06 15:04 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-09-06 15:04 Zelphir Kaltstahl [this message]
2020-09-06 15:20 ` get absolute path of given path Matt Wette
2020-09-06 23:39 ` Zelphir Kaltstahl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=53fcdc7a-5c55-3f11-5812-a02729a12295@posteo.de \
--to=zelphirkaltstahl@posteo.de \
--cc=guile-user@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).