[Larceny-users] RFC: FFI usage for getting process's environment variables

Derick Eddington derick.eddington at gmail.com
Tue Apr 7 08:38:13 EDT 2009


I've implemented SRFI 98: An Interface to Access Environment Variables.
I've used the FFI in order to access the C environ variable and to do my
own transcoding of strings.  This is my first time using Larceny's FFI,
so I would like to know if what I've done is correct or if it could be
done better, and I have some other questions (noted in the source code).
Also, attached is a little test program; it works for me.

Also, the document at doc/LarcenyNotes/note7-ffi.html says of ffi/dlsym
"handle can be #f, which means that the symbol will be resolved in the
symbol table of the running program", but that's no longer true; is this
intentional?


(library (srfi :98 os-environment-variables)
  (export
    get-environment-variable get-environment-variables)
  (import
    (rnrs base)
    (rnrs control)
    (rnrs bytevectors)
    (rnrs io ports)
    (primitives
     foreign-procedure #;foreign-variable foreign-null-pointer? sizeof:pointer
     %peek-pointer %peek8u void*->address ffi/dlopen ffi/dlsym))

  ;; TODO: Will the convenient string converters use the native transcoder in
  ;;       the future?  So that scheme-str->c-str-bv and c-str-ptr->scheme-str
  ;;       won't be needed.

  (define (scheme-str->c-str-bv x)
    (let* ((bv (string->bytevector x (native-transcoder)))
           (len (bytevector-length bv))
           (bv/z (make-bytevector (+ 1 len))))
      (bytevector-copy! bv 0 bv/z 0 len)
      (bytevector-u8-set! bv/z len 0)
      bv/z))

  (define (c-str-ptr->scheme-str x)
    (let loop ((x x) (a '()))
      (let ((b (%peek8u x)))
        (if (zero? b)
          (bytevector->string (u8-list->bytevector (reverse a))
                              (native-transcoder))
          (loop (+ 1 x) (cons b a))))))
  
  (define getenv
    (foreign-procedure "getenv" '(boxed) 'void*))
  
  (define (get-environment-variable name) 
    (unless (string? name)
      (assertion-violation 'get-environment-variable "not a string" name))
    (let ((p (getenv (scheme-str->c-str-bv name))))
      (and p
           (c-str-ptr->scheme-str (void*->address p)))))

  ;; TODO: Will foreign-variable support a pointer type in the future?
  ;;       Would this be the correct way to use it?
  #;(define environ
      (foreign-variable "environ" 'void*))

  ;; TODO: Is (ffi/dlopen "") okay?  It works for me.
  (define environ
    (%peek-pointer (ffi/dlsym (ffi/dlopen "") "environ")))

  (define (get-environment-variables)
    (define (entry->pair x) 
      (let* ((s (c-str-ptr->scheme-str x))
             (len (string-length s)))
        (let loop ((i 0))
          (if (< i len)
            (if (char=? #\= (string-ref s i))
              (cons (substring s 0 i)
                    (substring s (+ 1 i) len))
              (loop (+ 1 i)))
            (cons s #F)))))
    (let loop ((e environ) (a '()))
      (let ((entry (%peek-pointer e)))
        (if (foreign-null-pointer? entry)
          a
          (loop (+ sizeof:pointer e)
                (cons (entry->pair entry) a))))))
)


-- 
: Derick
----------------------------------------------------------------
-------------- next part --------------
;; Copyright (c) 2009 Derick Eddington
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; Except as contained in this notice, the name(s) of the above copyright
;; holders shall not be used in advertising or otherwise to promote the sale,
;; use or other dealings in this Software without prior written authorization.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.

#!r6rs
(import
  (rename (rnrs) (for-all andmap))
  (srfi :98 os-environment-variables)
  (srfi :78 lightweight-testing))

(check (list? (get-environment-variables))
       => #T)
(check (andmap (lambda (a)
                 (and (pair? a)
                      (string? (car a))
                      (positive? (string-length (car a)))
                      (string? (cdr a))))
               (get-environment-variables))
       => #T)
(check (andmap (lambda (a)
                 (let ((v (get-environment-variable (car a))))
                   (and (string? v)
                        (string=? v (cdr a)))))
               (get-environment-variables))
       => #T)
(assert (not (assoc "BLAH" (get-environment-variables))))
(check (get-environment-variable "BLAH")
       => #F)

(check-report)


More information about the Larceny-users mailing list