1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancments. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9 ;;; ** (c) Copyright 1981 Massachusetts Institute of Technology **
11 ;;; SYSTEM: The ``New'' Macsyma System Stuff
15 (macsyma-module system
)
17 (defmvar $showtime nil
18 "When T, the computation time is printed with each output expression.")
20 ;;; Standard Kinds of Input Prompts
23 "Prompt symbol of the demo function, playback, and the Maxima break loop.")
26 ;; A prefix and suffix that are wrapped around every prompt that Maxima
27 ;; emits. This is designed for use with text-based interfaces that drive Maxima
28 ;; through standard input and output and need to decorate prompts to make the
29 ;; output easier to parse. There are some more notes in
30 ;; doc/implementation/external-interface.txt.
31 (defvar *prompt-prefix
* "")
32 (defvar *prompt-suffix
* "")
33 (defvar *general-display-prefix
* "")
34 (defvar $alt_format_prompt nil
"If NIL, use DEFAULT-FORMAT-PROMPT to print input prompt; if a function, use it to print input prompt.")
36 (defun format-prompt (destination control-string
&rest arguments
)
37 "If $ALT_FORMAT_PROMPT is NIL, use DEFAULT-FORMAT-PROMPT to print
38 prompt; otherwise MFUNCALL $ALT_FORMAT_PROMPT to print prompt."
39 (funcall (if $alt_format_prompt
#'alt-format-prompt
#'default-format-prompt
)
40 destination control-string arguments
))
42 (defun alt-format-prompt (destination control-string arguments
)
43 "MFUNCALL $ALT_FORMAT_PROMPT with a heavy coating of error protection."
44 (handler-bind ((error (lambda(msg) (setq $alt_format_prompt nil
)
45 (format t
(intl:gettext
"Error in printing prompt; reverting to default.~%~a") msg
)
46 (throw 'macsyma-quit
'maxima-error
))))
47 (with-$error
(let ((prompt (mfuncall $alt_format_prompt destination control-string arguments
)))
48 (if (stringp prompt
) prompt
(merror "alt_format_prompt returned an object of type ~a, needed a string." (type-of prompt
)))))))
50 (defun default-format-prompt (destination control-string arguments
)
51 "Like AFORMAT, but add the prefix and suffix configured for a prompt. This
52 function deals correctly with the ~M control character, but only when
53 DESTINATION is an actual stream (rather than nil for a string)."
54 (let ((*print-circle
* nil
))
55 (if (null destination
)
56 ;; return value string is important
59 (apply #'aformat destination
64 (format destination
"~A~A~A"
72 (defvar $default_format_prompt
(symbol-function 'default-format-prompt
))
74 ;; "When time began" (or at least the start of version control history),
75 ;; the following comment was made at this point:
77 ;; instead of using this STRIPDOLLAR hackery, the
78 ;; MREAD function should call MFORMAT to print the prompt,
79 ;; and take a format string and format arguments.
80 ;; Even easier and more general is for MREAD to take
81 ;; a FUNARG as the prompt. -gjc
83 ;; I guess we're still failing miserably, but unfortunately MFORMAT/AFORMAT
84 ;; don't deal correctly with ~M plus a string output stream.
86 (declare (special *display-labels-p
*))
87 (if *display-labels-p
*
88 (format-prompt nil
"(~A~A) "
89 (print-invert-case (stripdollar $inchar
))
93 (defun break-prompt ()
94 (format-prompt nil
"~A"
95 (print-invert-case (stripdollar $prompt
))))
97 (defun toplevel-macsyma-eval (x)
98 ;; Catch rat-err's here.
100 ;; The idea is that eventually there will be quite a few "maybe catch this"
101 ;; errors, which will be raised and might well get eaten before they get as far
102 ;; as here. However, we want to display them nicely like merror rather than
103 ;; letting a lisp error percolate to the debugger and, as such, we catch them
104 ;; here and replace them with an merror call.
106 ;; Other random errors get to the lisp debugger, which is normally set to print
107 ;; them and continue, via *debugger-hook*.
108 (rat-error-to-merror (meval* x
)))
110 (defmvar $_
'$_
"last thing read in, corresponds to lisp +")
111 (defmvar $__
'$__
"thing read in which will be evaluated, corresponds to -")
113 (declare-top (special *mread-prompt
* $file_search_demo
))
115 (defvar accumulated-time
0.0)
118 (defun used-area (&optional unused
)
119 (declare (ignore unused
))
120 (ext:get-bytes-consed
))
123 (defun used-area (&optional unused
)
124 (declare (ignore unused
))
125 (sb-ext:get-bytes-consed
))
128 (defun used-area (&optional unused
)
129 (declare (ignore unused
))
130 (ccl::total-bytes-allocated
))
133 (defun used-area (&optional unused
)
134 (declare (ignore unused
))
135 (multiple-value-bind (real1 real2 run1 run2 gc1 gc2 space1 space2 gccount
)
137 (declare (ignore real1 real2 run1 run2 gc1 gc2 gccount
))
138 (dpb space1
(byte 24 24) space2
)))
142 (defun used-area (&optional unused
)
143 (declare (ignore unused
))
144 (declare (optimize (speed 3)))
145 (let ((.oldspace
(make-array 4 :element-type
146 #-
64bit
'(unsigned-byte 32)
147 #+64bit
'(unsigned-byte 64))))
148 (declare (type (simple-array #-
64bit
(unsigned-byte 32)
149 #+64bit
(unsigned-byte 64) (*))
152 (multiple-value-bind (.olduser .oldsystem .oldgcu .oldgcs
)
153 (excl::get-internal-run-times
)
154 (declare (ignore .olduser .oldsystem .oldgcs
))
155 (sys::gsgc-totalloc .oldspace t
)
156 (list (aref .oldspace
0) (aref .oldspace
2) .oldgcu
)))) ;; report just two kinds of space,
157 ;; cons-cells and other bytes,
158 ;; also report gc-user time
161 (defun used-area (&optional unused
)
162 (declare (ignore unused
))
163 (getf (system:room-values
) :total-allocated
))
165 #-
(or cmu scl sbcl clisp allegro openmcl lispworks
)
166 (defun used-area (&optional unused
)
167 (declare (ignore unused
))
170 (defun continue (&key
((:stream input-stream
) *standard-input
*) batch-or-demo-flag one-shot
)
171 (declare (special *socket-connection
* *maxima-run-string
*))
172 (if *maxima-run-string
* (setq batch-or-demo-flag
:batch
))
173 (if (eql batch-or-demo-flag
:demo
)
176 "~%At the '~A' prompt, type ';' and <enter> to proceed with the demonstration.~&To abort the demonstration, type 'end;' or 'eof;' and then <enter>.~%")
177 (print-invert-case (stripdollar $prompt
))))
191 (finish nil one-shot
))
193 (declare (ignorable area-before area-after
))
194 (catch 'return-from-debugger
195 (when (or (not (checklabel $inchar
))
196 (not (checklabel $outchar
)))
198 (setq c-tag
(makelabel $inchar
))
199 (let ((*mread-prompt
* (if batch-or-demo-flag nil
(main-prompt)))
203 (setq r
(dbm-read input-stream nil eof
))
204 ;; This is something of a hack. If we are running in a server mode
205 ;; (which we determine by checking *socket-connection*) and we get
206 ;; an eof on an input-stream that is not *standard-input*, switch
207 ;; the input stream to *standard-input*.
208 ;; There should probably be a better scheme for server mode.
212 (not (eq input-stream
*standard-input
*))
213 (boundp '*socket-connection
*))
215 (setq input-stream
*standard-input
*)
216 (if batch-or-demo-flag
219 (setq *mread-prompt
* nil
)
220 (setq r
(dbm-read input-stream nil eof
))))))
222 (cond ((and (eq r eof
) (boundp '*socket-connection
*)
223 (eq input-stream
*socket-connection
*))
224 (cond ((>= (setq eof-count
(+ 1 eof-count
)) 10)
225 (print "exiting on eof")
228 (cond ((and (consp r
) (keywordp (car r
)))
229 (break-call (car r
) (cdr r
) 'break-command
)
231 (if (and (not batch-or-demo-flag
)
232 (not (eq input-stream
*standard-input
*)))
233 (setq input-stream
*standard-input
*))
235 (format t
"~a" *general-display-prefix
*)
236 (if (eq r eof
) (return '$done
))
238 (unless $nolabels
(setf (symbol-value c-tag
) $__
))
239 (cond (batch-or-demo-flag
240 (let (($display2d nil
))
241 (displa `((mlabel) ,c-tag
, $__
)))))
242 (setq time-before
(get-internal-run-time)
243 etime-before
(get-internal-real-time))
244 (setq area-before
(used-area))
245 (setq $%
(toplevel-macsyma-eval $__
))
247 (if (and (not batch-or-demo-flag
)
248 (not (eq input-stream
*standard-input
*)))
249 (setq input-stream
*standard-input
*))
250 (setq etime-after
(get-internal-real-time)
251 time-after
(get-internal-run-time))
252 (setq area-after
(used-area))
253 (setq time-used
(quotient
254 (float (- time-after time-before
))
255 internal-time-units-per-second
)
257 (float (- etime-after etime-before
))
258 internal-time-units-per-second
))
259 (incf accumulated-time time-used
)
260 (setq d-tag
(makelabel $outchar
))
261 (unless $nolabels
(setf (symbol-value d-tag
) $%
))
263 (when $showtime
;; we don't distinguish showtime:all?? /RJF
264 (format t
(intl:gettext
"Evaluation took ~,4F seconds (~,4F elapsed)")
265 time-used etime-used
)
268 #+(or cmu scl sbcl clisp openmcl
)
269 (let ((total-bytes (- area-after area-before
)))
270 (cond ((> total-bytes
(* 1024 1024))
271 (format t
(intl:gettext
" using ~,3F MB.~%")
272 (/ total-bytes
(* 1024.0 1024.0))))
273 ((> total-bytes
1024)
274 (format t
(intl:gettext
" using ~,3F KB.~%") (/ total-bytes
1024.0)))
276 (format t
(intl:gettext
" using ~:D bytes.~%") total-bytes
))))
279 (let ((conses (- (car area-after
) (car area-before
)))
280 (other (- (cadr area-after
) (cadr area-before
)))
281 (gctime (- (caddr area-after
) (caddr area-before
))))
282 (if (= 0 gctime
) nil
(format t
(intl:gettext
" including GC time ~s s,") (* 0.001 gctime
)))
283 (format t
(intl:gettext
" using ~s cons-cells and ~s other bytes.~%") conses other
))
286 (putprop '$%
(cons time-used
0) 'time
)
287 (putprop d-tag
(cons time-used
0) 'time
))
288 (if (eq (caar r
) 'displayinput
)
289 (displa `((mlabel) ,d-tag
,$%
)))
290 (when (eq batch-or-demo-flag
':demo
)
291 (princ (break-prompt))
295 ;;those are common lisp characters you're reading here
296 (case (read-char #+(or sbcl cmu
) *standard-input
*
297 #-
(or sbcl cmu
) *terminal-io
*)
300 (princ (break-prompt))
305 " Pausing. Type a ';' and <enter> to continue demo.~%")))
306 ((#\space
#\
; #\n #\e #\x #\t))
308 (if quitting
(throw 'abort-demo nil
) (return nil
)))
309 (t (setq quitting t
))))))
310 ;; This is sort of a kludge -- eat newlines and blanks so that
312 (and batch-or-demo-flag
314 (setq char
(read-char input-stream nil nil
))
316 (when *maxima-run-string
*
317 (setq batch-or-demo-flag nil
318 *maxima-run-string
* nil
319 input-stream
*standard-input
*)
320 (throw 'return-from-debugger t
))
321 (throw 'macsyma-quit nil
))
322 (unless (member char
'(#\space
#\newline
#\return
#\tab
) :test
#'equal
)
323 (unread-char char input-stream
)
326 (defmfun $break
(&rest arg-list
)
327 (prog1 (apply #'$print arg-list
)
330 (defun mbreak-loop ()
331 (let ((*standard-input
* *debug-io
*)
332 (*standard-output
* *debug-io
*))
334 (format t
(intl:gettext
"~%Entering a Maxima break point. Type 'exit;' to resume."))
337 (setq r
(caddr (let ((*mread-prompt
* (break-prompt)))
338 (mread *standard-input
*))))
340 (($exit
) (throw 'break-exit t
))
341 (t (errset (displa (meval r
)))))))))
343 (defun merrbreak (&optional arg
)
344 (format *debug-io
* "~%Merrbreak:~A" arg
)
347 (defun retrieve (msg flag
&aux
(print? nil
))
348 (declare (special msg flag print?
))
349 (or (eq flag
'noprint
) (setq print? t
))
352 (format-prompt t
""))
354 (format-prompt t
""))
356 (format-prompt t
"~A" msg
)
359 (format-prompt t
"~{~A~}" (cdr msg
))
362 (format-prompt t
"~M" msg
)
364 (let ((res (mread-noprompt #+(or sbcl cmu
) *standard-input
*
365 #-
(or sbcl cmu
) *query-io
* nil
)))
366 (princ *general-display-prefix
*)
369 (defmfun $read
(&rest l
)
370 (meval (apply #'$readonly l
)))
372 (defmfun $readonly
(&rest l
)
373 (let ((*mread-prompt
*
375 (string-right-trim '(#\n)
376 (with-output-to-string (*standard-output
*) (apply #'$print l
)))
378 (setf *mread-prompt
* (format-prompt nil
"~A" *mread-prompt
*))
379 (third (mread #+(or sbcl cmu
) *standard-input
*
380 #-
(or sbcl cmu
) *query-io
*))))
382 ;; FUNCTION BATCH APPARENTLY NEVER CALLED. OMIT FROM GETTEXT SWEEP AND DELETE IT EVENTUALLY
383 (defun batch (filename &optional demo-p
384 &aux
(orig filename
) list
385 file-obj
(accumulated-time 0.0) (abortp t
))
386 (setq list
(if demo-p
'$file_search_demo
'$file_search_maxima
))
387 (setq filename
($file_search filename
(symbol-value list
)))
388 (or filename
(merror "Could not find ~M in ~M: ~M"
389 orig list
(symbol-value list
)))
392 (progn (batch-internal (setq file-obj
(open filename
)) demo-p
)
395 (format t
"~&Batch spent ~,4F seconds in evaluation.~%"
397 (if file-obj
(close file-obj
))
398 (when abortp
(format t
"~&(Batch of ~A aborted.)~%" filename
))))
401 (defun batch-internal (fileobj demo-p
)
402 (continue :stream
(make-echo-stream fileobj
*standard-output
*)
403 :batch-or-demo-flag
(if demo-p
':demo
':batch
)))
405 (defmfun $demo
(&rest arg-list
)
406 (let ((tem ($file_search
(car arg-list
) $file_search_demo
)))
407 (or tem
(merror (intl:gettext
"demo: could not find ~M in ~M.")
408 (car arg-list
) '$file_search_demo
))
409 ($batch tem
'$demo
)))
411 (defmfun $bug_report
()
412 (format t
(intl:gettext
"~%Please report bugs to:~%"))
413 (format t
" https://sourceforge.net/p/maxima/bugs~%")
414 (format t
(intl:gettext
"To report a bug, you must have a Sourceforge account.~%"))
415 (format t
(intl:gettext
"Please include the following information with your bug report:~%"))
416 (format t
"-------------------------------------------------------------~%")
417 ; Display the 2D-formatted build information
418 (let (($display2d t
))
419 (displa ($build_info
)))
420 (format t
"-------------------------------------------------------------~%")
421 (format t
(intl:gettext
"The above information is also reported by the function 'build_info()'.~%~%"))
424 ;; Declare a build_info structure, then remove it from the list of user-defined structures.
425 (defstruct1 '((%build_info
) $version $timestamp $host $lisp_name $lisp_version
426 $maxima_userdir $maxima_tempdir $maxima_objdir $maxima_frontend $maxima_frontend_version
))
427 (let nil
(declare (special $structures
))
428 (setq $structures
(cons '(mlist) (remove-if #'(lambda (x) (eq (caar x
) '%build_info
)) (cdr $structures
)))))
430 (defvar *maxima-build-info
* nil
)
432 (defmfun $build_info
()
438 ((year (sixth cl-user
:*maxima-build-time
*))
439 (month (fifth cl-user
:*maxima-build-time
*))
440 (day (fourth cl-user
:*maxima-build-time
*))
441 (hour (third cl-user
:*maxima-build-time
*))
442 (minute (second cl-user
:*maxima-build-time
*))
443 (seconds (first cl-user
:*maxima-build-time
*)))
448 ,(format nil
"~4,'0d-~2,'0d-~2,'0d ~2,'0d:~2,'0d:~2,'0d" year month day hour minute seconds
)
450 ,#+sbcl
(ensure-readably-printable-string (lisp-implementation-type)) #-sbcl
(lisp-implementation-type)
451 ,#+sbcl
(ensure-readably-printable-string (lisp-implementation-version)) #-sbcl
(lisp-implementation-version)
456 ,$maxima_frontend_version
))))))
458 ;; SBCL base strings aren't readably printable.
459 ;; Attempt a work-around. Yes, this is terribly ugly.
460 #+sbcl
(defun ensure-readably-printable-string (x)
461 (coerce x
`(simple-array character
(,(length x
)))))
463 (defun dimension-build-info (form result
)
464 (declare (special bkptht bkptdp lines break
))
465 ;; Usually the result of (MFUNCALL '$@ ...) is a string,
466 ;; but ensure that output makes sense even if it is not.
468 ((version-string (format nil
(intl:gettext
"Maxima version: ~a")
469 (coerce (mstring (mfuncall '$
@ form
'$version
)) 'string
)))
470 (timestamp-string (format nil
(intl:gettext
"Maxima build date: ~a")
471 (coerce (mstring (mfuncall '$
@ form
'$timestamp
)) 'string
)))
472 (host-string (format nil
(intl:gettext
"Host type: ~a")
473 (coerce (mstring (mfuncall '$
@ form
'$host
)) 'string
)))
474 (lisp-name-string (format nil
(intl:gettext
"Lisp implementation type: ~a")
475 (coerce (mstring (mfuncall '$
@ form
'$lisp_name
)) 'string
)))
476 (lisp-version-string (format nil
(intl:gettext
"Lisp implementation version: ~a")
477 (coerce (mstring (mfuncall '$
@ form
'$lisp_version
)) 'string
)))
478 (maxima-userdir-string (format nil
(intl:gettext
"User dir: ~a")
479 (coerce (mstring (mfuncall '$
@ form
'$maxima_userdir
)) 'string
)))
480 (maxima-tempdir-string (format nil
(intl:gettext
"Temp dir: ~a")
481 (coerce (mstring (mfuncall '$
@ form
'$maxima_tempdir
)) 'string
)))
482 (maxima-objdir-string (format nil
(intl:gettext
"Object dir: ~a")
483 (coerce (mstring (mfuncall '$
@ form
'$maxima_objdir
)) 'string
)))
484 (maxima-frontend-string (format nil
(intl:gettext
"Frontend: ~a")
485 (coerce (mstring (mfuncall '$
@ form
'$maxima_frontend
)) 'string
)))
486 (maxima-frontend-version-string (format nil
(intl:gettext
"Frontend version: ~a")
487 (coerce (mstring (mfuncall '$
@ form
'$maxima_frontend_version
)) 'string
)))
492 (forcebreak result
0)
493 (forcebreak (reverse (coerce version-string
'list
)) 0)
494 (forcebreak (reverse (coerce timestamp-string
'list
)) 0)
495 (forcebreak (reverse (coerce host-string
'list
)) 0)
496 (forcebreak (reverse (coerce lisp-name-string
'list
)) 0)
497 (forcebreak (reverse (coerce lisp-version-string
'list
)) 0)
498 (forcebreak (reverse (coerce maxima-userdir-string
'list
)) 0)
499 (forcebreak (reverse (coerce maxima-tempdir-string
'list
)) 0)
500 (forcebreak (reverse (coerce maxima-objdir-string
'list
)) 0)
501 (forcebreak (reverse (coerce maxima-frontend-string
'list
)) 0)
502 (if $maxima_frontend
(forcebreak (reverse (coerce maxima-frontend-version-string
'list
)) 0)))
505 (setf (get '%build_info
'dimension
) 'dimension-build-info
)
507 (defvar *maxima-started
* nil
)
509 (defvar *maxima-prolog
* "")
510 (defvar *maxima-epilog
* "")
512 (declare-top (special *maxima-initmac
* *maxima-initlisp
*))
514 (defvar *maxima-quiet
* nil
)
516 (defvar *maxima-run-string
* nil
)
518 (defun macsyma-top-level (&optional
(input-stream *standard-input
*) batch-flag
)
519 (let ((*package
* (find-package :maxima
)))
521 (format t
(intl:gettext
"Maxima restarted.~%"))
523 (if (not *maxima-quiet
*) (maxima-banner))
524 (setq *maxima-started
* t
)))
526 (if ($file_search
*maxima-initlisp
*) ($load
($file_search
*maxima-initlisp
*)))
527 (if ($file_search
*maxima-initmac
*) ($batchload
($file_search
*maxima-initmac
*)))
533 (catch #+kcl si
::*quit-tag
*
534 #+(or cmu scl sbcl openmcl lispworks
) 'continue
535 #-
(or kcl cmu scl sbcl openmcl lispworks
) nil
537 (continue :stream input-stream
:batch-or-demo-flag batch-flag
)
538 (format t
*maxima-epilog
*)
541 (defun maxima-banner ()
542 (format t
*maxima-prolog
*)
543 (format t
"~&Maxima ~a http://maxima.sourceforge.net~%"
545 (format t
(intl:gettext
"using Lisp ~a ~a") (lisp-implementation-type)
546 #-clisp
(lisp-implementation-version)
547 #+clisp
(subseq (lisp-implementation-version)
548 0 (1+ (search ")" (lisp-implementation-version)))))
549 (format t
(intl:gettext
"~%Distributed under the GNU Public License. See the file COPYING.~%"))
550 (format t
(intl:gettext
"Dedicated to the memory of William Schelter.~%"))
551 (format t
(intl:gettext
"The function bug_report() provides bug reporting information.~%")))
554 (si::putprop
:t
'throw-macsyma-top
'si
::break-command
)
556 (defun throw-macsyma-top ()
557 (throw 'macsyma-quit t
))
560 (defmfun $writefile
(x)
561 (let ((msg (dribble (maxima-string x
))))
562 (format t
"~&~A~&" msg
)
565 (defvar $appendfile nil
)
566 (defvar *appendfile-data
* #+(or sbcl cmu
) nil
)
569 (defmfun $appendfile
(name)
570 (if (and (symbolp name
)
571 (char= (char (symbol-name name
) 0) #\$
))
572 (setq name
(maxima-string name
)))
573 (if $appendfile
(merror (intl:gettext
"appendfile: already in appendfile, you must call closefile first.")))
574 (let ((stream (open name
:direction
:output
576 :if-does-not-exist
:create
)))
577 (setq *appendfile-data
* (list stream
*terminal-io
* name
))
579 (setq $appendfile
(make-two-way-stream
580 (make-echo-stream *terminal-io
* stream
)
581 (make-broadcast-stream *terminal-io
* stream
))
582 *terminal-io
* $appendfile
)
583 (multiple-value-bind (sec min hour day month year
)
585 (format t
(intl:gettext
"~&/* Starts dribbling to ~A (~d/~d/~d, ~2,'0d:~2,'0d:~2,'0d).*/~&")
586 name year month day hour min sec
))
590 (defmfun $closefile
()
592 (cond ((eq $appendfile
*terminal-io
*)
593 (format t
(intl:gettext
"~&/*Finished dribbling to ~A.*/~&")
594 (nth 2 *appendfile-data
*))
595 (setq *terminal-io
* (nth 1 *appendfile-data
*)))
596 (t (warn "*TERMINAL-IO* was rebound while APPENDFILE is on.~%~
597 You may miss some dribble output.")))
598 (close (nth 0 *appendfile-data
*))
599 (setq *appendfile-data
* nil $appendfile nil
))
600 (t (let ((msg (dribble)))
601 (format t
"~&~A~&" msg
))))
605 (defun start-dribble (name)
606 (let ((msg (dribble (maxima-string name
))))
607 (format t
"~&~A~&" msg
)
608 (setq *appendfile-data
* (cons name
*appendfile-data
*))
609 (multiple-value-bind (sec min hour day month year
)
611 (format t
(intl:gettext
"~&/* Starts dribbling to ~A (~d/~d/~d, ~2,'0d:~2,'0d:~2,'0d).*/~&")
612 name year month day hour min sec
))
616 (defmfun $writefile
(name)
617 (if (member name
*appendfile-data
* :test
#'string
=)
618 (merror (intl:gettext
"writefile: already in writefile, you must call closefile first.")))
619 (start-dribble name
))
622 (defmfun $appendfile
(name)
623 (if (member name
*appendfile-data
* :test
#'string
=)
624 (merror (intl:gettext
"appendfile: already in appendfile, you must call closefile first.")))
625 (start-dribble name
))
628 (defmfun $closefile
()
629 (cond (*appendfile-data
*
630 (let ((msg (dribble)))
631 (format t
"~&~A~&" msg
))
632 (multiple-value-bind (sec min hour day month year
)
634 (format t
(intl:gettext
"~&/* Quits dribbling to ~A (~d/~d/~d, ~2,'0d:~2,'0d:~2,'0d).*/~&")
635 (car *appendfile-data
*) year month day hour min sec
))
636 (setq *appendfile-data
* (cdr *appendfile-data
*))))
640 (ed (maxima-string x
)))
642 (defun nsubstring (x y
)
646 (subseq (print-invert-case (car x
)) 1))
648 (defmspec $with_stdout
(arg)
649 (declare (special $file_output_append
))
651 (let ((output (meval (car arg
))))
654 ((*standard-output
* output
)
658 (setq result
(meval* v
)))
661 ((fname (namestring (maxima-string output
)))
663 (if (or (eq $file_output_append
'$true
)
664 (eq $file_output_append t
))
665 `(*standard-output
* ,fname
:direction
:output
:if-exists
:append
:if-does-not-exist
:create
)
666 `(*standard-output
* ,fname
:direction
:output
:if-exists
:supersede
:if-does-not-exist
:create
))))
668 `(with-open-file ,filespec
669 (let ((body ',(cdr arg
)) result
)
671 (setq result
(meval* v
)))
674 (defmfun $sconcat
(&rest x
)
677 (setq ans
(concatenate 'string ans
681 (coerce (mstring v
) 'string
))))))
684 (defmfun $system
(&rest args
)
685 ;; If XMaxima is running, direct output from command into *SOCKET-CONNECTION*.
686 ;; From what I can tell, GCL, ECL, and Clisp cannot redirect the output into an existing stream. Oh well.
687 (let ((s (and (boundp '*socket-connection
*) *socket-connection
*))
689 #+(or gcl ecl lispworks
)
691 (declare (ignorable shell shell-opt
))
693 (cond ((string= *autoconf-windows
* "true")
694 (setf shell
"cmd") (setf shell-opt
"/c"))
695 (t (setf shell
"/bin/sh") (setf shell-opt
"-c")))
697 #+gcl
(system::system
(apply '$sconcat args
))
698 #+ecl
(si:system
(apply '$concat args
))
699 #+clisp
(let ((output (ext:run-shell-command
(apply '$sconcat args
)
700 :wait t
:output
:stream
)))
701 (loop for line
= (read-line output nil
)
703 (format (or s t
) "~a~%" line
)))
704 #+(or cmu scl
) (ext:run-program shell
(list shell-opt
(apply '$sconcat args
)) :output
(or s t
))
705 #+allegro
(excl:run-shell-command
(apply '$sconcat args
) :wait t
:output
(or s nil
))
706 #+sbcl
(sb-ext:run-program shell
707 #+(or win32 win64
) (cons shell-opt
(mapcar '$sconcat args
))
708 #-
(or win32 win64
) (list shell-opt
(apply '$sconcat args
))
709 :search t
:output
(or s t
))
710 #+openmcl
(ccl::run-program shell
711 #+windows
(cons shell-opt
(mapcar '$sconcat args
))
712 #-windows
(list shell-opt
(apply '$sconcat args
))
714 #+abcl
(extensions::run-shell-command
(apply '$sconcat args
) :output
(or s
*standard-output
*))
715 #+lispworks
(system:run-shell-command
(apply '$sconcat args
) :wait t
)))
717 (defmfun $room
(&optional
(arg nil arg-p
))
718 (if (and arg-p
(member arg
'(t nil
) :test
#'eq
))
722 (defun maxima-lisp-debugger (condition me-or-my-encapsulation
)
723 (declare (ignore me-or-my-encapsulation
))
724 ;; If outputting an error message creates an error this has the potential to trigger
725 ;; another error message - which causes an endless loop.
727 ;; If maxima is connected to a frontend (for example wxMaxima) using a local network
728 ;; socket and the frontend suddently crashes the network connection drops -which
729 ;; has the potential to cause this endless loop to happen.
731 ;; most lisps (at least gcl, sbcl and clisp) are intelligent enough to call (bye)
732 ;; if the socket connected to stdin, stdout and stderr drops.
733 ;; ECL 16.3.1 ran into an endless loop, though => if maxima runs into an error
734 ;; and cannot output an error message something is wrong enough to justify maxima
738 (format t
(intl:gettext
"~&Maxima encountered a Lisp error:~%~% ~A") condition
)
739 (format t
(intl:gettext
"~&~%Automatically continuing.~%To enable the Lisp debugger set *debugger-hook* to nil.~%"))
742 (error () (ignore-errors (bye))))
743 (throw 'return-from-debugger t
))
745 (let ((t0-real 0) (t0-run 0)
746 (float-units (float internal-time-units-per-second
)))
748 (defun initialize-real-and-run-time ()
749 (setq t0-real
(get-internal-real-time))
750 (setq t0-run
(get-internal-run-time)))
752 (defmfun $absolute_real_time
() (get-universal-time))
754 (defmfun $elapsed_real_time
()
755 (let ((elapsed-real-time (- (get-internal-real-time) t0-real
)))
756 (/ elapsed-real-time float-units
)))
758 (defmfun $elapsed_run_time
()
759 (let ((elapsed-run-time (- (get-internal-run-time) t0-run
)))
760 (/ elapsed-run-time float-units
))))
762 ;; Tries to manually trigger the lisp's garbage collector
763 ;; and returns true if it knew how to do that.
764 (defmfun $garbage_collect
()
770 (progn (si::gbc t
) t
)
772 (progn (sb-ext::gc
:full t
) t
)
774 (progn (ext:gc
:full t
) t
)
775 #-
(or allegro clisp ecl gcl sbcl cmucl
)