1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#pragma prototyped
23/*
24 * Glenn Fowler
25 * AT&T Research
26 *
27 * time conversion translation support
28 */
29
30#include <ast.h>
31#include <cdt.h>
32#include <iconv.h>
33#include <mc.h>
34#include <tm.h>
35#include <ast_nl_types.h>
36
37#include "lclib.h"
38
39static struct
40{
41	char*		format;
42	Lc_info_t*	locale;
43	char		null[1];
44} state;
45
46/*
47 * this is unix dadgummit
48 */
49
50static int
51standardized(Lc_info_t* li, register char** b)
52{
53	if ((li->lc->language->flags & (LC_debug|LC_default)) || streq(li->lc->language->code, "en"))
54	{
55		b[TM_TIME] = "%H:%M:%S";
56		b[TM_DATE] = "%m/%d/%y";
57		b[TM_DEFAULT] = "%a %b %e %T %Z %Y";
58		return 1;
59	}
60	return 0;
61}
62
63/*
64 * fix up LC_TIME data after loading
65 */
66
67static void
68fixup(Lc_info_t* li, register char** b)
69{
70	register char**		v;
71	register char**		e;
72	register int		n;
73
74	static int		must[] =
75	{
76					TM_TIME,
77					TM_DATE,
78					TM_DEFAULT,
79					TM_MERIDIAN,
80					TM_UT,
81					TM_DT,
82					TM_SUFFIXES,
83					TM_PARTS,
84					TM_HOURS,
85					TM_DAYS,
86					TM_LAST,
87					TM_THIS,
88					TM_NEXT,
89					TM_EXACT,
90					TM_NOISE,
91					TM_ORDINAL,
92					TM_CTIME,
93					TM_DATE_1,
94					TM_INTERNATIONAL,
95					TM_RECENT,
96					TM_DISTANT,
97					TM_MERIDIAN_TIME,
98					TM_ORDINALS,
99					TM_FINAL,
100					TM_WORK,
101	};
102
103	standardized(li, b);
104	for (v = b, e = b + TM_NFORM; v < e; v++)
105		if (!*v)
106			*v = state.null;
107	for (n = 0; n < elementsof(must); n++)
108		if (!*b[must[n]])
109			b[must[n]] = tm_data.format[must[n]];
110	if (li->lc->flags & LC_default)
111		for (n = 0; n < TM_NFORM; n++)
112			if (!*b[n])
113				b[n] = tm_data.format[n];
114	if (strchr(b[TM_UT], '%'))
115	{
116		tm_info.deformat = b[TM_UT];
117		for (n = TM_UT; n < TM_DT; n++)
118			b[n] = state.null;
119	}
120	else
121		tm_info.deformat = b[TM_DEFAULT];
122	tm_info.format = b;
123	if (!(tm_info.deformat = state.format))
124		tm_info.deformat = tm_info.format[TM_DEFAULT];
125	li->data = (void*)b;
126}
127
128#if _WINIX
129
130#include <ast_windows.h>
131
132typedef struct Map_s
133{
134	LCID		native;
135	int		local;
136} Map_t;
137
138static const Map_t map[] =
139{
140	LOCALE_S1159,			(TM_MERIDIAN+0),
141	LOCALE_S2359,			(TM_MERIDIAN+1),
142	LOCALE_SABBREVDAYNAME1,		(TM_DAY_ABBREV+1),
143	LOCALE_SABBREVDAYNAME2,		(TM_DAY_ABBREV+2),
144	LOCALE_SABBREVDAYNAME3,		(TM_DAY_ABBREV+3),
145	LOCALE_SABBREVDAYNAME4,		(TM_DAY_ABBREV+4),
146	LOCALE_SABBREVDAYNAME5,		(TM_DAY_ABBREV+5),
147	LOCALE_SABBREVDAYNAME6,		(TM_DAY_ABBREV+6),
148	LOCALE_SABBREVDAYNAME7,		(TM_DAY_ABBREV+0),
149	LOCALE_SABBREVMONTHNAME1,	(TM_MONTH_ABBREV+0),
150	LOCALE_SABBREVMONTHNAME2,	(TM_MONTH_ABBREV+1),
151	LOCALE_SABBREVMONTHNAME3,	(TM_MONTH_ABBREV+2),
152	LOCALE_SABBREVMONTHNAME4,	(TM_MONTH_ABBREV+3),
153	LOCALE_SABBREVMONTHNAME5,	(TM_MONTH_ABBREV+4),
154	LOCALE_SABBREVMONTHNAME6,	(TM_MONTH_ABBREV+5),
155	LOCALE_SABBREVMONTHNAME7,	(TM_MONTH_ABBREV+6),
156	LOCALE_SABBREVMONTHNAME8,	(TM_MONTH_ABBREV+7),
157	LOCALE_SABBREVMONTHNAME9,	(TM_MONTH_ABBREV+8),
158	LOCALE_SABBREVMONTHNAME10,	(TM_MONTH_ABBREV+9),
159	LOCALE_SABBREVMONTHNAME11,	(TM_MONTH_ABBREV+10),
160	LOCALE_SABBREVMONTHNAME12,	(TM_MONTH_ABBREV+11),
161	LOCALE_SDAYNAME1,		(TM_DAY+1),
162	LOCALE_SDAYNAME2,		(TM_DAY+2),
163	LOCALE_SDAYNAME3,		(TM_DAY+3),
164	LOCALE_SDAYNAME4,		(TM_DAY+4),
165	LOCALE_SDAYNAME5,		(TM_DAY+5),
166	LOCALE_SDAYNAME6,		(TM_DAY+6),
167	LOCALE_SDAYNAME7,		(TM_DAY+0),
168	LOCALE_SMONTHNAME1,		(TM_MONTH+0),
169	LOCALE_SMONTHNAME2,		(TM_MONTH+1),
170	LOCALE_SMONTHNAME3,		(TM_MONTH+2),
171	LOCALE_SMONTHNAME4,		(TM_MONTH+3),
172	LOCALE_SMONTHNAME5,		(TM_MONTH+4),
173	LOCALE_SMONTHNAME6,		(TM_MONTH+5),
174	LOCALE_SMONTHNAME7,		(TM_MONTH+6),
175	LOCALE_SMONTHNAME8,		(TM_MONTH+7),
176	LOCALE_SMONTHNAME9,		(TM_MONTH+8),
177	LOCALE_SMONTHNAME10,		(TM_MONTH+9),
178	LOCALE_SMONTHNAME11,		(TM_MONTH+10),
179	LOCALE_SMONTHNAME12,		(TM_MONTH+11),
180};
181
182#undef	extern
183
184/*
185 * convert ms word date spec w to posix strftime format f
186 * next char after f returned
187 * the caller already made sure f is big enough
188 */
189
190static char*
191word2posix(register char* f, register char* w, int alternate)
192{
193	register char*	r;
194	register int	c;
195	register int	p;
196	register int	n;
197
198	while (*w)
199	{
200		p = 0;
201		r = w;
202		while (*++w == *r);
203		if ((n = w - r) > 3 && alternate)
204			n--;
205		switch (*r)
206		{
207		case 'a':
208		case 'A':
209			if (!strncasecmp(w, "am/pm", 5))
210				w += 5;
211			else if (!strncasecmp(w, "a/p", 3))
212				w += 3;
213			c = 'p';
214			break;
215		case 'd':
216			switch (n)
217			{
218			case 1:
219				p = '-';
220				/*FALLTHROUGH*/
221			case 2:
222				c = 'd';
223				break;
224			case 3:
225				c = 'a';
226				break;
227			default:
228				c = 'A';
229				break;
230			}
231			break;
232		case 'h':
233			switch (n)
234			{
235			case 1:
236				p = '-';
237				/*FALLTHROUGH*/
238			default:
239				c = 'I';
240				break;
241			}
242			break;
243		case 'H':
244			switch (n)
245			{
246			case 1:
247				p = '-';
248				/*FALLTHROUGH*/
249			default:
250				c = 'H';
251				break;
252			}
253			break;
254		case 'M':
255			switch (n)
256			{
257			case 1:
258				p = '-';
259				/*FALLTHROUGH*/
260			case 2:
261				c = 'm';
262				break;
263			case 3:
264				c = 'b';
265				break;
266			default:
267				c = 'B';
268				break;
269			}
270			break;
271		case 'm':
272			switch (n)
273			{
274			case 1:
275				p = '-';
276				/*FALLTHROUGH*/
277			default:
278				c = 'M';
279				break;
280			}
281			break;
282		case 's':
283			switch (n)
284			{
285			case 1:
286				p = '-';
287				/*FALLTHROUGH*/
288			default:
289				c = 'S';
290				break;
291			}
292			break;
293		case 'y':
294			switch (n)
295			{
296			case 1:
297				p = '-';
298				/*FALLTHROUGH*/
299			case 2:
300				c = 'y';
301				break;
302			default:
303				c = 'Y';
304				break;
305			}
306			break;
307		case '\'':
308			if (n & 1)
309				for (w = r + 1; *w; *f++ = *w++)
310					if (*w == '\'')
311					{
312						w++;
313						break;
314					}
315			continue;
316		case '%':
317			while (r < w)
318			{
319				*f++ = *r++;
320				*f++ = *r++;
321			}
322			continue;
323		default:
324			while (r < w)
325				*f++ = *r++;
326			continue;
327		}
328		*f++ = '%';
329		if (p)
330			*f++ = '-';
331		*f++ = c;
332	}
333	*f++ = 0;
334	return f;
335}
336
337/*
338 * load the native LC_TIME data for the current locale
339 */
340
341static void
342native_lc_time(Lc_info_t* li)
343{
344	register char*	s;
345	register char*	t;
346	register char**	b;
347	register int	n;
348	register int	m;
349	register int	i;
350	LCID		lcid;
351	int		nt;
352	int		ns;
353	int		nl;
354	int		clock_24;
355	int		leading_0;
356	char		buf[256];
357
358	lcid = li->lc->index;
359	nt = 2 * GetLocaleInfo(lcid, LOCALE_STIME, 0, 0) + 7; /* HH:MM:SS */
360	ns = 3 * GetLocaleInfo(lcid, LOCALE_SSHORTDATE, 0, 0);
361	nl = 3 * GetLocaleInfo(lcid, LOCALE_SLONGDATE, 0, 0);
362	n = nt + ns + nl;
363	for (i = 0; i < elementsof(map); i++)
364		n += GetLocaleInfo(lcid, map[i].native, 0, 0);
365	if (!(b = newof(0, char*, TM_NFORM, n)))
366		return;
367	s = (char*)(b + TM_NFORM);
368	for (i = 0; i < elementsof(map); i++)
369	{
370		if (!(m = GetLocaleInfo(lcid, map[i].native, s, n)))
371			goto bad;
372		b[map[i].local] = s;
373		s += m;
374	}
375	if (!standardized(li, b))
376	{
377		/*
378		 * synthesize TM_TIME format from the ms word template
379		 */
380
381		if (!GetLocaleInfo(lcid, LOCALE_ITIME, buf, sizeof(buf)))
382			goto bad;
383		clock_24 = atoi(buf);
384		if (!GetLocaleInfo(lcid, LOCALE_ITLZERO, buf, sizeof(buf)))
385			goto bad;
386		leading_0 = atoi(buf);
387		if (!GetLocaleInfo(lcid, LOCALE_STIME, buf, sizeof(buf)))
388			goto bad;
389		b[TM_TIME] = s;
390		*s++ = '%';
391		if (!leading_0)
392			*s++ = '-';
393		*s++ = clock_24 ? 'H' : 'I';
394		for (t = buf; *s = *t++; s++);
395		*s++ = '%';
396		if (!leading_0)
397			*s++ = '-';
398		*s++ = 'M';
399		for (t = buf; *s = *t++; s++);
400		*s++ = '%';
401		if (!leading_0)
402			*s++ = '-';
403		*s++ = 'S';
404		*s++ = 0;
405
406		/*
407		 * synthesize TM_DATE format
408		 */
409
410		if (!GetLocaleInfo(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf)))
411			goto bad;
412		b[TM_DATE] = s;
413		s = word2posix(s, buf, 1);
414
415		/*
416		 * synthesize TM_DEFAULT format
417		 */
418
419		if (!GetLocaleInfo(lcid, LOCALE_SLONGDATE, buf, sizeof(buf)))
420			goto bad;
421		b[TM_DEFAULT] = s;
422		s = word2posix(s, buf, 1);
423		strcpy(s - 1, " %X");
424	}
425
426	/*
427	 * done
428	 */
429
430	fixup(li, b);
431	return;
432 bad:
433	free(b);
434}
435
436#else
437
438#if _lib_nl_langinfo && _hdr_langinfo
439
440#if _hdr_nl_types
441#include <nl_types.h>
442#endif
443
444#include <langinfo.h>
445
446typedef struct Map_s
447{
448	int		native;
449	int		local;
450} Map_t;
451
452static const Map_t map[] =
453{
454	AM_STR,				(TM_MERIDIAN+0),
455	PM_STR,				(TM_MERIDIAN+1),
456	ABDAY_1,			(TM_DAY_ABBREV+0),
457	ABDAY_2,			(TM_DAY_ABBREV+1),
458	ABDAY_3,			(TM_DAY_ABBREV+2),
459	ABDAY_4,			(TM_DAY_ABBREV+3),
460	ABDAY_5,			(TM_DAY_ABBREV+4),
461	ABDAY_6,			(TM_DAY_ABBREV+5),
462	ABDAY_7,			(TM_DAY_ABBREV+6),
463	ABMON_1,			(TM_MONTH_ABBREV+0),
464	ABMON_2,			(TM_MONTH_ABBREV+1),
465	ABMON_3,			(TM_MONTH_ABBREV+2),
466	ABMON_4,			(TM_MONTH_ABBREV+3),
467	ABMON_5,			(TM_MONTH_ABBREV+4),
468	ABMON_6,			(TM_MONTH_ABBREV+5),
469	ABMON_7,			(TM_MONTH_ABBREV+6),
470	ABMON_8,			(TM_MONTH_ABBREV+7),
471	ABMON_9,			(TM_MONTH_ABBREV+8),
472	ABMON_10,			(TM_MONTH_ABBREV+9),
473	ABMON_11,			(TM_MONTH_ABBREV+10),
474	ABMON_12,			(TM_MONTH_ABBREV+11),
475	DAY_1,				(TM_DAY+0),
476	DAY_2,				(TM_DAY+1),
477	DAY_3,				(TM_DAY+2),
478	DAY_4,				(TM_DAY+3),
479	DAY_5,				(TM_DAY+4),
480	DAY_6,				(TM_DAY+5),
481	DAY_7,				(TM_DAY+6),
482	MON_1,				(TM_MONTH+0),
483	MON_2,				(TM_MONTH+1),
484	MON_3,				(TM_MONTH+2),
485	MON_4,				(TM_MONTH+3),
486	MON_5,				(TM_MONTH+4),
487	MON_6,				(TM_MONTH+5),
488	MON_7,				(TM_MONTH+6),
489	MON_8,				(TM_MONTH+7),
490	MON_9,				(TM_MONTH+8),
491	MON_10,				(TM_MONTH+9),
492	MON_11,				(TM_MONTH+10),
493	MON_12,				(TM_MONTH+11),
494#ifdef _DATE_FMT
495	_DATE_FMT,			TM_DEFAULT,
496#else
497	D_T_FMT,			TM_DEFAULT,
498#endif
499	D_FMT,				TM_DATE,
500	T_FMT,				TM_TIME,
501#ifdef ERA
502	ERA,				TM_ERA,
503	ERA_D_T_FMT,			TM_ERA_DEFAULT,
504	ERA_D_FMT,			TM_ERA_DATE,
505	ERA_T_FMT,			TM_ERA_TIME,
506#endif
507#ifdef ALT_DIGITS
508	ALT_DIGITS,			TM_DIGITS,
509#endif
510};
511
512static void
513native_lc_time(Lc_info_t* li)
514{
515	register char*	s;
516	register char*	t;
517	register char**	b;
518	register int	n;
519	register int	i;
520
521	n = 0;
522	for (i = 0; i < elementsof(map); i++)
523	{
524		if (!(t = nl_langinfo(map[i].native)))
525			t = tm_data.format[map[i].local];
526		n += strlen(t) + 1;
527	}
528	if (!(b = newof(0, char*, TM_NFORM, n)))
529		return;
530	s = (char*)(b + TM_NFORM);
531	for (i = 0; i < elementsof(map); i++)
532	{
533		b[map[i].local] = s;
534		if (!(t = nl_langinfo(map[i].native)))
535			t = tm_data.format[map[i].local];
536		while (*s++ = *t++);
537	}
538	fixup(li, b);
539}
540
541#else
542
543#define native_lc_time(li)	((li->data=(void*)(tm_info.format=tm_data.format)),(tm_info.deformat=tm_info.format[TM_DEFAULT]))
544
545#endif
546
547#endif
548
549/*
550 * load the LC_TIME data for the current locale
551 */
552
553static void
554load(Lc_info_t* li)
555{
556	register char*		s;
557	register char**		b;
558	register char**		v;
559	register char**		e;
560	unsigned char*		u;
561	ssize_t			n;
562	iconv_t			cvt;
563	Sfio_t*			sp;
564	Sfio_t*			tp;
565	char			path[PATH_MAX];
566
567	if (b = (char**)li->data)
568	{
569		tm_info.format = b;
570		if (!(tm_info.deformat = state.format))
571			tm_info.deformat = tm_info.format[TM_DEFAULT];
572		return;
573	}
574	tm_info.format = tm_data.format;
575	if (!(tm_info.deformat = state.format))
576		tm_info.deformat = tm_info.format[TM_DEFAULT];
577	if (mcfind(NiL, NiL, LC_TIME, 0, path, sizeof(path)) && (sp = sfopen(NiL, path, "r")))
578	{
579		n = sfsize(sp);
580		tp = 0;
581		if (u = (unsigned char*)sfreserve(sp, 3, 1))
582		{
583			if (u[0] == 0xef && u[1] == 0xbb && u[2] == 0xbf && (cvt = iconv_open("", "utf")) != (iconv_t)(-1))
584			{
585				if (tp = sfstropen())
586				{
587					sfread(sp, u, 3);
588					n = iconv_move(cvt, sp, tp, SF_UNBOUND, NiL);
589				}
590				iconv_close(cvt);
591			}
592			if (!tp)
593				sfread(sp, u, 0);
594		}
595		if (b = newof(0, char*, TM_NFORM, n + 2))
596		{
597			v = b;
598			e = b + TM_NFORM;
599			s = (char*)e;
600			if (tp && memcpy(s, sfstrbase(tp), n) || !tp && sfread(sp, s, n) == n)
601			{
602				s[n] = '\n';
603				while (v < e)
604				{
605					*v++ = s;
606					if (!(s = strchr(s, '\n')))
607						break;
608					*s++ = 0;
609				}
610				fixup(li, b);
611			}
612			else
613				free(b);
614		}
615		if (tp)
616			sfclose(tp);
617		sfclose(sp);
618	}
619	else
620		native_lc_time(li);
621}
622
623/*
624 * check that tm_info.format matches the current locale
625 */
626
627char**
628tmlocale(void)
629{
630	Lc_info_t*	li;
631
632	if (!tm_info.format)
633	{
634		tm_info.format = tm_data.format;
635		if (!tm_info.deformat)
636			tm_info.deformat = tm_info.format[TM_DEFAULT];
637		else if (tm_info.deformat != tm_info.format[TM_DEFAULT])
638			state.format = tm_info.deformat;
639	}
640	li = LCINFO(AST_LC_TIME);
641	if (!li->data)
642		load(li);
643	return tm_info.format;
644}
645