1d6f907dcSJoerg Wunsch /*- 2ad7cf975SJoerg Wunsch * Copyright (C) 1996 3ad7cf975SJoerg Wunsch * David L. Nugent. All rights reserved. 4d6f907dcSJoerg Wunsch * 5d6f907dcSJoerg Wunsch * Redistribution and use in source and binary forms, with or without 6d6f907dcSJoerg Wunsch * modification, are permitted provided that the following conditions 7d6f907dcSJoerg Wunsch * are met: 8d6f907dcSJoerg Wunsch * 1. Redistributions of source code must retain the above copyright 9ad7cf975SJoerg Wunsch * notice, this list of conditions and the following disclaimer. 10d6f907dcSJoerg Wunsch * 2. Redistributions in binary form must reproduce the above copyright 11d6f907dcSJoerg Wunsch * notice, this list of conditions and the following disclaimer in the 12d6f907dcSJoerg Wunsch * documentation and/or other materials provided with the distribution. 13d6f907dcSJoerg Wunsch * 14ad7cf975SJoerg Wunsch * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15d6f907dcSJoerg Wunsch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16d6f907dcSJoerg Wunsch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ad7cf975SJoerg Wunsch * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18d6f907dcSJoerg Wunsch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19d6f907dcSJoerg Wunsch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20d6f907dcSJoerg Wunsch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21d6f907dcSJoerg Wunsch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22d6f907dcSJoerg Wunsch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23d6f907dcSJoerg Wunsch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24d6f907dcSJoerg Wunsch * SUCH DAMAGE. 25d6f907dcSJoerg Wunsch */ 26d6f907dcSJoerg Wunsch 271dcc6ec7SPhilippe Charnier #ifndef lint 281dcc6ec7SPhilippe Charnier static const char rcsid[] = 2997d92980SPeter Wemm "$FreeBSD$"; 301dcc6ec7SPhilippe Charnier #endif /* not lint */ 311dcc6ec7SPhilippe Charnier 32d6f907dcSJoerg Wunsch #include <stdio.h> 33d6f907dcSJoerg Wunsch #include <stdlib.h> 34d6f907dcSJoerg Wunsch #include <string.h> 35d6f907dcSJoerg Wunsch #include <ctype.h> 36*330e4230SBaptiste Daroussin #include <xlocale.h> 37*330e4230SBaptiste Daroussin #include <err.h> 38d6f907dcSJoerg Wunsch 39d6f907dcSJoerg Wunsch #include "psdate.h" 40d6f907dcSJoerg Wunsch 41d6f907dcSJoerg Wunsch 42d6f907dcSJoerg Wunsch static int 43d6f907dcSJoerg Wunsch a2i(char const ** str) 44d6f907dcSJoerg Wunsch { 45d6f907dcSJoerg Wunsch int i = 0; 46d6f907dcSJoerg Wunsch char const *s = *str; 47d6f907dcSJoerg Wunsch 48e7161f36SAndrey A. Chernov if (isdigit((unsigned char)*s)) { 49d6f907dcSJoerg Wunsch i = atoi(s); 50e7161f36SAndrey A. Chernov while (isdigit((unsigned char)*s)) 51d6f907dcSJoerg Wunsch ++s; 52d6f907dcSJoerg Wunsch *str = s; 53d6f907dcSJoerg Wunsch } 54d6f907dcSJoerg Wunsch return i; 55d6f907dcSJoerg Wunsch } 56d6f907dcSJoerg Wunsch 57d6f907dcSJoerg Wunsch static int 58d6f907dcSJoerg Wunsch numerics(char const * str) 59d6f907dcSJoerg Wunsch { 60e7161f36SAndrey A. Chernov int rc = isdigit((unsigned char)*str); 61d6f907dcSJoerg Wunsch 62d6f907dcSJoerg Wunsch if (rc) 63e7161f36SAndrey A. Chernov while (isdigit((unsigned char)*str) || *str == 'x') 64d6f907dcSJoerg Wunsch ++str; 65d6f907dcSJoerg Wunsch return rc && !*str; 66d6f907dcSJoerg Wunsch } 67d6f907dcSJoerg Wunsch 68d6f907dcSJoerg Wunsch static int 69d6f907dcSJoerg Wunsch aindex(char const * arr[], char const ** str, int len) 70d6f907dcSJoerg Wunsch { 71d6f907dcSJoerg Wunsch int l, i; 72d6f907dcSJoerg Wunsch char mystr[32]; 73d6f907dcSJoerg Wunsch 74d6f907dcSJoerg Wunsch mystr[len] = '\0'; 75d6f907dcSJoerg Wunsch l = strlen(strncpy(mystr, *str, len)); 76d6f907dcSJoerg Wunsch for (i = 0; i < l; i++) 77e7161f36SAndrey A. Chernov mystr[i] = (char) tolower((unsigned char)mystr[i]); 78d6f907dcSJoerg Wunsch for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++); 79d6f907dcSJoerg Wunsch if (arr[i] == NULL) 80d6f907dcSJoerg Wunsch i = -1; 81d6f907dcSJoerg Wunsch else { /* Skip past it */ 82e7161f36SAndrey A. Chernov while (**str && isalpha((unsigned char)**str)) 83d6f907dcSJoerg Wunsch ++(*str); 84d6f907dcSJoerg Wunsch /* And any following whitespace */ 85e7161f36SAndrey A. Chernov while (**str && (**str == ',' || isspace((unsigned char)**str))) 86d6f907dcSJoerg Wunsch ++(*str); 87d6f907dcSJoerg Wunsch } /* Return index */ 88d6f907dcSJoerg Wunsch return i; 89d6f907dcSJoerg Wunsch } 90d6f907dcSJoerg Wunsch 91d6f907dcSJoerg Wunsch static int 92d6f907dcSJoerg Wunsch weekday(char const ** str) 93d6f907dcSJoerg Wunsch { 94d6f907dcSJoerg Wunsch static char const *days[] = 95d6f907dcSJoerg Wunsch {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL}; 96d6f907dcSJoerg Wunsch 97d6f907dcSJoerg Wunsch return aindex(days, str, 3); 98d6f907dcSJoerg Wunsch } 99d6f907dcSJoerg Wunsch 100d6f907dcSJoerg Wunsch static void 101d6f907dcSJoerg Wunsch parse_time(char const * str, int *hour, int *min, int *sec) 102d6f907dcSJoerg Wunsch { 103d6f907dcSJoerg Wunsch *hour = a2i(&str); 104d6f907dcSJoerg Wunsch if ((str = strchr(str, ':')) == NULL) 105d6f907dcSJoerg Wunsch *min = *sec = 0; 106d6f907dcSJoerg Wunsch else { 107d6f907dcSJoerg Wunsch ++str; 108d6f907dcSJoerg Wunsch *min = a2i(&str); 109d6f907dcSJoerg Wunsch *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str); 110d6f907dcSJoerg Wunsch } 111d6f907dcSJoerg Wunsch } 112d6f907dcSJoerg Wunsch 113d6f907dcSJoerg Wunsch 114d6f907dcSJoerg Wunsch static void 115d6f907dcSJoerg Wunsch parse_datesub(char const * str, int *day, int *mon, int *year) 116d6f907dcSJoerg Wunsch { 117*330e4230SBaptiste Daroussin struct tm tm; 118*330e4230SBaptiste Daroussin locale_t l; 119d6f907dcSJoerg Wunsch int i; 120*330e4230SBaptiste Daroussin char *ret; 121*330e4230SBaptiste Daroussin const char *valid_formats[] = { 122*330e4230SBaptiste Daroussin "%d-%b-%y", 123*330e4230SBaptiste Daroussin "%d-%b-%Y", 124*330e4230SBaptiste Daroussin "%d-%m-%y", 125*330e4230SBaptiste Daroussin "%d-%m-%Y", 126*330e4230SBaptiste Daroussin NULL, 127*330e4230SBaptiste Daroussin }; 128d6f907dcSJoerg Wunsch 129*330e4230SBaptiste Daroussin l = newlocale(LC_ALL_MASK, "C", NULL); 130d6f907dcSJoerg Wunsch 131*330e4230SBaptiste Daroussin memset(&tm, 0, sizeof(tm)); 132*330e4230SBaptiste Daroussin for (i=0; valid_formats[i] != NULL; i++) { 133*330e4230SBaptiste Daroussin ret = strptime_l(str, valid_formats[i], &tm, l); 134*330e4230SBaptiste Daroussin if (ret && *ret == '\0') { 135*330e4230SBaptiste Daroussin *day = tm.tm_mday; 136*330e4230SBaptiste Daroussin *mon = tm.tm_mon; 137*330e4230SBaptiste Daroussin *year = tm.tm_year; 138*330e4230SBaptiste Daroussin freelocale(l); 139d6f907dcSJoerg Wunsch return; 140d6f907dcSJoerg Wunsch } 141d6f907dcSJoerg Wunsch } 142d6f907dcSJoerg Wunsch 143*330e4230SBaptiste Daroussin freelocale(l); 144*330e4230SBaptiste Daroussin 145*330e4230SBaptiste Daroussin errx(EXIT_FAILURE, "Invalid date"); 146*330e4230SBaptiste Daroussin } 147*330e4230SBaptiste Daroussin 148d6f907dcSJoerg Wunsch 149d6f907dcSJoerg Wunsch /*- 150d6f907dcSJoerg Wunsch * Parse time must be flexible, it handles the following formats: 151d6f907dcSJoerg Wunsch * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now 152d6f907dcSJoerg Wunsch * 0xnnnnnnnn UNIX timestamp in hexadecimal 153d6f907dcSJoerg Wunsch * 0nnnnnnnnn UNIX timestamp in octal 154d6f907dcSJoerg Wunsch * 0 Given time 155d6f907dcSJoerg Wunsch * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years 156d6f907dcSJoerg Wunsch * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years 157d6f907dcSJoerg Wunsch * dd[ ./-]mmm[ ./-]yy Date } 158d6f907dcSJoerg Wunsch * hh:mm:ss Time } May be combined 159d6f907dcSJoerg Wunsch */ 160d6f907dcSJoerg Wunsch 161d6f907dcSJoerg Wunsch time_t 162d6f907dcSJoerg Wunsch parse_date(time_t dt, char const * str) 163d6f907dcSJoerg Wunsch { 164d6f907dcSJoerg Wunsch char *p; 165d6f907dcSJoerg Wunsch int i; 166d6f907dcSJoerg Wunsch long val; 167d6f907dcSJoerg Wunsch struct tm *T; 168d6f907dcSJoerg Wunsch 169d6f907dcSJoerg Wunsch if (dt == 0) 170d6f907dcSJoerg Wunsch dt = time(NULL); 171d6f907dcSJoerg Wunsch 172e7161f36SAndrey A. Chernov while (*str && isspace((unsigned char)*str)) 173d6f907dcSJoerg Wunsch ++str; 174d6f907dcSJoerg Wunsch 175d6f907dcSJoerg Wunsch if (numerics(str)) { 1762bffe0d5SDavid Nugent dt = strtol(str, &p, 0); 177d6f907dcSJoerg Wunsch } else if (*str == '+' || *str == '-') { 178d6f907dcSJoerg Wunsch val = strtol(str, &p, 0); 179d6f907dcSJoerg Wunsch switch (*p) { 180d6f907dcSJoerg Wunsch case 'h': 181d6f907dcSJoerg Wunsch case 'H': /* hours */ 182d6f907dcSJoerg Wunsch dt += (val * 3600L); 183d6f907dcSJoerg Wunsch break; 184d6f907dcSJoerg Wunsch case '\0': 185d6f907dcSJoerg Wunsch case 'm': 186d6f907dcSJoerg Wunsch case 'M': /* minutes */ 187d6f907dcSJoerg Wunsch dt += (val * 60L); 188d6f907dcSJoerg Wunsch break; 189d6f907dcSJoerg Wunsch case 's': 190d6f907dcSJoerg Wunsch case 'S': /* seconds */ 191d6f907dcSJoerg Wunsch dt += val; 192d6f907dcSJoerg Wunsch break; 193d6f907dcSJoerg Wunsch case 'd': 194d6f907dcSJoerg Wunsch case 'D': /* days */ 195d6f907dcSJoerg Wunsch dt += (val * 86400L); 196d6f907dcSJoerg Wunsch break; 197d6f907dcSJoerg Wunsch case 'w': 198d6f907dcSJoerg Wunsch case 'W': /* weeks */ 199d6f907dcSJoerg Wunsch dt += (val * 604800L); 200d6f907dcSJoerg Wunsch break; 201d6f907dcSJoerg Wunsch case 'o': 202d6f907dcSJoerg Wunsch case 'O': /* months */ 203d6f907dcSJoerg Wunsch T = localtime(&dt); 204d6f907dcSJoerg Wunsch T->tm_mon += (int) val; 205d6f907dcSJoerg Wunsch i = T->tm_mday; 206d6f907dcSJoerg Wunsch goto fixday; 207d6f907dcSJoerg Wunsch case 'y': 208d6f907dcSJoerg Wunsch case 'Y': /* years */ 209d6f907dcSJoerg Wunsch T = localtime(&dt); 210d6f907dcSJoerg Wunsch T->tm_year += (int) val; 211d6f907dcSJoerg Wunsch i = T->tm_mday; 212d6f907dcSJoerg Wunsch fixday: 213d6f907dcSJoerg Wunsch dt = mktime(T); 214d6f907dcSJoerg Wunsch T = localtime(&dt); 215d6f907dcSJoerg Wunsch if (T->tm_mday != i) { 216d6f907dcSJoerg Wunsch T->tm_mday = 1; 217d6f907dcSJoerg Wunsch dt = mktime(T); 218d6f907dcSJoerg Wunsch dt -= (time_t) 86400L; 219d6f907dcSJoerg Wunsch } 220d6f907dcSJoerg Wunsch default: /* unknown */ 221d6f907dcSJoerg Wunsch break; /* leave untouched */ 222d6f907dcSJoerg Wunsch } 223d6f907dcSJoerg Wunsch } else { 224d6f907dcSJoerg Wunsch char *q, tmp[64]; 225d6f907dcSJoerg Wunsch 226d6f907dcSJoerg Wunsch /* 227d6f907dcSJoerg Wunsch * Skip past any weekday prefix 228d6f907dcSJoerg Wunsch */ 229d6f907dcSJoerg Wunsch weekday(&str); 230b8938b66SRobert Drehmel strlcpy(tmp, str, sizeof(tmp)); 231b8938b66SRobert Drehmel str = tmp; 232d6f907dcSJoerg Wunsch T = localtime(&dt); 233d6f907dcSJoerg Wunsch 234d6f907dcSJoerg Wunsch /* 235d6f907dcSJoerg Wunsch * See if we can break off any timezone 236d6f907dcSJoerg Wunsch */ 237d6f907dcSJoerg Wunsch while ((q = strrchr(tmp, ' ')) != NULL) { 238d6f907dcSJoerg Wunsch if (strchr("(+-", q[1]) != NULL) 239d6f907dcSJoerg Wunsch *q = '\0'; 240d6f907dcSJoerg Wunsch else { 241d6f907dcSJoerg Wunsch int j = 1; 242d6f907dcSJoerg Wunsch 243e7161f36SAndrey A. Chernov while (q[j] && isupper((unsigned char)q[j])) 244d6f907dcSJoerg Wunsch ++j; 245d6f907dcSJoerg Wunsch if (q[j] == '\0') 246d6f907dcSJoerg Wunsch *q = '\0'; 247d6f907dcSJoerg Wunsch else 248d6f907dcSJoerg Wunsch break; 249d6f907dcSJoerg Wunsch } 250d6f907dcSJoerg Wunsch } 251d6f907dcSJoerg Wunsch 252d6f907dcSJoerg Wunsch /* 253d6f907dcSJoerg Wunsch * See if there is a time hh:mm[:ss] 254d6f907dcSJoerg Wunsch */ 255d6f907dcSJoerg Wunsch if ((p = strchr(tmp, ':')) == NULL) { 256d6f907dcSJoerg Wunsch 257d6f907dcSJoerg Wunsch /* 258d6f907dcSJoerg Wunsch * No time string involved 259d6f907dcSJoerg Wunsch */ 260d6f907dcSJoerg Wunsch T->tm_hour = T->tm_min = T->tm_sec = 0; 261d6f907dcSJoerg Wunsch parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year); 262d6f907dcSJoerg Wunsch } else { 263d6f907dcSJoerg Wunsch char datestr[64], timestr[64]; 264d6f907dcSJoerg Wunsch 265d6f907dcSJoerg Wunsch /* 266d6f907dcSJoerg Wunsch * Let's chip off the time string 267d6f907dcSJoerg Wunsch */ 268d6f907dcSJoerg Wunsch if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */ 269d6f907dcSJoerg Wunsch int l = q - str; 270d6f907dcSJoerg Wunsch 271b8938b66SRobert Drehmel strlcpy(timestr, str, l + 1); 272b8938b66SRobert Drehmel strlcpy(datestr, q + 1, sizeof(datestr)); 273d6f907dcSJoerg Wunsch parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec); 274d6f907dcSJoerg Wunsch parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year); 275d6f907dcSJoerg Wunsch } else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */ 276d6f907dcSJoerg Wunsch int l = q - tmp; 277d6f907dcSJoerg Wunsch 278b8938b66SRobert Drehmel strlcpy(timestr, q + 1, sizeof(timestr)); 279b8938b66SRobert Drehmel strlcpy(datestr, tmp, l + 1); 280d6f907dcSJoerg Wunsch } else /* Bail out */ 281d6f907dcSJoerg Wunsch return dt; 282d6f907dcSJoerg Wunsch parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec); 283d6f907dcSJoerg Wunsch parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year); 284d6f907dcSJoerg Wunsch } 285d6f907dcSJoerg Wunsch dt = mktime(T); 286d6f907dcSJoerg Wunsch } 287d6f907dcSJoerg Wunsch return dt; 288d6f907dcSJoerg Wunsch } 289