;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996 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.
;;
;; As a special exception, the Free Software Foundation gives permission
;; for additional uses of the text contained in its release of GUILE.
;;
;; The exception is that, if you link the GUILE library with other files
;; to produce an executable, this does not by itself cause the
;; resulting executable to be covered by the GNU General Public License.
;; Your use of that executable is in no way restricted on account of
;; linking the GUILE library code into it.
;;
;; This exception does not however invalidate any other reasons why
;; the executable file might be covered by the GNU General Public License.
;;
;; This exception applies only to the code released by the
;; Free Software Foundation under the name GUILE.  If you copy
;; code from other Free Software Foundation releases into a copy of
;; GUILE, as the General Public License permits, the exception does
;; not apply to the code that you add in this way.  To avoid misleading
;; anyone as to the status of such modified files, you must delete
;; this exception notice from them.
;;
;; If you write modifications of your own for GUILE, it is your choice
;; whether to permit this exception to apply to your modifications.
;; If you do not wish that, delete this exception notice.

;;;; "Init.scm", Scheme initialization code for SCM.
;;; Author: Aubrey Jaffer.

(define (scheme-implementation-type) 'SCM)
(define (scheme-implementation-version) "5c3")

(define pathname->vicinity
  (let ((*vicinity-suffix*
	 (case (software-type)
	   ((AMIGA)	'(#\: #\/))
	   ((MS-DOS WINDOWS ATARIST OS/2)	'(#\\ #\/))
	   ((MACOS THINKC)	'(#\:))
	   ((NOSVE)	'(#\: #\.))
	   ((UNIX COHERENT)	'(#\/))
	   ((VMS)	'(#\: #\])))))
    (lambda (pathname)
      ;;Go up one level if PATHNAME ends in a vicinity suffix.
      (let loop ((i (- (string-length pathname) 2)))
	(cond ((negative? i) "")
	      ((memv (string-ref pathname i) *vicinity-suffix*)
	       (substring pathname 0 (+ i 1)))
	      (else (loop (- i 1))))))))

;;; This definition of PROGRAM-VICINITY is equivalent to the one defined
;;;  SLIB/require.scm.  It is used here to bootstrap
;;; IMPLEMENTATION-VICINITY and possibly LIBRARY-VICINITY.

(define (program-vicinity)
  (if *load-pathname*
      (pathname->vicinity *load-pathname*)
      (error "not loading but called" 'program-vicinity)))

(define in-vicinity string-append)

;;; This is the vicinity where this file resides.
(define implementation-vicinity #f)

;;; (library-vicinity) should be defined to be the pathname of the
;;; directory where files of Scheme library functions reside.

;;; If the environment variable SCHEME_LIBRARY_PATH is undefined, use
;;; (implementation-vicinity) as (library-vicinity).  "require.scm",
;;; the first file loaded from (library-vicinity), can redirect it.

(define library-vicinity #f)
(define home-vicinity #f)
(define (set-vicinities!)
  (set! implementation-vicinity
	(let ((vic (if *load-pathname* ;Happens when not dumped.
		       (program-vicinity)
		       (let ((path
			      (or (getenv "SCM_INIT_PATH")
				  (find-init-file (execpath #t)))))
			 (if path
			     (pathname->vicinity path)
			     (or (and (procedure? implementation-vicinity)
				      (implementation-vicinity))
				 (error "Can't find SCM_INIT_PATH")))))))
	  (lambda () vic)))
  (set! library-vicinity
	(let ((library-path (getenv "SCHEME_LIBRARY_PATH")))
	  (if library-path
	      (lambda () library-path)
	      (lambda ()
		(let ((olv library-vicinity)
		      (oload load))
		  (dynamic-wind
		   (lambda () (set! load identity))
		   (lambda ()
		     (try-load (in-vicinity (implementation-vicinity)
					    "require.scm")))
		   (lambda () (set! load oload)))
		  (if (eq? olv library-vicinity)
		      (error "Can't find library-vicinity"))
		  (library-vicinity))))))
  (set! home-vicinity
	(let ((home (getenv "HOME")))
	  (and home
	       (case (software-type)
		 ((UNIX COHERENT MS-DOS)	;V7 unix has a / on HOME
		  (if (not
		       (char=? #\/
			       (string-ref home (+ -1 (string-length home)))))
		      (set! home (string-append home "/"))))))
	  (lambda () home))))
(set-vicinities!)

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

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

(define (exec-self)
  (require 'i/o-extensions)
  (execv (execpath) (program-arguments)))

(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 (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")))

(define (read:sharp c port)
  (define (barf) (error "unknown # object" c))
  (define (feature? exp)
    (cond ((symbol? exp)
	   (or (memq exp *features*) (eq? exp (software-type))))
	  ((and (pair? exp) (list? exp))
	   (case (car exp)
	     ((not) (not (feature? (cadr exp))))
	     ((or) (if (null? (cdr exp)) #f
		       (or (feature? (cadr exp))
			   (feature? (cons 'or (cddr exp))))))
	     ((and) (if (null? (cdr exp)) #t
			(and (feature? (cadr exp))
			     (feature? (cons 'and (cddr exp))))))
	     (else (error "read:sharp+ invalid expression " exp))))))
  (case c ((#\') (read port))
	((#\.) (eval (read port)))
	((#\+) (if (feature? (read port))
		   (read port)
		   (begin (read port) (if #f #f))))
	((#\-) (if (not (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 ((metarg? #f))
		     (case (read-char port)
		       ((#\newline) (if metarg? (skip #t)))
		       ((#\\) (skip #t))
		       ((#\!) (if (not (and (eqv? #\# (peek-char port))
					    (read-char port)))
				  (skip metarg?)))
		       (else (skip metarg?))))
		   (barf)))
	(else (barf))))

(define type 'type)			;for /bin/sh hack.
(define : ':)
(define !#(if #f #f))			;for scsh hack.

;;;; 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 (identity x) x)

(if (not (defined? the-macro))
    (define the-macro identity))
(define sequence (the-macro begin))
(define copy-tree @copy-tree)

;;; 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.  MS-DOS does carriage 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+")))
(define (_IONBF mode) (string-append mode "0"))

(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 open 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 open 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))

(define (warn . args)
  (define cep (current-error-port))
  (perror "WARN")
  (errno 0)
  (display "WARN: " 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))

(define (error . 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))

(define set-errno errno)
(define slib:exit quit)
(define exit quit)

(define (print . args)
  (define result #f)
  (for-each (lambda (x) (set! result x) (write x) (display #\ )) args)
  (newline)
  result)

(define (file-exists? str)
  (let ((port (open-file str OPEN_READ)))
    (and port (close-port port) #t)))
(define (file-readable? str)
  (let ((port (open-file str OPEN_READ)))
    (and port
	 (char-ready? port)
	 (do ((c (read-char port)
		 (and (char-ready? port) (read-char port)))
	      (i 0 (+ 1 i))
	      (l '() (cons c l)))
	     ((or (not c) (eof-object? c) (<= 2 i))
	      (if (null? l) #f (list->string (reverse l))))))))

(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*))))

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

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

(if (not (defined? last-pair))
    (define (last-pair l) (if (pair? (cdr l)) (last-pair (cdr l)) l)))

(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:warn warn)
(define slib:tab #\tab)
(define slib:form-feed #\page)
(define slib:eval eval)

;;; Load.
(define load:indent 0)
(define (load:pre file)
  (define cep (current-error-port))
  (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))

(define (load:post filesuf)
  (define cep (current-error-port))
  (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 file . libs)
  (define filesuf file)
  (define hss (has-suffix? file (scheme-file-suffix)))
  (load:pre file)
  (or (and (defined? link:link) (not hss)
	   (or (let ((s2 (file-readable? file)))
		 (and s2 (not (equal? "#!" s2)) (apply link:link file libs)))
	       (and link:able-suffix
		    (let* ((fs (string-append file link:able-suffix))
			   (fs2 (file-readable? fs)))
		      (and fs2 (apply link:link fs libs) (set! filesuf fs) #t)
		      ))))
      (and (null? libs) (try-load file))
      ;;HERE is where the suffix gets specified
      (and (not hss) (errno 0)		; clean up error from TRY-LOAD above
	   (set! filesuf (string-append file (scheme-file-suffix)))
	   (try-load filesuf))
      (and (procedure? could-not-open) (could-not-open) #f)
      (begin (set! load:indent 0)
	     (error "LOAD couldn't find file " file)))
  (load:post filesuf))
(define load scm:load)
(define slib:load load)

(define (scm:load-source file)
  (define sfs (scheme-file-suffix))
  (define filesuf file)
  (load:pre file)
  (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))
  (load:post filesuf))
(define slib:load-source scm:load-source)

(load (in-vicinity (library-vicinity) "require"))

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

(cond ((or (defined? dyn:link)
	   (defined? vms:dynamic-link-call)
	   (file-exists? (in-vicinity (implementation-vicinity) "hobbit.tms")))
       (load (in-vicinity (implementation-vicinity) "Link"))))

(cond ((defined? link:link)
       (define (slib:load-compiled . args)
	 (or (apply link:link args)
	     (error "Couldn't link files " args)))
       (provide 'compiled)))

(define (string-upcase str) (string-upcase! (string-copy str)))
(define (string-downcase str) (string-downcase! (string-copy str)))
(define (string-capitalize str) (string-capitalize! (string-copy str)))
(define string-ci->symbol
  (if (equal? "a" (symbol->string 'a))
      (lambda (str) (string->symbol (string-downcase str)))
      (lambda (str) (string->symbol (string-upcase str)))))

(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:integer-expt integer-expt)

(define logical:bit-field bit-field)
(define bit-extract bit-field)
(define logical:bitwise-if bitwise-if)
(define logical:copy-bit copy-bit)
(define logical:copy-bit-field copy-bit-field)

(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))))

;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)
	    `(define ,name
	       (let ((transformer (lambda ,parms ,@body)))
		 (set! *defmacros* (acons ',name transformer *defmacros*))
		 (defmacro: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)))))

;;; Autoloads for SLIB procedures.

(define (tracef . args) (require 'trace) (apply tracef args))
(define (trace:tracef . args) (require 'trace) (apply trace:tracef args))
(define (trace-all . args) (require 'debug) (apply trace-all args))
(define (pretty-print . args) (require 'pretty-print)
  (apply pretty-print args))

;;; 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 break x
  (if (null? x) '()
      `(begin ,@(map (lambda (x) `(set! ,x (break:breakf ,x ',x))) x))))

(defmacro defvar (var val)
  `(if (not (defined? ,var)) (define ,var ,val)))
(defmacro defconst (name value)
  (cond ((list? name) `(defconst ,(car name) (lambda ,(cdr name) ,value)))
	(else (cond ((not (slib:eval `(defined? ,name))))
		    ((and (symbol? name) (eqv? (slib:eval value)
					       (slib:eval name))))
		    (else (slib:error 'trying-to-defconst name
				      'to-different-value value)))
	      `(define ,name ,value))))
(defmacro casev (key . clauses)
  (let ((clauses
	 (map (lambda (clause)
		(if (list? (car clause))
		    (cons (apply
			   append
			   (map (lambda (elt)
				  (case elt
				    ((unquote) '(unquote))
				    ((unquote-splicing) '(unquote-splicing))
				    (else
				     (eval (list 'quasiquote (list elt))))))
				(car clause)))
			  (cdr clause))
		    clause))
	      clauses)))
    `(case ,key ,@clauses)))

(define print-args
  (procedure->syntax
   (lambda (sexp env)
     (set! env (environment->tree env))
     (let ((frame (and (not (null? env)) (car env))))
       (cond ((not (null? (cdr sexp)))
	      (display "In")
	      (for-each (lambda (exp) (display #\ ) (display exp)) (cdr sexp))
	      (display ": ")))
       (do ((vars (car frame) (cdr vars))
	    (vals (cdr frame) (cdr vals)))
	   ((not (pair? vars))
	    (cond ((not (null? vars))
		   (write vars)
		   (display " := ")
		   (write vals)))
	    (newline))
	 (write (car vars))
	 (display " = ")
	 (write (car vals))
	 (display "; "))))))

(cond
 ((defined? stack-trace)

  #+breakpoint-error;; remove line to enable breakpointing on calls to ERROR
  (define (error . 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)
    (cond ((stack-trace) (newline cep)))
    (display " * Breakpoint established: (continue <val>) to return." cep)
    (newline cep) (force-output cep)
    (require 'debug) (apply breakpoint args))

  (define (user-interrupt . args)
    (define cep (current-error-port))
    (newline cep) (display "ERROR: user interrupt" cep)
    (newline cep)
    (cond ((stack-trace) (newline cep)))
    (display " * Breakpoint established: (continue <val>) to return." cep)
    (newline cep) (force-output cep)
    (require 'debug) (apply breakpoint args))
  ))

;;; ABS and MAGNITUDE can be the same.
(cond ((and (inexact? (string->number "0.0")) (not (defined? exp)))
       (or (and (defined? usr:lib)
		(usr:lib "m")
		(load (in-vicinity (implementation-vicinity) "Transcen")
		      (usr:lib "m")))
	   (load (in-vicinity (implementation-vicinity) "Transcen")))
       (set! abs magnitude)))

(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)
	(let ((dims (array-dimensions a)))
	  (if (pair? dims)
	      (map (lambda (ind) (if (number? ind) (list 0 (+ -1 ind)) ind))
		   dims)
	      dims)))))

;;;; Initialize statically linked add-ons
(cond ((defined? scm_init_extensions)
       (scm_init_extensions)
       (set! scm_init_extensions #f)))

;;; Use *argv* instead of (program-arguments), to allow option
;;; processing to be done on it.  "ScmInit.scm" must
;;;	(set! *argv* (program-arguments))
;;; if it wants to alter the arguments which BOOT-TAIL processes.
(define *argv* #f)

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

(define (boot-tail dumped?)
  (cond ((not *argv*)
	 (set! *argv* (program-arguments))
	 (cond (dumped?
		(set-vicinities!)
		(verbose (if (and (isatty? (current-input-port))
				  (isatty? (current-output-port)))
			     (if (<= (length *argv*) 1) 2 1)
			     0))))
	 (cond ((provided? 'getopt)
		(set! *optind* 1)
		(set! *optarg* #f)))))

;;; This loads the user's initialization file, or files named in
;;; program arguments.
  (or (eq? (software-type) 'THINKC)
      (member "-no-init-file" (program-arguments))
      (member "--no-init-file" (program-arguments))
      (try-load (in-vicinity (or (home-vicinity) (user-vicinity))
			     (string-append "ScmInit") (scheme-file-suffix)))
      (errno 0))

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

    (let* ((simple-opts "muqvbisH")	; Added H.  --- Foner.
	   (arg-opts '("a kbytes" "no-init-file" "-no-init-file"
				  "-version" "-help" "p number"
				  "r feature" "f filename" "l filename"
				  "d filename" "c string" "e string"
				  "o filename"))
	   (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)
	   (exe-name (symbol->string (scheme-implementation-type)))
	   (up-name (apply string (map char-upcase (string->list exe-name)))))

      (define (do-thunk thunk)
	(if *interactive*
	    (thunk)
	    (let ((complete #f))
	      (dynamic-wind
	       (lambda () #f)
	       (lambda ()
		 (thunk)
		 (set! complete #t))
	       (lambda () (if (not complete) (quit #f)))))))

      (define (do-string-arg)
	(require 'string-port)
	(do-thunk
	 (lambda ()
	   ((if *R4RS-macro* macro:eval 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 success?)
	(define cep (if success? (current-output-port) (current-error-port)))
	(define indent (make-string 6 #\ ))
	(define i 3)
	(cond ((char? opt) (set! opt (string opt)))
	      ;;((symbol? opt) (set! opt (symbol->string opt)))
	      )
	(display (string-append preopt opt postopt) cep)
	(newline cep)
	(display (string-append "Usage: "
				exe-name
				" [-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)
	(if success? (display success? cep) (quit #f)))

      ;; -a int => ignore (handled by run_scm)
      ;; -c str => (eval str)
      ;; -e str => (eval str)
      ;; -d str => (require 'database-utilities) (open-database str)
      ;; -f str => (load str)
      ;; -l str => (load str)
      ;; -r str => (require str)
      ;; -o str => (dump 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
      ;; --no-init-file => don't load init file
      ;; --help => print and exit
      ;; --version => print and exit
      ;; --     => last option

      (let loop ((option (getopt-- argc *argv* opts)))
	(case option
	  ((#\a)
	   (cond ((> *optind* 3)
		  (usage "scm: option `-" getopt:opt "' must be first" #f))
		 ((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") #f))))
	  ((#\e #\c) (do-string-arg))	;sh-like
	  ((#\f #\l) (do-load *optarg*)) ;(set-car! *argv* *optarg*)
	  ((#\d) (require 'database-utilities)
		 (open-database *optarg*))
	  ((#\o) (require 'dump)
		 (if (< *optind* (length *argv*))
		     (dump *optarg* #t)
		     (dump *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)
					  (require 'eval)
					  (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! didsomething #t)
		 (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* "'" #f)))
	  ((#\:) (usage "scm: option `-" getopt:opt "' requires an argument" #f))
	  ((#\?) (usage "scm: unrecognized option `-" getopt:opt "'" #f))
	  ((#\H) #f)			; Added H.  --- Foner.
	  ((#f) (set! moreopts #f)	;sh-like
		(cond ((and (< *optind* (length *argv*))
			    (string=? "-" (list-ref *argv* *optind*)))
		       (set! *optind* (+ 1 *optind*)))))
	  (else
	   (or (cond ((not (string? option)) #f)
		     ((string-ci=? "no-init-file" option))
		     ((string-ci=? "version" option)
		      (display
		       (string-append exe-name " "
				      (scheme-implementation-version)
				      "
Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
"
				      up-name
				      " may be distributed under the terms of"
				      " the GNU General Public Licence;
certain other uses are permitted as well."
				      " For details, see the file `COPYING',
which is included in the "
				      up-name " distribution.
There is no warranty, to the extent permitted by law.
"
				      ))
		      (cond ((execpath)
			     (display " This executable was loaded from ")
			     (display (execpath))
			     (newline)))
		      (quit #t))
		     ((string-ci=? "help" option)
		      (usage "This is "
			     up-name
			     ", a Scheme interpreter."
			     (string-append
			      "Latest info: "
			      "http://swissnet.ai.mit.edu/~jaffer/"
			      up-name ".html
"
			      ))
		      (quit #t))
		     (else #f))
	       (usage "scm: unknown option `--" option "'" #f))))

	(cond ((and moreopts (< *optind* (length *argv*)))
	       (loop (getopt-- argc *argv* opts)))
	      ((< *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))
	  ((and *R4RS-macro* (not (provided? '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 natural SCM repl.
    )
   (else
    (begin (errno 0)
	   (set! *interactive* #t)
	   (for-each load (cdr (program-arguments)))))))
