1 #include <time.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include "vary.h" 5 6 struct trans { 7 int val; 8 char *str; 9 }; 10 11 static struct trans trans_mon[] = { 12 { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, 13 { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, 14 { 10, "october" }, { 11, "november" }, { 12, "december" }, 15 { -1, NULL } 16 }; 17 18 static struct trans trans_wday[] = { 19 { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, 20 { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, 21 { -1, NULL } 22 }; 23 24 static char digits[] = "0123456789"; 25 26 static int 27 trans(const struct trans t[], const char *arg) 28 { 29 int f; 30 31 for (f = 0; t[f].val != -1; f++) 32 if (!strncasecmp(t[f].str, arg, 3) || 33 !strncasecmp(t[f].str, arg, strlen(t[f].str))) 34 return t[f].val; 35 36 return -1; 37 } 38 39 struct vary * 40 vary_append(struct vary *v, char *arg) 41 { 42 struct vary *result, **nextp; 43 44 if (v) { 45 result = v; 46 while (v->next) 47 v = v->next; 48 nextp = &v->next; 49 } else 50 nextp = &result; 51 52 *nextp = (struct vary *)malloc(sizeof(struct vary)); 53 (*nextp)->arg = arg; 54 (*nextp)->next = NULL; 55 return result; 56 } 57 58 static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 59 60 static int 61 daysinmonth(const struct tm *t) 62 { 63 int year; 64 65 year = t->tm_year + 1900; 66 67 if (t->tm_mon == 1) 68 if (!(year % 400)) 69 return 29; 70 else if (!(year % 100)) 71 return 28; 72 else if (!(year % 4)) 73 return 29; 74 else 75 return 28; 76 else if (t->tm_mon >= 0 && t->tm_mon < 12) 77 return mdays[t->tm_mon]; 78 79 return 0; 80 } 81 82 83 static int 84 adjyear(struct tm *t, char type, int val) 85 { 86 switch (type) { 87 case '+': 88 t->tm_year += val; 89 break; 90 case '-': 91 t->tm_year -= val; 92 break; 93 default: 94 t->tm_year = val; 95 if (t->tm_year < 69) 96 t->tm_year += 100; /* as per date.c */ 97 else if (t->tm_year > 1900) 98 t->tm_year -= 1900; /* struct tm holds years since 1900 */ 99 break; 100 } 101 return mktime(t) != -1; 102 } 103 104 static int 105 adjmon(struct tm *t, char type, int val, int istext) 106 { 107 if (val < 0) 108 return 0; 109 110 switch (type) { 111 case '+': 112 if (istext) 113 if (val <= t->tm_mon) 114 val += 11 - t->tm_mon; /* early next year */ 115 else 116 val -= t->tm_mon + 1; /* later this year */ 117 if (!adjyear(t, '+', (t->tm_mon + val) / 12)) 118 return 0; 119 val %= 12; 120 t->tm_mon += val; 121 if (t->tm_mon > 11) 122 t->tm_mon -= 12; 123 break; 124 125 case '-': 126 if (istext) 127 if (val-1 > t->tm_mon) 128 val = 13 - val + t->tm_mon; /* later last year */ 129 else 130 val = t->tm_mon - val + 1; /* early this year */ 131 if (!adjyear(t, '-', val / 12)) 132 return 0; 133 val %= 12; 134 if (val > t->tm_mon) { 135 if (!adjyear(t, '-', 1)) 136 return 0; 137 val -= 12; 138 } 139 t->tm_mon -= val; 140 break; 141 142 default: 143 if (val > 12 || val < 1) 144 return 0; 145 t->tm_mon = --val; 146 } 147 148 return mktime(t) != -1; 149 } 150 151 static int 152 adjday(struct tm *t, char type, int val) 153 { 154 int mdays; 155 switch (type) { 156 case '+': 157 while (val) { 158 mdays = daysinmonth(t); 159 if (val > mdays - t->tm_mday) { 160 val -= mdays - t->tm_mday + 1; 161 t->tm_mday = 1; 162 if (!adjmon(t, '+', 1, 0)) 163 return 0; 164 } else { 165 t->tm_mday += val; 166 val = 0; 167 } 168 } 169 break; 170 case '-': 171 while (val) 172 if (val >= t->tm_mday) { 173 val -= t->tm_mday; 174 t->tm_mday = 1; 175 if (!adjmon(t, '-', 1, 0)) 176 return 0; 177 t->tm_mday = daysinmonth(t); 178 } else { 179 t->tm_mday -= val; 180 val = 0; 181 } 182 break; 183 default: 184 if (val > 0 && val <= daysinmonth(t)) 185 t->tm_mday = val; 186 else 187 return 0; 188 break; 189 } 190 191 return mktime(t) != -1; 192 } 193 194 static int 195 adjwday(struct tm *t, char type, int val, int istext) 196 { 197 if (val < 0) 198 return 0; 199 200 switch (type) { 201 case '+': 202 if (istext) 203 if (val < t->tm_wday) 204 val = 7 - t->tm_wday + val; /* early next week */ 205 else 206 val -= t->tm_wday; /* later this week */ 207 else 208 val *= 7; /* "-W +5" == "5 weeks in the future" */ 209 return adjday(t, '+', val); 210 case '-': 211 if (istext) 212 if (val > t->tm_wday) 213 val = 7 - val + t->tm_wday; /* later last week */ 214 else 215 val = t->tm_wday - val; /* early this week */ 216 else 217 val *= 7; /* "-W -5" == "5 weeks ago" */ 218 return adjday(t, '-', val); 219 default: 220 if (val < t->tm_wday) 221 return adjday(t, '-', t->tm_wday - val); 222 else if (val > 6) 223 return 0; 224 else if (val > t->tm_wday) 225 return adjday(t, '+', val - t->tm_wday); 226 } 227 return 1; 228 } 229 230 static int 231 adjhour(struct tm *t, char type, int val) 232 { 233 if (val < 0) 234 return 0; 235 236 switch (type) { 237 case '+': 238 if (!adjday(t, '+', (t->tm_hour + val) / 24)) 239 return 0; 240 val %= 24; 241 t->tm_hour += val; 242 if (t->tm_hour > 23) 243 t->tm_hour -= 24; 244 break; 245 246 case '-': 247 if (!adjday(t, '-', val / 24)) 248 return 0; 249 val %= 24; 250 if (val > t->tm_hour) { 251 if (!adjday(t, '-', 1)) 252 return 0; 253 val -= 24; 254 } 255 t->tm_hour -= val; 256 break; 257 258 default: 259 if (val > 23) 260 return 0; 261 t->tm_hour = val; 262 } 263 264 return mktime(t) != -1; 265 } 266 267 static int 268 adjmin(struct tm *t, char type, int val) 269 { 270 if (val < 0) 271 return 0; 272 273 switch (type) { 274 case '+': 275 if (!adjhour(t, '+', (t->tm_min + val) / 60)) 276 return 0; 277 val %= 60; 278 t->tm_min += val; 279 if (t->tm_min > 59) 280 t->tm_min -= 60; 281 break; 282 283 case '-': 284 if (!adjhour(t, '-', val / 60)) 285 return 0; 286 val %= 60; 287 if (val > t->tm_min) { 288 if (!adjhour(t, '-', 1)) 289 return 0; 290 val -= 60; 291 } 292 t->tm_min -= val; 293 break; 294 295 default: 296 if (val > 59) 297 return 0; 298 t->tm_min = val; 299 } 300 301 return mktime(t) != -1; 302 } 303 304 const struct vary * 305 vary_apply(const struct vary *v, struct tm *t) 306 { 307 char type; 308 char which; 309 char *arg; 310 int len; 311 int val; 312 313 for (; v; v = v->next) { 314 type = *v->arg; 315 arg = v->arg; 316 if (type == '+' || type == '-') 317 arg++; 318 else 319 type = '\0'; 320 len = strlen(arg); 321 if (len < 2) 322 return v; 323 324 if (strspn(arg, digits) != len-1) { 325 val = trans(trans_wday, arg); 326 if (val != -1) { 327 if (!adjwday(t, type, val, 1)) 328 return v; 329 } else { 330 val = trans(trans_mon, arg); 331 if (val != -1) { 332 if (!adjmon(t, type, val, 1)) 333 return v; 334 } else 335 return v; 336 } 337 } else { 338 val = atoi(arg); 339 which = arg[len-1]; 340 341 switch (which) { 342 case 'M': 343 if (!adjmin(t, type, val)) 344 return v; 345 break; 346 case 'H': 347 if (!adjhour(t, type, val)) 348 return v; 349 break; 350 case 'd': 351 if (!adjday(t, type, val)) 352 return v; 353 break; 354 case 'w': 355 if (!adjwday(t, type, val, 0)) 356 return v; 357 break; 358 case 'm': 359 if (!adjmon(t, type, val, 0)) 360 return v; 361 break; 362 case 'y': 363 if (!adjyear(t, type, val)) 364 return v; 365 break; 366 default: 367 return v; 368 } 369 } 370 } 371 return 0; 372 } 373 374 void 375 vary_destroy(struct vary *v) 376 { 377 struct vary *n; 378 379 while (v) { 380 n = v->next; 381 free(v); 382 v = n; 383 } 384 } 385