OSDN Git Service

Clean up #include's.
[pg-rex/syncrep.git] / src / backend / utils / adt / formatting.c
1 /* -----------------------------------------------------------------------
2  * formatting.c
3  *
4  * $Header: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.14 2000/06/15 03:32:28 momjian Exp $
5  *
6  *
7  *       Portions Copyright (c) 1999-2000, PostgreSQL, Inc
8  *
9  *
10  *       TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
11  *
12  *       The PostgreSQL routines for a timestamp/int/float/numeric formatting,
13  *       inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
14  *
15  *
16  *       Cache & Memory:
17  *      Routines use (itself) internal cache for format pictures.
18  *
19  *      The cache uses a static buffers and is persistent across transactions.
20  *      If format-picture is bigger than cache buffer, parser is called always.
21  *
22  *       NOTE for Number version:
23  *      All in this version is implemented as keywords ( => not used
24  *      suffixes), because a format picture is for *one* item (number)
25  *      only. It not is as a timestamp version, where each keyword (can)
26  *      has suffix.
27  *
28  *       NOTE for Timestamp routines:
29  *      In this module the POSIX 'struct tm' type is *not* used, but rather
30  *      PgSQL type, which has tm_mon based on one (*non* zero) and
31  *      year *not* based on 1900, but is used full year number.
32  *      Module supports AD / BC / AM / PM.
33  *
34  *      Supported types for to_char():
35  *
36  *              Timestamp, Numeric, int4, int8, float4, float8
37  *
38  *      Supported types for reverse conversion:
39  *
40  *              Timestamp       - to_timestamp()
41  *              Date            - to_date()
42  *              Numeric         - to_number()
43  *
44  *
45  *      Karel Zak - Zakkr
46  *
47  * -----------------------------------------------------------------------
48  */
49
50 /* ----------
51  * UnComment me for DEBUG
52  * ----------
53  */
54 /***
55 #define DEBUG_TO_FROM_CHAR
56 #define DEBUG_elog_output       NOTICE
57 ***/
58
59 #include <stdio.h>
60 #include <string.h>
61 #include <ctype.h>
62 #include <sys/time.h>
63 #include <unistd.h>
64 #include <locale.h>
65 #include <math.h>
66 #include <float.h>
67
68 #include "postgres.h"
69 #include "utils/builtins.h"
70 #include "utils/date.h"
71 #include "utils/datetime.h"
72 #include "utils/formatting.h"
73 #include "utils/int8.h"
74 #include "utils/pg_locale.h"
75
76 /* ----------
77  * Routines type
78  * ----------
79  */
80 #define DCH_TYPE                1               /* DATE-TIME version    */
81 #define NUM_TYPE                2               /* NUMBER version       */
82
83 /* ----------
84  * KeyWord Index (ascii from position 32 (' ') to 126 (~))
85  * ----------
86  */
87 #define KeyWord_INDEX_SIZE              ('~' - ' ')
88 #define KeyWord_INDEX_FILTER(_c)        ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
89
90 /* ----------
91  * Maximal length of one node
92  * ----------
93  */
94 #define DCH_MAX_ITEM_SIZ                9               /* max julian day               */
95 #define NUM_MAX_ITEM_SIZ                8               /* roman number (RN has 15 chars)       */
96
97 /* ----------
98  * More is in float.c
99  * ----------
100  */
101 #define MAXFLOATWIDTH   64
102 #define MAXDOUBLEWIDTH  128
103
104 /* ----------
105  * External (defined in PgSQL dt.c (timestamp utils))
106  * ----------
107  */
108 extern char *months[],                  /* month abbreviation   */
109                    *days[];                             /* full days            */
110
111 /* ----------
112  * Format parser structs
113  * ----------
114  */
115 typedef struct
116 {
117         char       *name;                       /* suffix string                */
118         int                     len,                    /* suffix length                */
119                                 id,                             /* used in node->suffix */
120                                 type;                   /* prefix / postfix                     */
121 } KeySuffix;
122
123 typedef struct
124 {
125         char       *name;                       /* keyword                      */
126         /* action for keyword           */
127         int                     len,                    /* keyword length               */
128                                 (*action) (),
129                                 id;                             /* keyword id                   */
130 } KeyWord;
131
132 typedef struct
133 {
134         int                     type;                   /* node type                    */
135         KeyWord    *key;                        /* if node type is KEYWORD      */
136         int                     character,              /* if node type is CHAR         */
137                                 suffix;                 /* keyword suffix               */
138 } FormatNode;
139
140 #define NODE_TYPE_END           1
141 #define NODE_TYPE_ACTION        2
142 #define NODE_TYPE_CHAR          3
143
144 #define SUFFTYPE_PREFIX         1
145 #define SUFFTYPE_POSTFIX        2
146
147
148 /* ----------
149  * Full months
150  * ----------
151  */
152 static char *months_full[] = {
153         "January", "February", "March", "April", "May", "June", "July",
154         "August", "September", "October", "November", "December", NULL
155 };
156
157 /* ----------
158  * AC / DC
159  * ----------
160  */
161 #define YEAR_ABS(_y)    (_y < 0 ? -(_y -1) : _y)
162 #define BC_STR_ORIG " BC"
163
164 #define A_D_STR         "A.D."
165 #define a_d_STR         "a.d."
166 #define AD_STR          "AD"
167 #define ad_STR          "ad"
168
169 #define B_C_STR         "B.C."
170 #define b_c_STR         "b.c."
171 #define BC_STR          "BC"
172 #define bc_STR          "bc"
173
174
175 /* ----------
176  * AM / PM
177  * ----------
178  */
179 #define A_M_STR         "A.M."
180 #define a_m_STR         "a.m."
181 #define AM_STR          "AM"
182 #define am_STR          "am"
183
184 #define P_M_STR         "P.M."
185 #define p_m_STR         "p.m."
186 #define PM_STR          "PM"
187 #define pm_STR          "pm"
188
189
190 /* ----------
191  * Months in roman-numeral
192  * (Must be conversely for seq_search (in FROM_CHAR), because
193  *      'VIII' must be over 'V')
194  * ----------
195  */
196 static char *rm_months_upper[] =
197 {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
198
199 static char *rm_months_lower[] =
200 {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
201
202 /* ----------
203  * Roman numbers
204  * ----------
205  */
206 static char *rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
207 static char *rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
208 static char *rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
209
210 /* ----------
211  * Ordinal postfixes
212  * ----------
213  */
214 static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
215 static char *numth[] = {"st", "nd", "rd", "th", NULL};
216
217 /* ----------
218  * Flags & Options:
219  * ----------
220  */
221 #define TO_CHAR         1
222 #define FROM_CHAR       2
223
224 #define ONE_UPPER       1                       /* Name */
225 #define ALL_UPPER       2                       /* NAME */
226 #define ALL_LOWER       3                       /* name */
227
228 #define FULL_SIZ        0
229
230 #define MAX_MON_LEN 3
231 #define MAX_DY_LEN      3
232
233 #define TH_UPPER        1
234 #define TH_LOWER        2
235
236
237 #ifdef DEBUG_TO_FROM_CHAR
238 #define NOTICE_TM {\
239                 elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
240                         tm->tm_sec, tm->tm_year,\
241                         tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\
242                         tm->tm_mday, tm->tm_isdst,tm->tm_mon);\
243         }
244 #endif
245
246 /* ----------
247  * Flags for DCH version
248  * ----------
249  */
250 static int      DCH_global_flag = 0;
251
252 #define DCH_F_FX        0x01
253
254 #define IS_FX           (DCH_global_flag & DCH_F_FX)
255
256
257 /* ----------
258  * Number description struct
259  * ----------
260  */
261 typedef struct
262 {
263         int                     pre,                    /* (count) numbers before decimal */
264                                 post,                   /* (count) numbers after decimal  */
265                                 lsign,                  /* want locales sign              */
266                                 flag,                   /* number parametrs               */
267                                 pre_lsign_num,  /* tmp value for lsign            */
268                                 multi,                  /* multiplier for 'V'             */
269                                 zero_start,             /* position of first zero         */
270                                 zero_end,               /* position of last zero          */
271                                 need_locale;    /* needs it locale                */
272 } NUMDesc;
273
274 /* ----------
275  * Flags for NUMBER version
276  * ----------
277  */
278 #define NUM_F_DECIMAL   0x01
279 #define NUM_F_LDECIMAL  0x02
280 #define NUM_F_ZERO      0x04
281 #define NUM_F_BLANK 0x08
282 #define NUM_F_FILLMODE  0x10
283 #define NUM_F_LSIGN 0x20
284 #define NUM_F_BRACKET   0x40
285 #define NUM_F_MINUS 0x80
286 #define NUM_F_PLUS      0x100
287 #define NUM_F_ROMAN 0x200
288 #define NUM_F_MULTI 0x400
289
290 #define NUM_LSIGN_PRE   -1
291 #define NUM_LSIGN_POST  1
292 #define NUM_LSIGN_NONE  0
293
294 /* ----------
295  * Tests
296  * ----------
297  */
298 #define IS_DECIMAL(_f)  ((_f)->flag & NUM_F_DECIMAL)
299 #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
300 #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
301 #define IS_BLANK(_f)    ((_f)->flag & NUM_F_BLANK)
302 #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
303 #define IS_BRACKET(_f)  ((_f)->flag & NUM_F_BRACKET)
304 #define IS_MINUS(_f)    ((_f)->flag & NUM_F_MINUS)
305 #define IS_LSIGN(_f)    ((_f)->flag & NUM_F_LSIGN)
306 #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
307 #define IS_ROMAN(_f)    ((_f)->flag & NUM_F_ROMAN)
308 #define IS_MULTI(_f)    ((_f)->flag & NUM_F_MULTI)
309
310 /* ----------
311  * Format picture cache
312  *              (cache size:
313  *              Number part             = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
314  *              Date-time part  = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
315  *      )
316  * ----------
317  */
318 #define NUM_CACHE_SIZE          64
319 #define NUM_CACHE_FIELDS        16
320 #define DCH_CACHE_SIZE          128
321 #define DCH_CACHE_FIELDS        16
322
323 typedef struct
324 {
325         FormatNode      format[DCH_CACHE_SIZE + 1];
326         char            str[DCH_CACHE_SIZE + 1];
327         int                     age;
328 } DCHCacheEntry;
329
330 typedef struct
331 {
332         FormatNode      format[NUM_CACHE_SIZE + 1];
333         char            str[NUM_CACHE_SIZE + 1];
334         int                     age;
335         NUMDesc         Num;
336 } NUMCacheEntry;
337
338 static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1];    /* global cache for
339                                                                                                                  * date/time part */
340 static int      n_DCHCache = 0;         /* number of entries */
341 static int      DCHCounter = 0;
342
343 static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];    /* global cache for
344                                                                                                                  * number part */
345 static int      n_NUMCache = 0;         /* number of entries */
346 static int      NUMCounter = 0;
347
348 #define MAX_INT32       (2147483640)
349
350 /* ----------
351  * Private global-modul definitions
352  * ----------
353  */
354 static struct tm _tm,
355                    *tm = &_tm;
356
357 /* ----------
358  * Utils
359  * ----------
360  */
361 #ifndef MIN
362 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
363 #endif
364 #ifndef MAX
365 #define MAX(a,b) (((a)>(b)) ? (a) : (b))
366 #endif
367
368 /*****************************************************************************
369  *                      KeyWords definition & action
370  *****************************************************************************/
371
372 static int      dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
373 static int      dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
374 static int      dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
375
376 /* ----------
377  * Suffixes:
378  * ----------
379  */
380 #define DCH_S_FM        0x01
381 #define DCH_S_TH        0x02
382 #define DCH_S_th        0x04
383 #define DCH_S_SP        0x08
384
385 /* ----------
386  * Suffix tests
387  * ----------
388  */
389 #define S_THth(_s)      (((_s & DCH_S_TH) || (_s & DCH_S_th)) ? 1 : 0)
390 #define S_TH(_s)        ((_s & DCH_S_TH) ? 1 : 0)
391 #define S_th(_s)        ((_s & DCH_S_th) ? 1 : 0)
392 #define S_TH_TYPE(_s)   ((_s & DCH_S_TH) ? TH_UPPER : TH_LOWER)
393
394 #define S_FM(_s)        ((_s & DCH_S_FM) ? 1 : 0)
395 #define S_SP(_s)        ((_s & DCH_S_SP) ? 1 : 0)
396
397 /* ----------
398  * Suffixes definition for DATE-TIME TO/FROM CHAR
399  * ----------
400  */
401 static KeySuffix DCH_suff[] = {
402         {"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
403         {"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
404         {"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
405         {"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
406         {"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
407         /* last */
408         {NULL, 0, 0, 0}
409 };
410
411 /* ----------
412  * Format-pictures (KeyWord).
413  *
414  * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
415  *                complicated -to-> easy:
416  *
417  *      (example: "DDD","DD","Day","D" )
418  *
419  * (this specific sort needs the algorithm for sequential search for strings,
420  * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
421  * or "HH12"? You must first try "HH12", because "HH" is in string, but
422  * it is not good.
423  *
424  * (!)
425  *        Position for the keyword is simular as position in the enum DCH/NUM_poz
426  * (!)
427  *
428  * For fast search is used the 'int index[]', index is ascii table from position
429  * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
430  * position or -1 if char is not used in the KeyWord. Search example for
431  * string "MM":
432  *      1)      see in index to index['M' - 32],
433  *      2)      take keywords position (enum DCH_MM) from index
434  *      3)      run sequential search in keywords[] from this position
435  *
436  * ----------
437  */
438
439 typedef enum
440 {
441         DCH_A_D,
442         DCH_A_M,
443         DCH_AD,
444         DCH_AM,
445         DCH_B_C,
446         DCH_BC,
447         DCH_CC,
448         DCH_DAY,
449         DCH_DDD,
450         DCH_DD,
451         DCH_DY,
452         DCH_Day,
453         DCH_Dy,
454         DCH_D,
455         DCH_FX,                                         /* global suffix */
456         DCH_HH24,
457         DCH_HH12,
458         DCH_HH,
459         DCH_J,
460         DCH_MI,
461         DCH_MM,
462         DCH_MONTH,
463         DCH_MON,
464         DCH_Month,
465         DCH_Mon,
466         DCH_P_M,
467         DCH_PM,
468         DCH_Q,
469         DCH_RM,
470         DCH_SSSS,
471         DCH_SS,
472         DCH_WW,
473         DCH_W,
474         DCH_Y_YYY,
475         DCH_YYYY,
476         DCH_YYY,
477         DCH_YY,
478         DCH_Y,
479         DCH_a_d,
480         DCH_a_m,
481         DCH_ad,
482         DCH_am,
483         DCH_b_c,
484         DCH_bc,
485         DCH_cc,
486         DCH_day,
487         DCH_ddd,
488         DCH_dd,
489         DCH_dy,
490         DCH_d,
491         DCH_fx,
492         DCH_hh24,
493         DCH_hh12,
494         DCH_hh,
495         DCH_j,
496         DCH_mi,
497         DCH_mm,
498         DCH_month,
499         DCH_mon,
500         DCH_p_m,
501         DCH_pm,
502         DCH_q,
503         DCH_rm,
504         DCH_ssss,
505         DCH_ss,
506         DCH_ww,
507         DCH_w,
508         DCH_y_yyy,
509         DCH_yyyy,
510         DCH_yyy,
511         DCH_yy,
512         DCH_y,
513
514         /* last */
515         _DCH_last_
516 } DCH_poz;
517
518 typedef enum
519 {
520         NUM_COMMA,
521         NUM_DEC,
522         NUM_0,
523         NUM_9,
524         NUM_B,
525         NUM_C,
526         NUM_D,
527         NUM_E,
528         NUM_FM,
529         NUM_G,
530         NUM_L,
531         NUM_MI,
532         NUM_PL,
533         NUM_PR,
534         NUM_RN,
535         NUM_SG,
536         NUM_SP,
537         NUM_S,
538         NUM_TH,
539         NUM_V,
540         NUM_b,
541         NUM_c,
542         NUM_d,
543         NUM_e,
544         NUM_fm,
545         NUM_g,
546         NUM_l,
547         NUM_mi,
548         NUM_pl,
549         NUM_pr,
550         NUM_rn,
551         NUM_sg,
552         NUM_sp,
553         NUM_s,
554         NUM_th,
555         NUM_v,
556
557         /* last */
558         _NUM_last_
559 } NUM_poz;
560
561 /* ----------
562  * KeyWords for DATE-TIME version
563  * ----------
564  */
565 static KeyWord DCH_keywords[] = {
566 /*      keyword,        len, func.      type                             is in Index */
567         {"A.D.", 4, dch_date, DCH_A_D},         /* A */
568         {"A.M.", 4, dch_time, DCH_A_M},
569         {"AD", 2, dch_date, DCH_AD},
570         {"AM", 2, dch_time, DCH_AM},
571         {"B.C.", 4, dch_date, DCH_B_C},         /* B */
572         {"BC", 2, dch_date, DCH_BC},
573         {"CC", 2, dch_date, DCH_CC},/* C */
574         {"DAY", 3, dch_date, DCH_DAY},          /* D */
575         {"DDD", 3, dch_date, DCH_DDD},
576         {"DD", 2, dch_date, DCH_DD},
577         {"DY", 2, dch_date, DCH_DY},
578         {"Day", 3, dch_date, DCH_Day},
579         {"Dy", 2, dch_date, DCH_Dy},
580         {"D", 1, dch_date, DCH_D},
581         {"FX", 2, dch_global, DCH_FX},          /* F */
582         {"HH24", 4, dch_time, DCH_HH24},        /* H */
583         {"HH12", 4, dch_time, DCH_HH12},
584         {"HH", 2, dch_time, DCH_HH},
585         {"J", 1, dch_date, DCH_J},      /* J */
586         {"MI", 2, dch_time, DCH_MI},
587         {"MM", 2, dch_date, DCH_MM},
588         {"MONTH", 5, dch_date, DCH_MONTH},
589         {"MON", 3, dch_date, DCH_MON},
590         {"Month", 5, dch_date, DCH_Month},
591         {"Mon", 3, dch_date, DCH_Mon},
592         {"P.M.", 4, dch_time, DCH_P_M},         /* P */
593         {"PM", 2, dch_time, DCH_PM},
594         {"Q", 1, dch_date, DCH_Q},      /* Q */
595         {"RM", 2, dch_date, DCH_RM},/* R */
596         {"SSSS", 4, dch_time, DCH_SSSS},        /* S */
597         {"SS", 2, dch_time, DCH_SS},
598         {"WW", 2, dch_date, DCH_WW},/* W */
599         {"W", 1, dch_date, DCH_W},
600         {"Y,YYY", 5, dch_date, DCH_Y_YYY},      /* Y */
601         {"YYYY", 4, dch_date, DCH_YYYY},
602         {"YYY", 3, dch_date, DCH_YYY},
603         {"YY", 2, dch_date, DCH_YY},
604         {"Y", 1, dch_date, DCH_Y},
605         {"a.d.", 4, dch_date, DCH_a_d},         /* a */
606         {"a.m.", 4, dch_time, DCH_a_m},
607         {"ad", 2, dch_date, DCH_ad},
608         {"am", 2, dch_time, DCH_am},
609         {"b.c.", 4, dch_date, DCH_b_c},         /* b */
610         {"bc", 2, dch_date, DCH_bc},
611         {"cc", 2, dch_date, DCH_CC},/* c */
612         {"day", 3, dch_date, DCH_day},          /* d */
613         {"ddd", 3, dch_date, DCH_DDD},
614         {"dd", 2, dch_date, DCH_DD},
615         {"dy", 2, dch_date, DCH_dy},
616         {"d", 1, dch_date, DCH_D},
617         {"fx", 2, dch_global, DCH_FX},          /* f */
618         {"hh24", 4, dch_time, DCH_HH24},        /* h */
619         {"hh12", 4, dch_time, DCH_HH12},
620         {"hh", 2, dch_time, DCH_HH},
621         {"j", 1, dch_time, DCH_J},      /* j */
622         {"mi", 2, dch_time, DCH_MI},/* m */
623         {"mm", 2, dch_date, DCH_MM},
624         {"month", 5, dch_date, DCH_month},
625         {"mon", 3, dch_date, DCH_mon},
626         {"p.m.", 4, dch_time, DCH_p_m},         /* p */
627         {"pm", 2, dch_time, DCH_pm},
628         {"q", 1, dch_date, DCH_Q},      /* q */
629         {"rm", 2, dch_date, DCH_rm},/* r */
630         {"ssss", 4, dch_time, DCH_SSSS},        /* s */
631         {"ss", 2, dch_time, DCH_SS},
632         {"ww", 2, dch_date, DCH_WW},/* w */
633         {"w", 1, dch_date, DCH_W},
634         {"y,yyy", 5, dch_date, DCH_Y_YYY},      /* y */
635         {"yyyy", 4, dch_date, DCH_YYYY},
636         {"yyy", 3, dch_date, DCH_YYY},
637         {"yy", 2, dch_date, DCH_YY},
638         {"y", 1, dch_date, DCH_Y},
639 /* last */
640 {NULL, 0, NULL, 0}};
641
642 /* ----------
643  * KeyWords for NUMBER version
644  * ----------
645  */
646 static KeyWord NUM_keywords[] = {
647 /*      keyword,        len, func.      type                       is in Index */
648         {",", 1, NULL, NUM_COMMA},      /* , */
649         {".", 1, NULL, NUM_DEC},        /* . */
650         {"0", 1, NULL, NUM_0},          /* 0 */
651         {"9", 1, NULL, NUM_9},          /* 9 */
652         {"B", 1, NULL, NUM_B},          /* B */
653         {"C", 1, NULL, NUM_C},          /* C */
654         {"D", 1, NULL, NUM_D},          /* D */
655         {"E", 1, NULL, NUM_E},          /* E */
656         {"FM", 2, NULL, NUM_FM},        /* F */
657         {"G", 1, NULL, NUM_G},          /* G */
658         {"L", 1, NULL, NUM_L},          /* L */
659         {"MI", 2, NULL, NUM_MI},        /* M */
660         {"PL", 2, NULL, NUM_PL},        /* P */
661         {"PR", 2, NULL, NUM_PR},
662         {"RN", 2, NULL, NUM_RN},        /* R */
663         {"SG", 2, NULL, NUM_SG},        /* S */
664         {"SP", 2, NULL, NUM_SP},
665         {"S", 1, NULL, NUM_S},
666         {"TH", 2, NULL, NUM_TH},        /* T */
667         {"V", 1, NULL, NUM_V},          /* V */
668         {"b", 1, NULL, NUM_B},          /* b */
669         {"c", 1, NULL, NUM_C},          /* c */
670         {"d", 1, NULL, NUM_D},          /* d */
671         {"e", 1, NULL, NUM_E},          /* e */
672         {"fm", 2, NULL, NUM_FM},        /* f */
673         {"g", 1, NULL, NUM_G},          /* g */
674         {"l", 1, NULL, NUM_L},          /* l */
675         {"mi", 2, NULL, NUM_MI},        /* m */
676         {"pl", 2, NULL, NUM_PL},        /* p */
677         {"pr", 2, NULL, NUM_PR},
678         {"rn", 2, NULL, NUM_rn},        /* r */
679         {"sg", 2, NULL, NUM_SG},        /* s */
680         {"sp", 2, NULL, NUM_SP},
681         {"s", 1, NULL, NUM_S},
682         {"th", 2, NULL, NUM_th},        /* t */
683         {"v", 1, NULL, NUM_V},          /* v */
684
685 /* last */
686 {NULL, 0, NULL, 0}};
687
688
689 /* ----------
690  * KeyWords index for DATE-TIME version
691  * ----------
692  */
693 static int      DCH_index[KeyWord_INDEX_SIZE] = {
694 /*
695 0       1       2       3       4       5       6       7       8       9
696 */
697         /*---- first 0..31 chars are skiped ----*/
698
699         -1, -1, -1, -1, -1, -1, -1, -1,
700         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
701         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
702         -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
703         DCH_FX, -1, DCH_HH24, -1, DCH_J, -1, -1, DCH_MI, -1, -1,
704         DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, -1, -1, -1, DCH_WW, -1, DCH_Y_YYY,
705         -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
706         DCH_day, -1, DCH_fx, -1, DCH_hh24, -1, DCH_j, -1, -1, DCH_mi,
707         -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, -1, -1, -1, DCH_ww,
708         -1, DCH_y_yyy, -1, -1, -1, -1
709
710         /*---- chars over 126 are skiped ----*/
711 };
712
713 /* ----------
714  * KeyWords index for NUMBER version
715  * ----------
716  */
717 static int      NUM_index[KeyWord_INDEX_SIZE] = {
718 /*
719 0       1       2       3       4       5       6       7       8       9
720 */
721         /*---- first 0..31 chars are skiped ----*/
722
723         -1, -1, -1, -1, -1, -1, -1, -1,
724         -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
725         -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
726         -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
727         NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
728         NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
729         -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
730         NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
731         -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
732         -1, -1, -1, -1, -1, -1
733
734         /*---- chars over 126 are skiped ----*/
735 };
736
737 /* ----------
738  * Number processor struct
739  * ----------
740  */
741 typedef struct NUMProc
742 {
743         int                     type;                   /* FROM_CHAR (TO_NUMBER) or TO_CHAR */
744
745         NUMDesc    *Num;                        /* number description           */
746
747         int                     sign,                   /* '-' or '+'                   */
748                                 sign_wrote,             /* was sign write               */
749                                 sign_pos,               /* pre number sign position */
750                                 num_count,              /* number of write digits       */
751                                 num_in,                 /* is inside number             */
752                                 num_curr,               /* current position in number   */
753                                 num_pre,                /* space before first number    */
754
755                                 read_dec,               /* to_number - was read dec. point      */
756                                 read_post;              /* to_number - number of dec. digit */
757
758         char       *number,                     /* string with number   */
759                            *number_p,           /* pointer to current number pozition */
760                            *inout,                      /* in / out buffer      */
761                            *inout_p,            /* pointer to current inout pozition */
762                            *last_relevant,      /* last relevant number after decimal
763                                                                  * point */
764
765                            *L_negative_sign,/* Locale */
766                            *L_positive_sign,
767                            *decimal,
768                            *L_thousands_sep,
769                            *L_currency_symbol;
770 } NUMProc;
771
772
773 /* ----------
774  * Functions
775  * ----------
776  */
777 static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index);
778 static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
779 static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
780 static void parse_format(FormatNode *node, char *str, KeyWord *kw,
781                          KeySuffix *suf, int *index, int ver, NUMDesc *Num);
782 static char *DCH_processor(FormatNode *node, char *inout, int flag);
783
784 #ifdef DEBUG_TO_FROM_CHAR
785 static void dump_index(KeyWord *k, int *index);
786 static void dump_node(FormatNode *node, int max);
787
788 #endif
789
790 static char *get_th(char *num, int type);
791 static char *str_numth(char *dest, char *num, int type);
792 static int      int4len(int4 num);
793 static char *str_toupper(char *buff);
794 static char *str_tolower(char *buff);
795
796 /* static int is_acdc(char *str, int *len); */
797 static int      seq_search(char *name, char **array, int type, int max, int *len);
798 static int      dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
799 static int      dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
800 static int      dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
801 static char *fill_str(char *str, int c, int max);
802 static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag);
803 static char *int_to_roman(int number);
804 static void NUM_prepare_locale(NUMProc *Np);
805 static char *get_last_relevant_decnum(char *num);
806 static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
807 static void NUM_numpart_to_char(NUMProc *Np, int id);
808 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
809                           int plen, int sign, int type);
810 static DCHCacheEntry *DCH_cache_search(char *str);
811 static DCHCacheEntry *DCH_cache_getnew(char *str);
812 static NUMCacheEntry *NUM_cache_search(char *str);
813 static NUMCacheEntry *NUM_cache_getnew(char *str);
814
815
816 /* ----------
817  * Fast sequential search, use index for data selection which
818  * go to seq. cycle (it is very fast for non-wanted strings)
819  * (can't be used binary search in format parsing)
820  * ----------
821  */
822 static KeyWord *
823 index_seq_search(char *str, KeyWord *kw, int *index)
824 {
825         int                     poz;
826
827         if (!KeyWord_INDEX_FILTER(*str))
828                 return (KeyWord *) NULL;
829
830         if ((poz = *(index + (*str - ' '))) > -1)
831         {
832
833                 KeyWord    *k = kw + poz;
834
835                 do
836                 {
837                         if (!strncmp(str, k->name, k->len))
838                                 return k;
839                         k++;
840                         if (!k->name)
841                                 return (KeyWord *) NULL;
842                 } while (*str == *k->name);
843         }
844         return (KeyWord *) NULL;
845 }
846
847 static KeySuffix *
848 suff_search(char *str, KeySuffix *suf, int type)
849 {
850         KeySuffix  *s;
851
852         for (s = suf; s->name != NULL; s++)
853         {
854                 if (s->type != type)
855                         continue;
856
857                 if (!strncmp(str, s->name, s->len))
858                         return s;
859         }
860         return (KeySuffix *) NULL;
861 }
862
863 /* ----------
864  * Prepare NUMDesc (number description struct) via FormatNode struct
865  * ----------
866  */
867 static void
868 NUMDesc_prepare(NUMDesc *num, FormatNode *n)
869 {
870
871         if (n->type != NODE_TYPE_ACTION)
872                 return;
873
874         switch (n->key->id)
875         {
876
877                 case NUM_9:
878                         if (IS_BRACKET(num))
879                                 elog(ERROR, "to_char/to_number(): '9' must be ahead of 'PR'.");
880
881                         if (IS_MULTI(num))
882                         {
883                                 ++num->multi;
884                                 break;
885                         }
886                         if (IS_DECIMAL(num))
887                                 ++num->post;
888                         else
889                                 ++num->pre;
890                         break;
891
892                 case NUM_0:
893                         if (IS_BRACKET(num))
894                                 elog(ERROR, "to_char/to_number(): '0' must be ahead of 'PR'.");
895
896                         if (!IS_ZERO(num) && !IS_DECIMAL(num))
897                         {
898                                 num->flag |= NUM_F_ZERO;
899                                 num->zero_start = num->pre + 1;
900                         }
901                         if (!IS_DECIMAL(num))
902                                 ++num->pre;
903                         else
904                                 ++num->post;
905
906                         num->zero_end = num->pre + num->post;
907                         break;
908
909                 case NUM_B:
910                         if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
911                                 num->flag |= NUM_F_BLANK;
912                         break;
913
914                 case NUM_D:
915                         num->flag |= NUM_F_LDECIMAL;
916                         num->need_locale = TRUE;
917                 case NUM_DEC:
918                         if (IS_DECIMAL(num))
919                                 elog(ERROR, "to_char/to_number(): not unique decimal poit.");
920                         if (IS_MULTI(num))
921                                 elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
922                         num->flag |= NUM_F_DECIMAL;
923                         break;
924
925                 case NUM_FM:
926                         num->flag |= NUM_F_FILLMODE;
927                         break;
928
929                 case NUM_S:
930                         if (IS_LSIGN(num))
931                                 elog(ERROR, "to_char/to_number(): not unique 'S'.");
932
933                         if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
934                                 elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL'/'MI'/'SG'/'PR' together.");
935
936                         if (!IS_DECIMAL(num))
937                         {
938                                 num->lsign = NUM_LSIGN_PRE;
939                                 num->pre_lsign_num = num->pre;
940                                 num->need_locale = TRUE;
941                                 num->flag |= NUM_F_LSIGN;
942
943                         }
944                         else if (num->lsign == NUM_LSIGN_NONE)
945                         {
946                                 num->lsign = NUM_LSIGN_POST;
947                                 num->need_locale = TRUE;
948                                 num->flag |= NUM_F_LSIGN;
949                         }
950                         break;
951
952                 case NUM_MI:
953                         if (IS_LSIGN(num))
954                                 elog(ERROR, "to_char/to_number(): can't use 'S' and 'MI' together.");
955
956                         num->flag |= NUM_F_MINUS;
957                         break;
958
959                 case NUM_PL:
960                         if (IS_LSIGN(num))
961                                 elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL' together.");
962
963                         num->flag |= NUM_F_PLUS;
964                         break;
965
966                 case NUM_SG:
967                         if (IS_LSIGN(num))
968                                 elog(ERROR, "to_char/to_number(): can't use 'S' and 'SG' together.");
969
970                         num->flag |= NUM_F_MINUS;
971                         num->flag |= NUM_F_PLUS;
972                         break;
973
974                 case NUM_PR:
975                         if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
976                                 elog(ERROR, "to_char/to_number(): can't use 'PR' and 'S'/'PL'/'MI'/'SG' together.");
977                         num->flag |= NUM_F_BRACKET;
978                         break;
979
980                 case NUM_rn:
981                 case NUM_RN:
982                         num->flag |= NUM_F_ROMAN;
983                         break;
984
985                 case NUM_L:
986                 case NUM_G:
987                         num->need_locale = TRUE;
988                         break;
989
990                 case NUM_V:
991                         if (IS_DECIMAL(num))
992                                 elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
993                         num->flag |= NUM_F_MULTI;
994                         break;
995
996                 case NUM_E:
997                         elog(ERROR, "to_char/to_number(): 'E' is not supported.");
998         }
999
1000         return;
1001 }
1002
1003 /* ----------
1004  * Format parser, search small keywords and keyword's suffixes, and make
1005  * format-node tree.
1006  *
1007  * for DATE-TIME & NUMBER version
1008  * ----------
1009  */
1010 static void
1011 parse_format(FormatNode *node, char *str, KeyWord *kw,
1012                          KeySuffix *suf, int *index, int ver, NUMDesc *Num)
1013 {
1014         KeySuffix  *s;
1015         FormatNode *n;
1016         int                     node_set = 0,
1017                                 suffix,
1018                                 last = 0;
1019
1020 #ifdef DEBUG_TO_FROM_CHAR
1021         elog(DEBUG_elog_output, "to_char/number(): run parser.");
1022 #endif
1023
1024         n = node;
1025
1026         while (*str)
1027         {
1028                 suffix = 0;
1029
1030                 /* ----------
1031                  * Prefix
1032                  * ----------
1033                  */
1034                 if (ver == DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
1035                 {
1036                         suffix |= s->id;
1037                         if (s->len)
1038                                 str += s->len;
1039                 }
1040
1041                 /* ----------
1042                  * Keyword
1043                  * ----------
1044                  */
1045                 if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
1046                 {
1047
1048                         n->type = NODE_TYPE_ACTION;
1049                         n->suffix = 0;
1050                         node_set = 1;
1051                         if (n->key->len)
1052                                 str += n->key->len;
1053
1054                         /* ----------
1055                          * NUM version: Prepare global NUMDesc struct
1056                          * ----------
1057                          */
1058                         if (ver == NUM_TYPE)
1059                                 NUMDesc_prepare(Num, n);
1060
1061                         /* ----------
1062                          * Postfix
1063                          * ----------
1064                          */
1065                         if (ver == DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
1066                         {
1067                                 suffix |= s->id;
1068                                 if (s->len)
1069                                         str += s->len;
1070                         }
1071
1072                 }
1073                 else if (*str)
1074                 {
1075
1076                         /* ----------
1077                          * Special characters '\' and '"'
1078                          * ----------
1079                          */
1080                         if (*str == '"' && last != '\\')
1081                         {
1082
1083                                 int                     x = 0;
1084
1085                                 while (*(++str))
1086                                 {
1087                                         if (*str == '"' && x != '\\')
1088                                         {
1089                                                 str++;
1090                                                 break;
1091                                         }
1092                                         else if (*str == '\\' && x != '\\')
1093                                         {
1094                                                 x = '\\';
1095                                                 continue;
1096                                         }
1097                                         n->type = NODE_TYPE_CHAR;
1098                                         n->character = *str;
1099                                         n->key = (KeyWord *) NULL;
1100                                         n->suffix = 0;
1101                                         ++n;
1102                                         x = *str;
1103                                 }
1104                                 node_set = 0;
1105                                 suffix = 0;
1106                                 last = 0;
1107
1108                         }
1109                         else if (*str && *str == '\\' && last != '\\' && *(str + 1) == '"')
1110                         {
1111                                 last = *str;
1112                                 str++;
1113
1114                         }
1115                         else if (*str)
1116                         {
1117                                 n->type = NODE_TYPE_CHAR;
1118                                 n->character = *str;
1119                                 n->key = (KeyWord *) NULL;
1120                                 node_set = 1;
1121                                 last = 0;
1122                                 str++;
1123                         }
1124
1125                 }
1126
1127                 /* end */
1128                 if (node_set)
1129                 {
1130                         if (n->type == NODE_TYPE_ACTION)
1131                                 n->suffix = suffix;
1132                         ++n;
1133
1134                         n->suffix = 0;
1135                         node_set = 0;
1136                 }
1137
1138         }
1139
1140         n->type = NODE_TYPE_END;
1141         n->suffix = 0;
1142         return;
1143 }
1144
1145 /* ----------
1146  * Call keyword's function for each of (action) node in format-node tree
1147  * ----------
1148  */
1149 static char *
1150 DCH_processor(FormatNode *node, char *inout, int flag)
1151 {
1152         FormatNode *n;
1153         char       *s;
1154
1155
1156         /* ----------
1157          * Zeroing global flags
1158          * ----------
1159          */
1160         DCH_global_flag = 0;
1161
1162         for (n = node, s = inout; n->type != NODE_TYPE_END; n++)
1163         {
1164                 if (n->type == NODE_TYPE_ACTION)
1165                 {
1166
1167                         int                     len;
1168
1169                         /* ----------
1170                          * Call node action function
1171                          * ----------
1172                          */
1173                         len = n->key->action(n->key->id, s, n->suffix, flag, n);
1174                         if (len > 0)
1175                                 s += len;
1176                         else if (len == -1)
1177                                 continue;
1178
1179                 }
1180                 else
1181                 {
1182
1183                         /* ----------
1184                          * Remove to output char from input in TO_CHAR
1185                          * ----------
1186                          */
1187                         if (flag == TO_CHAR)
1188                                 *s = n->character;
1189
1190                         else
1191                         {
1192                                 /* ----------
1193                                  * Skip blank space in FROM_CHAR's input
1194                                  * ----------
1195                                  */
1196                                 if (isspace(n->character) && IS_FX == 0)
1197                                 {
1198                                         while (*s != '\0' && isspace((int) *(s + 1)))
1199                                                 ++s;
1200                                 }
1201                         }
1202                 }
1203
1204                 ++s;                                    /* ! */
1205
1206         }
1207
1208         if (flag == TO_CHAR)
1209                 *s = '\0';
1210         return inout;
1211 }
1212
1213
1214 /* ----------
1215  * DEBUG: Dump the FormatNode Tree (debug)
1216  * ----------
1217  */
1218 #ifdef DEBUG_TO_FROM_CHAR
1219
1220 #define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
1221 #define DUMP_FM(_suf)   (S_FM(_suf) ? "FM" : " ")
1222
1223 static void
1224 dump_node(FormatNode *node, int max)
1225 {
1226         FormatNode *n;
1227         int                     a;
1228
1229         elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
1230
1231         for (a = 0, n = node; a <= max; n++, a++)
1232         {
1233                 if (n->type == NODE_TYPE_ACTION)
1234                         elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
1235                           a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
1236                 else if (n->type == NODE_TYPE_CHAR)
1237                         elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
1238                 else if (n->type == NODE_TYPE_END)
1239                 {
1240                         elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
1241                         return;
1242                 }
1243                 else
1244                         elog(DEBUG_elog_output, "%d:\t UnKnown NODE !!!", a);
1245
1246         }
1247 }
1248
1249 #endif
1250
1251 /*****************************************************************************
1252  *                      Private utils
1253  *****************************************************************************/
1254
1255 /* ----------
1256  * Return ST/ND/RD/TH for simple (1..9) numbers
1257  * type --> 0 upper, 1 lower
1258  * ----------
1259  */
1260 static char *
1261 get_th(char *num, int type)
1262 {
1263         int                     len = strlen(num),
1264                                 last, seclast;
1265
1266         last = *(num + (len - 1));
1267         if (!isdigit((unsigned char) last))
1268                 elog(ERROR, "get_th: '%s' is not number.", num);
1269
1270         /*
1271          * All "teens" (<x>1[0-9]) get 'TH/th',
1272          * while <x>[02-9][123] still get 'ST/st', 'ND/nd', 'RD/rd', respectively
1273          */
1274         if ((len > 1) && ((seclast = num[len-2]) == '1'))
1275                 last = 0;
1276
1277         switch (last)
1278         {
1279                 case '1':
1280                         if (type == TH_UPPER)
1281                                 return numTH[0];
1282                         return numth[0];
1283                 case '2':
1284                         if (type == TH_UPPER)
1285                                 return numTH[1];
1286                         return numth[1];
1287                 case '3':
1288                         if (type == TH_UPPER)
1289                                 return numTH[2];
1290                         return numth[2];
1291                 default:
1292                         if (type == TH_UPPER)
1293                                 return numTH[3];
1294                         return numth[3];
1295         }
1296         return NULL;
1297 }
1298
1299 /* ----------
1300  * Convert string-number to ordinal string-number
1301  * type --> 0 upper, 1 lower
1302  * ----------
1303  */
1304 static char *
1305 str_numth(char *dest, char *num, int type)
1306 {
1307         sprintf(dest, "%s%s", num, get_th(num, type));
1308         return dest;
1309 }
1310
1311 /* ----------
1312  * Return length of integer writed in string
1313  * ----------
1314  */
1315 static int
1316 int4len(int4 num)
1317 {
1318         char            b[16];
1319
1320         return sprintf(b, "%d", num);
1321 }
1322
1323 /* ----------
1324  * Convert string to upper-string
1325  * ----------
1326  */
1327 static char *
1328 str_toupper(char *buff)
1329 {
1330         char       *p_buff = buff;
1331
1332         while (*p_buff)
1333         {
1334                 *p_buff = toupper((unsigned char) *p_buff);
1335                 ++p_buff;
1336         }
1337         return buff;
1338 }
1339
1340 /* ----------
1341  * Convert string to lower-string
1342  * ----------
1343  */
1344 static char *
1345 str_tolower(char *buff)
1346 {
1347         char       *p_buff = buff;
1348
1349         while (*p_buff)
1350         {
1351                 *p_buff = tolower((unsigned char) *p_buff);
1352                 ++p_buff;
1353         }
1354         return buff;
1355 }
1356
1357 /* ----------
1358  * Check if in string is AC or BC (return: 0==none; -1==BC; 1==AC)
1359  * ----------
1360  */
1361 /************* not used - use AD/BC format pictures instead  **********
1362 static int
1363 is_acdc(char *str, int *len)
1364 {
1365         char    *p;
1366
1367         for(p=str; *p != '\0'; p++) {
1368                 if (isspace(*p))
1369                         continue;
1370
1371                 if (*(p+1)) {
1372                         if (toupper(*p)=='B' && toupper(*(++p))=='C') {
1373                                 *len += (p - str) +1;
1374                                 return -1;
1375                         } else if (toupper(*p)=='A' && toupper(*(++p))=='C') {
1376                                 *len += (p - str) +1;
1377                                 return 1;
1378                         }
1379                 }
1380                 return 0;
1381         }
1382         return 0;
1383 }
1384 ******************************/
1385
1386 /* ----------
1387  * Sequential search with to upper/lower conversion
1388  * ----------
1389  */
1390 static int
1391 seq_search(char *name, char **array, int type, int max, int *len)
1392 {
1393         char       *p,
1394                            *n,
1395                           **a;
1396         int                     last,
1397                                 i;
1398
1399         *len = 0;
1400
1401         if (!*name)
1402                 return -1;
1403
1404         /* set first char */
1405         if (type == ONE_UPPER || ALL_UPPER)
1406                 *name = toupper((unsigned char) *name);
1407         else if (type == ALL_LOWER)
1408                 *name = tolower((unsigned char) *name);
1409
1410         for (last = 0, a = array; *a != NULL; a++)
1411         {
1412
1413                 /* comperate first chars */
1414                 if (*name != **a)
1415                         continue;
1416
1417                 for (i = 1, p = *a + 1, n = name + 1;; n++, p++, i++)
1418                 {
1419
1420                         /* search fragment (max) only */
1421                         if (max && i == max)
1422                         {
1423                                 *len = i;
1424                                 return a - array;
1425                         }
1426                         /* full size */
1427                         if (*p == '\0')
1428                         {
1429                                 *len = i;
1430                                 return a - array;
1431                         }
1432                         /* Not found in array 'a' */
1433                         if (*n == '\0')
1434                                 break;
1435
1436                         /*
1437                          * Convert (but convert new chars only)
1438                          */
1439                         if (i > last)
1440                         {
1441                                 if (type == ONE_UPPER || type == ALL_LOWER)
1442                                         *n = tolower((unsigned char) *n);
1443                                 else if (type == ALL_UPPER)
1444                                         *n = toupper((unsigned char) *n);
1445                                 last = i;
1446                         }
1447
1448 #ifdef DEBUG_TO_FROM_CHAR
1449
1450                         /*
1451                          * elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)", *n, *p,
1452                          * *a, name);
1453                          */
1454 #endif
1455                         if (*n != *p)
1456                                 break;
1457                 }
1458         }
1459
1460         return -1;
1461 }
1462
1463
1464 #ifdef DEBUG_TO_FROM_CHAR
1465 /* -----------
1466  * DEBUG: Call for debug and for index checking; (Show ASCII char
1467  * and defined keyword for each used position
1468  * ----------
1469  */
1470 static void
1471 dump_index(KeyWord *k, int *index)
1472 {
1473         int                     i,
1474                                 count = 0,
1475                                 free_i = 0;
1476
1477         elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
1478
1479         for (i = 0; i < KeyWord_INDEX_SIZE; i++)
1480         {
1481                 if (index[i] != -1)
1482                 {
1483                         elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
1484                         count++;
1485                 }
1486                 else
1487                 {
1488                         free_i++;
1489                         elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
1490                 }
1491         }
1492         elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
1493                  count, free_i);
1494 }
1495
1496 #endif
1497
1498 /* ----------
1499  * Skip TM / th in FROM_CHAR
1500  * ----------
1501  */
1502 #define SKIP_THth(_suf)         (S_THth(_suf) ? 2 : 0)
1503
1504
1505 /* ----------
1506  * Global format option for DCH version
1507  * ----------
1508  */
1509 static int
1510 dch_global(int arg, char *inout, int suf, int flag, FormatNode *node)
1511 {
1512         switch (arg)
1513         {
1514
1515                         case DCH_FX:
1516                         DCH_global_flag |= DCH_F_FX;
1517                         break;
1518         }
1519         return -1;
1520 }
1521
1522 /* ----------
1523  * Master function of TIME for:
1524  *                                        TO_CHAR       - write (inout) formated string
1525  *                                        FROM_CHAR - scan (inout) string by course of FormatNode
1526  * ----------
1527  */
1528 static int
1529 dch_time(int arg, char *inout, int suf, int flag, FormatNode *node)
1530 {
1531         char       *p_inout = inout;
1532
1533         switch (arg)
1534         {
1535
1536                 case DCH_A_M:
1537                 case DCH_P_M:
1538                         if (flag == TO_CHAR)
1539                         {
1540                                 strcpy(inout, (tm->tm_hour > 13 ? P_M_STR : A_M_STR));
1541                                 return 3;
1542
1543                         }
1544                         else if (flag == FROM_CHAR)
1545                         {
1546                                 if (strncmp(inout, P_M_STR, 4) == 0 && tm->tm_hour < 13)
1547                                         tm->tm_hour += 12;
1548                                 return 3;
1549                         }
1550
1551                 case DCH_AM:
1552                 case DCH_PM:
1553                         if (flag == TO_CHAR)
1554                         {
1555                                 strcpy(inout, (tm->tm_hour > 13 ? PM_STR : AM_STR));
1556                                 return 1;
1557
1558                         }
1559                         else if (flag == FROM_CHAR)
1560                         {
1561                                 if (strncmp(inout, PM_STR, 2) == 0 && tm->tm_hour < 13)
1562                                         tm->tm_hour += 12;
1563                                 return 1;
1564                         }
1565
1566                 case DCH_a_m:
1567                 case DCH_p_m:
1568                         if (flag == TO_CHAR)
1569                         {
1570                                 strcpy(inout, (tm->tm_hour > 13 ? p_m_STR : a_m_STR));
1571                                 return 3;
1572
1573                         }
1574                         else if (flag == FROM_CHAR)
1575                         {
1576                                 if (strncmp(inout, p_m_STR, 4) == 0 && tm->tm_hour < 13)
1577                                         tm->tm_hour += 12;
1578                                 return 3;
1579                         }
1580
1581                 case DCH_am:
1582                 case DCH_pm:
1583                         if (flag == TO_CHAR)
1584                         {
1585                                 strcpy(inout, (tm->tm_hour > 13 ? pm_STR : am_STR));
1586                                 return 1;
1587
1588                         }
1589                         else if (flag == FROM_CHAR)
1590                         {
1591                                 if (strncmp(inout, pm_STR, 2) == 0 && tm->tm_hour < 13)
1592                                         tm->tm_hour += 12;
1593                                 return 1;
1594                         }
1595
1596                 case DCH_HH:
1597                 case DCH_HH12:
1598                         if (flag == TO_CHAR)
1599                         {
1600                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
1601                                                 tm->tm_hour == 0 ? 12 :
1602                                           tm->tm_hour < 13 ? tm->tm_hour : tm->tm_hour - 12);
1603                                 if (S_THth(suf))
1604                                         str_numth(p_inout, inout, 0);
1605                                 if (S_FM(suf) || S_THth(suf))
1606                                         return strlen(p_inout) - 1;
1607                                 else
1608                                         return 1;
1609
1610                         }
1611                         else if (flag == FROM_CHAR)
1612                         {
1613                                 if (S_FM(suf))
1614                                 {
1615                                         sscanf(inout, "%d", &tm->tm_hour);
1616                                         return int4len((int4) tm->tm_hour) - 1 + SKIP_THth(suf);
1617                                 }
1618                                 else
1619                                 {
1620                                         sscanf(inout, "%02d", &tm->tm_hour);
1621                                         return 1 + SKIP_THth(suf);
1622                                 }
1623                         }
1624
1625                 case DCH_HH24:
1626                         if (flag == TO_CHAR)
1627                         {
1628                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour);
1629                                 if (S_THth(suf))
1630                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1631                                 if (S_FM(suf) || S_THth(suf))
1632                                         return strlen(p_inout) - 1;
1633                                 else
1634                                         return 1;
1635
1636                         }
1637                         else if (flag == FROM_CHAR)
1638                         {
1639                                 if (S_FM(suf))
1640                                 {
1641                                         sscanf(inout, "%d", &tm->tm_hour);
1642                                         return int4len((int4) tm->tm_hour) - 1 + SKIP_THth(suf);
1643                                 }
1644                                 else
1645                                 {
1646                                         sscanf(inout, "%02d", &tm->tm_hour);
1647                                         return 1 + SKIP_THth(suf);
1648                                 }
1649                         }
1650
1651                 case DCH_MI:
1652                         if (flag == TO_CHAR)
1653                         {
1654                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min);
1655                                 if (S_THth(suf))
1656                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1657                                 if (S_FM(suf) || S_THth(suf))
1658                                         return strlen(p_inout) - 1;
1659                                 else
1660                                         return 1;
1661
1662                         }
1663                         else if (flag == FROM_CHAR)
1664                         {
1665                                 if (S_FM(suf))
1666                                 {
1667                                         sscanf(inout, "%d", &tm->tm_min);
1668                                         return int4len((int4) tm->tm_min) - 1 + SKIP_THth(suf);
1669                                 }
1670                                 else
1671                                 {
1672                                         sscanf(inout, "%02d", &tm->tm_min);
1673                                         return 1 + SKIP_THth(suf);
1674                                 }
1675                         }
1676                 case DCH_SS:
1677                         if (flag == TO_CHAR)
1678                         {
1679                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec);
1680                                 if (S_THth(suf))
1681                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1682                                 if (S_FM(suf) || S_THth(suf))
1683                                         return strlen(p_inout) - 1;
1684                                 else
1685                                         return 1;
1686
1687                         }
1688                         else if (flag == FROM_CHAR)
1689                         {
1690                                 if (S_FM(suf))
1691                                 {
1692                                         sscanf(inout, "%d", &tm->tm_sec);
1693                                         return int4len((int4) tm->tm_sec) - 1 + SKIP_THth(suf);
1694                                 }
1695                                 else
1696                                 {
1697                                         sscanf(inout, "%02d", &tm->tm_sec);
1698                                         return 1 + SKIP_THth(suf);
1699                                 }
1700                         }
1701                 case DCH_SSSS:
1702                         if (flag == TO_CHAR)
1703                         {
1704                                 sprintf(inout, "%d", tm->tm_hour * 3600 +
1705                                                 tm->tm_min * 60 +
1706                                                 tm->tm_sec);
1707                                 if (S_THth(suf))
1708                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1709                                 return strlen(p_inout) - 1;
1710                         }
1711                         else if (flag == FROM_CHAR)
1712                                 elog(ERROR, "to_timestamp(): SSSS is not supported");
1713         }
1714         return -1;
1715 }
1716
1717 #define CHECK_SEQ_SEARCH(_l, _s) {                                      \
1718         if (_l <= 0) {                                                  \
1719                 elog(ERROR, "to_timestamp(): bad value for %s", _s);    \
1720         }                                                               \
1721 }
1722
1723 /* ----------
1724  * Master of DATE for:
1725  *                                        TO_CHAR       - write (inout) formated string
1726  *                                        FROM_CHAR - scan (inout) string by course of FormatNode
1727  * ----------
1728  */
1729 static int
1730 dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
1731 {
1732         char            buff[DCH_CACHE_SIZE],
1733                            *p_inout;
1734         int                     i,
1735                                 len;
1736
1737         p_inout = inout;
1738
1739         /* ----------
1740          * In the FROM-char is not difference between "January" or "JANUARY"
1741          * or "january", all is before search convert to "first-upper".
1742          * This convention is used for MONTH, MON, DAY, DY
1743          * ----------
1744          */
1745         if (flag == FROM_CHAR)
1746         {
1747                 if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month)
1748                 {
1749
1750                         tm->tm_mon = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len);
1751                         CHECK_SEQ_SEARCH(len, "MONTH/Month/month");
1752                         ++tm->tm_mon;
1753                         if (S_FM(suf))
1754                                 return len - 1;
1755                         else
1756                                 return 8;
1757
1758                 }
1759                 else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon)
1760                 {
1761
1762                         tm->tm_mon = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len);
1763                         CHECK_SEQ_SEARCH(len, "MON/Mon/mon");
1764                         ++tm->tm_mon;
1765                         return 2;
1766
1767                 }
1768                 else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day)
1769                 {
1770
1771                         tm->tm_wday = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len);
1772                         CHECK_SEQ_SEARCH(len, "DAY/Day/day");
1773                         if (S_FM(suf))
1774                                 return len - 1;
1775                         else
1776                                 return 8;
1777
1778                 }
1779                 else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy)
1780                 {
1781
1782                         tm->tm_wday = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len);
1783                         CHECK_SEQ_SEARCH(len, "DY/Dy/dy");
1784                         return 2;
1785
1786                 }
1787         }
1788
1789         switch (arg)
1790         {
1791
1792                 case DCH_A_D:
1793                 case DCH_B_C:
1794                         if (flag == TO_CHAR)
1795                         {
1796                                 strcpy(inout, (tm->tm_year < 0 ? B_C_STR : A_D_STR));
1797                                 return 3;
1798
1799                         }
1800                         else if (flag == FROM_CHAR)
1801                         {
1802                                 if (strncmp(inout, B_C_STR, 4) == 0 && tm->tm_year > 0)
1803                                         tm->tm_year = -(tm->tm_year);
1804                                 if (tm->tm_year < 0)
1805                                         tm->tm_year = tm->tm_year + 1;
1806                                 return 3;
1807                         }
1808
1809                 case DCH_AD:
1810                 case DCH_BC:
1811                         if (flag == TO_CHAR)
1812                         {
1813                                 strcpy(inout, (tm->tm_year < 0 ? BC_STR : AD_STR));
1814                                 return 1;
1815
1816                         }
1817                         else if (flag == FROM_CHAR)
1818                         {
1819                                 if (strncmp(inout, BC_STR, 2) == 0 && tm->tm_year > 0)
1820                                         tm->tm_year = -(tm->tm_year);
1821                                 if (tm->tm_year < 0)
1822                                         tm->tm_year = tm->tm_year + 1;
1823                                 return 1;
1824                         }
1825
1826                 case DCH_a_d:
1827                 case DCH_b_c:
1828                         if (flag == TO_CHAR)
1829                         {
1830                                 strcpy(inout, (tm->tm_year < 0 ? b_c_STR : a_d_STR));
1831                                 return 3;
1832
1833                         }
1834                         else if (flag == FROM_CHAR)
1835                         {
1836                                 if (strncmp(inout, b_c_STR, 4) == 0 && tm->tm_year > 0)
1837                                         tm->tm_year = -(tm->tm_year);
1838                                 if (tm->tm_year < 0)
1839                                         tm->tm_year = tm->tm_year + 1;
1840                                 return 3;
1841                         }
1842
1843                 case DCH_ad:
1844                 case DCH_bc:
1845                         if (flag == TO_CHAR)
1846                         {
1847                                 strcpy(inout, (tm->tm_year < 0 ? bc_STR : ad_STR));
1848                                 return 1;
1849
1850                         }
1851                         else if (flag == FROM_CHAR)
1852                         {
1853                                 if (strncmp(inout, bc_STR, 2) == 0 && tm->tm_year > 0)
1854                                         tm->tm_year = -(tm->tm_year);
1855                                 if (tm->tm_year < 0)
1856                                         tm->tm_year = tm->tm_year + 1;
1857                                 return 1;
1858                         }
1859
1860                 case DCH_MONTH:
1861                         strcpy(inout, months_full[tm->tm_mon - 1]);
1862                         sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout));
1863                         if (S_FM(suf))
1864                                 return strlen(p_inout) - 1;
1865                         else
1866                                 return 8;
1867
1868                 case DCH_Month:
1869                         sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
1870                         if (S_FM(suf))
1871                                 return strlen(p_inout) - 1;
1872                         else
1873                                 return 8;
1874
1875                 case DCH_month:
1876                         sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
1877                         *inout = tolower(*inout);
1878                         if (S_FM(suf))
1879                                 return strlen(p_inout) - 1;
1880                         else
1881                                 return 8;
1882
1883                 case DCH_MON:
1884                         strcpy(inout, months[tm->tm_mon - 1]);
1885                         inout = str_toupper(inout);
1886                         return 2;
1887
1888                 case DCH_Mon:
1889                         strcpy(inout, months[tm->tm_mon - 1]);
1890                         return 2;
1891
1892                 case DCH_mon:
1893                         strcpy(inout, months[tm->tm_mon - 1]);
1894                         *inout = tolower(*inout);
1895                         return 2;
1896
1897                 case DCH_MM:
1898                         if (flag == TO_CHAR)
1899                         {
1900                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon);
1901                                 if (S_THth(suf))
1902                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1903                                 if (S_FM(suf) || S_THth(suf))
1904                                         return strlen(p_inout) - 1;
1905                                 else
1906                                         return 1;
1907
1908                         }
1909                         else if (flag == FROM_CHAR)
1910                         {
1911                                 if (S_FM(suf))
1912                                 {
1913                                         sscanf(inout, "%d", &tm->tm_mon);
1914                                         return int4len((int4) tm->tm_mon) - 1 + SKIP_THth(suf);
1915                                 }
1916                                 else
1917                                 {
1918                                         sscanf(inout, "%02d", &tm->tm_mon);
1919                                         return 1 + SKIP_THth(suf);
1920                                 }
1921                         }
1922
1923                 case DCH_DAY:
1924                         strcpy(inout, days[tm->tm_wday]);
1925                         sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout));
1926                         if (S_FM(suf))
1927                                 return strlen(p_inout) - 1;
1928                         else
1929                                 return 8;
1930
1931                 case DCH_Day:
1932                         sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
1933                         if (S_FM(suf))
1934                                 return strlen(p_inout) - 1;
1935                         else
1936                                 return 8;
1937
1938                 case DCH_day:
1939                         sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
1940                         *inout = tolower(*inout);
1941                         if (S_FM(suf))
1942                                 return strlen(p_inout) - 1;
1943                         else
1944                                 return 8;
1945
1946                 case DCH_DY:
1947                         strcpy(inout, days[tm->tm_wday]);
1948                         inout = str_toupper(inout);
1949                         return 2;
1950
1951                 case DCH_Dy:
1952                         strcpy(inout, days[tm->tm_wday]);
1953                         return 2;
1954
1955                 case DCH_dy:
1956                         strcpy(inout, days[tm->tm_wday]);
1957                         *inout = tolower(*inout);
1958                         return 2;
1959
1960                 case DCH_DDD:
1961                         if (flag == TO_CHAR)
1962                         {
1963                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday);
1964                                 if (S_THth(suf))
1965                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1966                                 if (S_FM(suf) || S_THth(suf))
1967                                         return strlen(p_inout) - 1;
1968                                 else
1969                                         return 2;
1970
1971                         }
1972                         else if (flag == FROM_CHAR)
1973                         {
1974                                 if (S_FM(suf))
1975                                 {
1976                                         sscanf(inout, "%d", &tm->tm_yday);
1977                                         return int4len((int4) tm->tm_yday) - 1 + SKIP_THth(suf);
1978                                 }
1979                                 else
1980                                 {
1981                                         sscanf(inout, "%03d", &tm->tm_yday);
1982                                         return 2 + SKIP_THth(suf);
1983                                 }
1984                         }
1985
1986                 case DCH_DD:
1987                         if (flag == TO_CHAR)
1988                         {
1989                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday);
1990                                 if (S_THth(suf))
1991                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
1992                                 if (S_FM(suf) || S_THth(suf))
1993                                         return strlen(p_inout) - 1;
1994                                 else
1995                                         return 1;
1996
1997                         }
1998                         else if (flag == FROM_CHAR)
1999                         {
2000                                 if (S_FM(suf))
2001                                 {
2002                                         sscanf(inout, "%d", &tm->tm_mday);
2003                                         return int4len((int4) tm->tm_mday) - 1 + SKIP_THth(suf);
2004                                 }
2005                                 else
2006                                 {
2007                                         sscanf(inout, "%02d", &tm->tm_mday);
2008                                         return 1 + SKIP_THth(suf);
2009                                 }
2010                         }
2011                 case DCH_D:
2012                         if (flag == TO_CHAR)
2013                         {
2014                                 sprintf(inout, "%d", tm->tm_wday + 1);
2015                                 if (S_THth(suf))
2016                                 {
2017                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2018                                         return 2;
2019                                 }
2020                                 return 0;
2021                         }
2022                         else if (flag == FROM_CHAR)
2023                         {
2024                                 sscanf(inout, "%1d", &tm->tm_wday);
2025                                 if (tm->tm_wday)
2026                                         --tm->tm_wday;
2027                                 return 0 + SKIP_THth(suf);
2028                         }
2029
2030                 case DCH_WW:
2031                         if (flag == TO_CHAR)
2032                         {
2033                                 sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
2034                                                 (tm->tm_yday - tm->tm_wday + 7) / 7);
2035                                 if (S_THth(suf))
2036                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2037                                 if (S_FM(suf) || S_THth(suf))
2038                                         return strlen(p_inout) - 1;
2039                                 else
2040                                         return 1;
2041
2042                         }
2043                         else if (flag == FROM_CHAR)
2044                                 elog(ERROR, "to_datatime(): WW is not supported");
2045                 case DCH_Q:
2046                         if (flag == TO_CHAR)
2047                         {
2048                                 sprintf(inout, "%d", (tm->tm_mon - 1) / 3 + 1);
2049                                 if (S_THth(suf))
2050                                 {
2051                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2052                                         return 2;
2053                                 }
2054                                 return 0;
2055
2056                         }
2057                         else if (flag == FROM_CHAR)
2058                                 elog(ERROR, "to_datatime(): Q is not supported");
2059
2060                 case DCH_CC:
2061                         if (flag == TO_CHAR)
2062                         {
2063                                 i = tm->tm_year / 100 + 1;
2064                                 if (i <= 99 && i >= -99)
2065                                         sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i);
2066                                 else
2067                                         sprintf(inout, "%d", i);
2068                                 if (S_THth(suf))
2069                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2070                                 return strlen(p_inout) - 1;
2071
2072                         }
2073                         else if (flag == FROM_CHAR)
2074                                 elog(ERROR, "to_datatime(): CC is not supported");
2075                 case DCH_Y_YYY:
2076                         if (flag == TO_CHAR)
2077                         {
2078                                 i = YEAR_ABS(tm->tm_year) / 1000;
2079                                 sprintf(inout, "%d,%03d", i, YEAR_ABS(tm->tm_year) - (i * 1000));
2080                                 if (S_THth(suf))
2081                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2082
2083                                 /*
2084                                  * if (tm->tm_year < 0) strcat(inout, BC_STR_ORIG);
2085                                  */
2086                                 return strlen(p_inout) - 1;
2087
2088                         }
2089                         else if (flag == FROM_CHAR)
2090                         {
2091                                 int                     cc,
2092                                                         yy;
2093
2094                                 sscanf(inout, "%d,%03d", &cc, &yy);
2095                                 tm->tm_year = (cc * 1000) + yy;
2096
2097                                 if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999)
2098                                         len = 5;
2099                                 else
2100                                         len = int4len((int4) tm->tm_year) + 1;
2101                                 len += SKIP_THth(suf);
2102
2103                                 /*
2104                                  * AC/BC if (is_acdc(inout+len, &len) < 0 && tm->tm_year >
2105                                  * 0) tm->tm_year = -(tm->tm_year); if (tm->tm_year < 0)
2106                                  * tm->tm_year = tm->tm_year+1;
2107                                  */
2108                                 return len - 1;
2109                         }
2110
2111                 case DCH_YYYY:
2112                         if (flag == TO_CHAR)
2113                         {
2114                                 if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
2115                                         sprintf(inout, "%0*d", S_FM(suf) ? 0 : 4, YEAR_ABS(tm->tm_year));
2116                                 else
2117                                         sprintf(inout, "%d", YEAR_ABS(tm->tm_year));
2118                                 if (S_THth(suf))
2119                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2120
2121                                 /*
2122                                  * if (tm->tm_year < 0) strcat(inout, BC_STR_ORIG);
2123                                  */
2124                                 return strlen(p_inout) - 1;
2125
2126                         }
2127                         else if (flag == FROM_CHAR)
2128                         {
2129                                 sscanf(inout, "%d", &tm->tm_year);
2130                                 if (!S_FM(suf) && tm->tm_year <= 9999 && tm->tm_year >= -9999)
2131                                         len = 4;
2132                                 else
2133                                         len = int4len((int4) tm->tm_year);
2134                                 len += SKIP_THth(suf);
2135
2136                                 /*
2137                                  * AC/BC if (is_acdc(inout+len, &len) < 0 && tm->tm_year >
2138                                  * 0) tm->tm_year = -(tm->tm_year); if (tm->tm_year < 0)
2139                                  * tm->tm_year = tm->tm_year+1;
2140                                  */
2141                                 return len - 1;
2142                         }
2143
2144                 case DCH_YYY:
2145                         if (flag == TO_CHAR)
2146                         {
2147                                 sprintf(buff, "%03d", YEAR_ABS(tm->tm_year));
2148                                 i = strlen(buff);
2149                                 strcpy(inout, buff + (i - 3));
2150                                 if (S_THth(suf))
2151                                 {
2152                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2153                                         return 4;
2154                                 }
2155                                 return 2;
2156
2157                         }
2158                         else if (flag == FROM_CHAR)
2159                         {
2160                                 int                     yy;
2161
2162                                 sscanf(inout, "%03d", &yy);
2163                                 tm->tm_year = (tm->tm_year / 1000) * 1000 + yy;
2164                                 return 2 + SKIP_THth(suf);
2165                         }
2166
2167                 case DCH_YY:
2168                         if (flag == TO_CHAR)
2169                         {
2170                                 sprintf(buff, "%02d", YEAR_ABS(tm->tm_year));
2171                                 i = strlen(buff);
2172                                 strcpy(inout, buff + (i - 2));
2173                                 if (S_THth(suf))
2174                                 {
2175                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2176                                         return 3;
2177                                 }
2178                                 return 1;
2179
2180                         }
2181                         else if (flag == FROM_CHAR)
2182                         {
2183                                 int                     yy;
2184
2185                                 sscanf(inout, "%02d", &yy);
2186                                 tm->tm_year = (tm->tm_year / 100) * 100 + yy;
2187                                 return 1 + SKIP_THth(suf);
2188                         }
2189
2190                 case DCH_Y:
2191                         if (flag == TO_CHAR)
2192                         {
2193                                 sprintf(buff, "%1d", YEAR_ABS(tm->tm_year));
2194                                 i = strlen(buff);
2195                                 strcpy(inout, buff + (i - 1));
2196                                 if (S_THth(suf))
2197                                 {
2198                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2199                                         return 2;
2200                                 }
2201                                 return 0;
2202
2203                         }
2204                         else if (flag == FROM_CHAR)
2205                         {
2206                                 int                     yy;
2207
2208                                 sscanf(inout, "%1d", &yy);
2209                                 tm->tm_year = (tm->tm_year / 10) * 10 + yy;
2210                                 return 0 + SKIP_THth(suf);
2211                         }
2212
2213                 case DCH_RM:
2214                         if (flag == TO_CHAR)
2215                         {
2216                                 sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
2217                                                 rm_months_upper[12 - tm->tm_mon]);
2218                                 if (S_FM(suf))
2219                                         return strlen(p_inout) - 1;
2220                                 else
2221                                         return 3;
2222
2223                         }
2224                         else if (flag == FROM_CHAR)
2225                         {
2226                                 tm->tm_mon = 11 - seq_search(inout, rm_months_upper, ALL_UPPER, FULL_SIZ, &len);
2227                                 CHECK_SEQ_SEARCH(len, "RM");
2228                                 ++tm->tm_mon;
2229                                 if (S_FM(suf))
2230                                         return len - 1;
2231                                 else
2232                                         return 3;
2233                         }
2234
2235                 case DCH_rm:
2236                         if (flag == TO_CHAR)
2237                         {
2238                                 sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
2239                                                 rm_months_lower[12 - tm->tm_mon]);
2240                                 if (S_FM(suf))
2241                                         return strlen(p_inout) - 1;
2242                                 else
2243                                         return 3;
2244
2245                         }
2246                         else if (flag == FROM_CHAR)
2247                         {
2248                                 tm->tm_mon = 11 - seq_search(inout, rm_months_lower, ALL_UPPER, FULL_SIZ, &len);
2249                                 CHECK_SEQ_SEARCH(len, "rm");
2250                                 ++tm->tm_mon;
2251                                 if (S_FM(suf))
2252                                         return len - 1;
2253                                 else
2254                                         return 3;
2255                         }
2256
2257                 case DCH_W:
2258                         if (flag == TO_CHAR)
2259                         {
2260                                 sprintf(inout, "%d", (tm->tm_mday - tm->tm_wday + 7) / 7);
2261                                 if (S_THth(suf))
2262                                 {
2263                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2264                                         return 2;
2265                                 }
2266                                 return 0;
2267
2268                         }
2269                         else if (flag == FROM_CHAR)
2270                                 elog(ERROR, "to_datatime(): W is not supported");
2271
2272                 case DCH_J:
2273                         if (flag == TO_CHAR)
2274                         {
2275                                 sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
2276                                 if (S_THth(suf))
2277                                         str_numth(p_inout, inout, S_TH_TYPE(suf));
2278                                 return strlen(p_inout) - 1;
2279                         }
2280                         else if (flag == FROM_CHAR)
2281                                 elog(ERROR, "to_datatime(): J is not supported");
2282         }
2283         return -1;
2284 }
2285
2286 static DCHCacheEntry *
2287 DCH_cache_getnew(char *str)
2288 {
2289         DCHCacheEntry *ent = NULL;
2290
2291         /* counter overload check  - paranoa? */
2292         if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
2293         {
2294                 DCHCounter = 0;
2295
2296                 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2297                         ent->age = (++DCHCounter);
2298         }
2299
2300         /* ----------
2301          * Cache is full - needs remove any older entry
2302          * ----------
2303          */
2304         if (n_DCHCache > DCH_CACHE_FIELDS)
2305         {
2306
2307                 DCHCacheEntry *old = DCHCache + 0;
2308
2309 #ifdef DEBUG_TO_FROM_CHAR
2310                 elog(DEBUG_elog_output, "Cache is full (%d)", n_DCHCache);
2311 #endif
2312                 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2313                 {
2314                         if (ent->age < old->age)
2315                                 old = ent;
2316                 }
2317 #ifdef DEBUG_TO_FROM_CHAR
2318                 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2319 #endif
2320                 strcpy(old->str, str);  /* check str size before this func. */
2321                 /* old->format fill parser */
2322                 old->age = (++DCHCounter);
2323                 return old;
2324
2325         }
2326         else
2327         {
2328 #ifdef DEBUG_TO_FROM_CHAR
2329                 elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
2330 #endif
2331                 ent = DCHCache + n_DCHCache;
2332                 strcpy(ent->str, str);  /* check str size before this func. */
2333                 /* ent->format fill parser */
2334                 ent->age = (++DCHCounter);
2335                 ++n_DCHCache;
2336                 return ent;
2337         }
2338
2339         return (DCHCacheEntry *) NULL;          /* never */
2340 }
2341
2342 static DCHCacheEntry *
2343 DCH_cache_search(char *str)
2344 {
2345         int                     i = 0;
2346         DCHCacheEntry *ent;
2347
2348         /* counter overload check  - paranoa? */
2349         if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
2350         {
2351                 DCHCounter = 0;
2352
2353                 for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2354                         ent->age = (++DCHCounter);
2355         }
2356
2357         for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
2358         {
2359                 if (i == n_DCHCache)
2360                         break;
2361                 if (strcmp(ent->str, str) == 0)
2362                 {
2363                         ent->age = (++DCHCounter);
2364                         return ent;
2365                 }
2366                 i++;
2367         }
2368
2369         return (DCHCacheEntry *) NULL;
2370 }
2371
2372
2373 /****************************************************************************
2374  *                              Public routines
2375  ***************************************************************************/
2376
2377 /* -------------------
2378  * TIMESTAMP to_char()
2379  * -------------------
2380  */
2381 Datum
2382 timestamp_to_char(PG_FUNCTION_ARGS)
2383 {
2384         Timestamp       dt = PG_GETARG_TIMESTAMP(0);
2385         text       *fmt = PG_GETARG_TEXT_P(1);
2386         text       *result,
2387                            *result_tmp;
2388         FormatNode *format;
2389         char       *str;
2390         double          fsec;
2391         char       *tzn;
2392         int                     len = 0,
2393                                 tz,
2394                                 flag = 0,
2395                                 x = 0;
2396
2397         len = VARSIZE(fmt) - VARHDRSZ;
2398
2399         if ((!len) || (TIMESTAMP_NOT_FINITE(dt)))
2400                 return PointerGetDatum(textin(""));
2401
2402         tm->tm_sec = 0;
2403         tm->tm_year = 0;
2404         tm->tm_min = 0;
2405         tm->tm_wday = 0;
2406         tm->tm_hour = 0;
2407         tm->tm_yday = 0;
2408         tm->tm_mday = 1;
2409         tm->tm_isdst = 0;
2410         tm->tm_mon = 1;
2411
2412         if (TIMESTAMP_IS_EPOCH(dt))
2413         {
2414                 x = timestamp2tm(SetTimestamp(dt), NULL, tm, &fsec, NULL);
2415
2416         }
2417         else if (TIMESTAMP_IS_CURRENT(dt))
2418         {
2419                 x = timestamp2tm(SetTimestamp(dt), &tz, tm, &fsec, &tzn);
2420
2421         }
2422         else
2423                 x = timestamp2tm(dt, &tz, tm, &fsec, &tzn);
2424
2425         if (x != 0)
2426                 elog(ERROR, "to_char(): Unable to convert timestamp to tm");
2427
2428         tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7;
2429         tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1, 1) + 1;
2430
2431         /* ----------
2432          * Convert VARDATA() to string
2433          * ----------
2434          */
2435         str = (char *) palloc(len + 1);
2436         memcpy(str, VARDATA(fmt), len);
2437         *(str + len) = '\0';
2438
2439         /* ----------
2440          * Allocate result
2441          * ----------
2442          */
2443         result = (text *) palloc((len * DCH_MAX_ITEM_SIZ) + 1 + VARHDRSZ);
2444
2445         /* ----------
2446          * Allocate new memory if format picture is bigger than static cache
2447          * and not use cache (call parser always) - flag=1 show this variant
2448          * ----------
2449                  */
2450         if (len > DCH_CACHE_SIZE)
2451         {
2452
2453                 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
2454                 flag = 1;
2455
2456                 parse_format(format, str, DCH_keywords,
2457                                          DCH_suff, DCH_index, DCH_TYPE, NULL);
2458
2459                 (format + len)->type = NODE_TYPE_END;   /* Paranoa? */
2460
2461         }
2462         else
2463         {
2464
2465                 /* ----------
2466                  * Use cache buffers
2467                  * ----------
2468                  */
2469                 DCHCacheEntry *ent;
2470
2471                 flag = 0;
2472
2473                 if ((ent = DCH_cache_search(str)) == NULL)
2474                 {
2475
2476                         ent = DCH_cache_getnew(str);
2477
2478                         /* ----------
2479                          * Not in the cache, must run parser and save a new
2480                          * format-picture to the cache.
2481                          * ----------
2482                                  */
2483                         parse_format(ent->format, str, DCH_keywords,
2484                                                  DCH_suff, DCH_index, DCH_TYPE, NULL);
2485
2486                         (ent->format + len)->type = NODE_TYPE_END;      /* Paranoa? */
2487
2488 #ifdef DEBUG_TO_FROM_CHAR
2489                         /* dump_node(ent->format, len); */
2490                         /* dump_index(DCH_keywords, DCH_index);  */
2491 #endif
2492                 }
2493                 format = ent->format;
2494         }
2495
2496         DCH_processor(format, VARDATA(result), TO_CHAR);
2497
2498         if (flag)
2499                 pfree(format);
2500
2501         pfree(str);
2502
2503         /* ----------
2504          * for result is allocated max memory, which current format-picture
2505          * needs, now it must be re-allocate to result real size
2506          * ----------
2507          */
2508         len = strlen(VARDATA(result));
2509         result_tmp = result;
2510         result = (text *) palloc(len + 1 + VARHDRSZ);
2511
2512         strcpy(VARDATA(result), VARDATA(result_tmp));
2513         VARSIZE(result) = len + VARHDRSZ;
2514         pfree(result_tmp);
2515
2516         PG_RETURN_TEXT_P(result);
2517 }
2518
2519
2520 /* ---------------------
2521  * TO_TIMESTAMP()
2522  *
2523  * Make Timestamp from date_str which is formated at argument 'fmt'
2524  * ( to_timestamp is reverse to_char() )
2525  * ---------------------
2526  */
2527 Datum
2528 to_timestamp(PG_FUNCTION_ARGS)
2529 {
2530         text       *date_str = PG_GETARG_TEXT_P(0);
2531         text       *fmt = PG_GETARG_TEXT_P(1);
2532         FormatNode *format;
2533         int                     flag = 0;
2534         Timestamp       result;
2535         char       *str;
2536         int                     len = 0,
2537                                 fsec = 0,
2538                                 tz = 0;
2539
2540         tm->tm_sec = 0;
2541         tm->tm_year = 0;
2542         tm->tm_min = 0;
2543         tm->tm_wday = 0;
2544         tm->tm_hour = 0;
2545         tm->tm_yday = 0;
2546         tm->tm_mday = 1;
2547         tm->tm_isdst = 0;
2548         tm->tm_mon = 1;
2549
2550         len = VARSIZE(fmt) - VARHDRSZ;
2551
2552         if (len)
2553         {
2554
2555                 /* ----------
2556                  * Convert VARDATA() to string
2557                  * ----------
2558                  */
2559                 str = (char *) palloc(len + 1);
2560                 memcpy(str, VARDATA(fmt), len);
2561                 *(str + len) = '\0';
2562
2563                 /* ----------
2564                  * Allocate new memory if format picture is bigger than static cache
2565                  * and not use cache (call parser always) - flag=1 show this variant
2566                  * ----------
2567                          */
2568                 if (len > DCH_CACHE_SIZE)
2569                 {
2570
2571                         format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
2572                         flag = 1;
2573
2574                         parse_format(format, str, DCH_keywords,
2575                                                  DCH_suff, DCH_index, DCH_TYPE, NULL);
2576
2577                         (format + len)->type = NODE_TYPE_END;           /* Paranoa? */
2578                 }
2579                 else
2580                 {
2581
2582                         /* ----------
2583                          * Use cache buffers
2584                          * ----------
2585                          */
2586                         DCHCacheEntry *ent;
2587
2588                         flag = 0;
2589
2590                         if ((ent = DCH_cache_search(str)) == NULL)
2591                         {
2592
2593                                 ent = DCH_cache_getnew(str);
2594
2595                                 /* ----------
2596                                  * Not in the cache, must run parser and save a new
2597                                  * format-picture to the cache.
2598                                  * ----------
2599                                          */
2600                                 parse_format(ent->format, str, DCH_keywords,
2601                                                          DCH_suff, DCH_index, DCH_TYPE, NULL);
2602
2603                                 (ent->format + len)->type = NODE_TYPE_END;              /* Paranoa? */
2604 #ifdef DEBUG_TO_FROM_CHAR
2605                                 /* dump_node(ent->format, len); */
2606                                 /* dump_index(DCH_keywords, DCH_index); */
2607 #endif
2608                         }
2609                         format = ent->format;
2610                 }
2611
2612                 /* ----------
2613                  * Call action for each node in FormatNode tree
2614                  * ----------
2615                  */
2616 #ifdef DEBUG_TO_FROM_CHAR
2617                 /* dump_node(format, len); */
2618 #endif
2619                 VARDATA(date_str)[VARSIZE(date_str) - VARHDRSZ] = '\0';
2620                 DCH_processor(format, VARDATA(date_str), FROM_CHAR);
2621
2622                 pfree(str);
2623
2624                 if (flag)
2625                         pfree(format);
2626         }
2627
2628 #ifdef DEBUG_TO_FROM_CHAR
2629         NOTICE_TM;
2630 #endif
2631         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
2632         {
2633
2634 #ifdef USE_POSIX_TIME
2635                 tm->tm_isdst = -1;
2636                 tm->tm_year -= 1900;
2637                 tm->tm_mon -= 1;
2638
2639 #ifdef DEBUG_TO_FROM_CHAR
2640                 elog(DEBUG_elog_output, "TO-FROM_CHAR: Call mktime()");
2641                 NOTICE_TM;
2642 #endif
2643                 mktime(tm);
2644                 tm->tm_year += 1900;
2645                 tm->tm_mon += 1;
2646
2647 #if defined(HAVE_TM_ZONE)
2648                 tz = -(tm->tm_gmtoff);  /* tm_gmtoff is Sun/DEC-ism */
2649 #elif defined(HAVE_INT_TIMEZONE)
2650
2651 #ifdef __CYGWIN__
2652                 tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
2653 #else
2654                 tz = (tm->tm_isdst ? (timezone - 3600) : timezone);
2655 #endif
2656
2657 #else
2658 #error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
2659 #endif
2660
2661 #else                                                   /* !USE_POSIX_TIME */
2662                 tz = CTimeZone;
2663 #endif
2664         }
2665         else
2666         {
2667                 tm->tm_isdst = 0;
2668                 tz = 0;
2669         }
2670 #ifdef DEBUG_TO_FROM_CHAR
2671         NOTICE_TM;
2672 #endif
2673         if (tm2timestamp(tm, fsec, &tz, &result) != 0)
2674                 elog(ERROR, "to_timestamp(): can't convert 'tm' to timestamp.");
2675
2676         PG_RETURN_TIMESTAMP(result);
2677 }
2678
2679 /* ----------
2680  * TO_DATE
2681  *      Make Date from date_str which is formated at argument 'fmt'
2682  * ----------
2683  */
2684 Datum
2685 to_date(PG_FUNCTION_ARGS)
2686 {
2687         /* Quick hack: since our inputs are just like to_timestamp,
2688          * hand over the whole input info struct...
2689          */
2690         return DirectFunctionCall1(timestamp_date, to_timestamp(fcinfo));
2691 }
2692
2693 /**********************************************************************
2694  *      the NUMBER version part
2695  *********************************************************************/
2696
2697
2698 static char *
2699 fill_str(char *str, int c, int max)
2700 {
2701         memset(str, c, max);
2702         *(str + max + 1) = '\0';
2703         return str;
2704 }
2705
2706 #define zeroize_NUM(_n) {               \
2707         (_n)->flag              = 0;    \
2708         (_n)->lsign             = 0;    \
2709         (_n)->pre               = 0;    \
2710         (_n)->post              = 0;    \
2711         (_n)->pre_lsign_num = 0;        \
2712         (_n)->need_locale       = 0;    \
2713         (_n)->multi             = 0;    \
2714         (_n)->zero_start        = 0;    \
2715         (_n)->zero_end          = 0;    \
2716 }
2717
2718 static NUMCacheEntry *
2719 NUM_cache_getnew(char *str)
2720 {
2721         NUMCacheEntry *ent = NULL;
2722
2723         /* counter overload check  - paranoa? */
2724         if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
2725         {
2726                 NUMCounter = 0;
2727
2728                 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2729                         ent->age = (++NUMCounter);
2730         }
2731
2732         /* ----------
2733          * Cache is full - needs remove any older entry
2734          * ----------
2735          */
2736         if (n_NUMCache > NUM_CACHE_FIELDS)
2737         {
2738
2739                 NUMCacheEntry *old = NUMCache + 0;
2740
2741 #ifdef DEBUG_TO_FROM_CHAR
2742                 elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
2743 #endif
2744
2745                 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2746                 {
2747                         if (ent->age < old->age)
2748                                 old = ent;
2749                 }
2750 #ifdef DEBUG_TO_FROM_CHAR
2751                 elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
2752 #endif
2753                 strcpy(old->str, str);  /* check str size before this func. */
2754                 /* old->format fill parser */
2755                 old->age = (++NUMCounter);
2756
2757                 ent = old;
2758
2759         }
2760         else
2761         {
2762 #ifdef DEBUG_TO_FROM_CHAR
2763                 elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
2764 #endif
2765                 ent = NUMCache + n_NUMCache;
2766                 strcpy(ent->str, str);  /* check str size before this func. */
2767                 /* ent->format fill parser */
2768                 ent->age = (++NUMCounter);
2769                 ++n_NUMCache;
2770         }
2771
2772         zeroize_NUM(&ent->Num);
2773
2774         return ent;                                     /* never */
2775 }
2776
2777 static NUMCacheEntry *
2778 NUM_cache_search(char *str)
2779 {
2780         int                     i = 0;
2781         NUMCacheEntry *ent;
2782
2783         /* counter overload check  - paranoa? */
2784         if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
2785         {
2786                 NUMCounter = 0;
2787
2788                 for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2789                         ent->age = (++NUMCounter);
2790         }
2791
2792         for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
2793         {
2794                 if (i == n_NUMCache)
2795                         break;
2796                 if (strcmp(ent->str, str) == 0)
2797                 {
2798                         ent->age = (++NUMCounter);
2799                         return ent;
2800                 }
2801                 i++;
2802         }
2803
2804         return (NUMCacheEntry *) NULL;
2805 }
2806
2807 /* ----------
2808  * Cache routine for NUM to_char version
2809  * ----------
2810  */
2811 static FormatNode *
2812 NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag)
2813 {
2814         FormatNode *format = NULL;
2815         char       *str;
2816
2817         /* ----------
2818          * Convert VARDATA() to string
2819          * ----------
2820          */
2821         str = (char *) palloc(len + 1);
2822         memcpy(str, pars_str, len);
2823         *(str + len) = '\0';
2824
2825         /* ----------
2826          * Allocate new memory if format picture is bigger than static cache
2827          * and not use cache (call parser always) - flag=1 show this variant
2828          * ----------
2829                  */
2830         if (len > NUM_CACHE_SIZE)
2831         {
2832
2833                 format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
2834                 *flag = 1;
2835
2836                 zeroize_NUM(Num);
2837
2838                 parse_format(format, str, NUM_keywords,
2839                                          NULL, NUM_index, NUM_TYPE, Num);
2840
2841                 (format + len)->type = NODE_TYPE_END;   /* Paranoa? */
2842
2843         }
2844         else
2845         {
2846
2847                 /* ----------
2848                  * Use cache buffers
2849                  * ----------
2850                  */
2851                 NUMCacheEntry *ent;
2852
2853                 flag = 0;
2854
2855                 if ((ent = NUM_cache_search(str)) == NULL)
2856                 {
2857
2858                         ent = NUM_cache_getnew(str);
2859
2860                         /* ----------
2861                          * Not in the cache, must run parser and save a new
2862                          * format-picture to the cache.
2863                          * ----------
2864                                  */
2865                         parse_format(ent->format, str, NUM_keywords,
2866                                                  NULL, NUM_index, NUM_TYPE, &ent->Num);
2867
2868                         (ent->format + len)->type = NODE_TYPE_END;      /* Paranoa? */
2869
2870                 }
2871
2872                 format = ent->format;
2873
2874                 /* ----------
2875                  * Copy cache to used struct
2876                  * ----------
2877                  */
2878                 Num->flag = ent->Num.flag;
2879                 Num->lsign = ent->Num.lsign;
2880                 Num->pre = ent->Num.pre;
2881                 Num->post = ent->Num.post;
2882                 Num->pre_lsign_num = ent->Num.pre_lsign_num;
2883                 Num->need_locale = ent->Num.need_locale;
2884                 Num->multi = ent->Num.multi;
2885                 Num->zero_start = ent->Num.zero_start;
2886                 Num->zero_end = ent->Num.zero_end;
2887         }
2888
2889 #ifdef DEBUG_TO_FROM_CHAR
2890         /* dump_node(format, len); */
2891         dump_index(NUM_keywords, NUM_index);
2892 #endif
2893
2894         pfree(str);
2895         return format;
2896 }
2897
2898
2899 static char *
2900 int_to_roman(int number)
2901 {
2902         int                     len = 0,
2903                                 num = 0,
2904                                 set = 0;
2905         char       *p = NULL,
2906                            *result,
2907                                 numstr[5];
2908
2909         result = (char *) palloc(16);
2910         *result = '\0';
2911
2912         if (number > 3999 || number < 1)
2913         {
2914                 fill_str(result, '#', 15);
2915                 return result;
2916         }
2917         len = sprintf(numstr, "%d", number);
2918
2919         for (p = numstr; *p != '\0'; p++, --len)
2920         {
2921                 num = *p - 49;                  /* 48 ascii + 1 */
2922                 if (num < 0)
2923                         continue;
2924                 if (num == -1 && set == 0)
2925                         continue;
2926                 set = 1;
2927
2928                 if (len > 3)
2929                 {
2930                         while (num-- != -1)
2931                                 strcat(result, "M");
2932                 }
2933                 else
2934                 {
2935                         if (len == 3)
2936                                 strcat(result, rm100[num]);
2937                         else if (len == 2)
2938                                 strcat(result, rm10[num]);
2939                         else if (len == 1)
2940                                 strcat(result, rm1[num]);
2941                 }
2942         }
2943         return result;
2944 }
2945
2946
2947
2948 /* ----------
2949  * Locale
2950  * ----------
2951  */
2952 static void
2953 NUM_prepare_locale(NUMProc *Np)
2954 {
2955
2956 #ifdef USE_LOCALE
2957
2958         if (Np->Num->need_locale)
2959         {
2960
2961                 struct lconv *lconv;
2962
2963                 /* ----------
2964                  * Get locales
2965                  * ----------
2966                  */
2967                 lconv = PGLC_localeconv();
2968
2969                 /* ----------
2970                  * Positive / Negative number sign
2971                  * ----------
2972                  */
2973                 if (lconv->negative_sign && *lconv->negative_sign)
2974                         Np->L_negative_sign = lconv->negative_sign;
2975                 else
2976                         Np->L_negative_sign = "-";
2977
2978                 if (lconv->positive_sign && *lconv->positive_sign)
2979                         Np->L_positive_sign = lconv->positive_sign;
2980                 else
2981                         Np->L_positive_sign = "+";
2982
2983                 /* ----------
2984                  * Number thousands separator
2985                  * ----------
2986                  */
2987                 if (lconv->thousands_sep && *lconv->thousands_sep)
2988                         Np->L_thousands_sep = lconv->thousands_sep;
2989                 else
2990                         Np->L_thousands_sep = ",";
2991
2992                 /* ----------
2993                  * Number decimal point
2994                  * ----------
2995                  */
2996                 if (lconv->decimal_point && *lconv->decimal_point)
2997                         Np->decimal = lconv->decimal_point;
2998                 else
2999                         Np->decimal = ".";
3000
3001                 /* ----------
3002                  * Currency symbol
3003                  * ----------
3004                  */
3005                 if (lconv->currency_symbol && *lconv->currency_symbol)
3006                         Np->L_currency_symbol = lconv->currency_symbol;
3007                 else
3008                         Np->L_currency_symbol = " ";
3009
3010
3011                 if (!IS_LDECIMAL(Np->Num))
3012                         Np->decimal = ".";
3013         }
3014         else
3015         {
3016
3017 #endif
3018                 /* ----------
3019                  * Default values
3020                  * ----------
3021                  */
3022                 Np->L_negative_sign = "-";
3023                 Np->L_positive_sign = "+";
3024                 Np->decimal = ".";
3025                 Np->L_thousands_sep = ",";
3026                 Np->L_currency_symbol = " ";
3027
3028 #ifdef USE_LOCALE
3029         }
3030 #endif
3031 }
3032
3033 /* ----------
3034  * Return pointer of last relevant number after decimal point
3035  *      12.0500 --> last relevant is '5'
3036  * ----------
3037  */
3038 static char *
3039 get_last_relevant_decnum(char *num)
3040 {
3041         char       *result,
3042                            *p = strchr(num, '.');
3043
3044 #ifdef DEBUG_TO_FROM_CHAR
3045         elog(DEBUG_elog_output, "CALL: get_last_relevant_decnum()");
3046 #endif
3047
3048         if (!p)
3049                 p = num;
3050         result = p;
3051
3052         while (*(++p))
3053         {
3054                 if (*p != '0')
3055                         result = p;
3056         }
3057
3058         return result;
3059 }
3060
3061 /* ----------
3062  * Number extraction for TO_NUMBER()
3063  * ----------
3064  */
3065 static void
3066 NUM_numpart_from_char(NUMProc *Np, int id, int plen)
3067 {
3068
3069 #ifdef DEBUG_TO_FROM_CHAR
3070         elog(DEBUG_elog_output, " --- scan start --- ");
3071 #endif
3072
3073         if (*Np->inout_p == ' ')
3074                 Np->inout_p++;
3075
3076 #define OVERLOAD_TEST   (Np->inout_p >= Np->inout + plen)
3077
3078         if (*Np->inout_p == ' ')
3079                 Np->inout_p++;
3080
3081         if (OVERLOAD_TEST)
3082                 return;
3083
3084         /* ----------
3085          * read sign
3086          * ----------
3087          */
3088         if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9 || NUM_S))
3089         {
3090
3091 #ifdef DEBUG_TO_FROM_CHAR
3092                 elog(DEBUG_elog_output, "Try read sign (%c).", *Np->inout_p);
3093 #endif
3094                 /* ----------
3095                  * locale sign
3096                  * ----------
3097                  */
3098                 if (IS_LSIGN(Np->Num))
3099                 {
3100
3101                         int                     x = strlen(Np->L_negative_sign);
3102
3103 #ifdef DEBUG_TO_FROM_CHAR
3104                         elog(DEBUG_elog_output, "Try read locale sign (%c).", *Np->inout_p);
3105 #endif
3106                         if (!strncmp(Np->inout_p, Np->L_negative_sign, x))
3107                         {
3108                                 Np->inout_p += x - 1;
3109                                 *Np->number = '-';
3110                                 return;
3111                         }
3112
3113                         x = strlen(Np->L_positive_sign);
3114                         if (!strncmp(Np->inout_p, Np->L_positive_sign, x))
3115                         {
3116                                 Np->inout_p += x - 1;
3117                                 *Np->number = '+';
3118                                 return;
3119                         }
3120                 }
3121
3122 #ifdef DEBUG_TO_FROM_CHAR
3123                 elog(DEBUG_elog_output, "Try read sipmle sign (%c).", *Np->inout_p);
3124 #endif
3125                 /* ----------
3126                  * simple + - < >
3127                  * ----------
3128                  */
3129                 if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
3130                                                                         *Np->inout_p == '<'))
3131                 {
3132
3133                         *Np->number = '-';      /* set - */
3134                         Np->inout_p++;
3135
3136                 }
3137                 else if (*Np->inout_p == '+')
3138                 {
3139
3140                         *Np->number = '+';      /* set + */
3141                         Np->inout_p++;
3142                 }
3143         }
3144
3145         if (OVERLOAD_TEST)
3146                 return;
3147
3148         /* ----------
3149          * read digit
3150          * ----------
3151          */
3152         if (isdigit((unsigned char) *Np->inout_p))
3153         {
3154
3155                 if (Np->read_dec && Np->read_post == Np->Num->post)
3156                         return;
3157
3158                 *Np->number_p = *Np->inout_p;
3159                 Np->number_p++;
3160
3161                 if (Np->read_dec)
3162                         Np->read_post++;
3163
3164 #ifdef DEBUG_TO_FROM_CHAR
3165                 elog(DEBUG_elog_output, "Read digit (%c).", *Np->inout_p);
3166 #endif
3167
3168                 /* ----------
3169                  * read decimal point
3170                  * ----------
3171                  */
3172         }
3173         else if (IS_DECIMAL(Np->Num))
3174         {
3175
3176 #ifdef DEBUG_TO_FROM_CHAR
3177                 elog(DEBUG_elog_output, "Try read decimal point (%c).", *Np->inout_p);
3178 #endif
3179                 if (*Np->inout_p == '.')
3180                 {
3181
3182                         *Np->number_p = '.';
3183                         Np->number_p++;
3184                         Np->read_dec = TRUE;
3185
3186                 }
3187                 else
3188                 {
3189
3190                         int                     x = strlen(Np->decimal);
3191
3192 #ifdef DEBUG_TO_FROM_CHAR
3193                         elog(DEBUG_elog_output, "Try read locale point (%c).", *Np->inout_p);
3194 #endif
3195                         if (!strncmp(Np->inout_p, Np->decimal, x))
3196                         {
3197                                 Np->inout_p += x - 1;
3198                                 *Np->number_p = '.';
3199                                 Np->number_p++;
3200                                 Np->read_dec = TRUE;
3201                         }
3202                 }
3203         }
3204 }
3205
3206 /* ----------
3207  * Add digit or sign to number-string
3208  * ----------
3209  */
3210 static void
3211 NUM_numpart_to_char(NUMProc *Np, int id)
3212 {
3213         if (IS_ROMAN(Np->Num))
3214                 return;
3215
3216         /* Note: in this elog() output not set '\0' in 'inout' */
3217
3218 #ifdef DEBUG_TO_FROM_CHAR
3219
3220         /*
3221          * Np->num_curr is number of current item in format-picture, it is not
3222          * current position in inout!
3223          */
3224         elog(DEBUG_elog_output,
3225
3226                  "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: '%s', INOUT: '%s'",
3227                  Np->sign_wrote,
3228                  Np->num_curr,
3229                  Np->number_p,
3230                  Np->inout);
3231 #endif
3232         Np->num_in = FALSE;
3233
3234         /* ----------
3235          * Write sign
3236          * ----------
3237          */
3238         if (Np->num_curr == Np->sign_pos && Np->sign_wrote == FALSE)
3239         {
3240
3241 #ifdef DEBUG_TO_FROM_CHAR
3242                 elog(DEBUG_elog_output, "Writing sign to position: %d", Np->num_curr);
3243 #endif
3244                 if (IS_LSIGN(Np->Num))
3245                 {
3246
3247                         /* ----------
3248                          * Write locale SIGN
3249                          * ----------
3250                          */
3251                         if (Np->sign == '-')
3252                                 strcpy(Np->inout_p, Np->L_negative_sign);
3253                         else
3254                                 strcpy(Np->inout_p, Np->L_positive_sign);
3255                         Np->inout_p += strlen(Np->inout_p);
3256
3257                 }
3258                 else if (IS_BRACKET(Np->Num))
3259                 {
3260                         *Np->inout_p = '<'; /* Write < */
3261                         ++Np->inout_p;
3262
3263                 }
3264                 else if (Np->sign == '+')
3265                 {
3266                         *Np->inout_p = ' '; /* Write + */
3267                         ++Np->inout_p;
3268
3269                 }
3270                 else if (Np->sign == '-')
3271                 {                                               /* Write - */
3272                         *Np->inout_p = '-';
3273                         ++Np->inout_p;
3274                 }
3275                 Np->sign_wrote = TRUE;
3276
3277         }
3278         else if (Np->sign_wrote && IS_BRACKET(Np->Num) &&
3279                          (Np->num_curr == Np->num_count + (Np->num_pre ? 1 : 0)
3280                           + (IS_DECIMAL(Np->Num) ? 1 : 0)))
3281         {
3282                 /* ----------
3283                  * Write close BRACKET
3284                  * ----------
3285                  */
3286 #ifdef DEBUG_TO_FROM_CHAR
3287                 elog(DEBUG_elog_output, "Writing bracket to position %d", Np->num_curr);
3288 #endif
3289                 *Np->inout_p = '>';             /* Write '>' */
3290                 ++Np->inout_p;
3291         }
3292
3293         /* ----------
3294          * digits / FM / Zero / Dec. point
3295          * ----------
3296          */
3297         if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC ||
3298                 (id == NUM_S && Np->num_curr < Np->num_pre))
3299         {
3300
3301                 if (Np->num_curr < Np->num_pre &&
3302                         (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
3303                 {
3304
3305                         /* ----------
3306                          * Write blank space
3307                          * ----------
3308                          */
3309                         if (!IS_FILLMODE(Np->Num))
3310                         {
3311 #ifdef DEBUG_TO_FROM_CHAR
3312                                 elog(DEBUG_elog_output, "Writing blank space to position %d", Np->num_curr);
3313 #endif
3314                                 *Np->inout_p = ' ';             /* Write ' ' */
3315                                 ++Np->inout_p;
3316                         }
3317
3318                 }
3319                 else if (IS_ZERO(Np->Num) &&
3320                                  Np->num_curr < Np->num_pre &&
3321                                  Np->Num->zero_start <= Np->num_curr)
3322                 {
3323
3324                         /* ----------
3325                          * Write ZERO
3326                          * ----------
3327                          */
3328 #ifdef DEBUG_TO_FROM_CHAR
3329                         elog(DEBUG_elog_output, "Writing zero to position %d", Np->num_curr);
3330 #endif
3331                         *Np->inout_p = '0'; /* Write '0' */
3332                         ++Np->inout_p;
3333                         Np->num_in = TRUE;
3334
3335                 }
3336                 else
3337                 {
3338
3339                         /* ----------
3340                         * Write Decinal point
3341                         * ----------
3342                         */
3343                         if (*Np->number_p == '.')
3344                         {
3345
3346                                 if (!Np->last_relevant || *Np->last_relevant != '.')
3347                                 {
3348 #ifdef DEBUG_TO_FROM_CHAR
3349                                         elog(DEBUG_elog_output, "Writing decimal point to position %d", Np->num_curr);
3350 #endif
3351                                         strcpy(Np->inout_p, Np->decimal);       /* Write DEC/D */
3352                                         Np->inout_p += strlen(Np->inout_p);
3353
3354                                         /*
3355                                          * terrible Ora '0' -- 9.9 --> '0.'
3356                                          */
3357                                 }
3358                                 else if (IS_FILLMODE(Np->Num) && *Np->number == '0' &&
3359                                                  Np->last_relevant && *Np->last_relevant == '.')
3360                                 {
3361
3362                                         strcpy(Np->inout_p, Np->decimal);       /* Write DEC/D */
3363                                         Np->inout_p += strlen(Np->inout_p);
3364                                 }
3365
3366                         }
3367                         else
3368                         {
3369
3370                                 /* ----------
3371                                  * Write Digits
3372                                  * ----------
3373                                  */
3374                                 if (Np->last_relevant && Np->number_p > Np->last_relevant &&
3375                                         id != NUM_0)
3376                                         ;
3377
3378                                 /*
3379                                  * terrible Ora format: '0.1' -- 9.9 --> '  .1'
3380                                  */
3381                                 else if (!IS_ZERO(Np->Num) && *Np->number == '0' &&
3382                                                  Np->number == Np->number_p && Np->Num->post != 0)
3383                                 {
3384
3385                                         if (!IS_FILLMODE(Np->Num))
3386                                         {
3387                                                 *Np->inout_p = ' ';
3388                                                 ++Np->inout_p;
3389
3390                                                 /*
3391                                                  * total terible Ora: '0' -- FM9.9 --> '0.'
3392                                                  */
3393                                         }
3394                                         else if (Np->last_relevant && *Np->last_relevant == '.')
3395                                         {
3396                                                 *Np->inout_p = '0';
3397                                                 ++Np->inout_p;
3398                                         }
3399
3400                                 }
3401                                 else
3402                                 {
3403 #ifdef DEBUG_TO_FROM_CHAR
3404                                         elog(DEBUG_elog_output, "Writing digit '%c' to position %d", *Np->number_p, Np->num_curr);
3405 #endif
3406                                         *Np->inout_p = *Np->number_p;           /* Write DIGIT */
3407                                         ++Np->inout_p;
3408                                         Np->num_in = TRUE;
3409                                 }
3410                         }
3411                         ++Np->number_p;
3412                 }
3413         }
3414
3415         ++Np->num_curr;
3416 }
3417
3418 static char *
3419 NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
3420                           int plen, int sign, int type)
3421 {
3422         FormatNode *n;
3423         NUMProc         _Np,
3424                            *Np = &_Np;
3425
3426         Np->Num = Num;
3427         Np->type = type;
3428         Np->number = number;
3429         Np->inout = inout;
3430         Np->last_relevant = NULL;
3431         Np->read_post = 0;
3432         Np->read_dec = FALSE;
3433
3434         if (Np->Num->zero_start)
3435                 --Np->Num->zero_start;
3436
3437         /* ----------
3438          * Roman correction
3439          * ----------
3440          */
3441         if (IS_ROMAN(Np->Num))
3442         {
3443                 if (Np->type == FROM_CHAR)
3444                         elog(ERROR, "to_number(): RN is not supported");
3445
3446                 Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
3447                         Np->Num->pre = Np->num_pre = Np->sign = 0;
3448
3449                 if (IS_FILLMODE(Np->Num))
3450                 {
3451                         Np->Num->flag = 0;
3452                         Np->Num->flag |= NUM_F_FILLMODE;
3453                 }
3454                 else
3455                         Np->Num->flag = 0;
3456                 Np->Num->flag |= NUM_F_ROMAN;
3457         }
3458
3459         /* ----------
3460          * Sign
3461          * ----------
3462          */
3463         if (type == FROM_CHAR)
3464         {
3465                 Np->sign = FALSE;
3466                 Np->sign_pos = -1;
3467         }
3468         else
3469         {
3470                 Np->sign = sign;
3471
3472                 if (Np->sign != '-')
3473                 {
3474                         Np->Num->flag &= ~NUM_F_BRACKET;
3475                         Np->Num->flag &= ~NUM_F_MINUS;
3476                 }
3477                 else if (Np->sign != '+')
3478                         Np->Num->flag &= ~NUM_F_PLUS;
3479
3480                 if (Np->sign == '+' && IS_FILLMODE(Np->Num) && !IS_LSIGN(Np->Num))
3481                         Np->sign_wrote = TRUE;          /* needn't sign */
3482                 else
3483                         Np->sign_wrote = FALSE;         /* need sign */
3484
3485                 Np->sign_pos = -1;
3486
3487                 if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
3488                         Np->Num->lsign = NUM_LSIGN_POST;
3489
3490                 /* MI/PL/SG - write sign itself and not in number */
3491                 if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
3492                         Np->sign_wrote = TRUE;          /* needn't sign */
3493         }
3494
3495         /* ----------
3496          * Count
3497          * ----------
3498          */
3499         Np->num_count = Np->Num->post + Np->Num->pre - 1;
3500
3501         if (type == TO_CHAR)
3502         {
3503                 Np->num_pre = plen;
3504
3505                 if (IS_FILLMODE(Np->Num))
3506                 {
3507                         if (IS_DECIMAL(Np->Num))
3508                                 Np->last_relevant = get_last_relevant_decnum(
3509                                                                                                                          Np->number +
3510                                                                  ((Np->Num->zero_end - Np->num_pre > 0) ?
3511                                                                   Np->Num->zero_end - Np->num_pre : 0));
3512                 }
3513
3514                 if (!Np->sign_wrote && Np->num_pre == 0)
3515                         ++Np->num_count;
3516
3517                 if (!Np->sign_wrote)
3518                 {
3519
3520                         /* ----------
3521                          * Set SING position
3522                          * ----------
3523                          */
3524                         if (Np->Num->lsign == NUM_LSIGN_POST)
3525                         {
3526                                 Np->sign_pos = Np->num_count + (Np->num_pre ? 1 : 0);
3527
3528                                 if (IS_DECIMAL(Np->Num))                /* decimal point correctio */
3529                                         ++Np->sign_pos;
3530                         }
3531                         else if (IS_ZERO(Np->Num) && Np->num_pre > Np->Num->zero_start)
3532                                 Np->sign_pos = Np->Num->zero_start ? Np->Num->zero_start : 0;
3533
3534                         else
3535                                 Np->sign_pos = Np->num_pre && !IS_FILLMODE(Np->Num) ? Np->num_pre : 0;
3536
3537                         /* ----------
3538                          * terrible Ora format
3539                          * ----------
3540                          */
3541                         if (!IS_ZERO(Np->Num) && *Np->number == '0' &&
3542                                 !IS_FILLMODE(Np->Num) && Np->Num->post != 0)
3543                         {
3544
3545                                 ++Np->sign_pos;
3546
3547                                 if (IS_LSIGN(Np->Num))
3548                                 {
3549                                         if (Np->Num->lsign == NUM_LSIGN_PRE)
3550                                                 ++Np->sign_pos;
3551                                         else
3552                                                 --Np->sign_pos;
3553                                 }
3554                         }
3555                 }
3556
3557         }
3558         else
3559         {
3560                 Np->num_pre = 0;
3561                 *Np->number = ' ';              /* sign space */
3562                 *(Np->number + 1) = '\0';
3563         }
3564
3565         Np->num_in = 0;
3566         Np->num_curr = 0;
3567
3568 #ifdef DEBUG_TO_FROM_CHAR
3569         elog(DEBUG_elog_output,
3570
3571                  "\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_POS: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s",
3572                  Np->number,
3573                  Np->Num->pre,
3574                  Np->Num->post,
3575                  Np->num_count,
3576                  Np->num_pre,
3577                  Np->sign_pos,
3578                  Np->sign_wrote ? "Yes" : "No",
3579                  IS_ZERO(Np->Num) ? "Yes" : "No",
3580                  Np->Num->zero_start,
3581                  Np->Num->zero_end,
3582                  Np->last_relevant ? Np->last_relevant : "<not set>"
3583                 );
3584 #endif
3585
3586         /* ----------
3587          * Locale
3588          * ----------
3589          */
3590         NUM_prepare_locale(Np);
3591
3592         /* ----------
3593          * Processor direct cycle
3594          * ----------
3595          */
3596         if (Np->type == FROM_CHAR)
3597                 Np->number_p = Np->number + 1;  /* first char is space for sign */
3598         else if (Np->type == TO_CHAR)
3599                 Np->number_p = Np->number;
3600
3601         for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
3602         {
3603
3604                 if (Np->type == FROM_CHAR)
3605                 {
3606                         /* ----------
3607                          * Check non-string inout end
3608                          * ----------
3609                          */
3610                         if (Np->inout_p >= Np->inout + plen)
3611                                 break;
3612                 }
3613
3614                 /* ----------
3615                  * Format pictures actions
3616                  * ----------
3617                  */
3618                 if (n->type == NODE_TYPE_ACTION)
3619                 {
3620
3621                         /* ----------
3622                          * Create/reading digit/zero/blank/sing
3623                          * ----------
3624                          */
3625                         switch (n->key->id)
3626                         {
3627
3628                                 case NUM_9:
3629                                 case NUM_0:
3630                                 case NUM_DEC:
3631                                 case NUM_D:
3632                                 case NUM_S:
3633                                 case NUM_PR:
3634                                         if (Np->type == TO_CHAR)
3635                                         {
3636                                                 NUM_numpart_to_char(Np, n->key->id);
3637                                                 continue;               /* for() */
3638                                         }
3639                                         else
3640                                         {
3641                                                 NUM_numpart_from_char(Np, n->key->id, plen);
3642                                                 break;  /* switch() case: */
3643                                         }
3644
3645                                 case NUM_COMMA:
3646                                         if (Np->type == TO_CHAR)
3647                                         {
3648                                                 if (!Np->num_in)
3649                                                 {
3650                                                         if (IS_FILLMODE(Np->Num))
3651                                                                 continue;
3652                                                         else
3653                                                                 *Np->inout_p = ' ';
3654                                                 }
3655                                                 else
3656                                                         *Np->inout_p = ',';
3657
3658                                         }
3659                                         else if (Np->type == FROM_CHAR)
3660                                         {
3661                                                 if (!Np->num_in)
3662                                                 {
3663                                                         if (IS_FILLMODE(Np->Num))
3664                                                                 continue;
3665                                                 }
3666                                         }
3667                                         break;
3668
3669                                 case NUM_G:
3670                                         if (Np->type == TO_CHAR)
3671                                         {
3672                                                 if (!Np->num_in)
3673                                                 {
3674                                                         if (IS_FILLMODE(Np->Num))
3675                                                                 continue;
3676                                                         else
3677                                                         {
3678                                                                 int                     x = strlen(Np->L_thousands_sep);
3679
3680                                                                 memset(Np->inout_p, ' ', x);
3681                                                                 Np->inout_p += x - 1;
3682                                                         }
3683                                                 }
3684                                                 else
3685                                                 {
3686                                                         strcpy(Np->inout_p, Np->L_thousands_sep);
3687                                                         Np->inout_p += strlen(Np->inout_p) - 1;
3688                                                 }
3689
3690                                         }
3691                                         else if (Np->type == FROM_CHAR)
3692                                         {
3693                                                 if (!Np->num_in)
3694                                                 {
3695                                                         if (IS_FILLMODE(Np->Num))
3696                                                                 continue;
3697                                                 }
3698                                                 Np->inout_p += strlen(Np->L_thousands_sep) - 1;
3699                                         }
3700                                         break;
3701
3702                                 case NUM_L:
3703                                         if (Np->type == TO_CHAR)
3704                                         {
3705                                                 strcpy(Np->inout_p, Np->L_currency_symbol);
3706                                                 Np->inout_p += strlen(Np->inout_p) - 1;
3707
3708                                         }
3709                                         else if (Np->type == FROM_CHAR)
3710                                                 Np->inout_p += strlen(Np->L_currency_symbol) - 1;
3711                                         break;
3712
3713                                 case NUM_RN:
3714                                         if (IS_FILLMODE(Np->Num))
3715                                         {
3716                                                 strcpy(Np->inout_p, Np->number_p);
3717                                                 Np->inout_p += strlen(Np->inout_p) - 1;
3718                                         }
3719                                         else
3720                                                 Np->inout_p += sprintf(Np->inout_p, "%15s", Np->number_p) - 1;
3721                                         break;
3722
3723                                 case NUM_rn:
3724                                         if (IS_FILLMODE(Np->Num))
3725                                         {
3726                                                 strcpy(Np->inout_p, str_tolower(Np->number_p));
3727                                                 Np->inout_p += strlen(Np->inout_p) - 1;
3728                                         }
3729                                         else
3730                                                 Np->inout_p += sprintf(Np->inout_p, "%15s", str_tolower(Np->number_p)) - 1;
3731                                         break;
3732
3733                                 case NUM_th:
3734                                         if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
3735                                                 Np->sign == '-' || IS_DECIMAL(Np->Num))
3736                                                 continue;
3737
3738                                         if (Np->type == TO_CHAR)
3739                                                 strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
3740                                         Np->inout_p += 1;
3741                                         break;
3742
3743                                 case NUM_TH:
3744                                         if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
3745                                                 Np->sign == '-' || IS_DECIMAL(Np->Num))
3746                                                 continue;
3747
3748                                         if (Np->type == TO_CHAR)
3749                                                 strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
3750                                         Np->inout_p += 1;
3751                                         break;
3752
3753                                 case NUM_MI:
3754                                         if (Np->type == TO_CHAR)
3755                                         {
3756                                                 if (Np->sign == '-')
3757                                                         *Np->inout_p = '-';
3758                                                 else
3759                                                         *Np->inout_p = ' ';
3760
3761                                         }
3762                                         else if (Np->type == FROM_CHAR)
3763                                         {
3764                                                 if (*Np->inout_p == '-')
3765                                                         *Np->number = '-';
3766                                         }
3767                                         break;
3768
3769                                 case NUM_PL:
3770                                         if (Np->type == TO_CHAR)
3771                                         {
3772                                                 if (Np->sign == '+')
3773                                                         *Np->inout_p = '+';
3774                                                 else
3775                                                         *Np->inout_p = ' ';
3776
3777                                         }
3778                                         else if (Np->type == FROM_CHAR)
3779                                         {
3780                                                 if (*Np->inout_p == '+')
3781                                                         *Np->number = '+';
3782                                         }
3783                                         break;
3784
3785                                 case NUM_SG:
3786                                         if (Np->type == TO_CHAR)
3787                                                 *Np->inout_p = Np->sign;
3788
3789                                         else if (Np->type == FROM_CHAR)
3790                                         {
3791                                                 if (*Np->inout_p == '-')
3792                                                         *Np->number = '-';
3793                                                 else if (*Np->inout_p == '+')
3794                                                         *Np->number = '+';
3795                                         }
3796                                         break;
3797
3798
3799                                 default:
3800                                         continue;
3801                                         break;
3802                         }
3803
3804                 }
3805                 else
3806                 {
3807                         /* ----------
3808                          * Remove to output char from input in TO_CHAR
3809                          * ----------
3810                          */
3811                         if (Np->type == TO_CHAR)
3812                                 *Np->inout_p = n->character;
3813                 }
3814                 Np->inout_p++;
3815         }
3816
3817         if (Np->type == TO_CHAR)
3818         {
3819                 *Np->inout_p = '\0';
3820                 return Np->inout;
3821
3822         }
3823         else if (Np->type == FROM_CHAR)
3824         {
3825
3826                 if (*(Np->number_p - 1) == '.')
3827                         *(Np->number_p - 1) = '\0';
3828                 else
3829                         *Np->number_p = '\0';
3830
3831                 /* ----------
3832                  * Correction - precision of dec. number
3833                  * ----------
3834                  */
3835                 Np->Num->post = Np->read_post;
3836
3837 #ifdef DEBUG_TO_FROM_CHAR
3838                 elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
3839 #endif
3840                 return Np->number;
3841         }
3842         else
3843                 return NULL;
3844
3845         return NULL;
3846 }
3847
3848 /* ----------
3849  * MACRO: Start part of NUM - for all NUM's to_char variants
3850  *      (sorry, but I hate copy same code - macro is better..)
3851  * ----------
3852  */
3853 #define NUM_TOCHAR_prepare {                                                    \
3854                                                                         \
3855         len = VARSIZE(fmt) - VARHDRSZ;                                  \
3856                                                                         \
3857         if (len <= 0)                                                   \
3858                 return PointerGetDatum(textin(""));                                     \
3859                                                                         \
3860         result  = (text *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ);    \
3861         format  = NUM_cache(len, &Num, VARDATA(fmt), &flag);            \
3862 }
3863
3864 /* ----------
3865  * MACRO: Finish part of NUM
3866  * ----------
3867  */
3868 #define NUM_TOCHAR_finish {                                             \
3869                                                                         \
3870         NUM_processor(format, &Num, VARDATA(result),                    \
3871                 numstr, plen, sign, TO_CHAR);                           \
3872         pfree(orgnum);                                                  \
3873                                                                         \
3874         if (flag)                                                       \
3875                 pfree(format);                                          \
3876                                                                         \
3877         /* ----------                                           \
3878          * for result is allocated max memory, which current format-picture\
3879          * needs, now it must be re-allocate to result real size        \
3880          * ----------                                                   \
3881          */                                                             \
3882         len                     = strlen(VARDATA(result));                      \
3883         result_tmp      = result;                                       \
3884         result          = (text *) palloc( len + 1 + VARHDRSZ);         \
3885                                                                         \
3886         strcpy( VARDATA(result), VARDATA(result_tmp));                  \
3887         VARSIZE(result) = len + VARHDRSZ;                               \
3888         pfree(result_tmp);                                              \
3889 }
3890
3891 /* -------------------
3892  * NUMERIC to_number() (convert string to numeric)
3893  * -------------------
3894  */
3895 Datum
3896 numeric_to_number(PG_FUNCTION_ARGS)
3897 {
3898         text       *value = PG_GETARG_TEXT_P(0);
3899         text       *fmt = PG_GETARG_TEXT_P(1);
3900         NUMDesc         Num;
3901         Datum           result;
3902         FormatNode *format;
3903         char       *numstr;
3904         int                     flag = 0;
3905         int                     len = 0;
3906         int                     scale,
3907                                 precision;
3908
3909         len = VARSIZE(fmt) - VARHDRSZ;
3910
3911         if (len <= 0)
3912                 PG_RETURN_NULL();
3913
3914         format = NUM_cache(len, &Num, VARDATA(fmt), &flag);
3915
3916         numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
3917
3918         NUM_processor(format, &Num, VARDATA(value), numstr,
3919                                   VARSIZE(value) - VARHDRSZ, 0, FROM_CHAR);
3920
3921         scale = Num.post;
3922         precision = MAX(0, Num.pre) + scale;
3923
3924         if (flag)
3925                 pfree(format);
3926
3927         result = DirectFunctionCall3(numeric_in,
3928                                                                  CStringGetDatum(numstr),
3929                                                                  ObjectIdGetDatum(InvalidOid),
3930                                                                  Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
3931         pfree(numstr);
3932         return result;
3933 }
3934
3935 /* ------------------
3936  * NUMERIC to_char()
3937  * ------------------
3938  */
3939 Datum
3940 numeric_to_char(PG_FUNCTION_ARGS)
3941 {
3942         Numeric         value = PG_GETARG_NUMERIC(0);
3943         text       *fmt = PG_GETARG_TEXT_P(1);
3944         NUMDesc         Num;
3945         FormatNode *format;
3946         text       *result,
3947                            *result_tmp;
3948         int                     flag = 0;
3949         int                     len = 0,
3950                                 plen = 0,
3951                                 sign = 0;
3952         char       *numstr,
3953                            *orgnum,
3954                            *p;
3955         Numeric         x;
3956
3957         NUM_TOCHAR_prepare;
3958
3959         /* ----------
3960          * On DateType depend part (numeric)
3961          * ----------
3962          */
3963         if (IS_ROMAN(&Num))
3964         {
3965                 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
3966                                                                                                 NumericGetDatum(value),
3967                                                                                                 Int32GetDatum(0)));
3968                 numstr = orgnum = int_to_roman(numeric_int4(x));
3969                 pfree(x);
3970         }
3971         else
3972         {
3973                 Numeric         val = value;
3974
3975                 if (IS_MULTI(&Num))
3976                 {
3977                         Numeric         a = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
3978                                                                                         Int32GetDatum(10)));
3979                         Numeric         b = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
3980                                                                                         Int32GetDatum(Num.multi)));
3981
3982                         x = numeric_power(a, b);
3983                         val = numeric_mul(value, x);
3984                         pfree(x);
3985                         pfree(a);
3986                         pfree(b);
3987                         Num.pre += Num.multi;
3988                 }
3989
3990                 x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
3991                                                                                                 NumericGetDatum(val),
3992                                                                                                 Int32GetDatum(Num.post)));
3993                 orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
3994                                                                                                          NumericGetDatum(x)));
3995                 pfree(x);
3996
3997                 if (*orgnum == '-')
3998                 {                                               /* < 0 */
3999                         sign = '-';
4000                         numstr = orgnum + 1;
4001                 }
4002                 else
4003                 {
4004                         sign = '+';
4005                         numstr = orgnum;
4006                 }
4007                 if ((p = strchr(numstr, '.')))
4008                         len = p - numstr;
4009                 else
4010                         len = strlen(numstr);
4011
4012                 if (Num.pre > len)
4013                         plen = Num.pre - len;
4014
4015                 else if (len > Num.pre)
4016                 {
4017                         fill_str(numstr, '#', Num.pre);
4018                         *(numstr + Num.pre) = '.';
4019                         fill_str(numstr + 1 + Num.pre, '#', Num.post);
4020                 }
4021
4022                 if (IS_MULTI(&Num))
4023                         pfree(val);
4024         }
4025
4026         NUM_TOCHAR_finish;
4027         PG_RETURN_TEXT_P(result);
4028 }
4029
4030 /* ---------------
4031  * INT4 to_char()
4032  * ---------------
4033  */
4034 Datum
4035 int4_to_char(PG_FUNCTION_ARGS)
4036 {
4037         int32           value = PG_GETARG_INT32(0);
4038         text       *fmt = PG_GETARG_TEXT_P(1);
4039         NUMDesc         Num;
4040         FormatNode *format;
4041         text       *result,
4042                            *result_tmp;
4043         int                     flag = 0;
4044         int                     len = 0,
4045                                 plen = 0,
4046                                 sign = 0;
4047         char       *numstr,
4048                            *orgnum;
4049
4050         NUM_TOCHAR_prepare;
4051
4052         /* ----------
4053          * On DateType depend part (int32)
4054          * ----------
4055          */
4056         if (IS_ROMAN(&Num))
4057         {
4058                 numstr = orgnum = int_to_roman(value);
4059         }
4060         else
4061         {
4062                 if (IS_MULTI(&Num))
4063                 {
4064                         orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4065                                                 Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
4066                         Num.pre += Num.multi;
4067                 }
4068                 else
4069                 {
4070                         orgnum = DatumGetCString(DirectFunctionCall1(int4out,
4071                                                                          Int32GetDatum(value)));
4072                 }
4073                 len = strlen(orgnum);
4074
4075                 if (*orgnum == '-')
4076                 {                                               /* < 0 */
4077                         sign = '-';
4078                         --len;
4079                 }
4080                 else
4081                         sign = '+';
4082
4083                 if (Num.post)
4084                 {
4085                         int                     i;
4086
4087                         numstr = palloc(len + 1 + Num.post);
4088                         strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
4089                         *(numstr + len) = '.';
4090
4091                         for (i = len + 1; i <= Num.post + len + 1; i++)
4092                                 *(numstr + i) = '0';
4093                         *(numstr + Num.post + len + 1) = '\0';
4094                         pfree(orgnum);
4095                         orgnum = numstr;
4096                 }
4097                 else
4098                         numstr = orgnum + (*orgnum == '-' ? 1 : 0);
4099
4100                 if (Num.pre > len)
4101                         plen = Num.pre - len;
4102                 else if (len > Num.pre)
4103                 {
4104                         fill_str(numstr, '#', Num.pre);
4105                         *(numstr + Num.pre) = '.';
4106                         fill_str(numstr + 1 + Num.pre, '#', Num.post);
4107                 }
4108         }
4109
4110         NUM_TOCHAR_finish;
4111         PG_RETURN_TEXT_P(result);
4112 }
4113
4114 /* ---------------
4115  * INT8 to_char()
4116  * ---------------
4117  */
4118 Datum
4119 int8_to_char(PG_FUNCTION_ARGS)
4120 {
4121         int64           value = PG_GETARG_INT64(0);
4122         text       *fmt = PG_GETARG_TEXT_P(1);
4123         NUMDesc         Num;
4124         FormatNode *format;
4125         text       *result,
4126                            *result_tmp;
4127         int                     flag = 0;
4128         int                     len = 0,
4129                                 plen = 0,
4130                                 sign = 0;
4131         char       *numstr,
4132                            *orgnum;
4133
4134         NUM_TOCHAR_prepare;
4135
4136         /* ----------
4137          * On DateType depend part (int32)
4138          * ----------
4139          */
4140         if (IS_ROMAN(&Num))
4141         {
4142                 /* Currently don't support int8 conversion to roman... */
4143                 numstr = orgnum = int_to_roman(DatumGetInt32(
4144                         DirectFunctionCall1(int84, Int64GetDatum(value))));
4145         }
4146         else
4147         {
4148                 if (IS_MULTI(&Num))
4149                 {
4150                         double          multi = pow((double) 10, (double) Num.multi);
4151
4152                         value = DatumGetInt64(DirectFunctionCall2(int8mul,
4153                                                                   Int64GetDatum(value),
4154                                                                   DirectFunctionCall1(dtoi8,
4155                                                                                                           Float8GetDatum(multi))));
4156                         Num.pre += Num.multi;
4157                 }
4158
4159                 orgnum = DatumGetCString(DirectFunctionCall1(int8out,
4160                                                                                                          Int64GetDatum(value)));
4161                 len = strlen(orgnum);
4162
4163                 if (*orgnum == '-')
4164                 {                                               /* < 0 */
4165                         sign = '-';
4166                         --len;
4167                 }
4168                 else
4169                         sign = '+';
4170
4171                 if (Num.post)
4172                 {
4173                         int                     i;
4174
4175                         numstr = palloc(len + 1 + Num.post);
4176                         strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
4177                         *(numstr + len) = '.';
4178
4179                         for (i = len + 1; i <= Num.post + len + 1; i++)
4180                                 *(numstr + i) = '0';
4181                         *(numstr + Num.post + len + 1) = '\0';
4182                         pfree(orgnum);
4183                         orgnum = numstr;
4184                 }
4185                 else
4186                         numstr = orgnum + (*orgnum == '-' ? 1 : 0);
4187
4188                 if (Num.pre > len)
4189                         plen = Num.pre - len;
4190                 else if (len > Num.pre)
4191                 {
4192                         fill_str(numstr, '#', Num.pre);
4193                         *(numstr + Num.pre) = '.';
4194                         fill_str(numstr + 1 + Num.pre, '#', Num.post);
4195                 }
4196         }
4197
4198         NUM_TOCHAR_finish;
4199         PG_RETURN_TEXT_P(result);
4200 }
4201
4202 /* -----------------
4203  * FLOAT4 to_char()
4204  * -----------------
4205  */
4206 Datum
4207 float4_to_char(PG_FUNCTION_ARGS)
4208 {
4209         float4          value = PG_GETARG_FLOAT4(0);
4210         text       *fmt = PG_GETARG_TEXT_P(1);
4211         NUMDesc         Num;
4212         FormatNode *format;
4213         text       *result,
4214                            *result_tmp;
4215         int                     flag = 0;
4216         int                     len = 0,
4217                                 plen = 0,
4218                                 sign = 0;
4219         char       *numstr,
4220                            *orgnum,
4221                            *p;
4222
4223         NUM_TOCHAR_prepare;
4224
4225         if (IS_ROMAN(&Num))
4226         {
4227                 numstr = orgnum = int_to_roman((int) rint(value));
4228
4229         }
4230         else
4231         {
4232                 float4          val = value;
4233
4234                 if (IS_MULTI(&Num))
4235                 {
4236                         float           multi = pow((double) 10, (double) Num.multi);
4237
4238                         val = value * multi;
4239                         Num.pre += Num.multi;
4240                 }
4241
4242                 orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
4243                 len = sprintf(orgnum, "%.0f", fabs(val));
4244                 if (Num.pre > len)
4245                         plen = Num.pre - len;
4246                 if (len >= FLT_DIG)
4247                         Num.post = 0;
4248                 else if (Num.post + len > FLT_DIG)
4249                         Num.post = FLT_DIG - len;
4250                 sprintf(orgnum, "%.*f", Num.post, val);
4251
4252                 if (*orgnum == '-')
4253                 {                                               /* < 0 */
4254                         sign = '-';
4255                         numstr = orgnum + 1;
4256                 }
4257                 else
4258                 {
4259                         sign = '+';
4260                         numstr = orgnum;
4261                 }
4262                 if ((p = strchr(numstr, '.')))
4263                         len = p - numstr;
4264                 else
4265                         len = strlen(numstr);
4266
4267                 if (Num.pre > len)
4268                         plen = Num.pre - len;
4269
4270                 else if (len > Num.pre)
4271                 {
4272                         fill_str(numstr, '#', Num.pre);
4273                         *(numstr + Num.pre) = '.';
4274                         fill_str(numstr + 1 + Num.pre, '#', Num.post);
4275                 }
4276         }
4277
4278         NUM_TOCHAR_finish;
4279         PG_RETURN_TEXT_P(result);
4280 }
4281
4282 /* -----------------
4283  * FLOAT8 to_char()
4284  * -----------------
4285  */
4286 Datum
4287 float8_to_char(PG_FUNCTION_ARGS)
4288 {
4289         float8          value = PG_GETARG_FLOAT8(0);
4290         text       *fmt = PG_GETARG_TEXT_P(1);
4291         NUMDesc         Num;
4292         FormatNode *format;
4293         text       *result,
4294                            *result_tmp;
4295         int                     flag = 0;
4296         int                     len = 0,
4297                                 plen = 0,
4298                                 sign = 0;
4299         char       *numstr,
4300                            *orgnum,
4301                            *p;
4302
4303         NUM_TOCHAR_prepare;
4304
4305         if (IS_ROMAN(&Num))
4306         {
4307                 numstr = orgnum = int_to_roman((int) rint(value));
4308
4309         }
4310         else
4311         {
4312                 float8          val = value;
4313
4314                 if (IS_MULTI(&Num))
4315                 {
4316                         double          multi = pow((double) 10, (double) Num.multi);
4317
4318                         val = value * multi;
4319                         Num.pre += Num.multi;
4320                 }
4321                 orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4322                 len = sprintf(orgnum, "%.0f", fabs(val));
4323                 if (Num.pre > len)
4324                         plen = Num.pre - len;
4325                 if (len >= DBL_DIG)
4326                         Num.post = 0;
4327                 else if (Num.post + len > DBL_DIG)
4328                         Num.post = DBL_DIG - len;
4329                 sprintf(orgnum, "%.*f", Num.post, val);
4330
4331                 if (*orgnum == '-')
4332                 {                                               /* < 0 */
4333                         sign = '-';
4334                         numstr = orgnum + 1;
4335                 }
4336                 else
4337                 {
4338                         sign = '+';
4339                         numstr = orgnum;
4340                 }
4341                 if ((p = strchr(numstr, '.')))
4342                         len = p - numstr;
4343                 else
4344                         len = strlen(numstr);
4345
4346                 if (Num.pre > len)
4347                         plen = Num.pre - len;
4348
4349                 else if (len > Num.pre)
4350                 {
4351                         fill_str(numstr, '#', Num.pre);
4352                         *(numstr + Num.pre) = '.';
4353                         fill_str(numstr + 1 + Num.pre, '#', Num.post);
4354                 }
4355         }
4356
4357         NUM_TOCHAR_finish;
4358         PG_RETURN_TEXT_P(result);
4359 }