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 
39 static 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 
50 static int
standardized(Lc_info_t * li,register char ** b)51 standardized(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 
67 static void
fixup(Lc_info_t * li,register char ** b)68 fixup(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 
132 typedef struct Map_s
133 {
134 	LCID		native;
135 	int		local;
136 } Map_t;
137 
138 static 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 
190 static char*
word2posix(register char * f,register char * w,int alternate)191 word2posix(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 
341 static void
native_lc_time(Lc_info_t * li)342 native_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 
446 typedef struct Map_s
447 {
448 	int		native;
449 	int		local;
450 } Map_t;
451 
452 static 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 
512 static void
native_lc_time(Lc_info_t * li)513 native_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 
553 static void
load(Lc_info_t * li)554 load(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 
627 char**
tmlocale(void)628 tmlocale(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