Re: git: 0d4642a67e59 - main - Add --libxo support for geom status and list sub commands.
Date: Tue, 14 Oct 2025 21:32:06 UTC
On 13 Oct 2025, at 17:16, Kirk McKusick wrote:
> static void
> -list_one_provider(struct gprovider *pp, const char *prefix)
> +list_one_provider(struct gprovider *pp, const char *padding)
> {
> struct gconfig *conf;
> char buf[5];
>
> - printf("Name: %s\n", pp->lg_name);
> + xo_emit("{Lcw:Name}{:Name}\n", pp->lg_name);
> humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
> HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
Tag names should be lower case, for consistency and ease of use: s/:Name/:name/
(This applies throughout)
> - printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
> - buf);
> - printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
> + xo_emit("{P:/%s}{Lcw:Mediasize}{:Mediasize/%jd} ({N:/%s})\n",
> + padding, (intmax_t)pp->lg_mediasize, buf);
> + xo_emit("{P:/%s}{Lcw:Sectorsize}{:Sectorsize/%u} \n",
> + padding, pp->lg_sectorsize);
Using the "humanize" features will allow human-readable numbers in human-friendly styles (text, html) and real numbers in encoding styles (xml and json).
https://libxo.readthedocs.io/en/latest/field-modifiers.html?highlight=humanize#field-modifiers-1
Modifiers
~~~~~~~~~~~~~~~
Field modifiers are flags which modify the way content emitted for
particular output styles:
=== =============== ===================================================
M Name Description
=== =============== ===================================================
...
h humanize (hn) Format large numbers in human-readable style
\ hn-space Humanize: Place space between numeric and unit
\ hn-decimal Humanize: Add a decimal digit, if number < 10
\ hn-1000 Humanize: Use 1000 as divisor instead of 1024
Hmm.... looks like I'm lacking a flag for HN_B, sadly. Not sure how I missed that. I'll open a PR to add it.
If you want the full numbers, you can use the "e" flag to restrict emitting values to encodings styles.
> if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
> - printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
> - printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
> + xo_emit("{P:/%s}{Lcw:Stripesize}{Stripesize/%ju}\n",
> + padding, pp->lg_stripesize);
> + xo_emit("{P:/%s}{Lcw:Stripeoffset}{Stripeoffset/%ju}\n",
> + padding, pp->lg_stripeoffset);
Missing some colons before the tag names. Testing with the "warn" flag should point this out, if the code gets hit.
Style-wise, using hyphens ("{:strip-size/%ju}", "{:strip-offset/%ju}") helps make readable tag names.
> - printf("%sMode: %s\n", prefix, pp->lg_mode);
> + xo_emit("{P:/%s}{Lcw:Mode}{Mode}\n", padding, pp->lg_mode);
{:mode}
> @@ -940,27 +958,36 @@ list_one_geom(struct ggeom *gp)
> struct gconfig *conf;
> unsigned n;
>
> - printf("Geom name: %s\n", gp->lg_name);
> + xo_emit("{Lcw:Geom name}{:Name}\n", gp->lg_name);
> LIST_FOREACH(conf, &gp->lg_config, lg_config) {
> - printf("%s: %s\n", conf->lg_name, conf->lg_val);
> + xo_emit("{Lcwa:}{a:}\n", conf->lg_name, conf->lg_name,
> + conf->lg_val);
> }
> if (!LIST_EMPTY(&gp->lg_provider)) {
> - printf("Providers:\n");
> + xo_open_list("Providers");
Style-wise, list and container names should also be lower case.
> + xo_emit("{Tc:Providers}\n");
> n = 1;
> LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
> - printf("%u. ", n++);
> + xo_emit("{T:/%u} ", n++);
> + xo_open_instance("provider");
> list_one_provider(pp, " ");
> + xo_close_instance("provider");
> }
> + xo_close_list("Providers");
> }
> if (!LIST_EMPTY(&gp->lg_consumer)) {
> - printf("Consumers:\n");
> + xo_open_list("Consumers");
> + xo_emit("{Tc:Consumers}\n");
> n = 1;
> LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
> - printf("%u. ", n++);
> + xo_emit("{T:/%u} ", n++);
> + xo_open_instance("consumer");
> list_one_consumer(cp, " ");
> + xo_close_instance("consumer");
> }
> + xo_close_list("Consumers");
> }
For simple lists like this, use the "l" flag here, since it will yield more useable JSON (dropping the open/close list/instance):
https://libxo.readthedocs.io/en/latest/field-modifiers.html?highlight=leaf#the-leaf-list-modifier-l
The Leaf-List Modifier ({l:})
+++++++++++++++++++++++++++++
.. index:: Field Modifiers; Leaf-List
The leaf-list modifier is used to distinguish lists where each
instance consists of only a single value. In XML, these are
rendered as single elements, where JSON renders them as arrays::
EXAMPLE:
for (i = 0; i < num_users; i++) {
xo_emit("Member {l:user}\\n", user[i].u_name);
}
XML:
<user>phil</user>
<user>pallavi</user>
JSON:
"user": [ "phil", "pallavi" ]
The name of the field must match the name of the leaf list.
FWIW, there's some example code showing the differences between list and leaf-list rendering in XML and JSON appended below.
> @@ -1038,14 +1067,20 @@ std_list(struct gctl_req *req, unsigned flags __unused)
> "an instance named '%s'.",
> gclass_name, name);
> }
> + xo_open_container("Geom");
> list_one_geom(gp);
> + xo_close_container("Geom");
> }
> } else {
> + xo_open_list("Geoms");
> LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
> if (LIST_EMPTY(&gp->lg_provider) && !all)
> continue;
> + xo_open_instance("geom");
> list_one_geom(gp);
> + xo_close_instance("geom");
> }
> + xo_close_list("Geoms");
Same here ("l:").
> + xo_open_instance("status");
> LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
> - component = status_one_consumer(cp);
> - if (component == NULL)
> + cstate = status_one_consumer(cp, "state");
> + csyncr = status_one_consumer(cp, "synchronized");
> + if (cstate == NULL && csyncr == NULL)
> continue;
> + if (!gotone || script) {
> + if (!gotone) {
> + xo_emit("{:name/%*s} {:status/%*s} ",
> + name_len, name, status_len, status);
> + } else {
> + xo_emit("{d:name/%*s} {d:status/%*s} ",
> + name_len, name, status_len, status);
> + }
> + xo_open_list("components");
> + }
> +
> + xo_open_instance("components");
> + if (cstate != NULL && csyncr != NULL) {
> + xo_emit("{P:/%*s}{:compontent} ({:state}, {:synchronized})\n",
> + len, "", cp->lg_provider->lg_name, cstate, csyncr);
> + } else if (cstate != NULL) {
> + xo_emit("{P:/%*s}{:compontent} ({:state})\n",
> + len, "", cp->lg_provider->lg_name, cstate);
> + } else {
> + xo_emit("{P:/%*s}{:compontent} ({:synchronized})\n",
> + len, "", cp->lg_provider->lg_name, csyncr);
> + }
Extra "n"s in the tag names: s/compontent/component/
> + xo_close_instance("components");
Do these instances have keys? Or is this a simple list (leaf-list)?
> gotone = 1;
> - printf("%*s %*s %s\n", name_len, name, status_len, status,
> - component);
> - if (!script)
> - name = status = "";
> + if (!len && !script)
> + len = name_len + status_len + 4;
> }
> if (!gotone) {
> - printf("%*s %*s %s\n", name_len, name, status_len, status,
> - "N/A");
> + xo_emit("{:name/%*s} {:status/%*s} ", name_len, name, status_len, status);
> + xo_open_list("components");
> + xo_open_instance("components");
> + xo_emit("{P:/%*s}{d:compontent}\n", len, "", "N/A");
> + xo_close_instance("components");
> }
> + xo_close_list("components");
> + xo_close_instance("status");
Avoid the close_list call if you didn't call open_list. libxo's internal state transition FSM will generally catch this (and warn you), but it's better to avoid it.
Otherwise looks good.
Thanks,
Phil
-----------------
Bock % cat /tmp/foo.c
#include <libxo/xo.h>
const char *user[] = { "alice", "bob", "carl", "doug", NULL };
const int num_users = 4;
int
main (int argc, char **argv)
{
int i;
argc = xo_parse_args(argc, argv);
if (argc < 0)
return (argc);
int yes = (argc > 1);
xo_open_container("top");
if (yes) {
for (i = 0; i < num_users; i++) {
xo_emit("Member {l:user}\n", user[i]);
}
} else {
xo_open_list("test");
for (i = 0; i < num_users; i++) {
xo_open_instance("test");
xo_emit("Member {k:user}\n", user[i]);
xo_close_instance("test");
}
xo_close_list("test");
}
xo_close_container("top");
xo_finish();
exit(0);
}
Bock % cc -I work/root/include/ -L work/root/lib -lxo -o /tmp/foo /tmp/foo.c
Bock % /tmp/foo
Member alice
Member bob
Member carl
Member doug
Bock % /tmp/foo --libxo:XP
<top>
<test>
<user>alice</user>
</test>
<test>
<user>bob</user>
</test>
<test>
<user>carl</user>
</test>
<test>
<user>doug</user>
</test>
</top>
Bock % /tmp/foo --libxo:XP yes
<top>
<user>alice</user>
<user>bob</user>
<user>carl</user>
<user>doug</user>
</top>
Bock % /tmp/foo --libxo:JP
{
"top": {
"test": [
{
"user": "alice"
},
{
"user": "bob"
},
{
"user": "carl"
},
{
"user": "doug"
}
]
}
}
Bock % /tmp/foo --libxo:JP yes
{
"top": {
"user": [
"alice",
"bob",
"carl",
"doug"
]
}
}