1 /* $NetBSD: subr_prf.c,v 1.31 2024/08/31 07:12:57 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)printf.c 8.1 (Berkeley) 6/11/93 32 */ 33 34 /* 35 * Scaled down version of printf(3). 36 */ 37 38 #include <sys/cdefs.h> 39 #include <sys/types.h> 40 #include <sys/stdint.h> /* XXX: for intptr_t */ 41 42 #include "stand.h" 43 44 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT 45 #define INTMAX_T longlong_t 46 #define UINTMAX_T u_longlong_t 47 #else 48 #define INTMAX_T long 49 #define UINTMAX_T u_long 50 #endif 51 52 #if 0 /* XXX: abuse intptr_t until the situation with ptrdiff_t is clear */ 53 #define PTRDIFF_T ptrdiff_t 54 #else 55 #define PTRDIFF_T intptr_t 56 #endif 57 58 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 59 static void kprintn(void (*)(int), UINTMAX_T, int, int, int); 60 #else 61 static void kprintn(void (*)(int), UINTMAX_T, int); 62 #endif 63 static void sputchar(int); 64 static void kdoprnt(void (*)(int), const char *, va_list); 65 66 static char *sbuf, *ebuf; 67 68 const char hexdigits[16] = "0123456789abcdef"; 69 70 #define LONG 0x01 71 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT 72 #define LLONG 0x02 73 #endif 74 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 75 #define ALT 0x04 76 #define SPACE 0x08 77 #define LADJUST 0x10 78 #define SIGN 0x20 79 #define ZEROPAD 0x40 80 #define NEGATIVE 0x80 81 #define KPRINTN(base) kprintn(put, ul, base, lflag, width) 82 #define RADJUSTZEROPAD() \ 83 do { \ 84 if ((lflag & (ZEROPAD|LADJUST)) == ZEROPAD) { \ 85 while (width-- > 0) \ 86 put('0'); \ 87 } \ 88 } while (/*CONSTCOND*/0) 89 #define LADJUSTPAD() \ 90 do { \ 91 if (lflag & LADJUST) { \ 92 while (width-- > 0) \ 93 put(' '); \ 94 } \ 95 } while (/*CONSTCOND*/0) 96 #define RADJUSTPAD() \ 97 do { \ 98 if ((lflag & (ZEROPAD|LADJUST)) == 0) { \ 99 while (width-- > 0) \ 100 put(' '); \ 101 } \ 102 } while (/*CONSTCOND*/0) 103 #else /* LIBSA_PRINTF_WIDTH_SUPPORT */ 104 #define KPRINTN(base) kprintn(put, ul, base) 105 #define RADJUSTZEROPAD() /**/ 106 #define LADJUSTPAD() /**/ 107 #define RADJUSTPAD() /**/ 108 #endif /* LIBSA_PRINTF_WIDTH_SUPPORT */ 109 110 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT 111 #define KPRINT(base) \ 112 do { \ 113 ul = (lflag & LLONG) \ 114 ? va_arg(ap, u_longlong_t) \ 115 : (lflag & LONG) \ 116 ? va_arg(ap, u_long) \ 117 : va_arg(ap, u_int); \ 118 KPRINTN(base); \ 119 } while (/*CONSTCOND*/0) 120 #else /* LIBSA_PRINTF_LONGLONG_SUPPORT */ 121 #define KPRINT(base) \ 122 do { \ 123 ul = (lflag & LONG) \ 124 ? va_arg(ap, u_long) : va_arg(ap, u_int); \ 125 KPRINTN(base); \ 126 } while (/*CONSTCOND*/0) 127 #endif /* LIBSA_PRINTF_LONGLONG_SUPPORT */ 128 129 static void 130 null_sputchar(int c) 131 { 132 sbuf++; 133 } 134 135 static void 136 sputchar(int c) 137 { 138 139 if (sbuf < ebuf) 140 *sbuf++ = c; 141 } 142 143 void 144 vprintf(const char *fmt, va_list ap) 145 { 146 147 kdoprnt(putchar, fmt, ap); 148 } 149 150 int 151 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) 152 { 153 154 sbuf = buf; 155 ebuf = buf + size - 1; 156 kdoprnt(buf == NULL ? null_sputchar : sputchar, fmt, ap); 157 if (buf != NULL) 158 *sbuf = '\0'; 159 return sbuf - buf; 160 } 161 162 static void 163 kdoprnt(void (*put)(int), const char *fmt, va_list ap) 164 { 165 char *p; 166 int ch; 167 UINTMAX_T ul; 168 int lflag; 169 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 170 int width; 171 char *q; 172 #endif 173 174 for (;;) { 175 while ((ch = *fmt++) != '%') { 176 if (ch == '\0') 177 return; 178 put(ch); 179 } 180 lflag = 0; 181 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 182 width = 0; 183 #endif 184 reswitch: 185 switch (ch = *fmt++) { 186 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 187 case '#': 188 lflag |= ALT; 189 goto reswitch; 190 case ' ': 191 lflag |= SPACE; 192 goto reswitch; 193 case '-': 194 lflag |= LADJUST; 195 goto reswitch; 196 case '+': 197 lflag |= SIGN; 198 goto reswitch; 199 case '0': 200 if (width == 0) { 201 lflag |= ZEROPAD; 202 goto reswitch; 203 } 204 /* FALLTHROUGH */ 205 case '1': case '2': case '3': case '4': case '5': 206 case '6': case '7': case '8': case '9': 207 for (;;) { 208 width *= 10; 209 width += ch - '0'; 210 ch = *fmt; 211 if ((unsigned)ch - '0' > 9) 212 break; 213 ++fmt; 214 } 215 goto reswitch; 216 #endif 217 case 'l': 218 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT 219 if (*fmt == 'l') { 220 ++fmt; 221 lflag |= LLONG; 222 } else 223 #endif 224 lflag |= LONG; 225 goto reswitch; 226 case 'j': 227 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT 228 if (sizeof(intmax_t) == sizeof(long long)) 229 lflag |= LLONG; 230 else 231 #endif 232 if (sizeof(intmax_t) == sizeof(long)) 233 lflag |= LONG; 234 goto reswitch; 235 case 't': 236 if (sizeof(PTRDIFF_T) == sizeof(long)) 237 lflag |= LONG; 238 goto reswitch; 239 case 'z': 240 if (sizeof(ssize_t) == sizeof(long)) 241 lflag |= LONG; 242 goto reswitch; 243 case 'c': 244 ch = va_arg(ap, int); 245 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 246 --width; 247 #endif 248 RADJUSTPAD(); 249 put(ch & 0xFF); 250 LADJUSTPAD(); 251 break; 252 case 's': 253 p = va_arg(ap, char *); 254 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 255 for (q = p; *q != '\0'; ++q) 256 continue; 257 width -= q - p; 258 #endif 259 RADJUSTPAD(); 260 while ((ch = (unsigned char)*p++)) 261 put(ch); 262 LADJUSTPAD(); 263 break; 264 case 'd': 265 ul = 266 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT 267 (lflag & LLONG) ? va_arg(ap, longlong_t) : 268 #endif 269 (lflag & LONG) ? va_arg(ap, long) : va_arg(ap, int); 270 if ((INTMAX_T)ul < 0) { 271 ul = -(INTMAX_T)ul; 272 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 273 lflag |= NEGATIVE; 274 #else 275 put('-'); 276 #endif 277 } 278 KPRINTN(10); 279 break; 280 case 'o': 281 KPRINT(8); 282 break; 283 case 'u': 284 KPRINT(10); 285 break; 286 case 'p': 287 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 288 lflag |= (LONG|ALT); 289 #else 290 put('0'); 291 put('x'); 292 #endif 293 /* FALLTHROUGH */ 294 case 'x': 295 KPRINT(16); 296 break; 297 default: 298 if (ch == '\0') 299 return; 300 put(ch); 301 break; 302 } 303 } 304 } 305 306 static void 307 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 308 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width) 309 #else 310 kprintn(void (*put)(int), UINTMAX_T ul, int base) 311 #endif 312 { 313 /* hold a INTMAX_T in base 8 */ 314 char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */]; 315 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 316 char *q; 317 #endif 318 319 p = buf; 320 do { 321 *p++ = hexdigits[ul % base]; 322 } while (ul /= base); 323 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT 324 q = p; 325 if (lflag & ALT && *(p - 1) != '0') { 326 if (base == 8) { 327 *p++ = '0'; 328 } else if (base == 16) { 329 *p++ = 'x'; 330 *p++ = '0'; 331 } 332 } 333 if (lflag & NEGATIVE) 334 *p++ = '-'; 335 else if (lflag & SIGN) 336 *p++ = '+'; 337 else if (lflag & SPACE) 338 *p++ = ' '; 339 width -= p - buf; 340 if (lflag & ZEROPAD) { 341 while (p > q) 342 put(*--p); 343 } 344 #endif 345 RADJUSTPAD(); 346 RADJUSTZEROPAD(); 347 do { 348 put(*--p); 349 } while (p > buf); 350 LADJUSTPAD(); 351 } 352