/*
 * tstring.c
 *
 * 88-10-01 v1.0	created by greg yachuk, placed in the public domain
 * 88-10-06 v1.1	changed prerequisite list handling
 * 88-11-11 v1.2	fixed some bugs and added environment variables
 * 89-07-12 v1.3	stop appending shell commands, and flush output
 * 89-08-01 v1.4 AB	lots of new options and code
 * 89-10-30 v1.5	-f -S -q options, took some changes from v1.4
 * 90-04-18 v1.6	-b -- -W options, emulate <<, non-BSD cleanup
 */
#include <stdio.h>
#include <ctype.h>
#include <malloc.h>
#include <string.h>

#include "tstring.h"


char   *talloc(n)
int     n;
{
	char   *s;

	s = malloc(n);
	if (s == NULL)
		terror(1, "no free memory");
	return (s);
}


char   *trealloc(s, n)
char   *s;
int     n;
{
	s = realloc(s, n);
	if (s == NULL)
		talloc(n);	/* force an error */
	return (s);
}


char   *tstrncpy(s, n)
char   *s;
int     n;
{
	s = strncpy(talloc(n + 1), s, n);
	s[n] = '\0';
	return (s);
}


terror(n, s)
int     n;
char   *s;
{
	fputs("Make: ", stderr);
	fputs(s, stderr);
	putc('\n', stderr);
	if (n)
		exit(n);
}


/*
 * tstrspan -	move to the end of a quoted string, ignoring escaped quotes
 */
char   *tstrspan(str)
char   *str;
{
	char    quote;

	if (*str != '\'' && *str != '"')
		return (str + 1);

	quote = *str++;

	while (*str && *str != quote)
	{
		/* check for escaped quote */
		if (*str == '\\' && str[1] == quote)
			++str;
		++str;
	}

	return (str);
}


/*
 * tunquote -	remove quotes from a string
 */
char   *tunquote(str)
char   *str;
{
	char   *s;
	char   *d;

	d = s = str;

	while (*s)
	{
		while (*s && *s == '"')
			++s;

		while (*s && *s != '"')
			*d++ = *s++;
	}

	*d = '\0';

	return (str);
}


/*
 * tsplit -	split a string into two components, normally a directory
 *		path and a filename.  If a pointer to a directory is
 *		supplied, a string is allocated to contain the directory.
 *		The filename is returned as a pointer into the supplied
 *		string.
 */
char   *tsplit(s, seps, dp)
char   *s;
char   *seps;
char  **dp;
{
	char   *d;		/* directory portion */
	char   *f;		/* file portion */

	d = s;

	/* find the final separator */
	while ((f = strpbrk(d, seps)) != NULL)
		d = f + 1;

	/* back up to final component */
	f = d;

	/* if we are still at the beginning, there was no Directory */
	if (d == s || dp == NULL)
		d = NULL;
	else
	{
		int     len;

		/*
		 * by the time we get here, d points to the final separator
		 * char.  we can substitute a NULL for this sep-char.  Thus,
		 * we don't need to add 1 in the following length
		 * calculation. 
		 */
		len = d - s;

		d = talloc(len);
		d[--len] = '\0';
		while (--len >= 0)
			d[len] = s[len];
	}

	if (dp != NULL)
		*dp = d;

	return (f);
}


/*
 * token	- take an input string and return a token each call
 *		- default token delimiter characters are `isspace()'
 *		- separator chars are in addition to `isspace()'
 *		- text between quotes (" and ') is a single token
 *		- if requested, the separator char is returned
 *
 *	called as	s = token(string, seps, &schar);
 *		or	s = token(string, NULL, NULL);
 *
 *	followed by	s = token(NULL, seps, NULL);
 *		or	s = token(NULL, NULL, &schar);
 *
 *	returns NULL when no more tokens are available
 */
char   *token(s, sep, schar)
char   *s;
char   *sep;
char   *schar;
{
	static char *olds = NULL;

	if (s)
		olds = s;	/* we are starting all over again */

	if (schar)
		*schar = '\0';

	if (!olds || !*olds)
		return (NULL);	/* no tokens left */

	while (isspace(*olds) || (sep && strchr(sep, *olds)))
		++olds;		/* skip leading spaces and sep's */

	if (*olds == NULL)
		return (NULL);	/* remainder is all separator's */

	s = olds;

	while (*olds)
	{
		if (isspace(*olds) || (sep && strchr(sep, *olds)))
		{
			if (schar)
				*schar = *olds;
			*olds++ = '\0';	/* delimit the token */
			return (s);
		}
		else
		if (*olds == '"' || *olds == '\'')
		{
			olds = tstrspan(olds);
			if (*olds != '\0')
				++olds;	/* didn't hit eos, so skip quote */
		}
		else
			++olds;	/* otherwise, pass over char */
	}

	olds = NULL;
	return (s);		/* return last token */
}


/*
 * tokenize	- chop a string up into an array of (char *)'s
 */
char  **tokenize(input)
char   *input;
{
	char  **argv;
	int     argc = 0;
	int     alen;

	alen = 20;		/* good initial guess */
	argv = (char **) talloc((alen + 1) * sizeof(char *));

	input = token(input, NULL, NULL);	/* use default separators */
	while (input)
	{
		if (alen == argc)
			argv = (char **) trealloc((char *) argv,
					     (alen <<= 1) * sizeof(char *));
		argv[argc++] = input;
		input = token(NULL, NULL, NULL);
	}

	argv[argc] = NULL;	/* mark end of array */

	return (argv);
}


/*
 * tgets	- read input, swallowing escaped newlines as necessary
 */
char   *tgets(fd)
FILE   *fd;
{
	static char *input = NULL;
	static int inlen = 0;
	char   *ep;
	int     len;

	if (inlen == 0)
		input = talloc(inlen = 162);

	input[inlen - 2] = '\n';
	ep = input - 1;
	while ((fgets(input, inlen, fd)) != NULL)
	{
		for (;;)
		{
			while (input[inlen - 2] != '\n' && input[inlen - 2] != '\0')
			{
				len = inlen;
				input = trealloc(input, inlen <<= 1);
				ep = &input[len - 2];
				input[inlen - 2] = '\n';
				fgets(ep + 1, len + 1, fd);
			}

			while (*++ep);
			*--ep = '\0';
			do
			{
				--ep;
			} while (ep >= input && isspace(*ep));

			if (ep > input && *ep == '\\' && *--ep != '\\')
				fgets(ep + 1, inlen - (ep - input) - 1, fd);
			else
				break;
		}

		return (input);
	}

	inlen = 0;
	tfree(input);
	input = NULL;

	return (NULL);
}
