timelocal.c revision 53940
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 53940 1999-11-30 07:33:37Z 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 \ 5053940Sache (offsetof(struct lc_time_T, Ex_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 */ 8553940Sache "%a %Ex %X %Y", 8628021Sjoerg 8728021Sjoerg /* am */ 8828021Sjoerg "AM", 8928021Sjoerg 9028021Sjoerg /* pm */ 9128021Sjoerg "PM", 9228021Sjoerg 9328021Sjoerg /* date_fmt */ 9453940Sache "%a %Ex %X %Z %Y", 9551186Sdt 9651186Sdt { 9751186Sdt "January", "February", "March", "April", "May", "June", 9851186Sdt "July", "August", "September", "October", "November", "December" 9953940Sache }, 10053940Sache 10153940Sache /* Ex_fmt 10253940Sache ** To determine months / day order 10353940Sache */ 10453940Sache "%b %e" 10528021Sjoerg}; 10628021Sjoerg 10728021Sjoerg 10828021Sjoergint 10928021Sjoerg__time_load_locale(const char *name) 11028021Sjoerg{ 11128021Sjoerg static char * locale_buf; 11228021Sjoerg static char locale_buf_C[] = "C"; 11351186Sdt static int num_lines; 11428021Sjoerg 11528021Sjoerg int fd; 11628021Sjoerg char * lbuf; 11728021Sjoerg char * p; 11828021Sjoerg const char * plim; 11928021Sjoerg char filename[PATH_MAX]; 12028021Sjoerg struct stat st; 12128021Sjoerg size_t namesize; 12228021Sjoerg size_t bufsize; 12328021Sjoerg int save_using_locale; 12428021Sjoerg 12528021Sjoerg save_using_locale = _time_using_locale; 12628021Sjoerg _time_using_locale = 0; 12728021Sjoerg 12828021Sjoerg if (name == NULL) 12928021Sjoerg goto no_locale; 13028021Sjoerg 13128021Sjoerg if (!strcmp(name, "C") || !strcmp(name, "POSIX")) 13228021Sjoerg return 0; 13328021Sjoerg 13428021Sjoerg /* 13528021Sjoerg ** If the locale name is the same as our cache, use the cache. 13628021Sjoerg */ 13728021Sjoerg lbuf = locale_buf; 13828021Sjoerg if (lbuf != NULL && strcmp(name, lbuf) == 0) { 13951186Sdt set_from_buf(lbuf, num_lines); 14028021Sjoerg _time_using_locale = 1; 14128021Sjoerg return 0; 14228021Sjoerg } 14328021Sjoerg /* 14428021Sjoerg ** Slurp the locale file into the cache. 14528021Sjoerg */ 14628021Sjoerg namesize = strlen(name) + 1; 14728021Sjoerg 14828021Sjoerg if (!_PathLocale) 14928021Sjoerg goto no_locale; 15028021Sjoerg /* Range checking not needed, 'name' size is limited */ 15128021Sjoerg strcpy(filename, _PathLocale); 15228021Sjoerg strcat(filename, "/"); 15328021Sjoerg strcat(filename, name); 15428021Sjoerg strcat(filename, "/LC_TIME"); 15528021Sjoerg fd = open(filename, O_RDONLY); 15628021Sjoerg if (fd < 0) 15728021Sjoerg goto no_locale; 15828021Sjoerg if (fstat(fd, &st) != 0) 15928021Sjoerg goto bad_locale; 16028021Sjoerg if (st.st_size <= 0) 16128021Sjoerg goto bad_locale; 16228021Sjoerg bufsize = namesize + st.st_size; 16328021Sjoerg locale_buf = NULL; 16428021Sjoerg lbuf = (lbuf == NULL || lbuf == locale_buf_C) ? 16539327Simp malloc(bufsize) : reallocf(lbuf, bufsize); 16628021Sjoerg if (lbuf == NULL) 16728021Sjoerg goto bad_locale; 16828021Sjoerg (void) strcpy(lbuf, name); 16928021Sjoerg p = lbuf + namesize; 17028021Sjoerg plim = p + st.st_size; 17128021Sjoerg if (read(fd, p, (size_t) st.st_size) != st.st_size) 17228021Sjoerg goto bad_lbuf; 17328021Sjoerg if (close(fd) != 0) 17428021Sjoerg goto bad_lbuf; 17528021Sjoerg /* 17628021Sjoerg ** Parse the locale file into localebuf. 17728021Sjoerg */ 17828021Sjoerg if (plim[-1] != '\n') 17928021Sjoerg goto bad_lbuf; 18051186Sdt num_lines = split_lines(p, plim); 18151186Sdt if (num_lines >= LCTIME_SIZE_FULL) 18251186Sdt num_lines = LCTIME_SIZE_FULL; 18353940Sache else if (num_lines >= LCTIME_SIZE_2) 18453940Sache num_lines = LCTIME_SIZE_2; 18551186Sdt else if (num_lines >= LCTIME_SIZE_1) 18651186Sdt num_lines = LCTIME_SIZE_1; 18751186Sdt else 18851186Sdt goto reset_locale; 18951186Sdt set_from_buf(lbuf, num_lines); 19028021Sjoerg /* 19128021Sjoerg ** Record the successful parse in the cache. 19228021Sjoerg */ 19328021Sjoerg locale_buf = lbuf; 19428021Sjoerg 19528021Sjoerg _time_using_locale = 1; 19628021Sjoerg return 0; 19728021Sjoerg 19828021Sjoergreset_locale: 19928021Sjoerg /* 20028021Sjoerg * XXX - This may not be the correct thing to do in this case. 20128021Sjoerg * setlocale() assumes that we left the old locale alone. 20228021Sjoerg */ 20328021Sjoerg locale_buf = locale_buf_C; 20428021Sjoerg _time_localebuf = _C_time_locale; 20528021Sjoerg save_using_locale = 0; 20628021Sjoergbad_lbuf: 20728021Sjoerg free(lbuf); 20828021Sjoergbad_locale: 20928021Sjoerg (void) close(fd); 21028021Sjoergno_locale: 21128021Sjoerg _time_using_locale = save_using_locale; 21228021Sjoerg return -1; 21328021Sjoerg} 21451186Sdt 21551186Sdtstatic int 21651186Sdtsplit_lines(char *p, const char *plim) 21751186Sdt{ 21851186Sdt int i; 21951186Sdt 22051186Sdt for (i = 0; p < plim; i++) { 22151186Sdt p = strchr(p, '\n'); 22251186Sdt *p++ = '\0'; 22351186Sdt } 22451186Sdt return i; 22551186Sdt} 22651186Sdt 22751186Sdtstatic void 22851186Sdtset_from_buf(const char *p, int num_lines) 22951186Sdt{ 23051186Sdt const char **ap; 23151186Sdt int i; 23251186Sdt 23351186Sdt for (ap = (const char **) &_time_localebuf, i = 0; 23451186Sdt i < num_lines; ++ap, ++i) 23551186Sdt *ap = p += strlen(p) + 1; 23651186Sdt if (num_lines == LCTIME_SIZE_FULL) 23751186Sdt return; 23851186Sdt for (i = 0; i < 12; i++) 23951186Sdt _time_localebuf.alt_month[i] = _time_localebuf.month[i]; 24051186Sdt} 241