xref: /openbmc/qemu/docs/devel/qapi-domain.rst (revision b6f1244678bebaf7e2c775cfc66d452f95678ebf)
1======================
2The Sphinx QAPI Domain
3======================
4
5An extension to the `rST syntax
6<https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`_
7in Sphinx is provided by the QAPI Domain, located in
8``docs/sphinx/qapi_domain.py``. This extension is analogous to the
9`Python Domain
10<https://www.sphinx-doc.org/en/master/usage/domains/python.html>`_
11included with Sphinx, but provides special directives and roles
12for annotating and documenting QAPI definitions
13specifically.
14
15A `Domain
16<https://www.sphinx-doc.org/en/master/usage/domains/index.html>`_
17provides a set of special rST directives and cross-referencing roles to
18Sphinx for understanding rST markup written to document a specific
19language. By itself, this QAPI extension is only sufficient to parse rST
20markup written by hand; the `autodoc
21<https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html>`_
22functionality is provided elsewhere, in ``docs/sphinx/qapidoc.py``, by
23the "Transmogrifier".
24
25It is not expected that any developer nor documentation writer would
26never need to write *nor* read these special rST forms. However, in the
27event that something needs to be debugged, knowing the syntax of the
28domain is quite handy. This reference may also be useful as a guide for
29understanding the QAPI Domain extension code itself. Although most of
30these forms will not be needed for documentation writing purposes,
31understanding the cross-referencing syntax *will* be helpful when
32writing rST documentation elsewhere, or for enriching the body of
33QAPIDoc blocks themselves.
34
35
36Concepts
37========
38
39The QAPI Domain itself provides no mechanisms for reading the QAPI
40Schema or generating documentation from code that exists. It is merely
41the rST syntax used to describe things. For instance, the Sphinx Python
42domain adds syntax like ``:py:func:`` for describing Python functions in
43documentation, but it's the autodoc module that is responsible for
44reading Python code and generating such syntax. QAPI is analogous here:
45qapidoc.py is responsible for reading the QAPI Schema and generating rST
46syntax, and qapi_domain.py is responsible for translating that special
47syntax and providing APIs for Sphinx internals.
48
49In other words:
50
51qapi_domain.py adds syntax like ``.. qapi:command::`` to Sphinx, and
52qapidoc.py transforms the documentation in ``qapi/*.json`` into rST
53using directives defined by the domain.
54
55Or even shorter:
56
57``:py:`` is to ``:qapi:`` as *autodoc* is to *qapidoc*.
58
59
60Info Field Lists
61================
62
63`Field lists
64<https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#field-lists>`_
65are a standard syntax in reStructuredText. Sphinx `extends that syntax
66<https://www.sphinx-doc.org/en/master/usage/domains/python.html#info-field-lists>`_
67to give certain field list entries special meaning and parsing to, for
68example, add cross-references. The QAPI Domain takes advantage of this
69field list extension to document things like Arguments, Members, Values,
70and so on.
71
72The special parsing and handling of info field lists in Sphinx is provided by
73three main classes; Field, GroupedField, and TypedField. The behavior
74and formatting for each configured field list entry in the domain
75changes depending on which class is used.
76
77Field:
78  * Creates an ungrouped field: i.e., each entry will create its own
79    section and they will not be combined.
80  * May *optionally* support an argument.
81  * May apply cross-reference roles to *either* the argument *or* the
82    content body, both, or neither.
83
84This is used primarily for entries which are not expected to be
85repeated, i.e., items that may only show up at most once. The QAPI
86domain uses this class for "Errors" section.
87
88GroupedField:
89  * Creates a grouped field: i.e. multiple adjacent entries will be
90    merged into one section, and the content will form a bulleted list.
91  * *Must* take an argument.
92  * May optionally apply a cross-reference role to the argument, but not
93    the body.
94  * Can be configured to remove the bulleted list if there is only a
95    single entry.
96  * All items will be generated with the form: "argument -- body"
97
98This is used for entries which are expected to be repeated, but aren't
99expected to have two arguments, i.e. types without names, or names
100without types. The QAPI domain uses this class for features, returns,
101and enum values.
102
103TypedField:
104  * Creates a grouped, typed field. Multiple adjacent entries will be
105    merged into one section, and the content will form a bulleted list.
106  * *Must* take at least one argument, but supports up to two -
107    nominally, a name and a type.
108  * May optionally apply a cross-reference role to the type or the name
109    argument, but not the body.
110  * Can be configured to remove the bulleted list if there is only a
111    single entry.
112  * All items will be generated with the form "name (type) -- body"
113
114This is used for entries that are expected to be repeated and will have
115a name, a type, and a description. The QAPI domain uses this class for
116arguments, alternatives, and members. Wherever type names are referenced
117below, They must be a valid, documented type that will be
118cross-referenced in the HTML output; or one of the built-in JSON types
119(string, number, int, boolean, null, value, q_empty).
120
121
122``:feat:``
123----------
124
125Document a feature attached to a QAPI definition.
126
127:availability: This field list is available in the body of Command,
128               Event, Enum, Object and Alternate directives.
129:syntax: ``:feat name: Lorem ipsum, dolor sit amet...``
130:type: `sphinx.util.docfields.GroupedField
131       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.GroupedField.html?private=1>`_
132
133Example::
134
135   .. qapi:object:: BlockdevOptionsVirtioBlkVhostVdpa
136      :since: 7.2
137      :ifcond: CONFIG_BLKIO
138
139      Driver specific block device options for the virtio-blk-vhost-vdpa
140      backend.
141
142   :memb string path: path to the vhost-vdpa character device.
143   :feat fdset: Member ``path`` supports the special "/dev/fdset/N" path
144       (since 8.1)
145
146
147``:arg:``
148---------
149
150Document an argument to a QAPI command.
151
152:availability: This field list is only available in the body of the
153               Command directive.
154:syntax: ``:arg type name: description``
155:type: `sphinx.util.docfields.TypedField
156       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.TypedField.html?private=1>`_
157
158
159Example::
160
161   .. qapi:command:: job-pause
162      :since: 3.0
163
164      Pause an active job.
165
166      This command returns immediately after marking the active job for
167      pausing.  Pausing an already paused job is an error.
168
169      The job will pause as soon as possible, which means transitioning
170      into the PAUSED state if it was RUNNING, or into STANDBY if it was
171      READY.  The corresponding JOB_STATUS_CHANGE event will be emitted.
172
173      Cancelling a paused job automatically resumes it.
174
175      :arg string id: The job identifier.
176
177
178``:error:``
179-----------
180
181Document the error condition(s) of a QAPI command.
182
183:availability: This field list is only available in the body of the
184               Command directive.
185:syntax: ``:error: Lorem ipsum dolor sit amet ...``
186:type: `sphinx.util.docfields.Field
187       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.Field.html?private=1>`_
188
189The format of the :errors: field list description is free-form rST. The
190alternative spelling ":errors:" is also permitted, but strictly
191analogous.
192
193Example::
194
195   .. qapi:command:: block-job-set-speed
196      :since: 1.1
197
198      Set maximum speed for a background block operation.
199
200      This command can only be issued when there is an active block job.
201
202      Throttling can be disabled by setting the speed to 0.
203
204      :arg string device: The job identifier.  This used to be a device
205          name (hence the name of the parameter), but since QEMU 2.7 it
206          can have other values.
207      :arg int speed: the maximum speed, in bytes per second, or 0 for
208          unlimited.  Defaults to 0.
209      :error:
210          - If no background operation is active on this device,
211            DeviceNotActive
212
213
214``:return:``
215-------------
216
217Document the return type(s) and value(s) of a QAPI command.
218
219:availability: This field list is only available in the body of the
220               Command directive.
221:syntax: ``:return type: Lorem ipsum dolor sit amet ...``
222:type: `sphinx.util.docfields.GroupedField
223       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.GroupedField.html?private=1>`_
224
225
226Example::
227
228   .. qapi:command:: query-replay
229      :since: 5.2
230
231      Retrieve the record/replay information.  It includes current
232      instruction count which may be used for ``replay-break`` and
233      ``replay-seek`` commands.
234
235      :return ReplayInfo: record/replay information.
236
237      .. qmp-example::
238
239          -> { "execute": "query-replay" }
240          <- { "return": {
241                 "mode": "play", "filename": "log.rr", "icount": 220414 }
242             }
243
244
245``:return-nodesc:``
246-------------------
247
248Document the return type of a QAPI command, without an accompanying
249description.
250
251:availability: This field list is only available in the body of the
252               Command directive.
253:syntax: ``:return-nodesc: type``
254:type: `sphinx.util.docfields.Field
255       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.Field.html?private=1>`_
256
257
258Example::
259
260   .. qapi:command:: query-replay
261      :since: 5.2
262
263      Retrieve the record/replay information.  It includes current
264      instruction count which may be used for ``replay-break`` and
265      ``replay-seek`` commands.
266
267      :return-nodesc: ReplayInfo
268
269      .. qmp-example::
270
271          -> { "execute": "query-replay" }
272          <- { "return": {
273                 "mode": "play", "filename": "log.rr", "icount": 220414 }
274             }
275
276``:value:``
277-----------
278
279Document a possible value for a QAPI enum.
280
281:availability: This field list is only available in the body of the Enum
282               directive.
283:syntax: ``:value name: Lorem ipsum, dolor sit amet ...``
284:type: `sphinx.util.docfields.GroupedField
285       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.GroupedField.html?private=1>`_
286
287Example::
288
289   .. qapi:enum:: QapiErrorClass
290      :since: 1.2
291
292      QEMU error classes
293
294      :value GenericError: this is used for errors that don't require a specific
295          error class.  This should be the default case for most errors
296      :value CommandNotFound: the requested command has not been found
297      :value DeviceNotActive: a device has failed to be become active
298      :value DeviceNotFound: the requested device has not been found
299      :value KVMMissingCap: the requested operation can't be fulfilled because a
300          required KVM capability is missing
301
302
303``:alt:``
304------------
305
306Document a possible branch for a QAPI alternate.
307
308:availability: This field list is only available in the body of the
309               Alternate directive.
310:syntax: ``:alt type name: Lorem ipsum, dolor sit amet ...``
311:type: `sphinx.util.docfields.TypedField
312       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.TypedField.html?private=1>`_
313
314As a limitation of Sphinx, we must document the "name" of the branch in
315addition to the type, even though this information is not visible on the
316wire in the QMP protocol format. This limitation *may* be lifted at a
317future date.
318
319Example::
320
321   .. qapi:alternate:: StrOrNull
322      :since: 2.10
323
324      This is a string value or the explicit lack of a string (null
325      pointer in C).  Intended for cases when 'optional absent' already
326      has a different meaning.
327
328       :alt string s: the string value
329       :alt null n: no string value
330
331
332``:memb:``
333----------
334
335Document a member of an Event or Object.
336
337:availability: This field list is available in the body of Event or
338               Object directives.
339:syntax: ``:memb type name: Lorem ipsum, dolor sit amet ...``
340:type: `sphinx.util.docfields.TypedField
341       <https://pydoc.dev/sphinx/latest/sphinx.util.docfields.TypedField.html?private=1>`_
342
343This is fundamentally the same as ``:arg:`` and ``:alt:``, but uses the
344"Members" phrasing for Events and Objects (Structs and Unions).
345
346Example::
347
348   .. qapi:event:: JOB_STATUS_CHANGE
349      :since: 3.0
350
351      Emitted when a job transitions to a different status.
352
353      :memb string id: The job identifier
354      :memb JobStatus status: The new job status
355
356
357Arbitrary field lists
358---------------------
359
360Other field list names, while valid rST syntax, are prohibited inside of
361QAPI directives to help prevent accidental misspellings of info field
362list names. If you want to add a new arbitrary "non-value-added" field
363list to QAPI documentation, you must add the field name to the allow
364list in ``docs/conf.py``
365
366For example::
367
368   qapi_allowed_fields = {
369       "see also",
370   }
371
372Will allow you to add arbitrary field lists in QAPI directives::
373
374   .. qapi:command:: x-fake-command
375
376      :see also: Lorem ipsum, dolor sit amet ...
377
378
379Cross-references
380================
381
382Cross-reference `roles
383<https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html>`_
384in the QAPI domain are modeled closely after the `Python
385cross-referencing syntax
386<https://www.sphinx-doc.org/en/master/usage/domains/python.html#cross-referencing-python-objects>`_.
387
388QAPI definitions can be referenced using the standard `any
389<https://www.sphinx-doc.org/en/master/usage/referencing.html#role-any>`_
390role cross-reference syntax, such as with ```query-blockstats```.  In
391the event that disambiguation is needed, cross-references can also be
392written using a number of explicit cross-reference roles:
393
394* ``:qapi:mod:`block-core``` -- Reference a QAPI module. The link will
395  take you to the beginning of that section in the documentation.
396* ``:qapi:cmd:`query-block``` -- Reference a QAPI command.
397* ``:qapi:event:`JOB_STATUS_CHANGE``` -- Reference a QAPI event.
398* ``:qapi:enum:`QapiErrorClass``` -- Reference a QAPI enum.
399* ``:qapi:obj:`BlockdevOptionsVirtioBlkVhostVdpa`` -- Reference a QAPI
400  object (struct or union)
401* ``:qapi:alt:`StrOrNull``` -- Reference a QAPI alternate.
402* ``:qapi:type:`BlockDirtyInfo``` -- Reference *any* QAPI type; this
403  excludes modules, commands, and events.
404* ``:qapi:any:`block-job-set-speed``` -- Reference absolutely any QAPI entity.
405
406Type arguments in info field lists are converted into references as if
407you had used the ``:qapi:type:`` role. All of the special syntax below
408applies to both info field lists and standalone explicit
409cross-references.
410
411
412Type decorations
413----------------
414
415Type names in references can be surrounded by brackets, like
416``[typename]``, to indicate an array of that type.  The cross-reference
417will apply only to the type name between the brackets. For example;
418``:qapi:type:`[Qcow2BitmapInfoFlags]``` renders to:
419:qapi:type:`[QMP:Qcow2BitmapInfoFlags]`
420
421To indicate an optional argument/member in a field list, the type name
422can be suffixed with ``?``. The cross-reference will be transformed to
423"type, Optional" with the link applying only to the type name. For
424example; ``:qapi:type:`BitmapSyncMode?``` renders to:
425:qapi:type:`QMP:BitmapSyncMode?`
426
427
428Namespaces
429----------
430
431Mimicking the `Python domain target specification syntax
432<https://www.sphinx-doc.org/en/master/usage/domains/python.html#target-specification>`_,
433QAPI allows you to specify the fully qualified path for a data
434type.
435
436* A namespace can be explicitly provided;
437  e.g. ``:qapi:type:`QMP:BitmapSyncMode``
438* A module can be explicitly provided;
439  ``:qapi:type:`QMP:block-core.BitmapSyncMode``` will render to:
440  :qapi:type:`QMP:block-core.BitmapSyncMode`
441* If you don't want to display the "fully qualified" name, it can be
442  prefixed with a tilde; ``:qapi:type:`~QMP:block-core.BitmapSyncMode```
443  will render to: :qapi:type:`~QMP:block-core.BitmapSyncMode`
444
445
446Target resolution
447-----------------
448
449Any cross-reference to a QAPI type, whether using the ```any``` style of
450reference or the more explicit ```:qapi:any:`target``` syntax, allows
451for the presence or absence of either the namespace or module
452information.
453
454When absent, their value will be inferred from context by the presence
455of any ``qapi:namespace`` or ``qapi:module`` directives preceding the
456cross-reference.
457
458If no results are found when using the inferred values, other
459namespaces/modules will be searched as a last resort; but any explicitly
460provided values must always match in order to succeed.
461
462This allows for efficient cross-referencing with a minimum of syntax in
463the large majority of cases, but additional context or namespace markup
464may be required outside of the QAPI reference documents when linking to
465items that share a name across multiple documented QAPI schema.
466
467
468Custom link text
469----------------
470
471The name of a cross-reference link can be explicitly overridden like
472`most stock Sphinx references
473<https://www.sphinx-doc.org/en/master/usage/referencing.html#syntax>`_
474using the ``custom text <target>`` syntax.
475
476For example, ``:qapi:cmd:`Merge dirty bitmaps
477<block-dirty-bitmap-merge>``` will render as: :qapi:cmd:`Merge dirty
478bitmaps <QMP:block-dirty-bitmap-merge>`
479
480
481Directives
482==========
483
484The QAPI domain adds a number of custom directives for documenting
485various QAPI/QMP entities. The syntax is plain rST, and follows this
486general format::
487
488  .. qapi:directive:: argument
489     :option:
490     :another-option: with an argument
491
492     Content body, arbitrary rST is allowed here.
493
494
495Sphinx standard options
496-----------------------
497
498All QAPI directives inherit a number of `standard options
499<https://www.sphinx-doc.org/en/master/usage/domains/index.html#basic-markup>`_
500from Sphinx's ObjectDescription class.
501
502The dashed spellings of the below options were added in Sphinx 7.2, the
503undashed spellings are currently retained as aliases, but will be
504removed in a future version.
505
506* ``:no-index:`` and ``:noindex:`` -- Do not add this item into the
507  Index, and do not make it available for cross-referencing.
508* ``no-index-entry:`` and ``:noindexentry:`` -- Do not add this item
509  into the Index, but allow it to be cross-referenced.
510* ``no-contents-entry`` and ``:nocontentsentry:`` -- Exclude this item
511  from the Table of Contents.
512* ``no-typesetting`` -- Create TOC, Index and cross-referencing
513  entities, but don't actually display the content.
514
515
516QAPI standard options
517---------------------
518
519All QAPI directives -- *except* for namespace and module -- support
520these common options.
521
522* ``:namespace: name`` -- This option allows you to override the
523  namespace association of a given definition.
524* ``:module: modname`` -- Borrowed from the Python domain, this option allows
525  you to override the module association of a given definition.
526* ``:since: x.y`` -- Allows the documenting of "Since" information, which is
527  displayed in the signature bar.
528* ``:ifcond: CONDITION`` -- Allows the documenting of conditional availability
529  information, which is displayed in an eyecatch just below the
530  signature bar.
531* ``:deprecated:`` -- Adds an eyecatch just below the signature bar that
532  advertises that this definition is deprecated and should be avoided.
533* ``:unstable:`` -- Adds an eyecatch just below the signature bar that
534  advertises that this definition is unstable and should not be used in
535  production code.
536
537
538qapi:namespace
539--------------
540
541The ``qapi:namespace`` directive marks the start of a QAPI namespace. It
542does not take a content body, nor any options. All subsequent QAPI
543directives are associated with the most recent namespace. This affects
544the definition's "fully qualified name", allowing two different
545namespaces to create an otherwise identically named definition.
546
547This directive also influences how reference resolution works for any
548references that do not explicitly specify a namespace, so this directive
549can be used to nudge references into preferring targets from within that
550namespace.
551
552Example::
553
554   .. qapi:namespace:: QMP
555
556
557This directive has no visible effect.
558
559
560qapi:module
561-----------
562
563The ``qapi:module`` directive marks the start of a QAPI module. It may have
564a content body, but it can be omitted. All subsequent QAPI directives
565are associated with the most recent module; this effects their "fully
566qualified" name, but has no other effect.
567
568Example::
569
570   .. qapi:module:: block-core
571
572      Welcome to the block-core module!
573
574Will be rendered as:
575
576.. qapi:module:: block-core
577   :noindex:
578
579   Welcome to the block-core module!
580
581
582qapi:command
583------------
584
585This directive documents a QMP command. It may use any of the standard
586Sphinx or QAPI options, and the documentation body may contain
587``:arg:``, ``:feat:``, ``:error:``, or ``:return:`` info field list
588entries.
589
590Example::
591
592  .. qapi:command:: x-fake-command
593     :since: 42.0
594     :unstable:
595
596     This command is fake, so it can't hurt you!
597
598     :arg int foo: Your favorite number.
599     :arg string? bar: Your favorite season.
600     :return [string]: A lovely computer-written poem for you.
601
602
603Will be rendered as:
604
605  .. qapi:command:: x-fake-command
606     :noindex:
607     :since: 42.0
608     :unstable:
609
610     This command is fake, so it can't hurt you!
611
612     :arg int foo: Your favorite number.
613     :arg string? bar: Your favorite season.
614     :return [string]: A lovely computer-written poem for you.
615
616
617qapi:event
618----------
619
620This directive documents a QMP event. It may use any of the standard
621Sphinx or QAPI options, and the documentation body may contain
622``:memb:`` or ``:feat:`` info field list entries.
623
624Example::
625
626  .. qapi:event:: COMPUTER_IS_RUINED
627     :since: 0.1
628     :deprecated:
629
630     This event is emitted when your computer is *extremely* ruined.
631
632     :memb string reason: Diagnostics as to what caused your computer to
633        be ruined.
634     :feat sadness: When present, the diagnostic message will also
635        explain how sad the computer is as a result of your wrongdoings.
636
637Will be rendered as:
638
639.. qapi:event:: COMPUTER_IS_RUINED
640   :noindex:
641   :since: 0.1
642   :deprecated:
643
644   This event is emitted when your computer is *extremely* ruined.
645
646   :memb string reason: Diagnostics as to what caused your computer to
647      be ruined.
648   :feat sadness: When present, the diagnostic message will also explain
649      how sad the computer is as a result of your wrongdoings.
650
651
652qapi:enum
653---------
654
655This directive documents a QAPI enum. It may use any of the standard
656Sphinx or QAPI options, and the documentation body may contain
657``:value:`` or ``:feat:`` info field list entries.
658
659Example::
660
661  .. qapi:enum:: Mood
662     :ifcond: LIB_PERSONALITY
663
664     This enum represents your virtual machine's current mood!
665
666     :value Happy: Your VM is content and well-fed.
667     :value Hungry: Your VM needs food.
668     :value Melancholic: Your VM is experiencing existential angst.
669     :value Petulant: Your VM is throwing a temper tantrum.
670
671Will be rendered as:
672
673.. qapi:enum:: Mood
674   :noindex:
675   :ifcond: LIB_PERSONALITY
676
677   This enum represents your virtual machine's current mood!
678
679   :value Happy: Your VM is content and well-fed.
680   :value Hungry: Your VM needs food.
681   :value Melancholic: Your VM is experiencing existential angst.
682   :value Petulant: Your VM is throwing a temper tantrum.
683
684
685qapi:object
686-----------
687
688This directive documents a QAPI structure or union and represents a QMP
689object. It may use any of the standard Sphinx or QAPI options, and the
690documentation body may contain ``:memb:`` or ``:feat:`` info field list
691entries.
692
693Example::
694
695  .. qapi:object:: BigBlobOfStuff
696
697     This object has a bunch of disparate and unrelated things in it.
698
699     :memb int Birthday: Your birthday, represented in seconds since the
700                         UNIX epoch.
701     :memb [string] Fav-Foods: A list of your favorite foods.
702     :memb boolean? Bizarre-Docs: True if the documentation reference
703        should be strange.
704
705Will be rendered as:
706
707.. qapi:object:: BigBlobOfStuff
708   :noindex:
709
710   This object has a bunch of disparate and unrelated things in it.
711
712   :memb int Birthday: Your birthday, represented in seconds since the
713                       UNIX epoch.
714   :memb [string] Fav-Foods: A list of your favorite foods.
715   :memb boolean? Bizarre-Docs: True if the documentation reference
716      should be strange.
717
718
719qapi:alternate
720--------------
721
722This directive documents a QAPI alternate. It may use any of the
723standard Sphinx or QAPI options, and the documentation body may contain
724``:alt:`` or ``:feat:`` info field list entries.
725
726Example::
727
728  .. qapi:alternate:: ErrorCode
729
730     This alternate represents an Error Code from the VM.
731
732     :alt int ec: An error code, like the type you're used to.
733     :alt string em: An expletive-laced error message, if your
734        computer is feeling particularly cranky and tired of your
735        antics.
736
737Will be rendered as:
738
739.. qapi:alternate:: ErrorCode
740   :noindex:
741
742   This alternate represents an Error Code from the VM.
743
744   :alt int ec: An error code, like the type you're used to.
745   :alt string em: An expletive-laced error message, if your
746      computer is feeling particularly cranky and tired of your
747      antics.
748