+(setq rcs-format-
+ "$Header")
+
+;;
+;; format.l -[Fri Mar 4 12:20:16 1983 by jkf]-
+;;
+;; This is a function for printing or creating nicely formatted strings.
+;; This file is a modified version of the format program which runs in
+;; the mit lisps. When converting to franz, compatibility was the
+;; major goal, thus we still use the \ character as a string delimiter
+;; within a command string, even though it must be doubled in Franz.
+;;
+;; The file contains the user callable functions:
+;; format - lexpr for doing formated printed output or creating
+;; strings
+;; defformat - macro for adding a format directive
+;;
+
+; FORMAT prints several arguments according to a control argument.
+; The control argument is either a string or a list of strings and lists.
+; The strings and lists are interpreted consecutively.
+; Strings are for the most part just printed, except that the character ~
+; starts an escape sequence which directs other actions.
+; A ~ escape sequence has an (optional) numeric parameter followed by a
+; mode character.
+; These escape actions can use up one or more of the non-control arguments.
+; A list in the control-argument list is also interpreted as an escape.
+; Its first element is the mode, a symbol which may be any length,
+; and its remaining elements are parameters. The list (D 5) is equivalent
+; to the ~ escape "~5D"; similarly, each ~ escape has an equivalent list.
+; However, there are list escapes which have no ~ equivalent.
+
+; Any undefined list escape is simply evaluated.
+
+;These are the escape modes which are defined:
+; ~nD Takes any number and prints as a decimal integer. If no arg,
+; print without leading spaces. If arg and it fits in, put in leading
+; spaces; if it doesnt fit just print it. If second arg, use that
+; (or first char of STRING of it if not a number) instead of space
+; as a pad char.
+; ~nF Floating point
+; ~nE Exponential notation
+; ~nO Like D but octal
+; ~nA Character string. If there is an n then pad the string with spaces
+; on the right to make it n long. If it doesn't fit, ignore n.
+; ~n,m,minpad,padcharA Pad on the right to occupy at least
+; n columns, or if longer than that to begin with, pad to occupy
+; n+p*m columns for some nonnegative integer p.
+; at least minpad pad characters are produced in any case
+; (default if not supplied = 0).
+; padchar is used for padding purposes (default if not supplied = space).
+; if padchar is not a number, the first character in STRING of it is used.
+
+; A mode can actually be used to PRINC anything, not just a string.
+; ~S Prin1 an object. Just like ~A (including parameters) but uses PRIN1.
+; ~C One character, in any acceptable form.
+; Control and meta bits print as alpha, beta, epsilon.
+; ~n* Ignore the next n args. n defaults to 1.
+; ~n% Insert n newlines. n defaults to 1.
+; ~n| Insert n formfeeds. n defaults to 1.
+; ~nX Insert n spaces. n defaults to 1.
+; ~n~ Insert n tildes. n defaults to 1.
+; ~& Perform the :FRESH-LINE operation on the stream.
+; ~n,mT Tab to column n+pm, for p an integer >= 0.
+; ~Q Apply the next arg to no arguments.
+; (Q ...) Apply the next arg to the (unevaluated) parameters following the Q.
+; ~P Insert an "s", unless its argument is a 1
+; ~nG Goto the nth argument (zero based). The next command will get that
+; argument, etc.
+; ~E and ~F are not implemented. ~T is not implemented.
+
+; (FORMAT <stream> <control arg> <args>)
+; If <stream> is NIL, cons up and return a symbol.
+; If <stream> is T, use STANDARD-OUTPUT (saves typing).
+
+;; defformat:
+;; to add a format handler, the defformat macro is used.
+;; the form is (defformat code args type . body)
+;; where
+;; code is the code this will handle. the code can be a multi
+;; character symbol, however it will have to be called with \\code\\.
+;; args is either a one or two symbol list, depending on type
+;; type is either: none, one, or many.
+;; none means that type handler will not use any argument (it may use
+;; use parameters however)
+;; one means that it takes exactly one argument
+;; many means that it may take from zero to ?? arguments.
+;; body is the body of the function. Its return value is only important
+;; in the case of 'many' handlers since these handlers must return the
+;; list of the arguments they didn't use.
+;;
+;; 'none' handlers get passes a hunk which contains the parameters provide
+;; for this format directive.
+;; 'one' handlers are passed the argument and the parameters.
+;; 'many' handlers are passed the list of remaining arguments and the
+;; parameters. they return the arguments they don't use.
+
+;to do:
+; 3) make sure the semantics follows the lisp machine defs
+; 6) do exponential (~e) floating point formats correctly.
+; 7) move ferror elsewhere (near error would be a good place).
+; 8) document it.
+; 11) fix ~a to left justify if given correct flag
+; 13) make sure that multi character directives are lower cased
+; 14) make the 'x parameter work correctly
+; 15) fix the english printer (wrt stream arg) and add ordinal
+
+
+;;; Kludges to make MacLISP like some of the LISPM functions
+
+(declare (special Format-Standard-Output roman-old
+ format-params-supplied format format-handlers
+ format-sharpsign-vars))
+
+(setq format-sharpsign-vars 'franz-symbolic-character-names)
+
+;; format-params-supplied : numbers of parameters to format parameter
+;; roman-old when t, the roman printer will print IIII instead of IV
+
+(or (boundp 'roman-old) (setq roman-old nil))
+
+(declare (setq defmacro-for-compiling nil defmacro-displace-call nil ))
+ (defmacro nsubstring (&rest w) `(format\:nsubstring ,.w))
+ (defmacro string-search-char (&rest w) `(format\:string-search-char ,.w))
+ (defmacro ar-1 (ar ind) `(cxr ,ind ,ar))
+ (defmacro as-1 (val ar ind) `(rplacx ,ind ,ar ,val))
+ (defmacro >= (x y) `(not (< ,x ,y)))
+ (defmacro <= (x y) `(not (> ,x ,y)))
+ (defmacro neq (x y) `(not (= ,x ,y)))
+ (defmacro pop (stack) `(prog1 (car ,stack) (setq ,stack (cdr ,stack))))
+(declare (setq defmacro-for-compiling 't defmacro-displace-call 't))
+
+
+(declare
+ (special ctl-string ;The control string.
+ ctl-length ;string-length of ctl-string.
+ ctl-index ;Our current index into the control string.
+ ; Used by the conditional command. (NYI)
+ atsign-flag ;Modifier
+ colon-flag ;Modifier
+ format-temporary-area ;For temporary consing
+ format-arglist ;The original arg list, for ~G.
+ arglist-index ;How far we are in the current arglist
+ float-format ; format used when printing floats
+ poport ; franz's standard output
+ ))
+
+(defun format (stream ctl-string &rest args)
+ (let (format-string Format-Standard-Output
+ (format-arglist args)
+ (arglist-index 0))
+ (setq stream (cond ((eq stream 't) poport )
+ ((null stream)
+ (setq format-string 't)
+ (list nil))
+ (t stream)))
+ (setq Format-Standard-Output stream)
+ (cond ((symbolp ctl-string)
+ (setq ctl-string (get_pname ctl-string))))
+ (cond ((stringp ctl-string)
+ (format-ctl-string args ctl-string 0 (pntlen ctl-string)))
+ (t (do ((ctl-string ctl-string (cdr ctl-string)))
+ ((null ctl-string))
+ (setq args
+ (cond ((symbolp (car ctl-string))
+ (format-ctl-string
+ args
+ (car ctl-string)
+ 0
+ (pntlen (car ctl-string))))
+ (t (format-ctl-list args (car ctl-string))))))))
+ (and format-string
+ (setq format-string (maknam (nreverse (cdr stream)))))
+ format-string))
+
+(defun format-ctl-list (args ctl-list)
+ (format-ctl-op (car ctl-list) args (cdr ctl-list)))
+
+(defun format-ctl-string (args ctl-string ctl-index ctl-length)
+ (declare (fixnum ctl-index ctl-length))
+ (do ((ch) (tem) (str) (sym))
+ ((>= ctl-index ctl-length) args)
+ (setq tem (cond ((string-search-char #/~ ctl-string ctl-index))
+ (t ctl-length)))
+ (cond ((neq tem ctl-index) ;Put out some literal string
+ (do n ctl-index n (>= n tem)
+ (format:tyo (substringn ctl-string (setq n (1+ n)) 0)))
+ (and (>= (setq ctl-index tem) ctl-length)
+ (return args))))
+ ;; (ar-1 ch ctl-index) is a tilde.
+ (do ((atsign-flag nil) ;Modifier
+ (colon-flag nil) ;Modifier
+ (params (makhunk 10))
+ (param-leader -1)
+ ;PARAMS contains the list of numeric parameters
+ (param-flag nil) ;If T, a parameter has been started in PARAM
+ (param)) ;PARAM is the parameter currently
+ ; being constructed
+ ((>= (setq ctl-index (1+ ctl-index)) ctl-length))
+ (setq ch (getcharn ctl-string (1+ ctl-index)))
+ (cond ((and (>= ch #/0) (<= ch #/9)) ;
+ (setq param (+ (* (or param 0) 10.) (- ch #/0)) ;
+ param-flag t))
+ ((= ch #/@) ;ascii @
+ (setq atsign-flag t))
+ ((= ch #/:) ;ascii :
+ (setq colon-flag t))
+ ((or (= ch #/v) (= ch #/V)) ;ascii v, v
+ (as-1 (pop args) params
+ (setq param-leader (1+ param-leader)))
+ (setq arglist-index (1+ arglist-index))
+ (setq param nil param-flag nil))
+ ((= ch #/#)
+ (as-1 (length args) params
+ (setq param-leader (1+ param-leader))))
+ ((= ch #/,)
+ ;comma, begin another parameter, ascii ,
+ (and param-flag (as-1 param params (setq param-leader
+ (1+ param-leader))))
+ (setq param nil param-flag t))
+ ;omitted arguments made manifest by the
+ ;presence of a comma come through as nil
+ (t ;must be a command character
+ ;upper case to lower
+ (and (>= ch #/A) (<= ch #/Z) (setq ch (+ ch (- #/a #/A))))
+ (setq ctl-index (1+ ctl-index)) ;advance past command char
+ (and param-flag (as-1 param params (setq param-leader
+ (1+ param-leader))))
+ (setq param-flag nil param nil tem nil)
+ ;str gets a string which is the name of the operation to do
+ (setq
+ str (cond ((= ch #/\ ) ;ascii \
+ (let ((i (string-search-char
+ #/\
+ ctl-string
+ (1+ ctl-index))))
+ (and (null i)
+ (ferror nil
+ '|Unmatched \\ in control string.|))
+ (prog1 ; don't uppercase! we are a two
+ ; case system
+ (setq tem
+ (nsubstring ctl-string
+ (1+ ctl-index)
+ i))
+ (setq ctl-index i))))
+ ;makes ~<newline> work! ;SMH@EMS
+ ((= ch #\newline)
+ #.(list 'quote (concat "ch" #\newline)))
+ (t (ascii ch))))
+ ;; SYM gets the symbol corresponding to STR
+ (cond ((setq sym str)
+ (setq format-params-supplied param-leader)
+ (setq args (format-ctl-op sym args params)))
+ (t (ferror nil '|~C is an unknown FORMAT op in \"~A\"|
+ tem ctl-string)))
+ (return nil))))))
+
+;Perform a single formatted output operation on specified args.
+;Return the remaining args not used up by the operation.
+(defun format-ctl-op (op args params &aux tem)
+ (cond ((stringp op) (setq op (concat op)))) ; make into a symbol
+ (cond ((setq tem (assq op format-handlers))
+ (cond ((eq 'one (cadr tem))
+ (or args
+ (ferror nil "arg required for ~a, but no more args" op))
+ (funcall (caddr tem) (car args) params)
+ (setq arglist-index (1+ arglist-index))
+ (cdr args))
+ ((eq 'none (cadr tem))
+ (funcall (caddr tem) params)
+ args)
+ ((eq 'many (cadr tem))
+ (funcall (caddr tem) args params))
+ (t (ferror nil "Illegal format handler: ~s" tem))))
+ (t (ferror nil '|\"~S\" is not defined as a FORMAT command.| op)
+ args)))
+
+(setq format-handlers nil)
+;; Format handlers
+;;
+(defmacro defformat (name arglist type &rest body)
+ (let (newname)
+ ;; allow the name to be the fixnum rep of a character too.
+ (cond ((fixp name) (setq name (concat "ch" name))))
+
+ (cond ((not (memq type '(none one many)))
+ (ferror nil "The format type, \"~a\" is not: none, one or many"
+ type)))
+ (cond ((or (not (symbolp name))
+ (not (dtpr arglist)))
+ (ferror nil "Bad form for name and/or arglist: ~a ~a"
+ name arglist)))
+ (cond ((memq type '(one many))
+ (cond ((not (= (length arglist) 2))
+ (ferror nil "There should be 2 arguments to ~a" name))))
+ (t (cond ((not (= (length arglist) 1))
+ (ferror nil "There should be 1 argument to ~a" name)))))
+ (setq newname (concat name ":format-handler"))
+ `(progn 'compile
+ (defun ,newname ,arglist ,@body)
+ (let ((handler (assq ',name format-handlers)))
+ (cond (handler (rplaca (cddr handler) ',newname))
+ (t (setq format-handlers (cons (list ',name
+ ',type
+ ',newname)
+ format-handlers))))))))
+
+
+
+(defformat d (arg params) one
+ (let ((width (cxr 0 params))
+ (padchar (cxr 1 params)))
+ (cond ((and colon-flag (< arg 4000.) (> arg 0))
+ (roman-step arg 0))
+ (atsign-flag (english-print arg 'cardinal))
+ ((let ((base 10.) (*nopoint t))
+ (cond ((null padchar) (setq padchar 32.))
+ ((not (numberp padchar))
+ (setq padchar (getcharn padchar 1))))
+ (and width (format-ctl-justify width (flatc arg) padchar))
+ (format:patom arg))))))
+
+(defformat f (arg params) one
+ (cond ((not (floatp arg)) (format:patom arg))
+ (t (let ((float-format "%.16g")
+ (prec (cxr 0 params)))
+ (cond ((and prec (fixp prec) (> prec 0) (< prec 16))
+ (setq float-format (concat "%" prec "g"))))
+ (format:patom arg)))))
+
+; r format
+; no params and flags: print as cardinal (four)
+; no params and colon: print as ordinal (fourth)
+; no params and atsign: print as roman (IV)
+; no params and colon and atsign: print as old roman (IIII)
+; params: radix,mincol[0],padchar[<space>]
+; print in radix with at least mincol columns, padded on left
+; with padchar
+;
+(defformat r (arg params) one
+ (format:anyradix-printer arg params nil))
+
+; o format - like ~8r, but params are like ~d.
+;
+(defformat o (arg params) one
+ (format:anyradix-printer arg params 8.))
+
+(defun format:anyradix-printer (arg params radix)
+ ; this is called by ~r and ~o. for ~r, the mincol parameter starts at
+ ; cxr 1, for ~o the mincol parameter starts at cxr 0. We compute
+ ; paramstart as either 0 or 1
+ ; radix is given as third argument iff this is ~o
+ (let ((paramstart (cond (radix 0)
+ (t 1))))
+ (cond ((null radix) (setq radix (cxr 0 params))))
+ (cond ((null radix) ; if not to any given base
+ (cond ((and (null colon-flag) (null atsign-flag))
+ (english-print arg 'cardinal))
+ ((and colon-flag (null atsign-flag))
+ (english-print arg 'ordinal))
+ ((and (null colon-flag) atsign-flag)
+ (roman-step arg 0))
+ ((and colon-flag atsign-flag)
+ (let ((roman-old t))
+ (roman-step arg 0)))))
+ (t (let ((mincol (cxr paramstart params))
+ (padchr (or (cxr (+ 1 paramstart) params) #\space))
+ (res))
+ (cond (mincol ;; if mincol specified
+ (let ((Format-Standard-Output (list nil)))
+ (format-binpr arg radix)
+ (setq res (cdr Format-Standard-Output)))
+ (format-ctl-justify mincol (length res) padchr)
+ (mapc 'format:tyo (nreverse res)))
+ (t (format-binpr arg radix))))))))
+
+
+(defun format-binpr (x base)
+ (cond ((equal x 0)(format:patom 0))
+ ((or (> base 36.) (< base 2))
+ (ferror nil "\"~s\" is not a base between 2 and 36" base))
+ ((lessp x 0)
+ (format:patom '-)
+ (format-binpr1 (minus x) base))
+ (t (format-binpr1 x base)))
+ x)
+
+
+
+(defun format-binpr1 (x base)
+ (cond ((equal x 0))
+ (t (format-binpr1 (quotient x base) base)
+ (format-prc (remainder x base)))))
+
+(defun format-prc (x)
+ (cond ((< x 10.) (format:patom x))
+ (t (format:tyo (plus (- #/a 10.) x)))))
+ ; works for 10.=A, 35.=Z.
+
+;; must get the width stuff to work!!
+(defun format-ctl-octal (arg params)
+ (let ((width (cxr 0 params)) (padchar (cxr 1 params)))
+ (let ((base 8))
+ (cond ((null padchar)
+ (setq padchar 32.))
+ ((not (numberp padchar))
+ (setq padchar (getcharn padchar 1))))
+ (and width (format-ctl-justify width (flatc arg) padchar))
+ (format:patom arg))))
+
+(defformat a (arg params) one
+ (format-ctl-ascii arg params nil))
+
+(defun format-ctl-ascii (arg params prin1p)
+ (let ((edge (cxr 0 params))
+ (period (cxr 1 params))
+ (min (cxr 2 params))
+ (padchar (cxr 3 params)))
+ (cond ((null padchar)
+ (setq padchar #\space))
+ ((not (numberp padchar))
+ (setq padchar (getcharn padchar 1))))
+ (cond (prin1p (format:print arg))
+ (t (format:patom arg)))
+ (cond ((not (null edge))
+ (let ((width (cond (prin1p (flatsize arg)) ((flatc arg)))))
+ (cond ((not (null min))
+ (format-ctl-repeat-char min padchar)
+ (setq width (+ width min))))
+ (cond (period
+ (format-ctl-repeat-char
+ (- (+ edge (* (\\ (+ (- (max edge width) edge 1)
+ period)
+ period)
+ period))
+ width)
+ padchar))
+ (t (format-ctl-justify edge width padchar))))))))
+
+(defformat s (arg params) one
+ (format-ctl-ascii arg params t))
+
+(defformat c (arg params) one
+ (cond ((or (not (fixp arg))
+ (< arg 0)
+ (> arg 127))
+ (ferror nil "~s is not a legal character value" arg)))
+ (cond ((and (not colon-flag) (not atsign-flag))
+ ; just print out the character after converting to ascii
+ (format:patom (ascii arg)))
+ (t ; it may have an extended name, check for that first
+ (let (name)
+ (cond ((setq name (car
+ (rassq arg (symeval format-sharpsign-vars))))
+ ; it has an extended name.
+ ; if : flag, then print in human readable
+ (cond (colon-flag (format:patom name))
+ (atsign-flag (format:patom "#\\")
+ (format:patom name))))
+ ((< arg #\space)
+ ; convert from control to upper case
+ (setq arg (+ arg #/@))
+ (cond (colon-flag (format:patom "^")
+ (format:patom (ascii arg)))
+ (atsign-flag (format:patom "#^")
+ (format:patom (ascii arg)))))
+ (t (cond (colon-flag (format:patom (ascii arg)))
+ (atsign-flag (format:patom "#/")
+ (format:patom (ascii arg))))))))))
+
+(defformat p (args params) many
+ (let (arg)
+ (cond (colon-flag
+ (setq arg (nth (1- arglist-index) format-arglist)))
+ ((null args)
+ (ferror () "Argument required for p, but no more arguments"))
+ (t (setq arg (pop args)
+ arglist-index (1+ arglist-index))))
+ (if (= arg 1)
+ (if atsign-flag (format:tyo #/y))
+ (cond (atsign-flag
+ (format:tyo #/i)
+ (format:tyo #/e)
+ (format:tyo #/s))
+ (t (format:tyo #/s))))
+ args))
+
+(defformat * (args params) many
+ (let ((count (or (cxr 0 params) 1)))
+ (if colon-flag (setq count (minus count)))
+ (setq arglist-index (+ arglist-index count))
+;; (nthcdr count format-arglist) ;; ??? SMH@EMS
+ (nthcdr arglist-index format-arglist))) ;; SMH@EMS
+
+(defformat g (arg params) many
+ (let ((count (or (cxr 0 params) 1)))
+ (nthcdr count format-arglist)))
+
+(defformat % (params) none
+ (declare (fixnum i))
+ (let ((count (or (cxr 0 params) 1)))
+ (do i 0 (1+ i) (= i count)
+ (format:terpr))))
+
+; ~ at the end of the line
+; no params: ignore newline and following whitespace
+; @ flag: leave the newline in the string but ignore whitespace
+; : flag: ignore newline but leave the whitespace
+; :@ flags: leave both newline and whitespace
+;
+(defformat #\newline (params) none
+ (cond (atsign-flag
+ (format:tyo #\newline)))
+ (cond ((not colon-flag)
+ (setq ctl-index (1+ ctl-index))
+ (do ()
+ ((>= ctl-index ctl-length))
+ (cond ((memq (getcharn ctl-string ctl-index)
+ '(#\space #\tab))
+ (setq ctl-index (1+ ctl-index)))
+ (t (setq ctl-index (1- ctl-index))
+ (return)))))))
+
+
+(defformat & (params) none
+ (format:fresh-line))
+
+(defformat x (params) none
+ (format-ctl-repeat-char (cxr 0 params) #\space))
+
+(defformat \| (params) none
+ (format-ctl-repeat-char (cxr 0 params) #\ff))
+
+(defformat ~ (params) none
+ (format-ctl-repeat-char (cxr 0 params) #/~))
+
+(defun format-ctl-repeat-char (count char)
+ (declare (fixnum i))
+ (cond ((null count) (setq count 1)))
+ (do i 0 (1+ i) (=& i count)
+ (format:tyo char)))
+
+;; Several commands have a SIZE long object which they must print
+;; in a WIDTH wide field. If WIDTH is specified and is greater than
+;; the SIZE of the thing to be printed, this put out the right
+;; number of CHARs to fill the field. You can call this before
+;; or after printing the thing, to get leading or trailing padding.
+(defun format-ctl-justify (width size &optional (char #\space))
+ (and width (> width size) (format-ctl-repeat-char (- width size) char)))
+
+(defformat q (arg params) one
+ ;; convert params given to a list
+ (do ((ii format-params-supplied (1- ii))
+ (params-given nil))
+ ((< ii 0) (apply arg params-given))
+ (setq params-given (cons (cxr ii params) params-given))))
+
+;; Fixed nested ~[ ~] parser to handle ~:[ ~] and ~@:[ ~] as well. SMH@EMS
+(defun case-scan (goal &optional (lim ctl-length) &aux flag)
+ (declare (fixnum lim ctl-index flag))
+ (do ((ch))
+ ((>= ctl-index lim) nil)
+ (setq ch (getcharn ctl-string (setq ctl-index (1+ ctl-index))))
+ (cond ((= ch #/~)
+ (setq flag 0)
+ (do nil (nil)
+ (setq ch (getcharn ctl-string (setq ctl-index (1+ ctl-index))))
+ (cond ((= ch #/:) (setq flag (+ flag 2)))
+ ((= ch #/@) (setq flag (+ flag 1)))
+ (t (return nil))))
+ (cond ((= ch goal) (return flag))
+ ((= ch #/[) (case-scan #/] lim)))))))
+
+; [ format
+; the case selector is the first parameter given, and if no parameter
+; is given, then it is the next argument
+;
+(defformat \[ (args params) many ; ]
+ (let ((start ctl-index)
+ (num (cond ((> format-params-supplied -1)
+ (cxr 0 params))
+ (t (cond ((null args)
+ (error "the [ format requires an argument")))
+ (prog1 (car args)
+ (setq args (cdr args))
+ (setq arglist-index (1+ arglist-index)))))))
+ (and colon-flag (setq num (cond (num 1) (t 0))))
+ (and (null num)
+ (ferror nil
+ "The FORMAT \"[\" command requires a numeric parameter"))
+ (or (case-scan ; [
+ #/])
+ (ferror nil "Unbalanced \"[\" in FORMAT control string"))
+ (let ((i ctl-index) (tmp))
+ (setq ctl-index start)
+ (do n num (1- n) (= n 0)
+ (setq tmp (case-scan #/;
+ i))
+ (cond ((null tmp) (return nil))
+ ((and (numberp tmp)
+ (>= tmp 2))
+ (return nil)))))
+ args))
+
+(defformat \] (params) none nil)
+
+(defformat \; (params) none
+ (case-scan #/]))
+
+(defformat \{ (args params) many
+ (let ((loop-times (or (cxr 0 params) -1))
+ (loop-string) (loop-start) (loop-length)
+ (at-least-once nil)
+ (return-args)
+ (return-format-arglist)
+ (return-arglist-index))
+ (do ((i (format:string-search-char #/~ ctl-string ctl-index)
+ (format:string-search-char #/~ ctl-string (1+ i))))
+ ((or (null i) (= (1+ i) ctl-length))
+ (ferror () "No matching \"}\" for \"{\" in format"))
+ (cond ((= #/} (getcharn ctl-string (+ 2 i)))
+ (setq loop-start ctl-index
+ loop-length i
+ ctl-index (+ 2 i))
+ (return t))
+ ((and (= #/: (getcharn ctl-string (+ 2 i)))
+ (= #/} (getcharn ctl-string (+ 3 i))))
+ (setq loop-start ctl-index
+ loop-length i
+ ctl-index (+ 3 i)
+ at-least-once t)
+ (return t))))
+ (if (= 0 loop-length)
+ (setq loop-string (pop args)
+ arglist-index (1+ arglist-index)
+ loop-start 0
+ loop-length (pntlen loop-string))
+ (setq loop-string ctl-string))
+ (if (null atsign-flag)
+ (setq return-args (cdr args)
+ return-arglist-index arglist-index
+ arglist-index 0
+ return-format-arglist format-arglist
+ format-arglist (car args)
+ args format-arglist))
+ (*catch '(loop-stop loop-abort)
+ (do ((i loop-times (1- i)))
+ ((and (null at-least-once)
+ (or (null args) (= i 0))))
+ (setq at-least-once nil)
+ (cond ((null colon-flag)
+ (setq args (format-ctl-string args
+ loop-string
+ loop-start
+ loop-length)))
+ (t (*catch 'loop-stop
+ (format-ctl-string (car args)
+ loop-string
+ loop-start
+ loop-length))
+ (setq args (cdr args)
+ arglist-index (1+ arglist-index))))))
+ (cond (return-arglist-index
+ (setq args return-args
+ arglist-index (1+ return-arglist-index)
+ format-arglist return-format-arglist)))
+ args))
+
+(defformat \} (params) none nil)
+
+(defformat \^ (args params) many
+ (let ((terminate nil))
+ (cond ((null (cxr 0 params))
+ (setq terminate (null args)))
+ ((null (cxr 1 params))
+ (setq terminate (zerop (cxr 0 params))))
+ ((null (cxr 2 params))
+ (setq terminate (equal (cxr 1 params) (cxr 0 params))))
+ (t (setq terminate (and (< (cxr 0 params) (cxr 1 params))
+ (< (cxr 1 params) (cxr 2 params))))))
+ (if terminate
+ (if colon-flag (*throw 'loop-abort t) (*throw 'loop-stop t))
+ args)))
+
+
+(declare (special english-small english-medium english-large))
+
+(defun make-list-array (list)
+ (let ((a (makhunk (length list))))
+ (do ((i 0 (1+ i))
+ (ll list (cdr ll)))
+ ((null ll))
+ (rplacx i a (car ll)))
+ a))
+
+(setq english-small
+ (make-list-array '(|one| |two| |three| |four| |five| |six|
+ |seven| |eight| |nine| |ten| |eleven| |twelve|
+ |thirteen| |fourteen| |fifteen| |sixteen|
+ |seventeen| |eighteen| |nineteen|)))
+
+(setq english-medium
+ (make-list-array '(|twenty| |thirty| |forty| |fifty| |sixty| |seventy|
+ |eighty| |ninty|)))
+
+(setq english-large
+ (make-list-array '(|thousand| |million| |billion| |trillion| |quadrillion|
+ |quintillion|)))
+
+
+(defun english-print (n type)
+ (declare (fixnum i n limit))
+ (cond ((zerop n)
+ (cond ((eq type 'cardinal) (format:patom "zero"))
+ (t (format:patom "zeroth"))))
+ ((< n 0)
+ (format:patom '|minus|)
+ (format:tyo #\space)
+ (english-print (minus n) type))
+ (t
+ (do ((n n)
+ (p)
+ (flag)
+ (limit 1000000.
+ (quotient limit 1000.))
+ (i 1 (1- i)))
+ ((< i 0)
+ (cond ((> n 0)
+ (and flag (format:tyo #\space))
+ (english-print-thousand n))))
+ (cond ((not (< n limit))
+ (setq p (quotient n limit)
+ n (remainder n limit))
+ (cond (flag (format:tyo #\space))
+ (t (setq flag t)))
+ (english-print-thousand p)
+ (format:tyo #\space)
+ (format:patom (ar-1 english-large i))))))))
+
+(defun english-print-thousand (n)
+ (declare (fixnum i n limit))
+ (let ((n (remainder n 100.))
+ (h (quotient n 100.)))
+ (cond ((> h 0)
+ (format:patom (ar-1 english-small (1- h)))
+ (format:tyo #\space)
+ (format:patom '|hundred|)
+ (and (> n 0) (format:tyo #\space))))
+ (cond ((= n 0))
+ ((< n 20.)
+ (format:patom (ar-1 english-small (1- n))))
+ (t
+ (format:patom (ar-1 english-medium
+ (- (quotient n 10.) 2)))
+ (cond ((zerop (setq h (remainder n 10.))))
+ (t
+ (format:tyo #/-) ;ascii -
+ (format:patom (ar-1 english-small (1- h)))))))))
+
+(defun roman-step (x n)
+ (cond ((> x 9.)
+ (roman-step (quotient x 10.) (1+ n))
+ (setq x (remainder x 10.))))
+ (cond ((and (= x 9) (not roman-old))
+ (roman-char 0 n)
+ (roman-char 0 (1+ n)))
+ ((= x 5)
+ (roman-char 1 n))
+ ((and (= x 4) (not roman-old))
+ (roman-char 0 n)
+ (roman-char 1 n))
+ (t (cond ((> x 5)
+ (roman-char 1 n)
+ (setq x (- x 5))))
+ (do i 0 (1+ i) (>= i x)
+ (roman-char 0 n)))))
+
+(defun roman-char (i x)
+ (format:tyo (car (nthcdr (+ i x x) '(#/I #/V #/X #/L #/C #/D #/M)))
+ ; i v x l c d m
+))
+
+;;; Kludges to make MacLISP like some of the LISPM functions
+
+
+(defun format:tyo (char)
+ (cond ((dtpr Format-Standard-Output)
+ (rplacd Format-Standard-Output
+ (cons char (cdr Format-Standard-Output))))
+ (t (tyo char Format-Standard-Output))))
+
+(defun format:patom (arg)
+ (format:printorpatom arg nil))
+
+(defun format:print (arg)
+ (format:printorpatom arg t))
+
+(defun format:printorpatom (argument slashify)
+ (cond ((dtpr Format-Standard-Output)
+ (rplacd Format-Standard-Output
+ (nreconc (cond (slashify
+ (mapcar '(lambda (x)
+ (getcharn x 1))
+ (explode argument)))
+ ((exploden argument)))
+ (cdr Format-Standard-Output))))
+ (t (cond (slashify (print argument Format-Standard-Output))
+ (t (patom argument Format-Standard-Output))))))
+
+(defun format:terpr nil
+ (cond ((dtpr Format-Standard-Output)
+ (rplacd Format-Standard-Output
+ (cons #\newline (cdr Format-Standard-Output))))
+ (t (terpr Format-Standard-Output))))
+
+(defun format:fresh-line nil
+ (cond ((dtpr Format-Standard-Output)
+ (cond ((and (cdr Format-Standard-Output)
+ (not (= (cadr Format-Standard-Output) #\newline)))
+ (rplacd Format-Standard-Output
+ (cons #\newline (cdr Format-Standard-Output))))))
+ (t (and (not (= 0 (nwritn Format-Standard-Output)))
+ (terpr Format-Standard-Output)))))
+
+
+
+
+(defun format\:string-search-char (char str start-pos)
+ (declare (fixnum i start-pos str-len))
+ (do ((i start-pos (1+ i))
+ (str-len (flatc str)))
+ ((>& i str-len) nil)
+ (and (=& char (getcharn str (1+ i))) (return i))))
+
+(defun format\:nsubstring (str from to)
+ (declare (fixnum i from to))
+ (substring str (+ 1 from) (- to from))) ;substring is 1 based
+
+(defun ferror (&rest args)
+ (let (str)
+ ; if the first arg to ferror is a string we assume that it is the
+ ; format control string, otherwise we assume that it is a port
+ ; specification, and we ignore it since we want to build a string.
+ (if (stringp (car args))
+ then (setq str (lexpr-funcall 'format nil args))
+ else (setq str (lexpr-funcall 'format nil (cdr args))))
+ (error str)))
+
+
+(defun format-test nil
+ (format t "Start test, newline:~%freshline:~&")
+ (format t "decimal:~d, width=5:~5d~%" 10 10)
+ (format t "decimal pad with period:~10,vd~%" #/. 12)
+ (format t "char normal:~c, as # would read:~@c, human read:~:c~%"
+ #\space #\space #\space)
+ (format t "cardinal:~r, roman new:~@r, roman-old:~:@r~
+ <same line I hope>~@
+ new line but at beginning~:
+ same line, but spaced out~:@
+ new line and over two tabs~%" 4 4 4))
+
+(putprop 'format t 'version)