tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Enhancements to snprintb(3)



A few days ago, I suggested that snprintb(3) could be enhanced to produce multiple strings, each of which was not longer than some user-provided maximum length. My initial thought was to make multiple calls to snprintb(3), and update an argument with the set of bits that were processed. (Thinking that once all bits were processed, we could simply stop calling.)

That approach actually doesn't work well, since there's nothing in the rules that prevents the formatting string to reference a bit more than once, and in any order. Additionally, with the new-format, bits and bit-fields can be referenced multiple times, and can overlap each other with impugnity.

An alternate suggestion was made to simply return multiple strings from a single call to snprintb_m(3) (the _m suffix for "multiple"). The attached diffs implement this type of change, and includes updates to the man page, a bump in libutil's shared library version, and related updates to the src/distrib/ set-lists. I'd appreciate any feedback or other comments.

I'd like to commit this early next week (like Monday or Tuesday), since I've got some updates to usr.sbin/cpuctl that depend on this.


-------------------------------------------------------------------------
|   Paul Goyette   | PGP DSS Key fingerprint: |  E-mail addresses:      |
| Customer Service | FA29 0E3B 35AF E8AE 6651 |  paul at whooppee.com   |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette at juniper.net |
| Kernel Developer |                          | pgoyette at netbsd.org  |
-------------------------------------------------------------------------
Index: snprintb.c
===================================================================
RCS file: /cvsroot/src/common/lib/libutil/snprintb.c,v
retrieving revision 1.4
diff -u -p -r1.4 snprintb.c
--- snprintb.c  18 Jan 2009 12:05:49 -0000      1.4
+++ snprintb.c  6 May 2009 20:49:41 -0000
@@ -59,11 +59,14 @@ __KERNEL_RCSID(0, "$NetBSD: snprintb.c,v
 # endif
 
 int
-snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val)
+snprintb_m(char *buf, size_t buflen, const char *bitfmt, uint64_t val,
+          size_t l_max)
 {
-       char *bp = buf;
+       char *bp = buf, *s_bp = NULL;
+       const char *c_fmt, *s_fmt = NULL, *cur_fmt;
        const char *sbase;
-       int bit, ch, len, sep, flen;
+       int bit, ch, t_len, s_len = 0, l_len, f_len, v_len, sep;
+       int restart = 0;
        uint64_t field;
 
 #ifdef _KERNEL
@@ -89,12 +92,18 @@ snprintb(char *buf, size_t buflen, const
                goto internal;
        }
 
-       len = snprintf(bp, buflen, sbase, val);
-       if (len < 0)
+       /* Reserve space for trailing blank line if needed */
+       if (l_max > 0)
+               buflen--;
+
+       t_len = snprintf(bp, buflen, sbase, val);
+       if (t_len < 0)
                goto internal;
 
-       if ((size_t)len < buflen)
-               bp += len;
+       v_len = l_len = t_len;
+
+       if ((size_t)t_len < buflen)
+               bp += t_len;
        else
                bp += buflen - 1;
 
@@ -105,8 +114,55 @@ snprintb(char *buf, size_t buflen, const
        if ((val == 0) && (ch != '\177'))
                goto terminate;
 
-#define PUTC(c) if ((size_t)(++len) < buflen) *bp++ = (c)
-#define PUTS(s) while ((ch = *(s)++) != 0) PUTC(ch)
+#define STORE(c) { l_len++;                                            \
+                  if ((size_t)(++t_len) < buflen)                      \
+                       *bp++ = (c);                                    \
+                } while ( /* CONSTCOND */ 0)
+
+#define        BACKUP  { if (s_bp != NULL) {                                   
\
+                       bp = s_bp; s_bp = NULL;                         \
+                       t_len -= l_len - s_len;                         \
+                       restart = 1;                                    \
+                       bitfmt = s_fmt;                                 \
+                 }                                                     \
+                 STORE('>'); STORE('\0');                              \
+                 if ((size_t)t_len < buflen)                           \
+                       snprintf(bp, buflen - t_len, sbase, val);       \
+                 t_len += v_len; l_len = v_len; bp += v_len;           \
+               } while ( /* CONSTCOND */ 0)
+
+#define        PUTSEP                                                          
\
+               if (l_max > 0 && (size_t)l_len >= l_max) {              \
+                       BACKUP;                                         \
+                       STORE('<');                                     \
+               } else {                                                \
+                       /* Remember separator location */               \
+                       if ( l_max > 0 && sep != '<') {                 \
+                               s_len = l_len;                          \
+                               s_bp  = bp;                             \
+                               s_fmt = cur_fmt;                        \
+                       }                                               \
+                       STORE(sep);                                     \
+                       restart = 0;                                    \
+               }                                                       \
+
+#define        PUTCHR(c)                                                       
\
+               if (l_max > 0 && (size_t)l_len >= (l_max - 1)) {        \
+                       BACKUP;                                         \
+                       if (restart == 0) {                             \
+                               STORE(c);                               \
+                       } else                                          \
+                               sep = '<';                              \
+               } else {                                                \
+                       STORE(c);                                       \
+                       restart = 0;                                    \
+               }                                                       \
+
+#define PUTS(s) while ((ch = *(s)++) != 0) {                           \
+                       PUTCHR(ch);                                     \
+                       if (restart)                                    \
+                               break;                                  \
+               }
 
        /*
         * Chris Torek's new bitmask format is identified by a leading \177
@@ -114,12 +170,18 @@ snprintb(char *buf, size_t buflen, const
        sep = '<';
        if (ch != '\177') {
                /* old (standard) format. */
-               for (;(bit = *bitfmt++) != 0;) {
+               for (;(bit = *bitfmt) != 0;) {
+                       cur_fmt = bitfmt++;
                        if (val & (1 << (bit - 1))) {
-                               PUTC(sep);
-                               for (; (ch = *bitfmt) > ' '; ++bitfmt)
-                                       PUTC(ch);
+                               PUTSEP;
+                               if (restart)
+                                       continue;
                                sep = ',';
+                               for (; (ch = *bitfmt) > ' '; ++bitfmt) {
+                                       PUTCHR(ch); 
+                                       if (restart)
+                                               break;
+                               }
                        } else
                                for (; *bitfmt > ' '; ++bitfmt)
                                        continue;
@@ -127,33 +189,50 @@ snprintb(char *buf, size_t buflen, const
        } else {
                /* new quad-capable format; also does fields. */
                field = val;
-               while ((ch = *bitfmt++) != '\0') {
+               while (c_fmt = bitfmt, (ch = *bitfmt++) != '\0') {
                        bit = *bitfmt++;        /* now 0-origin */
                        switch (ch) {
                        case 'b':
                                if (((u_int)(val >> bit) & 1) == 0)
                                        goto skip;
-                               PUTC(sep);
+                               cur_fmt = c_fmt;
+                               PUTSEP;
+                               if (restart)
+                                       break;
                                PUTS(bitfmt);
-                               sep = ',';
+                               if (restart == 0)
+                                       sep = ',';
                                break;
                        case 'f':
                        case 'F':
-                               flen = *bitfmt++;       /* field length */
+                               cur_fmt = c_fmt;
+                               f_len = *bitfmt++;      /* field length */
                                field = (val >> bit) &
-                                           (((uint64_t)1 << flen) - 1);
+                                           (((uint64_t)1 << f_len) - 1);
                                if (ch == 'F')  /* just extract */
                                        break;
-                               PUTC(sep);
-                               sep = ',';
-                               PUTS(bitfmt);
-                               PUTC('=');
-                               flen = snprintf(bp, buflen - len, sbase, field);
-                               if (flen < 0)
-                                       goto internal;
-                               len += flen;
-                               if ((size_t)len < buflen)
-                                       bp += flen;
+                               PUTSEP;
+                               if (restart == 0) {
+                                       sep = ',';
+                                       PUTS(bitfmt);
+                               }
+                               if (restart == 0) {
+                                       PUTCHR('=');
+                               }
+                               if (restart == 0) {
+                                       f_len = snprintf(bp, buflen - t_len,
+                                                        sbase, field);
+                                       if (f_len < 0)
+                                               goto internal;
+                                       t_len += f_len;
+                                       l_len += f_len;
+                                       if ((size_t)t_len < buflen)
+                                               bp += f_len;
+                                       if (l_max > 0 &&
+                                           (size_t)l_len > l_max) {
+                                               PUTCHR('#');
+                                       }
+                               }
                                break;
                        case '=':
                        case ':':
@@ -166,7 +245,7 @@ snprintb(char *buf, size_t buflen, const
                                if ((int)field != bit)
                                        goto skip;
                                if (ch == '=') {
-                                       PUTC('=');
+                                       PUTCHR('=');
                                }
                                PUTS(bitfmt);
                                break;
@@ -178,16 +257,26 @@ snprintb(char *buf, size_t buflen, const
                        }
                }
        }
-       if (sep != '<') {
-               PUTC('>');
-       }
+       l_len++;
+       if ((size_t)(++t_len) < buflen)
+               *bp++ = '>';
 terminate:
-       *bp = '\0';
-       return len;
+       *bp++ = '\0';
+       if (l_max != 0) {
+               t_len++;
+               *bp = '\0';
+       }
+       return t_len;
 internal:
 #ifndef _KERNEL
        errno = EINVAL;
 #endif
        return -1;
 }
+
+int
+snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val)
+{
+       return snprintb_m(buf, buflen, bitfmt, val, 0);
+}
 #endif
Index: md.amd64
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/base/md.amd64,v
retrieving revision 1.45
diff -u -p -r1.45 md.amd64
--- md.amd64    2 May 2009 02:20:20 -0000       1.45
+++ md.amd64    6 May 2009 20:49:41 -0000
@@ -201,7 +201,7 @@
 ./usr/lib/i386/libusbhid.so.1                  base-compat-shlib       
compat,pic
 ./usr/lib/i386/libusbhid.so.1.0                        base-compat-shlib       
compat,pic
 ./usr/lib/i386/libutil.so.7                    base-compat-shlib       
compat,pic
-./usr/lib/i386/libutil.so.7.16                 base-compat-shlib       
compat,pic
+./usr/lib/i386/libutil.so.7.17                 base-compat-shlib       
compat,pic
 ./usr/lib/i386/libwrap.so.1                    base-compat-shlib       
compat,pic
 ./usr/lib/i386/libwrap.so.1.0                  base-compat-shlib       
compat,pic
 ./usr/lib/i386/libz.so.1                       base-compat-shlib       
compat,pic
Index: shl.mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/base/shl.mi,v
retrieving revision 1.469
diff -u -p -r1.469 shl.mi
--- shl.mi      2 May 2009 01:15:54 -0000       1.469
+++ shl.mi      6 May 2009 20:49:42 -0000
@@ -27,7 +27,7 @@
 ./lib/libradius.so.3.0                         base-sys-shlib          
dynamicroot
 ./lib/libtermcap.so.0.6                                base-sys-shlib          
dynamicroot
 ./lib/libtermlib.so.0.6                                base-sys-shlib          
dynamicroot
-./lib/libutil.so.7.16                          base-sys-shlib          
dynamicroot
+./lib/libutil.so.7.17                          base-sys-shlib          
dynamicroot
 ./lib/libz.so.1.0                              base-sys-shlib          
dynamicroot
 ./usr/lib/i18n/libBIG5.so.5.0                  base-i18n-shlib
 ./usr/lib/i18n/libDECHanyu.so.5.0              base-i18n-shlib
@@ -153,7 +153,7 @@
 ./usr/lib/libtermlib.so.0.6                    base-sys-shlib
 ./usr/lib/libukfs.so.1.0                       base-sys-shlib
 ./usr/lib/libusbhid.so.1.0                     base-sys-shlib
-./usr/lib/libutil.so.7.16                      base-sys-shlib
+./usr/lib/libutil.so.7.17                      base-sys-shlib
 ./usr/lib/libwrap.so.1.0                       base-net-shlib
 ./usr/lib/libz.so.1.0                          base-sys-shlib
 ./usr/lib/security/pam_afslog.so.2             base-sys-shlib          
kerberos,pam
Index: mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v
retrieving revision 1.1252
diff -u -p -r1.1252 mi
--- mi  4 May 2009 21:33:00 -0000       1.1252
+++ mi  6 May 2009 20:49:45 -0000
@@ -7471,6 +7471,7 @@
 ./usr/share/man/cat3/sl_init.0                 comp-c-catman           .cat
 ./usr/share/man/cat3/sleep.0                   comp-c-catman           .cat
 ./usr/share/man/cat3/snprintb.0                        comp-c-catman           
.cat
+./usr/share/man/cat3/snprintb_m.0              comp-c-catman           .cat
 ./usr/share/man/cat3/snprintf.0                        comp-c-catman           
.cat
 ./usr/share/man/cat3/sockaddr_snprintf.0       comp-c-catman           .cat
 ./usr/share/man/cat3/sockatmark.0              comp-c-catman           .cat
@@ -12783,6 +12784,7 @@
 ./usr/share/man/html3/sl_init.html             comp-c-htmlman          html
 ./usr/share/man/html3/sleep.html               comp-c-htmlman          html
 ./usr/share/man/html3/snprintb.html            comp-c-htmlman          html
+./usr/share/man/html3/snprintb_m.html          comp-c-htmlman          html
 ./usr/share/man/html3/snprintf.html            comp-c-htmlman          html
 ./usr/share/man/html3/sockaddr_snprintf.html   comp-c-htmlman          html
 ./usr/share/man/html3/sockatmark.html          comp-c-htmlman          html
@@ -18134,6 +18136,7 @@
 ./usr/share/man/man3/sl_init.3                 comp-c-man              .man
 ./usr/share/man/man3/sleep.3                   comp-c-man              .man
 ./usr/share/man/man3/snprintb.3                        comp-c-man              
.man
+./usr/share/man/man3/snprintb_m.3              comp-c-man              .man
 ./usr/share/man/man3/snprintf.3                        comp-c-man              
.man
 ./usr/share/man/man3/sockaddr_snprintf.3       comp-c-man              .man
 ./usr/share/man/man3/sockatmark.3              comp-c-man              .man
Index: mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/man/mi,v
retrieving revision 1.1135
diff -u -p -r1.1135 mi
--- mi  1 May 2009 22:59:53 -0000       1.1135
+++ mi  6 May 2009 20:49:47 -0000
@@ -1360,6 +1360,7 @@
 ./usr/share/man/cat4/scsi.0                    man-sys-catman          .cat
 ./usr/share/man/cat4/scsibus.0                 man-sys-catman          .cat
 ./usr/share/man/cat4/sd.0                      man-sys-catman          .cat
+./usr/share/man/cat4/sdtemp.0                  man-sys-catman          .cat
 ./usr/share/man/cat4/se.0                      man-sys-catman          .cat
 ./usr/share/man/cat4/sea.0                     man-sys-catman          .cat
 ./usr/share/man/cat4/sec.0                     man-sys-catman          .cat
@@ -3888,6 +3889,7 @@
 ./usr/share/man/html4/scsi.html                        man-sys-htmlman         
html
 ./usr/share/man/html4/scsibus.html             man-sys-htmlman         html
 ./usr/share/man/html4/sd.html                  man-sys-htmlman         html
+./usr/share/man/html4/sdtemp.html              man-sys-htmlman         html
 ./usr/share/man/html4/se.html                  man-sys-htmlman         html
 ./usr/share/man/html4/sea.html                 man-sys-htmlman         html
 ./usr/share/man/html4/sec.html                 man-sys-htmlman         html
@@ -6328,6 +6330,7 @@
 ./usr/share/man/man4/scsi.4                    man-sys-man             .man
 ./usr/share/man/man4/scsibus.4                 man-sys-man             .man
 ./usr/share/man/man4/sd.4                      man-sys-man             .man
+./usr/share/man/man4/sdtemp.4                  man-sys-man             .man
 ./usr/share/man/man4/se.4                      man-sys-man             .man
 ./usr/share/man/man4/sea.4                     man-sys-man             .man
 ./usr/share/man/man4/sec.4                     man-sys-man             .man
Index: util.h
===================================================================
RCS file: /cvsroot/src/include/util.h,v
retrieving revision 1.50
diff -u -p -r1.50 util.h
--- util.h      11 Jan 2009 03:04:12 -0000      1.50
+++ util.h      6 May 2009 20:49:48 -0000
@@ -115,6 +115,7 @@ void                pw_prompt(void);
 int            pw_setprefix(const char *);
 int            raise_default_signal(int);
 int            secure_path(const char *);
+int            snprintb_m(char *, size_t, const char *, uint64_t, size_t);
 int            snprintb(char *, size_t, const char *, uint64_t);
 int            sockaddr_snprintf(char *, size_t, const char *,
     const struct sockaddr *);
Index: Makefile
===================================================================
RCS file: /cvsroot/src/lib/libutil/Makefile,v
retrieving revision 1.60
diff -u -p -r1.60 Makefile
--- Makefile    18 Jan 2009 12:13:32 -0000      1.60
+++ Makefile    6 May 2009 20:49:48 -0000
@@ -77,5 +77,6 @@ MLINKS+=efun.3 efopen.3
 MLINKS+=efun.3 evasprintf.3
 MLINKS+=stat_flags.3 string_to_flags.3
 MLINKS+=stat_flags.3 flags_to_string.3
+MLINKS+=snprintb.3 snprintb_m.3
 
 .include <bsd.lib.mk>
Index: shlib_version
===================================================================
RCS file: /cvsroot/src/lib/libutil/shlib_version,v
retrieving revision 1.46
diff -u -p -r1.46 shlib_version
--- shlib_version       11 Jan 2009 02:57:18 -0000      1.46
+++ shlib_version       6 May 2009 20:49:49 -0000
@@ -2,4 +2,4 @@
 #      Remember to update distrib/sets/lists/base/shl.* when changing
 #
 major=7
-minor=16
+minor=17
Index: snprintb.3
===================================================================
RCS file: /cvsroot/src/lib/libutil/snprintb.3,v
retrieving revision 1.13
diff -u -p -r1.13 snprintb.3
--- snprintb.3  5 May 2009 13:12:25 -0000       1.13
+++ snprintb.3  6 May 2009 20:49:50 -0000
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd May 5, 2009
+.Dd May 7, 2009
 .Dt SNPRINTB 3
 .Os
 .Sh NAME
@@ -38,7 +38,10 @@
 .Sh SYNOPSIS
 .In util.h
 .Ft int
-.Fn "snprintb" "char *buf" "size_t buflen" "const char *fmt" "uint_t val"
+.Fn "snprintb" "char *buf" "size_t buflen" "const char *fmt" "uint64_t val"
+.Ft int
+.Fn "snprintb_m" "char *buf" "size_t buflen" "const char *fmt" "uint64_t val" \
+"size_t max"
 .Sh DESCRIPTION
 The
 .Fn snprintb
@@ -181,12 +184,43 @@ character.
 By convention, the format string has an additional NUL character at
 the end, following that delimiting the last bit-position\(endescription
 pair.
+.Pp
+The
+.Fn snprintb_m
+function accepts an additional
+.Fa max
+argument.
+If this argument is zero, the
+.Fn snprintb_m
+function returns exactly the same results in the
+.Fa buf
+as the
+.Fn snprintb
+function.
+If the
+.Fa max
+argument is present and has a non-zero value, it represents the maximum
+length of a formatted string.
+If the formatted string would require more than
+.Fa max
+characters, the
+.Fn snprintb_m
+function returns multiple formatted strings in the output buffer
+.Fa buf .
+Each string is NUL-terminated, and the last string is followed by an
+additional NUL character (or, if you prefer, a zero-length string).
 .Sh RETURN VALUES
 The
 .Fn snprintb
-function returns the number of bytes that would have written to the buffer
-if there was adequate space, excluding the terminating NUL, or \-1 in case
-an error occurred.
+and
+.Fn snprintb_m
+functions return the number of bytes that would have written to the buffer
+if there was adequate space, excluding the final terminating NUL, or \-1 in
+case an error occurred.
+For
+.Fn snprintb_m ,
+the NUL characters terminating each individual string are included in the
+total number of bytes.
 .Sh EXAMPLES
 Two examples of the old formatting style:
 .Bd -literal -offset indent


Home | Main Index | Thread Index | Old Index