1 /* $Header: /p/tcsh/cvsroot/tcsh/tc.printf.c,v 3.35 2006/03/02 18:46:45 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.35 2006/03/02 18:46:45 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 void doprnt (void (*) (int), const char *, va_list); 50 51 static void 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; 72 int sign = 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 } 81 else { 82 f++; /* skip the % */ 83 84 if (*f == '-') { /* minus: flush left */ 85 flush_left = 1; 86 f++; 87 } 88 89 if (*f == '0' || *f == '.') { 90 /* padding with 0 rather than blank */ 91 pad = '0'; 92 f++; 93 } 94 if (*f == '*') { /* field width */ 95 f_width = va_arg(ap, int); 96 f++; 97 } 98 else if (isdigit((unsigned char) *f)) { 99 f_width = atoi(f); 100 while (isdigit((unsigned char) *f)) 101 f++; /* skip the digits */ 102 } 103 104 if (*f == '.') { /* precision */ 105 f++; 106 if (*f == '*') { 107 prec = va_arg(ap, int); 108 f++; 109 } 110 else if (isdigit((unsigned char) *f)) { 111 prec = atoi(f); 112 while (isdigit((unsigned char) *f)) 113 f++; /* skip the digits */ 114 } 115 } 116 117 if (*f == '#') { /* alternate form */ 118 hash = 1; 119 f++; 120 } 121 122 if (*f == 'l') { /* long format */ 123 do_long++; 124 f++; 125 if (*f == 'l') { 126 do_long++; 127 f++; 128 } 129 } 130 if (*f == 'z') { /* size_t format */ 131 do_size_t++; 132 f++; 133 } 134 135 fmt = (unsigned char) *f; 136 if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) { 137 do_long = 1; 138 fmt = tolower(fmt); 139 } 140 bp = buf; 141 switch (fmt) { /* do the format */ 142 case 'd': 143 switch (do_long) { 144 case 0: 145 if (do_size_t) 146 l = (long) (va_arg(ap, size_t)); 147 else 148 l = (long) (va_arg(ap, int)); 149 break; 150 case 1: 151 #ifndef HAVE_LONG_LONG 152 default: 153 #endif 154 l = va_arg(ap, long); 155 break; 156 #ifdef HAVE_LONG_LONG 157 default: 158 l = va_arg(ap, long long); 159 break; 160 #endif 161 } 162 163 if (l < 0) { 164 sign = 1; 165 l = -l; 166 } 167 do { 168 *bp++ = (char) (l % 10) + '0'; 169 } while ((l /= 10) > 0); 170 if (sign) 171 *bp++ = '-'; 172 f_width = f_width - (int) (bp - buf); 173 if (!flush_left) 174 while (f_width-- > 0) 175 (*addchar) (pad | attributes); 176 for (bp--; bp >= buf; bp--) 177 (*addchar) (((unsigned char) *bp) | attributes); 178 if (flush_left) 179 while (f_width-- > 0) 180 (*addchar) (' ' | attributes); 181 break; 182 183 case 'p': 184 do_long = 1; 185 hash = 1; 186 fmt = 'x'; 187 /*FALLTHROUGH*/ 188 case 'o': 189 case 'x': 190 case 'u': 191 switch (do_long) { 192 case 0: 193 if (do_size_t) 194 u = va_arg(ap, size_t); 195 else 196 u = va_arg(ap, unsigned int); 197 break; 198 case 1: 199 #ifndef HAVE_LONG_LONG 200 default: 201 #endif 202 u = va_arg(ap, unsigned long); 203 break; 204 #ifdef HAVE_LONG_LONG 205 default: 206 u = va_arg(ap, unsigned long long); 207 break; 208 #endif 209 } 210 if (fmt == 'u') { /* unsigned decimal */ 211 do { 212 *bp++ = (char) (u % 10) + '0'; 213 } while ((u /= 10) > 0); 214 } 215 else if (fmt == 'o') { /* octal */ 216 do { 217 *bp++ = (char) (u % 8) + '0'; 218 } while ((u /= 8) > 0); 219 if (hash) 220 *bp++ = '0'; 221 } 222 else if (fmt == 'x') { /* hex */ 223 do { 224 i = (int) (u % 16); 225 if (i < 10) 226 *bp++ = i + '0'; 227 else 228 *bp++ = i - 10 + 'a'; 229 } while ((u /= 16) > 0); 230 if (hash) { 231 *bp++ = 'x'; 232 *bp++ = '0'; 233 } 234 } 235 i = f_width - (int) (bp - buf); 236 if (!flush_left) 237 while (i-- > 0) 238 (*addchar) (pad | attributes); 239 for (bp--; bp >= buf; bp--) 240 (*addchar) (((unsigned char) *bp) | attributes); 241 if (flush_left) 242 while (i-- > 0) 243 (*addchar) (' ' | attributes); 244 break; 245 246 247 case 'c': 248 i = va_arg(ap, int); 249 (*addchar) (i | attributes); 250 break; 251 252 case 'S': 253 case 'Q': 254 #ifdef SHORT_STRINGS 255 Bp = va_arg(ap, Char *); 256 if (!Bp) { 257 bp = NULL; 258 goto lcase_s; 259 } 260 f_width = f_width - Strlen(Bp); 261 if (!flush_left) 262 while (f_width-- > 0) 263 (*addchar) ((int) (pad | attributes)); 264 for (i = 0; *Bp && i < prec; i++) { 265 char cbuf[MB_LEN_MAX]; 266 size_t pos, len; 267 268 if (fmt == 'Q' && *Bp & QUOTE) 269 (*addchar) ('\\' | attributes); 270 len = one_wctomb(cbuf, *Bp & CHAR); 271 for (pos = 0; pos < len; pos++) 272 (*addchar) ((unsigned char)cbuf[pos] | attributes 273 | (*Bp & ATTRIBUTES)); 274 Bp++; 275 } 276 if (flush_left) 277 while (f_width-- > 0) 278 (*addchar) (' ' | attributes); 279 break; 280 #endif /* SHORT_STRINGS */ 281 282 case 's': 283 case 'q': 284 bp = va_arg(ap, char *); 285 lcase_s: 286 if (!bp) 287 bp = snil; 288 f_width = f_width - strlen(bp); 289 if (!flush_left) 290 while (f_width-- > 0) 291 (*addchar) (pad | attributes); 292 for (i = 0; *bp && i < prec; i++) { 293 if (fmt == 'q' && *bp & QUOTE) 294 (*addchar) ('\\' | attributes); 295 (*addchar) (((unsigned char) *bp & TRIM) | attributes); 296 bp++; 297 } 298 if (flush_left) 299 while (f_width-- > 0) 300 (*addchar) (' ' | attributes); 301 break; 302 303 case 'a': 304 attributes = va_arg(ap, int); 305 break; 306 307 case '%': 308 (*addchar) ('%' | attributes); 309 break; 310 311 default: 312 break; 313 } 314 flush_left = 0, f_width = 0, prec = INF, hash = 0; 315 do_size_t = 0, do_long = 0; 316 sign = 0; 317 pad = ' '; 318 } 319 } 320 } 321 322 323 static char *xstring, *xestring; 324 static void 325 xaddchar(int c) 326 { 327 if (xestring == xstring) 328 *xstring = '\0'; 329 else 330 *xstring++ = (char) c; 331 } 332 333 334 pret_t 335 /*VARARGS*/ 336 xsnprintf(char *str, size_t size, const char *fmt, ...) 337 { 338 va_list va; 339 va_start(va, fmt); 340 341 xstring = str; 342 xestring = str + size - 1; 343 doprnt(xaddchar, fmt, va); 344 va_end(va); 345 *xstring++ = '\0'; 346 #ifdef PURIFY 347 return 1; 348 #endif 349 } 350 351 pret_t 352 /*VARARGS*/ 353 xprintf(const char *fmt, ...) 354 { 355 va_list va; 356 va_start(va, fmt); 357 doprnt(xputchar, fmt, va); 358 va_end(va); 359 #ifdef PURIFY 360 return 1; 361 #endif 362 } 363 364 365 pret_t 366 xvprintf(const char *fmt, va_list va) 367 { 368 doprnt(xputchar, fmt, va); 369 #ifdef PURIFY 370 return 1; 371 #endif 372 } 373 374 pret_t 375 xvsnprintf(char *str, size_t size, const char *fmt, va_list va) 376 { 377 xstring = str; 378 xestring = str + size - 1; 379 doprnt(xaddchar, fmt, va); 380 *xstring++ = '\0'; 381 #ifdef PURIFY 382 return 1; 383 #endif 384 } 385 386 char * 387 xvasprintf(const char *fmt, va_list va) 388 { 389 size_t size; 390 char *buf; 391 392 buf = NULL; 393 size = 2048; /* Arbitrary */ 394 for (;;) { 395 va_list copy; 396 397 buf = xrealloc(buf, size); 398 xstring = buf; 399 xestring = buf + size - 1; 400 va_copy(copy, va); 401 doprnt(xaddchar, fmt, copy); 402 va_end(copy); 403 if (xstring < xestring) 404 break; 405 size *= 2; 406 } 407 *xstring++ = '\0'; 408 return xrealloc(buf, xstring - buf); 409 } 410 411 char * 412 xasprintf(const char *fmt, ...) 413 { 414 va_list va; 415 char *ret; 416 417 va_start (va, fmt); 418 ret = xvasprintf(fmt, va); 419 va_end(va); 420 return ret; 421 } 422 423 424 #ifdef PURIFY 425 /* Purify uses (some of..) the following functions to output memory-use 426 * debugging info. Given all the messing with file descriptors that 427 * tcsh does, the easiest way I could think of to get it (Purify) to 428 * print anything was by replacing some standard functions with 429 * ones that do tcsh output directly - see dumb hook in doreaddirs() 430 * (sh.dir.c) -sg 431 */ 432 #ifndef FILE 433 #define FILE int 434 #endif 435 int 436 fprintf(FILE *fp, const char* fmt, ...) 437 { 438 va_list va; 439 va_start(va, fmt); 440 doprnt(xputchar, fmt, va); 441 va_end(va); 442 return 1; 443 } 444 445 int 446 vfprintf(FILE *fp, const char *fmt, va_list va) 447 { 448 doprnt(xputchar, fmt, va); 449 return 1; 450 } 451 452 #endif /* PURIFY */ 453