summaryrefslogtreecommitdiff
path: root/install.scm
blob: 7922a48858614994a57de091ba8337ca000f0108 (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
#!/usr/bin/guile --no-auto-compile
!#

(use-modules (ice-9 ftw)
             (ice-9 i18n)
             (ice-9 match)
             (srfi srfi-1)
             (srfi srfi-11)
             (srfi srfi-26))

(define (recursive-scandir dir)
  "Return a list of all the file names in DIR, recursively."
  (define (components file)
    (string-split file #\/))

  (define suffix
    (let ((prefix-length (length (components dir))))
      (lambda (file)
        (string-join (drop (components file) prefix-length) "/"))))

  (define enter? (const #t))

  (define (leaf name stat result)
    (cons (suffix name) result))

  ;; No-op
  (define (down name stat result) result)
  (define (up name stat result) result)
  (define (skip name stat result) result)
  (define (error name stat errno result) result)

  (let-values (((files discard)
                (file-system-fold enter? leaf down up skip error '() dir)))
    (sort files string-locale<?)))

(define (mkdir-p dir)
  "Create directory DIR and all its ancestors."
  (define absolute?
    (string-prefix? "/" dir))

  (define not-slash
    (char-set-complement (char-set #\/)))

  (let loop ((components (string-tokenize dir not-slash))
             (root       (if absolute?
                             ""
                             ".")))
    (match components
      ((head tail ...)
       (let ((path (string-append root "/" head)))
         (catch 'system-error
           (lambda ()
             (mkdir path)
             (loop tail path))
           (lambda args
             (if (= EEXIST (system-error-errno args))
                 (loop tail path)
                 (apply throw args))))))
      (() #t))))

(define (symlink? file)
  "Return #t if FILE is a symbolic link."
  (eq? (stat:type (lstat file)) 'symlink))

(define (already-linked? src-file dest-file)
  "Return #t if SRC-FILE is a symbolic link to DEST-FILE."
  (and (symlink? dest-file)
       (string=? (readlink dest-file) src-file)))

(define %home (getenv "HOME"))

(define %dotfiles-dir
  (string-append (getcwd) "/dotfiles"))

(define (install-dotfile file)
  "Create a symlink to FILE in the current user's home directory."
  (let ((src-file (string-append %dotfiles-dir "/" file))
        (dest-file (string-append %home "/" file)))
    (cond
     ((not (file-exists? dest-file))
      (format #t "~a → ~a~%" file dest-file)
      (mkdir-p (dirname dest-file))
      (symlink src-file dest-file))
     ((already-linked? src-file dest-file)
      (format #t "~a already installed~%" file))
     (else
      (error "destination file already exists: " dest-file)))))

(define %dotfiles
  (recursive-scandir "dotfiles"))

(for-each install-dotfile %dotfiles)