xref: /dragonfly/usr.bin/calendar/dates.c (revision cbc43ac3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
5  * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The DragonFly Project
9  * by Aaron LI <aly@aaronly.me>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: head/usr.bin/calendar/dates.c 326276 2017-11-27 15:37:16Z pfg $
33  */
34 
35 #include <assert.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <time.h>
40 
41 #include "calendar.h"
42 #include "basics.h"
43 #include "dates.h"
44 #include "gregorian.h"
45 #include "io.h"
46 #include "utils.h"
47 
48 
49 struct event {
50 	bool		 variable;  /* Whether a variable event ? */
51 	char		 date[32];  /* Date in Gregorian calendar */
52 	char		 date_user[64];  /* Date in user-chosen calendar */
53 	struct cal_desc *description;  /* Event description */
54 	char		*extra;  /* Extra data of the event */
55 	struct event	*next;
56 };
57 
58 static struct cal_day *cal_days = NULL;
59 
60 
61 void
62 generate_dates(void)
63 {
64 	struct cal_day *dp;
65 	struct date date;
66 	int daycount, dow, year, month, day;
67 	int rd_month1, rd_nextmonth, rd_nextyear;
68 
69 	daycount = Options.day_end - Options.day_begin + 1;
70 	cal_days = xcalloc((size_t)daycount, sizeof(struct cal_day));
71 
72 	dow = dayofweek_from_fixed(Options.day_begin);
73 	gregorian_from_fixed(Options.day_begin, &date);
74 	year = date.year;
75 	month = date.month;
76 	day = date.day;
77 
78 	date.day = 1;
79 	rd_month1 = fixed_from_gregorian(&date);
80 	if (date.month == 12) {
81 		date_set(&date, date.year+1, 1, 1);
82 		rd_nextmonth = fixed_from_gregorian(&date);
83 		rd_nextyear = rd_nextmonth;
84 	} else {
85 		date.month++;
86 		rd_nextmonth = fixed_from_gregorian(&date);
87 		date_set(&date, date.year+1, 1, 1);
88 		rd_nextyear = fixed_from_gregorian(&date);
89 	}
90 
91 	for (int i = 0; i < daycount; i++) {
92 		dp = &cal_days[i];
93 		dp->rd = Options.day_begin + i;
94 
95 		if (dp->rd == rd_nextmonth) {
96 			month++;
97 			day = 1;
98 			rd_month1 = rd_nextmonth;
99 			if (dp->rd == rd_nextyear) {
100 				year++;
101 				month = 1;
102 			}
103 
104 			date_set(&date, year, month, day);
105 			if (date.month == 12) {
106 				date_set(&date, date.year+1, 1, 1);
107 				rd_nextmonth = fixed_from_gregorian(&date);
108 				rd_nextyear = rd_nextmonth;
109 			} else {
110 				date.month++;
111 				rd_nextmonth = fixed_from_gregorian(&date);
112 				date_set(&date, date.year+1, 1, 1);
113 				rd_nextyear = fixed_from_gregorian(&date);
114 			}
115 		}
116 
117 		dp->year = year;
118 		dp->month = month;
119 		dp->day = dp->rd - rd_month1 + 1;
120 		dp->dow[0] = (dow + i) % 7;
121 		dp->dow[1] = (dp->rd - rd_month1) / 7 + 1;
122 		dp->dow[2] = -((rd_nextmonth - dp->rd - 1) / 7 + 1);
123 		dp->last_dom = (dp->rd == rd_nextmonth - 1);
124 
125 		DPRINTF("%s: [%d] rd:%d, date:%d-%02d-%02d, dow:[%d,%d,%d]\n",
126 			__func__, i, dp->rd, dp->year, dp->month,
127 			dp->day, dp->dow[0], dp->dow[1], dp->dow[2]);
128 	}
129 }
130 
131 void
132 free_dates(void)
133 {
134 	struct event *e;
135 	struct cal_day *dp = NULL;
136 
137 	while ((dp = loop_dates(dp)) != NULL) {
138 		while ((e = dp->events) != NULL) {
139 			dp->events = e->next;
140 			free(e->extra);
141 			free(e);
142 		}
143 	}
144 	free(cal_days);
145 }
146 
147 struct cal_day *
148 loop_dates(struct cal_day *dp)
149 {
150 	int daycount = Options.day_end - Options.day_begin + 1;
151 
152 	if (dp == NULL)
153 		dp = &cal_days[0];
154 	else
155 		dp++;
156 
157 	if (dp < &cal_days[0] || dp > &cal_days[daycount-1])
158 		return NULL;
159 	else
160 		return dp;
161 }
162 
163 
164 struct cal_day *
165 find_rd(int rd, int offset)
166 {
167 	rd += offset;
168 	if (rd < Options.day_begin || rd > Options.day_end)
169 		return NULL;
170 
171 	return &cal_days[rd - Options.day_begin];
172 }
173 
174 
175 struct event *
176 event_add(struct cal_day *dp, bool day_first, bool variable,
177 	  struct cal_desc *desc, char *extra)
178 {
179 	struct event *e;
180 	struct date gdate;
181 	struct tm tm = { 0 };
182 
183 	e = xcalloc(1, sizeof(*e));
184 
185 	gregorian_from_fixed(dp->rd, &gdate);
186 	tm.tm_year = gdate.year - 1900;
187 	tm.tm_mon = gdate.month - 1;
188 	tm.tm_mday = gdate.day;
189 	strftime(e->date, sizeof(e->date),
190 		 (day_first ? "%e %b" : "%b %e"), &tm);
191 	if (Calendar->format_date != NULL) {
192 		(Calendar->format_date)(e->date_user, sizeof(e->date_user),
193 					dp->rd);
194 	}
195 
196 	e->variable = variable;
197 	e->description = desc;
198 	if (extra != NULL && extra[0] != '\0')
199 		e->extra = extra;
200 
201 	e->next = dp->events;
202 	dp->events = e;
203 
204 	return (e);
205 }
206 
207 void
208 event_print_all(FILE *fp)
209 {
210 	struct event *e;
211 	struct cal_day *dp = NULL;
212 	struct cal_desc *desc;
213 	struct cal_line *line;
214 
215 	while ((dp = loop_dates(dp)) != NULL) {
216 		for (e = dp->events; e != NULL; e = e->next) {
217 			fprintf(fp, "%s%c\t", e->date, e->variable ? '*' : ' ');
218 			if (e->date_user[0] != '\0')
219 				fprintf(fp, "[%s] ", e->date_user);
220 
221 			desc = e->description;
222 			for (line = desc->firstline; line; line = line->next) {
223 				fprintf(fp, "%s%s%s",
224 					(line == desc->firstline) ? "" : "\t\t",
225 					line->str,
226 					(line == desc->lastline) ? "" : "\n");
227 			}
228 			if (e->extra)
229 				fprintf(fp, " (%s)", e->extra);
230 
231 			fputc('\n', fp);
232 			fflush(fp);
233 		}
234 	}
235 }
236