libc/nls: Sync with FreeBSD.
[dragonfly.git] / usr.bin / calendar / day.c
blob016217f043d8eac7149b97872519b7e0215e3b8d
1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
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 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * $FreeBSD: src/usr.bin/calendar/day.c,v 1.27 2007/06/09 05:54:13 grog Exp $
30 * $DragonFly: src/usr.bin/calendar/day.c,v 1.6 2007/09/24 20:31:44 pavalos Exp $
33 #include <sys/types.h>
34 #include <sys/uio.h>
35 #include <ctype.h>
36 #include <err.h>
37 #include <locale.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
43 #include "pathnames.h"
44 #include "calendar.h"
46 extern struct iovec header[];
48 struct tm *tp;
49 static const struct tm tm0;
50 int *cumdays, yrdays;
52 static char dayname[10];
54 /* 1-based month, 0-based days, cumulative */
55 static int daytab[][14] = {
56 { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
57 { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
60 static char const *days[] = {
61 "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
64 static const char *months[] = {
65 "jan", "feb", "mar", "apr", "may", "jun",
66 "jul", "aug", "sep", "oct", "nov", "dec", NULL,
69 static struct fixs fndays[8]; /* full national days names */
70 static struct fixs ndays[8]; /* short national days names */
72 static struct fixs fnmonths[13]; /* full national months names */
73 static struct fixs nmonths[13]; /* short national month names */
75 void
76 setnnames(void)
78 char buf[80];
79 int i, l;
80 struct tm tm;
82 for (i = 0; i < 7; i++) {
83 tm.tm_wday = i;
84 strftime(buf, sizeof(buf), "%a", &tm);
85 for (l = strlen(buf);
86 l > 0 && isspace((unsigned char)buf[l - 1]);
87 l--)
89 buf[l] = '\0';
90 if (ndays[i].name != NULL)
91 free(ndays[i].name);
92 if ((ndays[i].name = strdup(buf)) == NULL)
93 errx(EXIT_FAILURE, "cannot allocate memory");
94 ndays[i].len = strlen(buf);
96 strftime(buf, sizeof(buf), "%A", &tm);
97 for (l = strlen(buf);
98 l > 0 && isspace((unsigned char)buf[l - 1]);
99 l--)
101 buf[l] = '\0';
102 if (fndays[i].name != NULL)
103 free(fndays[i].name);
104 if ((fndays[i].name = strdup(buf)) == NULL)
105 errx(EXIT_FAILURE, "cannot allocate memory");
106 fndays[i].len = strlen(buf);
109 for (i = 0; i < 12; i++) {
110 tm.tm_mon = i;
111 strftime(buf, sizeof(buf), "%b", &tm);
112 for (l = strlen(buf);
113 l > 0 && isspace((unsigned char)buf[l - 1]);
114 l--)
116 buf[l] = '\0';
117 if (nmonths[i].name != NULL)
118 free(nmonths[i].name);
119 if ((nmonths[i].name = strdup(buf)) == NULL)
120 errx(EXIT_FAILURE, "cannot allocate memory");
121 nmonths[i].len = strlen(buf);
123 strftime(buf, sizeof(buf), "%B", &tm);
124 for (l = strlen(buf);
125 l > 0 && isspace((unsigned char)buf[l - 1]);
126 l--)
128 buf[l] = '\0';
129 if (fnmonths[i].name != NULL)
130 free(fnmonths[i].name);
131 if ((fnmonths[i].name = strdup(buf)) == NULL)
132 errx(EXIT_FAILURE, "cannot allocate memory");
133 fnmonths[i].len = strlen(buf);
137 void
138 settime(time_t now)
140 char *oldl, *lbufp;
142 tp = localtime(&now);
143 if (isleap(tp->tm_year + 1900)) {
144 yrdays = 366;
145 cumdays = daytab[1];
146 } else {
147 yrdays = 365;
148 cumdays = daytab[0];
150 /* Friday displays Monday's events */
151 if (f_dayAfter == 0 && f_dayBefore == 0 && Friday != -1)
152 f_dayAfter = tp->tm_wday == Friday ? 3 : 1;
153 header[5].iov_base = dayname;
155 oldl = NULL;
156 lbufp = setlocale(LC_TIME, NULL);
157 if (lbufp != NULL && (oldl = strdup(lbufp)) == NULL)
158 errx(EXIT_FAILURE, "cannot allocate memory");
159 setlocale(LC_TIME, "C");
160 header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
161 setlocale(LC_TIME, (oldl != NULL ? oldl : ""));
162 if (oldl != NULL)
163 free(oldl);
165 setnnames();
168 /* convert Day[/Month][/Year] into unix time (since 1970)
169 * Day: two digits, Month: two digits, Year: digits
171 time_t
172 Mktime(char *dp)
174 time_t t;
175 int d, m, y;
176 struct tm tm;
178 time(&t);
179 tp = localtime(&t);
181 tm = tm0;
182 tm.tm_mday = tp->tm_mday;
183 tm.tm_mon = tp->tm_mon;
184 tm.tm_year = tp->tm_year;
186 switch (sscanf(dp, "%d.%d.%d", &d, &m, &y)) {
187 case 3:
188 if (y > 1900)
189 y -= 1900;
190 tm.tm_year = y;
191 /* FALLTHROUGH */
192 case 2:
193 tm.tm_mon = m - 1;
194 /* FALLTHROUGH */
195 case 1:
196 tm.tm_mday = d;
199 #ifdef DEBUG
200 fprintf(stderr, "Mktime: %d %d %s\n", (int)mktime(&tm), (int)t,
201 asctime(&tm));
202 #endif
203 return(mktime(&tm));
207 * Possible date formats include any combination of:
208 * 3-charmonth (January, Jan, Jan)
209 * 3-charweekday (Friday, Monday, mon.)
210 * numeric month or day (1, 2, 04)
212 * Any character may separate them, or they may not be separated. Any line,
213 * following a line that is matched, that starts with "whitespace", is shown
214 * along with the matched line.
217 isnow(char *endp, int *monthp, int *dayp, int *varp)
219 int day, flags, month = 0, v1, v2;
222 * CONVENTION
224 * Month: 1-12
225 * Monthname: Jan .. Dec
226 * Day: 1-31
227 * Weekday: Mon-Sun
231 flags = 0;
233 /* read first field */
234 /* didn't recognize anything, skip it */
235 if (!(v1 = getfield(endp, &endp, &flags)))
236 return (0);
238 /* Easter or Easter depending days */
239 if (flags & F_EASTER)
240 day = v1 - 1; /* days since January 1 [0-365] */
243 * 1. {Weekday,Day} XYZ ...
245 * where Day is > 12
247 else if (flags & F_ISDAY || v1 > 12) {
249 /* found a day; day: 1-31 or weekday: 1-7 */
250 day = v1;
252 /* {Day,Weekday} {Month,Monthname} ... */
253 /* if no recognizable month, assume just a day alone
254 * in other words, find month or use current month */
255 if (!(month = getfield(endp, &endp, &flags)))
256 month = tp->tm_mon + 1;
259 /* 2. {Monthname} XYZ ... */
260 else if (flags & F_ISMONTH) {
261 month = v1;
263 /* Monthname {day,weekday} */
264 /* if no recognizable day, assume the first day in month */
265 if (!(day = getfield(endp, &endp, &flags)))
266 day = 1;
269 /* Hm ... */
270 else {
271 v2 = getfield(endp, &endp, &flags);
274 * {Day} {Monthname} ...
275 * where Day <= 12
277 if (flags & F_ISMONTH) {
278 day = v1;
279 month = v2;
280 *varp = 0;
283 /* {Month} {Weekday,Day} ... */
284 else {
285 /* F_ISDAY set, v2 > 12, or no way to tell */
286 month = v1;
287 /* if no recognizable day, assume the first */
288 day = v2 ? v2 : 1;
289 *varp = 0;
293 /* convert Weekday into *next* Day,
294 * e.g.: 'Sunday' -> 22
295 * 'SundayLast' -> ??
297 if (flags & F_ISDAY) {
298 #ifdef DEBUG
299 fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month);
300 #endif
302 *varp = 1;
303 /* variable weekday, SundayLast, MondayFirst ... */
304 if (day < 0 || day >= 10) {
306 /* negative offset; last, -4 .. -1 */
307 if (day < 0) {
308 v1 = day/10 - 1; /* offset -4 ... -1 */
309 day = 10 + (day % 10); /* day 1 ... 7 */
311 /* day, eg '22nd' */
312 v2 = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
314 /* (month length - day) / 7 + 1 */
315 if (cumdays[month+1] - cumdays[month] >= v2
316 && ((int)((cumdays[month+1] -
317 cumdays[month] - v2) / 7) + 1) == -v1)
318 /* bingo ! */
319 day = v2;
321 /* set to yesterday */
322 else {
323 day = tp->tm_mday - 1;
324 if (day == 0)
325 return (0);
329 /* first, second ... +1 ... +5 */
330 else {
331 v1 = day/10; /* offset: +1 (first Sunday) ... */
332 day = day % 10;
334 /* day, eg '22th' */
335 v2 = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
337 /* Hurrah! matched */
338 if (((v2 - 1 + 7) / 7) == v1)
339 day = v2;
341 /* set to yesterday */
342 else {
343 day = tp->tm_mday - 1;
344 if (day == 0)
345 return (0);
350 /* wired */
351 else {
352 day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
353 *varp = 1;
357 if (!(flags & F_EASTER)) {
358 if (day + cumdays[month] > cumdays[month + 1]) { /* off end of month */
359 day -= (cumdays[month + 1] - cumdays[month]); /* adjust */
360 if (++month > 12) /* next year */
361 month = 1;
363 *monthp = month;
364 *dayp = day;
365 day = cumdays[month] + day;
367 else {
368 for (v1 = 0; day > cumdays[v1]; v1++)
370 *monthp = v1 - 1;
371 *dayp = day - cumdays[v1 - 1];
372 *varp = 1;
375 #ifdef DEBUG
376 fprintf(stderr, "day2: day %d(%d-%d) yday %d\n", *dayp, day,
377 cumdays[month], tp->tm_yday);
378 #endif
380 /* When days before or days after is specified */
381 /* no year rollover */
382 if (day >= tp->tm_yday - f_dayBefore &&
383 day <= tp->tm_yday + f_dayAfter)
384 return(1);
386 /* next year */
387 if (tp->tm_yday + f_dayAfter >= yrdays) {
388 int end = tp->tm_yday + f_dayAfter - yrdays;
389 if (day <= end)
390 return(1);
393 /* previous year */
394 if (tp->tm_yday - f_dayBefore < 0) {
395 int before = yrdays + (tp->tm_yday - f_dayBefore);
396 if (day >= before)
397 return(1);
400 return(0);
405 getmonth(char *s)
407 const char **p;
408 struct fixs *n;
410 for (n = fnmonths; n->name; ++n)
411 if (!strncasecmp(s, n->name, n->len))
412 return ((n - fnmonths) + 1);
413 for (n = nmonths; n->name; ++n)
414 if (!strncasecmp(s, n->name, n->len))
415 return ((n - nmonths) + 1);
416 for (p = months; *p; ++p)
417 if (!strncasecmp(s, *p, 3))
418 return ((p - months) + 1);
419 return(0);
424 getday(char *s)
426 const char **p;
427 struct fixs *n;
429 for (n = fndays; n->name; ++n)
430 if (!strncasecmp(s, n->name, n->len))
431 return((n - fndays) + 1);
432 for (n = ndays; n->name; ++n)
433 if (!strncasecmp(s, n->name, n->len))
434 return((n - ndays) + 1);
435 for (p = days; *p; ++p)
436 if (!strncasecmp(s, *p, 3))
437 return((p - days) + 1);
438 return(0);
441 /* return offset for variable weekdays
442 * -1 -> last weekday in month
443 * +1 -> first weekday in month
444 * ... etc ...
447 getdayvar(char *s)
449 int offs;
452 offs = strlen(s);
455 /* Sun+1 or Wednesday-2
456 * ^ ^ */
458 /* fprintf(stderr, "x: %s %s %d\n", s, s + offs - 2, offs); */
459 switch(*(s + offs - 2)) {
460 case '-':
461 return(-(atoi(s + offs - 1)));
462 case '+':
463 return(atoi(s + offs - 1));
468 * some aliases: last, first, second, third, fourth
471 /* last */
472 if (offs > 4 && !strcasecmp(s + offs - 4, "last"))
473 return(-1);
474 else if (offs > 5 && !strcasecmp(s + offs - 5, "first"))
475 return(+1);
476 else if (offs > 6 && !strcasecmp(s + offs - 6, "second"))
477 return(+2);
478 else if (offs > 5 && !strcasecmp(s + offs - 5, "third"))
479 return(+3);
480 else if (offs > 6 && !strcasecmp(s + offs - 6, "fourth"))
481 return(+4);
484 /* no offset detected */
485 return(0);