;;-*- Mode: Lisp; Package: CCL -*-
;;;
;;; Copyright 1994-2001 Clozure Associates
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;;     http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;-*- Mode: Lisp; Package: CCL -*-
;;;
;;;   Copyright (C) 1994-2001 Digitool, Inc
;;;   This file is part of Opensourced MCL.
;;;
;;;   Opensourced MCL is free software; you can redistribute it and/or
;;;   modify it under the terms of the GNU Lesser General Public
;;;   License as published by the Free Software Foundation; either
;;;   version 2.1 of the License, or (at your option) any later version.
;;;
;;;   Opensourced MCL 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
;;;   Lesser General Public License for more details.
;;;
;;;   You should have received a copy of the GNU Lesser General Public
;;;   License along with this library; if not, write to the Free Software
;;;   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; mac-file-io.lisp
;;

;; This file implements something similar to the high-level file I/O
;; primitives in Inside Macintosh.
;; It does NOT support asynchronous I/O (and neither does the Macintosh, really).

;; Routines that take an errorp parameter will signal an error if
;; the parameter is unspecified or true, otherwise, if there is an
;; error they return two values: NIL & the error number.
;; If there is no error, routines return one or more values the
;; first of which is non-NIL.

;;;;;;;;;;;;;
;;
;; Modification History
;;

(in-package "CCL")

(eval-when (:compile-toplevel :execute)
  (require 'sysequ))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (export '(with-FSOpen-file FSOpen FSClose FSRead FSWrite setFPos getFPos getEOF)))

(defmacro with-FSOpen-file ((pb filename &optional read-write-p (vrefnum 0))
                            &body body)
  `(let ((,pb (FSOpen ,filename ,read-write-p ,vrefnum)))
     (unwind-protect
       (progn ,@body)
       (FSClose ,pb))))

(defmacro with-FSOpen-file-noerr ((pb filename &optional read-write-p (vrefnum 0))
                                  &body body)
  `(let ((,pb (ignore-errors
               (FSOpen ,filename ,read-write-p ,vrefnum))))
     (when ,pb
       (unwind-protect
         (progn ,@body)
         (FSClose ,pb)))))

; Returns a paramBlock for doing furthur I/O with the file
(defun FSOpen (filename &optional read-write-p (vrefnum 0) (errorp t)
                        (resolve-aliases-p t))
  (when resolve-aliases-p (setq filename (truename filename)))
  (let ((paramBlock (make-record :hparamblockrec))
        ok)
    (unwind-protect
      (with-pstrs ((pname (mac-namestring filename)))
        (setf (pref paramblock :hparamblockrec.ioNameptr) pname
              (pref paramblock :hparamblockrec.ioVrefnum) vrefnum
              (pref paramblock :hparamblockrec.ioVersNum) 0
              (pref paramblock :hparamblockrec.ioPermssn) (if read-write-p #$fsRdWrPerm #$fsRdPerm)
              (pref paramblock :hparamblockrec.ioMisc) (%null-ptr))
        (#_PBOpenSync paramBlock)
        (let ((res (pref paramBlock :hparamblockrec.ioResult)))
          (if (eql #$NoErr res)
            (progn
              (setf (pref paramblock :hparamblockrec.ioPosOffSet) 0
                    (pref paramblock :hparamblockrec.ioPosMode) #$fsAtMark)
              (setq ok t)
              paramBlock)
            (maybe-file-error errorp res filename))))
      (unless ok
        (#_DisposePtr paramBlock)))))

(defun FSClose (paramBlock &optional (errorp t))
  (#_PBCloseSync paramBlock)
  (let ((errnum (pref paramBlock :hparamblockrec.ioResult)))
    (#_DisposePtr paramBlock)
    (or (eql errnum #$noErr)
        (maybe-file-error errorp errnum))))

; Returns two values: the number of bytes actually read, and the
; location of the file mark.
(defun fsRead (paramBlock count buffer &optional (offset 0) (errorp t))
  (setf (pref paramBlock :hparamblockrec.ioBuffer) (%inc-ptr buffer offset)
        (pref paramBlock :hparamblockrec.ioReqCount) count)
  (#_PBReadSync paramBlock)
  (setf (pref paramBlock :hparamblockrec.ioPosMode) #$fsAtMark)
  (let ((errnum (pref paramBlock :hparamblockrec.ioResult)))
    (if (or (eql #$noErr errnum) (eql #$eofErr errnum))
      (values (pref paramBlock :hparamblockrec.ioActCount)
              (pref paramBlock :hparamblockrec.ioPosOffset))
      (maybe-file-error errorp errnum))))

; Returns two values: the number of bytes actually written, and the
; location of the file mark.
(defun fsWrite (paramBlock count buffer &optional (offset 0) (errorp t))
  (setf (pref paramBlock :hparamblockrec.ioBuffer) (%inc-ptr buffer offset)
        (pref paramBlock :hparamblockrec.ioReqCount) count)
  (#_PBWriteSync paramBlock)
  (setf (pref paramBlock :hparamblockrec.ioPosMode) #$fsAtMark)
  (let ((errnum (pref paramBlock :hparamblockrec.ioResult)))
    (if (or (eql #$noErr errnum) (eql #$eofErr errnum))
      (values (pref paramBlock :hparamblockrec.ioActCount)
              (pref paramBlock :hparamblockrec.ioPosOffset))
      (maybe-file-error errorp errnum))))

(defun setFPos (paramBlock pos)
  (setf (pref paramBlock :hparamblockrec.ioPosOffset) pos
        (pref paramblock :hparamblockrec.ioPosMode) #$fsFromStart)
  pos)

(defun getFPos (paramBlock)
  (pref paramBlock :hparamblockrec.ioPosOffset))

(defun getEOF (paramBlock &optional (errorp t))
  (let* ((errnum (#_PBGetEOFSync paramBlock)))
    (if (eql #$noErr errnum)
      (%ptr-to-int (pref paramblock :hparamblockrec.ioMisc))
      (maybe-file-error errorp errnum))))

(defun GetVInfo (&key (volName "") (vRefNum 0))
  (let* ((vol-pathname (truename (make-pathname :type nil :name nil :defaults volName)))
         (directory    (pathname-directory vol-pathname)))
    (assert (and directory (eq :absolute (car directory))))
    (rlet ((paramBlock :hparamblockrec))
      (with-returned-pstrs ((pname (cadr directory)))
        (setf (pref paramblock :hparamblockrec.ioCompletion) (%null-ptr)
              (pref paramblock :hparamblockrec.ioNamePtr)    pname
              (pref paramblock :hparamblockrec.ioVRefNum)    vRefNum
              (pref paramblock :hparamblockrec.ioVolIndex)   0)
        (values (#_PBHGetVInfoSync paramBlock)
                (* (%get-unsigned-long paramblock $ioVAlBlkSiz)         ; see IM:Files 2-46
                   (pref paramblock :hparamblockrec.ioVFrBlk))
                (pref paramblock :hparamblockrec.ioVRefNum)
                (%get-string (pref paramblock :hparamblockrec.ioNamePtr)))))))

(defun maybe-file-error (errorp errnum &optional filename)
  (if errorp
    (%err-disp errnum filename)
    (values nil errnum)))

(provide :mac-file-io)

; End of mac-file-io.lisp
