/*
 *  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"

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

   HEADER MANIPULATIONS

   to retrieve true names and addresses from amidst the
   rfc822 junk.

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

/* end the string at the first at sign. */

void
splatat (s)
	char *s;
{
	while (*s) {
		if (*s == '@') *s = 0;
		s++;
	}
}

/* return a new string containing a massaged version of the
   people list `s': if `how' is JUSTNAME return only the names;
   if JUSTADDR, only the addresses, if CANONICAL return it all
   in `name <address>' format.

   pointer to static data which is overwritten with each call,
   and all that.
*/

/* 2 aug: the stuff to handle :; is ugly but I think functional.
   I'm not quite sure what to do with groupname:; with no
   intervening addresses, since it is officially legal (and
   explicitly so) but doesn't actually contain any people's
   names) so it just gets a ??? user, which is the best I
   could think of.  "foo: bar, baz;" gets canonized into
   "bar (foo), baz (foo)" which isn't quite the rfc's intent
   but expresses the group membership reasonably well.
*/

char *
canonaddr (how, s)
	int how;
	char *s;
{
	static char *ret = 0;
	char stopchar;
	char *groupname = 0;

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

	while (*s == ',' || isspace (*s)) s++;

	while (*s) {
		static char *angles = 0;
		static char *parens = 0;
		static char *neither = 0;
		int angleoffset = 0;
		int parenoffset = 0;
		int neitheroffset = 0;

		int inparens = 0;
		int inquotes = 0;
		int inangles = 0;
		int nextspecial = 0;

		if (angles) free (angles), angles = 0;
		if (parens) free (parens), parens = 0;
		if (neither) free (neither), neither = 0;

		angles = strdupe (s);
		parens = strdupe (s);
		neither = strdupe (s);

		angles[0] = 0;
		parens[0] = 0;
		neither[0] = 0;

		while (*s) {
			stopchar = ',';

			if (inangles) {
				if (nextspecial) {
					angles[angleoffset++] = *s;
					nextspecial = 0;
				} else if (*s == '\\') {
					nextspecial = 1;
				} else if (*s == '>') {
					inangles--;
				} else if (*s == '<') {
					inangles++;
				} else {
					angles[angleoffset++] = *s;
				}
			} else if (inparens) {
				if (nextspecial) {
					parens[parenoffset++] = *s;
					nextspecial = 0;
				} else if (*s == '\\') {
					nextspecial = 1;
				} else if (*s == ')') {
					inparens--;
				} else if (*s == '(') {
					inparens++;
				} else {
					parens[parenoffset++] = *s;
				}
			} else if (inquotes) {
				if (nextspecial) {
					neither[neitheroffset++] = *s;
					nextspecial = 0;
				} else if (*s == '\\') {
					nextspecial = 1;
				} else if (*s == '\"') {
					inquotes = 0;
				} else {
					neither[neitheroffset++] = *s;
				}
			} else {
				if (*s == '(') {
					inparens++;
				} else if (*s == '<') {
					inangles++;
				} else if (*s == '\"') {
					inquotes = 1;
				} else if (*s == ','
				       ||  *s == ';'
				       ||  *s == ':') {

					stopchar = *s;
					break;

				} else {
					neither[neitheroffset++] = *s;
				}
			}

			s++;
		}

		angles[angleoffset] = 0;
		parens[parenoffset] = 0;
		neither[neitheroffset] = 0;

		{
			char *theparen = parens;
			char *theangle = angles;
			char *theneither = neither;

			if (theparen) {
				while (*theparen && isspace (*theparen)) 
					theparen++;
				while (strlen (theparen) 
				&& isspace (theparen[strlen (theparen) - 1])) 
					theparen[strlen (theparen) - 1] = 0;
				if (!*theparen) theparen = 0;
			}

			if (theangle) {
				while (*theangle && isspace (*theangle)) 
					theangle++;
				while (strlen (theangle) 
				&& isspace (theangle[strlen (theangle) - 1])) 
					theangle[strlen (theangle) - 1] = 0;
				if (!*theangle) theangle = 0;
			}

			if (theneither) {
				while (*theneither && isspace (*theneither)) 
					theneither++;
				while (strlen(theneither) 
				&& isspace (theneither[strlen(theneither)-1])) 
					theneither[strlen (theneither)-1] = 0;
				if (!*theneither) theneither = 0;
			}

			if (stopchar == ':') {
				/* there *shouldn't* be a group name at
				   this point, but deal with it if there
				   is one.
				*/

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

				/* there should be a neither, too, but if
				   there's not, we'll just do without
				   a group name.  Who needs 'em anyway?
				*/

				if (theneither) {
					groupname = strdupe (theneither);
				}
			} else if (how == JUSTNAME) {
				if (theangle) {
					if (theneither)
						splatat (theneither);
						addstr (&ret, theneither);

					if (theparen) {
						addstr (&ret, " (");
						addstr (&ret, theparen);
						addstr (&ret, ")");
					}

					if (!theneither && !theparen) {
						splatat (theangle);
						addstr (&ret, theangle);
					}
				}
				else if (theparen) addstr (&ret, theparen);
				else if (theneither) {
					splatat (theneither);
					addstr (&ret, theneither);
				} else addstr (&ret, "???");

				if (groupname) {
					addstr (&ret, " (");
					addstr (&ret, groupname);
					addstr (&ret, ")");
				}
			} else if (how == JUSTADDR) {
				if (theangle) addstr (&ret, theangle);
				else if (theneither) addstr (&ret, theneither);
				else addstr (&ret, "???");
			} else {
				if (groupname) {
					addstr (&ret, " (");
					addstr (&ret, groupname);
					addstr (&ret, ")");
				}

				if (theangle) {
					if (theparen) {
						if (theneither) {
						    addstr (&ret, theneither);
						    addstr (&ret, " (");
						}

						addstr (&ret, theparen);

						if(theneither) addstr(&ret,")");
					} else if (theneither) {
						addstr (&ret, theneither);
					}

					addstr (&ret, " <");
					addstr (&ret, theangle);
					addstr (&ret, ">");
				} else if (theparen) {
					addstr (&ret, theparen);
					addstr (&ret, " <");

					if (theneither) addstr(&ret,theneither);
					else addstr (&ret, "???");

					addstr (&ret, ">");
				} else {
					addstr (&ret, "<");
					addstr (&ret, theneither);
					addstr (&ret, ">");
				}
			}

			if (stopchar == ';') {
				if (groupname) free (groupname), groupname = 0;
			}
		}

		/* got to make sure we don't trample the ; in a group:;
		   construct with no users, because we want to list the
		   group even if there's no one in it.

		   we can deal with chunks of , and ; so long as we do
		   remember to get rid of the current group at each ;
		*/

		if (stopchar == ':') {
			while (*s == ':' || isspace (*s)) s++;
		} else {
			while (*s == ';' || *s == ',' || isspace (*s)) {
				if (*s == ';') {
					if (groupname) free (groupname), 
					               groupname = 0;
				}

				s++;
			}
		}

		if (*s && *s != ':' && stopchar != ':') {
			addstr (&ret, ", ");
		}
	}

	/* this shouldn't happen, but who knows what fool might not
	   close their :; group
	*/

	if (groupname) free (groupname);

	return ret;
}
