timelocal.c revision 53960
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 53960 1999-11-30 19:24:07Z ache $ 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 *)) 4953940Sache#define LCTIME_SIZE_2 \ 5053960Sache (offsetof(struct lc_time_T, Ef_fmt) / sizeof(char *)) 5151186Sdt 5228021Sjoergconst struct lc_time_T _C_time_locale = { 5328021Sjoerg { 5428021Sjoerg "Jan", "Feb", "Mar", "Apr", "May", "Jun", 5528021Sjoerg "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 5628021Sjoerg }, { 5728021Sjoerg "January", "February", "March", "April", "May", "June", 5828021Sjoerg "July", "August", "September", "October", "November", "December" 5928021Sjoerg }, { 6028021Sjoerg "Sun", "Mon", "Tue", "Wed", 6128021Sjoerg "Thu", "Fri", "Sat" 6228021Sjoerg }, { 6328021Sjoerg "Sunday", "Monday", "Tuesday", "Wednesday", 6428021Sjoerg "Thursday", "Friday", "Saturday" 6528021Sjoerg }, 6628021Sjoerg 6728021Sjoerg /* X_fmt */ 6828021Sjoerg "%H:%M:%S", 6928021Sjoerg 7028021Sjoerg /* 7128021Sjoerg ** x_fmt 7228021Sjoerg ** Since the C language standard calls for 7328021Sjoerg ** "date, using locale's date format," anything goes. 7428021Sjoerg ** Using just numbers (as here) makes Quakers happier; 7528021Sjoerg ** it's also compatible with SVR4. 7628021Sjoerg */ 7728021Sjoerg "%m/%d/%y", 7828021Sjoerg 7928021Sjoerg /* 8028021Sjoerg ** c_fmt (ctime-compatible) 8128021Sjoerg ** Note that 8228021Sjoerg ** "%a %b %d %H:%M:%S %Y" 8328021Sjoerg ** is used by Solaris 2.3. 8428021Sjoerg */ 8553960Sache "%a %Ef %X %Y", 8628021Sjoerg 8728021Sjoerg /* am */ 8828021Sjoerg "AM", 8928021Sjoerg 9028021Sjoerg /* pm */ 9128021Sjoerg "PM", 9228021Sjoerg 9328021Sjoerg /* date_fmt */ 9453960Sache "%a %Ef %X %Z %Y", 9551186Sdt 9651186Sdt { 9751186Sdt "January", "February", "March", "April", "May", "June", 9851186Sdt "July", "August", "September", "October", "November", "December" 9953940Sache }, 10053940Sache 10153960Sache /* Ef_fmt 10253960Sache ** To determine short months / day order 10353940Sache */ 10453960Sache "%b %e", 10553960Sache 10653960Sache /* EF_fmt 10753960Sache ** To determine long months / day order 10853960Sache */ 10953960Sache "%B %e" 11028021Sjoerg}; 11128021Sjoerg 11228021Sjoerg 11328021Sjoergint 11428021Sjoerg__time_load_locale(const char *name) 11528021Sjoerg{ 11628021Sjoerg static char * locale_buf; 11728021Sjoerg static char locale_buf_C[] = "C"; 11851186Sdt static int num_lines; 11928021Sjoerg 12028021Sjoerg int fd; 12128021Sjoerg char * lbuf; 12228021Sjoerg char * p; 12328021Sjoerg const char * plim; 12428021Sjoerg char filename[PATH_MAX]; 12528021Sjoerg struct stat st; 12628021Sjoerg size_t namesize; 12728021Sjoerg size_t bufsize; 12828021Sjoerg int save_using_locale; 12928021Sjoerg 13028021Sjoerg save_using_locale = _time_using_locale; 13128021Sjoerg _time_using_locale = 0; 13228021Sjoerg 13328021Sjoerg if (name == NULL) 13428021Sjoerg goto no_locale; 13528021Sjoerg 13628021Sjoerg if (!strcmp(name, "C") || !strcmp(name, "POSIX")) 13728021Sjoerg return 0; 13828021Sjoerg 13928021Sjoerg /* 14028021Sjoerg ** If the locale name is the same as our cache, use the cache. 14128021Sjoerg */ 14228021Sjoerg lbuf = locale_buf; 14328021Sjoerg if (lbuf != NULL && strcmp(name, lbuf) == 0) { 14451186Sdt set_from_buf(lbuf, num_lines); 14528021Sjoerg _time_using_locale = 1; 14628021Sjoerg return 0; 14728021Sjoerg } 14828021Sjoerg /* 14928021Sjoerg ** Slurp the locale file into the cache. 15028021Sjoerg */ 15128021Sjoerg namesize = strlen(name) + 1; 15228021Sjoerg 15328021Sjoerg if (!_PathLocale) 15428021Sjoerg goto no_locale; 15528021Sjoerg /* Range checking not needed, 'name' size is limited */ 15628021Sjoerg strcpy(filename, _PathLocale); 15728021Sjoerg strcat(filename, "/"); 15828021Sjoerg strcat(filename, name); 15928021Sjoerg strcat(filename, "/LC_TIME"); 16028021Sjoerg fd = open(filename, O_RDONLY); 16128021Sjoerg if (fd < 0) 16228021Sjoerg goto no_locale; 16328021Sjoerg if (fstat(fd, &st) != 0) 16428021Sjoerg goto bad_locale; 16528021Sjoerg if (st.st_size <= 0) 16628021Sjoerg goto bad_locale; 16728021Sjoerg bufsize = namesize + st.st_size; 16828021Sjoerg locale_buf = NULL; 16928021Sjoerg lbuf = (lbuf == NULL || lbuf == locale_buf_C) ? 17039327Simp malloc(bufsize) : reallocf(lbuf, bufsize); 17128021Sjoerg if (lbuf == NULL) 17228021Sjoerg goto bad_locale; 17328021Sjoerg (void) strcpy(lbuf, name); 17428021Sjoerg p = lbuf + namesize; 17528021Sjoerg plim = p + st.st_size; 17628021Sjoerg if (read(fd, p, (size_t) st.st_size) != st.st_size) 17728021Sjoerg goto bad_lbuf; 17828021Sjoerg if (close(fd) != 0) 17928021Sjoerg goto bad_lbuf; 18028021Sjoerg /* 18128021Sjoerg ** Parse the locale file into localebuf. 18228021Sjoerg */ 18328021Sjoerg if (plim[-1] != '\n') 18428021Sjoerg goto bad_lbuf; 18551186Sdt num_lines = split_lines(p, plim); 18651186Sdt if (num_lines >= LCTIME_SIZE_FULL) 18751186Sdt num_lines = LCTIME_SIZE_FULL; 18853940Sache else if (num_lines >= LCTIME_SIZE_2) 18953940Sache num_lines = LCTIME_SIZE_2; 19051186Sdt else if (num_lines >= LCTIME_SIZE_1) 19151186Sdt num_lines = LCTIME_SIZE_1; 19251186Sdt else 19351186Sdt goto reset_locale; 19451186Sdt set_from_buf(lbuf, num_lines); 19528021Sjoerg /* 19628021Sjoerg ** Record the successful parse in the cache. 19728021Sjoerg */ 19828021Sjoerg locale_buf = lbuf; 19928021Sjoerg 20028021Sjoerg _time_using_locale = 1; 20128021Sjoerg return 0; 20228021Sjoerg 20328021Sjoergreset_locale: 20428021Sjoerg /* 20528021Sjoerg * XXX - This may not be the correct thing to do in this case. 20628021Sjoerg * setlocale() assumes that we left the old locale alone. 20728021Sjoerg */ 20828021Sjoerg locale_buf = locale_buf_C; 20928021Sjoerg _time_localebuf = _C_time_locale; 21028021Sjoerg save_using_locale = 0; 21128021Sjoergbad_lbuf: 21228021Sjoerg free(lbuf); 21328021Sjoergbad_locale: 21428021Sjoerg (void) close(fd); 21528021Sjoergno_locale: 21628021Sjoerg _time_using_locale = save_using_locale; 21728021Sjoerg return -1; 21828021Sjoerg} 21951186Sdt 22051186Sdtstatic int 22151186Sdtsplit_lines(char *p, const char *plim) 22251186Sdt{ 22351186Sdt int i; 22451186Sdt 22551186Sdt for (i = 0; p < plim; i++) { 22651186Sdt p = strchr(p, '\n'); 22751186Sdt *p++ = '\0'; 22851186Sdt } 22951186Sdt return i; 23051186Sdt} 23151186Sdt 23251186Sdtstatic void 23351186Sdtset_from_buf(const char *p, int num_lines) 23451186Sdt{ 23551186Sdt const char **ap; 23651186Sdt int i; 23751186Sdt 23851186Sdt for (ap = (const char **) &_time_localebuf, i = 0; 23951186Sdt i < num_lines; ++ap, ++i) 24051186Sdt *ap = p += strlen(p) + 1; 24151186Sdt if (num_lines == LCTIME_SIZE_FULL) 24251186Sdt return; 24351186Sdt for (i = 0; i < 12; i++) 24451186Sdt _time_localebuf.alt_month[i] = _time_localebuf.month[i]; 24551186Sdt} 246