1 /* $Header: /src/pub/tcsh/tc.printf.c,v 3.23 2002/03/08 17:36:47 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.23 2002/03/08 17:36:47 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 'o': 178 case 'x': 179 case 'u': 180 switch (do_long) { 181 case 0: 182 u = (unsigned long) (va_arg(ap, unsigned int)); 183 break; 184 case 1: 185 #ifndef HAVE_QUAD 186 default: 187 #endif 188 u = va_arg(ap, unsigned long); 189 break; 190 #ifdef HAVE_QUAD 191 default: 192 u = va_arg(ap, unsigned long long); 193 break; 194 #endif 195 } 196 if (fmt == 'u') { /* unsigned decimal */ 197 do { 198 *bp++ = (char) (u % 10) + '0'; 199 } while ((u /= 10) > 0); 200 } 201 else if (fmt == 'o') { /* octal */ 202 do { 203 *bp++ = (char) (u % 8) + '0'; 204 } while ((u /= 8) > 0); 205 if (hash) 206 *bp++ = '0'; 207 } 208 else if (fmt == 'x') { /* hex */ 209 do { 210 i = (int) (u % 16); 211 if (i < 10) 212 *bp++ = i + '0'; 213 else 214 *bp++ = i - 10 + 'a'; 215 } while ((u /= 16) > 0); 216 if (hash) { 217 *bp++ = 'x'; 218 *bp++ = '0'; 219 } 220 } 221 i = f_width - (int) (bp - buf); 222 if (!flush_left) 223 while (i-- > 0) 224 (*addchar) ((int) (pad | attributes)); 225 for (bp--; bp >= buf; bp--) 226 (*addchar) ((int) (((unsigned char) *bp) | attributes)); 227 if (flush_left) 228 while (i-- > 0) 229 (*addchar) ((int) (' ' | attributes)); 230 break; 231 232 233 case 'c': 234 i = va_arg(ap, int); 235 (*addchar) ((int) (i | attributes)); 236 break; 237 238 case 'S': 239 case 'Q': 240 #ifdef SHORT_STRINGS 241 Bp = va_arg(ap, Char *); 242 if (!Bp) { 243 bp = NULL; 244 goto lcase_s; 245 } 246 f_width = f_width - Strlen(Bp); 247 if (!flush_left) 248 while (f_width-- > 0) 249 (*addchar) ((int) (pad | attributes)); 250 for (i = 0; *Bp && i < prec; i++) { 251 if (fmt == 'Q' && *Bp & QUOTE) 252 (*addchar) ((int) ('\\' | attributes)); 253 (*addchar) ((int) ((*Bp & TRIM) | attributes)); 254 Bp++; 255 } 256 if (flush_left) 257 while (f_width-- > 0) 258 (*addchar) ((int) (' ' | attributes)); 259 break; 260 #endif /* SHORT_STRINGS */ 261 262 case 's': 263 case 'q': 264 bp = va_arg(ap, char *); 265 lcase_s: 266 if (!bp) 267 bp = "(nil)"; 268 f_width = f_width - strlen((char *) bp); 269 if (!flush_left) 270 while (f_width-- > 0) 271 (*addchar) ((int) (pad | attributes)); 272 for (i = 0; *bp && i < prec; i++) { 273 if (fmt == 'q' && *bp & QUOTE) 274 (*addchar) ((int) ('\\' | attributes)); 275 (*addchar) ((int) (((unsigned char) *bp & TRIM) | 276 attributes)); 277 bp++; 278 } 279 if (flush_left) 280 while (f_width-- > 0) 281 (*addchar) ((int) (' ' | attributes)); 282 break; 283 284 case 'a': 285 attributes = va_arg(ap, int); 286 break; 287 288 case '%': 289 (*addchar) ((int) ('%' | attributes)); 290 break; 291 292 default: 293 break; 294 } 295 flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0; 296 sign = 0; 297 pad = ' '; 298 } 299 } 300 } 301 302 303 static char *xstring, *xestring; 304 static void 305 xaddchar(c) 306 int c; 307 { 308 if (xestring == xstring) 309 *xstring = '\0'; 310 else 311 *xstring++ = (char) c; 312 } 313 314 315 pret_t 316 /*VARARGS*/ 317 #ifdef FUNCPROTO 318 xsnprintf(char *str, size_t size, const char *fmt, ...) 319 #else 320 xsnprintf(va_alist) 321 va_dcl 322 #endif 323 { 324 va_list va; 325 #ifdef FUNCPROTO 326 va_start(va, fmt); 327 #else 328 char *str, *fmt; 329 size_t size; 330 331 va_start(va); 332 str = va_arg(va, char *); 333 size = va_arg(va, size_t); 334 fmt = va_arg(va, char *); 335 #endif 336 337 xstring = str; 338 xestring = str + size - 1; 339 doprnt(xaddchar, fmt, va); 340 va_end(va); 341 *xstring++ = '\0'; 342 #ifdef PURIFY 343 return 1; 344 #endif 345 } 346 347 pret_t 348 /*VARARGS*/ 349 #ifdef FUNCPROTO 350 xprintf(const char *fmt, ...) 351 #else 352 xprintf(va_alist) 353 va_dcl 354 #endif 355 { 356 va_list va; 357 #ifdef FUNCPROTO 358 va_start(va, fmt); 359 #else 360 char *fmt; 361 362 va_start(va); 363 fmt = va_arg(va, char *); 364 #endif 365 doprnt(xputchar, fmt, va); 366 va_end(va); 367 #ifdef PURIFY 368 return 1; 369 #endif 370 } 371 372 373 pret_t 374 xvprintf(fmt, va) 375 const char *fmt; 376 va_list va; 377 { 378 doprnt(xputchar, fmt, va); 379 #ifdef PURIFY 380 return 1; 381 #endif 382 } 383 384 pret_t 385 xvsnprintf(str, size, fmt, va) 386 char *str; 387 size_t size; 388 const char *fmt; 389 va_list va; 390 { 391 xstring = str; 392 xestring = str + size - 1; 393 doprnt(xaddchar, fmt, va); 394 *xstring++ = '\0'; 395 #ifdef PURIFY 396 return 1; 397 #endif 398 } 399 400 401 402 #ifdef PURIFY 403 /* Purify uses (some of..) the following functions to output memory-use 404 * debugging info. Given all the messing with file descriptors that 405 * tcsh does, the easiest way I could think of to get it (Purify) to 406 * print anything was by replacing some standard functions with 407 * ones that do tcsh output directly - see dumb hook in doreaddirs() 408 * (sh.dir.c) -sg 409 */ 410 #ifndef FILE 411 #define FILE int 412 #endif 413 int 414 #ifdef FUNCPROTO 415 fprintf(FILE *fp, const char* fmt, ...) 416 #else 417 fprintf(va_alist) 418 va_dcl 419 #endif 420 { 421 va_list va; 422 #ifdef FUNCPROTO 423 va_start(va, fmt); 424 #else 425 FILE *fp; 426 const char *fmt; 427 428 va_start(va); 429 fp = va_arg(va, FILE *); 430 fmt = va_arg(va, const char *); 431 #endif 432 doprnt(xputchar, fmt, va); 433 va_end(va); 434 return 1; 435 } 436 437 int 438 vfprintf(fp, fmt, va) 439 FILE *fp; 440 const char *fmt; 441 va_list va; 442 { 443 doprnt(xputchar, fmt, va); 444 return 1; 445 } 446 447 #endif /* PURIFY */ 448