Home | History | Annotate | Line # | Download | only in libutil
t_snprintb.c revision 1.16
      1 /* $NetBSD: t_snprintb.c,v 1.16 2024/02/16 01:19:53 rillig Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2002, 2004, 2008, 2010, 2024 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code was contributed to The NetBSD Foundation by Christos Zoulas and
      8  * Roland Illig.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __COPYRIGHT("@(#) Copyright (c) 2008, 2010\
     34  The NetBSD Foundation, inc. All rights reserved.");
     35 __RCSID("$NetBSD: t_snprintb.c,v 1.16 2024/02/16 01:19:53 rillig Exp $");
     36 
     37 #include <stdio.h>
     38 #include <string.h>
     39 #include <util.h>
     40 #include <vis.h>
     41 
     42 #include <atf-c.h>
     43 
     44 static const char *
     45 vis_arr(const char *arr, size_t arrsize)
     46 {
     47 	static char buf[6][1024];
     48 	static size_t i;
     49 
     50 	i = (i + 1) % (sizeof(buf) / sizeof(buf[0]));
     51 	int rv = strnvisx(buf[i], sizeof(buf[i]), arr, arrsize,
     52 	    VIS_WHITE | VIS_OCTAL);
     53 	ATF_REQUIRE_MSG(rv >= 0, "strnvisx failed for size %zu", arrsize);
     54 	return buf[i];
     55 }
     56 
     57 static void
     58 check_unmodified_loc(const char *file, size_t line,
     59     const char *arr, size_t begin, size_t end)
     60 {
     61 	size_t mod_begin = begin, mod_end = end;
     62 	while (mod_begin < mod_end && arr[mod_begin] == 'Z')
     63 		mod_begin++;
     64 	while (mod_begin < mod_end && arr[mod_end - 1] == 'Z')
     65 		mod_end--;
     66 	ATF_CHECK_MSG(
     67 	    mod_begin == mod_end,
     68 	    "failed:\n"
     69 	    "\ttest case: %s:%zu\n"
     70 	    "\tout-of-bounds write from %zu to %zu: %s\n",
     71 	    file, line,
     72 	    mod_begin, mod_end, vis_arr(arr + mod_begin, mod_end - mod_begin));
     73 }
     74 
     75 static void
     76 h_snprintb_loc(const char *file, size_t line,
     77     size_t bufsize, const char *fmt, size_t fmtlen, uint64_t val,
     78     int exp_rv, const char *res, size_t reslen)
     79 {
     80 	char buf[1024];
     81 
     82 	// Calling snprintb with bufsize == 0 invokes undefined
     83 	// behavior due to out-of-range 'bp'.
     84 	ATF_REQUIRE(bufsize > 0);
     85 	ATF_REQUIRE(bufsize <= sizeof(buf));
     86 	ATF_REQUIRE(reslen <= sizeof(buf));
     87 
     88 	memset(buf, 'Z', sizeof(buf));
     89 	int rv = snprintb(buf, bufsize, fmt, val);
     90 	ATF_REQUIRE(rv >= 0);
     91 	size_t rlen = rv;
     92 
     93 	ATF_CHECK_MSG(
     94 	    rv == exp_rv && memcmp(buf, res, reslen) == 0
     95 	    && buf[rlen < bufsize ? rlen : bufsize - 1] == '\0',
     96 	    "failed:\n"
     97 	    "\ttest case: %s:%zu\n"
     98 	    "\tformat: %s\n"
     99 	    "\tvalue: %#jx\n"
    100 	    "\twant: %d bytes %s\n"
    101 	    "\thave: %d bytes %s\n",
    102 	    file, line,
    103 	    vis_arr(fmt, fmtlen),
    104 	    (uintmax_t)val,
    105 	    exp_rv, vis_arr(res, reslen),
    106 	    rv, vis_arr(buf, reslen));
    107 	check_unmodified_loc(file, line, buf, reslen, sizeof(buf));
    108 }
    109 
    110 #define	h_snprintb_len(bufsize, fmt, val, exp_rv, res)			\
    111 	h_snprintb_loc(__FILE__, __LINE__,				\
    112 	    bufsize, fmt, sizeof(fmt) - 1, val,				\
    113 	    exp_rv, res, sizeof(res))
    114 #define	h_snprintb(fmt, val, res)					\
    115 	h_snprintb_len(1024, fmt, val, sizeof(res) - 1, res)
    116 
    117 static void
    118 h_snprintb_error_loc(const char *file, size_t line,
    119     const char *fmt, size_t fmtlen)
    120 {
    121 	char buf[1024];
    122 
    123 	memset(buf, 'Z', sizeof(buf));
    124 	int rv = snprintb(buf, sizeof(buf), fmt, 0);
    125 	size_t buflen = rv;
    126 
    127 	ATF_REQUIRE(rv >= -1);
    128 	ATF_CHECK_MSG(rv == -1,
    129 	    "expected error but got success:\n"
    130 	    "\ttest case: %s:%zu\n"
    131 	    "\tformat: %s\n"
    132 	    "\tresult: %zu bytes %s\n",
    133 	    file, line,
    134 	    vis_arr(fmt, fmtlen),
    135 	    buflen, vis_arr(buf, buflen));
    136 }
    137 
    138 #define	h_snprintb_error(fmt)						\
    139 	h_snprintb_error_loc(__FILE__, __LINE__, fmt, sizeof(fmt) - 1)
    140 
    141 ATF_TC(snprintb);
    142 ATF_TC_HEAD(snprintb, tc)
    143 {
    144 	atf_tc_set_md_var(tc, "descr", "Checks snprintb(3)");
    145 }
    146 ATF_TC_BODY(snprintb, tc)
    147 {
    148 
    149 	// old-style format, octal
    150 	h_snprintb(
    151 	    "\010"
    152 	    "\002BITTWO"
    153 	    "\001BITONE",
    154 	    3,
    155 	    "03<BITTWO,BITONE>");
    156 
    157 	// old-style format, decimal
    158 	h_snprintb(
    159 	    "\012"
    160 	    "\0011"
    161 	    "\0119"
    162 	    "\02117"
    163 	    "\04032",
    164 	    0xffffffff,
    165 	    "4294967295<1,9,17,32>");
    166 
    167 	// old-style format, hexadecimal, from msb downto lsb
    168 	h_snprintb(
    169 	    "\020"
    170 	    "\04032"
    171 	    "\03024"
    172 	    "\02016"
    173 	    "\0108"
    174 	    "\0077"
    175 	    "\0066"
    176 	    "\0055"
    177 	    "\0044"
    178 	    "\0033"
    179 	    "\0022"
    180 	    "\0011"
    181 	    // The old-style format supports only 32 bits, interpreting the
    182 	    // \041 as part of the text belonging to bit 1.
    183 	    "\04133",
    184 	    0x0000ffff00ff0f35,
    185 	    "0xffff00ff0f35<24,6,5,3,1!33>");
    186 
    187 	// old-style format, hexadecimal, from lsb to msb
    188 	h_snprintb(
    189 	    "\020"
    190 	    "\0011"
    191 	    "\0022"
    192 	    "\0033"
    193 	    "\0044"
    194 	    "\0055"
    195 	    "\0066"
    196 	    "\0077"
    197 	    "\0108"
    198 	    "\02016"
    199 	    "\03024"
    200 	    "\04032"
    201 	    // The old-style format supports only 32 bits, interpreting the
    202 	    // \041 as part of the text belonging to bit 32.
    203 	    "\04133",
    204 	    0xffff0000ff00f0ca,
    205 	    "0xffff0000ff00f0ca<2,4,7,8,16,32!33>");
    206 
    207 	// The bits can be listed in arbitrary order, there can also be
    208 	// duplicates. A bit's description can be empty, resulting in several
    209 	// commas in a row.
    210 	h_snprintb(
    211 	    "\020"
    212 	    "\001lsb"
    213 	    "\040msb"
    214 	    "\011"
    215 	    "\012"
    216 	    "\002above-lsb"
    217 	    "\037below-msb"
    218 	    "\001lsb-again"
    219 	    "\040msb-again",
    220 	    0xc0000303,
    221 	    "0xc0000303<lsb,msb,,,above-lsb,below-msb,lsb-again,msb-again>");
    222 
    223 #if 0
    224 	// If the first bit number is 33 or more, snprintb invokes undefined
    225 	// behavior due to an out-of-bounds bit shift, though undetected by
    226 	// -ftrapv. Later bit numbers are properly checked.
    227 	h_snprintb(
    228 	    "\020"
    229 	    "\177undefined_behavior"
    230 	    "\001lsb",
    231 	    0xffffffffffffffff,
    232 	    "0xffffffffffffffff<?>");
    233 #endif
    234 
    235 	// old-style format, invalid number base 0
    236 	h_snprintb_error(
    237 	    "");
    238 
    239 	// old-style format, invalid number base 2
    240 	h_snprintb_error(
    241 	    "\002");
    242 
    243 	// old-style format, invalid number base 255 or -1
    244 	h_snprintb_error(
    245 	    "\377");
    246 
    247 	// old-style format, small buffer
    248 #if 0
    249 	// FIXME: Calling snprintb with buffer size 0 invokes undefined
    250 	// behavior due to out-of-bounds 'bp' pointer.
    251 	h_snprintb_len(
    252 	    0, "\020", 0,
    253 	    1, "");
    254 #endif
    255 	h_snprintb_len(
    256 	    1, "\020", 0,
    257 	    1, "");
    258 	h_snprintb_len(
    259 	    2, "\020", 0,
    260 	    1, "0");
    261 	h_snprintb_len(
    262 	    3, "\020", 0,
    263 	    1, "0");
    264 	h_snprintb_len(
    265 	    3, "\020", 7,
    266 	    3, "0x");
    267 	h_snprintb_len(
    268 	    4, "\020", 7,
    269 	    3, "0x7");
    270 	h_snprintb_len(
    271 	    7, "\020\001lsb", 7,
    272 	    8, "0x7<ls");
    273 	h_snprintb_len(
    274 	    8, "\020\001lsb", 7,
    275 	    8, "0x7<lsb");
    276 	h_snprintb_len(
    277 	    9, "\020\001lsb", 7,
    278 	    8, "0x7<lsb>");
    279 	h_snprintb_len(
    280 	    9, "\020\001one\002two", 7,
    281 	    12, "0x7<one,");
    282 	h_snprintb_len(
    283 	    10, "\020\001one\002two", 7,
    284 	    12, "0x7<one,t");
    285 	h_snprintb_len(
    286 	    12, "\020\001one\002two", 7,
    287 	    12, "0x7<one,two");
    288 	h_snprintb_len(
    289 	    13, "\020\001one\002two", 7,
    290 	    12, "0x7<one,two>");
    291 
    292 	// new-style format, single bits, octal
    293 	h_snprintb(
    294 	    "\177\010"
    295 	    "b\000bit0\0"
    296 	    "b\037bit31\0"
    297 	    "b\040bit32\0"
    298 	    "b\077bit63\0",
    299 	    0xf000000ff000000f,
    300 	    "01700000000776000000017<bit0,bit31,bit32,bit63>");
    301 
    302 	// new-style format, single bits, decimal
    303 	h_snprintb(
    304 	    "\177\012"
    305 	    "b\000bit0\0"
    306 	    "b\037bit31\0"
    307 	    "b\040bit32\0"
    308 	    "b\077bit63\0",
    309 	    0xf000000ff000000f,
    310 	    "17293822637553745935<bit0,bit31,bit32,bit63>");
    311 
    312 	// new-style format, single bits, hexadecimal
    313 	h_snprintb(
    314 	    "\177\020"
    315 	    "b\000bit0\0"
    316 	    "b\037bit31\0"
    317 	    "b\040bit32\0"
    318 	    "b\077bit63\0",
    319 	    0xf000000ff000000f,
    320 	    "0xf000000ff000000f<bit0,bit31,bit32,bit63>");
    321 
    322 	// new-style format, invalid number base 2
    323 	h_snprintb_error(
    324 	    "\177\002");
    325 
    326 	// new-style format, invalid number base 255 or -1
    327 	h_snprintb_error(
    328 	    "\177\377");
    329 
    330 	// new-style format, single bits, edge cases
    331 	//
    332 	// The bits can be listed in arbitrary order, there can also be
    333 	// duplicates. A bit's description can be empty, resulting in several
    334 	// commas in a row.
    335 	h_snprintb(
    336 	    "\177\020"
    337 	    "b\01lsb\0"
    338 	    "b\02\0"
    339 	    "b\03\0"
    340 	    "b\05NOTBOOT\0"
    341 	    "b\06FPP\0"
    342 	    "b\13SDVMA\0"
    343 	    "b\15VIDEO\0"
    344 	    "b\20LORES\0"
    345 	    "b\21FPA\0"
    346 	    "b\22DIAG\0"
    347 	    "b\16CACHE\0"
    348 	    "b\17IOCACHE\0"
    349 	    "b\22LOOPBACK\0"
    350 	    "b\04DBGCACHE\0",
    351 	    0xe86f,
    352 	    "0xe86f<lsb,,,NOTBOOT,FPP,SDVMA,VIDEO,CACHE,IOCACHE>");
    353 
    354 	// new-style format, octal, named bit-field
    355 	h_snprintb(
    356 	    "\177\010"
    357 	    "f\010\004Field\0"
    358 		"=\001one\0"
    359 		"=\002two\0",
    360 	    0x100,
    361 	    "0400<Field=01=one>");
    362 
    363 	// new-style format, decimal, named bit-field
    364 	h_snprintb(
    365 	    "\177\012"
    366 	    "f\010\004Field\0"
    367 		"=\1one\0"
    368 		"=\2two\0",
    369 	    0x100,
    370 	    "256<Field=1=one>");
    371 
    372 	// new-style format, hexadecimal, named bit-field
    373 	h_snprintb(
    374 	    "\177\020"
    375 	    "f\010\004Field\0"
    376 		"=\1one\0"
    377 		"=\2two\0",
    378 	    0x100,
    379 	    "0x100<Field=0x1=one>");
    380 
    381 	// new-style format, octal, unnamed bit-field
    382 	h_snprintb(
    383 	    "\177\010"
    384 	    "F\010\004Field\0"
    385 		":\001one\0"
    386 		":\002two\0",
    387 	    0x100,
    388 	    "0400<one>");
    389 
    390 	// new-style format, decimal, unnamed bit-field
    391 	h_snprintb(
    392 	    "\177\012"
    393 	    "F\010\004Field\0"
    394 		":\1one\0"
    395 		":\2two\0",
    396 	    0x100,
    397 	    "256<one>");
    398 
    399 	// new-style format, hexadecimal, unnamed bit-field
    400 	h_snprintb(
    401 	    "\177\020"
    402 	    "F\010\004Field\0"
    403 		":\1one\0"
    404 		":\2two\0",
    405 	    0x100,
    406 	    "0x100<one>");
    407 
    408 	// new-style format, hexadecimal, named bit-field, edge cases
    409 	//
    410 	// Field values can be listed in arbitrary order, there can also be
    411 	// duplicates. A field value's description can be empty, resulting in
    412 	// several '=' in a row. The ':' directive can emulate the '='
    413 	// directive, but not vice versa.
    414 	h_snprintb(
    415 	    "\177\20"
    416 	    "f\0\4Field\0"
    417 		"=\1one\0"
    418 		"=\1one-again\0"
    419 		"=\1\0"
    420 		"=\1\0"
    421 		":\1double\0"
    422 		":\1-colon\0"
    423 		":\1=equal\0"
    424 		"=\2TWO\0",
    425 	    1,
    426 	    "0x1<Field=0x1=one=one-again==double-colon=equal>");
    427 
    428 	// new-style format, hexadecimal, unnamed bit-field, edge cases
    429 	//
    430 	// Combining the 'F' and '=' directives generates output that doesn't
    431 	// look well-formed.
    432 	h_snprintb(
    433 	    "\177\20"
    434 	        "=\0all-zero\0"
    435 	        "=\1all-one\0"
    436 	        ":\1-continued\0"
    437 	    "F\0\4Field\0"
    438 		"=\1one\0"
    439 		"=\1one-again\0"
    440 		"=\1\0"
    441 		"=\1\0"
    442 		":\1double\0"
    443 		":\1-colon\0"
    444 		":\1=equal\0"
    445 		"=\2TWO\0",
    446 	    1,
    447 	    "0x1=all-one-continued<=one=one-again==double-colon=equal>");
    448 
    449 	// new-style format, bit-fields with fixed fallback value
    450 	//
    451 	// Only the first fallback value is used, all others are ignored.
    452 	h_snprintb(
    453 	    "\177\020"
    454 	    "f\0\4Field\0"
    455 		"=\1one\0"
    456 		"=\2two\0"
    457 		"*=other\0"
    458 		"*=yet-another\0"
    459 	    "b\1separator\0"
    460 	    "F\0\4Field\0"
    461 		":\1one\0"
    462 		":\2two\0"
    463 		"*other\0"
    464 		"*yet-another\0",
    465 	    3,
    466 	    "0x3<Field=0x3=other,separator,other>");
    467 
    468 	// new-style format, bit-fields with numeric fallback value
    469 	h_snprintb(
    470 	    "\177\020"
    471 	    "f\010\004Field\0"
    472 		"*=other(%04ju)\0"
    473 	    "b\000separator\0"
    474 	    "F\010\004Field\0"
    475 		"*other(%04ju)\0",
    476 	    0x301,
    477 	    "0x301<Field=0x3=other(0003),separator,other(0003)>");
    478 
    479 	// new-style format, bit-field with more than 8 bits
    480 	//
    481 	// The '=' and ':' directives can only match values from 0 to 255, so
    482 	// the fallback value always steps in. The complete value of the
    483 	// bit-field appears in the output, though.
    484 	h_snprintb(
    485 	    "\177\020"
    486 	    "f\010\020Field\0"
    487 		"=\377ones\0"
    488 		"*=other(%jx)\0"
    489 	    "F\010\020\0"
    490 		":\377ones\0"
    491 		"*other(%jx)\0",
    492 	    0x77ff55,
    493 	    "0x77ff55<Field=0x77ff=other(77ff),other(77ff)>");
    494 
    495 	// new-style format, bit-fields with no match
    496 	h_snprintb(
    497 	    "\177\020"
    498 	    "f\010\004Field\0"
    499 		"=\1one\0"
    500 		"=\2two\0",
    501 	    0x301,
    502 	    "0x301<Field=0x3>");
    503 	h_snprintb(
    504 	    "\177\020"
    505 	    "F\010\004\0"
    506 		":\1one\0"
    507 		":\2two\0",
    508 	    0x301,
    509 	    "0x301<>");
    510 	h_snprintb(
    511 	    "\177\020"
    512 	    "f\010\004Field\0"
    513 		"=\1one\0"
    514 		"=\2two\0"
    515 	    "b\000separator\0"
    516 	    "F\010\004\0"
    517 		":\1one\0"
    518 		":\2two\0",
    519 	    0x301,
    520 	    "0x301<Field=0x3,separator,>");
    521 
    522 	// new-style format, two separate bit-fields
    523 	h_snprintb(
    524 	    "\177\20"
    525 	    "f\0\4Field_1\0"
    526 		"=\1ONE\0"
    527 		"=\2TWO\0"
    528 	    "f\4\4Field_2\0"
    529 		"=\1ONE\0"
    530 		"=\2TWO\0",
    531 	    0x12,
    532 	    "0x12<Field_1=0x2=TWO,Field_2=0x1=ONE>");
    533 
    534 	// new-style format, mixed named and unnamed bit-fields
    535 	h_snprintb(
    536 	    "\177\20"
    537 	    "f\0\4Field_1\0"
    538 		"=\1ONE\0"
    539 		"=\2TWO\0"
    540 	    "F\x8\4\0"
    541 		"*Field_3=%jd\0"
    542 	    "f\4\4Field_2\0"
    543 		":\1:ONE\0"
    544 		":\2:TWO\0",
    545 	    0xD12,
    546 	    "0xd12<Field_1=0x2=TWO,Field_3=13,Field_2=0x1:ONE>");
    547 
    548 	// new-style format, descriptions with spaces
    549 	h_snprintb(
    550 	    "\177\020"
    551 	    "b\000has std options\0"
    552 	    "f\010\004std options\0"
    553 		"=\000no options\0"
    554 		"=\017all options\0"
    555 	    "F\020\004ext options\0"
    556 		":\000no ext options\0"
    557 		":\017all ext options\0",
    558 	    0x000001,
    559 	    "0x1<has std options,std options=0=no options,no ext options>");
    560 	h_snprintb(
    561 	    "\177\020"
    562 	    "f\010\004std options\0"
    563 		"*=other std options\0"
    564 	    "F\020\004ext\toptions\0"
    565 		"*other\text\toptions\0",
    566 	    0x000001,
    567 	    "0x1<std options=0=other std options,other\text\toptions>");
    568 
    569 	// It is possible but cumbersome to implement a reduced variant of
    570 	// rot13 using snprintb, shown here for lowercase letters only.
    571 	for (char ch = 'A'; ch <= '~'; ch++) {
    572 		char rot13 = ch >= 'a' && ch <= 'm' ? ch + 13
    573 		    : ch >= 'n' && ch <= 'z' ? ch - 13
    574 		    : '?';
    575 		char expected[8];
    576 		ATF_REQUIRE_EQ(7,
    577 		    snprintf(expected, sizeof(expected), "%#x<%c>", ch, rot13));
    578 		h_snprintb(
    579 		    "\177\020"
    580 		    "F\000\010\0"
    581 		    ":an\0:bo\0:cp\0:dq\0:er\0:fs\0:gt\0:hu\0"
    582 		    ":iv\0:jw\0:kx\0:ly\0:mz\0"
    583 		    ":na\0:ob\0:pc\0:qd\0:re\0:sf\0:tg\0:uh\0"
    584 		    ":vi\0:wj\0:xk\0:yl\0:zm\0"
    585 		    // If snprintf accepted "%jc", it would be possible to
    586 		    // echo the non-alphabetic characters instead of a
    587 		    // catchall question mark.
    588 		    "*?\0",
    589 		    ch,
    590 		    expected);
    591 	}
    592 
    593 	// new-style format, small buffer
    594 #if 0
    595 	// FIXME: Calling snprintb with buffer size 0 invokes undefined
    596 	// behavior due to out-of-bounds 'bp' pointer.
    597 	h_snprintb_len(
    598 	    0, "\177\020", 0,
    599 	    1, "");
    600 #endif
    601 	h_snprintb_len(
    602 	    1, "\177\020", 0,
    603 	    1, "");
    604 	h_snprintb_len(
    605 	    2, "\177\020", 0,
    606 	    1, "0");
    607 	h_snprintb_len(
    608 	    3, "\177\020", 0,
    609 	    1, "0");
    610 	h_snprintb_len(
    611 	    3, "\177\020", 7,
    612 	    3, "0x");
    613 	h_snprintb_len(
    614 	    4, "\177\020", 7,
    615 	    3, "0x7");
    616 	h_snprintb_len(
    617 	    7, "\177\020b\000lsb\0", 7,
    618 	    8, "0x7<ls");
    619 	h_snprintb_len(
    620 	    8, "\177\020b\000lsb\0", 7,
    621 	    8, "0x7<lsb");
    622 	h_snprintb_len(
    623 	    9, "\177\020b\000lsb\0", 7,
    624 	    8, "0x7<lsb>");
    625 	h_snprintb_len(
    626 	    9, "\177\020b\000one\0b\001two\0", 7,
    627 	    12, "0x7<one,");
    628 	h_snprintb_len(
    629 	    10, "\177\020b\000one\0b\001two\0", 7,
    630 	    12, "0x7<one,t");
    631 	h_snprintb_len(
    632 	    12, "\177\020b\000one\0b\001two\0", 7,
    633 	    12, "0x7<one,two");
    634 	h_snprintb_len(
    635 	    13, "\177\020b\000one\0b\001two\0", 7,
    636 	    12, "0x7<one,two>");
    637 
    638 }
    639 
    640 static void
    641 h_snprintb_m_loc(const char *file, size_t line,
    642     size_t bufsize, const char *fmt, size_t fmtlen, uint64_t val, size_t max,
    643     size_t exp_rv, const char *res, size_t reslen)
    644 {
    645 	char buf[1024];
    646 
    647 	ATF_REQUIRE(bufsize > 1);
    648 	ATF_REQUIRE(bufsize <= sizeof(buf));
    649 	ATF_REQUIRE(reslen <= sizeof(buf));
    650 
    651 	memset(buf, 'Z', sizeof(buf));
    652 	int rv = snprintb_m(buf, bufsize, fmt, val, max);
    653 	ATF_REQUIRE_MSG(rv >= 0,
    654 	    "formatting %jx with '%s' returns error %d",
    655 	    (uintmax_t)val, vis_arr(fmt, fmtlen), rv);
    656 
    657 	size_t total = rv;
    658 	ATF_CHECK_MSG(
    659 	    total == exp_rv && memcmp(buf, res, reslen) == 0,
    660 	    "failed:\n"
    661 	    "\ttest case: %s:%zu\n"
    662 	    "\tformat: %s\n"
    663 	    "\tvalue: %#jx\n"
    664 	    "\tmax: %zu\n"
    665 	    "\twant: %zu bytes %s\n"
    666 	    "\thave: %zu bytes %s\n",
    667 	    file, line,
    668 	    vis_arr(fmt, fmtlen),
    669 	    (uintmax_t)val,
    670 	    max,
    671 	    exp_rv, vis_arr(res, reslen),
    672 	    total, vis_arr(buf, reslen));
    673 	check_unmodified_loc(file, line, buf, reslen, sizeof(buf));
    674 }
    675 
    676 #define	h_snprintb_m_len(bufsize, fmt, val, line_max, exp_rv, res)	\
    677 	h_snprintb_m_loc(__FILE__, __LINE__,				\
    678 	    bufsize, fmt, sizeof(fmt) - 1, val, line_max,		\
    679 	    exp_rv, res, sizeof(res))
    680 #define	h_snprintb_m(fmt, val, max, res)				\
    681 	h_snprintb_m_len(1024, fmt, val, max, sizeof(res) - 1, res)
    682 
    683 ATF_TC(snprintb_m);
    684 ATF_TC_HEAD(snprintb_m, tc)
    685 {
    686 	atf_tc_set_md_var(tc, "descr", "Checks snprintb_m(3)");
    687 }
    688 ATF_TC_BODY(snprintb_m, tc)
    689 {
    690 	// old-style format, small maximum line length
    691 	h_snprintb_m_len(
    692 	    68,
    693 	    "\020"
    694 	    "\001bit1"
    695 	    "\002bit2"
    696 	    "\003bit3",
    697 	    0xffff,
    698 	    6,
    699 	    143,
    700 	    "0xffff>\0"
    701 	    "0xffff<>\0"
    702 	    "0xffffb>\0"
    703 	    "0xffffi>\0"
    704 	    "0xfffft>\0"
    705 	    "0xffff1>\0"
    706 	    "0xffff<>\0"
    707 	    "0xff\0"
    708 	);
    709 
    710 	// new-style format, small maximum line length
    711 	h_snprintb_m_len(
    712 	    68,
    713 	    "\177\020"
    714 	    "b\000bit1\0"
    715 	    "b\001bit2\0"
    716 	    "b\002bit3\0",
    717 	    0xffff,
    718 	    6,
    719 	    143,
    720 	    "0xffff>\0"
    721 	    "0xffff<>\0"
    722 	    "0xffffb>\0"
    723 	    "0xffffi>\0"
    724 	    "0xfffft>\0"
    725 	    "0xffff1>\0"
    726 	    "0xffff<>\0"
    727 	    "0xff\0"
    728 	);
    729 
    730 	// new-style format, buffer too small for number
    731 	h_snprintb_m_len(
    732 	    2,
    733 	    "\177\020",
    734 	    0,
    735 	    64,
    736 	    2,
    737 	    "\0"
    738 	);
    739 
    740 	// new-style format, buffer too small for '<'
    741 	h_snprintb_m_len(
    742 	    6,
    743 	    "\177\020"
    744 	    "b\000lsb\0",
    745 	    0xff,
    746 	    64,
    747 	    10,
    748 	    "0xff\0"
    749 	);
    750 
    751 	// new-style format, buffer too small for description
    752 	h_snprintb_m_len(
    753 	    7,
    754 	    "\177\020"
    755 	    "b\000lsb\0",
    756 	    0xff,
    757 	    64,
    758 	    10,
    759 	    "0xff<\0"
    760 	);
    761 
    762 	// new-style format, buffer too small for complete description
    763 	h_snprintb_m_len(
    764 	    9,
    765 	    "\177\020"
    766 	    "b\000lsb\0",
    767 	    0xff,
    768 	    64,
    769 	    10,
    770 	    "0xff<ls\0"
    771 	);
    772 
    773 	// new-style format, buffer too small for '>'
    774 	h_snprintb_m_len(
    775 	    10,
    776 	    "\177\020"
    777 	    "b\000lsb\0",
    778 	    0xff,
    779 	    64,
    780 	    10,
    781 	    "0xff<lsb\0"
    782 	);
    783 
    784 	// new-style format, buffer too small for second line
    785 	h_snprintb_m_len(
    786 	    11,
    787 	    "\177\020"
    788 	    "b\000lsb\0"
    789 	    "b\001two\0",
    790 	    0xff,
    791 	    11,
    792 	    20,
    793 	    "0xff<lsb>\0"
    794 	);
    795 
    796 	// new-style format, buffer too small for number in line 2
    797 	h_snprintb_m_len(
    798 	    12,
    799 	    "\177\020"
    800 	    "b\000lsb\0"
    801 	    "b\001two\0",
    802 	    0xff,
    803 	    11,
    804 	    20,
    805 	    "0xff<lsb>\0"
    806 	    "\0"
    807 	);
    808 
    809 	// new-style format, buffer too small for complete number in line 2
    810 	h_snprintb_m_len(
    811 	    15,
    812 	    "\177\020"
    813 	    "b\000lsb\0"
    814 	    "b\001two\0",
    815 	    0xff,
    816 	    11,
    817 	    20,
    818 	    "0xff<lsb>\0"
    819 	    "0xf\0"		// XXX: incomplete number may be misleading
    820 	);
    821 
    822 	// new-style format, buffer too small for '<' in line 2
    823 	h_snprintb_m_len(
    824 	    16,
    825 	    "\177\020"
    826 	    "b\000lsb\0"
    827 	    "b\001two\0",
    828 	    0xff,
    829 	    11,
    830 	    20,
    831 	    "0xff<lsb>\0"
    832 	    "0xff\0"
    833 	);
    834 
    835 	// new-style format, buffer too small for description in line 2
    836 	h_snprintb_m_len(
    837 	    17,
    838 	    "\177\020"
    839 	    "b\000lsb\0"
    840 	    "b\001two\0",
    841 	    0xff,
    842 	    11,
    843 	    20,
    844 	    "0xff<lsb>\0"
    845 	    "0xff<\0"
    846 	);
    847 
    848 	// new-style format, line too small for unmatched field value
    849 	h_snprintb_m_len(
    850 	    30,
    851 	    "\177\020"
    852 	    "f\000\004bits\0"
    853 	        ":\000other\0",
    854 	    0xff,
    855 	    11,
    856 	    22,
    857 	    "0xff<bits=0xf>\0"	// XXX: line too long (14 > 11)
    858 	    "0xff#>\0"		// XXX: why '#'? unbalanced '<>'
    859 	);
    860 
    861 	// new-style format, line too small for field value
    862 	h_snprintb_m_len(
    863 	    30,
    864 	    "\177\020"
    865 	    "f\000\004bits\0"
    866 		":\017other\0",
    867 	    0xff,
    868 	    11,
    869 	    27,
    870 	    "0xff<bits=0xf>\0"	// XXX: line too long (14 > 11)
    871 	    "0xff#other>\0"	// XXX: unbalanced '<>'
    872 	);
    873 
    874 	// new-style format, buffer too small for fallback
    875 	h_snprintb_m_len(
    876 	    20,
    877 	    "\177\020"
    878 	    "f\000\004bits\0"
    879 		"*=fallback\0"
    880 	    "b\0024\0",
    881 	    0xff,
    882 	    64,
    883 	    26,
    884 	    "0xff<bits=0xf=fall\0"
    885 	);
    886 
    887 	h_snprintb_m(
    888 	    "\177\020"
    889 	    "b\0LSB\0"
    890 	    "b\1_BITONE\0"
    891 	    "f\4\4NIBBLE2\0"
    892 	    "f\x10\4BURST\0"
    893 		"=\04FOUR\0"
    894 		"=\17FIFTEEN\0"
    895 	    "b\x1fMSB\0",
    896 	    0x800f0701,
    897 	    33,
    898 	    "0x800f0701<LSB,NIBBLE2=0>\0"
    899 	    "0x800f0701<BURST=0xf=FIFTEEN,MSB>\0"
    900 	);
    901 
    902 	h_snprintb_m(
    903 	    "\177\020"
    904 	    "b\0LSB\0"
    905 	    "b\1_BITONE\0"
    906 	    "f\4\4NIBBLE2\0"
    907 	    "f\x10\4BURST\0"
    908 		"=\04FOUR\0"
    909 		"=\17FIFTEEN\0"
    910 	    "b\x1fMSB\0",
    911 	    0x800f0701,
    912 	    32,
    913 	    "0x800f0701<LSB,NIBBLE2=0>\0"
    914 	    "0x800f0701<BURST=0xf=FIFTEEN>\0"
    915 	    "0x800f0701<MSB>\0");
    916 }
    917 
    918 ATF_TP_ADD_TCS(tp)
    919 {
    920 
    921 	ATF_TP_ADD_TC(tp, snprintb);
    922 	ATF_TP_ADD_TC(tp, snprintb_m);
    923 
    924 	return atf_no_error();
    925 }
    926