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