Index: head/usr.bin/calendar/calendar.1 =================================================================== --- head/usr.bin/calendar/calendar.1 (revision 367363) +++ head/usr.bin/calendar/calendar.1 (revision 367364) @@ -1,373 +1,374 @@ .\" Copyright (c) 1989, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" 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. .\" .\" @(#)calendar.1 8.1 (Berkeley) 6/29/93 .\" $FreeBSD$ .\" -.Dd November 4, 2020 +.Dd November 5, 2020 .Dt CALENDAR 1 .Os .Sh NAME .Nm calendar .Nd reminder service .Sh SYNOPSIS .Nm .Op Fl A Ar num .Op Fl a .Op Fl B Ar num .Op Fl D Ar moon|sun .Op Fl d .Op Fl F Ar friday .Op Fl f Ar calendarfile .Op Fl l Ar longitude .Oo .Bk -words .Fl t Ar dd Ns .Sm off .Op . Ar mm Op . Ar year .Sm on .Ek .Oc .Op Fl U Ar UTC-offset .Op Fl W Ar num .Sh DESCRIPTION The .Nm utility checks the current directory for a file named .Pa calendar and displays lines that fall into the specified date range. On the day before a weekend (normally Friday), events for the next three days are displayed. .Pp The following options are available: .Bl -tag -width Ds .It Fl A Ar num Print lines from today and the next .Ar num days (forward, future). .It Fl a Process the ``calendar'' files for users found in .Pa /etc/passwd and mail the results to them. This can result in multiple messages for specific files, since .Pa /etc/passwd does not require home directories to be unique. In particular, by default .Ar root , .Ar toor and .Ar daemon share the same home directory. If this directory contains calendar information, .Nm will process the file three times. .Pp This option requires super-user privileges. .It Fl B Ar num Print lines from today and the previous .Ar num days (backward, past). .It Fl D Ar moon|sun Print UTC offset, longitude and moon or sun information. .It Fl d Debug option: print current date information. .It Fl F Ar friday Specify which day of the week is ``Friday'' (the day before the weekend begins). Default is 5. .It Fl f Pa calendarfile Use .Pa calendarfile as the default calendar file. .It Fl l Ar longitude Perform lunar and solar calculations from this longitude. If neither longitude nor UTC offset is specified, the calculations will be based on the difference between UTC time and localtime. If both are specified, UTC offset overrides longitude. .It Xo Fl t .Sm off .Ar dd .Op . Ar mm Op . Ar year .Sm on .Xc For test purposes only: set date directly to argument values. .It Fl U Ar UTC-offset Perform lunar and solar calculations from this UTC offset. If neither UTC offset nor longitude is specified, the calculations will be based on the difference between UTC time and localtime. If both are specified, UTC offset overrides longitude. .It Fl W Ar num Print lines from today and the next .Ar num days (forward, future). Ignore weekends when calculating the number of days. .El .Sh FILE FORMAT To handle calendars in your national code table you can specify .Dq LANG= in the calendar file as early as possible. .Pp To handle the local name of sequences, you can specify them as: .Dq SEQUENCE= in the calendar file as early as possible. .Pp The names of the following special days are recognized: .Bl -tag -width 123456789012345 -compact .It Easter Catholic Easter. .It Paskha Orthodox Easter. .It NewMoon The lunar New Moon. .It FullMoon The lunar Full Moon. .It MarEquinox The solar equinox in March. .It JunSolstice The solar solstice in June. .It SepEquinox The solar equinox in September. .It DecSolstice The solar solstice in December. .It ChineseNewYear The first day of the Chinese year. .El These names may be reassigned to their local names via an assignment like .Dq Easter=Pasen in the calendar file. .Pp Other lines should begin with a month and day. They may be entered in almost any format, either numeric or as character strings. If the proper locale is set, national month and weekday names can be used. A single asterisk (``*'') matches every month. A day without a month matches that day of every week. A month without a day matches the first of that month. Two numbers default to the month followed by the day. Lines with leading tabs default to the last entered date, allowing multiple line specifications for a single date. .Pp The names of the recognized special days may be followed by a positive or negative integer, like: .Dq Easter+3 or .Dq Paskha-4 . .Pp Weekdays may be followed by ``-4'' ...\& ``+5'' (aliases for last, first, second, third, fourth) for moving events like ``the last Monday in April''. .Pp By convention, dates followed by an asterisk are not fixed, i.e., change from year to year. .Pp Day descriptions start after the first character in the line; if the line does not contain a character, it is not displayed. If the first character in the line is a character, it is treated as a continuation of the previous line. .Pp The .Nm file is preprocessed by a limited subset of .Xr cpp 1 internally, allowing the inclusion of shared files such as lists of company holidays or meetings. This limited subset consists of \fB#include\fR, \fB#define\fR, -\fB#undef\fR, \fB#ifdef\fR, \fB#ifndef\fR, and \fB#else\fR. +\fB#undef\fR, \fB#ifdef\fR, \fB#ifndef\fR, \fB#else\fR, \fB#warning\fR, +and \fB#error\fR. .Pp Conditions can be nested and the consistency of opening and closing instructions is checked. Only the first word after #define is used as the name of the condition variable being defined. -More than word following #ifdef, #ifndef, or #undef is a ayntax +More than word following #ifdef, #ifndef, or #undef is considered a syntax error, since names cannot include white-space. Included files are parsed in a global scope with regard to the condition variables being defined or tested therein. All conditional blocks are implicitly closed at the end of a file, and missing #endif instructions are assumed to be present on implied succeeding lines. .Pp If the shared file is not referenced by a full pathname, .Nm searches in the current (or home) directory first, and then in the directory .Pa /usr/share/calendar . Empty lines and text protected by the C commenting syntax .Pq Li /* ... */ or .Pq Li // are ignored. .Pp Some possible calendar entries ( characters highlighted by \fB\et\fR sequence): .Bd -unfilled -offset indent LANG=C Easter=Ostern #include #include 6/15\fB\et\fRJune 15 (if ambiguous, will default to month/day). Jun. 15\fB\et\fRJune 15. 15 June\fB\et\fRJune 15. Thursday\fB\et\fREvery Thursday. June\fB\et\fREvery June 1st. 15 *\fB\et\fR15th of every month. 2010/4/15\fB\et\fR15 April 2010 May Sun+2\fB\et\fRsecond Sunday in May (Muttertag) 04/SunLast\fB\et\fRlast Sunday in April, \fB\et\fRsummer time in Europe Easter\fB\et\fREaster Ostern-2\fB\et\fRGood Friday (2 days before Easter) Paskha\fB\et\fROrthodox Easter .Ed .Sh FILES .Bl -tag -width calendar.christian -compact .It Pa calendar file in current directory. .It Pa ~/.calendar .Pa calendar HOME directory. A chdir is done into this directory if it exists. .It Pa ~/.calendar/calendar calendar file to use if no calendar file exists in the current directory. .It Pa ~/.calendar/nomail do not send mail if this file exists. .It Pa /usr/share/calendar system wide location of calendar files provided as part of the base system. .It Pa /usr/local/share/calendar system wide location for calendar files provided by a port or package. .El .Pp The order of precedence in searches for a calendar file is: current directory, ~/.calendar, /usr/local/share/calendar, /usr/share/calendar. Files of similar names are ignored in lower precedence locations. .Pp The following default calendar files are provided by the deskutils/calendar-data port. .Pp .Bl -tag -width calendar.southafrica -compact .It Pa calendar.all File which includes all the default files. .It Pa calendar.australia Calendar of events in Australia. .It Pa calendar.birthday Births and deaths of famous (and not-so-famous) people. .It Pa calendar.christian Christian holidays. This calendar should be updated yearly by the local system administrator so that roving holidays are set correctly for the current year. .It Pa calendar.computer Days of special significance to computer people. .It Pa calendar.croatian Calendar of events in Croatia. .It Pa calendar.dutch Calendar of events in the Netherlands. .It Pa calendar.freebsd Birthdays of .Fx committers. .It Pa calendar.french Calendar of events in France. .It Pa calendar.german Calendar of events in Germany. .It Pa calendar.history Everything else, mostly U.S.\& historical events. .It Pa calendar.holiday Other holidays, including the not-well-known, obscure, and .Em really obscure. .It Pa calendar.judaic Jewish holidays. The entries for this calendar have been obtained from the deskutils/hebcal port. .It Pa calendar.music Musical events, births, and deaths. Strongly oriented toward rock 'n' roll. .It Pa calendar.newzealand Calendar of events in New Zealand. .It Pa calendar.russian Russian calendar. .It Pa calendar.southafrica Calendar of events in South Africa. .It Pa calendar.usholiday U.S.\& holidays. This calendar should be updated yearly by the local system administrator so that roving holidays are set correctly for the current year. .It Pa calendar.world Includes all calendar files except for national files. .El .Sh COMPATIBILITY The .Nm program previously selected lines which had the correct date anywhere in the line. This is no longer true, the date is only recognized when it occurs at the beginning of a line. .Sh SEE ALSO .Xr at 1 , .Xr mail 1 , .Xr cron 8 .Sh HISTORY A .Nm command appeared in .At v7 . .Sh NOTES Chinese New Year is calculated at 120 degrees east of Greenwich, which roughly corresponds with the east coast of China. For people west of China, this might result that the start of Chinese New Year and the day of the related new moon might differ. .Pp The phases of the moon and the longitude of the sun are calculated against the local position which corresponds with 30 degrees times the time-difference towards Greenwich. .Pp The new and full moons are happening on the day indicated: They might happen in the time period in the early night or in the late evening. It does not indicate that they are starting in the night on that date. .Pp Because of minor differences between the output of the formulas used and other sources on the Internet, Druids and Werewolves should double-check the start and end time of solar and lunar events. .Sh BUGS The .Nm does only recognise the cpp directives #include, #define, #ifdef, #ifndef and #else. It supports nested conditions, but does not perform any validation on the correct use and nesting of conditions. #endif without prior #ifdef or #define is ignored and #else outside a conditional section skips input lines up to the next #endif. .Pp There is no possibility to properly specify the local position needed for solar and lunar calculations. Index: head/usr.bin/calendar/calendar.h =================================================================== --- head/usr.bin/calendar/calendar.h (revision 367363) +++ head/usr.bin/calendar/calendar.h (revision 367364) @@ -1,206 +1,205 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * 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. * * $FreeBSD$ */ #include #include #define SECSPERDAY (24 * 60 * 60) #define SECSPERHOUR (60 * 60) #define SECSPERMINUTE (60) #define MINSPERHOUR (60) #define HOURSPERDAY (24) #define FSECSPERDAY (24.0 * 60.0 * 60.0) #define FSECSPERHOUR (60.0 * 60.0) #define FSECSPERMINUTE (60.0) #define FMINSPERHOUR (60.0) #define FHOURSPERDAY (24.0) #define DAYSPERYEAR 365 #define DAYSPERLEAPYEAR 366 /* Not yet categorized */ extern struct passwd *pw; extern int doall; extern time_t t1, t2; extern const char *calendarFile; extern int yrdays; extern struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; extern struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice; extern double UTCOffset; extern int EastLongitude; #ifdef WITH_ICONV extern const char *outputEncoding; #endif #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) /* Flags to determine the returned values by determinestyle() in parsedata.c */ #define F_NONE 0x00000 #define F_MONTH 0x00001 #define F_DAYOFWEEK 0x00002 #define F_DAYOFMONTH 0x00004 #define F_MODIFIERINDEX 0x00008 #define F_MODIFIEROFFSET 0x00010 #define F_SPECIALDAY 0x00020 #define F_ALLMONTH 0x00040 #define F_ALLDAY 0x00080 #define F_VARIABLE 0x00100 #define F_EASTER 0x00200 #define F_CNY 0x00400 #define F_PASKHA 0x00800 #define F_NEWMOON 0x01000 #define F_FULLMOON 0x02000 #define F_MAREQUINOX 0x04000 #define F_SEPEQUINOX 0x08000 #define F_JUNSOLSTICE 0x10000 #define F_DECSOLSTICE 0x20000 #define F_YEAR 0x40000 #define STRING_EASTER "Easter" #define STRING_PASKHA "Paskha" #define STRING_CNY "ChineseNewYear" #define STRING_NEWMOON "NewMoon" #define STRING_FULLMOON "FullMoon" #define STRING_MAREQUINOX "MarEquinox" #define STRING_SEPEQUINOX "SepEquinox" #define STRING_JUNSOLSTICE "JunSolstice" #define STRING_DECSOLSTICE "DecSolstice" #define MAXCOUNT 125 /* Random number of maximum number of * repeats of an event. Should be 52 * (number of weeks per year), if you * want to show two years then it * should be 104. If you are seeing * more than this you are using this * program wrong. */ /* * All the astronomical calculations are carried out for the meridian 120 * degrees east of Greenwich. */ #define UTCOFFSET_CNY 8.0 extern int debug; /* show parsing of the input */ extern int year1, year2; /* events.c */ /* * Event sorting related functions: * - Use event_add() to create a new event * - Use event_continue() to add more text to the last added event * - Use event_print_all() to display them in time chronological order */ struct event *event_add(int, int, int, int, char *, char *); void event_continue(struct event *events, char *txt); void event_print_all(FILE *fp); struct event { int year; int month; int day; int var; - char *date; char *text; char *extra; struct event *next; }; /* locale.c */ struct fixs { char *name; size_t len; }; extern const char *days[]; extern const char *fdays[]; extern const char *fmonths[]; extern const char *months[]; extern const char *sequences[]; extern struct fixs fndays[8]; /* full national days names */ extern struct fixs fnmonths[13]; /* full national months names */ extern struct fixs ndays[8]; /* short national days names */ extern struct fixs nmonths[13]; /* short national month names */ extern struct fixs nsequences[10]; void setnnames(void); void setnsequences(char *); /* day.c */ extern const struct tm tm0; extern char dayname[]; void settimes(time_t,int before, int after, int friday, struct tm *tp1, struct tm *tp2); time_t Mktime(char *); /* parsedata.c */ int parsedaymonth(char *, int *, int *, int *, int *, char **); void dodebug(char *type); /* io.c */ void cal(void); void closecal(FILE *); FILE *opencalin(void); FILE *opencalout(void); /* ostern.c / paskha.c */ int paskha(int); int easter(int); int j2g(int); /* dates.c */ extern int cumdaytab[][14]; extern int monthdaytab[][14]; extern int debug_remember; void generatedates(struct tm *tp1, struct tm *tp2); void dumpdates(void); int remember_ymd(int y, int m, int d); int remember_yd(int y, int d, int *rm, int *rd); int first_dayofweek_of_year(int y); int first_dayofweek_of_month(int y, int m); int walkthrough_dates(struct event **e); void addtodate(struct event *e); /* pom.c */ #define MAXMOONS 18 void pom(int year, double UTCoffset, int *fms, int *nms); void fpom(int year, double utcoffset, double *ffms, double *fnms); /* sunpos.c */ void equinoxsolstice(int year, double UTCoffset, int *equinoxdays, int *solsticedays); void fequinoxsolstice(int year, double UTCoffset, double *equinoxdays, double *solsticedays); int calculatesunlongitude30(int year, int degreeGMToffset, int *ichinesemonths); #ifdef WITH_ICONV void set_new_encoding(void); #endif Index: head/usr.bin/calendar/events.c =================================================================== --- head/usr.bin/calendar/events.c (revision 367363) +++ head/usr.bin/calendar/events.c (revision 367364) @@ -1,244 +1,237 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1992-2009 Edwin Groothuis . * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #ifdef WITH_ICONV #include #include #include -#include static iconv_t conv = (iconv_t)-1; static char *currentEncoding = NULL; #endif #include "pathnames.h" #include "calendar.h" #ifdef WITH_ICONV void set_new_encoding(void) { const char *newenc; newenc = nl_langinfo(CODESET); if (currentEncoding == NULL) { currentEncoding = strdup(newenc); if (currentEncoding == NULL) errx(1, "set_new_encoding: cannot allocate memory"); return; } if (strcmp(currentEncoding, newenc) == 0) return; free(currentEncoding); currentEncoding = strdup(newenc); if (currentEncoding == NULL) errx(1, "set_new_encoding: cannot allocate memory"); if (conv != (iconv_t) -1) { iconv_close(conv); conv = (iconv_t) -1; } } #endif static char * convert(char *input) { char *output; #ifdef WITH_ICONV size_t inleft, outleft, converted = 0; char *outbuf, *tmp; char *inbuf; size_t outlen; if (currentEncoding == NULL) { output = strdup(input); if (output == NULL) errx(1, "convert: cannot allocate memory"); return (output); } if (conv == (iconv_t)-1) { conv = iconv_open(outputEncoding, currentEncoding); if (conv == (iconv_t)-1) { if (errno == EINVAL) errx(1, "Conversion is not supported"); else err(1, "Initialization failure"); } } inleft = strlen(input); inbuf = input; outlen = inleft; if ((output = malloc(outlen + 1)) == NULL) errx(1, "convert: cannot allocate memory"); for (;;) { errno = 0; outbuf = output + converted; outleft = outlen - converted; converted = iconv(conv, (char **) &inbuf, &inleft, &outbuf, &outleft); if (converted != (size_t) -1 || errno == EINVAL) { /* finished or invalid multibyte, so truncate and ignore */ break; } if (errno != E2BIG) { free(output); err(1, "convert"); } converted = outbuf - output; outlen += inleft * 2; if ((tmp = realloc(output, outlen + 1)) == NULL) { free(output); errx(1, "convert: cannot allocate memory"); } output = tmp; outbuf = output + converted; } /* flush the iconv conversion */ iconv(conv, NULL, NULL, &outbuf, &outleft); /* null terminate the string */ *outbuf = '\0'; #else output = strdup(input); if (output == NULL) errx(1, "convert: cannot allocate memory"); #endif return (output); } struct event * event_add(int year, int month, int day, int var, char *txt, char *extra) { struct event *e; /* * Creating a new event: * - Create a new event * - Copy the machine readable day and month * - Copy the text of the event */ e = (struct event *)calloc(1, sizeof(struct event)); if (e == NULL) errx(1, "event_add: cannot allocate memory"); e->year = year; e->month = month; e->day = day; e->var = var; e->text = convert(txt); if (e->text == NULL) errx(1, "event_add: cannot allocate memory"); e->extra = NULL; if (extra != NULL && extra[0] != '\0') e->extra = convert(extra); addtodate(e); return (e); } void event_continue(struct event *e, char *txt) { char *oldtext, *text; text = convert(txt); oldtext = e->text; if (oldtext == NULL) errx(1, "event_continue: cannot allocate memory"); asprintf(&e->text, "%s\n%s", oldtext, text); if (e->text == NULL) errx(1, "event_continue: cannot allocate memory"); free(oldtext); free(text); return; } void event_print_all(FILE *fp) { struct event *e; struct tm tm; char dbuf[80]; static int d_first; - const char *lang; - lang = getenv("LANG"); - if (lang == NULL) - lang = "C"; - if (setlocale(LC_ALL, lang) == NULL) - (void)setlocale(LC_ALL, "C"); d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); while (walkthrough_dates(&e) != 0) { if (e) { #ifdef DEBUG fprintf(stderr, "event_print_all month: %d, day: %d\n", e->month, e->day); #endif memset(&tm, 0, sizeof(struct tm)); tm.tm_mday = e->day; tm.tm_mon = e->month - 1; tm.tm_year = e->year - 1900; (void)strftime(dbuf, sizeof(dbuf), d_first ? "%e %b" : "%b %e", &tm); } /* * Go through all events and print the text of the matching * dates */ while (e != NULL) { (void)fprintf(fp, "%s%c%s%s%s%s\n", dbuf, e->var ? '*' : ' ', e->text, e->extra != NULL ? " (" : "", e->extra != NULL ? e->extra : "", e->extra != NULL ? ")" : "" ); e = e->next; } } } Index: head/usr.bin/calendar/io.c =================================================================== --- head/usr.bin/calendar/io.c (revision 367363) +++ head/usr.bin/calendar/io.c (revision 367364) @@ -1,659 +1,697 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #if 0 #ifndef lint static char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "calendar.h" enum { T_OK = 0, T_ERR, T_PROCESS, }; const char *calendarFile = "calendar"; /* default calendar file */ static const char *calendarHomes[] = {".calendar", _PATH_INCLUDE_LOCAL, _PATH_INCLUDE}; /* HOME */ static const char *calendarNoMail = "nomail";/* don't sent mail if file exist */ static char path[MAXPATHLEN]; static const char *cal_home; static const char *cal_dir; static const char *cal_file; static int cal_line; struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice; static int cal_parse(FILE *in, FILE *out); static StringList *definitions = NULL; static struct event *events[MAXCOUNT]; static char *extradata[MAXCOUNT]; static char * trimlr(char **buf) { char *walk = *buf; char *sep; char *last; while (isspace(*walk)) walk++; *buf = walk; sep = walk; while (*sep != '\0' && !isspace(*sep)) sep++; if (*sep != '\0') { last = sep + strlen(sep) - 1; while (last > walk && isspace(*last)) last--; *(last+1) = 0; } return (sep); } static FILE * cal_fopen(const char *file) { FILE *fp; char *home = getenv("HOME"); unsigned int i; struct stat sb; static bool warned = false; if (home == NULL || *home == '\0') { warnx("Cannot get home directory"); return (NULL); } if (chdir(home) != 0) { warnx("Cannot enter home directory \"%s\"", home); return (NULL); } for (i = 0; i < nitems(calendarHomes); i++) { if (chdir(calendarHomes[i]) != 0) continue; if ((fp = fopen(file, "r")) != NULL) { cal_home = home; cal_dir = calendarHomes[i]; cal_file = file; return (fp); } } warnx("can't open calendar file \"%s\"", file); if (!warned && stat(_PATH_INCLUDE_LOCAL, &sb) != 0) { warnx("calendar data files now provided by calendar-data pkg."); warned = true; } return (NULL); } static char* cal_path(void) { static char buffer[MAXPATHLEN + 10]; if (cal_dir == NULL) snprintf(buffer, sizeof(buffer), "%s", cal_file); else if (cal_dir[0] == '/') snprintf(buffer, sizeof(buffer), "%s/%s", cal_dir, cal_file); else snprintf(buffer, sizeof(buffer), "%s/%s/%s", cal_home, cal_dir, cal_file); return (buffer); } #define WARN0(format) \ warnx(format " in %s line %d", cal_path(), cal_line) #define WARN1(format, arg1) \ warnx(format " in %s line %d", arg1, cal_path(), cal_line) +static char* +cmptoken(char *line, const char* token) +{ + char len = strlen(token); + + if (strncmp(line, token, len) != 0) + return NULL; + return (line + len); +} + static int token(char *line, FILE *out, int *skip, int *unskip) { char *walk, *sep, a, c; const char *this_cal_home; const char *this_cal_dir; const char *this_cal_file; int this_cal_line; - if (strncmp(line, "endif", 5) == 0) { + while (isspace(*line)) + line++; + + if (cmptoken(line, "endif")) { if (*skip + *unskip == 0) { WARN0("#endif without prior #ifdef or #ifndef"); return (T_ERR); } if (*skip > 0) --*skip; else --*unskip; return (T_OK); } - if (strncmp(line, "ifdef", 5) == 0) { - walk = line + 5; + walk = cmptoken(line, "ifdef"); + if (walk != NULL) { sep = trimlr(&walk); if (*walk == '\0') { WARN0("Expecting arguments after #ifdef"); return (T_ERR); } if (*sep != '\0') { WARN1("Expecting a single word after #ifdef " "but got \"%s\"", walk); return (T_ERR); } if (*skip != 0 || definitions == NULL || sl_find(definitions, walk) == NULL) ++*skip; else ++*unskip; return (T_OK); } - if (strncmp(line, "ifndef", 6) == 0) { - walk = line + 6; + walk = cmptoken(line, "ifndef"); + if (walk != NULL) { sep = trimlr(&walk); if (*walk == '\0') { WARN0("Expecting arguments after #ifndef"); return (T_ERR); } if (*sep != '\0') { WARN1("Expecting a single word after #ifndef " "but got \"%s\"", walk); return (T_ERR); } if (*skip != 0 || (definitions != NULL && sl_find(definitions, walk) != NULL)) ++*skip; else ++*unskip; return (T_OK); } - if (strncmp(line, "else", 4) == 0) { - walk = line + 4; + walk = cmptoken(line, "else"); + if (walk != NULL) { (void)trimlr(&walk); if (*walk != '\0') { WARN0("Expecting no arguments after #else"); return (T_ERR); } if (*skip + *unskip == 0) { WARN0("#else without prior #ifdef or #ifndef"); return (T_ERR); } if (*skip == 0) { ++*skip; --*unskip; } else if (*skip == 1) { --*skip; ++*unskip; } return (T_OK); } if (*skip != 0) return (T_OK); - if (strncmp(line, "include", 7) == 0) { - walk = line + 7; - + walk = cmptoken(line, "include"); + if (walk != NULL) { (void)trimlr(&walk); if (*walk == '\0') { WARN0("Expecting arguments after #include"); return (T_ERR); } if (*walk != '<' && *walk != '\"') { WARN0("Excecting '<' or '\"' after #include"); return (T_ERR); } a = *walk == '<' ? '>' : '\"'; walk++; c = walk[strlen(walk) - 1]; if (a != c) { WARN1("Unterminated include expecting '%c'", a); return (T_ERR); } walk[strlen(walk) - 1] = '\0'; this_cal_home = cal_home; this_cal_dir = cal_dir; this_cal_file = cal_file; this_cal_line = cal_line; if (cal_parse(cal_fopen(walk), out)) return (T_ERR); cal_home = this_cal_home; cal_dir = this_cal_dir; cal_file = this_cal_file; cal_line = this_cal_line; return (T_OK); } - if (strncmp(line, "define", 6) == 0) { + walk = cmptoken(line, "define"); + if (walk != NULL) { if (definitions == NULL) definitions = sl_init(); - walk = line + 6; sep = trimlr(&walk); *sep = '\0'; if (*walk == '\0') { WARN0("Expecting arguments after #define"); return (T_ERR); } if (sl_find(definitions, walk) == NULL) sl_add(definitions, strdup(walk)); return (T_OK); } - if (strncmp(line, "undef", 5) == 0) { + walk = cmptoken(line, "undef"); + if (walk != NULL) { if (definitions != NULL) { - walk = line + 5; sep = trimlr(&walk); if (*walk == '\0') { WARN0("Expecting arguments after #undef"); return (T_ERR); } if (*sep != '\0') { WARN1("Expecting a single word after #undef " "but got \"%s\"", walk); return (T_ERR); } walk = sl_find(definitions, walk); if (walk != NULL) walk[0] = '\0'; } return (T_OK); } - return (T_PROCESS); + walk = cmptoken(line, "warning"); + if (walk != NULL) { + (void)trimlr(&walk); + WARN1("Warning: %s", walk); + } + walk = cmptoken(line, "error"); + if (walk != NULL) { + (void)trimlr(&walk); + WARN1("Error: %s", walk); + return (T_ERR); + } + + WARN1("Undefined pre-processor command \"#%s\"", line); + return (T_ERR); } +static void +setup_locale(const char *locale) +{ + (void)setlocale(LC_ALL, locale); +#ifdef WITH_ICONV + if (!doall) + set_new_encoding(); +#endif + setnnames(); +} + #define REPLACE(string, slen, struct_) \ if (strncasecmp(buf, (string), (slen)) == 0 && buf[(slen)]) { \ if (struct_.name != NULL) \ free(struct_.name); \ if ((struct_.name = strdup(buf + (slen))) == NULL) \ errx(1, "cannot allocate memory"); \ struct_.len = strlen(buf + (slen)); \ continue; \ } static int cal_parse(FILE *in, FILE *out) { + char *mylocale = NULL; char *line = NULL; char *buf; size_t linecap = 0; ssize_t linelen; ssize_t l; static int count = 0; int i; int month[MAXCOUNT]; int day[MAXCOUNT]; int year[MAXCOUNT]; int skip = 0; int unskip = 0; char *pp, p; int flags; char *c, *cc; bool incomment = false; if (in == NULL) return (1); cal_line = 0; while ((linelen = getline(&line, &linecap, in)) > 0) { cal_line++; buf = line; if (buf[linelen - 1] == '\n') buf[--linelen] = '\0'; if (incomment) { c = strstr(buf, "*/"); if (c) { c += 2; linelen -= c - buf; buf = c; incomment = false; } else { continue; } } if (!incomment) { do { c = strstr(buf, "//"); cc = strstr(buf, "/*"); if (c != NULL && (cc == NULL || c - cc < 0)) { /* single line comment */ *c = '\0'; linelen = c - buf; break; } else if (cc != NULL) { c = strstr(cc + 2, "*/"); if (c != NULL) { /* multi-line comment ending on same line */ c += 2; memmove(cc, c, buf + linelen + 1 - c); linelen -= c - cc; } else { /* multi-line comment */ *cc = '\0'; linelen = cc - buf; incomment = true; break; } } } while (c != NULL || cc != NULL); } for (l = linelen; l > 0 && isspace((unsigned char)buf[l - 1]); l--) ; buf[l] = '\0'; if (buf[0] == '\0') continue; if (buf == line && *buf == '#') { switch (token(buf+1, out, &skip, &unskip)) { case T_ERR: free(line); return (1); case T_OK: continue; case T_PROCESS: break; default: break; } } if (skip != 0) continue; /* * Setting LANG in user's calendar was an old workaround * for 'calendar -a' being run with C locale to properly * print user's calendars in their native languages. * Now that 'calendar -a' does fork with setusercontext(), * and does not run iconv(), this variable has little use. */ if (strncmp(buf, "LANG=", 5) == 0) { - (void)setlocale(LC_ALL, buf + 5); -#ifdef WITH_ICONV - if (!doall) - set_new_encoding(); -#endif - setnnames(); + if (mylocale == NULL) + mylocale = strdup(setlocale(LC_ALL, NULL)); + setup_locale(buf + 5); continue; } /* Parse special definitions: Easter, Paskha etc */ REPLACE("Easter=", 7, neaster); REPLACE("Paskha=", 7, npaskha); REPLACE("ChineseNewYear=", 15, ncny); REPLACE("NewMoon=", 8, nnewmoon); REPLACE("FullMoon=", 9, nfullmoon); REPLACE("MarEquinox=", 11, nmarequinox); REPLACE("SepEquinox=", 11, nsepequinox); REPLACE("JunSolstice=", 12, njunsolstice); REPLACE("DecSolstice=", 12, ndecsolstice); if (strncmp(buf, "SEQUENCE=", 9) == 0) { setnsequences(buf + 9); continue; } /* * If the line starts with a tab, the data has to be * added to the previous line */ if (buf[0] == '\t') { for (i = 0; i < count; i++) event_continue(events[i], buf); continue; } /* Get rid of leading spaces (non-standard) */ while (isspace((unsigned char)buf[0])) memcpy(buf, buf + 1, strlen(buf)); /* No tab in the line, then not a valid line */ if ((pp = strchr(buf, '\t')) == NULL) continue; /* Trim spaces in front of the tab */ while (isspace((unsigned char)pp[-1])) pp--; p = *pp; *pp = '\0'; if ((count = parsedaymonth(buf, year, month, day, &flags, extradata)) == 0) continue; *pp = p; if (count < 0) { /* Show error status based on return value */ if (debug) WARN1("Ignored: \"%s\"", buf); if (count == -1) continue; count = -count + 1; } /* Find the last tab */ while (pp[1] == '\t') pp++; for (i = 0; i < count; i++) { if (debug) WARN1("got \"%s\"", pp); events[i] = event_add(year[i], month[i], day[i], ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp, extradata[i]); } } while (skip-- > 0 || unskip-- > 0) { cal_line++; WARN0("Missing #endif assumed"); } free(line); fclose(in); + if (mylocale != NULL) { + setup_locale(mylocale); + free(mylocale); + } return (0); } void cal(void) { FILE *fpin; FILE *fpout; int i; for (i = 0; i < MAXCOUNT; i++) extradata[i] = (char *)calloc(1, 20); if ((fpin = opencalin()) == NULL) return; if ((fpout = opencalout()) == NULL) { fclose(fpin); return; } if (cal_parse(fpin, fpout)) return; event_print_all(fpout); closecal(fpout); } FILE * opencalin(void) { struct stat sbuf; FILE *fpin; /* open up calendar file */ cal_file = calendarFile; if ((fpin = fopen(calendarFile, "r")) == NULL) { if (doall) { if (chdir(calendarHomes[0]) != 0) return (NULL); if (stat(calendarNoMail, &sbuf) == 0) return (NULL); if ((fpin = fopen(calendarFile, "r")) == NULL) return (NULL); } else { fpin = cal_fopen(calendarFile); } } return (fpin); } FILE * opencalout(void) { int fd; /* not reading all calendar files, just set output to stdout */ if (!doall) return (stdout); /* set output to a temporary file, so if no output don't send mail */ snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); if ((fd = mkstemp(path)) < 0) return (NULL); return (fdopen(fd, "w+")); } void closecal(FILE *fp) { struct stat sbuf; int nread, pdes[2], status; char buf[1024]; if (!doall) return; rewind(fp); if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) goto done; if (pipe(pdes) < 0) goto done; switch (fork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); goto done; case 0: /* child -- set stdin to pipe output */ if (pdes[0] != STDIN_FILENO) { (void)dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } (void)close(pdes[1]); execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", "\"Reminder Service\"", (char *)NULL); warn(_PATH_SENDMAIL); _exit(1); } /* parent -- write to pipe input */ (void)close(pdes[0]); write(pdes[1], "From: \"Reminder Service\" <", 26); write(pdes[1], pw->pw_name, strlen(pw->pw_name)); write(pdes[1], ">\nTo: <", 7); write(pdes[1], pw->pw_name, strlen(pw->pw_name)); write(pdes[1], ">\nSubject: ", 11); write(pdes[1], dayname, strlen(dayname)); write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30); while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) (void)write(pdes[1], buf, nread); (void)close(pdes[1]); done: (void)fclose(fp); (void)unlink(path); while (wait(&status) >= 0); }