[comp.sources.misc] v17i098: calentool - day/week/month/year-at-a-glance SunView tool, Part17/23

billr@saab.CNA.TEK.COM (Bill Randle) (04/07/91)

Submitted-by: Bill Randle <billr@saab.CNA.TEK.COM>
Posting-number: Volume 17, Issue 98
Archive-name: calentool/part17

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 17 (of 23)."
# Contents:  calentool.c common.c
# Wrapped by billr@saab on Thu Mar 28 08:38:28 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'calentool.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'calentool.c'\"
else
echo shar: Extracting \"'calentool.c'\" \(22129 characters\)
sed "s/^X//" >'calentool.c' <<'END_OF_FILE'
X/*
X * $Header: calentool.c,v 2.5 91/03/27 16:44:59 billr Exp $
X */
X/*
X * calentool.c
X *
X * calentool - a year/month/week/day-at-a-glance calendar for Sun workstations.
X *
X * Author: Philip Heller, Sun Microsystems. Inc. <terrapin!heller@sun.com>
X *
X * Original source Copyright (C) 1987, Sun Microsystems, Inc.
X *	All Rights Reserved
X * Permission is hereby granted to use and modify this program in source
X * or binary form as long as it is not sold for profit and this copyright
X * notice remains intact.
X *
X *
X * Changes/additions by: Bill Randle, Tektronix, Inc. <billr@saab.CNA.TEK.COM>
X *
X * Changes and additions Copyright (C) 1988, 1989, 1991 Tektronix, Inc.
X *	All Rights Reserved
X * Permission is hereby granted to use and modify the modifications in source
X * or binary form as long as they are not sold for profit and this copyright
X * notice remains intact.
X */
X#include <stdio.h>
X#include <suntool/sunview.h>
X#include <suntool/panel.h>
X#include <suntool/seln.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <signal.h>
X#include "ct.h"
X
Xextern Pixfont *font, *sfont;
Xextern char apts_pathname[], tmpapts_pathname[];
Xextern char clockstr[];
Xextern char *smonthnames[];
Xextern Panel_item clock_pi;
Xextern Frame fframe;
Xextern Panel panel;
Xextern int day_is_open;
X#ifndef NO_SUN_MOON
Xextern Frame sframe;
X#endif
X
XFrame frame = 0;
XPixwin *main_pixwin;
Xstruct tm today, current;
Xstruct tm First, Last;
Xstruct tm olddate, closedate;
Xint mainsw_state, selected_type;
Xint x_coord, y_coord, startx, starty;
Xint read_only;
Xint nr_weekdays, dayslot_width, day_message_size, n_tslots, n_slots;
Xint week_message_size, weekslot_width, weekslot_height, dayslot_height;
Xint ybox_height, ybox_width;
Xstruct dayslot *slots;
Xstruct rect_limits boxlims[31];
Xstruct rect_limits mboxlims[12];
Xstruct week_arrow week_arrows[6];
Xstruct weekrect week_boxes[7];
XPixrect *ic_pr, *rev_ic_pr, *na_ic_pr;
XRect *closed_rect;
XRect ilabel_rect = {1, 67, 62, 9}; /* location of timestring under icon */
Xchar *mailto = NULL;
Xint one_based = 0, new_entry = 0, version2 = 0;
Xint otherfile = 0;
Xchar *othername;
Xint beep = 0, show_time = 0, beep_open = 0;
Xint include_old = 0, save_old = 0;
X#ifndef NO_HOLIDAYS
Xint holiday_a = 0, holiday_c = 0, holiday_i = 0;
Xint holiday_j = 0, holiday_s = 0;
X#endif
Xchar *progname;
Xstruct itimerval cal_timer;
Xint update_interval = 0;
Xint show_future = 1;
XIcon icon, rev_icon, na_icon;
XCursor year_cursor, month_cursor, week_cursor, day_cursor;
XCursor wait_cursor;
Xchar orig_apts_pathname[160];
Xint orig_ro;
Xchar timestr[16], datestr_day[3];
Xstruct pr_prpos where;
Xint working_msg;
Xint hour24, monday_first, day_first;
Xint expire_days = 0;
Xint start_hour, end_hour, num_notes;
Xint print_dev = PR_DEFAULT;
Xint user_font = 0;
Xint appt_check_limit, week_ofs = 0;
Xint locked = 0;
XSeln_client s_client;
X
Xstatic short icon_data[] = {
X#include "std.icon"
X};
Xstatic short rev_icon_data[] = {
X#include "rev.icon"
X};
Xstatic short na_icon_data[] = {
X#include "nap.icon"
X};
Xmpr_static(ic_mpr, 64, 64, 1, icon_data);
Xmpr_static(rev_ic_mpr, 64, 64, 1, rev_icon_data);
Xmpr_static(na_ic_mpr, 64, 64, 1, na_icon_data);
X
Xstatic short year_cursor_data[] = {
X#include "year.cursor"
X};
Xstatic short month_cursor_data[] = {
X#include "month.cursor"
X};
Xstatic short week_cursor_data[] = {
X#include "week.cursor"
X};
Xstatic short day_cursor_data[] = { 
X#include "day.cursor"
X};
Xstatic short wait_cursor_data[] = {
X#include <images/hglass.cursor>
X};
Xmpr_static(year_cursor_pr, 16, 16, 1, year_cursor_data);
Xmpr_static(month_cursor_pr, 16, 16, 1, month_cursor_data);
Xmpr_static(week_cursor_pr, 16, 16, 1, week_cursor_data);
Xmpr_static(day_cursor_pr, 16, 16, 1, day_cursor_data);
Xmpr_static(wait_cursor_pr, 16, 16, 1, wait_cursor_data);
X
X#ifndef NO_SUN_MOON
Xstatic short moon_icon_data[] = {
X#include "moony.icon"
X};
Xstatic short sun_icon_data[] = {
X#include "sunny.icon"
X};
Xmpr_static(moon_icon_pr, 64, 64, 1, moon_icon_data);
Xmpr_static(sun_icon_pr, 64, 64, 1, sun_icon_data);
X#endif
X
Xstatic short tri_up_data[] = {
X#include <images/tri_up.pr>
X};
Xmpr_static(tri_up_pr, 16, 16, 1, tri_up_data);
X
Xstatic short tri_right_data[] = {
X#include <images/tri_right.pr>
X};
Xmpr_static(tri_right_pr, 16, 16, 1, tri_right_data);
X
Xchar *strcpy(), *strcat(), *rindex();
Xstatic Notify_value itimer_handler();
XNotify_value leave();
XNotify_value myframe_interposer();
Xvoid sel_func_key_proc();
XSeln_result sel_reply_proc();
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X	int   flag, i;
X	int   printit = 0, toolrunning = 1, user_pos = 0;
X	int   printit_dst = 0, limitcheck = -1;
X	extern char *optarg;
X	
X	if (progname = rindex(*argv, '/'))
X		progname++;
X	else
X		progname = *argv;
X
X	/*
X	 * setup defaults which might be changed by command
X	 * line options
X	 */
X	nr_weekdays = NR_WEEKDAYS;
X	monday_first = MON_FIRST;
X	day_first = DAY_FIRST;
X	hour24 = HOUR_24;
X	start_hour = START_HOUR;
X	end_hour = END_HOUR;
X	num_notes = N_NOTESLOTS;
X
X	get_today();	/* initial day is today */
X	current = today;
X	read_only = 0;
X	working_msg = 0;
X
X	/*
X	 * Check for -p, -P, -m or -M option, which means we don't want
X	 * to create the tool. Also check for -Wp and -Wi so we can supply
X	 * a resonable default if the user doesn't supply one.
X	 */
X	i = argc;
X	while (--i > 0) {
X		if (!strncmp(argv[i], "-p", 2) || !strncmp(argv[i], "-m", 2)
X		    || !strncmp(argv[i], "-P", 2) || !strncmp(argv[i], "-M", 2)) {
X			/* standard getopt doesn't allow optional
X			 * arguments, so we do a check here for
X			 * obsolete usage of the -p option
X			 */
X			if ((argv[i][1] == 'p' || argv[i][1] == 'P') &&
X				argv[i][2] == '\0') {
X				fprintf(stderr, "calentool: -p/-P requires a 'd', 'w' or 'm' argument\n");
X				exit(1);
X			}
X			toolrunning = 0;
X		} else if (!strcmp(argv[i], "-Wp"))
X			user_pos = 1;
X		else if (!strcmp(argv[i], "-Wt"))
X			user_font = 1;
X	}
X	if (toolrunning) {
X		/* 
X		** Parse args: window_create must be called before getopt, so
X		** it can interpret and extract -W flags for SunView.
X		*/
X		frame = window_create(NULL, FRAME,
X				FRAME_ARGC_PTR_ARGV, &argc, argv,
X				WIN_ERROR_MSG,
X				"Can't create base frame. Are you in Suntools?",
X				0);
X		if ((s_client = seln_create(sel_func_key_proc, sel_reply_proc, NULL)) == NULL)
X			err_rpt("Can't create selection svc client", NON_FATAL);
X	}
X
X	while ((flag = getopt(argc, argv, "1:2:567bBd:eEf:il:m:M:op:P:rtu:zH:h:wx:T:s:S:N:")) != EOF)
X		switch (flag) {   
X		    case 'f':	/* use this file */
X			otherfile = 1;
X			othername = optarg;
X			break;
X			
X		    case 'd':	/* starting date */
X			(void)parse_date(optarg, TRUE);
X			break;
X
X		    case 'r':	/* read only file */
X			read_only = 1;
X			break;
X			
X		    case '1':	/* -12 -- 12 hour time */
X			if (*optarg == '2')
X				hour24 = FALSE;
X			break;
X
X		    case '2':	/* -24 -- 24 hour time */
X			if (*optarg == '4')
X				hour24 = TRUE;
X			break;
X
X		    case 'e':	/* European calendar Mon-Sun */
X			monday_first = TRUE;
X			break;
X
X		    case 'E':	/* European style */
X			monday_first = TRUE;
X			hour24 = TRUE;
X			day_first = TRUE;
X			break;
X
X		    case 'p':	/* print and exit */
X			read_only = 1;
X			switch (*optarg) {
X				case 'd':
X				case 'D':
X					printit = PRI_DAY;
X					break;
X				case 'w':
X					printit = PRI_WEEK;
X					week_ofs = 0;
X					break;
X				case 'W':
X					printit = PRI_WEEK;
X					week_ofs = 1;
X					break;
X				case 'm':
X				case 'M':
X					printit = PRI_MONTH;
X					break;
X				default:
X					fprintf(stderr, "calentool: unknown print option.", NON_FATAL);
X					break;
X			}
X			if (!printit_dst)
X				printit_dst = DST_STDOUT;
X			break;
X
X		    case 'P':	/* print and exit (ignore some notes) */
X			read_only = 1;
X			switch (*optarg) {
X				case 'd':
X				case 'D':
X					printit = PRI_DAY_XNOTES;
X					break;
X				case 'w':
X					printit = PRI_WEEK_XNOTES;
X					week_ofs = 0;
X					break;
X				case 'W':
X					printit = PRI_WEEK_XNOTES;
X					week_ofs = 1;
X					break;
X				case 'm':
X				case 'M':
X					printit = PRI_MONTH_XNOTES;
X					break;
X				default:
X					fprintf(stderr, "calentool: unknown print option.", NON_FATAL);
X					break;
X			}
X			if (!printit_dst)
X				printit_dst = DST_STDOUT;
X			break;
X
X		    case 'l':	/* appt check limit */
X			if (optarg)
X				limitcheck = atoi(optarg);
X			else
X				limitcheck = 0;
X			break;
X
X		    case 'm':	/* mail today's appts and exit */
X			read_only = 1;
X			if (!printit)
X				printit = PRI_DAY;
X			printit_dst = DST_MAIL;
X			mailto = optarg;
X			break;
X
X		    case 'M':	/* mail today's appts and exit (ignore
X				   some notes) */
X			read_only = 1;
X			if (!printit)
X				printit = PRI_DAY_XNOTES;
X			printit_dst = DST_MAIL;
X			mailto = optarg;
X			break;
X
X		    case 'b':	/* beep to console for pending appt */
X			beep = 1;
X			break;
X
X		    case 'B':	/* beep then open wondow */
X			beep_open = 1;
X			break;
X
X		    case 'i':	/* include old appt files */
X			include_old = 1;
X			break;
X
X		    case 'o':	/* save outdated appts to another file */
X			save_old = 1;
X			break;
X
X		    case 't':	/* display current time under icon */
X			show_time = 1;
X			break;
X
X		    case '5':	/* Mon - Fri week display */
X			nr_weekdays = 5;
X			break;
X
X		    case '6':	/* Mon - Sat week display */
X			nr_weekdays = 6;
X			break;
X
X		    case '7':	/* Sun - Sat (or Mon-Sun) week display */
X			nr_weekdays = 7;
X			break;
X
X		    case 'u':	/* update interval (in seconds) */
X			update_interval = atoi(optarg);
X			break;
X
X		    case 'w':	/* display Working! message */
X			working_msg = 1;
X			break;
X
X		    case 'x':	/* eXpire appts after so many days */
X			expire_days = atoi(optarg);
X			break;
X
X		    case 'z':	/* zero offset -- new style appts file */
X			one_based = 1;
X			break;
X
X#ifndef NO_HOLIDAYS
X		    case 'h':
X			/* show certain holidays */
X			switch (*optarg) {
X				case 'A':
X					/* all holidays */
X					holiday_a = holiday_c = holiday_i = 1;
X					holiday_j = holiday_s = 1;
X					break;
X				case 'a':
X					/* astronomical events */
X					holiday_a = 1;
X					break;
X				case 'c':
X					/* Christian holidays */
X					holiday_c = 1;
X					break;
X				case 'i':
X					/* Islamic holidays */
X					holiday_i = 1;
X					break;
X				case 'j':
X					/* Jewish holidays */
X					holiday_j = 1;
X					break;
X				case 's':
X					/* secular holidays */
X					holiday_s = 1;
X					break;
X				default:
X					fprintf(stderr, "calentool: unknown holiday option (must be one of \"Aacijs\")");
X					break;
X			}
X			break;
X
X		    case 'H':
X			/* show certain holidays day/week display only */
X			switch (*optarg) {
X				case 'A':
X					/* all holidays */
X					holiday_a = holiday_c = holiday_i = 2;
X					holiday_j = holiday_s = 2;
X					break;
X				case 'a':
X					/* astronomical events */
X					holiday_a = 2;
X					break;
X				case 'c':
X					/* Christian holidays */
X					holiday_c = 2;
X					break;
X				case 'i':
X					/* Islamic holidays */
X					holiday_i = 2;
X					break;
X				case 'j':
X					/* Jewish holidays */
X					holiday_j = 2;
X					break;
X				case 's':
X					/* secular holidays */
X					holiday_s = 2;
X					break;
X				default:
X					fprintf(stderr, "calentool: unknown holiday option (must be one of \"Aacijs\")");
X					break;
X			}
X			break;
X#else
X		    case 'h':
X		    case 'H':
X			    fprintf(stderr, "calentool: -h and -H options not available\n");
X			    break;
X#endif
X		    case 'T':
X			if (!strncmp(optarg, "ps", 2))
X				print_dev = PR_POSTSCRIPT;
X			else
X				print_dev = PR_ASCII;
X			break;
X
X		    case 's':	/* start hour */
X			start_hour = atoi(optarg);
X			if (start_hour < 0 || start_hour > 23)
X				fprintf(stderr, "calentool: start hour must be in the range 0 - 23\n");
X			break;
X
X		    case 'S':	/* Stop (end) hour */
X			end_hour = atoi(optarg);
X			if (end_hour < 1 || end_hour > 24)
X				fprintf(stderr, "calentool: end hour must be in the range 1 - 24\n");
X			break;
X
X		    case 'N':	/* number of note slots */
X			num_notes = atoi(optarg);
X			break;
X
X		    case '?':
X		    default:
X			fprintf(stderr, "usage: %s [options...]\nwhere <options> are:\n", progname);
X			fprintf(stderr, " -f <appointment_file>\n");
X			fprintf(stderr, " -r		// readonly\n");
X			fprintf(stderr, " -p <dwWm>	// print selected day [d], week [w] or month [m] appts and exit\n");
X			fprintf(stderr, " -P <dwWm>	// like -p, only don't print marked notes\n");
X			fprintf(stderr, " -m <user>	// mail selected days appts to <user>\n");
X			fprintf(stderr, " -M <user>	// like -m, only don't mail marked notes\n");
X			fprintf(stderr, " [-5|-6|-7]	// 5, 6 or 7-day week display\n");
X			fprintf(stderr, " [-12|-24]	// 12 or 24 hour time format\n");
X			fprintf(stderr, " -d <date>	// display appts for <date>\n");
X			fprintf(stderr, " -b		// beep and display message when appt is pending\n");
X			fprintf(stderr, " -B		// beep and open window when appt is pending\n");
X			fprintf(stderr, " -i		// auto-include outdated appts files\n");
X			fprintf(stderr, " -l [0|1]	// set limit check for appts to include notes\n");
X			fprintf(stderr, " -o		// create outdated include files\n");
X			fprintf(stderr, " -t		// display time below icon\n");
X			fprintf(stderr, " -u <interval>	// time update interval (seconds)\n");
X			fprintf(stderr, " -w		// display 'Working!' message\n");
X			fprintf(stderr, " -e		// Mon-Sun week display\n");
X			fprintf(stderr, " -E		// European options (-e, -24)\n");
X#ifndef NO_HOLIDAYS
X			fprintf(stderr, " -h <Aacijs>	// display selected computed holidays\n");
X			fprintf(stderr, "		// A=All, a=astronomical, c=Christian,\n");
X			fprintf(stderr, "		// i=Islamic, j=Jewish, s=secular\n");
X			fprintf(stderr, " -H <Aacijs>	// like -h, only flag them as marked\n");
X#endif
X			fprintf(stderr, " -T <printdev>	// printer output type for -p and -m options\n");
X			fprintf(stderr, " -s <hour>	// start hour\n");
X			fprintf(stderr, " -S <hour>	// stop hour\n");
X			fprintf(stderr, " -N <num>	// number of note slots\n");
X			fprintf(stderr, " -x <days>	// expire appts after <days> days\n");
X			fprintf(stderr, " -z		// conversion flag (see INSTALL)\n");
X			fprintf(stderr, " <window_opts>	// Suntools -W options\n");
X			exit(1);
X			break;
X		}
X
X	if (toolrunning) {
X		(void) notify_set_signal_func(frame, leave, SIGHUP, NOTIFY_ASYNC);
X		(void) notify_set_signal_func(frame, leave, SIGINT, NOTIFY_ASYNC);
X		(void) notify_set_signal_func(frame, leave, SIGTERM, NOTIFY_ASYNC);
X	}
X
X	/* set default printer to something useful */
X	if (print_dev == PR_DEFAULT) {
X		if (printit == PRI_MONTH || printit == PRI_MONTH_XNOTES)
X			print_dev = PR_POSTSCRIPT;
X		else
X			print_dev = PR_ASCII;
X	}
X	/*
X	** setup number of slots and allocate memory for them
X	*/
X	if ( start_hour >= end_hour )
X		err_rpt("Start Hour must be less than Stop Hour", FATAL);
X	n_tslots = (end_hour - start_hour) * 2;
X	n_slots = n_tslots + num_notes;
X	appt_check_limit = APPT_CHECK_LIMIT;  /* set default */
X	if (limitcheck == 0)
X		appt_check_limit = n_tslots;
X	else if (limitcheck == 1)
X		appt_check_limit = n_slots;
X	/* make room for n_slots dayslot entries and week entries */
X	if ((slots = (struct dayslot *)malloc(n_slots* sizeof(struct dayslot))) == NULL)
X		err_rpt("Can't get enough storage for day slots", FATAL);
X	for (i=0; i<nr_weekdays; i++)
X		if ((week_boxes[i].weekslots = 
X		    (struct dayslot *)malloc(n_slots*sizeof(struct dayslot))) == NULL)
X			err_rpt("Can't get enough storage for week slots", FATAL);
X
X	/* 
X	** Find the calendar file, etc.
X	*/
X	if (do_files(FALSE))
X		/* can't open (or create) appts file */
X		exit(1);
X	strcpy(orig_apts_pathname, apts_pathname);
X	orig_ro = read_only;
X	get_printer();
X	
X	if (printit) {
X		print_apts(printit, printit_dst);
X		if (save_old || expire_days) {
X			read_only = 0;  /* allow updates */
X			expire(expire_days);
X		}
X		exit(0);
X	}
X
X	get_fonts();
X
X	if (!update_interval) {
X		if (!strcmp(UPDATE_RATE, "minute"))
X			update_interval = 60;
X		else if (!strcmp(UPDATE_RATE, "second"))
X			update_interval = 1; /* check in 1 sec */
X		else {
X			err_rpt("unknown update interval, defaulting to seconds", NON_FATAL);
X			update_interval = 1; /* check in 1 sec */
X		}
X	}
X
X	/* create the cursors */
X	year_cursor = cursor_create(CURSOR_IMAGE, &year_cursor_pr,
X			CURSOR_XHOT, 7, CURSOR_YHOT, 7,
X			CURSOR_OP, PIX_SRC^PIX_DST,
X			0);
X	month_cursor = cursor_create(CURSOR_IMAGE, &month_cursor_pr,
X			CURSOR_XHOT, 7, CURSOR_YHOT, 7,
X			CURSOR_OP, PIX_SRC^PIX_DST,
X			0);
X	week_cursor = cursor_create(CURSOR_IMAGE, &week_cursor_pr,
X			CURSOR_XHOT, 7, CURSOR_YHOT, 7,
X			CURSOR_OP, PIX_SRC^PIX_DST,
X			0);
X	day_cursor = cursor_create(CURSOR_IMAGE, &day_cursor_pr,
X			CURSOR_XHOT, 7, CURSOR_YHOT, 7,
X			CURSOR_OP, PIX_SRC^PIX_DST,
X			0);
X	wait_cursor = cursor_create(CURSOR_IMAGE, &wait_cursor_pr,
X			CURSOR_XHOT, 7, CURSOR_YHOT, 7,
X			CURSOR_OP, PIX_SRC^PIX_DST,
X			0);
X
X	/* create the icons */
X	icon = icon_create(0);
X	rev_icon = icon_create(0);
X	na_icon = icon_create(0);
X	/*
X	 * make the icon big enough to display the time
X	 * underneath the base icon image if -t option
X	 */
X	ic_pr = mem_create(64, (show_time ? 77 : 64), 1);
X	rev_ic_pr = mem_create(64, (show_time ? 77 : 64), 1);
X	na_ic_pr = mem_create(64, (show_time ? 77 : 64), 1);
X	sprintf(datestr_day, "%2d", today.tm_mday);
X	/* the basic standard icon */
X	pr_rop(ic_pr, 0, 0, 64, 64, PIX_SRC, &ic_mpr, 0, 0);
X	if (show_time) {
X		/* time string displayed below icon */
X		format_icon_time();
X		if (update_interval >= 60) {
X			/* no seconds displayed, adjust label position */
X			ilabel_rect.r_left += 2*sfont->pf_defaultsize.x - 2;
X			ilabel_rect.r_width -= 2*sfont->pf_defaultsize.x;
X		}
X		if (!hour24) {
X			/* am/pm, increase size of label rect */
X			ilabel_rect.r_left -= sfont->pf_defaultsize.x;
X			ilabel_rect.r_width += sfont->pf_defaultsize.x;
X		}
X		/* draw a box around the time appendage */
X		pr_vector(ic_pr, 0, 64, 63, 64, PIX_SET, 1);
X		pr_vector(ic_pr, 63, 64, 63, 76, PIX_SET, 1);
X		pr_vector(ic_pr, 63, 76, 0, 76, PIX_SET, 1);
X		pr_vector(ic_pr, 0, 76, 0, 64, PIX_SET, 1);
X
X		pr_rop(rev_ic_pr, 0, 0, 64, 77, PIX_SRC, ic_pr, 0, 0);
X		pr_rop(rev_ic_pr, 0, 0, 64, 64, PIX_SRC, &rev_ic_mpr, 0, 0);
X		pr_rop(na_ic_pr, 0, 0, 64, 77, PIX_SRC, ic_pr, 0, 0);
X		pr_rop(na_ic_pr, 0, 0, 64, 64, PIX_SRC, &na_ic_mpr, 0, 0);
X	} else {
X		pr_rop(rev_ic_pr, 0, 0, 64, 64, PIX_SRC, &rev_ic_mpr, 0, 0);
X		pr_rop(na_ic_pr, 0, 0, 64, 64, PIX_SRC, &na_ic_mpr, 0, 0);
X	}
X	/* add in the date */
X	where.pr = ic_pr;
X	where.pos.x = 13;
X	where.pos.y = 49 + 5;
X	pf_ttext(where, PIX_NOT(PIX_SRC), sfont, datestr_day);
X	where.pr = na_ic_pr;
X	pf_ttext(where, PIX_NOT(PIX_SRC), sfont, datestr_day);
X	where.pr = rev_ic_pr;
X	pf_ttext(where, PIX_SRC, sfont, datestr_day);
X	where.pos.x = 39;
X	where.pr = ic_pr;
X	pf_ttext(where, PIX_NOT(PIX_SRC), sfont, smonthnames[today.tm_mon]);
X	where.pr = na_ic_pr;
X	pf_ttext(where, PIX_NOT(PIX_SRC), sfont, smonthnames[today.tm_mon]);
X	where.pr = rev_ic_pr;
X	pf_ttext(where, PIX_SRC, sfont, smonthnames[today.tm_mon]);
X	(void)icon_init_from_pr(icon, ic_pr);
X	(void)icon_init_from_pr(rev_icon, rev_ic_pr);
X	(void)icon_init_from_pr(na_icon, na_ic_pr);
X	if (show_time) {
X		/* add label field */
X		icon_set(icon, ICON_LABEL_RECT, &ilabel_rect,
X				ICON_LABEL, timestr,
X				ICON_FONT, sfont,
X				0);
X		icon_set(rev_icon, ICON_LABEL_RECT, &ilabel_rect,
X				ICON_LABEL, timestr,
X				ICON_FONT, sfont,
X				0);
X		icon_set(na_icon, ICON_LABEL_RECT, &ilabel_rect,
X				ICON_LABEL, timestr,
X				ICON_FONT, sfont,
X				0);
X	}
X
X	week_message_size = WEEK_MESSAGE_SIZE;
X	weekslot_width = (font->pf_defaultsize.x * week_message_size) + 6;
X	dayslot_width = (nr_weekdays - 1) * weekslot_width;
X	day_message_size = dayslot_width / font->pf_defaultsize.x;
X	ybox_height = dayslot_height = weekslot_height = font->pf_defaultsize.y + 6;
X	ybox_width = 3 * font->pf_defaultsize.x;
X
X	init_pixrects();	/* Initialize pictures. */
X
X	/* Create the rest of the tool */
X	if (user_pos)
X		window_set(frame,
X			 WIN_WIDTH, (nr_weekdays+1)*weekslot_width+40,
X			 WIN_HEIGHT, (n_slots+5)*weekslot_height+72,
X			 FRAME_ICON, icon,
X			 0);
X	else
X		/* supply default position */
X		window_set(frame,
X			 WIN_WIDTH, (nr_weekdays+1)*weekslot_width+40,
X			 WIN_HEIGHT, (n_slots+5)*weekslot_height+72,
X			 WIN_X, 100, WIN_Y, 56,
X			 FRAME_ICON, icon,
X			 0);
X
X	closed_rect = (Rect *)window_get(frame, FRAME_CLOSED_RECT);
X	create_panel();
X	create_main_window();
X	create_attr_frame();	/* normally hidden */
X	create_del_frame();	/* normally hidden */
X	create_file_frame();	/* normally hidden */
X	create_date_frame();	/* normally hidden */
X#ifndef NO_PRINTER
X	create_print_frame();	/* normally hidden */
X#endif
X	check_calendar();	/* set proper icon */
X
X	/* interpose on frame events (specifically open/close) */
X	(void) notify_interpose_event_func(frame, myframe_interposer, NOTIFY_SAFE);
X
X	timerclear(&(cal_timer.it_interval));
X	timerclear(&(cal_timer.it_value));
X	cal_timer.it_value.tv_sec = (long)update_interval;
X	cal_timer.it_value.tv_usec = 0L;
X	(void) notify_set_itimer_func(frame, itimer_handler, ITIMER_REAL, &cal_timer, NULL);
X
X	window_main_loop(frame);
X
X	(void)leave();
X}            
X             
X
Xstatic Notify_value
Xitimer_handler(me, which)
XNotify_client me;
Xint which;
X{
X	static int timeout_cntr = 0;
X	Icon cur_icon;
X
X	timerclear(&(cal_timer.it_interval));
X	timerclear(&(cal_timer.it_value));
X	cal_timer.it_value.tv_sec = (long)update_interval;
X	cal_timer.it_value.tv_usec = 0L;
X	(void) notify_set_itimer_func(frame, itimer_handler, ITIMER_REAL, &cal_timer, NULL);
X	get_today();
X	/* update date/time displayed in the panel */
X	panel_set(clock_pi, PANEL_LABEL_STRING, clockstr, 0);
X	if (show_time && (int)window_get(frame, FRAME_CLOSED))
X		update_icon_time();
X#ifndef NO_SUN_MOON
X	/* update data in Sun Data frame (if it's open) */
X	if (sframe)
X		write_times();
X#endif
X	/* only check appointments every TIME_OUT minutes */
X	timeout_cntr += update_interval;
X#undef TIME_OUT
X#define TIME_OUT 1
X	if (timeout_cntr/60 < TIME_OUT)
X		return(NOTIFY_DONE);
X	timeout_cntr = 0;
X	/* check todays appointments */
X	check_calendar();
X	return(NOTIFY_DONE);
X}
X
XNotify_value
Xleave(me, signal, when)
XNotify_client me;
Xint signal;
XNotify_signal_mode when;
X{
X	if (day_is_open)
X		close_day();
X
X	/* create outdated include files (if necessary) */
X	if (save_old || expire_days)
X		expire(expire_days);
X
X	/* delete tmp file */
X	if (access(tmpapts_pathname, R_OK) == 0 && unlink(tmpapts_pathname) < 0)
X		perror(tmpapts_pathname);
X
X	seln_destroy(s_client);
X	exit(0);
X	/* NOTREACHED */
X	return(NOTIFY_DONE);
X}
END_OF_FILE
if test 22129 -ne `wc -c <'calentool.c'`; then
    echo shar: \"'calentool.c'\" unpacked with wrong size!
fi
# end of 'calentool.c'
fi
if test -f 'common.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'common.c'\"
else
echo shar: Extracting \"'common.c'\" \(29124 characters\)
sed "s/^X//" >'common.c' <<'END_OF_FILE'
X/*
X * $Header: common.c,v 1.2 91/03/27 16:45:05 billr Exp $
X */
X/*
X * common.c
X *
X * Author: by: Bill Randle, Tektronix, Inc. <billr@saab.CNA.TEK.COM>
X *
X * Copyright (C) 1988, 1989, 1991 Tektronix, Inc.
X *	All Rights Reserved
X * Permission is hereby granted to use and modify the modifications in source
X * or binary form as long as they are not sold for profit and this copyright
X * notice remains intact.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/time.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#ifndef CALENCHECK
X# include <suntool/sunview.h>
X# include <pwd.h>
X#endif
X#ifndef NO_DEFAULTS
X# include <sunwindow/defaults.h>
X#endif
X#include "ct.h"
X
Xstruct tm save_day;
Xstruct appt_entry future[MAX_FUTURE_ENTRIES];
Xint findex = 0;         /* index into struct future array */;
Xint day_is_open = 0;	/* indicates when slot info is current */
Xchar apts_pathname[160], tmpapts_pathname[160];
Xchar apts_dir[128], lib_dir[128];
Xchar t_title[160];
X
Xextern struct tm current, today, First;
Xextern struct dayslot *slots;
Xextern char apts_pathname[], tmpapts_pathname[];
Xextern char *progname;
Xextern char *othername;
Xextern int n_slots, n_tslots, read_only, new_entry;
Xextern int otherfile, one_based, version2;
Xextern int show_future, start_hour;
Xextern int mainsw_state;
X
X#ifndef CALENCHECK
Xextern struct tm olddate, closedate;
Xextern Frame frame, prompt_frame;
Xextern Icon icon, rev_icon, na_icon;
Xextern char datestr_day[];
Xextern struct pr_prpos where;
Xextern Pixrect *ic_pr, *rev_ic_pr, *na_ic_pr;
Xextern int beep, beep_open;
Xextern int appt_check_limit;
Xextern Pixfont *sfont;
Xextern char *smonthnames[];
Xextern char *mailto;
Xextern int locked;
Xextern char *version();
X#endif
X
X#ifndef NO_HOLIDAYS
Xextern int holiday_a, holiday_c, holiday_i, holiday_j, holiday_s;
X
Xextern int a_dates(), c_dates(), i_dates(), j_dates(), s_dates();
Xextern struct appt_entry a_appts[], c_appts[];
Xextern struct appt_entry i_appts[], j_appts[];
Xextern struct appt_entry s_appts[];
X#endif
Xextern char *strcpy(), *strncpy(), *index();
X
X/*
X * Add an appointment entry pointed to by aptr to the day slot
X * specified by slotno. This routine is also used by paste()
X * when copying an entry off the save shelf. If dpyflag is true,
X * then any deactivated slots are cleared on the display (used by
X * paste). Also used to add a deleted entry for a specific day.
X */
Xadd_to_slot(slotno, aptr, dpyflag)
Xint slotno;
Xstruct appt_entry *aptr;
Xint dpyflag;
X{
X	struct appt_entry *nappt, *optr;
X	int n_arrows, n, nbi, found = 0, i;
X
X	if ((nappt = (struct appt_entry *)malloc(sizeof(struct appt_entry))) == NULL)
X		err_rpt("out of memory", FATAL);
X	if (aptr == NULL) {
X		/* fill in some needed fields */
X		nappt->arrows = nappt->flags = 0;
X		nappt->sindex = 0;
X		nappt->str[0] = '\0';
X	} else
X		*nappt = *aptr;
X	nappt->next = NULL;
X	/* add appt to list of appts for this slot */
X	if (slots[slotno].first == NULL) {
X		slots[slotno].first = nappt;
X		slots[slotno].cur_appt = nappt;
X	} else {
X		/* search for end of list */
X		for (optr=slots[slotno].first;optr->next;optr=optr->next)
X			;
X		optr->next = nappt;
X	}
X	/* make sure it doesn't extend too far and truncate if neccessary */
X	if (slotno >= n_tslots)
X		nappt->arrows = 0;	/* force notes to have no arrows */
X	else if ((slotno + nappt->arrows) >= n_tslots)
X		nappt->arrows = n_tslots - slotno - 1;	/* truncate */
X	n_arrows = nappt->arrows;
X	if (nappt->flags & DELETED) {
X		/* look for matching non-deleted appt in list */
X		for (optr=slots[slotno].first;optr && !found;optr=optr->next)
X			if (!strcmp(nappt->str, optr->str)
X			    && !(optr->flags & DELETED)
X			    && Repeating(optr->flags)) {
X				found = 1;
X				break;
X			}
X		if (found && slots[slotno].cur_appt == optr) {
X			/* the deleted appt is the current one */
X			/* if it's active, undisplay it and display
X			 * next one in list (if any)
X			 */
X			if (slots[slotno].active) {
X				if (slots[slotno].active > 1)
X					/* there's another one here */
X					next_appt(slotno, dpyflag);
X				else
X					deactivate_slot(slotno, dpyflag);
X				/* adjust reference counts */
X				slots[slotno].count--;
X				slots[slotno].active--;
X				i = 0;
X				while (++i <= n_arrows)
X					--slots[slotno+i].count;
X			} else {
X				/* currently inactive */
X				/* set current to next one in the list */
X				if (optr->next)
X					slots[slotno].cur_appt = optr->next;
X				else
X					slots[slotno].cur_appt = slots[slotno].first;
X				/* adjust the counts */
X				slots[slotno].active--;
X				while (n_arrows >= 0)
X					slots[slotno+(n_arrows--)].count--;
X			}
X		} else {
X			/* just adjust the counts */
X			slots[slotno].active--;
X			while (n_arrows >= 0)
X				slots[slotno+(n_arrows--)].count--;
X		}
X	} else {
X		/* look for matching deleted appt in list */
X		if (Repeating(nappt->flags)) {
X			for (optr=slots[slotno].first;optr && !found;optr=optr->next)
X				if (!strcmp(nappt->str, optr->str) && optr->flags & DELETED) {
X					found = 1;
X					break;
X				}
X			if (found) {
X				/* just adjust reference counts and return */
X				slots[slotno].active++;
X				while (n_arrows >= 0)
X					slots[slotno+(n_arrows--)].count++;
X				return;
X			}
X		}
X		/*
X		 * Make sure there are no overlaps with the appt we
X		 * are adding. If there are, hide the overlapping appt.
X		 */
X		if (slots[slotno].active)
X			deactivate_slot(slotno, dpyflag);
X		/* set current one to the new one */
X		slots[slotno].cur_appt = nappt;
X		/* now go back and put in the info for the appt we're inserting */
X		slots[slotno].count++;
X		slots[slotno].active++;
X		if (n_arrows > 0) {
X			slots[slotno+n_arrows].count++;
X			while (--n_arrows > 0)
X				slots[slotno+n_arrows].count++;
X		}
X	}
X	if (dpyflag)
X		draw_day_appts();	/* redraw display */
X}
X
X/* add a note to the current day */
Xadd_note(appt)
Xstruct appt_entry *appt;
X{
X	int	slotno, found = 0;
X	struct appt_entry *optr;
X
X	/* This used to just find a free slot and add the note
X	 * to it. However, with deleted notes we need to find
X	 * the matching slotno (if it exists) to make sure that
X	 * the deleted and non-deleted notes end up in the same
X	 * slot number (so they won't be displayed).
X	 */
X	if (appt->flags & DELETED) {
X		/* look for matching non-deleted note */
X		for (slotno=n_tslots; slotno<n_slots && !found; slotno++) {
X			if (!slots[slotno].active)
X				break;	/* no more notes */
X			for (optr=slots[slotno].first;optr;optr=optr->next) {
X				if (!strcmp(appt->str, optr->str)
X				    && !(optr->flags & DELETED)
X				    && Repeating(optr->flags)) {
X					found = 1;
X					break;
X				}
X			}
X		}
X	} else {
X		/* look for free slot and/or matching deleted note */
X		for (slotno=n_tslots; slotno<n_slots && !found; slotno++) {
X			if (!slots[slotno].active)
X				break;	/* no more notes */
X			for (optr=slots[slotno].first;optr;optr=optr->next)
X				if (!strcmp(appt->str, optr->str)
X				    && (optr->flags & DELETED)
X				    && Repeating(appt->flags)) {
X					found = 1;
X					break;
X				}
X		}
X	}
X	if (found)
X		--slotno;  /* for loop incremented slotno */
X	if (slotno == n_slots) {
X		/* overflow of notes field, so
X		 * add to last note field list
X		 */
X		slotno = n_slots - 1;
X	}
X	add_to_slot(slotno, appt, FALSE);
X}
X
X/*
X * Fills in appointments for the day.  
X * The ".tmp.aptsXXXXX" file is filled out
X * with all the lines from the ".appointments" file 
X * which do not pertain to the current day.
X */
Xint
Xget_day_appts()
X{
X	FILE *apts, *temp_apts;
X	int slotno, n_arrows, i, j, k;
X	int read_stat, some_appt = 0;
X	int runl;
X	struct appt_entry appt;
X	struct appt_entry *nappt, *aptr;
X	char buf[MAX_STRLEN], *sptr;
X
X	if ((apts = fopen(apts_pathname, "r")) == NULL)
X		err_rpt("can't open appointments file", FATAL);
X
X	if (!read_only)
X	        if ((temp_apts = fopen(tmpapts_pathname, "w")) == NULL)
X			err_rpt("can't open temp file for writing", FATAL);
X
X	for (i=0; i<n_slots; i++) {	/* init each slot */
X		slots[i].count = 0;
X		slots[i].cur_appt = NULL;
X		slots[i].first = NULL;
X		slots[i].active = 0;
X	}
X	First = current;
X	findex = 0;
X
X#ifndef NO_HOLIDAYS
X	/*
X	 * First check to see if the user has selected any holiday
X	 * options and add them in.
X	 */
X	working(TRUE);
X	if (holiday_a) {
X		j = a_dates(holiday_a);
X		for (k=0; k<j; k++)
X			if (ymd2_compare(&current, &a_appts[k]) == 0) {
X				some_appt |= (holiday_a == 1 ? SOME_MKNOTES : SOME_NOTES);
X				add_note(&a_appts[k]);
X			}
X	}
X	working(FALSE);
X	if (holiday_c) {
X		j = c_dates(holiday_c);
X		for (k=0; k<j; k++)
X			if (ymd2_compare(&current, &c_appts[k]) == 0) {
X				some_appt |= (holiday_c == 1 ? SOME_MKNOTES : SOME_NOTES);
X				add_note(&c_appts[k]);
X			}
X	}
X	working(TRUE);
X	if (holiday_i) {
X		j = i_dates(holiday_i);
X		for (k=0; k<j; k++)
X			if (ymd2_compare(&current, &i_appts[k]) == 0) {
X				some_appt |= (holiday_i == 1 ? SOME_MKNOTES : SOME_NOTES);
X				/* look for \n */
X				if ((sptr = index(i_appts[k].str, '\n')) != NULL) {
X					appt = i_appts[k];
X					/* two notes in one */
X					strcpy(buf, appt.str);
X					*sptr = '\0';
X					add_note(&appt);
X					/* now second half of string in the next note */
X					strcpy(appt.str, &buf[(int)(sptr-appt.str)+1]);
X					add_note(&appt);
X				} else
X					add_note(&i_appts[k]);
X			}
X	}
X	working(FALSE);
X	if (holiday_j) {
X		j = j_dates(holiday_j);
X		for (k=0; k<j; k++)
X			if (ymd2_compare(&current, &j_appts[k]) == 0) {
X				some_appt |= (holiday_j == 1 ? SOME_MKNOTES : SOME_NOTES);
X				add_note(&j_appts[k]);
X			}
X	}
X	working(TRUE);
X	if (holiday_s) {
X		j = s_dates(holiday_s);
X		for (k=0; k<j; k++)
X			if (ymd2_compare(&current, &s_appts[k]) == 0) {
X				some_appt |= (holiday_s == 1 ? SOME_MKNOTES : SOME_NOTES);
X				add_note(&s_appts[k]);
X			}
X	}
X	working(FALSE);
X#endif
X			
X	/*
X	 * now go thru the appointments file
X	 */
X	while ((read_stat=get_aentry(apts, &appt, FALSE, FALSE, First.tm_mon+1)) != EOF) {
X		if (read_stat)
X			continue;	/* read error (ignore) */
X		if (appt.flags & A_COMMENT) {
X			if (put_aentry(temp_apts, &appt)) {
X				/* write error */
X				break;
X			}
X			continue;
X		}
X		current.tm_year = appt.year;
X		current.tm_mon = appt.month;
X		current.tm_mday = appt.day;
X		if (appt.flags & ALL_YEARS)
X			current.tm_year = First.tm_year;
X		if (appt.flags & ALL_MONTHS)
X			current.tm_mon = First.tm_mon;
X		if (appt.flags & ALL_DAYS)
X			current.tm_mday = First.tm_mday;
X		else if (appt.flags & EVERY_MON_FRI) {
X			if (First.tm_wday >= MON && First.tm_wday <= FRI)
X				current.tm_mday = First.tm_mday;
X			else
X				current.tm_mday = 0;
X		} else if (appt.flags & EVERY_SOMEDAY) {
X			if ((Pickday(appt.flags) == First.tm_wday)
X			    && (chk_week(appt.repeat, First.tm_mday))) {
X				if (appt.flags & RUN) {
X					runl = appt.runlength;
X					find_date(&appt);
X					while (ymd_compare(current, First) < 0 && --runl) {
X						current.tm_mday += 7;
X						find_date(&appt);
X					}
X				} else
X					current.tm_mday = First.tm_mday;
X			} else
X				current.tm_mday = 0;
X		} else if (appt.flags & REPEAT) {
X			if (appt.flags & RUN)
X				runl = appt.runlength;
X			else
X				runl = 1;
X			while (ymd_compare(current, First) < 0 && runl) {
X				if (appt.flags & RUN)
X					--runl;
X				if (runl) {
X					current.tm_mday += appt.repeat;
X					fix_current_day();
X				}
X			}
X		}
X		if (ymd_compare(current, First) == 0) {
X			/* if it's for this day, fill in slot info */
X			if (appt.flags & A_NOTE) {
X				/* notes section */
X				add_note(&appt);
X				if (appt.flags & MARKED)
X					/* marked note */
X					some_appt |= SOME_MKNOTES;
X				else
X					/* regular note */
X					some_appt |= SOME_NOTES;
X			} else {
X				/* regular appointment */
X				slotno = (appt.hour-start_hour) * 2 + appt.minute / 30;
X				if (slotno < 0)
X					slotno = 0;
X				if (slotno >= n_tslots)
X					slotno = n_tslots - 1;
X				/* add this appt to the list of appts for the slot */
X				/* and update all the reference counts */
X				add_to_slot(slotno, &appt, FALSE);
X				some_appt |= SOME_APPTS;
X			}
X		} else if (appt.flags & LOOKAHEAD) {
X			/* This lookahead appt was not for today, so
X			 * put it in the temp file.
X			 */
X			if (put_aentry(temp_apts, &appt)) {
X				/* write error */
X				break;
X			}
X			if (appt.flags & EVERY_SOMEDAY) {
X				/* find next occurance of this appt */
X				/* starting from the current day */
X				current.tm_mday = First.tm_mday;
X				fix_current_day();
X				find_date(&appt); /* may modify current */
X			}
X			if (ymd_compare(current, First) > 0) {
X				/* this appt is happening in
X				 * the future, so remind us of it if
X				 * it is within the lookahead window.
X				 */
X				save_day = current;
X				current.tm_mday -= appt.lookahead;
X				fix_current_day();
X				if (ymd_compare(current, First) <=0) {
X					/* save this one for the future popup window */
X					if (findex > MAX_FUTURE_ENTRIES-1) {
X						err_rpt("Too many future reminders", NON_FATAL);
X						continue;
X					}
X					future[findex] = appt;
X					/* fix up ymd */
X					future[findex].year = save_day.tm_year;
X					future[findex].month = save_day.tm_mon;
X					future[findex].day = save_day.tm_mday;
X					++findex;
X					some_appt |= SOME_FUTURES;
X				}
X			}
X                } else { 	/* line is not for today */
X			/* copy it to temp file */
X			if (put_aentry(temp_apts, &appt)) {
X				/* write error */
X				break;
X			}
X		}
X        }
X	if (!read_only) {
X		if (ferror(temp_apts))
X			err_rpt("write on temp file failed", FATAL);
X        	fclose(temp_apts);        
X	}
X        fclose(apts);             
X	current = First;
X	fix_current_day();
X	orphan_check();
X
X	return(some_appt);
X}
X
X
X/* check for match on weekly re-ocurring appts */
Xchk_week(repeat, curday)
Xint repeat, curday;
X{
X	int weeknr = 0;
X
X	if ((repeat & ALL_WEEKS) == ALL_WEEKS)
X		return(1);	/* every week */
X	if ((repeat & LAST_WEEK) && ((curday+7) > monthlength(current.tm_mon)))
X		return(1);	/* last week in month */
X
X	while (curday > 7) {
X		/* find which week this day is in */
X		curday -= 7;
X		weeknr++;
X	}
X	if (repeat & (0x1<<weeknr))
X		return(1);
X	
X	return(0);	/* no match */
X}
X
X
X/*
X * get date of next occurrance of a weekly repeated appt
X * (it may bridge into next week, month or year)
X */
Xfind_date(appt)
Xstruct appt_entry *appt;
X{
X	struct tm save;
X	int runl;
X
X	save = current;
X	fix_current_day();
X	/* set current to match dow of repeated appt */
X	if (appt->flags & EVERY_MON_FRI) {
X		if (current.tm_wday == SUN)
X			current.tm_mday++;
X		else if (current.tm_wday == SAT)
X			current.tm_mday += 2;
X	} else 
X		current.tm_mday += Pickday(appt->flags) - current.tm_wday;
X	fix_current_day();
X	if (ymd_compare(current, save) < 0) {
X		/* already happened, so start looking next week */
X		current.tm_mday += 7;
X		fix_current_day();
X	}
X	/* search for first matching week */
X	while (!chk_week(appt->repeat, current.tm_mday)) {
X		current.tm_mday += 7;
X		fix_current_day();
X	}
X	/* now check to make sure this is legal, i.e. there
X	 * were no month or year restrictions
X	 */
X	if (!(appt->flags & RUN) && ((!(appt->flags & ALL_YEARS) && current.tm_year != save.tm_year)
X	   || (!(appt->flags & ALL_MONTHS) && current.tm_mon != save.tm_mon)))
X		/* invalid date, due to month or year wrap */
X		current = save;
X}
X
X/*
X * orphan_check() - check each slot for orphan appointments. Orphans
X * are a deleted recurring appointment where it is deleted on a
X * specific date and the original appointment no longer exists.
X */
Xorphan_check()
X{
X	int i, n_arrows;
X	struct appt_entry *aptr, *optr;
X
X	for (i=0; i<n_slots; i++) {
X		while (slots[i].first != NULL) {
X			optr = slots[i].first;
X			if (chk_deleted(&slots[i], optr) == -1) {
X				/* just adjust the counts */
X				n_arrows = optr->arrows;
X				while (n_arrows >= 0)
X					slots[i+(n_arrows--)].count++;
X				slots[i].active++;
X				if (optr->next) {
X					if (slots[i].cur_appt == slots[i].first)
X						slots[i].cur_appt = slots[i].first->next;
X					slots[i].first = slots[i].first->next;
X				} else {
X					/* last one */
X					if (slots[i].cur_appt == slots[i].first)
X						slots[i].cur_appt = NULL;
X					slots[i].first = NULL;
X					slots[i].active = 0;
X				}
X				free(optr);
X				new_entry = 1;
X			} else {
X				while (aptr = optr->next) {
X					if (chk_deleted(&slots[i], aptr) == -1) {
X						/* just adjust the counts */
X						n_arrows = aptr->arrows;
X						while (n_arrows >= 0)
X							slots[i+(n_arrows--)].count++;
X						slots[i].active++;
X						optr->next = aptr->next;
X						if (slots[i].cur_appt == aptr)
X							slots[i].cur_appt = aptr->next;
X						free(aptr);
X						new_entry = 1;
X					} else {
X						optr = aptr;
X					}
X				}
X				break;
X			}
X		}
X		if (chk_deleted(&slots[i], slots[i].cur_appt)) {
X			next_appt(i, FALSE);
X			if (slots[i].cur_appt == NULL)
X				/* only deleted appts, so reset to first */
X				slots[i].cur_appt = slots[i].first;
X		}
X	}
X}
X
X/* check to see if appt is deleted */
Xint
Xchk_deleted(slptr, aptr)
Xstruct dayslot *slptr;
Xstruct appt_entry *aptr;
X{
X	int found = 0;
X	struct appt_entry *optr;
X
X	if (slptr->first == NULL || aptr == NULL)
X		return(0);
X	if (aptr->flags & DELETED) {
X		/* run through the list and look for a matching
X		 * repeating non-deleted entry. If we don't find one,
X		 * this appt is a deleted orphan.
X		 */
X		for (optr=slptr->first; optr; optr=optr->next)
X			if (!(optr->flags & DELETED) && Repeating(optr->flags))
X				/* now see if the current one matches */
X				if (!strcmp(optr->str, aptr->str))
X					return(1);
X		return(-1);  /* orphan */
X	}
X	if (Repeating(aptr->flags)) {
X		/* run through the list to see if there are any deleted */
X		for (optr=slptr->first; optr; optr=optr->next)
X			if (optr->flags & DELETED) {
X				/* now see if the current one matches */
X				if (!strcmp(optr->str, aptr->str))
X					return(1);
X			}
X	}
X	
X	return(0);
X}
X
X/*
X * When timer has expired check to see if we are close to an
X * appointment. If so, switch to the other icon so we have a
X * visual indication and beep the console (if enabled).
X */
Xcheck_calendar()
X{
X	int appt_pending = 0; 	/* no appointments pending */
X	int slotno = 0; 	/* start with first timeslot */
X	int smin, tmin;
X	int sno, save_ro;
X	static int echoed_sno = -1;
X	static int new_day = 0;
X	FILE *console;
X	struct appt_entry *aptr;
X	struct tm Saveday;
X	char *getenv();
X	static time_t lastmod = (time_t)0;
X	struct stat stbuf;
X#ifndef CALENCHECK
X	char msgfile[128];
X	int some_appts = 0;	/* no appointments today */
X	static int icon_in_use = STD_ICON;
X	Icon cur_icon;
X	FILE *msgf;
X#endif
X
X#ifndef CALENCHECK
X	if (locked)	/* can't change yet */
X		return;
X	lock_cursors();
X	locked++;	/* make it unique to us */
X	strcpy(msgfile, getenv("HOME"));
X	strcat(msgfile, "/.msgfile");
X#endif
X
X	sno = echoed_sno;	/* assume no console echo */
X	get_today();
X	stat(apts_pathname, &stbuf);
X	Saveday = current;
X	/*
X	 * Check to see if we're not displaying today.
X	 * (Or in the case of "calencheck" if the appointments
X	 * file has been modified.) If so, we need to update our
X	 * slot information.
X	 */
X#ifndef CALENCHECK
X	if ((int)window_get(frame, FRAME_CLOSED) && ymd_compare(closedate, today) != 0)
X		new_day++;
X#endif
X	if (ymd_compare(current, today) != 0 || new_day) {
X		if (day_is_open)
X			close_day();
X		current = today;
X#ifdef CALENCHECK
X		new_day++;
X#endif
X	}
X	if (stbuf.st_mtime > lastmod) {
X		lastmod = stbuf.st_mtime;
X		day_is_open = FALSE;	/* force reading appts file */
X		sno = echoed_sno = -1;
X	}
X	if (day_is_open) {
X		/* slot info is current */
X#ifndef CALENCHECK
X		unlock_cursors();
X#endif
X	} else {
X#ifdef CALENCHECK
X		(void)get_day_appts();
X#else
X		save_ro = read_only;
X		read_only = 1;	/* force read only mode */
X		err2console(TRUE);
X		(void)get_day_appts();
X		err2console(FALSE);
X#endif
X		day_is_open = TRUE;
X		read_only = save_ro;
X		if (new_day)
X			sno = echoed_sno = -1;
X	}
X
X	slotno = (today.tm_hour - start_hour)*2 + today.tm_min/30;
X	if (slotno < 0)
X		slotno = 0;
X	/* our current time (minutes past midnight) */
X	tmin = today.tm_hour * 60 + today.tm_min;
X	if (slots[slotno].count > 0 && slotno < n_tslots)
X		/* something going on now */
X		appt_pending++;
X	while (slotno < n_tslots) {
X		if (slots[slotno].count > 0) {
X			/* convert slotno back to time difference */
X			smin = start_hour * 60 + slotno * 30 - tmin;
X			if (smin < 0)
X				smin = 0;
X			if (slots[slotno].active) {
X				/* get all valid appts at this time */
X				for (aptr=slots[slotno].first; aptr;
X				    aptr=aptr->next)
X					if (!chk_deleted(&slots[slotno], aptr)
X					    && aptr->warn
X					    && (smin <= aptr->warn)) {
X						sno = slotno;
X						appt_pending++;
X						break;
X					}
X				if (sno > echoed_sno)
X					break;
X			}
X		}
X		slotno++;
X	}
X	if (!appt_pending) {
X#ifndef CALENCHECK
X		/*
X		 * Is there anything happening today (optionally
X		 * including memos)?
X		 * Don't care about things that happened before now
X		 * so start looking at <slotno>, which is set to
X		 * reflect the current hour (or 0 if before start_hour).
X		 */
X		slotno = (today.tm_hour - start_hour)*2 + today.tm_min/30;
X		if (slotno < 0)
X			slotno = 0;
X		/*
X		 * appt_check_limit is typically either "n_tslots"
X		 * or "n_slots" depending on whether we include the
X		 * notes section when indicating that we still have
X		 * appts today.
X		 */
X		while (slotno < appt_check_limit)
X			if (slots[slotno++].count) {
X				some_appts++;
X				break;
X			}
X		/* maybe change the icon */
X		if ((some_appts && (icon_in_use != STD_ICON)) ||
X		   (!some_appts && (icon_in_use != NA_ICON))) {
X			window_set(frame,
X			    FRAME_ICON, (some_appts?
X				icon : na_icon),
X			    0);
X			icon_in_use = some_appts ? STD_ICON : NA_ICON;
X		}
X		/* clean out the ~/.msgfile file */
X		if (beep && ((msgf = fopen(msgfile, "w")) != NULL)) {
X			fprintf(msgf, "I'm out running around.");
X			fclose(msgf);
X		}
X#endif
X	} else {
X 		/* notify the user via the console (once) ... */
X#ifdef CALENCHECK
X		if (sno > echoed_sno) {
X			echoed_sno = sno;
X			/* get all valid appts at this time */
X			for (aptr=slots[sno].first; aptr; aptr=aptr->next)
X				if (!chk_deleted(&slots[sno], aptr)) {
X					if (getenv("WINDOW_PARENT") != NULL
X					    && (console = fopen("/dev/console", "w")) != NULL) {
X						fprintf(console, "<< %s >> %s\n",
X						    progname, aptr->str);
X						fclose(console);
X					} else {
X						fprintf(stderr, "\007\007<< %s >> %s\n",
X						    progname, aptr->str);
X					}
X				}
X		}
X#else
X		if ((beep || beep_open) && sno > echoed_sno) {
X			echoed_sno = sno;
X			window_bell(frame);
X			if (beep_open) {
X				olddate = Saveday = today;
X				mainsw_state = DISPLAYING_DAY;
X				window_set(frame, FRAME_CLOSED, FALSE, 0);
X			}
X			if (beep)
X				if ((console = fopen("/dev/console", "w")) != NULL) {
X					/* get all valid appts at this time */
X					for (aptr=slots[sno].first; aptr;
X					    aptr=aptr->next)
X						if (!chk_deleted(&slots[sno], aptr))
X							fprintf(console, "<< %s >> %s\n", progname, aptr->str);
X					fclose(console);
X				}
X			/*
X			 * also put a copy in ~/.msgfile, in case
X			 * nlock(1) is running
X			 */
X			if ((msgf = fopen(msgfile, "w")) != NULL) {
X				fprintf(msgf, "%s", slots[sno].cur_appt->str);
X				fclose(msgf);
X			}
X		}
X 		/* ... and change the icon */
X		if (icon_in_use != REV_ICON) {
X			window_set(frame,
X			    FRAME_ICON, rev_icon,
X			    0);
X			icon_in_use = REV_ICON;
X		}
X#endif
X	}
X#ifdef CALENCHECK
X	if (new_day)
X		Saveday = today;
X#else
X	if (new_day && (int)window_get(frame, FRAME_CLOSED)) {
X		/* update times so that it opens on today */
X		closedate = olddate = Saveday = today;
X		/* update date field of the icons */
X		sprintf(datestr_day, "%d", today.tm_mday);
X		/* replace the date */
X		where.pr = ic_pr;
X		where.pos.x = 13;
X		where.pos.y = 49 + 5;
X		pr_rop(where.pr, 14, 47, 10, 8, PIX_CLR, NULL, 0, 0);
X		pr_rop(where.pr, 40, 47, 16, 8, PIX_CLR, NULL, 0, 0);
X		pf_ttext(where, PIX_NOT(PIX_SRC), sfont, datestr_day);
X		where.pr = na_ic_pr;
X		pr_rop(where.pr, 14, 47, 10, 8, PIX_CLR, NULL, 0, 0);
X		pr_rop(where.pr, 40, 47, 16, 8, PIX_CLR, NULL, 0, 0);
X		pf_ttext(where, PIX_NOT(PIX_SRC), sfont, datestr_day);
X		where.pr = rev_ic_pr;
X		pr_rop(where.pr, 14, 47, 10, 8, PIX_SET, NULL, 0, 0);
X		pr_rop(where.pr, 40, 47, 16, 8, PIX_SET, NULL, 0, 0);
X		pf_ttext(where, PIX_SRC, sfont, datestr_day);
X		where.pos.x = 39;
X		where.pr = ic_pr;
X		pf_ttext(where, PIX_NOT(PIX_SRC), sfont, smonthnames[today.tm_mon]);
X		where.pr = na_ic_pr;
X		pf_ttext(where, PIX_NOT(PIX_SRC), sfont, smonthnames[today.tm_mon]);
X		where.pr = rev_ic_pr;
X		pf_ttext(where, PIX_SRC, sfont, smonthnames[today.tm_mon]);
X		icon_set(icon, ICON_IMAGE, ic_pr, 0);
X		icon_set(rev_icon, ICON_IMAGE, rev_ic_pr, 0);
X		icon_set(na_icon, ICON_IMAGE, na_ic_pr, 0);
X		switch (icon_in_use) {
X			case STD_ICON:
X				window_set(frame, FRAME_ICON, icon, 0);
X				break;
X			case REV_ICON:
X				window_set(frame, FRAME_ICON, rev_icon, 0);
X				break;
X			case NA_ICON:
X				window_set(frame, FRAME_ICON, na_icon, 0);
X				break;
X		}
X		new_day = 0;
X		show_future = 1;	/* show future appts again */
X	}
X#endif
X#ifndef CALENCHECK
X	current = Saveday;
X	err2console(TRUE);
X	(void)get_day_appts();
X	err2console(FALSE);
X	if (locked == 2)
X		unlock_cursors();
X#endif
X}
X
Xdo_files(window_prompt)
Xint window_prompt;
X{
X	char *slash, *default_ptr, *envptr;
X	char buff[80];
X	int to_slash, getpid(), fd, errflag, numask;
X	struct passwd *pw;
X	struct stat statbuf;
X	FILE *appts;
X	char *strcpy(), *rindex(), *getenv();
X
X	/* the tmp file */
X	sprintf(tmpapts_pathname, "/tmp/appts%d", getpid());
X	if (otherfile) {
X		strcpy(apts_pathname, othername);
X		if ((slash = rindex(apts_pathname, '/')) != NULL) {
X			to_slash = slash - apts_pathname;
X			strncpy(apts_dir, apts_pathname, to_slash);
X			apts_dir[to_slash] = '\0';
X		} else {
X			strcpy(apts_dir, ".");
X		}
X	} else {
X#ifndef NO_DEFAULTS
X		if ((default_ptr = defaults_get_string("/CalenTool/Appts", NULL, 0)) != NULL) {
X			if ((slash = rindex(default_ptr, '/')) != NULL) {
X				to_slash = slash - default_ptr;
X				strncpy(apts_dir, default_ptr, to_slash);
X				apts_dir[to_slash] = '\0';
X			} else {
X				strcpy(apts_dir, ".");
X			}
X		} else
X#endif
X		if ((envptr = getenv("CALENTOOL_DIR")) != NULL) {   
X			strcpy(apts_dir, envptr);
X#ifndef CALENCHECK
X		} else if (mailto) {
X			if ((pw = getpwnam(mailto)) == NULL)
X				/* no entry */
X				exit(1);
X			envptr = pw->pw_dir; /* home directory */
X			strcpy(apts_dir, envptr);
X#endif
X		} else if ((envptr = getenv("HOME")) != NULL) {   
X			strcpy(apts_dir, envptr);
X		} else {   
X			apts_dir[0] = '\0';
X		}
X		if (*apts_dir) {
X			/* prepend directory on pathnames */
X			sprintf(apts_pathname, "%s/.appointments", apts_dir);
X		} else {
X			/* use current directory */
X			strcpy(apts_pathname, ".appointments");
X		}
X	}
X	
X	/* directory for date/event data files */
X#ifndef NO_DEFAULTS
X	if ((default_ptr = defaults_get_string("/CalenTool/LibDir", NULL, 0)) != NULL)
X		strcpy(lib_dir, default_ptr);
X	else
X#endif
X		strcpy(lib_dir, DATELIB_DIR);
X
X	errflag = 0;
X	if (access(apts_pathname, R_OK) == -1) {
X#ifndef CALENCHECK
X		if (window_prompt) {
X			fprintf(stderr, "nonexistant file\n");
X			sprintf(buff, "Cannot access calendar file %s - create?", apts_pathname);
X			create_prompt_frame(buff, TRUE);
X			if ((int)window_loop(prompt_frame) == 0) {
X				/* try to create the file */
X				fprintf(stderr, "creating file\n");
X				if ((fd=open(apts_pathname, O_CREAT|O_RDWR, 0644)) <= 0) {
X					perror(apts_pathname);
X					errflag = 1;
X					fprintf(stderr, "..error\n");
X				} else {
X					if (write(fd, HEADER, sizeof(HEADER)) != sizeof(HEADER)) {
X						perror("writing header");
X						errflag = 1;
X					}
X					close(fd);
X					fprintf(stderr, "..wrote header\n");
X					one_based = 1;
X				}
X			} else {
X				fprintf(stderr, "window_ret != 0\n");
X				errflag = 1;
X			}
X			window_set(prompt_frame, WIN_SHOW, FALSE, 0);
X			if (errflag)
X				return(1);
X		} else {
X#endif
X			fprintf(stderr, "Cannot access calendar file %s - create? ", apts_pathname);
X			fgets(buff, 80, stdin);
X			if (buff[0] == 'y' || buff[0] == 'Y') {
X				if ((fd=open(apts_pathname, O_CREAT|O_RDWR, 0644)) <= 0) {
X					perror(apts_pathname);
X					return(1);
X				} else {
X					if (write(fd, HEADER, sizeof(HEADER)) != sizeof(HEADER)) {
X						perror("writing header");
X						close(fd);
X						return(1);
X					}
X					close(fd);
X					one_based = 1;
X				}
X			} else
X				return(1);
X		}
X#ifndef CALENCHECK
X	}
X	if (!read_only)
X		if (access(apts_pathname, W_OK) == -1)
X			read_only = 1;
X	/*
X	 * set permissions on tmp file based on .appointments file
X	 * with the expception that we need at least write permission
X	 * for the owner.
X	 */
X	(void)stat(apts_pathname, &statbuf);
X	numask = ~statbuf.st_mode & 0077;  /* yes, this is octal 77 */
X	(void)umask(numask);
X
X	/* update base frame label, if the tool is running */
X	if (frame) {
X		strcpy(t_title, version());
X		if (read_only)
X			strcat(t_title, " [Read Only]");
X		strcat(t_title, "  -  ");
X		strcat(t_title, apts_pathname);
X		window_set(frame, FRAME_LABEL, t_title, 0);
X	}
X#endif
X
X	/* check first line of appts file to see if it is the new style */
X	if ((appts = fopen(apts_pathname, "r")) != NULL) {
X		fgets(buff, 80, appts);
X		fclose(appts);
X		if (!strcmp(buff, OHEADER) || !strncmp(buff, HEADER, 18)) {
X			version2 = 1;
X			one_based = 1;
X		}
X#ifdef CALENCHECK
X		else
X			err_rpt("wrong version appointments file format", FATAL);
X#endif
X	}
X
X#ifndef CALENCHECK
X	/* Convert from old format to new. There may some appts files
X	 * that are one-based, but are still old style. These are
X	 * also handled. (Note: for old-style one-based appts files
X	 * the user MUST start calentool with the -z flag.)
X	 */
X	 if (!version2)
X		ver1to2();
X#endif
X	
X	return(0);
X}
X
END_OF_FILE
if test 29124 -ne `wc -c <'common.c'`; then
    echo shar: \"'common.c'\" unpacked with wrong size!
fi
# end of 'common.c'
fi
echo shar: End of archive 17 \(of 23\).
cp /dev/null ark17isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 23 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.