1 /* $Header: /src/pub/tcsh/tc.printf.c,v 3.24 2003/12/02 17:59:30 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("$Id: tc.printf.c,v 3.24 2003/12/02 17:59:30 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 32766 /* should be bigger than any field to print */ 45 46 static char buf[128]; 47 48 static void xaddchar __P((int)); 49 static void doprnt __P((void (*) __P((int)), const char *, va_list)); 50 51 static void 52 doprnt(addchar, sfmt, ap) 53 void (*addchar) __P((int)); 54 const char *sfmt; 55 va_list ap; 56 { 57 char *bp; 58 const char *f; 59 #ifdef SHORT_STRINGS 60 Char *Bp; 61 #endif /* SHORT_STRINGS */ 62 #ifdef HAVE_QUAD 63 long long l; 64 unsigned long long u; 65 #else 66 long l; 67 unsigned long u; 68 #endif 69 int i; 70 int fmt; 71 unsigned char pad = ' '; 72 int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0; 73 int sign = 0; 74 int attributes = 0; 75 76 77 f = sfmt; 78 for (; *f; f++) { 79 if (*f != '%') { /* then just out the char */ 80 (*addchar) ((int) (((unsigned char)*f) | attributes)); 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((char *) 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 132 fmt = (unsigned char) *f; 133 if (fmt != 'S' && fmt != 'Q' && Isupper(fmt)) { 134 do_long = 1; 135 fmt = Tolower(fmt); 136 } 137 bp = buf; 138 switch (fmt) { /* do the format */ 139 case 'd': 140 switch (do_long) { 141 case 0: 142 l = (long) (va_arg(ap, int)); 143 break; 144 case 1: 145 #ifndef HAVE_QUAD 146 default: 147 #endif 148 l = va_arg(ap, long); 149 break; 150 #ifdef HAVE_QUAD 151 default: 152 l = va_arg(ap, long long); 153 break; 154 #endif 155 } 156 157 if (l < 0) { 158 sign = 1; 159 l = -l; 160 } 161 do { 162 *bp++ = (char) (l % 10) + '0'; 163 } while ((l /= 10) > 0); 164 if (sign) 165 *bp++ = '-'; 166 f_width = f_width - (int) (bp - buf); 167 if (!flush_left) 168 while (f_width-- > 0) 169 (*addchar) ((int) (pad | attributes)); 170 for (bp--; bp >= buf; bp--) 171 (*addchar) ((int) (((unsigned char) *bp) | attributes)); 172 if (flush_left) 173 while (f_width-- > 0) 174 (*addchar) ((int) (' ' | attributes)); 175 break; 176 177 case 'p': 178 do_long = 1; 179 hash = 1; 180 fmt = 'x'; 181 /*FALLTHROUGH*/ 182 case 'o': 183 case 'x': 184 case 'u': 185 switch (do_long) { 186 case 0: 187 u = (unsigned long) (va_arg(ap, unsigned int)); 188 break; 189 case 1: 190 #ifndef HAVE_QUAD 191 default: 192 #endif 193 u = va_arg(ap, unsigned long); 194 break; 195 #ifdef HAVE_QUAD 196 default: 197 u = va_arg(ap, unsigned long long); 198 break; 199 #endif 200 } 201 if (fmt == 'u') { /* unsigned decimal */ 202 do { 203 *bp++ = (char) (u % 10) + '0'; 204 } while ((u /= 10) > 0); 205 } 206 else if (fmt == 'o') { /* octal */ 207 do { 208 *bp++ = (char) (u % 8) + '0'; 209 } while ((u /= 8) > 0); 210 if (hash) 211 *bp++ = '0'; 212 } 213 else if (fmt == 'x') { /* hex */ 214 do { 215 i = (int) (u % 16); 216 if (i < 10) 217 *bp++ = i + '0'; 218 else 219 *bp++ = i - 10 + 'a'; 220 } while ((u /= 16) > 0); 221 if (hash) { 222 *bp++ = 'x'; 223 *bp++ = '0'; 224 } 225 } 226 i = f_width - (int) (bp - buf); 227 if (!flush_left) 228 while (i-- > 0) 229 (*addchar) ((int) (pad | attributes)); 230 for (bp--; bp >= buf; bp--) 231 (*addchar) ((int) (((unsigned char) *bp) | attributes)); 232 if (flush_left) 233 while (i-- > 0) 234 (*addchar) ((int) (' ' | attributes)); 235 break; 236 237 238 case 'c': 239 i = va_arg(ap, int); 240 (*addchar) ((int) (i | attributes)); 241 break; 242 243 case 'S': 244 case 'Q': 245 #ifdef SHORT_STRINGS 246 Bp = va_arg(ap, Char *); 247 if (!Bp) { 248 bp = NULL; 249 goto lcase_s; 250 } 251 f_width = f_width - Strlen(Bp); 252 if (!flush_left) 253 while (f_width-- > 0) 254 (*addchar) ((int) (pad | attributes)); 255 for (i = 0; *Bp && i < prec; i++) { 256 if (fmt == 'Q' && *Bp & QUOTE) 257 (*addchar) ((int) ('\\' | attributes)); 258 (*addchar) ((int) ((*Bp & TRIM) | attributes)); 259 Bp++; 260 } 261 if (flush_left) 262 while (f_width-- > 0) 263 (*addchar) ((int) (' ' | attributes)); 264 break; 265 #endif /* SHORT_STRINGS */ 266 267 case 's': 268 case 'q': 269 bp = va_arg(ap, char *); 270 lcase_s: 271 if (!bp) 272 bp = "(nil)"; 273 f_width = f_width - strlen((char *) bp); 274 if (!flush_left) 275 while (f_width-- > 0) 276 (*addchar) ((int) (pad | attributes)); 277 for (i = 0; *bp && i < prec; i++) { 278 if (fmt == 'q' && *bp & QUOTE) 279 (*addchar) ((int) ('\\' | attributes)); 280 (*addchar) ((int) (((unsigned char) *bp & TRIM) | 281 attributes)); 282 bp++; 283 } 284 if (flush_left) 285 while (f_width-- > 0) 286 (*addchar) ((int) (' ' | attributes)); 287 break; 288 289 case 'a': 290 attributes = va_arg(ap, int); 291 break; 292 293 case '%': 294 (*addchar) ((int) ('%' | attributes)); 295 break; 296 297 default: 298 break; 299 } 300 flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0; 301 sign = 0; 302 pad = ' '; 303 } 304 } 305 } 306 307 308 static char *xstring, *xestring; 309 static void 310 xaddchar(c) 311 int c; 312 { 313 if (xestring == xstring) 314 *xstring = '\0'; 315 else 316 *xstring++ = (char) c; 317 } 318 319 320 pret_t 321 /*VARARGS*/ 322 #ifdef FUNCPROTO 323 xsnprintf(char *str, size_t size, const char *fmt, ...) 324 #else 325 xsnprintf(va_alist) 326 va_dcl 327 #endif 328 { 329 va_list va; 330 #ifdef FUNCPROTO 331 va_start(va, fmt); 332 #else 333 char *str, *fmt; 334 size_t size; 335 336 va_start(va); 337 str = va_arg(va, char *); 338 size = va_arg(va, size_t); 339 fmt = va_arg(va, char *); 340 #endif 341 342 xstring = str; 343 xestring = str + size - 1; 344 doprnt(xaddchar, fmt, va); 345 va_end(va); 346 *xstring++ = '\0'; 347 #ifdef PURIFY 348 return 1; 349 #endif 350 } 351 352 pret_t 353 /*VARARGS*/ 354 #ifdef FUNCPROTO 355 xprintf(const char *fmt, ...) 356 #else 357 xprintf(va_alist) 358 va_dcl 359 #endif 360 { 361 va_list va; 362 #ifdef FUNCPROTO 363 va_start(va, fmt); 364 #else 365 char *fmt; 366 367 va_start(va); 368 fmt = va_arg(va, char *); 369 #endif 370 doprnt(xputchar, fmt, va); 371 va_end(va); 372 #ifdef PURIFY 373 return 1; 374 #endif 375 } 376 377 378 pret_t 379 xvprintf(fmt, va) 380 const char *fmt; 381 va_list va; 382 { 383 doprnt(xputchar, fmt, va); 384 #ifdef PURIFY 385 return 1; 386 #endif 387 } 388 389 pret_t 390 xvsnprintf(str, size, fmt, va) 391 char *str; 392 size_t size; 393 const char *fmt; 394 va_list va; 395 { 396 xstring = str; 397 xestring = str + size - 1; 398 doprnt(xaddchar, fmt, va); 399 *xstring++ = '\0'; 400 #ifdef PURIFY 401 return 1; 402 #endif 403 } 404 405 406 407 #ifdef PURIFY 408 /* Purify uses (some of..) the following functions to output memory-use 409 * debugging info. Given all the messing with file descriptors that 410 * tcsh does, the easiest way I could think of to get it (Purify) to 411 * print anything was by replacing some standard functions with 412 * ones that do tcsh output directly - see dumb hook in doreaddirs() 413 * (sh.dir.c) -sg 414 */ 415 #ifndef FILE 416 #define FILE int 417 #endif 418 int 419 #ifdef FUNCPROTO 420 fprintf(FILE *fp, const char* fmt, ...) 421 #else 422 fprintf(va_alist) 423 va_dcl 424 #endif 425 { 426 va_list va; 427 #ifdef FUNCPROTO 428 va_start(va, fmt); 429 #else 430 FILE *fp; 431 const char *fmt; 432 433 va_start(va); 434 fp = va_arg(va, FILE *); 435 fmt = va_arg(va, const char *); 436 #endif 437 doprnt(xputchar, fmt, va); 438 va_end(va); 439 return 1; 440 } 441 442 int 443 vfprintf(fp, fmt, va) 444 FILE *fp; 445 const char *fmt; 446 va_list va; 447 { 448 doprnt(xputchar, fmt, va); 449 return 1; 450 } 451 452 #endif /* PURIFY */ 453