Mercurial > pidgin.yaz
comparison libpurple/protocols/msn/msnutils.c @ 20400:ea9a5566a156
propagate from branch 'im.pidgin.rlaager.merging.msnp13-and-sf-1621854-4' (head 36b8a3e05397b5918f311a046fa580c5bb8846e0)
to branch 'im.pidgin.cpw.khc.msnp14' (head 39ac2e1b7754245b292605b0e3055ebb1b954c5d)
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Sun, 15 Apr 2007 05:00:56 +0000 |
parents | |
children | 599b5a4aebab |
comparison
equal
deleted
inserted
replaced
20395:bb940f08c820 | 20400:ea9a5566a156 |
---|---|
1 /** | |
2 * @file msnutils.c Utility functions | |
3 * | |
4 * purple | |
5 * | |
6 * Purple is the legal property of its developers, whose names are too numerous | |
7 * to list here. Please refer to the COPYRIGHT file distributed with this | |
8 * source distribution. | |
9 * | |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 */ | |
24 #include "msn.h" | |
25 #include "msnutils.h" | |
26 #include "time.h" | |
27 //#include <openssl/md5.h> | |
28 | |
29 char *rand_guid(void); | |
30 | |
31 /************************************************************************** | |
32 * Util | |
33 **************************************************************************/ | |
34 char * | |
35 rand_guid() | |
36 { | |
37 return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", | |
38 rand() % 0xAAFF + 0x1111, | |
39 rand() % 0xAAFF + 0x1111, | |
40 rand() % 0xAAFF + 0x1111, | |
41 rand() % 0xAAFF + 0x1111, | |
42 rand() % 0xAAFF + 0x1111, | |
43 rand() % 0xAAFF + 0x1111, | |
44 rand() % 0xAAFF + 0x1111, | |
45 rand() % 0xAAFF + 0x1111); | |
46 } | |
47 | |
48 void | |
49 msn_parse_format(const char *mime, char **pre_ret, char **post_ret) | |
50 { | |
51 char *cur; | |
52 GString *pre = g_string_new(NULL); | |
53 GString *post = g_string_new(NULL); | |
54 unsigned int colors[3]; | |
55 | |
56 if (pre_ret != NULL) *pre_ret = NULL; | |
57 if (post_ret != NULL) *post_ret = NULL; | |
58 | |
59 cur = strstr(mime, "FN="); | |
60 | |
61 if (cur && (*(cur = cur + 3) != ';')) | |
62 { | |
63 pre = g_string_append(pre, "<FONT FACE=\""); | |
64 | |
65 while (*cur && *cur != ';') | |
66 { | |
67 pre = g_string_append_c(pre, *cur); | |
68 cur++; | |
69 } | |
70 | |
71 pre = g_string_append(pre, "\">"); | |
72 post = g_string_prepend(post, "</FONT>"); | |
73 } | |
74 | |
75 cur = strstr(mime, "EF="); | |
76 | |
77 if (cur && (*(cur = cur + 3) != ';')) | |
78 { | |
79 while (*cur && *cur != ';') | |
80 { | |
81 pre = g_string_append_c(pre, '<'); | |
82 pre = g_string_append_c(pre, *cur); | |
83 pre = g_string_append_c(pre, '>'); | |
84 post = g_string_prepend_c(post, '>'); | |
85 post = g_string_prepend_c(post, *cur); | |
86 post = g_string_prepend_c(post, '/'); | |
87 post = g_string_prepend_c(post, '<'); | |
88 cur++; | |
89 } | |
90 } | |
91 | |
92 cur = strstr(mime, "CO="); | |
93 | |
94 if (cur && (*(cur = cur + 3) != ';')) | |
95 { | |
96 int i; | |
97 | |
98 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]); | |
99 | |
100 if (i > 0) | |
101 { | |
102 char tag[64]; | |
103 | |
104 if (i == 1) | |
105 { | |
106 colors[1] = 0; | |
107 colors[2] = 0; | |
108 } | |
109 else if (i == 2) | |
110 { | |
111 unsigned int temp = colors[0]; | |
112 | |
113 colors[0] = colors[1]; | |
114 colors[1] = temp; | |
115 colors[2] = 0; | |
116 } | |
117 else if (i == 3) | |
118 { | |
119 unsigned int temp = colors[2]; | |
120 | |
121 colors[2] = colors[0]; | |
122 colors[0] = temp; | |
123 } | |
124 | |
125 g_snprintf(tag, sizeof(tag), | |
126 "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">", | |
127 colors[0], colors[1], colors[2]); | |
128 | |
129 pre = g_string_append(pre, tag); | |
130 post = g_string_prepend(post, "</FONT>"); | |
131 } | |
132 } | |
133 | |
134 cur = strstr(mime, "RL="); | |
135 | |
136 if (cur && (*(cur = cur + 3) != ';')) | |
137 { | |
138 if (*cur == '1') | |
139 { | |
140 /* RTL text was received */ | |
141 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">"); | |
142 post = g_string_prepend(post, "</SPAN>"); | |
143 } | |
144 } | |
145 | |
146 cur = g_strdup(purple_url_decode(pre->str)); | |
147 g_string_free(pre, TRUE); | |
148 | |
149 if (pre_ret != NULL) | |
150 *pre_ret = cur; | |
151 else | |
152 g_free(cur); | |
153 | |
154 cur = g_strdup(purple_url_decode(post->str)); | |
155 g_string_free(post, TRUE); | |
156 | |
157 if (post_ret != NULL) | |
158 *post_ret = cur; | |
159 else | |
160 g_free(cur); | |
161 } | |
162 | |
163 /*encode the str to RFC2047 style | |
164 * Currently only support the UTF-8 and base64 encode | |
165 */ | |
166 char * | |
167 msn_encode_mime(const char *str) | |
168 { | |
169 char *base64; | |
170 | |
171 base64 = gaim_base64_encode((guchar *)str, strlen(str)); | |
172 return g_strdup_printf("=?utf-8?B?%s?=", base64); | |
173 } | |
174 | |
175 /* | |
176 * We need this because we're only supposed to encode spaces in the font | |
177 * names. purple_url_encode() isn't acceptable. | |
178 */ | |
179 static const char * | |
180 encode_spaces(const char *str) | |
181 { | |
182 static char buf[BUF_LEN]; | |
183 const char *c; | |
184 char *d; | |
185 | |
186 g_return_val_if_fail(str != NULL, NULL); | |
187 | |
188 for (c = str, d = buf; *c != '\0'; c++) | |
189 { | |
190 if (*c == ' ') | |
191 { | |
192 *d++ = '%'; | |
193 *d++ = '2'; | |
194 *d++ = '0'; | |
195 } | |
196 else | |
197 *d++ = *c; | |
198 } | |
199 | |
200 return buf; | |
201 } | |
202 | |
203 /* | |
204 * Taken from the zephyr plugin. | |
205 * This parses HTML formatting (put out by one of the gtkimhtml widgets | |
206 * and converts it to msn formatting. It doesn't deal with the tag closing, | |
207 * but gtkimhtml widgets give valid html. | |
208 * It currently deals properly with <b>, <u>, <i>, <font face=...>, | |
209 * <font color=...>, <span dir=...>, <span style="direction: ...">. | |
210 * It ignores <font back=...> and <font size=...> | |
211 */ | |
212 void | |
213 msn_import_html(const char *html, char **attributes, char **message) | |
214 { | |
215 int len, retcount = 0; | |
216 const char *c; | |
217 char *msg; | |
218 char *fontface = NULL; | |
219 char fonteffect[4]; | |
220 char fontcolor[7]; | |
221 char direction = '0'; | |
222 | |
223 gboolean has_bold = FALSE; | |
224 gboolean has_italic = FALSE; | |
225 gboolean has_underline = FALSE; | |
226 gboolean has_strikethrough = FALSE; | |
227 | |
228 g_return_if_fail(html != NULL); | |
229 g_return_if_fail(attributes != NULL); | |
230 g_return_if_fail(message != NULL); | |
231 | |
232 len = strlen(html); | |
233 msg = g_malloc0(len + 1); | |
234 | |
235 memset(fontcolor, 0, sizeof(fontcolor)); | |
236 strcat(fontcolor, "0"); | |
237 memset(fonteffect, 0, sizeof(fonteffect)); | |
238 | |
239 for (c = html; *c != '\0';) | |
240 { | |
241 if (*c == '<') | |
242 { | |
243 if (!g_ascii_strncasecmp(c + 1, "br>", 3)) | |
244 { | |
245 msg[retcount++] = '\r'; | |
246 msg[retcount++] = '\n'; | |
247 c += 4; | |
248 } | |
249 else if (!g_ascii_strncasecmp(c + 1, "i>", 2)) | |
250 { | |
251 if (!has_italic) | |
252 { | |
253 strcat(fonteffect, "I"); | |
254 has_italic = TRUE; | |
255 } | |
256 c += 3; | |
257 } | |
258 else if (!g_ascii_strncasecmp(c + 1, "b>", 2)) | |
259 { | |
260 if (!has_bold) | |
261 { | |
262 strcat(fonteffect, "B"); | |
263 has_bold = TRUE; | |
264 } | |
265 c += 3; | |
266 } | |
267 else if (!g_ascii_strncasecmp(c + 1, "u>", 2)) | |
268 { | |
269 if (!has_underline) | |
270 { | |
271 strcat(fonteffect, "U"); | |
272 has_underline = TRUE; | |
273 } | |
274 c += 3; | |
275 } | |
276 else if (!g_ascii_strncasecmp(c + 1, "s>", 2)) | |
277 { | |
278 if (!has_strikethrough) | |
279 { | |
280 strcat(fonteffect, "S"); | |
281 has_strikethrough = TRUE; | |
282 } | |
283 c += 3; | |
284 } | |
285 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8)) | |
286 { | |
287 c += 9; | |
288 | |
289 if (!g_ascii_strncasecmp(c, "mailto:", 7)) | |
290 c += 7; | |
291 | |
292 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) | |
293 msg[retcount++] = *c++; | |
294 | |
295 if (*c != '\0') | |
296 c += 2; | |
297 | |
298 /* ignore descriptive string */ | |
299 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4)) | |
300 c++; | |
301 | |
302 if (*c != '\0') | |
303 c += 4; | |
304 } | |
305 else if (!g_ascii_strncasecmp(c + 1, "span", 4)) | |
306 { | |
307 /* Bi-directional text support using CSS properties in span tags */ | |
308 c += 5; | |
309 | |
310 while (*c != '\0' && *c != '>') | |
311 { | |
312 while (*c == ' ') | |
313 c++; | |
314 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9)) | |
315 { | |
316 c += 9; | |
317 direction = '1'; | |
318 } | |
319 else if (!g_ascii_strncasecmp(c, "style=\"", 7)) | |
320 { | |
321 /* Parse inline CSS attributes */ | |
322 char *attributes; | |
323 int attr_len = 0; | |
324 c += 7; | |
325 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"') | |
326 attr_len++; | |
327 if (*(c + attr_len) == '"') | |
328 { | |
329 char *attr_dir; | |
330 attributes = g_strndup(c, attr_len); | |
331 attr_dir = purple_markup_get_css_property(attributes, "direction"); | |
332 if (attr_dir && (!strncasecmp(attr_dir, "RTL", 3))) | |
333 direction = '1'; | |
334 g_free(attr_dir); | |
335 g_free(attributes); | |
336 } | |
337 | |
338 } | |
339 else | |
340 { | |
341 c++; | |
342 } | |
343 } | |
344 if (*c == '>') | |
345 c++; | |
346 } | |
347 else if (!g_ascii_strncasecmp(c + 1, "font", 4)) | |
348 { | |
349 c += 5; | |
350 | |
351 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1)) | |
352 c++; | |
353 | |
354 if (!g_ascii_strncasecmp(c, "color=\"#", 7)) | |
355 { | |
356 c += 8; | |
357 | |
358 fontcolor[0] = *(c + 4); | |
359 fontcolor[1] = *(c + 5); | |
360 fontcolor[2] = *(c + 2); | |
361 fontcolor[3] = *(c + 3); | |
362 fontcolor[4] = *c; | |
363 fontcolor[5] = *(c + 1); | |
364 | |
365 c += 8; | |
366 } | |
367 else if (!g_ascii_strncasecmp(c, "face=\"", 6)) | |
368 { | |
369 const char *end = NULL; | |
370 const char *comma = NULL; | |
371 unsigned int namelen = 0; | |
372 | |
373 c += 6; | |
374 end = strchr(c, '\"'); | |
375 comma = strchr(c, ','); | |
376 | |
377 if (comma == NULL || comma > end) | |
378 namelen = (unsigned int)(end - c); | |
379 else | |
380 namelen = (unsigned int)(comma - c); | |
381 | |
382 fontface = g_strndup(c, namelen); | |
383 c = end + 2; | |
384 } | |
385 else | |
386 { | |
387 /* Drop all unrecognized/misparsed font tags */ | |
388 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) | |
389 c++; | |
390 | |
391 if (*c != '\0') | |
392 c += 2; | |
393 } | |
394 } | |
395 else | |
396 { | |
397 while ((*c != '\0') && (*c != '>')) | |
398 c++; | |
399 if (*c != '\0') | |
400 c++; | |
401 } | |
402 } | |
403 else if (*c == '&') | |
404 { | |
405 if (!g_ascii_strncasecmp(c, "<", 4)) | |
406 { | |
407 msg[retcount++] = '<'; | |
408 c += 4; | |
409 } | |
410 else if (!g_ascii_strncasecmp(c, ">", 4)) | |
411 { | |
412 msg[retcount++] = '>'; | |
413 c += 4; | |
414 } | |
415 else if (!g_ascii_strncasecmp(c, " ", 6)) | |
416 { | |
417 msg[retcount++] = ' '; | |
418 c += 6; | |
419 } | |
420 else if (!g_ascii_strncasecmp(c, """, 6)) | |
421 { | |
422 msg[retcount++] = '"'; | |
423 c += 6; | |
424 } | |
425 else if (!g_ascii_strncasecmp(c, "&", 5)) | |
426 { | |
427 msg[retcount++] = '&'; | |
428 c += 5; | |
429 } | |
430 else if (!g_ascii_strncasecmp(c, "'", 6)) | |
431 { | |
432 msg[retcount++] = '\''; | |
433 c += 6; | |
434 } | |
435 else | |
436 msg[retcount++] = *c++; | |
437 } | |
438 else | |
439 msg[retcount++] = *c++; | |
440 } | |
441 | |
442 if (fontface == NULL) | |
443 fontface = g_strdup("MS Sans Serif"); | |
444 | |
445 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c", | |
446 encode_spaces(fontface), | |
447 fonteffect, fontcolor, direction); | |
448 *message = g_strdup(msg); | |
449 | |
450 g_free(fontface); | |
451 g_free(msg); | |
452 } | |
453 | |
454 void | |
455 msn_parse_socket(const char *str, char **ret_host, int *ret_port) | |
456 { | |
457 char *host; | |
458 char *c; | |
459 int port; | |
460 | |
461 host = g_strdup(str); | |
462 | |
463 if ((c = strchr(host, ':')) != NULL){ | |
464 *c = '\0'; | |
465 port = atoi(c + 1); | |
466 }else{ | |
467 port = 1863; | |
468 } | |
469 | |
470 *ret_host = host; | |
471 *ret_port = port; | |
472 } | |
473 /*************************************************************************** | |
474 * MSN Time Related Funciton | |
475 ***************************************************************************/ | |
476 #if 0 | |
477 int | |
478 msn_convert_iso8601(const char *timestr,struct tm tm_time) | |
479 { | |
480 char temp[64]; | |
481 struct tm ctime; | |
482 time_t ts; | |
483 | |
484 gaim_debug_info("MaYuan","convert string is{%s}\n",timestr); | |
485 tzset(); | |
486 /*copy string first*/ | |
487 memset(temp, 0, sizeof(temp)); | |
488 strncpy(temp, timestr, strlen(timestr)); | |
489 | |
490 /*convert via strptime()*/ | |
491 memset(&ctime, 0, sizeof(struct tm)); | |
492 strptime(temp, "%d %b %Y %T %Z", &ctime); | |
493 ts = mktime(&ctime) - timezone; | |
494 localtime_r(&ts, tm_time); | |
495 } | |
496 #endif | |
497 | |
498 /*************************************************************************** | |
499 * MSN Challenge Computing Function | |
500 ***************************************************************************/ | |
501 /*check the edian of system*/ | |
502 int | |
503 isBigEndian(void) | |
504 { | |
505 short int word = 0x0100; | |
506 char *byte = (char *)&word; | |
507 | |
508 return(byte[0]); | |
509 } | |
510 | |
511 /*swap utility*/ | |
512 unsigned int | |
513 swapInt(unsigned int dw) | |
514 { | |
515 unsigned int tmp; | |
516 tmp = (dw & 0x000000FF); | |
517 tmp = ((dw & 0x0000FF00) >> 0x08) | (tmp << 0x08); | |
518 tmp = ((dw & 0x00FF0000) >> 0x10) | (tmp << 0x08); | |
519 tmp = ((dw & 0xFF000000) >> 0x18) | (tmp << 0x08); | |
520 return(tmp); | |
521 } | |
522 | |
523 /* | |
524 * Handle MSN Chanllege computation | |
525 *This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges | |
526 */ | |
527 #define BUFSIZE 256 | |
528 void | |
529 msn_handle_chl(char *input, char *output) | |
530 { | |
531 GaimCipher *cipher; | |
532 GaimCipherContext *context; | |
533 char *productKey = MSNP13_WLM_PRODUCT_KEY, | |
534 *productID = MSNP13_WLM_PRODUCT_ID, | |
535 *hexChars = "0123456789abcdef", | |
536 buf[BUFSIZE]; | |
537 unsigned char md5Hash[16], *newHash; | |
538 unsigned int *md5Parts, *chlStringParts, newHashParts[5]; | |
539 | |
540 long long nHigh=0, nLow=0; | |
541 | |
542 int i, bigEndian; | |
543 | |
544 /* Determine our endianess */ | |
545 bigEndian = isBigEndian(); | |
546 | |
547 /* Create the MD5 hash by using Gaim MD5 algorithm*/ | |
548 cipher = gaim_ciphers_find_cipher("md5"); | |
549 context = gaim_cipher_context_new(cipher, NULL); | |
550 | |
551 gaim_cipher_context_append(context, (const guchar *)input, | |
552 strlen(input)); | |
553 gaim_cipher_context_append(context, (const guchar *)productKey, | |
554 strlen(productKey)); | |
555 gaim_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL); | |
556 gaim_cipher_context_destroy(context); | |
557 | |
558 /* Split it into four integers */ | |
559 md5Parts = (unsigned int *)md5Hash; | |
560 for(i=0; i<4; i++){ | |
561 /* check for endianess */ | |
562 if(bigEndian) | |
563 md5Parts[i] = swapInt(md5Parts[i]); | |
564 | |
565 /* & each integer with 0x7FFFFFFF */ | |
566 /* and save one unmodified array for later */ | |
567 newHashParts[i] = md5Parts[i]; | |
568 md5Parts[i] &= 0x7FFFFFFF; | |
569 } | |
570 | |
571 /* make a new string and pad with '0' */ | |
572 snprintf(buf, BUFSIZE-5, "%s%s", input, productID); | |
573 i = strlen(buf); | |
574 memset(&buf[i], '0', 8 - (i % 8)); | |
575 buf[i + (8 - (i % 8))]='\0'; | |
576 | |
577 /* split into integers */ | |
578 chlStringParts = (unsigned int *)buf; | |
579 | |
580 /* this is magic */ | |
581 for (i=0; i<(strlen(buf)/4)-1; i+=2){ | |
582 long long temp; | |
583 | |
584 if(bigEndian){ | |
585 chlStringParts[i] = swapInt(chlStringParts[i]); | |
586 chlStringParts[i+1] = swapInt(chlStringParts[i+1]); | |
587 } | |
588 | |
589 temp=(md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF; | |
590 nHigh=(md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF; | |
591 nLow=nLow + nHigh + temp; | |
592 } | |
593 nHigh=(nHigh+md5Parts[1]) % 0x7FFFFFFF; | |
594 nLow=(nLow+md5Parts[3]) % 0x7FFFFFFF; | |
595 | |
596 newHashParts[0]^=nHigh; | |
597 newHashParts[1]^=nLow; | |
598 newHashParts[2]^=nHigh; | |
599 newHashParts[3]^=nLow; | |
600 | |
601 /* swap more bytes if big endian */ | |
602 for(i=0; i<4 && bigEndian; i++) | |
603 newHashParts[i] = swapInt(newHashParts[i]); | |
604 | |
605 /* make a string of the parts */ | |
606 newHash = (unsigned char *)newHashParts; | |
607 | |
608 /* convert to hexadecimal */ | |
609 for (i=0; i<16; i++) | |
610 { | |
611 output[i*2]=hexChars[(newHash[i]>>4)&0xF]; | |
612 output[(i*2)+1]=hexChars[newHash[i]&0xF]; | |
613 } | |
614 | |
615 output[32]='\0'; | |
616 | |
617 // gaim_debug_info("MaYuan","chl output{%s}\n",output); | |
618 } | |
619 | |
620 #if (!defined(_XOPEN_SOURCE))||defined(_WIN32) | |
621 | |
622 #ifndef __P | |
623 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__) | |
624 # define __P(args) args | |
625 # else | |
626 # define __P(args) () | |
627 # endif /* GCC. */ | |
628 #endif /* Not __P. */ | |
629 | |
630 #if defined(HAVE_LOCALTIME_R) && ! HAVE_LOCALTIME_R && ! defined localtime_r | |
631 # ifdef _LIBC | |
632 # define localtime_r __localtime_r | |
633 # else | |
634 /* Approximate localtime_r as best we can in its absence. */ | |
635 # define localtime_r my_localtime_r | |
636 static struct tm *localtime_r __P ((const time_t *, struct tm *)); | |
637 static struct tm * | |
638 localtime_r (t, tp) | |
639 const time_t *t; | |
640 struct tm *tp; | |
641 { | |
642 struct tm *l = localtime (t); | |
643 if (! l) | |
644 return 0; | |
645 *tp = *l; | |
646 return tp; | |
647 } | |
648 # endif /* ! _LIBC */ | |
649 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ | |
650 | |
651 | |
652 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL | |
653 | |
654 #if defined __GNUC__ && __GNUC__ >= 2 | |
655 # define match_string(cs1, s2) \ | |
656 ({ size_t len = strlen (cs1); \ | |
657 int result = strncasecmp ((cs1), (s2), len) == 0; \ | |
658 if (result) (s2) += len; \ | |
659 result; }) | |
660 #else | |
661 /* Oh come on. Get a reasonable compiler. */ | |
662 # define match_string(cs1, s2) \ | |
663 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1)) | |
664 #endif | |
665 | |
666 /* We intentionally do not use isdigit() for testing because this will | |
667 lead to problems with the wide character version. */ | |
668 #define get_number(from, to, n) \ | |
669 do { \ | |
670 int __n = n; \ | |
671 val = 0; \ | |
672 while (*rp == ' ') \ | |
673 ++rp; \ | |
674 if ((*rp < '0') || (*rp > '9')) \ | |
675 return NULL; \ | |
676 do { \ | |
677 val *= 10; \ | |
678 val += *rp++ - '0'; \ | |
679 } while ((--__n > 0) && (val * 10 <= to) && (*rp >= '0') && (*rp <= '9')); \ | |
680 if ((val < from) || (val > to)) \ | |
681 return NULL; \ | |
682 } while (0) | |
683 | |
684 #ifdef _NL_CURRENT | |
685 # define get_alt_number(from, to, n) \ | |
686 ({ \ | |
687 __label__ do_normal; \ | |
688 if (*decided != raw) \ | |
689 { \ | |
690 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \ | |
691 int __n = n; \ | |
692 int any = 0; \ | |
693 while (*rp == ' ') \ | |
694 ++rp; \ | |
695 val = 0; \ | |
696 do { \ | |
697 val *= 10; \ | |
698 while (*alts != '\0') \ | |
699 { \ | |
700 size_t len = strlen (alts); \ | |
701 if (strncasecmp (alts, rp, len) == 0) \ | |
702 break; \ | |
703 alts += len + 1; \ | |
704 ++val; \ | |
705 } \ | |
706 if (*alts == '\0') \ | |
707 { \ | |
708 if (*decided == not && ! any) \ | |
709 goto do_normal; \ | |
710 /* If we haven't read anything it's an error. */ \ | |
711 if (! any) \ | |
712 return NULL; \ | |
713 /* Correct the premature multiplication. */ \ | |
714 val /= 10; \ | |
715 break; \ | |
716 } \ | |
717 else \ | |
718 *decided = loc; \ | |
719 } while (--__n > 0 && val * 10 <= to); \ | |
720 if (val < from || val > to) \ | |
721 return NULL; \ | |
722 } \ | |
723 else \ | |
724 { \ | |
725 do_normal: \ | |
726 get_number (from, to, n); \ | |
727 } \ | |
728 0; \ | |
729 }) | |
730 #else | |
731 # define get_alt_number(from, to, n) \ | |
732 /* We don't have the alternate representation. */ \ | |
733 get_number(from, to, n) | |
734 #endif | |
735 | |
736 #define recursive(new_fmt) \ | |
737 (*(new_fmt) != '\0' \ | |
738 && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL) | |
739 | |
740 | |
741 #ifdef _LIBC | |
742 /* This is defined in locale/C-time.c in the GNU libc. */ | |
743 extern const struct locale_data _nl_C_LC_TIME; | |
744 extern const unsigned short int __mon_yday[2][13]; | |
745 | |
746 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string) | |
747 # define ab_weekday_name \ | |
748 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string) | |
749 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string) | |
750 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string) | |
751 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string) | |
752 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string) | |
753 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string) | |
754 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string) | |
755 # define HERE_T_FMT_AMPM \ | |
756 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string) | |
757 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string) | |
758 | |
759 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n) | |
760 #else | |
761 static char const weekday_name[][10] = | |
762 { | |
763 "Sunday", "Monday", "Tuesday", "Wednesday", | |
764 "Thursday", "Friday", "Saturday" | |
765 }; | |
766 static char const ab_weekday_name[][4] = | |
767 { | |
768 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
769 }; | |
770 static char const month_name[][10] = | |
771 { | |
772 "January", "February", "March", "April", "May", "June", | |
773 "July", "August", "September", "October", "November", "December" | |
774 }; | |
775 static char const ab_month_name[][4] = | |
776 { | |
777 "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
778 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
779 }; | |
780 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" | |
781 # define HERE_D_FMT "%m/%d/%y" | |
782 # define HERE_AM_STR "AM" | |
783 # define HERE_PM_STR "PM" | |
784 # define HERE_T_FMT_AMPM "%I:%M:%S %p" | |
785 # define HERE_T_FMT "%H:%M:%S" | |
786 | |
787 const unsigned short int __mon_yday[2][13] = | |
788 { | |
789 /* Normal years. */ | |
790 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | |
791 /* Leap years. */ | |
792 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | |
793 }; | |
794 #endif | |
795 | |
796 /* Status of lookup: do we use the locale data or the raw data? */ | |
797 enum locale_status { not, loc, raw }; | |
798 | |
799 | |
800 #ifndef __isleap | |
801 /* Nonzero if YEAR is a leap year (every 4 years, | |
802 except every 100th isn't, and every 400th is). */ | |
803 # define __isleap(year) \ | |
804 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) | |
805 #endif | |
806 | |
807 /* Compute the day of the week. */ | |
808 static void | |
809 day_of_the_week (struct tm *tm) | |
810 { | |
811 /* We know that January 1st 1970 was a Thursday (= 4). Compute the | |
812 the difference between this data in the one on TM and so determine | |
813 the weekday. */ | |
814 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); | |
815 int wday = (-473 | |
816 + (365 * (tm->tm_year - 70)) | |
817 + (corr_year / 4) | |
818 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) | |
819 + (((corr_year / 4) / 25) / 4) | |
820 + __mon_yday[0][tm->tm_mon] | |
821 + tm->tm_mday - 1); | |
822 tm->tm_wday = ((wday % 7) + 7) % 7; | |
823 } | |
824 | |
825 /* Compute the day of the year. */ | |
826 static void | |
827 day_of_the_year (struct tm *tm) | |
828 { | |
829 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] | |
830 + (tm->tm_mday - 1)); | |
831 } | |
832 | |
833 static char * | |
834 #ifdef _LIBC | |
835 internal_function | |
836 #endif | |
837 strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm, | |
838 enum locale_status *decided, int era_cnt)); | |
839 | |
840 static char * | |
841 #ifdef _LIBC | |
842 internal_function | |
843 #endif | |
844 strptime_internal (rp, fmt, tm, decided, era_cnt) | |
845 const char *rp; | |
846 const char *fmt; | |
847 struct tm *tm; | |
848 enum locale_status *decided; | |
849 int era_cnt; | |
850 { | |
851 const char *rp_backup; | |
852 int cnt; | |
853 size_t val; | |
854 int have_I, is_pm; | |
855 int century, want_century; | |
856 int want_era; | |
857 int have_wday, want_xday; | |
858 int have_yday; | |
859 int have_mon, have_mday; | |
860 #ifdef _NL_CURRENT | |
861 size_t num_eras; | |
862 #endif | |
863 struct era_entry *era; | |
864 | |
865 have_I = is_pm = 0; | |
866 century = -1; | |
867 want_century = 0; | |
868 want_era = 0; | |
869 era = NULL; | |
870 | |
871 have_wday = want_xday = have_yday = have_mon = have_mday = 0; | |
872 | |
873 while (*fmt != '\0') | |
874 { | |
875 /* A white space in the format string matches 0 more or white | |
876 space in the input string. */ | |
877 if (isspace (*fmt)) | |
878 { | |
879 while (isspace (*rp)) | |
880 ++rp; | |
881 ++fmt; | |
882 continue; | |
883 } | |
884 | |
885 /* Any character but `%' must be matched by the same character | |
886 in the iput string. */ | |
887 if (*fmt != '%') | |
888 { | |
889 match_char (*fmt++, *rp++); | |
890 continue; | |
891 } | |
892 | |
893 ++fmt; | |
894 #ifndef _NL_CURRENT | |
895 /* We need this for handling the `E' modifier. */ | |
896 start_over: | |
897 #endif | |
898 | |
899 /* Make back up of current processing pointer. */ | |
900 rp_backup = rp; | |
901 | |
902 switch (*fmt++) | |
903 { | |
904 case '%': | |
905 /* Match the `%' character itself. */ | |
906 match_char ('%', *rp++); | |
907 break; | |
908 case 'a': | |
909 case 'A': | |
910 /* Match day of week. */ | |
911 for (cnt = 0; cnt < 7; ++cnt) | |
912 { | |
913 #ifdef _NL_CURRENT | |
914 if (*decided !=raw) | |
915 { | |
916 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) | |
917 { | |
918 if (*decided == not | |
919 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), | |
920 weekday_name[cnt])) | |
921 *decided = loc; | |
922 break; | |
923 } | |
924 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) | |
925 { | |
926 if (*decided == not | |
927 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), | |
928 ab_weekday_name[cnt])) | |
929 *decided = loc; | |
930 break; | |
931 } | |
932 } | |
933 #endif | |
934 if (*decided != loc | |
935 && (match_string (weekday_name[cnt], rp) | |
936 || match_string (ab_weekday_name[cnt], rp))) | |
937 { | |
938 *decided = raw; | |
939 break; | |
940 } | |
941 } | |
942 if (cnt == 7) | |
943 /* Does not match a weekday name. */ | |
944 return NULL; | |
945 tm->tm_wday = cnt; | |
946 have_wday = 1; | |
947 break; | |
948 case 'b': | |
949 case 'B': | |
950 case 'h': | |
951 /* Match month name. */ | |
952 for (cnt = 0; cnt < 12; ++cnt) | |
953 { | |
954 #ifdef _NL_CURRENT | |
955 if (*decided !=raw) | |
956 { | |
957 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) | |
958 { | |
959 if (*decided == not | |
960 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), | |
961 month_name[cnt])) | |
962 *decided = loc; | |
963 break; | |
964 } | |
965 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) | |
966 { | |
967 if (*decided == not | |
968 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), | |
969 ab_month_name[cnt])) | |
970 *decided = loc; | |
971 break; | |
972 } | |
973 } | |
974 #endif | |
975 if (match_string (month_name[cnt], rp) | |
976 || match_string (ab_month_name[cnt], rp)) | |
977 { | |
978 *decided = raw; | |
979 break; | |
980 } | |
981 } | |
982 if (cnt == 12) | |
983 /* Does not match a month name. */ | |
984 return NULL; | |
985 tm->tm_mon = cnt; | |
986 want_xday = 1; | |
987 break; | |
988 case 'c': | |
989 /* Match locale's date and time format. */ | |
990 #ifdef _NL_CURRENT | |
991 if (*decided != raw) | |
992 { | |
993 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) | |
994 { | |
995 if (*decided == loc) | |
996 return NULL; | |
997 else | |
998 rp = rp_backup; | |
999 } | |
1000 else | |
1001 { | |
1002 if (*decided == not && | |
1003 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) | |
1004 *decided = loc; | |
1005 want_xday = 1; | |
1006 break; | |
1007 } | |
1008 *decided = raw; | |
1009 } | |
1010 #endif | |
1011 if (!recursive (HERE_D_T_FMT)) | |
1012 return NULL; | |
1013 want_xday = 1; | |
1014 break; | |
1015 case 'C': | |
1016 /* Match century number. */ | |
1017 #ifdef _NL_CURRENT | |
1018 match_century: | |
1019 #endif | |
1020 get_number (0, 99, 2); | |
1021 century = val; | |
1022 want_xday = 1; | |
1023 break; | |
1024 case 'd': | |
1025 case 'e': | |
1026 /* Match day of month. */ | |
1027 get_number (1, 31, 2); | |
1028 tm->tm_mday = val; | |
1029 have_mday = 1; | |
1030 want_xday = 1; | |
1031 break; | |
1032 case 'F': | |
1033 if (!recursive ("%Y-%m-%d")) | |
1034 return NULL; | |
1035 want_xday = 1; | |
1036 break; | |
1037 case 'x': | |
1038 #ifdef _NL_CURRENT | |
1039 if (*decided != raw) | |
1040 { | |
1041 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) | |
1042 { | |
1043 if (*decided == loc) | |
1044 return NULL; | |
1045 else | |
1046 rp = rp_backup; | |
1047 } | |
1048 else | |
1049 { | |
1050 if (*decided == not | |
1051 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) | |
1052 *decided = loc; | |
1053 want_xday = 1; | |
1054 break; | |
1055 } | |
1056 *decided = raw; | |
1057 } | |
1058 #endif | |
1059 /* Fall through. */ | |
1060 case 'D': | |
1061 /* Match standard day format. */ | |
1062 if (!recursive (HERE_D_FMT)) | |
1063 return NULL; | |
1064 want_xday = 1; | |
1065 break; | |
1066 case 'k': | |
1067 case 'H': | |
1068 /* Match hour in 24-hour clock. */ | |
1069 get_number (0, 23, 2); | |
1070 tm->tm_hour = val; | |
1071 have_I = 0; | |
1072 break; | |
1073 case 'I': | |
1074 /* Match hour in 12-hour clock. */ | |
1075 get_number (1, 12, 2); | |
1076 tm->tm_hour = val % 12; | |
1077 have_I = 1; | |
1078 break; | |
1079 case 'j': | |
1080 /* Match day number of year. */ | |
1081 get_number (1, 366, 3); | |
1082 tm->tm_yday = val - 1; | |
1083 have_yday = 1; | |
1084 break; | |
1085 case 'm': | |
1086 /* Match number of month. */ | |
1087 get_number (1, 12, 2); | |
1088 tm->tm_mon = val - 1; | |
1089 have_mon = 1; | |
1090 want_xday = 1; | |
1091 break; | |
1092 case 'M': | |
1093 /* Match minute. */ | |
1094 get_number (0, 59, 2); | |
1095 tm->tm_min = val; | |
1096 break; | |
1097 case 'n': | |
1098 case 't': | |
1099 /* Match any white space. */ | |
1100 while (isspace (*rp)) | |
1101 ++rp; | |
1102 break; | |
1103 case 'p': | |
1104 /* Match locale's equivalent of AM/PM. */ | |
1105 #ifdef _NL_CURRENT | |
1106 if (*decided != raw) | |
1107 { | |
1108 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) | |
1109 { | |
1110 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) | |
1111 *decided = loc; | |
1112 break; | |
1113 } | |
1114 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) | |
1115 { | |
1116 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) | |
1117 *decided = loc; | |
1118 is_pm = 1; | |
1119 break; | |
1120 } | |
1121 *decided = raw; | |
1122 } | |
1123 #endif | |
1124 if (!match_string (HERE_AM_STR, rp)) | |
1125 if (match_string (HERE_PM_STR, rp)) | |
1126 is_pm = 1; | |
1127 else | |
1128 return NULL; | |
1129 break; | |
1130 case 'r': | |
1131 #ifdef _NL_CURRENT | |
1132 if (*decided != raw) | |
1133 { | |
1134 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) | |
1135 { | |
1136 if (*decided == loc) | |
1137 return NULL; | |
1138 else | |
1139 rp = rp_backup; | |
1140 } | |
1141 else | |
1142 { | |
1143 if (*decided == not && | |
1144 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), | |
1145 HERE_T_FMT_AMPM)) | |
1146 *decided = loc; | |
1147 break; | |
1148 } | |
1149 *decided = raw; | |
1150 } | |
1151 #endif | |
1152 if (!recursive (HERE_T_FMT_AMPM)) | |
1153 return NULL; | |
1154 break; | |
1155 case 'R': | |
1156 if (!recursive ("%H:%M")) | |
1157 return NULL; | |
1158 break; | |
1159 case 's': | |
1160 { | |
1161 /* The number of seconds may be very high so we cannot use | |
1162 the `get_number' macro. Instead read the number | |
1163 character for character and construct the result while | |
1164 doing this. */ | |
1165 time_t secs = 0; | |
1166 if (*rp < '0' || *rp > '9') | |
1167 /* We need at least one digit. */ | |
1168 return NULL; | |
1169 | |
1170 do | |
1171 { | |
1172 secs *= 10; | |
1173 secs += *rp++ - '0'; | |
1174 } | |
1175 while (*rp >= '0' && *rp <= '9'); | |
1176 | |
1177 if (localtime_r (&secs, tm) == NULL) | |
1178 /* Error in function. */ | |
1179 return NULL; | |
1180 } | |
1181 break; | |
1182 case 'S': | |
1183 get_number (0, 61, 2); | |
1184 tm->tm_sec = val; | |
1185 break; | |
1186 case 'X': | |
1187 #ifdef _NL_CURRENT | |
1188 if (*decided != raw) | |
1189 { | |
1190 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) | |
1191 { | |
1192 if (*decided == loc) | |
1193 return NULL; | |
1194 else | |
1195 rp = rp_backup; | |
1196 } | |
1197 else | |
1198 { | |
1199 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) | |
1200 *decided = loc; | |
1201 break; | |
1202 } | |
1203 *decided = raw; | |
1204 } | |
1205 #endif | |
1206 /* Fall through. */ | |
1207 case 'T': | |
1208 if (!recursive (HERE_T_FMT)) | |
1209 return NULL; | |
1210 break; | |
1211 case 'u': | |
1212 get_number (1, 7, 1); | |
1213 tm->tm_wday = val % 7; | |
1214 have_wday = 1; | |
1215 break; | |
1216 case 'g': | |
1217 get_number (0, 99, 2); | |
1218 /* XXX This cannot determine any field in TM. */ | |
1219 break; | |
1220 case 'G': | |
1221 if (*rp < '0' || *rp > '9') | |
1222 return NULL; | |
1223 /* XXX Ignore the number since we would need some more | |
1224 information to compute a real date. */ | |
1225 do | |
1226 ++rp; | |
1227 while (*rp >= '0' && *rp <= '9'); | |
1228 break; | |
1229 case 'U': | |
1230 case 'V': | |
1231 case 'W': | |
1232 get_number (0, 53, 2); | |
1233 /* XXX This cannot determine any field in TM without some | |
1234 information. */ | |
1235 break; | |
1236 case 'w': | |
1237 /* Match number of weekday. */ | |
1238 get_number (0, 6, 1); | |
1239 tm->tm_wday = val; | |
1240 have_wday = 1; | |
1241 break; | |
1242 case 'y': | |
1243 #ifdef _NL_CURRENT | |
1244 match_year_in_century: | |
1245 #endif | |
1246 /* Match year within century. */ | |
1247 get_number (0, 99, 2); | |
1248 /* The "Year 2000: The Millennium Rollover" paper suggests that | |
1249 values in the range 69-99 refer to the twentieth century. */ | |
1250 tm->tm_year = val >= 69 ? val : val + 100; | |
1251 /* Indicate that we want to use the century, if specified. */ | |
1252 want_century = 1; | |
1253 want_xday = 1; | |
1254 break; | |
1255 case 'Y': | |
1256 /* Match year including century number. */ | |
1257 get_number (0, 9999, 4); | |
1258 tm->tm_year = val - 1900; | |
1259 want_century = 0; | |
1260 want_xday = 1; | |
1261 break; | |
1262 case 'Z': | |
1263 /* XXX How to handle this? */ | |
1264 break; | |
1265 case 'E': | |
1266 #ifdef _NL_CURRENT | |
1267 switch (*fmt++) | |
1268 { | |
1269 case 'c': | |
1270 /* Match locale's alternate date and time format. */ | |
1271 if (*decided != raw) | |
1272 { | |
1273 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); | |
1274 | |
1275 if (*fmt == '\0') | |
1276 fmt = _NL_CURRENT (LC_TIME, D_T_FMT); | |
1277 | |
1278 if (!recursive (fmt)) | |
1279 { | |
1280 if (*decided == loc) | |
1281 return NULL; | |
1282 else | |
1283 rp = rp_backup; | |
1284 } | |
1285 else | |
1286 { | |
1287 if (strcmp (fmt, HERE_D_T_FMT)) | |
1288 *decided = loc; | |
1289 want_xday = 1; | |
1290 break; | |
1291 } | |
1292 *decided = raw; | |
1293 } | |
1294 /* The C locale has no era information, so use the | |
1295 normal representation. */ | |
1296 if (!recursive (HERE_D_T_FMT)) | |
1297 return NULL; | |
1298 want_xday = 1; | |
1299 break; | |
1300 case 'C': | |
1301 if (*decided != raw) | |
1302 { | |
1303 if (era_cnt >= 0) | |
1304 { | |
1305 era = _nl_select_era_entry (era_cnt); | |
1306 if (match_string (era->era_name, rp)) | |
1307 { | |
1308 *decided = loc; | |
1309 break; | |
1310 } | |
1311 else | |
1312 return NULL; | |
1313 } | |
1314 else | |
1315 { | |
1316 num_eras = _NL_CURRENT_WORD (LC_TIME, | |
1317 _NL_TIME_ERA_NUM_ENTRIES); | |
1318 for (era_cnt = 0; era_cnt < (int) num_eras; | |
1319 ++era_cnt, rp = rp_backup) | |
1320 { | |
1321 era = _nl_select_era_entry (era_cnt); | |
1322 if (match_string (era->era_name, rp)) | |
1323 { | |
1324 *decided = loc; | |
1325 break; | |
1326 } | |
1327 } | |
1328 if (era_cnt == (int) num_eras) | |
1329 { | |
1330 era_cnt = -1; | |
1331 if (*decided == loc) | |
1332 return NULL; | |
1333 } | |
1334 else | |
1335 break; | |
1336 } | |
1337 | |
1338 *decided = raw; | |
1339 } | |
1340 /* The C locale has no era information, so use the | |
1341 normal representation. */ | |
1342 goto match_century; | |
1343 case 'y': | |
1344 if (*decided == raw) | |
1345 goto match_year_in_century; | |
1346 | |
1347 get_number(0, 9999, 4); | |
1348 tm->tm_year = val; | |
1349 want_era = 1; | |
1350 want_xday = 1; | |
1351 break; | |
1352 case 'Y': | |
1353 if (*decided != raw) | |
1354 { | |
1355 num_eras = _NL_CURRENT_WORD (LC_TIME, | |
1356 _NL_TIME_ERA_NUM_ENTRIES); | |
1357 for (era_cnt = 0; era_cnt < (int) num_eras; | |
1358 ++era_cnt, rp = rp_backup) | |
1359 { | |
1360 era = _nl_select_era_entry (era_cnt); | |
1361 if (recursive (era->era_format)) | |
1362 break; | |
1363 } | |
1364 if (era_cnt == (int) num_eras) | |
1365 { | |
1366 era_cnt = -1; | |
1367 if (*decided == loc) | |
1368 return NULL; | |
1369 else | |
1370 rp = rp_backup; | |
1371 } | |
1372 else | |
1373 { | |
1374 *decided = loc; | |
1375 era_cnt = -1; | |
1376 break; | |
1377 } | |
1378 | |
1379 *decided = raw; | |
1380 } | |
1381 get_number (0, 9999, 4); | |
1382 tm->tm_year = val - 1900; | |
1383 want_century = 0; | |
1384 want_xday = 1; | |
1385 break; | |
1386 case 'x': | |
1387 if (*decided != raw) | |
1388 { | |
1389 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); | |
1390 | |
1391 if (*fmt == '\0') | |
1392 fmt = _NL_CURRENT (LC_TIME, D_FMT); | |
1393 | |
1394 if (!recursive (fmt)) | |
1395 { | |
1396 if (*decided == loc) | |
1397 return NULL; | |
1398 else | |
1399 rp = rp_backup; | |
1400 } | |
1401 else | |
1402 { | |
1403 if (strcmp (fmt, HERE_D_FMT)) | |
1404 *decided = loc; | |
1405 break; | |
1406 } | |
1407 *decided = raw; | |
1408 } | |
1409 if (!recursive (HERE_D_FMT)) | |
1410 return NULL; | |
1411 break; | |
1412 case 'X': | |
1413 if (*decided != raw) | |
1414 { | |
1415 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); | |
1416 | |
1417 if (*fmt == '\0') | |
1418 fmt = _NL_CURRENT (LC_TIME, T_FMT); | |
1419 | |
1420 if (!recursive (fmt)) | |
1421 { | |
1422 if (*decided == loc) | |
1423 return NULL; | |
1424 else | |
1425 rp = rp_backup; | |
1426 } | |
1427 else | |
1428 { | |
1429 if (strcmp (fmt, HERE_T_FMT)) | |
1430 *decided = loc; | |
1431 break; | |
1432 } | |
1433 *decided = raw; | |
1434 } | |
1435 if (!recursive (HERE_T_FMT)) | |
1436 return NULL; | |
1437 break; | |
1438 default: | |
1439 return NULL; | |
1440 } | |
1441 break; | |
1442 #else | |
1443 /* We have no information about the era format. Just use | |
1444 the normal format. */ | |
1445 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' | |
1446 && *fmt != 'x' && *fmt != 'X') | |
1447 /* This is an illegal format. */ | |
1448 return NULL; | |
1449 | |
1450 goto start_over; | |
1451 #endif | |
1452 case 'O': | |
1453 switch (*fmt++) | |
1454 { | |
1455 case 'd': | |
1456 case 'e': | |
1457 /* Match day of month using alternate numeric symbols. */ | |
1458 get_alt_number (1, 31, 2); | |
1459 tm->tm_mday = val; | |
1460 have_mday = 1; | |
1461 want_xday = 1; | |
1462 break; | |
1463 case 'H': | |
1464 /* Match hour in 24-hour clock using alternate numeric | |
1465 symbols. */ | |
1466 get_alt_number (0, 23, 2); | |
1467 tm->tm_hour = val; | |
1468 have_I = 0; | |
1469 break; | |
1470 case 'I': | |
1471 /* Match hour in 12-hour clock using alternate numeric | |
1472 symbols. */ | |
1473 get_alt_number (1, 12, 2); | |
1474 tm->tm_hour = val - 1; | |
1475 have_I = 1; | |
1476 break; | |
1477 case 'm': | |
1478 /* Match month using alternate numeric symbols. */ | |
1479 get_alt_number (1, 12, 2); | |
1480 tm->tm_mon = val - 1; | |
1481 have_mon = 1; | |
1482 want_xday = 1; | |
1483 break; | |
1484 case 'M': | |
1485 /* Match minutes using alternate numeric symbols. */ | |
1486 get_alt_number (0, 59, 2); | |
1487 tm->tm_min = val; | |
1488 break; | |
1489 case 'S': | |
1490 /* Match seconds using alternate numeric symbols. */ | |
1491 get_alt_number (0, 61, 2); | |
1492 tm->tm_sec = val; | |
1493 break; | |
1494 case 'U': | |
1495 case 'V': | |
1496 case 'W': | |
1497 get_alt_number (0, 53, 2); | |
1498 /* XXX This cannot determine any field in TM without | |
1499 further information. */ | |
1500 break; | |
1501 case 'w': | |
1502 /* Match number of weekday using alternate numeric symbols. */ | |
1503 get_alt_number (0, 6, 1); | |
1504 tm->tm_wday = val; | |
1505 have_wday = 1; | |
1506 break; | |
1507 case 'y': | |
1508 /* Match year within century using alternate numeric symbols. */ | |
1509 get_alt_number (0, 99, 2); | |
1510 tm->tm_year = val >= 69 ? val : val + 100; | |
1511 want_xday = 1; | |
1512 break; | |
1513 default: | |
1514 return NULL; | |
1515 } | |
1516 break; | |
1517 default: | |
1518 return NULL; | |
1519 } | |
1520 } | |
1521 | |
1522 if (have_I && is_pm) | |
1523 tm->tm_hour += 12; | |
1524 | |
1525 if (century != -1) | |
1526 { | |
1527 if (want_century) | |
1528 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; | |
1529 else | |
1530 /* Only the century, but not the year. Strange, but so be it. */ | |
1531 tm->tm_year = (century - 19) * 100; | |
1532 } | |
1533 | |
1534 #ifdef _NL_CURRENT | |
1535 if (era_cnt != -1) | |
1536 { | |
1537 era = _nl_select_era_entry(era_cnt); | |
1538 if (want_era) | |
1539 tm->tm_year = (era->start_date[0] | |
1540 + ((tm->tm_year - era->offset) | |
1541 * era->absolute_direction)); | |
1542 else | |
1543 /* Era start year assumed. */ | |
1544 tm->tm_year = era->start_date[0]; | |
1545 } | |
1546 else | |
1547 #endif | |
1548 if (want_era) | |
1549 return NULL; | |
1550 | |
1551 if (want_xday && !have_wday) | |
1552 { | |
1553 if ( !(have_mon && have_mday) && have_yday) | |
1554 { | |
1555 /* We don't have tm_mon and/or tm_mday, compute them. */ | |
1556 int t_mon = 0; | |
1557 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) | |
1558 t_mon++; | |
1559 if (!have_mon) | |
1560 tm->tm_mon = t_mon - 1; | |
1561 if (!have_mday) | |
1562 tm->tm_mday = | |
1563 (tm->tm_yday | |
1564 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); | |
1565 } | |
1566 day_of_the_week (tm); | |
1567 } | |
1568 if (want_xday && !have_yday) | |
1569 day_of_the_year (tm); | |
1570 | |
1571 return (char *) rp; | |
1572 } | |
1573 | |
1574 | |
1575 char * | |
1576 msn_strptime (buf, format, tm) | |
1577 const char *buf; | |
1578 const char *format; | |
1579 struct tm *tm; | |
1580 { | |
1581 enum locale_status decided; | |
1582 | |
1583 #ifdef _NL_CURRENT | |
1584 decided = not; | |
1585 #else | |
1586 decided = raw; | |
1587 #endif | |
1588 return strptime_internal (buf, format, tm, &decided, -1); | |
1589 } | |
1590 #else | |
1591 #define msn_strptime strptime | |
1592 #endif |