1 /* $Header: /src/pub/tcsh/tc.printf.c,v 3.19 1998/10/25 15:10:37 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. 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.19 1998/10/25 15:10:37 christos 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 Bp = va_arg(ap, Char *); 211 if (!Bp) { 212 bp = NULL; 213 goto lcase_s; 214 } 215 f_width = f_width - Strlen(Bp); 216 if (!flush_left) 217 while (f_width-- > 0) 218 (*addchar) ((int) (pad | attributes)); 219 for (i = 0; *Bp && i < prec; i++) { 220 if (fmt == 'Q' && *Bp & QUOTE) 221 (*addchar) ((int) ('\\' | attributes)); 222 (*addchar) ((int) ((*Bp & TRIM) | attributes)); 223 Bp++; 224 } 225 if (flush_left) 226 while (f_width-- > 0) 227 (*addchar) ((int) (' ' | attributes)); 228 break; 229 230 case 's': 231 case 'q': 232 bp = va_arg(ap, char *); 233 lcase_s: 234 if (!bp) 235 bp = "(nil)"; 236 f_width = f_width - strlen((char *) bp); 237 if (!flush_left) 238 while (f_width-- > 0) 239 (*addchar) ((int) (pad | attributes)); 240 for (i = 0; *bp && i < prec; i++) { 241 if (fmt == 'q' && *bp & QUOTE) 242 (*addchar) ((int) ('\\' | attributes)); 243 (*addchar) ((int) (((unsigned char) *bp & TRIM) | 244 attributes)); 245 bp++; 246 } 247 if (flush_left) 248 while (f_width-- > 0) 249 (*addchar) ((int) (' ' | attributes)); 250 break; 251 252 case 'a': 253 attributes = va_arg(ap, int); 254 break; 255 256 case '%': 257 (*addchar) ((int) ('%' | attributes)); 258 break; 259 260 default: 261 break; 262 } 263 flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0; 264 sign = 0; 265 pad = ' '; 266 } 267 } 268 } 269 270 271 static char *xstring, *xestring; 272 static void 273 xaddchar(c) 274 int c; 275 { 276 if (xestring == xstring) 277 *xstring = '\0'; 278 else 279 *xstring++ = (char) c; 280 } 281 282 283 pret_t 284 /*VARARGS*/ 285 #ifdef FUNCPROTO 286 xsnprintf(char *str, size_t size, const char *fmt, ...) 287 #else 288 xsnprintf(va_alist) 289 va_dcl 290 #endif 291 { 292 va_list va; 293 #ifdef FUNCPROTO 294 va_start(va, fmt); 295 #else 296 char *str, *fmt; 297 size_t size; 298 299 va_start(va); 300 str = va_arg(va, char *); 301 size = va_arg(va, size_t); 302 fmt = va_arg(va, char *); 303 #endif 304 305 xstring = str; 306 xestring = str + size - 1; 307 doprnt(xaddchar, fmt, va); 308 va_end(va); 309 *xstring++ = '\0'; 310 #ifdef PURIFY 311 return 1; 312 #endif 313 } 314 315 pret_t 316 /*VARARGS*/ 317 #ifdef FUNCPROTO 318 xprintf(const char *fmt, ...) 319 #else 320 xprintf(va_alist) 321 va_dcl 322 #endif 323 { 324 va_list va; 325 #ifdef FUNCPROTO 326 va_start(va, fmt); 327 #else 328 char *fmt; 329 330 va_start(va); 331 fmt = va_arg(va, char *); 332 #endif 333 doprnt(xputchar, fmt, va); 334 va_end(va); 335 #ifdef PURIFY 336 return 1; 337 #endif 338 } 339 340 341 pret_t 342 xvprintf(fmt, va) 343 const char *fmt; 344 va_list va; 345 { 346 doprnt(xputchar, fmt, va); 347 #ifdef PURIFY 348 return 1; 349 #endif 350 } 351 352 pret_t 353 xvsnprintf(str, size, fmt, va) 354 char *str; 355 size_t size; 356 const char *fmt; 357 va_list va; 358 { 359 xstring = str; 360 xestring = str + size - 1; 361 doprnt(xaddchar, fmt, va); 362 *xstring++ = '\0'; 363 #ifdef PURIFY 364 return 1; 365 #endif 366 } 367 368 369 370 #ifdef PURIFY 371 /* Purify uses (some of..) the following functions to output memory-use 372 * debugging info. Given all the messing with file descriptors that 373 * tcsh does, the easiest way I could think of to get it (Purify) to 374 * print anything was by replacing some standard functions with 375 * ones that do tcsh output directly - see dumb hook in doreaddirs() 376 * (sh.dir.c) -sg 377 */ 378 #ifndef FILE 379 #define FILE int 380 #endif 381 int 382 #ifdef FUNCPROTO 383 fprintf(FILE *fp, const char* fmt, ...) 384 #else 385 fprintf(va_alist) 386 va_dcl 387 #endif 388 { 389 va_list va; 390 #ifdef FUNCPROTO 391 va_start(va, fmt); 392 #else 393 FILE *fp; 394 const char *fmt; 395 396 va_start(va); 397 fp = va_arg(va, FILE *); 398 fmt = va_arg(va, const char *); 399 #endif 400 doprnt(xputchar, fmt, va); 401 va_end(va); 402 return 1; 403 } 404 405 int 406 vfprintf(fp, fmt, va) 407 FILE *fp; 408 const char *fmt; 409 va_list va; 410 { 411 doprnt(xputchar, fmt, va); 412 return 1; 413 } 414 415 #endif /* PURIFY */ 416