[net.sources] suntools alarmer with bug fixes

black@unc.UUCP (Samuel Black) (04/30/86)

A while ago, I posted an alarmer for use with suntools.  Well,
with the advent of Daylight Savings Time, I found a bug.
Another bug happens when the alarm rings for a long time;
something about pixrect allocating too much space.  Ths bug
fix for this is a kludge:  the icon flashes for a while, stops,
flashes again, ...  If anyone has a better fix (short of making
the whole thing a tool), please let me know.

Compile the program as

	cc -O -s -o alarm -DSUN -DICON_DIR=\"icon directory\" alarm.c

Not defining SUN gives an alarmer for standard terminals that
rings the bell and prints the message on the screen.  When you use
this version, don't forget to kill the process before you logout.


----------------------------------

	You can't spell 'geek' without a double-E.
			- Jon Bentley

	...!{decvax,ihnp4}!mcnc!unc!black	(usenet)
	black%unc@csnet-relay.csnet		(arpanet)

----------------------------------

#----------------------  CUT HERE  ----------------------
#! /bin/sh
#
#  This is a shell archive.  Use /bin/sh to unpack.
#
echo "extracting alarm.c (11913 bytes)"
sed 's/^X //' << '***FUBAR***' > alarm.c
X /*
X **  #define SUN ==> Suntools alarmer
X **	   else ==> general alarmer
X **
X **	Sam Black
X **	22 January, 1986
X **
X **  This program reads in an alarm database (default ~/.alarm) and
X **  signals the user when an alarm goes off.
X **
X **  #define SUN ==> Flash the alarm bell icon.
X **	   else ==> Ring the terminal's bell.
X **
X */
X #include <stdio.h>
X #include <pwd.h>
X #include <sgtty.h>
X #include <ctype.h>
X #include <signal.h>
X #include <setjmp.h>
X #include <sys/file.h>
X #include <sys/time.h>
X #include <sys/types.h>
X #include <sys/timeb.h>
X 
X /*
X **  define some useful macros
X **  (DEB, when, otherwise, nitems, IOCTL, NE, FREENODE).
X */
X 
X #ifdef DEBUG
X #	define DEB(x)	fprintf/**/x
X #else
X #	define DEB(x)
X #endif DEBUG
X 
X #define when		break; case
X #define otherwise	break; default
X #define nitems(x)	(sizeof(x) / sizeof(x[0]))
X 
X #define IOCTL(op, par)			\
X 	if (ioctl(0, op, par) < 0)	\
X 	{				\
X 		perror("tty");		\
X 		exit(-1);		\
X 	}
X 
X #define NEW(t)	(t *) malloc(sizeof(t))
X #define FREENODE			\
X 	{				\
X 		TIMES *tmp = alarms;	\
X 					\
X 		alarms = alarms->next;	\
X 		free(tmp);		\
X 	}
X 
X /*
X **  Set up some default program parameters.
X */
X #define CONFIG_FILE	".alarm"
X #define RESET_FILE	".alarmreset"
X #define RESET		"kill -%d %ld"
X #define SIGREAD		SIGEMT
X 
X #ifdef SUN
X #	define QUERY		"\033[11t"
X #	define RESPONSE		"\033[%dt"
X #	define ICONIC		2
X 
X #	define SIGNAL(x)	printf(alarm_/**/x)
X 
X #	ifndef ICON_DIR
X #		define ICON_DIR	"/unc/black/icons"
X #	endif ICON_DIR
X 
X #	define IMAGE		"\033]I%s/alarm_%s\033\134"
X #	define IHEADER		"\033]L\033\134"
X #else
X #	define SIGNAL(x)
X #endif SUN
X 
X /*
X **  Time constants
X */
X #define SPM	 60		/* seconds per minute */
X #define SPH	(60 * SPM)	/* seconds per hour */
X #define SPD	(24 * SPH)	/* seconds per day */
X #define LOOK	(SPD >> 1)
X #define JAN_86	504921600L	/* 1 January, 1986 */
X 
X /*
X **  Types
X */
X #define STRLEN		80
X typedef char STRING[STRLEN];
X 
X typedef struct TIMES		/* the TIMES structure */
X {
X 	int time;		/* how many seconds into the epoch */
X 	STRING message;
X 	struct TIMES *next;
X } TIMES;
X 
X /*
X **  Routines used
X */
X static TIMES *read_alarm_file();
X static TIMES *insert();
X static long gettime();
X static void usage();
X static void lowercase();
X static long date_to_time();
X int reset_alarm();
X long time();
X 
X #ifdef SUN
X #	define REPEAT 512
X #	define SHOWCOUNT 8
X 	static int iconic();
X 	static void setup_term();
X 	int reset_term();
X #endif SUN
X 
X jmp_buf env;
X char *pname;
X 
X /*
X **  main():  process command line and loop on the database.
X */
X main(argc, argv)
X int argc;
X char *argv[];
X {
X #ifdef SUN
X 	char *icon_dir;
X 	STRING	alarm_on,	/* alarm on icon (normal) */
X 		alarm_inv,	/* alarm on icon (inverted) */
X 		alarm_off;	/* alarm off icon */
X #endif SUN
X 	STRING alarmdb;		/* alarm file name */
X 	STRING reset;		/* reset file name */
X 	int rfile;
X 	TIMES *alarms;		/* alarm structure */
X 	long getpid();
X 	char *getenv();
X 	struct passwd *getpwuid();
X 	struct passwd *p_entry;
X 	int history = -SPH;
X 	int i;
X 
X 	pname = *argv;
X 
X 	/*
X 	**  Get user's home directory (for ~/.alarm file).
X 	*/
X 	if ( (p_entry = getpwuid(getuid())) )
X 	{
X 		sprintf(alarmdb, "%s/%s", p_entry->pw_dir, CONFIG_FILE);
X 		sprintf(reset, "%s/%s", p_entry->pw_dir, RESET_FILE);
X 	}
X 	else
X 	{
X 		perror("getuid");
X 		exit(-1);
X 	}
X 
X 	/*
X 	**  Take the arguments.  The only legal ones now are an
X 	**  optional database file and an optional history value.
X 	*/
X 	while (--argc)
X 		if (**++argv == '-')
X 			switch ((*argv)[1])
X 			{
X 				when 'h':  i = atoi(*++argv);
X 					   if ((i >= 0) && (i <= SPM))
X 						history = -60 * i;
X 					   else
X 						usage(pname);
X 					   --argc;
X 				otherwise: usage(pname);
X 			}
X 		else
X 			strcpy(alarmdb, *argv);
X 
X 
X 	/*
X 	**  Enable the user to signal this process
X 	*/
X 	if ((rfile = open(reset, O_TRUNC | O_WRONLY | O_CREAT, 0700)) == -1)
X 	{
X 		perror("open");
X 		exit(-1);
X 	}
X 	else
X 	{
X 		sprintf(reset, RESET, SIGREAD, getpid());
X 		write(rfile, reset, strlen(reset));
X 		close(rfile);
X 	}
X 
X 	/*
X 	**  Read in the alarm file.
X 	*/
X 	DEB((stderr, "About to read alarm file\n"));
X 	alarms = read_alarm_file(alarmdb, history);
X 
X #ifdef SUN
X 	/*
X 	**  Set up the icons
X 	*/
X 	if (!(icon_dir = getenv("ICONDIR")))
X 		icon_dir = ICON_DIR;
X 	sprintf(alarm_on,  IMAGE, icon_dir, "on");
X 	sprintf(alarm_inv, IMAGE, icon_dir, "on2");
X 	sprintf(alarm_off, IMAGE, icon_dir, "off");
X 
X 	/*
X 	**  Put up the initial icon and set up the tty driver.
X 	*/
X 	setup_term();
X 	printf(IHEADER);
X 	SIGNAL(off);
X 	fflush(stdout);
X 
X 	/*
X 	**  Set up signal trapping (Note:  DO NOT trap SIGWINCH!!).
X 	*/
X 	for (i = SIGHUP; i <= SIGPROF; ++i)
X 		signal(i, reset_term);
X #endif SUN
X 
X 	/*
X 	**  Trap SIGREAD to re-read .alarm file.
X 	*/
X 	signal(SIGREAD, reset_alarm);
X 	if (setjmp(env))
X 	{
X 		while (alarms)
X 			FREENODE;
X 		alarms = read_alarm_file(alarmdb, 0);
X 		signal(SIGREAD, reset_alarm);
X 	}
X 
X 	/*
X 	**  Repeat ad nauseum.
X 	*/
X 	for (;;)
X 	{
X 		/*
X 		**  Sleep until the next alarm.
X 		*/
X 		if (alarms->time > time(0))
X 		{
X 			DEB((stderr, "Sleeping for %ld seconds.\n",
X 				alarms->time - time(0)));
X 			sleep((int) (alarms->time - time(0)));
X 		}
X 		else
X 			DEB((stderr, "Not sleeping.\n"));
X 
X 		/*
X 		**  Check to see if we need to re-read the file.
X 		*/
X 		if (!alarms->next)
X 		{
X 			FREENODE;
X 			alarms = read_alarm_file(alarmdb, 0);
X 		}
X 		else
X 		{
X #ifdef SUN
X 			int times = 0;
X 			/*
X 			**  Indicate the alarm, and wait for the user
X 			**  to open the window.
X 			*/
X 			while (iconic())
X 			{
X 				if ((++times % REPEAT) < SHOWCOUNT)
X 				{
X 					SIGNAL(on);
X 					SIGNAL(inv);
X 				}
X 			}
X #else
X 			printf("\007\007\007");
X #endif SUN
X 
X 			/*
X 			**  Dump ALL current alarm messages.
X 			*/
X 			putchar('\n');
X 			do
X 			{
X 				puts(alarms->message);
X 				FREENODE;
X 			} while (alarms->next && (alarms->time <= time(0)));
X 
X 			/*
X 			**  Turn the alarm off.
X 			*/
X 			SIGNAL(off);
X 			fflush(stdout);
X 		}
X 	}
X }
X 
X #ifdef SUN
X struct sgttyb save_tty;
X 
X /*
X **  setup_term(): set up tty driver for suntools query.
X */
X static void
X setup_term()
X {
X 	struct sgttyb tty;
X 
X 	IOCTL(TIOCGETP, &tty);
X 	save_tty = tty;
X 
X 	tty.sg_flags &= ~ECHO;
X 	tty.sg_flags |= CBREAK;
X 
X 	IOCTL(TIOCSETP, &tty);
X }
X 
X /*
X **  reset_term(): reset tty driver to its initial state.
X */
X int
X reset_term(sig)
X int sig;
X {
X 	IOCTL(TIOCSETP, &save_tty);
X 	exit(sig);
X }
X #endif SUN
X 
X /*
X **  reset_alarm(): trap alarm signals.
X */
X int
X reset_alarm()
X {
X 	longjmp(env);
X }
X 
X int daily;
X int cwday,	/* day of week */
X     cday,	/* day of month */
X     cmonth,	/* month of year */
X     cyear;	/* year */
X int TZ;		/* difference to GMT */
X 
X /*
X **  read_alarm_file(): Read in alarm database.
X */
X static TIMES *
X read_alarm_file(fname, history)
X STRING fname;
X int history;
X {
X 	FILE *fopen();
X 	struct tm *localtime();
X 	FILE *alarmdb;
X 	TIMES *alarms, *cur;
X 	char input[160];
X 	long ctime;		/* current time-of-day */
X 	long itime;		/* alarm entry time-of-day */
X 	long rtime;		/* relative to current time-of-day */
X 	struct tm *ltime;
X 	struct timeb tp;
X 
X 	/*
X 	**  Open the alarm database
X 	*/
X 	if (!(alarmdb = fopen(fname, "r")))
X 	{
X 		perror(fname);
X 		usage(pname);
X 	}
X 
X 	/*
X 	**  Calculate the current time of day.
X 	*/
X 	ftime(&tp);
X 	ctime = tp.time;
X 	DEB((stderr, "dstflag = %d\n", tp.dstflag));
X 	TZ = (tp.timezone * SPM) - (tp.dstflag * SPH);
X 	ltime = localtime(&ctime);
X 	DEB((stderr, "Current time = %ld\n", ctime));
X 	cmonth = ltime->tm_mon + 1;
X 	cday = ltime->tm_mday;
X 	cyear = ltime->tm_year;
X 	cwday = ltime->tm_wday - 7;
X 	DEB((stderr, "cmonth = %d, cday = %d, cyear = %d, cwday = %d\n",
X 		cmonth, cday, cyear, cwday));
X 
X 	/*
X 	**  Read the alarms file and put each alarm
X 	**  in the structure, sorted by time.  Only
X 	**  alarms in the next LOOK seconds (default
X 	**  12 hours) are entered.
X 	*/
X 	alarms = NEW(TIMES);
X 	alarms->time = ctime + LOOK;
X 	strcpy(alarms->message, "12 hour timer interrupt");
X 	alarms->next = NULL;
X 
X 	while (fgets(input, sizeof(input), alarmdb))
X 	{
X 		/*
X 		**  Parse the input line.
X 		*/
X 		input[strlen(input) - 1] = '\0';
X 		rtime = (itime = gettime(input)) - ctime;
X 
X 		/*
X 		**  Fix daily alarms to be in the previous 12 hours
X 		**  or the last 12 hours, whichever is closer.
X 		*/
X 		if (daily)
X 		{
X 			if (rtime > LOOK)
X 			{
X 				rtime -= SPD;
X 				itime -= SPD;
X 			}
X 			else if (rtime < -LOOK)
X 			{
X 				rtime += SPD;
X 				itime += SPD;
X 			}
X 		}
X 
X 		/*
X 		**  Only insert alarms that are in the next
X 		**  LOOK seconds, and are within the history
X 		**  limit (default 1/2 hour).
X 		*/
X 		if ((rtime < LOOK) && (rtime >= history))
X 		{
X 			cur = NEW(TIMES);
X 			cur->time = itime;
X 			strcpy(cur->message, input);
X 
X 			alarms = insert(alarms, cur);
X 		}
X 	}
X 
X 	fclose(alarmdb);
X 	traverse(alarms);
X 	return(alarms);
X }
X 
X #define ISDAY(s) !strncmp(dow, s, strlen(dow))
X 
X /*
X **  gettime(): Input line parser.
X */
X static long
X gettime(input)
X char *input;
X {
X 	int date[5];
X 	STRING dow;	/* for weekly or daily alarms */
X 	static STRING days[] =
X 	{
X 		"sundays",
X 		"mondays",
X 		"tuesdays",
X 		"wednesdays",
X 		"thursdays",
X 		"fridays",
X 		"saturdays"
X 	};
X 
X 	daily = 0;
X 	/*
X 	**  Look for comments (blank lines, # lines)
X 	*/
X 	if ((sscanf(input, "%s", dow) != 1) || (*dow == '#'))
X 		return(0L);
X 
X 	/*
X 	**  Look for a specific date.
X 	*/
X 	if (isdigit(*dow))
X 		sscanf(input, "%d/%d %d:%d",
X 			&date[0], &date[1], &date[3], &date[4]);
X 
X 	/*
X 	**  Look for daily or weekly alarms.
X 	*/
X 	else
X 	{
X 		register int i;
X 
X 		sscanf(input, "%s %d:%d", dow, &date[3], &date[4]);
X 		lowercase(dow);
X 		*date = cmonth;
X 
X 		/*
X 		**  Daily.
X 		*/
X 		if (ISDAY("any") || ISDAY("daily"))
X 		{
X 			date[1] = cday;
X 			daily = 1;
X 		}
X 		/*
X 		**  Weekly.
X 		*/
X 		else
X 		{
X 			for (i = 0; i < nitems(days); ++i)
X 				if (ISDAY(days[i]))
X 				{
X 					date[1] = cday + ((i - cwday) % 7);
X 					break;
X 				}
X 			if (i == nitems(days))
X 			{
X 				fprintf(stderr,
X 					"'%s' is an invalid alarm entry.\n",
X 					input);
X 				return(0L);
X 			}
X 		}
X 
X 		DEB((stderr, "periodic reminder: %s becomes %d/%d\n",
X 				dow, date[0], date[1]));
X 	}
X 
X 	/*
X 	**  Take care of December 31.
X 	*/
X 	if ((date[0] > cmonth) || ((date[0] == cmonth) && (date[1] >= cday)))
X 		date[2] = cyear;
X 	else
X 		date[2] = cyear + 1;
X 
X 	return(date_to_time(date));
X }
X 
X /*
X **  lowercase(): Convert string to lower case.
X */
X static void
X lowercase(str)
X char *str;
X {
X 	for ( ; *str; ++str)
X 		if (isupper(*str))
X 			*str = tolower(*str);
X }
X 
X /*
X **  date_to_time(): Take an array of
X **
X **		[month day year hour minute]
X **
X **  and return the number of seconds since the epoch.
X **  (1 January, 1970)
X */
X static long
X date_to_time(date)
X int *date;
X {
X 	static int corr[] =
X 	{
X 		0, 1, -1, 0, 0, 1, 1, 2, 3, 3, 4, 4
X 	};
X 	int month  = *date - 1;
X 	int day  = *++date - 1;
X 	int year = *++date;
X 	int hour = *++date;
X 	int min = *++date;
X 	int leap = ((month > 1) && (dysize(1900 + year) == 366));
X 	register int ydisp = (30 * month + day + corr[month]) * SPD;
X 	register int i;
X 
X 	if (leap)
X 		ydisp += SPD;
X 
X 	/*
X 	**  Add in years since 1986.
X 	*/
X 	for (i = 86; i < year; ++i)
X 		ydisp += (SPD * dysize(1900 + i));
X 
X 	DEB((stderr, "Time for %d/%d, %d at %d:%2d = %ld\n",
X 			month+1, day+1, year, hour, min,
X 			JAN_86 + TZ + ydisp + (hour * SPH) + (min * SPM)));
X 
X 	/*
X 	**  Sum everything (including time zone adjustment).
X 	*/
X 	return(JAN_86 + TZ + ydisp + (hour * SPH) + (min * SPM));
X }
X 
X /*
X **  insert(): Perform an insertion sort.
X */
X static TIMES *
X insert(node, el)
X TIMES *node, *el;
X {
X 	TIMES *last = NULL;
X 	TIMES *root = node;
X 
X 	DEB((stderr, "Inserting node.  time = %ld, msg = '%s'\n",
X 			el->time, el->message));
X 	while (node->next && (node->time < el->time))
X 	{
X 		last = node;
X 		node = node->next;
X 	}
X 
X 	el->next = node;
X 	if (last)
X 		last->next = el;
X 	else
X 		root = el;
X 
X 	return(root);
X }
X 
X /*
X **  traverse(): Dump the alarm list.
X */
X traverse(alarms)
X TIMES *alarms;
X {
X #ifdef DEBUG
X 	putc('\n', stderr);
X 	for ( ; alarms; alarms = alarms->next)
X 		fprintf(stderr, "alarm at time = %ld; msg = %s\n",
X 			alarms->time, alarms->message);
X #endif DEBUG
X }
X 
X #ifdef SUN
X /*
X **  iconic(): Query suntools to see if the window is iconic.
X */
X static int
X iconic()
X {
X 	int ans;
X 
X 	IOCTL(TIOCFLUSH, 0);
X 	printf(QUERY);
X 	scanf(RESPONSE, &ans);
X 
X 	return(ans == ICONIC);
X }
X #endif SUN
X 
X /*
X **  usage(): Error message.
X */
X static void
X usage(pname)
X char *pname;
X {
X 	printf("usage: %s [-h history] [alarmfile]\n", pname);
X 	exit(-1);
X }
***FUBAR***
echo "end of shar file"
exit