1 /* $Header: /src/pub/tcsh/tc.printf.c,v 3.21 2001/06/21 23:26:54 kim 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. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 #include "sh.h" 40 41 RCSID("$Id: tc.printf.c,v 3.21 2001/06/21 23:26:54 kim Exp $") 42 43 #ifdef lint 44 #undef va_arg 45 #define va_arg(a, b) (a ? (b) 0 : (b) 0) 46 #endif 47 48 #define INF 32766 /* should be bigger than any field to print */ 49 50 static char buf[128]; 51 52 static void xaddchar __P((int)); 53 static void doprnt __P((void (*) __P((int)), const char *, va_list)); 54 55 static void 56 doprnt(addchar, sfmt, ap) 57 void (*addchar) __P((int)); 58 const char *sfmt; 59 va_list ap; 60 { 61 register char *bp; 62 register const char *f; 63 #ifdef SHORT_STRINGS 64 register Char *Bp; 65 #endif /* SHORT_STRINGS */ 66 register long l; 67 register unsigned long u; 68 register int i; 69 register int fmt; 70 register unsigned char pad = ' '; 71 int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 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) ((int) (((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((char *) 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 = 1; 124 f++; 125 } 126 127 fmt = (unsigned char) *f; 128 if (fmt != 'S' && fmt != 'Q' && Isupper(fmt)) { 129 do_long = 1; 130 fmt = Tolower(fmt); 131 } 132 bp = buf; 133 switch (fmt) { /* do the format */ 134 case 'd': 135 if (do_long) 136 l = va_arg(ap, long); 137 else 138 l = (long) (va_arg(ap, int)); 139 if (l < 0) { 140 sign = 1; 141 l = -l; 142 } 143 do { 144 *bp++ = (char) (l % 10) + '0'; 145 } while ((l /= 10) > 0); 146 if (sign) 147 *bp++ = '-'; 148 f_width = f_width - (int) (bp - buf); 149 if (!flush_left) 150 while (f_width-- > 0) 151 (*addchar) ((int) (pad | attributes)); 152 for (bp--; bp >= buf; bp--) 153 (*addchar) ((int) (((unsigned char) *bp) | attributes)); 154 if (flush_left) 155 while (f_width-- > 0) 156 (*addchar) ((int) (' ' | attributes)); 157 break; 158 159 case 'o': 160 case 'x': 161 case 'u': 162 if (do_long) 163 u = va_arg(ap, unsigned long); 164 else 165 u = (unsigned long) (va_arg(ap, unsigned int)); 166 if (fmt == 'u') { /* unsigned decimal */ 167 do { 168 *bp++ = (char) (u % 10) + '0'; 169 } while ((u /= 10) > 0); 170 } 171 else if (fmt == 'o') { /* octal */ 172 do { 173 *bp++ = (char) (u % 8) + '0'; 174 } while ((u /= 8) > 0); 175 if (hash) 176 *bp++ = '0'; 177 } 178 else if (fmt == 'x') { /* hex */ 179 do { 180 i = (int) (u % 16); 181 if (i < 10) 182 *bp++ = i + '0'; 183 else 184 *bp++ = i - 10 + 'a'; 185 } while ((u /= 16) > 0); 186 if (hash) { 187 *bp++ = 'x'; 188 *bp++ = '0'; 189 } 190 } 191 i = f_width - (int) (bp - buf); 192 if (!flush_left) 193 while (i-- > 0) 194 (*addchar) ((int) (pad | attributes)); 195 for (bp--; bp >= buf; bp--) 196 (*addchar) ((int) (((unsigned char) *bp) | attributes)); 197 if (flush_left) 198 while (i-- > 0) 199 (*addchar) ((int) (' ' | attributes)); 200 break; 201 202 203 case 'c': 204 i = va_arg(ap, int); 205 (*addchar) ((int) (i | attributes)); 206 break; 207 208 case 'S': 209 case 'Q': 210 #ifdef SHORT_STRINGS 211 Bp = va_arg(ap, Char *); 212 if (!Bp) { 213 bp = NULL; 214 goto lcase_s; 215 } 216 f_width = f_width - Strlen(Bp); 217 if (!flush_left) 218 while (f_width-- > 0) 219 (*addchar) ((int) (pad | attributes)); 220 for (i = 0; *Bp && i < prec; i++) { 221 if (fmt == 'Q' && *Bp & QUOTE) 222 (*addchar) ((int) ('\\' | attributes)); 223 (*addchar) ((int) ((*Bp & TRIM) | attributes)); 224 Bp++; 225 } 226 if (flush_left) 227 while (f_width-- > 0) 228 (*addchar) ((int) (' ' | attributes)); 229 break; 230 #endif /* SHORT_STRINGS */ 231 232 case 's': 233 case 'q': 234 bp = va_arg(ap, char *); 235 lcase_s: 236 if (!bp) 237 bp = "(nil)"; 238 f_width = f_width - strlen((char *) bp); 239 if (!flush_left) 240 while (f_width-- > 0) 241 (*addchar) ((int) (pad | attributes)); 242 for (i = 0; *bp && i < prec; i++) { 243 if (fmt == 'q' && *bp & QUOTE) 244 (*addchar) ((int) ('\\' | attributes)); 245 (*addchar) ((int) (((unsigned char) *bp & TRIM) | 246 attributes)); 247 bp++; 248 } 249 if (flush_left) 250 while (f_width-- > 0) 251 (*addchar) ((int) (' ' | attributes)); 252 break; 253 254 case 'a': 255 attributes = va_arg(ap, int); 256 break; 257 258 case '%': 259 (*addchar) ((int) ('%' | attributes)); 260 break; 261 262 default: 263 break; 264 } 265 flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0; 266 sign = 0; 267 pad = ' '; 268 } 269 } 270 } 271 272 273 static char *xstring, *xestring; 274 static void 275 xaddchar(c) 276 int c; 277 { 278 if (xestring == xstring) 279 *xstring = '\0'; 280 else 281 *xstring++ = (char) c; 282 } 283 284 285 pret_t 286 /*VARARGS*/ 287 #ifdef FUNCPROTO 288 xsnprintf(char *str, size_t size, const char *fmt, ...) 289 #else 290 xsnprintf(va_alist) 291 va_dcl 292 #endif 293 { 294 va_list va; 295 #ifdef FUNCPROTO 296 va_start(va, fmt); 297 #else 298 char *str, *fmt; 299 size_t size; 300 301 va_start(va); 302 str = va_arg(va, char *); 303 size = va_arg(va, size_t); 304 fmt = va_arg(va, char *); 305 #endif 306 307 xstring = str; 308 xestring = str + size - 1; 309 doprnt(xaddchar, fmt, va); 310 va_end(va); 311 *xstring++ = '\0'; 312 #ifdef PURIFY 313 return 1; 314 #endif 315 } 316 317 pret_t 318 /*VARARGS*/ 319 #ifdef FUNCPROTO 320 xprintf(const char *fmt, ...) 321 #else 322 xprintf(va_alist) 323 va_dcl 324 #endif 325 { 326 va_list va; 327 #ifdef FUNCPROTO 328 va_start(va, fmt); 329 #else 330 char *fmt; 331 332 va_start(va); 333 fmt = va_arg(va, char *); 334 #endif 335 doprnt(xputchar, fmt, va); 336 va_end(va); 337 #ifdef PURIFY 338 return 1; 339 #endif 340 } 341 342 343 pret_t 344 xvprintf(fmt, va) 345 const char *fmt; 346 va_list va; 347 { 348 doprnt(xputchar, fmt, va); 349 #ifdef PURIFY 350 return 1; 351 #endif 352 } 353 354 pret_t 355 xvsnprintf(str, size, fmt, va) 356 char *str; 357 size_t size; 358 const char *fmt; 359 va_list va; 360 { 361 xstring = str; 362 xestring = str + size - 1; 363 doprnt(xaddchar, fmt, va); 364 *xstring++ = '\0'; 365 #ifdef PURIFY 366 return 1; 367 #endif 368 } 369 370 371 372 #ifdef PURIFY 373 /* Purify uses (some of..) the following functions to output memory-use 374 * debugging info. Given all the messing with file descriptors that 375 * tcsh does, the easiest way I could think of to get it (Purify) to 376 * print anything was by replacing some standard functions with 377 * ones that do tcsh output directly - see dumb hook in doreaddirs() 378 * (sh.dir.c) -sg 379 */ 380 #ifndef FILE 381 #define FILE int 382 #endif 383 int 384 #ifdef FUNCPROTO 385 fprintf(FILE *fp, const char* fmt, ...) 386 #else 387 fprintf(va_alist) 388 va_dcl 389 #endif 390 { 391 va_list va; 392 #ifdef FUNCPROTO 393 va_start(va, fmt); 394 #else 395 FILE *fp; 396 const char *fmt; 397 398 va_start(va); 399 fp = va_arg(va, FILE *); 400 fmt = va_arg(va, const char *); 401 #endif 402 doprnt(xputchar, fmt, va); 403 va_end(va); 404 return 1; 405 } 406 407 int 408 vfprintf(fp, fmt, va) 409 FILE *fp; 410 const char *fmt; 411 va_list va; 412 { 413 doprnt(xputchar, fmt, va); 414 return 1; 415 } 416 417 #endif /* PURIFY */ 418