Home | History | Annotate | Line # | Download | only in libutil
t_snprintb.c revision 1.18
      1 /* $NetBSD: t_snprintb.c,v 1.18 2024/02/16 18:13:47 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.18 2024/02/16 18:13:47 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-field with 8 bits
    496 	h_snprintb(
    497 	    "\177\020"
    498 	    "F\010\010\0"
    499 		":\377all\0"
    500 		"*other\0",
    501 	    0xff00,
    502 	    "0xff00<all>");
    503 
    504 	// new-style format, bit-fields with no match
    505 	h_snprintb(
    506 	    "\177\020"
    507 	    "f\010\004Field\0"
    508 		"=\1one\0"
    509 		"=\2two\0",
    510 	    0x301,
    511 	    "0x301<Field=0x3>");
    512 	h_snprintb(
    513 	    "\177\020"
    514 	    "F\010\004\0"
    515 		":\1one\0"
    516 		":\2two\0",
    517 	    0x301,
    518 	    "0x301<>");
    519 	h_snprintb(
    520 	    "\177\020"
    521 	    "f\010\004Field\0"
    522 		"=\1one\0"
    523 		"=\2two\0"
    524 	    "b\000separator\0"
    525 	    "F\010\004\0"
    526 		":\1one\0"
    527 		":\2two\0",
    528 	    0x301,
    529 	    "0x301<Field=0x3,separator,>");
    530 
    531 	// new-style format, two separate bit-fields
    532 	h_snprintb(
    533 	    "\177\20"
    534 	    "f\0\4Field_1\0"
    535 		"=\1ONE\0"
    536 		"=\2TWO\0"
    537 	    "f\4\4Field_2\0"
    538 		"=\1ONE\0"
    539 		"=\2TWO\0",
    540 	    0x12,
    541 	    "0x12<Field_1=0x2=TWO,Field_2=0x1=ONE>");
    542 
    543 	// new-style format, mixed named and unnamed bit-fields
    544 	h_snprintb(
    545 	    "\177\20"
    546 	    "f\0\4Field_1\0"
    547 		"=\1ONE\0"
    548 		"=\2TWO\0"
    549 	    "F\x8\4\0"
    550 		"*Field_3=%jd\0"
    551 	    "f\4\4Field_2\0"
    552 		":\1:ONE\0"
    553 		":\2:TWO\0",
    554 	    0xD12,
    555 	    "0xd12<Field_1=0x2=TWO,Field_3=13,Field_2=0x1:ONE>");
    556 
    557 	// new-style format, descriptions with spaces
    558 	h_snprintb(
    559 	    "\177\020"
    560 	    "b\000has std options\0"
    561 	    "f\010\004std options\0"
    562 		"=\000no options\0"
    563 		"=\017all options\0"
    564 	    "F\020\004ext options\0"
    565 		":\000no ext options\0"
    566 		":\017all ext options\0",
    567 	    0x000001,
    568 	    "0x1<has std options,std options=0=no options,no ext options>");
    569 	h_snprintb(
    570 	    "\177\020"
    571 	    "f\010\004std options\0"
    572 		"*=other std options\0"
    573 	    "F\020\004ext\toptions\0"
    574 		"*other\text\toptions\0",
    575 	    0x000001,
    576 	    "0x1<std options=0=other std options,other\text\toptions>");
    577 
    578 	// It is possible but cumbersome to implement a reduced variant of
    579 	// rot13 using snprintb, shown here for lowercase letters only.
    580 	for (char ch = 'A'; ch <= '~'; ch++) {
    581 		char rot13 = ch >= 'a' && ch <= 'm' ? ch + 13
    582 		    : ch >= 'n' && ch <= 'z' ? ch - 13
    583 		    : '?';
    584 		char expected[8];
    585 		ATF_REQUIRE_EQ(7,
    586 		    snprintf(expected, sizeof(expected), "%#x<%c>", ch, rot13));
    587 		h_snprintb(
    588 		    "\177\020"
    589 		    "F\000\010\0"
    590 		    ":an\0:bo\0:cp\0:dq\0:er\0:fs\0:gt\0:hu\0"
    591 		    ":iv\0:jw\0:kx\0:ly\0:mz\0"
    592 		    ":na\0:ob\0:pc\0:qd\0:re\0:sf\0:tg\0:uh\0"
    593 		    ":vi\0:wj\0:xk\0:yl\0:zm\0"
    594 		    // If snprintf accepted "%jc", it would be possible to
    595 		    // echo the non-alphabetic characters instead of a
    596 		    // catchall question mark.
    597 		    "*?\0",
    598 		    ch,
    599 		    expected);
    600 	}
    601 
    602 	// new-style format, small buffer
    603 #if 0
    604 	// FIXME: Calling snprintb with buffer size 0 invokes undefined
    605 	// behavior due to out-of-bounds 'bp' pointer.
    606 	h_snprintb_len(
    607 	    0, "\177\020", 0,
    608 	    1, "");
    609 #endif
    610 	h_snprintb_len(
    611 	    1, "\177\020", 0,
    612 	    1, "");
    613 	h_snprintb_len(
    614 	    2, "\177\020", 0,
    615 	    1, "0");
    616 	h_snprintb_len(
    617 	    3, "\177\020", 0,
    618 	    1, "0");
    619 	h_snprintb_len(
    620 	    3, "\177\020", 7,
    621 	    3, "0x");
    622 	h_snprintb_len(
    623 	    4, "\177\020", 7,
    624 	    3, "0x7");
    625 	h_snprintb_len(
    626 	    7, "\177\020b\000lsb\0", 7,
    627 	    8, "0x7<ls");
    628 	h_snprintb_len(
    629 	    8, "\177\020b\000lsb\0", 7,
    630 	    8, "0x7<lsb");
    631 	h_snprintb_len(
    632 	    9, "\177\020b\000lsb\0", 7,
    633 	    8, "0x7<lsb>");
    634 	h_snprintb_len(
    635 	    9, "\177\020b\000one\0b\001two\0", 7,
    636 	    12, "0x7<one,");
    637 	h_snprintb_len(
    638 	    10, "\177\020b\000one\0b\001two\0", 7,
    639 	    12, "0x7<one,t");
    640 	h_snprintb_len(
    641 	    12, "\177\020b\000one\0b\001two\0", 7,
    642 	    12, "0x7<one,two");
    643 	h_snprintb_len(
    644 	    13, "\177\020b\000one\0b\001two\0", 7,
    645 	    12, "0x7<one,two>");
    646 
    647 }
    648 
    649 static void
    650 h_snprintb_m_loc(const char *file, size_t line,
    651     size_t bufsize, const char *fmt, size_t fmtlen, uint64_t val, size_t max,
    652     size_t exp_rv, const char *res, size_t reslen)
    653 {
    654 	char buf[1024];
    655 
    656 	ATF_REQUIRE(bufsize > 1);
    657 	ATF_REQUIRE(bufsize <= sizeof(buf));
    658 	ATF_REQUIRE(reslen <= sizeof(buf));
    659 
    660 	memset(buf, 'Z', sizeof(buf));
    661 	int rv = snprintb_m(buf, bufsize, fmt, val, max);
    662 	ATF_REQUIRE_MSG(rv >= 0,
    663 	    "formatting %jx with '%s' returns error %d",
    664 	    (uintmax_t)val, vis_arr(fmt, fmtlen), rv);
    665 
    666 	size_t total = rv;
    667 	ATF_CHECK_MSG(
    668 	    total == exp_rv && memcmp(buf, res, reslen) == 0,
    669 	    "failed:\n"
    670 	    "\ttest case: %s:%zu\n"
    671 	    "\tformat: %s\n"
    672 	    "\tvalue: %#jx\n"
    673 	    "\tmax: %zu\n"
    674 	    "\twant: %zu bytes %s\n"
    675 	    "\thave: %zu bytes %s\n",
    676 	    file, line,
    677 	    vis_arr(fmt, fmtlen),
    678 	    (uintmax_t)val,
    679 	    max,
    680 	    exp_rv, vis_arr(res, reslen),
    681 	    total, vis_arr(buf, reslen));
    682 	check_unmodified_loc(file, line, buf, reslen, sizeof(buf));
    683 }
    684 
    685 #define	h_snprintb_m_len(bufsize, fmt, val, line_max, exp_rv, res)	\
    686 	h_snprintb_m_loc(__FILE__, __LINE__,				\
    687 	    bufsize, fmt, sizeof(fmt) - 1, val, line_max,		\
    688 	    exp_rv, res, sizeof(res))
    689 #define	h_snprintb_m(fmt, val, max, res)				\
    690 	h_snprintb_m_len(1024, fmt, val, max, sizeof(res) - 1, res)
    691 
    692 ATF_TC(snprintb_m);
    693 ATF_TC_HEAD(snprintb_m, tc)
    694 {
    695 	atf_tc_set_md_var(tc, "descr", "Checks snprintb_m(3)");
    696 }
    697 ATF_TC_BODY(snprintb_m, tc)
    698 {
    699 	// old-style format, small maximum line length
    700 	h_snprintb_m_len(
    701 	    68,
    702 	    "\020"
    703 	    "\001bit1"
    704 	    "\002bit2"
    705 	    "\003bit3",
    706 	    0xffff,
    707 	    6,
    708 	    143,
    709 	    "0xffff>\0"
    710 	    "0xffff<>\0"
    711 	    "0xffffb>\0"
    712 	    "0xffffi>\0"
    713 	    "0xfffft>\0"
    714 	    "0xffff1>\0"
    715 	    "0xffff<>\0"
    716 	    "0xff\0"
    717 	);
    718 
    719 	// new-style format, small maximum line length
    720 	h_snprintb_m_len(
    721 	    68,
    722 	    "\177\020"
    723 	    "b\000bit1\0"
    724 	    "b\001bit2\0"
    725 	    "b\002bit3\0",
    726 	    0xffff,
    727 	    6,
    728 	    143,
    729 	    "0xffff>\0"
    730 	    "0xffff<>\0"
    731 	    "0xffffb>\0"
    732 	    "0xffffi>\0"
    733 	    "0xfffft>\0"
    734 	    "0xffff1>\0"
    735 	    "0xffff<>\0"
    736 	    "0xff\0"
    737 	);
    738 
    739 	// new-style format, buffer too small for number
    740 	h_snprintb_m_len(
    741 	    2,
    742 	    "\177\020",
    743 	    0,
    744 	    64,
    745 	    2,
    746 	    "\0"
    747 	);
    748 
    749 	// new-style format, buffer too small for '<'
    750 	h_snprintb_m_len(
    751 	    6,
    752 	    "\177\020"
    753 	    "b\000lsb\0",
    754 	    0xff,
    755 	    64,
    756 	    10,
    757 	    "0xff\0"
    758 	);
    759 
    760 	// new-style format, buffer too small for description
    761 	h_snprintb_m_len(
    762 	    7,
    763 	    "\177\020"
    764 	    "b\000lsb\0",
    765 	    0xff,
    766 	    64,
    767 	    10,
    768 	    "0xff<\0"
    769 	);
    770 
    771 	// new-style format, buffer too small for complete description
    772 	h_snprintb_m_len(
    773 	    9,
    774 	    "\177\020"
    775 	    "b\000lsb\0",
    776 	    0xff,
    777 	    64,
    778 	    10,
    779 	    "0xff<ls\0"
    780 	);
    781 
    782 	// new-style format, buffer too small for '>'
    783 	h_snprintb_m_len(
    784 	    10,
    785 	    "\177\020"
    786 	    "b\000lsb\0",
    787 	    0xff,
    788 	    64,
    789 	    10,
    790 	    "0xff<lsb\0"
    791 	);
    792 
    793 	// new-style format, buffer too small for second line
    794 	h_snprintb_m_len(
    795 	    11,
    796 	    "\177\020"
    797 	    "b\000lsb\0"
    798 	    "b\001two\0",
    799 	    0xff,
    800 	    11,
    801 	    20,
    802 	    "0xff<lsb>\0"
    803 	);
    804 
    805 	// new-style format, buffer too small for number in line 2
    806 	h_snprintb_m_len(
    807 	    12,
    808 	    "\177\020"
    809 	    "b\000lsb\0"
    810 	    "b\001two\0",
    811 	    0xff,
    812 	    11,
    813 	    20,
    814 	    "0xff<lsb>\0"
    815 	    "\0"
    816 	);
    817 
    818 	// new-style format, buffer too small for complete number in line 2
    819 	h_snprintb_m_len(
    820 	    15,
    821 	    "\177\020"
    822 	    "b\000lsb\0"
    823 	    "b\001two\0",
    824 	    0xff,
    825 	    11,
    826 	    20,
    827 	    "0xff<lsb>\0"
    828 	    "0xf\0"		// XXX: incomplete number may be misleading
    829 	);
    830 
    831 	// new-style format, buffer too small for '<' in line 2
    832 	h_snprintb_m_len(
    833 	    16,
    834 	    "\177\020"
    835 	    "b\000lsb\0"
    836 	    "b\001two\0",
    837 	    0xff,
    838 	    11,
    839 	    20,
    840 	    "0xff<lsb>\0"
    841 	    "0xff\0"
    842 	);
    843 
    844 	// new-style format, buffer too small for description in line 2
    845 	h_snprintb_m_len(
    846 	    17,
    847 	    "\177\020"
    848 	    "b\000lsb\0"
    849 	    "b\001two\0",
    850 	    0xff,
    851 	    11,
    852 	    20,
    853 	    "0xff<lsb>\0"
    854 	    "0xff<\0"
    855 	);
    856 
    857 	// new-style format, line too small for unmatched field value
    858 	h_snprintb_m_len(
    859 	    30,
    860 	    "\177\020"
    861 	    "f\000\004bits\0"
    862 	        ":\000other\0",
    863 	    0xff,
    864 	    11,
    865 	    22,
    866 	    "0xff<bits=0xf>\0"	// XXX: line too long (14 > 11)
    867 	    "0xff#>\0"		// XXX: why '#'? unbalanced '<>'
    868 	);
    869 
    870 	// new-style format, line too small for field value
    871 	h_snprintb_m_len(
    872 	    30,
    873 	    "\177\020"
    874 	    "f\000\004bits\0"
    875 		":\017other\0",
    876 	    0xff,
    877 	    11,
    878 	    27,
    879 	    "0xff<bits=0xf>\0"	// XXX: line too long (14 > 11)
    880 	    "0xff#other>\0"	// XXX: unbalanced '<>'
    881 	);
    882 
    883 	// new-style format, buffer too small for fallback
    884 	h_snprintb_m_len(
    885 	    20,
    886 	    "\177\020"
    887 	    "f\000\004bits\0"
    888 		"*=fallback\0"
    889 	    "b\0024\0",
    890 	    0xff,
    891 	    64,
    892 	    26,
    893 	    "0xff<bits=0xf=fall\0"
    894 	);
    895 
    896 	h_snprintb_m(
    897 	    "\177\020"
    898 	    "b\0LSB\0"
    899 	    "b\1_BITONE\0"
    900 	    "f\4\4NIBBLE2\0"
    901 	    "f\x10\4BURST\0"
    902 		"=\04FOUR\0"
    903 		"=\17FIFTEEN\0"
    904 	    "b\x1fMSB\0",
    905 	    0x800f0701,
    906 	    33,
    907 	    "0x800f0701<LSB,NIBBLE2=0>\0"
    908 	    "0x800f0701<BURST=0xf=FIFTEEN,MSB>\0"
    909 	);
    910 
    911 	h_snprintb_m(
    912 	    "\177\020"
    913 	    "b\0LSB\0"
    914 	    "b\1_BITONE\0"
    915 	    "f\4\4NIBBLE2\0"
    916 	    "f\x10\4BURST\0"
    917 		"=\04FOUR\0"
    918 		"=\17FIFTEEN\0"
    919 	    "b\x1fMSB\0",
    920 	    0x800f0701,
    921 	    32,
    922 	    "0x800f0701<LSB,NIBBLE2=0>\0"
    923 	    "0x800f0701<BURST=0xf=FIFTEEN>\0"
    924 	    "0x800f0701<MSB>\0");
    925 }
    926 
    927 ATF_TP_ADD_TCS(tp)
    928 {
    929 
    930 	ATF_TP_ADD_TC(tp, snprintb);
    931 	ATF_TP_ADD_TC(tp, snprintb_m);
    932 
    933 	return atf_no_error();
    934 }
    935