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