1316958Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/tc.printf.c,v 3.38 2015/06/06 21:19:08 christos Exp $ */ 259243Sobrien/* 359243Sobrien * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints 459243Sobrien * through the putchar() routine. Feel free to use for 559243Sobrien * anything... -- 7/17/87 Paul Placeway 659243Sobrien */ 759243Sobrien/*- 859243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 959243Sobrien * All rights reserved. 1059243Sobrien * 1159243Sobrien * Redistribution and use in source and binary forms, with or without 1259243Sobrien * modification, are permitted provided that the following conditions 1359243Sobrien * are met: 1459243Sobrien * 1. Redistributions of source code must retain the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer. 1659243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1759243Sobrien * notice, this list of conditions and the following disclaimer in the 1859243Sobrien * documentation and/or other materials provided with the distribution. 19100616Smp * 3. Neither the name of the University nor the names of its contributors 2059243Sobrien * may be used to endorse or promote products derived from this software 2159243Sobrien * without specific prior written permission. 2259243Sobrien * 2359243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2459243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2559243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2659243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2759243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2859243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2959243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3059243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3159243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3259243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3359243Sobrien * SUCH DAMAGE. 3459243Sobrien */ 3559243Sobrien#include "sh.h" 3659243Sobrien 37316958SdchaginRCSID("$tcsh: tc.printf.c,v 3.38 2015/06/06 21:19:08 christos Exp $") 3859243Sobrien 3959243Sobrien#ifdef lint 4059243Sobrien#undef va_arg 4159243Sobrien#define va_arg(a, b) (a ? (b) 0 : (b) 0) 4259243Sobrien#endif 4359243Sobrien 44167465Smp#define INF INT_MAX /* should be bigger than any field to print */ 4559243Sobrien 46145479Smpstatic char snil[] = "(nil)"; 4759243Sobrien 48167465Smpstatic void xaddchar (int); 49316958Sdchaginstatic int doprnt (void (*) (int), const char *, va_list); 5059243Sobrien 51316958Sdchaginstatic int 52167465Smpdoprnt(void (*addchar) (int), const char *sfmt, va_list ap) 5359243Sobrien{ 54100616Smp char *bp; 55100616Smp const char *f; 5659243Sobrien#ifdef SHORT_STRINGS 57167465Smp const Char *Bp; 5859243Sobrien#endif /* SHORT_STRINGS */ 59145479Smp#ifdef HAVE_LONG_LONG 60100616Smp long long l; 61100616Smp unsigned long long u; 62100616Smp#else 63100616Smp long l; 64100616Smp unsigned long u; 65100616Smp#endif 66167465Smp char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */ 67100616Smp int i; 68100616Smp int fmt; 69100616Smp unsigned char pad = ' '; 70167465Smp int flush_left = 0, f_width = 0, prec = INF, hash = 0; 71316958Sdchagin int do_long = 0, do_size_t = 0, do_ptrdiff_t = 0; 72316958Sdchagin int sign = 0, count = 0; 7359243Sobrien int attributes = 0; 7459243Sobrien 7559243Sobrien 7659243Sobrien f = sfmt; 7759243Sobrien for (; *f; f++) { 7859243Sobrien if (*f != '%') { /* then just out the char */ 79167465Smp (*addchar) (((unsigned char)*f) | attributes); 80316958Sdchagin count++; 8159243Sobrien } 8259243Sobrien else { 8359243Sobrien f++; /* skip the % */ 8459243Sobrien 8559243Sobrien if (*f == '-') { /* minus: flush left */ 8659243Sobrien flush_left = 1; 8759243Sobrien f++; 8859243Sobrien } 8959243Sobrien 9059243Sobrien if (*f == '0' || *f == '.') { 9159243Sobrien /* padding with 0 rather than blank */ 9259243Sobrien pad = '0'; 9359243Sobrien f++; 9459243Sobrien } 9559243Sobrien if (*f == '*') { /* field width */ 9659243Sobrien f_width = va_arg(ap, int); 9759243Sobrien f++; 9859243Sobrien } 99145479Smp else if (isdigit((unsigned char) *f)) { 10059243Sobrien f_width = atoi(f); 101145479Smp while (isdigit((unsigned char) *f)) 10259243Sobrien f++; /* skip the digits */ 10359243Sobrien } 10459243Sobrien 10559243Sobrien if (*f == '.') { /* precision */ 10659243Sobrien f++; 10759243Sobrien if (*f == '*') { 10859243Sobrien prec = va_arg(ap, int); 10959243Sobrien f++; 11059243Sobrien } 111145479Smp else if (isdigit((unsigned char) *f)) { 112145479Smp prec = atoi(f); 113145479Smp while (isdigit((unsigned char) *f)) 11459243Sobrien f++; /* skip the digits */ 11559243Sobrien } 11659243Sobrien } 11759243Sobrien 11859243Sobrien if (*f == '#') { /* alternate form */ 11959243Sobrien hash = 1; 12059243Sobrien f++; 12159243Sobrien } 12259243Sobrien 12359243Sobrien if (*f == 'l') { /* long format */ 124100616Smp do_long++; 12559243Sobrien f++; 126100616Smp if (*f == 'l') { 127100616Smp do_long++; 128100616Smp f++; 129100616Smp } 13059243Sobrien } 131167465Smp if (*f == 'z') { /* size_t format */ 132167465Smp do_size_t++; 133167465Smp f++; 134167465Smp } 135316958Sdchagin if (*f == 't') { /* ptrdiff_t format */ 136316958Sdchagin do_ptrdiff_t++; 137316958Sdchagin f++; 138316958Sdchagin } 13959243Sobrien 14059243Sobrien fmt = (unsigned char) *f; 141145479Smp if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) { 14259243Sobrien do_long = 1; 143145479Smp fmt = tolower(fmt); 14459243Sobrien } 14559243Sobrien bp = buf; 14659243Sobrien switch (fmt) { /* do the format */ 14759243Sobrien case 'd': 148100616Smp switch (do_long) { 149100616Smp case 0: 150167465Smp if (do_size_t) 151167465Smp l = (long) (va_arg(ap, size_t)); 152167465Smp else 153167465Smp l = (long) (va_arg(ap, int)); 154100616Smp break; 155100616Smp case 1: 156145479Smp#ifndef HAVE_LONG_LONG 157100616Smp default: 158100616Smp#endif 15959243Sobrien l = va_arg(ap, long); 160100616Smp break; 161145479Smp#ifdef HAVE_LONG_LONG 162100616Smp default: 163100616Smp l = va_arg(ap, long long); 164100616Smp break; 165100616Smp#endif 166100616Smp } 167100616Smp 16859243Sobrien if (l < 0) { 16959243Sobrien sign = 1; 17059243Sobrien l = -l; 17159243Sobrien } 17259243Sobrien do { 17359243Sobrien *bp++ = (char) (l % 10) + '0'; 17459243Sobrien } while ((l /= 10) > 0); 17559243Sobrien if (sign) 17659243Sobrien *bp++ = '-'; 17759243Sobrien f_width = f_width - (int) (bp - buf); 17859243Sobrien if (!flush_left) 179316958Sdchagin while (f_width-- > 0) { 180167465Smp (*addchar) (pad | attributes); 181316958Sdchagin count++; 182316958Sdchagin } 183316958Sdchagin for (bp--; bp >= buf; bp--) { 184167465Smp (*addchar) (((unsigned char) *bp) | attributes); 185316958Sdchagin count++; 186316958Sdchagin } 18759243Sobrien if (flush_left) 188316958Sdchagin while (f_width-- > 0) { 189167465Smp (*addchar) (' ' | attributes); 190316958Sdchagin count++; 191316958Sdchagin } 19259243Sobrien break; 19359243Sobrien 194131962Smp case 'p': 195131962Smp do_long = 1; 196131962Smp hash = 1; 197131962Smp fmt = 'x'; 198131962Smp /*FALLTHROUGH*/ 19959243Sobrien case 'o': 20059243Sobrien case 'x': 20159243Sobrien case 'u': 202100616Smp switch (do_long) { 203100616Smp case 0: 204167465Smp if (do_size_t) 205167465Smp u = va_arg(ap, size_t); 206316958Sdchagin else if (do_ptrdiff_t) 207316958Sdchagin u = va_arg(ap, ptrdiff_t); 208167465Smp else 209167465Smp u = va_arg(ap, unsigned int); 210100616Smp break; 211100616Smp case 1: 212145479Smp#ifndef HAVE_LONG_LONG 213100616Smp default: 214100616Smp#endif 21559243Sobrien u = va_arg(ap, unsigned long); 216100616Smp break; 217145479Smp#ifdef HAVE_LONG_LONG 218100616Smp default: 219100616Smp u = va_arg(ap, unsigned long long); 220100616Smp break; 221100616Smp#endif 222100616Smp } 22359243Sobrien if (fmt == 'u') { /* unsigned decimal */ 22459243Sobrien do { 22559243Sobrien *bp++ = (char) (u % 10) + '0'; 22659243Sobrien } while ((u /= 10) > 0); 22759243Sobrien } 22859243Sobrien else if (fmt == 'o') { /* octal */ 22959243Sobrien do { 23059243Sobrien *bp++ = (char) (u % 8) + '0'; 23159243Sobrien } while ((u /= 8) > 0); 23259243Sobrien if (hash) 23359243Sobrien *bp++ = '0'; 23459243Sobrien } 23559243Sobrien else if (fmt == 'x') { /* hex */ 23659243Sobrien do { 23759243Sobrien i = (int) (u % 16); 23859243Sobrien if (i < 10) 23959243Sobrien *bp++ = i + '0'; 24059243Sobrien else 24159243Sobrien *bp++ = i - 10 + 'a'; 24259243Sobrien } while ((u /= 16) > 0); 24359243Sobrien if (hash) { 24459243Sobrien *bp++ = 'x'; 24559243Sobrien *bp++ = '0'; 24659243Sobrien } 24759243Sobrien } 24859243Sobrien i = f_width - (int) (bp - buf); 24959243Sobrien if (!flush_left) 250316958Sdchagin while (i-- > 0) { 251167465Smp (*addchar) (pad | attributes); 252316958Sdchagin count++; 253316958Sdchagin } 25459243Sobrien for (bp--; bp >= buf; bp--) 255167465Smp (*addchar) (((unsigned char) *bp) | attributes); 25659243Sobrien if (flush_left) 257316958Sdchagin while (i-- > 0) { 258167465Smp (*addchar) (' ' | attributes); 259316958Sdchagin count++; 260316958Sdchagin } 26159243Sobrien break; 26259243Sobrien 26359243Sobrien 26459243Sobrien case 'c': 26559243Sobrien i = va_arg(ap, int); 266167465Smp (*addchar) (i | attributes); 267316958Sdchagin count++; 26859243Sobrien break; 26959243Sobrien 27059243Sobrien case 'S': 27159243Sobrien case 'Q': 27283098Smp#ifdef SHORT_STRINGS 27359243Sobrien Bp = va_arg(ap, Char *); 27459243Sobrien if (!Bp) { 27559243Sobrien bp = NULL; 27659243Sobrien goto lcase_s; 27759243Sobrien } 27859243Sobrien f_width = f_width - Strlen(Bp); 27959243Sobrien if (!flush_left) 280316958Sdchagin while (f_width-- > 0) { 28159243Sobrien (*addchar) ((int) (pad | attributes)); 282316958Sdchagin count++; 283316958Sdchagin } 28459243Sobrien for (i = 0; *Bp && i < prec; i++) { 285145479Smp char cbuf[MB_LEN_MAX]; 286145479Smp size_t pos, len; 287145479Smp 288316958Sdchagin if (fmt == 'Q' && *Bp & QUOTE) { 289167465Smp (*addchar) ('\\' | attributes); 290316958Sdchagin count++; 291316958Sdchagin } 292316958Sdchagin len = one_wctomb(cbuf, *Bp); 293316958Sdchagin for (pos = 0; pos < len; pos++) { 294167465Smp (*addchar) ((unsigned char)cbuf[pos] | attributes 295167465Smp | (*Bp & ATTRIBUTES)); 296316958Sdchagin count++; 297316958Sdchagin } 29859243Sobrien Bp++; 29959243Sobrien } 30059243Sobrien if (flush_left) 301316958Sdchagin while (f_width-- > 0) { 302167465Smp (*addchar) (' ' | attributes); 303316958Sdchagin count++; 304316958Sdchagin } 30559243Sobrien break; 30683098Smp#endif /* SHORT_STRINGS */ 30759243Sobrien 30859243Sobrien case 's': 30959243Sobrien case 'q': 31059243Sobrien bp = va_arg(ap, char *); 31159243Sobrienlcase_s: 31259243Sobrien if (!bp) 313145479Smp bp = snil; 314167465Smp f_width = f_width - strlen(bp); 31559243Sobrien if (!flush_left) 316316958Sdchagin while (f_width-- > 0) { 317167465Smp (*addchar) (pad | attributes); 318316958Sdchagin count++; 319316958Sdchagin } 32059243Sobrien for (i = 0; *bp && i < prec; i++) { 321316958Sdchagin if (fmt == 'q' && *bp & QUOTE) { 322167465Smp (*addchar) ('\\' | attributes); 323316958Sdchagin count++; 324316958Sdchagin } 325167465Smp (*addchar) (((unsigned char) *bp & TRIM) | attributes); 326316958Sdchagin count++; 32759243Sobrien bp++; 32859243Sobrien } 32959243Sobrien if (flush_left) 330316958Sdchagin while (f_width-- > 0) { 331167465Smp (*addchar) (' ' | attributes); 332316958Sdchagin count++; 333316958Sdchagin } 33459243Sobrien break; 33559243Sobrien 33659243Sobrien case 'a': 33759243Sobrien attributes = va_arg(ap, int); 33859243Sobrien break; 33959243Sobrien 34059243Sobrien case '%': 341167465Smp (*addchar) ('%' | attributes); 342316958Sdchagin count++; 34359243Sobrien break; 34459243Sobrien 34559243Sobrien default: 34659243Sobrien break; 34759243Sobrien } 348167465Smp flush_left = 0, f_width = 0, prec = INF, hash = 0; 349316958Sdchagin do_ptrdiff_t = 0, do_size_t = 0, do_long = 0; 35059243Sobrien sign = 0; 35159243Sobrien pad = ' '; 35259243Sobrien } 35359243Sobrien } 354316958Sdchagin return count; 35559243Sobrien} 35659243Sobrien 35759243Sobrien 35859243Sobrienstatic char *xstring, *xestring; 35959243Sobrienstatic void 360167465Smpxaddchar(int c) 36159243Sobrien{ 36259243Sobrien if (xestring == xstring) 36359243Sobrien *xstring = '\0'; 36459243Sobrien else 36559243Sobrien *xstring++ = (char) c; 36659243Sobrien} 36759243Sobrien 36859243Sobrien 369316958Sdchaginint 37059243Sobrien/*VARARGS*/ 37159243Sobrienxsnprintf(char *str, size_t size, const char *fmt, ...) 37259243Sobrien{ 373316958Sdchagin int count; 37459243Sobrien va_list va; 37559243Sobrien va_start(va, fmt); 37659243Sobrien 37759243Sobrien xstring = str; 37859243Sobrien xestring = str + size - 1; 379316958Sdchagin count = doprnt(xaddchar, fmt, va); 38059243Sobrien va_end(va); 38159243Sobrien *xstring++ = '\0'; 382316958Sdchagin return count; 38359243Sobrien} 38459243Sobrien 385316958Sdchaginint 38659243Sobrien/*VARARGS*/ 38759243Sobrienxprintf(const char *fmt, ...) 38859243Sobrien{ 389316958Sdchagin int count; 39059243Sobrien va_list va; 39159243Sobrien va_start(va, fmt); 392316958Sdchagin count = doprnt(xputchar, fmt, va); 39359243Sobrien va_end(va); 394316958Sdchagin return count; 39559243Sobrien} 39659243Sobrien 397316958Sdchaginint 398167465Smpxvprintf(const char *fmt, va_list va) 39959243Sobrien{ 400316958Sdchagin return doprnt(xputchar, fmt, va); 40159243Sobrien} 40259243Sobrien 403316958Sdchaginint 404167465Smpxvsnprintf(char *str, size_t size, const char *fmt, va_list va) 40559243Sobrien{ 406316958Sdchagin int count; 40759243Sobrien xstring = str; 40859243Sobrien xestring = str + size - 1; 409316958Sdchagin count = doprnt(xaddchar, fmt, va); 41059243Sobrien *xstring++ = '\0'; 411316958Sdchagin return count; 41259243Sobrien} 41359243Sobrien 414167465Smpchar * 415167465Smpxvasprintf(const char *fmt, va_list va) 416167465Smp{ 417167465Smp size_t size; 418167465Smp char *buf; 41959243Sobrien 420167465Smp buf = NULL; 421167465Smp size = 2048; /* Arbitrary */ 422167465Smp for (;;) { 423167465Smp va_list copy; 42459243Sobrien 425167465Smp buf = xrealloc(buf, size); 426167465Smp xstring = buf; 427167465Smp xestring = buf + size - 1; 428167465Smp va_copy(copy, va); 429167465Smp doprnt(xaddchar, fmt, copy); 430167465Smp va_end(copy); 431167465Smp if (xstring < xestring) 432167465Smp break; 433167465Smp size *= 2; 434167465Smp } 435167465Smp *xstring++ = '\0'; 436167465Smp return xrealloc(buf, xstring - buf); 437167465Smp} 438167465Smp 439167465Smpchar * 440167465Smpxasprintf(const char *fmt, ...) 441167465Smp{ 442167465Smp va_list va; 443167465Smp char *ret; 444167465Smp 445167465Smp va_start (va, fmt); 446167465Smp ret = xvasprintf(fmt, va); 447167465Smp va_end(va); 448167465Smp return ret; 449167465Smp} 450167465Smp 451167465Smp 45259243Sobrien#ifdef PURIFY 45359243Sobrien/* Purify uses (some of..) the following functions to output memory-use 45459243Sobrien * debugging info. Given all the messing with file descriptors that 45559243Sobrien * tcsh does, the easiest way I could think of to get it (Purify) to 45659243Sobrien * print anything was by replacing some standard functions with 45759243Sobrien * ones that do tcsh output directly - see dumb hook in doreaddirs() 45859243Sobrien * (sh.dir.c) -sg 45959243Sobrien */ 46059243Sobrien#ifndef FILE 46159243Sobrien#define FILE int 46259243Sobrien#endif 46359243Sobrienint 46459243Sobrienfprintf(FILE *fp, const char* fmt, ...) 46559243Sobrien{ 466316958Sdchagin int count; 46759243Sobrien va_list va; 46859243Sobrien va_start(va, fmt); 469316958Sdchagin count = doprnt(xputchar, fmt, va); 47059243Sobrien va_end(va); 471316958Sdchagin return count; 47259243Sobrien} 47359243Sobrien 47459243Sobrienint 475167465Smpvfprintf(FILE *fp, const char *fmt, va_list va) 47659243Sobrien{ 477316958Sdchagin return doprnt(xputchar, fmt, va); 47859243Sobrien} 47959243Sobrien 48059243Sobrien#endif /* PURIFY */ 481