1 1.30 kim /* $NetBSD: cal.c,v 1.30 2020/06/29 14:01:14 kim Exp $ */ 2 1.6 glass 3 1.1 cgd /* 4 1.6 glass * Copyright (c) 1989, 1993, 1994 5 1.6 glass * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Kim Letkeman. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.18 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.7 lukem #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.24 lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ 38 1.24 lukem The Regents of the University of California. All rights reserved."); 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd #ifndef lint 42 1.6 glass #if 0 43 1.6 glass static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94"; 44 1.6 glass #else 45 1.30 kim __RCSID("$NetBSD: cal.c,v 1.30 2020/06/29 14:01:14 kim Exp $"); 46 1.6 glass #endif 47 1.1 cgd #endif /* not lint */ 48 1.1 cgd 49 1.1 cgd #include <sys/types.h> 50 1.6 glass 51 1.6 glass #include <ctype.h> 52 1.6 glass #include <err.h> 53 1.14 yamt #include <errno.h> 54 1.14 yamt #include <limits.h> 55 1.1 cgd #include <stdio.h> 56 1.6 glass #include <stdlib.h> 57 1.4 cgd #include <string.h> 58 1.26 roy #include <term.h> 59 1.6 glass #include <time.h> 60 1.8 christos #include <tzfile.h> 61 1.6 glass #include <unistd.h> 62 1.1 cgd 63 1.1 cgd #define SATURDAY 6 /* 1 Jan 1 was a Saturday */ 64 1.1 cgd 65 1.17 atatat #define FIRST_MISSING_DAY reform->first_missing_day 66 1.17 atatat #define NUMBER_MISSING_DAYS reform->missing_days 67 1.1 cgd 68 1.1 cgd #define MAXDAYS 42 /* max slots in a month array */ 69 1.1 cgd #define SPACE -1 /* used in day array */ 70 1.1 cgd 71 1.1 cgd static int days_in_month[2][13] = { 72 1.1 cgd {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 73 1.1 cgd {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 74 1.1 cgd }; 75 1.1 cgd 76 1.27 joerg static int empty[MAXDAYS] = { 77 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 78 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 79 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 80 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 81 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 82 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 83 1.1 cgd }; 84 1.27 joerg static int shift_days[2][4][MAXDAYS + 1]; 85 1.1 cgd 86 1.27 joerg static const char *month_names[12] = { 87 1.1 cgd "January", "February", "March", "April", "May", "June", 88 1.1 cgd "July", "August", "September", "October", "November", "December", 89 1.1 cgd }; 90 1.1 cgd 91 1.30 kim static const char *day_headings = "Su Mo Tu We Th Fr Sa"; 92 1.30 kim static const char *j_day_headings = " Su Mo Tu We Th Fr Sa"; 93 1.1 cgd 94 1.17 atatat /* leap years according to the julian calendar */ 95 1.17 atatat #define j_leap_year(y, m, d) \ 96 1.17 atatat (((m) > 2) && \ 97 1.17 atatat !((y) % 4)) 98 1.17 atatat 99 1.17 atatat /* leap years according to the gregorian calendar */ 100 1.17 atatat #define g_leap_year(y, m, d) \ 101 1.17 atatat (((m) > 2) && \ 102 1.17 atatat ((!((y) % 4) && ((y) % 100)) || \ 103 1.17 atatat !((y) % 400))) 104 1.17 atatat 105 1.17 atatat /* leap year -- account for gregorian reformation at some point */ 106 1.1 cgd #define leap_year(yr) \ 107 1.17 atatat ((yr) <= reform->year ? j_leap_year((yr), 3, 1) : \ 108 1.17 atatat g_leap_year((yr), 3, 1)) 109 1.1 cgd 110 1.17 atatat /* number of julian leap days that have passed by a given date */ 111 1.17 atatat #define j_leap_days(y, m, d) \ 112 1.17 atatat ((((y) - 1) / 4) + j_leap_year(y, m, d)) 113 1.17 atatat 114 1.17 atatat /* number of gregorian leap days that have passed by a given date */ 115 1.17 atatat #define g_leap_days(y, m, d) \ 116 1.17 atatat ((((y) - 1) / 4) - (((y) - 1) / 100) + (((y) - 1) / 400) + \ 117 1.17 atatat g_leap_year(y, m, d)) 118 1.17 atatat 119 1.17 atatat /* 120 1.17 atatat * Subtracting the gregorian leap day count (for a given date) from 121 1.17 atatat * the julian leap day count (for the same date) describes the number 122 1.17 atatat * of days from the date before the shift to the next date that 123 1.17 atatat * appears in the calendar. Since we want to know the number of 124 1.17 atatat * *missing* days, not the number of days that the shift spans, we 125 1.17 atatat * subtract 2. 126 1.17 atatat * 127 1.17 atatat * Alternately... 128 1.17 atatat * 129 1.17 atatat * There's a reason they call the Dark ages the Dark Ages. Part of it 130 1.17 atatat * is that we don't have that many records of that period of time. 131 1.17 atatat * One of the reasons for this is that a lot of the Dark Ages never 132 1.17 atatat * actually took place. At some point in the first millenium A.D., a 133 1.17 atatat * ruler of some power decided that he wanted the number of the year 134 1.17 atatat * to be different than what it was, so he changed it to coincide 135 1.17 atatat * nicely with some event (a birthday or anniversary, perhaps a 136 1.17 atatat * wedding, or maybe a centennial for a largish city). One of the 137 1.17 atatat * side effects of this upon the Gregorian reform is that two Julian 138 1.17 atatat * leap years (leap days celebrated during centennial years that are 139 1.17 atatat * not quatro-centennial years) were skipped. 140 1.17 atatat */ 141 1.17 atatat #define GREGORIAN_MAGIC 2 142 1.17 atatat 143 1.17 atatat /* number of centuries since the reform, not inclusive */ 144 1.17 atatat #define centuries_since_reform(yr) \ 145 1.17 atatat ((yr) > reform->year ? ((yr) / 100) - (reform->year / 100) : 0) 146 1.17 atatat 147 1.17 atatat /* number of centuries since the reform whose modulo of 400 is 0 */ 148 1.17 atatat #define quad_centuries_since_reform(yr) \ 149 1.17 atatat ((yr) > reform->year ? ((yr) / 400) - (reform->year / 400) : 0) 150 1.1 cgd 151 1.1 cgd /* number of leap years between year 1 and this year, not inclusive */ 152 1.1 cgd #define leap_years_since_year_1(yr) \ 153 1.17 atatat ((yr) / 4 - centuries_since_reform(yr) + quad_centuries_since_reform(yr)) 154 1.17 atatat 155 1.27 joerg static struct reform { 156 1.17 atatat const char *country; 157 1.17 atatat int ambiguity, year, month, date; 158 1.17 atatat long first_missing_day; 159 1.17 atatat int missing_days; 160 1.17 atatat /* 161 1.17 atatat * That's 2 for standard/julian display, 4 for months possibly 162 1.17 atatat * affected by the Gregorian shift, and MAXDAYS + 1 for the 163 1.17 atatat * days that get displayed, plus a crib slot. 164 1.17 atatat */ 165 1.17 atatat } *reform, reforms[] = { 166 1.25 lukem { "DEFAULT", 0, 1752, 9, 3, 0, 0 }, 167 1.25 lukem { "Italy", 1, 1582, 10, 5, 0, 0 }, 168 1.25 lukem { "Spain", 1, 1582, 10, 5, 0, 0 }, 169 1.25 lukem { "Portugal", 1, 1582, 10, 5, 0, 0 }, 170 1.25 lukem { "Poland", 1, 1582, 10, 5, 0, 0 }, 171 1.25 lukem { "France", 2, 1582, 12, 10, 0, 0 }, 172 1.25 lukem { "Luxembourg", 2, 1582, 12, 22, 0, 0 }, 173 1.25 lukem { "Netherlands", 2, 1582, 12, 22, 0, 0 }, 174 1.25 lukem { "Bavaria", 0, 1583, 10, 6, 0, 0 }, 175 1.25 lukem { "Austria", 2, 1584, 1, 7, 0, 0 }, 176 1.25 lukem { "Switzerland", 2, 1584, 1, 12, 0, 0 }, 177 1.25 lukem { "Hungary", 0, 1587, 10, 22, 0, 0 }, 178 1.25 lukem { "Germany", 0, 1700, 2, 19, 0, 0 }, 179 1.25 lukem { "Norway", 0, 1700, 2, 19, 0, 0 }, 180 1.25 lukem { "Denmark", 0, 1700, 2, 19, 0, 0 }, 181 1.25 lukem { "Great Britain", 0, 1752, 9, 3, 0, 0 }, 182 1.25 lukem { "England", 0, 1752, 9, 3, 0, 0 }, 183 1.25 lukem { "America", 0, 1752, 9, 3, 0, 0 }, 184 1.25 lukem { "Sweden", 0, 1753, 2, 18, 0, 0 }, 185 1.25 lukem { "Finland", 0, 1753, 2, 18, 0, 0 }, 186 1.25 lukem { "Japan", 0, 1872, 12, 20, 0, 0 }, 187 1.25 lukem { "China", 0, 1911, 11, 7, 0, 0 }, 188 1.25 lukem { "Bulgaria", 0, 1916, 4, 1, 0, 0 }, 189 1.25 lukem { "U.S.S.R.", 0, 1918, 2, 1, 0, 0 }, 190 1.25 lukem { "Serbia", 0, 1919, 1, 19, 0, 0 }, 191 1.25 lukem { "Romania", 0, 1919, 1, 19, 0, 0 }, 192 1.25 lukem { "Greece", 0, 1924, 3, 10, 0, 0 }, 193 1.25 lukem { "Turkey", 0, 1925, 12, 19, 0, 0 }, 194 1.25 lukem { "Egypt", 0, 1928, 9, 18, 0, 0 }, 195 1.25 lukem { NULL, 0, 0, 0, 0, 0, 0 }, 196 1.17 atatat }; 197 1.1 cgd 198 1.27 joerg static int julian; 199 1.27 joerg static int dow; 200 1.27 joerg static int hilite; 201 1.27 joerg static const char *md, *me; 202 1.27 joerg 203 1.27 joerg static void init_hilite(void); 204 1.27 joerg static int getnum(const char *); 205 1.27 joerg static void gregorian_reform(const char *); 206 1.27 joerg static void reform_day_array(int, int, int *, int *, int *,int *,int *,int *); 207 1.27 joerg static int ascii_day(char *, int); 208 1.27 joerg static void center(const char *, int, int); 209 1.27 joerg static void day_array(int, int, int *); 210 1.27 joerg static int day_in_week(int, int, int); 211 1.27 joerg static int day_in_year(int, int, int); 212 1.27 joerg static void monthrange(int, int, int, int, int); 213 1.27 joerg static void trim_trailing_spaces(char *); 214 1.27 joerg __dead static void usage(void); 215 1.6 glass 216 1.6 glass int 217 1.12 perry main(int argc, char **argv) 218 1.1 cgd { 219 1.1 cgd struct tm *local_time; 220 1.6 glass time_t now; 221 1.20 joerg int ch, yflag; 222 1.20 joerg long month, year; 223 1.17 atatat int before, after, use_reform; 224 1.14 yamt int yearly = 0; 225 1.20 joerg char *when, *eoi; 226 1.1 cgd 227 1.14 yamt before = after = 0; 228 1.17 atatat use_reform = yflag = year = 0; 229 1.17 atatat when = NULL; 230 1.28 christos while ((ch = getopt(argc, argv, "A:B:C:d:hjR:ry3")) != -1) { 231 1.12 perry switch (ch) { 232 1.14 yamt case 'A': 233 1.14 yamt after = getnum(optarg); 234 1.23 dholland if (after < 0) 235 1.23 dholland errx(1, "Argument to -A must be positive"); 236 1.14 yamt break; 237 1.14 yamt case 'B': 238 1.14 yamt before = getnum(optarg); 239 1.23 dholland if (before < 0) 240 1.23 dholland errx(1, "Argument to -B must be positive"); 241 1.14 yamt break; 242 1.28 christos case 'C': 243 1.28 christos after = before = getnum(optarg); 244 1.28 christos if (after < 0) 245 1.28 christos errx(1, "Argument to -C must be positive"); 246 1.28 christos break; 247 1.17 atatat case 'd': 248 1.17 atatat dow = getnum(optarg); 249 1.17 atatat if (dow < 0 || dow > 6) 250 1.17 atatat errx(1, "illegal day of week value: use 0-6"); 251 1.17 atatat break; 252 1.15 atatat case 'h': 253 1.15 atatat init_hilite(); 254 1.15 atatat break; 255 1.1 cgd case 'j': 256 1.1 cgd julian = 1; 257 1.1 cgd break; 258 1.17 atatat case 'R': 259 1.17 atatat when = optarg; 260 1.17 atatat break; 261 1.17 atatat case 'r': 262 1.17 atatat use_reform = 1; 263 1.17 atatat break; 264 1.1 cgd case 'y': 265 1.1 cgd yflag = 1; 266 1.1 cgd break; 267 1.13 perry case '3': 268 1.14 yamt before = after = 1; 269 1.13 perry break; 270 1.1 cgd case '?': 271 1.1 cgd default: 272 1.1 cgd usage(); 273 1.12 perry /* NOTREACHED */ 274 1.1 cgd } 275 1.12 perry } 276 1.13 perry 277 1.1 cgd argc -= optind; 278 1.1 cgd argv += optind; 279 1.1 cgd 280 1.17 atatat if (when != NULL) 281 1.17 atatat gregorian_reform(when); 282 1.17 atatat if (reform == NULL) 283 1.17 atatat gregorian_reform("DEFAULT"); 284 1.17 atatat 285 1.1 cgd month = 0; 286 1.12 perry switch (argc) { 287 1.1 cgd case 2: 288 1.20 joerg month = strtol(*argv++, &eoi, 10); 289 1.20 joerg if (month < 1 || month > 12 || *eoi != '\0') 290 1.6 glass errx(1, "illegal month value: use 1-12"); 291 1.20 joerg year = strtol(*argv, &eoi, 10); 292 1.20 joerg if (year < 1 || year > 9999 || *eoi != '\0') 293 1.20 joerg errx(1, "illegal year value: use 1-9999"); 294 1.20 joerg break; 295 1.1 cgd case 1: 296 1.20 joerg year = strtol(*argv, &eoi, 10); 297 1.21 joerg if (year < 1 || year > 9999 || (*eoi != '\0' && *eoi != '/' && *eoi != '-')) 298 1.6 glass errx(1, "illegal year value: use 1-9999"); 299 1.21 joerg if (*eoi != '\0') { 300 1.20 joerg month = strtol(eoi + 1, &eoi, 10); 301 1.20 joerg if (month < 1 || month > 12 || *eoi != '\0') 302 1.20 joerg errx(1, "illegal month value: use 1-12"); 303 1.20 joerg } 304 1.1 cgd break; 305 1.1 cgd case 0: 306 1.1 cgd (void)time(&now); 307 1.1 cgd local_time = localtime(&now); 308 1.17 atatat if (use_reform) 309 1.17 atatat year = reform->year; 310 1.17 atatat else 311 1.17 atatat year = local_time->tm_year + TM_YEAR_BASE; 312 1.17 atatat if (!yflag) { 313 1.17 atatat if (use_reform) 314 1.17 atatat month = reform->month; 315 1.17 atatat else 316 1.17 atatat month = local_time->tm_mon + 1; 317 1.17 atatat } 318 1.1 cgd break; 319 1.1 cgd default: 320 1.1 cgd usage(); 321 1.1 cgd } 322 1.1 cgd 323 1.14 yamt if (!month) { 324 1.14 yamt /* yearly */ 325 1.14 yamt month = 1; 326 1.14 yamt before = 0; 327 1.14 yamt after = 11; 328 1.14 yamt yearly = 1; 329 1.14 yamt } 330 1.14 yamt 331 1.14 yamt monthrange(month, year, before, after, yearly); 332 1.13 perry 333 1.1 cgd exit(0); 334 1.1 cgd } 335 1.1 cgd 336 1.1 cgd #define DAY_LEN 3 /* 3 spaces per day */ 337 1.1 cgd #define J_DAY_LEN 4 /* 4 spaces per day */ 338 1.1 cgd #define WEEK_LEN 20 /* 7 * 3 - one space at the end */ 339 1.1 cgd #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */ 340 1.1 cgd #define HEAD_SEP 2 /* spaces between day headings */ 341 1.1 cgd #define J_HEAD_SEP 2 342 1.14 yamt #define MONTH_PER_ROW 3 /* how many monthes in a row */ 343 1.14 yamt #define J_MONTH_PER_ROW 2 344 1.1 cgd 345 1.27 joerg static void 346 1.14 yamt monthrange(int month, int year, int before, int after, int yearly) 347 1.1 cgd { 348 1.14 yamt int startmonth, startyear; 349 1.14 yamt int endmonth, endyear; 350 1.14 yamt int i, row; 351 1.14 yamt int days[3][MAXDAYS]; 352 1.15 atatat char lineout[256]; 353 1.14 yamt int inayear; 354 1.14 yamt int newyear; 355 1.14 yamt int day_len, week_len, head_sep; 356 1.14 yamt int month_per_row; 357 1.15 atatat int skip, r_off, w_off; 358 1.14 yamt 359 1.14 yamt if (julian) { 360 1.14 yamt day_len = J_DAY_LEN; 361 1.14 yamt week_len = J_WEEK_LEN; 362 1.14 yamt head_sep = J_HEAD_SEP; 363 1.14 yamt month_per_row = J_MONTH_PER_ROW; 364 1.14 yamt } 365 1.14 yamt else { 366 1.14 yamt day_len = DAY_LEN; 367 1.14 yamt week_len = WEEK_LEN; 368 1.14 yamt head_sep = HEAD_SEP; 369 1.14 yamt month_per_row = MONTH_PER_ROW; 370 1.14 yamt } 371 1.14 yamt 372 1.14 yamt month--; 373 1.14 yamt 374 1.14 yamt startyear = year - (before + 12 - 1 - month) / 12; 375 1.14 yamt startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12); 376 1.14 yamt endyear = year + (month + after) / 12; 377 1.14 yamt endmonth = (month + after) % 12; 378 1.1 cgd 379 1.14 yamt if (startyear < 0 || endyear > 9999) { 380 1.29 christos errx(1, "year should be in 1-9999"); 381 1.1 cgd } 382 1.1 cgd 383 1.14 yamt year = startyear; 384 1.14 yamt month = startmonth; 385 1.14 yamt inayear = newyear = (year != endyear || yearly); 386 1.14 yamt if (inayear) { 387 1.14 yamt skip = month % month_per_row; 388 1.14 yamt month -= skip; 389 1.14 yamt } 390 1.14 yamt else { 391 1.14 yamt skip = 0; 392 1.14 yamt } 393 1.14 yamt 394 1.14 yamt do { 395 1.14 yamt if (newyear) { 396 1.14 yamt (void)snprintf(lineout, sizeof(lineout), "%d", year); 397 1.14 yamt center(lineout, week_len * month_per_row + 398 1.14 yamt head_sep * (month_per_row - 1), 0); 399 1.14 yamt (void)printf("\n\n"); 400 1.14 yamt newyear = 0; 401 1.14 yamt } 402 1.14 yamt 403 1.14 yamt for (i = 0; i < skip; i++) 404 1.14 yamt center("", week_len, head_sep); 405 1.14 yamt 406 1.14 yamt for (; i < month_per_row; i++) { 407 1.14 yamt int sep; 408 1.14 yamt 409 1.14 yamt if (year == endyear && month + i > endmonth) 410 1.14 yamt break; 411 1.14 yamt 412 1.14 yamt sep = (i == month_per_row - 1) ? 0 : head_sep; 413 1.14 yamt day_array(month + i + 1, year, days[i]); 414 1.14 yamt if (inayear) { 415 1.14 yamt center(month_names[month + i], week_len, sep); 416 1.14 yamt } 417 1.14 yamt else { 418 1.14 yamt snprintf(lineout, sizeof(lineout), "%s %d", 419 1.14 yamt month_names[month + i], year); 420 1.14 yamt center(lineout, week_len, sep); 421 1.1 cgd } 422 1.1 cgd } 423 1.14 yamt printf("\n"); 424 1.14 yamt 425 1.14 yamt for (i = 0; i < skip; i++) 426 1.14 yamt center("", week_len, head_sep); 427 1.14 yamt 428 1.14 yamt for (; i < month_per_row; i++) { 429 1.14 yamt int sep; 430 1.14 yamt 431 1.14 yamt if (year == endyear && month + i > endmonth) 432 1.14 yamt break; 433 1.14 yamt 434 1.14 yamt sep = (i == month_per_row - 1) ? 0 : head_sep; 435 1.17 atatat if (dow) { 436 1.17 atatat printf("%s ", (julian) ? 437 1.17 atatat j_day_headings + 4 * dow : 438 1.17 atatat day_headings + 3 * dow); 439 1.17 atatat printf("%.*s", dow * (julian ? 4 : 3) - 1, 440 1.17 atatat (julian) ? j_day_headings : day_headings); 441 1.17 atatat } else 442 1.17 atatat printf("%s", (julian) ? j_day_headings : day_headings); 443 1.17 atatat printf("%*s", sep, ""); 444 1.14 yamt } 445 1.14 yamt printf("\n"); 446 1.1 cgd 447 1.1 cgd for (row = 0; row < 6; row++) { 448 1.19 lukem char *p = NULL; 449 1.16 yamt 450 1.16 yamt memset(lineout, ' ', sizeof(lineout)); 451 1.14 yamt for (i = 0; i < skip; i++) { 452 1.16 yamt /* nothing */ 453 1.14 yamt } 454 1.15 atatat w_off = 0; 455 1.14 yamt for (; i < month_per_row; i++) { 456 1.14 yamt int col, *dp; 457 1.14 yamt 458 1.14 yamt if (year == endyear && month + i > endmonth) 459 1.14 yamt break; 460 1.14 yamt 461 1.15 atatat p = lineout + i * (week_len + 2) + w_off; 462 1.14 yamt dp = &days[i][row * 7]; 463 1.15 atatat for (col = 0; col < 7; 464 1.15 atatat col++, p += day_len + r_off) { 465 1.15 atatat r_off = ascii_day(p, *dp++); 466 1.15 atatat w_off += r_off; 467 1.15 atatat } 468 1.1 cgd } 469 1.3 cgd *p = '\0'; 470 1.1 cgd trim_trailing_spaces(lineout); 471 1.1 cgd (void)printf("%s\n", lineout); 472 1.1 cgd } 473 1.13 perry 474 1.14 yamt skip = 0; 475 1.14 yamt month += month_per_row; 476 1.14 yamt if (month >= 12) { 477 1.14 yamt month -= 12; 478 1.14 yamt year++; 479 1.14 yamt newyear = 1; 480 1.13 perry } 481 1.14 yamt } while (year < endyear || (year == endyear && month <= endmonth)); 482 1.13 perry } 483 1.13 perry 484 1.1 cgd /* 485 1.1 cgd * day_array -- 486 1.1 cgd * Fill in an array of 42 integers with a calendar. Assume for a moment 487 1.1 cgd * that you took the (maximum) 6 rows in a calendar and stretched them 488 1.1 cgd * out end to end. You would have 42 numbers or spaces. This routine 489 1.1 cgd * builds that array for any month from Jan. 1 through Dec. 9999. 490 1.1 cgd */ 491 1.27 joerg static void 492 1.12 perry day_array(int month, int year, int *days) 493 1.1 cgd { 494 1.6 glass int day, dw, dm; 495 1.15 atatat time_t t; 496 1.15 atatat struct tm *tm; 497 1.15 atatat 498 1.15 atatat t = time(NULL); 499 1.15 atatat tm = localtime(&t); 500 1.15 atatat tm->tm_year += TM_YEAR_BASE; 501 1.15 atatat tm->tm_mon++; 502 1.15 atatat tm->tm_yday++; /* jan 1 is 1 for us, not 0 */ 503 1.1 cgd 504 1.17 atatat for (dm = month + year * 12, dw = 0; dw < 4; dw++) { 505 1.17 atatat if (dm == shift_days[julian][dw][MAXDAYS]) { 506 1.17 atatat memmove(days, shift_days[julian][dw], 507 1.17 atatat MAXDAYS * sizeof(int)); 508 1.17 atatat return; 509 1.17 atatat } 510 1.1 cgd } 511 1.17 atatat 512 1.6 glass memmove(days, empty, MAXDAYS * sizeof(int)); 513 1.1 cgd dm = days_in_month[leap_year(year)][month]; 514 1.1 cgd dw = day_in_week(1, month, year); 515 1.1 cgd day = julian ? day_in_year(1, month, year) : 1; 516 1.15 atatat while (dm--) { 517 1.15 atatat if (hilite && year == tm->tm_year && 518 1.15 atatat (julian ? (day == tm->tm_yday) : 519 1.15 atatat (month == tm->tm_mon && day == tm->tm_mday))) 520 1.17 atatat days[dw++] = SPACE - day++; 521 1.15 atatat else 522 1.15 atatat days[dw++] = day++; 523 1.15 atatat } 524 1.1 cgd } 525 1.1 cgd 526 1.1 cgd /* 527 1.1 cgd * day_in_year -- 528 1.1 cgd * return the 1 based day number within the year 529 1.1 cgd */ 530 1.27 joerg static int 531 1.12 perry day_in_year(int day, int month, int year) 532 1.1 cgd { 533 1.6 glass int i, leap; 534 1.1 cgd 535 1.1 cgd leap = leap_year(year); 536 1.1 cgd for (i = 1; i < month; i++) 537 1.1 cgd day += days_in_month[leap][i]; 538 1.6 glass return (day); 539 1.1 cgd } 540 1.1 cgd 541 1.1 cgd /* 542 1.1 cgd * day_in_week 543 1.1 cgd * return the 0 based day number for any date from 1 Jan. 1 to 544 1.17 atatat * 31 Dec. 9999. Returns the day of the week of the first 545 1.17 atatat * missing day for any given Gregorian shift. 546 1.1 cgd */ 547 1.27 joerg static int 548 1.12 perry day_in_week(int day, int month, int year) 549 1.1 cgd { 550 1.1 cgd long temp; 551 1.1 cgd 552 1.1 cgd temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) 553 1.1 cgd + day_in_year(day, month, year); 554 1.1 cgd if (temp < FIRST_MISSING_DAY) 555 1.17 atatat return ((temp - dow + 6 + SATURDAY) % 7); 556 1.1 cgd if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) 557 1.17 atatat return (((temp - dow + 6 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); 558 1.17 atatat return ((FIRST_MISSING_DAY - dow + 6 + SATURDAY) % 7); 559 1.1 cgd } 560 1.1 cgd 561 1.27 joerg static int 562 1.12 perry ascii_day(char *p, int day) 563 1.1 cgd { 564 1.15 atatat int display, val, rc; 565 1.15 atatat char *b; 566 1.25 lukem static const char *aday[] = { 567 1.1 cgd "", 568 1.1 cgd " 1", " 2", " 3", " 4", " 5", " 6", " 7", 569 1.1 cgd " 8", " 9", "10", "11", "12", "13", "14", 570 1.1 cgd "15", "16", "17", "18", "19", "20", "21", 571 1.1 cgd "22", "23", "24", "25", "26", "27", "28", 572 1.1 cgd "29", "30", "31", 573 1.1 cgd }; 574 1.1 cgd 575 1.1 cgd if (day == SPACE) { 576 1.1 cgd memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN); 577 1.15 atatat return (0); 578 1.1 cgd } 579 1.17 atatat if (day < SPACE) { 580 1.15 atatat b = p; 581 1.17 atatat day = SPACE - day; 582 1.15 atatat } else 583 1.15 atatat b = NULL; 584 1.1 cgd if (julian) { 585 1.7 lukem if ((val = day / 100) != 0) { 586 1.1 cgd day %= 100; 587 1.1 cgd *p++ = val + '0'; 588 1.1 cgd display = 1; 589 1.1 cgd } else { 590 1.1 cgd *p++ = ' '; 591 1.1 cgd display = 0; 592 1.1 cgd } 593 1.1 cgd val = day / 10; 594 1.1 cgd if (val || display) 595 1.1 cgd *p++ = val + '0'; 596 1.1 cgd else 597 1.1 cgd *p++ = ' '; 598 1.1 cgd *p++ = day % 10 + '0'; 599 1.1 cgd } else { 600 1.1 cgd *p++ = aday[day][0]; 601 1.1 cgd *p++ = aday[day][1]; 602 1.1 cgd } 603 1.15 atatat 604 1.15 atatat rc = 0; 605 1.15 atatat if (b != NULL) { 606 1.26 roy const char *t; 607 1.26 roy char h[64]; 608 1.15 atatat int l; 609 1.15 atatat 610 1.15 atatat l = p - b; 611 1.15 atatat memcpy(h, b, l); 612 1.15 atatat p = b; 613 1.15 atatat 614 1.15 atatat if (md != NULL) { 615 1.15 atatat for (t = md; *t; rc++) 616 1.15 atatat *p++ = *t++; 617 1.15 atatat memcpy(p, h, l); 618 1.15 atatat p += l; 619 1.15 atatat for (t = me; *t; rc++) 620 1.15 atatat *p++ = *t++; 621 1.15 atatat } else { 622 1.15 atatat for (t = &h[0]; l--; t++) { 623 1.15 atatat *p++ = *t; 624 1.15 atatat rc++; 625 1.15 atatat *p++ = '\b'; 626 1.15 atatat rc++; 627 1.15 atatat *p++ = *t; 628 1.15 atatat } 629 1.15 atatat } 630 1.15 atatat } 631 1.15 atatat 632 1.1 cgd *p = ' '; 633 1.15 atatat return (rc); 634 1.1 cgd } 635 1.1 cgd 636 1.27 joerg static void 637 1.12 perry trim_trailing_spaces(char *s) 638 1.1 cgd { 639 1.6 glass char *p; 640 1.1 cgd 641 1.6 glass for (p = s; *p; ++p) 642 1.6 glass continue; 643 1.11 christos while (p > s && isspace((unsigned char)*--p)) 644 1.6 glass continue; 645 1.1 cgd if (p > s) 646 1.1 cgd ++p; 647 1.1 cgd *p = '\0'; 648 1.1 cgd } 649 1.1 cgd 650 1.27 joerg static void 651 1.25 lukem center(const char *str, int len, int separate) 652 1.1 cgd { 653 1.6 glass 654 1.1 cgd len -= strlen(str); 655 1.1 cgd (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, ""); 656 1.1 cgd if (separate) 657 1.1 cgd (void)printf("%*s", separate, ""); 658 1.1 cgd } 659 1.1 cgd 660 1.17 atatat /* 661 1.17 atatat * gregorian_reform -- 662 1.17 atatat * Given a description of date on which the Gregorian Reform was 663 1.17 atatat * applied. The argument can be any of the "country" names 664 1.17 atatat * listed in the reforms array (case insensitive) or a date of 665 1.17 atatat * the form YYYY/MM/DD. The date and month can be omitted if 666 1.17 atatat * doing so would not select more than one different built-in 667 1.17 atatat * reform point. 668 1.17 atatat */ 669 1.27 joerg static void 670 1.17 atatat gregorian_reform(const char *p) 671 1.17 atatat { 672 1.17 atatat int year, month, date; 673 1.17 atatat int i, days, diw, diy; 674 1.17 atatat char c; 675 1.17 atatat 676 1.17 atatat i = sscanf(p, "%d%*[/,-]%d%*[/,-]%d%c", &year, &month, &date, &c); 677 1.17 atatat switch (i) { 678 1.17 atatat case 4: 679 1.17 atatat /* 680 1.17 atatat * If the character was sscanf()ed, then there's more 681 1.17 atatat * stuff than we need. 682 1.17 atatat */ 683 1.17 atatat errx(1, "date specifier %s invalid", p); 684 1.17 atatat case 0: 685 1.17 atatat /* 686 1.17 atatat * Not a form we can sscanf(), so void these, and we 687 1.17 atatat * can try matching "country" names later. 688 1.17 atatat */ 689 1.17 atatat year = month = date = -1; 690 1.17 atatat break; 691 1.17 atatat case 1: 692 1.17 atatat month = 0; 693 1.17 atatat /*FALLTHROUGH*/ 694 1.17 atatat case 2: 695 1.17 atatat date = 0; 696 1.17 atatat /*FALLTHROUGH*/ 697 1.17 atatat case 3: 698 1.17 atatat /* 699 1.17 atatat * At last, some sanity checking on the values we were 700 1.17 atatat * given. 701 1.17 atatat */ 702 1.17 atatat if (year < 1 || year > 9999) 703 1.17 atatat errx(1, "%d: illegal year value: use 1-9999", year); 704 1.17 atatat if (i > 1 && (month < 1 || month > 12)) 705 1.17 atatat errx(1, "%d: illegal month value: use 1-12", month); 706 1.17 atatat if ((i == 3 && date < 1) || date < 0 || 707 1.17 atatat date > days_in_month[1][month]) 708 1.17 atatat /* 709 1.17 atatat * What about someone specifying a leap day in 710 1.17 atatat * a non-leap year? Well...that's a tricky 711 1.17 atatat * one. We can't yet *say* whether the year 712 1.17 atatat * in question is a leap year. What if the 713 1.17 atatat * date given was, for example, 1700/2/29? is 714 1.17 atatat * that a valid leap day? 715 1.17 atatat * 716 1.17 atatat * So...we punt, and hope that saying 29 in 717 1.17 atatat * the case of February isn't too bad an idea. 718 1.17 atatat */ 719 1.17 atatat errx(1, "%d: illegal date value: use 1-%d", date, 720 1.17 atatat days_in_month[1][month]); 721 1.17 atatat break; 722 1.17 atatat } 723 1.17 atatat 724 1.17 atatat /* 725 1.17 atatat * A complete date was specified, so use the other pope. 726 1.17 atatat */ 727 1.17 atatat if (date > 0) { 728 1.17 atatat static struct reform Goestheveezl; 729 1.17 atatat 730 1.17 atatat reform = &Goestheveezl; 731 1.17 atatat reform->country = "Bompzidaize"; 732 1.17 atatat reform->year = year; 733 1.17 atatat reform->month = month; 734 1.17 atatat reform->date = date; 735 1.17 atatat } 736 1.17 atatat 737 1.17 atatat /* 738 1.17 atatat * No date information was specified, so let's try to match on 739 1.17 atatat * country name. 740 1.17 atatat */ 741 1.17 atatat else if (year == -1) { 742 1.17 atatat for (reform = &reforms[0]; reform->year; reform++) { 743 1.17 atatat if (strcasecmp(p, reform->country) == 0) 744 1.17 atatat break; 745 1.17 atatat } 746 1.17 atatat } 747 1.17 atatat 748 1.17 atatat /* 749 1.17 atatat * We have *some* date information, but not a complete date. 750 1.17 atatat * Let's see if we have enough to pick a single entry from the 751 1.17 atatat * list that's not ambiguous. 752 1.17 atatat */ 753 1.17 atatat else { 754 1.17 atatat for (reform = &reforms[0]; reform->year; reform++) { 755 1.17 atatat if ((year == 0 || year == reform->year) && 756 1.17 atatat (month == 0 || month == reform->month) && 757 1.17 atatat (date == 0 || month == reform->date)) 758 1.17 atatat break; 759 1.17 atatat } 760 1.17 atatat 761 1.17 atatat if (i <= reform->ambiguity) 762 1.17 atatat errx(1, "%s: ambiguous short reform date specification", p); 763 1.17 atatat } 764 1.17 atatat 765 1.17 atatat /* 766 1.17 atatat * Oops...we reached the end of the list. 767 1.17 atatat */ 768 1.17 atatat if (reform->year == 0) 769 1.17 atatat errx(1, "reform name %s invalid", p); 770 1.17 atatat 771 1.17 atatat /* 772 1.28 christos * 773 1.17 atatat */ 774 1.17 atatat reform->missing_days = 775 1.17 atatat j_leap_days(reform->year, reform->month, reform->date) - 776 1.17 atatat g_leap_days(reform->year, reform->month, reform->date) - 777 1.17 atatat GREGORIAN_MAGIC; 778 1.17 atatat 779 1.17 atatat reform->first_missing_day = 780 1.17 atatat (reform->year - 1) * 365 + 781 1.17 atatat day_in_year(reform->date, reform->month, reform->year) + 782 1.17 atatat date + 783 1.17 atatat j_leap_days(reform->year, reform->month, reform->date); 784 1.17 atatat 785 1.17 atatat /* 786 1.17 atatat * Once we know the day of the week of the first missing day, 787 1.17 atatat * skip back to the first of the month's day of the week. 788 1.17 atatat */ 789 1.17 atatat diw = day_in_week(reform->date, reform->month, reform->year); 790 1.17 atatat diw = (diw + 8 - (reform->date % 7)) % 7; 791 1.17 atatat diy = day_in_year(1, reform->month, reform->year); 792 1.17 atatat 793 1.17 atatat /* 794 1.17 atatat * We might need all four of these (if you switch from Julian 795 1.17 atatat * to Gregorian at some point after 9900, you get a gap of 73 796 1.17 atatat * days, and that can affect four months), and it doesn't hurt 797 1.17 atatat * all that much to precompute them, so there. 798 1.17 atatat */ 799 1.17 atatat date = 1; 800 1.17 atatat days = 0; 801 1.17 atatat for (i = 0; i < 4; i++) 802 1.17 atatat reform_day_array(reform->month + i, reform->year, 803 1.17 atatat &days, &date, &diw, &diy, 804 1.17 atatat shift_days[0][i], 805 1.17 atatat shift_days[1][i]); 806 1.17 atatat } 807 1.17 atatat 808 1.17 atatat /* 809 1.17 atatat * reform_day_array -- 810 1.17 atatat * Pre-calculates the given month's calendar (in both "standard" 811 1.17 atatat * and "julian day" representations) with respect for days 812 1.17 atatat * skipped during a reform period. 813 1.17 atatat */ 814 1.27 joerg static void 815 1.17 atatat reform_day_array(int month, int year, int *done, int *date, int *diw, int *diy, 816 1.17 atatat int *scal, int *jcal) 817 1.17 atatat { 818 1.17 atatat int mdays; 819 1.17 atatat 820 1.17 atatat /* 821 1.17 atatat * If the reform was in the month of october or later, then 822 1.17 atatat * the month number from the caller could "overflow". 823 1.17 atatat */ 824 1.17 atatat if (month > 12) { 825 1.17 atatat month -= 12; 826 1.17 atatat year++; 827 1.17 atatat } 828 1.17 atatat 829 1.17 atatat /* 830 1.17 atatat * Erase months, and set crib number. The crib number is used 831 1.17 atatat * later to determine if the month to be displayed is here or 832 1.17 atatat * should be built on the fly with the generic routine 833 1.17 atatat */ 834 1.17 atatat memmove(scal, empty, MAXDAYS * sizeof(int)); 835 1.17 atatat scal[MAXDAYS] = month + year * 12; 836 1.17 atatat memmove(jcal, empty, MAXDAYS * sizeof(int)); 837 1.17 atatat jcal[MAXDAYS] = month + year * 12; 838 1.17 atatat 839 1.17 atatat /* 840 1.17 atatat * It doesn't matter what the actual month is when figuring 841 1.17 atatat * out if this is a leap year or not, just so long as February 842 1.17 atatat * gets the right number of days in it. 843 1.17 atatat */ 844 1.17 atatat mdays = days_in_month[g_leap_year(year, 3, 1)][month]; 845 1.17 atatat 846 1.17 atatat /* 847 1.17 atatat * Bounce back to the first "row" in the day array, and fill 848 1.17 atatat * in any days that actually occur. 849 1.17 atatat */ 850 1.17 atatat for (*diw %= 7; (*date - *done) <= mdays; (*date)++, (*diy)++) { 851 1.17 atatat /* 852 1.17 atatat * "date" doesn't get reset by the caller across calls 853 1.17 atatat * to this routine, so we can actually tell that we're 854 1.17 atatat * looking at April the 41st. Much easier than trying 855 1.17 atatat * to calculate the absolute julian day for a given 856 1.17 atatat * date and then checking that. 857 1.17 atatat */ 858 1.17 atatat if (*date < reform->date || 859 1.17 atatat *date >= reform->date + reform->missing_days) { 860 1.17 atatat scal[*diw] = *date - *done; 861 1.17 atatat jcal[*diw] = *diy; 862 1.17 atatat (*diw)++; 863 1.17 atatat } 864 1.17 atatat } 865 1.17 atatat *done += mdays; 866 1.17 atatat } 867 1.17 atatat 868 1.27 joerg static int 869 1.14 yamt getnum(const char *p) 870 1.14 yamt { 871 1.25 lukem unsigned long result; 872 1.14 yamt char *ep; 873 1.14 yamt 874 1.14 yamt errno = 0; 875 1.14 yamt result = strtoul(p, &ep, 10); 876 1.14 yamt if (p[0] == '\0' || *ep != '\0') 877 1.14 yamt goto error; 878 1.14 yamt if (errno == ERANGE && result == ULONG_MAX) 879 1.14 yamt goto error; 880 1.14 yamt if (result > INT_MAX) 881 1.14 yamt goto error; 882 1.14 yamt 883 1.14 yamt return (int)result; 884 1.14 yamt 885 1.14 yamt error: 886 1.14 yamt errx(1, "bad number: %s", p); 887 1.14 yamt /*NOTREACHED*/ 888 1.14 yamt } 889 1.14 yamt 890 1.27 joerg static void 891 1.15 atatat init_hilite(void) 892 1.15 atatat { 893 1.25 lukem const char *term; 894 1.26 roy int errret; 895 1.15 atatat 896 1.15 atatat hilite++; 897 1.15 atatat 898 1.15 atatat if (!isatty(fileno(stdout))) 899 1.15 atatat return; 900 1.15 atatat 901 1.25 lukem term = getenv("TERM"); 902 1.25 lukem if (term == NULL) 903 1.25 lukem term = "dumb"; 904 1.26 roy if (setupterm(term, fileno(stdout), &errret) != 0 && errret != 1) 905 1.15 atatat return; 906 1.15 atatat 907 1.26 roy if (hilite > 1) 908 1.26 roy md = enter_reverse_mode; 909 1.26 roy else 910 1.26 roy md = enter_bold_mode; 911 1.26 roy me = exit_attribute_mode; 912 1.15 atatat if (me == NULL || md == NULL) 913 1.15 atatat md = me = NULL; 914 1.15 atatat } 915 1.15 atatat 916 1.27 joerg static void 917 1.12 perry usage(void) 918 1.1 cgd { 919 1.6 glass 920 1.14 yamt (void)fprintf(stderr, 921 1.28 christos "usage: cal [-3hjry] [-A after] [-B before] [-C context] [-d day-of-week] " 922 1.17 atatat "[-R reform-spec]\n [[month] year]\n"); 923 1.1 cgd exit(1); 924 1.1 cgd } 925