The branch main has been updated by christos:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=aa58af04dc88aabf9d2fade2c7d126031cb66f42

commit aa58af04dc88aabf9d2fade2c7d126031cb66f42
Author:     Christos Margiolis <[email protected]>
AuthorDate: 2026-01-12 13:32:06 +0000
Commit:     Christos Margiolis <[email protected]>
CommitDate: 2026-01-12 13:32:32 +0000

    sndctl(8): Add libxo support
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Reviewed by:    ziaee, mckusick
    Differential Revision:  https://reviews.freebsd.org/D54032
---
 usr.sbin/sndctl/Makefile |   2 +-
 usr.sbin/sndctl/sndctl.8 |  11 +++-
 usr.sbin/sndctl/sndctl.c | 151 +++++++++++++++++++++++++++++++----------------
 3 files changed, 111 insertions(+), 53 deletions(-)

diff --git a/usr.sbin/sndctl/Makefile b/usr.sbin/sndctl/Makefile
index d6697bb88fd5..cb06b84f5766 100644
--- a/usr.sbin/sndctl/Makefile
+++ b/usr.sbin/sndctl/Makefile
@@ -5,6 +5,6 @@ PACKAGE=        sound
 PROG=          sndctl
 SRCS=          ${PROG}.c
 MAN=           ${PROG}.8
-LDFLAGS+=      -lnv -lmixer
+LDFLAGS+=      -lnv -lmixer -lxo -lsbuf
 
 .include <bsd.prog.mk>
diff --git a/usr.sbin/sndctl/sndctl.8 b/usr.sbin/sndctl/sndctl.8
index 4c3810f3c16b..73414bd95325 100644
--- a/usr.sbin/sndctl/sndctl.8
+++ b/usr.sbin/sndctl/sndctl.8
@@ -27,7 +27,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 5, 2025
+.Dd December 2, 2025
 .Dt SNDCTL 8
 .Os
 .Sh NAME
@@ -35,6 +35,7 @@
 .Nd list and modify soundcard properties
 .Sh SYNOPSIS
 .Nm
+.Op Fl -libxo
 .Op Fl f Ar device
 .Op Fl hov
 .Op Ar control Ns Oo = Ns Ar value Oc Ar ...
@@ -46,6 +47,13 @@ control-driven interface, in order to filter and/or set 
specific properties.
 .Pp
 The options are as follows:
 .Bl -tag -width "-f device"
+.It Fl -libxo
+Generate output via
+.Xr libxo 3
+in a selection of different human and machine readable formats.
+See
+.Xr xo_options 7
+for details on command line arguments.
 .It Fl f Ar device
 Choose a specific audio device
 .Pq see Sx FILES .
@@ -175,6 +183,7 @@ $ sndctl -f /dev/dsp0 `cat info`
 .Sh SEE ALSO
 .Xr sndstat 4 ,
 .Xr sound 4 ,
+.Xr xo_options 7 ,
 .Xr mixer 8 ,
 .Xr sysctl 8
 .Sh AUTHORS
diff --git a/usr.sbin/sndctl/sndctl.c b/usr.sbin/sndctl/sndctl.c
index e6dac67e2ea7..bbc2da6a4ab9 100644
--- a/usr.sbin/sndctl/sndctl.c
+++ b/usr.sbin/sndctl/sndctl.c
@@ -30,6 +30,7 @@
 
 #include <sys/nv.h>
 #include <sys/queue.h>
+#include <sys/sbuf.h>
 #include <sys/sndstat.h>
 #include <sys/soundcard.h>
 #include <sys/sysctl.h>
@@ -45,6 +46,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <libxo/xo.h>
+
+#define SNDCTL_XO_VERSION      "1"
 
 /* Taken from sys/dev/sound/pcm/ */
 #define STATUS_LEN     64
@@ -171,7 +175,7 @@ static struct snd_ctl dev_ctls[] = {
 
 static struct snd_ctl chan_ctls[] = {
 #define F(member)      offsetof(struct snd_chan, member)
-       /*{ "name",             F(name),                STR,    NULL },*/
+       { "name",               F(name),                STR,    NULL },
        { "parentchan",         F(parentchan),          STR,    NULL },
        { "unit",               F(unit),                NUM,    NULL },
        { "caps",               F(caps),                STR,    NULL },
@@ -342,7 +346,7 @@ sysctl_int(const char *buf, const char *arg, int *var)
        size = sizeof(int);
        /* Read current value. */
        if (sysctlbyname(buf, &prev, &size, NULL, 0) < 0) {
-               warn("sysctlbyname(%s)", buf);
+               xo_warn("sysctlbyname(%s)", buf);
                return (-1);
        }
 
@@ -351,24 +355,24 @@ sysctl_int(const char *buf, const char *arg, int *var)
                errno = 0;
                n = strtol(arg, NULL, 10);
                if (errno == EINVAL || errno == ERANGE) {
-                       warn("strtol(%s)", arg);
+                       xo_warn("strtol(%s)", arg);
                        return (-1);
                }
 
                /* Apply new value. */
                if (sysctlbyname(buf, NULL, 0, &n, size) < 0) {
-                       warn("sysctlbyname(%s, %d)", buf, n);
+                       xo_warn("sysctlbyname(%s, %d)", buf, n);
                        return (-1);
                }
        }
 
        /* Read back applied value for good measure. */
        if (sysctlbyname(buf, &n, &size, NULL, 0) < 0) {
-               warn("sysctlbyname(%s)", buf);
+               xo_warn("sysctlbyname(%s)", buf);
                return (-1);
        }
 
-       if (arg != NULL)
+       if (arg != NULL && xo_get_style(NULL) == XO_STYLE_TEXT)
                printf("%s: %d -> %d\n", buf, prev, n);
        if (var != NULL)
                *var = n;
@@ -386,7 +390,7 @@ sysctl_str(const char *buf, const char *arg, char *var, 
size_t varsz)
        /* Read current value. */
        size = sizeof(prev);
        if (sysctlbyname(buf, prev, &size, NULL, 0) < 0) {
-               warn("sysctlbyname(%s)", buf);
+               xo_warn("sysctlbyname(%s)", buf);
                return (-1);
        }
 
@@ -395,26 +399,26 @@ sysctl_str(const char *buf, const char *arg, char *var, 
size_t varsz)
                size = strlen(arg);
                /* Apply new value. */
                if (sysctlbyname(buf, NULL, 0, arg, size) < 0) {
-                       warn("sysctlbyname(%s, %s)", buf, arg);
+                       xo_warn("sysctlbyname(%s, %s)", buf, arg);
                        return (-1);
                }
                /* Get size of new string. */
                if (sysctlbyname(buf, NULL, &size, NULL, 0) < 0) {
-                       warn("sysctlbyname(%s)", buf);
+                       xo_warn("sysctlbyname(%s)", buf);
                        return (-1);
                }
        }
 
        if ((tmp = calloc(1, size)) == NULL)
-               err(1, "calloc");
+               xo_err(1, "calloc");
        /* Read back applied value for good measure. */
        if (sysctlbyname(buf, tmp, &size, NULL, 0) < 0) {
-               warn("sysctlbyname(%s)", buf);
+               xo_warn("sysctlbyname(%s)", buf);
                free(tmp);
                return (-1);
        }
 
-       if (arg != NULL)
+       if (arg != NULL && xo_get_style(NULL) == XO_STYLE_TEXT)
                printf("%s: %s -> %s\n", buf, prev, tmp);
        if (var != NULL)
                strlcpy(var, tmp, varsz);
@@ -436,27 +440,27 @@ read_dev(char *path)
        int fd, caps, unit, t1, t2, t3;
 
        if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
-               err(1, "open(/dev/sndstat)");
+               xo_err(1, "open(/dev/sndstat)");
 
        if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL) < 0)
-               err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)");
+               xo_err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)");
 
        arg.nbytes = 0;
        arg.buf = NULL;
        if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0)
-               err(1, "ioctl(SNDSTIOC_GET_DEVS#1)");
+               xo_err(1, "ioctl(SNDSTIOC_GET_DEVS#1)");
 
        if ((arg.buf = malloc(arg.nbytes)) == NULL)
-               err(1, "malloc");
+               xo_err(1, "malloc");
 
        if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0)
-               err(1, "ioctl(SNDSTIOC_GET_DEVS#2)");
+               xo_err(1, "ioctl(SNDSTIOC_GET_DEVS#2)");
 
        if ((nvl = nvlist_unpack(arg.buf, arg.nbytes, 0)) == NULL)
-               err(1, "nvlist_unpack");
+               xo_err(1, "nvlist_unpack");
 
        if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
-               errx(1, "no soundcards attached");
+               xo_errx(1, "no soundcards attached");
 
        if (path == NULL || (path != NULL && strcmp(basename(path), "dsp") == 
0))
                unit = mixer_get_dunit();
@@ -475,12 +479,12 @@ read_dev(char *path)
                        break;;
        }
        if (i == nitems)
-               errx(1, "device not found");
+               xo_errx(1, "device not found");
 
 #define NV(type, item) \
        nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item)
        if ((dp = calloc(1, sizeof(struct snd_dev))) == NULL)
-               err(1, "calloc");
+               xo_err(1, "calloc");
 
        dp->unit = -1;
        strlcpy(dp->name, NV(string, NAMEUNIT), sizeof(dp->name));
@@ -492,9 +496,9 @@ read_dev(char *path)
 #undef NV
 
        if (dp->play.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY))
-               errx(1, "%s: playback channel list empty", dp->name);
+               xo_errx(1, "%s: playback channel list empty", dp->name);
        if (dp->rec.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC))
-               errx(1, "%s: recording channel list empty", dp->name);
+               xo_errx(1, "%s: recording channel list empty", dp->name);
 
 #define NV(type, mode, item)                                           \
        nvlist_get_ ## type (nvlist_get_nvlist(di[i],                   \
@@ -526,7 +530,7 @@ read_dev(char *path)
                goto done;
 
        if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO))
-               errx(1, "%s: provider_info list empty", dp->name);
+               xo_errx(1, "%s: provider_info list empty", dp->name);
 
 #define NV(type, item)                                                 \
        nvlist_get_ ## type (nvlist_get_nvlist(di[i],                   \
@@ -549,13 +553,13 @@ read_dev(char *path)
        if (sysctl_int("hw.snd.latency", NULL, &t1) ||
            sysctl_int("hw.snd.latency_profile", NULL, &t2) ||
            sysctl_int("kern.timecounter.alloweddeviation", NULL, &t3))
-               err(1, "%s: sysctl", dp->name);
+               xo_err(1, "%s: sysctl", dp->name);
        if (t1 == 0 && t2 == 0 && t3 == 0)
                dp->realtime = 1;
 
        if (!nvlist_exists(nvlist_get_nvlist(di[i],
            SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO))
-               errx(1, "%s: channel info list empty", dp->name);
+               xo_errx(1, "%s: channel info list empty", dp->name);
 
        cdi = nvlist_get_nvlist_array(
            nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
@@ -567,7 +571,7 @@ read_dev(char *path)
 #define NV(type, item) \
        nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item)
                if ((ch = calloc(1, sizeof(struct snd_chan))) == NULL)
-                       err(1, "calloc");
+                       xo_err(1, "calloc");
 
                strlcpy(ch->name, NV(string, NAME), sizeof(ch->name));
                strlcpy(ch->parentchan, NV(string, PARENTCHAN),
@@ -654,7 +658,7 @@ print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, bool 
simple,
        struct snd_ctl *cp;
        size_t len;
 
-       if (ctl->type != GRP) {
+       if (ctl->type != GRP && xo_get_style(NULL) == XO_STYLE_TEXT) {
                if (simple)
                        printf("%s=", ctl->name);
                else
@@ -663,10 +667,10 @@ print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, 
bool simple,
 
        switch (ctl->type) {
        case STR:
-               printf("%s\n", (char *)dp + ctl->off);
+               xo_emit("{a:%s/%s}\n", ctl->name, (char *)dp + ctl->off);
                break;
        case NUM:
-               printf("%d\n", *(int *)((intptr_t)dp + ctl->off));
+               xo_emit("{a:%s/%d}\n", ctl->name, *(int *)((intptr_t)dp + 
ctl->off));
                break;
        case VOL:
                break;
@@ -691,7 +695,7 @@ print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, 
bool simple,
        size_t len;
        int v;
 
-       if (ctl->type != GRP) {
+       if (ctl->type != GRP && xo_get_style(NULL) == XO_STYLE_TEXT) {
                if (simple)
                        printf("%s.%s=", ch->name, ctl->name);
                else
@@ -700,14 +704,14 @@ print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, 
bool simple,
 
        switch (ctl->type) {
        case STR:
-               printf("%s\n", (char *)ch + ctl->off);
+               xo_emit("{a:%s/%s}\n", ctl->name, (char *)ch + ctl->off);
                break;
        case NUM:
-               printf("%d\n", *(int *)((intptr_t)ch + ctl->off));
+               xo_emit("{a:%s/%d}\n", ctl->name, *(int *)((intptr_t)ch + 
ctl->off));
                break;
        case VOL:
                v = *(int *)((intptr_t)ch + ctl->off);
-               printf("%.2f:%.2f\n",
+               xo_emit("{a:%s/%.2f:%.2f}\n", ctl->name,
                    MIX_VOLNORM(v & 0x00ff), MIX_VOLNORM((v >> 8) & 0x00ff));
                break;
        case GRP:
@@ -728,31 +732,46 @@ print_dev(struct snd_dev *dp)
 {
        struct snd_chan *ch;
        struct snd_ctl *ctl;
+       struct sbuf sb;
+       char buf[16];
 
-       if (!oflag) {
-               printf("%s: <%s> %s", dp->name, dp->desc, dp->status);
+       xo_open_instance("devices");
 
-               printf(" (");
+       if (!oflag || xo_get_style(NULL) != XO_STYLE_TEXT) {
+               sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
+
+               sbuf_printf(&sb, "(");
                if (dp->play.pchans)
-                       printf("play");
+                       sbuf_printf(&sb, "play");
                if (dp->play.pchans && dp->rec.pchans)
-                       printf("/");
+                       sbuf_printf(&sb, "/");
                if (dp->rec.pchans)
-                       printf("rec");
-               printf(")\n");
+                       sbuf_printf(&sb, "rec");
+               sbuf_printf(&sb, ")");
+
+               xo_emit("{:header/%s: <%s> %s %s}\n",
+                   dp->name, dp->desc, dp->status, sbuf_data(&sb));
+
+               sbuf_delete(&sb);
        }
 
        for (ctl = dev_ctls; ctl->name != NULL; ctl++)
                print_dev_ctl(dp, ctl, oflag, false);
 
        if (vflag) {
+               xo_open_list("channels");
                TAILQ_FOREACH(ch, &dp->chans, next) {
-                       if (!oflag)
+                       xo_open_instance("channels");
+                       if (!oflag && xo_get_style(NULL) == XO_STYLE_TEXT)
                                printf("    %s\n", ch->name);
                        for (ctl = chan_ctls; ctl->name != NULL; ctl++)
                                print_chan_ctl(ch, ctl, oflag, false);
+                       xo_close_instance("channels");
                }
+               xo_close_list("channels");
        }
+
+       xo_close_instance("devices");
 }
 
 static int
@@ -916,8 +935,9 @@ mod_rec_format(struct snd_dev *dp, void *arg)
 static void __dead2
 usage(void)
 {
-       fprintf(stderr, "usage: %s [-f device] [-hov] [control[=value] ...]\n",
+       xo_error("usage: %s [--libxo] [-f device] [-hov] [control[=value] 
...]\n",
            getprogname());
+       xo_finish();
        exit(1);
 }
 
@@ -932,6 +952,10 @@ main(int argc, char *argv[])
        bool show = true, found;
        int c;
 
+       argc = xo_parse_args(argc, argv);
+       if (argc < 0)
+               exit(1);
+
        while ((c = getopt(argc, argv, "f:hov")) != -1) {
                switch (c) {
                case 'f':
@@ -952,11 +976,20 @@ main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
+       xo_set_version(SNDCTL_XO_VERSION);
+       xo_open_container("sndctl");
+
        dp = read_dev(path);
 
+       xo_open_container("executed_controls");
        while (argc > 0) {
-               if ((s = strdup(*argv)) == NULL)
-                       err(1, "strdup(%s)", *argv);
+               if ((s = strdup(*argv)) == NULL) {
+                       xo_close_container("executed_controls");
+                       xo_close_container("sndctl");
+                       if (xo_finish() < 0)
+                               xo_err(1, "xo_finish");
+                       xo_err(1, "strdup(%s)", *argv);
+               }
 
                propstr = strsep(&s, "=");
                if (propstr == NULL)
@@ -966,11 +999,19 @@ main(int argc, char *argv[])
                for (ctl = dev_ctls; ctl->name != NULL; ctl++) {
                        if (strcmp(ctl->name, propstr) != 0)
                                continue;
-                       if (s == NULL) {
-                               print_dev_ctl(dp, ctl, true, true);
+                       if (s == NULL)
                                show = false;
-                       } else if (ctl->mod != NULL && ctl->mod(dp, s) < 0)
-                               warnx("%s(%s) failed", ctl->name, s);
+                       else if (ctl->mod != NULL && ctl->mod(dp, s) < 0)
+                               xo_warnx("%s(%s) failed", ctl->name, s);
+                       if (s == NULL || xo_get_style(NULL) != XO_STYLE_TEXT) {
+                               /*
+                                * Print the control in libxo mode in all
+                                * cases, otherwise we'll not be printing any
+                                * controls that were modified or whose
+                                * ctl->mod() failed.
+                                */
+                               print_dev_ctl(dp, ctl, true, true);
+                       }
                        found = true;
                        break;
                }
@@ -985,17 +1026,25 @@ main(int argc, char *argv[])
                        }
                }
                if (!found)
-                       warnx("%s: no such property", propstr);
+                       xo_warnx("%s: no such property", propstr);
 next:
                free(s);
                argc--;
                argv++;
        }
+       xo_close_container("executed_controls");
 
-       if (show) {
+       if (show || xo_get_style(NULL) != XO_STYLE_TEXT) {
+               xo_open_list("devices");
                print_dev(dp);
+               xo_close_list("devices");
        }
        free_dev(dp);
 
+
+       xo_close_container("sndctl");
+       if (xo_finish() < 0)
+               xo_err(1, "xo_finish");
+
        return (0);
 }

Reply via email to