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