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