;CL-ECMA-48 - Provide for emitting ECMA-48 control functions.
;Copyright (C) 2017,2018,2019 Prince Trippy programmer@verisimilitudes.net .

;This program is free software: you can redistribute it and/or modify it under the terms of the
;GNU Affero General Public License version 3 as published by the Free Software Foundation.

;This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
;even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
;See the GNU Affero General Public License for more details.

;You should have received a copy of the GNU Affero General Public License along with this program.
;If not, see <http://www.gnu.org/licenses/>.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (let ((string (mapcar 'code-char (loop for i from 0 to 127 collecting i))))
    (or (every 'identity string)
        (error "This Common Lisp implementation does not supply characters for all character codes 0 to 127, inclusive.
Due to this, CL-ECMA-48 will not function entirely correctly.
It is possible to continue, but any function using NIL rather than a character will probably signal an error."))
    (and (equal (coerce (subseq string 32 127) 'string)
                " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
         (every 'digit-char-p (subseq string #x30 #x3A))
         (notany 'graphic-char-p (subseq string 0 32))
         (not (graphic-char-p (elt string 127)))
         (pushnew :ascii *features*))))

(cl:defpackage #:cl-ecma-48
  (:documentation "This package exports a macro for defining ECMA-48 control functions and the 162 functions defined by this.")
  (:use #:common-lisp)
  (:shadow #:null #:substitute #:ed #-ascii #:princ)
  (:export #:define-control-function)
  (:nicknames #:ecma-48))

(cl:in-package #:cl-ecma-48)

#-ascii
(defun princ (value &optional (stream *standard-output*) &aux (*standard-output* stream))
  "Print the decimal representation of value to stream using characters #x30 to #x39, if it is an integer.
The normal PRINC behavior takes place, otherwise."
  (if (integerp value)
      (write-string (map 'string (lambda (c)
                                   (code-char (+ #x30 (digit-char-p c))))
                         (princ-to-string value)))
      (cl:princ value))
  value)

(defmacro defc0 (symbol character &optional documentation)
  "Define an emitter for a control function belonging to the C0 set."
  `(defun ,symbol (&optional (stream *standard-output*) &aux (*standard-output* stream))
     ,@(if documentation `(,documentation))
     (declare (ignorable stream))
     (write-char ,(if (characterp character) character (code-char character)))
     (values)))

(defmacro defc1 (symbol character &optional documentation)
  "Define an emitter for a control function belonging to the C1 set."
  `(defun ,symbol (&optional (stream *standard-output*) &aux (*standard-output* stream))
     ,@(if documentation `(,documentation))
     (declare (ignorable stream))
     (write-string ,(format nil "~c~c" (code-char #x1B) (if (characterp character) character (code-char character))))
     (values)))

(defmacro deffs (symbol character &optional documentation)
  "Define an emitter for an independent control function belonging to the Fs set."
  `(defc1 ,symbol ,character ,documentation))

(defmacro defcs (symbol lambda-list characters &optional documentation
                 &aux (&o (position '&optional lambda-list)) (optional (if &o (subseq lambda-list &o)))
                      (&r (position '&rest lambda-list)) (rest (if &r (subseq lambda-list &r)))
                      (required (subseq lambda-list 0 (or &o &r))))
  "Define an emitter for a control function that is a control sequence."
  (if (intersection lambda-list (set-difference lambda-list-keywords '(&optional &rest)))
      (error "Invalid lambda-list keywords have been used.
Only &OPTIONAL and &REST are permitted."))
  (if (notevery (lambda (e) (or (symbolp e)
                                (and (listp e)
                                     (symbolp (car e))
                                     (consp (cdr e))
                                     (cl:null (cddr e)))))
                lambda-list)
      (error "Invalid lambda-list elements have been used.
Only symbols and lists of length two starting with symbols are permitted."))
  `(defun ,symbol ,(append required (cond (&o optional)
                                        (&r (if (symbolp (cadr rest))
                                                `(,(cadr rest) &optional)
                                                `(&optional ,(cadr rest))))
                                        (t '(&optional)))
                         '((stream *standard-output*) &aux (*standard-output* stream) (*print-base* 10) *print-radix*))
     ,@(if documentation `(,documentation))
     (declare (ignorable stream))
     ,@(loop for elt in required collecting `(check-type ,elt (or unsigned-byte character)))
     ,@(cond (&o (loop for elt in (cdr optional) collecting `(check-type ,(if (listp elt) (car elt) elt) (or cl:null unsigned-byte character))))
             (&r `((check-type ,(if (listp (cadr rest)) (caadr rest) (cadr rest)) (or list unsigned-byte character)))))
     (control-sequence-introducer)
     ,@(nconc (mapcon (lambda (n)
                        `((princ ,(car n))
                          ,@(if (cdr n)
                                (list `(write-char ,(code-char #x3B))))))
                      required)
              (cond (&o (append (if (not required)
                                    (let ((a (if (symbolp (cadr optional))
                                                 (cadr optional)
                                                 (caadr optional))))
                                      `((or (cl:null ,a)
                                            ,@(if (not (symbolp (cadr optional)))
                                                  `((eql ,(caadr optional) ,(cadadr optional))))
                                            (princ ,a)))))
                                (cond ((butlast (cddr optional))
                                       (let ((g (mapcar (lambda (n &aux (s (if (symbolp n) n (car n)))
                                                                        (v (if (not (symbolp n)) (cadr n))))
                                                          (list s v (gensym (symbol-name s))))
                                                        (if required
                                                            (cdr optional)
                                                            (cddr optional)))))
                                         `((let ,(mapcar (lambda (s)
                                                           `(,(caddr s)
                                                              (or (cl:null ,(car s))
                                                                  ,@(if (cadr s) `((eql ,(car s) ,(cadr s)))))))
                                                         g)
                                             (declare (type boolean ,@(mapcar 'caddr g))
                                                      (dynamic-extent ,@(mapcar 'caddr g)))
                                             ,@(mapcon (lambda (n)
                                                         (if (cdr n)
                                                             `((or (and ,@(mapcar 'caddr n))
                                                                   (write-char ,(code-char #x3B)))
                                                               (or ,(caddar n) (princ ,(caar n))))
                                                             `((or ,(caddar n)
                                                                   (progn (write-char ,(code-char #x3B))
                                                                          (princ ,(caar n)))))))
                                                       g)))))
                                      ((or required (cddr optional))
                                       (let* ((l (car (last optional)))
                                              (a (if (symbolp l) l (car l))))
                                         `((or (cl:null ,a)
                                               ,@(if (not (symbolp l))
                                                     `((eql ,(car l) ,(cadr l))))
                                               (progn (write-char ,(code-char #x3B))
                                                      (princ ,a)))))))))
                    (&r `(,@(if required `((if ,(if (symbolp (cadr rest))
                                                    (cadr rest)
                                                    (caadr rest))
                                               (write-char ,(code-char #x3B)))))
                            (if (listp ,(if (symbolp (cadr rest))
                                            (cadr rest)
                                            (caadr rest)))
                                (mapl (lambda (list &aux (elt (car list)))
                                        (or (cl:null elt)
                                            ,@(if (not (symbolp (cadr rest)))
                                                  `((eql elt ,(cadadr rest))))
                                            (princ elt))
                                        (if (cdr list) (write-char ,(code-char #x3B))))
                                      ,(if (symbolp (cadr rest))
                                           (cadr rest)
                                           (caadr rest)))
                                (or (cl:null ,(if (symbolp (cadr rest))
                                                  (cadr rest)
                                                  (caadr rest)))
                                    ,@(if (not (symbolp (cadr rest)))
                                          `((eql ,(if (symbolp (cadr rest))
                                                      (cadr rest)
                                                      (caadr rest))
                                                 ,(cadadr rest))))
                                    (princ ,(if (symbolp (cadr rest))
                                                (cadr rest)
                                                (caadr rest)))))))))
     ,(typecase characters
        (character `(write-char ,characters))
        (integer `(write-char ,(code-char characters)))
        (t `(write-string ,(make-array (length characters) :element-type 'character
                                       :initial-contents (mapcar (lambda (n)
                                                                   (if (characterp n) n (code-char n)))
                                                                 characters)))))
     (values)))

(defmacro define-control-function (identity type sequence &optional documentation)
  "Define an ECMA-48 control function emitting function.
The identity is a symbol or a list of two symbols, acronym and proper name.
The type is one of :C0, :C1, :Fs, or a modified lambda list containing required, &OPTIONAL, and &REST arguments.
All non-required arguments may be given a default value according to normal &OPTIONAL convention.
The sequence is the value designating the control function.
The optional documentation is a documentation string for this function."
  (check-type identity (or list symbol))
  (check-type type (or list (member :C0 :C1 :Fs)))
  (check-type sequence (or character sequence unsigned-byte))
  (check-type documentation (or cl:null string))
  (let ((acronym (if (symbolp identity) identity (first identity)))
        (name (if (symbolp identity) identity (second identity))))
    `(eval-when (:compile-toplevel :load-toplevel :execute)
       (,(getf '(:C0 defc0 :C1 defc1 :Fs deffs) type 'defcs)
         ,name ,@(if (listp type) `(,type)) ,sequence ,documentation)
       (setf (symbol-function ',acronym) (symbol-function ',name)
             (documentation ',acronym 'function) ,documentation
             (get ',acronym :expansion) ',name
             (get ',name :acronym) ',acronym))))

#.`(progn
     ,@(mapcan
        (lambda (d)
          `((define-control-function ,@d)
            (export ',(car d))))
        '(((ack acknowledge) :C0 #x06 "Acknowledge with affirmative response.  See ISO 1745.")
          ((apc application-program-command) :C1 #x5F "Begins a command string delimited by STRING TERMINATOR (ST).")
          ((bel bell) :C0 #x07 "Bell typically sounds or flashes the device for attention.")
          ((bph break-permitted-here) :C1 #x42 "Indicates a line break is permitted between two graphical characters.")
          ((bs backspace) :C0 #x08 "Backspace, moving in the reverse of the normal data movement.")
          ((can cancel) :C0 #x18 "Cancel previous data by signalling according to an external protocol.")
          ((csi control-sequence-introducer) :C1 #x5B "Introduce a control sequence.") ;Violate the order for niceties.
          ((cbt cursor-backward-tabulation) (&optional (count 1)) #x5A "Move the cursor to the nth preceding character tabstop.")
          ((cch cancel-character) :C1 #x54 "Ignore the preceding graphical character and this control function.")
          ((cha cursor-character-absolute) (&optional (count 1)) #x47 "Move the cursor to the nth position of the current line.")
          ((cht cursor-forward-tabulation) (&optional (count 1)) #x49 "Move the cursor to the nth following character tabstop.")
          ((cmd coding-method-delimiter) :Fs #x64 "Delimit a string of data and switch control.  See ECMA-35.")
          ((cnl cursor-next-line) (&optional (count 1)) #x45 "Move the cursor to the first position of the nth following line.")
          ((cpl cursor-preceding-line) (&optional (count 1)) #x46 "Move the cursor to the first position of the nth preceding line.")
          ((cpr active-position-report) (&optional (line 1) (character 1)) #x52 "Reports current line and character position.")
          ((cr carriage-return) :C0 #x0D "Carriage return moves to the home or limit position of a line.")
          ((ctc cursor-tabulation-control) (&rest (tabstops 0)) #x57 "Modify tabstops according to parameters.  See ECMA-48.")
          ((cub cursor-left) (&optional (count 1)) #x44 "Move the cursor left count times.")
          ((cud cursor-down) (&optional (count 1)) #x42 "Move the cursor down count times.")
          ((cuf cursor-right) (&optional (count 1)) #x43 "Move the cursor right count times.")
          ((cup cursor-position) (&optional (line 1) (character 1)) #x48 "Reposition the cursor at line and character.")
          ((cuu cursor-up) (&optional (count 1)) #x41 "Move the cursor up count times.")
          ((cvt cursor-line-tabulation) (&optional (count 1)) #x59 "Move the cursor to the nth following line tabstop.")
          ((da device-attributes) (&optional (n 0)) #x63 "If n is zero, request a DA; if not, self identify with n.")
          ((daq define-area-qualification) (&rest (n 0)) #x6F "Define a qualified area according to parameters.  See ECMA-48.")
          ((dch delete-character) (&optional (count 1)) #x50 "Remove the current and count-1 following or preceding characters.")
          ((dcs device-control-string) :C1 #x50 "Begin a control string delimited by STRING TERMINATOR (ST).")
          ((dc1 device-control-one) :C0 #x11 "Device control one is used for primary device control.  See ECMA-48.")
          ((dc2 device-control-two) :C0 #x12 "Device control two is used for secondary device control.  See ECMA-48.")
          ((dc3 device-control-three) :C0 #x13 "Device control three is used for tertiary device control.  See ECMA-48.")
          ((dc4 device-control-four) :C0 #x14 "Device control four is used for miscellaneous device control.  See ECMA-48.")
          ((dl delete-line) (&optional (count 1)) #x4D "Remove the current and count-1 following or preceding lines.")
          ((dle data-link-escape) :C0 #x10 "Data link escape provides transmission control functions.  See ISO 1745.")
          ((dmi disable-manual-input) :Fs #x60 "Disable the manual input facilities of a device.")
          ((dsr device-status-report) (&optional (n 0)) #x6E "Report or request a status.  A 6 requests a CPR.  See ECMA-48.")
          ((dta dimension-text-area) (m n) (#x20 #x54) "Establish rectangular text dimensions.")
          ((ea erase-in-area) (&optional (n 0)) #x4F "Erase a section of the current qualified area.  See ECMA-48.")
          ((ech erase-character) (&optional (count 1)) #x58 "Erase the current and count-1 following characters.")
          ((ed erase-in-page) (&optional (n 0)) #x4A "Erase a section of the current page.  See ECMA-48.")
          ((ef erase-in-field) (&optional (n 0)) #x4E "Erase a section of the current field.  See ECMA-48.")
          ((el erase-in-line) (&optional (n 0)) #x4B "Erase a section of the current line.  See ECMA-48.")
          ((em end-of-medium) :C0 #x19 "End of medium is used to identify the end of a medium for access.")
          ((emi enable-manual-input) :Fs #x62 "Enable the manual input facilities of a device.")
          ((enq enquiry) :C0 #x05 "Enquiry is used as a request for response.  See ISO 1745.")
          ((eot end-of-transmission) :C0 #x04 "End of transmission indicates a finishing.  See ISO 1745.")
          ((epa end-of-guarded-area) :C1 #x57 "Indicate the end of a guarded area.  See ECMA-48.")
          ((esa end-of-selected-area) :C1 #x47 "Indicate the end of a selected area.  See ECMA-48.")
          ((esc escape) :C0 #x1B "Escape causes following characters to be interpreted differently.  See ECMA-35.")
          ((etb end-of-transmission-block) :C0 #x17 "End of transmission block indicates the end of a block.  See ISO 1745.")
          ((etx end-of-text) :C0 #x03 "End of text indicates the end of a text.  See ISO 1745.")
          ((ff form-feed) :C0 #x12 "Form feed breaks a form or page.")
          ((fnk function-key) (n) (#x20 #x57) "Indicate the nth function key.")
          ((fnt font-selection) (&optional (font 0) (register 0)) (#x20 #x44) "Select a font.  See ECMA-48.")
          ((gcc graphic-character-combination) (&optional (n 0)) (#x20 #x5F) "Combine graphic characters.  See ECMA-48.")
          ((gsm graphic-size-modification) (&optional (height 100) (width 100)) (#x20 #x42) "Modify font size.  See ECMA-48.")
          ((gss graphic-size-selection) (height) (#x20 #x43) "Establish font height, and implicitly width, in terms of SSU.")
          ((hpa character-position-absolute) (&optional (count 1)) #x60 "Move to the nth position of the current line.  See CHA.")
          ((hpb character-position-backward) (&optional (count 1)) #x6A "Move opposite of the normal data movement count times.  See BS.")
          ((hpr character-position-forward) (&optional (count 1)) #x61 "Move with the normal data movement count times.")
          ((ht character-tabulation) :C0 #x09 "Character tabulation moves to the following character tabstop.")
          ((htj character-tabulation-with-justification) :C1 #x49 "Justify with regards to the following tabstop.  See ECMA-48.")
          ((hts character-tabulation-set) :C1 #x48 "Set a tabstop.")
          ((hvp character-and-line-position) (&optional (line 1) (character 1)) #x66 "Move to line and character.  See CUP.")
          ((ich insert-character) (&optional (count 1)) #x40 "Insert room at the current and count-1 following or preceding characters.")
          ((idcs identify-device-control-string) (string) (#x20 #x4F) "Define the structure of a control string.  See ECMA-35.  See ECMA-48.")
          ((igs identify-graphic-subrepertoire) (repertoire) (#x20 #x4D) "See ISO 7350.  See ISO 10367.  See ECMA-48.")
          ((il insert-line) (&optional (count 1)) #x4C "Insert a line at the current and count-1 following or preceding lines.")
          ((int interrupt) :Fs #x61 "Alert a device to stop and begin an already agreed upon execution.")
          ((is1 information-separator-one) :C0 #x1F "Information separator one can be used to organize units.  See ECMA-48.")
          ((is2 information-separator-two) :C0 #x1E "Information separator two can be used to organize records.  See ECMA-48.")
          ((is3 information-separator-three) :C0 #x1D "Information separator three can be used to organize groups.  See ECMA-48.")
          ((is4 information-separator-four) :C0 #x1C "Information separator four can be used to organize files.  See ECMA-48.")
          ((jfy justify) (&rest (n 0)) (#x20 #x46) "Delimit and define justification of a string.  See ECMA-48.")
          ((lf line-feed) :C0 #x0A "Line feed moves to the corresponding position of the following line.")
          ((ls0 locking-shift-zero) :C0 #x0F "Locking shift zero is an extension control function.  See ECMA-35.")
          ((ls1 locking-shift-one) :C0 #x0E "Locking shift one is an extension control function.  See ECMA-35.")
          ((ls1r locking-shift-one-right) :Fs #x7E "See ECMA-35.")
          ((ls2 locking-shift-two) :Fs #x6E "See ECMA-35.")
          ((ls2r locking-shift-two-right) :Fs #x7D "See ECMA-35.")
          ((ls3 locking-shift-three) :Fs #x6F "See ECMA-35.")
          ((ls3r locking-shift-three-right) :Fs #x7C  "See ECMA-35.")
          ((mc media-copy) (&optional (n 0)) #x69 "Begin or end transfer of data to an auxiliary device.  See ECMA-48.")
          ((mw message-waiting) :C1 #x55 "Indicate that a message is waiting.  A response can be sent with DSR.")
          ((nak negative-acknowledge) :C0 #x15  "Negative acknowledge gives a negative response.  See ISO 1745.")
          ((nbh no-break-here) :C1 #x43 "Indicates a line break is not permitted between two graphical characters.")
          ((nel next-line) :C1 #x45 "Move to the home or limit position of the following line.  See CNL.")
          ((np next-page) (&optional (count 1)) #x55 "Display the nth following page.")
          ((nul null) :C0 #x00 "Null is a control function that may be added or removed with little or no consequence.")
          ((osc operating-system-command) :C1 #x5D "Begins an operating system command string delimited by STRING TERMINATOR (ST).")
          ((pec presentation-expand-or-contract) (&optional (spacing 0)) (#x20 #x5A) "Controls spacing and size of text.  See ECMA-48.")
          ((pfs page-format-selection) (&optional (n 0)) (#x20 #x4A) "Specify page size and format based on paper.  See ECMA-48.")
          ((pld partial-line-forward) :C1 #x4B "Move forward by a partial line to allow for subscripts or return to the active line.")
          ((plu partial-line-backward) :C1 #x4C "Move backwards by a partial line to allow for superscripts or return to the active line.")
          ((pm privacy-message) :C1 #x5E "Begins a privacy message command string delimited by STRING TERMINATOR (ST).")
          ((pp preceding-page) (&optional (count 1)) #x56 "Display the nth preceding page.")
          ((ppa page-position-absolute) (&optional (count 1)) (#x20 #x50) "Move to the corresponding position of the nth page.")
          ((ppb page-position-backward) (&optional (count 1)) (#x20 #x52) "Move to the corresponding position of the nth preceding page.")
          ((ppr page-position-forward) (&optional (count 1)) (#x20 #x51) "Move to the corresponding position of the nth following page.")
          ((ptx parallel-texts) (&optional (n 0)) #x5C "Delimit a string of characters to be displayed in parallel.  See ECMA-48.")
          ((pu1 private-use-one) :C1 #x51 "This control function has no standard behavior and is intended for private use as wanted.")
          ((pu2 private-use-two) :C1 #x52 "This control function has no standard behavior and is intended for private use as wanted.")
          (quad (&rest (n 0)) (#x20 #x48) "Position string with layout as specified.  See ECMA-48.")
          ((rep repeat) (&optional (count 1)) #x62 "Repeat the preceding graphic character count times.")
          ((ri reverse-line-feed) :C1 #x4D "Moves to the corresponding position of the preceding line.")
          ((ris reset-to-initial-state) :Fs #x63 "Reset the device to its initial state.")
          ((rm reset-mode) (&rest n) #x6C "Reset the device as specified.  See ECMA-48.")
          ((sacs set-additional-character-separation) (&optional (n 0)) (#x20 #x5C) "Works in terms of SSU.  See ECMA-48.")
          ((sapv select-alternative-presentation-variants) (&rest (n 0)) (#x20 #x5D) "See ECMA-48.")
          ((sci single-character-introducer) :C1 #x5A "The following character is consumed.  See ECMA-48.")
          ((sco select-character-orientation) (&optional (rotation 0)) (#x20 #x65) "Establish character rotation.  See ECMA-48.")
          ((scp select-character-path) (path effect) (#x20 #x6B) "Establish character direction.  See ECMA-48.")
          ((scs set-character-spacing) (spacing) (#x20 #x67) "Establish character spacing in terms of SSU.  See ECMA-48.")
          ((sd scroll-down) (&optional (count 1)) #x54 "Move the contents of the device down count positions, appearing to scroll down.")
          ((sds start-directed-string) (&optional (n 0)) #x5D "Delimit a string of characters of a certain direction.  See ECMA-48.")
          ((see select-editing-extent) (&optional (extent 0)) #x51 "Establish editing bounds.  See ECMA-48.")
          ((sef sheet-eject-and-feed) (&optional (m 0) (n 0)) (#x20 #x59) "Control paper feeding and ejecting during printing.  See ECMA-48.")
          ((sgr select-graphic-rendition) (&rest (n 0)) #x6D "Establish character font and other characteristics.  See ECMA-48.")
          ((shs select-character-spacing) (&optional (spacing 0)) (#x20 #x4B) "Establish character spacing.  See ECMA-48.")
          ((si shift-in) :C0 #x0F "Shift in causes following characters to be interpreted differently.  See ECMA-35.")
          ((simd select-implicit-movement-direction) (&optional (direction 0)) #x5E "Establish the implicit movement direction.  See ECMA-48.")
          ((sl scroll-left) (&optional (count 1)) (#x20 #x40) "Move the contents of the device left count positions, appearing to scroll left.")
          ((slh set-line-home) (n) (#x20 #x55) "Establish position n of the active line as the line home.")
          ((sll set-line-limit) (n) (#x20 #x56) "Establish position n of the active line as the line limit.")
          ((sls set-line-spacing) (n) (#x20 #x68) "Establish line spacing in terms of SSU.")
          ((sm set-mode) (&rest n) #x68 "Set the device as specified.  See ECMA-48.")
          ((so shift-out) :C0 #x0E "Shift out causes following characters to be interpreted differently.  See ECMA-35.")
          ((soh start-of-heading) :C0 #x01 "Start of heading begins a heading.  See ISO 1745.")
          ((sos start-of-string) :C1 #x58 "Begins a string delimited by STRING TERMINATOR (ST).")
          ((spa start-of-guarded-area) :C1 #x56 "Indicate the beginning of a guarded area.")
          ((spd select-presentation-directions) (&optional (character 0) (effect 0)) (#x20 #x53) "See ECMA-48.")
          ((sph set-page-home) (n) (#x20 #x69) "Establish line position n of the active page as the page home.")
          ((spi spacing-increment) (line character) (#x20 #x47) "Establish line and character spacing in terms of SSU.")
          ((spl set-page-limit) (n) (#x20 #x6A) "Establish line position n of the active page as the page limit.")
          ((spqr select-print-quality-and-rapidity) (&optional (n 0)) (#x20 #x58) "Control speed and quality during printing.  See ECMA-48.")
          ((sr scroll-right) (&optional (count 1)) (#x20 #x41) "Move the contents of the device right count positions, appearing to scroll right.")
          ((srcs set-reduced-character-separation) (&optional (n 0)) (#x20 #x66) "Establish character separation in terms of SSU.")
          ((srs start-reversed-string) (&optional (n 0)) #x5B "Delimit a string of characters of a certain reversed direction.  See ECMA-48.")
          ((ssa start-of-selected-area) :C1 #x46 "Indicate the beginning of a selected area.")
          ((ssu select-size-unit) (&optional (unit 0)) (#x20 #x49) "Define the size unit from several visual units.  See ECMA-48.")
          ((ssw set-space-width) (n) (#x20 #x5B) "Establish space width in terms of SSU.")
          ((ss2 single-shift-two) :C1 #x4E "Cause following characters to be interpreted differently.  See ECMA-35.")
          ((ss3 single-shift-three) :C1 #x4F "Cause following characters to be interpreted differently.  See ECMA-35.")
          ((st string-terminator) :C1 #x5C "Terminate a string.")
          ((stab selective-tabulation) (n) (#x20 #x5E) "Align according to tabulation specified.  See ISO 8613-6.")
          ((sts set-transmit-state) :C1 #x53 "Establish that data transmission is possible.  See ECMA-48.")
          ((stx start-of-text) :C0 #x02 "Start of text begins a text and ends a heading.  See ISO 1745.")
          ((su scroll-up) (&optional (count 1)) #x53 "Move the contents of the device up count positions, appearing to scroll up.")
          ((sub substitute) :C0 #x1A "Substitute for a character that is not to actually be sent.")
          ((svs select-line-spacing) (&optional (spacing 0)) (#x20 #x4C) "Establish line spacing from several visual units.  See ECMA-48.")
          ((syn synchronous-idle) :C0 #x16 "Synchronous idle is used for synchronizing purposes.  See ISO 1745.")
          ((tac tabulation-aligned-centred) (n) (#x20 #x62) "Establish a tabstop used for center alignment.  See ECMA-48.")
          ((tale tabulation-aligned-leading-edge) (n) (#x20 #x61) "Establish a tabstop used for end alignment.  See ECMA-48.")
          ((tate tabulation-aligned-trailing-edge) (n) (#x20 #x60) "Establish a tabstop used for front alignment.  See ECMA-48.")
          ((tbc tabulation-clear) (&optional (n 0)) #x67 "Clear tabstops as specified.  See ECMA-48.")
          ((tcc tabulation-centered-on-character) (line &optional (character 32)) (#x20 #x63) ;oh my
           "Establish a tabstop used for center alignment of the first occurence of character or for front alignment.  See ECMA-48.")
          ((tsr tabulation-stop-remove) (n) (#x20 #x64) "Remove a tabstop at position n of the active line and following lines.")
          ((tss thin-space-specification) (n) (#x20 #x45) "Establish thin space width in terms of SSU.")
          ((vpa line-position-absolute) (&optional (count 1)) #x64 "Move to the nth line position.")
          ((vpb line-position-backward) (&optional (count 1)) #x6B "Move opposite of the normal line progression count times.")
          ((vpr line-position-forward) (&optional (count 1))  #x65 "Move with the normal line progression count times.")
          ((vt line-tabulation) :C0 #x0B "Line tabulation moves to the following tabstop.")
          ((vts line-tabulation-set) :C1 #x4A "Set a line tabstop at the active line."))))