FreeBASIC  0.91.0
str_format.c
Go to the documentation of this file.
1 /* format function */
2 
3 #include "fb.h"
4 #include <math.h>
5 
6 typedef enum _eMaskType {
10 } eMaskType;
11 
12 typedef struct _FormatMaskInfo {
14 
20  int has_sign;
26 
27  int has_ampm;
28 
29  ssize_t length_min;
30  ssize_t length_opt;
32 
33 #define FB_MAXFIXLEN 19 /* floor( log10( pow( 2.0, 63 ) ) ) + 1 */
34 
42 static
44  (
45  double number,
46  char *pachFixPart,
47  ssize_t *pcchLenFix,
48  char *pachFracPart,
49  ssize_t *pcchLenFrac,
50  char *pchSign,
51  char chDecimalPoint,
52  int precision
53  )
54 {
55  char *pszFracStart, *pszFracEnd;
56  char chSign;
57  double dblFix;
58  double dblFrac = modf( number, &dblFix );
59  long long llFix = (long long) dblFix;
60  ssize_t len_fix, len_frac;
61 
62  /* make fractional part positive */
63  if( dblFrac < 0.0 )
64  dblFrac = -dblFrac;
65 
66  /* Store fractional part of number into buffer */
67  len_frac = sprintf( pachFracPart, "%.*f", precision, dblFrac );
68 
69  /* Remove trailing zeroes and - if it completely consists of zeroes -
70  * also remove the decimal point */
71  pszFracStart = pachFracPart;
72  if( *pszFracStart=='-' )
73  ++pszFracStart; /* Required for -0.0 value */
74  ++pszFracStart;
75  pszFracEnd = pachFracPart + len_frac;
76  while( pszFracEnd!=pszFracStart ) {
77  --pszFracEnd;
78  if( *pszFracEnd!='0' ) {
79  if( *pszFracEnd!=chDecimalPoint ) {
80  ++pszFracStart;
81  ++pszFracEnd;
82  }
83  break;
84  }
85  }
86 
87  /* Move usable fractional part of number to begin of buffer */
88  len_frac = pszFracEnd - pszFracStart;
89  memmove( pachFracPart, pszFracStart, len_frac );
90  pachFracPart[len_frac] = 0;
91 
92  /* Store fix part of the number into buffer */
93  if( llFix==0 && number < 0.0 ) {
94  pachFixPart[0] = 0;
95  len_fix = 0;
96  chSign = '-';
97  } else if( llFix==0 && number > 0.0 ) {
98  pachFixPart[0] = 0;
99  len_fix = 0;
100  chSign = '+';
101  } else {
102  if( llFix < 0 ) {
103  chSign = '-';
104  llFix = -llFix;
105  } else if( llFix > 0 ) {
106  chSign = '+';
107  } else {
108  chSign = 0;
109  }
110  len_fix = sprintf( pachFixPart, "%" FB_LL_FMTMOD "d", llFix );
111  }
112 
113  if( pcchLenFix!=NULL )
114  *pcchLenFix = len_fix;
115  if( pcchLenFrac!=NULL )
116  *pcchLenFrac = len_frac;
117  if( pchSign!=NULL )
118  *pchSign = chSign;
119 }
120 
121 static
123  (
124  double num,
125  char decimal_point,
126  char thousands_separator
127  )
128 {
129  char FixPart[128], FracPart[128], chSign;
130  ssize_t LenFix, LenFrac, LenSign, LenDecPoint, LenTotal;
131  FBSTRING *dst;
132 
133  fb_hGetNumberParts( num,
134  FixPart, &LenFix,
135  FracPart, &LenFrac,
136  &chSign,
137  '.',
138  11 );
139 
140  LenSign = ( ( chSign=='-' ) ? 1 : 0 );
141  LenDecPoint = ( ( LenFrac!=0 ) ? 1 : 0 );
142  LenTotal = LenSign + LenFix + LenDecPoint + LenFrac;
143 
144  /* alloc temp string */
145  dst = fb_hStrAllocTemp_NoLock( NULL, LenTotal );
146  if( dst != NULL )
147  {
148  if( LenSign!=0 ) {
149  dst->data[0] = chSign;
150  }
151  FB_MEMCPY( dst->data + LenSign, FixPart, LenFix );
152  if( LenDecPoint!=0 ) {
153  dst->data[LenSign + LenFix] = decimal_point;
154  }
155  FB_MEMCPY( dst->data + LenSign + LenFix + LenDecPoint,
156  FracPart,
157  LenFrac );
158  dst->data[LenTotal] = 0;
159  }
160  else
161  dst = &__fb_ctx.null_desc;
162 
163  return dst;
164 }
165 
166 static double hRound
167  (
168  double value,
169  const FormatMaskInfo *pInfo
170  )
171 {
172  double fix, frac = modf( value, &fix );
173 
174  if( pInfo->num_digits_frac == 0 )
175  {
176  /* round it here, because modf() at GetNumParts won't */
177 
178  /* convert to fixed-point because the imprecision and the optimizations
179  that can be done by gcc (ie: keeping values on fpu stack as twords) */
180  long long int intfrac = (long long int)(frac * 1.E+15);
181  if( intfrac > (long long int)(5.E+14) )
182  value = ceil( value );
183  else if( intfrac < -(long long int)(5.E+14) )
184  value = floor( value );
185  }
186  else
187  {
188  /* remove the fraction of the fraction to be compatible with
189  VBDOS (ie: 2.55 -> 2.5, not 2.6 as in VB6) */
190  if( frac != 0.0 )
191  {
192  double p10 = pow( 10.0, pInfo->num_digits_frac );
193 
194  double fracfrac = modf( frac * p10, &frac );
195 
196  /* convert to fixed-point, see above */
197  long long int intfrac = (long long int)(fracfrac * (1.E+15 / p10) );
198 
199  if( intfrac > (long long int)(5.E+14 / p10) )
200  frac += 1.0;
201  else if( intfrac < -(long long int)(5.E+14 / p10) )
202  frac += -1.0;
203 
204  frac /= p10;
205 
206  value = fix + frac;
207  }
208  }
209 
210  return value;
211 }
212 
225 static
226 int fb_hProcessMask
227  (
228  FBSTRING *dst,
229  const char *mask,
230  ssize_t mask_length,
231  double value,
232  FormatMaskInfo *pInfo,
233  char chThousandsSep,
234  char chDecimalPoint,
235  char chDateSep,
236  char chTimeSep
237  )
238 {
239  char FixPart[128], FracPart[128], ExpPart[128], chSign = 0;
240  ssize_t LenFix, LenFrac, LenExp = 0, IndexFix, IndexFrac, IndexExp = 0;
241  ssize_t ExpValue, ExpAdjust = 0, NumSkipFix = 0, NumSkipExp = 0;
242  int do_skip = FALSE, do_exp = FALSE, do_string = FALSE;
243  int did_sign = FALSE, did_exp = FALSE, did_hour = FALSE, did_thousandsep = FALSE;
244  int do_num_frac = FALSE, last_was_comma = FALSE, was_k_div = FALSE;
245  int do_output = dst!=NULL;
246  int do_add = FALSE;
247  ssize_t LenOut;
248  char *pszOut;
249  ssize_t i;
250 
251  DBG_ASSERT( pInfo!=NULL );
252 
253  if( !do_output ) {
254  memset( pInfo, 0, sizeof(FormatMaskInfo) );
255  pszOut = NULL;
256  LenOut = 0;
257  } else {
258  if( pInfo->mask_type==eMT_Number ) {
259  if( pInfo->has_percent )
260  value *= 100.0;
261  value /= pow( 10.0, pInfo->num_digits_omit );
262  }
263  pszOut = dst->data;
264  LenOut = FB_STRSIZE( dst );
265  }
266 
267  if( value != 0.0 )
268  ExpValue = (int)floor( log10( fabs( value ) ) ) + 1;
269  else
270  ExpValue = 0;
271 
272  if( do_output )
273  {
274  if( pInfo->mask_type==eMT_Number )
275  {
276  /* When output of exponent is required, shift value to the
277  * left (* 10^n) as far as possible. "As far as possible" depends
278  * on the number of digits required by the number as a textual
279  * representation. */
280 
281  if( pInfo->has_exponent )
282  {
283  /* exponent too big? scale (up or down) */
284  if( ExpValue <= 0 )
285  {
286  ExpValue -= pInfo->num_digits_fix;
287  }
288  else
289  {
290  if( pInfo->num_digits_frac > 0 )
291  {
292  if( ExpValue > pInfo->num_digits_fix )
293  ExpValue -= pInfo->num_digits_fix;
294  else
295  ExpValue = 0;
296  }
297  else
298  {
299  if( ExpValue > FB_MAXFIXLEN )
300  ExpValue -= FB_MAXFIXLEN;
301  else
302  ExpValue = 0;
303  }
304  }
305 
306  if( ExpValue != 0 )
307  value *= pow( 10.0, -ExpValue );
308 
309  LenExp = sprintf( ExpPart, "%d", (int)ExpValue );
310 
311  if( ExpValue < 0 )
312  IndexExp = ExpAdjust = 1;
313  else
314  IndexExp = ExpAdjust = 0;
315 
316  NumSkipExp = pInfo->exp_digits - ( LenExp - ExpAdjust );
317  }
318  /* value between (+|-)0.0..1.0 */
319  else if( ExpValue < 0 )
320  {
321  /* too small? */
322  if( -ExpValue >= pInfo->num_digits_frac )
323  {
324 #if 0
325  /* can't scale? */
326  if( (pInfo->num_digits_frac == 0 ) ||
327  (-ExpValue > pInfo->num_digits_fix +
328  pInfo->num_digits_frac -
329  pInfo->num_digits_omit) )
330  value = 0.0;
331  else
332  value *= pow( 10.0, -ExpValue + pInfo->num_digits_fix );
333 #else
334  value = 0.0;
335 #endif
336  ExpValue = 0;
337  }
338 
339  }
340  /* value is 0.0 or (+|-)1.0... */
341  else
342  {
343  /* too big to fit on a long long? */
344  if( ExpValue > FB_MAXFIXLEN )
345  {
346  ExpValue -= FB_MAXFIXLEN;
347  value *= pow( 10.0, -ExpValue );
348  }
349  else
350  ExpValue = 0;
351  }
352 
353  value = hRound( value, pInfo );
354 
355  fb_hGetNumberParts( value,
356  FixPart, &LenFix,
357  FracPart, &LenFrac,
358  &chSign,
359  '.',
360  pInfo->num_digits_frac );
361 
362 
363  /* handle too big numbers */
364  if( (ExpValue > 0) && !pInfo->has_exponent )
365  {
366  int i;
367  for( i = 0; i < ExpValue; i++ )
368  FixPart[LenFix+i] = '0';
369 
370  LenFix += ExpValue;
371 
372  FixPart[LenFix] = '\0';
373  }
374 
375  /* Number of digits to skip on output */
376  NumSkipFix = pInfo->num_digits_fix - LenFix;
377 
378  }
379  }
380  else
381  {
382  /* just assume the max possible */
383  LenFix = (ExpValue > FB_MAXFIXLEN? ExpValue : FB_MAXFIXLEN);
384  LenFrac = 0;
385  }
386 
387  IndexFix = IndexFrac = 0;
388  for( i=0; i!=mask_length; ++i ) {
389  const char *pszAdd = mask + i;
390  char *pszAddFree = NULL;
391  int LenAdd = 1;
392  char chCurrent = *pszAdd;
393  if( do_skip ) {
394  do_skip = FALSE;
395  if( !do_output ) {
396  ++pInfo->length_min;
397  } else {
398  do_add = TRUE;
399  }
400  } else if( do_exp ) {
401  if( !do_output ) {
402  pInfo->has_exponent = TRUE;
403  switch( chCurrent ) {
404  case '-':
405  pInfo->exponent_add_plus = FALSE;
406  ++pInfo->length_opt;
407  break;
408  case '+':
409  pInfo->exponent_add_plus = TRUE;
410  ++pInfo->length_min;
411  break;
412  default:
414  return FALSE;
415  }
416  } else {
417  if( pInfo->exponent_add_plus || ExpValue<0 ) {
418  if( ExpValue < 0 ) {
419  pszAdd = "-";
420  } else {
421  pszAdd = "+";
422  }
423  do_add = TRUE;
424  }
425  }
426  do_exp = FALSE;
427  did_exp = TRUE;
428  do_num_frac = FALSE;
429  } else if( do_string ) {
430  if( chCurrent=='"' ) {
431  do_string = FALSE;
432  } else if( !do_output ) {
433  ++pInfo->length_min;
434  } else {
435  do_add = TRUE;
436  }
437  } else {
438  if( do_output ) {
439  switch (chCurrent ) {
440  case '.':
441  case '#':
442  case '0':
443  if( !pInfo->has_sign && !did_sign ) {
444  did_sign = TRUE;
445  if( pInfo->sign_add_plus || chSign=='-' ) {
446  pszAdd = &chSign;
447  do_add = TRUE;
448  } else {
449  --i;
450  continue;
451  }
452  } else if( NumSkipFix < 0 ) {
453  DBG_ASSERT( IndexFix!=LenFix );
454  pszAdd = FixPart + IndexFix;
455  if( pInfo->has_thousand_sep ) {
456  int remaining = LenFix - IndexFix;
457  if( (remaining % 3)==0 ) {
458  if( did_thousandsep ) {
459  did_thousandsep = FALSE;
460  LenAdd = 3;
461  } else {
462  did_thousandsep = TRUE;
463  pszAdd = &chThousandsSep;
464  LenAdd = 1;
465  }
466  } else {
467  LenAdd = remaining % 3;
468  }
469  } else {
470  LenAdd = -NumSkipFix;
471  }
472  do_add = TRUE;
473  if( !did_thousandsep ) {
474  IndexFix += LenAdd;
475  NumSkipFix += LenAdd;
476  }
477  }
478  break;
479  }
480  if( do_add )
481  --i;
482  }
483 
484  if( !do_add ) {
485  switch( chCurrent ) {
486  case '%':
487  case ',':
488  case '#':
489  case '0':
490  case '+':
491  case 'E':
492  case 'e':
493  if( pInfo->mask_type==eMT_Unknown ) {
494  pInfo->mask_type = eMT_Number;
495 #if 0
496  } else if( pInfo->mask_type!=eMT_Number ) {
498  return FALSE;
499 #endif
500  }
501  break;
502  case '-':
503  case '.':
504  if( pInfo->mask_type==eMT_Unknown ) {
505  pInfo->mask_type = eMT_Number;
506  }
507  break;
508  case 'd':
509  case 'n':
510  case 'm':
511  case 'M':
512  case 'y':
513  case 'h':
514  case 'H':
515  case 's':
516  case 't':
517  case ':':
518  case '/':
519  if( pInfo->mask_type==eMT_Unknown ) {
520  pInfo->mask_type = eMT_DateTime;
521 #if 0
522  } else if( pInfo->mask_type!=eMT_DateTime ) {
524  return FALSE;
525 #endif
526  }
527  break;
528  }
529 
530 
531  /* Here comes the real interpretation */
532  switch( chCurrent ) {
533  case '%':
534  if( !do_output ) {
535  if( pInfo->mask_type==eMT_Number ) {
536  if( !pInfo->has_percent ) {
537  pInfo->has_percent = TRUE;
538  ++pInfo->length_min;
539  } else {
541  return FALSE;
542  }
543  } else {
544  ++pInfo->length_min;
545  }
546  } else {
547  do_add = TRUE;
548  }
549  break;
550  case '.':
551  if( !do_output ) {
552  if( pInfo->mask_type==eMT_Number ) {
553  if( !pInfo->has_decimal_point ) {
554  pInfo->has_decimal_point = TRUE;
555  if( last_was_comma ) {
556  pInfo->num_digits_omit += 3;
557  was_k_div = TRUE;
558  } else if( pInfo->num_digits_omit!=0 ) {
559  pInfo->num_digits_omit += 3;
560  }
561  }
562  }
563  ++pInfo->length_min;
564  } else {
565  do_add = TRUE;
566  if( pInfo->mask_type==eMT_Number ) {
567  pszAdd = &chDecimalPoint;
568  }
569  }
570  do_num_frac = TRUE;
571  break;
572  case ',':
573  if( !do_output ) {
574  if( pInfo->mask_type==eMT_Number ) {
575  if( last_was_comma ) {
576  pInfo->num_digits_omit += 3;
577  was_k_div = TRUE;
578  }
579  last_was_comma = TRUE;
580  } else {
581  ++pInfo->length_min;
582  }
583  } else {
584  if( pInfo->mask_type==eMT_Number ) {
585  if( last_was_comma ) {
586  was_k_div = TRUE;
587  }
588  last_was_comma = TRUE;
589  } else {
590  do_add = TRUE;
591  }
592  }
593  break;
594  case '#':
595  case '0':
596  if( !do_output ) {
597  if( pInfo->mask_type==eMT_Number ) {
598  if( do_num_frac ) {
599  ++pInfo->num_digits_frac;
600  } else if( did_exp ) {
601  ++pInfo->exp_digits;
602  } else {
603  ++pInfo->num_digits_fix;
604  }
605  if( chCurrent=='#' ) {
606  ++pInfo->length_opt;
607  } else {
608  ++pInfo->length_min;
609  }
610  } else {
611  ++pInfo->length_min;
612  }
613  } else {
614  if( pInfo->mask_type==eMT_Number ) {
615  if( do_num_frac ) {
616  if( IndexFrac!=LenFrac ) {
617  pszAdd = FracPart + IndexFrac++;
618  do_add = TRUE;
619  } else if( chCurrent=='0' ) {
620  do_add = TRUE;
621  }
622  } else if( did_exp ) {
623  if( NumSkipExp > 0 ) {
624  if( chCurrent=='0' ) {
625  do_add = TRUE;
626  }
627  --NumSkipExp;
628  } else if( IndexExp!=LenExp ) {
629  pszAdd = ExpPart + IndexExp++;
630  if( (IndexExp-ExpAdjust)>=pInfo->exp_digits
631  && ( IndexExp!=LenExp ) )
632  {
633  --i;
634  }
635  do_add = TRUE;
636  } else {
637  DBG_ASSERT( FALSE );
638  }
639 
640  } else {
641  if( pInfo->has_thousand_sep ) {
642  int remaining = LenFix - IndexFix + NumSkipFix;
643  if( (remaining % 3)==0 ) {
644  if( did_thousandsep ) {
645  did_thousandsep = FALSE;
646  } else {
647  if( NumSkipFix==0 && IndexFix!=0 ) {
648  did_thousandsep = TRUE;
649  pszAdd = &chThousandsSep;
650  LenAdd = 1;
651  do_add = TRUE;
652  --i;
653  }
654  }
655  }
656  }
657 
658  if( !do_add ) {
659  if( NumSkipFix ) {
660  if( chCurrent=='0' )
661  do_add = TRUE;
662  --NumSkipFix;
663  } else {
664  if( IndexFix!=LenFix ) {
665  pszAdd = FixPart + IndexFix++;
666  do_add = TRUE;
667  } else if( chCurrent=='0' ) {
668  do_add = TRUE;
669  }
670  }
671  }
672  }
673  } else {
674  do_add = TRUE;
675  }
676  }
677  break;
678  case 'E':
679  case 'e':
680  if( pInfo->mask_type==eMT_Number ) {
681  if( !did_exp ) {
682  do_exp = TRUE;
683  if( !do_output ) {
684  ++pInfo->length_min;
685  } else {
686  do_add = TRUE;
687  }
688  } else {
690  return FALSE;
691  }
692  } else {
693  if( !do_output ) {
694  ++pInfo->length_min;
695  } else {
696  do_add = TRUE;
697  }
698  }
699  break;
700  case '\\':
701  do_skip = TRUE;
702  break;
703  case '*':
704  case '$':
705  case '(':
706  case ')':
707  case ' ':
708  case '\t':
709  if( !do_output ) {
710  ++pInfo->length_min;
711  } else {
712  do_add = TRUE;
713  }
714  break;
715  case '+':
716  case '-':
717  /* position of the sign? */
718  if( !do_output ) {
719  ++pInfo->length_min;
720  if( !pInfo->has_sign ) {
721  pInfo->has_sign = TRUE;
722  pInfo->sign_add_plus = chCurrent=='+';
723  }
724  } else {
725  if( pInfo->mask_type == eMT_DateTime ) {
726  do_add = TRUE;
727  } else if( !did_sign ) {
728  did_sign = TRUE;
729  if( pInfo->sign_add_plus || chSign=='-' ) {
730  pszAdd = &chSign;
731  do_add = TRUE;
732  }
733  } else {
734  do_add = TRUE;
735  }
736  }
737  break;
738  case 'd':
739  /* day */
740  case 'm':
741  /* minute or month */
742  case 'n':
743  /* minute */
744  case 'M':
745  /* month */
746  case 'y':
747  /* year */
748  case 'h':
749  case 'H':
750  /* hour */
751  case 's':
752  /* second */
753  case 't':
754  /* complete short time */
755  if( pInfo->mask_type==eMT_DateTime ) {
756  int old_did_hour = did_hour;
757  int count = 1;
758  while( mask[i+count]==chCurrent )
759  ++count;
760  did_hour = FALSE;
761  if( chCurrent=='m' ) {
762  if( count>2 || !old_did_hour ) {
763  chCurrent = 'M';
764  }
765  }
766  if( chCurrent=='t' && count==5 ) {
767  FBSTRING *tmp;
768  i += count-1;
769  fb_IntlGetTimeFormat( FixPart, sizeof(FixPart), FALSE );
770  tmp = fb_hStrFormat ( value,
771  FixPart,
772  strlen(FixPart) );
773  if( !do_output ) {
774  pInfo->length_min += FB_STRSIZE(tmp);
775  } else {
776  pszAddFree = strdup( tmp->data );
777  LenAdd = FB_STRSIZE( tmp );
778  do_add = TRUE;
779  }
780  fb_hStrDelTemp( tmp );
781  } else if( chCurrent=='t' && (count==1 || count==2) ) {
782  i += count-1;
783  if( !do_output ) {
784  pInfo->length_min += count;
785  pInfo->has_ampm = TRUE;
786  } else {
787  int hour = fb_Hour( value );
788  if( hour >= 12 ) {
789  pszAdd = "PM";
790  } else {
791  pszAdd = "AM";
792  }
793  LenAdd = count;
794  do_add = TRUE;
795  }
796  } else if( chCurrent=='d' && count==5 ) {
797  FBSTRING *tmp;
798  i += count-1;
799  fb_IntlGetDateFormat( FixPart, sizeof(FixPart), FALSE );
800  tmp = fb_hStrFormat ( value,
801  FixPart,
802  strlen(FixPart) );
803  if( !do_output ) {
804  pInfo->length_min += FB_STRSIZE(tmp);
805  } else {
806  pszAddFree = strdup( tmp->data );
807  LenAdd = FB_STRSIZE( tmp );
808  do_add = TRUE;
809  }
810  fb_hStrDelTemp( tmp );
811  } else if( chCurrent=='d' && count==1 ) {
812  i += count-1;
813  if( !do_output ) {
814  ++pInfo->length_min;
815  ++pInfo->length_opt;
816  } else {
817  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
818  "%d",
819  fb_Day( value ) );
820  do_add = TRUE;
821  }
822  } else if( chCurrent=='d' && count==2 ) {
823  i += count-1;
824  if( !do_output ) {
825  pInfo->length_min += 2;
826  } else {
827  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
828  "%02d",
829  fb_Day( value ) );
830  do_add = TRUE;
831  }
832  } else if( chCurrent=='d' && (count==3 || count==4) ) {
833  int weekday = fb_Weekday( value, FB_WEEK_DAY_SUNDAY );
834  FBSTRING *tmp = fb_WeekdayName( weekday, (count==3), FB_WEEK_DAY_SUNDAY );
835  i += count-1;
836  if( !do_output ) {
837  pInfo->length_min += FB_STRSIZE( tmp );
838  } else {
839  pszAddFree = strdup( tmp->data );
840  LenAdd = 0;
841  do_add = TRUE;
842  }
843  fb_hStrDelTemp( tmp );
844  } else if( (chCurrent=='m' || chCurrent == 'n') && count==1 ) {
845  i += count-1;
846  if( !do_output ) {
847  ++pInfo->length_min;
848  ++pInfo->length_opt;
849  } else {
850  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
851  "%d",
852  fb_Minute( value ) );
853  do_add = TRUE;
854  }
855  } else if( (chCurrent=='m' || chCurrent == 'n') && count==2 ) {
856  i += count-1;
857  if( !do_output ) {
858  pInfo->length_min += 2;
859  } else {
860  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
861  "%02d",
862  fb_Minute( value ) );
863  do_add = TRUE;
864  }
865  } else if( (chCurrent=='h' || chCurrent=='H') && count==1 ) {
866  i += count-1;
867  if( !do_output ) {
868  ++pInfo->length_min;
869  ++pInfo->length_opt;
870  } else {
871  int hour = fb_Hour( value );
872  if( pInfo->has_ampm && chCurrent=='h') {
873  if( hour > 12 ) {
874  hour -= 12;
875  } else if( hour==0 ) {
876  hour += 12;
877  }
878  }
879  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
880  "%d",
881  hour );
882  do_add = TRUE;
883  }
884  did_hour = TRUE;
885  } else if( (chCurrent=='h' || chCurrent=='H') && count==2 ) {
886  i += count-1;
887  if( !do_output ) {
888  pInfo->length_min += 2;
889  } else {
890  int hour = fb_Hour( value );
891  if( pInfo->has_ampm && chCurrent=='h' ) {
892  if( hour > 12 ) {
893  hour -= 12;
894  } else if( hour==0 ) {
895  hour += 12;
896  }
897  }
898  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
899  "%02d",
900  hour );
901  do_add = TRUE;
902  }
903  did_hour = TRUE;
904  } else if( chCurrent=='s' && count==1 ) {
905  i += count-1;
906  if( !do_output ) {
907  ++pInfo->length_min;
908  ++pInfo->length_opt;
909  } else {
910  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
911  "%d",
912  fb_Second( value ) );
913  do_add = TRUE;
914  }
915  } else if( chCurrent=='s' && count==2 ) {
916  i += count-1;
917  if( !do_output ) {
918  pInfo->length_min += 2;
919  } else {
920  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
921  "%02d",
922  fb_Second( value ) );
923  do_add = TRUE;
924  }
925  } else if( chCurrent=='M' && count==1 ) {
926  i += count-1;
927  if( !do_output ) {
928  ++pInfo->length_min;
929  ++pInfo->length_opt;
930  } else {
931  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
932  "%d",
933  fb_Month( value ) );
934  do_add = TRUE;
935  }
936  } else if( chCurrent=='M' && count==2 ) {
937  i += count-1;
938  if( !do_output ) {
939  pInfo->length_min += 2;
940  } else {
941  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
942  "%02d",
943  fb_Month( value ) );
944  do_add = TRUE;
945  }
946  } else if( chCurrent=='M' && (count==3 || count==4) ) {
947  int month = fb_Month( value );
948  FBSTRING *tmp = fb_MonthName( month, (count==3) );
949  i += count-1;
950  if( !do_output ) {
951  pInfo->length_min += FB_STRSIZE( tmp );
952  } else {
953  pszAddFree = strdup( tmp->data );
954  LenAdd = 0;
955  do_add = TRUE;
956  }
957  fb_hStrDelTemp( tmp );
958  } else if( chCurrent=='y' && count<3 ) {
959  i += count-1;
960  if( !do_output ) {
961  pInfo->length_min += 2;
962  } else {
963  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
964  "%02d",
965  fb_Year( value ) % 100);
966  do_add = TRUE;
967  }
968  } else if( chCurrent=='y' && count==4 ) {
969  i += count-1;
970  if( !do_output ) {
971  pInfo->length_min += 4;
972  } else {
973  LenAdd = sprintf( ((pszAdd = FixPart), FixPart),
974  "%04d",
975  fb_Year( value ));
976  do_add = TRUE;
977  }
978  } else {
979  if( !do_output ) {
980  ++pInfo->length_min;
981  } else {
982  do_add = TRUE;
983  }
984  }
985  } else {
986  if( !do_output ) {
987  ++pInfo->length_min;
988  } else {
989  do_add = TRUE;
990  }
991  }
992  break;
993  case '/':
994  /* date divider */
995  if( !do_output ) {
996  ++pInfo->length_min;
997  } else {
998  if( pInfo->mask_type==eMT_DateTime ) {
999  pszAdd = &chDateSep;
1000  }
1001  do_add = TRUE;
1002  }
1003  break;
1004  case ':':
1005  /* time divider */
1006  if( !do_output ) {
1007  ++pInfo->length_min;
1008  } else {
1009  if( pInfo->mask_type==eMT_DateTime ) {
1010  pszAdd = &chTimeSep;
1011  }
1012  do_add = TRUE;
1013  }
1014  break;
1015  case 'a':
1016  case 'A':
1017  /* AM/PM or A/P (in any combination of cases) */
1018  if( pInfo->mask_type==eMT_DateTime ) {
1019  if( (strncasecmp( mask+i, "AM/PM", 5 )==0)
1020  || (strncasecmp( mask+i, "A/P", 3 )==0) )
1021  {
1022  if( !do_output ) {
1023  if( pInfo->mask_type==eMT_Unknown ) {
1024  pInfo->mask_type = eMT_DateTime;
1025  } else if( pInfo->mask_type!=eMT_DateTime ) {
1027  return FALSE;
1028  }
1029  pInfo->has_ampm = TRUE;
1030  } else {
1031  int ampm_small = mask[i+1]=='/';
1032  LenAdd = ( ampm_small ? 1 : 2 );
1033  if( fb_Hour( value ) >= 12 ) {
1034  pszAdd = mask + i + LenAdd + 1;
1035  } else {
1036  pszAdd = mask + i;
1037  }
1038  do_add = TRUE;
1039  }
1040  i += ((mask[i+1]=='/') ? 2 : 4);
1041  } else {
1042  if( !do_output ) {
1043  ++pInfo->length_min;
1044  } else {
1045  do_add = TRUE;
1046  }
1047  }
1048  } else {
1049  if( !do_output ) {
1050  ++pInfo->length_min;
1051  } else {
1052  do_add = TRUE;
1053  }
1054  }
1055  break;
1056  case '"':
1057  do_string = TRUE;
1058  break;
1059  default:
1060  if( !do_output ) {
1061  ++pInfo->length_min;
1062  } else {
1063  do_add = TRUE;
1064  }
1065  break;
1066  }
1067  }
1068  }
1069  if( last_was_comma && (chCurrent!=',' || i==(mask_length-1)) ) {
1070  if( !do_output && !was_k_div ) {
1071  pInfo->has_thousand_sep = TRUE;
1072  }
1073  last_was_comma = FALSE;
1074  was_k_div = FALSE;
1075  }
1076  if( do_add ) {
1077  do_add = FALSE;
1078  DBG_ASSERT(do_output);
1079  DBG_ASSERT(pszOut!=NULL);
1080  if( pszAddFree!=NULL )
1081  pszAdd = pszAddFree;
1082  if( LenAdd==0 )
1083  LenAdd = strlen( pszAdd );
1084  DBG_ASSERT(LenOut>=LenAdd);
1085  FB_MEMCPY( pszOut, pszAdd, LenAdd );
1086  pszOut += LenAdd;
1087  LenOut -= LenAdd;
1088  if( pszAddFree!=NULL ) {
1089  free( pszAddFree );
1090  }
1091  }
1092  }
1093 
1094  if( !do_output ) {
1095  if( !pInfo->has_decimal_point ) {
1096  if( pInfo->num_digits_omit!=0 ) {
1097  pInfo->num_digits_omit += 3;
1098  }
1099  }
1100  if( pInfo->has_thousand_sep ) {
1101  pInfo->length_min += (pInfo->num_digits_fix - 1) / 3;
1102  }
1103  if( LenFix > pInfo->num_digits_fix )
1104  pInfo->length_min += LenFix - pInfo->num_digits_fix;
1105  if( pInfo->exp_digits < 5 )
1106  pInfo->length_opt += 5 - pInfo->exp_digits;
1107  if( !pInfo->has_sign )
1108  pInfo->length_min += 1;
1109  } else {
1110  DBG_ASSERT( LenOut>=0 );
1111  *pszOut = 0;
1112  fb_hStrSetLength( dst, pszOut - dst->data );
1113  }
1114 
1115  return TRUE;
1116 }
1117 
1119  (
1120  double value,
1121  const char *mask,
1122  size_t mask_length
1123  )
1124 {
1125  FBSTRING *dst = &__fb_ctx.null_desc;
1126  const char *pszIntlResult;
1127  char chDecimalPoint, chThousandsSep, chDateSep, chTimeSep;
1128 
1130 
1131  FB_LOCK();
1132  pszIntlResult = fb_IntlGet( eFIL_NumDecimalPoint, FALSE );
1133  chDecimalPoint = (( pszIntlResult==NULL ) ? '.' : *pszIntlResult );
1134  pszIntlResult = fb_IntlGet( eFIL_NumThousandsSeparator, FALSE );
1135  chThousandsSep = (( pszIntlResult==NULL ) ? ',' : *pszIntlResult );
1136  pszIntlResult = fb_IntlGet( eFIL_DateDivider, FALSE );
1137  chDateSep = (( pszIntlResult==NULL ) ? '/' : *pszIntlResult );
1138  pszIntlResult = fb_IntlGet( eFIL_TimeDivider, FALSE );
1139  chTimeSep = (( pszIntlResult==NULL ) ? ':' : *pszIntlResult );
1140  FB_UNLOCK();
1141 
1142  if( chDecimalPoint==0 )
1143  chDecimalPoint = '.';
1144  if( chThousandsSep==0 )
1145  chThousandsSep = ',';
1146 
1147  FB_STRLOCK();
1148 
1149  if( mask == NULL || mask_length==0 )
1150  {
1151  dst = fb_hBuildDouble( value, chDecimalPoint, 0 );
1152  }
1153  else
1154  {
1155  FormatMaskInfo info;
1156 
1157  /* Extract all information from the mask string */
1158  if( fb_hProcessMask( NULL,
1159  mask, mask_length,
1160  value, &info,
1161  chThousandsSep, chDecimalPoint,
1162  chDateSep, chTimeSep ) )
1163  {
1164  dst = fb_hStrAllocTemp_NoLock( NULL, info.length_min + info.length_opt );
1165  if( dst == NULL )
1166  {
1168  dst = &__fb_ctx.null_desc;
1169  }
1170  else
1171  {
1172  /* Build the new string according to the mask */
1173  fb_hProcessMask( dst,
1174  mask, mask_length,
1175  value, &info,
1176  chThousandsSep, chDecimalPoint,
1177  chDateSep, chTimeSep );
1178  }
1179  }
1180  }
1181 
1182  FB_STRUNLOCK();
1183 
1184  return dst;
1185 }
1186 
1188  (
1189  double value,
1190  FBSTRING *mask
1191  )
1192 {
1193  FBSTRING *dst;
1194 
1195  dst = fb_hStrFormat( value, mask->data, FB_STRSIZE(mask) );
1196 
1197  /* del if temp */
1198  fb_hStrDelTemp( mask );
1199 
1200  return dst;
1201 }