;;;-*- Mode: Lisp; Package: CCL -*-
;;;
;;;   Copyright (C) 2003-2009 Clozure Associates
;;;   This file is part of Clozure CL.  
;;;
;;;   Clozure CL is licensed under the terms of the Lisp Lesser GNU Public
;;;   License , known as the LLGPL and distributed with Clozure CL as the
;;;   file "LICENSE".  The LLGPL consists of a preamble and the LGPL,
;;;   which is distributed with Clozure CL as the file "LGPL".  Where these
;;;   conflict, the preamble takes precedence.  
;;;
;;;   Clozure CL is referenced in the preamble as the "LIBRARY."
;;;
;;;   The LLGPL is also available online at
;;;   http://opensource.franz.com/preamble.html


;;; Utilities for dealing with Pascal strings
;;;
;;; In 68K Mac Pascal, strings were represented by a pointer to a
;;; "length byte", which indicated the number of data bytes immediately
;;; following.

(in-package "CCL")

(eval-when (:compile-toplevel :load-toplevel :execute)
  ;; What else should be exported ?  What else should be added
  ;; to this file ?
  (export '(with-pstrs with-returned-pstrs %get-string)))

(defun %pstr-pointer (string pointer)
  (multiple-value-bind (s o n) (dereference-base-string string)
    (declare (fixnum o n))
    (%copy-ivector-to-ptr s o pointer 1 n)
    (setf (%get-byte pointer 0) n))
  nil)

(defun %pstr-segment-pointer (string pointer start end)
  (declare (fixnum start end))
  (let* ((n (- end start)))
    (multiple-value-bind (s o) (dereference-base-string string)
      (declare (fixnum o))
      (%copy-ivector-to-ptr s (the fixnum (+ o start)) pointer 1 n)
    (setf (%get-byte pointer 0) n)
    nil)))

(defun %get-string (pointer)
  (let* ((len (%get-unsigned-byte pointer)))
    (%copy-ptr-to-ivector
     pointer
     1
     (make-string len :element-type 'base-char)
     0
     len)))

(defun (setf %get-string) (lisp-string pointer)
  (let* ((len (length lisp-string)))
    (multiple-value-bind (string offset)
        (dereference-base-string lisp-string)
      (setf (%get-unsigned-byte pointer) len)
      (%copy-ivector-to-ptr string offset pointer 1 len))
    lisp-string))

(defmacro with-pstr ((sym str &optional start end) &rest body &environment env)
  (multiple-value-bind (body decls) (parse-body body env nil)
    (if (and (base-string-p str) (null start) (null end))
      (let ((strlen (%i+ (length str) 1)))
        `(%stack-block ((,sym ,strlen))
           ,@decls
           (%pstr-pointer ,str ,sym)
           ,@body))
      (let ((strname (gensym))
            (start-name (gensym))
            (end-name (gensym)))
        `(let ((,strname ,str)
               ,@(if (or start end)
                   `((,start-name ,(or start 0))
                     (,end-name ,(or end `(length ,strname))))))
           (%vstack-block (,sym
                           (the fixnum
                             (1+
                              (the fixnum
                                ,(if (or start end)
                                     `(byte-length
                                       ,strname ,start-name ,end-name)
                                     `(length ,strname))))))
             ,@decls
             ,(if (or start end)
                `(%pstr-segment-pointer ,strname ,sym ,start-name ,end-name)
                `(%pstr-pointer ,strname ,sym))
             ,@body))))))


(defmacro with-returned-pstr ((sym str &optional start end) &body body)
   `(%stack-block ((,sym 256))
      ,(if (or start end)
         `(%pstr-segment-pointer ,str ,sym ,start ,end)
         `(%pstr-pointer ,str ,sym))
      ,@body))

(defmacro with-pstrs (speclist &body body)
   (with-specs-aux 'with-pstr speclist body))

(defmacro with-returned-pstrs (speclist &body body)
   (with-specs-aux 'with-returned-pstr speclist body))


