1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1985-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* Phong Vo <kpv@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * time conversion translation support
1N/A */
1N/A
1N/A#include <ast.h>
1N/A#include <cdt.h>
1N/A#include <iconv.h>
1N/A#include <mc.h>
1N/A#include <tm.h>
1N/A#include <ast_nl_types.h>
1N/A
1N/A#include "lclib.h"
1N/A
1N/Astatic struct
1N/A{
1N/A char* format;
1N/A Lc_info_t* locale;
1N/A char null[1];
1N/A} state;
1N/A
1N/A/*
1N/A * this is unix dadgummit
1N/A */
1N/A
1N/Astatic int
1N/Astandardized(Lc_info_t* li, register char** b)
1N/A{
1N/A if ((li->lc->language->flags & (LC_debug|LC_default)) || streq(li->lc->language->code, "en"))
1N/A {
1N/A b[TM_TIME] = "%H:%M:%S";
1N/A b[TM_DATE] = "%m/%d/%y";
1N/A b[TM_DEFAULT] = "%a %b %e %T %Z %Y";
1N/A return 1;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * fix up LC_TIME data after loading
1N/A */
1N/A
1N/Astatic void
1N/Afixup(Lc_info_t* li, register char** b)
1N/A{
1N/A register char** v;
1N/A register char** e;
1N/A register int n;
1N/A
1N/A static int must[] =
1N/A {
1N/A TM_TIME,
1N/A TM_DATE,
1N/A TM_DEFAULT,
1N/A TM_MERIDIAN,
1N/A TM_UT,
1N/A TM_DT,
1N/A TM_SUFFIXES,
1N/A TM_PARTS,
1N/A TM_HOURS,
1N/A TM_DAYS,
1N/A TM_LAST,
1N/A TM_THIS,
1N/A TM_NEXT,
1N/A TM_EXACT,
1N/A TM_NOISE,
1N/A TM_ORDINAL,
1N/A TM_CTIME,
1N/A TM_DATE_1,
1N/A TM_INTERNATIONAL,
1N/A TM_RECENT,
1N/A TM_DISTANT,
1N/A TM_MERIDIAN_TIME,
1N/A TM_ORDINALS,
1N/A TM_FINAL,
1N/A TM_WORK,
1N/A };
1N/A
1N/A standardized(li, b);
1N/A for (v = b, e = b + TM_NFORM; v < e; v++)
1N/A if (!*v)
1N/A *v = state.null;
1N/A for (n = 0; n < elementsof(must); n++)
1N/A if (!*b[must[n]])
1N/A b[must[n]] = tm_data.format[must[n]];
1N/A if (li->lc->flags & LC_default)
1N/A for (n = 0; n < TM_NFORM; n++)
1N/A if (!*b[n])
1N/A b[n] = tm_data.format[n];
1N/A if (strchr(b[TM_UT], '%'))
1N/A {
1N/A tm_info.deformat = b[TM_UT];
1N/A for (n = TM_UT; n < TM_DT; n++)
1N/A b[n] = state.null;
1N/A }
1N/A else
1N/A tm_info.deformat = b[TM_DEFAULT];
1N/A tm_info.format = b;
1N/A if (!(tm_info.deformat = state.format))
1N/A tm_info.deformat = tm_info.format[TM_DEFAULT];
1N/A li->data = (void*)b;
1N/A}
1N/A
1N/A#if _WINIX
1N/A
1N/A#include <ast_windows.h>
1N/A
1N/Atypedef struct Map_s
1N/A{
1N/A LCID native;
1N/A int local;
1N/A} Map_t;
1N/A
1N/Astatic const Map_t map[] =
1N/A{
1N/A LOCALE_S1159, (TM_MERIDIAN+0),
1N/A LOCALE_S2359, (TM_MERIDIAN+1),
1N/A LOCALE_SABBREVDAYNAME1, (TM_DAY_ABBREV+1),
1N/A LOCALE_SABBREVDAYNAME2, (TM_DAY_ABBREV+2),
1N/A LOCALE_SABBREVDAYNAME3, (TM_DAY_ABBREV+3),
1N/A LOCALE_SABBREVDAYNAME4, (TM_DAY_ABBREV+4),
1N/A LOCALE_SABBREVDAYNAME5, (TM_DAY_ABBREV+5),
1N/A LOCALE_SABBREVDAYNAME6, (TM_DAY_ABBREV+6),
1N/A LOCALE_SABBREVDAYNAME7, (TM_DAY_ABBREV+0),
1N/A LOCALE_SABBREVMONTHNAME1, (TM_MONTH_ABBREV+0),
1N/A LOCALE_SABBREVMONTHNAME2, (TM_MONTH_ABBREV+1),
1N/A LOCALE_SABBREVMONTHNAME3, (TM_MONTH_ABBREV+2),
1N/A LOCALE_SABBREVMONTHNAME4, (TM_MONTH_ABBREV+3),
1N/A LOCALE_SABBREVMONTHNAME5, (TM_MONTH_ABBREV+4),
1N/A LOCALE_SABBREVMONTHNAME6, (TM_MONTH_ABBREV+5),
1N/A LOCALE_SABBREVMONTHNAME7, (TM_MONTH_ABBREV+6),
1N/A LOCALE_SABBREVMONTHNAME8, (TM_MONTH_ABBREV+7),
1N/A LOCALE_SABBREVMONTHNAME9, (TM_MONTH_ABBREV+8),
1N/A LOCALE_SABBREVMONTHNAME10, (TM_MONTH_ABBREV+9),
1N/A LOCALE_SABBREVMONTHNAME11, (TM_MONTH_ABBREV+10),
1N/A LOCALE_SABBREVMONTHNAME12, (TM_MONTH_ABBREV+11),
1N/A LOCALE_SDAYNAME1, (TM_DAY+1),
1N/A LOCALE_SDAYNAME2, (TM_DAY+2),
1N/A LOCALE_SDAYNAME3, (TM_DAY+3),
1N/A LOCALE_SDAYNAME4, (TM_DAY+4),
1N/A LOCALE_SDAYNAME5, (TM_DAY+5),
1N/A LOCALE_SDAYNAME6, (TM_DAY+6),
1N/A LOCALE_SDAYNAME7, (TM_DAY+0),
1N/A LOCALE_SMONTHNAME1, (TM_MONTH+0),
1N/A LOCALE_SMONTHNAME2, (TM_MONTH+1),
1N/A LOCALE_SMONTHNAME3, (TM_MONTH+2),
1N/A LOCALE_SMONTHNAME4, (TM_MONTH+3),
1N/A LOCALE_SMONTHNAME5, (TM_MONTH+4),
1N/A LOCALE_SMONTHNAME6, (TM_MONTH+5),
1N/A LOCALE_SMONTHNAME7, (TM_MONTH+6),
1N/A LOCALE_SMONTHNAME8, (TM_MONTH+7),
1N/A LOCALE_SMONTHNAME9, (TM_MONTH+8),
1N/A LOCALE_SMONTHNAME10, (TM_MONTH+9),
1N/A LOCALE_SMONTHNAME11, (TM_MONTH+10),
1N/A LOCALE_SMONTHNAME12, (TM_MONTH+11),
1N/A};
1N/A
1N/A#undef extern
1N/A
1N/A/*
1N/A * convert ms word date spec w to posix strftime format f
1N/A * next char after f returned
1N/A * the caller already made sure f is big enough
1N/A */
1N/A
1N/Astatic char*
1N/Aword2posix(register char* f, register char* w, int alternate)
1N/A{
1N/A register char* r;
1N/A register int c;
1N/A register int p;
1N/A register int n;
1N/A
1N/A while (*w)
1N/A {
1N/A p = 0;
1N/A r = w;
1N/A while (*++w == *r);
1N/A if ((n = w - r) > 3 && alternate)
1N/A n--;
1N/A switch (*r)
1N/A {
1N/A case 'a':
1N/A case 'A':
1N/A if (!strncasecmp(w, "am/pm", 5))
1N/A w += 5;
1N/A else if (!strncasecmp(w, "a/p", 3))
1N/A w += 3;
1N/A c = 'p';
1N/A break;
1N/A case 'd':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A case 2:
1N/A c = 'd';
1N/A break;
1N/A case 3:
1N/A c = 'a';
1N/A break;
1N/A default:
1N/A c = 'A';
1N/A break;
1N/A }
1N/A break;
1N/A case 'h':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A default:
1N/A c = 'I';
1N/A break;
1N/A }
1N/A break;
1N/A case 'H':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A default:
1N/A c = 'H';
1N/A break;
1N/A }
1N/A break;
1N/A case 'M':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A case 2:
1N/A c = 'm';
1N/A break;
1N/A case 3:
1N/A c = 'b';
1N/A break;
1N/A default:
1N/A c = 'B';
1N/A break;
1N/A }
1N/A break;
1N/A case 'm':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A default:
1N/A c = 'M';
1N/A break;
1N/A }
1N/A break;
1N/A case 's':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A default:
1N/A c = 'S';
1N/A break;
1N/A }
1N/A break;
1N/A case 'y':
1N/A switch (n)
1N/A {
1N/A case 1:
1N/A p = '-';
1N/A /*FALLTHROUGH*/
1N/A case 2:
1N/A c = 'y';
1N/A break;
1N/A default:
1N/A c = 'Y';
1N/A break;
1N/A }
1N/A break;
1N/A case '\'':
1N/A if (n & 1)
1N/A for (w = r + 1; *w; *f++ = *w++)
1N/A if (*w == '\'')
1N/A {
1N/A w++;
1N/A break;
1N/A }
1N/A continue;
1N/A case '%':
1N/A while (r < w)
1N/A {
1N/A *f++ = *r++;
1N/A *f++ = *r++;
1N/A }
1N/A continue;
1N/A default:
1N/A while (r < w)
1N/A *f++ = *r++;
1N/A continue;
1N/A }
1N/A *f++ = '%';
1N/A if (p)
1N/A *f++ = '-';
1N/A *f++ = c;
1N/A }
1N/A *f++ = 0;
1N/A return f;
1N/A}
1N/A
1N/A/*
1N/A * load the native LC_TIME data for the current locale
1N/A */
1N/A
1N/Astatic void
1N/Anative_lc_time(Lc_info_t* li)
1N/A{
1N/A register char* s;
1N/A register char* t;
1N/A register char** b;
1N/A register int n;
1N/A register int m;
1N/A register int i;
1N/A LCID lcid;
1N/A int nt;
1N/A int ns;
1N/A int nl;
1N/A int clock_24;
1N/A int leading_0;
1N/A char buf[256];
1N/A
1N/A lcid = li->lc->index;
1N/A nt = 2 * GetLocaleInfo(lcid, LOCALE_STIME, 0, 0) + 7; /* HH:MM:SS */
1N/A ns = 3 * GetLocaleInfo(lcid, LOCALE_SSHORTDATE, 0, 0);
1N/A nl = 3 * GetLocaleInfo(lcid, LOCALE_SLONGDATE, 0, 0);
1N/A n = nt + ns + nl;
1N/A for (i = 0; i < elementsof(map); i++)
1N/A n += GetLocaleInfo(lcid, map[i].native, 0, 0);
1N/A if (!(b = newof(0, char*, TM_NFORM, n)))
1N/A return;
1N/A s = (char*)(b + TM_NFORM);
1N/A for (i = 0; i < elementsof(map); i++)
1N/A {
1N/A if (!(m = GetLocaleInfo(lcid, map[i].native, s, n)))
1N/A goto bad;
1N/A b[map[i].local] = s;
1N/A s += m;
1N/A }
1N/A if (!standardized(li, b))
1N/A {
1N/A /*
1N/A * synthesize TM_TIME format from the ms word template
1N/A */
1N/A
1N/A if (!GetLocaleInfo(lcid, LOCALE_ITIME, buf, sizeof(buf)))
1N/A goto bad;
1N/A clock_24 = atoi(buf);
1N/A if (!GetLocaleInfo(lcid, LOCALE_ITLZERO, buf, sizeof(buf)))
1N/A goto bad;
1N/A leading_0 = atoi(buf);
1N/A if (!GetLocaleInfo(lcid, LOCALE_STIME, buf, sizeof(buf)))
1N/A goto bad;
1N/A b[TM_TIME] = s;
1N/A *s++ = '%';
1N/A if (!leading_0)
1N/A *s++ = '-';
1N/A *s++ = clock_24 ? 'H' : 'I';
1N/A for (t = buf; *s = *t++; s++);
1N/A *s++ = '%';
1N/A if (!leading_0)
1N/A *s++ = '-';
1N/A *s++ = 'M';
1N/A for (t = buf; *s = *t++; s++);
1N/A *s++ = '%';
1N/A if (!leading_0)
1N/A *s++ = '-';
1N/A *s++ = 'S';
1N/A *s++ = 0;
1N/A
1N/A /*
1N/A * synthesize TM_DATE format
1N/A */
1N/A
1N/A if (!GetLocaleInfo(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf)))
1N/A goto bad;
1N/A b[TM_DATE] = s;
1N/A s = word2posix(s, buf, 1);
1N/A
1N/A /*
1N/A * synthesize TM_DEFAULT format
1N/A */
1N/A
1N/A if (!GetLocaleInfo(lcid, LOCALE_SLONGDATE, buf, sizeof(buf)))
1N/A goto bad;
1N/A b[TM_DEFAULT] = s;
1N/A s = word2posix(s, buf, 1);
1N/A strcpy(s - 1, " %X");
1N/A }
1N/A
1N/A /*
1N/A * done
1N/A */
1N/A
1N/A fixup(li, b);
1N/A return;
1N/A bad:
1N/A free(b);
1N/A}
1N/A
1N/A#else
1N/A
1N/A#if _lib_nl_langinfo && _hdr_langinfo
1N/A
1N/A#if _hdr_nl_types
1N/A#include <nl_types.h>
1N/A#endif
1N/A
1N/A#include <langinfo.h>
1N/A
1N/Atypedef struct Map_s
1N/A{
1N/A int native;
1N/A int local;
1N/A} Map_t;
1N/A
1N/Astatic const Map_t map[] =
1N/A{
1N/A AM_STR, (TM_MERIDIAN+0),
1N/A PM_STR, (TM_MERIDIAN+1),
1N/A ABDAY_1, (TM_DAY_ABBREV+0),
1N/A ABDAY_2, (TM_DAY_ABBREV+1),
1N/A ABDAY_3, (TM_DAY_ABBREV+2),
1N/A ABDAY_4, (TM_DAY_ABBREV+3),
1N/A ABDAY_5, (TM_DAY_ABBREV+4),
1N/A ABDAY_6, (TM_DAY_ABBREV+5),
1N/A ABDAY_7, (TM_DAY_ABBREV+6),
1N/A ABMON_1, (TM_MONTH_ABBREV+0),
1N/A ABMON_2, (TM_MONTH_ABBREV+1),
1N/A ABMON_3, (TM_MONTH_ABBREV+2),
1N/A ABMON_4, (TM_MONTH_ABBREV+3),
1N/A ABMON_5, (TM_MONTH_ABBREV+4),
1N/A ABMON_6, (TM_MONTH_ABBREV+5),
1N/A ABMON_7, (TM_MONTH_ABBREV+6),
1N/A ABMON_8, (TM_MONTH_ABBREV+7),
1N/A ABMON_9, (TM_MONTH_ABBREV+8),
1N/A ABMON_10, (TM_MONTH_ABBREV+9),
1N/A ABMON_11, (TM_MONTH_ABBREV+10),
1N/A ABMON_12, (TM_MONTH_ABBREV+11),
1N/A DAY_1, (TM_DAY+0),
1N/A DAY_2, (TM_DAY+1),
1N/A DAY_3, (TM_DAY+2),
1N/A DAY_4, (TM_DAY+3),
1N/A DAY_5, (TM_DAY+4),
1N/A DAY_6, (TM_DAY+5),
1N/A DAY_7, (TM_DAY+6),
1N/A MON_1, (TM_MONTH+0),
1N/A MON_2, (TM_MONTH+1),
1N/A MON_3, (TM_MONTH+2),
1N/A MON_4, (TM_MONTH+3),
1N/A MON_5, (TM_MONTH+4),
1N/A MON_6, (TM_MONTH+5),
1N/A MON_7, (TM_MONTH+6),
1N/A MON_8, (TM_MONTH+7),
1N/A MON_9, (TM_MONTH+8),
1N/A MON_10, (TM_MONTH+9),
1N/A MON_11, (TM_MONTH+10),
1N/A MON_12, (TM_MONTH+11),
1N/A#ifdef _DATE_FMT
1N/A _DATE_FMT, TM_DEFAULT,
1N/A#else
1N/A D_T_FMT, TM_DEFAULT,
1N/A#endif
1N/A D_FMT, TM_DATE,
1N/A T_FMT, TM_TIME,
1N/A#ifdef ERA
1N/A ERA, TM_ERA,
1N/A ERA_D_T_FMT, TM_ERA_DEFAULT,
1N/A ERA_D_FMT, TM_ERA_DATE,
1N/A ERA_T_FMT, TM_ERA_TIME,
1N/A#endif
1N/A#ifdef ALT_DIGITS
1N/A ALT_DIGITS, TM_DIGITS,
1N/A#endif
1N/A};
1N/A
1N/Astatic void
1N/Anative_lc_time(Lc_info_t* li)
1N/A{
1N/A register char* s;
1N/A register char* t;
1N/A register char** b;
1N/A register int n;
1N/A register int i;
1N/A
1N/A n = 0;
1N/A for (i = 0; i < elementsof(map); i++)
1N/A {
1N/A if (!(t = nl_langinfo(map[i].native)))
1N/A t = tm_data.format[map[i].local];
1N/A n += strlen(t) + 1;
1N/A }
1N/A if (!(b = newof(0, char*, TM_NFORM, n)))
1N/A return;
1N/A s = (char*)(b + TM_NFORM);
1N/A for (i = 0; i < elementsof(map); i++)
1N/A {
1N/A b[map[i].local] = s;
1N/A if (!(t = nl_langinfo(map[i].native)))
1N/A t = tm_data.format[map[i].local];
1N/A while (*s++ = *t++);
1N/A }
1N/A fixup(li, b);
1N/A}
1N/A
1N/A#else
1N/A
1N/A#define native_lc_time(li) ((li->data=(void*)(tm_info.format=tm_data.format)),(tm_info.deformat=tm_info.format[TM_DEFAULT]))
1N/A
1N/A#endif
1N/A
1N/A#endif
1N/A
1N/A/*
1N/A * load the LC_TIME data for the current locale
1N/A */
1N/A
1N/Astatic void
1N/Aload(Lc_info_t* li)
1N/A{
1N/A register char* s;
1N/A register char** b;
1N/A register char** v;
1N/A register char** e;
1N/A unsigned char* u;
1N/A ssize_t n;
1N/A iconv_t cvt;
1N/A Sfio_t* sp;
1N/A Sfio_t* tp;
1N/A char path[PATH_MAX];
1N/A
1N/A if (b = (char**)li->data)
1N/A {
1N/A tm_info.format = b;
1N/A if (!(tm_info.deformat = state.format))
1N/A tm_info.deformat = tm_info.format[TM_DEFAULT];
1N/A return;
1N/A }
1N/A tm_info.format = tm_data.format;
1N/A if (!(tm_info.deformat = state.format))
1N/A tm_info.deformat = tm_info.format[TM_DEFAULT];
1N/A if (mcfind(NiL, NiL, LC_TIME, 0, path, sizeof(path)) && (sp = sfopen(NiL, path, "r")))
1N/A {
1N/A n = sfsize(sp);
1N/A tp = 0;
1N/A if (u = (unsigned char*)sfreserve(sp, 3, 1))
1N/A {
1N/A if (u[0] == 0xef && u[1] == 0xbb && u[2] == 0xbf && (cvt = iconv_open("", "utf")) != (iconv_t)(-1))
1N/A {
1N/A if (tp = sfstropen())
1N/A {
1N/A sfread(sp, u, 3);
1N/A n = iconv_move(cvt, sp, tp, SF_UNBOUND, NiL);
1N/A }
1N/A iconv_close(cvt);
1N/A }
1N/A if (!tp)
1N/A sfread(sp, u, 0);
1N/A }
1N/A if (b = newof(0, char*, TM_NFORM, n + 2))
1N/A {
1N/A v = b;
1N/A e = b + TM_NFORM;
1N/A s = (char*)e;
1N/A if (tp && memcpy(s, sfstrbase(tp), n) || !tp && sfread(sp, s, n) == n)
1N/A {
1N/A s[n] = '\n';
1N/A while (v < e)
1N/A {
1N/A *v++ = s;
1N/A if (!(s = strchr(s, '\n')))
1N/A break;
1N/A *s++ = 0;
1N/A }
1N/A fixup(li, b);
1N/A }
1N/A else
1N/A free(b);
1N/A }
1N/A if (tp)
1N/A sfclose(tp);
1N/A sfclose(sp);
1N/A }
1N/A else
1N/A native_lc_time(li);
1N/A}
1N/A
1N/A/*
1N/A * check that tm_info.format matches the current locale
1N/A */
1N/A
1N/Achar**
1N/Atmlocale(void)
1N/A{
1N/A Lc_info_t* li;
1N/A
1N/A if (!tm_info.format)
1N/A {
1N/A tm_info.format = tm_data.format;
1N/A if (!tm_info.deformat)
1N/A tm_info.deformat = tm_info.format[TM_DEFAULT];
1N/A else if (tm_info.deformat != tm_info.format[TM_DEFAULT])
1N/A state.format = tm_info.deformat;
1N/A }
1N/A li = LCINFO(AST_LC_TIME);
1N/A if (!li->data)
1N/A load(li);
1N/A return tm_info.format;
1N/A}