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