1 /* Parse a string, yielding a struct partime that describes it. */ 2 3 /* Copyright 1993, 1994, 1995 Paul Eggert 4 Distributed under license by the Free Software Foundation, Inc. 5 6 This file is part of RCS. 7 8 RCS is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2, or (at your option) 11 any later version. 12 13 RCS is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with RCS; see the file COPYING. 20 If not, write to the Free Software Foundation, 21 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 23 Report problems and direct all questions to: 24 25 rcs-bugs@cs.purdue.edu 26 27 */ 28 29 /* 30 * $FreeBSD: src/gnu/usr.bin/rcs/lib/partime.c,v 1.6 1999/08/27 23:36:44 peter Exp $ 31 * $DragonFly: src/gnu/usr.bin/rcs/lib/partime.c,v 1.2 2003/06/17 04:25:47 dillon Exp $ 32 */ 33 34 #if has_conf_h 35 # include "conf.h" 36 #else 37 # ifdef __STDC__ 38 # define P(x) x 39 # else 40 # define const 41 # define P(x) () 42 # endif 43 # include <limits.h> 44 # include <time.h> 45 #endif 46 47 #include <ctype.h> 48 #undef isdigit 49 #define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */ 50 51 #include "partime.h" 52 53 /* Lookup tables for names of months, weekdays, time zones. */ 54 55 #define NAME_LENGTH_MAXIMUM 4 56 57 struct name_val { 58 char name[NAME_LENGTH_MAXIMUM]; 59 int val; 60 }; 61 62 63 static char const *parse_decimal P((char const*,int,int,int,int,int*,int*)); 64 static char const *parse_fixed P((char const*,int,int*)); 65 static char const *parse_pattern_letter P((char const*,int,struct partime*)); 66 static char const *parse_prefix P((char const*,struct partime*,int*)); 67 static char const *parse_ranged P((char const*,int,int,int,int*)); 68 static int lookup P((char const*,struct name_val const[])); 69 static int merge_partime P((struct partime*, struct partime const*)); 70 static void undefine P((struct partime*)); 71 72 73 static struct name_val const month_names[] = { 74 {"jan",0}, {"feb",1}, {"mar",2}, {"apr",3}, {"may",4}, {"jun",5}, 75 {"jul",6}, {"aug",7}, {"sep",8}, {"oct",9}, {"nov",10}, {"dec",11}, 76 {"", TM_UNDEFINED} 77 }; 78 79 static struct name_val const weekday_names[] = { 80 {"sun",0}, {"mon",1}, {"tue",2}, {"wed",3}, {"thu",4}, {"fri",5}, {"sat",6}, 81 {"", TM_UNDEFINED} 82 }; 83 84 #define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) 85 #define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) 86 #define zs(t,s) {s, hr60(t)} 87 #define zd(t,s,d) zs(t, s), zs((t)+100, d) 88 89 static struct name_val const zone_names[] = { 90 zs(-1000, "hst"), /* Hawaii */ 91 zd(-1000,"hast","hadt"),/* Hawaii-Aleutian */ 92 zd(- 900,"akst","akdt"),/* Alaska */ 93 zd(- 800, "pst", "pdt"),/* Pacific */ 94 zd(- 700, "mst", "mdt"),/* Mountain */ 95 zd(- 600, "cst", "cdt"),/* Central */ 96 zd(- 500, "est", "edt"),/* Eastern */ 97 zd(- 400, "ast", "adt"),/* Atlantic */ 98 zd(- 330, "nst", "ndt"),/* Newfoundland */ 99 zs( 000, "utc"), /* Coordinated Universal */ 100 zs( 000, "cut"), /* " */ 101 zs( 000, "ut"), /* Universal */ 102 zs( 000, "z"), /* Zulu (required by ISO 8601) */ 103 zd( 000, "gmt", "bst"),/* Greenwich Mean, British Summer */ 104 zs( 000, "wet"), /* Western Europe */ 105 zs( 100, "met"), /* Middle Europe */ 106 zs( 100, "cet"), /* Central Europe */ 107 zs( 200, "eet"), /* Eastern Europe */ 108 zs( 530, "ist"), /* India */ 109 zd( 900, "jst", "jdt"),/* Japan */ 110 zd( 900, "kst", "kdt"),/* Korea */ 111 zd( 1200,"nzst","nzdt"),/* New Zealand */ 112 { "lt", 1 }, 113 #if 0 114 /* The following names are duplicates or are not well attested. */ 115 zs(-1100, "sst"), /* Samoa */ 116 zs(-1000, "tht"), /* Tahiti */ 117 zs(- 930, "mqt"), /* Marquesas */ 118 zs(- 900, "gbt"), /* Gambier */ 119 zd(- 900, "yst", "ydt"),/* Yukon - name is no longer used */ 120 zs(- 830, "pit"), /* Pitcairn */ 121 zd(- 500, "cst", "cdt"),/* Cuba */ 122 zd(- 500, "ast", "adt"),/* Acre */ 123 zd(- 400, "wst", "wdt"),/* Western Brazil */ 124 zd(- 400, "ast", "adt"),/* Andes */ 125 zd(- 400, "cst", "cdt"),/* Chile */ 126 zs(- 300, "wgt"), /* Western Greenland */ 127 zd(- 300, "est", "edt"),/* Eastern South America */ 128 zs(- 300, "mgt"), /* Middle Greenland */ 129 zd(- 200, "fst", "fdt"),/* Fernando de Noronha */ 130 zs(- 100, "egt"), /* Eastern Greenland */ 131 zs(- 100, "aat"), /* Atlantic Africa */ 132 zs(- 100, "act"), /* Azores and Canaries */ 133 zs( 000, "wat"), /* West Africa */ 134 zs( 100, "cat"), /* Central Africa */ 135 zd( 100, "mez","mesz"),/* Mittel-Europaeische Zeit */ 136 zs( 200, "sat"), /* South Africa */ 137 zd( 200, "ist", "idt"),/* Israel */ 138 zs( 300, "eat"), /* East Africa */ 139 zd( 300, "ast", "adt"),/* Arabia */ 140 zd( 300, "msk", "msd"),/* Moscow */ 141 zd( 330, "ist", "idt"),/* Iran */ 142 zs( 400, "gst"), /* Gulf */ 143 zs( 400, "smt"), /* Seychelles & Mascarene */ 144 zd( 400, "esk", "esd"),/* Yekaterinburg */ 145 zd( 400, "bsk", "bsd"),/* Baku */ 146 zs( 430, "aft"), /* Afghanistan */ 147 zd( 500, "osk", "osd"),/* Omsk */ 148 zs( 500, "pkt"), /* Pakistan */ 149 zd( 500, "tsk", "tsd"),/* Tashkent */ 150 zs( 545, "npt"), /* Nepal */ 151 zs( 600, "bgt"), /* Bangladesh */ 152 zd( 600, "nsk", "nsd"),/* Novosibirsk */ 153 zs( 630, "bmt"), /* Burma */ 154 zs( 630, "cct"), /* Cocos */ 155 zs( 700, "ict"), /* Indochina */ 156 zs( 700, "jvt"), /* Java */ 157 zd( 700, "isk", "isd"),/* Irkutsk */ 158 zs( 800, "hkt"), /* Hong Kong */ 159 zs( 800, "pst"), /* Philippines */ 160 zs( 800, "sgt"), /* Singapore */ 161 zd( 800, "cst", "cdt"),/* China */ 162 zd( 800, "ust", "udt"),/* Ulan Bator */ 163 zd( 800, "wst", "wst"),/* Western Australia */ 164 zd( 800, "ysk", "ysd"),/* Yakutsk */ 165 zs( 900, "blt"), /* Belau */ 166 zs( 900, "mlt"), /* Moluccas */ 167 zd( 900, "vsk", "vsd"),/* Vladivostok */ 168 zd( 930, "cst", "cst"),/* Central Australia */ 169 zs( 1000, "gst"), /* Guam */ 170 zd( 1000, "gsk", "gsd"),/* Magadan */ 171 zd( 1000, "est", "est"),/* Eastern Australia */ 172 zd( 1100,"lhst","lhst"),/* Lord Howe */ 173 zd( 1100, "psk", "psd"),/* Petropavlovsk-Kamchatski */ 174 zs( 1100,"ncst"), /* New Caledonia */ 175 zs( 1130,"nrft"), /* Norfolk */ 176 zd( 1200, "ask", "asd"),/* Anadyr */ 177 zs( 1245,"nz-chat"), /* Chatham */ 178 zs( 1300, "tgt"), /* Tongatapu */ 179 #endif 180 {"", -1} 181 }; 182 183 static int 184 lookup (s, table) 185 char const *s; 186 struct name_val const table[]; 187 /* Look for a prefix of S in TABLE, returning val for first matching entry. */ 188 { 189 int j; 190 char buf[NAME_LENGTH_MAXIMUM]; 191 192 for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) { 193 unsigned char c = *s++; 194 buf[j] = isupper (c) ? tolower (c) : c; 195 if (!isalpha (c)) 196 break; 197 } 198 for (; table[0].name[0]; table++) 199 for (j = 0; buf[j] == table[0].name[j]; ) 200 if (++j == NAME_LENGTH_MAXIMUM || !table[0].name[j]) 201 goto done; 202 done: 203 return table[0].val; 204 } 205 206 207 static void 208 undefine (t) struct partime *t; 209 /* Set *T to ``undefined'' values. */ 210 { 211 t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon 212 = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday 213 = t->ymodulus = t->yweek 214 = TM_UNDEFINED; 215 t->zone = TM_UNDEFINED_ZONE; 216 } 217 218 /* 219 * Array of patterns to look for in a date string. 220 * Order is important: we look for the first matching pattern 221 * whose values do not contradict values that we already know about. 222 * See `parse_pattern_letter' below for the meaning of the pattern codes. 223 */ 224 static char const * const patterns[] = { 225 /* 226 * These traditional patterns must come first, 227 * to prevent an ISO 8601 format from misinterpreting their prefixes. 228 */ 229 "E_n_y", "x", /* RFC 822 */ 230 "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ 231 "y/N/D$", /* traditional RCS */ 232 233 /* ISO 8601:1988 formats, generalized a bit. */ 234 "y-N-D$", "4ND$", "Y-N$", 235 "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", 236 "--N$", "---D$", "DT", 237 "Y-d$", "4d$", "R=d$", "-d$", "dT", 238 "y-W-X", "yWX", "y=W", 239 "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", 240 "-w-X", "w-XT", "---X$", "XT", "4$", 241 "T", 242 "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", 243 "Y", "Z", 244 245 0 246 }; 247 248 static char const * 249 parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi; 250 /* 251 * Parse an initial prefix of STR, setting *T accordingly. 252 * Return the first character after the prefix, or 0 if it couldn't be parsed. 253 * Start with pattern *PI; if success, set *PI to the next pattern to try. 254 * Set *PI to -1 if we know there are no more patterns to try; 255 * if *PI is initially negative, give up immediately. 256 */ 257 { 258 int i = *pi; 259 char const *pat; 260 unsigned char c; 261 262 if (i < 0) 263 return 0; 264 265 /* Remove initial noise. */ 266 while (!isalnum (c = *str) && c != '-' && c != '+') { 267 if (!c) { 268 undefine (t); 269 *pi = -1; 270 return str; 271 } 272 str++; 273 } 274 275 /* Try a pattern until one succeeds. */ 276 while ((pat = patterns[i++]) != 0) { 277 char const *s = str; 278 undefine (t); 279 do { 280 if (!(c = *pat++)) { 281 *pi = i; 282 return s; 283 } 284 } while ((s = parse_pattern_letter (s, c, t)) != 0); 285 } 286 287 return 0; 288 } 289 290 static char const * 291 parse_fixed (s, digits, res) char const *s; int digits, *res; 292 /* 293 * Parse an initial prefix of S of length DIGITS; it must be a number. 294 * Store the parsed number into *RES. 295 * Return the first character after the prefix, or 0 if it couldn't be parsed. 296 */ 297 { 298 int n = 0; 299 char const *lim = s + digits; 300 while (s < lim) { 301 unsigned d = *s++ - '0'; 302 if (9 < d) 303 return 0; 304 n = 10*n + d; 305 } 306 *res = n; 307 return s; 308 } 309 310 static char const * 311 parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res; 312 /* 313 * Parse an initial prefix of S of length DIGITS; 314 * it must be a number in the range LO through HI. 315 * Store the parsed number into *RES. 316 * Return the first character after the prefix, or 0 if it couldn't be parsed. 317 */ 318 { 319 s = parse_fixed (s, digits, res); 320 return s && lo<=*res && *res<=hi ? s : 0; 321 } 322 323 static char const * 324 parse_decimal (s, digits, lo, hi, resolution, res, fres) 325 char const *s; 326 int digits, lo, hi, resolution, *res, *fres; 327 /* 328 * Parse an initial prefix of S of length DIGITS; 329 * it must be a number in the range LO through HI 330 * and it may be followed by a fraction that is to be computed using RESOLUTION. 331 * Store the parsed number into *RES; store the fraction times RESOLUTION, 332 * rounded to the nearest integer, into *FRES. 333 * Return the first character after the prefix, or 0 if it couldn't be parsed. 334 */ 335 { 336 s = parse_fixed (s, digits, res); 337 if (s && lo<=*res && *res<=hi) { 338 int f = 0; 339 if ((s[0]==',' || s[0]=='.') && isdigit ((unsigned char) s[1])) { 340 char const *s1 = ++s; 341 int num10 = 0, denom10 = 10, product; 342 while (isdigit ((unsigned char) *++s)) 343 denom10 *= 10; 344 s = parse_fixed (s1, s - s1, &num10); 345 product = num10*resolution; 346 f = (product + (denom10>>1)) / denom10; 347 f -= f & (product%denom10 == denom10>>1); /* round to even */ 348 if (f < 0 || product/resolution != num10) 349 return 0; /* overflow */ 350 } 351 *fres = f; 352 return s; 353 } 354 return 0; 355 } 356 357 char * 358 parzone (s, zone) char const *s; long *zone; 359 /* 360 * Parse an initial prefix of S; it must denote a time zone. 361 * Set *ZONE to the number of seconds east of GMT, 362 * or to TM_LOCAL_ZONE if it is the local time zone. 363 * Return the first character after the prefix, or 0 if it couldn't be parsed. 364 */ 365 { 366 char sign; 367 int hh, mm, ss; 368 int minutesEastOfUTC; 369 long offset, z; 370 371 /* 372 * The formats are LT, n, n DST, nDST, no, o 373 * where n is a time zone name 374 * and o is a time zone offset of the form [-+]hh[:mm[:ss]]. 375 */ 376 switch (*s) { 377 case '-': case '+': 378 z = 0; 379 break; 380 381 default: 382 minutesEastOfUTC = lookup (s, zone_names); 383 if (minutesEastOfUTC == -1) 384 return 0; 385 386 /* Don't bother to check rest of spelling. */ 387 while (isalpha ((unsigned char) *s)) 388 s++; 389 390 /* Don't modify LT. */ 391 if (minutesEastOfUTC == 1) { 392 *zone = TM_LOCAL_ZONE; 393 return (char *) s; 394 } 395 396 z = minutesEastOfUTC * 60L; 397 398 /* Look for trailing " DST". */ 399 if ( 400 (s[-1]=='T' || s[-1]=='t') && 401 (s[-2]=='S' || s[-2]=='s') && 402 (s[-3]=='D' || s[-3]=='t') 403 ) 404 goto trailing_dst; 405 while (isspace ((unsigned char) *s)) 406 s++; 407 if ( 408 (s[0]=='D' || s[0]=='d') && 409 (s[1]=='S' || s[1]=='s') && 410 (s[2]=='T' || s[2]=='t') 411 ) { 412 s += 3; 413 trailing_dst: 414 *zone = z + 60*60; 415 return (char *) s; 416 } 417 418 switch (*s) { 419 case '-': case '+': break; 420 default: return (char *) s; 421 } 422 } 423 sign = *s++; 424 425 if (!(s = parse_ranged (s, 2, 0, 23, &hh))) 426 return 0; 427 mm = ss = 0; 428 if (*s == ':') 429 s++; 430 if (isdigit ((unsigned char) *s)) { 431 if (!(s = parse_ranged (s, 2, 0, 59, &mm))) 432 return 0; 433 if (*s==':' && s[-3]==':' && isdigit ((unsigned char) s[1])) { 434 if (!(s = parse_ranged (s + 1, 2, 0, 59, &ss))) 435 return 0; 436 } 437 } 438 if (isdigit ((unsigned char) *s)) 439 return 0; 440 offset = (hh*60 + mm)*60L + ss; 441 *zone = z + (sign=='-' ? -offset : offset); 442 /* 443 * ?? Are fractions allowed here? 444 * If so, they're not implemented. 445 */ 446 return (char *) s; 447 } 448 449 static char const * 450 parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t; 451 /* 452 * Parse an initial prefix of S, matching the pattern whose code is C. 453 * Set *T accordingly. 454 * Return the first character after the prefix, or 0 if it couldn't be parsed. 455 */ 456 { 457 switch (c) { 458 case '$': /* The next character must be a non-digit. */ 459 if (isdigit ((unsigned char) *s)) 460 return 0; 461 break; 462 463 case '-': case '/': case ':': 464 /* These characters stand for themselves. */ 465 if (*s++ != c) 466 return 0; 467 break; 468 469 case '4': /* 4-digit year */ 470 s = parse_fixed (s, 4, &t->tm.tm_year); 471 break; 472 473 case '=': /* optional '-' */ 474 s += *s == '-'; 475 break; 476 477 case 'A': /* AM or PM */ 478 /* 479 * This matches the regular expression [AaPp][Mm]?. 480 * It must not be followed by a letter or digit; 481 * otherwise it would match prefixes of strings like "PST". 482 */ 483 switch (*s++) { 484 case 'A': case 'a': 485 if (t->tm.tm_hour == 12) 486 t->tm.tm_hour = 0; 487 break; 488 489 case 'P': case 'p': 490 if (t->tm.tm_hour != 12) 491 t->tm.tm_hour += 12; 492 break; 493 494 default: return 0; 495 } 496 switch (*s) { 497 case 'M': case 'm': s++; break; 498 } 499 if (isalnum (*s)) 500 return 0; 501 break; 502 503 case 'D': /* day of month [01-31] */ 504 s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday); 505 break; 506 507 case 'd': /* day of year [001-366] */ 508 s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday); 509 t->tm.tm_yday--; 510 break; 511 512 case 'E': /* extended day of month [1-9, 01-31] */ 513 s = parse_ranged (s, ( 514 isdigit ((unsigned char) s[0]) && 515 isdigit ((unsigned char) s[1]) 516 ) + 1, 1, 31, &t->tm.tm_mday); 517 break; 518 519 case 'h': /* hour [00-23 followed by optional fraction] */ 520 { 521 int frac; 522 s = parse_decimal (s, 2, 0, 23, 60*60, &t->tm.tm_hour, &frac); 523 t->tm.tm_min = frac / 60; 524 t->tm.tm_sec = frac % 60; 525 } 526 break; 527 528 case 'm': /* minute [00-59 followed by optional fraction] */ 529 s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec); 530 break; 531 532 case 'n': /* month name [e.g. "Jan"] */ 533 if (!TM_DEFINED (t->tm.tm_mon = lookup (s, month_names))) 534 return 0; 535 /* Don't bother to check rest of spelling. */ 536 while (isalpha ((unsigned char) *s)) 537 s++; 538 break; 539 540 case 'N': /* month [01-12] */ 541 s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); 542 t->tm.tm_mon--; 543 break; 544 545 case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */ 546 s = parse_fixed (s, 1, &t->tm.tm_year); 547 t->ymodulus = 10; 548 break; 549 550 case_R: 551 case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */ 552 s = parse_fixed (s, 2, &t->tm.tm_year); 553 t->ymodulus = 100; 554 break; 555 556 case 's': /* second [00-60 followed by optional fraction] */ 557 { 558 int frac; 559 s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac); 560 t->tm.tm_sec += frac; 561 } 562 break; 563 564 case 'T': /* 'T' or 't' */ 565 switch (*s++) { 566 case 'T': case 't': break; 567 default: return 0; 568 } 569 break; 570 571 case 't': /* traditional hour [1-9 or 01-12] */ 572 s = parse_ranged (s, ( 573 isdigit ((unsigned char) s[0]) && isdigit ((unsigned char) s[1]) 574 ) + 1, 1, 12, &t->tm.tm_hour); 575 break; 576 577 case 'w': /* 'W' or 'w' only (stands for current week) */ 578 switch (*s++) { 579 case 'W': case 'w': break; 580 default: return 0; 581 } 582 break; 583 584 case 'W': /* 'W' or 'w', followed by a week of year [00-53] */ 585 switch (*s++) { 586 case 'W': case 'w': break; 587 default: return 0; 588 } 589 s = parse_ranged (s, 2, 0, 53, &t->yweek); 590 break; 591 592 case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */ 593 s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday); 594 t->tm.tm_wday--; 595 break; 596 597 case 'x': /* weekday name [e.g. "Sun"] */ 598 if (!TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names))) 599 return 0; 600 /* Don't bother to check rest of spelling. */ 601 while (isalpha ((unsigned char) *s)) 602 s++; 603 break; 604 605 case 'y': /* either R or Y */ 606 if ( 607 isdigit ((unsigned char) s[0]) && 608 isdigit ((unsigned char) s[1]) && 609 !isdigit ((unsigned char) s[2]) 610 ) 611 goto case_R; 612 /* fall into */ 613 case 'Y': /* year in full [4 or more digits] */ 614 { 615 int len = 0; 616 while (isdigit ((unsigned char) s[len])) 617 len++; 618 if (len < 4) 619 return 0; 620 s = parse_fixed (s, len, &t->tm.tm_year); 621 } 622 break; 623 624 case 'Z': /* time zone */ 625 s = parzone (s, &t->zone); 626 break; 627 628 case '_': /* possibly empty sequence of non-alphanumerics */ 629 while (!isalnum (*s) && *s) 630 s++; 631 break; 632 633 default: /* bad pattern */ 634 return 0; 635 } 636 return s; 637 } 638 639 static int 640 merge_partime (t, u) struct partime *t; struct partime const *u; 641 /* 642 * If there is no conflict, merge into *T the additional information in *U 643 * and return 0. Otherwise do nothing and return -1. 644 */ 645 { 646 # define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b)) 647 if ( 648 conflict (t->tm.tm_sec, u->tm.tm_sec) || 649 conflict (t->tm.tm_min, u->tm.tm_min) || 650 conflict (t->tm.tm_hour, u->tm.tm_hour) || 651 conflict (t->tm.tm_mday, u->tm.tm_mday) || 652 conflict (t->tm.tm_mon, u->tm.tm_mon) || 653 conflict (t->tm.tm_year, u->tm.tm_year) || 654 conflict (t->tm.tm_wday, u->tm.tm_yday) || 655 conflict (t->ymodulus, u->ymodulus) || 656 conflict (t->yweek, u->yweek) || 657 ( 658 t->zone != u->zone && 659 t->zone != TM_UNDEFINED_ZONE && 660 u->zone != TM_UNDEFINED_ZONE 661 ) 662 ) 663 return -1; 664 # undef conflict 665 # define merge_(a,b) if (TM_DEFINED (b)) (a) = (b); 666 merge_ (t->tm.tm_sec, u->tm.tm_sec) 667 merge_ (t->tm.tm_min, u->tm.tm_min) 668 merge_ (t->tm.tm_hour, u->tm.tm_hour) 669 merge_ (t->tm.tm_mday, u->tm.tm_mday) 670 merge_ (t->tm.tm_mon, u->tm.tm_mon) 671 merge_ (t->tm.tm_year, u->tm.tm_year) 672 merge_ (t->tm.tm_wday, u->tm.tm_yday) 673 merge_ (t->ymodulus, u->ymodulus) 674 merge_ (t->yweek, u->yweek) 675 # undef merge_ 676 if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone; 677 return 0; 678 } 679 680 char * 681 partime (s, t) char const *s; struct partime *t; 682 /* 683 * Parse a date/time prefix of S, putting the parsed result into *T. 684 * Return the first character after the prefix. 685 * The prefix may contain no useful information; 686 * in that case, *T will contain only undefined values. 687 */ 688 { 689 struct partime p; 690 691 undefine (t); 692 while (*s) { 693 int i = 0; 694 char const *s1; 695 do { 696 if (!(s1 = parse_prefix (s, &p, &i))) 697 return (char *) s; 698 } while (merge_partime (t, &p) != 0); 699 s = s1; 700 } 701 return (char *) s; 702 } 703