[1] | 1 | #include "dietfeatures.h" |
---|
| 2 | #include "dietstdio.h" |
---|
| 3 | #include "dietwarning.h" |
---|
| 4 | |
---|
| 5 | #include <stdarg.h> |
---|
| 6 | #include <stdlib.h> |
---|
| 7 | #include <string.h> |
---|
| 8 | #include <errno.h> |
---|
| 9 | |
---|
| 10 | |
---|
| 11 | #define MAX_WIDTH 10*1024 |
---|
| 12 | |
---|
| 13 | static inline unsigned long skip_to(const char *format) { |
---|
| 14 | unsigned long nr; |
---|
| 15 | for (nr=0; format[nr] && (format[nr]!='%'); ++nr); |
---|
| 16 | return nr; |
---|
| 17 | } |
---|
| 18 | |
---|
| 19 | #define A_WRITE(fn,buf,sz) ((fn)->put((void*)(buf),(sz),(fn)->data)) |
---|
| 20 | #define B_WRITE(fn,buf,sz) { if ((unsigned long)(sz) > (((unsigned long)(int)(-1))>>1) || len+(int)(sz)<len) return -1; A_WRITE(fn,buf,sz); } while (0) |
---|
| 21 | |
---|
| 22 | static const char pad_line[2][16]= { " ", "0000000000000000", }; |
---|
| 23 | static int write_pad(unsigned int* dlen,struct arg_printf* fn, unsigned int len, int padwith) { |
---|
| 24 | int nr=0; |
---|
| 25 | if ((int)len<=0) return 0; |
---|
| 26 | if(*dlen+len<len) return -1; |
---|
| 27 | for (;len>15;len-=16,nr+=16) { |
---|
| 28 | A_WRITE(fn,pad_line[(padwith=='0')?1:0],16); |
---|
| 29 | } |
---|
| 30 | if (len>0) { |
---|
| 31 | A_WRITE(fn,pad_line[(padwith=='0')?1:0],(unsigned int)len); nr+=len; |
---|
| 32 | } |
---|
| 33 | *dlen += nr; |
---|
| 34 | return 0; |
---|
| 35 | } |
---|
| 36 | |
---|
| 37 | int __v_printf(struct arg_printf* fn, const char *format, va_list arg_ptr) |
---|
| 38 | { |
---|
| 39 | unsigned int len=0; |
---|
| 40 | #ifdef WANT_ERROR_PRINTF |
---|
| 41 | int _errno = errno; |
---|
| 42 | #endif |
---|
| 43 | |
---|
| 44 | while (*format) { |
---|
| 45 | unsigned long sz = skip_to(format); |
---|
| 46 | if (sz) { |
---|
| 47 | B_WRITE(fn,format,sz); len+=sz; |
---|
| 48 | format+=sz; |
---|
| 49 | } |
---|
| 50 | if (*format=='%') { |
---|
| 51 | char buf[128]; |
---|
| 52 | union { char*s; } u_str; |
---|
| 53 | #define s u_str.s |
---|
| 54 | |
---|
| 55 | int retval; |
---|
| 56 | unsigned char ch, padwith=' ', precpadwith=' '; |
---|
| 57 | |
---|
| 58 | char flag_in_sign=0; |
---|
| 59 | char flag_upcase=0; |
---|
| 60 | char flag_hash=0; |
---|
| 61 | char flag_left=0; |
---|
| 62 | char flag_space=0; |
---|
| 63 | char flag_sign=0; |
---|
| 64 | char flag_dot=0; |
---|
| 65 | signed char flag_long=0; |
---|
| 66 | |
---|
| 67 | unsigned int base; |
---|
| 68 | unsigned int width=0, preci=0; |
---|
| 69 | |
---|
| 70 | long number=0; |
---|
| 71 | #ifdef WANT_LONGLONG_PRINTF |
---|
| 72 | long long llnumber=0; |
---|
| 73 | #endif |
---|
| 74 | |
---|
| 75 | ++format; |
---|
| 76 | inn_printf: |
---|
| 77 | switch(ch=*format++) { |
---|
| 78 | case 0: |
---|
| 79 | return -1; |
---|
| 80 | break; |
---|
| 81 | |
---|
| 82 | /* FLAGS */ |
---|
| 83 | case '#': |
---|
| 84 | flag_hash=-1; |
---|
| 85 | |
---|
| 86 | case 'h': |
---|
| 87 | --flag_long; |
---|
| 88 | goto inn_printf; |
---|
| 89 | #if __WORDSIZE != 64 |
---|
| 90 | case 'j': |
---|
| 91 | #endif |
---|
| 92 | case 'q': /* BSD ... */ |
---|
| 93 | case 'L': |
---|
| 94 | ++flag_long; /* fall through */ |
---|
| 95 | #if __WORDSIZE == 64 |
---|
| 96 | case 'j': |
---|
| 97 | #endif |
---|
| 98 | case 'z': |
---|
| 99 | case 'l': |
---|
| 100 | ++flag_long; |
---|
| 101 | goto inn_printf; |
---|
| 102 | |
---|
| 103 | case '-': |
---|
| 104 | flag_left=1; |
---|
| 105 | goto inn_printf; |
---|
| 106 | |
---|
| 107 | case ' ': |
---|
| 108 | flag_space=1; |
---|
| 109 | goto inn_printf; |
---|
| 110 | |
---|
| 111 | case '+': |
---|
| 112 | flag_sign=1; |
---|
| 113 | goto inn_printf; |
---|
| 114 | |
---|
| 115 | case '0': |
---|
| 116 | case '1': |
---|
| 117 | case '2': |
---|
| 118 | case '3': |
---|
| 119 | case '4': |
---|
| 120 | case '5': |
---|
| 121 | case '6': |
---|
| 122 | case '7': |
---|
| 123 | case '8': |
---|
| 124 | case '9': |
---|
| 125 | if(flag_dot) return -1; |
---|
| 126 | width=strtoul(format-1,(char**)&s,10); |
---|
| 127 | if (width>MAX_WIDTH) return -1; |
---|
| 128 | if (ch=='0' && !flag_left) padwith='0'; |
---|
| 129 | format=s; |
---|
| 130 | goto inn_printf; |
---|
| 131 | |
---|
| 132 | case '*': |
---|
| 133 | { |
---|
| 134 | /* A negative field width is taken as a '-' flag followed by |
---|
| 135 | * a positive field width |
---|
| 136 | **/ |
---|
| 137 | int tmp; |
---|
| 138 | if ((tmp=va_arg(arg_ptr,int))<0) { |
---|
| 139 | flag_left=1; |
---|
| 140 | tmp=-tmp; |
---|
| 141 | } |
---|
| 142 | if ((width=(unsigned long)tmp)>MAX_WIDTH) return -1; |
---|
| 143 | goto inn_printf; |
---|
| 144 | } |
---|
| 145 | case '.': |
---|
| 146 | flag_dot=1; |
---|
| 147 | if (*format=='*') { |
---|
| 148 | int tmp=va_arg(arg_ptr,int); |
---|
| 149 | preci=tmp<0?0:tmp; |
---|
| 150 | ++format; |
---|
| 151 | } else { |
---|
| 152 | long int tmp=strtol(format,(char**)&s,10); |
---|
| 153 | preci=tmp<0?0:tmp; |
---|
| 154 | format=s; |
---|
| 155 | } |
---|
| 156 | if (preci>MAX_WIDTH) return -1; |
---|
| 157 | goto inn_printf; |
---|
| 158 | |
---|
| 159 | /* print a char or % */ |
---|
| 160 | case 'c': |
---|
| 161 | ch=(char)va_arg(arg_ptr,int); |
---|
| 162 | case '%': |
---|
| 163 | B_WRITE(fn,&ch,1); ++len; |
---|
| 164 | break; |
---|
| 165 | |
---|
| 166 | #ifdef WANT_ERROR_PRINTF |
---|
| 167 | /* print an error message */ |
---|
| 168 | case 'm': |
---|
| 169 | s=strerror(_errno); |
---|
| 170 | sz=strlen(s); |
---|
| 171 | B_WRITE(fn,s,sz); len+=sz; |
---|
| 172 | break; |
---|
| 173 | #endif |
---|
| 174 | /* print a string */ |
---|
| 175 | case 's': |
---|
| 176 | s=va_arg(arg_ptr,char *); |
---|
| 177 | #ifdef WANT_NULL_PRINTF |
---|
| 178 | if (!s) s="(null)"; |
---|
| 179 | #endif |
---|
| 180 | sz = strlen(s); |
---|
| 181 | if (flag_dot && sz>preci) sz=preci; |
---|
| 182 | preci=0; |
---|
| 183 | flag_dot^=flag_dot; |
---|
| 184 | padwith=precpadwith=' '; |
---|
| 185 | |
---|
| 186 | print_out: |
---|
| 187 | { |
---|
| 188 | char *sign=s; |
---|
| 189 | int todo=0; |
---|
| 190 | int vs; |
---|
| 191 | |
---|
| 192 | if (! (width||preci) ) { |
---|
| 193 | B_WRITE(fn,s,sz); len+=sz; |
---|
| 194 | break; |
---|
| 195 | } |
---|
| 196 | |
---|
| 197 | if (flag_in_sign) todo=1; |
---|
| 198 | if (flag_hash>0) todo=flag_hash; |
---|
| 199 | if (todo) { |
---|
| 200 | s+=todo; |
---|
| 201 | sz-=todo; |
---|
| 202 | width-=todo; |
---|
| 203 | } |
---|
| 204 | |
---|
| 205 | /* These are the cases for 1234 or "1234" respectively: |
---|
| 206 | %.6u -> "001234" |
---|
| 207 | %6u -> " 1234" |
---|
| 208 | %06u -> "001234" |
---|
| 209 | %-6u -> "1234 " |
---|
| 210 | %.6s -> "1234" |
---|
| 211 | %6s -> " 1234" |
---|
| 212 | %06s -> " 1234" |
---|
| 213 | %-6s -> "1234 " |
---|
| 214 | %6.5u -> " 01234" |
---|
| 215 | %6.5s -> " 1234" |
---|
| 216 | In this code, for %6.5s, 6 is width, 5 is preci. |
---|
| 217 | flag_dot means there was a '.' and preci is set. |
---|
| 218 | flag_left means there was a '-'. |
---|
| 219 | sz is 4 (strlen("1234")). |
---|
| 220 | padwith will be '0' for %06u, ' ' otherwise. |
---|
| 221 | precpadwith is '0' for %u, ' ' for %s. |
---|
| 222 | */ |
---|
| 223 | |
---|
| 224 | if (flag_dot && width==0) width=preci; |
---|
| 225 | if (!flag_dot) preci=sz; |
---|
| 226 | if (!flag_left && padwith==' ') { /* do left-side padding with spaces */ |
---|
| 227 | if (write_pad(&len,fn,width-preci,padwith)) |
---|
| 228 | return -1; |
---|
| 229 | } |
---|
| 230 | if (todo) { |
---|
| 231 | B_WRITE(fn,sign,todo); |
---|
| 232 | len+=todo; |
---|
| 233 | } |
---|
| 234 | if (!flag_left && padwith!=' ') { /* do left-side padding with '0' */ |
---|
| 235 | if (write_pad(&len,fn,width-preci,padwith)) |
---|
| 236 | return -1; |
---|
| 237 | } |
---|
| 238 | /* do preci padding */ |
---|
| 239 | if (write_pad(&len,fn,preci-sz,precpadwith)) |
---|
| 240 | return -1; |
---|
| 241 | /* write actual string */ |
---|
| 242 | B_WRITE(fn,s,sz); len+=sz; |
---|
| 243 | if (flag_left) { |
---|
| 244 | if (write_pad(&len,fn,width-preci,padwith)) |
---|
| 245 | return -1; |
---|
| 246 | } |
---|
| 247 | |
---|
| 248 | break; |
---|
| 249 | } |
---|
| 250 | |
---|
| 251 | /* print an integer value */ |
---|
| 252 | case 'b': |
---|
| 253 | base=2; |
---|
| 254 | sz=0; |
---|
| 255 | goto num_printf; |
---|
| 256 | case 'p': |
---|
| 257 | flag_hash=2; |
---|
| 258 | flag_long=1; |
---|
| 259 | ch='x'; |
---|
| 260 | case 'X': |
---|
| 261 | flag_upcase=(ch=='X'); |
---|
| 262 | case 'x': |
---|
| 263 | base=16; |
---|
| 264 | sz=0; |
---|
| 265 | if (flag_hash) { |
---|
| 266 | buf[1]='0'; |
---|
| 267 | buf[2]=ch; |
---|
| 268 | flag_hash=2; |
---|
| 269 | sz=2; |
---|
| 270 | } |
---|
| 271 | if (preci>width) width=preci; |
---|
| 272 | goto num_printf; |
---|
| 273 | case 'd': |
---|
| 274 | case 'i': |
---|
| 275 | flag_in_sign=1; |
---|
| 276 | case 'u': |
---|
| 277 | base=10; |
---|
| 278 | sz=0; |
---|
| 279 | goto num_printf; |
---|
| 280 | case 'o': |
---|
| 281 | base=8; |
---|
| 282 | sz=0; |
---|
| 283 | if (flag_hash) { |
---|
| 284 | buf[1]='0'; |
---|
| 285 | flag_hash=1; |
---|
| 286 | ++sz; |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | num_printf: |
---|
| 290 | s=buf+1; |
---|
| 291 | |
---|
| 292 | if (flag_long>0) { |
---|
| 293 | #ifdef WANT_LONGLONG_PRINTF |
---|
| 294 | if (flag_long>1) |
---|
| 295 | llnumber=va_arg(arg_ptr,long long); |
---|
| 296 | else |
---|
| 297 | #endif |
---|
| 298 | number=va_arg(arg_ptr,long); |
---|
| 299 | } |
---|
| 300 | else { |
---|
| 301 | number=va_arg(arg_ptr,int); |
---|
| 302 | if (sizeof(int) != sizeof(long) && !flag_in_sign) |
---|
| 303 | number&=((unsigned int)-1); |
---|
| 304 | } |
---|
| 305 | |
---|
| 306 | if (flag_in_sign) { |
---|
| 307 | #ifdef WANT_LONGLONG_PRINTF |
---|
| 308 | if ((flag_long>1)&&(llnumber<0)) { |
---|
| 309 | llnumber=-llnumber; |
---|
| 310 | flag_in_sign=2; |
---|
| 311 | } else |
---|
| 312 | #endif |
---|
| 313 | if (number<0) { |
---|
| 314 | number=-number; |
---|
| 315 | flag_in_sign=2; |
---|
| 316 | } |
---|
| 317 | } |
---|
| 318 | if (flag_long<0) number&=0xffff; |
---|
| 319 | if (flag_long<-1) number&=0xff; |
---|
| 320 | #ifdef WANT_LONGLONG_PRINTF |
---|
| 321 | if (flag_long>1) |
---|
| 322 | retval = __lltostr(s+sz,sizeof(buf)-5,(unsigned long long) llnumber,base,flag_upcase); |
---|
| 323 | else |
---|
| 324 | #endif |
---|
| 325 | retval = __ltostr(s+sz,sizeof(buf)-5,(unsigned long) number,base,flag_upcase); |
---|
| 326 | |
---|
| 327 | /* When 0 is printed with an explicit precision 0, the output is empty. */ |
---|
| 328 | if (flag_dot && retval == 1 && s[sz] == '0') { |
---|
| 329 | if (preci == 0||flag_hash > 0) { |
---|
| 330 | sz = 0; |
---|
| 331 | } |
---|
| 332 | flag_hash = 0; |
---|
| 333 | } else sz += retval; |
---|
| 334 | |
---|
| 335 | if (flag_in_sign==2) { |
---|
| 336 | *(--s)='-'; |
---|
| 337 | ++sz; |
---|
| 338 | } else if ((flag_in_sign)&&(flag_sign || flag_space)) { |
---|
| 339 | *(--s)=(flag_sign)?'+':' '; |
---|
| 340 | ++sz; |
---|
| 341 | } else flag_in_sign=0; |
---|
| 342 | |
---|
| 343 | precpadwith='0'; |
---|
| 344 | |
---|
| 345 | goto print_out; |
---|
| 346 | |
---|
| 347 | #ifdef WANT_FLOATING_POINT_IN_PRINTF |
---|
| 348 | /* print a floating point value */ |
---|
| 349 | case 'f': |
---|
| 350 | case 'g': |
---|
| 351 | { |
---|
| 352 | int g=(ch=='g'); |
---|
| 353 | double d=va_arg(arg_ptr,double); |
---|
| 354 | s=buf+1; |
---|
| 355 | if (width==0) width=1; |
---|
| 356 | if (!flag_dot) preci=6; |
---|
| 357 | if (flag_sign || d < +0.0) flag_in_sign=1; |
---|
| 358 | |
---|
| 359 | sz=__dtostr(d,s,sizeof(buf)-1,width,preci,g); |
---|
| 360 | |
---|
| 361 | if (flag_dot) { |
---|
| 362 | char *tmp; |
---|
| 363 | if ((tmp=strchr(s,'.'))) { |
---|
| 364 | if (preci || flag_hash) ++tmp; |
---|
| 365 | while (preci>0 && *++tmp) --preci; |
---|
| 366 | *tmp=0; |
---|
| 367 | } else if (flag_hash) { |
---|
| 368 | s[sz]='.'; |
---|
| 369 | s[++sz]='\0'; |
---|
| 370 | } |
---|
| 371 | } |
---|
| 372 | |
---|
| 373 | if (g) { |
---|
| 374 | char *tmp,*tmp1; /* boy, is _this_ ugly! */ |
---|
| 375 | if ((tmp=strchr(s,'.'))) { |
---|
| 376 | tmp1=strchr(tmp,'e'); |
---|
| 377 | while (*tmp) ++tmp; |
---|
| 378 | if (tmp1) tmp=tmp1; |
---|
| 379 | while (*--tmp=='0') ; |
---|
| 380 | if (*tmp!='.') ++tmp; |
---|
| 381 | *tmp=0; |
---|
| 382 | if (tmp1) strcpy(tmp,tmp1); |
---|
| 383 | } |
---|
| 384 | } |
---|
| 385 | |
---|
| 386 | if ((flag_sign || flag_space) && d>=0) { |
---|
| 387 | *(--s)=(flag_sign)?'+':' '; |
---|
| 388 | ++sz; |
---|
| 389 | } |
---|
| 390 | |
---|
| 391 | sz=strlen(s); |
---|
| 392 | if (width<sz) width=sz; |
---|
| 393 | precpadwith='0'; |
---|
| 394 | flag_dot=0; |
---|
| 395 | flag_hash=0; |
---|
| 396 | goto print_out; |
---|
| 397 | } |
---|
| 398 | #endif |
---|
| 399 | |
---|
| 400 | default: |
---|
| 401 | break; |
---|
| 402 | } |
---|
| 403 | } |
---|
| 404 | } |
---|
| 405 | return len; |
---|
| 406 | } |
---|
| 407 | |
---|
| 408 | link_warning("__v_printf","warning: the printf functions add several kilobytes of bloat.") |
---|