;;;; 	Copyright (C) 1995 Free Software Foundation, Inc.
;;;; 
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;; 
;;;; This program 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 General Public License for more details.
;;;; 
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING.  If not, write to
;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;;;; 


;;; Parts of this file derived from
;;; 	"Init.scm", Scheme initialization code for SCM.
;;; 	Copyright (C) 1991, 1992, 1993, 1994 Aubrey Jaffer.
;;;


;;; {Features}
;;;
;;; Features are named options which may or may not be present
;;; in an interpreter.  They can be tested for either by code or
;;; by conditional forms (e.g. "#- hash (load-slib-hash)" )
;;;

(set! *features*
      (append '(getenv tmpnam abort transcript with-file
		ieee-p1178 rev4-report rev4-optional-procedures
		hash object-hash delay eval dynamic-wind
		multiarg-apply multiarg/and- logical defmacro
		string-port source current-time)
	      *features*))


;; Evaluate a boolean expression whose terms are feature names.
;;
(define (read:eval-feature exp)
  (cond ((symbol? exp)
	 (or (memq exp *features*) (eq? exp (software-type))))
	((and (pair? exp) (list? exp))
	 (case (car exp)
	   ((not) (not (read:eval-feature (cadr exp))))
	   ((or) (if (null? (cdr exp)) #f
		     (or (read:eval-feature (cadr exp))
			 (read:eval-feature (cons 'or (cddr exp))))))
	   ((and) (if (null? (cdr exp)) #t
		      (and (read:eval-feature (cadr exp))
			   (read:eval-feature (cons 'and (cddr exp))))))
	   (else (error "read:sharp+ invalid expression " exp))))))


;;; {Reader Extensions}
;;;
;;; Reader code for various "#c" forms.
;;;

  
(define (read:sharp c port)
  (define (barf)
    (error "unknown # object" c))
  (case c ((#\') (read port))
	((#\+) (if (read:eval-feature (read port))
		   (read port)
		   (begin (read port) (if #f #f))))
	((#\-) (if (not (read:eval-feature (read port)))
		   (read port)
		   (begin (read port) (if #f #f))))
	((#\b) (read:uniform-vector #t port))
	((#\a) (read:uniform-vector #\a port))
	((#\u) (read:uniform-vector 1 port))
	((#\e) (read:uniform-vector -1 port))
	((#\s) (read:uniform-vector 1.0 port))
	((#\i) (read:uniform-vector 1/3 port))
	((#\c) (read:uniform-vector 0+i port))
	((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
	 (read:array c port))
	((#\!) (if (= 1 (line-number))
		   (let skip () (if (eq? #\newline (peek-char port))
				    (if #f #f)
				    (begin (read-char port) (skip))))
		   (barf)))
	(else (barf))))

(define (read:array digit port)
  (define chr0 (char->integer #\0))
  (let ((rank (let readnum ((val (- (char->integer digit) chr0)))
		(if (char-numeric? (peek-char port))
		    (readnum (+ (* 10 val)
				(- (char->integer (read-char port)) chr0)))
		    val)))
	(prot (if (eq? #\( (peek-char port))
		  '()
		  (let ((c (read-char port)))
		    (case c ((#\b) #t)
			  ((#\a) #\a)
			  ((#\u) 1)
			  ((#\e) -1)
			  ((#\s) 1.0)
			  ((#\i) 1/3)
			  ((#\c) 0+i)
			  (else (error "read:array unknown option " c)))))))
    (if (eq? (peek-char port) #\()
	(list->uniform-array rank prot (read port))
	(error "read:array list not found"))))

(define (read:uniform-vector proto port)
  (if (eq? #\( (peek-char port))
      (list->uniform-array 1 proto (read port))
      (error "read:uniform-vector list not found")))


;;; {Here are Some Revised^2 Scheme Functions}
;;;

(define 1+
  (let ((+ +))
    (lambda (n) (+ n 1))))

(define -1+
  (let ((+ +))
    (lambda (n) (+ n -1))))

(define 1- -1+)
(define <? <)
(define <=? <=)
(define =? =)
(define >? >)
(define >=? >=)
(define t #t)
(define nil #f)
(define sequence begin)

(set! apply (lambda (fun . args) (@apply fun (apply:nconc-to-last args))))

(define (call-with-current-continuation proc)
  (@call-with-current-continuation proc))



;;; {Slib-ish Names for Bit-twiddling Functions}
;;;

(define logical:logand logand)
(define logical:logior logior)
(define logical:logxor logxor)
(define logical:lognot lognot)
(define logical:ash ash)
(define logical:logcount logcount)
(define logical:integer-length integer-length)
(define logical:bit-extract bit-extract)
(define logical:integer-expt integer-expt)

(define (logical:ipow-by-squaring x k acc proc)
  (cond ((zero? k) acc)
	((= 1 k) (proc acc x))
	(else (logical:ipow-by-squaring (proc x x)
					(quotient k 2)
					(if (even? k) acc (proc acc x))
					proc))))



;;; {Basic Port Code}
;;; 
;;; Specificly, the parts of the low-level port code that are written in 
;;; Scheme rather than C.
;;;

;; VMS does something strange when output is sent to both
;; CURRENT-OUTPUT-PORT and CURRENT-ERROR-PORT.
(case (software-type)
  ((VMS) (set-current-error-port (current-output-port))))

;; OPEN_READ, OPEN_WRITE, and OPEN_BOTH are used to request the proper
;; mode to open files in.  MSDOS does carraige return - newline
;; translation if not opened in `b' mode.
;;
(define OPEN_READ (case (software-type)
		    ((MS-DOS WINDOWS ATARIST) "rb")
		    (else "r")))
(define OPEN_WRITE (case (software-type)
		     ((MS-DOS WINDOWS ATARIST) "wb")
		     (else "w")))
(define OPEN_BOTH (case (software-type)
		    ((MS-DOS WINDOWS ATARIST) "r+b")
		    (else "r+")))

;; A hook called for files that can not be opened.
;; !!! Fixme!  This hook should take the same arguments
;; as open-file.  Perhaps there should be support for
;; something like Emacs' file-name-handler-alist.
;;
(define could-not-open #f)

(define (open-input-file str)
  (or (open-file str OPEN_READ)
      (and (procedure? could-not-open) (could-not-open) #f)
      (error "OPEN-INPUT-FILE couldn't find file " str)))

(define (open-output-file str)
  (or (open-file str OPEN_WRITE)
      (and (procedure? could-not-open) (could-not-open) #f)
      (error "OPEN-OUTPUT-FILE couldn't find file " str)))

(define (open-io-file str) (open-file str OPEN_BOTH))

(define close-input-port close-port)
(define close-output-port close-port)
(define close-io-port close-port)

(define (call-with-input-file str proc)
  (let* ((file (open-input-file str))
	 (ans (proc file)))
    (close-input-port file)
    ans))

(define (call-with-output-file str proc)
  (let* ((file (open-output-file str))
	 (ans (proc file)))
    (close-output-port file)
    ans))

(define (with-input-from-port port thunk)
  (let* ((swaports (lambda () (set! port (set-current-input-port port)))))
    (dynamic-wind swaports thunk swaports)))

(define (with-output-to-port port thunk)
  (let* ((swaports (lambda () (set! port (set-current-output-port port)))))
    (dynamic-wind swaports thunk swaports)))

(define (with-error-to-port port thunk)
  (let* ((swaports (lambda () (set! port (set-current-error-port port)))))
    (dynamic-wind swaports thunk swaports)))

(define (with-input-from-file file thunk)
  (let* ((nport (open-input-file file))
	 (ans (with-input-from-port nport thunk)))
    (close-port nport)
    ans))

(define (with-output-to-file file thunk)
  (let* ((nport (open-output-file file))
	 (ans (with-output-to-port nport thunk)))
    (close-port nport)
    ans))

(define (with-error-to-file file thunk)
  (let* ((nport (open-output-file file))
	 (ans (with-error-to-port nport thunk)))
    (close-port nport)
    ans))


(if (not (defined? output-port-width))
    (define (output-port-width . arg) 80))

(if (not (defined? output-port-height))
    (define (output-port-height . arg) 24))

(define (with-input-from-string thunk)
  (call-with-input-string
   (lambda (p) (with-input-from-port p (thunk)))))

(define (with-output-to-string thunk)
  (call-with-input-string
   (lambda (p) (with-output-to-port p (thunk)))))

(define (with-error-to-string thunk)
  (call-with-input-string
   (lambda (p) (with-error-to-port p (thunk)))))



;;; {Symbol Properties}
;;;

(define (symbol-property sym prop)
  (let ((pair (assoc prop (symbol-pref sym))))
    (and pair (cdr pair))))

(define (set-symbol-property! sym prop val)
  (let ((pair (assoc prop (symbol-pref sym))))
    (if pair
	(set-cdr! pair val)
	(symbol-pset! sym (acons prop val (symbol-pref sym))))))

(define (set-symbol-property! sym prop val)
  (let ((pair (assoc prop (symbol-pref sym))))
    (if pair
	(set-cdr! pair val)
	(symbol-pset! sym (acons prop val (symbol-pref sym))))))

(define (symbol-property-remove! sym prop)
  (let ((pair (assoc prop (symbol-pref sym))))
    (if pair
	(symbol-pset! sym (delq! pair (symbol-pref sym))))))


;;; {Error Handling}
;;;


;; %%bad-throw is the hook that is called upon a throw to a an unhandled
;; key.  If the key has a default handler (a throw-handler-default property),
;; it is applied to the throw.
;;
(define (%%bad-throw key . args)
  (let ((default (and (symbol? key)
		      (symbol-property key 'throw-handler-default))))
    (and default (apply default key args))))

;; (error . args) is short for (throw (quote error) . args)
;;
(define (error . args)
  (apply throw 'error args))

;; Error handling a la SCM.
;;
(define (%%default-error-handler ignored . args)
  (define cep (current-error-port))
  (perror "ERROR")
  (errno 0)
  (display "ERROR: " cep)
  (if (not (null? args))
      (begin (display (car args) cep)
	     (for-each (lambda (x) (display #\  cep) (write x cep))
		       (cdr args))))
  (newline cep)
  (force-output cep)
  (abort))

;; Install SCM error handling as the default.
;;
(set-symbol-property! 'error
		      'throw-handler-default
		      %%default-error-handler)

;; A number of internally defined error types are represented
;; as integers.  Here is the mapping to symbolic names
;; and error messages.
;;
(define %%system-errors
  '((-1 UNKNOWN "Unknown error")
    (0 ARGn  "Wrong type argument to ")
    (1 ARG1  "Wrong type argument in position 1 to ")
    (2 ARG2  "Wrong type argument in position 2 to ")
    (3 ARG3  "Wrong type argument in position 3 to ")
    (4 ARG4  "Wrong type argument in position 4 to ")
    (5 ARG5  "Wrong type argument in position 5 to ")
    (6 ARG5  "Wrong type argument in position 5 to ")
    (7 ARG5  "Wrong type argument in position 5 to ")
    (8 WNA "Wrong number of arguments to ")
    (9 OVFLOW "Numerical overflow to ")
    (10 OUTOFRANGE "Argument out of range to ")
    (11 NALLOC "Could not allocate to ")
    (12 EXIT "Exit (internal error?).")
    (13 HUP_SIGNAL "hang-up")
    (14 INT_SIGNAL "user interrupt")
    (15 FPE_SIGNAL "arithmetic error")
    (16 BUS_SIGNAL "bus error")
    (17 SEGV_SIGNAL "segmentation violation")
    (18 ALRM_SIGNAL "alarm")))

;; The default handler for built-in error types when
;; thrown by their symbolic name.
;;
(define (%%handle-system-error ignored desc proc . args)
  (let* ((b (assoc desc %%system-errors))
	 (msghead (cond
		   (b (caddr b))
		   ((or (symbol? desc) (string? desc))
		    (string-append desc " "))
		   (#t "Unknown error")))
	 (msg (if (symbol? proc)
		  (string-append msghead proc ":")
		  msghead))
	 (rest (if (and proc (not (symbol? proc)))
		   (cons proc args)
		   args))
	 (fixed-args (cons msg rest)))
    (apply error fixed-args)))

;; Install default handlers for built-in errors.
;;
(map (lambda (err)
       (set-symbol-property! (cadr err)
			     'throw-handler-default
			     %%handle-system-error))
     %%system-errors)


;; All system errors are thrown as %%system-error.  Here
;; is the default handler that rethrows a more specific 
;; error.
;;
(define (%%generic-system-error-handler ignored desc . args)
  (let ((key (assoc desc %%system-errors)))
    (if key
	(apply throw (cadr key) desc args)
	(apply throw 'UNKNOWN desc args))))

(set-symbol-property! '%%system-error
		      'throw-handler-default
		      %%handle-system-error)





;;; {Misc.}
;;;

(define slib:exit quit)
(define exit quit)

(define (terms)
  (list-file (in-vicinity (implementation-vicinity) "COPYING")))

(define (list-file file)
  (call-with-input-file file
    (lambda (inport)
      (do ((c (read-char inport) (read-char inport)))
	  ((eof-object? c))
	(write-char c)))))

(define (file-exists? str)
  (let ((port (open-file str OPEN_READ)))
    (if port (begin (close-port port) #t)
	#f)))

(define set-errno errno)

(define difftime -)
(define offset-time +)

(if (not (memq 'ed *features*))
    (begin
      (define (ed . args)
	(system (apply string-append
		       (or (getenv "EDITOR") "ed")
		       (map (lambda (s) (string-append " " s)) args))))
      (set! *features* (cons 'ed *features*))))

(define (has-suffix? str suffix)
  (let ((sufl (string-length suffix))
	(sl (string-length str)))
    (and (> sl sufl)
	 (string=? (substring str (- sl sufl) sl) suffix))))

(define slib:error error)
(define slib:tab #\tab)
(define slib:form-feed #\page)
(define slib:eval eval)


;;; {List Comparison}
;;;

;; Compare two lists, describing insertions/deletions needed
;; to change one to the other.
;;
(define (diff-lists a b cmp?)
  (let* ((a-len (length a))
	 (b-len (length b))
	 (memo (make-array #f (+ a-len 1) (+ 1 b-len)))
	 (cost (compute-cost! a a-len b b-len memo cmp?))
	 (cost-at (lambda (x y) (array-ref memo x y))))
    (letrec ((findpath (lambda (aa a-pos bb b-pos)
			 (cond
			  ((eq? a-pos 0) (map (lambda (e) `(+ ,e)) bb))
			  ((eq? b-pos 0) (map (lambda (e) `(- ,e)) aa))
			  ((cmp? (car aa) (car bb))
			   `((.. ,(car aa))
			     ,@(findpath (cdr aa) (+ -1 a-pos)
					 (cdr bb) (+ -1 b-pos))))
			  ((eq? (+ -1 (cost-at a-pos b-pos))
				(cost-at (+ -1 a-pos) b-pos))
			   `((- ,(car aa))
			     ,@(findpath (cdr aa) (+ -1 a-pos) bb b-pos)))
			  (else
			   `((+ ,(car bb))
			     ,@(findpath aa a-pos (cdr bb) (+ -1 b-pos))))))))
      (findpath a a-len b b-len))))


;; Compute the number of insertions/deletions needed to change
;; one list into another.  The memo is a 2d array of at least 
;; a-len X b-len elements.  The memo is used to speed up computing
;; the cost, but really the side effects on the array are interesting
;; output.  Tracing the table later is how a specific sequence
;; of ins/del is computed.
;;

(define (compute-cost! a a-len b b-len memo cmp?)
  (let ((answer
	 (cond
	  ((eq? 0 b-len) a-len)

	  ((eq? 0 a-len) b-len)

	  ((array-ref memo a-len b-len)
	   (array-ref memo a-len b-len))

	  ((cmp? (car a) (car b))
	   (compute-cost! (cdr a) (+ -1 a-len)
			  (cdr b) (+ -1 b-len)
			  memo cmp?))

	  (else
	   (let ((first-way (compute-cost! (cdr a) (+ -1 a-len)
					   b b-len
					   memo cmp?))
		 (second-way (compute-cost! a a-len
					    (cdr b) (+ -1 b-len)
					    memo cmp?)))
	     (+ 1 (min first-way second-way)))))))

    (array-set! memo answer a-len b-len)
    answer))



;;; {File Systems}
;;;



;;; {Load}
;;;

(define load:indent 0)

(define (scm:load file . libs)
  (define sfs (scheme-file-suffix))
  (define cep (current-error-port))
  (define filesuf file)
  (define hss (has-suffix? file sfs))
  (cond ((> (verbose) 1)
 	 (display
 	  (string-append ";" (make-string load:indent #\ ) "loading " file)
 	  cep)
 	 (set! load:indent (modulo (+ 2 load:indent) 16))
 	 (newline cep)))
  (force-output cep)
  (or (and (defined? link:link) (not hss)
	   (or (apply link:link file libs)
	       (and link:able-suffix
		    (let ((fs (string-append file link:able-suffix)))
		      (cond ((not (file-exists? fs)) #f)
			    ((apply link:link fs libs) (set! filesuf fs) #t)
			    (else #f))))))
      (and (null? libs) (try-load file))
      ;;HERE is where the suffix gets specified
      (and (not hss)
	   (begin (set! filesuf (string-append file sfs))
		  (try-load filesuf)))
      (and (procedure? could-not-open) (could-not-open) #f)
      (begin
	(set! load:indent 0)
	(error "LOAD couldn't find file " file)))

  (errno 0)
  (cond ((> (verbose) 1)
	 (set! load:indent (modulo (+ -2 load:indent) 16))
	 (display (string-append ";" (make-string load:indent #\ )
				 "done loading " filesuf)
		  cep)
	 (newline cep)
	 (force-output cep))))

(define (scm:load-source file)
  (define sfs (scheme-file-suffix))
  (define cep (current-error-port))
  (define filesuf file)
  (cond ((> (verbose) 1)
	 (display ";loading " cep) (write file cep) (newline cep)))
  (force-output cep)
  (or (and (or (try-load file)
	       ;;HERE is where the suffix gets specified
	       (and (not (has-suffix? file sfs))
		    (begin (set! filesuf (string-append file sfs))
			   (try-load filesuf)))))
      (and (procedure? could-not-open) (could-not-open) #f)
      (error "LOAD couldn't find file " file))
  (errno 0)
  (cond ((> (verbose) 1)
	 (display ";done loading " cep) (write filesuf cep) (newline cep)
	 (force-output cep))))


;; library-vicinity should return the pathname of the
;; directory where files of Scheme library functions reside.
;;
(define library-vicinity
  (let ((library-path
	 (or (getenv "SCHEME_LIBRARY_PATH")
	     (case (software-type)
	       ((UNIX COHERENT) (or (and (defined? compiled-library-path)
					 (compiled-library-path))
				    "/usr/local/lib/slib/"))
	       ((VMS) "lib$scheme:")
	       ((MS-DOS WINDOWS ATARIST) "C:\\SCM\\SLIB\\")
	       ((OS/2) "\\languages\\scm\\slib\\")
	       ((MACOS THINKC) "camus Napoleon:Think C4.0:scm3.0:")
	       ((AMIGA) "Scheme:libs/")
	       (else "")))))

    (lambda () library-path)))

;; program-vicinity is here in case the Scheme Library cannot be found.
;;
(define program-vicinity
  (let ((*vicinity-suffix*
	 (case (software-type)
	   ((UNIX COHERENT) '(#\/))
	   ((AMIGA) '(#\: #\/))
	   ((VMS) '(#\: #\]))
	   ((MS-DOS WINDOWS ATARIST OS/2) '(#\\))
	   ((MACOS THINKC) '(#\:)))))
    (lambda ()
      (let loop ((i (- (string-length *load-pathname*) 1)))
	(cond ((negative? i) "")
	      ((memv (string-ref *load-pathname* i)
		     *vicinity-suffix*)
	       (substring *load-pathname* 0 (+ i 1)))
	      (else (loop (- i 1))))))))

;;; Here for backward compatability
;;
(define scheme-file-suffix
  (case (software-type)
    ((NOSVE) (lambda () "_scm"))
    (else (lambda () ".scm"))))

(define in-vicinity string-append)

;;; This is the vicinity where this file resides.
(define implementation-vicinity
  (let ((vic (program-vicinity)))
    (lambda () vic)))

(define load scm:load)
(define slib:load load)
(define slib:load-source scm:load-source)

(cond ((try-load
	(in-vicinity (library-vicinity) "require" (scheme-file-suffix))))
      (else
       (perror "WARNING")
       (display "WARNING: Couldn't find require.scm in (library-vicinity)"
		(current-error-port))
       (write (library-vicinity) (current-error-port))
       (newline (current-error-port))
       (errno 0)))

;;; DO NOT MOVE!  This has to be done after "require.scm" is loaded.
(define slib:load-source scm:load-source)
(define slib:load scm:load)



;;; {Autoloads for SLIB Procedures}
;;;

(define (tracef . args) (require 'trace) (apply tracef args))
(define (trace:tracef . args) (require 'trace) (apply trace:tracef args))
(define (pretty-print . args) (require 'pretty-print)
  (apply pretty-print args))
(define (pp . args) (apply pretty-print args))
(define (pk key val) (pp (list key val)) val)
(define (print . args) (require 'debug) (apply print args))
(define (hash-inquirer . args)
  (require 'hash-table)
  (apply hash-inquirer args))
(define (hash-associator . args)
  (require 'hash-table)
  (apply hash-associator args))
(define (make-hash-table . args)
  (require 'hash-table)
  (apply make-hash-table args))


;;; {Dynamic Loading}
;;;

(if (or (defined? dld:link)
	(defined? shl:load)
	(defined? vms:dynamic-link-call)
	(file-exists? (in-vicinity (implementation-vicinity) "hobbit.tms")))
    (try-load (in-vicinity (implementation-vicinity)
			   "Link" (scheme-file-suffix))))

(cond ((defined? link:link)
       (define slib:load-compiled link:link)
       (provide 'compiled)))

;;; {Macros}
;;;

;defmacro from dorai@cs.rice.edu (heavily hacked by jaffer):
(define *defmacros* '())
(define (defmacro? m) (and (assq m *defmacros*) #t))

(define defmacro:transformer
  (lambda (f)
    (procedure->memoizing-macro
      (lambda (exp env)
	(copy-tree (apply f (cdr exp)))))))

(define defmacro
  (let ((defmacro-transformer
	  (lambda (name parms . body)
	    (let ((transformer `(lambda ,parms ,@body)))
	      `(define ,name
		 (,(lambda (transformer)
		     (set! *defmacros* (acons name transformer *defmacros*))
		     (defmacro:transformer transformer))
		  ,transformer))))))
    (set! *defmacros* (acons 'defmacro defmacro-transformer *defmacros*))
    (defmacro:transformer defmacro-transformer)))

(define (macroexpand-1 e)
  (if (pair? e) (let ((a (car e)))
		  (cond ((symbol? a) (set! a (assq a *defmacros*))
				     (if a (apply (cdr a) (cdr e)) e))
			(else e)))
      e))

(define (macroexpand e)
  (if (pair? e) (let ((a (car e)))
		  (cond ((symbol? a)
			 (set! a (assq a *defmacros*))
			 (if a (macroexpand (apply (cdr a) (cdr e))) e))
			(else e)))
      e))

(define gentemp
  (let ((*gensym-counter* -1))
    (lambda ()
      (set! *gensym-counter* (+ *gensym-counter* 1))
      (string->symbol
       (string-append "scm:G" (number->string *gensym-counter*))))))

(define defmacro:eval slib:eval)
(define defmacro:load load)

(define (slib:eval-load <filename> evl)
  (if (not (file-exists? <filename>))
      (set! <filename> (string-append <filename> (scheme-file-suffix))))
  (call-with-input-file <filename>
    (lambda (port)
      (let ((old-load-pathname *load-pathname*))
	(set! *load-pathname* <filename>)
	(do ((o (read port) (read port)))
	    ((eof-object? o))
	  (evl o))
	(set! *load-pathname* old-load-pathname)))))


;;; {Some Handy Macros}
;;;

;;; Trace gets redefmacroed when tracef autoloads.
(defmacro trace x
  (if (null? x) '()
      `(begin ,@(map (lambda (x) `(set! ,x (trace:tracef ,x ',x))) x))))

(defmacro defvar (var val)
  `(if (not (defined? ,var)) (define ,var ,val)))


;;; {Transcendental Functions}
;;;

(cond ((and (inexact? (string->number "0.0")) (not (defined? exp)))
       (if (defined? usr:lib)
	   (load (in-vicinity (implementation-vicinity) "Transcen")
		 (usr:lib "m"))
	   (load (in-vicinity (implementation-vicinity) "Transcen"
			      (scheme-file-suffix))))
       (set! abs magnitude)))


;;; {Arrays}
;;;

(if (defined? array?)
    (begin
      (define uniform-vector? array?)
      (define make-uniform-vector dimensions->uniform-array)
;      (define uniform-vector-ref array-ref)
      (define (uniform-vector-set! u i o)
	(uniform-vector-set1! u o i))
      (define uniform-vector-fill! array-fill!)
      (define uniform-vector-read! uniform-array-read!)
      (define uniform-vector-write uniform-array-write)

      (define (make-array fill . args)
	(dimensions->uniform-array args () fill))
      (define (make-uniform-array prot . args)
	(dimensions->uniform-array args prot))
      (define (list->array ndim lst)
	(list->uniform-array ndim '() lst))
      (define (list->uniform-vector prot lst)
	(list->uniform-array 1 prot lst))
      (define (array-shape a)
	(map (lambda (ind) (if (number? ind) (list 0 (+ -1 ind)) ind))
	     (array-dimensions a)))))


;;; {Lvectors}
;;;

;; These are the offsets of hook functions within a type lvector.
;; They must agree with declarations in scm.h (bleah)
;;
(define lvector_hook_ref_fn 1)
(define lvector_hook_set_fn 2)
(define lvector_hook_print_fn 3)
(define lvector_hook_equal_fn 4)
(define lvector_hook_isa_fn 5)


;;; {The Module System}
;;;

(load (in-vicinity (implementation-vicinity) "Gmodules.scm"))


;;; {Running Repls}
;;;


;; Mystery integer passed to error handlers:
;;
(define repl-quit -1)
(define repl-abort -2)
(define on-a-new-stack #f)

(define (verbose-repl verbosity prompt env)
  (let ((old-v (verbose)))
    (dynamic-wind
     (lambda () (set! old-v (verbose verbosity)))
     (lambda () (repl prompt env))
     (lambda () (set! verbosity (verbose old-v))))))

(define guile-prompt "guile> ")
(define (guile-repl-thunk)
  (verbose-repl (gscm-default-verbosity) guile-prompt #f))

(define %%repl-thunk guile-repl-thunk)

(define rooted-repl
  (lambda (inp)
    (with-dynamic-root
     (lambda ()
       (let ((new-stack-req
	      (call-with-current-continuation
	       (lambda (cc)
		 (set! on-a-new-stack
		       (lambda (thunk)
			 (call-with-current-continuation
			  (lambda (cc2) (cc (cons thunk cc2))))))
		 (with-input-from-port inp
		   (lambda () (%%repl-thunk)))))))
	 ((cdr new-stack-req) ((car new-stack-req)))))
     (lambda (errcode)
       (with-input-from-port inp
	 (lambda ()
	   (cond
	    ((= errcode repl-quit) #t)
	    (#t (%%repl-thunk)))))))))

(define stand-alone-repl
  (let ((stdin *stdin*))
    (lambda () (rooted-repl stdin))))


(define (synthetic-repl prompt read eval print port)
  (let ((repl (lambda ()
		(let loop ((form (begin (prompt) (read))))
		  (print (eval form))
		  (loop (begin (prompt) (read)))))))
    (with-dynamic-root
     (lambda () (with-input-from-port port repl))
     (lambda (errcode)
       (with-input-from-port port
	 (lambda ()
	   (cond
	    ((= errcode repl-quit) #t)
	    (#t (repl)))))))))



;;; {Pleasant Wrappers for System Calls}
;;;

(load (in-vicinity (implementation-vicinity) "Gsystem.scm"))


;;;  {Shorthand for small equal?-based Hash Tables}
;;;

(define aref (hash-inquirer equal?))
(define aset! (hash-associator equal?))
(define (make-table) (make-hash-table 64))



;;; {Parsing and Acting on the Command Line}
;;;

;;; Use *argv* instead of (program-arguments), to allow option
;;; processing to be done on it.
(define *argv* (program-arguments))

;;; This loads the user's initialization file, or files named in
;;; program arguments.

(or
 (eq? (software-type) 'THINKC)
 (member "-no-init-file" (program-arguments))
 (try-load
  (in-vicinity
   (let ((home (getenv "HOME")))
     (cond
      (home (case (software-type)
	      ((UNIX COHERENT)
	       (if (char=? #\/ (string-ref home (+ -1 (string-length home))))
		   home			;V7 unix has a / on HOME
		   (string-append home "/")))
	      (else home)))

      ((and (defined? getpw) (defined? geteuid) (getpw (geteuid)))
       (vector-ref (getpw (geteuid)) 5))

      ((defined? user-vicinity) (user-vicinity))

      (t "/")))
   "ScmInit.scm"))
 (errno 0))

(if (not (defined? *R4RS-macro*))
    (define *R4RS-macro* #f))
(if (not (defined? *interactive*))
    (define *interactive* #f))

(if (not (defined? 'type))
    (define type #f))

(cond
 ((and (> (length *argv*) 1) (char=? #\- (string-ref (cadr *argv*) 0)))
  (load (in-vicinity (library-vicinity) "getopt"))
;;; (else
;;;  (define *optind* 1)
;;;  (define getopt:opt #f)
;;;  (define (getopt argc argv optstring) #f))

  (let* ((simple-opts "muqvbis")
	 (arg-opts '("a kbytes" "no-init-file" "p number"
				"r feature" "f filename" "l filename"
				"c string" "e string"))
	 (opts (apply string-append ":" simple-opts
		      (map (lambda (o)
			     (string-append (string (string-ref o 0)) ":"))
			   arg-opts)))
	 (argc (length *argv*))
	 (didsomething #f)
	 (moreopts #t))

    (define (do-thunk thunk)
      (if *interactive*
	  (thunk)
	  (let ((complete #f))
	    (with-dynamic-root
	     (lambda ()
	       (thunk)
	       (set! complete #t))
	     (lambda status
	       ;; The thunk tried to escape its continuation in
	       ;; an unusual way.  Give up.
	       (quit))))))

    (define (do-string-arg)
      (require 'string-port)
      (do-thunk
       (lambda ()
	 (eval
	  (call-with-input-string
	   (string-append "(begin " *optarg* ")")
	   read))))
      (set! didsomething #t))

    (define (do-load file)
      (do-thunk
       (lambda ()
	 (cond (*R4RS-macro* (require 'macro) (macro:load file))
	       (else (load file)))))
      (set! didsomething #t))

    (define (usage preopt opt postopt)
      (define cep (current-error-port))
      (define indent (make-string 6 #\ ))
      (define i 3)
      (if (char? opt) (set! opt (string opt)))
      (display (string-append preopt opt postopt) cep)
      (newline cep)
      (display (string-append "Usage: " (car (program-arguments))
			      " [-a kbytes] [-" simple-opts "]") cep)
      (for-each
       (lambda (o)
	 (display (string-append " [-" o "]") cep)
	 (set! i (+ 1 i))
	 (cond ((zero? (modulo i 4)) (newline cep) (display indent cep))))
       (cdr arg-opts))
      (display " [-- | -s | -] [file] [args...]" cep) (newline cep)
      (exit #f))

    ;; -a int => ignore (handled by run_scm)
    ;; -c str => (eval str)
    ;; -e str => (eval str)
    ;; -f str => (load str)
    ;; -l str => (load str)
    ;; -r str => (require str)
    ;; -p int => (verbose int)
    ;; -m     => (set! *R4RS-macro* #t)
    ;; -u     => (set! *R4RS-macro* #f)
    ;; -v     => (verbose 3)
    ;; -q     => (verbose 0)
    ;; -i     => (set! *interactive* #t)
    ;; -b     => (set! *interactive* #f)
    ;; -s     => set argv, don't execute first one
    ;; -no-init-file => don't load init file
    ;; --     => last option

    (let loop ()
      (case (getopt argc *argv* opts)
	((#\a)
	 (cond ((> *optind* 3)
		(usage "scm: option `-" getopt:opt "' must be first"))
	       ((or (not (exact? (string->number *optarg*)))
		    (not (<= 1 (string->number *optarg*) 10000)))
		;;	This size limit should match scm.c ^^
		(usage "scm: option `-" getopt:opt
		       (string-append *optarg* "' unreasonable")))))
	((#\e #\c) (do-string-arg))	;sh-like
	((#\f #\l);;(set-car! *argv* *optarg*)
	 (do-load *optarg*))
	((#\r) (do-thunk (lambda ()
			   (if (and (= 1 (string-length *optarg*))
				    (char-numeric? (string-ref *optarg* 0)))
			       (case (string-ref *optarg* 0)
				 ((#\2) (require 'rev3-procedures)
					(require 'rev2-procedures))
				 ((#\3) (require 'rev3-procedures))
				 ((#\4) (require 'rev4-optional-procedures))
				 ((#\5) (require 'dynamic-wind)
					(require 'values)
					(require 'macro)
					(set! *R4RS-macro* #t))
				 (else (require (string->symbol *optarg*))))
			       (require (string->symbol *optarg*))))))
	((#\p) (verbose (string->number *optarg*)))
	((#\q) (verbose 0))
	((#\v) (verbose 3))
	((#\i) (set! *interactive* #t)	;sh-like
	       (verbose (max 2 (verbose))))
	((#\b) (set! *interactive* #f))
	((#\s) (set! moreopts #f)	;sh-like
	       (set! didsomething #t)
	       (set! *interactive* #t))
	((#\m) (set! *R4RS-macro* #t))
	((#\u) (set! *R4RS-macro* #f))
	((#\n) (if (not (string=? "o-init-file" *optarg*))
		   (usage "scm: unrecognized option `-n" *optarg* "'")))
	((#\:) (usage "scm: option `-" getopt:opt "' requires an argument"))
	((#\?) (usage "scm: unrecognized option `-" getopt:opt "'"))
	((#f) (set! moreopts #f)	;sh-like
	      (cond ((and (< *optind* (length *argv*))
			  (string=? "-" (list-ref *argv* *optind*)))
		     (set! *optind* (+ 1 *optind*)))))
	(else (usage "scm: unknown option `-" getopt:opt "'")))

      (cond ((and moreopts (< *optind* (length *argv*)))
	     (loop))
	    ((< *optind* (length *argv*)) ;No more opts
	     (set! *argv* (list-tail *argv* *optind*))
	     (set! *optind* 1)
	     (cond ((not didsomething) (do-load (car *argv*))
				       (set! *optind* (+ 1 *optind*))))
	     (cond ((and (> (verbose) 2)
			 (not (= (+ -1 *optind*) (length *argv*))))
		    (display "scm: extra command arguments unused:"
			     (current-error-port))
		    (for-each (lambda (x) (display (string-append " " x)
						   (current-error-port)))
			      (list-tail *argv* (+ -1 *optind*)))
		    (newline (current-error-port)))))
	    ((and (not didsomething) (= *optind* (length *argv*)))
	     (set! *interactive* #t)))))

  (cond ((not *interactive*) (quit))
	(*R4RS-macro*
	 (require 'repl)
	 (require 'macro)
	 (let* ((oquit quit))
	   (set! quit (lambda () (repl:quit)))
	   (set! exit quit)
	   (repl:top-level macro:eval)
	   (oquit))))
  ;;otherwise, fall into non-macro SCM repl.
  )
 (else
  (begin (errno 0)
	 (for-each load (cdr (program-arguments))))))


