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