]> git.saurik.com Git - apple/libc.git/blob - stdtime/timelocal.c
514d3f169db04511030eab55fb8dd52e830981d5
[apple/libc.git] / stdtime / timelocal.c
1 /*-
2 * Copyright (c) 1997 FreeBSD Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: src/lib/libc/stdtime/timelocal.c,v 1.8.2.2 2000/10/26 16:21:35 ache Exp $
27 */
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/syslimits.h>
32 #include <fcntl.h>
33 #include <locale.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include "setlocale.h"
39 #include "timelocal.h"
40
41 static int split_lines(char *, const char *);
42 static void set_from_buf(const char *, int);
43
44 struct lc_time_T _time_localebuf;
45 int _time_using_locale;
46
47 #define LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *))
48 #define LCTIME_SIZE_1 \
49 (offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *))
50 #define LCTIME_SIZE_2 \
51 (offsetof(struct lc_time_T, Ef_fmt) / sizeof(char *))
52
53 const struct lc_time_T _C_time_locale = {
54 {
55 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
56 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
57 }, {
58 "January", "February", "March", "April", "May", "June",
59 "July", "August", "September", "October", "November", "December"
60 }, {
61 "Sun", "Mon", "Tue", "Wed",
62 "Thu", "Fri", "Sat"
63 }, {
64 "Sunday", "Monday", "Tuesday", "Wednesday",
65 "Thursday", "Friday", "Saturday"
66 },
67
68 /* X_fmt */
69 "%H:%M:%S",
70
71 /*
72 ** x_fmt
73 ** Since the C language standard calls for
74 ** "date, using locale's date format," anything goes.
75 ** Using just numbers (as here) makes Quakers happier;
76 ** it's also compatible with SVR4.
77 */
78 "%m/%d/%y",
79
80 /*
81 ** c_fmt (ctime-compatible)
82 ** Not used, just compatibility placeholder.
83 */
84 NULL,
85
86 /* am */
87 "AM",
88
89 /* pm */
90 "PM",
91
92 /* date_fmt */
93 "%a %Ef %X %Z %Y",
94
95 {
96 "January", "February", "March", "April", "May", "June",
97 "July", "August", "September", "October", "November", "December"
98 },
99
100 /* Ef_fmt
101 ** To determine short months / day order
102 */
103 "%b %e",
104
105 /* EF_fmt
106 ** To determine long months / day order
107 */
108 "%B %e"
109 };
110
111
112 int
113 __time_load_locale(const char *name)
114 {
115 static char * locale_buf;
116 static char locale_buf_C[] = "C";
117 static int num_lines;
118
119 int fd;
120 char * lbuf;
121 char * p;
122 const char * plim;
123 char filename[PATH_MAX];
124 struct stat st;
125 size_t namesize;
126 size_t bufsize;
127 int save_using_locale;
128
129 save_using_locale = _time_using_locale;
130 _time_using_locale = 0;
131
132 if (name == NULL)
133 goto no_locale;
134
135 if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
136 return 0;
137
138 /*
139 ** If the locale name is the same as our cache, use the cache.
140 */
141 lbuf = locale_buf;
142 if (lbuf != NULL && strcmp(name, lbuf) == 0) {
143 set_from_buf(lbuf, num_lines);
144 _time_using_locale = 1;
145 return 0;
146 }
147 /*
148 ** Slurp the locale file into the cache.
149 */
150 namesize = strlen(name) + 1;
151
152 if (!_PathLocale)
153 goto no_locale;
154 /* Range checking not needed, 'name' size is limited */
155 strcpy(filename, _PathLocale);
156 strcat(filename, "/");
157 strcat(filename, name);
158 strcat(filename, "/LC_TIME");
159 fd = open(filename, O_RDONLY);
160 if (fd < 0)
161 goto no_locale;
162 if (fstat(fd, &st) != 0)
163 goto bad_locale;
164 if (st.st_size <= 0)
165 goto bad_locale;
166 bufsize = namesize + st.st_size;
167 locale_buf = NULL;
168 lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
169 malloc(bufsize) : reallocf(lbuf, bufsize);
170 if (lbuf == NULL)
171 goto bad_locale;
172 (void) strcpy(lbuf, name);
173 p = lbuf + namesize;
174 plim = p + st.st_size;
175 if (read(fd, p, (size_t) st.st_size) != st.st_size)
176 goto bad_lbuf;
177 if (close(fd) != 0)
178 goto bad_lbuf;
179 /*
180 ** Parse the locale file into localebuf.
181 */
182 if (plim[-1] != '\n')
183 goto bad_lbuf;
184 num_lines = split_lines(p, plim);
185 if (num_lines >= LCTIME_SIZE_FULL)
186 num_lines = LCTIME_SIZE_FULL;
187 else if (num_lines >= LCTIME_SIZE_2)
188 num_lines = LCTIME_SIZE_2;
189 else if (num_lines >= LCTIME_SIZE_1)
190 num_lines = LCTIME_SIZE_1;
191 else
192 goto reset_locale;
193 set_from_buf(lbuf, num_lines);
194 /*
195 ** Record the successful parse in the cache.
196 */
197 locale_buf = lbuf;
198
199 _time_using_locale = 1;
200 return 0;
201
202 reset_locale:
203 /*
204 * XXX - This may not be the correct thing to do in this case.
205 * setlocale() assumes that we left the old locale alone.
206 */
207 locale_buf = locale_buf_C;
208 _time_localebuf = _C_time_locale;
209 save_using_locale = 0;
210 bad_lbuf:
211 free(lbuf);
212 bad_locale:
213 (void)close(fd);
214 no_locale:
215 _time_using_locale = save_using_locale;
216 return -1;
217 }
218
219 static int
220 split_lines(char *p, const char *plim)
221 {
222 int i;
223
224 for (i = 0; p < plim; i++) {
225 p = strchr(p, '\n');
226 *p++ = '\0';
227 }
228 return i;
229 }
230
231 static void
232 set_from_buf(const char *p, int num_lines)
233 {
234 const char **ap;
235 int i;
236
237 for (ap = (const char **) &_time_localebuf, i = 0;
238 i < num_lines; ++ap, ++i)
239 *ap = p += strlen(p) + 1;
240 if (num_lines >= LCTIME_SIZE_2)
241 return;
242 for (i = 0; i < 12; i++)
243 _time_localebuf.alt_month[i] = _time_localebuf.month[i];
244 }