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