/* Copyright (C) 2001-2011 by George Williams */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "ustring.h" #include "utype.h" /* unicode printf. Expect arguments to be given using $ notation */ /* But there's no way I'm going to implement all of printf now. I'll do what I */ /* think is important and leave the rest for later. Maybe */ /* args begin with 1 */ struct state { int argmax; struct args { unsigned int is_alt:1; /* # flag */ unsigned int is_zeropad:1; /* 0 flag */ unsigned int is_leftadj:1; /* - flag */ unsigned int is_blank:1; /* " " flag */ unsigned int is_signed:1; /* + flag */ unsigned int is_thousand:1; /* ' flag */ unsigned int is_short:1; /* h */ unsigned int is_long:1; /* l */ unsigned int hasformat:1; /* else it's a precision/fieldwidth */ char format; int fieldwidth, precision; enum arg_type { at_int, at_double, at_ustr, at_astr, at_iptr } arg_type; long ival; const unichar_t *uval; double dval; } *args; unichar_t *opt, *end; int cnt; }; #define addchar(state,ch) (++((state)->cnt),(state)->opt<(state)->end?*((state)->opt)++ = (ch): 0) static int isspec(int ch) { char *str = "%npSscaAgGfFeEouxXdi"; while ( *str && *str!=ch ) ++str; return( *str==ch ); } static void padvalue(struct state *state,int arg,unichar_t *txt,int fieldwidth) { int len=0, padc; padc = state->args[arg].is_zeropad?'0':' '; if ( fieldwidth>0 ) { len = u_strlen(txt); if ( !state->args[arg].is_leftadj ) { while ( len0 ) { len = precision>0?precision:u_strlen(txt); padc = state->args[arg].is_zeropad?'0':' '; if ( !state->args[arg].is_leftadj ) { while ( len=state->argmax ) return; if (( precision = state->args[arg].precision )<0 ) precision = state->args[-state->args[arg].precision-1].ival; if (( fieldwidth = state->args[arg].fieldwidth )<0 ) fieldwidth = state->args[-state->args[arg].fieldwidth-1].ival; if ( fieldwidth<0 ) { fieldwidth = -fieldwidth; state->args[arg].is_leftadj = true; } switch ( state->args[arg].format ) { case 'n': *((int *) (state->args[arg].uval)) = state->cnt; break; case 'c': buf[0] = state->args[arg].ival; buf[1] = '\0'; padvalue(state,arg,buf,fieldwidth); break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'u': trans = state->args[arg].format=='X'?HEX:hex; pt = buf+sizeof(buf)/sizeof(buf[0])-1; *pt-- = '\0'; neg = false; radix = state->args[arg].format=='d' || state->args[arg].format=='i' || state->args[arg].format=='u'?10: state->args[arg].format=='o'?8:16; val = state->args[arg].ival; if ( state->args[arg].ival<0 && (state->args[arg].format=='d' || state->args[arg].format=='i')) { neg = true; val = -val; } for ( i=0; val!=0 || iargs[arg].is_thousand && i!=0 && i%3==0 ) *pt-- = ','; /* !!!!! locale !!!!! */ *pt-- = trans[val%radix]; val /= radix; } if ( state->args[arg].is_alt ) { if ( radix==8 && pt[1]!='0' ) *pt-- = '0'; else if ( radix==16 && state->args[arg].ival!=0 ) { *pt-- = state->args[arg].format; *pt-- = '0'; } } if ( state->args[arg].format=='d' || state->args[arg].format=='i' ) { if ( neg ) *pt-- = '-'; else if ( state->args[arg].is_signed ) *pt-- = '+'; else if ( state->args[arg].is_blank ) *pt-- = ' '; } padvalue(state,arg,pt+1,fieldwidth); break; case 's': if ( state->args[arg].uval == NULL ) { static unichar_t null[] = { '<','n','u','l','l','>', '\0' }; padstr(state,arg,null,fieldwidth,precision); } else if ( state->args[arg].is_short ) { unichar_t *temp = def2u_copy((char *) (state->args[arg].uval)); padstr(state,arg,temp,fieldwidth,precision); free(temp); } else padstr(state,arg,state->args[arg].uval,fieldwidth,precision); break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': /* This doesn't really do a good job!!!! */ switch ( state->args[arg].format ) { case 'e': case 'E': sprintf(cbuf,"%e",state->args[arg].dval); break; case 'f': case 'F': sprintf(cbuf,"%f",state->args[arg].dval); break; case 'g': case 'G': sprintf(cbuf,"%g",state->args[arg].dval); break; case 'a': case 'A': sprintf(cbuf,"%a",state->args[arg].dval); break; } uc_strcpy(buf,cbuf); padvalue(state,arg,buf,fieldwidth); break; /* a 'p' conversion is converted into the equivalent 'x' conversion earlier */ } } int u_vsnprintf(unichar_t *str, int len, const unichar_t *format, va_list ap ) { struct state state; struct args args[20], temp; const unichar_t *pt; int argmax = 0, arg, ac, val, hadarg; memset(&state,'\0',sizeof(state)); memset(args,'\0',sizeof(args)); ac = 0; for ( pt=format; *pt; ) { if ( *pt!='%' ) ++pt; else if ( pt[1]=='%' ) pt += 2; else { for ( ++pt, arg=0; isdigit(*pt); ++pt ) arg = 10*arg + tovalue(*pt); ++ac; if ( *pt=='$' ) { if ( arg>argmax ) argmax = arg; } else { if ( ac>argmax ) argmax = ac; } while ( *pt && !isspec(*pt)) { if ( *pt=='*' ) { ++ac; ++pt; for ( ++pt, arg=0; isdigit(*pt); ++pt ) arg = 10*arg + tovalue(*pt); if ( *pt=='$' ) { if ( arg>argmax ) argmax = arg; } else { if ( ac>argmax ) argmax = ac; } } ++pt; } } } state.argmax = argmax; if ( argmax>sizeof(args)/sizeof(args[0]) ) state.args = gcalloc(argmax,sizeof(struct args)); else state.args = args; state.opt = str; state.end = str+len; ac = 1; for ( pt=format; *pt; ) { if ( *pt!='%' ) ++pt; else if ( pt[1]=='%' ) pt+=2; else { ++pt; memset(&temp,'\0',sizeof(temp)); hadarg = 0; if ( isdigit(*pt)) { for ( arg=0; isdigit(*pt); ++pt ) arg = 10*arg + tovalue(*pt); if ( *pt=='$' ) { hadarg = true; ++pt; } else temp.fieldwidth = arg; } while ( 1 ) { if ( *pt=='#' ) temp.is_alt=true; else if ( *pt=='0' ) temp.is_zeropad=true; else if ( *pt=='-' ) temp.is_leftadj=true; else if ( *pt==' ' ) temp.is_blank=true; else if ( *pt=='+' ) temp.is_signed=true; else if ( *pt=='\'' ) temp.is_thousand=true; else break; ++pt; } if ( *pt=='*' ) { temp.fieldwidth = -ac++; for ( ++pt, val=0; isdigit(*pt); ++pt ) val = 10*val + tovalue(*pt); if ( *pt=='$' ) temp.fieldwidth = -val; } else if ( isdigit(*pt)) { while ( isdigit(*pt)) { temp.fieldwidth = 10*temp.fieldwidth + tovalue(*pt); ++pt; } } temp.precision = 0x800000; if ( *pt=='.' ) { ++pt; if ( *pt=='*' ) { temp.precision = -ac++; for ( ++pt, val=0; isdigit(*pt); ++pt ) val = 10*val + tovalue(*pt); if ( *pt=='$' ) temp.precision = -val; } else if ( isdigit(*pt)) { temp.precision = 0; while ( isdigit(*pt)) { temp.precision = 10*temp.precision + tovalue(*pt); ++pt; } } } if ( *pt=='h' ) { temp.is_short=true; ++pt; } else if ( *pt=='l' ) { temp.is_long=true; ++pt; } if ( temp.fieldwidth<0 ) state.args[-temp.fieldwidth-1].arg_type = at_int; if ( temp.precision<0 ) state.args[-temp.precision-1].arg_type = at_int; temp.format = *pt++; temp.hasformat = true; if ( temp.format=='d' || temp.format=='i' || temp.format=='o' || temp.format=='u' || temp.format=='x' || temp.format=='X' || temp.format=='c' ) { temp.arg_type = at_int; if ( temp.precision == (int) 0x800000 ) temp.precision = 1; } else if ( temp.format=='e' || temp.format=='E' || temp.format=='f' || temp.format=='F' || temp.format=='g' || temp.format=='G' ) { temp.arg_type = at_double; if ( temp.precision == (int) 0x800000 ) temp.precision = 6; } else if ( temp.format=='a' || temp.format=='A' ) { /* aA hex conversion of double */ temp.arg_type = at_double; if ( temp.precision == (int) 0x800000 ) temp.precision = 2*sizeof(double)-2; } else if ( temp.format=='s' && temp.is_short ) temp.arg_type = at_astr; else if ( temp.format=='s' ) temp.arg_type = at_ustr; else if ( temp.format=='p' ) { temp.arg_type = at_int; temp.format = 'x'; temp.is_alt = true; if ( sizeof(int) < sizeof( void * ) ) temp.is_long = true; } else if ( temp.format=='n' ) temp.arg_type = at_iptr; if ( !hadarg ) arg = ac; ++ac; state.args[arg-1] = temp; } } /* Now read the args in order */ for ( arg=0; arg