1 /* 2 * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints 3 * through the putchar() routine. Feel free to use for 4 * anything... -- 7/17/87 Paul Placeway 5 */ 6 /*- 7 * Copyright (c) 1980, 1991 The Regents of the University of California. 8 * All rights reserved. 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 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include "sh.h" 35 36 #ifdef lint 37 #undef va_arg 38 #define va_arg(a, b) (a ? (b) 0 : (b) 0) 39 #endif 40 41 #define INF INT_MAX /* should be bigger than any field to print */ 42 43 static char snil[] = "(nil)"; 44 45 static void xaddchar (int); 46 static int doprnt (void (*) (int), const char *, va_list); 47 48 static int 49 doprnt(void (*addchar) (int), const char *sfmt, va_list ap) 50 { 51 char *bp; 52 const char *f; 53 #ifdef SHORT_STRINGS 54 const Char *Bp; 55 #endif /* SHORT_STRINGS */ 56 #ifdef HAVE_LONG_LONG 57 long long l; 58 unsigned long long u; 59 #else 60 long l; 61 unsigned long u; 62 #endif 63 char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */ 64 int i; 65 int fmt; 66 unsigned char pad = ' '; 67 int flush_left = 0, f_width = 0, prec = INF, hash = 0; 68 int do_long = 0, do_size_t = 0, do_ptrdiff_t = 0; 69 int sign = 0, count = 0; 70 int attributes = 0; 71 72 73 f = sfmt; 74 for (; *f; f++) { 75 if (*f != '%') { /* then just out the char */ 76 (*addchar) (((unsigned char)*f) | attributes); 77 count++; 78 } 79 else { 80 f++; /* skip the % */ 81 82 if (*f == '-') { /* minus: flush left */ 83 flush_left = 1; 84 f++; 85 } 86 87 if (*f == '0' || *f == '.') { 88 /* padding with 0 rather than blank */ 89 pad = '0'; 90 f++; 91 } 92 if (*f == '*') { /* field width */ 93 f_width = va_arg(ap, int); 94 f++; 95 } 96 else if (isdigit((unsigned char) *f)) { 97 f_width = atoi(f); 98 while (isdigit((unsigned char) *f)) 99 f++; /* skip the digits */ 100 } 101 102 if (*f == '.') { /* precision */ 103 f++; 104 if (*f == '*') { 105 prec = va_arg(ap, int); 106 f++; 107 } 108 else if (isdigit((unsigned char) *f)) { 109 prec = atoi(f); 110 while (isdigit((unsigned char) *f)) 111 f++; /* skip the digits */ 112 } 113 } 114 115 if (*f == '#') { /* alternate form */ 116 hash = 1; 117 f++; 118 } 119 120 if (*f == 'l') { /* long format */ 121 do_long++; 122 f++; 123 if (*f == 'l') { 124 do_long++; 125 f++; 126 } 127 } 128 if (*f == 'z') { /* size_t format */ 129 do_size_t++; 130 f++; 131 } 132 if (*f == 't') { /* ptrdiff_t format */ 133 do_ptrdiff_t++; 134 f++; 135 } 136 137 fmt = (unsigned char) *f; 138 if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) { 139 do_long = 1; 140 fmt = tolower(fmt); 141 } 142 bp = buf; 143 switch (fmt) { /* do the format */ 144 case 'd': 145 switch (do_long) { 146 case 0: 147 if (do_size_t) 148 l = (long) (va_arg(ap, size_t)); 149 else 150 l = (long) (va_arg(ap, int)); 151 break; 152 case 1: 153 #ifndef HAVE_LONG_LONG 154 default: 155 #endif 156 l = va_arg(ap, long); 157 break; 158 #ifdef HAVE_LONG_LONG 159 default: 160 l = va_arg(ap, long long); 161 break; 162 #endif 163 } 164 165 if (l < 0) { 166 sign = 1; 167 l = -l; 168 } 169 do { 170 *bp++ = (char) (l % 10) + '0'; 171 } while ((l /= 10) > 0); 172 if (sign) 173 *bp++ = '-'; 174 f_width = f_width - (int) (bp - buf); 175 if (!flush_left) 176 while (f_width-- > 0) { 177 (*addchar) (pad | attributes); 178 count++; 179 } 180 for (bp--; bp >= buf; bp--) { 181 (*addchar) (((unsigned char) *bp) | attributes); 182 count++; 183 } 184 if (flush_left) 185 while (f_width-- > 0) { 186 (*addchar) (' ' | attributes); 187 count++; 188 } 189 break; 190 191 case 'p': 192 do_long = 1; 193 hash = 1; 194 fmt = 'x'; 195 /*FALLTHROUGH*/ 196 case 'o': 197 case 'x': 198 case 'u': 199 switch (do_long) { 200 case 0: 201 if (do_size_t) 202 u = va_arg(ap, size_t); 203 else if (do_ptrdiff_t) 204 u = va_arg(ap, ptrdiff_t); 205 else 206 u = va_arg(ap, unsigned int); 207 break; 208 case 1: 209 #ifndef HAVE_LONG_LONG 210 default: 211 #endif 212 u = va_arg(ap, unsigned long); 213 break; 214 #ifdef HAVE_LONG_LONG 215 default: 216 u = va_arg(ap, unsigned long long); 217 break; 218 #endif 219 } 220 if (fmt == 'u') { /* unsigned decimal */ 221 do { 222 *bp++ = (char) (u % 10) + '0'; 223 } while ((u /= 10) > 0); 224 } 225 else if (fmt == 'o') { /* octal */ 226 do { 227 *bp++ = (char) (u % 8) + '0'; 228 } while ((u /= 8) > 0); 229 if (hash) 230 *bp++ = '0'; 231 } 232 else if (fmt == 'x') { /* hex */ 233 do { 234 i = (int) (u % 16); 235 if (i < 10) 236 *bp++ = i + '0'; 237 else 238 *bp++ = i - 10 + 'a'; 239 } while ((u /= 16) > 0); 240 if (hash) { 241 *bp++ = 'x'; 242 *bp++ = '0'; 243 } 244 } 245 i = f_width - (int) (bp - buf); 246 if (!flush_left) 247 while (i-- > 0) { 248 (*addchar) (pad | attributes); 249 count++; 250 } 251 for (bp--; bp >= buf; bp--) 252 (*addchar) (((unsigned char) *bp) | attributes); 253 if (flush_left) 254 while (i-- > 0) { 255 (*addchar) (' ' | attributes); 256 count++; 257 } 258 break; 259 260 261 case 'c': 262 i = va_arg(ap, int); 263 (*addchar) (i | attributes); 264 count++; 265 break; 266 267 case 'S': 268 case 'Q': 269 #ifdef SHORT_STRINGS 270 Bp = va_arg(ap, Char *); 271 if (!Bp) { 272 bp = NULL; 273 goto lcase_s; 274 } 275 f_width = f_width - Strlen(Bp); 276 if (!flush_left) 277 while (f_width-- > 0) { 278 (*addchar) ((int) (pad | attributes)); 279 count++; 280 } 281 for (i = 0; *Bp && i < prec; i++) { 282 char cbuf[MB_LEN_MAX]; 283 size_t pos, len; 284 285 if (fmt == 'Q' && *Bp & QUOTE) { 286 (*addchar) ('\\' | attributes); 287 count++; 288 } 289 len = one_wctomb(cbuf, *Bp); 290 for (pos = 0; pos < len; pos++) { 291 (*addchar) ((unsigned char)cbuf[pos] | attributes 292 | (*Bp & ATTRIBUTES)); 293 count++; 294 } 295 Bp++; 296 } 297 if (flush_left) 298 while (f_width-- > 0) { 299 (*addchar) (' ' | attributes); 300 count++; 301 } 302 break; 303 #endif /* SHORT_STRINGS */ 304 305 case 's': 306 case 'q': 307 bp = va_arg(ap, char *); 308 lcase_s: 309 if (!bp) 310 bp = snil; 311 f_width = f_width - strlen(bp); 312 if (!flush_left) 313 while (f_width-- > 0) { 314 (*addchar) (pad | attributes); 315 count++; 316 } 317 for (i = 0; *bp && i < prec; i++) { 318 if (fmt == 'q' && *bp & QUOTE) { 319 (*addchar) ('\\' | attributes); 320 count++; 321 } 322 (*addchar) (((unsigned char) *bp & TRIM) | attributes); 323 count++; 324 bp++; 325 } 326 if (flush_left) 327 while (f_width-- > 0) { 328 (*addchar) (' ' | attributes); 329 count++; 330 } 331 break; 332 333 case 'a': 334 attributes = va_arg(ap, int); 335 break; 336 337 case '%': 338 (*addchar) ('%' | attributes); 339 count++; 340 break; 341 342 default: 343 break; 344 } 345 flush_left = 0, f_width = 0, prec = INF, hash = 0; 346 do_ptrdiff_t = 0, do_size_t = 0, do_long = 0; 347 sign = 0; 348 pad = ' '; 349 } 350 } 351 return count; 352 } 353 354 355 static char *xstring, *xestring; 356 static void 357 xaddchar(int c) 358 { 359 if (xestring == xstring) 360 *xstring = '\0'; 361 else 362 *xstring++ = (char) c; 363 } 364 365 366 int 367 /*VARARGS*/ 368 xsnprintf(char *str, size_t size, const char *fmt, ...) 369 { 370 int count; 371 va_list va; 372 va_start(va, fmt); 373 374 xstring = str; 375 xestring = str + size - 1; 376 count = doprnt(xaddchar, fmt, va); 377 va_end(va); 378 *xstring++ = '\0'; 379 return count; 380 } 381 382 int 383 /*VARARGS*/ 384 xprintf(const char *fmt, ...) 385 { 386 int count; 387 va_list va; 388 va_start(va, fmt); 389 count = doprnt(xputchar, fmt, va); 390 va_end(va); 391 return count; 392 } 393 394 int 395 xvprintf(const char *fmt, va_list va) 396 { 397 return doprnt(xputchar, fmt, va); 398 } 399 400 int 401 xvsnprintf(char *str, size_t size, const char *fmt, va_list va) 402 { 403 int count; 404 xstring = str; 405 xestring = str + size - 1; 406 count = doprnt(xaddchar, fmt, va); 407 *xstring++ = '\0'; 408 return count; 409 } 410 411 char * 412 xvasprintf(const char *fmt, va_list va) 413 { 414 size_t size; 415 char *buf; 416 417 buf = NULL; 418 size = 2048; /* Arbitrary */ 419 for (;;) { 420 va_list copy; 421 422 buf = xrealloc(buf, size); 423 xstring = buf; 424 xestring = buf + size - 1; 425 va_copy(copy, va); 426 doprnt(xaddchar, fmt, copy); 427 va_end(copy); 428 if (xstring < xestring) 429 break; 430 size *= 2; 431 } 432 *xstring++ = '\0'; 433 return xrealloc(buf, xstring - buf); 434 } 435 436 char * 437 xasprintf(const char *fmt, ...) 438 { 439 va_list va; 440 char *ret; 441 442 va_start (va, fmt); 443 ret = xvasprintf(fmt, va); 444 va_end(va); 445 return ret; 446 } 447 448 449 #ifdef PURIFY 450 /* Purify uses (some of..) the following functions to output memory-use 451 * debugging info. Given all the messing with file descriptors that 452 * tcsh does, the easiest way I could think of to get it (Purify) to 453 * print anything was by replacing some standard functions with 454 * ones that do tcsh output directly - see dumb hook in doreaddirs() 455 * (sh.dir.c) -sg 456 */ 457 #ifndef FILE 458 #define FILE int 459 #endif 460 int 461 fprintf(FILE *fp, const char* fmt, ...) 462 { 463 int count; 464 va_list va; 465 va_start(va, fmt); 466 count = doprnt(xputchar, fmt, va); 467 va_end(va); 468 return count; 469 } 470 471 int 472 vfprintf(FILE *fp, const char *fmt, va_list va) 473 { 474 return doprnt(xputchar, fmt, va); 475 } 476 477 #endif /* PURIFY */ 478