t_snprintb.c revision 1.12 1 /* $NetBSD: t_snprintb.c,v 1.12 2024/01/27 21:42:29 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.12 2024/01/27 21:42:29 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 arrlen)
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, arrlen,
52 VIS_WHITE | VIS_OCTAL);
53 ATF_REQUIRE_MSG(rv >= 0, "strnvisx failed for length %zu", arrlen);
54 return buf[i];
55 }
56
57 static void
58 h_snprintb_loc(const char *file, size_t line,
59 size_t bufsize, const char *fmt, size_t fmtlen, uint64_t val,
60 int exp_rv, const char *res, size_t reslen)
61 {
62 char buf[1024];
63
64 // Calling snprintb with bufsize == 0 invokes undefined
65 // behavior due to out-of-range 'bp'.
66 ATF_REQUIRE(bufsize > 0);
67 ATF_REQUIRE(bufsize <= sizeof(buf));
68
69 memset(buf, 'Z', sizeof(buf));
70 int rv = snprintb(buf, bufsize, fmt, val);
71 ATF_REQUIRE(rv >= 0);
72 size_t rlen = rv;
73
74 ATF_CHECK_MSG(
75 rv == exp_rv && memcmp(buf, res, reslen) == 0
76 && buf[rlen < bufsize ? rlen : bufsize - 1] == '\0',
77 "failed:\n"
78 "\ttest case: %s:%zu\n"
79 "\tformat: %s\n"
80 "\tvalue: %#jx\n"
81 "\twant: %d bytes %s\n"
82 "\thave: %d bytes %s\n",
83 file, line,
84 vis_arr(fmt, fmtlen),
85 (uintmax_t)val,
86 exp_rv, vis_arr(res, reslen),
87 rv, vis_arr(buf, reslen));
88 }
89
90 #define h_snprintb_len(bufsize, fmt, val, exp_rv, res) \
91 h_snprintb_loc(__FILE__, __LINE__, \
92 bufsize, fmt, sizeof(fmt) - 1, val, \
93 exp_rv, res, sizeof(res) - 1)
94 #define h_snprintb(fmt, val, res) \
95 h_snprintb_len(1024, fmt, val, sizeof(res) - 1, res)
96
97 static void
98 h_snprintb_error_loc(const char *file, size_t line,
99 const char *fmt, size_t fmtlen)
100 {
101 char buf[1024];
102
103 memset(buf, 'Z', sizeof(buf));
104 int rv = snprintb(buf, sizeof(buf), fmt, 0);
105 size_t buflen = rv;
106
107 ATF_REQUIRE(rv >= -1);
108 ATF_CHECK_MSG(rv == -1,
109 "expected error but got success:\n"
110 "\ttest case: %s:%zu\n"
111 "\tformat: %s\n"
112 "\tresult: %zu bytes %s\n",
113 file, line,
114 vis_arr(fmt, fmtlen),
115 buflen, vis_arr(buf, buflen));
116 }
117
118 #define h_snprintb_error(fmt) \
119 h_snprintb_error_loc(__FILE__, __LINE__, fmt, sizeof(fmt) - 1)
120
121 ATF_TC(snprintb);
122 ATF_TC_HEAD(snprintb, tc)
123 {
124 atf_tc_set_md_var(tc, "descr", "Checks snprintb(3)");
125 }
126 ATF_TC_BODY(snprintb, tc)
127 {
128
129 // old-style format, octal
130 h_snprintb(
131 "\010"
132 "\002BITTWO"
133 "\001BITONE",
134 3,
135 "03<BITTWO,BITONE>");
136
137 // old-style format, decimal
138 h_snprintb(
139 "\012"
140 "\0011"
141 "\0119"
142 "\02117"
143 "\04032",
144 0xffffffff,
145 "4294967295<1,9,17,32>");
146
147 // old-style format, hexadecimal, from msb downto lsb
148 h_snprintb(
149 "\020"
150 "\04032"
151 "\03024"
152 "\02016"
153 "\0108"
154 "\0077"
155 "\0066"
156 "\0055"
157 "\0044"
158 "\0033"
159 "\0022"
160 "\0011"
161 // The old-style format supports only 32 bits, interpreting the
162 // \041 as part of the text belonging to bit 1.
163 "\04133",
164 0x0000ffff00ff0f35,
165 "0xffff00ff0f35<24,6,5,3,1!33>");
166
167 // old-style format, hexadecimal, from lsb to msb
168 h_snprintb(
169 "\020"
170 "\0011"
171 "\0022"
172 "\0033"
173 "\0044"
174 "\0055"
175 "\0066"
176 "\0077"
177 "\0108"
178 "\02016"
179 "\03024"
180 "\04032"
181 // The old-style format supports only 32 bits, interpreting the
182 // \041 as part of the text belonging to bit 32.
183 "\04133",
184 0xffff0000ff00f0ca,
185 "0xffff0000ff00f0ca<2,4,7,8,16,32!33>");
186
187 // The bits can be listed in arbitrary order, there can also be
188 // duplicates. A bit's description can be empty, resulting in several
189 // commas in a row.
190 h_snprintb(
191 "\020"
192 "\001lsb"
193 "\040msb"
194 "\011"
195 "\012"
196 "\002above-lsb"
197 "\037below-msb"
198 "\001lsb-again"
199 "\040msb-again",
200 0xc0000303,
201 "0xc0000303<lsb,msb,,,above-lsb,below-msb,lsb-again,msb-again>");
202
203 #if 0
204 // If the first bit number is 33 or more, snprintb invokes undefined
205 // behavior due to an out-of-bounds bit shift, though undetected by
206 // -ftrapv. Later bit numbers are properly checked.
207 h_snprintb(
208 "\020"
209 "\177undefined_behavior"
210 "\001lsb",
211 0xffffffffffffffff,
212 "0xffffffffffffffff<?>");
213 #endif
214
215 // old-style format, invalid number base 0
216 h_snprintb_error(
217 "");
218
219 // old-style format, invalid number base 2
220 h_snprintb_error(
221 "\002");
222
223 // old-style format, invalid number base 255 or -1
224 h_snprintb_error(
225 "\377");
226
227 // old-style format, small buffer
228 #if 0
229 // FIXME: Calling snprintb with buffer size 0 invokes undefined
230 // behavior due to out-of-bounds 'bp' pointer.
231 h_snprintb_len(
232 0, "\020", 0,
233 1, "ZZZ");
234 #endif
235 h_snprintb_len(
236 1, "\020", 0,
237 1, "\0ZZZ");
238 h_snprintb_len(
239 2, "\020", 0,
240 1, "0\0ZZZ");
241 h_snprintb_len(
242 3, "\020", 0,
243 1, "0\0ZZZ");
244 h_snprintb_len(
245 3, "\020", 7,
246 3, "0x\0ZZZ");
247 h_snprintb_len(
248 4, "\020", 7,
249 3, "0x7\0ZZZ");
250 h_snprintb_len(
251 7, "\020\001lsb", 7,
252 8, "0x7<ls\0ZZZ");
253 h_snprintb_len(
254 8, "\020\001lsb", 7,
255 8, "0x7<lsb\0ZZZ");
256 h_snprintb_len(
257 9, "\020\001lsb", 7,
258 8, "0x7<lsb>\0ZZZ");
259 h_snprintb_len(
260 9, "\020\001one\002two", 7,
261 12, "0x7<one,\0ZZZ");
262 h_snprintb_len(
263 10, "\020\001one\002two", 7,
264 12, "0x7<one,t\0ZZZ");
265 h_snprintb_len(
266 12, "\020\001one\002two", 7,
267 12, "0x7<one,two\0ZZZ");
268 h_snprintb_len(
269 13, "\020\001one\002two", 7,
270 12, "0x7<one,two>\0ZZZ");
271
272 // new-style format, single bits, octal
273 h_snprintb(
274 "\177\010"
275 "b\000bit0\0"
276 "b\037bit31\0"
277 "b\040bit32\0"
278 "b\077bit63\0",
279 0xf000000ff000000f,
280 "01700000000776000000017<bit0,bit31,bit32,bit63>");
281
282 // new-style format, single bits, decimal
283 h_snprintb(
284 "\177\012"
285 "b\000bit0\0"
286 "b\037bit31\0"
287 "b\040bit32\0"
288 "b\077bit63\0",
289 0xf000000ff000000f,
290 "17293822637553745935<bit0,bit31,bit32,bit63>");
291
292 // new-style format, single bits, hexadecimal
293 h_snprintb(
294 "\177\020"
295 "b\000bit0\0"
296 "b\037bit31\0"
297 "b\040bit32\0"
298 "b\077bit63\0",
299 0xf000000ff000000f,
300 "0xf000000ff000000f<bit0,bit31,bit32,bit63>");
301
302 // new-style format, invalid number base 2
303 h_snprintb_error(
304 "\177\002");
305
306 // new-style format, invalid number base 255 or -1
307 h_snprintb_error(
308 "\177\377");
309
310 // new-style format, single bits, edge cases
311 //
312 // The bits can be listed in arbitrary order, there can also be
313 // duplicates. A bit's description can be empty, resulting in several
314 // commas in a row.
315 h_snprintb(
316 "\177\020"
317 "b\01lsb\0"
318 "b\02\0"
319 "b\03\0"
320 "b\05NOTBOOT\0"
321 "b\06FPP\0"
322 "b\13SDVMA\0"
323 "b\15VIDEO\0"
324 "b\20LORES\0"
325 "b\21FPA\0"
326 "b\22DIAG\0"
327 "b\16CACHE\0"
328 "b\17IOCACHE\0"
329 "b\22LOOPBACK\0"
330 "b\04DBGCACHE\0",
331 0xe86f,
332 "0xe86f<lsb,,,NOTBOOT,FPP,SDVMA,VIDEO,CACHE,IOCACHE>");
333
334 // new-style format, octal, named bit-field
335 h_snprintb(
336 "\177\010"
337 "f\010\004Field\0"
338 "=\001one\0"
339 "=\002two\0",
340 0x100,
341 "0400<Field=01=one>");
342
343 // new-style format, decimal, named bit-field
344 h_snprintb(
345 "\177\012"
346 "f\010\004Field\0"
347 "=\1one\0"
348 "=\2two\0",
349 0x100,
350 "256<Field=1=one>");
351
352 // new-style format, hexadecimal, named bit-field
353 h_snprintb(
354 "\177\020"
355 "f\010\004Field\0"
356 "=\1one\0"
357 "=\2two\0",
358 0x100,
359 "0x100<Field=0x1=one>");
360
361 // new-style format, octal, unnamed bit-field
362 h_snprintb(
363 "\177\010"
364 "F\010\004Field\0"
365 ":\001one\0"
366 ":\002two\0",
367 0x100,
368 "0400<one>");
369
370 // new-style format, decimal, unnamed bit-field
371 h_snprintb(
372 "\177\012"
373 "F\010\004Field\0"
374 ":\1one\0"
375 ":\2two\0",
376 0x100,
377 "256<one>");
378
379 // new-style format, hexadecimal, unnamed bit-field
380 h_snprintb(
381 "\177\020"
382 "F\010\004Field\0"
383 ":\1one\0"
384 ":\2two\0",
385 0x100,
386 "0x100<one>");
387
388 // new-style format, hexadecimal, named bit-field, edge cases
389 //
390 // Field values can be listed in arbitrary order, there can also be
391 // duplicates. A field value's description can be empty, resulting in
392 // several '=' in a row. The ':' directive can emulate the '='
393 // directive, but not vice versa.
394 h_snprintb(
395 "\177\20"
396 "f\0\4Field\0"
397 "=\1one\0"
398 "=\1one-again\0"
399 "=\1\0"
400 "=\1\0"
401 ":\1double\0"
402 ":\1-colon\0"
403 ":\1=equal\0"
404 "=\2TWO\0",
405 1,
406 "0x1<Field=0x1=one=one-again==double-colon=equal>");
407
408 // new-style format, hexadecimal, unnamed bit-field, edge cases
409 //
410 // Combining the 'F' and '=' directives generates output that doesn't
411 // look well-formed.
412 h_snprintb(
413 "\177\20"
414 "=\0all-zero\0"
415 "=\1all-one\0"
416 ":\1-continued\0"
417 "F\0\4Field\0"
418 "=\1one\0"
419 "=\1one-again\0"
420 "=\1\0"
421 "=\1\0"
422 ":\1double\0"
423 ":\1-colon\0"
424 ":\1=equal\0"
425 "=\2TWO\0",
426 1,
427 "0x1=all-one-continued<=one=one-again==double-colon=equal>");
428
429 // new-style format, bit-fields with fixed fallback value
430 //
431 // Only the first fallback value is used, all others are ignored.
432 h_snprintb(
433 "\177\020"
434 "f\0\4Field\0"
435 "=\1one\0"
436 "=\2two\0"
437 "*=other\0"
438 "*=yet-another\0"
439 "b\1separator\0"
440 "F\0\4Field\0"
441 ":\1one\0"
442 ":\2two\0"
443 "*other\0"
444 "*yet-another\0",
445 3,
446 "0x3<Field=0x3=other,separator,other>");
447
448 // new-style format, bit-fields with numeric fallback value
449 h_snprintb(
450 "\177\020"
451 "f\010\004Field\0"
452 "*=other(%04ju)\0"
453 "b\000separator\0"
454 "F\010\004Field\0"
455 "*other(%04ju)\0",
456 0x301,
457 "0x301<Field=0x3=other(0003),separator,other(0003)>");
458
459 // new-style format, bit-field with more than 8 bits
460 //
461 // The '=' and ':' directives can only match values from 0 to 255, so
462 // the fallback value always steps in. The complete value of the
463 // bit-field appears in the output, though.
464 h_snprintb(
465 "\177\020"
466 "f\010\020Field\0"
467 "=\377ones\0"
468 "*=other(%jx)\0"
469 "F\010\020\0"
470 ":\377ones\0"
471 "*other(%jx)\0",
472 0x77ff55,
473 "0x77ff55<Field=0x77ff=other(77ff),other(77ff)>");
474
475 // new-style format, bit-fields with no match
476 h_snprintb(
477 "\177\020"
478 "f\010\004Field\0"
479 "=\1one\0"
480 "=\2two\0",
481 0x301,
482 "0x301<Field=0x3>");
483 h_snprintb(
484 "\177\020"
485 "F\010\004\0"
486 ":\1one\0"
487 ":\2two\0",
488 0x301,
489 "0x301<>");
490 h_snprintb(
491 "\177\020"
492 "f\010\004Field\0"
493 "=\1one\0"
494 "=\2two\0"
495 "b\000separator\0"
496 "F\010\004\0"
497 ":\1one\0"
498 ":\2two\0",
499 0x301,
500 "0x301<Field=0x3,separator,>");
501
502 // new-style format, two separate bit-fields
503 h_snprintb(
504 "\177\20"
505 "f\0\4Field_1\0"
506 "=\1ONE\0"
507 "=\2TWO\0"
508 "f\4\4Field_2\0"
509 "=\1ONE\0"
510 "=\2TWO\0",
511 0x12,
512 "0x12<Field_1=0x2=TWO,Field_2=0x1=ONE>");
513
514 // new-style format, mixed named and unnamed bit-fields
515 h_snprintb(
516 "\177\20"
517 "f\0\4Field_1\0"
518 "=\1ONE\0"
519 "=\2TWO\0"
520 "F\x8\4\0"
521 "*Field_3=%jd\0"
522 "f\4\4Field_2\0"
523 ":\1:ONE\0"
524 ":\2:TWO\0",
525 0xD12,
526 "0xd12<Field_1=0x2=TWO,Field_3=13,Field_2=0x1:ONE>");
527
528 // new-style format, descriptions with spaces
529 h_snprintb(
530 "\177\020"
531 "b\000has std options\0"
532 "f\010\004std options\0"
533 "=\000no options\0"
534 "=\017all options\0"
535 "F\020\004ext options\0"
536 ":\000no ext options\0"
537 ":\017all ext options\0",
538 0x000001,
539 "0x1<has std options,std options=0=no options,no ext options>");
540 h_snprintb(
541 "\177\020"
542 "f\010\004std options\0"
543 "*=other std options\0"
544 "F\020\004ext\toptions\0"
545 "*other\text\toptions\0",
546 0x000001,
547 "0x1<std options=0=other std options,other\text\toptions>");
548
549 // It is possible but cumbersome to implement a reduced variant of
550 // rot13 using snprintb, shown here for lowercase letters only.
551 for (char ch = 'A'; ch <= '~'; ch++) {
552 char rot13 = ch >= 'a' && ch <= 'm' ? ch + 13
553 : ch >= 'n' && ch <= 'z' ? ch - 13
554 : '?';
555 char expected[8];
556 ATF_REQUIRE_EQ(7,
557 snprintf(expected, sizeof(expected), "%#x<%c>", ch, rot13));
558 h_snprintb(
559 "\177\020"
560 "F\000\010\0"
561 ":an\0:bo\0:cp\0:dq\0:er\0:fs\0:gt\0:hu\0"
562 ":iv\0:jw\0:kx\0:ly\0:mz\0"
563 ":na\0:ob\0:pc\0:qd\0:re\0:sf\0:tg\0:uh\0"
564 ":vi\0:wj\0:xk\0:yl\0:zm\0"
565 // If snprintf accepted "%jc", it would be possible to
566 // echo the non-alphabetic characters instead of a
567 // catchall question mark.
568 "*?\0",
569 ch,
570 expected);
571 }
572 }
573
574 static void
575 h_snprintb_m_loc(const char *file, size_t line,
576 const char *fmt, size_t fmtlen, uint64_t val, int line_max,
577 const char *res, size_t reslen)
578 {
579 char buf[1024];
580
581 int rv = snprintb_m(buf, sizeof(buf), fmt, val, line_max);
582
583 ATF_REQUIRE_MSG(rv >= 0, "formatting %jx with '%s' returns error %d",
584 (uintmax_t)val, vis_arr(fmt, fmtlen), rv);
585
586 size_t buflen = rv;
587 ATF_CHECK_MSG(
588 buflen == reslen && memcmp(buf, res, reslen) == 0,
589 "failed:\n"
590 "\ttest case: %s:%zu\n"
591 "\tformat: %s\n"
592 "\tvalue: %#jx\n"
593 "\twant: %zu bytes %s\n"
594 "\thave: %zu bytes %s\n",
595 file, line,
596 vis_arr(fmt, fmtlen),
597 (uintmax_t)val,
598 reslen, vis_arr(res, reslen),
599 buflen, vis_arr(buf, buflen));
600 }
601
602 #define h_snprintb_m(fmt, val, line_max, res) \
603 h_snprintb_m_loc(__FILE__, __LINE__, \
604 fmt, sizeof(fmt) - 1, val, line_max, \
605 res, sizeof(res) - 1)
606
607 ATF_TC(snprintb_m);
608 ATF_TC_HEAD(snprintb_m, tc)
609 {
610 atf_tc_set_md_var(tc, "descr", "Checks snprintb_m(3)");
611 }
612 ATF_TC_BODY(snprintb_m, tc)
613 {
614 h_snprintb_m(
615 "\177\020"
616 "b\0LSB\0"
617 "b\1_BITONE\0"
618 "f\4\4NIBBLE2\0"
619 "f\x10\4BURST\0"
620 "=\04FOUR\0"
621 "=\17FIFTEEN\0"
622 "b\x1fMSB\0",
623 0x800f0701,
624 33,
625 "0x800f0701<LSB,NIBBLE2=0>\0"
626 "0x800f0701<BURST=0xf=FIFTEEN,MSB>\0");
627
628 h_snprintb_m(
629 "\177\020"
630 "b\0LSB\0"
631 "b\1_BITONE\0"
632 "f\4\4NIBBLE2\0"
633 "f\x10\4BURST\0"
634 "=\04FOUR\0"
635 "=\17FIFTEEN\0"
636 "b\x1fMSB\0",
637 0x800f0701,
638 32,
639 "0x800f0701<LSB,NIBBLE2=0>\0"
640 "0x800f0701<BURST=0xf=FIFTEEN>\0"
641 "0x800f0701<MSB>\0");
642 }
643
644 ATF_TP_ADD_TCS(tp)
645 {
646
647 ATF_TP_ADD_TC(tp, snprintb);
648 ATF_TP_ADD_TC(tp, snprintb_m);
649
650 return atf_no_error();
651 }
652