Merge from vendor branch BSDTAR:
[dragonfly.git] / contrib / tcsh / tc.printf.c
1 /* $Header: /src/pub/tcsh/tc.printf.c,v 3.23 2002/03/08 17:36:47 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("$Id: tc.printf.c,v 3.23 2002/03/08 17:36:47 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     32766           /* should be bigger than any field to print */
45
46 static char buf[128];
47
48 static  void    xaddchar        __P((int));
49 static  void    doprnt          __P((void (*) __P((int)), const char *, va_list));
50
51 static void
52 doprnt(addchar, sfmt, ap)
53     void    (*addchar) __P((int));
54     const char   *sfmt;
55     va_list ap;
56 {
57     char *bp;
58     const char *f;
59 #ifdef SHORT_STRINGS
60     Char *Bp;
61 #endif /* SHORT_STRINGS */
62 #ifdef HAVE_QUAD
63     long long l;
64     unsigned long long u;
65 #else
66     long l;
67     unsigned long u;
68 #endif
69     int i;
70     int fmt;
71     unsigned char pad = ' ';
72     int     flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
73     int     sign = 0;
74     int     attributes = 0;
75
76
77     f = sfmt;
78     for (; *f; f++) {
79         if (*f != '%') {        /* then just out the char */
80             (*addchar) ((int) (((unsigned char)*f) | attributes));
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((char *) 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
132             fmt = (unsigned char) *f;
133             if (fmt != 'S' && fmt != 'Q' && Isupper(fmt)) {
134                 do_long = 1;
135                 fmt = Tolower(fmt);
136             }
137             bp = buf;
138             switch (fmt) {      /* do the format */
139             case 'd':
140                 switch (do_long) {
141                 case 0:
142                     l = (long) (va_arg(ap, int));
143                     break;
144                 case 1:
145 #ifndef HAVE_QUAD
146                 default:
147 #endif
148                     l = va_arg(ap, long);
149                     break;
150 #ifdef HAVE_QUAD
151                 default:
152                     l = va_arg(ap, long long);
153                     break;
154 #endif
155                 }
156
157                 if (l < 0) {
158                     sign = 1;
159                     l = -l;
160                 }
161                 do {
162                     *bp++ = (char) (l % 10) + '0';
163                 } while ((l /= 10) > 0);
164                 if (sign)
165                     *bp++ = '-';
166                 f_width = f_width - (int) (bp - buf);
167                 if (!flush_left)
168                     while (f_width-- > 0) 
169                         (*addchar) ((int) (pad | attributes));
170                 for (bp--; bp >= buf; bp--) 
171                     (*addchar) ((int) (((unsigned char) *bp) | attributes));
172                 if (flush_left)
173                     while (f_width-- > 0)
174                         (*addchar) ((int) (' ' | attributes));
175                 break;
176
177             case 'o':
178             case 'x':
179             case 'u':
180                 switch (do_long) {
181                 case 0:
182                     u = (unsigned long) (va_arg(ap, unsigned int));
183                     break;
184                 case 1:
185 #ifndef HAVE_QUAD
186                 default:
187 #endif
188                     u = va_arg(ap, unsigned long);
189                     break;
190 #ifdef HAVE_QUAD
191                 default:
192                     u = va_arg(ap, unsigned long long);
193                     break;
194 #endif
195                 }
196                 if (fmt == 'u') {       /* unsigned decimal */
197                     do {
198                         *bp++ = (char) (u % 10) + '0';
199                     } while ((u /= 10) > 0);
200                 }
201                 else if (fmt == 'o') {  /* octal */
202                     do {
203                         *bp++ = (char) (u % 8) + '0';
204                     } while ((u /= 8) > 0);
205                     if (hash)
206                         *bp++ = '0';
207                 }
208                 else if (fmt == 'x') {  /* hex */
209                     do {
210                         i = (int) (u % 16);
211                         if (i < 10)
212                             *bp++ = i + '0';
213                         else
214                             *bp++ = i - 10 + 'a';
215                     } while ((u /= 16) > 0);
216                     if (hash) {
217                         *bp++ = 'x';
218                         *bp++ = '0';
219                     }
220                 }
221                 i = f_width - (int) (bp - buf);
222                 if (!flush_left)
223                     while (i-- > 0)
224                         (*addchar) ((int) (pad | attributes));
225                 for (bp--; bp >= buf; bp--)
226                     (*addchar) ((int) (((unsigned char) *bp) | attributes));
227                 if (flush_left)
228                     while (i-- > 0)
229                         (*addchar) ((int) (' ' | attributes));
230                 break;
231
232
233             case 'c':
234                 i = va_arg(ap, int);
235                 (*addchar) ((int) (i | attributes));
236                 break;
237
238             case 'S':
239             case 'Q':
240 #ifdef SHORT_STRINGS
241                 Bp = va_arg(ap, Char *);
242                 if (!Bp) {
243                     bp = NULL;
244                     goto lcase_s;
245                 }
246                 f_width = f_width - Strlen(Bp);
247                 if (!flush_left)
248                     while (f_width-- > 0)
249                         (*addchar) ((int) (pad | attributes));
250                 for (i = 0; *Bp && i < prec; i++) {
251                     if (fmt == 'Q' && *Bp & QUOTE)
252                         (*addchar) ((int) ('\\' | attributes));
253                     (*addchar) ((int) ((*Bp & TRIM) | attributes));
254                     Bp++;
255                 }
256                 if (flush_left)
257                     while (f_width-- > 0)
258                         (*addchar) ((int) (' ' | attributes));
259                 break;
260 #endif /* SHORT_STRINGS */
261
262             case 's':
263             case 'q':
264                 bp = va_arg(ap, char *);
265 lcase_s:
266                 if (!bp)
267                     bp = "(nil)";
268                 f_width = f_width - strlen((char *) bp);
269                 if (!flush_left)
270                     while (f_width-- > 0)
271                         (*addchar) ((int) (pad | attributes));
272                 for (i = 0; *bp && i < prec; i++) {
273                     if (fmt == 'q' && *bp & QUOTE)
274                         (*addchar) ((int) ('\\' | attributes));
275                     (*addchar) ((int) (((unsigned char) *bp & TRIM) |
276                                         attributes));
277                     bp++;
278                 }
279                 if (flush_left)
280                     while (f_width-- > 0)
281                         (*addchar) ((int) (' ' | attributes));
282                 break;
283
284             case 'a':
285                 attributes = va_arg(ap, int);
286                 break;
287
288             case '%':
289                 (*addchar) ((int) ('%' | attributes));
290                 break;
291
292             default:
293                 break;
294             }
295             flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
296             sign = 0;
297             pad = ' ';
298         }
299     }
300 }
301
302
303 static char *xstring, *xestring;
304 static void
305 xaddchar(c)
306     int     c;
307 {
308     if (xestring == xstring)
309         *xstring = '\0';
310     else
311         *xstring++ = (char) c;
312 }
313
314
315 pret_t
316 /*VARARGS*/
317 #ifdef FUNCPROTO
318 xsnprintf(char *str, size_t size, const char *fmt, ...)
319 #else
320 xsnprintf(va_alist)
321     va_dcl
322 #endif
323 {
324     va_list va;
325 #ifdef FUNCPROTO
326     va_start(va, fmt);
327 #else
328     char *str, *fmt;
329     size_t size;
330
331     va_start(va);
332     str = va_arg(va, char *);
333     size = va_arg(va, size_t);
334     fmt = va_arg(va, char *);
335 #endif
336
337     xstring = str;
338     xestring = str + size - 1;
339     doprnt(xaddchar, fmt, va);
340     va_end(va);
341     *xstring++ = '\0';
342 #ifdef PURIFY
343     return 1;
344 #endif
345 }
346
347 pret_t
348 /*VARARGS*/
349 #ifdef FUNCPROTO
350 xprintf(const char *fmt, ...)
351 #else
352 xprintf(va_alist)
353     va_dcl
354 #endif
355 {
356     va_list va;
357 #ifdef FUNCPROTO
358     va_start(va, fmt);
359 #else
360     char   *fmt;
361
362     va_start(va);
363     fmt = va_arg(va, char *);
364 #endif
365     doprnt(xputchar, fmt, va);
366     va_end(va);
367 #ifdef PURIFY
368     return 1;
369 #endif
370 }
371
372
373 pret_t
374 xvprintf(fmt, va)
375     const char   *fmt;
376     va_list va;
377 {
378     doprnt(xputchar, fmt, va);
379 #ifdef PURIFY
380     return 1;
381 #endif
382 }
383
384 pret_t
385 xvsnprintf(str, size, fmt, va)
386     char   *str;
387     size_t size;
388     const char   *fmt;
389     va_list va;
390 {
391     xstring = str;
392     xestring = str + size - 1;
393     doprnt(xaddchar, fmt, va);
394     *xstring++ = '\0';
395 #ifdef PURIFY
396     return 1;
397 #endif
398 }
399
400
401
402 #ifdef PURIFY
403 /* Purify uses (some of..) the following functions to output memory-use
404  * debugging info.  Given all the messing with file descriptors that
405  * tcsh does, the easiest way I could think of to get it (Purify) to
406  * print anything was by replacing some standard functions with
407  * ones that do tcsh output directly - see dumb hook in doreaddirs()
408  * (sh.dir.c) -sg
409  */
410 #ifndef FILE
411 #define FILE int
412 #endif
413 int 
414 #ifdef FUNCPROTO
415 fprintf(FILE *fp, const char* fmt, ...)
416 #else
417 fprintf(va_alist)
418     va_dcl
419 #endif
420 {
421     va_list va;
422 #ifdef FUNCPROTO
423     va_start(va, fmt);
424 #else
425     FILE *fp;
426     const char   *fmt;
427
428     va_start(va);
429     fp = va_arg(va, FILE *);
430     fmt = va_arg(va, const char *);
431 #endif
432     doprnt(xputchar, fmt, va);
433     va_end(va);
434     return 1;
435 }
436
437 int 
438 vfprintf(fp, fmt, va)
439     FILE *fp;
440     const char   *fmt;
441     va_list va;
442 {
443     doprnt(xputchar, fmt, va);
444     return 1;
445 }
446
447 #endif  /* PURIFY */