/*
 * ---------------------------------------------------------------------------
 * $Id: myaliases.c,v 1.17 2002/03/08 15:36:00 stephan Exp $
 * ---------------------------------------------------------------------------
 * $Log: myaliases.c,v $
 * Revision 1.17  2002/03/08 15:36:00  stephan
 * LDAPv3 scheme modifications
 *
 * Revision 1.16  2002/02/05 11:41:47  stephan
 * don't print an empty ou: identifier
 *
 * Revision 1.15  2001/02/21 11:38:25  stephan
 * generate another alias
 *
 * Revision 1.14  2001/02/06 11:58:13  stephan
 * if no fullname or lastname, use name (for LDAP)
 *
 * Revision 1.13  2001/02/05 13:04:10  stephan
 * generation of LDAP groups and again multiple email-addresses for persons.
 *
 * Revision 1.12  2001/01/31 15:06:19  stephan
 * Netscape scheme definitions added for LDAP
 *
 * Revision 1.11  2001/01/24 16:19:12  stephan
 * added print_departments()
 *
 * Revision 1.10  2001/01/23 14:07:14  stephan
 * possibility to only generate grouplists
 *
 * Revision 1.9  2001/01/19 18:58:10  stephan
 * added usage()
 *
 * Revision 1.8  2001/01/19 18:47:23  stephan
 * multiple groups possible, sorting of all lists, one mailaddress with ldap
 *
 * Revision 1.7  2001/01/10 15:58:59  stephan
 * changes for mailboxes on server where aliases reside
 *
 * Revision 1.6  2000/10/25 09:39:20  stephan
 * generate another alias
 *
 * Revision 1.5  2000/09/07 08:54:18  stephan
 * also firstnamelastname as alias
 *
 * Revision 1.4  2000/04/11 14:34:24  stephan
 * some bugs
 *
 * Revision 1.3  2000/04/11 14:30:43  stephan
 * also print aliases
 *
 * Revision 1.2  2000/04/07 14:13:59  stephan
 * generate aliases and ldap files
 *
 * Revision 1.1  2000/03/01 15:19:38  stephan
 * Initial revision
 *
 * ---------------------------------------------------------------------------
 */
static char *rcsid="@(#)$Id: myaliases.c,v 1.17 2002/03/08 15:36:00 stephan Exp $";

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define LDAPv3
#define MAX_ALIASSIZE	512
#define DSP_OFFSET	50

#define GRP_ALL		"All"

#define FLG_ALIASES	(1<<0)
#define FLG_GROUPS	(1<<1)
#define FLG_PRIMARY	(1<<2)
#define FLG_LDAP	(1<<3)
#define FLG_DEPARTMENT	(1<<4)

#define FLG_LONGNAMES	(FLG_ALIASES|FLG_PRIMARY)

#define IDXlastname			0
#define IDXinitiallastname		1
#define IDXinitial_dot_lastname		2
#define IDXinitial_dash_lastname	3
#define IDXinitial_us_lastname		4
#define IDXfirstnamelastname		5
#define IDXfirstname_dot_lastname	6
#define IDXfirstname_dash_lastname	7
#define IDXfirstname_us_lastname	8
#define IDXinitialsubfixlastname	9
#define IDXcount			10		

#define is_white(c)	(c==' '||c=='\t'||c=='\n'||c=='\r')
#define is_sep(c)	(c=='='||c==','||c==':'||c==';')

struct group_t;

typedef struct user_t
{
	char *name;
	char *fullname;
	char *first;
	char *middle;
	char *last;
	char *office;
	char **departments;
	char *phone;
	char **aliases;

	struct user_t *prev;
	struct user_t *next;
} user_t;

typedef struct group_member_t
{
	user_t *u;
	char *name;

	struct group_member_t *prev;
	struct group_member_t *next;
} group_member_t;

typedef struct group_t
{
	char *name;
	struct group_member_t *members;

	struct group_t *prev;
	struct group_t *next;
} group_t;

static user_t *users = 0;
static group_t *groups = 0;

static void usage(char *);
static void print_ldap(char *, char *, char *);
static void print_namingcontext(char *, char *);
static void print_toplevel(char *, char *);
static void print_aliases(int);
static void normalise_group_name(char *);
static void print_departments(int);
static void print_group_entry(int, char *, user_t *, int);
static void print_group_all(int);
static void print_groups(int);
static int adduser(int, char *, char *, char *, char *[], int);
static group_t *getgroup(char *);
static int addtogroup(int, char *, user_t *);
static int buildalias(int, char *);
char *fullname(char *[], int);
char *middlename(char *[], int);

#ifdef FORWARDING_SYSTEM
static char *primary_alias(int, char *, char *[], int);
#endif

static char **generate_aliases(int, char *, char *[], int);
static int is_collission(char *, char *);
static char *skipwhite(char *);
static char *skipword(char *);
static void trim(char *);

int main(int argc, char **argv)
{
	char line[1024];
	int c, ok = 0, flags = 0;
	char *namingcontext=0;
	char *organisation=0;
	char *domain=0;

	if(argc <= 1)
		flags = FLG_ALIASES|FLG_GROUPS;
	else
		while((c = getopt(argc, argv, "ad:gl:mo:p")) != EOF)
			switch(c)
			{
			case 'a': flags |= FLG_ALIASES;		break;
			case 'd': domain=optarg;        	break;
			case 'g': flags |= FLG_GROUPS;		break;
			case 'l': flags |= FLG_LDAP; 
				  namingcontext=optarg; 	break;
			case 'm': flags |= FLG_DEPARTMENT;	break;
			case 'o': organisation=optarg; 		break;
			case 'p': flags |= FLG_LONGNAMES;	break;
			default : usage(0); 			exit(1);
			}
		if(argc-optind)
		{
			usage(0);
			exit(1);
		}

	if(flags&FLG_LDAP)
	{ 
		if(flags != FLG_LDAP)
		{
			fprintf(stderr, 
				"flag -l [LDAP] is mutually exclusive\n");
			flags = FLG_LDAP;
		}
		if(!domain)
		{
			fprintf(stderr, 
				"flag -l [LDAP] requires flag -d <domain>\n");
			exit(1);
		}
	}

	while(fgets(line, sizeof(line)-1, stdin))
		ok |= buildalias(flags, line);

	if(flags&FLG_ALIASES)
		print_aliases(flags);
	else if(flags&FLG_LDAP)
		print_ldap(namingcontext, organisation, domain);

	if(flags&FLG_GROUPS)
		print_groups(flags);

	if(flags&FLG_DEPARTMENT)
		print_departments(flags);

	return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}

static void usage(char *name)
{
printf(
"usage: %s [-a] [-d <domain> -l <context>] [-l] [-o <org>] [-p]\n"
"where:\n"
"  -a          : (default option) generate aliaslist\n"
"  -g          : (default option) generate aliaslist and grouplist\n"
"  -d <domain> : generate an LDAP ldiff-file for mail domain <domain>\n"
"  -l <context>: generate an LDAP ldiff-file with naming-context <context>\n"
"  -m          : generate list with <user> <primary-department>\n"
"  -o <org>    : use <org> for the LDAP organisation name\n"
"  -p          : generate an list with <login>:<alias> for longnames\n"
"\n%s\n"
"(C) Stephan Leemburg, This software is lisenced under the GPL Licence\n",
	name && *name ? name: "myaliases", rcsid
);
}

static void print_ldap(char *namingcontext, char *organisation, char *domain)
{
	register user_t *u;
	register group_t *g;

	print_namingcontext(namingcontext, domain);

	for(u = users; u; u = u->next)
	{
		register int i;
		char *mail = u->name;

		/*
		 * required elements of the objectclass tree...
		 */

		if(!*u->fullname)
			u->fullname = u->name;

		if(!*u->last)
			u->last = u->name;

		if(u->aliases)
		{
			if(u->aliases[IDXinitiallastname]
			&& *u->aliases[IDXinitiallastname])
				mail = u->aliases[IDXinitiallastname];

			else if(u->aliases[IDXlastname] 
			     && *u->aliases[IDXlastname])
				mail = u->aliases[IDXlastname];
		}

		printf("dn: cn=%s,%s\n", u->fullname, namingcontext);
		printf("objectclass: top\n");
		printf("objectclass: person\n");
		printf("objectclass: organizationalPerson\n");
		printf("objectclass: inetOrgPerson\n");
		printf("uid: %s\n", u->name);
		printf("cn: %s\n", u->fullname);
		printf("sn: %s%s%s\n", 
			*u->middle ? u->middle : "",
			*u->middle ? " " : "",
			u->last
			);
		if(*u->phone)
			printf("telephonenumber: %s\n", u->phone);
		printf("givenname: %s\n", u->first);

		if(organisation && *organisation)
			printf("o: %s\n", organisation);

		if(u->office && *u->office)
			printf("ou: %s\n", u->office);

		printf("mail: %s@%s\n", mail, domain);
		for(i=0; i <IDXcount; i++)
			if(u->aliases[i]
			&& *u->aliases[i]
			&& strcasecmp(mail, u->aliases[i]))
				printf("mail: %s@%s\n", u->aliases[i], domain);
		printf("\n");
	}

	for(g = groups; g; g = g->next)
	{
		register group_member_t *m;

		normalise_group_name(g->name);

		/*
		 * group memberlist
		 */

		printf("dn: cn=%s,%s\n", g->name, namingcontext);
		printf("objectclass: top\n");
		printf("objectclass: groupOfNames\n");
#ifdef LDAPv3
		printf("objectclass: inetOrgPerson\n");
#else
		printf("objectclass: rfc822MailGroup\n");
#endif
		printf("description: %s\n", g->name);
#ifdef LDAPv3
		printf("sn: %s\n", g->name);
#endif
		printf("cn: %s\n", g->name);
		printf("owner: cn=%s,%s\n", g->name, namingcontext);
		printf("mail: %s@%s\n", g->name, domain);
		printf("telephonenumber: %s\n", "Group");

		if(organisation && *organisation)
			printf("o: %s\n", organisation);

		printf("ou: %s\n", g->name);

		for(m = g->members; m; m = m->next)
			printf("member: cn=%s,%s\n", 
				m->u->fullname, 
				namingcontext
				);

		printf("\n");
	}
}

static void print_namingcontext(char *namingcontext, char *domain)
{
	char *p;

	if(!*namingcontext)
		return;

#ifndef LDAPv3
	p=namingcontext+strlen(namingcontext)-1; 
	for(--p; p > namingcontext; p--)
		if(*p == ',')
			print_toplevel(p+1, domain);
#endif

	print_toplevel(namingcontext, domain);
}

static void print_toplevel(char *dn, char *domain)
{
	char val[128];
	register int i;

	printf("dn: %s\n", dn);

	dn = skipwhite(dn);
	dn = skipword(dn);

	if(*dn)
		dn = skipwhite(++dn);

	for(i=0; *dn && !is_white(*dn) && *dn != ','; dn++)
		val[i++] = *dn;
	val[i]=0;

	printf("dc: %s\n", val);
	printf("objectClass: top\n");
	printf("objectClass: domain\n");
	printf("objectClass: domainRelatedObject\n");
	printf("associatedDomain: %s\n\n", domain);
}

static void print_aliases(int flags)
{
	register user_t *u;
	register int i;

	if(flags&FLG_PRIMARY)
	{
		printf("# generated namelist\n\n");
		for(u = users; u; u = u->next)
		{
			if(u->aliases
			&& u->aliases[IDXinitiallastname]
			&& *u->aliases[IDXinitiallastname])
				printf("%s:%*s%s\n", 
					u->name,
					DSP_OFFSET - strlen(u->name), 
					"",
					u->aliases[IDXinitiallastname]
				);
		}
		return;
	}

	printf("# generated aliaslist\n\n");

	for(u = users; u; u = u->next)
	{
		for(i = 0; u->aliases && u->aliases[i]; i++)
			if(*u->aliases[i] && strcasecmp(u->name, u->aliases[i]))
				printf("%s:%*s%s\n", 
					u->aliases[i], 
					DSP_OFFSET - strlen(u->aliases[i]), 
					"",
					u->name
				);
		printf("\n");
	}
}

static void normalise_group_name(char *name)
{
	register char *p, *s;

	for(s=p=name; p && *p; p++)
	{
		if(is_white(*p))
		{
			if(is_white(*(p+1)))
				continue;

			*p = '-';
		}
		*s++ = tolower(*p);
	}
	*s = 0;
}

static void print_departments(int flags)
{
	register user_t *u;

	for(u = users; u; u = u->next)
		if(u->office && *u->office)
		{
			normalise_group_name(u->office);

			printf("%s:%s\n", u->name, u->office);
		}
}

static void print_group_entry(int first, char *group, user_t *u, int last)
{
	char *name = u->name;

	if(u->aliases 
	&& u->aliases[IDXinitiallastname]
	&& *u->aliases[IDXinitiallastname])
		name = u->aliases[IDXinitiallastname];

	printf("%*s%s%*s%s%s\n", 
		strlen(group),
		first ? group : "",
		first ? ":" : " ",
		DSP_OFFSET - strlen(group), 
		"",
		name,
		last ? "\n" : ","
		);
}

static void print_group_all(int flags)
{
#ifdef GENERATE_GROUP_ALL
	register user_t *u;

	for(u = users; u; u = u->next)
		print_group_entry(u==users, GRP_ALL, u, u->next ? 0 : 1);
#endif
}

static void print_groups(int flags)
{
	register group_member_t *m;
	register group_t *g;
	int first;

	printf("\n# generated grouplist\n\n");

	print_group_all(flags);

	for(g = groups; g; g = g->next)
	{
		normalise_group_name(g->name);

		first = 1;
		for(m = g->members; m; m = m->next)
		{
			print_group_entry(first, 
					g->name, 
					m->u, 
					m->next?0:1
					);
			if(first)
				first = 0;
		}
	}
}

static int buildalias(int flags, char *line)
{
	/* line must look like login:gecos */

	register int i;
	register char *s;
	char *words[10];
	char *phone;
	char *office;
	char *login;
#define max_words (sizeof(words)/sizeof(char*))

	if(!line || !*line)
		return 0;

	login = skipwhite(line);

	for(s = login; *s && *s != ':'; s++)
		;

	if(*s != ':')
		return 0;

	*s++ = 0;

	for(line = s; *s && *s != ','; s++)
		;
	if(*s)
		*s++ = 0;

	for(office = s = skipwhite(s); *s && *s != ','; s++)
		;

	if(*s)
		*s++ = 0;

	trim(office);

	for(phone = s = skipwhite(s); *s && *s != ','; s++)
		;
	*s++ = 0;

	trim(phone);

	for(s=line; *s; s++)
		if(*s == '.')
			*s = ' ';

	for(i = 0, line = skipwhite(line); i < max_words && *line; i++)
	{
		words[i] = skipwhite(line);
		line = skipword(words[i]);
		if(*line)
			*line++ = 0;
	}
	if(!i)
	{
		printf("# %s has no gecos info, skipped\n", login);
		return 0;
	}
	return adduser(flags, login, office, phone, words, i);
}

static int adduser(int flags, 
		char *login, 
		char *office, /* gets modified !! */
		char *phone, 
		char *names[],
		int count
		)
{
	user_t *u = (user_t*)malloc(sizeof(user_t));
	if(u)
	{
		register char *s;
		register int i, size;

#ifndef FORWARDING_SYSTEM
		u->name = strdup(login);
#else
		u->name = strdup(primary_alias(flags, login, names, count));
#endif
		u->first = strdup(count ? names[0] : "");
		u->middle = strdup(middlename(names, count));
		u->last = strdup(count > 1 ? names[count-1] : "");
		u->fullname = strdup(fullname(names, count));
		u->phone = strdup(phone);
		u->aliases = generate_aliases(flags, login, names, count);

		u->departments = 0;

		for(size=1, s=office; s && *s; s++)
			if(*s == '/')
				size++;
		if(size)
		{
			char *deps = strdup(office);

			u->departments=(char **)malloc((size+1)*sizeof(char*));
			if(!u->departments)
				printf("# no memory for departments of %s\n",
					login);

#ifndef GENERATE_GROUP_ALL
			addtogroup(flags, GRP_ALL, u);
#endif 

			for(i=0; i<size; i++)
			{
				u->departments[i] = skipwhite(deps);

				for(s=u->departments[i]; *s && *s != '/'; s++)
					;
				if(*s)
				{
					*s++ = 0;
					deps = skipwhite(s);
				}
				trim(u->departments[i]);

				addtogroup(flags, u->departments[i], u);
			}
			u->departments[size] = 0;
		}

		if(u->departments && u->departments[0])
			u->office = u->departments[0];
		
		if(users)
		{
			user_t *ut;

			for(ut=users; ut->next; ut=ut->next)
				if(strcasecmp(
					ut->aliases[IDXinitiallastname], 
					u->aliases[IDXinitiallastname]) >0)
					break;

			if(strcasecmp(
				ut->aliases[IDXinitiallastname], 
				u->aliases[IDXinitiallastname]) > 0)
			{
				u->next = ut;
				u->prev = ut->prev;
				if(u->prev)
					u->prev->next = u;
				ut->prev = u;

				if(ut == users)
					users = u;
			}
			else
			{
				u->prev = ut;
				u->next = ut->next;
				if(u->next)
					u->next->prev = u;
				ut->next = u;
			}
		}
		else
		{
			users = u;
			u->prev = u->next = 0;
		}
	}
	return u ? 1 : 0;
}

char *fullname(char *names[], int count)
{
	register int i;
	static char buf[128];

	*buf = 0;

	if(count > 0)
		for(i=0; i<count; i++)
		{
			if(i)
				strcat(buf, " ");

			strcat(buf, names[i]);
		}
	return buf;
}

char *middlename(char *names[], int count)
{
	register int i;
	static char buf[128];

	*buf = 0;

	if(count > 2)
		for(i=1; i<count-1; i++)
		{
			if(i>1)
				strcat(buf, " ");

			strcat(buf, names[i]);
		}
	return buf;
}

#ifdef FORWARDING_SYSTEM
static char *primary_alias(int flags, char *login, char *names[], int count)
{
	register int i;
	static char alias[MAX_ALIASSIZE + 1];
	static char space[MAX_ALIASSIZE + 1];

	switch(count)
	{
	case 0:
		snprintf(alias, MAX_ALIASSIZE, "%s", login);
		break;

	case 1:
		snprintf(alias, MAX_ALIASSIZE, "%s", names[0]);
		break;
		
	case 2:
		snprintf(alias, MAX_ALIASSIZE, "%c%s", *names[0], names[1]);
		break;
		
	default:
		for(i = 0; i < count - 1 && i < MAX_ALIASSIZE; i++)
			space[i] = names[i][0];
		space[i] = 0;

		snprintf(alias, MAX_ALIASSIZE, "%s%s", space,names[count-1]);
		break;
	}
	for(i = 0; alias[i]; i++)
		alias[i] = tolower(alias[i]);

	return alias;
}
#endif

static char **generate_aliases(int flags, char *login, char *names[], int count)
{
	char alias[MAX_ALIASSIZE + 1];
	char space[MAX_ALIASSIZE + 1];
	register int index, i;
	static char empty[] = "";
	register char *s;
	char **list = 0;

	list = (char **)malloc((IDXcount + 1) * sizeof(char*));

	for(index = 0; index < IDXcount; index++) 
	{

		list[index] = empty;

		if(count <= 0)
			continue;

		switch(index)
		{

		case IDXlastname:

			snprintf(alias, MAX_ALIASSIZE, "%s", names[count-1]);
			break;

		case IDXinitiallastname:
		case IDXinitial_dot_lastname:
		case IDXinitial_dash_lastname:
		case IDXinitial_us_lastname:

			if(count <= 1)
				continue;

			snprintf(alias, MAX_ALIASSIZE, "%c", *names[0]);
			for(i = 1; i < count-1; i++)
			{
				snprintf(space, MAX_ALIASSIZE, "%s%s%c",
					alias,
					index==IDXinitiallastname?"":
					index==IDXinitial_dot_lastname?".":
					index==IDXinitial_dash_lastname?"-":
					index==IDXinitial_us_lastname?"_":
					"",
					*names[i]
					);
				strcpy(alias, space);
			}
			snprintf(space, MAX_ALIASSIZE, "%s%s%s", 
				alias,
				index==IDXinitiallastname?"":
				index==IDXinitial_dot_lastname?".":
				index==IDXinitial_dash_lastname?"-":
				index==IDXinitial_us_lastname?"_":
				"",
				names[count-1]
				);
			strcpy(alias, space);
			break;

		case IDXfirstnamelastname:
		case IDXfirstname_dot_lastname:
		case IDXfirstname_dash_lastname:
		case IDXfirstname_us_lastname:

			if(count <= 1)
				continue;

			snprintf(alias, MAX_ALIASSIZE, "%s", names[0]);
			for(i = 1; i < count; i++)
			{
				snprintf(space, MAX_ALIASSIZE, "%s%s%s", 
					alias,
					index==IDXfirstnamelastname?"":
					index==IDXfirstname_dot_lastname?".":
					index==IDXfirstname_dash_lastname?"-":
					index==IDXfirstname_us_lastname?"_":
					"",
					names[i]
					);
				strcpy(alias, space);
			}
			break;

		case IDXinitialsubfixlastname:
			if(count <= 2)
				continue;
			
			snprintf(alias, MAX_ALIASSIZE, "%c", *names[0]);
			for(i = 1; i < count; i++)
			{
				snprintf(space, MAX_ALIASSIZE, "%s%s", 
					alias,
					names[i]
					);
				strcpy(alias, space);
			}
			break;
		}
		for(s = alias; *s; s++)
			*s = tolower(*s);

		if(!is_collission(login, alias))
		{
			for(i=0; i<index; i++)
				if(!strcasecmp(list[i], alias))
					break;

			if(i == index)
				list[index] = strdup(alias);
		}

	}
	list[index] = 0;

	return list;
}

static int is_collission(char *login, char *alias)
{
	register user_t *u;
	register int i;

	for(u = users; u; u = u->next)
		for(i = 0; u->aliases && u->aliases[i]; i++)
			if(!strcasecmp(u->aliases[i], alias))
			{
				printf("# collission %s:%s with %s:%s\n",
					login,
					alias,
					u->name,
					u->aliases[i]
					);
				return 1;
			}
	return 0;
}

static group_t *getgroup(char *group)
{
	register char *s;
	register group_t *g, *gt;

	group = skipwhite(group);
	trim(group);

	if(!*group)
		return 0;

	for(g = groups; g; g = g->next)
		if(!strcasecmp(g->name, group))
			return g;

	g = (group_t*)malloc(sizeof(group_t));
	if(!g)
	{
		printf("## FAIL: alloc(group) for group %s\n", group);
		return 0;
	}
	g->members = 0;
	g->prev = g->next = 0;
	g->name = strdup(group);
	trim(g->name);

	for(s = g->name; s && *s; s++)
		*s = tolower(*s);

	if(!groups)
	{
		groups = g;
		return g;
	}
	
	for(gt = groups; gt->next; gt = gt->next)
		if(strcasecmp(gt->name, g->name)>0)
			break;

	if(strcasecmp(gt->name, g->name)>0)
	{
		g->next = gt;
		g->prev = gt->prev;
		if(g->prev)
			g->prev->next = g;
		gt->prev = g;

		if(gt == groups)
			groups = g;
	}
	else
	{
		g->prev = gt;
		g->next = gt->next;
		if(g->next)
			g->next->prev = g;
		gt->next = g;
	}
	return g;
}

static int addtogroup(int flags, char *group, user_t *u)
{
	register group_t *g;
	register group_member_t *m, *mt;

	g = getgroup(group);
	if(!g)
		return 0;

	m = (group_member_t*)malloc(sizeof(group_member_t));
	if(!m)
	{
		printf("## FAIL: alloc(member) for group %s, user %s\n",
			group,
			u->name
			);
		return 0;
	}
	m->u = u;
	m->name = m->u->name;
	m->prev = m->next = 0;

	if(!g->members)
	{
		g->members = m;
		return 1;
	}

	if(m->u->aliases 
	&& m->u->aliases[IDXinitiallastname] 
	&& *m->u->aliases[IDXinitiallastname] )
		m->name = m->u->aliases[IDXinitiallastname];
	
	for(mt = g->members; mt->next; mt = mt->next)
		if(strcasecmp(mt->name, m->name)>0)
			break;

	if(strcasecmp(mt->name, m->name)>0)
	{
		m->next = mt;
		m->prev = mt->prev;
		if(m->prev)
			m->prev->next = m;
		mt->prev = m;

		if(mt == g->members)
			g->members = m;
	}
	else
	{
		m->prev = mt;
		m->next = mt->next;
		if(m->next)
			m->next->prev = m;
		mt->next = m;
	}
	return 1;
}

static char *skipwhite(char *s)
{
	for(; *s && is_white(*s); s++)
		;

	return s;
}

static char *skipword(char *s)
{
	for(; *s && *s != ' ' && !is_white(*s) && !is_sep(*s); s++)
		;

	return s;
}

static void trim(char *s)
{
	register char *e;

	for(e = s; *e; e++)
		;
	for(--e; e >= s && is_white(*e); e--)
		*e = 0;
}
