Subject: calendar(1) extension... RFC
To: None <tech-userlevel@netbsd.org>
From: Mason Loring Bliss <mason@acheron.middleboro.ma.us>
List: tech-userlevel
Date: 11/17/2002 01:45:08
--GV0iVqYguTV4Q9ER
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

What do you folks think about this? It's a trivially small amount of code
that makes calendar(1) truly useful, as opposed to fairly marginal in
utility.

To summarize the change, this adds a flag, '-x', that extends calendar(1)'s
syntax a bit, allowing the processing of calendar file lines like this:

07/01 +15@1962 Matt

This would start noting this event fifteen days in advance, and the output
would look something like this:

In 5 days: 12/19 Matt (1962: 40 years ago)

The +foo and @bar markers can be placed anywhere in the string, in any
order, and they don't have to be adjacent. If they are preceded by a space
character, this is omitted from the final output along with the markers
themselves.

The point of this is that you can set an arbitrary amount of notice for
events, thus receiving sufficient and persistent warning (sufficient being
something you define) to prepare for birthdays, anniversaries, and other
such events. As it is now, calendar(1) provides no way to extend the amount
of warning you get for events without getting a large amount of advance
warning for *everything*, which, to me, is a bit annoying.

An important thing to note is that if you don't do anything to your
calendar files and don't use the '-x' switch, effectively nothing changes.



Here's the patch, for your inspection. Thanks in advance for comments. I'd
like to commit this sometime, if folks don't disapprove of it. (About the
only change I can think of is requiring a '-v' flag to turn on the "in foo
days" prefix, independant of the '-x' flag...)


--- /usr/src/usr.bin/calendar/calendar.c	Tue Dec  4 10:55:32 2001
+++ calendar.c	Sun Nov 17 01:30:11 2002
@@ -78,6 +78,7 @@
 static char *fname =3D "calendar", *datestr =3D NULL;
 static struct passwd *pw;
 static int doall;
+static int extendsyntax;
 static char path[MAXPATHLEN + 1];
=20
 /* 1-based month, 0-based days, cumulative */
@@ -86,7 +87,8 @@
 	{ 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
 };
 static struct tm *tp;
-static int *cumdays, offset, yrdays;
+static int *cumdays, offset, yrdays, yearofevent, yearselapsed,
+    haveyear, realoffset;
 static char dayname[10];
=20
 static struct iovec header[] =3D {
@@ -118,6 +120,9 @@
 static void	 getmmdd(struct tm *, char *);
 static int	 getmonth(char *);
 static int	 isnow(char *);
+static int	 geteventoffset(char *);
+static void	 getyears(char *);
+static void	 stripmarker(char *, char *);
 static FILE	*opencal(void);
 static void	 settime(void);
 static void	 usage(void) __attribute__((__noreturn__));
@@ -130,7 +135,7 @@
 	int ch;
 	const char *caldir;
=20
-	while ((ch =3D getopt(argc, argv, "-ad:f:l:w:")) !=3D -1)
+	while ((ch =3D getopt(argc, argv, "-ad:f:l:w:x")) !=3D -1)
 		switch (ch) {
 		case '-':		/* backward contemptible */
 		case 'a':
@@ -152,6 +157,9 @@
 		case 'w':
 			atodays(ch, optarg, &weekend);
 			break;
+		case 'x':
+			extendsyntax =3D 1;
+			break;
 		case '?':
 		default:
 			usage();
@@ -185,7 +193,7 @@
 {
 	int printing;
 	FILE *fp;
-	char *line;
+	char *line, *plural;
=20
 	if ((fp =3D opencal()) =3D=3D NULL)
 		return;
@@ -194,8 +202,33 @@
 			continue;
 		if (line[0] !=3D '\t')
 			printing =3D isnow(line) ? 1 : 0;
-		if (printing)
-			(void)fprintf(fp, "%s\n", line);
+		if (printing) {
+			if (extendsyntax) {
+				if (realoffset =3D=3D 0)
+					(void)fputs("Today: ", fp);
+				else if (realoffset =3D=3D 1)
+					(void)fputs("Tomorrow: ", fp);
+				else
+					(void)fprintf(fp, "In %d days: ",
+					    realoffset);
+			}
+			(void)fprintf(fp, "%s", line);
+			if (extendsyntax && haveyear) {
+				plural =3D abs(yearselapsed) =3D=3D 1 ? "" : "s";
+				if (yearselapsed =3D=3D 0)
+					(void)fprintf(fp, " (%d: this year)",
+					    yearofevent);
+				else if (yearselapsed < 0)
+					(void)fprintf(fp, " (%d: in %d year%s)",
+					    yearofevent, abs(yearselapsed),
+					    plural);
+				else
+					(void)fprintf(fp,
+					    " (%d: %d year%s ago)", yearofevent,
+					    yearselapsed, plural);
+			}
+			(void)fputc('\n', fp);
+		}
 		free(line);
=20
 	}
@@ -240,7 +273,8 @@
 isnow(endp)
 	char *endp;
 {
-	int day, flags, month, v1, v2;
+	int day, flags, month, v1, v2, eventoffset;
+	char *savebuf =3D endp;
=20
 #define	F_ISMONTH	0x01
 #define	F_ISDAY		0x02
@@ -273,16 +307,89 @@
 		day =3D tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
 	day =3D cumdays[month] + day;
=20
-	/* if today or today + offset days */
-	if (day >=3D tp->tm_yday && day <=3D tp->tm_yday + offset)
+	/* if event-specific offset present, don't use standard offset */
+	if (extendsyntax) {
+		eventoffset =3D geteventoffset(savebuf);
+		(void) getyears(savebuf);
+		if (haveyear)
+			yearselapsed =3D tp->tm_year - (yearofevent - 1900);
+	} else
+		eventoffset =3D offset;
+
+	/* if today or today + eventoffset days */
+	if (day >=3D tp->tm_yday && day <=3D tp->tm_yday + eventoffset) {
+		realoffset =3D day - tp->tm_yday;
 		return (1);
+	}
 	/* if number of days left in this year + days to event in next year */
-	if (yrdays - tp->tm_yday + day <=3D offset)
+	if (yrdays - tp->tm_yday + day <=3D eventoffset) {
+		realoffset =3D yrdays - tp->tm_yday + day;
 		return (1);
+	}
 	return (0);
 }
=20
 static int
+geteventoffset(buf)
+	char *buf;
+{
+	int eventoffset;
+	char *p;
+
+	/* see if we have an offset marker */
+	if ((p =3D strchr(buf, '+')) =3D=3D '\0')
+		return (offset);
+
+	if (sscanf((char *) (p + 1), "%i", &eventoffset) =3D=3D 1) {
+		(void) stripmarker(buf, p);
+		return (eventoffset);
+	}
+
+	return (offset);
+}
+
+static void
+getyears(buf)
+	char *buf;
+{
+	char *p;
+
+	/* see if we have an offset marker */
+	if ((p =3D strchr(buf, '@')) =3D=3D '\0') {
+		haveyear =3D 0;
+		return;
+	}
+
+	if (sscanf((char *) (p + 1), "%i", &yearofevent) =3D=3D 1) {
+		(void) stripmarker(buf, p);
+		haveyear =3D 1;
+		return;
+	}
+
+	haveyear =3D 0;
+	return;
+}
+
+static void
+stripmarker(buf, mark)
+	char *buf, *mark;
+{
+	int i, cal_buffsize;
+	char *marker =3D mark;
+
+	cal_buffsize =3D strlen(buf);
+
+	/* find extent of marker; remove marker and leading space */
+	i =3D (marker - buf) + 1;	/* offset to start of digits */
+	while (isdigit(buf[i]) && i < cal_buffsize)
+		++i;
+	if (isspace(*(marker - 1)))
+		--marker;
+	memmove((void *) marker, (void *) (buf + i), cal_buffsize - i + 1);
+}
+
+
+static int
 getfield(p, endp, flags)
 	char *p, **endp;
 	int *flags;
@@ -516,6 +623,6 @@
 usage(void)
 {
 	(void)fprintf(stderr, "Usage: %s [-a] [-d MMDD[[YY]YY]"
-	    " [-f fname] [-l days] [-w days]\n", getprogname());
+	    " [-f fname] [-l days] [-w days] [-x]\n", getprogname());
 	exit(1);
 }
--- /usr/src/usr.bin/calendar/calendar.1	Thu Feb  7 20:36:20 2002
+++ calendar.1	Sun Nov 17 01:22:05 2002
@@ -46,6 +46,7 @@
 .Op Fl l Ar days
 .Op Fl w Ar days
 .Op Fl d Ar MMDD[[YY]YY]
+.Op Fl x
 .Sh DESCRIPTION
 The
 .Nm
@@ -82,6 +83,12 @@
 value is two, which causes
 .Nm
 to print entries through the weekend on Fridays.
+.It Fl x
+Allow for extended syntax of two forms, +n where n is one or more digits
+denoting the number of days' warning this event is to be given, and @y,
+where y is the year in which the event has taken or will take place. Either
+option may be individually present or absent. Entries will, in any case, be
+printed with slightly more verbose output format.
 .El
 .Pp
 Lines should begin with a month and day.

--=20
Mason Loring Bliss   mason@acheron.middleboro.ma.us   Ewige Blumenkraft!
https://www.deadsexy.org/  awake ? sleep : random() & 2 ? dream : sleep;

--GV0iVqYguTV4Q9ER
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (NetBSD)

iD8DBQE91zr0ykMMY715wXIRAuATAJ9DrUa3aKjye9cfXFrOtG7BADVzWgCghdnV
yDdJX2waM21+S69iIpQmUeQ=
=aYh+
-----END PGP SIGNATURE-----

--GV0iVqYguTV4Q9ER--