Vendor branch: upgrade tcsh from 6.19.00 to 6.22.02
[dragonfly.git] / contrib / tcsh-6 / tc.printf.c
1 /*
2  * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
3  *             through the putchar() routine.  Feel free to use for
4  *             anything...  -- 7/17/87 Paul Placeway
5  */
6 /*-
7  * Copyright (c) 1980, 1991 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include "sh.h"
35
36 #ifdef lint
37 #undef va_arg
38 #define va_arg(a, b) (a ? (b) 0 : (b) 0)
39 #endif
40
41 #define INF     INT_MAX         /* should be bigger than any field to print */
42
43 static char snil[] = "(nil)";
44
45 static  void    xaddchar        (int);
46 static  int     doprnt          (void (*) (int), const char *, va_list);
47
48 static int
49 doprnt(void (*addchar) (int), const char *sfmt, va_list ap)
50 {
51     char *bp;
52     const char *f;
53 #ifdef SHORT_STRINGS
54     const Char *Bp;
55 #endif /* SHORT_STRINGS */
56 #ifdef HAVE_LONG_LONG
57     long long l;
58     unsigned long long u;
59 #else
60     long l;
61     unsigned long u;
62 #endif
63     char buf[(CHAR_BIT * sizeof (l) + 2) / 3 + 1]; /* Octal: 3 bits per char */
64     int i;
65     int fmt;
66     unsigned char pad = ' ';
67     int     flush_left = 0, f_width = 0, prec = INF, hash = 0;
68     int     do_long = 0, do_size_t = 0, do_ptrdiff_t = 0;
69     int     sign = 0, count = 0;
70     int     attributes = 0;
71
72
73     f = sfmt;
74     for (; *f; f++) {
75         if (*f != '%') {        /* then just out the char */
76             (*addchar) (((unsigned char)*f) | attributes);
77             count++;
78         }
79         else {
80             f++;                /* skip the % */
81
82             if (*f == '-') {    /* minus: flush left */
83                 flush_left = 1;
84                 f++;
85             }
86
87             if (*f == '0' || *f == '.') {
88                 /* padding with 0 rather than blank */
89                 pad = '0';
90                 f++;
91             }
92             if (*f == '*') {    /* field width */
93                 f_width = va_arg(ap, int);
94                 f++;
95             }
96             else if (isdigit((unsigned char) *f)) {
97                 f_width = atoi(f);
98                 while (isdigit((unsigned char) *f))
99                     f++;        /* skip the digits */
100             }
101
102             if (*f == '.') {    /* precision */
103                 f++;
104                 if (*f == '*') {
105                     prec = va_arg(ap, int);
106                     f++;
107                 }
108                 else if (isdigit((unsigned char) *f)) {
109                     prec = atoi(f);
110                     while (isdigit((unsigned char) *f))
111                         f++;    /* skip the digits */
112                 }
113             }
114
115             if (*f == '#') {    /* alternate form */
116                 hash = 1;
117                 f++;
118             }
119
120             if (*f == 'l') {    /* long format */
121                 do_long++;
122                 f++;
123                 if (*f == 'l') {
124                     do_long++;
125                     f++;
126                 }
127             }
128             if (*f == 'z') {    /* size_t format */
129                 do_size_t++;
130                 f++;
131             }
132             if (*f == 't') {    /* ptrdiff_t format */
133                 do_ptrdiff_t++;
134                 f++;
135             }
136
137             fmt = (unsigned char) *f;
138             if (fmt != 'S' && fmt != 'Q' && isupper(fmt)) {
139                 do_long = 1;
140                 fmt = tolower(fmt);
141             }
142             bp = buf;
143             switch (fmt) {      /* do the format */
144             case 'd':
145                 switch (do_long) {
146                 case 0:
147                     if (do_size_t)
148                         l = (long) (va_arg(ap, size_t));
149                     else
150                         l = (long) (va_arg(ap, int));
151                     break;
152                 case 1:
153 #ifndef HAVE_LONG_LONG
154                 default:
155 #endif
156                     l = va_arg(ap, long);
157                     break;
158 #ifdef HAVE_LONG_LONG
159                 default:
160                     l = va_arg(ap, long long);
161                     break;
162 #endif
163                 }
164
165                 if (l < 0) {
166                     sign = 1;
167                     l = -l;
168                 }
169                 do {
170                     *bp++ = (char) (l % 10) + '0';
171                 } while ((l /= 10) > 0);
172                 if (sign)
173                     *bp++ = '-';
174                 f_width = f_width - (int) (bp - buf);
175                 if (!flush_left)
176                     while (f_width-- > 0)  {
177                         (*addchar) (pad | attributes);
178                         count++;
179                     }
180                 for (bp--; bp >= buf; bp--)  {
181                     (*addchar) (((unsigned char) *bp) | attributes);
182                     count++;
183                 }
184                 if (flush_left)
185                     while (f_width-- > 0) {
186                         (*addchar) (' ' | attributes);
187                         count++;
188                     }
189                 break;
190
191             case 'p':
192                 do_long = 1;
193                 hash = 1;
194                 fmt = 'x';
195                 /*FALLTHROUGH*/
196             case 'o':
197             case 'x':
198             case 'u':
199                 switch (do_long) {
200                 case 0:
201                     if (do_size_t)
202                         u = va_arg(ap, size_t);
203                     else if (do_ptrdiff_t)
204                         u = va_arg(ap, ptrdiff_t);
205                     else
206                         u = va_arg(ap, unsigned int);
207                     break;
208                 case 1:
209 #ifndef HAVE_LONG_LONG
210                 default:
211 #endif
212                     u = va_arg(ap, unsigned long);
213                     break;
214 #ifdef HAVE_LONG_LONG
215                 default:
216                     u = va_arg(ap, unsigned long long);
217                     break;
218 #endif
219                 }
220                 if (fmt == 'u') {       /* unsigned decimal */
221                     do {
222                         *bp++ = (char) (u % 10) + '0';
223                     } while ((u /= 10) > 0);
224                 }
225                 else if (fmt == 'o') {  /* octal */
226                     do {
227                         *bp++ = (char) (u % 8) + '0';
228                     } while ((u /= 8) > 0);
229                     if (hash)
230                         *bp++ = '0';
231                 }
232                 else if (fmt == 'x') {  /* hex */
233                     do {
234                         i = (int) (u % 16);
235                         if (i < 10)
236                             *bp++ = i + '0';
237                         else
238                             *bp++ = i - 10 + 'a';
239                     } while ((u /= 16) > 0);
240                     if (hash) {
241                         *bp++ = 'x';
242                         *bp++ = '0';
243                     }
244                 }
245                 i = f_width - (int) (bp - buf);
246                 if (!flush_left)
247                     while (i-- > 0) {
248                         (*addchar) (pad | attributes);
249                         count++;
250                     }
251                 for (bp--; bp >= buf; bp--)
252                     (*addchar) (((unsigned char) *bp) | attributes);
253                 if (flush_left)
254                     while (i-- > 0) {
255                         (*addchar) (' ' | attributes);
256                         count++;
257                     }
258                 break;
259
260
261             case 'c':
262                 i = va_arg(ap, int);
263                 (*addchar) (i | attributes);
264                 count++;
265                 break;
266
267             case 'S':
268             case 'Q':
269 #ifdef SHORT_STRINGS
270                 Bp = va_arg(ap, Char *);
271                 if (!Bp) {
272                     bp = NULL;
273                     goto lcase_s;
274                 }
275                 f_width = f_width - Strlen(Bp);
276                 if (!flush_left)
277                     while (f_width-- > 0) {
278                         (*addchar) ((int) (pad | attributes));
279                         count++;
280                     }
281                 for (i = 0; *Bp && i < prec; i++) {
282                     char cbuf[MB_LEN_MAX];
283                     size_t pos, len;
284
285                     if (fmt == 'Q' && *Bp & QUOTE) {
286                         (*addchar) ('\\' | attributes);
287                         count++;
288                     }
289                     len = one_wctomb(cbuf, *Bp);
290                     for (pos = 0; pos < len; pos++) {
291                         (*addchar) ((unsigned char)cbuf[pos] | attributes
292                                     | (*Bp & ATTRIBUTES));
293                         count++;
294                     }
295                     Bp++;
296                 }
297                 if (flush_left)
298                     while (f_width-- > 0) {
299                         (*addchar) (' ' | attributes);
300                         count++;
301                     }
302                 break;
303 #endif /* SHORT_STRINGS */
304
305             case 's':
306             case 'q':
307                 bp = va_arg(ap, char *);
308 lcase_s:
309                 if (!bp)
310                     bp = snil;
311                 f_width = f_width - strlen(bp);
312                 if (!flush_left)
313                     while (f_width-- > 0) {
314                         (*addchar) (pad | attributes);
315                         count++;
316                     }
317                 for (i = 0; *bp && i < prec; i++) {
318                     if (fmt == 'q' && *bp & QUOTE) {
319                         (*addchar) ('\\' | attributes);
320                         count++;
321                     }
322                     (*addchar) (((unsigned char) *bp & TRIM) | attributes);
323                     count++;
324                     bp++;
325                 }
326                 if (flush_left)
327                     while (f_width-- > 0) {
328                         (*addchar) (' ' | attributes);
329                         count++;
330                     }
331                 break;
332
333             case 'a':
334                 attributes = va_arg(ap, int);
335                 break;
336
337             case '%':
338                 (*addchar) ('%' | attributes);
339                 count++;
340                 break;
341
342             default:
343                 break;
344             }
345             flush_left = 0, f_width = 0, prec = INF, hash = 0;
346             do_ptrdiff_t = 0, do_size_t = 0, do_long = 0;
347             sign = 0;
348             pad = ' ';
349         }
350     }
351     return count;
352 }
353
354
355 static char *xstring, *xestring;
356 static void
357 xaddchar(int c)
358 {
359     if (xestring == xstring)
360         *xstring = '\0';
361     else
362         *xstring++ = (char) c;
363 }
364
365
366 int
367 /*VARARGS*/
368 xsnprintf(char *str, size_t size, const char *fmt, ...)
369 {
370     int count;
371     va_list va;
372     va_start(va, fmt);
373
374     xstring = str;
375     xestring = str + size - 1;
376     count = doprnt(xaddchar, fmt, va);
377     va_end(va);
378     *xstring++ = '\0';
379     return count;
380 }
381
382 int
383 /*VARARGS*/
384 xprintf(const char *fmt, ...)
385 {
386     int count;
387     va_list va;
388     va_start(va, fmt);
389     count = doprnt(xputchar, fmt, va);
390     va_end(va);
391     return count;
392 }
393
394 int
395 xvprintf(const char *fmt, va_list va)
396 {
397     return doprnt(xputchar, fmt, va);
398 }
399
400 int
401 xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
402 {
403     int count;
404     xstring = str;
405     xestring = str + size - 1;
406     count = doprnt(xaddchar, fmt, va);
407     *xstring++ = '\0';
408     return count;
409 }
410
411 char *
412 xvasprintf(const char *fmt, va_list va)
413 {
414     size_t size;
415     char *buf;
416
417     buf = NULL;
418     size = 2048; /* Arbitrary */
419     for (;;) {
420         va_list copy;
421
422         buf = xrealloc(buf, size);
423         xstring = buf;
424         xestring = buf + size - 1;
425         va_copy(copy, va);
426         doprnt(xaddchar, fmt, copy);
427         va_end(copy);
428         if (xstring < xestring)
429             break;
430         size *= 2;
431     }
432     *xstring++ = '\0';
433     return xrealloc(buf, xstring - buf);
434 }
435
436 char *
437 xasprintf(const char *fmt, ...)
438 {
439     va_list va;
440     char *ret;
441
442     va_start (va, fmt);
443     ret = xvasprintf(fmt, va);
444     va_end(va);
445     return ret;
446 }
447
448
449 #ifdef PURIFY
450 /* Purify uses (some of..) the following functions to output memory-use
451  * debugging info.  Given all the messing with file descriptors that
452  * tcsh does, the easiest way I could think of to get it (Purify) to
453  * print anything was by replacing some standard functions with
454  * ones that do tcsh output directly - see dumb hook in doreaddirs()
455  * (sh.dir.c) -sg
456  */
457 #ifndef FILE
458 #define FILE int
459 #endif
460 int 
461 fprintf(FILE *fp, const char* fmt, ...)
462 {
463     int count;
464     va_list va;
465     va_start(va, fmt);
466     count = doprnt(xputchar, fmt, va);
467     va_end(va);
468     return count;
469 }
470
471 int 
472 vfprintf(FILE *fp, const char *fmt, va_list va)
473 {
474     return doprnt(xputchar, fmt, va);
475 }
476
477 #endif  /* PURIFY */