/*
 * string operations
 */

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include "libc.h"
#include "news.h"

/* forwards */
char *findhost();

/*
 * Return strsave() of the first word in "tokens".
 * Words are delimited by spaces.
 */
char *
first(tokens)
char *tokens;
{
	return strsvto(tokens, ' ');
}

/*
 * Turn a newsgroup name into a file name, in place.
 */
void
mkfilenm(ng)
register char *ng;
{
	for (; *ng != '\0'; ng++)
		if (*ng == NGDELIM)
			*ng = FNDELIM;
}

void
trim(s)					/* trim trailing newline */
char *s;
{
	register char *nl;

	INDEX(s, '\n', nl);
	if (nl != NULL)
		*nl = '\0';
}

char *
skipsp(s)				/* skip any whitespace at *s */
register char *s;
{
	while (iswhite(*s))
		s++;
	return s;
}

char *
strsvto(s, c)				/* save s up to (but excluding) c */
char *s;
int c;
{
	register char *endp, *copy;

	endp = index(s, c);		/* find interesting part's end of s */
	if (endp != NULL)
		*endp = '\0';		/* restored below */
	copy = strsave(s);		/* copy interesting substring of s */
    	if (endp != NULL)
	    	*endp = c;
	return copy;
}

int
charcount(s, c)			/* how many c's in s? */
register char *s;
register int c;
{
	register int count = 0;

#ifdef CLASSY
	for (; (s = index(s, c)) != NULL; s = (s == NULL? NULL: s+1))
		++count;
#else
	while (*s != '\0')
		if (*s++ == c)
			++count;
#endif				/* CLASSY */
	return count;
}

char *
nullify(s)				/* return s or "" if NULL */
register char *s;
{
	if (s == NULL)
		return "";
	else
		return s;
}

/*
 * If c is NUL, hostchar will be false, so don't test (optimisation: ==).
 */
#define nothostchar(c, ch) (!hostchar(c, ch) /* || (c) == '\0' */ )
/*
 * True if c can be part of a hostname. RFC 850 allows letters, digits, periods

 * and hyphens and specifically disallows blanks. False may mean c is NUL.
 */
#define hostchar(c, ch) ((ch) = (c), \
	(isascii(ch) && isalnum(ch) || (ch) == '.' || (ch) == '-'))

/*
 * Return true iff any host in hosts appears in s, as per hostin().
 * hosts are separated by non-hostname characters.
 */
boolean
anyhostin(hosts, s)
char *hosts, *s;
{
	register char *host = hosts;

	while (*host != '\0') {
		register char *delimp;
		register int ch;
		register int delim;
		register boolean hostisin;

		while (nothostchar(*host, ch) && *host != '\0')
			++host;			/* skip leading delims */
		if (*host == '\0')		/* no more hosts */
			break;
		for (delimp = host; hostchar(*delimp, ch); delimp++)
			;			/* skip to next delim */
		delim = *delimp;		/* may be NUL */
		*delimp = '\0';			/* terminate host */
		hostisin = hostin(host, s);
		*delimp = delim;		/* restore hosts delimiter */
		if (hostisin)
			return YES;
		host = delimp;			/* advance to next host */
	}
	return NO;
}

/*
 * Return true iff host appears in s, with no characters from the alphabet
 * of legal hostname characters immediately adjacent.
 */
boolean
hostin(host, s)
register char *host, *s;
{
	return findhost(host, s) != NULL;
}

/*
 * Return the number of machines appearing in path,
 * by counting transitions from delimiters.
 * See hostin() for the rules, and the macros.
 */
int
hopcount(path)
register char *path;
{
	register int count = 0;
	register int ch;

	for (; *path != '\0'; path++)
		if (nothostchar(path[0], ch) && hostchar(path[1], ch))
			++count;	/* trailing edge of delimiters */
	return count;
}

char *
sendersite(path)
register char *path;
{
	register char *p;
	register int ch;
	static char *sender = NULL;

	nnfree(&sender);		/* free the last answer */
	for (p = path; hostchar(*p, ch); p++)
		;
	if (*p == '\0')			/* only a user name */
		return hostname();	/* a local posting */
	else {
		register int delim = *p;

		*p = '\0';
		sender = strsave(path);	/* copy the first machine name */
		*p = delim;
		return sender;
	}
}

/*
 * Canonicalise rawpath: NULL -> "", chop last site (actually user name) but no

 * its leading delimiter, and if Approved:, chop everything after the site,
 * and its trailing delimiter, from Approved: (or Sender:) (user@host).
 * Result is malloced memory.
 */
char *
canonpath(rawpath, approved, sender)
char *rawpath, *approved, *sender;
{
	register char *newpath = strsave(nullify(rawpath));
	register char *p, *lastdelim = newpath, *site = NULL;	/* p-csu v3 */
	register int ch;

	for (p = newpath; *p != '\0'; ++p)
		if (nothostchar(*p, ch))
			lastdelim = p + 1;	/* just past delim; p-csu v3 */
	if (lastdelim != NULL)
		*lastdelim = '\0';		/* omit user's name */

	if (approved != NULL) {			/* moderated article */
		site = index(approved, '@');
		if (site == NULL)
			site = index(nullify(sender), '@');
	}
	if (site != NULL) {
		p = findhost(site+1, newpath);
		if (p != NULL && *p++ != '\0')	/* delim after site? p-csu v3 */
			*p = '\0';		/* terminate newpath after site */
	}
	return newpath;
}

/*
 * Return pointer to the first byte after host in path, if any,
 * with no characters from the alphabet of legal hostname characters
 * immediately adjacent.
 * This function is a profiling hot spot, so it has been optimised.
 */
char *
findhost(host, path)
register char *host, *path;
{
	register int hostlen = strlen(host), ch;

	/* Special case: match host!path or host. */
	if (STREQN(path, host, hostlen) && nothostchar(path[hostlen], ch))
		return &path[hostlen];

	/* Match path2!host!path or path2!host. */
	while (*path != '\0')
		if (hostchar(path[0], ch))	/* can't start after here */
			++path;
		else if ((++path, STREQN(path, host, hostlen)) &&
	   	    nothostchar(path[hostlen], ch))
			return &path[hostlen];
	return NULL;
}
