timelocal.c revision 51186
128021Sjoerg/*- 228021Sjoerg * Copyright (c) 1997 FreeBSD Inc. 328021Sjoerg * All rights reserved. 428021Sjoerg * 528021Sjoerg * Redistribution and use in source and binary forms, with or without 628021Sjoerg * modification, are permitted provided that the following conditions 728021Sjoerg * are met: 828021Sjoerg * 1. Redistributions of source code must retain the above copyright 928021Sjoerg * notice, this list of conditions and the following disclaimer. 1028021Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1128021Sjoerg * notice, this list of conditions and the following disclaimer in the 1228021Sjoerg * documentation and/or other materials provided with the distribution. 1328021Sjoerg * 1428021Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1528021Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1628021Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1728021Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1828021Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1928021Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2028021Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2128021Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2228021Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2328021Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2428021Sjoerg * SUCH DAMAGE. 2528021Sjoerg * 2650476Speter * $FreeBSD: head/lib/libc/stdtime/timelocal.c 51186 1999-09-11 21:35:21Z dt $ 2728021Sjoerg */ 2828021Sjoerg 2928021Sjoerg#include <sys/types.h> 3028021Sjoerg#include <sys/stat.h> 3128021Sjoerg#include <sys/syslimits.h> 3228021Sjoerg#include <fcntl.h> 3328021Sjoerg#include <locale.h> 3451186Sdt#include <stddef.h> 3528021Sjoerg#include <stdlib.h> 3628021Sjoerg#include <string.h> 3728021Sjoerg#include "setlocale.h" 3828021Sjoerg#include "timelocal.h" 3928021Sjoerg 4051186Sdtstatic int split_lines(char *, const char *); 4151186Sdtstatic void set_from_buf(const char *, int); 4251186Sdt 4328021Sjoergstruct lc_time_T _time_localebuf; 4428021Sjoergint _time_using_locale; 4528021Sjoerg 4651186Sdt#define LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *)) 4751186Sdt#define LCTIME_SIZE_1 \ 4851186Sdt (offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *)) 4951186Sdt 5028021Sjoergconst struct lc_time_T _C_time_locale = { 5128021Sjoerg { 5228021Sjoerg "Jan", "Feb", "Mar", "Apr", "May", "Jun", 5328021Sjoerg "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 5428021Sjoerg }, { 5528021Sjoerg "January", "February", "March", "April", "May", "June", 5628021Sjoerg "July", "August", "September", "October", "November", "December" 5728021Sjoerg }, { 5828021Sjoerg "Sun", "Mon", "Tue", "Wed", 5928021Sjoerg "Thu", "Fri", "Sat" 6028021Sjoerg }, { 6128021Sjoerg "Sunday", "Monday", "Tuesday", "Wednesday", 6228021Sjoerg "Thursday", "Friday", "Saturday" 6328021Sjoerg }, 6428021Sjoerg 6528021Sjoerg /* X_fmt */ 6628021Sjoerg "%H:%M:%S", 6728021Sjoerg 6828021Sjoerg /* 6928021Sjoerg ** x_fmt 7028021Sjoerg ** Since the C language standard calls for 7128021Sjoerg ** "date, using locale's date format," anything goes. 7228021Sjoerg ** Using just numbers (as here) makes Quakers happier; 7328021Sjoerg ** it's also compatible with SVR4. 7428021Sjoerg */ 7528021Sjoerg "%m/%d/%y", 7628021Sjoerg 7728021Sjoerg /* 7828021Sjoerg ** c_fmt (ctime-compatible) 7928021Sjoerg ** Note that 8028021Sjoerg ** "%a %b %d %H:%M:%S %Y" 8128021Sjoerg ** is used by Solaris 2.3. 8228021Sjoerg */ 8328021Sjoerg "%a %b %e %X %Y", 8428021Sjoerg 8528021Sjoerg /* am */ 8628021Sjoerg "AM", 8728021Sjoerg 8828021Sjoerg /* pm */ 8928021Sjoerg "PM", 9028021Sjoerg 9128021Sjoerg /* date_fmt */ 9251186Sdt "%a %b %e %X %Z %Y", 9351186Sdt 9451186Sdt { 9551186Sdt "January", "February", "March", "April", "May", "June", 9651186Sdt "July", "August", "September", "October", "November", "December" 9751186Sdt } 9828021Sjoerg}; 9928021Sjoerg 10028021Sjoerg 10128021Sjoergint 10228021Sjoerg__time_load_locale(const char *name) 10328021Sjoerg{ 10428021Sjoerg static char * locale_buf; 10528021Sjoerg static char locale_buf_C[] = "C"; 10651186Sdt static int num_lines; 10728021Sjoerg 10828021Sjoerg int fd; 10928021Sjoerg char * lbuf; 11028021Sjoerg char * p; 11128021Sjoerg const char * plim; 11228021Sjoerg char filename[PATH_MAX]; 11328021Sjoerg struct stat st; 11428021Sjoerg size_t namesize; 11528021Sjoerg size_t bufsize; 11628021Sjoerg int save_using_locale; 11728021Sjoerg 11828021Sjoerg save_using_locale = _time_using_locale; 11928021Sjoerg _time_using_locale = 0; 12028021Sjoerg 12128021Sjoerg if (name == NULL) 12228021Sjoerg goto no_locale; 12328021Sjoerg 12428021Sjoerg if (!strcmp(name, "C") || !strcmp(name, "POSIX")) 12528021Sjoerg return 0; 12628021Sjoerg 12728021Sjoerg /* 12828021Sjoerg ** If the locale name is the same as our cache, use the cache. 12928021Sjoerg */ 13028021Sjoerg lbuf = locale_buf; 13128021Sjoerg if (lbuf != NULL && strcmp(name, lbuf) == 0) { 13251186Sdt set_from_buf(lbuf, num_lines); 13328021Sjoerg _time_using_locale = 1; 13428021Sjoerg return 0; 13528021Sjoerg } 13628021Sjoerg /* 13728021Sjoerg ** Slurp the locale file into the cache. 13828021Sjoerg */ 13928021Sjoerg namesize = strlen(name) + 1; 14028021Sjoerg 14128021Sjoerg if (!_PathLocale) 14228021Sjoerg goto no_locale; 14328021Sjoerg /* Range checking not needed, 'name' size is limited */ 14428021Sjoerg strcpy(filename, _PathLocale); 14528021Sjoerg strcat(filename, "/"); 14628021Sjoerg strcat(filename, name); 14728021Sjoerg strcat(filename, "/LC_TIME"); 14828021Sjoerg fd = open(filename, O_RDONLY); 14928021Sjoerg if (fd < 0) 15028021Sjoerg goto no_locale; 15128021Sjoerg if (fstat(fd, &st) != 0) 15228021Sjoerg goto bad_locale; 15328021Sjoerg if (st.st_size <= 0) 15428021Sjoerg goto bad_locale; 15528021Sjoerg bufsize = namesize + st.st_size; 15628021Sjoerg locale_buf = NULL; 15728021Sjoerg lbuf = (lbuf == NULL || lbuf == locale_buf_C) ? 15839327Simp malloc(bufsize) : reallocf(lbuf, bufsize); 15928021Sjoerg if (lbuf == NULL) 16028021Sjoerg goto bad_locale; 16128021Sjoerg (void) strcpy(lbuf, name); 16228021Sjoerg p = lbuf + namesize; 16328021Sjoerg plim = p + st.st_size; 16428021Sjoerg if (read(fd, p, (size_t) st.st_size) != st.st_size) 16528021Sjoerg goto bad_lbuf; 16628021Sjoerg if (close(fd) != 0) 16728021Sjoerg goto bad_lbuf; 16828021Sjoerg /* 16928021Sjoerg ** Parse the locale file into localebuf. 17028021Sjoerg */ 17128021Sjoerg if (plim[-1] != '\n') 17228021Sjoerg goto bad_lbuf; 17351186Sdt num_lines = split_lines(p, plim); 17451186Sdt if (num_lines >= LCTIME_SIZE_FULL) 17551186Sdt num_lines = LCTIME_SIZE_FULL; 17651186Sdt else if (num_lines >= LCTIME_SIZE_1) 17751186Sdt num_lines = LCTIME_SIZE_1; 17851186Sdt else 17951186Sdt goto reset_locale; 18051186Sdt set_from_buf(lbuf, num_lines); 18128021Sjoerg /* 18228021Sjoerg ** Record the successful parse in the cache. 18328021Sjoerg */ 18428021Sjoerg locale_buf = lbuf; 18528021Sjoerg 18628021Sjoerg _time_using_locale = 1; 18728021Sjoerg return 0; 18828021Sjoerg 18928021Sjoergreset_locale: 19028021Sjoerg /* 19128021Sjoerg * XXX - This may not be the correct thing to do in this case. 19228021Sjoerg * setlocale() assumes that we left the old locale alone. 19328021Sjoerg */ 19428021Sjoerg locale_buf = locale_buf_C; 19528021Sjoerg _time_localebuf = _C_time_locale; 19628021Sjoerg save_using_locale = 0; 19728021Sjoergbad_lbuf: 19828021Sjoerg free(lbuf); 19928021Sjoergbad_locale: 20028021Sjoerg (void) close(fd); 20128021Sjoergno_locale: 20228021Sjoerg _time_using_locale = save_using_locale; 20328021Sjoerg return -1; 20428021Sjoerg} 20551186Sdt 20651186Sdtstatic int 20751186Sdtsplit_lines(char *p, const char *plim) 20851186Sdt{ 20951186Sdt int i; 21051186Sdt 21151186Sdt for (i = 0; p < plim; i++) { 21251186Sdt p = strchr(p, '\n'); 21351186Sdt *p++ = '\0'; 21451186Sdt } 21551186Sdt return i; 21651186Sdt} 21751186Sdt 21851186Sdtstatic void 21951186Sdtset_from_buf(const char *p, int num_lines) 22051186Sdt{ 22151186Sdt const char **ap; 22251186Sdt int i; 22351186Sdt 22451186Sdt for (ap = (const char **) &_time_localebuf, i = 0; 22551186Sdt i < num_lines; ++ap, ++i) 22651186Sdt *ap = p += strlen(p) + 1; 22751186Sdt if (num_lines == LCTIME_SIZE_FULL) 22851186Sdt return; 22951186Sdt for (i = 0; i < 12; i++) 23051186Sdt _time_localebuf.alt_month[i] = _time_localebuf.month[i]; 23151186Sdt} 232