/*	utility.c	Utility functions for Wine
 *			Author:		acb
 *			Commenced:	10-9-1993
 *
 *			This unit contains the implementations of
 *			various Windows API functions that perform
 *			utility tasks; i.e., that do not fit into
 *			any major category but perform useful tasks.
 */

#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdlib.h>
#include "windows.h"
#include "stddebug.h"
/* #define DEBUG_UTILITY /* */
/* #undef  DEBUG_UTILITY /* */
#include "debug.h"


static char Copyright[] = "Copyright Andrew C. Bulhak, 1993";


/*	MulDiv is a simple function that may as well have been
 *	implemented as a macro; however Microsoft, in their infinite
 *	wisdom, have implemented it as a DLL function and therefore
 *	so should we. 
 *	Basically, it takes two 16-bit integers, multiplies them
 *	and divides by a third integer.
 */

int MulDiv(int foo, int bar, int baz)
{
	return (long)(((int)foo*bar)/baz);
};

/*	UTILITY_strip015() removes \015 (^M, CR) from a string;
 *	this is done to convert a MS-DOS-style string to a more
 *	UNIX-friendly format. Replacement is done in-place.
 */

void UTILITY_strip015(char *dest) {
	char *src = dest;

	while(*src) {
		while(*src == '\015') src++;	/* Skip \015s */
		while((*src) && (*src != '\015')) *(dest++) = *(src++);
	};
	*dest = '\0';	/* Add null terminator */
};

/**********************************************************************
 *					DebugPrintString
 */
int
DebugPrintString(char *str)
{
    fprintf(stderr, "%s", str);
    return 0;
}

/*
 *	OutputDebugString strips CRs from its (string) parameter and
 *	calls DebugPrintString(), which was written by someone else. 
 *	Since this is part of the standard Windows API, it needs no 
 *	references to nonstandard DLLs.
 */

void OutputDebugString(LPSTR foo)
{
	UTILITY_strip015(foo);
	DebugPrintString(foo);
};

/*	UTILITY_qualify(source, dest) takes the format string source and
 *	changes all the parameters to correspond to Linux integer sizes
 *	rather than Windows sizes. For example, it converts %i to %hi
 *	and %lx to %x. No array size checking is done at present.
 */

static void UTILITY_qualify(const char *source, char *dest)
{
	dprintf_utility(stddeb, "UTILITY_qualify(\"%s\", \"%s\");\n", 
			source, dest);
	if(!source) return;	/* Dumbass attack! */
	while(*source) {
		/* Find next format code. */
		while((*source != '%') && (*source)) {
			*(dest++) = *(source++);
		}
		/* Yeah, I know I shouldn't use gotos.... */
		if (!(*source)) goto loop_end;
		/* skip the '%' */
		*(dest++) = *(source++);
		/* Now insert a size qualifier, if needed. */
		switch(*source) {
			case 'i':
			case 'd':
			case 'x':
			case 'X':
			case 'u':
			case 'o':
				/* We have a 16-bit value here. */
				*(dest++) = 'h';
				break;
		};
		/* Here we go 'round the mulberry bush... */
loop_end:
	};
	*dest = '\0';
};

/*	UTILITY_argsize() evaluates the size of the argument list that
 *	accompanies a vsprintf() or wvsprintf() call.
 *	Arguments:
 *		char *format;	printf-style format string.
 *		BOOL windows;	if this is TRUE, we assume that ints are
 *				16 bits in size; otherwise we deal with
 *				32-bit variables.
 *	Returns:
 *		size (in bytes) of the arguments that follow the call.
 */

size_t UTILITY_argsize(const char *format, BOOL windows)
{
	size_t size = 0;

#define INT_SIZE (windows ? 2 : 4)

	while(*format) {
		while((*format) && (*format != '%')) format++;	/* skip ahead */
		if(*format) {
			char modifier = ' ';
			dprintf_utility(stddeb, "found:\t\"%%");
			format++;		/* skip past '%' */
			/* First skip the flags, field width, etc. */
			/* First the flags */
			if ((*format == '#') || (*format == '-') || (*format == '+')
				|| (*format == ' ')) {
				dprintf_utility(stddeb, "%c", *format);
				format++;
			}
			/* Now the field width, etc. */
			while(isdigit(*format)) {
				dprintf_utility(stddeb, "%c", *format);
				format++;
			}
			if(*format == '.') {
				dprintf_utility(stddeb, "%c", *format);
				format++;
			}
			while(isdigit(*format)) {
				dprintf_utility(stddeb, "%c", *format);
				format++;
			}
			/* Now we handle the rest */
			if((*format == 'h') || (*format == 'l') || (*format == 'L')) {
				dprintf_utility(stddeb, "%c", modifier);
				modifier = *(format++);
			}
			/* Handle the actual type. */
				dprintf_utility(stddeb, "%c\"\n", *format);
			switch(*format) {
				case 'd':
				case 'i':
				case 'o':
				case 'x':
				case 'X':
				case 'u':
				case 'c':
					size += ((modifier == 'l') ? 4 : INT_SIZE);
					break;
				case 's': size += sizeof(char *); break;
				case 'e':
				case 'E':
				case 'f':
				case 'g':
				case 'G':
					/* It doesn't look as if Windows' wvsprintf()
					   supports floating-point arguments. However,
					   I'll leave this code here just in case. */
					size += (modifier == 'L') ? sizeof(long double) : sizeof(double);
					break;
				case 'p': size += sizeof(void *); break;
				case 'n': size += sizeof(int *); break;
			};
		};
	};
#undef INT_SIZE
	dprintf_utility(stddeb, "UTILITY_argsize: returning %i\n", size);
	return size;
};

/*	UTILITY_convertArgs() creates a 32-bit argument list from a 16-bit list.
 *	This is used to allow wvsprintf() arguments to be fed through 
 *	vsprintf().
 *
 *	Arguments:
 *		char *fmt;	format string
 *		char *winarg;	Windows-style arguments
 *	
 *	Returns:
 *		malloc()ed pointer to new argument list. This should
 *		be free()d as soon as it is finished with.
 */

char *UTILITY_convertArgs(char *format, char *winarg)
{
	char *result = (char *)malloc(UTILITY_argsize(format, 0));
	char *rptr = result;

	while(*format) {
		while((*format) && (*format != '%')) format++;	/* skip ahead */
		if(*format) {
			char modifier = ' ';
			dprintf_utility(stddeb, "found:\t\"%%");
			format++;		/* skip past '%' */
			/* First skip the flags, field width, etc. */
			/* First the flags */
			if ((*format == '#') || (*format == '-') || (*format == '+')
				|| (*format == ' ')) format++;
			/* Now the field width, etc. */
			while(isdigit(*format)) format++;
			if(*format == '.') format++;
			while(isdigit(*format)) format++;
			/* Now we handle the rest */
			if((*format == 'h') || (*format == 'l') || (*format == 'L'))
				modifier = *(format++);
			/* Handle the actual type. */
			dprintf_utility(stddeb, "%c\"\n", *format);
			switch(*format) {
				case 'd':
				case 'i':
					*(((int *)rptr)++) = (modifier=='l') ? *(((int *)winarg)++) : *(((short *)winarg)++);
					break;
				case 'o':
				case 'x':
				case 'X':
				case 'u':
				case 'c':
					*(((unsigned int *)rptr)++) = (modifier=='l') ? *(((unsigned int *)winarg)++) 
						: *(((unsigned short *)winarg)++);
					break;
				case 's':
				case 'p':
				case 'n':	/* A pointer, is a pointer, is a pointer... */
					*(((char **)rptr)++) = *(((char **)winarg)++);
					break;
				case 'e':
				case 'E':
				case 'f':
				case 'g':
				case 'G':
					/* It doesn't look as if Windows' wvsprintf()
					   supports floating-point arguments. However,
					   I'll leave this code here just in case. */
					if(modifier=='L')
						*(((long double *)rptr)++) = *(((long double *)winarg)++);
					else *(((double *)rptr)++) = *(((double *)winarg)++);
					break;
			}
		}
	}
	return result;
};

#ifndef WINELIB
INT windows_wsprintf(BYTE *win_stack)
{
	LPSTR lpOutput, lpFormat, ptr;
	BYTE new_stack[1024], *stack_ptr;

	lpOutput = (LPSTR) *(DWORD*)win_stack;
	win_stack += 4;
	lpFormat = (LPSTR) *(DWORD*)win_stack;
	win_stack += 4;

	/* create 32-bit stack for libc's vsprintf() */

	for (ptr = lpFormat, stack_ptr = new_stack; *ptr; ptr++) {
		if (*ptr != '%' || *++ptr == '%')
			continue;

		/* skip width/precision */
		while (*ptr == '-' || *ptr == '+' || *ptr == '.' ||
		       *ptr == ' ' || isdigit(*ptr) || *ptr == '#')
			ptr++;
			
		switch (*ptr) {
			case 's':
				*(DWORD*)stack_ptr = *(DWORD*)win_stack;
				stack_ptr += 4;
 				win_stack += 4;
				break;
			case 'l':
				*(DWORD*)stack_ptr = *(DWORD*)win_stack;
				stack_ptr += 4;
				win_stack += 4;
				ptr++; /* skip next type character */
				break;
			case 'c':
	
/* windows' wsprintf() %c ignores 0's, we replace 0 with SPACE to make sure
   that the remaining part of the string isn't ignored by the winapp */
				
				if (*(WORD*)win_stack)
					*(DWORD*)stack_ptr = *(WORD*)win_stack;
				else
					*(DWORD*)stack_ptr = ' ';
				stack_ptr += 4;
				win_stack += 2;
				break;
			case 'd':
			case 'i':
				*(int*)stack_ptr = *(INT*)win_stack;
				stack_ptr += 4;
				win_stack += 2;
				break;
			case 'u':
			case 'x':
			case 'X':
				*(DWORD*)stack_ptr = *(WORD*)win_stack;
				stack_ptr += 4;
				win_stack += 2;
				break;
			default:
				*(DWORD*)stack_ptr = 0;
				stack_ptr += 4;
				win_stack += 4;
				fprintf(stderr, "wsprintf: oops, unknown formattype %c used!\n", *ptr);
				break;
		}
	}

	return vsprintf(lpOutput, lpFormat, new_stack);
}
#endif

/**************************************************************************
 *                wsprintf        [USER.420] (not used by relay)
 */
int wsprintf(LPSTR lpOutput, LPSTR lpFormat, ...)
{
	va_list valist;
	int ArgCnt;

	va_start(valist, lpFormat);
	ArgCnt = vsprintf(lpOutput, lpFormat, valist);
	va_end(valist);

	return ArgCnt;
}


/*	wvsprintf() is an implementation of vsprintf(). This
 *	implementation converts the arguments to 32-bit integers and
 *	calls the standard library function vsprintf().
 *
 *	Known shortcomings:
 *		wvsprintf() doesn't yet convert the arguments back after
 *		calling vsprintf(), so if Windows implements %n and a
 *		program depends on it, we're in trouble.
 */

int wvsprintf(LPSTR buf, LPSTR format, LPSTR args)
{
	char *newargs;
	int result;

	if(!buf || !format) return 0;

	/* Convert agruments to 32-bit values */
	newargs = UTILITY_convertArgs(format, args);
	result = vsprintf(buf, format, newargs);

	free(newargs);
	return result;
};
