/*
 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 *%	  Copyright (C) 1989, by WATCOM Systems Inc. All rights     %
 *%	  reserved. No part of this software may be reproduced	    %
 *%	  in any form or by any means - graphic, electronic or	    %
 *%	  mechanical, including photocopying, recording, taping     %
 *%	  or information storage and retrieval systems - except     %
 *%	  with the written permission of WATCOM Systems Inc.	    %
 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  WILDARGV - split DOS command line into individual arguments expanding
	     those that contain ? or *.
  This module is a substitute for the "initargv" module contained in the
  library.

  Modified:	By:		Reason:
  ---------	---		-------
  23-aug-89	John Dahms	was ignoring files with Archive or
				read only attributes turned on. (Bug fix)
  15-sep-91	F.W.Crigger	Use _LpCmdLine, _LpPgmName, _argc, _argv,
  				___Argc, ___Argv
  13-jul-92	John Dahms	add (void near *) cast in _allocate
  02-nov-93	A.F.Scian	fixed so that it compiles as C++
  22-feb-95	Greg Bentz	alpha support
  15-sep-95	Greg Bentz	ppc support
  13-dec-95	Greg Bentz	MS-style " processing
  13-dec-95	Greg Bentz	allow for historical behaviour
  06-may-96	Greg Bentz	support wide characters
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <direct.h>
#include <malloc.h>
#include <tchar.h>

#ifdef _UNICODE
    #define CMDLINE _LpwCmdLine
    #define PGMNAME _LpwPgmName
    #define _ARGC _wargc
    #define _ARGV _wargv
    #define ___ARGC ___wArgc
    #define ___ARGV ___wArgv
    #define __INIT_ARGV __wInit_Argv
#else
    #define CMDLINE _LpCmdLine
    #define PGMNAME _LpPgmName
    #define _ARGC _argc
    #define _ARGV _argv
    #define ___ARGC ___Argc
    #define ___ARGV ___Argv
    #define __INIT_ARGV __Init_Argv
#endif

#ifdef __cplusplus
extern "C" {
#endif

extern	int	__historical_splitparms;
extern	void	_Not_Enough_Memory();
_WCRTLINK extern TCHAR	*CMDLINE;
_WCRTLINK extern TCHAR	*PGMNAME;
extern	int	_ARGC;			/* argument count  */
extern	TCHAR **_ARGV;			/* argument vector */
_WCRTLINK extern int	  ___ARGC;	/* argument count */
_WCRTLINK extern TCHAR  **___ARGV;	/* argument vector */

#ifdef __cplusplus
};
#endif


static void *_allocate( unsigned amount )
    {
	void *p;

#if defined(__386__) || defined(__AXP__) || defined(__PPC__)
	p = malloc( amount );
#else
	p = _nmalloc( amount );
    #if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__)
	if( (void near *) p == NULL )  p = malloc( amount );
    #endif
#endif
	if( p == NULL )  _Not_Enough_Memory();
	return( p );
    }


static int _make_argv( TCHAR *p, TCHAR ***argv )
    {
	int		argc;
	TCHAR		*start;
	TCHAR		*new_arg;
	TCHAR		wildcard;
	TCHAR		lastchar;
	struct _tdirent *dir;
	struct _tdirent *dirent;
	TCHAR		drive[_MAX_DRIVE];
	TCHAR		directory[_MAX_DIR];
	TCHAR		name[_MAX_FNAME];
	TCHAR		extin[_MAX_EXT];
	TCHAR		pathin[_MAX_PATH];
	enum QUOTE_STATE {
	    QUOTE_NONE,		/* no " active in current parm */
	    QUOTE_DELIMITER,	/* " was first char and must be last */
	    QUOTE_STARTED	/* " was seen, look for a match */
	};
        enum QUOTE_STATE state;

	argc = 1;
	for(;;) {
	    while( *p == ' ' || *p == '\t' ) {
		++p;	/* skip over blanks or tabs */
	    }
	    if( *p == '\0' ) break;
	    /* we are at the start of a parm */
	    wildcard = 0;
	    state = QUOTE_NONE;
	    if( *p == '\"' ) {
                p++;
		state = QUOTE_DELIMITER;
	    }
	    new_arg = start = p;
	    for(;;) {
		if( *p == '\"' ) {
		    if( !__historical_splitparms ) {
			p++;
			if( state == QUOTE_NONE ) {
			    state = QUOTE_STARTED;
			    continue;
			} else if( state != QUOTE_NONE ) {
			    state = QUOTE_NONE;
			    continue;
			}
		    } else {
			if( state == QUOTE_DELIMITER ) {
			    break;
			}
		    }
		}
		if( *p == ' ' || *p == '\t' ) {
		    if( state == QUOTE_NONE ) {
			break;
		    }
		}
		if( *p == '\0' ) break;
		if( *p == '\\' ) {
		    if( !__historical_splitparms ) {
			if( p[1] == '\"' ) {
			    ++p;
			    if( p[-2] == '\\' ) {
				continue;
			    }
			}
		    } else {
			if( state == QUOTE_DELIMITER ) {
			    if( p[1] == '\"' || p[1] == '\\' ) {
				++p;
			    }
			} else {
			    if( p[1] == '\"' ) {
				++p;
			    }
			}
		    }
		} else if( *p == '?' || *p == '*' ) {
		    if( state == QUOTE_NONE ) {
			wildcard = 1;
		    }
		}
		*new_arg++ = *p++;
	    }
	    *argv = (TCHAR **) realloc( *argv, (argc+2) * sizeof( TCHAR * ) );
	    if( *argv == NULL )  _Not_Enough_Memory();
	    (*argv)[ argc ] = start;
	    ++argc;
	    lastchar = *p;
	    *new_arg = '\0';
	    ++p;
	    if( wildcard ) {
		/* expand file names */
		dir = _topendir( start );
		if( dir != NULL ) {
		    --argc;
		    _tsplitpath( start, drive, directory, name, extin );
		    for(;;) {
			dirent = _treaddir( dir );
			if( dirent == NULL ) break;
			if( dirent->d_attr &
			  (_A_HIDDEN+_A_SYSTEM+_A_VOLID+_A_SUBDIR) ) continue;
			_tsplitpath( dirent->d_name, NULL, NULL, name, extin );
			_tmakepath( pathin, drive, directory, name, extin );
			*argv = (TCHAR **) realloc( *argv, (argc+2) * sizeof( TCHAR * ) );
			if( *argv == NULL )  _Not_Enough_Memory();
			new_arg = (TCHAR *)
			    _allocate( (_tcslen( pathin ) + 1) * sizeof( TCHAR ) );
			_tcscpy( new_arg, pathin );
			(*argv)[argc++] = new_arg;
		    }
		    _tclosedir( dir );
		}
	    }
	    if( lastchar == '\0' ) break;
	}
	return( argc );
    }


#ifdef __cplusplus
extern "C"
#endif
void __INIT_ARGV()
    {
	TCHAR *cln;

	_ARGV = (TCHAR **) _allocate( 2 * sizeof( TCHAR * ) );
	_ARGV[0] = PGMNAME;	/* fill in program name */
	cln = _allocate( (_tcslen( CMDLINE ) + 1) * sizeof( TCHAR ) );
	_tcscpy( cln, CMDLINE );
	_ARGC = _make_argv( cln, &_ARGV );
	_ARGV[_ARGC] = NULL;
	___ARGC = _ARGC;
	___ARGV = _ARGV;
	__targc = ___ARGC;	/* from stdlib.h */
	__targv = ___ARGV;	/* from stdlib.h */
    }
