1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2020 The DragonFly Project. All rights reserved. 5 * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The DragonFly Project 9 * by Aaron LI <aly@aaronly.me> 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD: head/usr.bin/calendar/parsedata.c 326276 2017-11-27 15:37:16Z pfg $ 33 */ 34 35 #include <ctype.h> 36 #include <err.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <time.h> 41 42 #include "calendar.h" 43 #include "basics.h" 44 #include "days.h" 45 #include "gregorian.h" 46 #include "io.h" 47 #include "nnames.h" 48 #include "parsedata.h" 49 #include "utils.h" 50 51 struct dateinfo { 52 int flags; 53 int sday_id; 54 int year; 55 int month; 56 int dayofmonth; 57 int dayofweek; 58 int offset; 59 int index; 60 }; 61 62 static bool check_dayofweek(const char *s, size_t *len, int *dow); 63 static bool check_month(const char *s, size_t *len, int *month); 64 static bool determine_style(const char *date, struct dateinfo *di); 65 static bool is_onlydigits(const char *s, bool endstar); 66 static bool parse_angle(const char *s, double *result); 67 static const char *parse_int_ranged(const char *s, size_t len, int min, 68 int max, int *result); 69 static bool parse_index(const char *s, int *index); 70 static void show_dateinfo(struct dateinfo *di); 71 72 /* 73 * Expected styles: 74 * 75 * Date ::= Year . '/' . Month . '/' . DayOfMonth | 76 * Year . ' ' . Month . ' ' . DayOfMonth | 77 * Month . '/' . DayOfMonth | 78 * Month . ' ' . DayOfMonth | 79 * Month . '/' . DayOfWeek . Index | 80 * Month . ' ' . DayOfWeek . Index | 81 * MonthName . '/' . AllDays | 82 * MonthName . ' ' . AllDays | 83 * AllDays . '/' . MonthName | 84 * AllDays . ' ' . MonthName | 85 * AllMonths . '/' . DayOfMonth | 86 * AllMonths . ' ' . DayOfMonth | 87 * DayOfMonth . '/' . AllMonths | 88 * DayOfMonth . ' ' . AllMonths | 89 * DayOfMonth . '/' . Month | 90 * DayOfMonth . ' ' . Month | 91 * DayOfWeek . Index . '/' . MonthName | 92 * DayOfWeek . Index . ' ' . MonthName | 93 * DayOfWeek . Index 94 * SpecialDay . Offset 95 * 96 * Year ::= '0' ... '9' | '00' ... '09' | '10' ... '99' | 97 * '100' ... '999' | '1000' ... '9999' 98 * 99 * Month ::= MonthName | MonthNumber 100 * MonthNumber ::= '0' ... '9' | '00' ... '09' | '10' ... '12' 101 * MonthName ::= MonthNameShort | MonthNameLong 102 * MonthNameLong ::= 'January' ... 'December' 103 * MonthNameShort ::= 'Jan' ... 'Dec' | 'Jan.' ... 'Dec.' 104 * 105 * DayOfWeek ::= DayOfWeekShort | DayOfWeekLong 106 * DayOfWeekShort ::= 'Mon' ... 'Sun' 107 * DayOfWeekLong ::= 'Monday' ... 'Sunday' 108 * DayOfMonth ::= '0' ... '9' | '00' ... '09' | '10' ... '29' | 109 * '30' ... '31' 110 * 111 * AllMonths ::= '*' 112 * AllDays ::= '*' 113 * 114 * Index ::= '' | IndexName | 115 * '+' . IndexNumber | '-' . IndexNumber 116 * IndexName ::= 'First' | 'Second' | 'Third' | 'Fourth' | 117 * 'Fifth' | 'Last' 118 * IndexNumber ::= '1' ... '5' 119 * 120 * Offset ::= '' | '+' . OffsetNumber | '-' . OffsetNumber 121 * OffsetNumber ::= '0' ... '9' | '00' ... '99' | '000' ... '299' | 122 * '300' ... '359' | '360' ... '365' 123 * 124 * SpecialDay ::= 'Easter' | 'Paskha' | 'Advent' | 125 * 'ChineseNewYear' | 126 * 'ChineseQingming' | 'ChineseJieqi' | 127 * 'NewMoon' | 'FullMoon' | 128 * 'MarEquinox' | 'SepEquinox' | 129 * 'JunSolstice' | 'DecSolstice' 130 */ 131 static bool 132 determine_style(const char *date, struct dateinfo *di) 133 { 134 static char date2[128]; 135 struct specialday *sday; 136 char *p, *p1, *p2; 137 size_t len; 138 139 snprintf(date2, sizeof(date2), "%s", date); 140 141 if ((p = strchr(date2, ' ')) == NULL && 142 (p = strchr(date2, '/')) == NULL) { 143 for (size_t i = 0; specialdays[i].id != SD_NONE; i++) { 144 sday = &specialdays[i]; 145 if (strncasecmp(date2, sday->name, sday->len) == 0) { 146 len = sday->len; 147 } else if (sday->n_len > 0 && strncasecmp( 148 date2, sday->n_name, sday->n_len) == 0) { 149 len = sday->n_len; 150 } else { 151 continue; 152 } 153 154 di->flags |= (F_SPECIALDAY | F_VARIABLE); 155 di->sday_id = sday->id; 156 if (strlen(date2) == len) 157 return true; 158 159 di->offset = (int)strtol(date2+len, NULL, 10); 160 di->flags |= F_OFFSET; 161 return true; 162 } 163 164 if (check_dayofweek(date2, &len, &di->dayofweek)) { 165 di->flags |= (F_DAYOFWEEK | F_VARIABLE); 166 if (strlen(date2) == len) 167 return true; 168 if (parse_index(date2+len, &di->index)) { 169 di->flags |= F_INDEX; 170 return true; 171 } 172 } 173 174 goto error; 175 } 176 177 *p = '\0'; 178 p1 = date2; 179 p2 = p + 1; 180 /* Now p1 and p2 point to the first and second fields, respectively */ 181 182 if ((p = strchr(p2, ' ')) != NULL || 183 (p = strchr(p2, '/')) != NULL) { 184 /* Got a year in the date string. */ 185 di->flags |= F_YEAR; 186 di->year = (int)strtol(p1, NULL, 10); 187 *p = '\0'; 188 p1 = p2; 189 p2 = p + 1; 190 } 191 192 /* Both month and day as numbers */ 193 if (is_onlydigits(p1, false) && is_onlydigits(p2, true)) { 194 di->flags |= (F_MONTH | F_DAYOFMONTH); 195 if (strchr(p2, '*') != NULL) 196 di->flags |= F_VARIABLE; 197 198 int m = (int)strtol(p1, NULL, 10); 199 int d = (int)strtol(p2, NULL, 10); 200 if (m > 12 && d > 12) { 201 warnx("%s: invalid month |%d| in date: |%s|", 202 __func__, m, date); 203 goto error; 204 } 205 if (m > 12) 206 swap(&m, &d); 207 208 di->month = m; 209 di->dayofmonth = d; 210 return true; 211 } 212 213 /* Check if there is an every-month specifier */ 214 if ((strcmp(p1, "*") == 0 && is_onlydigits(p2, false)) || 215 (strcmp(p2, "*") == 0 && is_onlydigits(p1, false) && (p2 = p1))) { 216 di->flags |= (F_ALLMONTH | F_DAYOFMONTH); 217 di->dayofmonth = (int)strtol(p2, NULL, 10); 218 return true; 219 } 220 221 /* Month as a number, then a weekday */ 222 if (is_onlydigits(p1, false) && 223 check_dayofweek(p2, &len, &di->dayofweek)) { 224 di->flags |= (F_MONTH | F_DAYOFWEEK | F_VARIABLE); 225 di->month = (int)strtol(p1, NULL, 10); 226 227 if (strlen(p2) == len) 228 return true; 229 if (parse_index(p2+len, &di->index)) { 230 di->flags |= F_INDEX; 231 return true; 232 } 233 234 warnx("%s: invalid weekday part |%s| in date |%s|", 235 __func__, p2, date); 236 goto error; 237 } 238 239 /* 240 * Check if there is a month string. 241 * NOTE: Need to check month name/string *after* month number, 242 * because a national month name can be the *same* as the 243 * month number (e.g., 'zh_CN.UTF-8' on macOS), which can 244 * confuse the date parsing if this case is checked *before* 245 * the month number case. 246 */ 247 if (check_month(p1, &len, &di->month) || 248 (check_month(p2, &len, &di->month) && (p2 = p1))) { 249 /* Now p2 is the non-month part */ 250 di->flags |= F_MONTH; 251 if (strcmp(p2, "*") == 0) { 252 di->flags |= F_ALLDAY; 253 return true; 254 } 255 if (is_onlydigits(p2, false)) { 256 di->dayofmonth = (int)strtol(p2, NULL, 10); 257 di->flags |= F_DAYOFMONTH; 258 return true; 259 } 260 if (check_dayofweek(p2, &len, &di->dayofweek)) { 261 di->flags |= (F_DAYOFWEEK | F_VARIABLE); 262 if (strlen(p2) == len) 263 return true; 264 if (parse_index(p2+len, &di->index)) { 265 di->flags |= F_INDEX; 266 return true; 267 } 268 } 269 270 warnx("%s: invalid non-month part |%s| in date |%s|", 271 __func__, p2, date); 272 goto error; 273 } 274 275 error: 276 warnx("%s: unrecognized date: |%s|", __func__, date); 277 return false; 278 } 279 280 static void 281 show_dateinfo(struct dateinfo *di) 282 { 283 struct specialday *sday; 284 285 fprintf(stderr, "flags: 0x%x -", di->flags); 286 287 if ((di->flags & F_YEAR) != 0) 288 fprintf(stderr, " year(%d)", di->year); 289 if ((di->flags & F_MONTH) != 0) 290 fprintf(stderr, " month(%d)", di->month); 291 if ((di->flags & F_DAYOFWEEK) != 0) 292 fprintf(stderr, " dayofweek(%d)", di->dayofweek); 293 if ((di->flags & F_DAYOFMONTH) != 0) 294 fprintf(stderr, " dayofmonth(%d)", di->dayofmonth); 295 if ((di->flags & F_INDEX) != 0) 296 fprintf(stderr, " index(%d)", di->index); 297 298 if ((di->flags & F_SPECIALDAY) != 0) { 299 fprintf(stderr, " specialday"); 300 for (size_t i = 0; specialdays[i].id != SD_NONE; i++) { 301 sday = &specialdays[i]; 302 if (di->sday_id == sday->id) 303 fprintf(stderr, "(%s)", sday->name); 304 } 305 } 306 if ((di->flags & F_OFFSET) != 0) 307 fprintf(stderr, " offset(%d)", di->offset); 308 309 if ((di->flags & F_ALLMONTH) != 0) 310 fprintf(stderr, " allmonth"); 311 if ((di->flags & F_ALLDAY) != 0) 312 fprintf(stderr, " allday"); 313 if ((di->flags & F_VARIABLE) != 0) 314 fprintf(stderr, " variable"); 315 316 fprintf(stderr, "\n"); 317 fflush(stderr); 318 } 319 320 int 321 parse_cal_date(const char *date, int *flags, struct cal_day **dayp, char **edp) 322 { 323 struct specialday *sday; 324 struct dateinfo di; 325 int index, offset; 326 327 memset(&di, 0, sizeof(di)); 328 di.flags = F_NONE; 329 330 if (!determine_style(date, &di)) { 331 if (Options.debug) 332 show_dateinfo(&di); 333 return -1; 334 } 335 336 if (Options.debug >= 3) 337 show_dateinfo(&di); 338 339 *flags = di.flags; 340 index = (di.flags & F_INDEX) ? di.index : 0; 341 offset = (di.flags & F_OFFSET) ? di.offset : 0; 342 343 /* Specified year, month and day (e.g., '2020/Aug/16') */ 344 if ((di.flags & ~F_VARIABLE) == (F_YEAR | F_MONTH | F_DAYOFMONTH) && 345 Calendar->find_days_ymd != NULL) { 346 return (Calendar->find_days_ymd)(di.year, di.month, 347 di.dayofmonth, dayp, edp); 348 } 349 350 /* Specified month and day (e.g., 'Aug/16') */ 351 if ((di.flags & ~F_VARIABLE) == (F_MONTH | F_DAYOFMONTH) && 352 Calendar->find_days_ymd != NULL) { 353 return (Calendar->find_days_ymd)(-1, di.month, di.dayofmonth, 354 dayp, edp); 355 } 356 357 /* Same day every month (e.g., '* 16') */ 358 if (di.flags == (F_ALLMONTH | F_DAYOFMONTH) && 359 Calendar->find_days_dom != NULL) { 360 return (Calendar->find_days_dom)(di.dayofmonth, dayp, edp); 361 } 362 363 /* Every day of a month (e.g., 'Aug *') */ 364 if (di.flags == (F_ALLDAY | F_MONTH) && 365 Calendar->find_days_month != NULL) { 366 return (Calendar->find_days_month)(di.month, dayp, edp); 367 } 368 369 /* 370 * Every day-of-week of a month (e.g., 'Aug/Sun') 371 * One indexed day-of-week of a month (e.g., 'Aug/Sun+3') 372 */ 373 if ((di.flags & ~F_INDEX) == (F_MONTH | F_DAYOFWEEK | F_VARIABLE) && 374 Calendar->find_days_mdow != NULL) { 375 return (Calendar->find_days_mdow)(di.month, di.dayofweek, 376 index, dayp, edp); 377 } 378 379 /* 380 * Every day-of-week of the year (e.g., 'Sun') 381 * One indexed day-of-week of every month (e.g., 'Sun+3') 382 */ 383 if ((di.flags & ~F_INDEX) == (F_DAYOFWEEK | F_VARIABLE) && 384 Calendar->find_days_mdow != NULL) { 385 return (Calendar->find_days_mdow)(-1, di.dayofweek, index, 386 dayp, edp); 387 } 388 389 /* Special days with optional offset (e.g., 'ChineseNewYear+14') */ 390 if ((di.flags & F_SPECIALDAY) != 0) { 391 for (size_t i = 0; specialdays[i].id != SD_NONE; i++) { 392 sday = &specialdays[i]; 393 if (di.sday_id == sday->id && sday->find_days != NULL) 394 return (sday->find_days)(offset, dayp, edp); 395 } 396 } 397 398 warnx("%s: Unsupported date |%s| in '%s' calendar", 399 __func__, date, Calendar->name); 400 if (Options.debug) 401 show_dateinfo(&di); 402 403 return -1; 404 } 405 406 static bool 407 check_month(const char *s, size_t *len, int *month) 408 { 409 struct nname *nname; 410 411 for (int i = 0; month_names[i].name != NULL; i++) { 412 nname = &month_names[i]; 413 414 if (nname->fn_name && 415 strncasecmp(s, nname->fn_name, nname->fn_len) == 0) { 416 *len = nname->fn_len; 417 *month = nname->value; 418 return (true); 419 } 420 421 if (nname->n_name && 422 strncasecmp(s, nname->n_name, nname->n_len) == 0) { 423 *len = nname->n_len; 424 *month = nname->value; 425 return (true); 426 } 427 428 if (nname->f_name && 429 strncasecmp(s, nname->f_name, nname->f_len) == 0) { 430 *len = nname->f_len; 431 *month = nname->value; 432 return (true); 433 } 434 435 if (strncasecmp(s, nname->name, nname->len) == 0) { 436 *len = nname->len; 437 *month = nname->value; 438 return (true); 439 } 440 } 441 442 return (false); 443 } 444 445 static bool 446 check_dayofweek(const char *s, size_t *len, int *dow) 447 { 448 struct nname *nname; 449 450 for (int i = 0; dow_names[i].name != NULL; i++) { 451 nname = &dow_names[i]; 452 453 if (nname->fn_name && 454 strncasecmp(s, nname->fn_name, nname->fn_len) == 0) { 455 *len = nname->fn_len; 456 *dow = nname->value; 457 return (true); 458 } 459 460 if (nname->n_name && 461 strncasecmp(s, nname->n_name, nname->n_len) == 0) { 462 *len = nname->n_len; 463 *dow = nname->value; 464 return (true); 465 } 466 467 if (nname->f_name && 468 strncasecmp(s, nname->f_name, nname->f_len) == 0) { 469 *len = nname->f_len; 470 *dow = nname->value; 471 return (true); 472 } 473 474 if (strncasecmp(s, nname->name, nname->len) == 0) { 475 *len = nname->len; 476 *dow = nname->value; 477 return (true); 478 } 479 } 480 481 return (false); 482 } 483 484 static bool 485 is_onlydigits(const char *s, bool endstar) 486 { 487 for (int i = 0; s[i] != '\0'; i++) { 488 if (endstar && i > 0 && s[i] == '*' && s[i+1] == '\0') 489 return (true); 490 if (!isdigit((unsigned char)s[i])) 491 return (false); 492 } 493 return (true); 494 } 495 496 static bool 497 parse_index(const char *s, int *index) 498 { 499 struct nname *nname; 500 bool parsed = false; 501 502 if (s[0] == '+' || s[0] == '-') { 503 char *endp; 504 int v = (int)strtol(s, &endp, 10); 505 if (*endp != '\0') 506 return false; /* has trailing junk */ 507 if (v == 0 || v <= -6 || v >= 6) { 508 warnx("%s: invalid value: %d", __func__, v); 509 return false; 510 } 511 512 *index = v; 513 parsed = true; 514 } 515 516 for (int i = 0; !parsed && sequence_names[i].name != NULL; i++) { 517 nname = &sequence_names[i]; 518 if (strcasecmp(s, nname->name) == 0 || 519 (nname->n_name && strcasecmp(s, nname->n_name) == 0)) { 520 *index = nname->value; 521 parsed = true; 522 } 523 } 524 525 DPRINTF2("%s: |%s| -> %d (status=%s)\n", 526 __func__, s, *index, (parsed ? "ok" : "fail")); 527 return parsed; 528 } 529 530 531 /* 532 * Parse the specified length of a string to an integer and check its range. 533 * Return the pointer to the next character of the parsed string on success, 534 * otherwise return NULL. 535 */ 536 static const char * 537 parse_int_ranged(const char *s, size_t len, int min, int max, int *result) 538 { 539 if (strlen(s) < len) 540 return NULL; 541 542 const char *end = s + len; 543 int v = 0; 544 while (s < end) { 545 if (isdigit((unsigned char)*s) == 0) 546 return NULL; 547 v = 10 * v + (*s - '0'); 548 s++; 549 } 550 551 if (v < min || v > max) 552 return NULL; 553 554 *result = v; 555 return end; 556 } 557 558 /* 559 * Parse the timezone string (format: ±hh:mm, ±hhmm, or ±hh) to the number 560 * of seconds east of UTC. 561 * Return true on success, otherwise false. 562 */ 563 bool 564 parse_timezone(const char *s, int *result) 565 { 566 if (*s != '+' && *s != '-') 567 return false; 568 char sign = *s++; 569 570 int hh = 0; 571 int mm = 0; 572 if ((s = parse_int_ranged(s, 2, 0, 23, &hh)) == NULL) 573 return false; 574 if (*s != '\0') { 575 if (*s == ':') 576 s++; 577 if ((s = parse_int_ranged(s, 2, 0, 59, &mm)) == NULL) 578 return false; 579 } 580 if (*s != '\0') 581 return false; 582 583 int offset = hh * 3600 + mm * 60; 584 *result = (sign == '+') ? offset : -offset; 585 586 DPRINTF("%s: parsed |%s| -> %d seconds\n", __func__, s, *result); 587 return true; 588 } 589 590 /* 591 * Parse a angle/coordinate string in format of a single float number or 592 * [+-]d:m:s, where 'd' and 'm' are degrees and minutes of integer type, 593 * and 's' is seconds of float type. 594 * Return true on success, otherwise false. 595 */ 596 static bool 597 parse_angle(const char *s, double *result) 598 { 599 char sign = '+'; 600 if (*s == '+' || *s == '-') 601 sign = *s++; 602 603 char *endp; 604 double v; 605 v = strtod(s, &endp); 606 if (s == endp || *endp != '\0') { 607 /* try to parse format of 'd:m:s' */ 608 int deg = 0; 609 int min = 0; 610 double sec = 0.0; 611 switch (sscanf(s, "%d:%d:%lf", °, &min, &sec)) { 612 case 3: 613 case 2: 614 case 1: 615 if (min < 0 || min >= 60 || sec < 0 || sec >= 60) 616 return false; 617 v = deg + min / 60.0 + sec / 3600.0; 618 break; 619 default: 620 return false; 621 } 622 } 623 624 *result = (sign == '+') ? v : -v; 625 return true; 626 } 627 628 /* 629 * Parse location of format: latitude,longitude[,elevation] 630 * where 'latitude' and 'longitude' can be represented as a float number or 631 * in '[+-]d:m:s' format, and 'elevation' is optional and should be a 632 * positive float number. 633 * Return true on success, otherwise false. 634 */ 635 bool 636 parse_location(const char *s, double *latitude, double *longitude, 637 double *elevation) 638 { 639 char *ds = xstrdup(s); 640 const char *sep = ","; 641 char *p; 642 double v; 643 644 p = strtok(ds, sep); 645 if (parse_angle(p, &v) && fabs(v) <= 90) { 646 *latitude = v; 647 } else { 648 warnx("%s: invalid latitude: |%s|", __func__, p); 649 return false; 650 } 651 652 p = strtok(NULL, sep); 653 if (p == NULL) { 654 warnx("%s: missing longitude", __func__); 655 return false; 656 } 657 if (parse_angle(p, &v) && fabs(v) <= 180) { 658 *longitude = v; 659 } else { 660 warnx("%s: invalid longitude: |%s|", __func__, p); 661 return false; 662 } 663 664 p = strtok(NULL, sep); 665 if (p != NULL) { 666 char *endp; 667 v = strtod(p, &endp); 668 if (p == endp || *endp != '\0' || v < 0) { 669 warnx("%s: invalid elevation: |%s|", __func__, p); 670 return false; 671 } 672 *elevation = v; 673 } 674 675 if ((p = strtok(NULL, sep)) != NULL) { 676 warnx("%s: unknown value: |%s|", __func__, p); 677 return false; 678 } 679 680 return true; 681 } 682 683 /* 684 * Parse date string of format '[[[CC]YY]MM]DD' into a fixed date. 685 * Return true on success, otherwise false. 686 */ 687 bool 688 parse_date(const char *date, int *rd_out) 689 { 690 size_t len; 691 time_t now; 692 struct tm tm; 693 struct date gdate; 694 695 now = time(NULL); 696 tzset(); 697 localtime_r(&now, &tm); 698 date_set(&gdate, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); 699 700 len = strlen(date); 701 if (len < 2) 702 return false; 703 704 if (!parse_int_ranged(date+len-2, 2, 1, 31, &gdate.day)) 705 return false; 706 707 if (len >= 4) { 708 if (!parse_int_ranged(date+len-4, 2, 1, 12, &gdate.month)) 709 return false; 710 } 711 712 if (len >= 6) { 713 if (!parse_int_ranged(date, len-4, 0, 9999, &gdate.year)) 714 return false; 715 if (gdate.year < 69) /* Y2K */ 716 gdate.year += 2000; 717 else if (gdate.year < 100) 718 gdate.year += 1900; 719 } 720 721 *rd_out = fixed_from_gregorian(&gdate); 722 723 DPRINTF("%s: parsed |%s| -> %d-%02d-%02d\n", 724 __func__, date, gdate.year, gdate.month, gdate.day); 725 return true; 726 } 727 728 /* 729 * Parse time string of format 'hh:mm[:ss]' into a float time in 730 * units of days. 731 * Return true on success, otherwise false. 732 */ 733 bool 734 parse_time(const char *time, double *t_out) 735 { 736 int hh = 0; 737 int mm = 0; 738 int ss = 0; 739 740 switch (sscanf(time, "%d:%d:%d", &hh, &mm, &ss)) { 741 case 3: 742 case 2: 743 break; 744 default: 745 return false; 746 } 747 748 if (hh < 0 || hh >= 24 || mm < 0 || mm >= 60 || ss < 0 || ss > 60) 749 return false; 750 751 *t_out = (hh + mm/60.0 + ss/3600.0) / 24.0; 752 753 DPRINTF("%s: parsed |%s| -> %.3lf day\n", __func__, time, *t_out); 754 return true; 755 } 756