• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdlib.h>
2 #include <langinfo.h>
3 #include <time.h>
4 #include <ctype.h>
5 #include <stddef.h>
6 #include <string.h>
7 #include <strings.h>
8 
__getzonename(const char * restrict s,struct tm * restrict tm)9 int __getzonename(const char *restrict s, struct tm *restrict tm)
10 {
11     const char *p = s;
12     struct tm old;
13     memcpy(&old, tm, sizeof(struct tm));
14     /* Possible time zone names like +XXX or -XXX */
15     if (*p == '+' || *p == '-') {
16         p++;
17     }
18 
19     /* The time zone name is adjacent to the offset second data,
20      * and the following symbol belongs to the offset second */
21     while (*p && (*p != '+' && *p != '-' && *p != ' ')) {
22         p++;
23     }
24 
25     /* In the structure struct tm, tm_zone is declared as const char * type, so use static */
26     static char buf[16] = {0};
27     memset(buf, 0x0, sizeof(buf));
28     int len = p - s;
29     memcpy(buf, s, len);
30     tm->__tm_zone = buf;
31 
32     /* Re-fetch local data, extract tm_isdst flag. */
33     time_t t = mktime(&old);
34     struct tm *tmp = localtime(&t);
35     if (tmp) {
36         tm->tm_isdst = tmp->tm_isdst;
37     }
38     return len;
39 }
40 
__getgmtoff(const char * restrict s,struct tm * restrict tm)41 int __getgmtoff(const char *restrict s, struct tm *restrict tm)
42 {
43     const char *p = s;
44     int sign = 1;
45     int i;
46     int isexit = 0;
47     long m = 0;
48     long h = 0;
49 
50     /* The possible formats for time offset are HHMM(-HHMM) or HH:MM(-HH:MM) */
51     if (*p == '-') {
52         sign = -1;
53     }
54     p++;
55     tm->__tm_gmtoff = 0;
56 
57     /* get hours */
58     for (i = 0; i < 2 && *p; i++, p++) {
59         if (isdigit(*p)) {
60             h = h * 10 + (*p - 0x30);
61         } else {
62             p--;
63             isexit = 1;
64             break;
65         }
66     }
67 
68     if (!isexit) {
69         /* Possible time zone formats are HH:MM. */
70         if (*p == ':') {
71             *p++;
72         }
73 
74         /* get minutes */
75         for (i = 0; i < 2 && *p; i++, p++) {
76             if (isdigit(*p)) {
77                 m = m * 10 + (*p - 0x30);
78             } else {
79                 p--;
80                 isexit = 1;
81                 break;
82             }
83         }
84     }
85 
86     /* Convert hours and minutes to seconds */
87     tm->__tm_gmtoff = sign * (h * 3600 + m * 60);
88 
89     return p - s;
90 }
91 
strptime(const char * restrict s,const char * restrict f,struct tm * restrict tm)92 char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
93 {
94 	int i, w, neg, adj, min, range, *dest, dummy;
95 	const char *ex;
96 	size_t len;
97 	int want_century = 0, century = 0, relyear = 0;
98 	while (*f) {
99 		if (*f != '%') {
100 			if (isspace(*f)) for (; *s && isspace(*s); s++);
101 			else if (*s != *f) return 0;
102 			else s++;
103 			f++;
104 			continue;
105 		}
106 		f++;
107 		if (*f == '+') f++;
108 		if (isdigit(*f)) {
109 			char *new_f;
110 			w=strtoul(f, &new_f, 10);
111 			f = new_f;
112 		} else {
113 			w=-1;
114 		}
115 		adj=0;
116 		switch (*f++) {
117 		case 'a': case 'A':
118 			dest = &tm->tm_wday;
119 			min = ABDAY_1;
120 			range = 7;
121 			goto symbolic_range;
122 		case 'b': case 'B': case 'h':
123 			dest = &tm->tm_mon;
124 			min = ABMON_1;
125 			range = 12;
126 			goto symbolic_range;
127 		case 'c':
128 			s = strptime(s, nl_langinfo(D_T_FMT), tm);
129 			if (!s) return 0;
130 			break;
131 		case 'C':
132 			dest = &century;
133 			if (w<0) w=2;
134 			want_century |= 2;
135 			goto numeric_digits;
136 		case 'd': case 'e':
137 			dest = &tm->tm_mday;
138 			min = 1;
139 			range = 31;
140 			goto numeric_range;
141 		case 'D':
142 			s = strptime(s, "%m/%d/%y", tm);
143 			if (!s) return 0;
144 			break;
145 		case 'F':
146 			s = strptime(s, "%Y-%m-%d", tm);
147 			if (!s) {
148 				return 0;
149 			}
150 			break;
151 		case 'g':
152 			dest = &tm->tm_year;
153 			min = 0;
154 			range = 99;
155 			w = 2;
156 			want_century = 0;
157 			goto numeric_digits;
158 		case 'G':
159 			do {
160 				++s;
161 			} while (isdigit(*s));
162 			continue;
163 		case 'k':
164 		case 'H':
165 			dest = &tm->tm_hour;
166 			min = 0;
167 			range = 24;
168 			goto numeric_range;
169 		case 'l':
170 		case 'I':
171 			dest = &tm->tm_hour;
172 			min = 1;
173 			range = 12;
174 			goto numeric_range;
175 		case 'j':
176 			dest = &tm->tm_yday;
177 			min = 1;
178 			range = 366;
179 			adj = 1;
180 			goto numeric_range;
181 		case 'm':
182 			dest = &tm->tm_mon;
183 			min = 1;
184 			range = 12;
185 			adj = 1;
186 			goto numeric_range;
187 		case 'M':
188 			dest = &tm->tm_min;
189 			min = 0;
190 			range = 60;
191 			goto numeric_range;
192 		case 'n': case 't':
193 			for (; *s && isspace(*s); s++);
194 			break;
195 		case 'p':
196 		case 'P':
197 			ex = nl_langinfo(AM_STR);
198 			len = strlen(ex);
199 			if (!strncasecmp(s, ex, len)) {
200 				tm->tm_hour %= 12;
201 				s += len;
202 				break;
203 			}
204 			ex = nl_langinfo(PM_STR);
205 			len = strlen(ex);
206 			if (!strncasecmp(s, ex, len)) {
207 				tm->tm_hour %= 12;
208 				tm->tm_hour += 12;
209 				s += len;
210 				break;
211 			}
212 			return 0;
213 		case 'r':
214 			s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
215 			if (!s) return 0;
216 			break;
217 		case 'R':
218 			s = strptime(s, "%H:%M", tm);
219 			if (!s) return 0;
220 			break;
221 		case 's': {
222 			time_t secs = 0;
223 			if (!isdigit(*s)) {
224 				return 0;
225 			}
226 			do {
227 				secs *= 10;
228 				secs += *s - '0';
229 				s++;
230 			} while (isdigit(*s));
231 			if (localtime_r(&secs, tm) == NULL) {
232 				return 0;
233 			}
234 			break;
235 		}
236 		case 'S':
237 			dest = &tm->tm_sec;
238 			min = 0;
239 			range = 61;
240 			goto numeric_range;
241 		case 'T':
242 			s = strptime(s, "%H:%M:%S", tm);
243 			if (!s) return 0;
244 			break;
245 		case 'u': {
246 			if (!isdigit(*s)) {
247 				return 0;
248 			}
249 			int wday = 0;
250 			int rulim = 7;
251 			do {
252 				wday *= 10;
253 				wday += *s++ - '0';
254 				rulim /= 10;
255 			} while ((wday * 10 < 7) && rulim && isdigit(*s));
256 			if (wday < 1 || wday > 7) {
257 				return 0;
258 			}
259 			tm->tm_wday = wday % 7;
260 			continue;
261 		}
262 		case 'U':
263 		case 'W':
264 			/* Throw away result, for now. (FIXME?) */
265 			dest = &dummy;
266 			min = 0;
267 			range = 54;
268 			goto numeric_range;
269 		case 'w':
270 			dest = &tm->tm_wday;
271 			min = 0;
272 			range = 7;
273 			goto numeric_range;
274 		case 'v':
275 			if (!(s = strptime(s, "%e-%b-%Y", tm))) {
276 				return 0;
277 			}
278 			break;
279 		case 'V': {
280 			int r = 0;
281 			int rulim = 53;
282 			if (!isdigit(*s)) {
283 				return 0;
284 			}
285 			do {
286 				r *= 10;
287 				r += *s++ - '0';
288 				rulim /= 10;
289 			} while ((r * 10 < 53) && rulim && isdigit(*s));
290 			if (r < 0 || r > 53) {
291 				return 0;
292 			}
293 			continue;
294 		}
295 		case 'x':
296 			s = strptime(s, nl_langinfo(D_FMT), tm);
297 			if (!s) return 0;
298 			break;
299 		case 'X':
300 			s = strptime(s, nl_langinfo(T_FMT), tm);
301 			if (!s) return 0;
302 			break;
303 		case 'y':
304 			dest = &relyear;
305 			w = 2;
306 			want_century |= 1;
307 			goto numeric_digits;
308 		case 'Y':
309 			dest = &tm->tm_year;
310 			if (w<0) w=4;
311 			adj = 1900;
312 			want_century = 0;
313 			goto numeric_digits;
314 		case 'z':
315 			s += __getgmtoff((const char *)s, tm);
316 			continue;
317 		case 'Z':
318 			tzset();
319 			s += __getzonename((const char *)s, tm);
320 			continue;
321 		case '%':
322 			if (*s++ != '%') return 0;
323 			break;
324 		default:
325 			return 0;
326 		numeric_range:
327 			if (!isdigit(*s)) return 0;
328 			*dest = 0;
329 			for (i=1; i<=min+range && isdigit(*s); i*=10)
330 				*dest = *dest * 10 + *s++ - '0';
331 			if (*dest - min >= (unsigned)range) return 0;
332 			*dest -= adj;
333 			switch((char *)dest - (char *)tm) {
334 			case offsetof(struct tm, tm_yday):
335 				;
336 			}
337 			goto update;
338 		numeric_digits:
339 			neg = 0;
340 			if (*s == '+') s++;
341 			else if (*s == '-') neg=1, s++;
342 			if (!isdigit(*s)) return 0;
343 			for (*dest=i=0; i<w && isdigit(*s); i++)
344 				*dest = *dest * 10 + *s++ - '0';
345 			if (neg) *dest = -*dest;
346 			*dest -= adj;
347 			goto update;
348 		symbolic_range:
349 			for (i=2*range-1; i>=0; i--) {
350 				ex = nl_langinfo(min+i);
351 				len = strlen(ex);
352 				if (strncasecmp(s, ex, len)) continue;
353 				s += len;
354 				*dest = i % range;
355 				break;
356 			}
357 			if (i<0) return 0;
358 			goto update;
359 		update:
360 			//FIXME
361 			;
362 		}
363 	}
364 	if (want_century) {
365 		tm->tm_year = relyear;
366 		if (want_century & 2) tm->tm_year += century * 100 - 1900;
367 		else if (tm->tm_year <= 68) tm->tm_year += 100;
368 	}
369 	return (char *)s;
370 }
371