/* $Source$
 * $State$
 * $Revision$
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libsys.h"

struct for_main {
	int argc;
	char **argv;
	char **envp;
};

/*
 * Marshal the command line and environment variable data provided by
 * MS-DOS, into the standard format expected by a C main(...) routine.  The
 * environment variable data has been copied to the near data segment.
 *
 * Return zero if everything went well, non-zero otherwise.
 */
int _sys_initmain(char *arg_data, size_t n_env_vars, char *env_data,
    unsigned msdos_ver_major, struct for_main *out)
{
	char **argv, **envp;
	char c, *p, **pp;
	unsigned char n;

	/*
	 * Allocate space for argv[] for the maximum possible number of
	 * arguments.  We can shrink this later.
	 */
	if ((argv = sbrk(65 * sizeof(char *))) == OUT_OF_MEMORY)
		return -1;
	out->argv = argv;

	/* Sanity-check the original command line. */
	p = arg_data;
	n = (unsigned char)*p;
	if (n > 0x7E || p[1 + n] != '\r')
		return -1;

	/* Initialize argv[0] to an empty string first. */
	*p = 0;
	argv[0] = p;

	/*
	 * Split the command line string into separate arguments.
	 * TODO: handle double-quoting, backslashes, etc.
	 */
	pp = argv + 1;
	do
	{
		do
			c = *++p;
		while (c == ' ' || c == '\t');

		if (c != '\r')
		{
			*pp++ = p;
			do
				c = *++p;
			while (c != ' ' && c != '\t' && c != '\r');
			*p = 0;
		}
	} while (c != '\r');

	/* Wrap up argv[].  Compute argc.  Free up unneeded space. */
	out->argc = pp - argv;
	*pp++ = NULL;
	if (brk(pp) != 0)
		return -1;

	/* Allocate space for envp[]. */
	if ((envp = sbrk((n_env_vars + 1) * sizeof(char *))) == OUT_OF_MEMORY)
		return -1;
	out->envp = envp;

	/*
	 * Populate envp[].  The environment data is simply a series of ASCIIZ
	 * strings, one after another, terminated by an empty string.
	 */
	pp = envp;
	p = env_data;
	while (*p)
	{
		*pp++ = p;
		while (*++p);
		++p;
	}
	*pp = NULL;

	/*
	 * If there is a program name (MS-DOS version is 3 or above), point
	 * argv[0] to it.
	 *
	 * p points to the zero byte just before the count of strings (a
	 * shortword) following the environment variables, so advance p by 3
	 * bytes to get at the program name.
	 */
	if (msdos_ver_major >= 3)
	{
		p += 3;
		argv[0] = p;
	}

	/* We are done. */
	return 0;
}
