/*
 * s m i l e y . c
 * 
 * DaviD W. Sanderson is to blame for this.
 */

#include "smiley.h"
#include "patchlevel.h"

/*
 * macros
 */

/*
 * swrite() - write() a "string variable" (char *)
 */

#define	swrite(fd, s)	(void) write((fd), (s), strlen(s))

/*
 * lwrite() - write() a "string literal" (char [])
 */

#define	lwrite(fd, lit)	(void) write((fd), (lit), sizeof(lit)-1)

/*
 * PUTFACE() - write() a smiley and its description
 */

#define	PUTFACE(fd, i)	swrite((fd), faces[i].face);	\
			lwrite((fd), tab);		\
			swrite((fd), faces[i].desc);	\
			lwrite((fd), newline)

/*
 * FD - file descriptor to which to write the output
 */

#define FD	1

/*
 * declarations
 */

extern int      write();
extern int      strlen();
extern char    *bsearch();

/*
 * definitions
 */

static char     ENVAR[] = "SMILEY";
static char     tab[] = "\t";
static char     newline[] = "\n";

/*
 * facecmp() - comparison routine for using bsearch()
 */
int
facecmp(f1, f2)
	struct smiley  *f1;
	struct smiley  *f2;
{
	return strcmp(f1->face, f2->face);
}

/*
 * fsearch() - return index of face, or -1 on failure
 * 
 * Note that this assumes that there will be at most one entry in
 * faces[] for a particular smiley.  It also assumes that faces[] is
 * sorted in ascending order.
 */
int
fsearch(s)
	char           *s;
{
	struct smiley   f;
	struct smiley  *ans;

	f.face = s;
	ans = (struct smiley *) bsearch(
		(char *) &f,
		(char *) faces, (unsigned) nfaces, sizeof faces[0],
		facecmp);

	if (ans)
		return ans - faces;
	else
		return -1;
}

/*
 * explain() - look through the list of smileys for the given face.
 * 
 * If it is found, then print out the description.
 * 
 * The return value is zero if the face is not found, nonzero otherwise.
 */
static int
explain(s)
	char           *s;
{
	int             i;

	if((i = fsearch(s)) == -1)
		return 0;

	PUTFACE(FD, i);
	return 1;
#if 0
	int		found = 0;

	for (i = 0; i < nfaces; i++)
	{
		extern int      strcmp();

		if (strcmp(s, faces[i].face) == 0)
		{
			found = 1;
			PUTFACE(FD, i);
		}
	}
	return found;
#endif
}

/*
 * main() - main program
 */

main(ac, av)
	int             ac;
	char          **av;
{
	extern char    *optarg;
	extern int      optind;
	extern int      opterr;

	char           *args = "Velf";
	int             Vflag = 0;
	int             eflag = 0;
	int             lflag = 0;
	int             fflag = 0;
	int             errflag = 0;
	int             c;

	/*
	 * process command-line options
	 */
	while ((c = getopt(ac, av, args)) != -1)
	{
		switch (c)
		{
		case 'V':	/* version */
			Vflag = 1;
			break;
		case 'e':	/* environment */
			eflag = 1;
			break;
		case 'l':	/* list */
			lflag = 1;
			break;
		case 'f':	/* face only */
			fflag = 1;
			break;
		case '?':
			errflag = 1;
			break;
		}
	}
	if (errflag)
	{
		static char     msg0[] = "usage: ";
		static char    *msg1[] =
		{
			" [-V] [-e] [-l] [-f] [smiley ...]\n",
			"where:\t-V\tprint program version\n",
			"\t-e\texplain the face in $",
			(char *) 0
		};
		static char    *msg2[] =
		{
			"\n",
			"\t-l\tlist all the smileys\n",
			"\t-f\tprint a random smiley, face only\n",
			"\tsmiley\texplain the given smileys\n",
			"(no args)\tprint a random smiley\n",
			(char *) 0
		};

		char           **p;

		lwrite(FD, msg0);
		swrite(FD, av[0]);

		for (p = msg1; *p; p++)
			swrite(FD, *p);

		lwrite(FD, ENVAR);

		for (p = msg2; *p; p++)
			swrite(FD, *p);

		return 1;
	}

	/*
	 * perform command-line options
	 */

	/*
	 * Vflag - print version information
	 */
	if (Vflag)
	{
		static char     stat0[] = " faces, ";
		static char     stat1[] = " definitions\n";
		char *num;
		int i;
		unsigned long ndefs;
		extern char *ltoa();

		swrite(FD, av[0]);
		lwrite(FD, version);

		/*
		 * Print counts of the smiley faces and definitions
		 * The definitions for a smiley are separated by
		 * newlines, but not terminated by newlines.
		 */
		num = ltoa((unsigned long)nfaces, 10);
		swrite(FD, num);
		lwrite(FD, stat0);

		ndefs = nfaces;
		for (i=0; i<nfaces; i++)
		{
			char *def;

			for (def = faces[i].desc; *def; def++)
			{
				if (*def == '\n')
					ndefs++;
			}
		}
		num = ltoa(ndefs, 10);
		swrite(FD, num);
		lwrite(FD, stat1);

#ifdef __DATE__
#ifdef __TIME__
		{
			static char     when0[] = "last compiled ";
			static char     when1[] = __TIME__;
			static char     when2[] = " ";
			static char     when3[] = __DATE__;
			static char     when4[] = "\n";

			lwrite(FD, when0);
			lwrite(FD, when1);
			lwrite(FD, when2);
			lwrite(FD, when3);
			lwrite(FD, when4);
		}
#endif
#endif

		return 0;
	}

	/*
	 * eflag - explain $SMILEY
	 */
	if (eflag)
	{
		extern char    *getenv();
		int	        unknown = 1;
		char           *str;

		if ((str = getenv(ENVAR)) != (char *)0)
		{
			if(explain(str) != 0)
				unknown = 0;
		}
		else
		{
			static char     notset[] = " not set\n";

			lwrite(FD, ENVAR);
			lwrite(FD, notset);
		}
		return unknown;
	}

	/*
	 * lflag - list smileys
	 */
	if (lflag)
	{
		int             i;

		for (i = 0; i < nfaces; i++)
		{
			PUTFACE(FD, i);
		}
		return 0;
	}

	/*
	 * literal smileys - try to explain them
	 *
	 * In this case the exit status is the number of smileys
	 * that were not found.
	 */
	if (optind < ac)
	{
		int	unknown = 0;

		for (; optind < ac; optind++)
		{
			if(!explain(av[optind]))
				unknown++;
		}

		return unknown;
	}

	/*
	 * otherwise - generate a random smiley
	 */
	{
		extern int      rand();
		extern void     srand();
		extern long     time();
		/* extern int      getpid(); */

		char           *f;

		/*
		 * seed with the current time + the pid. (The pid
		 * prevents smileys started at identical times from
		 * getting the same random seed)
		 */
		/* srand((unsigned) time((long *)0) + (unsigned) getpid()); */

		/* this feature was supressed for two reasons, first of all, the Amiga
		 * doesn't support pids by default and secondly, time returns the time
		 * in seconds since the base date and so it's a unique value
		 *                                    -- Yves Perrenoud
		*/
		srand((unsigned) time((long *)0));

		f = faces[rand() % nfaces].face;

		if (fflag)
		{
			swrite(FD, f);
			lwrite(FD, newline);
		}
		else
		{
			(void) explain(f);
		}
	}
	return 0;
}
