diff options
author | wollman <wollman@FreeBSD.org> | 1995-08-04 18:43:01 +0000 |
---|---|---|
committer | wollman <wollman@FreeBSD.org> | 1995-08-04 18:43:01 +0000 |
commit | 99cef2aaa79c69732233a84462345b30bef389fc (patch) | |
tree | 8641f513c554873ed1c7b7aaa44b82a99dd1fe16 /lib/libc/stdtime | |
parent | 98a78b7aa4b5b9f0897522f5c0d101215abb82a0 (diff) | |
download | FreeBSD-src-99cef2aaa79c69732233a84462345b30bef389fc.zip FreeBSD-src-99cef2aaa79c69732233a84462345b30bef389fc.tar.gz |
Implement locale-sensitive strftime () from ADO (heavily modified
by me). This probably loses for multibyte characters, but I have no
way of telling. I'll let ache decide whether to add this support to
startup_setlocale. Note that for this to make any sense at all, the
symlinks in /usr/share/locale must go. (For the moment, this doesn't
make any difference since there are no locales supplied.)
Obtained from: Arthur David Olson <ado@elsie.nci.nih.gov>
Diffstat (limited to 'lib/libc/stdtime')
-rw-r--r-- | lib/libc/stdtime/strftime.c | 344 |
1 files changed, 240 insertions, 104 deletions
diff --git a/lib/libc/stdtime/strftime.c b/lib/libc/stdtime/strftime.c index 1c50d65..a300ed6 100644 --- a/lib/libc/stdtime/strftime.c +++ b/lib/libc/stdtime/strftime.c @@ -1,16 +1,3 @@ -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)strftime.c 7.19"; -/* -** Based on the UCB version with the ID appearing below. -** This is ANSIish only when time is treated identically in all locales and -** when "multibyte character == plain character". -*/ -#endif /* !defined NOID */ -#endif /* !defined lint */ - -#include "private.h" - /* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. @@ -28,49 +15,120 @@ static char elsieid[] = "@(#)strftime.c 7.19"; * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ +#ifdef LIBC_RCS +static const char rcsid[] = + "$Id$"; +#endif + +#ifndef lint +#ifndef NOID +static const char elsieid[] = "@(#)strftime.c 7.38"; +/* +** Based on the UCB version with the ID appearing below. +** This is ANSIish only when "multibyte character == plain character". +*/ +#endif /* !defined NOID */ +#endif /* !defined lint */ + +#include "private.h" + #ifndef LIBC_SCCS #ifndef lint -static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; +static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; #endif /* !defined lint */ #endif /* !defined LIBC_SCCS */ #include "tzfile.h" +#include <fcntl.h> +#include <locale.h> +#include <rune.h> /* for _PATH_LOCALE */ +#include <sys/stat.h> -static const char afmt[][4] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; -static const char Afmt[][10] = { - "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", - "Saturday" -}; -static const char bfmt[][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", - "Oct", "Nov", "Dec" +#define LOCALE_HOME _PATH_LOCALE + +struct lc_time_T { + const char * mon[12]; + const char * month[12]; + const char * wday[7]; + const char * weekday[7]; + const char * X_fmt; + const char * x_fmt; + const char * c_fmt; + const char * am; + const char * pm; + const char * date_fmt; }; -static const char Bfmt[][10] = { - "January", "February", "March", "April", "May", "June", "July", - "August", "September", "October", "November", "December" + +static struct lc_time_T localebuf; +static struct lc_time_T * _loc P((void)); +static int using_locale; + +#define Locale (using_locale ? &localebuf : &C_time_locale) + +static const struct lc_time_T C_time_locale = { + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }, { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }, { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" + }, { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }, + + /* X_fmt */ + "%H:%M:%S", + + /* + ** x_fmt + ** Since the C language standard calls for + ** "date, using locale's date format," anything goes. + ** Using just numbers (as here) makes Quakers happier; + ** it's also compatible with SVR4. + */ + "%m/%d/%y", + + /* + ** c_fmt + ** Note that + ** "%a %b %d %H:%M:%S %Y" + ** is used by Solaris 2.3. + */ + "%D %X", /* %m/%d/%y %H:%M:%S */ + + /* am */ + "AM", + + /* pm */ + "PM", + + /* date_fmt */ + "%a %b %e %H:%M:%S %Z %Y" }; -static char *_add P((const char *, char *, const char *)); -static char *_conv P((int, const char *, char *, const char *)); -static char *_fmt P((const char *, const struct tm *, char *, const char *)); -static char *_secs P((const struct tm *, char *, const char *)); +static char * _add P((const char *, char *, const char *)); +static char * _conv P((int, const char *, char *, const char *)); +static char * _fmt P((const char *, const struct tm *, char *, const char *)); size_t strftime P((char *, size_t, const char *, const struct tm *)); -extern char *tzname[]; +extern char * tzname[]; size_t strftime(s, maxsize, format, t) - char *s; - size_t maxsize; - const char *format; - const struct tm *t; + char *const s; + const size_t maxsize; + const char *const format; + const struct tm *const t; { char *p; - p = _fmt(format, t, s, s + maxsize); + tzset(); + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize); if (p == s + maxsize) return 0; *p = '\0'; @@ -80,36 +138,37 @@ strftime(s, maxsize, format, t) static char * _fmt(format, t, pt, ptlim) const char *format; - const struct tm *t; + const struct tm *const t; char *pt; - const char *ptlim; + const char *const ptlim; { - for (; *format; ++format) { + for ( ; *format; ++format) { if (*format == '%') { label: - switch(*++format) { + switch (*++format) { case '\0': --format; break; case 'A': pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? - "?" : Afmt[t->tm_wday], pt, ptlim); + "?" : Locale->weekday[t->tm_wday], + pt, ptlim); continue; case 'a': pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? - "?" : afmt[t->tm_wday], pt, ptlim); + "?" : Locale->wday[t->tm_wday], + pt, ptlim); continue; case 'B': pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? - "?" : Bfmt[t->tm_mon], pt, ptlim); + "?" : Locale->month[t->tm_mon], + pt, ptlim); continue; case 'b': case 'h': pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? - "?" : bfmt[t->tm_mon], pt, ptlim); - continue; - case 'c': - pt = _fmt("%D %X", t, pt, ptlim); + "?" : Locale->mon[t->tm_mon], + pt, ptlim); continue; case 'C': /* @@ -122,27 +181,10 @@ label: pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, "%02d", pt, ptlim); continue; - case 'D': - pt = _fmt("%m/%d/%y", t, pt, ptlim); + case 'c': + pt = _fmt(Locale->c_fmt, t, pt, ptlim); continue; - case 'x': - /* - ** Version 3.0 of strftime from Arnold Robbins - ** (arnold@skeeve.atl.ga.us) does the - ** equivalent of... - ** _fmt("%a %b %e %Y"); - ** ...for %x; since the X3J11 C language - ** standard calls for "date, using locale's - ** date format," anything goes. Using just - ** numbers (as here) makes Quakers happier. - ** Word from Paul Eggert (eggert@twinsun.com) - ** is that %Y-%m-%d is the ISO standard date - ** format, specified in ISO 2014 and later - ** ISO 8601:1988, with a summary available in - ** pub/doc/ISO/english/ISO8601.ps.Z on - ** ftp.uni-erlangen.de. - ** (ado, 5/30/93) - */ + case 'D': pt = _fmt("%m/%d/%y", t, pt, ptlim); continue; case 'd': @@ -221,7 +263,9 @@ label: pt = _add("\n", pt, ptlim); continue; case 'p': - pt = _add(t->tm_hour >= 12 ? "PM" : "AM", + pt = _add((t->tm_hour >= 12) ? + Locale->pm : + Locale->am, pt, ptlim); continue; case 'R': @@ -233,11 +277,7 @@ label: case 'S': pt = _conv(t->tm_sec, "%02d", pt, ptlim); continue; - case 's': - pt = _secs(t, pt, ptlim); - continue; case 'T': - case 'X': pt = _fmt("%H:%M:%S", t, pt, ptlim); continue; case 't': @@ -278,11 +318,12 @@ label: ** 1 falls on a Thursday, are December 29-31 ** of the PREVIOUS year part of week 1??? ** (ado 5/24/93) - ** + */ + /* ** You are understood not to expect this. */ { - int i; + int i; i = (t->tm_yday + 10 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7; @@ -297,7 +338,7 @@ label: ** Fri Jan 1: 53 ** Sun Jan 1: 52 ** Sat Jan 1: 53 if previous - ** year a leap + ** year a leap ** year, else 52 */ if (i == TM_FRIDAY) @@ -336,6 +377,12 @@ label: case 'w': pt = _conv(t->tm_wday, "%d", pt, ptlim); continue; + case 'X': + pt = _fmt(Locale->X_fmt, t, pt, ptlim); + continue; + case 'x': + pt = _fmt(Locale->x_fmt, t, pt, ptlim); + continue; case 'y': pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, "%02d", pt, ptlim); @@ -345,16 +392,17 @@ label: pt, ptlim); continue; case 'Z': -#ifdef TM_ZONE - if (t->TM_ZONE) - pt = _add(t->TM_ZONE, pt, ptlim); + if (t->tm_zone != NULL) + pt = _add(t->tm_zone, pt, ptlim); else -#endif /* defined TM_ZONE */ if (t->tm_isdst == 0 || t->tm_isdst == 1) { pt = _add(tzname[t->tm_isdst], pt, ptlim); } else pt = _add("?", pt, ptlim); continue; + case '+': + pt = _fmt(Locale->date_fmt, t, pt, ptlim); + continue; case '%': /* * X311J/88-090 (4.12.3.5): if conversion char is @@ -374,42 +422,130 @@ label: static char * _conv(n, format, pt, ptlim) - int n; - const char *format; - char *pt; - const char *ptlim; + const int n; + const char *const format; + char *const pt; + const char *const ptlim; { - char buf[INT_STRLEN_MAXIMUM(int) + 1]; + char buf[INT_STRLEN_MAXIMUM(int) + 1]; (void) sprintf(buf, format, n); return _add(buf, pt, ptlim); } static char * -_secs(t, pt, ptlim) - const struct tm *t; - char *pt; - const char *ptlim; -{ - static char buf[INT_STRLEN_MAXIMUM(int) + 1]; - register time_t s; - register char *p; - struct tm tmp; - - /* Make a copy, mktime(3) modifies the tm struct. */ - tmp = *t; - s = mktime(&tmp); - (void) sprintf(buf, "%d", s); - return(_add(buf, pt, ptlim)); -} - -static char * _add(str, pt, ptlim) const char *str; char *pt; - const char *ptlim; + const char *const ptlim; { while (pt < ptlim && (*pt = *str++) != '\0') ++pt; return pt; } + +extern char *_PathLocale; + +int +__time_load_locale(const char *name) +{ + static const char lc_time[] = "LC_TIME"; + static char * locale_buf; + static char locale_buf_C[] = "C"; + + int fd; + char * lbuf; + char * p; + const char ** ap; + const char * plim; + char filename[FILENAME_MAX]; + struct stat st; + size_t namesize; + size_t bufsize; + + using_locale = 0; + + if (!strcmp(name, "C") || !strcmp(name, "POSIX")) + return 0; + + if (name == NULL || *name == '\0') { + goto no_locale; + } + /* + ** If the locale name is the same as our cache, use the cache. + */ + lbuf = locale_buf; + if (lbuf != NULL && strcmp(name, lbuf) == 0) { + p = lbuf; + for (ap = (const char **) &localebuf; + ap < (const char **) (&localebuf + 1); + ++ap) + *ap = p += strlen(p) + 1; + using_locale = 1; + return 0; + } + /* + ** Slurp the locale file into the cache. + */ + namesize = strlen(name) + 1; + + snprintf(filename, sizeof filename, + "%s/%s/%s", + _PathLocale, name, lc_time); + fd = open(filename, O_RDONLY); + if (fd < 0) { + goto no_locale; + } + if (fstat(fd, &st) != 0) + goto bad_locale; + if (st.st_size <= 0) + goto bad_locale; + bufsize = namesize + st.st_size; + locale_buf = NULL; + lbuf = (lbuf == NULL || lbuf == locale_buf_C) ? + malloc(bufsize) : realloc(lbuf, bufsize); + if (lbuf == NULL) + goto bad_locale; + (void) strcpy(lbuf, name); + p = lbuf + namesize; + plim = p + st.st_size; + if (read(fd, p, (size_t) st.st_size) != st.st_size) + goto bad_lbuf; + if (close(fd) != 0) + goto bad_lbuf; + /* + ** Parse the locale file into localebuf. + */ + if (plim[-1] != '\n') + goto bad_lbuf; + for (ap = (const char **) &localebuf; + ap < (const char **) (&localebuf + 1); + ++ap) { + if (p == plim) + goto bad_lbuf; + *ap = p; + while (*p != '\n') + ++p; + *p++ = '\0'; + } + /* + ** Record the successful parse in the cache. + */ + locale_buf = lbuf; + + using_locale = 1; + return 0; + +bad_lbuf: + free(lbuf); +bad_locale: + (void) close(fd); +no_locale: + /* + * XXX - This may not be the correct thing to do in this case. + * setlocale() assumes that we left the old locale alone. + */ + locale_buf = locale_buf_C; + localebuf = C_time_locale; + return -1; +} |