1 1.59 kre /* $NetBSD: printf.c,v 1.59 2024/11/24 12:33:00 kre Exp $ */ 2 1.14 tls 3 1.1 cgd /* 4 1.17 mrg * Copyright (c) 1989, 1993 5 1.17 mrg * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.29 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.16 christos #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.17 mrg #if !defined(BUILTIN) && !defined(SHELL) 35 1.33 lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993\ 36 1.33 lukem The Regents of the University of California. All rights reserved."); 37 1.17 mrg #endif 38 1.5 jtc #endif 39 1.1 cgd 40 1.1 cgd #ifndef lint 41 1.16 christos #if 0 42 1.17 mrg static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95"; 43 1.16 christos #else 44 1.59 kre __RCSID("$NetBSD: printf.c,v 1.59 2024/11/24 12:33:00 kre Exp $"); 45 1.16 christos #endif 46 1.1 cgd #endif /* not lint */ 47 1.1 cgd 48 1.17 mrg #include <sys/types.h> 49 1.17 mrg 50 1.4 jtc #include <ctype.h> 51 1.19 perry #include <err.h> 52 1.19 perry #include <errno.h> 53 1.22 kleink #include <inttypes.h> 54 1.19 perry #include <limits.h> 55 1.19 perry #include <locale.h> 56 1.23 wiz #include <stdarg.h> 57 1.1 cgd #include <stdio.h> 58 1.4 jtc #include <stdlib.h> 59 1.2 mycroft #include <string.h> 60 1.19 perry #include <unistd.h> 61 1.4 jtc 62 1.25 christos #ifdef __GNUC__ 63 1.25 christos #define ESCAPE '\e' 64 1.25 christos #else 65 1.25 christos #define ESCAPE 033 66 1.25 christos #endif 67 1.5 jtc 68 1.39 kre static void conv_escape_str(char *, void (*)(int), int); 69 1.39 kre static char *conv_escape(char *, char *, int); 70 1.25 christos static char *conv_expand(const char *); 71 1.57 kre static wchar_t getchr(void); 72 1.57 kre static long double getdouble(void); 73 1.25 christos static int getwidth(void); 74 1.23 wiz static intmax_t getintmax(void); 75 1.23 wiz static char *getstr(void); 76 1.57 kre static char *mklong(const char *, char, char); 77 1.57 kre static intmax_t wide_char(const char *, int); 78 1.23 wiz static void check_conversion(const char *, const char *); 79 1.41 kre static void usage(void); 80 1.25 christos 81 1.26 dsl static void b_count(int); 82 1.26 dsl static void b_output(int); 83 1.30 christos static size_t b_length; 84 1.26 dsl static char *b_fmt; 85 1.26 dsl 86 1.5 jtc static int rval; 87 1.59 kre static char ** gargv, ** firstarg; 88 1.57 kre static int long_double; 89 1.4 jtc 90 1.59 kre #define ARGNUM ((int)(gargv - firstarg)) 91 1.59 kre 92 1.25 christos #ifdef BUILTIN /* csh builtin */ 93 1.25 christos #define main progprintf 94 1.16 christos #endif 95 1.16 christos 96 1.25 christos #ifdef SHELL /* sh (aka ash) builtin */ 97 1.5 jtc #define main printfcmd 98 1.5 jtc #include "../../bin/sh/bltin/bltin.h" 99 1.5 jtc #endif /* SHELL */ 100 1.5 jtc 101 1.1 cgd #define PF(f, func) { \ 102 1.25 christos if (fieldwidth != -1) { \ 103 1.25 christos if (precision != -1) \ 104 1.32 christos error = printf(f, fieldwidth, precision, func); \ 105 1.1 cgd else \ 106 1.32 christos error = printf(f, fieldwidth, func); \ 107 1.25 christos } else if (precision != -1) \ 108 1.32 christos error = printf(f, precision, func); \ 109 1.1 cgd else \ 110 1.32 christos error = printf(f, func); \ 111 1.1 cgd } 112 1.1 cgd 113 1.26 dsl #define APF(cpp, f, func) { \ 114 1.26 dsl if (fieldwidth != -1) { \ 115 1.26 dsl if (precision != -1) \ 116 1.32 christos error = asprintf(cpp, f, fieldwidth, precision, func); \ 117 1.26 dsl else \ 118 1.32 christos error = asprintf(cpp, f, fieldwidth, func); \ 119 1.26 dsl } else if (precision != -1) \ 120 1.32 christos error = asprintf(cpp, f, precision, func); \ 121 1.26 dsl else \ 122 1.32 christos error = asprintf(cpp, f, func); \ 123 1.26 dsl } 124 1.26 dsl 125 1.51 christos #define isodigit(c) ((c) >= '0' && (c) <= '7') 126 1.52 christos #define octtobin(c) ((c) - '0') 127 1.52 christos #define check(c, a) (c) >= (a) && (c) <= (a) + 5 ? (c) - (a) + 10 128 1.52 christos #define hextobin(c) (check(c, 'a') : check(c, 'A') : (c) - '0') 129 1.59 kre 130 1.32 christos #ifdef main 131 1.32 christos int main(int, char *[]); 132 1.32 christos #endif 133 1.44 kre 134 1.44 kre int 135 1.44 kre main(int argc, char *argv[]) 136 1.1 cgd { 137 1.18 lukem char *fmt, *start; 138 1.18 lukem int fieldwidth, precision; 139 1.25 christos char nextch; 140 1.4 jtc char *format; 141 1.36 christos char ch; 142 1.49 kre int error; 143 1.4 jtc 144 1.5 jtc #if !defined(SHELL) && !defined(BUILTIN) 145 1.15 cgd (void)setlocale (LC_ALL, ""); 146 1.5 jtc #endif 147 1.1 cgd 148 1.46 kre rval = 0; /* clear for builtin versions (avoid holdover) */ 149 1.57 kre long_double = 0; 150 1.53 kre clearerr(stdout); /* for the builtin version */ 151 1.46 kre 152 1.50 kre if (argc > 2 && strchr(argv[1], '%') == NULL) { 153 1.50 kre int o; 154 1.50 kre 155 1.50 kre /* 156 1.56 kre * We only do this for argc > 2, as: 157 1.56 kre * 158 1.56 kre * for argc <= 1 159 1.56 kre * at best we have a bare "printf" so there cannot be 160 1.56 kre * any options, thus getopts() would be a waste of time. 161 1.56 kre * The usage() below is assured. 162 1.56 kre * 163 1.56 kre * for argc == 2 164 1.56 kre * There is only one arg (argv[1]) which logically must 165 1.56 kre * be intended to be the (required) format string for 166 1.56 kre * printf, without which we can do nothing so rather 167 1.56 kre * than usage() if it happens to start with a '-' we 168 1.56 kre * just avoid getopts() and treat it as a format string. 169 1.56 kre * 170 1.56 kre * Then, for argc > 2, we also skip this if there is a '%' 171 1.56 kre * anywhere in argv[1] as it is likely that would be intended 172 1.56 kre * to be the format string, rather than options, even if it 173 1.56 kre * starts with a '-' so we skip getopts() in that case as well. 174 1.56 kre * 175 1.56 kre * Note that this would fail should there ever be an option 176 1.56 kre * which takes an arbitrary string value, which could be given 177 1.56 kre * as -Oabc%def so should that ever become possible, remove 178 1.56 kre * the strchr() test above. 179 1.50 kre */ 180 1.50 kre 181 1.57 kre while ((o = getopt(argc, argv, "L")) != -1) { 182 1.50 kre switch (o) { 183 1.57 kre case 'L': 184 1.57 kre long_double = 1; 185 1.57 kre break; 186 1.50 kre case '?': 187 1.50 kre default: 188 1.50 kre usage(); 189 1.50 kre return 1; 190 1.50 kre } 191 1.5 jtc } 192 1.50 kre argc -= optind; 193 1.50 kre argv += optind; 194 1.50 kre } else { 195 1.50 kre argc -= 1; /* drop argv[0] (the program name) */ 196 1.50 kre argv += 1; 197 1.1 cgd } 198 1.1 cgd 199 1.56 kre if (argc < 1) { /* Nothing left at all? */ 200 1.5 jtc usage(); 201 1.30 christos return 1; 202 1.5 jtc } 203 1.5 jtc 204 1.56 kre format = *argv; /* First remaining arg is the format string */ 205 1.59 kre firstarg = gargv = ++argv; /* remaining args are for that to consume */ 206 1.4 jtc 207 1.35 christos #define SKIP1 "#-+ 0'" 208 1.34 christos #define SKIP2 "0123456789" 209 1.4 jtc do { 210 1.6 jtc /* 211 1.6 jtc * Basic algorithm is to scan the format string for conversion 212 1.6 jtc * specifications -- once one is found, find out if the field 213 1.41 kre * width or precision is a '*'; if it is, gather up value. 214 1.6 jtc * Note, format strings are reused as necessary to use up the 215 1.41 kre * provided arguments, arguments of zero/null string are 216 1.6 jtc * provided to use up the format string. 217 1.6 jtc */ 218 1.6 jtc 219 1.6 jtc /* find next format specification */ 220 1.30 christos for (fmt = format; (ch = *fmt++) != '\0';) { 221 1.25 christos if (ch == '\\') { 222 1.25 christos char c_ch; 223 1.59 kre 224 1.39 kre fmt = conv_escape(fmt, &c_ch, 0); 225 1.25 christos putchar(c_ch); 226 1.25 christos continue; 227 1.25 christos } 228 1.25 christos if (ch != '%' || (*fmt == '%' && ++fmt)) { 229 1.25 christos (void)putchar(ch); 230 1.25 christos continue; 231 1.25 christos } 232 1.25 christos 233 1.41 kre /* 234 1.41 kre * Ok - we've found a format specification, 235 1.41 kre * Save its address for a later printf(). 236 1.41 kre */ 237 1.25 christos start = fmt - 1; 238 1.25 christos 239 1.25 christos /* skip to field width */ 240 1.25 christos fmt += strspn(fmt, SKIP1); 241 1.34 christos if (*fmt == '*') { 242 1.34 christos fmt++; 243 1.34 christos fieldwidth = getwidth(); 244 1.44 kre } else { 245 1.34 christos fieldwidth = -1; 246 1.25 christos 247 1.44 kre /* skip to possible '.' for precision */ 248 1.44 kre fmt += strspn(fmt, SKIP2); 249 1.44 kre } 250 1.44 kre 251 1.34 christos if (*fmt == '.') { 252 1.44 kre /* get following precision */ 253 1.34 christos fmt++; 254 1.34 christos if (*fmt == '*') { 255 1.34 christos fmt++; 256 1.34 christos precision = getwidth(); 257 1.44 kre } else { 258 1.34 christos precision = -1; 259 1.44 kre fmt += strspn(fmt, SKIP2); 260 1.44 kre } 261 1.34 christos } else 262 1.34 christos precision = -1; 263 1.25 christos 264 1.25 christos ch = *fmt; 265 1.25 christos if (!ch) { 266 1.44 kre warnx("%s: missing format character", start); 267 1.42 kre return 1; 268 1.25 christos } 269 1.44 kre 270 1.41 kre /* 271 1.41 kre * null terminate format string to we can use it 272 1.41 kre * as an argument to printf. 273 1.41 kre */ 274 1.25 christos nextch = fmt[1]; 275 1.25 christos fmt[1] = 0; 276 1.44 kre 277 1.25 christos switch (ch) { 278 1.25 christos 279 1.25 christos case 'B': { 280 1.25 christos const char *p = conv_expand(getstr()); 281 1.41 kre 282 1.32 christos if (p == NULL) 283 1.32 christos goto out; 284 1.25 christos *fmt = 's'; 285 1.25 christos PF(start, p); 286 1.32 christos if (error < 0) 287 1.32 christos goto out; 288 1.6 jtc break; 289 1.25 christos } 290 1.25 christos case 'b': { 291 1.41 kre /* 292 1.41 kre * There has to be a better way to do this, 293 1.26 dsl * but the string we generate might have 294 1.41 kre * embedded nulls 295 1.41 kre */ 296 1.26 dsl static char *a, *t; 297 1.26 dsl char *cp = getstr(); 298 1.41 kre 299 1.26 dsl /* Free on entry in case shell longjumped out */ 300 1.26 dsl if (a != NULL) 301 1.26 dsl free(a); 302 1.26 dsl a = NULL; 303 1.26 dsl if (t != NULL) 304 1.26 dsl free(t); 305 1.26 dsl t = NULL; 306 1.41 kre 307 1.26 dsl /* Count number of bytes we want to output */ 308 1.26 dsl b_length = 0; 309 1.39 kre conv_escape_str(cp, b_count, 0); 310 1.26 dsl t = malloc(b_length + 1); 311 1.26 dsl if (t == NULL) 312 1.32 christos goto out; 313 1.32 christos (void)memset(t, 'x', b_length); 314 1.26 dsl t[b_length] = 0; 315 1.41 kre 316 1.26 dsl /* Get printf to calculate the lengths */ 317 1.25 christos *fmt = 's'; 318 1.26 dsl APF(&a, start, t); 319 1.32 christos if (error == -1) 320 1.32 christos goto out; 321 1.26 dsl b_fmt = a; 322 1.41 kre 323 1.26 dsl /* Output leading spaces and data bytes */ 324 1.39 kre conv_escape_str(cp, b_output, 1); 325 1.41 kre 326 1.26 dsl /* Add any trailing spaces */ 327 1.26 dsl printf("%s", b_fmt); 328 1.25 christos break; 329 1.25 christos } 330 1.57 kre case 'C': { 331 1.57 kre wchar_t p = (wchar_t)getintmax(); 332 1.57 kre char *f = mklong(start, 'c', 'l'); 333 1.57 kre 334 1.57 kre PF(f, p); 335 1.57 kre if (error < 0) 336 1.57 kre goto out; 337 1.57 kre break; 338 1.57 kre } 339 1.25 christos case 'c': { 340 1.57 kre wchar_t p = getchr(); 341 1.57 kre char *f = mklong(start, ch, 'l'); 342 1.41 kre 343 1.57 kre PF(f, p); 344 1.32 christos if (error < 0) 345 1.32 christos goto out; 346 1.25 christos break; 347 1.25 christos } 348 1.25 christos case 's': { 349 1.25 christos char *p = getstr(); 350 1.41 kre 351 1.25 christos PF(start, p); 352 1.32 christos if (error < 0) 353 1.32 christos goto out; 354 1.25 christos break; 355 1.25 christos } 356 1.25 christos case 'd': 357 1.25 christos case 'i': { 358 1.25 christos intmax_t p = getintmax(); 359 1.57 kre char *f = mklong(start, ch, 'j'); 360 1.41 kre 361 1.25 christos PF(f, p); 362 1.32 christos if (error < 0) 363 1.32 christos goto out; 364 1.25 christos break; 365 1.25 christos } 366 1.25 christos case 'o': 367 1.25 christos case 'u': 368 1.25 christos case 'x': 369 1.25 christos case 'X': { 370 1.43 kre uintmax_t p = (uintmax_t)getintmax(); 371 1.57 kre char *f = mklong(start, ch, 'j'); 372 1.41 kre 373 1.25 christos PF(f, p); 374 1.32 christos if (error < 0) 375 1.32 christos goto out; 376 1.25 christos break; 377 1.25 christos } 378 1.40 kre case 'a': 379 1.40 kre case 'A': 380 1.25 christos case 'e': 381 1.25 christos case 'E': 382 1.25 christos case 'f': 383 1.40 kre case 'F': 384 1.25 christos case 'g': 385 1.25 christos case 'G': { 386 1.57 kre long double p = getdouble(); 387 1.41 kre 388 1.57 kre if (long_double) { 389 1.57 kre char * f = mklong(start, ch, 'L'); 390 1.57 kre PF(f, p); 391 1.57 kre } else { 392 1.57 kre double pp = (double)p; 393 1.57 kre PF(start, pp); 394 1.57 kre } 395 1.32 christos if (error < 0) 396 1.32 christos goto out; 397 1.4 jtc break; 398 1.25 christos } 399 1.44 kre case '%': 400 1.44 kre /* Don't ask, but this is useful ... */ 401 1.44 kre if (fieldwidth == 'N' && precision == 'B') 402 1.44 kre return 0; 403 1.44 kre /* FALLTHROUGH */ 404 1.6 jtc default: 405 1.25 christos warnx("%s: invalid directive", start); 406 1.30 christos return 1; 407 1.4 jtc } 408 1.25 christos *fmt++ = ch; 409 1.25 christos *fmt = nextch; 410 1.25 christos /* escape if a \c was encountered */ 411 1.25 christos if (rval & 0x100) 412 1.53 kre goto done; 413 1.6 jtc } 414 1.25 christos } while (gargv != argv && *gargv); 415 1.1 cgd 416 1.59 kre done:; 417 1.53 kre (void)fflush(stdout); 418 1.53 kre if (ferror(stdout)) { 419 1.53 kre clearerr(stdout); 420 1.53 kre err(1, "write error"); 421 1.53 kre } 422 1.32 christos return rval & ~0x100; 423 1.59 kre out:; 424 1.32 christos warn("print failed"); 425 1.32 christos return 1; 426 1.4 jtc } 427 1.4 jtc 428 1.26 dsl /* helper functions for conv_escape_str */ 429 1.26 dsl 430 1.26 dsl static void 431 1.30 christos /*ARGSUSED*/ 432 1.26 dsl b_count(int ch) 433 1.26 dsl { 434 1.26 dsl b_length++; 435 1.26 dsl } 436 1.26 dsl 437 1.26 dsl /* Output one converted character for every 'x' in the 'format' */ 438 1.26 dsl 439 1.26 dsl static void 440 1.26 dsl b_output(int ch) 441 1.26 dsl { 442 1.26 dsl for (;;) { 443 1.26 dsl switch (*b_fmt++) { 444 1.26 dsl case 0: 445 1.26 dsl b_fmt--; 446 1.26 dsl return; 447 1.26 dsl case ' ': 448 1.26 dsl putchar(' '); 449 1.26 dsl break; 450 1.26 dsl default: 451 1.26 dsl putchar(ch); 452 1.26 dsl return; 453 1.26 dsl } 454 1.26 dsl } 455 1.26 dsl } 456 1.26 dsl 457 1.4 jtc 458 1.4 jtc /* 459 1.41 kre * Print SysV echo(1) style escape string 460 1.25 christos * Halts processing string if a \c escape is encountered. 461 1.4 jtc */ 462 1.26 dsl static void 463 1.39 kre conv_escape_str(char *str, void (*do_putchar)(int), int quiet) 464 1.4 jtc { 465 1.4 jtc int value; 466 1.25 christos int ch; 467 1.26 dsl char c; 468 1.25 christos 469 1.30 christos while ((ch = *str++) != '\0') { 470 1.25 christos if (ch != '\\') { 471 1.26 dsl do_putchar(ch); 472 1.25 christos continue; 473 1.25 christos } 474 1.25 christos 475 1.25 christos ch = *str++; 476 1.25 christos if (ch == 'c') { 477 1.25 christos /* \c as in SYSV echo - abort all processing.... */ 478 1.25 christos rval |= 0x100; 479 1.25 christos break; 480 1.25 christos } 481 1.25 christos 482 1.41 kre /* 483 1.25 christos * %b string octal constants are not like those in C. 484 1.41 kre * They start with a \0, and are followed by 0, 1, 2, 485 1.41 kre * or 3 octal digits. 486 1.25 christos */ 487 1.25 christos if (ch == '0') { 488 1.30 christos int octnum = 0, i; 489 1.59 kre 490 1.30 christos for (i = 0; i < 3; i++) { 491 1.30 christos if (!isdigit((unsigned char)*str) || *str > '7') 492 1.30 christos break; 493 1.31 dsl octnum = (octnum << 3) | (*str++ - '0'); 494 1.30 christos } 495 1.30 christos do_putchar(octnum); 496 1.25 christos continue; 497 1.25 christos } 498 1.25 christos 499 1.25 christos /* \[M][^|-]C as defined by vis(3) */ 500 1.25 christos if (ch == 'M' && *str == '-') { 501 1.26 dsl do_putchar(0200 | str[1]); 502 1.25 christos str += 2; 503 1.25 christos continue; 504 1.25 christos } 505 1.25 christos if (ch == 'M' && *str == '^') { 506 1.4 jtc str++; 507 1.25 christos value = 0200; 508 1.25 christos ch = '^'; 509 1.25 christos } else 510 1.25 christos value = 0; 511 1.25 christos if (ch == '^') { 512 1.25 christos ch = *str++; 513 1.25 christos if (ch == '?') 514 1.25 christos value |= 0177; 515 1.25 christos else 516 1.25 christos value |= ch & 037; 517 1.26 dsl do_putchar(value); 518 1.25 christos continue; 519 1.1 cgd } 520 1.25 christos 521 1.25 christos /* Finally test for sequences valid in the format string */ 522 1.39 kre str = conv_escape(str - 1, &c, quiet); 523 1.26 dsl do_putchar(c); 524 1.4 jtc } 525 1.4 jtc } 526 1.4 jtc 527 1.4 jtc /* 528 1.41 kre * Print "standard" escape characters 529 1.4 jtc */ 530 1.25 christos static char * 531 1.39 kre conv_escape(char *str, char *conv_ch, int quiet) 532 1.4 jtc { 533 1.52 christos int value = 0; 534 1.51 christos char ch, *begin; 535 1.51 christos int c; 536 1.4 jtc 537 1.25 christos ch = *str++; 538 1.4 jtc 539 1.25 christos switch (ch) { 540 1.38 kre case '\0': 541 1.39 kre if (!quiet) 542 1.39 kre warnx("incomplete escape sequence"); 543 1.38 kre rval = 1; 544 1.38 kre value = '\\'; 545 1.38 kre --str; 546 1.38 kre break; 547 1.38 kre 548 1.4 jtc case '0': case '1': case '2': case '3': 549 1.4 jtc case '4': case '5': case '6': case '7': 550 1.51 christos str--; 551 1.51 christos for (c = 3; c-- && isodigit(*str); str++) { 552 1.51 christos value <<= 3; 553 1.51 christos value += octtobin(*str); 554 1.51 christos } 555 1.25 christos break; 556 1.4 jtc 557 1.4 jtc case 'x': 558 1.41 kre /* 559 1.41 kre * Hexadecimal character constants are not required to be 560 1.41 kre * supported (by SuS v1) because there is no consistent 561 1.41 kre * way to detect the end of the constant. 562 1.41 kre * Supporting 2 byte constants is a compromise. 563 1.41 kre */ 564 1.51 christos begin = str; 565 1.51 christos for (c = 2; c-- && isxdigit((unsigned char)*str); str++) { 566 1.51 christos value <<= 4; 567 1.52 christos value += hextobin(*str); 568 1.51 christos } 569 1.51 christos if (str == begin) { 570 1.51 christos if (!quiet) 571 1.51 christos warnx("\\x%s: missing hexadecimal number " 572 1.51 christos "in escape", begin); 573 1.51 christos rval = 1; 574 1.51 christos } 575 1.25 christos break; 576 1.25 christos 577 1.25 christos case '\\': value = '\\'; break; /* backslash */ 578 1.25 christos case '\'': value = '\''; break; /* single quote */ 579 1.25 christos case '"': value = '"'; break; /* double quote */ 580 1.25 christos case 'a': value = '\a'; break; /* alert */ 581 1.25 christos case 'b': value = '\b'; break; /* backspace */ 582 1.25 christos case 'e': value = ESCAPE; break; /* escape */ 583 1.45 kre case 'E': value = ESCAPE; break; /* escape */ 584 1.25 christos case 'f': value = '\f'; break; /* form-feed */ 585 1.25 christos case 'n': value = '\n'; break; /* newline */ 586 1.25 christos case 'r': value = '\r'; break; /* carriage-return */ 587 1.25 christos case 't': value = '\t'; break; /* tab */ 588 1.25 christos case 'v': value = '\v'; break; /* vertical-tab */ 589 1.4 jtc 590 1.25 christos default: 591 1.39 kre if (!quiet) 592 1.39 kre warnx("unknown escape sequence `\\%c'", ch); 593 1.25 christos rval = 1; 594 1.26 dsl value = ch; 595 1.4 jtc break; 596 1.25 christos } 597 1.4 jtc 598 1.52 christos *conv_ch = (char)value; 599 1.25 christos return str; 600 1.25 christos } 601 1.4 jtc 602 1.25 christos /* expand a string so that everything is printable */ 603 1.4 jtc 604 1.25 christos static char * 605 1.25 christos conv_expand(const char *str) 606 1.25 christos { 607 1.25 christos static char *conv_str; 608 1.25 christos char *cp; 609 1.36 christos char ch; 610 1.4 jtc 611 1.25 christos if (conv_str) 612 1.25 christos free(conv_str); 613 1.25 christos /* get a buffer that is definitely large enough.... */ 614 1.25 christos conv_str = malloc(4 * strlen(str) + 1); 615 1.25 christos if (!conv_str) 616 1.32 christos return NULL; 617 1.25 christos cp = conv_str; 618 1.4 jtc 619 1.36 christos while ((ch = *(const char *)str++) != '\0') { 620 1.25 christos switch (ch) { 621 1.25 christos /* Use C escapes for expected control characters */ 622 1.25 christos case '\\': ch = '\\'; break; /* backslash */ 623 1.25 christos case '\'': ch = '\''; break; /* single quote */ 624 1.25 christos case '"': ch = '"'; break; /* double quote */ 625 1.25 christos case '\a': ch = 'a'; break; /* alert */ 626 1.25 christos case '\b': ch = 'b'; break; /* backspace */ 627 1.25 christos case ESCAPE: ch = 'e'; break; /* escape */ 628 1.25 christos case '\f': ch = 'f'; break; /* form-feed */ 629 1.25 christos case '\n': ch = 'n'; break; /* newline */ 630 1.25 christos case '\r': ch = 'r'; break; /* carriage-return */ 631 1.25 christos case '\t': ch = 't'; break; /* tab */ 632 1.25 christos case '\v': ch = 'v'; break; /* vertical-tab */ 633 1.25 christos default: 634 1.25 christos /* Copy anything printable */ 635 1.36 christos if (isprint((unsigned char)ch)) { 636 1.25 christos *cp++ = ch; 637 1.25 christos continue; 638 1.25 christos } 639 1.25 christos /* Use vis(3) encodings for the rest */ 640 1.25 christos *cp++ = '\\'; 641 1.25 christos if (ch & 0200) { 642 1.25 christos *cp++ = 'M'; 643 1.36 christos ch &= (char)~0200; 644 1.25 christos } 645 1.25 christos if (ch == 0177) { 646 1.25 christos *cp++ = '^'; 647 1.25 christos *cp++ = '?'; 648 1.25 christos continue; 649 1.25 christos } 650 1.25 christos if (ch < 040) { 651 1.25 christos *cp++ = '^'; 652 1.25 christos *cp++ = ch | 0100; 653 1.25 christos continue; 654 1.25 christos } 655 1.25 christos *cp++ = '-'; 656 1.25 christos *cp++ = ch; 657 1.25 christos continue; 658 1.25 christos } 659 1.25 christos *cp++ = '\\'; 660 1.25 christos *cp++ = ch; 661 1.1 cgd } 662 1.4 jtc 663 1.25 christos *cp = 0; 664 1.25 christos return conv_str; 665 1.1 cgd } 666 1.1 cgd 667 1.5 jtc static char * 668 1.57 kre mklong(const char *str, char ch, char longer) 669 1.1 cgd { 670 1.5 jtc static char copy[64]; 671 1.15 cgd size_t len; 672 1.1 cgd 673 1.1 cgd len = strlen(str) + 2; 674 1.25 christos if (len > sizeof copy) { 675 1.53 kre warnx("format \"%s\" too complex", str); 676 1.25 christos len = 4; 677 1.53 kre rval = 1; 678 1.25 christos } 679 1.15 cgd (void)memmove(copy, str, len - 3); 680 1.57 kre copy[len - 3] = longer; 681 1.1 cgd copy[len - 2] = ch; 682 1.1 cgd copy[len - 1] = '\0'; 683 1.30 christos return copy; 684 1.1 cgd } 685 1.1 cgd 686 1.57 kre static wchar_t 687 1.23 wiz getchr(void) 688 1.1 cgd { 689 1.1 cgd if (!*gargv) 690 1.30 christos return 0; 691 1.57 kre return (wchar_t)wide_char(*gargv++, 0); 692 1.1 cgd } 693 1.1 cgd 694 1.5 jtc static char * 695 1.23 wiz getstr(void) 696 1.1 cgd { 697 1.30 christos static char empty[] = ""; 698 1.59 kre 699 1.1 cgd if (!*gargv) 700 1.30 christos return empty; 701 1.30 christos return *gargv++; 702 1.1 cgd } 703 1.1 cgd 704 1.5 jtc static int 705 1.25 christos getwidth(void) 706 1.1 cgd { 707 1.36 christos unsigned long val; 708 1.25 christos char *s, *ep; 709 1.25 christos 710 1.25 christos s = *gargv; 711 1.44 kre if (s == NULL) 712 1.42 kre return 0; 713 1.25 christos gargv++; 714 1.4 jtc 715 1.25 christos errno = 0; 716 1.25 christos val = strtoul(s, &ep, 0); 717 1.59 kre if (!isdigit(*(unsigned char *)s)) { 718 1.59 kre warnx("Arg %d: '%s' value for '*' width/precision" 719 1.59 kre " must be an unsigned integer", ARGNUM, s); 720 1.59 kre rval = 1; 721 1.59 kre val = 0; 722 1.59 kre } else 723 1.59 kre check_conversion(s, ep); 724 1.4 jtc 725 1.25 christos /* Arbitrarily 'restrict' field widths to 1Mbyte */ 726 1.36 christos if (val > 1 << 20) { 727 1.59 kre warnx("Arg %d: %s: invalid field width/precision", ARGNUM, s); 728 1.59 kre rval = 1; 729 1.25 christos return 0; 730 1.25 christos } 731 1.25 christos 732 1.36 christos return (int)val; 733 1.1 cgd } 734 1.1 cgd 735 1.22 kleink static intmax_t 736 1.23 wiz getintmax(void) 737 1.1 cgd { 738 1.22 kleink intmax_t val; 739 1.25 christos char *cp, *ep; 740 1.1 cgd 741 1.25 christos cp = *gargv; 742 1.25 christos if (cp == NULL) 743 1.25 christos return 0; 744 1.25 christos gargv++; 745 1.4 jtc 746 1.25 christos if (*cp == '\"' || *cp == '\'') 747 1.57 kre return wide_char(cp, 1); 748 1.4 jtc 749 1.11 jtc errno = 0; 750 1.25 christos val = strtoimax(cp, &ep, 0); 751 1.59 kre if (*cp != '+' && *cp != '-' && !isdigit(*(unsigned char *)cp)) { 752 1.59 kre warnx("Arg %d: '%s' numeric value required", ARGNUM, cp); 753 1.59 kre rval = 1; 754 1.59 kre } else 755 1.59 kre check_conversion(cp, ep); 756 1.11 jtc return val; 757 1.11 jtc } 758 1.11 jtc 759 1.57 kre static long double 760 1.23 wiz getdouble(void) 761 1.1 cgd { 762 1.57 kre long double val; 763 1.4 jtc char *ep; 764 1.1 cgd 765 1.1 cgd if (!*gargv) 766 1.42 kre return 0.0; 767 1.4 jtc 768 1.53 kre /* This is a NetBSD extension, not required by POSIX (it is useless) */ 769 1.53 kre if (*(ep = *gargv) == '\"' || *ep == '\'') 770 1.57 kre return (long double)wide_char(ep, 1); 771 1.1 cgd 772 1.11 jtc errno = 0; 773 1.57 kre val = strtold(*gargv, &ep); 774 1.12 jtc check_conversion(*gargv++, ep); 775 1.12 jtc return val; 776 1.12 jtc } 777 1.12 jtc 778 1.53 kre /* 779 1.57 kre * Fetch a wide character from the string given 780 1.53 kre * 781 1.57 kre * if all that character must consume the entire string 782 1.57 kre * after an initial leading byte (ascii char) is ignored, 783 1.57 kre * (used for parsing intger args using the 'X syntax) 784 1.53 kre * 785 1.57 kre * if !all then there is no requirement that the whole 786 1.57 kre * string be consumed (remaining characters are just ignored) 787 1.57 kre * but the character is to start at *p. 788 1.57 kre * (used for fetching the first chartacter of a string arg for %c) 789 1.53 kre */ 790 1.53 kre static intmax_t 791 1.57 kre wide_char(const char *p, int all) 792 1.53 kre { 793 1.57 kre wchar_t wch; 794 1.57 kre size_t len; 795 1.57 kre int n; 796 1.57 kre 797 1.57 kre (void)mbtowc(NULL, NULL, 0); 798 1.58 kre n = mbtowc(&wch, p + all, (len = strlen(p + all)) + 1); 799 1.57 kre if (n < 0) { 800 1.59 kre warn("Arg %d: %s", ARGNUM, p); 801 1.59 kre rval = 1; 802 1.57 kre } else if (all && (size_t)n != len) { 803 1.59 kre warnx("Arg %d: %s: not completely converted", 804 1.59 kre ARGNUM, p); 805 1.53 kre rval = 1; 806 1.53 kre } 807 1.53 kre 808 1.57 kre return (intmax_t) wch; 809 1.53 kre } 810 1.53 kre 811 1.12 jtc static void 812 1.23 wiz check_conversion(const char *s, const char *ep) 813 1.12 jtc { 814 1.59 kre if (!*s) { 815 1.59 kre warnx("Arg %d: unexpected empty value ('')", ARGNUM); 816 1.59 kre rval = 1; 817 1.59 kre return; 818 1.59 kre } 819 1.59 kre 820 1.4 jtc if (*ep) { 821 1.12 jtc if (ep == s) 822 1.59 kre warnx("Arg %d: %s: numeric value expected", ARGNUM, s); 823 1.11 jtc else 824 1.59 kre warnx("Arg %d: %s: not completely converted", 825 1.59 kre ARGNUM, s); 826 1.11 jtc rval = 1; 827 1.59 kre return; 828 1.59 kre } 829 1.59 kre 830 1.59 kre if (errno == ERANGE) { 831 1.59 kre warnx("Arg %d: %s: %s", ARGNUM, s, strerror(ERANGE)); 832 1.4 jtc rval = 1; 833 1.4 jtc } 834 1.5 jtc } 835 1.5 jtc 836 1.5 jtc static void 837 1.23 wiz usage(void) 838 1.5 jtc { 839 1.57 kre (void)fprintf(stderr, 840 1.57 kre "Usage: %s [-L] format [arg ...]\n", getprogname()); 841 1.1 cgd } 842