grovel.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       grovel.lisp (36543B)
       ---
            1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
            2 ;;;
            3 ;;; grovel.lisp --- The CFFI Groveller.
            4 ;;;
            5 ;;; Copyright (C) 2005-2006, Dan Knap <dankna@accela.net>
            6 ;;; Copyright (C) 2005-2006, Emily Backes <lucca@accela.net>
            7 ;;; Copyright (C) 2007, Stelian Ionescu <sionescu@cddr.org>
            8 ;;; Copyright (C) 2007, Luis Oliveira <loliveira@common-lisp.net>
            9 ;;;
           10 ;;; Permission is hereby granted, free of charge, to any person
           11 ;;; obtaining a copy of this software and associated documentation
           12 ;;; files (the "Software"), to deal in the Software without
           13 ;;; restriction, including without limitation the rights to use, copy,
           14 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
           15 ;;; of the Software, and to permit persons to whom the Software is
           16 ;;; furnished to do so, subject to the following conditions:
           17 ;;;
           18 ;;; The above copyright notice and this permission notice shall be
           19 ;;; included in all copies or substantial portions of the Software.
           20 ;;;
           21 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
           22 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           23 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
           24 ;;; NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
           25 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
           26 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           27 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
           28 ;;; DEALINGS IN THE SOFTWARE.
           29 ;;;
           30 
           31 (in-package #:cffi-grovel)
           32 
           33 ;;;# Error Conditions
           34 
           35 (define-condition grovel-error (simple-error) ())
           36 
           37 (defun grovel-error (format-control &rest format-arguments)
           38   (error 'grovel-error
           39          :format-control format-control
           40          :format-arguments format-arguments))
           41 
           42 ;;; This warning is signalled when cffi-grovel can't find some macro.
           43 ;;; Signalled by CONSTANT or CONSTANTENUM.
           44 (define-condition missing-definition (warning)
           45   ((%name :initarg :name :reader name-of))
           46   (:report (lambda (condition stream)
           47              (format stream "No definition for ~A"
           48                      (name-of condition)))))
           49 
           50 ;;;# Grovelling
           51 
           52 ;;; The header of the intermediate C file.
           53 (defparameter *header*
           54   "/*
           55  * This file has been automatically generated by cffi-grovel.
           56  * Do not edit it by hand.
           57  */
           58 
           59 ")
           60 
           61 ;;; C code generated by cffi-grovel is inserted between the contents
           62 ;;; of *PROLOGUE* and *POSTSCRIPT*, inside the main function's body.
           63 
           64 (defparameter *prologue*
           65   "
           66 #include <grovel/common.h>
           67 
           68 int main(int argc, char**argv) {
           69   int autotype_tmp;
           70   FILE *output = argc > 1 ? fopen(argv[1], \"w\") : stdout;
           71   fprintf(output, \";;;; This file has been automatically generated by \"
           72                   \"cffi-grovel.\\n;;;; Do not edit it by hand.\\n\\n\");
           73 ")
           74 
           75 (defparameter *postscript*
           76   "
           77   if  (output != stdout)
           78     fclose(output);
           79   return 0;
           80 }
           81 ")
           82 
           83 (defun unescape-for-c (text)
           84   (with-output-to-string (result)
           85     (loop for i below (length text)
           86           for char = (char text i) do
           87           (cond ((eql char #\") (princ "\\\"" result))
           88                 ((eql char #\newline) (princ "\\n" result))
           89                 (t (princ char result))))))
           90 
           91 (defun c-format (out fmt &rest args)
           92   (let ((text (unescape-for-c (format nil "~?" fmt args))))
           93     (format out "~&  fputs(\"~A\", output);~%" text)))
           94 
           95 (defun c-printf (out fmt &rest args)
           96   (flet ((item (item)
           97            (format out "~A" (unescape-for-c (format nil item)))))
           98     (format out "~&  fprintf(output, \"")
           99     (item fmt)
          100     (format out "\"")
          101     (loop for arg in args do
          102           (format out ", ")
          103           (item arg))
          104     (format out ");~%")))
          105 
          106 (defun c-print-integer-constant (out arg &optional foreign-type)
          107   (let ((foreign-type (or foreign-type :int)))
          108     (c-format out "#.(cffi-grovel::convert-intmax-constant ")
          109     (format out "~&  fprintf(output, \"%\"PRIiMAX, (intmax_t)~A);~%"
          110             arg)
          111     (c-format out " ")
          112     (c-write out `(quote ,foreign-type))
          113     (c-format out ")")))
          114 
          115 ;;; TODO: handle packages in a better way. One way is to process each
          116 ;;; grovel form as it is read (like we already do for wrapper
          117 ;;; forms). This way in can expect *PACKAGE* to have sane values.
          118 ;;; This would require that "header forms" come before any other
          119 ;;; forms.
          120 (defun c-print-symbol (out symbol &optional no-package)
          121   (c-format out
          122             (let ((package (symbol-package symbol)))
          123               (cond
          124                 ((eq (find-package '#:keyword) package) ":~(~A~)")
          125                 (no-package "~(~A~)")
          126                 ((eq (find-package '#:cl) package) "cl:~(~A~)")
          127                 (t "~(~A~)")))
          128             symbol))
          129 
          130 (defun c-write (out form &optional no-package)
          131   (cond
          132     ((and (listp form)
          133           (eq 'quote (car form)))
          134      (c-format out "'")
          135      (c-write out (cadr form) no-package))
          136     ((listp form)
          137      (c-format out "(")
          138      (loop for subform in form
          139            for first-p = t then nil
          140            unless first-p do (c-format out " ")
          141         do (c-write out subform no-package))
          142      (c-format out ")"))
          143     ((symbolp form)
          144      (c-print-symbol out form no-package))))
          145 
          146 ;;; Always NIL for now, add {ENABLE,DISABLE}-AUTO-EXPORT grovel forms
          147 ;;; later, if necessary.
          148 (defvar *auto-export* nil)
          149 
          150 (defun c-export (out symbol)
          151   (when (and *auto-export* (not (keywordp symbol)))
          152     (c-format out "(cl:export '")
          153     (c-print-symbol out symbol t)
          154     (c-format out ")~%")))
          155 
          156 (defun c-section-header (out section-type section-symbol)
          157   (format out "~%  /* ~A section for ~S */~%"
          158           section-type
          159           section-symbol))
          160 
          161 (defun remove-suffix (string suffix)
          162   (let ((suffix-start (- (length string) (length suffix))))
          163     (if (and (> suffix-start 0)
          164              (string= string suffix :start1 suffix-start))
          165         (subseq string 0 suffix-start)
          166         string)))
          167 
          168 (defgeneric %process-grovel-form (name out arguments)
          169   (:method (name out arguments)
          170     (declare (ignore out arguments))
          171     (grovel-error "Unknown Grovel syntax: ~S" name)))
          172 
          173 (defun process-grovel-form (out form)
          174   (%process-grovel-form (form-kind form) out (cdr form)))
          175 
          176 (defun form-kind (form)
          177   ;; Using INTERN here instead of FIND-SYMBOL will result in less
          178   ;; cryptic error messages when an undefined grovel/wrapper form is
          179   ;; found.
          180   (intern (symbol-name (car form)) '#:cffi-grovel))
          181 
          182 (defvar *header-forms* '(c include define flag typedef))
          183 
          184 (defun header-form-p (form)
          185   (member (form-kind form) *header-forms*))
          186 
          187 (defun generate-c-file (input-file output-defaults)
          188   (nest
          189    (with-standard-io-syntax)
          190    (let ((c-file (make-c-file-name output-defaults "__grovel"))
          191          (*print-readably* nil)
          192          (*print-escape* t)))
          193    (with-open-file (out c-file :direction :output :if-exists :supersede))
          194    (with-open-file (in input-file :direction :input))
          195    (flet ((read-forms (s)
          196             (do ((forms ())
          197                  (form (read s nil nil) (read s nil nil)))
          198                 ((null form) (nreverse forms))
          199               (labels
          200                   ((process-form (f)
          201                      (case (form-kind f)
          202                        (flag (warn "Groveler clause FLAG is deprecated, use CC-FLAGS instead.")))
          203                      (case (form-kind f)
          204                        (in-package
          205                         (setf *package* (find-package (second f)))
          206                         (push f forms))
          207                        (progn
          208                          ;; flatten progn forms
          209                          (mapc #'process-form (rest f)))
          210                        (t (push f forms)))))
          211                 (process-form form))))))
          212    (let* ((forms (read-forms in))
          213           (header-forms (remove-if-not #'header-form-p forms))
          214           (body-forms (remove-if #'header-form-p forms)))
          215      (write-string *header* out)
          216      (dolist (form header-forms)
          217        (process-grovel-form out form))
          218      (write-string *prologue* out)
          219      (dolist (form body-forms)
          220        (process-grovel-form out form))
          221      (write-string *postscript* out)
          222      c-file)))
          223 
          224 (defun tmp-lisp-file-name (defaults)
          225   (make-pathname :name (strcat (pathname-name defaults) ".grovel-tmp")
          226                  :type "lisp" :defaults defaults))
          227 
          228 
          229 
          230 ;;; *PACKAGE* is rebound so that the IN-PACKAGE form can set it during
          231 ;;; *the extent of a given grovel file.
          232 (defun process-grovel-file (input-file &optional (output-defaults input-file))
          233   (with-standard-io-syntax
          234     (let* ((c-file (generate-c-file input-file output-defaults))
          235            (o-file (make-o-file-name c-file))
          236            (exe-file (make-exe-file-name c-file))
          237            (lisp-file (tmp-lisp-file-name c-file))
          238            (inputs (list (cc-include-grovel-argument) c-file)))
          239       (handler-case
          240           (progn
          241             ;; at least MKCL wants to separate compile and link
          242             (cc-compile o-file inputs)
          243             (link-executable exe-file (list o-file)))
          244         (error (e)
          245           (grovel-error "~a" e)))
          246       (invoke exe-file lisp-file)
          247       lisp-file)))
          248 
          249 ;;; OUT is lexically bound to the output stream within BODY.
          250 (defmacro define-grovel-syntax (name lambda-list &body body)
          251   (with-unique-names (name-var args)
          252     `(defmethod %process-grovel-form ((,name-var (eql ',name)) out ,args)
          253        (declare (ignorable out))
          254        (destructuring-bind ,lambda-list ,args
          255          ,@body))))
          256 
          257 (define-grovel-syntax c (body)
          258   (format out "~%~A~%" body))
          259 
          260 (define-grovel-syntax include (&rest includes)
          261   (format out "~{#include <~A>~%~}" includes))
          262 
          263 (define-grovel-syntax define (name &optional value)
          264   (format out "#define ~A~@[ ~A~]~%" name value))
          265 
          266 (define-grovel-syntax typedef (base-type new-type)
          267   (format out "typedef ~A ~A;~%" base-type new-type))
          268 
          269 ;;; Is this really needed?
          270 (define-grovel-syntax ffi-typedef (new-type base-type)
          271   (c-format out "(cffi:defctype ~S ~S)~%" new-type base-type))
          272 
          273 (define-grovel-syntax flag (&rest flags)
          274   (appendf *cc-flags* (parse-command-flags-list flags)))
          275 
          276 (define-grovel-syntax cc-flags (&rest flags)
          277   (appendf *cc-flags* (parse-command-flags-list flags)))
          278 
          279 (define-grovel-syntax pkg-config-cflags (pkg &key optional)
          280   (let ((output-stream (make-string-output-stream))
          281         (program+args (list "pkg-config" pkg "--cflags")))
          282     (format *debug-io* "~&;~{ ~a~}~%" program+args)
          283     (handler-case
          284         (progn
          285           (run-program program+args
          286                        :output (make-broadcast-stream output-stream *debug-io*)
          287                        :error-output output-stream)
          288           (appendf *cc-flags*
          289                    (parse-command-flags (get-output-stream-string output-stream))))
          290       (error (e)
          291         (let ((message (format nil "~a~&~%~a~&"
          292                                e (get-output-stream-string output-stream))))
          293           (cond (optional
          294                  (format *debug-io* "~&; ERROR: ~a" message)
          295                  (format *debug-io* "~&~%; Attempting to continue anyway.~%"))
          296                 (t
          297                  (grovel-error "~a" message))))))))
          298 
          299 ;;; This form also has some "read time" effects. See GENERATE-C-FILE.
          300 (define-grovel-syntax in-package (name)
          301   (c-format out "(cl:in-package #:~A)~%~%" name))
          302 
          303 (define-grovel-syntax ctype (lisp-name size-designator)
          304   (c-section-header out "ctype" lisp-name)
          305   (c-export out lisp-name)
          306   (c-format out "(cffi:defctype ")
          307   (c-print-symbol out lisp-name t)
          308   (c-format out " ")
          309   (format out "~&  type_name(output, TYPE_SIGNED_P(~A), ~:[sizeof(~A)~;~D~]);~%"
          310           size-designator
          311           (etypecase size-designator
          312             (string nil)
          313             (integer t))
          314           size-designator)
          315   (c-format out ")~%")
          316   (unless (keywordp lisp-name)
          317     (c-export out lisp-name))
          318   (let ((size-of-constant-name (symbolicate '#:size-of- lisp-name)))
          319     (c-export out size-of-constant-name)
          320     (c-format out "(cl:defconstant "
          321               size-of-constant-name lisp-name)
          322     (c-print-symbol out size-of-constant-name)
          323     (c-format out " (cffi:foreign-type-size '")
          324     (c-print-symbol out lisp-name)
          325     (c-format out "))~%")))
          326 
          327 ;;; Syntax differs from anything else in CFFI.  Fix?
          328 (define-grovel-syntax constant ((lisp-name &rest c-names)
          329                                 &key (type 'integer) documentation optional)
          330   (when (keywordp lisp-name)
          331     (setf lisp-name (format-symbol "~A" lisp-name)))
          332   (c-section-header out "constant" lisp-name)
          333   (dolist (c-name c-names)
          334     (format out "~&#ifdef ~A~%" c-name)
          335     (c-export out lisp-name)
          336     (c-format out "(cl:defconstant ")
          337     (c-print-symbol out lisp-name t)
          338     (c-format out " ")
          339     (ecase type
          340       (integer
          341        (format out "~&  if(_64_BIT_VALUE_FITS_SIGNED_P(~A))~%" c-name)
          342        (format out "    fprintf(output, \"%lli\", (long long signed) ~A);" c-name)
          343        (format out "~&  else~%")
          344        (format out "    fprintf(output, \"%llu\", (long long unsigned) ~A);" c-name))
          345       (double-float
          346        (format out "~&  fprintf(output, \"%s\", print_double_for_lisp((double)~A));~%" c-name)))
          347     (when documentation
          348       (c-format out " ~S" documentation))
          349     (c-format out ")~%")
          350     (format out "~&#else~%"))
          351   (unless optional
          352     (c-format out "(cl:warn 'cffi-grovel:missing-definition :name '~A)~%"
          353               lisp-name))
          354   (dotimes (i (length c-names))
          355     (format out "~&#endif~%")))
          356 
          357 (define-grovel-syntax feature (lisp-feature-name c-name &key (feature-list 'cl:*features*))
          358   (c-section-header out "feature" lisp-feature-name)
          359   (format out "~&#ifdef ~A~%" c-name)
          360   (c-format out "(cl:pushnew '")
          361   (c-print-symbol out lisp-feature-name t)
          362   (c-format out " ")
          363   (c-print-symbol out feature-list)
          364   (c-format out ")~%")
          365   (format out "~&#endif~%"))
          366 
          367 (define-grovel-syntax cunion (union-lisp-name union-c-name &rest slots)
          368   (let ((documentation (when (stringp (car slots)) (pop slots))))
          369     (c-section-header out "cunion" union-lisp-name)
          370     (c-export out union-lisp-name)
          371     (dolist (slot slots)
          372       (let ((slot-lisp-name (car slot)))
          373         (c-export out slot-lisp-name)))
          374     (c-format out "(cffi:defcunion (")
          375     (c-print-symbol out union-lisp-name t)
          376     (c-printf out " :size %llu)" (format nil "(long long unsigned) sizeof(~A)" union-c-name))
          377     (when documentation
          378       (c-format out "~%  ~S" documentation))
          379     (dolist (slot slots)
          380       (destructuring-bind (slot-lisp-name slot-c-name &key type count)
          381           slot
          382         (declare (ignore slot-c-name))
          383         (c-format out "~%  (")
          384         (c-print-symbol out slot-lisp-name t)
          385         (c-format out " ")
          386         (c-write out type)
          387         (etypecase count
          388           (integer
          389            (c-format out " :count ~D" count))
          390           ((eql :auto)
          391            ;; nb, works like :count :auto does in cstruct below
          392            (c-printf out " :count %llu"
          393                      (format nil "(long long unsigned) sizeof(~A)" union-c-name)))
          394           (null t))
          395         (c-format out ")")))
          396     (c-format out ")~%")))
          397 
          398 (defun make-from-pointer-function-name (type-name)
          399   (symbolicate '#:make- type-name '#:-from-pointer))
          400 
          401 ;;; DEFINE-C-STRUCT-WRAPPER (in ../src/types.lisp) seems like a much
          402 ;;; cleaner way to do this.  Unless I can find any advantage in doing
          403 ;;; it this way I'll delete this soon.  --luis
          404 (define-grovel-syntax cstruct-and-class-item (&rest arguments)
          405   (process-grovel-form out (cons 'cstruct arguments))
          406   (destructuring-bind (struct-lisp-name struct-c-name &rest slots)
          407       arguments
          408     (declare (ignore struct-c-name))
          409     (let* ((slot-names (mapcar #'car slots))
          410            (reader-names (mapcar
          411                           (lambda (slot-name)
          412                             (intern
          413                              (strcat (symbol-name struct-lisp-name) "-"
          414                                      (symbol-name slot-name))))
          415                           slot-names))
          416            (initarg-names (mapcar
          417                            (lambda (slot-name)
          418                              (intern (symbol-name slot-name) "KEYWORD"))
          419                            slot-names))
          420            (slot-decoders (mapcar (lambda (slot)
          421                                     (destructuring-bind
          422                                           (lisp-name c-name
          423                                                      &key type count
          424                                                      &allow-other-keys)
          425                                         slot
          426                                       (declare (ignore lisp-name c-name))
          427                                       (cond ((and (eq type :char) count)
          428                                              'cffi:foreign-string-to-lisp)
          429                                             (t nil))))
          430                                   slots))
          431            (defclass-form
          432             `(defclass ,struct-lisp-name ()
          433                ,(mapcar (lambda (slot-name initarg-name reader-name)
          434                           `(,slot-name :initarg ,initarg-name
          435                                        :reader ,reader-name))
          436                         slot-names
          437                         initarg-names
          438                         reader-names)))
          439            (make-function-name
          440             (make-from-pointer-function-name struct-lisp-name))
          441            (make-defun-form
          442             ;; this function is then used as a constructor for this class.
          443             `(defun ,make-function-name (pointer)
          444                (cffi:with-foreign-slots
          445                    (,slot-names pointer ,struct-lisp-name)
          446                  (make-instance ',struct-lisp-name
          447                                 ,@(loop for slot-name in slot-names
          448                                         for initarg-name in initarg-names
          449                                         for slot-decoder in slot-decoders
          450                                         collect initarg-name
          451                                         if slot-decoder
          452                                         collect `(,slot-decoder ,slot-name)
          453                                         else collect slot-name))))))
          454       (c-export out make-function-name)
          455       (dolist (reader-name reader-names)
          456         (c-export out reader-name))
          457       (c-write out defclass-form)
          458       (c-write out make-defun-form))))
          459 
          460 (define-grovel-syntax cstruct (struct-lisp-name struct-c-name &rest slots)
          461   (let ((documentation (when (stringp (car slots)) (pop slots))))
          462     (c-section-header out "cstruct" struct-lisp-name)
          463     (c-export out struct-lisp-name)
          464     (dolist (slot slots)
          465       (let ((slot-lisp-name (car slot)))
          466         (c-export out slot-lisp-name)))
          467     (c-format out "(cffi:defcstruct (")
          468     (c-print-symbol out struct-lisp-name t)
          469     (c-printf out " :size %llu)"
          470               (format nil "(long long unsigned) sizeof(~A)" struct-c-name))
          471     (when documentation
          472       (c-format out "~%  ~S" documentation))
          473     (dolist (slot slots)
          474       (destructuring-bind (slot-lisp-name slot-c-name &key type count)
          475           slot
          476         (c-format out "~%  (")
          477         (c-print-symbol out slot-lisp-name t)
          478         (c-format out " ")
          479         (etypecase type
          480           ((eql :auto)
          481            (format out "~&  SLOT_SIGNED_P(autotype_tmp, ~A, ~A~@[[0]~]);~@*~%~
          482                         ~&  type_name(output, autotype_tmp, sizeofslot(~A, ~A~@[[0]~]));~%"
          483                    struct-c-name
          484                    slot-c-name
          485                    (not (null count))))
          486           ((or cons symbol)
          487            (c-write out type))
          488           (string
          489            (c-format out "~A" type)))
          490         (etypecase count
          491           (null t)
          492           (integer
          493            (c-format out " :count ~D" count))
          494           ((eql :auto)
          495            (c-printf out " :count %llu"
          496                      (format nil "(long long unsigned) countofslot(~A, ~A)"
          497                              struct-c-name
          498                              slot-c-name)))
          499           ((or symbol string)
          500            (format out "~&#ifdef ~A~%" count)
          501            (c-printf out " :count %llu"
          502                      (format nil "(long long unsigned) (~A)" count))
          503            (format out "~&#endif~%")))
          504         (c-printf out " :offset %lli)"
          505                   (format nil "(long long signed) offsetof(~A, ~A)"
          506                           struct-c-name
          507                           slot-c-name))))
          508     (c-format out ")~%")
          509     (let ((size-of-constant-name
          510            (symbolicate '#:size-of- struct-lisp-name)))
          511       (c-export out size-of-constant-name)
          512       (c-format out "(cl:defconstant "
          513                 size-of-constant-name struct-lisp-name)
          514       (c-print-symbol out size-of-constant-name)
          515       (c-format out " (cffi:foreign-type-size '(:struct ")
          516       (c-print-symbol out struct-lisp-name)
          517       (c-format out ")))~%"))))
          518 
          519 (defmacro define-pseudo-cvar (str name type &key read-only)
          520   (let ((c-parse (let ((*read-eval* nil)
          521                        (*readtable* (copy-readtable nil)))
          522                    (setf (readtable-case *readtable*) :preserve)
          523                    (read-from-string str))))
          524     (typecase c-parse
          525       (symbol `(cffi:defcvar (,(symbol-name c-parse) ,name
          526                                :read-only ,read-only)
          527                    ,type))
          528       (list (unless (and (= (length c-parse) 2)
          529                          (null (second c-parse))
          530                          (symbolp (first c-parse))
          531                          (eql #\* (char (symbol-name (first c-parse)) 0)))
          532               (grovel-error "Unable to parse c-string ~s." str))
          533             (let ((func-name (symbolicate "%" name '#:-accessor)))
          534               `(progn
          535                  (declaim (inline ,func-name))
          536                  (cffi:defcfun (,(string-trim "*" (symbol-name (first c-parse)))
          537                                  ,func-name) :pointer)
          538                  (define-symbol-macro ,name
          539                      (cffi:mem-ref (,func-name) ',type)))))
          540       (t (grovel-error "Unable to parse c-string ~s." str)))))
          541 
          542 (defun foreign-name-to-symbol (s)
          543   (intern (substitute #\- #\_ (string-upcase s))))
          544 
          545 (defun choose-lisp-and-foreign-names (string-or-list)
          546   (etypecase string-or-list
          547     (string (values string-or-list (foreign-name-to-symbol string-or-list)))
          548     (list (destructuring-bind (fname lname &rest args) string-or-list
          549             (declare (ignore args))
          550             (assert (and (stringp fname) (symbolp lname)))
          551             (values fname lname)))))
          552 
          553 (define-grovel-syntax cvar (name type &key read-only)
          554   (multiple-value-bind (c-name lisp-name)
          555       (choose-lisp-and-foreign-names name)
          556     (c-section-header out "cvar" lisp-name)
          557     (c-export out lisp-name)
          558     (c-printf out "(cffi-grovel::define-pseudo-cvar \"%s\" "
          559               (format nil "indirect_stringify(~A)" c-name))
          560     (c-print-symbol out lisp-name t)
          561     (c-format out " ")
          562     (c-write out type)
          563     (when read-only
          564       (c-format out " :read-only t"))
          565     (c-format out ")~%")))
          566 
          567 ;;; FIXME: where would docs on enum elements go?
          568 (define-grovel-syntax cenum (name &rest enum-list)
          569   (destructuring-bind (name &key base-type define-constants)
          570       (ensure-list name)
          571     (c-section-header out "cenum" name)
          572     (c-export out name)
          573     (c-format out "(cffi:defcenum (")
          574     (c-print-symbol out name t)
          575     (when base-type
          576       (c-printf out " ")
          577       (c-print-symbol out base-type t))
          578     (c-format out ")")
          579     (dolist (enum enum-list)
          580       (destructuring-bind ((lisp-name &rest c-names) &key documentation)
          581           enum
          582         (declare (ignore documentation))
          583         (check-type lisp-name keyword)
          584         (loop for c-name in c-names do
          585           (check-type c-name string)
          586           (c-format out "  (")
          587           (c-print-symbol out lisp-name)
          588           (c-format out " ")
          589           (c-print-integer-constant out c-name base-type)
          590           (c-format out ")~%"))))
          591     (c-format out ")~%")
          592     (when define-constants
          593       (define-constants-from-enum out enum-list))))
          594 
          595 (define-grovel-syntax constantenum (name &rest enum-list)
          596   (destructuring-bind (name &key base-type define-constants)
          597       (ensure-list name)
          598     (c-section-header out "constantenum" name)
          599     (c-export out name)
          600     (c-format out "(cffi:defcenum (")
          601     (c-print-symbol out name t)
          602     (when base-type
          603       (c-printf out " ")
          604       (c-print-symbol out base-type t))
          605     (c-format out ")")
          606     (dolist (enum enum-list)
          607       (destructuring-bind ((lisp-name &rest c-names)
          608                            &key optional documentation) enum
          609         (declare (ignore documentation))
          610         (check-type lisp-name keyword)
          611         (c-format out "~%  (")
          612         (c-print-symbol out lisp-name)
          613         (loop for c-name in c-names do
          614           (check-type c-name string)
          615           (format out "~&#ifdef ~A~%" c-name)
          616           (c-format out " ")
          617           (c-print-integer-constant out c-name base-type)
          618           (format out "~&#else~%"))
          619         (unless optional
          620           (c-format out
          621                     "~%  #.(cl:progn ~
          622                            (cl:warn 'cffi-grovel:missing-definition :name '~A) ~
          623                            -1)"
          624                     lisp-name))
          625         (dotimes (i (length c-names))
          626           (format out "~&#endif~%"))
          627         (c-format out ")")))
          628     (c-format out ")~%")
          629     (when define-constants
          630       (define-constants-from-enum out enum-list))))
          631 
          632 (defun define-constants-from-enum (out enum-list)
          633   (dolist (enum enum-list)
          634     (destructuring-bind ((lisp-name &rest c-names) &rest options)
          635         enum
          636       (%process-grovel-form
          637        'constant out
          638        `((,(intern (string lisp-name)) ,(car c-names))
          639          ,@options)))))
          640 
          641 (defun convert-intmax-constant (constant base-type)
          642   "Convert the C CONSTANT to an integer of BASE-TYPE. The constant is
          643 assumed to be an integer printed using the PRIiMAX printf(3) format
          644 string."
          645   ;; | C Constant |  Type   | Return Value | Notes                                 |
          646   ;; |------------+---------+--------------+---------------------------------------|
          647   ;; |         -1 |  :int32 |           -1 |                                       |
          648   ;; | 0xffffffff |  :int32 |           -1 | CONSTANT may be a positive integer if |
          649   ;; |            |         |              | sizeof(intmax_t) > sizeof(int32_t)    |
          650   ;; | 0xffffffff | :uint32 |   4294967295 |                                       |
          651   ;; |         -1 | :uint32 |   4294967295 |                                       |
          652   ;; |------------+---------+--------------+---------------------------------------|
          653   (let* ((canonical-type (cffi::canonicalize-foreign-type base-type))
          654          (type-bits (* 8 (cffi:foreign-type-size canonical-type)))
          655          (2^n (ash 1 type-bits)))
          656     (ecase canonical-type
          657       ((:unsigned-char :unsigned-short :unsigned-int
          658         :unsigned-long :unsigned-long-long)
          659        (mod constant 2^n))
          660       ((:char :short :int :long :long-long)
          661        (let ((v (mod constant 2^n)))
          662          (if (logbitp (1- type-bits) v)
          663              (- (mask-field (byte (1- type-bits) 0) v)
          664                 (ash 1 (1- type-bits)))
          665              v))))))
          666 
          667 (defun foreign-type-to-printf-specification (type)
          668   "Return the printf specification associated with the foreign type TYPE."
          669   (ecase (cffi::canonicalize-foreign-type type)
          670     (:char               "\"%hhd\"")
          671     (:unsigned-char      "\"%hhu\"")
          672     (:short              "\"%hd\"")
          673     (:unsigned-short     "\"%hu\"")
          674     (:int                "\"%d\"")
          675     (:unsigned-int       "\"%u\"")
          676     (:long               "\"%ld\"")
          677     (:unsigned-long      "\"%lu\"")
          678     (:long-long          "\"%lld\"")
          679     (:unsigned-long-long "\"%llu\"")))
          680 
          681 ;; Defines a bitfield, with elements specified as ((LISP-NAME C-NAME)
          682 ;; &key DOCUMENTATION).  NAME-AND-OPTS can be either a symbol as name,
          683 ;; or a list (NAME &key BASE-TYPE).
          684 (define-grovel-syntax bitfield (name-and-opts &rest masks)
          685   (destructuring-bind (name &key base-type)
          686       (ensure-list name-and-opts)
          687     (c-section-header out "bitfield" name)
          688     (c-export out name)
          689     (c-format out "(cffi:defbitfield (")
          690     (c-print-symbol out name t)
          691     (when base-type
          692       (c-printf out " ")
          693       (c-print-symbol out base-type t))
          694     (c-format out ")")
          695     (dolist (mask masks)
          696       (destructuring-bind ((lisp-name &rest c-names)
          697                            &key optional documentation) mask
          698         (declare (ignore documentation))
          699         (check-type lisp-name symbol)
          700         (c-format out "~%  (")
          701         (c-print-symbol out lisp-name)
          702         (c-format out " ")
          703         (dolist (c-name c-names)
          704           (check-type c-name string)
          705           (format out "~&#ifdef ~A~%" c-name)
          706           (format out "~&  fprintf(output, ~A, ~A);~%"
          707                   (foreign-type-to-printf-specification (or base-type :int))
          708                   c-name)
          709           (format out "~&#else~%"))
          710         (unless optional
          711           (c-format out
          712                     "~%  #.(cl:progn ~
          713                            (cl:warn 'cffi-grovel:missing-definition :name '~A) ~
          714                            -1)"
          715                     lisp-name))
          716         (dotimes (i (length c-names))
          717           (format out "~&#endif~%"))
          718         (c-format out ")")))
          719     (c-format out ")~%")))
          720 
          721 
          722 ;;;# Wrapper Generation
          723 ;;;
          724 ;;; Here we generate a C file from a s-exp specification but instead
          725 ;;; of compiling and running it, we compile it as a shared library
          726 ;;; that can be subsequently loaded with LOAD-FOREIGN-LIBRARY.
          727 ;;;
          728 ;;; Useful to get at macro functionality, errno, system calls,
          729 ;;; functions that handle structures by value, etc...
          730 ;;;
          731 ;;; Matching CFFI bindings are generated along with said C file.
          732 
          733 (defun process-wrapper-form (out form)
          734   (%process-wrapper-form (form-kind form) out (cdr form)))
          735 
          736 ;;; The various operators push Lisp forms onto this list which will be
          737 ;;; written out by PROCESS-WRAPPER-FILE once everything is processed.
          738 (defvar *lisp-forms*)
          739 
          740 (defun generate-c-lib-file (input-file output-defaults)
          741   (let ((*lisp-forms* nil)
          742         (c-file (make-c-file-name output-defaults "__wrapper")))
          743     (with-open-file (out c-file :direction :output :if-exists :supersede)
          744       (with-open-file (in input-file :direction :input)
          745         (write-string *header* out)
          746         (loop for form = (read in nil nil) while form
          747               do (process-wrapper-form out form))))
          748     (values c-file (nreverse *lisp-forms*))))
          749 
          750 (defun make-soname (lib-soname output-defaults)
          751   (make-pathname :name lib-soname
          752                  :defaults output-defaults))
          753 
          754 (defun generate-bindings-file (lib-file lib-soname lisp-forms output-defaults)
          755   (with-standard-io-syntax
          756     (let ((lisp-file (tmp-lisp-file-name output-defaults))
          757           (*print-readably* nil)
          758           (*print-escape* t))
          759       (with-open-file (out lisp-file :direction :output :if-exists :supersede)
          760         (format out ";;;; This file was automatically generated by cffi-grovel.~%~
          761                    ;;;; Do not edit by hand.~%")
          762         (let ((*package* (find-package '#:cl))
          763               (named-library-name
          764                 (let ((*package* (find-package :keyword))
          765                       (*read-eval* nil))
          766                   (read-from-string lib-soname))))
          767           (pprint `(progn
          768                      (cffi:define-foreign-library
          769                          (,named-library-name
          770                           :type :grovel-wrapper
          771                           :search-path ,(directory-namestring lib-file))
          772                        (t ,(namestring (make-so-file-name lib-soname))))
          773                      (cffi:use-foreign-library ,named-library-name))
          774                   out)
          775           (fresh-line out))
          776         (dolist (form lisp-forms)
          777           (print form out))
          778         (terpri out))
          779       lisp-file)))
          780 
          781 (defun cc-include-grovel-argument ()
          782   (format nil "-I~A" (truename (system-source-directory :cffi-grovel))))
          783 
          784 ;;; *PACKAGE* is rebound so that the IN-PACKAGE form can set it during
          785 ;;; *the extent of a given wrapper file.
          786 (defun process-wrapper-file (input-file
          787                              &key
          788                                (output-defaults (make-pathname :defaults input-file :type "processed"))
          789                                lib-soname)
          790   (with-standard-io-syntax
          791     (multiple-value-bind (c-file lisp-forms)
          792         (generate-c-lib-file input-file output-defaults)
          793     (let ((lib-file (make-so-file-name (make-soname lib-soname output-defaults)))
          794           (o-file (make-o-file-name output-defaults "__wrapper")))
          795         (cc-compile o-file (list (cc-include-grovel-argument) c-file))
          796         (link-shared-library lib-file (list o-file))
          797         ;; FIXME: hardcoded library path.
          798         (values (generate-bindings-file lib-file lib-soname lisp-forms output-defaults)
          799                 lib-file)))))
          800 
          801 (defgeneric %process-wrapper-form (name out arguments)
          802   (:method (name out arguments)
          803     (declare (ignore out arguments))
          804     (grovel-error "Unknown Grovel syntax: ~S" name)))
          805 
          806 ;;; OUT is lexically bound to the output stream within BODY.
          807 (defmacro define-wrapper-syntax (name lambda-list &body body)
          808   (with-unique-names (name-var args)
          809     `(defmethod %process-wrapper-form ((,name-var (eql ',name)) out ,args)
          810        (declare (ignorable out))
          811        (destructuring-bind ,lambda-list ,args
          812          ,@body))))
          813 
          814 (define-wrapper-syntax progn (&rest forms)
          815   (dolist (form forms)
          816     (process-wrapper-form out form)))
          817 
          818 (define-wrapper-syntax in-package (name)
          819   (assert (find-package name) (name)
          820           "Wrapper file specified (in-package ~s)~%~
          821            however that does not name a known package."
          822           name)
          823   (setq *package* (find-package name))
          824   (push `(in-package ,name) *lisp-forms*))
          825 
          826 (define-wrapper-syntax c (&rest strings)
          827   (dolist (string strings)
          828     (write-line string out)))
          829 
          830 (define-wrapper-syntax flag (&rest flags)
          831   (appendf *cc-flags* (parse-command-flags-list flags)))
          832 
          833 (define-wrapper-syntax proclaim (&rest proclamations)
          834   (push `(proclaim ,@proclamations) *lisp-forms*))
          835 
          836 (define-wrapper-syntax declaim (&rest declamations)
          837   (push `(declaim ,@declamations) *lisp-forms*))
          838 
          839 (define-wrapper-syntax define (name &optional value)
          840   (format out "#define ~A~@[ ~A~]~%" name value))
          841 
          842 (define-wrapper-syntax include (&rest includes)
          843   (format out "~{#include <~A>~%~}" includes))
          844 
          845 ;;; FIXME: this function is not complete.  Should probably follow
          846 ;;; typedefs?  Should definitely understand pointer types.
          847 (defun c-type-name (typespec)
          848   (let ((spec (ensure-list typespec)))
          849     (if (stringp (car spec))
          850         (car spec)
          851         (case (car spec)
          852           ((:uchar :unsigned-char) "unsigned char")
          853           ((:unsigned-short :ushort) "unsigned short")
          854           ((:unsigned-int :uint) "unsigned int")
          855           ((:unsigned-long :ulong) "unsigned long")
          856           ((:long-long :llong) "long long")
          857           ((:unsigned-long-long :ullong) "unsigned long long")
          858           (:pointer "void*")
          859           (:string "char*")
          860           (t (cffi::foreign-name (car spec) nil))))))
          861 
          862 (defun cffi-type (typespec)
          863   (if (and (listp typespec) (stringp (car typespec)))
          864       (second typespec)
          865       typespec))
          866 
          867 (defun symbol* (s)
          868   (check-type s (and symbol (not null)))
          869   s)
          870 
          871 (define-wrapper-syntax defwrapper (name-and-options rettype &rest args)
          872   (multiple-value-bind (lisp-name foreign-name options)
          873       (cffi::parse-name-and-options name-and-options)
          874     (let* ((foreign-name-wrap (strcat foreign-name "_cffi_wrap"))
          875            (fargs (mapcar (lambda (arg)
          876                             (list (c-type-name (second arg))
          877                                   (cffi::foreign-name (first arg) nil)))
          878                           args))
          879            (fargnames (mapcar #'second fargs)))
          880       ;; output C code
          881       (format out "~A ~A" (c-type-name rettype) foreign-name-wrap)
          882       (format out "(~{~{~A ~A~}~^, ~})~%" fargs)
          883       (format out "{~%  return ~A(~{~A~^, ~});~%}~%~%" foreign-name fargnames)
          884       ;; matching bindings
          885       (push `(cffi:defcfun (,foreign-name-wrap ,lisp-name ,@options)
          886                  ,(cffi-type rettype)
          887                ,@(mapcar (lambda (arg)
          888                            (list (symbol* (first arg))
          889                                  (cffi-type (second arg))))
          890                          args))
          891             *lisp-forms*))))
          892 
          893 (define-wrapper-syntax defwrapper* (name-and-options rettype args &rest c-lines)
          894   ;; output C code
          895   (multiple-value-bind (lisp-name foreign-name options)
          896       (cffi::parse-name-and-options name-and-options)
          897     (let ((foreign-name-wrap (strcat foreign-name "_cffi_wrap"))
          898           (fargs (mapcar (lambda (arg)
          899                            (list (c-type-name (second arg))
          900                                  (cffi::foreign-name (first arg) nil)))
          901                          args)))
          902       (format out "~A ~A" (c-type-name rettype)
          903               foreign-name-wrap)
          904       (format out "(~{~{~A ~A~}~^, ~})~%" fargs)
          905       (format out "{~%~{  ~A~%~}}~%~%" c-lines)
          906       ;; matching bindings
          907       (push `(cffi:defcfun (,foreign-name-wrap ,lisp-name ,@options)
          908                  ,(cffi-type rettype)
          909                ,@(mapcar (lambda (arg)
          910                            (list (symbol* (first arg))
          911                                  (cffi-type (second arg))))
          912                          args))
          913             *lisp-forms*))))