From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Zelphir Kaltstahl Newsgroups: gmane.lisp.guile.user Subject: get absolute path of given path Date: Sun, 6 Sep 2020 17:04:04 +0200 Message-ID: <53fcdc7a-5c55-3f11-5812-a02729a12295@posteo.de> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="5868"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Icedove/68.10.0 To: Guile User Original-X-From: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Sun Sep 06 17:04:40 2020 Return-path: Envelope-to: guile-user@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kEwDf-0001Ps-Rs for guile-user@m.gmane-mx.org; Sun, 06 Sep 2020 17:04:39 +0200 Original-Received: from localhost ([::1]:39690 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kEwDe-0005mG-Tt for guile-user@m.gmane-mx.org; Sun, 06 Sep 2020 11:04:38 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:58362) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kEwDV-0005lm-Bj for guile-user@gnu.org; Sun, 06 Sep 2020 11:04:29 -0400 Original-Received: from mout01.posteo.de ([185.67.36.65]:41817) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kEwDR-0006gS-Vh for guile-user@gnu.org; Sun, 06 Sep 2020 11:04:28 -0400 Original-Received: from submission (posteo.de [89.146.220.130]) by mout01.posteo.de (Postfix) with ESMTPS id 9C6D7160065 for ; Sun, 6 Sep 2020 17:04:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.de; s=2017; t=1599404663; bh=CJYvlxuZqnA+oYTBI1F9nTTXBcW0Atd98ggEfKRpsXM=; h=To:From:Subject:Date:From; b=bp4CNq3X2b3XEVyAWtegvliuJkjwlQkVY9sF00ezLpxTaHLIOMKJ/5SDz9JN78hGq CATHH64WqntfRG2jVVY41XpAkSZdkE2A1lGVSIp6HtrTKDgBk37I1YvPeGoSa4lqw3 0AD8KPIg3m9SsHGTD/gUG6lK+K9MkrKFMXVR8+BhD+s5kH8nx4cE8FWPuVXz/GjHPE I6Yor/MU8cIAv8UODu3/fuZuGklbIwNFeGG89cuqNkCP1NBr4zqnIoazHOzQXi9MvY cztXOZ0ks0bIb0iglLjfOPw0+xm+6tOsKwtfWnYYOMVJ374ilW/6GqfV6QRnSMFDxv RTIZn/FkbBVnQ== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4Bkvnc5rFlz6tmV for ; Sun, 6 Sep 2020 17:04:04 +0200 (CEST) Content-Language: en-US Received-SPF: pass client-ip=185.67.36.65; envelope-from=zelphirkaltstahl@posteo.de; helo=mout01.posteo.de X-detected-operating-system: by eggs.gnu.org: First seen = 2020/09/06 11:04:24 X-ACL-Warn: Detected OS = Linux 3.11 and newer [fuzzy] X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Content-Filtered-By: Mailman/MimeDel 2.1.23 X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Original-Sender: "guile-user" Xref: news.gmane.io gmane.lisp.guile.user:16870 Archived-At: 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