120253Sjoerg/*- 220302Sjoerg * Copyright (C) 1996 320302Sjoerg * David L. Nugent. All rights reserved. 420253Sjoerg * 520253Sjoerg * Redistribution and use in source and binary forms, with or without 620253Sjoerg * modification, are permitted provided that the following conditions 720253Sjoerg * are met: 820253Sjoerg * 1. Redistributions of source code must retain the above copyright 920302Sjoerg * notice, this list of conditions and the following disclaimer. 1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1120253Sjoerg * notice, this list of conditions and the following disclaimer in the 1220253Sjoerg * documentation and/or other materials provided with the distribution. 1320253Sjoerg * 1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1720302Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2420253Sjoerg * SUCH DAMAGE. 2520253Sjoerg */ 2620253Sjoerg 2730259Scharnier#ifndef lint 2830259Scharnierstatic const char rcsid[] = 2950479Speter "$FreeBSD$"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 3220253Sjoerg#include <stdio.h> 3320253Sjoerg#include <stdlib.h> 3420253Sjoerg#include <string.h> 3520253Sjoerg#include <ctype.h> 3620253Sjoerg 3720253Sjoerg#include "psdate.h" 3820253Sjoerg 3920253Sjoerg 4020253Sjoergstatic int 4120253Sjoerga2i(char const ** str) 4220253Sjoerg{ 4320253Sjoerg int i = 0; 4420253Sjoerg char const *s = *str; 4520253Sjoerg 4661957Sache if (isdigit((unsigned char)*s)) { 4720253Sjoerg i = atoi(s); 4861957Sache while (isdigit((unsigned char)*s)) 4920253Sjoerg ++s; 5020253Sjoerg *str = s; 5120253Sjoerg } 5220253Sjoerg return i; 5320253Sjoerg} 5420253Sjoerg 5520253Sjoergstatic int 5620253Sjoergnumerics(char const * str) 5720253Sjoerg{ 5861957Sache int rc = isdigit((unsigned char)*str); 5920253Sjoerg 6020253Sjoerg if (rc) 6161957Sache while (isdigit((unsigned char)*str) || *str == 'x') 6220253Sjoerg ++str; 6320253Sjoerg return rc && !*str; 6420253Sjoerg} 6520253Sjoerg 6620253Sjoergstatic int 6720253Sjoergaindex(char const * arr[], char const ** str, int len) 6820253Sjoerg{ 6920253Sjoerg int l, i; 7020253Sjoerg char mystr[32]; 7120253Sjoerg 7220253Sjoerg mystr[len] = '\0'; 7320253Sjoerg l = strlen(strncpy(mystr, *str, len)); 7420253Sjoerg for (i = 0; i < l; i++) 7561957Sache mystr[i] = (char) tolower((unsigned char)mystr[i]); 7620253Sjoerg for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++); 7720253Sjoerg if (arr[i] == NULL) 7820253Sjoerg i = -1; 7920253Sjoerg else { /* Skip past it */ 8061957Sache while (**str && isalpha((unsigned char)**str)) 8120253Sjoerg ++(*str); 8220253Sjoerg /* And any following whitespace */ 8361957Sache while (**str && (**str == ',' || isspace((unsigned char)**str))) 8420253Sjoerg ++(*str); 8520253Sjoerg } /* Return index */ 8620253Sjoerg return i; 8720253Sjoerg} 8820253Sjoerg 8920253Sjoergstatic int 9020253Sjoergweekday(char const ** str) 9120253Sjoerg{ 9220253Sjoerg static char const *days[] = 9320253Sjoerg {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL}; 9420253Sjoerg 9520253Sjoerg return aindex(days, str, 3); 9620253Sjoerg} 9720253Sjoerg 9820253Sjoergstatic int 9920253Sjoergmonth(char const ** str) 10020253Sjoerg{ 10120253Sjoerg static char const *months[] = 10220253Sjoerg {"jan", "feb", "mar", "apr", "may", "jun", "jul", 10320253Sjoerg "aug", "sep", "oct", "nov", "dec", NULL}; 10420253Sjoerg 10520253Sjoerg return aindex(months, str, 3); 10620253Sjoerg} 10720253Sjoerg 10820253Sjoergstatic void 10920253Sjoergparse_time(char const * str, int *hour, int *min, int *sec) 11020253Sjoerg{ 11120253Sjoerg *hour = a2i(&str); 11220253Sjoerg if ((str = strchr(str, ':')) == NULL) 11320253Sjoerg *min = *sec = 0; 11420253Sjoerg else { 11520253Sjoerg ++str; 11620253Sjoerg *min = a2i(&str); 11720253Sjoerg *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str); 11820253Sjoerg } 11920253Sjoerg} 12020253Sjoerg 12120253Sjoerg 12220253Sjoergstatic void 12320253Sjoergparse_datesub(char const * str, int *day, int *mon, int *year) 12420253Sjoerg{ 12520253Sjoerg int i; 12620253Sjoerg 12720253Sjoerg static char const nchrs[] = "0123456789 \t,/-."; 12820253Sjoerg 12920253Sjoerg if ((i = month(&str)) != -1) { 13020253Sjoerg *mon = i; 13120253Sjoerg if ((i = a2i(&str)) != 0) 13220253Sjoerg *day = i; 13320253Sjoerg } else if ((i = a2i(&str)) != 0) { 13420253Sjoerg *day = i; 13520253Sjoerg while (*str && strchr(nchrs + 10, *str) != NULL) 13620253Sjoerg ++str; 13720253Sjoerg if ((i = month(&str)) != -1) 13820253Sjoerg *mon = i; 13920253Sjoerg else if ((i = a2i(&str)) != 0) 14020253Sjoerg *mon = i - 1; 14120253Sjoerg } else 14220253Sjoerg return; 14320253Sjoerg 14420253Sjoerg while (*str && strchr(nchrs + 10, *str) != NULL) 14520253Sjoerg ++str; 14661957Sache if (isdigit((unsigned char)*str)) { 14720253Sjoerg *year = atoi(str); 14820253Sjoerg if (*year > 1900) 14920253Sjoerg *year -= 1900; 15020253Sjoerg else if (*year < 32) 15120253Sjoerg *year += 100; 15220253Sjoerg } 15320253Sjoerg} 15420253Sjoerg 15520253Sjoerg 15620253Sjoerg/*- 15720253Sjoerg * Parse time must be flexible, it handles the following formats: 15820253Sjoerg * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now 15920253Sjoerg * 0xnnnnnnnn UNIX timestamp in hexadecimal 16020253Sjoerg * 0nnnnnnnnn UNIX timestamp in octal 16120253Sjoerg * 0 Given time 16220253Sjoerg * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years 16320253Sjoerg * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years 16420253Sjoerg * dd[ ./-]mmm[ ./-]yy Date } 16520253Sjoerg * hh:mm:ss Time } May be combined 16620253Sjoerg */ 16720253Sjoerg 16820253Sjoergtime_t 16920253Sjoergparse_date(time_t dt, char const * str) 17020253Sjoerg{ 17120253Sjoerg char *p; 17220253Sjoerg int i; 17320253Sjoerg long val; 17420253Sjoerg struct tm *T; 17520253Sjoerg 17620253Sjoerg if (dt == 0) 17720253Sjoerg dt = time(NULL); 17820253Sjoerg 17961957Sache while (*str && isspace((unsigned char)*str)) 18020253Sjoerg ++str; 18120253Sjoerg 18220253Sjoerg if (numerics(str)) { 18344775Sdavidn dt = strtol(str, &p, 0); 18420253Sjoerg } else if (*str == '+' || *str == '-') { 18520253Sjoerg val = strtol(str, &p, 0); 18620253Sjoerg switch (*p) { 18720253Sjoerg case 'h': 18820253Sjoerg case 'H': /* hours */ 18920253Sjoerg dt += (val * 3600L); 19020253Sjoerg break; 19120253Sjoerg case '\0': 19220253Sjoerg case 'm': 19320253Sjoerg case 'M': /* minutes */ 19420253Sjoerg dt += (val * 60L); 19520253Sjoerg break; 19620253Sjoerg case 's': 19720253Sjoerg case 'S': /* seconds */ 19820253Sjoerg dt += val; 19920253Sjoerg break; 20020253Sjoerg case 'd': 20120253Sjoerg case 'D': /* days */ 20220253Sjoerg dt += (val * 86400L); 20320253Sjoerg break; 20420253Sjoerg case 'w': 20520253Sjoerg case 'W': /* weeks */ 20620253Sjoerg dt += (val * 604800L); 20720253Sjoerg break; 20820253Sjoerg case 'o': 20920253Sjoerg case 'O': /* months */ 21020253Sjoerg T = localtime(&dt); 21120253Sjoerg T->tm_mon += (int) val; 21220253Sjoerg i = T->tm_mday; 21320253Sjoerg goto fixday; 21420253Sjoerg case 'y': 21520253Sjoerg case 'Y': /* years */ 21620253Sjoerg T = localtime(&dt); 21720253Sjoerg T->tm_year += (int) val; 21820253Sjoerg i = T->tm_mday; 21920253Sjoerg fixday: 22020253Sjoerg dt = mktime(T); 22120253Sjoerg T = localtime(&dt); 22220253Sjoerg if (T->tm_mday != i) { 22320253Sjoerg T->tm_mday = 1; 22420253Sjoerg dt = mktime(T); 22520253Sjoerg dt -= (time_t) 86400L; 22620253Sjoerg } 22720253Sjoerg default: /* unknown */ 22820253Sjoerg break; /* leave untouched */ 22920253Sjoerg } 23020253Sjoerg } else { 23120253Sjoerg char *q, tmp[64]; 23220253Sjoerg 23320253Sjoerg /* 23420253Sjoerg * Skip past any weekday prefix 23520253Sjoerg */ 23620253Sjoerg weekday(&str); 237130633Srobert strlcpy(tmp, str, sizeof(tmp)); 238130633Srobert str = tmp; 23920253Sjoerg T = localtime(&dt); 24020253Sjoerg 24120253Sjoerg /* 24220253Sjoerg * See if we can break off any timezone 24320253Sjoerg */ 24420253Sjoerg while ((q = strrchr(tmp, ' ')) != NULL) { 24520253Sjoerg if (strchr("(+-", q[1]) != NULL) 24620253Sjoerg *q = '\0'; 24720253Sjoerg else { 24820253Sjoerg int j = 1; 24920253Sjoerg 25061957Sache while (q[j] && isupper((unsigned char)q[j])) 25120253Sjoerg ++j; 25220253Sjoerg if (q[j] == '\0') 25320253Sjoerg *q = '\0'; 25420253Sjoerg else 25520253Sjoerg break; 25620253Sjoerg } 25720253Sjoerg } 25820253Sjoerg 25920253Sjoerg /* 26020253Sjoerg * See if there is a time hh:mm[:ss] 26120253Sjoerg */ 26220253Sjoerg if ((p = strchr(tmp, ':')) == NULL) { 26320253Sjoerg 26420253Sjoerg /* 26520253Sjoerg * No time string involved 26620253Sjoerg */ 26720253Sjoerg T->tm_hour = T->tm_min = T->tm_sec = 0; 26820253Sjoerg parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year); 26920253Sjoerg } else { 27020253Sjoerg char datestr[64], timestr[64]; 27120253Sjoerg 27220253Sjoerg /* 27320253Sjoerg * Let's chip off the time string 27420253Sjoerg */ 27520253Sjoerg if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */ 27620253Sjoerg int l = q - str; 27720253Sjoerg 278130633Srobert strlcpy(timestr, str, l + 1); 279130633Srobert strlcpy(datestr, q + 1, sizeof(datestr)); 28020253Sjoerg parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec); 28120253Sjoerg parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year); 28220253Sjoerg } else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */ 28320253Sjoerg int l = q - tmp; 28420253Sjoerg 285130633Srobert strlcpy(timestr, q + 1, sizeof(timestr)); 286130633Srobert strlcpy(datestr, tmp, l + 1); 28720253Sjoerg } else /* Bail out */ 28820253Sjoerg return dt; 28920253Sjoerg parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec); 29020253Sjoerg parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year); 29120253Sjoerg } 29220253Sjoerg dt = mktime(T); 29320253Sjoerg } 29420253Sjoerg return dt; 29520253Sjoerg} 296