Skip to content
Snippets Groups Projects
formatting.c 107 KiB
Newer Older
/* -----------------------------------------------------------------------
 * formatting.c
 *
 * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.106 2006/02/12 23:48:23 momjian Exp $
 *	 Portions Copyright (c) 1999-2005, PostgreSQL Global Development Group
 *	 TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
 *	 The PostgreSQL routines for a timestamp/int/float/numeric formatting,
 *	 inspired by the Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
 *	 Cache & Memory:
 *	Routines use (itself) internal cache for format pictures.
 *
Bruce Momjian's avatar
Bruce Momjian committed
 *	The cache uses a static buffers and is persistent across transactions.
 *	If format-picture is bigger than cache buffer, parser is called always.
 *	 NOTE for Number version:
 *	All in this version is implemented as keywords ( => not used
 *	suffixes), because a format picture is for *one* item (number)
 *	only. It not is as a timestamp version, where each keyword (can)
 *	has suffix.
 *	 NOTE for Timestamp routines:
 *	In this module the POSIX 'struct tm' type is *not* used, but rather
 *	PgSQL type, which has tm_mon based on one (*non* zero) and
 *	year *not* based on 1900, but is used full year number.
 *	Module supports AD / BC / AM / PM.
 *	Supported types for to_char():
 *
 *		Timestamp, Numeric, int4, int8, float4, float8
 *	Supported types for reverse conversion:
 *		Timestamp	- to_timestamp()
 *		Numeric		- to_number()
 *
 *
 * TODO
 *	- better number building (formatting) / parsing, now it isn't
 *		  ideal code
 *	- add support for abstime
 *	- add support for roman number to standard number conversion
 *	- add support for number spelling
 *	- add support for string to string formatting (we must be better
 *	  than Oracle :-),
 *		to_char('Hello', 'X X X X X') -> 'H e l l o'
 *
 * -----------------------------------------------------------------------
 */
/* ----------
 * UnComment me for DEBUG
 * ----------
 */
#define DEBUG_TO_FROM_CHAR
Bruce Momjian's avatar
Bruce Momjian committed
#define DEBUG_elog_output	DEBUG3
#include <ctype.h>
#include <unistd.h>
#include <math.h>
Bruce Momjian's avatar
Bruce Momjian committed
#include <float.h>
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/formatting.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"
/* ----------
 * Routines type
 * ----------
 */
#define DCH_TYPE		1		/* DATE-TIME version	*/
#define NUM_TYPE		2		/* NUMBER version	*/

/* ----------
 * KeyWord Index (ascii from position 32 (' ') to 126 (~))
 * ----------
 */
#define KeyWord_INDEX_SIZE		('~' - ' ')
#define KeyWord_INDEX_FILTER(_c)	((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
 * Maximal length of one node
#define DCH_MAX_ITEM_SIZ		9		/* max julian day		*/
#define NUM_MAX_ITEM_SIZ		8		/* roman number (RN has 15 chars)	*/
Bruce Momjian's avatar
Bruce Momjian committed
 * More is in float.c
#define MAXFLOATWIDTH	64
#define MAXDOUBLEWIDTH	128
 * External (defined in PgSQL datetime.c (timestamp utils))
extern char *months[],			/* month abbreviation	*/
		   *days[];				/* full days		*/
 * Format parser structs
	char	   *name;			/* suffix string		*/
	int			len,			/* suffix length		*/
				id,				/* used in node->suffix */
				type;			/* prefix / postfix			*/
typedef struct FormatNode FormatNode;

	const char *name;			/* keyword			*/
	int			len;			/* keyword length		*/
	int			(*action) (int arg, char *inout,		/* action for keyword */
								  int suf, bool is_to_char, bool is_interval,
									   FormatNode *node, void *data);
	bool		isitdigit;		/* is expected output/input digit */
struct FormatNode
	int			type;			/* node type			*/
	const KeyWord *key;			/* if node type is KEYWORD	*/
	int			character,		/* if node type is CHAR		*/
				suffix;			/* keyword suffix		*/
#define NODE_TYPE_ACTION	2
#define NODE_TYPE_CHAR		3

#define SUFFTYPE_PREFIX		1
#define SUFFTYPE_POSTFIX	2


/* ----------
 * Full months
 * ----------
 */
static char *months_full[] = {
	"January", "February", "March", "April", "May", "June", "July",
	"August", "September", "October", "November", "December", NULL
static char *days_short[] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
};

/*
 *	There is no 0 AD.  Years go from 1 BC to 1 AD, so we make it
 *	positive and map year == -1 to year zero, and shift all negative
 *	years up one.  For interval years, we just return the year.
 */
#define ADJUST_YEAR(year, is_interval)	((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
#define BC_STR_ORIG " BC"

#define A_D_STR		"A.D."
#define a_d_STR		"a.d."
#define AD_STR		"AD"
#define ad_STR		"ad"

#define B_C_STR		"B.C."
#define b_c_STR		"b.c."
#define BC_STR		"BC"
#define bc_STR		"bc"


/* ----------
 * AM / PM
 * ----------
 */
#define A_M_STR		"A.M."
#define a_m_STR		"a.m."
#define AM_STR		"AM"
#define am_STR		"am"

#define P_M_STR		"P.M."
#define p_m_STR		"p.m."
#define PM_STR		"PM"
#define pm_STR		"pm"

/* ----------
 * Months in roman-numeral
 * (Must be conversely for seq_search (in FROM_CHAR), because
 *	'VIII' must be over 'V')
static char *rm_months_upper[] =
{"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
static char *rm_months_lower[] =
{"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
static char *rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
static char *rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
static char *rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
/* ----------
 * Ordinal postfixes
static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
static char *numth[] = {"st", "nd", "rd", "th", NULL};
/* ----------
 * Flags & Options:
#define ONE_UPPER	1			/* Name */
#define ALL_UPPER	2			/* NAME */
#define ALL_LOWER	3			/* name */
#define MAX_MON_LEN 3
#define MAX_DY_LEN	3

#define TH_UPPER	1
#define TH_LOWER	2

/* ----------
 * Flags for DCH version
 * ----------
 */
static bool DCH_global_fx = false;


/* ----------
 * Number description struct
 * ----------
 */
	int			pre,			/* (count) numbers before decimal */
				post,			/* (count) numbers after decimal  */
				lsign,			/* want locales sign		  */
				pre_lsign_num,	/* tmp value for lsign		  */
				multi,			/* multiplier for 'V'		  */
				zero_start,		/* position of first zero	  */
				zero_end,		/* position of last zero	  */
				need_locale;	/* needs it locale		  */
 * Flags for NUMBER version
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_DECIMAL		(1 << 1)
#define NUM_F_LDECIMAL		(1 << 2)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_BLANK			(1 << 4)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_FILLMODE		(1 << 5)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_LSIGN			(1 << 6)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_BRACKET		(1 << 7)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_MINUS			(1 << 8)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_ROMAN			(1 << 10)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_PLUS_POST		(1 << 12)
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_F_MINUS_POST	(1 << 13)
#define NUM_LSIGN_POST	1
#define NUM_LSIGN_NONE	0

/* ----------
 * Tests
 * ----------
 */
#define IS_DECIMAL(_f)	((_f)->flag & NUM_F_DECIMAL)
#define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
#define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
#define IS_BLANK(_f)	((_f)->flag & NUM_F_BLANK)
#define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
#define IS_BRACKET(_f)	((_f)->flag & NUM_F_BRACKET)
#define IS_MINUS(_f)	((_f)->flag & NUM_F_MINUS)
#define IS_LSIGN(_f)	((_f)->flag & NUM_F_LSIGN)
#define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
#define IS_ROMAN(_f)	((_f)->flag & NUM_F_ROMAN)
#define IS_MULTI(_f)	((_f)->flag & NUM_F_MULTI)

Bruce Momjian's avatar
Bruce Momjian committed
/* ----------
 * Format picture cache
 *		Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
Bruce Momjian's avatar
Bruce Momjian committed
 *		Date-time part	= DCH_CACHE_SIZE * DCH_CACHE_FIELDS
 *	)
 * ----------
 */
#define NUM_CACHE_SIZE		64
Bruce Momjian's avatar
Bruce Momjian committed
#define NUM_CACHE_FIELDS	16
#define DCH_CACHE_SIZE		128
#define DCH_CACHE_FIELDS	16

typedef struct
{
	FormatNode	format[DCH_CACHE_SIZE + 1];
	char		str[DCH_CACHE_SIZE + 1];
	int			age;
Bruce Momjian's avatar
Bruce Momjian committed
} DCHCacheEntry;

typedef struct
{
	FormatNode	format[NUM_CACHE_SIZE + 1];
	char		str[NUM_CACHE_SIZE + 1];
	int			age;
Bruce Momjian's avatar
Bruce Momjian committed
} NUMCacheEntry;

/* global cache for --- date/time part */
static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1];
static int	n_DCHCache = 0;		/* number of entries */
static int	DCHCounter = 0;
Bruce Momjian's avatar
Bruce Momjian committed

/* global cache for --- number part */
static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
static NUMCacheEntry *last_NUMCacheEntry;
static int	n_NUMCache = 0;		/* number of entries */
static int	NUMCounter = 0;
Bruce Momjian's avatar
Bruce Momjian committed

#define MAX_INT32	(2147483600)
Bruce Momjian's avatar
Bruce Momjian committed

/* ----------
 * For char->date/time conversion
 * ----------
 */
typedef struct
{
	int			hh,
				am,
				pm,
				mi,
				ss,
				ssss,
				d,
				dd,
				ddd,
				mm,
				yysz;			/* is it YY or YYYY ? */
#define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar))
/* ----------
 * Debug
 * ----------
 */
		elog(DEBUG_elog_output, "TMFC:\nhh %d\nam %d\npm %d\nmi %d\nss %d\nssss %d\nd %d\ndd %d\nddd %d\nmm %d\nms: %d\nyear %d\nbc %d\niw %d\nww %d\nw %d\ncc %d\nq %d\nj %d\nus: %d\nyysz: %d", \
			(_X)->hh, (_X)->am, (_X)->pm, (_X)->mi, (_X)->ss, \
			(_X)->ssss, (_X)->d, (_X)->dd, (_X)->ddd, (_X)->mm, (_X)->ms, \
			(_X)->year, (_X)->bc, (_X)->iw, (_X)->ww, (_X)->w, \
			(_X)->cc, (_X)->q, (_X)->j, (_X)->us, (_X)->yysz);
		elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
			(_X)->tm_sec, (_X)->tm_year,\
			(_X)->tm_min, (_X)->tm_wday, (_X)->tm_hour, (_X)->tm_yday,\
			(_X)->tm_mday, (_X)->tm_isdst, (_X)->tm_mon)
#else
#define DEBUG_TMFC(_X)
#define DEBUG_TM(_X)
/* ----------
 * Datetime to char conversion
 * ----------
 */
typedef struct TmToChar
{
Bruce Momjian's avatar
Bruce Momjian committed
	struct pg_tm tm;			/* classic 'tm' struct */
	fsec_t		fsec;			/* fractional seconds */
} TmToChar;

#define tmtcTm(_X)	(&(_X)->tm)
#define tmtcTzn(_X) ((_X)->tzn)
#define tmtcFsec(_X)	((_X)->fsec)

do {	\
	(_X)->tm_sec  = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
	(_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
	(_X)->tm_mday = (_X)->tm_mon  = 1; \
} while(0)
do { \
	ZERO_tm( tmtcTm(_X) ); \
	tmtcFsec(_X) = 0; \
	tmtcTzn(_X) = NULL; \
} while(0)
/*
 *	to_char(time) appears to to_char() as an interval, so this check
 *	is really for interval and time data types.
 */
#define INVALID_FOR_INTERVAL  \
do { \
	if (is_interval) \
		ereport(ERROR, \
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
				 errmsg("invalid format specification for an interval value"), \
				 errhint("Intervals are not tied to specific calendar dates."))); \
} while(0)
/*****************************************************************************
 *			KeyWords definition & action
 *****************************************************************************/

static int dch_global(int arg, char *inout, int suf, bool is_to_char,
		   bool is_interval, FormatNode *node, void *data);
static int dch_time(int arg, char *inout, int suf, bool is_to_char,
		 bool is_interval, FormatNode *node, void *data);
static int dch_date(int arg, char *inout, int suf, bool is_to_char,
		 bool is_interval, FormatNode *node, void *data);
/* ----------
 * Suffixes:
#define DCH_S_FM	0x01
#define DCH_S_TH	0x02
#define DCH_S_th	0x04
#define DCH_S_SP	0x08
/* ----------
 * Suffix tests
#define S_THth(_s)	((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)
#define S_TH(_s)	(((_s) & DCH_S_TH) ? 1 : 0)
#define S_th(_s)	(((_s) & DCH_S_th) ? 1 : 0)
#define S_TH_TYPE(_s)	(((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)
#define S_FM(_s)	(((_s) & DCH_S_FM) ? 1 : 0)
#define S_SP(_s)	(((_s) & DCH_S_SP) ? 1 : 0)
#define S_TM(_s)	(((_s) & DCH_S_TM) ? 1 : 0)

/* ----------
 * Suffixes definition for DATE-TIME TO/FROM CHAR
 * ----------
 */
static KeySuffix DCH_suff[] = {
	{"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
	{"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
	{"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
	{"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
	{"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
	{"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
	{"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
	{NULL, 0, 0, 0}
};

/* ----------
 * Format-pictures (KeyWord).
 * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
 *		  complicated -to-> easy:
 *	(example: "DDD","DD","Day","D" )
 *
 * (this specific sort needs the algorithm for sequential search for strings,
 * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
 * or "HH12"? You must first try "HH12", because "HH" is in string, but
 * it is not good.
 *	 - Position for the keyword is similar as position in the enum DCH/NUM_poz.
 * For fast search is used the 'int index[]', index is ascii table from position
 * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
 * position or -1 if char is not used in the KeyWord. Search example for
 *	1)	see in index to index['M' - 32],
 *	2)	take keywords position (enum DCH_MM) from index
 *	3)	run sequential search in keywords[] from this position
	DCH_A_D,
	DCH_A_M,
	DCH_AD,
	DCH_AM,
	DCH_B_C,
	DCH_BC,
	DCH_CC,
	DCH_DAY,
	DCH_DDD,
	DCH_DD,
	DCH_DY,
	DCH_Day,
	DCH_Dy,
	DCH_D,
	DCH_FX,						/* global suffix */
	DCH_HH24,
	DCH_HH12,
	DCH_HH,
	DCH_IW,
	DCH_J,
	DCH_MI,
	DCH_MM,
	DCH_MONTH,
	DCH_MON,
	DCH_MS,
	DCH_Month,
	DCH_Mon,
	DCH_P_M,
	DCH_PM,
	DCH_Q,
	DCH_RM,
	DCH_SSSS,
	DCH_SS,
	DCH_TZ,
	DCH_US,
	DCH_WW,
	DCH_W,
	DCH_Y_YYY,
	DCH_YYYY,
	DCH_YYY,
	DCH_YY,
	DCH_Y,
	DCH_a_d,
	DCH_a_m,
	DCH_ad,
	DCH_am,
	DCH_b_c,
	DCH_bc,
	DCH_cc,
	DCH_day,
	DCH_ddd,
	DCH_dd,
	DCH_dy,
	DCH_d,
	DCH_fx,
	DCH_hh24,
	DCH_hh12,
	DCH_hh,
	DCH_iw,
	DCH_j,
	DCH_mi,
	DCH_mm,
	DCH_month,
	DCH_mon,
	DCH_ms,
	DCH_p_m,
	DCH_pm,
	DCH_q,
	DCH_rm,
	DCH_ssss,
	DCH_ss,
	DCH_tz,
	DCH_us,
	DCH_ww,
	DCH_w,
	DCH_y_yyy,
	DCH_yyyy,
	DCH_yyy,
	DCH_yy,
	DCH_y,
	NUM_COMMA,
	NUM_DEC,
	NUM_0,
	NUM_9,
	NUM_B,
	NUM_C,
	NUM_D,
	NUM_E,
	NUM_FM,
	NUM_G,
	NUM_L,
	NUM_MI,
	NUM_PL,
	NUM_PR,
	NUM_RN,
	NUM_SG,
	NUM_SP,
	NUM_S,
	NUM_TH,
	NUM_V,
	NUM_b,
	NUM_c,
	NUM_d,
	NUM_e,
	NUM_fm,
	NUM_g,
	NUM_l,
	NUM_mi,
	NUM_pl,
	NUM_pr,
	NUM_rn,
	NUM_sg,
	NUM_sp,
	NUM_s,
	NUM_th,
	NUM_v,
} NUM_poz;

/* ----------
 * KeyWords for DATE-TIME version
 * ----------
 */
static const KeyWord DCH_keywords[] = {
/*	keyword, len, func, type, isitdigit			 is in Index */
	{"A.D.", 4, dch_date, DCH_A_D, FALSE},		/* A */
	{"A.M.", 4, dch_time, DCH_A_M, FALSE},
	{"AD", 2, dch_date, DCH_AD, FALSE},
	{"AM", 2, dch_time, DCH_AM, FALSE},
	{"B.C.", 4, dch_date, DCH_B_C, FALSE},		/* B */
	{"BC", 2, dch_date, DCH_BC, FALSE},
	{"CC", 2, dch_date, DCH_CC, TRUE},	/* C */
	{"DAY", 3, dch_date, DCH_DAY, FALSE},		/* D */
	{"DDD", 3, dch_date, DCH_DDD, TRUE},
	{"DD", 2, dch_date, DCH_DD, TRUE},
	{"DY", 2, dch_date, DCH_DY, FALSE},
	{"Day", 3, dch_date, DCH_Day, FALSE},
	{"Dy", 2, dch_date, DCH_Dy, FALSE},
	{"D", 1, dch_date, DCH_D, TRUE},
	{"FX", 2, dch_global, DCH_FX, FALSE},		/* F */
	{"HH24", 4, dch_time, DCH_HH24, TRUE},		/* H */
	{"HH12", 4, dch_time, DCH_HH12, TRUE},
	{"HH", 2, dch_time, DCH_HH, TRUE},
	{"IW", 2, dch_date, DCH_IW, TRUE},	/* I */
	{"IYYY", 4, dch_date, DCH_IYYY, TRUE},
	{"IYY", 3, dch_date, DCH_IYY, TRUE},
	{"IY", 2, dch_date, DCH_IY, TRUE},
	{"I", 1, dch_date, DCH_I, TRUE},
	{"J", 1, dch_date, DCH_J, TRUE},	/* J */
	{"MI", 2, dch_time, DCH_MI, TRUE},
	{"MM", 2, dch_date, DCH_MM, TRUE},
	{"MONTH", 5, dch_date, DCH_MONTH, FALSE},
	{"MON", 3, dch_date, DCH_MON, FALSE},
	{"MS", 2, dch_time, DCH_MS, TRUE},
	{"Month", 5, dch_date, DCH_Month, FALSE},
	{"Mon", 3, dch_date, DCH_Mon, FALSE},
	{"P.M.", 4, dch_time, DCH_P_M, FALSE},		/* P */
	{"PM", 2, dch_time, DCH_PM, FALSE},
	{"Q", 1, dch_date, DCH_Q, TRUE},	/* Q */
	{"RM", 2, dch_date, DCH_RM, FALSE}, /* R */
	{"SSSS", 4, dch_time, DCH_SSSS, TRUE},		/* S */
	{"SS", 2, dch_time, DCH_SS, TRUE},
	{"TZ", 2, dch_time, DCH_TZ, FALSE}, /* T */
	{"US", 2, dch_time, DCH_US, TRUE},	/* U */
	{"WW", 2, dch_date, DCH_WW, TRUE},	/* W */
	{"W", 1, dch_date, DCH_W, TRUE},
	{"Y,YYY", 5, dch_date, DCH_Y_YYY, TRUE},	/* Y */
	{"YYYY", 4, dch_date, DCH_YYYY, TRUE},
	{"YYY", 3, dch_date, DCH_YYY, TRUE},
	{"YY", 2, dch_date, DCH_YY, TRUE},
	{"Y", 1, dch_date, DCH_Y, TRUE},
	{"a.d.", 4, dch_date, DCH_a_d, FALSE},		/* a */
	{"a.m.", 4, dch_time, DCH_a_m, FALSE},
	{"ad", 2, dch_date, DCH_ad, FALSE},
	{"am", 2, dch_time, DCH_am, FALSE},
	{"b.c.", 4, dch_date, DCH_b_c, FALSE},		/* b */
	{"bc", 2, dch_date, DCH_bc, FALSE},
	{"cc", 2, dch_date, DCH_CC, TRUE},	/* c */
	{"day", 3, dch_date, DCH_day, FALSE},		/* d */
	{"ddd", 3, dch_date, DCH_DDD, TRUE},
	{"dd", 2, dch_date, DCH_DD, TRUE},
	{"dy", 2, dch_date, DCH_dy, FALSE},
	{"d", 1, dch_date, DCH_D, TRUE},
	{"fx", 2, dch_global, DCH_FX, FALSE},		/* f */
	{"hh24", 4, dch_time, DCH_HH24, TRUE},		/* h */
	{"hh12", 4, dch_time, DCH_HH12, TRUE},
	{"hh", 2, dch_time, DCH_HH, TRUE},
	{"iw", 2, dch_date, DCH_IW, TRUE},	/* i */
	{"iyyy", 4, dch_date, DCH_IYYY, TRUE},
	{"iyy", 3, dch_date, DCH_IYY, TRUE},
	{"iy", 2, dch_date, DCH_IY, TRUE},
	{"i", 1, dch_date, DCH_I, TRUE},
	{"j", 1, dch_time, DCH_J, TRUE},	/* j */
	{"mi", 2, dch_time, DCH_MI, TRUE},	/* m */
	{"mm", 2, dch_date, DCH_MM, TRUE},
	{"month", 5, dch_date, DCH_month, FALSE},
	{"mon", 3, dch_date, DCH_mon, FALSE},
	{"ms", 2, dch_time, DCH_MS, TRUE},
	{"p.m.", 4, dch_time, DCH_p_m, FALSE},		/* p */
	{"pm", 2, dch_time, DCH_pm, FALSE},
	{"q", 1, dch_date, DCH_Q, TRUE},	/* q */
	{"rm", 2, dch_date, DCH_rm, FALSE}, /* r */
	{"ssss", 4, dch_time, DCH_SSSS, TRUE},		/* s */
	{"ss", 2, dch_time, DCH_SS, TRUE},
	{"tz", 2, dch_time, DCH_tz, FALSE}, /* t */
	{"us", 2, dch_time, DCH_US, TRUE},	/* u */
	{"ww", 2, dch_date, DCH_WW, TRUE},	/* w */
	{"w", 1, dch_date, DCH_W, TRUE},
	{"y,yyy", 5, dch_date, DCH_Y_YYY, TRUE},	/* y */
	{"yyyy", 4, dch_date, DCH_YYYY, TRUE},
	{"yyy", 3, dch_date, DCH_YYY, TRUE},
	{"yy", 2, dch_date, DCH_YY, TRUE},
	{"y", 1, dch_date, DCH_Y, TRUE},
{NULL, 0, NULL, 0}};
 * KeyWords for NUMBER version (now, isitdigit info is not needful here..)
static const KeyWord NUM_keywords[] = {
/*	keyword,	len, func.	type			   is in Index */
	{",", 1, NULL, NUM_COMMA},	/* , */
	{".", 1, NULL, NUM_DEC},	/* . */
	{"0", 1, NULL, NUM_0},		/* 0 */
	{"9", 1, NULL, NUM_9},		/* 9 */
	{"B", 1, NULL, NUM_B},		/* B */
	{"C", 1, NULL, NUM_C},		/* C */
	{"D", 1, NULL, NUM_D},		/* D */
	{"E", 1, NULL, NUM_E},		/* E */
	{"FM", 2, NULL, NUM_FM},	/* F */
	{"G", 1, NULL, NUM_G},		/* G */
	{"L", 1, NULL, NUM_L},		/* L */
	{"MI", 2, NULL, NUM_MI},	/* M */
	{"PL", 2, NULL, NUM_PL},	/* P */
	{"PR", 2, NULL, NUM_PR},
	{"RN", 2, NULL, NUM_RN},	/* R */
	{"SG", 2, NULL, NUM_SG},	/* S */
	{"SP", 2, NULL, NUM_SP},
	{"S", 1, NULL, NUM_S},
	{"TH", 2, NULL, NUM_TH},	/* T */
	{"V", 1, NULL, NUM_V},		/* V */
	{"b", 1, NULL, NUM_B},		/* b */
	{"c", 1, NULL, NUM_C},		/* c */
	{"d", 1, NULL, NUM_D},		/* d */
	{"e", 1, NULL, NUM_E},		/* e */
	{"fm", 2, NULL, NUM_FM},	/* f */
	{"g", 1, NULL, NUM_G},		/* g */
	{"l", 1, NULL, NUM_L},		/* l */
	{"mi", 2, NULL, NUM_MI},	/* m */
	{"pl", 2, NULL, NUM_PL},	/* p */
	{"pr", 2, NULL, NUM_PR},
	{"rn", 2, NULL, NUM_rn},	/* r */
	{"sg", 2, NULL, NUM_SG},	/* s */
	{"sp", 2, NULL, NUM_SP},
	{"s", 1, NULL, NUM_S},
	{"th", 2, NULL, NUM_th},	/* t */
	{"v", 1, NULL, NUM_V},		/* v */

/* last */
{NULL, 0, NULL, 0}};


/* ----------
 * KeyWords index for DATE-TIME version
 * ----------
 */
static const int DCH_index[KeyWord_INDEX_SIZE] = {
	/*---- first 0..31 chars are skipped ----*/
	-1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
Bruce Momjian's avatar
Bruce Momjian committed
	DCH_FX, -1, DCH_HH24, DCH_IW, DCH_J, -1, -1, DCH_MI, -1, -1,
	DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
	-1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
Bruce Momjian's avatar
Bruce Momjian committed
	DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iw, DCH_j, -1, -1, DCH_mi,
	-1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
	-1, DCH_y_yyy, -1, -1, -1, -1

	/*---- chars over 126 are skiped ----*/

/* ----------
 * KeyWords index for NUMBER version
 * ----------
 */
static const int NUM_index[KeyWord_INDEX_SIZE] = {
/*
0	1	2	3	4	5	6	7	8	9
*/
	/*---- first 0..31 chars are skiped ----*/

	-1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
	-1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
	-1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
	NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
	NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
	NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
	-1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
	-1, -1, -1, -1, -1, -1

	/*---- chars over 126 are skiped ----*/
};

/* ----------
 * Number processor struct
 * ----------
 */
typedef struct NUMProc
{
	NUMDesc    *Num;			/* number description		*/

	int			sign,			/* '-' or '+'			*/
				sign_wrote,		/* was sign write		*/
				num_count,		/* number of write digits	*/
				num_in,			/* is inside number		*/
				num_curr,		/* current position in number	*/
				num_pre,		/* space before first number	*/

				read_dec,		/* to_number - was read dec. point	*/
				read_post,		/* to_number - number of dec. digit */
				read_pre;		/* to_number - number non-dec. digit */

	char	   *number,			/* string with number	*/
			   *number_p,		/* pointer to current number position */
			   *inout,			/* in / out buffer	*/
			   *inout_p,		/* pointer to current inout position */
			   *last_relevant,	/* last relevant number after decimal point */
			   *L_negative_sign,	/* Locale */
			   *L_positive_sign,
			   *decimal,
			   *L_thousands_sep,
			   *L_currency_symbol;
static const KeyWord *index_seq_search(char *str, const KeyWord *kw,
				 const int *index);
static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
			 KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
static char *DCH_processor(FormatNode *node, char *inout, bool is_to_char,
			  bool is_interval, void *data);
static void dump_index(const KeyWord *k, const int *index);
static void dump_node(FormatNode *node, int max);
#endif

static char *get_th(char *num, int type);
static char *str_numth(char *dest, char *num, int type);
static int	strdigits_len(char *str);
static char *str_toupper(char *buff);
static char *str_tolower(char *buff);
/* static int is_acdc(char *str, int *len); */
static int	seq_search(char *name, char **array, int type, int max, int *len);
static void do_to_timestamp(text *date_txt, text *fmt,
				struct pg_tm * tm, fsec_t *fsec);
static char *fill_str(char *str, int c, int max);
static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree);
static char *int_to_roman(int number);
static void NUM_prepare_locale(NUMProc *Np);
static char *get_last_relevant_decnum(char *num);
static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
static void NUM_numpart_to_char(NUMProc *Np, int id);
static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
			  int plen, int sign, bool is_to_char);
static DCHCacheEntry *DCH_cache_search(char *str);
static DCHCacheEntry *DCH_cache_getnew(char *str);
static NUMCacheEntry *NUM_cache_search(char *str);
static NUMCacheEntry *NUM_cache_getnew(char *str);
static void NUM_cache_remove(NUMCacheEntry *ent);
static char *localize_month_full(int index);
static char *localize_month(int index);
static char *localize_day_full(int index);
static char *localize_day(int index);

/* ----------
 * Fast sequential search, use index for data selection which
 * go to seq. cycle (it is very fast for unwanted strings)
 * (can't be used binary search in format parsing)
 * ----------
 */
static const KeyWord *
index_seq_search(char *str, const KeyWord *kw, const int *index)
	if (!KeyWord_INDEX_FILTER(*str))
	if ((poz = *(index + (*str - ' '))) > -1)
	{

		do
		{
			if (!strncmp(str, k->name, k->len))
		} while (*str == *k->name);
}

static KeySuffix *
suff_search(char *str, KeySuffix *suf, int type)
{
	KeySuffix  *s;

	for (s = suf; s->name != NULL; s++)
	{
		if (!strncmp(str, s->name, s->len))
			return s;
	}
}

/* ----------
 * Prepare NUMDesc (number description struct) via FormatNode struct
 * ----------
 */
NUMDesc_prepare(NUMDesc *num, FormatNode *n)
{