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 
strptime(const char * restrict s,const char * restrict f,struct tm * restrict tm)9 char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
10 {
11 	int i, w, neg, adj, min, range, *dest, dummy;
12 	const char *ex;
13 	size_t len;
14 	int want_century = 0, century = 0;
15 	while (*f) {
16 		if (*f != '%') {
17 			if (isspace(*f)) for (; *s && isspace(*s); s++);
18 			else if (*s != *f) return 0;
19 			else s++;
20 			f++;
21 			continue;
22 		}
23 		f++;
24 		if (*f == '+') f++;
25 		if (isdigit(*f)) w=strtoul(f, (void *)&f, 10);
26 		else w=-1;
27 		adj=0;
28 		switch (*f++) {
29 		case 'a': case 'A':
30 			dest = &tm->tm_wday;
31 			min = ABDAY_1;
32 			range = 7;
33 			goto symbolic_range;
34 		case 'b': case 'B': case 'h':
35 			dest = &tm->tm_mon;
36 			min = ABMON_1;
37 			range = 12;
38 			goto symbolic_range;
39 		case 'c':
40 			s = strptime(s, nl_langinfo(D_T_FMT), tm);
41 			if (!s) return 0;
42 			break;
43 		case 'C':
44 			dest = &century;
45 			if (w<0) w=2;
46 			want_century |= 2;
47 			goto numeric_digits;
48 		case 'd': case 'e':
49 			dest = &tm->tm_mday;
50 			min = 1;
51 			range = 31;
52 			goto numeric_range;
53 		case 'D':
54 			s = strptime(s, "%m/%d/%y", tm);
55 			if (!s) return 0;
56 			break;
57 		case 'H':
58 			dest = &tm->tm_hour;
59 			min = 0;
60 			range = 24;
61 			goto numeric_range;
62 		case 'I':
63 			dest = &tm->tm_hour;
64 			min = 1;
65 			range = 12;
66 			goto numeric_range;
67 		case 'j':
68 			dest = &tm->tm_yday;
69 			min = 1;
70 			range = 366;
71 			goto numeric_range;
72 		case 'm':
73 			dest = &tm->tm_mon;
74 			min = 1;
75 			range = 12;
76 			adj = 1;
77 			goto numeric_range;
78 		case 'M':
79 			dest = &tm->tm_min;
80 			min = 0;
81 			range = 60;
82 			goto numeric_range;
83 		case 'n': case 't':
84 			for (; *s && isspace(*s); s++);
85 			break;
86 		case 'p':
87 			ex = nl_langinfo(AM_STR);
88 			len = strlen(ex);
89 			if (!strncasecmp(s, ex, len)) {
90 				tm->tm_hour %= 12;
91 				break;
92 			}
93 			ex = nl_langinfo(PM_STR);
94 			len = strlen(ex);
95 			if (!strncasecmp(s, ex, len)) {
96 				tm->tm_hour %= 12;
97 				tm->tm_hour += 12;
98 				break;
99 			}
100 			return 0;
101 		case 'r':
102 			s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
103 			if (!s) return 0;
104 			break;
105 		case 'R':
106 			s = strptime(s, "%H:%M", tm);
107 			if (!s) return 0;
108 			break;
109 		case 'S':
110 			dest = &tm->tm_sec;
111 			min = 0;
112 			range = 61;
113 			goto numeric_range;
114 		case 'T':
115 			s = strptime(s, "%H:%M:%S", tm);
116 			if (!s) return 0;
117 			break;
118 		case 'U':
119 		case 'W':
120 			/* Throw away result, for now. (FIXME?) */
121 			dest = &dummy;
122 			min = 0;
123 			range = 54;
124 			goto numeric_range;
125 		case 'w':
126 			dest = &tm->tm_wday;
127 			min = 0;
128 			range = 7;
129 			goto numeric_range;
130 		case 'x':
131 			s = strptime(s, nl_langinfo(D_FMT), tm);
132 			if (!s) return 0;
133 			break;
134 		case 'X':
135 			s = strptime(s, nl_langinfo(T_FMT), tm);
136 			if (!s) return 0;
137 			break;
138 		case 'y':
139 			dest = &tm->tm_year;
140 			w = 2;
141 			want_century |= 1;
142 			goto numeric_digits;
143 		case 'Y':
144 			dest = &tm->tm_year;
145 			if (w<0) w=4;
146 			adj = 1900;
147 			want_century = 0;
148 			goto numeric_digits;
149 		case '%':
150 			if (*s++ != '%') return 0;
151 			break;
152 		default:
153 			return 0;
154 		numeric_range:
155 			if (!isdigit(*s)) return 0;
156 			*dest = 0;
157 			for (i=1; i<=min+range && isdigit(*s); i*=10)
158 				*dest = *dest * 10 + *s++ - '0';
159 			if (*dest - min >= (unsigned)range) return 0;
160 			*dest -= adj;
161 			switch((char *)dest - (char *)tm) {
162 			case offsetof(struct tm, tm_yday):
163 				;
164 			}
165 			goto update;
166 		numeric_digits:
167 			neg = 0;
168 			if (*s == '+') s++;
169 			else if (*s == '-') neg=1, s++;
170 			if (!isdigit(*s)) return 0;
171 			for (*dest=i=0; i<w && isdigit(*s); i++)
172 				*dest = *dest * 10 + *s++ - '0';
173 			if (neg) *dest = -*dest;
174 			*dest -= adj;
175 			goto update;
176 		symbolic_range:
177 			for (i=2*range-1; i>=0; i--) {
178 				ex = nl_langinfo(min+i);
179 				len = strlen(ex);
180 				if (strncasecmp(s, ex, len)) continue;
181 				s += len;
182 				*dest = i % range;
183 				break;
184 			}
185 			if (i<0) return 0;
186 			goto update;
187 		update:
188 			//FIXME
189 			;
190 		}
191 	}
192 	if (want_century) {
193 		if (want_century & 2) tm->tm_year += century * 100 - 1900;
194 		else if (tm->tm_year <= 68) tm->tm_year += 100;
195 	}
196 	return (char *)s;
197 }
198