BSD 4_4 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 12 Jul 1985 15:52:58 +0000 (07:52 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Fri, 12 Jul 1985 15:52:58 +0000 (07:52 -0800)
Work on file usr/src/old/lisp/lisplib/format.l

Synthesized-from: CSRG/cd3/4.4

usr/src/old/lisp/lisplib/format.l [new file with mode: 0644]

diff --git a/usr/src/old/lisp/lisplib/format.l b/usr/src/old/lisp/lisplib/format.l
new file mode 100644 (file)
index 0000000..d38d1bd
--- /dev/null
@@ -0,0 +1,863 @@
+(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)