Files
simple-ini/main.rkt

165 lines
7.4 KiB
Racket

#lang racket/base
(require racket/string)
(require racket/file)
(require racket/port)
(provide file->ini
ini->file
ini-get
ini-set!
make-ini
get-ini-file
)
(define (get-ini-file f)
(if (symbol? f)
(let* ((pref-dir (find-system-path 'pref-dir)))
(build-path pref-dir (string-append (symbol->string f) ".ini")))
(build-path f)))
(define (ini->file ini file)
(let ((out (open-output-file (get-ini-file file) #:exists 'replace)))
(let ((last-is-newline #f))
(for-each (lambda (section)
(let ((section-name (car section)))
(if (eq? section-name 'nil)
#t
(begin
(unless last-is-newline (newline out))
(display "[" out)
(display section-name out)
(display "]" out)
(newline out)))
(let ((lines (cdr section)))
(for-each (lambda (line)
(if (eq? (car line) 'comment)
(begin
(set! last-is-newline #f)
(display "; " out)
(display (cadr line) out)
(newline out))
(if (eq? (car line) 'empty)
(begin
(newline out)
(set! last-is-newline #t))
(if (eq? (car line) 'keyval)
(begin
(set! last-is-newline #f)
(display (cadr line) out)
(display "=" out)
(display"VALUE:" out)
(write (caddr line) out)
(newline out))
(error "Unknown line format")))))
lines))))
(mcdr ini)))
(close-output-port out)))
(define (make-ini)
(mcons 'ini (list)))
(define re-num #px"^([+]|[-])?([0-9]+([.]([0-9]+))?)$")
(define re-bool #px"^(#f|#t|true|false)$")
(define re-value #px"^VALUE:(.*)$")
(define (interpret s)
(let ((m (regexp-match re-value s)))
(if (eq? m #f)
(let ((ss (string-downcase (string-trim s))))
(let ((m-num (regexp-match re-num ss)))
(if (eq? m-num #f)
(let ((m-b (regexp-match re-bool ss)))
(if (eq? m-b #f)
s
(if (or (string=? ss "#t") (string=? ss "true"))
#t
#f)
))
(string->number (car m-num)))))
(let* ((content (cadr m)))
(with-input-from-string content read)))))
(define (file->ini file*)
(let* ((file (get-ini-file file*))
(lines (if (file-exists? file) (file->lines file) '()))
(re-section #px"^\\[([a-zA-Z0-9_-]+)\\]$")
(re-keyval #px"^([a-zA-Z0-9_-]+)[=](.*)$")
(re-comment #px"^[;](.*)$"))
(letrec ((f (lambda (sections section lines)
(if (null? lines)
(append sections (list section))
(let* ((line (string-trim (car lines)))
(empty (string=? line ""))
(m-comment (regexp-match re-comment line))
(m-keyval (regexp-match re-keyval line))
(m-section (regexp-match re-section line)))
(if empty
(f sections (append section (list (list 'empty))) (cdr lines))
(if m-comment
(f sections (append section (list (list 'comment (cadr m-comment)))) (cdr lines))
(if m-keyval
(f sections (append section
(list
(list 'keyval (string->symbol
(string-trim (string-downcase
(cadr m-keyval))))
(interpret (string-trim (caddr m-keyval)))))) (cdr lines))
(if m-section
(f (append sections (list section)) (list (string->symbol (string-trim (cadr m-section)))) (cdr lines))
(error "Unknown INI line"))))))))))
(mcons 'ini (f '() (list 'nil) lines)))))
(define (ini-get ini section key def-val)
(letrec ((g (lambda (ini)
(if (null? ini)
def-val
(if (eq? section (caar ini))
(letrec ((f (lambda (l)
(if (null? l)
def-val
(let ((entry (car l)))
(if (eq? (car entry) 'keyval)
(if (eq? (cadr entry) key)
(caddr entry)
(f (cdr l)))
(f (cdr l))))))))
(f (cdar ini)))
(g (cdr ini)))))))
(g (mcdr ini))))
(define (ini-set! ini section key val)
(let ((found #f))
(letrec ((for-sect (lambda (sect)
(if (null? sect)
(if found
'()
(begin
(set! found #t)
(list (list 'keyval key val))))
(let ((entry (car sect)))
(if (eq? (car entry) 'keyval)
(if (eq? (cadr entry) key)
(begin
(set! found #t)
(cons (list 'keyval key val) (for-sect (cdr sect))))
(cons entry (for-sect (cdr sect))))
(cons entry (for-sect (cdr sect)))))))))
(letrec ((for-ini (lambda (ini)
(if (null? ini)
(if found
'()
(list (list section (list 'keyval key val))))
(let* ((ini-section (car ini))
(section-key (car ini-section)))
(if (eq? section-key section)
(cons (cons section (for-sect (cdr ini-section))) (for-ini (cdr ini)))
(cons ini-section (for-ini (cdr ini)))))))))
(let ((new-ini (for-ini (mcdr ini))))
(set-mcdr! ini new-ini)
ini)))))