From a99e83a2dc64913b9c73fdffb51d90f2b6a7b3e2 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Fri, 15 Mar 2013 23:52:39 +0000 Subject: [PATCH] Importing usr.bin/cal --- commands/Makefile | 2 +- commands/cal/Makefile | 6 - commands/cal/cal.c | 315 -------------- man/man1/Makefile | 2 +- man/man1/cal.1 | 29 -- releasetools/nbsd_ports | 1 + usr.bin/Makefile | 2 +- usr.bin/cal/Makefile | 8 + usr.bin/cal/README | 42 ++ usr.bin/cal/cal.1 | 160 +++++++ usr.bin/cal/cal.c | 919 ++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1133 insertions(+), 353 deletions(-) delete mode 100644 commands/cal/Makefile delete mode 100644 commands/cal/cal.c delete mode 100644 man/man1/cal.1 create mode 100644 usr.bin/cal/Makefile create mode 100644 usr.bin/cal/README create mode 100644 usr.bin/cal/cal.1 create mode 100644 usr.bin/cal/cal.c diff --git a/commands/Makefile b/commands/Makefile index a38a66cb0..33bd80cb0 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -2,7 +2,7 @@ .include -SUBDIR= add_route arp ash at backup banner basename btrace cal \ +SUBDIR= add_route arp ash at backup banner basename btrace \ cawf cd cdprobe cpp \ chmod chown ci cleantmp cmp co \ comm compress cp crc cron crontab cut \ diff --git a/commands/cal/Makefile b/commands/cal/Makefile deleted file mode 100644 index 603d422db..000000000 --- a/commands/cal/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# LSC For now... -NOGCCERROR:=yes -PROG= cal -MAN= - -.include diff --git a/commands/cal/cal.c b/commands/cal/cal.c deleted file mode 100644 index b06fcbd05..000000000 --- a/commands/cal/cal.c +++ /dev/null @@ -1,315 +0,0 @@ -/* cal - print a calendar Author: Maritn Minow */ - -#include -#include -#include - -#define do3months domonth -#define IO_SUCCESS 0 /* Unix definitions */ -#define IO_ERROR 1 -#define EOS 0 - -#define ENTRY_SIZE 3 /* 3 bytes per value */ -#define DAYS_PER_WEEK 7 /* Sunday, etc. */ -#define WEEKS_PER_MONTH 6 /* Max. weeks in a month */ -#define MONTHS_PER_LINE 3 /* Three months across */ -#define MONTH_SPACE 3 /* Between each month */ - -char *badarg = {"Bad argument\n"}; -char *how = {"Usage: cal [month] year\n"}; - -/* Calendar() stuffs data into layout[], - * output() copies from layout[] to outline[], (then trims blanks). - */ -char layout[MONTHS_PER_LINE][WEEKS_PER_MONTH][DAYS_PER_WEEK][ENTRY_SIZE]; -char outline[(MONTHS_PER_LINE * DAYS_PER_WEEK * ENTRY_SIZE) - + (MONTHS_PER_LINE * MONTH_SPACE) - + 1]; - -char *weekday = " S M Tu W Th F S"; -char *monthname[] = { - "???", /* No month 0 */ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -int main(int argc, char **argv); -void doyear(int year); -void domonth(int year, int month); -void output(int nmonths); -void calendar(int year, int month, int indx); -void usage(char *s); -int date(int year, int month, int week, int wday); -void setmonth(int year, int month); -int getdate(int week, int wday); -static int Jan1(int year); - -int main(argc, argv) -int argc; -char *argv[]; -{ - register int year; - - register int arg1val; - int arg1len; - int arg2val; - - if (argc <= 1) { - usage(how); - } else { - arg1val = atoi(argv[1]); - arg1len = strlen(argv[1]); - if (argc == 2) { - /* Only one argument, if small, it's a month. If - * large, it's a year. Note: cal 0082 Year - * 0082 cal 82 Year 0082 */ - if (arg1len <= 2 && arg1val <= 12) - do3months(year, arg1val); - else - doyear(arg1val); - } else { - /* Two arguments, allow 1980 12 or 12 1980 */ - arg2val = atoi(argv[2]); - if (arg1len > 2) - do3months(arg1val, arg2val); - else - do3months(arg2val, arg1val); - } - } - return(IO_SUCCESS); -} - -void doyear(year) -int year; -/* Print the calendar for an entire year. */ -{ - register int month; - - if (year < 1 || year > 9999) usage(badarg); - if (year < 100) - printf("\n\n\n 00%2d\n\n", year); - else - printf("\n\n\n%35d\n\n", year); - for (month = 1; month <= 12; month += MONTHS_PER_LINE) { - printf("%12s%23s%23s\n", - monthname[month], - monthname[month + 1], - monthname[month + 2]); - printf("%s %s %s\n", weekday, weekday, weekday); - calendar(year, month + 0, 0); - calendar(year, month + 1, 1); - calendar(year, month + 2, 2); - output(3); -#if MONTHS_PER_LINE != 3 -#error "the above will not work" -#endif - } - printf("\n\n\n"); -} - -void domonth(year, month) -int year; -int month; -/* Do one specific month -- note: no longer used */ -{ - if (year < 1 || year > 9999) usage(badarg); - if (month <= 0 || month > 12) usage(badarg); - printf("%9s%5d\n\n%s\n", monthname[month], year, weekday); - calendar(year, month, 0); - output(1); - printf("\n\n"); -} - -void output(nmonths) -int nmonths; /* Number of months to do */ -/* Clean up and output the text. */ -{ - register int week; - register int month; - register char *outp; - int i; - char tmpbuf[21], *p; - - for (week = 0; week < WEEKS_PER_MONTH; week++) { - outp = outline; - for (month = 0; month < nmonths; month++) { - /* The -1 in the following removes the unwanted - * leading blank from the entry for Sunday. */ - p = &layout[month][week][0][1]; - for (i = 0; i < 20; i++) tmpbuf[i] = *p++; - tmpbuf[20] = 0; - sprintf(outp, "%s ", tmpbuf); - outp += (DAYS_PER_WEEK * ENTRY_SIZE) + MONTH_SPACE - 1; - } - while (outp > outline && outp[-1] == ' ') outp--; - *outp = EOS; - printf("%s\n", outline); - } -} - -void calendar(year, month, indx) -int year; -int month; -int indx; /* Which of the three months */ -/* Actually build the calendar for this month. */ -{ - register char *tp; - int week; - register int wday; - register int today; - - setmonth(year, month); - for (week = 0; week < WEEKS_PER_MONTH; week++) { - for (wday = 0; wday < DAYS_PER_WEEK; wday++) { - tp = &layout[indx][week][wday][0]; - *tp++ = ' '; - today = getdate(week, wday); - if (today <= 0) { - *tp++ = ' '; - *tp++ = ' '; - } else if (today < 10) { - *tp++ = ' '; - *tp = (today + '0'); - } else { - *tp++ = (today / 10) + '0'; - *tp = (today % 10) + '0'; - } - } - } -} - -void usage(s) -char *s; -{ -/* Fatal parameter error. */ - - fprintf(stderr, "%s", s); - exit(IO_ERROR); -} - -/* Calendar routines, intended for eventual porting to TeX - * - * date(year, month, week, wday) - * Returns the date on this week (0 is first, 5 last possible) - * and day of the week (Sunday == 0) - * Note: January is month 1. - * - * setmonth(year, month) - * Parameters are as above, sets getdate() for this month. - * - * int - * getdate(week, wday) - * Parameters are as above, uses the data set by setmonth() - */ - -/* This structure is used to pass data between setmonth() and getdate(). - * It needs considerable expansion if the Julian->Gregorian change is - * to be extended to other countries. - */ - -static struct { - int this_month; /* month number used in 1752 checking */ - int feb; /* Days in February for this month */ - int sept; /* Days in September for this month */ - int days_in_month; /* Number of days in this month */ - int dow_first; /* Day of week of the 1st day in month */ -} info; - -static int day_month[] = { /* 30 days hath September... */ - 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -int date(year, month, week, wday) -int year; /* Calendar date being computed */ -int month; /* January == 1 */ -int week; /* Week in the month 0..5 inclusive */ -int wday; /* Weekday, Sunday == 0 */ -/* Return the date of the month that fell on this week and weekday. - * Return zero if it's out of range. - */ -{ - setmonth(year, month); - return(getdate(week, wday)); -} - -void setmonth(year, month) -int year; /* Year to compute */ -int month; /* Month, January is month 1 */ -/* Setup the parameters needed to compute this month - * (stored in the info structure). - */ -{ - register int i; - - if (month < 1 || month > 12) {/* Verify caller's parameters */ - info.days_in_month = 0; /* Garbage flag */ - return; - } - info.this_month = month; /* used in 1752 checking */ - info.dow_first = Jan1(year); /* Day of January 1st for now */ - info.feb = 29; /* Assume leap year */ - info.sept = 30; /* Assume normal year */ - /* Determine whether it's an ordinary year, a leap year or the - * magical calendar switch year of 1752. */ - switch ((Jan1(year + 1) + 7 - info.dow_first) % 7) { - case 1: /* Not a leap year */ - info.feb = 28; - case 2: /* Ordinary leap year */ - break; - - default: /* The magical moment arrives */ - info.sept = 19; /* 19 days hath September */ - break; - } - info.days_in_month = - (month == 2) ? info.feb - : (month == 9) ? info.sept - : day_month[month]; - for (i = 1; i < month; i++) { - switch (i) { /* Special months? */ - case 2: /* February */ - info.dow_first += info.feb; - break; - - case 9: info.dow_first += info.sept; break; - - default: - info.dow_first += day_month[i]; - break; - } - } - info.dow_first %= 7; /* Now it's Sunday to Saturday */ -} - -int getdate(week, wday) -int week; -int wday; -{ - register int today; - - /* Get a first guess at today's date and make sure it's in range. */ - today = (week * 7) + wday - info.dow_first + 1; - if (today <= 0 || today > info.days_in_month) - return(0); - else if (info.sept == 19 && info.this_month == 9 - && today >= 3) /* The magical month? */ - return(today + 11); /* If so, some dates changed */ - else /* Otherwise, */ - return(today); /* Return the date */ -} - -static int Jan1(year) -int year; -/* Return day of the week for Jan 1 of the specified year. */ -{ - register int day; - - day = year + 4 + ((year + 3) / 4); /* Julian Calendar */ - if (year > 1800) { /* If it's recent, do */ - day -= ((year - 1701) / 100); /* Clavian correction */ - day += ((year - 1601) / 400); /* Gregorian correction */ - } - if (year > 1752) /* Adjust for Gregorian */ - day += 3; /* calendar */ - return(day % 7); -} diff --git a/man/man1/Makefile b/man/man1/Makefile index ccc3e5d2e..4129edc45 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -1,5 +1,5 @@ MAN= ash.1 at.1 banner.1 basename.1 \ - bsfilt.1 cal.1 cawf.1 chgrp.1 \ + bsfilt.1 cawf.1 chgrp.1 \ chmod.1 cmp.1 comm.1 compress.1 \ cp.1 crc.1 crontab.1 dd.1 \ df.1 dhrystone.1 dosdir.1 dosread.1 doswrite.1 \ diff --git a/man/man1/cal.1 b/man/man1/cal.1 deleted file mode 100644 index be5c1614c..000000000 --- a/man/man1/cal.1 +++ /dev/null @@ -1,29 +0,0 @@ -.TH CAL 1 -.SH NAME -cal \- print a calendar -.SH SYNOPSIS -\fBcal\fR [\fImonth\fR] \fIyear\fR -.br -.de FL -.TP -\\fB\\$1\\fR -\\$2 -.. -.de EX -.TP 20 -\\fB\\$1\\fR -# \\$2 -.. -.SH EXAMPLES -.TP 20 -.B cal 3 1992 -# Print March 1992 -.SH DESCRIPTION -.PP -\fICal\fR prints a calendar for a month or year. The year can be -between 1 and 9999. -Note that the year 91 is not a synonym for 1991, but is itself a -valid year about 19 centuries ago. The calendar produced is the one used -by England and her colonies. Try Sept. 1752, Feb 1900, and Feb 2000. If -you do not understand what is going on, look up \fICalendar, Gregorian\fR in a -good encyclopedia. diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index fad10506b..cacb6b51d 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -123,6 +123,7 @@ 2009/05/08 12:48:43,usr.bin/apropos 2012/10/17 12:00:00,usr.bin/bzip2 2012/10/17 12:00:00,usr.bin/bzip2recover +2013/03/15 12:00:00,usr.bin/cal 2009/04/11 12:10:02,usr.bin/chpass 2012/10/17 12:00:00,usr.bin/cksum 2012/10/17 12:00:00,usr.bin/col diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 20427ea8b..09ee24dd9 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -6,7 +6,7 @@ SUBDIR= \ \ bzip2 bzip2recover \ - chpass cksum \ + cal chpass cksum \ col ctags \ du \ \ diff --git a/usr.bin/cal/Makefile b/usr.bin/cal/Makefile new file mode 100644 index 000000000..cccaf2ad7 --- /dev/null +++ b/usr.bin/cal/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.5 2010/02/03 15:34:45 roy Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= cal +LDADD+= -lterminfo +DPADD+= ${LIBTERMINFO} + +.include diff --git a/usr.bin/cal/README b/usr.bin/cal/README new file mode 100644 index 000000000..638ac9dff --- /dev/null +++ b/usr.bin/cal/README @@ -0,0 +1,42 @@ +The cal(1) date routines were written from scratch, basically from first +principles. The algorithm for calculating the day of week from any +Gregorian date was "reverse engineered". This was necessary as most of +the documented algorithms have to do with date calculations for other +calendars (e.g. julian) and are only accurate when converted to gregorian +within a narrow range of dates. + +1 Jan 1 is a Saturday because that's what cal says and I couldn't change +that even if I was dumb enough to try. From this we can easily calculate +the day of week for any date. The algorithm for a zero based day of week: + + calculate the number of days in all prior years (year-1)*365 + add the number of leap years (days?) since year 1 + (not including this year as that is covered later) + add the day number within the year + this compensates for the non-inclusive leap year + calculation + if the day in question occurs before the gregorian reformation + (3 sep 1752 for our purposes), then simply return + (value so far - 1 + SATURDAY's value of 6) modulo 7. + if the day in question occurs during the reformation (3 sep 1752 + to 13 sep 1752 inclusive) return THURSDAY. This is my + idea of what happened then. It does not matter much as + this program never tries to find day of week for any day + that is not the first of a month. + otherwise, after the reformation, use the same formula as the + days before with the additional step of subtracting the + number of days (11) that were adjusted out of the calendar + just before taking the modulo. + +It must be noted that the number of leap years calculation is sensitive +to the date for which the leap year is being calculated. A year that occurs +before the reformation is determined to be a leap year if its modulo of +4 equals zero. But after the reformation, a year is only a leap year if +its modulo of 4 equals zero and its modulo of 100 does not. Of course, +there is an exception for these century years. If the modulo of 400 equals +zero, then the year is a leap year anyway. This is, in fact, what the +gregorian reformation was all about (a bit of error in the old algorithm +that caused the calendar to be inaccurate.) + +Once we have the day in year for the first of the month in question, the +rest is trivial. diff --git a/usr.bin/cal/cal.1 b/usr.bin/cal/cal.1 new file mode 100644 index 000000000..25081ea89 --- /dev/null +++ b/usr.bin/cal/cal.1 @@ -0,0 +1,160 @@ +.\" $NetBSD: cal.1,v 1.20 2007/12/24 13:56:00 wiz Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kim Letkeman. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cal.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd December 21, 2007 +.Dt CAL 1 +.Os +.Sh NAME +.Nm cal +.Nd displays a calendar +.Sh SYNOPSIS +.Nm +.Op Fl 3hjry +.Op Fl A Ar after +.Op Fl B Ar before +.Op Fl d Ar day-of-week +.Op Fl R Ar reform-spec +.Op Oo Ar month Oc Ar year +.Sh DESCRIPTION +.Nm +displays a simple calendar. +If arguments are not specified, +the current month is displayed. +The options are as follows: +.Bl -tag -width Ds +.It Fl 3 +Same as +.Dq Fl A Ar 1 Fl B Ar 1 . +.It Fl A Ar after +Display +.Ar after +months after the specified month. +.It Fl B Ar before +Display +.Ar before +months before the specified month. +.It Fl d Ar day-of-week +Specifies the day of the week on which the calendar should start. +Valid values are 0 through 6, presenting Sunday through Saturday, +inclusively. +The default output starts on Sundays. +.It Fl h +Highlight the current day, if present in the displayed calendar. +If output is to a terminal, then the appropriate terminal sequences +are used, otherwise overstriking is used. +If more than one +.Fl h +is used and output is to a terminal, the current date will be +highlighted in inverse video instead of bold. +.It Fl j +Display Julian dates (days one-based, numbered from January 1). +.It Fl R Ar reform-spec +Selects an alternate Gregorian reform point from the default of +September 3rd, 1752. +The +.Ar reform-spec +can be selected by one of the built-in names (see +.Sx NOTES +for a list) or by a date of the form YYYY/MM/DD. +The date and month may be omitted, provided that what is specified +uniquely selects a given built-in reform point. +If an exact date is specified, then that date is taken to be the first +missing date of the Gregorian Reform to be applied. +.It Fl r +Display the month in which the Gregorian Reform adjustment was +applied, if no other +.Ar month +or +.Ar year +information is given. +If used in conjunction with +.Fl y , +then the entire year is displayed. +.It Fl y +Display a calendar for the current year. +.El +.Pp +If no parameters are specified, the current month's calendar is +displayed. +A single parameter specifies the year and optionally the month +in ISO format: +.Dq Li cal 2007-12 +Two parameters denote the month (1 - 12) and year. +Note that the century must be included in the year. +.Pp +A year starts on Jan 1. +.Sh NOTES +In the USA and Great Britain the Gregorian Reformation occurred in 1752. +By this time, most countries had recognized the reformation (although a +few did not recognize it until the 1900's.) +Eleven days following September 2, 1752 were eliminated by the reformation, +so the calendar for that month is a bit unusual. +.Pp +In view of the chaotic way the Gregorian calendar was adopted throughout +the world in the years between 1582 and 1928 make sure to take into account +the date of the Gregorian Reformation in your region if you are checking a +calendar for a very old date. +.Pp +.Nm +has a decent built-in list of Gregorian Reform dates and the names of +the countries where the reform was adopted: +.Pp +.Bd -literal + Italy Oct. 5, 1582 Denmark Feb. 19, 1700 + Spain Oct. 5, 1582 Great Britain Sep. 3, 1752 + Portugal Oct. 5, 1582 Sweden Feb. 18, 1753 + Poland Oct. 5, 1582 Finland Feb. 18, 1753 + France Dec. 12, 1582 Japan Dec. 20, 1872 + Luxembourg Dec. 22, 1582 China Nov. 7, 1911 + Netherlands Dec. 22, 1582 Bulgaria Apr. 1, 1916 + Bavaria Oct. 6, 1583 U.S.S.R. Feb. 1, 1918 + Austria Jan. 7, 1584 Serbia Jan. 19, 1919 + Switzerland Jan. 12, 1584 Romania Jan. 19, 1919 + Hungary Oct. 22, 1587 Greece Mar. 10, 1924 + Germany Feb. 19, 1700 Turkey Dec. 19, 1925 + Norway Feb. 19, 1700 Egypt Sep. 18, 1928 +.Ed +.Pp +The country known as +.Em Great Britain +can also be referred to as +.Em England +since that has less letters and no spaces in it. +This is meant only as a measure of expediency, not as a possible +slight to anyone involved. +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/usr.bin/cal/cal.c b/usr.bin/cal/cal.c new file mode 100644 index 000000000..50dadee8e --- /dev/null +++ b/usr.bin/cal/cal.c @@ -0,0 +1,919 @@ +/* $NetBSD: cal.c,v 1.27 2011/08/29 13:55:22 joerg Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kim Letkeman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: cal.c,v 1.27 2011/08/29 13:55:22 joerg Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SATURDAY 6 /* 1 Jan 1 was a Saturday */ + +#define FIRST_MISSING_DAY reform->first_missing_day +#define NUMBER_MISSING_DAYS reform->missing_days + +#define MAXDAYS 42 /* max slots in a month array */ +#define SPACE -1 /* used in day array */ + +static int days_in_month[2][13] = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, +}; + +static int empty[MAXDAYS] = { + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, +}; +static int shift_days[2][4][MAXDAYS + 1]; + +static const char *month_names[12] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", +}; + +static const char *day_headings = " S M Tu W Th F S"; +static const char *j_day_headings = " S M Tu W Th F S"; + +/* leap years according to the julian calendar */ +#define j_leap_year(y, m, d) \ + (((m) > 2) && \ + !((y) % 4)) + +/* leap years according to the gregorian calendar */ +#define g_leap_year(y, m, d) \ + (((m) > 2) && \ + ((!((y) % 4) && ((y) % 100)) || \ + !((y) % 400))) + +/* leap year -- account for gregorian reformation at some point */ +#define leap_year(yr) \ + ((yr) <= reform->year ? j_leap_year((yr), 3, 1) : \ + g_leap_year((yr), 3, 1)) + +/* number of julian leap days that have passed by a given date */ +#define j_leap_days(y, m, d) \ + ((((y) - 1) / 4) + j_leap_year(y, m, d)) + +/* number of gregorian leap days that have passed by a given date */ +#define g_leap_days(y, m, d) \ + ((((y) - 1) / 4) - (((y) - 1) / 100) + (((y) - 1) / 400) + \ + g_leap_year(y, m, d)) + +/* + * Subtracting the gregorian leap day count (for a given date) from + * the julian leap day count (for the same date) describes the number + * of days from the date before the shift to the next date that + * appears in the calendar. Since we want to know the number of + * *missing* days, not the number of days that the shift spans, we + * subtract 2. + * + * Alternately... + * + * There's a reason they call the Dark ages the Dark Ages. Part of it + * is that we don't have that many records of that period of time. + * One of the reasons for this is that a lot of the Dark Ages never + * actually took place. At some point in the first millenium A.D., a + * ruler of some power decided that he wanted the number of the year + * to be different than what it was, so he changed it to coincide + * nicely with some event (a birthday or anniversary, perhaps a + * wedding, or maybe a centennial for a largish city). One of the + * side effects of this upon the Gregorian reform is that two Julian + * leap years (leap days celebrated during centennial years that are + * not quatro-centennial years) were skipped. + */ +#define GREGORIAN_MAGIC 2 + +/* number of centuries since the reform, not inclusive */ +#define centuries_since_reform(yr) \ + ((yr) > reform->year ? ((yr) / 100) - (reform->year / 100) : 0) + +/* number of centuries since the reform whose modulo of 400 is 0 */ +#define quad_centuries_since_reform(yr) \ + ((yr) > reform->year ? ((yr) / 400) - (reform->year / 400) : 0) + +/* number of leap years between year 1 and this year, not inclusive */ +#define leap_years_since_year_1(yr) \ + ((yr) / 4 - centuries_since_reform(yr) + quad_centuries_since_reform(yr)) + +static struct reform { + const char *country; + int ambiguity, year, month, date; + long first_missing_day; + int missing_days; + /* + * That's 2 for standard/julian display, 4 for months possibly + * affected by the Gregorian shift, and MAXDAYS + 1 for the + * days that get displayed, plus a crib slot. + */ +} *reform, reforms[] = { + { "DEFAULT", 0, 1752, 9, 3, 0, 0 }, + { "Italy", 1, 1582, 10, 5, 0, 0 }, + { "Spain", 1, 1582, 10, 5, 0, 0 }, + { "Portugal", 1, 1582, 10, 5, 0, 0 }, + { "Poland", 1, 1582, 10, 5, 0, 0 }, + { "France", 2, 1582, 12, 10, 0, 0 }, + { "Luxembourg", 2, 1582, 12, 22, 0, 0 }, + { "Netherlands", 2, 1582, 12, 22, 0, 0 }, + { "Bavaria", 0, 1583, 10, 6, 0, 0 }, + { "Austria", 2, 1584, 1, 7, 0, 0 }, + { "Switzerland", 2, 1584, 1, 12, 0, 0 }, + { "Hungary", 0, 1587, 10, 22, 0, 0 }, + { "Germany", 0, 1700, 2, 19, 0, 0 }, + { "Norway", 0, 1700, 2, 19, 0, 0 }, + { "Denmark", 0, 1700, 2, 19, 0, 0 }, + { "Great Britain", 0, 1752, 9, 3, 0, 0 }, + { "England", 0, 1752, 9, 3, 0, 0 }, + { "America", 0, 1752, 9, 3, 0, 0 }, + { "Sweden", 0, 1753, 2, 18, 0, 0 }, + { "Finland", 0, 1753, 2, 18, 0, 0 }, + { "Japan", 0, 1872, 12, 20, 0, 0 }, + { "China", 0, 1911, 11, 7, 0, 0 }, + { "Bulgaria", 0, 1916, 4, 1, 0, 0 }, + { "U.S.S.R.", 0, 1918, 2, 1, 0, 0 }, + { "Serbia", 0, 1919, 1, 19, 0, 0 }, + { "Romania", 0, 1919, 1, 19, 0, 0 }, + { "Greece", 0, 1924, 3, 10, 0, 0 }, + { "Turkey", 0, 1925, 12, 19, 0, 0 }, + { "Egypt", 0, 1928, 9, 18, 0, 0 }, + { NULL, 0, 0, 0, 0, 0, 0 }, +}; + +static int julian; +static int dow; +static int hilite; +static const char *md, *me; + +static void init_hilite(void); +static int getnum(const char *); +static void gregorian_reform(const char *); +static void reform_day_array(int, int, int *, int *, int *,int *,int *,int *); +static int ascii_day(char *, int); +static void center(const char *, int, int); +static void day_array(int, int, int *); +static int day_in_week(int, int, int); +static int day_in_year(int, int, int); +static void monthrange(int, int, int, int, int); +static void trim_trailing_spaces(char *); +__dead static void usage(void); + +int +main(int argc, char **argv) +{ + struct tm *local_time; + time_t now; + int ch, yflag; + long month, year; + int before, after, use_reform; + int yearly = 0; + char *when, *eoi; + + before = after = 0; + use_reform = yflag = year = 0; + when = NULL; + while ((ch = getopt(argc, argv, "A:B:d:hjR:ry3")) != -1) { + switch (ch) { + case 'A': + after = getnum(optarg); + if (after < 0) + errx(1, "Argument to -A must be positive"); + break; + case 'B': + before = getnum(optarg); + if (before < 0) + errx(1, "Argument to -B must be positive"); + break; + case 'd': + dow = getnum(optarg); + if (dow < 0 || dow > 6) + errx(1, "illegal day of week value: use 0-6"); + break; + case 'h': + init_hilite(); + break; + case 'j': + julian = 1; + break; + case 'R': + when = optarg; + break; + case 'r': + use_reform = 1; + break; + case 'y': + yflag = 1; + break; + case '3': + before = after = 1; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (when != NULL) + gregorian_reform(when); + if (reform == NULL) + gregorian_reform("DEFAULT"); + + month = 0; + switch (argc) { + case 2: + month = strtol(*argv++, &eoi, 10); + if (month < 1 || month > 12 || *eoi != '\0') + errx(1, "illegal month value: use 1-12"); + year = strtol(*argv, &eoi, 10); + if (year < 1 || year > 9999 || *eoi != '\0') + errx(1, "illegal year value: use 1-9999"); + break; + case 1: + year = strtol(*argv, &eoi, 10); + if (year < 1 || year > 9999 || (*eoi != '\0' && *eoi != '/' && *eoi != '-')) + errx(1, "illegal year value: use 1-9999"); + if (*eoi != '\0') { + month = strtol(eoi + 1, &eoi, 10); + if (month < 1 || month > 12 || *eoi != '\0') + errx(1, "illegal month value: use 1-12"); + } + break; + case 0: + (void)time(&now); + local_time = localtime(&now); + if (use_reform) + year = reform->year; + else + year = local_time->tm_year + TM_YEAR_BASE; + if (!yflag) { + if (use_reform) + month = reform->month; + else + month = local_time->tm_mon + 1; + } + break; + default: + usage(); + } + + if (!month) { + /* yearly */ + month = 1; + before = 0; + after = 11; + yearly = 1; + } + + monthrange(month, year, before, after, yearly); + + exit(0); +} + +#define DAY_LEN 3 /* 3 spaces per day */ +#define J_DAY_LEN 4 /* 4 spaces per day */ +#define WEEK_LEN 20 /* 7 * 3 - one space at the end */ +#define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */ +#define HEAD_SEP 2 /* spaces between day headings */ +#define J_HEAD_SEP 2 +#define MONTH_PER_ROW 3 /* how many monthes in a row */ +#define J_MONTH_PER_ROW 2 + +static void +monthrange(int month, int year, int before, int after, int yearly) +{ + int startmonth, startyear; + int endmonth, endyear; + int i, row; + int days[3][MAXDAYS]; + char lineout[256]; + int inayear; + int newyear; + int day_len, week_len, head_sep; + int month_per_row; + int skip, r_off, w_off; + + if (julian) { + day_len = J_DAY_LEN; + week_len = J_WEEK_LEN; + head_sep = J_HEAD_SEP; + month_per_row = J_MONTH_PER_ROW; + } + else { + day_len = DAY_LEN; + week_len = WEEK_LEN; + head_sep = HEAD_SEP; + month_per_row = MONTH_PER_ROW; + } + + month--; + + startyear = year - (before + 12 - 1 - month) / 12; + startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12); + endyear = year + (month + after) / 12; + endmonth = (month + after) % 12; + + if (startyear < 0 || endyear > 9999) { + errx(1, "year should be in 1-9999\n"); + } + + year = startyear; + month = startmonth; + inayear = newyear = (year != endyear || yearly); + if (inayear) { + skip = month % month_per_row; + month -= skip; + } + else { + skip = 0; + } + + do { + if (newyear) { + (void)snprintf(lineout, sizeof(lineout), "%d", year); + center(lineout, week_len * month_per_row + + head_sep * (month_per_row - 1), 0); + (void)printf("\n\n"); + newyear = 0; + } + + for (i = 0; i < skip; i++) + center("", week_len, head_sep); + + for (; i < month_per_row; i++) { + int sep; + + if (year == endyear && month + i > endmonth) + break; + + sep = (i == month_per_row - 1) ? 0 : head_sep; + day_array(month + i + 1, year, days[i]); + if (inayear) { + center(month_names[month + i], week_len, sep); + } + else { + snprintf(lineout, sizeof(lineout), "%s %d", + month_names[month + i], year); + center(lineout, week_len, sep); + } + } + printf("\n"); + + for (i = 0; i < skip; i++) + center("", week_len, head_sep); + + for (; i < month_per_row; i++) { + int sep; + + if (year == endyear && month + i > endmonth) + break; + + sep = (i == month_per_row - 1) ? 0 : head_sep; + if (dow) { + printf("%s ", (julian) ? + j_day_headings + 4 * dow : + day_headings + 3 * dow); + printf("%.*s", dow * (julian ? 4 : 3) - 1, + (julian) ? j_day_headings : day_headings); + } else + printf("%s", (julian) ? j_day_headings : day_headings); + printf("%*s", sep, ""); + } + printf("\n"); + + for (row = 0; row < 6; row++) { + char *p = NULL; + + memset(lineout, ' ', sizeof(lineout)); + for (i = 0; i < skip; i++) { + /* nothing */ + } + w_off = 0; + for (; i < month_per_row; i++) { + int col, *dp; + + if (year == endyear && month + i > endmonth) + break; + + p = lineout + i * (week_len + 2) + w_off; + dp = &days[i][row * 7]; + for (col = 0; col < 7; + col++, p += day_len + r_off) { + r_off = ascii_day(p, *dp++); + w_off += r_off; + } + } + *p = '\0'; + trim_trailing_spaces(lineout); + (void)printf("%s\n", lineout); + } + + skip = 0; + month += month_per_row; + if (month >= 12) { + month -= 12; + year++; + newyear = 1; + } + } while (year < endyear || (year == endyear && month <= endmonth)); +} + +/* + * day_array -- + * Fill in an array of 42 integers with a calendar. Assume for a moment + * that you took the (maximum) 6 rows in a calendar and stretched them + * out end to end. You would have 42 numbers or spaces. This routine + * builds that array for any month from Jan. 1 through Dec. 9999. + */ +static void +day_array(int month, int year, int *days) +{ + int day, dw, dm; + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + tm->tm_year += TM_YEAR_BASE; + tm->tm_mon++; + tm->tm_yday++; /* jan 1 is 1 for us, not 0 */ + + for (dm = month + year * 12, dw = 0; dw < 4; dw++) { + if (dm == shift_days[julian][dw][MAXDAYS]) { + memmove(days, shift_days[julian][dw], + MAXDAYS * sizeof(int)); + return; + } + } + + memmove(days, empty, MAXDAYS * sizeof(int)); + dm = days_in_month[leap_year(year)][month]; + dw = day_in_week(1, month, year); + day = julian ? day_in_year(1, month, year) : 1; + while (dm--) { + if (hilite && year == tm->tm_year && + (julian ? (day == tm->tm_yday) : + (month == tm->tm_mon && day == tm->tm_mday))) + days[dw++] = SPACE - day++; + else + days[dw++] = day++; + } +} + +/* + * day_in_year -- + * return the 1 based day number within the year + */ +static int +day_in_year(int day, int month, int year) +{ + int i, leap; + + leap = leap_year(year); + for (i = 1; i < month; i++) + day += days_in_month[leap][i]; + return (day); +} + +/* + * day_in_week + * return the 0 based day number for any date from 1 Jan. 1 to + * 31 Dec. 9999. Returns the day of the week of the first + * missing day for any given Gregorian shift. + */ +static int +day_in_week(int day, int month, int year) +{ + long temp; + + temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + + day_in_year(day, month, year); + if (temp < FIRST_MISSING_DAY) + return ((temp - dow + 6 + SATURDAY) % 7); + if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) + return (((temp - dow + 6 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); + return ((FIRST_MISSING_DAY - dow + 6 + SATURDAY) % 7); +} + +static int +ascii_day(char *p, int day) +{ + int display, val, rc; + char *b; + static const char *aday[] = { + "", + " 1", " 2", " 3", " 4", " 5", " 6", " 7", + " 8", " 9", "10", "11", "12", "13", "14", + "15", "16", "17", "18", "19", "20", "21", + "22", "23", "24", "25", "26", "27", "28", + "29", "30", "31", + }; + + if (day == SPACE) { + memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN); + return (0); + } + if (day < SPACE) { + b = p; + day = SPACE - day; + } else + b = NULL; + if (julian) { + if ((val = day / 100) != 0) { + day %= 100; + *p++ = val + '0'; + display = 1; + } else { + *p++ = ' '; + display = 0; + } + val = day / 10; + if (val || display) + *p++ = val + '0'; + else + *p++ = ' '; + *p++ = day % 10 + '0'; + } else { + *p++ = aday[day][0]; + *p++ = aday[day][1]; + } + + rc = 0; + if (b != NULL) { + const char *t; + char h[64]; + int l; + + l = p - b; + memcpy(h, b, l); + p = b; + + if (md != NULL) { + for (t = md; *t; rc++) + *p++ = *t++; + memcpy(p, h, l); + p += l; + for (t = me; *t; rc++) + *p++ = *t++; + } else { + for (t = &h[0]; l--; t++) { + *p++ = *t; + rc++; + *p++ = '\b'; + rc++; + *p++ = *t; + } + } + } + + *p = ' '; + return (rc); +} + +static void +trim_trailing_spaces(char *s) +{ + char *p; + + for (p = s; *p; ++p) + continue; + while (p > s && isspace((unsigned char)*--p)) + continue; + if (p > s) + ++p; + *p = '\0'; +} + +static void +center(const char *str, int len, int separate) +{ + + len -= strlen(str); + (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, ""); + if (separate) + (void)printf("%*s", separate, ""); +} + +/* + * gregorian_reform -- + * Given a description of date on which the Gregorian Reform was + * applied. The argument can be any of the "country" names + * listed in the reforms array (case insensitive) or a date of + * the form YYYY/MM/DD. The date and month can be omitted if + * doing so would not select more than one different built-in + * reform point. + */ +static void +gregorian_reform(const char *p) +{ + int year, month, date; + int i, days, diw, diy; + char c; + + i = sscanf(p, "%d%*[/,-]%d%*[/,-]%d%c", &year, &month, &date, &c); + switch (i) { + case 4: + /* + * If the character was sscanf()ed, then there's more + * stuff than we need. + */ + errx(1, "date specifier %s invalid", p); + case 0: + /* + * Not a form we can sscanf(), so void these, and we + * can try matching "country" names later. + */ + year = month = date = -1; + break; + case 1: + month = 0; + /*FALLTHROUGH*/ + case 2: + date = 0; + /*FALLTHROUGH*/ + case 3: + /* + * At last, some sanity checking on the values we were + * given. + */ + if (year < 1 || year > 9999) + errx(1, "%d: illegal year value: use 1-9999", year); + if (i > 1 && (month < 1 || month > 12)) + errx(1, "%d: illegal month value: use 1-12", month); + if ((i == 3 && date < 1) || date < 0 || + date > days_in_month[1][month]) + /* + * What about someone specifying a leap day in + * a non-leap year? Well...that's a tricky + * one. We can't yet *say* whether the year + * in question is a leap year. What if the + * date given was, for example, 1700/2/29? is + * that a valid leap day? + * + * So...we punt, and hope that saying 29 in + * the case of February isn't too bad an idea. + */ + errx(1, "%d: illegal date value: use 1-%d", date, + days_in_month[1][month]); + break; + } + + /* + * A complete date was specified, so use the other pope. + */ + if (date > 0) { + static struct reform Goestheveezl; + + reform = &Goestheveezl; + reform->country = "Bompzidaize"; + reform->year = year; + reform->month = month; + reform->date = date; + } + + /* + * No date information was specified, so let's try to match on + * country name. + */ + else if (year == -1) { + for (reform = &reforms[0]; reform->year; reform++) { + if (strcasecmp(p, reform->country) == 0) + break; + } + } + + /* + * We have *some* date information, but not a complete date. + * Let's see if we have enough to pick a single entry from the + * list that's not ambiguous. + */ + else { + for (reform = &reforms[0]; reform->year; reform++) { + if ((year == 0 || year == reform->year) && + (month == 0 || month == reform->month) && + (date == 0 || month == reform->date)) + break; + } + + if (i <= reform->ambiguity) + errx(1, "%s: ambiguous short reform date specification", p); + } + + /* + * Oops...we reached the end of the list. + */ + if (reform->year == 0) + errx(1, "reform name %s invalid", p); + + /* + * + */ + reform->missing_days = + j_leap_days(reform->year, reform->month, reform->date) - + g_leap_days(reform->year, reform->month, reform->date) - + GREGORIAN_MAGIC; + + reform->first_missing_day = + (reform->year - 1) * 365 + + day_in_year(reform->date, reform->month, reform->year) + + date + + j_leap_days(reform->year, reform->month, reform->date); + + /* + * Once we know the day of the week of the first missing day, + * skip back to the first of the month's day of the week. + */ + diw = day_in_week(reform->date, reform->month, reform->year); + diw = (diw + 8 - (reform->date % 7)) % 7; + diy = day_in_year(1, reform->month, reform->year); + + /* + * We might need all four of these (if you switch from Julian + * to Gregorian at some point after 9900, you get a gap of 73 + * days, and that can affect four months), and it doesn't hurt + * all that much to precompute them, so there. + */ + date = 1; + days = 0; + for (i = 0; i < 4; i++) + reform_day_array(reform->month + i, reform->year, + &days, &date, &diw, &diy, + shift_days[0][i], + shift_days[1][i]); +} + +/* + * reform_day_array -- + * Pre-calculates the given month's calendar (in both "standard" + * and "julian day" representations) with respect for days + * skipped during a reform period. + */ +static void +reform_day_array(int month, int year, int *done, int *date, int *diw, int *diy, + int *scal, int *jcal) +{ + int mdays; + + /* + * If the reform was in the month of october or later, then + * the month number from the caller could "overflow". + */ + if (month > 12) { + month -= 12; + year++; + } + + /* + * Erase months, and set crib number. The crib number is used + * later to determine if the month to be displayed is here or + * should be built on the fly with the generic routine + */ + memmove(scal, empty, MAXDAYS * sizeof(int)); + scal[MAXDAYS] = month + year * 12; + memmove(jcal, empty, MAXDAYS * sizeof(int)); + jcal[MAXDAYS] = month + year * 12; + + /* + * It doesn't matter what the actual month is when figuring + * out if this is a leap year or not, just so long as February + * gets the right number of days in it. + */ + mdays = days_in_month[g_leap_year(year, 3, 1)][month]; + + /* + * Bounce back to the first "row" in the day array, and fill + * in any days that actually occur. + */ + for (*diw %= 7; (*date - *done) <= mdays; (*date)++, (*diy)++) { + /* + * "date" doesn't get reset by the caller across calls + * to this routine, so we can actually tell that we're + * looking at April the 41st. Much easier than trying + * to calculate the absolute julian day for a given + * date and then checking that. + */ + if (*date < reform->date || + *date >= reform->date + reform->missing_days) { + scal[*diw] = *date - *done; + jcal[*diw] = *diy; + (*diw)++; + } + } + *done += mdays; +} + +static int +getnum(const char *p) +{ + unsigned long result; + char *ep; + + errno = 0; + result = strtoul(p, &ep, 10); + if (p[0] == '\0' || *ep != '\0') + goto error; + if (errno == ERANGE && result == ULONG_MAX) + goto error; + if (result > INT_MAX) + goto error; + + return (int)result; + +error: + errx(1, "bad number: %s", p); + /*NOTREACHED*/ +} + +static void +init_hilite(void) +{ + const char *term; + int errret; + + hilite++; + + if (!isatty(fileno(stdout))) + return; + + term = getenv("TERM"); + if (term == NULL) + term = "dumb"; + if (setupterm(term, fileno(stdout), &errret) != 0 && errret != 1) + return; + + if (hilite > 1) + md = enter_reverse_mode; + else + md = enter_bold_mode; + me = exit_attribute_mode; + if (me == NULL || md == NULL) + md = me = NULL; +} + +static void +usage(void) +{ + + (void)fprintf(stderr, + "usage: cal [-3hjry] [-A after] [-B before] [-d day-of-week] " + "[-R reform-spec]\n [[month] year]\n"); + exit(1); +}