aboutsummaryrefslogtreecommitdiff
path: root/deepenv.scm
blob: 98c9400afa5c2860fda459d0620dad9af6cdc336 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
;; Deepenv
;; Copyright (C) 2024 Guillaume Pasquet

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

(import scheme
        (chicken base)
        (chicken pathname)
        (chicken file)
        (chicken io)
        (chicken string)
        (chicken process)
        (chicken process-context)
        srfi-1
        srfi-13
        srfi-14)

(define (dir-has-.env? dir)
  (file-exists? (make-pathname dir ".env")))

(define (split-env-line line)
  (let* ((definition (string-split line "="))
         (key (car definition))
         (value (string-trim-both (cadr definition)
                                  (list->char-set (list #\") char-set:whitespace))))
    (cons key value)))

(define (line-empty? line)
  (string=? "" (string-trim-both line)))

(define (line-comment? line)
  (let ((clean-line (string-trim-both line)))
    (and (> (string-length clean-line) 0)
	 (eq? (string-ref clean-line 0) #\#))))

(define (line-eof? line)
  (eq? line #!eof))

(define (line-ignore? line)
  (or (line-empty? line)
      (line-comment? line)))

(define (read-env-line port)
  "Read an env-definition line from PORT and return as a cons cell"
  (let ((line (read-line port)))
    (cond
     ((line-eof? line) #!eof)
     ((line-ignore? line) '())
     (else (split-env-line line)))))

(define (load-dir-.env dir)
  "Load a .env file present in `.dir` and return its environment definitions as a alist"
  (let ((.env-file (conc dir "/.env")))
    (if (file-exists? .env-file)
        (filter
	 (lambda (item)
	   (not (null? item)))
	 (with-input-from-file .env-file
	   (lambda () (read-list (current-input-port) read-env-line))))
        '())))

(define (path-parts path)
  (call-with-values
      (lambda () (decompose-directory path))
    (lambda (origin base elts)
      (if (not (eq? origin #f))
          (cons origin elts)
          (if (not (eq? base #f))
              (cons base elts)
              elts)))))

(define (path-parents path)
  (fold (lambda (item acc)
          (if (> (length acc) 0)
              (if (string=? (car acc) "/")
                  (cons (conc "/" item) acc)
                  (cons (conc (car acc) "/" item) acc))
              (list item)))
        '()
        (path-parts path)))

(define (.env-union alist2 alist1)
  (lset-union (lambda (a b) (string=? (car a) (car b)))
              alist2 alist1))

(define (resolve-program program)
  (let* ((paths (map
                     (lambda (path)
                       (conc path "/" program))
                     (string-split (get-environment-variable "PATH") ":")))
         (executables (filter file-exists? paths)))
    (if (> (length executables) 0)
        (car executables)
        program)))

(let* ((parents (path-parents (current-directory)))
       (environment (fold .env-union '() (cons (get-environment-variables) (reverse (map load-dir-.env parents)))))
       (program (resolve-program (car (command-line-arguments))))
       (arguments (cdr (command-line-arguments))))
  (if program
      (process-execute program arguments environment)
      (display "Program not found\n")))