;;; -*- mode:lisp;package:user;base:10.;fonts:(cptfontb) -*- ;;; ;;;$Header: /ct/interp/directio.l,v 1.33 84/09/26 13:35:27 alex Exp $ ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; DIRECTIO ;;; ;;; ;;; ;;; LISP Code to implement direct input-output for Ada. ;;; ;;; ;;; ;;; John Shelton Early 1983 ;;; ;;; Updated by Mark Miller Wed 28 Sept 1983 ;;; ;;; ;;; ;;; Data elements are stored as PRIN-able and READ-able S- ;;; ;;; expresssions in a file. The first element in a file contains ;;; ;;; the size (in elements) of the file. This is always read upon ;;; ;;; init. ;;; ;;; ;;; ;;; Looking forward in a file requires READing forward across all ;;; ;;; intermediary text. This can be rather slow for files with very ;;; ;;; many objects, or many large objects. A DIRECTORY at the ;;; ;;; beginning of the file that gave the bytecount starting position ;;; ;;; of each object would speed up such things. Such a directory ;;; ;;; would itself be expensive to maintain if the file contained many ;;; ;;; small objects. The directory scheme has been implemented. ;;; ;;; ;;; ;;; To save lots of disk access during normal direct reads, we cache ;;; ;;; a lot of stuff in a QUEUE of 500 elements. This queue records ;;; ;;; the last 500 (lineal) elements read from the file so we don't ;;; ;;; have to read from the beginning of the file to find recent ;;; ;;; items. This won't help in extreme cases, but should help in ;;; ;;; general. ;;; ;;; ;;; ;;; The read cache behaves as a queue, and is implemented as an ;;; ;;; array with start and end pointers. (We use an array to avoid ;;; ;;; unnecessary consing, and to increase the speed of reference.) ;;; ;;; Unfortunately, this means a fixed overhead to a direct file, ;;; ;;; but, what-the-hell, there is plenty of address space on the ;;; ;;; target machines, anyway. ;;; ;;; ;;; ;;; This file is part of a proprietary software project. Source ;;; ;;; code and documentation describing implementation details are ;;; ;;; available on a confidential, non-disclosure basis only. These ;;; ;;; materials, including this file in particular, are trade secrets ;;; ;;; of Computer * Thought Corporation. ;;; ;;; ;;; ;;; (c) Copyright 1982 and 1983, Computer * Thought Corporation. ;;; ;;; All Rights Reserved. ;;; ;;; ;;; ;;; Reference materials: ;;; ;;; Foderaro and Sklower, The FRANZ LISP Manual, September 1981. ;;; ;;; Weinreb and Moon, LISP MACHINE MANUAL, Symbolics, July 1981. ;;; ;;; Charniak et al., 1980. Artificial Intelligence Programming. ;;; ;;; Miller, 1982. The C*T Ada Tutor: Guide to the Implementation. ;;; ;;; AJPO, Feb 1983. ANSI/MIL-STD-1815A Ada Reference Manual. ;;; ;;; The following code assumes familiarity with these materials. ;;; ;;; ;;; ;;; ASSUMES CT_LOAD AND SUITABLE FILEMAP ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ensure presence of needed files. (eval-when (compile load eval) (ct_load 'charmac)) ;Char set exten. (eval-when (compile load eval) (ct_load 'aip)) ;AIP macros pkg. (eval-when (compile load eval) (ct_load 'compat)) ;Franz/LM compat pkg. (eval-when (compile load eval) (ct_load 'cthash)) ;Hash Table. (eval-when (compile load eval) (ct_load 'iocompat)) ;TextIO Compat. (eval-when (compile load eval) (ct_load 'bifmacs)) (eval-when (compile load eval) (ct_load 'stdenv)) (eval-when (compile load eval) (ct_load 'diana)) ; diana structure (eval-when (compile load eval) (ct_load 'sequenio)) (eval-when (compile load eval) ;For string package (ct_load 'ctstrl)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Compiler Declarations and Global Variables -- #+franz (declare (macros t)) (declare (ct_includef 'intrpdcl)) (declare (special *dirio_temp_file_list* **direct_file_list** **direct_file_num**)) (defvar *temp_directory* (ct_load_get 'temp_directory)) ;; temporary file directory used in dump_cache ;;; Size of the read_cache, used in a lot of places. (declare (special *read_cache_size*)) (declare (special *directory_number_width*)) (declare (special *read_position_threshhold*)) (defun direct_init () (let ((base 10.) (ibase 10.)) (setq *read_cache_size* 500.) ;;; The directory is implemented as fixnums stored in the file in fixed ;;; width slots. (This makes it easy to compute the address of a directory ;;; entry in a file.) There is always one extra directory entry that is ;;; ignored. ;;; How many bytes a directory entry takes, including spaces. (setq *directory_number_width* 10.) ;;; If we HAVE to read from a file, how many elements away from where we ;;; are do we want to use direct positioning instead of just reading. ;;; Also, this number indicates how far before the desired position we ;;; start reading. This should be less than *read_cache_size* and seems ;;; that it ought to be about 1/4 of *read_cache_size*. (setq *read_position_threshhold* 50.))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Internal Use Only Functions/Macros/Flavors/Methods -- ;;;;;;;;;;; (ct_defflavor ada_direct_io_file ;;;;;;;;;;; (real_stream ;; This may change through course of r/w. name ;; Filename of the file we diddle. file_size ;; Size in records of external file. (index 1) ;; Next record to be read or written. (filepos 1) ;; Where file thinks it really is. mode ;; One of in, out, inout. status ;; One of open, closed. write_cache ;; Where we store writes. (read_cache (gensym)) ;; The read cache. (read_cache_start 0) ;; First element of read q. (read_cache_end 0) ;; Last element of read q. (read_cache_start_key 0) ;; Key of first element. (read_cache_end_key 0) ;; Key of last element. ) () :settable-instance-variables :gettable-instance-variables) ;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file d_init) () ;;;;;;;;;;;;;;;;;;;;;;; ;;; Create the hash table. Also, initialize file_size. And, create ;;; the read_cache array. (let ((base 10.) (ibase 10.)) (setq write_cache (ct_make_hash_table 464 'dump_cache self)) #+franz (*array read_cache t *read_cache_size*) #+(or 3600 cadr) (setq read_cache (make-array *read_cache_size*)) (setq file_size (ct_read real_stream)) (ct_send self 'init_filepos) )) ;;;;;;;;;;;;;;;;; (defun fixed_width_print (fixnum stream) ;;;;;;;;;;;;;;;;; ;;; This function prints an integer right justified in a ;;; fixed field, with one trailing space. ;;; (fixed_width_print 145 terminal-io) will print " 145 " ;;; on the terminal when *directory_number_width* is 6. ;;; (The quote signs will not be printed, of course.) #+lispm (let ((base 10.) (ibase 10.)) (ct_format stream "~VD" *directory_number_width* fixnum) ;(1- *directory_number_width*) #+franz ;;; Must resort to kludges since Franz Format is inadequate. (princ (pad_right_list (exploden fixnum) *directory_number_width* #/ ) stream) )) #+franz ;;;;;;;;;;;;;; (defun pad_right_list (str n char) ;;;;;;;;;;;;;; ;;; Internal to fixed_width_print, above. ;;; This is a special purpose function which places a string (ahem) ;;; in a field of spaces with one space on the right and the rest ;;; of the spaces on the left. (ct_if (< (length str) n) (maknam (append (firstn (- n 1 (length str)) (circular-list char)) str (list char))) (maknam str))) ;;; NB: The write_cache (implemented as a hash-table) stores ;;; all write requests. This means we don't actually have ;;; to write to disk every time the user writes an element. ;;; When the hash table gets full, the rehash-function ;;; DUMP_CACHE is called. Instead of increasing the size of ;;; the table, dump_cache writes all the queued elements to ;;; disk in one sweep, and then clears the hash table. ;;;;;;;;;; (defun dump_cache (table) ;;;;;;;;;; ;;; Puts all the cached write info into the file, by rewriting the file ;;; from scratch. The directory is printed first. (let* ((file (ht_owner table)) (base 10.) (ibase 10.) (ocount (get-iv ada_direct_io_file file 'file_size)) (count (max (ht_max_key table) ocount)) (fname (get-iv ada_direct_io_file file 'name)) (tempname #|(fs:make-pathname ':name (ct_string_downcase (gensym)) ':directory (ct_send (ct_send (ct_send file 'real_stream) 'pathname) ':directory))|# (ct_string_append *temp_directory* (ct_format nil (gensym)))) (foo (ct_open_out tempname)) (w 0) (prinlength nil) (prinlevel nil) (*nopoint t) newstream) (ct_send file 'init_filepos) ; Ready to read first elt. (fixed_width_print count foo) ; Print the number of entries. (fixed_width_print 0 foo) ; First elt is at locn 0. ;; Print the entries in the directory for items already in file. (loop for i from 1 to ocount for item = (ct_send file 'read_element) for hitem = (ct_gethash i table) for width first (1+ (flatc (ct_if hitem (cdr hitem) item))) then (+ width 1 (flatc (ct_if hitem (cdr hitem) item))) doing (fixed_width_print width foo) finally (setq w width)) (ct_send file 'init_filepos) ; Ready to read first elt. (ct_if (not w) (setq w 0)) ; Previous loop can clr w. ;; Print entries in directory for items not in file. (loop for i from (1+ ocount) to count for hitem = (cdr (ct_gethash i table)) for width first (+ 1 (flatc hitem) w) then (+ width 1 (flatc hitem)) doing (fixed_width_print width foo)) ;; Now, print elts that were in old file, replacing with new if can. (loop for i from 1 to ocount for item = (ct_send file 'read_element) for hitem = (ct_gethash i table) doing (prin1_sp (ct_if hitem (cdr hitem) item) foo)) ;; Next, print elements that are after any elements in the old ;; file, printing NILs where nothing has been stored. (loop for i from (1+ ocount) to count for hitem = (cdr (ct_gethash i table)) doing (prin1_sp hitem foo)) ;; Now close up the old & new files, and rename. (ct_closef foo) (ct_closef (get-iv ada_direct_io_file file 'real_stream)) (ct_deletef fname) ;;(ct_renamef tempname fname) (ct_copy_temp_file tempname fname) (ct_deletef tempname) (set-iv ada_direct_io_file file 'real_stream (ct_open_in fname)) (ct_send file 'recompute_file_size) (ct_clrhash table))) (defun ct_copy_temp_file (tempname fname) (let ((bar (ct_open_out fname))) (do ((foo (ct_open_in (ct_string_append tempname ".lisp.1"))) (ch nil) (eof nil)) (eof) (ct_if (setq ch (ct_tyi foo nil)) (ct_tyo ch bar) (setq eof t)) ) (ct_closef bar) )) (defun get_directory (stream) (do ((dirl (ct_send (ct_send stream 'pathname) 'directory) (cdr dirl)) (dir "")) ((null dirl) (ct_string_append dir "//")) (setq dir (ct_string_append dir "//" (car dirl))) )) #| ;;; file copy (defun ct_file_copy (from to) (do ((eof nil) (ch nil)) (eof) (cond ((setq eof (null (setq ch (ct_tyi from nil))))) (t (ct_tyo ch to))) )) |# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file recompute_file_size) () ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Reads the filesize from the directory. Assumes the file is just at ;;; the beginning. Then sets file ready to read first element. (setq file_size (ct_read real_stream)) (ct_send self 'init_filepos)) ;;; For convenience, all elements are read or written as LISP S-exps, so ;;; that LISP's READ can get them back easily. ;;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file find_element) (n) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Finds the Nth element in a file by doing: ;;; 0. The caller should look in the write cache. Find_element won't. ;;; 1. Check to see if it should exist. If not, ERROR. ;;; 2. Check to see if element is after end of existing file, but ;;; before some element in the write cache. Return NIL. ;;; 3. Check to see if element is in read cache. If so, access it ;;; using AREF (quickly) and return it. ;;; 4. Else, decide to read forward in file, accumulating stuff in ;;; the read cache, or reset the file and read cache, and read ;;; forward to find it. (let (a b (base 10.) (ibase 10.)) (cond ;; No possible element: (i.e. past last element) ((> n (max (ht_max_key write_cache) file_size)) (ada_raise '|end_error| "Attempt to read record past end of file.")) ;; Not in real file nor cache, so must be nil. ((> n file_size) nil) ;; In the read_cache. ((<= read_cache_start_key n (1- read_cache_end_key)) #+franz (apply read_cache (list (remainder (- (+ *read_cache_size* read_cache_end) (- read_cache_end_key n)) *read_cache_size*))) #+(or 3600 cadr) (aref read_cache (remainder (- (+ *read_cache_size* read_cache_end) (- read_cache_end_key n)) *read_cache_size*)) ) ;; If we get here, it is not in the read cache. We will have to ;; read stuff from the file, and adjust the read cache as we go ;; along. (t ;; If read too far, go to beginning of file. After this, the ;; filepos must be before the desired element. (ct_if (> filepos n) (ct_send self 'reset)) ;; If we would have to read too many elements to get to the ;; right one, use direct positioning to get there quicker. ;; This puts us at most *read_position_threshhold* elements ;; away from the correct element. We don't zap right to it ;; because we want to put some nearby elements in the ;; read_cache, assuming it is faster to find stuff in the cache ;; than to position a file. (ct_if (> n (+ filepos *read_position_threshhold*)) (ct_send self 'element_position (- n *read_position_threshhold*))) ;; If we are not just about to read the element just after the ;; last one in the queue, we reset the queue. This makes sure ;; queue contains only a linear, adjacent collection of elements. (ct_if (not (= filepos read_cache_end_key)) (ct_send self 'clear_read_cache)) (ct_if (zerop read_cache_start_key) ;If read cache is empty, (setq read_cache_start_key filepos ;start it correctly. read_cache_end_key filepos)) (setq b (- n filepos -1)) (ct_dotimes b (setq a (ct_send self 'read_element)) (ct_if (= (remainder (1+ read_cache_end) *read_cache_size*) read_cache_start) ;; If r_c full, roll both end and start one. (setq read_cache_start (remainder (1+ read_cache_start) *read_cache_size*) read_cache_start_key (1+ read_cache_start_key) read_cache_end (remainder (1+ read_cache_end) *read_cache_size*) read_cache_end_key (1+ read_cache_end_key)) ;; If the cache is not full, roll only end. (setq read_cache_end (remainder (1+ read_cache_end) *read_cache_size*) read_cache_end_key (1+ read_cache_end_key))) ;; Finally, store in the array. #+franz (eval `(store (,read_cache ,(remainder (+ *read_cache_size* (1- read_cache_end)) *read_cache_size*)) ',a)) #+(or 3600 cadr) (aset a read_cache (remainder (+ *read_cache_size* (1- read_cache_end)) *read_cache_size*)) ) a)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file clear_read_cache) () ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Clears the read cache. Simple enough. (setq read_cache_start_key 0 read_cache_start 0 read_cache_end_key 0 read_cache_end 0)) ;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file reset) () ;;;;;;;;;;;;;;;;;;; ;;; Positions file to read first element, and clears the read cache. (ct_send self 'init_filepos) (ct_send self 'clear_read_cache)) ;;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file read_element) () ;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Reads an actual element from a file. Updates filepos. (setq filepos (1+ filepos)) (ct_read real_stream)) ;;;;;;;;;;; (defun open_direct (name mode) ;;;;;;;;;;; ;;; Open_direct opens a file up for direct_io. (ct_make_instance 'ada_direct_io_file 'name name 'real_stream (ct_open_in name) 'mode mode 'status 'open)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; "Directory" manipulation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; It has been decided to "bite the bullet" and write a directory of ;;; elements to disk to make accessing quicker. (I do not know whether ;;; this will really make accessing quicker.) A directory is a ;;; sequence of numbers, all of uniform width (printed in decimal) ;;; written at the beginning of a file. Each number represents the ;;; BYTE COUNT of the element within the file, with the first element ;;; being at position 0, even though it is after the directory. The ;;; true position is calculated by adding to the directory entry an ;;; OFFSET which is easily calculated as the product of the number of ;;; entries in the file and the directory_number_width, which tells how ;;; many digits are in each number. ;;; As an example, the initial directory number width is 9, which ;;; allows 8 digits and a terminating space. ;;; The first (zero) entry in the directory is actually a count of how ;;; many elements are in the file. This is also, of course, the number ;;; of entries in the directory. ;;; A method element_position will move the pointer in the file so that ;;; the next element to be read will be element N. This is ;;; accomplished by calculating the position in the file of the ;;; directory entry for element N, reading the directory entry, and ;;; then positioning the file pointer. The file position is also ;;; updated. (NB: File pointer is a value manipulated by the file ;;; system that indicates which byte is about to be read from a file. ;;; File position is a value we maintain that indicates what ELEMENT is ;;; about to be read from the file.) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file element_position) (n) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Positions a file to be ready to read element n. ;; First, position the file pointer to be ready to read the dir entry. (ct_send self 'set_pointer (* n *directory_number_width*)) ;; Now, position the file pointer correctly, by computing the sum of ;; the directory value (which is read) and the offset. (let ((num (ct_read real_stream))) (ct_send self 'set_pointer (+ num (* *directory_number_width* (+ 2 file_size))))) ;; Finally, update filepos to indicate which element is next. (setq filepos n)) ;;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file init_filepos) () ;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Sets filepos to 1, and actually positions file pointer there, too. (setq filepos 1) (ct_send self 'set_pointer (* *directory_number_width* (+ 2 file_size)))) ;;;;;;;;;;;;;;;;;;;;;;;;; (ct_defmethod (ada_direct_io_file set_pointer) (pos) ;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Sets the file pointer (bytes). #+lispm (set-iv ada_direct_io_file real_stream 'pointer pos) #+franz (fseek real_stream pos 0) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Externally Call-able Functions/Macros/Methods -- ;;;;;;;;;;;;;;;;;;;; (defun ada_direct_io_create (ar) ;;;;;;;;;;;;;;;;;;;; ;;; Creates a new file for read, write, or both. Form is ignored. (with_ada_parameters ((file file) (mode mode) (name name) (form form)) (let* ((fmod (#+lispm string #+franz get_pname (implode (second (diana_get mode 'lx_symrep))))) (fmode (cond ((equal "in_file" fmod) 'in) ((equal "out_file" fmod) 'out) (t 'inout))) (fname (#+lispm string #+franz get_pname (implode (second (convert_strrep_to_lexstr name))))) (fform (#+lispm string #+franz get_pname (implode (second (convert_strrep_to_lexstr form))))) (ffile (ct_if (eq (get_filepos file) 0) nil (get_filepos file))) (fileo (cond (ffile (ct_nth ffile **direct_file_list**)) (t nil))) (tempfile nil)) (ct_if fileo (ada_raise '|status_error| "File object already exists.")) (ct_if (null fmode) (setq fmode 'inout)) (ct_if (not (memq fmode '(in out inout))) (ada_raise '|status_error| "Mode must be in, out, or inout")) (ct_if (equal fname "") ; Implies file is temporary. (setq fname (ct_string_append "#junk" (#+lispm string #+franz get_pname (gensym))) tempfile t )) (let* ((f (errset (ct_open_out fname) nil)) ;Open up file (to create it). (f (ct_if f (car f) (ada_raise '|name_error| "cannot create the file")))) (fixed_width_print 0 f) ;Indicate 0 records. (fixed_width_print 0 f) ;Print dummy address. (ct_closef f)) (setq fileo (open_direct fname fmode)) (setq **direct_file_list** (nconc **direct_file_list** (list fileo)) file (list **direct_file_num**) **direct_file_num** (add1 **direct_file_num**)) (ct_send fileo 'd_init) (ct_if tempfile (setq *dirio_temp_file_list* (cons fileo *dirio_temp_file_list*))) (list file mode name form)))) ;;;;;;;;;;;;;;;;;;;; (defun ada_direct_io_open (ar) ;;;;;;;;;;;;;;;;;;;; ;;; Opens an existing file for input. (with_ada_parameters ((file file) (mode mode) (name name) (form form)) (let* ((fmode (cond ((equal mode '*unassigned*) (ada_raise '|program_error| "unassigned MODE")) (t (#+lispm string #+franz get_pname (implode (second (diana_get mode 'lx_symrep))))) )) (fmode (cond ((equal "in_file" fmode) 'in) ((equal "out_file" fmode) 'out) (t 'inout))) (fform (#+lispm string #+franz get_pname (implode (second (convert_strrep_to_lexstr form))))) (fname (cond ((equal name '*unassigned*) "") (t (#+lispm string #+franz get_pname (implode (second (convert_strrep_to_lexstr name))))) )) (ffile (ct_if (eq (get_filepos file) 0) nil (get_filepos file))) (fileo (cond ((null ffile) (ct_if (not (ct_probef fname)) (ada_raise '|name_error| "Non-existing file cannot be open") (find_ada_file fname **sequen_file_list**))) (t (ct_nth ffile **direct_file_list**)))) ) (ct_if (not (ct_probef fname)) (ada_raise '|name_error| "File does not exist.")) (ct_if (null fmode) (setq fmode 'inout)) (ct_if (not (memq fmode '(in out inout))) (ada_raise '|status_error| "Mode must be in, out or inout.")) (setq fileo (let ((f (errset (open_direct fname fmode) nil))) (ct_if f (car f) (ada_raise '|name_error| "cannot open file")))) (setq **direct_file_list** (nconc **direct_file_list** (list fileo)) file (list **direct_file_num**) **direct_file_num** (add1 **direct_file_num**)) (ct_send fileo 'd_init) (list file mode name form)))) ;;;;;;;;;;;;;;;;;;;;; (defun ada_direct_io_close (ar) ;;;;;;;;;;;;;;;;;;;;; ;;; Closes a file, writing the write_cache iff it has changed. This ;;; is determined easily by looking at the max_key. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (neq (get-iv ada_direct_io_file fileo 'status) 'open) (ada_raise '|status_error| "Cannot close a file already closed.")) (ct_if (not (zerop (ht_max_key (ct_send fileo 'write_cache)))) (dump_cache (ct_send fileo 'write_cache)));Dump contents of cache, (set-iv ada_direct_io_file fileo 'status 'closed) ;;which rewrites the file. (ct_closef (ct_send fileo 'real_stream)) (list file)))) ;;;;;;;;;;;;;;;;;;;; (defun ada_direct_io_delete (ar) ;;;;;;;;;;;;;;;;;;;; ;;; Closes and deletes the file. Does not bother to write the write ;;; cache, since it would be lost anyway. (with_ada_parameters ( (file file)) (let* ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (neq (get-iv ada_direct_io_file fileo 'status) 'open) (ada_raise '|status_error| "Cannot delete a file already closed.")) (set-iv ada_direct_io_file fileo 'status 'closed) (ct_closef (get-iv ada_direct_io_file fileo 'real_stream)) (ct_deletef (get-iv ada_direct_io_file fileo 'name)) (list file)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ada_direct_io_reset_with_mode (ar) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; procedure RESET (FILE : in out FILE_TYPE; ;;; MODE : in FILE_MODE); ;;; ;;; Resets the given file so that reading from or writing to its ;;; elements can be restarted from the beginning of the file; in ;;; particular, for direct access this means that the current index ;;; is set to 1. If a MODE parameter is supplied, the current mode ;;; of the given file is set to the given mode. ;;; ;;; The exception STATUS_ERROR is raised if the file is not open. ;;; The exception USE_ERROR is raised if the environment does not ;;; support resetting for the external file and, also, if the environ- ;;; ment does not support resetting to the specified mode for the ;;; external file. ;;; {Operates by closing and re-opening the file.} (with_ada_parameters ((file file) (mode mode)) (let* ((fmode (#+lispm string #+franz get_pname (implode (second (diana_get mode 'lx_symrep))))) (fmode (cond ((equal "in_file" fmode) 'in) ((equal "out_file" fmode) 'out) (t 'inout))) (fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (neq (get-iv ada_direct_io_file fileo 'status) 'open) (ada_raise '|status_error| "File not open, cannot reset.")) (ct_if (not (memq fmode '(inout in out))) (ada_raise '|status_error| "Mode must be in_file or out_file when resetting.")) (ct_if (not (zerop (ht_max_key (ct_send fileo 'write_cache)))) (dump_cache (ct_send fileo 'write_cache))) (ct_send fileo 'reset) (set-iv ada_direct_io_file fileo 'index 1) ; Next read or write at index=1. (set-iv ada_direct_io_file fileo 'mode fmode) (list file fmode)))) ;;;;;;;;;;;;;;;;;;; (defun ada_direct_io_reset (ar) ;;;;;;;;;;;;;;;;;;; ;;; Resets the file, and NO mode change. ;;; Mode defaults to previous. Does not actually write anything to disk. ;;; You must close file to get a permanent copy. (with_ada_parameters ((file file)) (let* ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (not (zerop (ht_max_key (ct_send fileo 'write_cache)))) (dump_cache (ct_send fileo 'write_cache))) (ct_send fileo 'reset) (set-iv ada_direct_io_file fileo 'index 1) ; Next read or write at index=1. (list file )))) ;;; Returns the file's mode. FUNCTION ;;;;;;;;;;;;;;;;;; (defun ada_direct_io_mode (ar) ;;;;;;;;;;;;;;;;;; (with_ada_parameters ((file file)) (let* ((fileo (ct_nth (file_exist_check file) **direct_file_list**)) (fmode (get-iv ada_direct_io_file fileo 'mode)) (str (cond ((equal fmode 'in) "in_file") ((equal fmode 'out) "out_file") (t "inout_file")))) (ct_if (neq (get-iv ada_direct_io_file fileo 'status) 'open) (ada_raise '|status_error| "File must be open.")) (get-enum str *activation*)))) ;;;;;;;;;;;;;;;;;; (defun ada_direct_io_name (ar) ;;;;;;;;;;;;;;;;;; ;;; Returns the file's name. FUNCTION. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (neq (get-iv ada_direct_io_file fileo 'status) 'open) (ada_raise '|status_error| "File must be open.")) (cons 'lex_string (list (exploden (get-iv ada_direct_io_file fileo 'name))))))) ;;;;;;;;;;;;;;; (defun ada_direct_io_form (ar) ;;;;;;;;;;;;;;; ;;; Returns the file's form. FUNCTION. (We don't use forms, so the null ;;; string is always returned. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (neq (get-iv ada_direct_io_file fileo 'status) 'open) (ada_raise '|status_error| "File must be open.")) (cons 'lex_string (list nil))))) ;;;;;;;;;;;;;;;;;; (defun ada_direct_io_is_open (ar) ;;;;;;;;;;;;;;;;;; ;;; Returns T if file is open; nil if not. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'open) (get-enum "true" *activation*) (get-enum "false" *activation*))))) ;;;;;;;;;;;;;;;; (defun ada_direct_io_write (ar) ;;;;;;;;;;;;;;;; ;;; write a item into the write cache following current index ;;; increment the index after writing. (with_ada_parameters ((file file) (item item)) (let* ((fileo (ct_nth (file_exist_check file) **direct_file_list**)) (to (get-iv ada_direct_io_file fileo 'index)) (itemo (ada_parameter item)) (base 10.) (ibase 10.) (prinlength nil) (prinlevel nil)) (ct_if (eq (get-iv ada_direct_io_file fileo 'mode) 'in) (ada_raise '|mode_error| "File must be open for output.")) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'closed) (ada_raise '|status_error| "File must be open to write.")) (ct_puthash to (get_direct_io_value item itemo) (ct_send fileo 'write_cache)) (set-iv ada_direct_io_file fileo 'index (add1 to)) (list item file to)))) ;;;;;;;;;;;;;;;; (defun ada_direct_io_write_to (ar) ;;;;;;;;;;;;;;;; ;;; Sets Nth element of a file. The index is specified ;;; as given input. Increment the index after ;;; writing. (with_ada_parameters ((file file) (item item) (to to)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**)) (itemo (ada_parameter item)) (base 10.) (ibase 10.) (prinlength nil) (prinlevel nil)) (ct_if (equal to '*unassigned*) (ada_raise '|program_error| "unitialized TO parameter")) (ct_if (eq (get-iv ada_direct_io_file fileo 'mode) 'in) (ada_raise '|mode_error| "File must be open for output.")) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'closed) (ada_raise '|status_error| "File must be open to write.")) (ct_puthash to (get_direct_io_value item itemo) (ct_send fileo 'write_cache)) (set-iv ada_direct_io_file fileo 'index (add1 to)) (list item file to)))) (defun get_direct_io_value (item itemo) (cond ((numberp item) item) ((is_char item) (ct_send itemo 'printvalue)) ((is_array item) (ct_send item 'printvalue)) ((is_enumeration item) (ct_send itemo 'printvalue)) ((ada_record_type itemo) (mapcar '(lambda (x) (ct_send (cdr x) 'printvalue)) (ada_record_value%record item))) (t (print "Unknown Item")))) (defun ada_direct_io_read (ar) ;;;;;;;;;;;;;;; ;;; Retrieves the Next element from the file, checking first to see if ;;; is in the write_cache. Increment the index after reading. (with_ada_parameters ((file file)(item item)) (let* ((fileo (ct_nth (file_exist_check file) **direct_file_list**)) (from (get-iv ada_direct_io_file fileo 'index)) (direct_value (ct_gethash from (ct_send fileo 'write_cache))) (itemo (ada_parameter item)) (base 10.) (ibase 10.) (prinlength nil) (prinlevel nil)) (ct_if (eq (get-iv ada_direct_io_file fileo 'mode) 'out) (ada_raise '|mode_error| "File must be open for input.")) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'closed) (ada_raise '|status_error| "File must be open.")) (ct_if (not direct_value) (setq direct_value (ct_send fileo 'find_element from)) (setq direct_value (cdr direct_value))) (cond ((ada_num_p itemo) (setq item direct_value)) ((ada_array_type itemo) (direct_read_array direct_value item)) ((ada_enumeration_type itemo) (setq item (get_enum_literal direct_value itemo))) ((ada_record_type itemo) (direct_read_record direct_value item)) (t (print "Unknown Item"))) (set-iv ada_direct_io_file fileo 'index (add1 from)) (list item fileo from)))) ;;;;;;;;;;;;;;; (defun ada_direct_io_read_from (ar) ;;;;;;;;;;;;;;; ;;; Retrieves the Nth element from the file, checking first to see if ;;; is in the write_cache. Increment the index after reading. (with_ada_parameters ((file file)(item item)(from from)) (let* ((fileo (ct_nth (file_exist_check file) **direct_file_list**)) (itemo (ada_parameter item)) (direct_value (ct_gethash from (ct_send fileo 'write_cache))) (base 10.) (ibase 10.) (prinlength nil) (prinlevel nil)) (ct_if (equal from '*unassigned*) (ada_raise '|program_error| "unitialized FROM parameter")) (ct_if (eq (get-iv ada_direct_io_file fileo 'mode) 'out) (ada_raise '|mode_error| "File must be open for input.")) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'closed) (ada_raise '|status_error| "File must be open.")) (ct_if (not direct_value) (setq direct_value (ct_send fileo 'find_element from)) (setq direct_value (cdr direct_value))) (cond ((ada_num_p itemo) (setq item direct_value)) ((ada_array_type itemo) (direct_read_array direct_value item)) ((ada_enumeration_type itemo) (setq item (get_enum_literal direct_value itemo))) ((ada_record_type itemo) (direct_read_record direct_value item)) (t (print "Unknown Item"))) (set-iv ada_direct_io_file fileo 'index (add1 from)) (list item fileo from)))) (defun direct_read_array (value item) (let* ((list-of-values value) (arraystorage (get-iv dt_array_type item 'array_storage)) (eleobj #+lispm (aref arraystorage 0) #+franz (arraycall t arraystorage 0))) (fill-in-value-for-array list-of-values item ))) (defun direct_read_record (direct_value item) (let* ((recvalue (ada_record_value%record item)) (num_of_fields (length recvalue)) (nuval direct_value)) (uci_loop (initial count num_of_fields reclist recvalue nuvalue nuval itemo (cdr (first reclist))) (do (cond ((ada_num_p itemo) (ct_send itemo 'set_val nil (car nuvalue))) ((ada_enumeration_type itemo) (ct_send itemo 'set_val nil (get_enum_literal (car nuvalue) itemo))) ((is_array itemo) (fill-in-value-for-array (car nuvalue) itemo)) ((ada_record_type itemo) (fill-in-value-for-record (car nuvalue) itemo)))) (until (eq count 0)) (next count (sub1 count) reclist (cdr reclist) nuvalue (cdr nuvalue) itemo (cdr (first reclist))) ))) ;;;;;;;;;;;;; (defun ada_direct_io_set_index (ar) ;;;;;;;;;;;;; ;;; Sets the index for a given file. An index can be set to any integer ;;; you wish; no checking is done for validity until read happens. (You ;;; can write wherever you please!) (with_ada_parameters ((file file) (to to)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (equal to '*unassigned*) (ada_raise '|program_error| "unitialized TO parameter")) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'closed) (ada_raise '|status_error| "File must be open to set index.")) (set-iv ada_direct_io_file fileo 'index to) (list file to)))) ;;;;;;;;; (defun ada_direct_io_index (ar) ;;;;;;;;; ;;; Returns the current index. This is an Ada function. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (ct_if (eq (get-iv ada_direct_io_file fileo 'status) 'closed) (ada_raise '|status_error| "File must be open.")) (get-iv ada_direct_io_file fileo 'index)))) ;;;;;;;; (defun ada_direct_io_size (ar) ;;;;;;;; ;;; Returns the number of records (elements) in the file. This is an ;;; Ada FUNCTION. The file size can be changed by writing, but not by ;;; reading. The number returned includes elements in the write cache. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**))) (max (get-iv ada_direct_io_file fileo 'file_size) (ht_max_key (ct_send fileo 'write_cache)))))) ;;;;;;;;;;;;;;; (defun ada_direct_io_end_of_file (ar) ;;;;;;;;;;;;;;; ;;; Returns T or NIL (which should translate to an Ada boolean). ;;; T indicates the index is greater than the size of the file. (with_ada_parameters ((file file)) (let ((fileo (ct_nth (file_exist_check file) **direct_file_list**)) (base 10.) (ibase 10.)) (ct_if (eq (get-iv ada_direct_io_file fileo 'mode) 'out) (ada_raise '|mode_error| "Direct out files never reach EOF.") (let ((val (> (get-iv ada_direct_io_file fileo 'index) (max (get-iv ada_direct_io_file fileo 'file_size) (ht_max_key (ct_send fileo 'write_cache)))))) (ct_if val (get-enum "true" *activation*) (get-enum "false" *activation*))))))) (ct_defmethod (ada_direct_io_file delete) () (ct_if (neq status 'open) (ada_raise '|status_error| "Cannot delete a file already closed.")) (setq status 'closed) (ct_deletef real_stream)) (ct_defmethod (ada_direct_io_file close) () (ct_if (neq status 'open) (ada_raise '|status_error| "Cannot close a file already closed.")) (setq status 'closed) (ct_closef real_stream)) (defun dir_get_status (fileo) (get-iv ada_direct_io_file fileo 'status)) (defun delete_dirio_temp_file () (progn (ct_if *dirio_temp_file_list* (aip_for (x in *dirio_temp_file_list*) (when (eq (dir_get_status x) 'open)) (do (ct_send x 'delete)))) (ct_if **direct_file_list** (aip_for (x in **direct_file_list**) (when (eq (dir_get_status x) 'open)) (do (ct_send x 'close)))) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; eof ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;