2016-01-06 11:55:25 +01:00
|
|
|
;; Additional library functions for TinySCHEME.
|
|
|
|
;;
|
|
|
|
;; Copyright (C) 2016 g10 Code GmbH
|
|
|
|
;;
|
|
|
|
;; This file is part of GnuPG.
|
|
|
|
;;
|
|
|
|
;; GnuPG 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.
|
|
|
|
;;
|
|
|
|
;; GnuPG 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/>.
|
|
|
|
|
|
|
|
(macro (assert form)
|
2016-12-19 15:28:07 +01:00
|
|
|
(let ((tag (get-tag form)))
|
|
|
|
`(if (not ,(cadr form))
|
2017-02-17 10:43:20 +01:00
|
|
|
(throw ,(if (and (pair? tag) (string? (car tag)) (number? (cdr tag)))
|
2016-12-19 15:28:07 +01:00
|
|
|
`(string-append ,(car tag) ":"
|
|
|
|
,(number->string (+ 1 (cdr tag)))
|
|
|
|
": Assertion failed: ")
|
|
|
|
"Assertion failed: ")
|
|
|
|
(quote ,(cadr form))))))
|
2016-01-06 11:55:25 +01:00
|
|
|
(assert #t)
|
2016-12-19 15:28:07 +01:00
|
|
|
(assert (not #f))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
2017-04-19 16:09:44 +02:00
|
|
|
;; Trace displays and returns the given value. A debugging aid.
|
|
|
|
(define (trace x)
|
|
|
|
(display x)
|
|
|
|
(newline)
|
|
|
|
x)
|
|
|
|
|
|
|
|
;; Stringification.
|
|
|
|
(define (stringify expression)
|
|
|
|
(let ((p (open-output-string)))
|
|
|
|
(write expression p)
|
|
|
|
(get-output-string p)))
|
|
|
|
|
2016-01-06 11:55:25 +01:00
|
|
|
(define (filter pred lst)
|
|
|
|
(cond ((null? lst) '())
|
|
|
|
((pred (car lst))
|
|
|
|
(cons (car lst) (filter pred (cdr lst))))
|
|
|
|
(else (filter pred (cdr lst)))))
|
|
|
|
|
|
|
|
(define (any p l)
|
|
|
|
(cond ((null? l) #f)
|
|
|
|
((p (car l)) #t)
|
|
|
|
(else (any p (cdr l)))))
|
|
|
|
|
|
|
|
(define (all p l)
|
|
|
|
(cond ((null? l) #t)
|
|
|
|
((not (p (car l))) #f)
|
|
|
|
(else (all p (cdr l)))))
|
|
|
|
|
2016-11-03 14:37:15 +01:00
|
|
|
;; Return the first element of a list.
|
|
|
|
(define first car)
|
|
|
|
|
|
|
|
;; Return the last element of a list.
|
|
|
|
(define (last lst)
|
|
|
|
(if (null? (cdr lst))
|
|
|
|
(car lst)
|
|
|
|
(last (cdr lst))))
|
|
|
|
|
|
|
|
;; Compute the powerset of a list.
|
|
|
|
(define (powerset set)
|
|
|
|
(if (null? set)
|
|
|
|
'(())
|
|
|
|
(let ((rst (powerset (cdr set))))
|
|
|
|
(append (map (lambda (x) (cons (car set) x))
|
|
|
|
rst)
|
|
|
|
rst))))
|
|
|
|
|
2016-01-06 11:55:25 +01:00
|
|
|
;; Is PREFIX a prefix of S?
|
|
|
|
(define (string-prefix? s prefix)
|
|
|
|
(and (>= (string-length s) (string-length prefix))
|
|
|
|
(string=? prefix (substring s 0 (string-length prefix)))))
|
|
|
|
(assert (string-prefix? "Scheme" "Sch"))
|
|
|
|
|
|
|
|
;; Is SUFFIX a suffix of S?
|
|
|
|
(define (string-suffix? s suffix)
|
|
|
|
(and (>= (string-length s) (string-length suffix))
|
|
|
|
(string=? suffix (substring s (- (string-length s)
|
|
|
|
(string-length suffix))
|
|
|
|
(string-length s)))))
|
|
|
|
(assert (string-suffix? "Scheme" "eme"))
|
|
|
|
|
2016-06-21 12:12:56 +02:00
|
|
|
;; Locate the first occurrence of needle in haystack starting at offset.
|
|
|
|
(ffi-define (string-index haystack needle [offset]))
|
|
|
|
(assert (= 2 (string-index "Hallo" #\l)))
|
|
|
|
(assert (= 3 (string-index "Hallo" #\l 3)))
|
|
|
|
(assert (equal? #f (string-index "Hallo" #\.)))
|
|
|
|
|
|
|
|
;; Locate the last occurrence of needle in haystack starting at offset.
|
|
|
|
(ffi-define (string-rindex haystack needle [offset]))
|
|
|
|
(assert (= 3 (string-rindex "Hallo" #\l)))
|
|
|
|
(assert (equal? #f (string-rindex "Hallo" #\a 2)))
|
|
|
|
(assert (equal? #f (string-rindex "Hallo" #\.)))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
2016-11-16 12:02:03 +01:00
|
|
|
;; Split HAYSTACK at each character that makes PREDICATE true at most
|
|
|
|
;; N times.
|
|
|
|
(define (string-split-pln haystack predicate lookahead n)
|
2016-06-21 12:12:56 +02:00
|
|
|
(let ((length (string-length haystack)))
|
2016-11-16 12:02:03 +01:00
|
|
|
(define (split acc offset n)
|
2016-06-21 12:12:56 +02:00
|
|
|
(if (>= offset length)
|
2017-04-04 12:02:54 +02:00
|
|
|
(reverse! acc)
|
2016-11-16 12:02:03 +01:00
|
|
|
(let ((i (lookahead haystack offset)))
|
2016-06-21 12:12:56 +02:00
|
|
|
(if (or (eq? i #f) (= 0 n))
|
2017-04-04 12:02:54 +02:00
|
|
|
(reverse! (cons (substring haystack offset length) acc))
|
2016-06-21 12:12:56 +02:00
|
|
|
(split (cons (substring haystack offset i) acc)
|
2016-11-16 12:02:03 +01:00
|
|
|
(+ i 1) (- n 1))))))
|
|
|
|
(split '() 0 n)))
|
|
|
|
|
|
|
|
(define (string-indexp haystack offset predicate)
|
|
|
|
(cond
|
|
|
|
((= (string-length haystack) offset)
|
|
|
|
#f)
|
|
|
|
((predicate (string-ref haystack offset))
|
|
|
|
offset)
|
|
|
|
(else
|
|
|
|
(string-indexp haystack (+ 1 offset) predicate))))
|
|
|
|
|
|
|
|
;; Split HAYSTACK at each character that makes PREDICATE true at most
|
|
|
|
;; N times.
|
|
|
|
(define (string-splitp haystack predicate n)
|
|
|
|
(string-split-pln haystack predicate
|
|
|
|
(lambda (haystack offset)
|
|
|
|
(string-indexp haystack offset predicate))
|
|
|
|
n))
|
|
|
|
(assert (equal? '("a" "b") (string-splitp "a b" char-whitespace? -1)))
|
|
|
|
(assert (equal? '("a" "b") (string-splitp "a\tb" char-whitespace? -1)))
|
|
|
|
(assert (equal? '("a" "" "b") (string-splitp "a \tb" char-whitespace? -1)))
|
|
|
|
|
|
|
|
;; Split haystack at delimiter at most n times.
|
|
|
|
(define (string-splitn haystack delimiter n)
|
|
|
|
(string-split-pln haystack
|
|
|
|
(lambda (c) (char=? c delimiter))
|
|
|
|
(lambda (haystack offset)
|
|
|
|
(string-index haystack delimiter offset))
|
|
|
|
n))
|
2016-06-21 12:12:56 +02:00
|
|
|
(assert (= 2 (length (string-splitn "foo:bar:baz" #\: 1))))
|
|
|
|
(assert (string=? "foo" (car (string-splitn "foo:bar:baz" #\: 1))))
|
|
|
|
(assert (string=? "bar:baz" (cadr (string-splitn "foo:bar:baz" #\: 1))))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
|
|
|
;; Split haystack at delimiter.
|
|
|
|
(define (string-split haystack delimiter)
|
|
|
|
(string-splitn haystack delimiter -1))
|
2016-06-21 12:12:56 +02:00
|
|
|
(assert (= 3 (length (string-split "foo:bar:baz" #\:))))
|
|
|
|
(assert (string=? "foo" (car (string-split "foo:bar:baz" #\:))))
|
|
|
|
(assert (string=? "bar" (cadr (string-split "foo:bar:baz" #\:))))
|
|
|
|
(assert (string=? "baz" (caddr (string-split "foo:bar:baz" #\:))))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
2016-10-07 16:16:15 +02:00
|
|
|
;; Split haystack at newlines.
|
|
|
|
(define (string-split-newlines haystack)
|
|
|
|
(if *win32*
|
|
|
|
(map (lambda (line) (if (string-suffix? line "\r")
|
|
|
|
(substring line 0 (- (string-length line) 1))
|
|
|
|
line))
|
|
|
|
(string-split haystack #\newline))
|
|
|
|
(string-split haystack #\newline)))
|
|
|
|
|
2016-01-06 11:55:25 +01:00
|
|
|
;; Trim the prefix of S containing only characters that make PREDICATE
|
2016-06-21 12:12:56 +02:00
|
|
|
;; true.
|
2016-01-06 11:55:25 +01:00
|
|
|
(define (string-ltrim predicate s)
|
2016-11-16 12:02:03 +01:00
|
|
|
(if (string=? s "")
|
|
|
|
""
|
|
|
|
(let loop ((s' (string->list s)))
|
|
|
|
(if (predicate (car s'))
|
|
|
|
(loop (cdr s'))
|
|
|
|
(list->string s')))))
|
|
|
|
(assert (string=? "" (string-ltrim char-whitespace? "")))
|
2016-06-21 12:12:56 +02:00
|
|
|
(assert (string=? "foo" (string-ltrim char-whitespace? " foo")))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
|
|
|
;; Trim the suffix of S containing only characters that make PREDICATE
|
|
|
|
;; true.
|
|
|
|
(define (string-rtrim predicate s)
|
2016-11-16 12:02:03 +01:00
|
|
|
(if (string=? s "")
|
|
|
|
""
|
2017-04-04 12:02:54 +02:00
|
|
|
(let loop ((s' (reverse! (string->list s))))
|
2016-11-16 12:02:03 +01:00
|
|
|
(if (predicate (car s'))
|
|
|
|
(loop (cdr s'))
|
2017-04-04 12:02:54 +02:00
|
|
|
(list->string (reverse! s'))))))
|
2016-11-16 12:02:03 +01:00
|
|
|
(assert (string=? "" (string-rtrim char-whitespace? "")))
|
2016-06-21 12:12:56 +02:00
|
|
|
(assert (string=? "foo" (string-rtrim char-whitespace? "foo ")))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
|
|
|
;; Trim both the prefix and suffix of S containing only characters
|
|
|
|
;; that make PREDICATE true.
|
|
|
|
(define (string-trim predicate s)
|
|
|
|
(string-ltrim predicate (string-rtrim predicate s)))
|
2016-11-16 12:02:03 +01:00
|
|
|
(assert (string=? "" (string-trim char-whitespace? "")))
|
2016-06-21 12:12:56 +02:00
|
|
|
(assert (string=? "foo" (string-trim char-whitespace? " foo ")))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
2016-06-21 12:12:56 +02:00
|
|
|
;; Check if needle is contained in haystack.
|
|
|
|
(ffi-define (string-contains? haystack needle))
|
|
|
|
(assert (string-contains? "Hallo" "llo"))
|
|
|
|
(assert (not (string-contains? "Hallo" "olla")))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
2017-04-18 18:51:06 +02:00
|
|
|
;; Translate characters.
|
|
|
|
(define (string-translate s from to)
|
|
|
|
(list->string (map (lambda (c)
|
|
|
|
(let ((i (string-index from c)))
|
|
|
|
(if i (string-ref to i) c))) (string->list s))))
|
|
|
|
(assert (equal? (string-translate "foo/bar" "/" ".") "foo.bar"))
|
|
|
|
|
2016-01-06 11:55:25 +01:00
|
|
|
;; Read a word from port P.
|
|
|
|
(define (read-word . p)
|
|
|
|
(list->string
|
|
|
|
(let f ()
|
|
|
|
(let ((c (apply peek-char p)))
|
|
|
|
(cond
|
|
|
|
((eof-object? c) '())
|
|
|
|
((char-alphabetic? c)
|
|
|
|
(apply read-char p)
|
|
|
|
(cons c (f)))
|
|
|
|
(else
|
|
|
|
(apply read-char p)
|
|
|
|
'()))))))
|
|
|
|
|
2016-11-16 12:02:03 +01:00
|
|
|
(define (list->string-reversed lst)
|
|
|
|
(let* ((len (length lst))
|
|
|
|
(str (make-string len)))
|
|
|
|
(let loop ((i (- len 1))
|
|
|
|
(l lst))
|
|
|
|
(if (< i 0)
|
|
|
|
(begin
|
|
|
|
(assert (null? l))
|
|
|
|
str)
|
|
|
|
(begin
|
|
|
|
(string-set! str i (car l))
|
|
|
|
(loop (- i 1) (cdr l)))))))
|
|
|
|
|
2016-01-06 11:55:25 +01:00
|
|
|
;; Read a line from port P.
|
|
|
|
(define (read-line . p)
|
2016-11-16 12:02:03 +01:00
|
|
|
(let loop ((acc '()))
|
|
|
|
(let ((c (apply peek-char p)))
|
|
|
|
(cond
|
|
|
|
((eof-object? c)
|
|
|
|
(if (null? acc)
|
|
|
|
c ;; #eof
|
|
|
|
(list->string-reversed acc)))
|
|
|
|
((char=? c #\newline)
|
|
|
|
(apply read-char p)
|
|
|
|
(list->string-reversed acc))
|
|
|
|
(else
|
|
|
|
(apply read-char p)
|
|
|
|
(loop (cons c acc)))))))
|
2016-01-06 11:55:25 +01:00
|
|
|
|
|
|
|
;; Read everything from port P.
|
|
|
|
(define (read-all . p)
|
2016-06-21 12:12:56 +02:00
|
|
|
(let loop ((acc (open-output-string)))
|
|
|
|
(let ((c (apply peek-char p)))
|
|
|
|
(cond
|
|
|
|
((eof-object? c) (get-output-string acc))
|
|
|
|
(else
|
|
|
|
(write-char (apply read-char p) acc)
|
|
|
|
(loop acc))))))
|
2016-11-07 12:21:26 +01:00
|
|
|
|
2016-11-07 13:12:01 +01:00
|
|
|
;;
|
|
|
|
;; Windows support.
|
|
|
|
;;
|
|
|
|
|
|
|
|
;; Like call-with-input-file but opens the file in 'binary' mode.
|
|
|
|
(define (call-with-binary-input-file filename proc)
|
|
|
|
(letfd ((fd (open filename (logior O_RDONLY O_BINARY))))
|
|
|
|
(proc (fdopen fd "rb"))))
|
|
|
|
|
|
|
|
;; Like call-with-output-file but opens the file in 'binary' mode.
|
|
|
|
(define (call-with-binary-output-file filename proc)
|
|
|
|
(letfd ((fd (open filename (logior O_WRONLY O_CREAT O_BINARY) #o600)))
|
|
|
|
(proc (fdopen fd "wb"))))
|
|
|
|
|
2016-11-07 12:21:26 +01:00
|
|
|
;;
|
|
|
|
;; Libc functions.
|
|
|
|
;;
|
|
|
|
|
2016-11-08 15:11:12 +01:00
|
|
|
;; Change the read/write offset.
|
|
|
|
(ffi-define (seek fd offset whence))
|
|
|
|
|
|
|
|
;; Constants for WHENCE.
|
|
|
|
(ffi-define SEEK_SET)
|
|
|
|
(ffi-define SEEK_CUR)
|
|
|
|
(ffi-define SEEK_END)
|
|
|
|
|
2016-11-07 12:21:26 +01:00
|
|
|
;; Get our process id.
|
|
|
|
(ffi-define (getpid))
|
|
|
|
|
2016-11-07 17:40:43 +01:00
|
|
|
;; Copy data from file descriptor SOURCE to every file descriptor in
|
|
|
|
;; SINKS.
|
|
|
|
(ffi-define (splice source . sinks))
|
2016-11-07 16:59:15 +01:00
|
|
|
|
2016-11-07 12:21:26 +01:00
|
|
|
;;
|
|
|
|
;; Random numbers.
|
|
|
|
;;
|
|
|
|
|
|
|
|
;; Seed the random number generator.
|
|
|
|
(ffi-define (srandom seed))
|
|
|
|
|
|
|
|
;; Get a pseudo-random number between 0 (inclusive) and SCALE
|
|
|
|
;; (exclusive).
|
|
|
|
(ffi-define (random scale))
|
|
|
|
|
|
|
|
;; Create a string of the given SIZE containing pseudo-random data.
|
|
|
|
(ffi-define (make-random-string size))
|