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