Annotation of 42BSD/ucb/lisp/lisplib/format.l, revision 1.1.1.1

1.1       root        1: (setq rcs-format-
                      2:    "$Header")
                      3: 
                      4: ;;
                      5: ;;  format.l                           -[Fri Mar  4 12:20:16 1983 by jkf]-
                      6: ;;
                      7: ;;   This is a function for printing or creating nicely formatted strings.
                      8: ;; This file is a modified version of the format program which runs in
                      9: ;; the mit lisps.  When converting to franz, compatibility was the
                     10: ;; major goal, thus we still use the \ character as a string delimiter
                     11: ;; within a command string, even though it must be doubled in Franz.
                     12: ;;
                     13: ;; The file contains the user callable functions:
                     14: ;;   format    - lexpr for doing formated printed output or creating
                     15: ;;               strings
                     16: ;;   defformat  - macro for adding a format directive
                     17: ;;
                     18: 
                     19: ; FORMAT prints several arguments according to a control argument.
                     20: ; The control argument is either a string or a list of strings and lists.
                     21: ; The strings and lists are interpreted consecutively.
                     22: ; Strings are for the most part just printed, except that the character ~
                     23: ; starts an escape sequence which directs other actions.
                     24: ; A ~ escape sequence has an (optional) numeric parameter followed by a
                     25: ; mode character.
                     26: ; These escape actions can use up one or more of the non-control arguments.
                     27: ; A list in the control-argument list is also interpreted as an escape.
                     28: ; Its first element is the mode, a symbol which may be any length,
                     29: ; and its remaining elements are parameters.  The list (D 5) is equivalent
                     30: ; to the ~ escape "~5D";  similarly, each ~ escape has an equivalent list.
                     31: ; However, there are list escapes which have no ~ equivalent.
                     32: 
                     33: ; Any undefined list escape is simply evaluated.
                     34: 
                     35: ;These are the escape modes which are defined:
                     36: ; ~nD Takes any number and prints as a decimal integer.  If no arg,
                     37: ;     print without leading spaces.  If arg and it fits in, put in leading
                     38: ;     spaces; if it doesnt fit just print it.  If second arg, use that
                     39: ;     (or first char of STRING of it if not a number) instead of space
                     40: ;     as a pad char.
                     41: ; ~nF Floating point
                     42: ; ~nE Exponential notation
                     43: ; ~nO Like D but octal
                     44: ; ~nA Character string.  If there is an n then pad the string with spaces 
                     45: ;     on the right to make it n long.  If it doesn't fit, ignore n.
                     46: ; ~n,m,minpad,padcharA  Pad on the right to occupy at least
                     47: ;     n columns, or if longer than that to begin with, pad to occupy
                     48: ;     n+p*m columns for some nonnegative integer p.
                     49: ;     at least minpad pad characters are produced in any case
                     50: ;     (default if not supplied = 0).
                     51: ;     padchar is used for padding purposes (default if not supplied = space).
                     52: ;      if padchar is not a number, the first character in STRING of it is used.
                     53: 
                     54: ;     A mode can actually be used to PRINC anything, not just a string.
                     55: ; ~S  Prin1 an object.  Just like ~A (including parameters) but uses PRIN1.
                     56: ; ~C  One character, in any acceptable form.
                     57: ;      Control and meta bits print as alpha, beta, epsilon.
                     58: ; ~n* Ignore the next n args.  n defaults to 1.
                     59: ; ~n% Insert n newlines.  n defaults to 1.
                     60: ; ~n| Insert n formfeeds.  n defaults to 1.
                     61: ; ~nX Insert n spaces.  n defaults to 1.
                     62: ; ~n~ Insert n tildes.  n defaults to 1.
                     63: ; ~&  Perform the :FRESH-LINE operation on the stream.
                     64: ; ~n,mT  Tab to column n+pm, for p an integer >= 0.
                     65: ; ~Q  Apply the next arg to no arguments.
                     66: ; (Q ...) Apply the next arg to the (unevaluated) parameters following the Q.
                     67: ; ~P  Insert an "s", unless its argument is a 1
                     68: ; ~nG  Goto the nth argument (zero based).  The next command will get that
                     69: ;      argument, etc.
                     70: ; ~E and ~F are not implemented.  ~T is not implemented.
                     71: 
                     72: ; (FORMAT <stream> <control arg> <args>)
                     73: ; If <stream> is NIL, cons up and return a symbol.
                     74: ; If <stream> is T, use STANDARD-OUTPUT (saves typing).
                     75: 
                     76: ;; defformat:
                     77: ;;  to add a format handler, the defformat macro is used.
                     78: ;; the form is (defformat code args type . body)
                     79: ;;   where
                     80: ;;   code is the code this will handle.  the code can be a multi
                     81: ;;    character symbol, however it will have to be called with \\code\\.
                     82: ;;   args is either a one or two symbol list, depending on type
                     83: ;;   type is either: none, one, or many.
                     84: ;;    none means that type handler will not use any argument (it may use
                     85: ;;      use parameters however)
                     86: ;;    one means that it takes exactly one argument
                     87: ;;    many means that it may take from zero to ?? arguments.
                     88: ;;   body is the body of the function.  Its return value is only important
                     89: ;;    in the case of 'many' handlers since these handlers must return the
                     90: ;;    list of the arguments they didn't use.
                     91: ;;
                     92: ;;  'none' handlers get passes a hunk which contains the parameters provide
                     93: ;;     for this format directive.
                     94: ;;  'one' handlers are passed the argument and the parameters.
                     95: ;;  'many' handlers are passed the list of remaining arguments and the
                     96: ;;     parameters. they return the arguments they don't use.
                     97: 
                     98: ;to do:
                     99: ;  3) make sure the semantics follows the lisp machine defs
                    100: ;  6) do exponential (~e) floating point formats correctly.
                    101: ;  7) move ferror elsewhere (near error would be a good place).
                    102: ;  8) document it.
                    103: ;  11) fix ~a to left justify if given correct flag
                    104: ;  13) make sure that multi character directives are lower cased
                    105: ;  14) make the 'x parameter work correctly
                    106: ;  15) fix the english printer (wrt stream arg) and add ordinal
                    107: 
                    108: 
                    109: ;;; Kludges to make MacLISP like some of the LISPM functions
                    110: 
                    111: (declare (special Format-Standard-Output roman-old 
                    112:                  format-params-supplied format format-handlers
                    113:                  format-sharpsign-vars))
                    114: 
                    115: (setq format-sharpsign-vars 'franz-symbolic-character-names)
                    116: 
                    117: ;; format-params-supplied : numbers of parameters to format parameter
                    118: ;; roman-old when t, the roman printer will print IIII instead of IV
                    119: 
                    120: (or (boundp 'roman-old) (setq roman-old nil))
                    121: 
                    122: (declare (setq defmacro-for-compiling nil defmacro-displace-call nil ))
                    123:   (defmacro nsubstring (&rest w) `(format\:nsubstring ,.w))
                    124:   (defmacro string-search-char (&rest w) `(format\:string-search-char ,.w))
                    125:   (defmacro ar-1 (ar ind) `(cxr ,ind ,ar))
                    126:   (defmacro as-1 (val ar ind) `(rplacx ,ind ,ar ,val))
                    127:   (defmacro >= (x y) `(not (< ,x ,y)))
                    128:   (defmacro <= (x y) `(not (> ,x ,y)))
                    129:   (defmacro neq (x y) `(not (= ,x ,y)))
                    130:   (defmacro pop (stack) `(prog1 (car ,stack) (setq ,stack (cdr ,stack))))
                    131: (declare (setq defmacro-for-compiling 't defmacro-displace-call 't))
                    132: 
                    133: 
                    134: (declare
                    135:  (special ctl-string            ;The control string.
                    136:          ctl-length             ;string-length of ctl-string.
                    137:          ctl-index              ;Our current index into the control string.
                    138:                                 ; Used by the conditional command. (NYI)
                    139:          atsign-flag            ;Modifier
                    140:          colon-flag             ;Modifier
                    141:          format-temporary-area  ;For temporary consing
                    142:          format-arglist         ;The original arg list, for ~G.
                    143:          arglist-index          ;How far we are in the current arglist
                    144:          float-format           ; format used when printing floats
                    145:          poport                ; franz's standard output
                    146:          ))
                    147: 
                    148: (defun format (stream ctl-string &rest args)
                    149:   (let (format-string Format-Standard-Output
                    150:        (format-arglist args)
                    151:        (arglist-index 0))
                    152:     (setq stream (cond ((eq stream 't) poport )
                    153:                       ((null stream)
                    154:                        (setq format-string 't)
                    155:                        (list nil))
                    156:                       (t stream)))
                    157:     (setq Format-Standard-Output stream)
                    158:     (cond ((symbolp ctl-string)
                    159:           (setq ctl-string (get_pname ctl-string))))
                    160:     (cond ((stringp ctl-string)
                    161:           (format-ctl-string args ctl-string))
                    162:          (t (do ((ctl-string ctl-string (cdr ctl-string)))
                    163:               ((null ctl-string))
                    164:               (setq args
                    165:                     (cond ((symbolp (car ctl-string))
                    166:                            (format-ctl-string args (car ctl-string)))
                    167:                           (t (format-ctl-list args (car ctl-string))))))))
                    168:     (and format-string
                    169:         (setq format-string (maknam (nreverse (cdr stream)))))
                    170:     format-string))
                    171: 
                    172: (defun format-ctl-list (args ctl-list)
                    173:        (format-ctl-op (car ctl-list) args (cdr ctl-list)))
                    174: 
                    175: (defun format-ctl-string (args ctl-string)
                    176:     (declare (fixnum ctl-index ctl-length))
                    177:     (do   ((ctl-index 0) (ch) (tem) (str) (sym)
                    178:           (ctl-length (flatc ctl-string)))
                    179:          ((>= ctl-index ctl-length) args)
                    180:        (setq tem (cond ((string-search-char #/~ ctl-string ctl-index))
                    181:                        (t ctl-length)))
                    182:        (cond ((neq tem ctl-index)              ;Put out some literal string
                    183:               (setq str (nsubstring ctl-string ctl-index tem))
                    184:               (format:patom str)
                    185:               (and (>= (setq ctl-index tem) ctl-length)
                    186:                    (return args))))
                    187:        ;; (ar-1 ch ctl-index) is a tilde.
                    188:        (do ((atsign-flag nil)  ;Modifier
                    189:             (colon-flag nil)   ;Modifier
                    190:             (params (makhunk 10))
                    191:             (param-leader -1)
                    192:                                ;PARAMS contains the list of numeric parameters
                    193:             (param-flag nil)   ;If T, a parameter has been started in PARAM
                    194:             (param))           ;PARAM is the parameter currently
                    195:                                ; being constructed
                    196:            ((>= (setq ctl-index (1+ ctl-index)) ctl-length))
                    197:          (setq ch (getcharn ctl-string (1+ ctl-index)))
                    198:          (cond ((and (>= ch #/0) (<= ch #/9))                   ; 
                    199:                 (setq param (+ (* (or param 0) 10.) (- ch #/0)) ; 
                    200:                     param-flag t))
                    201:                ((= ch #/@)                                      ;ascii @
                    202:                 (setq atsign-flag t))
                    203:                ((= ch #/:)                                      ;ascii :
                    204:                 (setq colon-flag t))
                    205:                ((or (= ch #/v) (= ch #/V))                     ;ascii v, v
                    206:                 (as-1 (pop args) params
                    207:                       (setq param-leader (1+ param-leader)))
                    208:                 (setq arglist-index (1+ arglist-index))
                    209:                 (setq param nil param-flag nil))
                    210:                ((= ch #/#)
                    211:                 (as-1 (length args) params
                    212:                       (setq param-leader (1+ param-leader))))
                    213:                ((= ch #/,)
                    214:                   ;comma, begin another parameter, ascii ,
                    215:                 (and param-flag (as-1 param params (setq param-leader
                    216:                                                          (1+ param-leader))))
                    217:                 (setq param nil param-flag t))
                    218:                  ;omitted arguments made manifest by the
                    219:                  ;presence of a comma come through as nil
                    220:                (t              ;must be a command character
                    221:                    ;upper case to lower 
                    222:                  (and (>= ch #/A) (<= ch #/Z) (setq ch (+ ch (- #/a #/A))))
                    223:                  (setq ctl-index (1+ ctl-index)) ;advance past command char
                    224:                  (and param-flag (as-1 param params (setq param-leader
                    225:                                                           (1+ param-leader))))
                    226:                  (setq param-flag nil param nil tem nil)
                    227:                   ;str gets a string which is the name of the operation to do
                    228:                  (setq
                    229:                    str (cond ((= ch #/\ )                       ;ascii \
                    230:                               (let ((i (string-search-char
                    231:                                           #/\
                    232:                                           ctl-string
                    233:                                           (1+ ctl-index))))
                    234:                                  (and (null i)
                    235:                                       (ferror nil
                    236:                                          '|Unmatched \\ in control string.|))
                    237:                                  (prog1 ; don't uppercase! we are a two
                    238:                                     ; case system
                    239:                                     (setq tem
                    240:                                           (nsubstring ctl-string
                    241:                                                       (1+ ctl-index)
                    242:                                                       i))
                    243:                                     (setq ctl-index i))))
                    244:                              (t (ascii ch))))
                    245:                   ;; SYM gets the symbol corresponding to STR
                    246:                  (cond ((setq sym str)
                    247:                         (setq format-params-supplied param-leader)
                    248:                         (setq args (format-ctl-op sym args params)))
                    249:                        (t (ferror nil '|~C is an unknown FORMAT op in \"~A\"|
                    250:                                   tem ctl-string)))
                    251:                  (return nil))))))
                    252: 
                    253: ;Perform a single formatted output operation on specified args.
                    254: ;Return the remaining args not used up by the operation.
                    255: (defun format-ctl-op (op args params &aux tem)
                    256:    (cond ((stringp op) (setq op (concat op))))  ; make into a symbol
                    257:    (cond ((setq tem (assq op format-handlers))
                    258:          (cond ((eq 'one (cadr tem))
                    259:                 (or args
                    260:                     (ferror nil "arg required for ~a, but no more args" op))
                    261:                 (funcall (caddr tem) (car args) params)
                    262:                 (setq arglist-index (1+ arglist-index))
                    263:                 (cdr args))
                    264:                ((eq 'none (cadr tem))
                    265:                 (funcall (caddr tem) params)
                    266:                 args)
                    267:                ((eq 'many (cadr tem))
                    268:                 (funcall (caddr tem) args params))
                    269:                (t (ferror nil "Illegal format handler: ~s" tem))))
                    270:         (t (ferror nil '|\"~S\" is not defined as a FORMAT command.| op)
                    271:            args)))
                    272: 
                    273: (setq format-handlers nil)
                    274: ;; Format handlers
                    275: ;;
                    276: (defmacro defformat (name arglist type &rest body)
                    277:    (let (newname)
                    278:       ;; allow the name to be the fixnum rep of a character too.
                    279:       (cond ((fixp name) (setq name (concat "ch" name))))
                    280:       
                    281:       (cond ((not (memq type '(none one many)))
                    282:             (ferror nil "The format type, \"~a\" is not: none, one or many"
                    283:                     type)))
                    284:       (cond ((or (not (symbolp name))
                    285:                 (not (dtpr arglist)))
                    286:             (ferror nil "Bad form for name and/or arglist: ~a ~a"
                    287:                     name arglist)))
                    288:       (cond ((memq type '(one many))
                    289:             (cond ((not (= (length arglist) 2))
                    290:                    (ferror nil "There should be 2 arguments to ~a" name))))
                    291:            (t (cond ((not (= (length arglist) 1))
                    292:                      (ferror nil "There should be 1 argument to ~a" name)))))
                    293:       (setq newname (concat name ":format-handler"))
                    294:       `(progn 'compile
                    295:              (defun ,newname ,arglist ,@body)
                    296:              (let ((handler (assq ',name format-handlers)))
                    297:                 (cond (handler (rplaca (cddr handler) ',newname))
                    298:                       (t (setq format-handlers (cons (list ',name
                    299:                                                            ',type
                    300:                                                            ',newname)
                    301:                                                      format-handlers))))))))
                    302: 
                    303: 
                    304: 
                    305: (defformat d (arg params) one
                    306:    (let ((width (cxr 0 params))
                    307:         (padchar (cxr 1 params)))
                    308:     (cond ((and colon-flag (< arg 4000.) (> arg 0))
                    309:           (roman-step arg 0))
                    310:          (atsign-flag (english-print arg 'cardinal))
                    311:          ((let ((base 10.) (*nopoint t))
                    312:             (cond ((null padchar) (setq padchar 32.))
                    313:                   ((not (numberp padchar))
                    314:                    (setq padchar (getcharn padchar 1))))
                    315:             (and width (format-ctl-justify width (flatc arg) padchar))
                    316:             (format:patom arg))))))
                    317: 
                    318: (defformat f (arg params) one
                    319:    (cond ((not (floatp arg)) (format:patom arg))
                    320:         (t (let ((float-format "%.16g")
                    321:                  (prec (cxr 0 params)))
                    322:               (cond ((and prec (fixp prec) (> prec 0) (< prec 16))
                    323:                      (setq float-format (concat "%" prec "g"))))
                    324:               (format:patom arg)))))
                    325: 
                    326: ; r format
                    327: ; no params and flags: print as cardinal (four)
                    328: ; no params and colon: print as ordinal  (fourth)
                    329: ; no params and atsign: print as roman (IV)
                    330: ; no params and colon and atsign: print as old roman (IIII)
                    331: ; params:  radix,mincol[0],padchar[<space>]
                    332: ;              print in radix with at least mincol columns, padded on left
                    333: ;              with padchar
                    334: ;
                    335: (defformat r (arg params) one
                    336:    (format:anyradix-printer arg params nil))
                    337: 
                    338: ; o format - like ~8r, but params are like ~d.
                    339: ;
                    340: (defformat o (arg params) one
                    341:    (format:anyradix-printer arg params 8.))
                    342: 
                    343: (defun format:anyradix-printer (arg params radix)
                    344:    ; this is called by ~r and ~o.  for ~r, the mincol parameter starts at
                    345:    ; cxr 1, for ~o the mincol parameter starts at cxr 0.  We compute
                    346:    ; paramstart as either 0 or 1
                    347:    ; radix is given as third argument iff this is ~o
                    348:    (let ((paramstart (cond (radix 0)
                    349:                           (t 1))))
                    350:       (cond ((null radix) (setq radix (cxr 0 params))))
                    351:       (cond ((null radix)      ; if not to any given base
                    352:             (cond ((and (null colon-flag) (null atsign-flag))
                    353:                    (english-print arg 'cardinal))
                    354:                   ((and colon-flag (null atsign-flag))
                    355:                    (english-print arg 'ordinal))
                    356:                   ((and (null colon-flag) atsign-flag)
                    357:                    (roman-step arg 0))
                    358:                   ((and colon-flag atsign-flag)
                    359:                    (let ((roman-old t))
                    360:                       (roman-step arg 0)))))
                    361:            (t (let ((mincol (cxr paramstart params))
                    362:                     (padchr (or (cxr (+ 1 paramstart) params) #\space))
                    363:                     (res))
                    364:                  (cond (mincol         ;; if mincol specified
                    365:                           (let ((Format-Standard-Output (list nil)))
                    366:                              (format-binpr arg radix)
                    367:                              (setq res (cdr Format-Standard-Output)))
                    368:                           (format-ctl-justify mincol (length res) padchr)
                    369:                           (mapc 'format:tyo (nreverse res)))
                    370:                        (t (format-binpr arg radix))))))))
                    371:               
                    372: 
                    373: (defun format-binpr (x base)
                    374:    (cond ((equal x 0)(format:patom 0))
                    375:         ((or (> base 36.) (< base 2))
                    376:          (ferror nil "\"~s\" is not a base between 2 and 36" base))
                    377:         ((lessp x 0)
                    378:          (format:patom '-)
                    379:          (format-binpr1 (minus x) base))
                    380:         (t (format-binpr1 x base)))
                    381:    x)
                    382: 
                    383: 
                    384: 
                    385: (defun format-binpr1 (x base)
                    386:    (cond ((equal x 0))
                    387:         (t (format-binpr1 (quotient x base) base)
                    388:            (format-prc (remainder x base)))))
                    389: 
                    390: (defun format-prc (x)
                    391:    (cond ((< x 10.) (format:patom x))
                    392:         (t (format:tyo (plus (- #/a 10.) x)))))
                    393:                        ; works for 10.=A, 35.=Z.
                    394: 
                    395: ;; must get the width stuff to work!!
                    396: (defun format-ctl-octal (arg params)
                    397:    (let ((width (cxr 0 params)) (padchar (cxr 1 params)))
                    398:       (let ((base 8))
                    399:         (cond ((null padchar)
                    400:                (setq padchar 32.))
                    401:               ((not (numberp padchar))
                    402:                (setq padchar (getcharn padchar 1))))
                    403:         (and width (format-ctl-justify width (flatc arg) padchar))
                    404:         (format:patom arg))))
                    405: 
                    406: (defformat a (arg params) one
                    407:    (format-ctl-ascii arg params nil))
                    408: 
                    409: (defun format-ctl-ascii (arg params prin1p)
                    410:     (let ((edge (cxr 0 params))
                    411:          (period (cxr 1 params))
                    412:           (min (cxr 2 params))
                    413:          (padchar (cxr 3 params)))
                    414:         (cond ((null padchar)
                    415:                (setq padchar #\space))
                    416:               ((not (numberp padchar))
                    417:                (setq padchar (getcharn padchar 1))))
                    418:          (cond (prin1p (format:print arg))
                    419:                (t (format:patom arg)))
                    420:         (cond ((not (null edge))
                    421:                (let ((width (cond (prin1p (flatsize arg)) ((flatc arg)))))
                    422:                  (cond ((not (null min))
                    423:                         (format-ctl-repeat-char min padchar)
                    424:                         (setq width (+ width min))))
                    425:                  (cond (period
                    426:                         (format-ctl-repeat-char
                    427:                          (- (+ edge (* (\\ (+ (- (max edge width) edge 1)
                    428:                                               period)
                    429:                                            period)
                    430:                                        period))
                    431:                             width)
                    432:                          padchar))
                    433:                        (t (format-ctl-justify edge width padchar))))))))
                    434: 
                    435: (defformat s (arg params) one
                    436:     (format-ctl-ascii arg params t))
                    437: 
                    438: (defformat c (arg params) one
                    439:    (cond ((or (not (fixp arg))
                    440:              (< arg 0)
                    441:              (> arg 127))
                    442:          (ferror nil "~s is not a legal character value" arg)))
                    443:    (cond ((and (not colon-flag) (not atsign-flag))
                    444:          ; just print out the character after converting to ascii
                    445:          (format:patom (ascii arg)))
                    446:         (t ; it may have an extended name, check for that first
                    447:            (let (name)
                    448:               (cond ((setq name (car
                    449:                                    (rassq arg (symeval format-sharpsign-vars))))
                    450:                      ; it has an extended name.
                    451:                      ; if : flag, then print in human readable
                    452:                      (cond (colon-flag (format:patom name))
                    453:                            (atsign-flag (format:patom "#\\")
                    454:                                         (format:patom name))))
                    455:                     ((< arg #\space)
                    456:                      ; convert from control to upper case
                    457:                      (setq arg (+ arg #/@))
                    458:                      (cond (colon-flag (format:patom "^")
                    459:                                        (format:patom (ascii arg)))
                    460:                            (atsign-flag (format:patom "#^")
                    461:                                         (format:patom (ascii arg)))))
                    462:                     (t (cond (colon-flag (format:patom (ascii arg)))
                    463:                              (atsign-flag (format:patom "#/")
                    464:                                           (format:patom (ascii arg))))))))))
                    465: 
                    466: (defformat p (args params) many
                    467:   (let (arg)
                    468:     (cond (colon-flag
                    469:           (setq arg (nth (1- arglist-index) format-arglist)))
                    470:          ((null args)
                    471:           (ferror () "Argument required for p, but no more arguments"))
                    472:          (t (setq arg (pop args)
                    473:                   arglist-index (1+ arglist-index))))
                    474:     (if (= arg 1)
                    475:        (if atsign-flag (format:tyo #/y))
                    476:        (cond (atsign-flag
                    477:               (format:tyo #/i)
                    478:               (format:tyo #/e)
                    479:               (format:tyo #/s))
                    480:              (t (format:tyo #/s))))
                    481:     args))
                    482: 
                    483: (defformat *  (args params) many
                    484:   (let ((count (or (cxr 0 params) 1)))
                    485:     (if colon-flag (setq count (minus count)))
                    486:     (setq arglist-index (+ arglist-index count))
                    487:     (nthcdr count format-arglist)))
                    488: 
                    489: (defformat g (arg params) many
                    490:        (let ((count (or (cxr 0 params) 1)))
                    491:            (nthcdr count format-arglist)))
                    492: 
                    493: (defformat % (params) none
                    494:        (declare (fixnum i))
                    495:        (let ((count (or (cxr 0 params) 1)))
                    496:            (do i 0 (1+ i) (= i count)
                    497:                (format:terpr))))
                    498: 
                    499: ;  ~ at the end of the line
                    500: ;  no params: ignore newline and following whitespace
                    501: ;  @ flag: leave the newline in the string but ignore whitespace
                    502: ;  : flag: ignore newline but leave the whitespace
                    503: ;  :@ flags: leave both newline and whitespace
                    504: ;
                    505: (defformat #\newline (params) none
                    506:    (cond (atsign-flag
                    507:            (format:tyo #\newline)))
                    508:    (cond ((not colon-flag)
                    509:          (setq ctl-index (1+ ctl-index))
                    510:          (do ()
                    511:              ((>= ctl-index ctl-length))
                    512:              (cond ((memq (getcharn ctl-string ctl-index)
                    513:                           '(#\space #\tab))
                    514:                     (setq ctl-index (1+ ctl-index)))
                    515:                    (t (setq ctl-index (1- ctl-index))
                    516:                       (return)))))))
                    517: 
                    518:                 
                    519: (defformat & (params) none
                    520:     (format:fresh-line))
                    521: 
                    522: (defformat x (params) none
                    523:    (format-ctl-repeat-char (cxr 0 params) #\space))
                    524: 
                    525: (defformat \| (params) none
                    526:    (format-ctl-repeat-char (cxr 0 params) #\ff))
                    527:    
                    528: (defformat ~ (params) none
                    529:    (format-ctl-repeat-char (cxr 0 params) #/~))
                    530: 
                    531: (defun format-ctl-repeat-char (count char)
                    532:     (declare (fixnum i))
                    533:     (cond ((null count) (setq count 1)))
                    534:     (do i 0 (1+ i) (=& i count)
                    535:        (format:tyo char)))
                    536: 
                    537: ;; Several commands have a SIZE long object which they must print
                    538: ;; in a WIDTH wide field.  If WIDTH is specified and is greater than
                    539: ;; the SIZE of the thing to be printed, this put out the right
                    540: ;; number of  CHARs to fill the field.  You can call this before
                    541: ;; or after printing the thing, to get leading or trailing padding.
                    542: (defun format-ctl-justify (width size &optional (char #\space))
                    543:     (and width (> width size) (format-ctl-repeat-char (- width size) char)))
                    544: 
                    545: (defformat q (arg params) one
                    546:    ;; convert params given to a list
                    547:    (do ((ii format-params-supplied (1- ii))
                    548:        (params-given nil))
                    549:        ((< ii 0) (apply arg params-given))
                    550:        (setq params-given (cons (cxr ii params) params-given))))
                    551: 
                    552: (defun case-scan (goal &optional (lim ctl-length) (times 1))
                    553:   (declare (fixnum cnt lim times ctl-index))
                    554:   (*catch 'case-scan
                    555:     (do ((cnt 0 (1+ cnt)))
                    556:        ((>= cnt times) t)
                    557:       (do ((ch))
                    558:          ((>= ctl-index lim)
                    559:           (*throw 'case-scan nil))
                    560:        (setq ch (getcharn ctl-string (1+ ctl-index))
                    561:              ctl-index (1+ ctl-index))
                    562:        (cond ((= ch #/~)
                    563:               (setq ch (getcharn ctl-string (1+ ctl-index))
                    564:                     ctl-index (1+ ctl-index))
                    565:               (cond ((= ch goal)
                    566:                      (return t))
                    567:                     ((= ch #/[)
                    568:                      (case-scan #/] lim)))))))))
                    569: 
                    570: ; [ format
                    571: ;  the case selector is the first parameter given, and if no  parameter
                    572: ;  is given, then it is the next argument
                    573: ;
                    574: (defformat \[ (args params) many
                    575:    (let ((start ctl-index)
                    576:         (num (cond ((> format-params-supplied -1)
                    577:                     (cxr 0 params))
                    578:                    (t (cond ((null args)
                    579:                              (error "the [ format requires an argument")))
                    580:                       (prog1 (car args)
                    581:                              (setq args (cdr args))
                    582:                              (setq arglist-index (1+ arglist-index)))))))
                    583:       (and colon-flag (setq num (cond (num 1) (t 0))))
                    584:       (and (null num)
                    585:           (ferror nil
                    586:               "The FORMAT \"[\" command must be given a numeric parameter"))
                    587:       (cond ((>= num 0)
                    588:             (or (case-scan #/])
                    589:                 (ferror nil "Unbalanced conditional in FORMAT control string"))
                    590:             (let ((i ctl-index))
                    591:                (setq ctl-index start)
                    592:                (case-scan #/; i num))))
                    593:       args))
                    594: 
                    595: (defformat \] (params) none nil)
                    596: 
                    597: (defformat \; (params) none
                    598:    (case-scan #/]))
                    599: 
                    600: ;; FIXTHIS:
                    601: ;; The following doesn't bind format-arglist and arglist-index properly.
                    602: (defformat \{ (args params) many
                    603:   (let ((loop-times (or (cxr 0 params) -1))
                    604:        (loop-string)
                    605:        (at-least-once nil))
                    606:     (do ((i (format\:string-search-char #/~ ctl-string ctl-index)
                    607:            (format\:string-search-char #/~ ctl-string (1+ i))))
                    608:         ((or (null i) (= (1+ i) ctl-length))
                    609:         (ferror () "No matching \"}\" for \"{\" in format"))
                    610:       (cond ((= #/} (getcharn ctl-string (+ 2 i)))
                    611:             (setq loop-string
                    612:                   (format\:nsubstring ctl-string ctl-index i)
                    613:                   ctl-index (+ 2 i))
                    614:             (return t))
                    615:            ((and (= #/: (getcharn ctl-string (+ 2 i)))
                    616:                  (= #/} (getcharn ctl-string (+ 3 i))))
                    617:             (setq loop-string
                    618:                   (format\:nsubstring ctl-string ctl-index i)
                    619:                   ctl-index (+ 3 i)
                    620:                   at-least-once t)
                    621:             (return t))))
                    622:     (if (= 0 (flatc loop-string))
                    623:        (setq loop-string (pop args)
                    624:              arglist-index (1+ arglist-index)))
                    625:     (if (null atsign-flag) (setq args (car args)))
                    626:     (*catch '(loop-stop loop-abort)
                    627:       (do ((i loop-times (1- i)))
                    628:          ((and (null at-least-once)
                    629:                (or (null args) (= i 0))))
                    630:        (setq at-least-once nil)
                    631:        (cond ((null colon-flag)
                    632:               (setq args (format-ctl-string args loop-string)))
                    633:              (t (*catch 'loop-stop
                    634:                    (format-ctl-string (car args) loop-string))
                    635:                 (setq args (cdr args)
                    636:                       arglist-index (1+ arglist-index))))))
                    637:     args))
                    638: 
                    639: (defformat \} (params) none nil)
                    640: 
                    641: (defformat \^ (args params) many
                    642:   (let ((terminate nil))
                    643:     (cond ((null (cxr 0 params))
                    644:           (setq terminate (null args)))
                    645:          ((null (cxr 1 params))
                    646:           (setq terminate (zerop (cxr 0 params))))
                    647:          ((null (cxr 2 params))
                    648:           (setq terminate (equal (cxr 1 params) (cxr 0 params))))
                    649:          (t (setq terminate (and (< (cxr 0 params) (cxr 1 params))
                    650:                                  (< (cxr 1 params) (cxr 2 params))))))
                    651:     (if terminate
                    652:        (if colon-flag (*throw 'loop-abort t) (*throw 'loop-stop t))
                    653:        args))) 
                    654: 
                    655: 
                    656: (declare (special english-small english-medium english-large))
                    657: 
                    658: (defun make-list-array (list)
                    659:    (let ((a (makhunk (length list))))
                    660:       (do ((i 0 (1+ i))
                    661:           (ll list (cdr ll)))
                    662:          ((null ll))
                    663:          (rplacx i a (car ll)))
                    664:       a))
                    665: 
                    666: (setq english-small
                    667:    (make-list-array '(|one| |two| |three| |four| |five| |six|
                    668:                            |seven| |eight| |nine| |ten| |eleven| |twelve|
                    669:                            |thirteen| |fourteen| |fifteen| |sixteen|
                    670:                            |seventeen| |eighteen| |nineteen|)))
                    671: 
                    672: (setq english-medium
                    673:    (make-list-array '(|twenty| |thirty| |forty| |fifty| |sixty| |seventy|
                    674:                               |eighty| |ninty|)))
                    675: 
                    676: (setq english-large
                    677:    (make-list-array '(|thousand| |million| |billion| |trillion| |quadrillion|
                    678:                                 |quintillion|)))
                    679: 
                    680: 
                    681: (defun english-print (n type)
                    682:        (declare (fixnum i n limit))
                    683:        (cond ((zerop n)
                    684:              (cond ((eq type 'cardinal) (format:patom "zero"))
                    685:                    (t (format:patom "zeroth"))))
                    686:             ((< n 0)
                    687:              (format:patom '|minus|)
                    688:              (format:tyo #\space)
                    689:              (english-print (minus n) type))
                    690:             (t
                    691:              (do ((n n)
                    692:                    (p)
                    693:                   (flag)
                    694:                   (limit 1000000.
                    695:                          (quotient limit 1000.))
                    696:                   (i 1 (1- i)))
                    697:                  ((< i 0)
                    698:                   (cond ((> n 0)
                    699:                          (and flag (format:tyo #\space))
                    700:                          (english-print-thousand n))))
                    701:                  (cond ((not (< n limit))
                    702:                         (setq p (quotient n limit)
                    703:                               n (remainder n limit))
                    704:                         (cond (flag (format:tyo #\space))
                    705:                               (t (setq flag t)))
                    706:                         (english-print-thousand p)
                    707:                         (format:tyo #\space)
                    708:                         (format:patom (ar-1 english-large i))))))))
                    709: 
                    710: (defun english-print-thousand (n)
                    711:        (declare (fixnum i n limit))
                    712:        (let ((n (remainder n 100.))
                    713:             (h (quotient n 100.)))
                    714:            (cond ((> h 0)
                    715:                   (format:patom (ar-1 english-small (1- h)))
                    716:                   (format:tyo #\space)
                    717:                   (format:patom '|hundred|)
                    718:                   (and (> n 0) (format:tyo #\space))))
                    719:            (cond ((= n 0))
                    720:                  ((< n 20.)
                    721:                   (format:patom (ar-1 english-small (1- n))))
                    722:                  (t
                    723:                   (format:patom (ar-1 english-medium
                    724:                                                   (- (quotient n 10.) 2)))
                    725:                   (cond ((zerop (setq h (remainder n 10.))))
                    726:                         (t
                    727:                          (format:tyo #/-) ;ascii -
                    728:                          (format:patom (ar-1 english-small (1- h)))))))))
                    729: 
                    730: (defun roman-step (x n)
                    731:     (cond ((> x 9.)
                    732:           (roman-step (quotient x 10.) (1+ n))
                    733:           (setq x (remainder  x 10.))))
                    734:     (cond ((and (= x 9) (not roman-old))
                    735:           (roman-char 0 n)
                    736:           (roman-char 0 (1+ n)))
                    737:          ((= x 5)
                    738:           (roman-char 1 n))
                    739:          ((and (= x 4) (not roman-old))
                    740:           (roman-char 0 n)
                    741:           (roman-char 1 n))
                    742:          (t (cond ((> x 5)
                    743:                    (roman-char 1 n)
                    744:                    (setq x (- x 5))))
                    745:             (do i 0 (1+ i) (>= i x)
                    746:               (roman-char 0 n)))))
                    747: 
                    748: (defun roman-char (i x)
                    749:     (format:tyo (car (nthcdr (+ i x x) '(#/I #/V #/X #/L #/C #/D #/M)))
                    750:                         ;  i   v   x   l   c   d   m
                    751: ))
                    752: 
                    753: ;;; Kludges to make MacLISP like some of the LISPM functions
                    754: 
                    755: 
                    756: (defun format:tyo (char)
                    757:    (cond ((dtpr Format-Standard-Output)
                    758:          (rplacd Format-Standard-Output
                    759:                  (cons char (cdr Format-Standard-Output))))
                    760:         (t (tyo char Format-Standard-Output))))
                    761: 
                    762: (defun format:patom (arg)
                    763:    (format:printorpatom arg nil))
                    764: 
                    765: (defun format:print (arg)
                    766:    (format:printorpatom arg t))
                    767: 
                    768: (defun format:printorpatom (argument slashify)
                    769:    (cond ((dtpr Format-Standard-Output)
                    770:          (rplacd Format-Standard-Output
                    771:                  (nreconc (cond (slashify
                    772:                                         (mapcar '(lambda (x)
                    773:                                                     (getcharn x 1))
                    774:                                                 (explode argument)))
                    775:                                        ((exploden argument)))
                    776:                                  (cdr Format-Standard-Output))))
                    777:         (t (cond (slashify (print argument Format-Standard-Output))
                    778:                  (t (patom argument Format-Standard-Output))))))
                    779: 
                    780: (defun format:terpr nil
                    781:    (cond ((dtpr Format-Standard-Output)
                    782:          (rplacd Format-Standard-Output
                    783:                  (cons #\newline (cdr Format-Standard-Output))))
                    784:         (t (terpr Format-Standard-Output))))
                    785: 
                    786: (defun format:fresh-line nil
                    787:    (cond ((dtpr Format-Standard-Output)
                    788:          (cond ((and (cdr Format-Standard-Output)
                    789:                      (not (= (cadr Format-Standard-Output) #\newline)))
                    790:                 (rplacd Format-Standard-Output
                    791:                         (cons #\newline (cdr Format-Standard-Output))))))
                    792:         (t (and (not (= 0 (nwritn Format-Standard-Output)))
                    793:                 (terpr Format-Standard-Output)))))
                    794:    
                    795:          
                    796: 
                    797: 
                    798: (defun format\:string-search-char (char str start-pos)
                    799:        (declare (fixnum i start-pos str-len))
                    800:        (do ((i start-pos (1+ i))
                    801:            (str-len (flatc str)))
                    802:           ((>& i str-len) nil)
                    803:           (and (=& char (getcharn str (1+ i))) (return i))))
                    804: 
                    805: (defun format\:nsubstring (str from to)
                    806:        (declare (fixnum i from to))
                    807:        (substring str (+ 1 from) (- to from)))  ;substring is 1 based
                    808: 
                    809: (defun ferror (&rest args)
                    810:    (let (str)
                    811:       ; if the first arg to ferror is a string we assume that it is the
                    812:       ; format control string, otherwise we assume that it is a port
                    813:       ; specification, and we ignore it since we want to build a string.
                    814:       (if (stringp (car args))
                    815:         then (setq str (lexpr-funcall 'format nil args))
                    816:         else (setq str (lexpr-funcall 'format nil (cdr args))))
                    817:       (error str)))
                    818: 
                    819: 
                    820: (defun format-test nil
                    821:    (format t "Start test, newline:~%freshline:~&")
                    822:    (format t "decimal:~d, width=5:~5d~%" 10 10)
                    823:    (format t "decimal pad with period:~10,vd~%" #/.  12)
                    824:    (format t "char normal:~c, as # would read:~@c, human read:~:c~%"
                    825:           #\space #\space #\space)
                    826:    (format t "cardinal:~r, roman new:~@r, roman-old:~:@r~
                    827:                <same line I hope>~@
                    828:                new line but at beginning~:
                    829:    same line, but spaced out~:@
                    830:        new line and over two tabs~%" 4 4 4))
                    831: 
                    832: (putprop 'format t 'version)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.