• 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 
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 = &century;
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 >= (int)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