#!/bin/sh
exec guile -q -s "$0" "$@"
!#

(use-modules (system foreign)
             (rnrs bytevector))

(define lib
  (dynamic-link (string-append (getenv "builddir") "/libtest-ffi")))

(define-syntax test
  (syntax-rules ()
    ((_ exp res)
     (let ((expected res)
           (actual exp))
       (if (not (equal? actual expected))
           (error "Bad return from expression" 'exp actual expected))))))

;;;
;;; No args
;;;
(define f-v-
  (make-foreign-function void (dynamic-func "test_ffi_v_" lib) '()))
(test (f-v-) *unspecified*)

(define f-s8-
  (make-foreign-function int8 (dynamic-func "test_ffi_s8_" lib) '()))
(test (f-s8-) -100)

(define f-u8-
  (make-foreign-function uint8 (dynamic-func "test_ffi_u8_" lib) '()))
(test (f-u8-) 200)

(define f-s16-
  (make-foreign-function int16 (dynamic-func "test_ffi_s16_" lib) '()))
(test (f-s16-) -20000)

(define f-u16-
  (make-foreign-function uint16 (dynamic-func "test_ffi_u16_" lib) '()))
(test (f-u16-) 40000)

(define f-s32-
  (make-foreign-function int32 (dynamic-func "test_ffi_s32_" lib) '()))
(test (f-s32-) -2000000000)

(define f-u32-
  (make-foreign-function uint32 (dynamic-func "test_ffi_u32_" lib) '()))
(test (f-u32-) 4000000000)

(define f-s64-
  (make-foreign-function int64 (dynamic-func "test_ffi_s64_" lib) '()))
(test (f-s64-) -2000000000)

(define f-u64-
  (make-foreign-function uint64 (dynamic-func "test_ffi_u64_" lib) '()))
(test (f-u64-) 4000000000)

;;;
;;; One u8 arg
;;;
(define f-v-u8
  (make-foreign-function void (dynamic-func "test_ffi_v_u8" lib) (list uint8)))
(test (f-v-u8 10) *unspecified*)

(define f-s8-u8
  (make-foreign-function int8 (dynamic-func "test_ffi_s8_u8" lib) (list uint8)))
(test (f-s8-u8 10) -90)

(define f-u8-u8
  (make-foreign-function uint8 (dynamic-func "test_ffi_u8_u8" lib) (list uint8)))
(test (f-u8-u8 10) 210)

(define f-s16-u8
  (make-foreign-function int16 (dynamic-func "test_ffi_s16_u8" lib) (list uint8)))
(test (f-s16-u8 10) -19990)

(define f-u16-u8
  (make-foreign-function uint16 (dynamic-func "test_ffi_u16_u8" lib) (list uint8)))
(test (f-u16-u8 10) 40010)

(define f-s32-u8
  (make-foreign-function int32 (dynamic-func "test_ffi_s32_u8" lib) (list uint8)))
(test (f-s32-u8 10) -1999999990)

(define f-u32-u8
  (make-foreign-function uint32 (dynamic-func "test_ffi_u32_u8" lib) (list uint8)))
(test (f-u32-u8 10) 4000000010)

(define f-s64-u8
  (make-foreign-function int64 (dynamic-func "test_ffi_s64_u8" lib) (list uint8)))
(test (f-s64-u8 10) -1999999990)

(define f-u64-u8
  (make-foreign-function uint64 (dynamic-func "test_ffi_u64_u8" lib) (list uint8)))
(test (f-u64-u8 10) 4000000010)


;;;
;;; One s64 arg
;;;
(define f-v-s64
  (make-foreign-function void (dynamic-func "test_ffi_v_s64" lib) (list int64)))
(test (f-v-s64 10) *unspecified*)

(define f-s8-s64
  (make-foreign-function int8 (dynamic-func "test_ffi_s8_s64" lib) (list int64)))
(test (f-s8-s64 10) -90)

(define f-u8-s64
  (make-foreign-function uint8 (dynamic-func "test_ffi_u8_s64" lib) (list int64)))
(test (f-u8-s64 10) 210)

(define f-s16-s64
  (make-foreign-function int16 (dynamic-func "test_ffi_s16_s64" lib) (list int64)))
(test (f-s16-s64 10) -19990)

(define f-u16-s64
  (make-foreign-function uint16 (dynamic-func "test_ffi_u16_s64" lib) (list int64)))
(test (f-u16-s64 10) 40010)

(define f-s32-s64
  (make-foreign-function int32 (dynamic-func "test_ffi_s32_s64" lib) (list int64)))
(test (f-s32-s64 10) -1999999990)

(define f-u32-s64
  (make-foreign-function uint32 (dynamic-func "test_ffi_u32_s64" lib) (list int64)))
(test (f-u32-s64 10) 4000000010)

(define f-s64-s64
  (make-foreign-function int64 (dynamic-func "test_ffi_s64_s64" lib) (list int64)))
(test (f-s64-s64 10) -1999999990)

(define f-u64-s64
  (make-foreign-function uint64 (dynamic-func "test_ffi_u64_s64" lib) (list int64)))
(test (f-u64-s64 10) 4000000010)


;;
;; Multiple int args of differing types
;;
(define f-sum
  (make-foreign-function int64 (dynamic-func "test_ffi_sum" lib)
                         (list int8 int16 int32 int64)))
(test (f-sum -1 2000 -30000 40000000000)
      (+ -1 2000 -30000 40000000000))

;;
;; Structs
;;
(define f-sum-struct
  (make-foreign-function int64 (dynamic-func "test_ffi_sum_struct" lib)
                         (list (list int8 int16 int32 int64))))
(test (f-sum-struct (make-c-struct (list int8 int16 int32 int64)
                                   (list -1 2000 -30000 40000000000)))
      (+ -1 2000 -30000 40000000000))
;;
;; Structs
;;
(define f-memcpy
  (make-foreign-function '* (dynamic-func "test_ffi_memcpy" lib)
                         (list '* '* int32)))
(let* ((src (bytevector->foreign (u8-list->bytevector '(0 1 2 3 4 5 6 7))))
       (dest (bytevector->foreign (make-bytevector 16 0)))
       (res (f-memcpy dest src (bytevector-length (foreign->bytevector src)))))
  (or (= (foreign-ref dest) (foreign-ref res))
      (error "memcpy res not equal to dest"))
  (or (equal? (bytevector->u8-list (foreign->bytevector dest))
              '(0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0))
      (error "unexpected dest")))


;;;
;;; Global symbols.
;;;

(use-modules ((rnrs bytevector) #:select (utf8->string)))

(if (defined? 'setlocale)
    (setlocale LC_ALL "C"))

(define global (dynamic-link))

(define strerror
  (make-foreign-function '* (dynamic-func "strerror" global)
                         (list int)))

(define strlen
  (make-foreign-function size_t (dynamic-func "strlen" global)
                         (list '*)))

(let* ((ptr (strerror ENOENT))
       (len (strlen ptr))
       (bv  (foreign->bytevector ptr 'u8 0 len))
       (str (utf8->string bv)))
  (test #t (not (not (string-contains str "file")))))

;; Local Variables:
;; mode: scheme
;; End: