/*
 *  hap -- a mail notification program
 * 
 *  copyright 1995 by Eric Fischer, etaoin@uchicago.edu
 * 
 *  copies of hap may be redistributed under the terms of the
 *  GNU public license, copies of which are available from
 *  the Free Software Foundation, 59 Temple Place, Boston, MA
 *  02111 USA.
 *
 */

#include "hap.h"

#define STANDARD_OUT 1
#define STANDARD_IN  0

extern int savecurs;  /* do we have a spiffy terminal? */

/**************************************************************************

   MAIN PROGRAM

***************************************************************************/

int
main (argc, argv)
	int argc;
	char **argv;
{
	int delay = 30;       /* time between checks (-seconds) */
	int beep = 1;         /* do we beep at each notification? (--silent) */
	int watchmesg = 0;    /* watch mesg y/n setting of tty (--mesg) */
	int warnmissing = 1;  /* warn if files are missing (--no-warn) */
	int listall = 0;      /* list all messages at startup (--all) */

	mailbox *boxes = 0;   /* list of mailboxes we're watching */
	time_t lastkbtouch = 0;   /* time of the last keypress we know about */

	char *whattoprint = 0;  /* this notice */
	char *notices = 0;      /* notices already on the screen */

	program = argv[0];
	if (program == 0) program = "hap";

	/* find out who called us.  this must be done *before* we
	   fork, of course.
	*/

	shell = getppid();

	{
		int tempdelay = 0;
		int option_index = 0;

		static struct option longopts[] = {
			"help",     0, 0, 'h',
			"version",  0, 0, 'V',
			"biff",     0, 0, 'b',
			"silent",   0, 0, 's',
			"quiet",    0, 0, 's', 
			"kill",     0, 0, 'k',
			"quote",    0, 0, 'q',
			"verbose",  0, 0, 'v',
			"terse",    0, 0, 't',
			"mesg",     0, 0, 'm',
			"all",      0, 0, 'a',
			"debug",    0, 0, 'd',
			"no-inverse",0,0, 'i',
			"addresses",0, 0, 'A',
			"address",  0, 0, 'A',
			
			0,	    0, 0, 0
		    };

		while (1) {
			int c;

			c = getopt_long(argc, argv, "hVbskqvtmadiA0123456789",
			    longopts, &option_index);

			if (c == EOF) break;

			if (isdigit (c)) {
				tempdelay = (tempdelay * 10) + (c - '0');
				continue;
			}

			switch (c) {
				case 'h': usage();            break;
				case 'V': version();          break;
				case 'b': biff = 1;           break;
				case 's': beep = 0;           break;
				case 'k': diediedie();        break;
				case 'q': wantquote = 1;
				          terse = 0;          break;
				case 'v': wantquote = 5;
				          terse = 0;          break;
				case 't': wantquote = 0;
				          terse = 1;          break;
				case 'm': watchmesg = 1;      break;
				case 'a': listall = 1;        break;
				case 'd': debug = 1;          break;
				case 'i': likeinverse = 0;    break;
				case 'A': preferaddr = 1;     break;

				default: usage();             break;
			}

		}

		if (optind < argc) {
			if (optind + 1 < argc) {
				fileannounce = 1;
			} else {
				fileannounce = 0;
			}

			for (; optind < argc; optind++) {
				mailbox *newm = 
				    (mailbox *) xmalloc (sizeof (mailbox));

				newm->next = boxes;
				boxes = newm;

				boxes->size = 0;
				boxes->isdir = 0;
				boxes->name = expandname (argv[optind]);
			}
		} else {
			fileannounce = 0;
		}

		if (tempdelay) delay = tempdelay;
	}

	/* complain if some idiot put us in their .cshrc */

	/* 
	if (!isatty (STANDARD_OUT) && !terminalforce) {
		fprintf (stderr, "%s: standard output isn't a terminal.\n",
		    program);
		fprintf (stderr, "%s: use the --file option if you really %s",
		    program, "meant it\n");
		exit (1);
	}
	*/

	/* if we don't have a list of files, use the system mailbox */

	if (boxes == 0) {
		boxes = (mailbox *) xmalloc (sizeof (mailbox));

		boxes->next = 0;
		boxes->size = 0;
		boxes->isdir = 0;
		boxes->name = expandname ("%");

		warnmissing = 0;  /* if they don't type a name,
		                     they can't misspell it */
	}

	/* warn the user if they've mistyped the names, and find out
	   how big each is intially */

	{
		mailbox *this;

		for (this = boxes; this; this = this->next) {
			long size = getsize (this->name);

			if (size == -1) {
				if (warnmissing) {
					fprintf (stderr, "%s: warning: ",
					    mybasename (program));
					perror (this->name);
				}

				this->size = 0;
				this->mtime = 0;
			} else {
				this->size = size;
				this->mtime = getmtime (this->name);

				if (checkdir(this->name)) {
					this->isdir = lookdir (this->name);
				}

				if (listall) {
					this->size = 0;
					if (this->isdir) freelist(this->isdir);
					this->isdir = 0;
					this->mtime = 0;
				}
			}
		}
	}

	/* make sure the output is not in fact line buffered, but buffered
	   for real until we flush it, and with a big buffer */

#ifdef HAVE_SETVBUF
	setvbuf (stdout, thisbuffer, _IOFBF, THISBUFFERSIZE);
#else
#   ifdef HAVE_SETBUFFER
	setbuffer (stdout, thisbuffer, THISBUFFERSIZE);
#   endif
#endif

	/* make sure we've got a terminal that can do something useful */

	inittcap();

	/* create a daemon process */

	{
		int pid = 0;

		if (!debug) pid = fork();

		if (pid == -1) {
			fprintf (stderr, "%s: fork failed.  Sorry.\n", program);
			perror (program);
			exit (1);
		}

		if (pid != 0) {
			exit (0);  /* we're the parent process, quit */
		}
	}

	/* set up signal handler so we can see the blood when
	   we get killed
	*/

	signal (SIGTERM, alas);

	/* and make sure keyboard signals don't take us down
	   (only relevant to sh users, I think)
	*/

	if (!debug) {
		signal (SIGINT, SIG_IGN);
		signal (SIGQUIT, SIG_IGN);
	}

	/* find last keyboard touch time.  tiny delay to make sure that
	   we don't check this until the shell has already printed the
	   prompt and done a read(), because if we do it too early it'll
	   have changed by the time we print our first notice, and if
	   they've used --all and get new mail before typing anything
	   they'll get two "New mail in..." lines.   Ick.
	*/

	sleep (1);
	lastkbtouch = getlasttime (STANDARD_IN);

	/* we need this here because mesg gets checked at the end
	   of the loop but we don't want to annoy them with mail
	   notifications if mesg is already n when we start up
	   and there's already mail to list.
	*/

	if (watchmesg) {
		while (mesgy (STANDARD_OUT) && alive()) {
			sleep (delay);
		}
	}

	/* now loop until our login goes away. */

	do {
		mailbox *each;

		if (whattoprint) free (whattoprint), whattoprint = 0;

		/* check to see if the user is actually there.
		   if the last time on the keyboard isn't the same
		   as the previous time we checked, decide that there's
		   actually someone there and get rid of any old messages
		   we have saved up.

		   important to do this before getting the new messages
		   (even though it'd probably be better to wait for the
		   keystroke until just before printing, since it might
		   take a while to get stuff from the file) because the
		   "New mail in..." line is included only if lastadd
		   has changed, and we want it to be included at the top
		   of every notification.
		*/

		{
			time_t kbtime = getlasttime (STANDARD_IN);

			if (kbtime != lastkbtouch) {  /* kb has been touched */
				if (notices) free (notices), notices = 0;
				lastadd = 0;
			}

			/* if we have a boring terminal that's not always
			   doing mail at the same place on the screen, don't
			   give multiple notifications for the same message.
			*/

			if (!savecurs) {
				if (notices) free (notices), notices = 0;
				lastadd = 0;
			}

			lastkbtouch = kbtime;
		}

		/* step through the mailboxes.  if each has changed,
		   add the new messages to the list of all the ones
		   we want to print
		*/

		for (each = boxes; each; each = each->next) {
		    	if (checkdir (each->name)) {
				whattoprint = mhnotice (each, whattoprint);
			} else {
				whattoprint = filenotice (each, whattoprint);
			}
		}

		/* if there's anything new, spew out any old stuff we've
		   been saving up, followed by whatever's new, then add
		   the new stuff to the old stuff for next time.
		*/

		if (whattoprint) {
			if (beep) printf ("\007");

			if (terse) {
				if (notices) tespewtwo (notices, whattoprint);
				else tespew (whattoprint);
			} else {
				startmessage();

				if (notices) spewtwo (notices, whattoprint);
				else spew (whattoprint);

				endmessage();
			}

			notices = addtostr (notices, whattoprint);
		}

		sleep (delay);

		/* if they care about mesg, check it and wait until
		   mesg is y again.

		   check alive() because they might log out without
		   resetting mesg to y, and we don't want to get caught
		   in an infinite loop.
		*/

		if (watchmesg) {
			while (mesgy (STANDARD_OUT) && alive()) {
				sleep (delay);
			}
		}
	} while (alive());

	/* issue a few last words...

	   generally the user will never see these; they're visible
	   only if exiting from a subshell or su rather than from
	   a legitimate login.
	*/

	/*
	startmessage();
	printf ("the end is near for %s, process id %d, user id %s",
	    mybasename (program), getpid(), whoami());
	crlf();
	printf ("(parent process, id %d, has exited)", shell);
	crlf();
	endmessage();
	*/

	return 0;
}

