[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

get absolute path of given path

From: Zelphir Kaltstahl
Subject: get absolute path of given path
Date: Sun, 6 Sep 2020 17:04:04 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Icedove/68.10.0

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

(define-module (path-handling)
  #:export (path-split

 (srfi srfi-1))



(define displayln
  (lambda* (#:key (output-port (current-output-port)) (verbose #t) . msgs)
    (when verbose
      (display (string-append
                 (map (lambda (msg) (simple-format #f "~a" msg)) msgs)
                 " ") "\n")

;; alias for displayln
(define debug displayln)


 (ice-9 exceptions))

(define char->string
  (λ (c)
     (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."
     [(= (string-length str) 1)
      (car (string->list str))]
        (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))))


(define list-prefix?
  (λ (lst lst-prefix)
     [(null? lst-prefix) #t]
     [(null? lst) #f]
       [(equal? (car lst) (car lst-prefix))
        (list-prefix? (cdr lst) (cdr lst-prefix))]
       [else #f])])))


(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))
     (λ (p2 p1)
        [(null? p2) p1]
        [(absolute-path? p2) p2]
         (let ([dir-sep (car (string->list file-name-separator-string))])
            ;; 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.
     (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
             (dirname (or (current-filename)
                          (canonicalize-path ".")))))
     [(absolute-path? path) path]
      ;; In case the path is not absolute already, we look
      ;; for it in the current directory.
      (let next-parent ([path-parts
                          (path-join working-directory path))])
        (debug "current path-parts:" path-parts)
         ;; 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]
          (let ([path-str (apply path-join path-parts)])
            (debug "current path-str:" path-str)
                (λ (exception)
                  (debug "an exception was raised:" exception)
                   [(and (eq? (exception-kind exception)
                         (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)))]
                    (debug "unexpected exception:" exception)]))
              (λ ()
                (debug "trying to canonicalize-path" path-str)
                (canonicalize-path path-str))
              #:unwind? #t))]))])))

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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]