Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/bin/date/vary.c
39478 views
1
/*-
2
* Copyright (c) 1997 Brian Somers <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/cdefs.h>
28
#include <err.h>
29
#include <time.h>
30
#include <stdint.h>
31
#include <string.h>
32
#include <stdlib.h>
33
#include "vary.h"
34
35
struct trans {
36
int64_t val;
37
const char *str;
38
};
39
40
static struct trans trans_mon[] = {
41
{ 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
42
{ 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
43
{ 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
44
{ -1, NULL }
45
};
46
47
static struct trans trans_wday[] = {
48
{ 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
49
{ 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
50
{ -1, NULL }
51
};
52
53
static char digits[] = "0123456789";
54
static int adjhour(struct tm *, char, int64_t, int);
55
56
static int
57
domktime(struct tm *t, char type)
58
{
59
time_t ret;
60
61
while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
62
/* While mktime() fails, adjust by an hour */
63
adjhour(t, type == '-' ? type : '+', 1, 0);
64
65
return ret;
66
}
67
68
static int
69
trans(const struct trans t[], const char *arg)
70
{
71
int f;
72
73
for (f = 0; t[f].val != -1; f++)
74
if (!strncasecmp(t[f].str, arg, 3) ||
75
!strncasecmp(t[f].str, arg, strlen(t[f].str)))
76
return t[f].val;
77
78
return -1;
79
}
80
81
struct vary *
82
vary_append(struct vary *v, char *arg)
83
{
84
struct vary *result, **nextp;
85
86
if (v) {
87
result = v;
88
while (v->next)
89
v = v->next;
90
nextp = &v->next;
91
} else
92
nextp = &result;
93
94
if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL)
95
err(1, "malloc");
96
(*nextp)->arg = arg;
97
(*nextp)->next = NULL;
98
return result;
99
}
100
101
static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
102
103
static int
104
daysinmonth(const struct tm *t)
105
{
106
int year;
107
108
year = t->tm_year + 1900;
109
110
if (t->tm_mon == 1)
111
if (!(year % 400))
112
return 29;
113
else if (!(year % 100))
114
return 28;
115
else if (!(year % 4))
116
return 29;
117
else
118
return 28;
119
else if (t->tm_mon >= 0 && t->tm_mon < 12)
120
return mdays[t->tm_mon];
121
122
return 0;
123
}
124
125
126
static int
127
adjyear(struct tm *t, char type, int64_t val, int mk)
128
{
129
switch (type) {
130
case '+':
131
t->tm_year += val;
132
break;
133
case '-':
134
t->tm_year -= val;
135
break;
136
default:
137
t->tm_year = val;
138
if (t->tm_year < 69)
139
t->tm_year += 100; /* as per date.c */
140
else if (t->tm_year > 1900)
141
t->tm_year -= 1900; /* struct tm holds years since 1900 */
142
break;
143
}
144
return !mk || domktime(t, type) != -1;
145
}
146
147
static int
148
adjmon(struct tm *t, char type, int64_t val, int istext, int mk)
149
{
150
int lmdays;
151
152
if (val < 0)
153
return 0;
154
155
switch (type) {
156
case '+':
157
if (istext) {
158
if (val <= t->tm_mon)
159
val += 11 - t->tm_mon; /* early next year */
160
else
161
val -= t->tm_mon + 1; /* later this year */
162
}
163
if (val) {
164
if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
165
return 0;
166
val %= 12;
167
t->tm_mon += val;
168
if (t->tm_mon > 11)
169
t->tm_mon -= 12;
170
}
171
break;
172
173
case '-':
174
if (istext) {
175
if (val-1 > t->tm_mon)
176
val = 13 - val + t->tm_mon; /* later last year */
177
else
178
val = t->tm_mon - val + 1; /* early this year */
179
}
180
if (val) {
181
if (!adjyear(t, '-', val / 12, 0))
182
return 0;
183
val %= 12;
184
if (val > t->tm_mon) {
185
if (!adjyear(t, '-', 1, 0))
186
return 0;
187
val -= 12;
188
}
189
t->tm_mon -= val;
190
}
191
break;
192
193
default:
194
if (val > 12 || val < 1)
195
return 0;
196
t->tm_mon = --val;
197
}
198
199
/* e.g., -v-1m on March, 31 is the last day of February in common sense */
200
lmdays = daysinmonth(t);
201
if (t->tm_mday > lmdays)
202
t->tm_mday = lmdays;
203
204
return !mk || domktime(t, type) != -1;
205
}
206
207
static int
208
adjday(struct tm *t, char type, int64_t val, int mk)
209
{
210
int lmdays;
211
212
switch (type) {
213
case '+':
214
while (val) {
215
lmdays = daysinmonth(t);
216
if (val > lmdays - t->tm_mday) {
217
val -= lmdays - t->tm_mday + 1;
218
t->tm_mday = 1;
219
if (!adjmon(t, '+', 1, 0, 0))
220
return 0;
221
} else {
222
t->tm_mday += val;
223
val = 0;
224
}
225
}
226
break;
227
case '-':
228
while (val)
229
if (val >= t->tm_mday) {
230
val -= t->tm_mday;
231
t->tm_mday = 1;
232
if (!adjmon(t, '-', 1, 0, 0))
233
return 0;
234
t->tm_mday = daysinmonth(t);
235
} else {
236
t->tm_mday -= val;
237
val = 0;
238
}
239
break;
240
default:
241
if (val > 0 && val <= daysinmonth(t))
242
t->tm_mday = val;
243
else
244
return 0;
245
break;
246
}
247
248
return !mk || domktime(t, type) != -1;
249
}
250
251
static int
252
adjwday(struct tm *t, char type, int64_t val, int istext, int mk)
253
{
254
if (val < 0)
255
return 0;
256
257
switch (type) {
258
case '+':
259
if (istext)
260
if (val < t->tm_wday)
261
val = 7 - t->tm_wday + val; /* early next week */
262
else
263
val -= t->tm_wday; /* later this week */
264
else
265
val *= 7; /* "-v+5w" == "5 weeks in the future" */
266
return !val || adjday(t, '+', val, mk);
267
case '-':
268
if (istext) {
269
if (val > t->tm_wday)
270
val = 7 - val + t->tm_wday; /* later last week */
271
else
272
val = t->tm_wday - val; /* early this week */
273
} else
274
val *= 7; /* "-v-5w" == "5 weeks ago" */
275
return !val || adjday(t, '-', val, mk);
276
default:
277
if (val < t->tm_wday)
278
return adjday(t, '-', t->tm_wday - val, mk);
279
else if (val > 6)
280
return 0;
281
else if (val > t->tm_wday)
282
return adjday(t, '+', val - t->tm_wday, mk);
283
}
284
return 1;
285
}
286
287
static int
288
adjhour(struct tm *t, char type, int64_t val, int mk)
289
{
290
if (val < 0)
291
return 0;
292
293
switch (type) {
294
case '+':
295
if (val) {
296
int days;
297
298
days = (t->tm_hour + val) / 24;
299
val %= 24;
300
t->tm_hour += val;
301
t->tm_hour %= 24;
302
if (!adjday(t, '+', days, 0))
303
return 0;
304
}
305
break;
306
307
case '-':
308
if (val) {
309
int days;
310
311
days = val / 24;
312
val %= 24;
313
if (val > t->tm_hour) {
314
days++;
315
val -= 24;
316
}
317
t->tm_hour -= val;
318
if (!adjday(t, '-', days, 0))
319
return 0;
320
}
321
break;
322
323
default:
324
if (val > 23)
325
return 0;
326
t->tm_hour = val;
327
}
328
329
return !mk || domktime(t, type) != -1;
330
}
331
332
static int
333
adjmin(struct tm *t, char type, int64_t val, int mk)
334
{
335
if (val < 0)
336
return 0;
337
338
switch (type) {
339
case '+':
340
if (val) {
341
if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
342
return 0;
343
val %= 60;
344
t->tm_min += val;
345
if (t->tm_min > 59)
346
t->tm_min -= 60;
347
}
348
break;
349
350
case '-':
351
if (val) {
352
if (!adjhour(t, '-', val / 60, 0))
353
return 0;
354
val %= 60;
355
if (val > t->tm_min) {
356
if (!adjhour(t, '-', 1, 0))
357
return 0;
358
val -= 60;
359
}
360
t->tm_min -= val;
361
}
362
break;
363
364
default:
365
if (val > 59)
366
return 0;
367
t->tm_min = val;
368
}
369
370
return !mk || domktime(t, type) != -1;
371
}
372
373
static int
374
adjsec(struct tm *t, char type, int64_t val, int mk)
375
{
376
if (val < 0)
377
return 0;
378
379
switch (type) {
380
case '+':
381
if (val) {
382
if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
383
return 0;
384
val %= 60;
385
t->tm_sec += val;
386
if (t->tm_sec > 59)
387
t->tm_sec -= 60;
388
}
389
break;
390
391
case '-':
392
if (val) {
393
if (!adjmin(t, '-', val / 60, 0))
394
return 0;
395
val %= 60;
396
if (val > t->tm_sec) {
397
if (!adjmin(t, '-', 1, 0))
398
return 0;
399
val -= 60;
400
}
401
t->tm_sec -= val;
402
}
403
break;
404
405
default:
406
if (val > 59)
407
return 0;
408
t->tm_sec = val;
409
}
410
411
return !mk || domktime(t, type) != -1;
412
}
413
414
const struct vary *
415
vary_apply(const struct vary *v, struct tm *t)
416
{
417
char type;
418
char which;
419
char *arg;
420
size_t len;
421
int64_t val;
422
423
for (; v; v = v->next) {
424
type = *v->arg;
425
arg = v->arg;
426
if (type == '+' || type == '-')
427
arg++;
428
else
429
type = '\0';
430
len = strlen(arg);
431
if (len < 2)
432
return v;
433
434
if (type == '\0')
435
t->tm_isdst = -1;
436
437
if (strspn(arg, digits) != len-1) {
438
val = trans(trans_wday, arg);
439
if (val != -1) {
440
if (!adjwday(t, type, val, 1, 1))
441
return v;
442
} else {
443
val = trans(trans_mon, arg);
444
if (val != -1) {
445
if (!adjmon(t, type, val, 1, 1))
446
return v;
447
} else
448
return v;
449
}
450
} else {
451
val = atoi(arg);
452
which = arg[len-1];
453
454
switch (which) {
455
case 'S':
456
if (!adjsec(t, type, val, 1))
457
return v;
458
break;
459
case 'M':
460
if (!adjmin(t, type, val, 1))
461
return v;
462
break;
463
case 'H':
464
if (!adjhour(t, type, val, 1))
465
return v;
466
break;
467
case 'd':
468
t->tm_isdst = -1;
469
if (!adjday(t, type, val, 1))
470
return v;
471
break;
472
case 'w':
473
t->tm_isdst = -1;
474
if (!adjwday(t, type, val, 0, 1))
475
return v;
476
break;
477
case 'm':
478
t->tm_isdst = -1;
479
if (!adjmon(t, type, val, 0, 1))
480
return v;
481
break;
482
case 'y':
483
t->tm_isdst = -1;
484
if (!adjyear(t, type, val, 1))
485
return v;
486
break;
487
default:
488
return v;
489
}
490
}
491
}
492
return 0;
493
}
494
495
void
496
vary_destroy(struct vary *v)
497
{
498
struct vary *n;
499
500
while (v) {
501
n = v->next;
502
free(v);
503
v = n;
504
}
505
}
506
507