/*****

      $Source: d:\gunther\source\grc/RCS/rcutils.c,v $
      $Author: gunther $
      $Date: 1996/01/02 23:17:24 $
      $Revision: 1.4 $

	Changed by: Fred Kiefer (kiefer@isys.de) 1996/6/2

 *****/

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <errno.h>
#include "port.h"

#include "rcutils.h"
#include "wstring.h"
#include "resnt.h"


extern int yyerror();

void *
xmalloc(size_t n)
{
	long *q;

	q = malloc(n);
	if (q == NULL)
		err_msg(GRC_ERR_MEM | GRC_ERR_FATAL);
	return q;
}

node*
list_new()
{
	/* Creation of a new list. This function and the next two implement
		a very simple list with a dummy head, that can store any kind 
		of data. A count of the elements in the list is kept in the head
		as an optimisation */
	node* list;

	list = (node*)xmalloc(sizeof(node));
	list->u.count = 0;
	list->next = NULL;
	return list;
}


void
list_add(node *a_list, void *entry)
{
	/* Add a new entry to a list. The entry may be what ever 
		fits into a void *, even just an integer. */
	node *p;
	node *fresh = (node*)xmalloc(sizeof(node));

	fresh->next = NULL;
	fresh->u.data = entry;

	/* go to the end of the list */
	for (p = a_list; p->next; p = p->next);

	a_list->u.count++;
	p->next = fresh;
}

void
list_free(node *a_list, int free_entries)
{
	/* Frees the entries of the list and the list itself.
		The data of the entries is also freeed if free_entries is true. */
	node *p;
	node *q;
	
	for(p = a_list->next; p; p = q)
	{
		q = p->next;
		if (free_entries)
			free(p->u.data);

		free(p);
	}
	free(a_list);
}

char *
strtoupper(char *text)
{
	/* Uppercases a string in place */
	/* This is here in case we ever decide to implement
		a special handling for extra characters */
	char *p;

	for(p = text; *p; p++)
	{
		*p = toupper(*p);
	}
	return text;
}

int
expand_string(char *to, char *from)
{
	/* Does all the special processing a C compiler
		normaly does on its string input. 
		NOTE: If the next character after '\' isnt 
		a special character it is thrown away. */
	char *s;
	int i = 0;

	debug(("Expanding string %s", from));
	for(s = from; *s; s++)
	{
		if (*s == '\\')
		{
			switch(*(++s)){
			case 'n':	to[i++] = '\n'; break;
			case 't':	to[i++] = '\t'; break;
			case 'v':	to[i++] = '\v'; break;
			case 'b':	to[i++] = '\b'; break;
			case 'r':	to[i++] = '\r'; break;
			case 'f':	to[i++] = '\f'; break;
			case 'a':	to[i++] = '\a'; break;
			case '\\':	to[i++] = '\\'; break;
			case '\?':	to[i++] = '\?'; break;
			case '\'': to[i++] = '\''; break;
			case '\"':	to[i++] = '\"'; break;
			case 'x':
			{
				/* hex number */
				char c = '\0';

				while(isxdigit(*(++s)))
				{
					c = c << 4;
					if (isdigit(*s))
					{
						c += *s - '0';
					}
					else
					{
						c += toupper(*s) - 'A' + 10;
					}
				}
				s--;
				to[i++] = c;
				break;
			}
			default:
			{
				/* oct number */
				char c = '\0';
				char *t = s;

				while(isdigit(*s) && *s < '8')
				{
					c = c << 3;
					c += *s - '0';
					s++;
				}

				if (t != s) {
					s--;
					to[i++] = c;
				}
				break;
			}
			}
		}
		else
		{
			to[i++] = *s;
		}
	}
	to[i] = '\0';
	return i;
}

int
int_log2(int n)
{
	/* Integer logarithmn of n. 
		We should find a better way here */
	return (int) ceil(log((double) n) / log(2.0));
}

int
getIDlength(wchar_t *id)
{
	if(*id == INVAL_WCHAR)
		/* id points to a WORD identifier */
		return 2 * sizeof(wchar_t);
	else
		/* id points to an UNICODE identifier string */
		return (wstrlen(id) + 1) * sizeof(wchar_t);
}

char *
search_include(char *filename)
{
	static char path[260];

	_searchenv(filename, "C_INCLUDE_PATH", path);
	if (*path)
		return path;

	_searchenv(filename, "CPLUS_INCLUDE_PATH", path);
	if (*path)
		return path;

	_searchenv(filename, "RCINCLUDE", path);
	if (*path)
		return path;

	return filename;
}

int 
filesize(const char *filename)
{
	struct stat s;

	stat(filename, &s);
	return s.st_size;
}

int
is_header(const char *filename)
{
	char *ptr = (char *) filename;
	char *ptr1;
    
	while((ptr1 = strchr(ptr, '.')) != 0) 
		ptr = ptr1+1;

	return !stricmp(ptr, "h");
}


int 
is_resfile(const char *filename)
{
	char *ptr = (char *) filename;
	char *ptr1;
    
	while((ptr1 = strchr(ptr, '.')) != 0) 
		ptr = ptr1+1;

	return !stricmp(ptr, "res");
}


int 
is_rcfile(const char *filename)
{
	char *ptr = (char *) filename;
	char *ptr1;
    
	while((ptr1 = strchr(ptr, '.')) != 0) 
		ptr = ptr1+1;

	return !stricmp(ptr, "rc");
}

/*-----------------------------------------
 * basefile()
 * - returns the real filename from a
 *   complete path string (like the UNIX
 *   'basename' command)
 *
 * Parameters:
 * - path:          points to the complete
 *                  path string
 * 
 * Returns:
 * the filename portion of the complete path
 *-----------------------------------------*/
char *
basefile(char *path)
{
    char *ptr = strchr(path, 0);
    
#if defined(_WIN32) || defined(_MSDOS_)
    while((*ptr != '/') && 
          (*ptr != '\\') && 
          (ptr >= path)) 
        ptr--;
#else
    while((ptr != '/') &&
          (ptr >= path))
        ptr--;
#endif

    ptr++;
	return ptr;
}


struct errmsg
{
    int code;
    char *text;
};

/* some error messages */
static const struct errmsg error_messages[] =
{
	{GRC_OK, "No error"},
    { GRC_ERR_OPEN, "Error opening file" },
    { GRC_ERR_READ, "Error reading file" },
    { GRC_ERR_WIN32, "Win32 error" },
    { GRC_ERR_MEM, "Out of memory" },
    { GRC_ERR_FILENOTFOUND, "File not found" },
    { GRC_NO_RESOURCE, "That's no resource." },
    { GRC_ERR_SYNTAX, "Syntax error" },
    { GRC_ERR_EOF, "Unexpected end of file" },
    { GRC_ERR_INVALIDSTRING, "String value expected" },
    { GRC_ERR_UNEXPECTED_EOF, "Unexpected end of file" },
    { GRC_ERR_UNDEFINED, "Undefined symbol" },
    { GRC_ERR_BEGIN_UNEXPECT, "Unexpected BEGIN statement" },
    { GRC_ERR_END_UNEXPECT, "Unexpected END statement" },
    { GRC_ERR_NUMBER_EXPECTED, "Numeric value expected." },
    { GRC_ERR_PARSE, "Parse error" },
    { GRC_COMPLETE, "Item already complete." },
    { GRC_ERR_CTRL_UNEXPECT, "Dialog: unexpected control definition" },
    { GRC_ERR_CTRL_EXPECT, "Dialog: control definition expected" },
    { GRC_ERR_EXPR, "Error in expression" },
    { GRC_ERR_SUBLANG_EXPECT, "Sub-language specifier expected" },
    { GRC_NOT_IMPLEMENTED_YET, "Feature not implemented yet." },
    { GRC_err_bmp_filename_expected, "Bitmap filename expected" },
    { GRC_err_ico_filename_expected, "Icon filename expected" },    
    { GRC_err_font_filename_expected, "Font filename expected" },    
    { GRC_err_dlg_invalid, "Invalid DIALOG definition" },
    { GRC_err_invalid_stmnt, "Invalid statement at this place" },
    { GRC_err_dlg_ctl_expected, "dialog item statement expected" },
    { GRC_err_menu_invalid, "Invalid MENU definition" },    
    { GRC_err_menuitem_invalid, "Invalid MENUITEM definition" },
    { GRC_err_strtable_invalid, "Invalid STRINGTABLE definition" },
    { GRC_err_string_invalid, "Invalid string definition" },    
    { GRC_err_invalid_accelerator, "Invalid ACCELERATORS definition" },
    { GRC_err_one_char, "Multi-character strings are not supported at this place" }, 
    { GRC_err_invalid_icon_file, "Invalid icon file" },
    { GRC_err_invalid_cursor_file, "Invalid cursor file" },
    { GRC_err_nomem, "Insufficient memory available" },
    { GRC_err_unicode_ansi_conversion, "UNICODE to ANSI string conversion failed" },
    { GRC_err_ansi_unicode_conversion, "ANSI to UNICODE string conversion failed" },
    { GRC_err_invalid_userres, "Invalid user defined ressource" },
    { GRC_err_invalid_rcdata, "Invalid rcdata ressource" },
    { GRC_err_invalid_versioninfo, "Invalid version info ressource" },
    { 0, 0 }
};

void
err_msg(int code)
{
	/* Display matchin error message and exit if it is fatal */
	int fatal = code & GRC_ERR_FATAL;

	code = code & ~GRC_ERR_FATAL;
	yyerror(getErrorText(code));
	if (fatal)
		exit(code);
}


char *
getErrorText(int code)
{
    int i;

    for(i = 0; error_messages[i].text; i++)
    {
        if(error_messages[i].code == code)
            return error_messages[i].text;
    }

    return "No special error message found.";
}


void
open_error(const char *filename)
{
    char err[512];
    sprintf(err, "Could not open '%s': %s", filename, strerror(errno));
    yyerror(err);
}


void 
read_error(const char *filename)
{
    char err[512];
    sprintf(err, "Could not read from '%s': %s", filename, strerror(errno));
    yyerror(err);
}


void
write_error(const char *filename)
{
    char err[512];
    sprintf(err, "Could not write to '%s': %s", filename, strerror(errno));
    yyerror(err);
}

void
seek_error(const char *filename)
{
	char err[512];

	sprintf(err, "Could not seek inside '%s': %s", filename, strerror(errno));
	yyerror(err);
}
