; -*- Mode:LISP; Package:STEVE; Readtable:CL; Base:10 -*-

;Copyright (c) May 1983 by Christopher Eliot
; and Massachusetts Institute of Technology.  All rights reserved.
;Permission to copy all or part of this material is granted, provided
; that the copies are not made or distributed for resale, the MIT
; copyright notice and reference to the source file and the software
; distribution version appear, and that notice is given that copying
; is by permission of Massachusetts Institute of Technology.

;
;Editor input-output support functions.
;


(defun editor-pathname (pathname)
  (let ((x (errset (pathname pathname) t)))
    (if x (car x)
	(ed-lose "Bad pathname"))))

(defun editor-default (pathname)
  (setq pathname (editor-pathname pathname))
  (let ((defpath (or (and *editor-buffer* (buffer-file-name *editor-buffer*))
		     (merge-pathname-defaults 
		      (user-workingdir-pathname) "foo.lsp"))))
    (merge-pathname-defaults pathname defpath (send defpath :type))))

;File/buffer finding.

(defun select-buffer-named (name last-buffer-selected)
  (let ((buffer (point-selected (if (string= name "")
				    last-buffer-selected
				    name)
				:create nil)))
    (when (null buffer)
      (with-query-line
        (format terminal-io "Can't find buffer named ~a.  Create it? "
		name)
	(if (ed-y-or-n-p "Type Y to create it")
	    (point-selected name)
	    (select-buffer))))))

;File reading.

(eval-when (compile)
  (if (and (fboundp 'frobulate-line-for-read-file)
	   (pairp (fsymeval 'frobulate-line-for-read-file))
	   (eq 'subst (car (fsymeval 'frobulate-line-for-read-file))))
      (fset 'frobulate-line-for-read-file #'list)))

(defun frobulate-record-for-read-file (buffer line string &aux len p)
  (setq len (simple-string-length string))
  (if (not (setq p (%string-posq #\return string 0 len)))
      (frobulate-line-for-read-file buffer line string)
      (do ((i 0)) (nil)
	(let* ((cnt (-& (or p len) i))
	       (new (make-string cnt :initial-element #\space)))
	  (%string-replace new string 0 i cnt)
	  (setq line (frobulate-line-for-read-file buffer line new))
	  (when (null p) (return line))
	  (when (and (<& (setq i (1+& p)) len)
		     (char= (schar string i) #\linefeed))
	    (setq i (1+& i)))
	  (setq p (%string-posq #\return string i (-& len i)))))))

(defun frobulate-line-for-read-file (buffer line string)
  (compiler-let ((compiler:*open-compile-xref-switch t))
    (setf (line-chars line) string)
    (setf (line-char-count line) (simple-string-length string))
    (setf (line-bashed? line) 0)
    (let ((new-line (si:make-extend 8 (get 'line 'si:flavor))))
      (setf (line-buffer new-line) buffer)
      (setf (line-buffer-pointers new-line) nil)
      (setf (line-chars new-line) "")
      (setf (line-char-count new-line) 0)
      (setf (line-previous new-line) line)
      (setf (line-next new-line) nil)
      (setf (line-bashed? new-line) *editor-redisplay-count*)
      (setf (line-graphic-length new-line) #.*:min-fixnum)
      (setf (line-parens new-line) nil)
      (setf (line-string-bits new-line) :reserved-for-future-use)

      (setf (line-next line) new-line)
      new-line)))

;What should this do about BPs in the old buffer?
(defun read-file-into-buffer (pathname &optional (buffer *editor-buffer*)
			      &aux line string eofp)
 (when (not (0p (logand& (buffer-access buffer)
			 buffer-access-buffer-read-only)))
  (ed-warn :read-only))
 (setq line (make-line buffer nil nil))
 (setf (buffer-content buffer) line)
 (loop for point in *all-edit-cursors*
       if (eq (edit-cursor-buffer point) buffer)
       do (progn (setf (edit-cursor-home-line point) line)
		 (setf (edit-cursor-home-pos point) 0)
		 (send point :move line 0)
		 (unless (null (edit-cursor-window point))
		   (setq re-generate-screen-really t))))
 (setf (buffer-modified? buffer) nil)
 (setq pathname (pathname pathname))
 (setf (buffer-access buffer) buffer-access-any)
 (setf (buffer-environment buffer) nil)
 (setq *context-change-flag* t)
 (setf (buffer-mark-ring buffer) (make-vector mark-ring-size :initial-element nil))
 (setf (buffer-mark-ring-index buffer) 0)
 (with-notify-line
  (oustr "Reading File..." terminal-io)
  (send terminal-io :send-if-handles :set-terminal-mode :passall nil)
  (send buffer :set-file-name pathname)
  (if (send pathname :operation-handled-p :open-for-editor)
      (let ((stream nil))
	(unwind-protect
	  (progn (unless (streamp (setq stream (send pathname :open-for-editor
						     :read)))
		   (ed-lose stream))
		 (loop while (setq string (send stream :next-record))
		       do (setq line (frobulate-record-for-read-file
				       buffer line string))))
	  (when (streamp stream) (close stream))))
      (with-open-file (inf pathname :direction :in :noerror t)
	(when (not (streamp inf)) (ed-lose inf))
	(do () (nil)
	  (multiple-value (string eofp) (send inf :readline ()))
	  (when (not (null eofp)) (return nil))
	  (setq line (frobulate-line-for-read-file buffer line string)))))

  (or (null string)
      (frobulate-line-for-read-file buffer line string))
  (setf (buffer-modified? buffer) nil)
  (when (argument?)
    (set-file-read-only buffer))
  (send terminal-io :send-if-handles :set-terminal-mode 
	:passall *editor-device-mode*)))

;File writing.
(defun output-buffer-to-file (filename buffer &optional (access-bypass nil)
			      &aux truename)
  (when (and (not (0p (logand& (buffer-access buffer)
			       buffer-access-file-read-only)))
	     (null access-bypass))
    (ed-lose "File Read Only"))
  (with-notify-line
   (oustr "Writing File..." terminal-io)
   (send terminal-io :send-if-handles :set-terminal-mode :passall nil)
   (if (send (setq filename (pathname filename))
	     :operation-handled-p :open-for-editor)
       (let ((stream nil))
	 (unwind-protect
	   (progn
	     (unless (streamp (setq stream (send filename :open-for-editor
						 :write)))
	       (ed-lose stream))
	     ;Make sure there is a Newline at the end of the buffer
	     (do ((line (buffer-content buffer) next) (next))
		 ((not (setq next (line-next line))) line))
	     (do ((line (buffer-content buffer) next) (next) (char-count))
		 ((null line))
	       (setq char-count (line-char-count line))
	       (when (null (setq next (line-next line)))
		 (when (=& char-count 0) (return nil)))
	       (send stream :write-record (line-chars line) char-count))
	     (close stream)
	     (setq truename (send stream :truename)))
	   (when (and (streamp stream) (eq (send stream :status) :open))
	     (send stream :close :abort))))
       (with-open-file (outf filename :direction :out :noerror t)
	 (unless (streamp outf) (ed-lose outf))
	 (do ((line (buffer-content buffer) (line-next line)))
	     ((null line))
	     (send outf :oustr (line-chars line) 0 (line-char-count line))
	     (and (line-next line) (send outf :terpri)))
	 (close outf)
	 (setq truename (send outf :truename))))
   (send buffer :set-file-name filename)
   (setf (buffer-modified? buffer) nil))
  ;Ditty stuff here.  Compute the string for printing before entering
  ; the with-error-line-remaining construct, so that the pause doesn't
  ; show after the echo-area has been cleared.
  (setq truename (send truename :string-for-printing))
  (with-error-line-remaining (format terminal-io "Written ~a" truename))
  (send terminal-io :send-if-handles :set-terminal-mode 
	:passall *editor-device-mode*))
