]> git.puffer.fish Git - matthieu/frr.git/commitdiff
doc/developer: improve printfrr extension docs
authorDavid Lamparter <equinox@diac24.net>
Sun, 21 Mar 2021 16:35:43 +0000 (17:35 +0100)
committerDavid Lamparter <equinox@diac24.net>
Tue, 30 Mar 2021 20:34:56 +0000 (22:34 +0200)
The table is getting rather clunky, let's just break this out and make
it pretty there.

Signed-off-by: David Lamparter <equinox@diac24.net>
doc/developer/_static/overrides.css
doc/developer/conf.py
doc/developer/logging.rst

index 1d702bb6e9af44edfdee4e9c7b3c92dac53e3c63..302b8d6bd7d60728aaebc4d2da4432df26358b11 100644 (file)
@@ -214,6 +214,22 @@ pre {
 .highlight .na { color: var(--primary-2); }
 .highlight .nv { color: var(--complement-0); }
 
+.rst-content code.frrfmtout {
+       background-color: var(--secondary-1-9);
+       border-color: var(--secondary-1-1);
+       font-size:100%;
+}
+.rst-content code.frrfmtout::before {
+       content: "⇒ \"";
+}
+.rst-content code.frrfmtout::after {
+       content: "\"";
+}
+.rst-content code.frrfmtout span {
+       color: var(--secondary-1-4);
+       font-size:100%;
+}
+
 strong {
        font-weight:500;
 }
index f4bb65ec79b493fc7f87ac2b59d5d436b5aaa89d..20265f4aadef96ff3dc37c789a82942d57ec653b 100644 (file)
@@ -17,6 +17,8 @@ import os
 import re
 import pygments
 from sphinx.highlighting import lexers
+from sphinx.util import logging
+logger = logging.getLogger(__name__)
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -362,11 +364,37 @@ texinfo_documents = [
 with open("../extra/frrlexer.py", "rb") as lex:
     frrlexerpy = lex.read()
 
+frrfmt_re = re.compile(r'^\s*%(?P<spec>[^\s]+)\s+\((?P<types>.*)\)\s*$')
+
+def parse_frrfmt(env, text, node):
+    from sphinx import addnodes
+
+    m = frrfmt_re.match(text)
+    if not m:
+        logger.warning('could not parse frrfmt:: %r' % (text), location=node)
+        node += addnodes.desc_name(text, text)
+        return text
+
+    spec, types = m.group('spec'), m.group('types')
+
+    node += addnodes.desc_sig_operator('%', '%')
+    node += addnodes.desc_name(spec + ' ', spec + ' ')
+    plist = addnodes.desc_parameterlist()
+    for typ in types.split(','):
+        typ = typ.strip()
+        plist += addnodes.desc_parameter(typ, typ)
+    node += plist
+    return '%' + spec
+
 # custom extensions here
 def setup(app):
     # object type for FRR CLI commands, can be extended to document parent CLI
     # node later on
     app.add_object_type("clicmd", "clicmd")
+
+    # printfrr extensions
+    app.add_object_type("frrfmt", "frrfmt", parse_node=parse_frrfmt)
+
     # css overrides for HTML theme
     app.add_stylesheet("overrides.css")
     # load Pygments lexer for FRR config syntax
index f19554b5321b6c22c7b9ba7bdc245814f3a8b1e1..b827afd6cc5cc9eccae1341b7caa518d128f8e68 100644 (file)
@@ -1,5 +1,7 @@
 .. _logging:
 
+.. highlight:: c
+
 Logging
 =======
 
@@ -52,50 +54,50 @@ are available:
       if (ret != buf)
          XFREE(MTYPE_FOO, ret);
 
-Extensions
-^^^^^^^^^^
-
-``printfrr()`` format strings can be extended with suffixes after `%p` or
-`%d`.  The following extended format specifiers are available:
-
-+-----------+--------------------------+----------------------------------------------+
-| Specifier | Argument                 | Output                                       |
-+===========+==========================+==============================================+
-| ``%Lu``   | ``uint64_t``             | ``12345``                                    |
-+-----------+--------------------------+----------------------------------------------+
-| ``%Ld``   | ``int64_t``              | ``-12345``                                   |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pVA``  | ``struct va_format *``   | (recursive printfrr)                         |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pI4``  | ``struct in_addr *``     | ``1.2.3.4``                                  |
-|           |                          |                                              |
-|           | ``in_addr_t *``          |                                              |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pI6``  | ``struct in6_addr *``    | ``fe80::1234``                               |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pIA``  | ``struct ipaddr *``      | ``1.2.3.4``                                  |
-|           |                          |                                              |
-|           |                          | ``fe80::1234``                               |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pFX``  | ``struct prefix *``      | ``fe80::1234/64``                            |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pSG4`` | ``struct prefix_sg *``   | ``(*,1.2.3.4)``                              |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pRN``  | ``struct route_node *``  | ``192.168.1.0/24`` (dst-only node)           |
-|           |                          |                                              |
-|           |                          | ``2001:db8::/32 from fe80::/64`` (SADR node) |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pNHv`` | ``struct nexthop *``     | ``1.2.3.4, via eth0``                        |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pNHs`` | ``struct nexthop *``     | ``1.2.3.4 if 15``                            |
-+-----------+--------------------------+----------------------------------------------+
-| ``%pFX``  | ``struct bgp_dest *``    | ``fe80::1234/64`` (available in BGP only)    |
-+-----------+--------------------------+----------------------------------------------+
-| ``%.*pHX``| ``int len, void *ptr``   | ``12 34 56 78`` (hexdump)                    |
-+-----------+--------------------------+----------------------------------------------+
+.. c:function:: ssize_t bprintfrr(struct fbuf *fb, const char *fmt, ...)
+.. c:function:: ssize_t vbprintfrr(struct fbuf *fb, const char *fmt, va_list)
+
+   These are the "lowest level" functions, which the other variants listed
+   above use to implement their functionality on top.  Mainly useful for
+   implementing printfrr extensions since those get a ``struct fbuf *`` to
+   write their output to.
+
+.. c:macro:: FMT_NSTD(expr)
+
+   This macro turns off/on format warnings as needed when non-ISO-C
+   compatible printfrr extensions are used (e.g. ``%.*p`` or ``%Ld``.)::
+
+      vty_out(vty, "standard compatible %pI4\n", &addr);
+      FMT_NSTD(vty_out(vty, "non-standard %-47.*pHX\n", (int)len, buf));
+
+   When the frr-format plugin is in use, this macro is a no-op since the
+   frr-format plugin supports all printfrr extensions.  Since the FRR CI
+   includes a system with the plugin enabled, this means format errors will
+   not slip by undetected even with FMT_NSTD.
+
+.. note::
 
+   ``printfrr()`` does not support the ``%n`` format.
+
+AS-Safety
+^^^^^^^^^
+
+``printfrr()`` are AS-Safe under the following conditions:
+
+* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory)
+* floating point specifiers are not AS-Safe (system printf is used for these)
+* the positional ``%1$d`` syntax should not be used (8 arguments are supported
+  while AS-Safe)
+* extensions are only AS-Safe if their printer is AS-Safe
+
+printfrr Extensions
+-------------------
+
+``printfrr()`` format strings can be extended with suffixes after `%p` or `%d`.
 Printf features like field lengths can be used normally with these extensions,
-e.g. ``%-15pI4`` works correctly.
+e.g. ``%-15pI4`` works correctly, **except if the extension consumes the
+width or precision**.  Extensions that do so are listed below as ``%*pXX``
+rather than ``%pXX``.
 
 The extension specifier after ``%p`` or ``%d`` is always an uppercase letter;
 by means of established pattern uppercase letters and numbers form the type
@@ -103,10 +105,7 @@ identifier which may be followed by lowercase flags.
 
 You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended
 printers and what exactly they do.  More printers are likely to be added as
-needed/useful, so the list above may become outdated.
-
-``%Ld`` is not an "extension" for printfrr; it's wired directly into the main
-printf logic.
+needed/useful, so the list here may be outdated.
 
 .. note::
 
@@ -115,16 +114,218 @@ printf logic.
    **not** available when calling ``snprintf`` directly.  You need to call
    ``snprintfrr`` instead.
 
-AS-Safety
-^^^^^^^^^
+Networking data types
+^^^^^^^^^^^^^^^^^^^^^
 
-``printfrr()`` are AS-Safe under the following conditions:
+.. role:: frrfmtout(code)
 
-* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory)
-* floating point specifiers are not AS-Safe (system printf is used for these)
-* the positional ``%1$d`` syntax should not be used (8 arguments are supported
-  while AS-Safe)
-* extensions are only AS-Safe if their printer is AS-Safe
+.. frrfmt:: %pI4 (struct in_addr *, in_addr_t *)
+
+   :frrfmtout:`1.2.3.4`
+
+.. frrfmt:: %pI6 (struct in6_addr *)
+
+   :frrfmtout:`fe80::1234`
+
+.. frrfmt:: %pEA (struct ethaddr *)
+
+   :frrfmtout:`01:23:45:67:89:ab`
+
+.. frrfmt:: %pIA (struct ipaddr *)
+
+   :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234`
+
+.. frrfmt:: %pFX (struct prefix *)
+
+   :frrfmtout:`1.2.3.0/24` / :frrfmtout:`fe80::1234/64`
+
+   This accepts the following types:
+
+   - :c:struct:`prefix`
+   - :c:struct:`prefix_ipv4`
+   - :c:struct:`prefix_ipv6`
+   - :c:struct:`prefix_eth`
+   - :c:struct:`prefix_evpn`
+   - :c:struct:`prefix_fs`
+
+   It does **not** accept the following types:
+
+   - :c:struct:`prefix_ls`
+   - :c:struct:`prefix_rd`
+   - :c:struct:`prefix_ptr`
+   - :c:struct:`prefix_sg` (use :frrfmt:`%pSG4`)
+   - :c:union:`prefixptr` (dereference to get :c:struct:`prefix`)
+   - :c:union:`prefixconstptr` (dereference to get :c:struct:`prefix`)
+
+.. frrfmt:: %pSG4 (struct prefix_sg *)
+
+   :frrfmtout:`(*,1.2.3.4)`
+
+   This is *(S,G)* output for use in pimd.  (Note prefix_sg is not a prefix
+   "subclass" like the other prefix_* structs.)
+
+.. frrfmt:: %pSU (union sockunion *)
+
+   ``%pSU``: :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234`
+
+   ``%pSUs``: :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234%89`
+   (adds IPv6 scope ID as integer)
+
+   ``%pSUp``: :frrfmtout:`1.2.3.4:567` / :frrfmtout:`[fe80::1234]:567`
+   (adds port)
+
+   ``%pSUps``: :frrfmtout:`1.2.3.4:567` / :frrfmtout:`[fe80::1234%89]:567`
+   (adds port and scope ID)
+
+.. frrfmt:: %pRN (struct route_node *, struct bgp_node *, struct agg_node *)
+
+   :frrfmtout:`192.168.1.0/24` (dst-only node)
+
+   :frrfmtout:`2001:db8::/32 from fe80::/64` (SADR node)
+
+.. frrfmt:: %pNH (struct nexthop *)
+
+   ``%pNHvv``: :frrfmtout:`via 1.2.3.4, eth0` — verbose zebra format
+
+   ``%pNHv``: :frrfmtout:`1.2.3.4, via eth0` — slightly less verbose zebra format
+
+   ``%pNHs``: :frrfmtout:`1.2.3.4 if 15` — same as :c:func:`nexthop2str()`
+
+.. frrfmt:: %pBD (struct bgp_dest *)
+
+   :frrfmtout:`fe80::1234/64`
+
+   (only available in bgpd.)
+
+.. frrfmt:: %dPF (int)
+
+   :frrfmtout:`AF_INET`
+
+   Prints an `AF_*` / `PF_*` constant.  ``PF`` is used here to avoid confusion
+   with `AFI` constants, even though the FRR codebase prefers `AF_INET` over
+   `PF_INET` & co.
+
+.. frrfmt:: %dSO (int)
+
+   :frrfmtout:`SOCK_STREAM`
+
+General utility formats
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. frrfmt:: %m (no argument)
+
+   :frrfmtout:`Permission denied`
+
+   Prints ``strerror(errno)``.  Does **not** consume any input argument, don't
+   pass ``errno``!
+
+   (This is a GNU extension not specific to FRR.  FRR guarantees it is
+   available on all systems in printfrr, though BSDs support it in printf too.)
+
+.. frrfmt:: %pSQ (char *)
+
+   ([S]tring [Q]uote.)  Like ``%s``, but produce a quoted string.  Options:
+
+      ``n`` - treat ``NULL`` as empty string instead.
+
+      ``q`` - include ``""`` quotation marks.  Note: ``NULL`` is printed as
+      ``(null)``, not ``"(null)"`` unless ``n`` is used too.  This is
+      intentional.
+
+      ``s`` - use escaping suitable for RFC5424 syslog.  This means ``]`` is
+      escaped too.
+
+   If a length is specified (``%*pSQ`` or ``%.*pSQ``), null bytes in the input
+   string do not end the string and are just printed as ``\x00``.
+
+.. frrfmt:: %pSE (char *)
+
+   ([S]tring [E]scape.)  Like ``%s``, but escape special characters.
+   Options:
+
+      ``n`` - treat ``NULL`` as empty string instead.
+
+   Unlike :frrfmt:`%pSQ`, this escapes many more characters that are fine for
+   a quoted string but not on their own.
+
+   If a length is specified (``%*pSE`` or ``%.*pSE``), null bytes in the input
+   string do not end the string and are just printed as ``\x00``.
+
+.. frrfmt:: %pVA (struct va_format *)
+
+   Recursively invoke printfrr, with arguments passed in through:
+
+   .. c:struct:: va_format
+
+      .. c:member:: const char *fmt
+
+         Format string to use for the recursive printfrr call.
+
+      .. c:member:: va_list *va
+
+         Formatting arguments.  Note this is passed as a pointer, not - as in
+         most other places - a direct struct reference.  Internally uses
+         ``va_copy()`` so repeated calls can be made (e.g. for determining
+         output length.)
+
+.. frrfmt:: %pFB (struct fbuf *)
+
+   Insert text from a ``struct fbuf *``, i.e. the output of a call to
+   :c:func:`bprintfrr()`.
+
+.. frrfmt:: %*pHX (void *, char *, unsigned char *)
+
+   ``%pHX``: :frrfmtout:`12 34 56 78`
+
+   ``%pHXc``: :frrfmtout:`12:34:56:78` (separate with [c]olon)
+
+   ``%pHXn``: :frrfmtout:`12345678` (separate with [n]othing)
+
+   Insert hexdump.  This specifier requires a precision or width to be
+   specified.  A precision (``%.*pHX``) takes precedence, but generates a
+   compiler warning since precisions are undefined for ``%p`` in ISO C.  If
+   no precision is given, the width is used instead (and normal handling of
+   the width is suppressed).
+
+   Note that width and precision are ``int`` arguments, not ``size_t``.  Use
+   like::
+
+     char *buf;
+     size_t len;
+
+     snprintfrr(out, sizeof(out), "... %*pHX ...", (int)len, buf);
+
+     /* with padding to width - would generate a warning due to %.*p */
+     FMT_NSTD(snprintfrr(out, sizeof(out), "... %-47.*pHX ...", (int)len, buf));
+
+.. frrfmt:: %*pHS (void *, char *, unsigned char *)
+
+   ``%pHS``: :frrfmtout:`hex.dump`
+
+   This is a complementary format for :frrfmt:`%*pHX` to print the text
+   representation for a hexdump.  Non-printable characters are replaced with
+   a dot.
+
+Integer formats
+^^^^^^^^^^^^^^^
+
+.. note::
+
+   These formats currently only exist for advanced type checking with the
+   ``frr-format`` GCC plugin.  They should not be used directly since they will
+   cause compiler warnings when used without the plugin.  Use with
+   :c:macro:`FMT_NSTD` if necessary.
+
+   It is possible ISO C23 may introduce another format for these, possibly
+   ``%w64d`` discussed in `JTC 1/SC 22/WG 14/N2680 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2680.pdf>`_.
+
+.. frrfmt:: %Lu (uint64_t)
+
+   :frrfmtout:`12345`
+
+.. frrfmt:: %Ld (int64_t)
+
+   :frrfmtout:`-12345`
 
 Log levels
 ----------