2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: sfvprintf.c /main/3 1995/11/01 18:38:26 rswiston $ */
24 /***************************************************************
26 * AT&T - PROPRIETARY *
28 * THIS IS PROPRIETARY SOURCE CODE LICENSED BY *
31 * Copyright (c) 1995 AT&T Corp. *
32 * All Rights Reserved *
34 * This software is licensed by AT&T Corp. *
35 * under the terms and conditions of the license in *
36 * http://www.research.att.com/orgs/ssr/book/reuse *
38 * This software was created by the *
39 * Software Engineering Research Department *
40 * AT&T Bell Laboratories *
42 * For further information contact *
43 * gsf@research.att.com *
45 ***************************************************************/
48 /* The engine for formatting data
50 ** Written by Kiem-Phong Vo (06/27/90)
54 #define HIGHBIT ulong_hibit
56 #define HIGHBIT (~(((ulong)~0L) >> 1))
59 #define F_LEFT 000001 /* left justification (-) */
60 #define F_SIGN 000002 /* must set a sign - or + */
61 #define F_BLANK 000004 /* if not - and +, then prepend a blank */
62 #define F_ZERO 000010 /* zero padding on the left side */
63 #define F_ALTER 000020 /* various alternative formats (#) */
64 #define F_PAD 000040 /* there will be some sort of padding */
66 #define F_REPEAT 000100 /* repeat pattern up to precision */
67 #define F_MINUS 000200 /* has a minus sign */
68 #define F_PREFIX (F_MINUS|F_SIGN|F_BLANK)
70 #define F_LONG 001000 /* object is long */
71 #define F_FLOAT 002000 /* %fFeEgG format */
72 #define F_GFORMAT 004000 /* a %gG format */
73 #define F_LDOUBLE 010000 /* object is long double */
75 #define FPRECIS 6 /* default precision for floats */
77 /* elt: element to be assigned value.
78 ** arge: if argf is used, &arge can be passed on to argf to get value.
79 ** argf: function to get argument if any.
80 ** args: is the va_list being processed.
81 ** etype: The type of the element.
82 ** type: The type of the object (int, double, ...) being gotten out of args.
83 ** fmt: the format character.
84 ** t_user,n_user: stuff between parens.
86 #define GETARG(elt,arge,argf,args,etype,type,fmt,t_user,n_user) \
88 elt = (etype)va_arg(args,type); \
89 else if((*argf)(fmt,(char*)(&arge),t_user,n_user) < 0) \
91 else elt = (etype)arge; \
93 #define GETARGL(elt,arge,argf,args,etype,type,fmt,t_user,n_user) \
95 __va_copy( elt, va_arg(args,type) ); \
96 else if((*argf)(fmt,(char*)(&arge),t_user,n_user) < 0) \
98 else __va_copy( elt, arge ); \
102 sfvprintf(Sfio_t* f, const char* form, va_list args)
104 sfvprintf(f,form,args)
105 Sfio_t* f; /* file to print to */
106 char* form; /* format to use */
107 va_list args; /* arg list if !argf */
110 reg long n, lval, base;
111 reg char *sp, *ssp, *d;
114 reg char *ep, *endep, *endsp, *endd;
115 reg int precis, width, n_output, r;
116 int fmt, sign, decpt, dot;
117 Double_t dval; /* could be long double */
126 char buf[SF_MAXDIGITS];
128 char* t_user; /* stuff between () */
129 int n_user; /* its length */
136 #define SFBUF(f) (d = (char*)f->next, endd = (char*)f->endb)
137 #define SFINIT(f) (SFBUF(f), n_output = 0)
138 #define SFEND(f) ((n_output += (uchar*)d - f->next), (f->next = (uchar*)d))
139 #define SFputc(f,c) \
141 { SFEND(f); if(SFFLSBUF(f,c) < 0) break; n_output += 1; SFBUF(f); } \
142 else { *d++ = (char)c; } \
144 #define SFnputc(f,c,n) \
146 { SFEND(f); if(SFNPUTC(f,(int)c,(int)n) != n) break; \
147 n_output += (int)n; SFBUF(f); } \
148 else { while(n--) *d++ = (char)c; } \
150 #define SFwrite(f,s,n) \
152 { SFEND(f); if(SFWRITE(f,(Void_t*)s,(int)n) != n) break; \
153 n_output += (int)n; SFBUF(f); } \
154 else MEMCPY(d,s,(int)n); \
157 /* make sure stream is in write mode and buffer is not NULL */
158 if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE,0) < 0)
164 { f->data = f->next = (uchar*)data;
165 f->endw = f->endb = f->data+sizeof(data);
171 ep = endep = NIL(char*);
177 while((n = *form++) )
181 { /* collect the non-pattern chars */
182 sp = ssp = (char*)(form-1);
183 while((n = *++ssp) && n != '%')
191 endep = ep = NIL(char*);
192 endsp = sp = buf+(sizeof(buf)-1);
197 loop_flags: /* LOOP FOR FLAGS, WIDTH AND PRECISION */
200 switch(fmt = *form++)
202 case LEFTP : /* get the type which is enclosed in balanced () */
203 t_user = (char*)form;
207 case 0 : /* not balancable, retract */
212 case LEFTP : /* increasing nested level */
215 case RIGHTP : /* decreasing nested level */
218 n_user = (form-1) - t_user;
235 case '.' : /* argument count */
241 case '*' : /* variable width, precision, or base */
242 if((dot == 0 && width >= 0) || (dot == 1 && precis >= 0) )
243 { form -= 1; /* bad pattern specification */
246 GETARG(lval,aint,argf,args,long,uint,'d',t_user,n_user);
248 case '0' : /* defining width or precision */
254 case '1' : case '2' : case '3' :
255 case '4' : case '5' : case '6' :
256 case '7' : case '8' : case '9' :
258 for(n = *form; isdigit(n); n = *++form)
259 lval = (lval<<3) + (lval<<1) + (n - '0');
262 { if((width = (int)lval) < 0)
270 else base = (int)lval;
273 /* modifier for object's length */
283 /* PRINTF DIRECTIVES */
285 case '&' : /* change extension function */
287 extf = va_arg(args,Extf_p);
288 else if((*argf)('&',(char*)(&extf),t_user,n_user) < 0)
291 case '@' : /* change argument getting function */
293 argf = va_arg(args,Argf_p);
294 else if((*argf)('@',(char*)(&argf),t_user,n_user) < 0)
297 case ':' : /* stack a pair of format/arglist */
300 fa->form = (char*)form;
301 GETARG(form,form,argf,args,char*,char*,'1',t_user,n_user);
304 #if (defined(CSRG_BASED) && !defined(__LP64__)) || (defined(linux) && !defined(__LP64__))
305 GETARG(argsp,argsp,argf,args,va_list*,va_list*,'2',t_user,n_user);
306 memcpy((Void_t*)(&(fa->args)), (Void_t*)(&args), sizeof(va_list));
307 memcpy((Void_t*)(&args), (Void_t*)argsp, sizeof(va_list));
309 GETARGL(argsp,argsp,argf,args,va_list*,va_list*,'2',t_user,n_user);
310 __va_copy( fa->args, args );
311 __va_copy( args, argsp );
319 default : /* unknown directive */
322 #if defined(CSRG_BASED) && defined(__i386__)
323 va_list savarg = args; /* is this portable? */
325 va_list savarg; /* is this portable? Sorry .. NO. */
326 __va_copy( savarg, args );
329 GETARG(sp,astr,argf,args,char*,char*,fmt,t_user,n_user);
331 n = (*extf)(sp,fmt,precis,&astr,(int)base,t_user,n_user);
335 #if defined(CSRG_BASED) && !defined(__LP64__)
336 args = savarg; /* extf failed, treat as if unmatched */
338 __va_copy( args, savarg ); /* extf failed, treat as if unmatched */
346 case 's': /* a string */
347 GETARG(sp,astr,argf,args,char*,char*,'s',t_user,n_user);
350 { /* standard error string for null pointer */
351 endsp = (sp = "(null)") + 6;
355 { /* set other bound */
361 else while(*ssp++ && --n >= 0) ;
364 else if(precis >= 0 && precis < n)
368 flags &= ~(F_SIGN|F_BLANK|F_ALTER);
372 case 'n': /* return current output length */
375 { GETARG(alp,alp,argf,args,
376 ulong*,ulong*,'N',t_user,n_user);
380 { GETARG(aip,aip,argf,args,
381 uint*,uint*,'n',t_user,n_user);
385 case 'c': /* a character */
386 GETARG(fmt,achar,argf,args,int,uint,'c',t_user,n_user);
388 flags = (flags&~(F_SIGN|F_BLANK|F_ZERO))|F_REPEAT;
392 case 'p': /* pointer value */
393 GETARG(ssp,astr,argf,args,char*,char*,'p',t_user,n_user);
395 flags = (flags&~(F_SIGN|F_BLANK|F_ZERO))|F_ALTER;
404 flags &= ~(F_SIGN|F_BLANK);
406 { GETARG(lval,along,argf,args,
407 long,ulong,'U',t_user,n_user);
410 { GETARG(lval,aint,argf,args,
411 long,uint,'u',t_user,n_user);
417 { GETARG(lval,along,argf,args,
418 long,long,'D',t_user,n_user);
421 { GETARG(lval,aint,argf,args,
422 long,int,'d',t_user,n_user);
426 if(lval == 0 && precis == 0)
429 if(lval < 0 && (fmt == 'd' || fmt == 'i'))
432 { /* avoid overflow */
433 if(base < 2 || base > SF_RADIX)
435 lval = ((ulong)HIGHBIT)/base;
437 (ulong)HIGHBIT - ((ulong)lval)*base];
451 ssp = "0123456789ABCDEF";
457 if(base < 2 || base > SF_RADIX)
463 { /* special fast conversion for base 10 */
464 sfucvt(lval,sp,n,ssp);
466 else if((base & (base-1)) == 0)
467 { /* calculate shift amount for power-of-2 base */
469 n = base < 4 ? 1 : 2;
471 n = base < 16 ? 3 : 4;
472 else n = base < 64 ? 5 : 6;
475 { *--sp = ssp[lval&(base-1)];
476 } while(lval = ((ulong)lval) >> n);
480 { *--sp = ssp[((ulong)lval)%base];
481 } while(lval = ((ulong)lval)/((ulong)base));
484 /* zero padding for precision */
485 for(precis -= (endsp-sp); precis > 0; --precis)
495 { if(width > 0 && (flags&F_ZERO))
496 { /* do 0 padding first */
498 n = base < 10 ? 2 : 3;
499 else if(fmt == 'x' || fmt == 'X')
502 n += (flags&(F_MINUS|F_SIGN)) ? 1 : 0;
503 n = width - (n + (endsp-sp));
508 { /* base#value notation */
511 *--sp = (char)('0'+base);
513 { *--sp = _Sfdec[(base <<= 1)+1];
514 *--sp = _Sfdec[base];
517 else if(fmt == 'x' || fmt == 'X')
527 case 'g': /* %g and %G ultimately become %e or %f */
535 { GETARG(dval,dval,argf,args,
536 Double_t,Double_t,'G',t_user,n_user);
543 GETARG(sdval,sdval,argf,args,
544 double,double,'F',t_user,n_user);
547 GETARG(dval,dval,argf,args,
548 double,double,'F',t_user,n_user);
552 if(fmt == 'e' || fmt == 'E')
553 { n = (precis = precis < 0 ? FPRECIS : precis)+1;
554 ep = _sfcvt(dval,(int)min(n,SF_FDIGITS),&decpt,&sign,1);
557 else if(fmt == 'f' || fmt == 'F')
558 { precis = precis < 0 ? FPRECIS : precis;
559 ep = _sfcvt(dval,min(precis,SF_FDIGITS),&decpt,&sign,0);
563 /* 'g' or 'G' format */
564 precis = precis < 0 ? FPRECIS : precis == 0 ? 1 : precis;
565 ep = _sfcvt(dval,min(precis,SF_FDIGITS),&decpt,&sign,1);
572 { /* zap trailing 0s */
573 if((n = sfslen()) > precis)
575 while((n -= 1) >= 1 && ep[n] == '0')
581 flags = (flags & ~F_ZERO) | F_GFORMAT;
582 if(decpt < -3 || decpt > precis)
583 { precis = (int)(n-1);
587 { precis = (int)(n - decpt);
591 e_format: /* build the x.yyyy string */
594 sp = endsp = buf+1; /* reserve space for sign */
595 *endsp++ = *ep ? *ep++ : '0';
597 if(precis > 0 || (flags&F_ALTER))
598 *endsp++ = GETDECIMAL(dc,lv);
601 while((*endsp++ = *ep++) && ep <= endep)
603 precis -= (endsp -= 1) - ssp;
605 /* build the exponent */
606 ep = endep = buf+(sizeof(buf)-1);
608 { if((n = decpt - 1) < 0)
612 *--ep = (char)('0' + (lval - n*10));
616 *--ep = (char)('0' + n);
617 if(endep-ep <= 1) /* at least 2 digits */
620 /* the e/Exponent separator and sign */
621 *--ep = (decpt > 0 || dval == 0.) ? '+' : '-';
622 *--ep = isupper(fmt) ? 'E' : 'e';
624 flags = (flags&~F_ZERO)|F_FLOAT;
627 f_format: /* data before the decimal point */
631 endsp = (sp = ep)+sfslen();
637 endsp = sp = buf+1; /* save a space for sign */
639 while(ep < endep && (*endsp++ = *ep++))
644 if(precis > 0 || (flags&F_ALTER))
645 *endsp++ = GETDECIMAL(dc,lv);
648 { /* output zeros for negative exponent */
649 ssp = endsp + min(n,precis);
657 while((*endsp++ = *ep++) && ep <= endep)
659 precis -= (endsp -= 1) - ssp;
664 { /* if a %gG, output the sign now */
667 flags &= ~(F_SIGN|F_BLANK);
669 else flags |= F_MINUS;
676 else if(flags&(F_MINUS|F_SIGN|F_BLANK))
677 fmt = (flags&F_MINUS) ? '-' : (flags&F_SIGN) ? '+' : ' ';
679 n = (endsp-sp) + (endep-ep) + (precis <= 0 ? 0 : precis) +
680 ((flags&F_PREFIX) ? 1 : 0);
681 if((lval = width-n) > 0)
682 { /* check for padding */
684 { /* right padding */
687 else if(flags&F_PREFIX)
688 { /* blank padding, output prefix now */
697 { /* output prefix */
705 v = (flags&F_ZERO) ? '0' : ' ';
709 if((n = precis) > 0 && ((flags&F_REPEAT) || !(flags&F_FLOAT)))
710 { /* repeated chars or padding for integer precision */
711 v = (flags&F_REPEAT) ? fmt : '0';
717 if((n = endsp-sp) > 0)
720 if(flags&(F_FLOAT|F_LEFT))
721 { /* F_FLOAT: right padding for float precision */
725 /* F_FLOAT: the exponent of %eE */
726 if((n = endep-(sp=ep)) > 0)
729 /* F_LEFT: right padding */
735 pop_fa: if(fa = fast)
736 { /* pop the format stack and continue */
738 memcpy((Void_t*)(&args), (Void_t*)(&(fa->args)), sizeof(va_list));
749 r = f->next - f->data;
750 if((d = (char*)f->data) == data)
751 f->endw = f->endr = f->endb = f->data = NIL(uchar*);
754 if(((f->flags&SF_SHARE) && !(f->flags&SF_PUBLIC)) ||
755 (r > 0 && (d == data || ((f->flags&SF_LINE) && !(f->flags&SF_STRING)))) )
756 (void)SFWRITE(f,(Void_t*)d,r);