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