xref: /dragonfly/usr.bin/calendar/dates.c (revision d19ef5a274debcb71f2e8cd8dce8b954dc73944b)
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
generate_dates(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
free_dates(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 *
loop_dates(struct cal_day * dp)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 *
find_rd(int rd,int offset)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 *
event_add(struct cal_day * dp,bool day_first,bool variable,struct cal_desc * desc,char * extra)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
event_print_all(FILE * fp)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