Rev 6412 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
647 | andrew_pro | 1 | /* |
2 | function for format output to the string |
||
6424 | siemargl | 3 | |
4 | Siemargl update formats as http://www.cplusplus.com/reference/cstdio/printf/, no wchar though |
||
5 | http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html is used too |
||
6 | %g explain https://support.microsoft.com/en-us/kb/43392 |
||
7 | |||
8 | todo: |
||
9 | -%e |
||
10 | -simplify justifying |
||
11 | -fix %o, %x |
||
12 | -fix precision in %g |
||
13 | -%a |
||
14 | -NAN, INF |
||
15 | -%n nothing printed |
||
16 | -%17.18f digits maximum format |
||
17 | %C as w_char L'x' |
||
647 | andrew_pro | 18 | */ |
19 | |||
6424 | siemargl | 20 | |
21 | //#include |
||
647 | andrew_pro | 22 | #include |
23 | #include |
||
6424 | siemargl | 24 | #include |
647 | andrew_pro | 25 | #include |
6424 | siemargl | 26 | #include |
647 | andrew_pro | 27 | |
6424 | siemargl | 28 | enum flags_t |
647 | andrew_pro | 29 | { |
6424 | siemargl | 30 | flag_unsigned = 0x02, |
31 | flag_register = 0x04, |
||
32 | flag_plus = 0x08, |
||
33 | flag_left_just = 0x10, |
||
34 | flag_lead_zeros = 0x20, |
||
35 | flag_space_plus = 0x40, |
||
36 | flag_hash_sign = 0x80, |
||
37 | flag_point = 0x100 |
||
38 | }; |
||
647 | andrew_pro | 39 | |
6424 | siemargl | 40 | int formatted_double_to_string_scientific(long double number, int format1, int format2, char *s, int flags) |
41 | { |
||
42 | strcpy(s, "%e not implemented yet, sorry"); |
||
43 | return strlen(s); |
||
44 | } |
||
647 | andrew_pro | 45 | |
6424 | siemargl | 46 | int formatted_double_to_string(long double number, int format1, int format2, char *s, int flags) |
47 | { |
||
48 | long double nafter, beforpointdigit; |
||
49 | long long intdigit, mul; |
||
50 | int div; |
||
51 | int i; |
||
52 | char* size; |
||
53 | int fmt1; |
||
54 | int fmt2; |
||
55 | char buf[100], *pbuf = buf; |
||
56 | char buf_low[50], *pbuf_lo = buf_low; |
||
647 | andrew_pro | 57 | |
6424 | siemargl | 58 | if((flags & flag_point) == 0) format2 = 6; // default prec if no point spec |
647 | andrew_pro | 59 | |
6424 | siemargl | 60 | size = s; |
61 | if (number < 0) {*s++ = '-'; number = -number; } |
||
62 | else |
||
63 | { |
||
64 | if (flags & flag_plus) *s++ = '+'; else |
||
65 | if (flags & flag_space_plus) *s++ = ' '; |
||
66 | } |
||
647 | andrew_pro | 67 | |
6424 | siemargl | 68 | fmt1 = 1; |
69 | fmt2 = format2; |
||
70 | if (fmt2 > 18) fmt2 = 18; //maximum size of long long type |
||
647 | andrew_pro | 71 | |
6424 | siemargl | 72 | beforpointdigit = floor(number + 0.00000000000001); |
73 | nafter = number - beforpointdigit; |
||
74 | |||
75 | //print part of number before point |
||
76 | mul = 1; |
||
77 | for(i = 0; i < sizeof buf - 1; i++) |
||
78 | { |
||
79 | mul *= 10; |
||
80 | if ((beforpointdigit/mul) < 1.0) { fmt1 = i + 1; break; } |
||
647 | andrew_pro | 81 | } |
6424 | siemargl | 82 | if (i == sizeof buf - 1 || fmt1 > 17) |
83 | { |
||
84 | strcpy(s, "[too big number for %f, %a]"); |
||
85 | return strlen(s); |
||
86 | } |
||
647 | andrew_pro | 87 | |
6424 | siemargl | 88 | mul /= 10; |
89 | while(mul > 1) |
||
90 | { |
||
91 | div = beforpointdigit / mul; |
||
92 | *pbuf++ = (char)div + '0'; |
||
93 | beforpointdigit = beforpointdigit - div * mul; |
||
94 | mul /= 10; |
||
95 | } |
||
96 | *pbuf++=(char)beforpointdigit + '0'; |
||
647 | andrew_pro | 97 | |
6424 | siemargl | 98 | //print part of number after point |
99 | mul = 1; |
||
100 | for(i = 0; i < fmt2; i++) |
||
101 | { |
||
102 | nafter = nafter*10; |
||
103 | mul *= 10; |
||
104 | } |
||
647 | andrew_pro | 105 | |
6424 | siemargl | 106 | intdigit = roundl(nafter); |
647 | andrew_pro | 107 | |
6424 | siemargl | 108 | mul /= 10; |
109 | for(i = 0; i < fmt2 - 1; i++) |
||
110 | { |
||
111 | div = intdigit / mul; |
||
112 | *pbuf_lo++=(char)div + '0'; |
||
113 | intdigit = intdigit - div * mul; |
||
114 | mul /= 10; |
||
115 | if (mul == 1) break; |
||
116 | } |
||
117 | *pbuf_lo++ = (char)intdigit + '0'; |
||
647 | andrew_pro | 118 | |
119 | |||
6424 | siemargl | 120 | memcpy(s, buf, pbuf - buf); s += pbuf - buf; |
121 | if (roundl(nafter) != 0 || fmt2 != 0) |
||
122 | { |
||
123 | *s++ = '.'; |
||
124 | memcpy(s, buf_low, pbuf_lo - buf_low); s += pbuf_lo - buf_low; |
||
125 | } else if (flags & flag_hash_sign) |
||
126 | *s++ = '.'; |
||
647 | andrew_pro | 127 | |
6424 | siemargl | 128 | // right justifiyng and forward zeros |
129 | div = (s - size); |
||
130 | if ((flags & flag_left_just) == 0 && div < format1) |
||
131 | { |
||
132 | pbuf = size; |
||
133 | if ((flags & flag_lead_zeros) != 0) |
||
134 | if (*pbuf == '+' || *pbuf == '-' || *pbuf == ' ') { pbuf++; div--; } // sign already at place |
||
135 | for (i = 0; i < div; i++) |
||
136 | size[format1 - i - 1] = pbuf[div - 1 - i]; |
||
137 | for (i = 0; i < format1 - div - (pbuf - size); i++) |
||
138 | if (flags & flag_lead_zeros) |
||
139 | pbuf[i] = '0'; |
||
140 | else |
||
141 | pbuf[i] = ' '; |
||
142 | |||
143 | return format1; |
||
647 | andrew_pro | 144 | } |
6424 | siemargl | 145 | |
146 | return s - size; |
||
647 | andrew_pro | 147 | } |
148 | |||
6424 | siemargl | 149 | int formatted_long_to_string(long long number, int format1, int prec, char *s, int flags) |
647 | andrew_pro | 150 | { |
6424 | siemargl | 151 | int i; |
152 | int fmt; |
||
153 | char* size = s; |
||
154 | long long digit; |
||
155 | long long mul; |
||
156 | int div; |
||
157 | char buf[100], *pbuf = buf; |
||
647 | andrew_pro | 158 | |
6424 | siemargl | 159 | if (number == -9223372036854775807LL - 1) // overflow all our math, cant minus this |
160 | { |
||
161 | strcpy(buf, "9223372036854775808"); |
||
162 | pbuf += 19; |
||
163 | *s++ = '-'; |
||
164 | goto verybig; |
||
165 | } |
||
647 | andrew_pro | 166 | |
6424 | siemargl | 167 | if (flags & flag_point) flags &= ~flag_lead_zeros; // conflicting flags |
647 | andrew_pro | 168 | |
6424 | siemargl | 169 | if (number < 0) {*s++ = '-'; number = -number; } |
170 | else |
||
171 | { |
||
172 | if (flags & flag_plus) *s++ = '+'; else |
||
173 | if (flags & flag_space_plus) *s++ = ' '; |
||
174 | } |
||
647 | andrew_pro | 175 | |
6424 | siemargl | 176 | digit = number; |
647 | andrew_pro | 177 | |
6424 | siemargl | 178 | mul = (digit < 0) ? -1 : 1; |
647 | andrew_pro | 179 | |
6424 | siemargl | 180 | for(i = 0; i < sizeof buf - 2; i++) |
181 | { |
||
182 | if (digit / mul < 10) { fmt = i + 1; break; } |
||
183 | mul *= 10; |
||
184 | } |
||
185 | |||
186 | // add leading zeros |
||
187 | for(i = 0; i < prec - fmt; i++) *pbuf++ = '0'; |
||
188 | |||
189 | for(i = 0; i < fmt - 1; i++) |
||
190 | { |
||
191 | div = digit / mul; |
||
192 | *pbuf++ = (char)div + '0'; |
||
193 | digit = digit - div * mul; |
||
194 | mul /= 10; |
||
195 | if (mul == 1 || mul == -1) break; |
||
196 | } |
||
197 | *pbuf++ = (char)digit + '0'; |
||
198 | |||
199 | verybig: |
||
200 | memcpy(s, buf, pbuf - buf); s += pbuf - buf; |
||
201 | |||
202 | // right justifiyng and forward zeros |
||
203 | div = (s - size); |
||
204 | if ((flags & flag_left_just) == 0 && div < format1) |
||
205 | { |
||
206 | pbuf = size; |
||
207 | if ((flags & flag_lead_zeros) != 0) |
||
208 | if (*pbuf == '+' || *pbuf == '-' || *pbuf == ' ') { pbuf++; div--; } // sign already at place |
||
209 | for (i = 0; i < div; i++) |
||
210 | size[format1 - i - 1] = pbuf[div - 1 - i]; |
||
211 | for (i = 0; i < format1 - div - (pbuf - size); i++) |
||
212 | if (flags & flag_lead_zeros) |
||
213 | pbuf[i] = '0'; |
||
647 | andrew_pro | 214 | else |
6424 | siemargl | 215 | pbuf[i] = ' '; |
647 | andrew_pro | 216 | |
6424 | siemargl | 217 | return format1; |
218 | } |
||
219 | |||
220 | return s - size; |
||
647 | andrew_pro | 221 | } |
222 | |||
6424 | siemargl | 223 | int formatted_hex_to_string(long long number, int fmt1, char *s, int flags) |
647 | andrew_pro | 224 | { |
225 | long n; |
||
226 | int i,pos; |
||
6424 | siemargl | 227 | // int fmt; |
647 | andrew_pro | 228 | long size; |
229 | int difference_pos; |
||
230 | char xdigs_lower[16]="0123456789abcdef"; |
||
231 | char xdigs_upper[16]="0123456789ABCDEF"; |
||
232 | char buf[200]; |
||
233 | |||
234 | n=(long)number; |
||
235 | size=(int)s; |
||
236 | if (n<0) {*s='-';s++;n=-n;} |
||
237 | |||
238 | if (n==0) {*s='0';s++;goto end;} |
||
239 | for(i=0;i<200;i++) {buf[i]=0;} |
||
240 | |||
241 | i=0; |
||
242 | if (flag_register==0) |
||
243 | { |
||
244 | while (n>0) |
||
245 | { |
||
246 | buf[i]=xdigs_lower[n & 15]; |
||
247 | n=n>>4; |
||
248 | i++; |
||
249 | } |
||
250 | } |
||
251 | else |
||
252 | { |
||
253 | while (n>0) |
||
254 | { |
||
255 | buf[i]=xdigs_upper[n & 15]; |
||
256 | n=n>>4; |
||
257 | i++; |
||
258 | } |
||
259 | } |
||
260 | |||
261 | pos=i; |
||
262 | difference_pos=i; |
||
263 | |||
264 | for(i=pos-1;i>=0;i--) |
||
265 | { |
||
266 | *s=buf[i]; |
||
267 | s++; |
||
268 | } |
||
269 | |||
270 | if (fmt1-difference_pos>0) |
||
271 | { |
||
272 | for(i=difference_pos+1;i<=fmt1;i++) |
||
273 | { |
||
274 | *s=' '; |
||
275 | s++; |
||
276 | } |
||
277 | } |
||
278 | end:size=(int)s-size; |
||
279 | return(size); |
||
280 | } |
||
281 | |||
6424 | siemargl | 282 | int formatted_octa_to_string(long long number, int fmt1, char *s, int flags) |
647 | andrew_pro | 283 | { |
284 | long n; |
||
285 | int i,pos; |
||
6424 | siemargl | 286 | // int fmt; |
647 | andrew_pro | 287 | long size; |
288 | int difference_pos; |
||
289 | char xdigs_lower[16]="012345678"; |
||
290 | char buf[200]; |
||
291 | |||
292 | n=number; |
||
293 | size=(int)s; |
||
294 | if (n<0) {*s='-';s++;n=-n;} |
||
295 | |||
296 | if (n==0) {*s='0';s++;goto end;} |
||
297 | for(i=0;i<200;i++) {buf[i]=0;} |
||
298 | |||
299 | i=0; |
||
300 | if (flag_register==0) |
||
301 | { |
||
302 | while (n>0) |
||
303 | { |
||
304 | buf[i]=xdigs_lower[n & 7]; |
||
305 | n=n>>3; |
||
306 | i++; |
||
307 | } |
||
308 | } |
||
309 | |||
310 | pos=i; |
||
311 | difference_pos=i; |
||
312 | |||
313 | for(i=pos-1;i>=0;i--) |
||
314 | { |
||
315 | *s=buf[i]; |
||
316 | s++; |
||
317 | } |
||
318 | |||
319 | if (fmt1-difference_pos>0) |
||
320 | { |
||
321 | for(i=difference_pos+1;i<=fmt1;i++) |
||
322 | { |
||
323 | *s=' '; |
||
324 | s++; |
||
325 | } |
||
326 | } |
||
327 | end:size=(int)s-size; |
||
328 | return(size); |
||
329 | } |
||
330 | |||
6424 | siemargl | 331 | //int vsnprintf (char * s, size_t n, const char * format, va_list arg ); |
332 | int format_print(char *dest, size_t maxlen, const char *fmt0, va_list argp) |
||
647 | andrew_pro | 333 | { |
6424 | siemargl | 334 | int i; |
335 | int length; |
||
336 | int fmt1, fmt2; // width, precision |
||
337 | size_t pos, posc; |
||
338 | long long intdigit; |
||
339 | long double doubledigit; |
||
340 | // float floatdigit; |
||
341 | const char *fmt, *fmtc; // first point to %, fmtc points to specifier |
||
342 | char *s; // pointer to current dest char |
||
343 | char *str; |
||
344 | char buf[200]; // buffer for current argument value print representation |
||
345 | int format_flag; |
||
346 | int flag_long; // 2 = long double or long long int or wchar |
||
347 | int *point_to_n = NULL; |
||
348 | int flags; // parsed flags |
||
647 | andrew_pro | 349 | |
6424 | siemargl | 350 | fmt = fmt0; |
351 | s = dest; |
||
352 | pos = 0; |
||
353 | while(pos < maxlen) |
||
354 | { |
||
355 | if (*fmt != '%') // usual char |
||
647 | andrew_pro | 356 | { |
6424 | siemargl | 357 | if ('\0' == (*s++ = *fmt++)) break; |
358 | pos++; |
||
359 | continue; |
||
360 | } |
||
647 | andrew_pro | 361 | |
6424 | siemargl | 362 | if (*(fmt + 1) == '%') // %% |
363 | { |
||
364 | *s++ = '%'; pos++; |
||
365 | fmt += 2; |
||
366 | continue; |
||
367 | } |
||
368 | //checking to containg format in the string |
||
369 | fmtc = fmt; |
||
370 | posc = pos; |
||
647 | andrew_pro | 371 | |
6424 | siemargl | 372 | flags = 0; |
373 | format_flag = 0; |
||
374 | flag_long = 0; // 2 = long double or long long int or wchar |
||
647 | andrew_pro | 375 | |
6424 | siemargl | 376 | while(*fmtc != '\0' && !format_flag) // searching end of format |
377 | { |
||
378 | fmtc++; posc++; |
||
379 | switch( *fmtc ) |
||
380 | { |
||
381 | case 'a': |
||
382 | format_flag = 1; |
||
383 | flags |= flag_unsigned; |
||
384 | break; |
||
385 | case 'A': |
||
386 | format_flag = 1; |
||
387 | flags |= flag_unsigned | flag_register; |
||
388 | break; |
||
389 | case 'c': case 'd': case 'i': case 'e': case 'f': case 'g': case 's': case 'n': |
||
390 | format_flag = 1; |
||
391 | break; |
||
392 | case 'E': case 'F': case 'G': |
||
393 | format_flag = 1; |
||
394 | flags |= flag_register; |
||
395 | break; |
||
396 | case 'l': |
||
397 | flag_long = flag_long ? 2 : 1; // ll.eq.L |
||
398 | break; |
||
399 | case 'L': |
||
400 | flag_long = 2; |
||
401 | break; |
||
402 | case 'o': case 'u': case 'x': case 'p': |
||
403 | format_flag = 1; |
||
404 | flags |= flag_unsigned; |
||
405 | break; |
||
406 | case 'X': case 'P': |
||
407 | format_flag = 1; |
||
408 | flags |= flag_unsigned | flag_register; |
||
409 | break; |
||
410 | case '+': |
||
411 | flags |= flag_plus; |
||
412 | break; |
||
413 | case '-': |
||
414 | flags |= flag_left_just; |
||
415 | break; |
||
416 | case ' ': // space |
||
417 | flags |= flag_space_plus; |
||
418 | break; |
||
419 | case '#': |
||
420 | flags |= flag_hash_sign; |
||
421 | break; |
||
422 | case '*': case '.': // just skip |
||
423 | break; |
||
424 | default: |
||
425 | if(isdigit(*fmtc)) break; |
||
426 | strncpy(dest, "print format error - in % invalid char found", maxlen); |
||
427 | return -1; // non format char found - user error |
||
428 | } |
||
429 | } |
||
647 | andrew_pro | 430 | |
6424 | siemargl | 431 | if (format_flag == 0) |
432 | { |
||
433 | strncpy(dest, "print format error - % without format specifier", maxlen); |
||
434 | return -1; // format char not found - user error |
||
435 | } |
||
647 | andrew_pro | 436 | |
6424 | siemargl | 437 | fmt1 = 0; |
438 | fmt2 = 0; |
||
439 | if (posc - pos > 1) // try to read width, precision |
||
440 | { |
||
441 | fmt++; |
||
442 | for(i = pos + 1; i < posc; i++) |
||
443 | { |
||
444 | switch(*fmt) |
||
445 | { |
||
446 | case '0': |
||
447 | if(fmt1 == 0 && (flags & flag_point) == 0) flags |= flag_lead_zeros; |
||
448 | case '1': case '2': case '3': case '4': |
||
449 | case '5': case '6': case '7': case '8': case '9': |
||
450 | if ((flags & flag_point) == 0) |
||
451 | fmt1 = fmt1 * 10 + (*fmt -'0'); |
||
452 | else |
||
453 | fmt2 = fmt2 * 10 + (*fmt -'0'); |
||
454 | break; |
||
455 | case '*': |
||
456 | if (flag_point == 0) |
||
457 | fmt1 = va_arg(argp, int); |
||
458 | else |
||
459 | fmt2 = va_arg(argp, int); |
||
460 | break; |
||
461 | case '.': |
||
462 | flags |= flag_point; |
||
463 | break; |
||
464 | case 'l': case 'L': case '+': // valid chars - skip |
||
465 | case '-': case ' ': case '#': |
||
466 | break; |
||
467 | default: // must be error |
||
468 | strncpy(dest, "print format error - %width.precision", maxlen); |
||
469 | return -1; // format char not found - user error |
||
470 | } |
||
471 | fmt++; |
||
472 | } |
||
473 | } |
||
647 | andrew_pro | 474 | |
6424 | siemargl | 475 | // do real work - format arguments values |
647 | andrew_pro | 476 | |
6424 | siemargl | 477 | length = 0; |
478 | switch(*fmtc) |
||
479 | { |
||
480 | case 'n': |
||
481 | point_to_n = va_arg(argp, int*); |
||
482 | break; |
||
483 | case 'c': |
||
484 | if (pos + 1 <= maxlen) |
||
485 | { |
||
486 | buf[0] = (char)va_arg(argp, int); |
||
487 | length = 1; |
||
488 | } |
||
489 | break; |
||
490 | case 's': // special case - without buf |
||
491 | str = va_arg(argp, char*); |
||
492 | length = strlen(str); |
||
493 | if ((flags & flag_point) && (length > fmt2)) length = fmt2; // cut by precision |
||
494 | if (pos + length > maxlen) length = maxlen - pos; |
||
495 | memcpy(s, str ,length); |
||
496 | s += length; |
||
497 | pos += length; |
||
498 | break; |
||
499 | case 'd': case 'i': case 'u': case 'U': |
||
500 | if (flag_long == 0) intdigit = va_arg(argp, int); else |
||
501 | if (flag_long == 1) intdigit = va_arg(argp, long); else |
||
502 | if (flag_long == 2) intdigit = va_arg(argp, long long); |
||
503 | length = formatted_long_to_string(intdigit, fmt1, fmt2, buf, flags); |
||
504 | break; |
||
505 | case 'o': |
||
506 | if (flag_long == 0) intdigit = va_arg(argp, int); else |
||
507 | if (flag_long == 1) intdigit = va_arg(argp, long); else |
||
508 | if (flag_long == 2) intdigit = va_arg(argp, long long); |
||
509 | length = formatted_octa_to_string(intdigit, fmt1, buf, flags); |
||
510 | break; |
||
511 | case 'p': case 'P': case 'x': case 'X': |
||
512 | if (flag_long == 0) intdigit = va_arg(argp, int); else |
||
513 | if (flag_long == 1) intdigit = va_arg(argp, long); else |
||
514 | if (flag_long == 2) intdigit = va_arg(argp, long long); |
||
515 | length=formatted_hex_to_string(intdigit, fmt1, buf, flags); |
||
516 | break; |
||
517 | case 'a': case 'A': case 'f': case 'F': |
||
518 | if (flag_long <= 1) doubledigit = va_arg(argp, double); else |
||
519 | if (flag_long == 2) doubledigit = va_arg(argp, long double); |
||
520 | length = formatted_double_to_string(doubledigit, fmt1, fmt2, buf, flags); |
||
521 | break; |
||
522 | case 'e': case 'E': |
||
523 | if (flag_long <= 1) doubledigit = va_arg(argp, double); else |
||
524 | if (flag_long == 2) doubledigit = va_arg(argp, long double); |
||
525 | length = formatted_double_to_string_scientific(doubledigit, fmt1, fmt2, buf, flags); |
||
526 | break; |
||
527 | case 'g': case 'G': |
||
528 | //prec special case, this is just workaround |
||
529 | if (flag_long <= 1) doubledigit = va_arg(argp, double); else |
||
530 | if (flag_long == 2) doubledigit = va_arg(argp, long double); |
||
531 | length = formatted_double_to_string(doubledigit, fmt1, fmt2, buf, flags); |
||
532 | i = formatted_double_to_string_scientific(doubledigit, fmt1, fmt2, buf + sizeof buf / 2, flags); |
||
533 | if(length > i) |
||
534 | { |
||
535 | memcpy(buf, buf + sizeof buf / 2, i); |
||
536 | length = i; |
||
537 | } |
||
538 | break; |
||
539 | } |
||
540 | if (*fmtc != 's' && length > 0) // skip multiple string copying |
||
541 | { |
||
542 | if (pos + length > maxlen) length = maxlen - pos; |
||
543 | memcpy(s, buf, length); |
||
544 | s += length; |
||
545 | pos += length; |
||
546 | } |
||
547 | fmt = fmtc + 1; |
||
548 | } |
||
647 | andrew_pro | 549 | |
6424 | siemargl | 550 | if (point_to_n) *point_to_n = pos; |
551 | return(pos); |
||
647 | andrew_pro | 552 | }=>=>=>=>>>=fmt1;i++) |