timelocal.c revision 72167
155714Skris/*- 255714Skris * Copyright (c) 1997 FreeBSD Inc. 355714Skris * All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8296341Sdelphij * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296341Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1755714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296341Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2455714Skris * SUCH DAMAGE. 2555714Skris * 2655714Skris * $FreeBSD: head/lib/libc/stdtime/timelocal.c 72167 2001-02-08 17:06:37Z phantom $ 2755714Skris */ 2855714Skris 2955714Skris#include "namespace.h" 3055714Skris#include <sys/types.h> 3155714Skris#include <sys/stat.h> 3255714Skris#include <sys/syslimits.h> 3355714Skris#include <fcntl.h> 3455714Skris#include <locale.h> 3555714Skris#include <stddef.h> 3655714Skris#include <stdlib.h> 37296341Sdelphij#include <string.h> 3855714Skris#include <unistd.h> 3955714Skris#include "un-namespace.h" 40296341Sdelphij 4155714Skris#include "setlocale.h" 4255714Skris#include "timelocal.h" 4355714Skris 4455714Skrisstatic int split_lines(char *, const char *); 4555714Skrisstatic void set_from_buf(const char *, int); 4655714Skris 4755714Skrisstatic struct lc_time_T _time_localebuf; 4855714Skrisstatic int _time_using_locale; 4955714Skris 5055714Skris#define LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *)) 5155714Skris#define LCTIME_SIZE_1 \ 52296341Sdelphij (offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *)) 5355714Skris#define LCTIME_SIZE_2 \ 5455714Skris (offsetof(struct lc_time_T, Ef_fmt) / sizeof(char *)) 5555714Skris 5655714Skrisstatic const struct lc_time_T _C_time_locale = { 5755714Skris { 5855714Skris "Jan", "Feb", "Mar", "Apr", "May", "Jun", 5955714Skris "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 6055714Skris }, { 6155714Skris "January", "February", "March", "April", "May", "June", 6255714Skris "July", "August", "September", "October", "November", "December" 6355714Skris }, { 6455714Skris "Sun", "Mon", "Tue", "Wed", 6555714Skris "Thu", "Fri", "Sat" 66296341Sdelphij }, { 6755714Skris "Sunday", "Monday", "Tuesday", "Wednesday", 68296341Sdelphij "Thursday", "Friday", "Saturday" 6955714Skris }, 7055714Skris 7155714Skris /* X_fmt */ 72296341Sdelphij "%H:%M:%S", 73296341Sdelphij 74296341Sdelphij /* 75296341Sdelphij ** x_fmt 76296341Sdelphij ** Since the C language standard calls for 77296341Sdelphij ** "date, using locale's date format," anything goes. 78296341Sdelphij ** Using just numbers (as here) makes Quakers happier; 7955714Skris ** it's also compatible with SVR4. 8055714Skris */ 8155714Skris "%m/%d/%y", 82296341Sdelphij 83296341Sdelphij /* 8455714Skris ** c_fmt (ctime-compatible) 85296341Sdelphij ** Not used, just compatibility placeholder. 86296341Sdelphij */ 87296341Sdelphij NULL, 88296341Sdelphij 89296341Sdelphij /* am */ 90296341Sdelphij "AM", 91296341Sdelphij 92296341Sdelphij /* pm */ 93296341Sdelphij "PM", 94296341Sdelphij 95296341Sdelphij /* date_fmt */ 9655714Skris "%a %Ef %X %Z %Y", 9755714Skris 98296341Sdelphij { 99296341Sdelphij "January", "February", "March", "April", "May", "June", 100296341Sdelphij "July", "August", "September", "October", "November", "December" 101296341Sdelphij }, 102296341Sdelphij 103296341Sdelphij /* Ef_fmt 104296341Sdelphij ** To determine short months / day order 105296341Sdelphij */ 10655714Skris "%b %e", 107296341Sdelphij 108296341Sdelphij /* EF_fmt 10955714Skris ** To determine long months / day order 110296341Sdelphij */ 111296341Sdelphij "%B %e" 11255714Skris}; 113296341Sdelphij 114296341Sdelphijstruct lc_time_T * 115296341Sdelphij__get_current_time_locale(void) { 116 return (_time_using_locale 117 ? &_time_localebuf 118 : (struct lc_time_T *)&_C_time_locale); 119} 120 121int 122__time_load_locale(const char *name) 123{ 124 static char * locale_buf; 125 static char locale_buf_C[] = "C"; 126 static int num_lines; 127 128 int fd; 129 char * lbuf; 130 char * p; 131 const char * plim; 132 char filename[PATH_MAX]; 133 struct stat st; 134 size_t namesize; 135 size_t bufsize; 136 int save_using_locale; 137 138 save_using_locale = _time_using_locale; 139 _time_using_locale = 0; 140 141 if (name == NULL) 142 goto no_locale; 143 144 if (!strcmp(name, "C") || !strcmp(name, "POSIX")) 145 return 0; 146 147 /* 148 ** If the locale name is the same as our cache, use the cache. 149 */ 150 lbuf = locale_buf; 151 if (lbuf != NULL && strcmp(name, lbuf) == 0) { 152 set_from_buf(lbuf, num_lines); 153 _time_using_locale = 1; 154 return 0; 155 } 156 /* 157 ** Slurp the locale file into the cache. 158 */ 159 namesize = strlen(name) + 1; 160 161 if (!_PathLocale) 162 goto no_locale; 163 /* Range checking not needed, 'name' size is limited */ 164 strcpy(filename, _PathLocale); 165 strcat(filename, "/"); 166 strcat(filename, name); 167 strcat(filename, "/LC_TIME"); 168 fd = _open(filename, O_RDONLY); 169 if (fd < 0) 170 goto no_locale; 171 if (_fstat(fd, &st) != 0) 172 goto bad_locale; 173 if (st.st_size <= 0) 174 goto bad_locale; 175 bufsize = namesize + st.st_size; 176 locale_buf = NULL; 177 lbuf = (lbuf == NULL || lbuf == locale_buf_C) ? 178 malloc(bufsize) : reallocf(lbuf, bufsize); 179 if (lbuf == NULL) 180 goto bad_locale; 181 (void) strcpy(lbuf, name); 182 p = lbuf + namesize; 183 plim = p + st.st_size; 184 if (_read(fd, p, (size_t) st.st_size) != st.st_size) 185 goto bad_lbuf; 186 if (_close(fd) != 0) 187 goto bad_lbuf; 188 /* 189 ** Parse the locale file into localebuf. 190 */ 191 if (plim[-1] != '\n') 192 goto bad_lbuf; 193 num_lines = split_lines(p, plim); 194 if (num_lines >= LCTIME_SIZE_FULL) 195 num_lines = LCTIME_SIZE_FULL; 196 else if (num_lines >= LCTIME_SIZE_2) 197 num_lines = LCTIME_SIZE_2; 198 else if (num_lines >= LCTIME_SIZE_1) 199 num_lines = LCTIME_SIZE_1; 200 else 201 goto reset_locale; 202 set_from_buf(lbuf, num_lines); 203 /* 204 ** Record the successful parse in the cache. 205 */ 206 locale_buf = lbuf; 207 208 _time_using_locale = 1; 209 return 0; 210 211reset_locale: 212 /* 213 * XXX - This may not be the correct thing to do in this case. 214 * setlocale() assumes that we left the old locale alone. 215 */ 216 locale_buf = locale_buf_C; 217 _time_localebuf = _C_time_locale; 218 save_using_locale = 0; 219bad_lbuf: 220 free(lbuf); 221bad_locale: 222 (void)_close(fd); 223no_locale: 224 _time_using_locale = save_using_locale; 225 return -1; 226} 227 228static int 229split_lines(char *p, const char *plim) 230{ 231 int i; 232 233 for (i = 0; p < plim; i++) { 234 p = strchr(p, '\n'); 235 *p++ = '\0'; 236 } 237 return i; 238} 239 240static void 241set_from_buf(const char *p, int num_lines) 242{ 243 const char **ap; 244 int i; 245 246 for (ap = (const char **) &_time_localebuf, i = 0; 247 i < num_lines; ++ap, ++i) 248 *ap = p += strlen(p) + 1; 249 if (num_lines >= LCTIME_SIZE_2) 250 return; 251 for (i = 0; i < 12; i++) 252 _time_localebuf.alt_month[i] = _time_localebuf.month[i]; 253} 254