;;; Pathname expansion, to replace the deprecated core functionality.
;
; Copyright (c) 2014, The CHICKEN Team
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
;
;   Redistributions of source code must retain the above copyright
;   notice, this list of conditions and the following disclaimer.
;
;   Redistributions in binary form must reproduce the above copyright
;   notice, this list of conditions and the following disclaimer in
;   the documentation and/or other materials provided with the
;   distribution.
;
;   Neither the name of the author nor the names of its contributors
;   may be used to endorse or promote products derived from this
;   software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
; COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
; OF THE POSSIBILITY OF SUCH DAMAGE.

(module pathname-expand
    (pathname-expand)

(import chicken scheme)
(use srfi-13 files posix)

;; Expand pathname starting with "~", and/or apply base directory to
;; relative pathname
;
; Inspired by Gambit's "path-expand" procedure.

(define pathname-expand
  (let* ((home
          ;; Effective uid might be changed at runtime so this has to
          ;; be a lambda, but we could try to cache the result on uid.
          (lambda ()
            (cond-expand
              ((and windows (not cygwin))
               (or (get-environment-variable "USERPROFILE")
                   (get-environment-variable "HOME")
                   "."))
              (else
               (let ((info (user-information (current-effective-user-id))))
                 (list-ref info 5))))))
         (slash
          (cond-expand
            ((and windows (not cygwin)) '(#\\ #\/))
            (else '(#\/))))
         (ts (string-append "~" (string (car slash))))
         (tts (string-append "~" ts)))
    (lambda (path #!optional (base (current-directory)))
      (if (absolute-pathname? path)
          path
          (let ((len (string-length path)))
            (cond
             ((or (string=? "~~" path)
                  (and (fx>= len 3) (string=? tts (substring path 0 3))))
              ;; Repository-path
              (let ((rp (repository-path)))
                (if rp
                    (string-append rp (substring path 2 len))
                    (signal
                     (make-composite-condition
                      (make-property-condition
                       'exn 'location 'pathname-expand
                       'message "No repository path defined"
                       'arguments (list path))
                      (make-property-condition 'pathname-expand)
                      (make-property-condition 'repository-path))))))
             ((or (string=? "~" path)
                  (and (fx> len 2) (string=? ts (substring path 0 2))))
              ;; Current user's home dir
              (string-append (home) (substring path 1 len)))
             ((and (fx> len 0) (char=? #\~ (string-ref path 0)))
              ;; Arbitrary user's home dir
              (let ((rest (substring path 1 len)))
                (if (and (fx> len 1) (memq (string-ref path 1) slash))
                    (string-append (home) rest)
                    (let* ((p (string-index path (lambda (c) (memq c slash))))
                           (user (substring path 1 (or p len)))
                           (info (user-information user)))
                      (if info
                          (let ((dir (list-ref info 5)))
                            (if p
                                (make-pathname dir (substring path p))
                                dir))
                          (signal
                           (make-composite-condition
                            (make-property-condition
                             'exn 'location 'pathname-expand
                             'message "Cannot expand homedir for user"
                             'arguments (list path))
                            (make-property-condition 'pathname-expand)
                            (make-property-condition 'username))))))))
             (else (make-pathname base path))))))))

)