tc.printf.c revision 100616
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 37RCSID("$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 46static char buf[128]; 47 48static void xaddchar __P((int)); 49static void doprnt __P((void (*) __P((int)), const char *, va_list)); 50 51static void 52doprnt(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 *); 265lcase_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 303static char *xstring, *xestring; 304static void 305xaddchar(c) 306 int c; 307{ 308 if (xestring == xstring) 309 *xstring = '\0'; 310 else 311 *xstring++ = (char) c; 312} 313 314 315pret_t 316/*VARARGS*/ 317#ifdef FUNCPROTO 318xsnprintf(char *str, size_t size, const char *fmt, ...) 319#else 320xsnprintf(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 347pret_t 348/*VARARGS*/ 349#ifdef FUNCPROTO 350xprintf(const char *fmt, ...) 351#else 352xprintf(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 373pret_t 374xvprintf(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 384pret_t 385xvsnprintf(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 413int 414#ifdef FUNCPROTO 415fprintf(FILE *fp, const char* fmt, ...) 416#else 417fprintf(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 437int 438vfprintf(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