/* util.c
 *
 * various string, file, list operations.
 *
 *
 * $Id: util.c 16943 2008-10-06 08:02:35Z campbellbarton $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 * 
 */

#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h> /* for log10 */

#include "MEM_guardedalloc.h"

#include "DNA_listBase.h"
#include "DNA_userdef_types.h"

#include "BLI_blenlib.h"
#include "BLI_storage.h"
#include "BLI_storage_types.h"
#include "BLI_dynamiclist.h"

#include "BLI_util.h"
#include "BKE_utildefines.h"


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef WIN32 
#include <unistd.h>
#else
#include <io.h>
#endif

#ifdef WIN32

#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#include <windows.h>
#include <shlobj.h>

#include "BLI_winstuff.h"

/* for duplicate_defgroup */
#if !(defined vsnprintf)
#define vsnprintf _vsnprintf
#endif

#endif


#ifndef WIN32
#include <sys/time.h>
#endif

#ifdef __APPLE__
#include <sys/param.h>
#include <CoreFoundation/CoreFoundation.h>
#endif

#ifdef __linux__
#include "binreloc.h"
#endif

/* local */

static int add_win32_extension(char *name);

/* implementation */

/* Ripped this from blender.c */
void addlisttolist(ListBase *list1, ListBase *list2)
{
	if (list2->first==0) return;

	if (list1->first==0) {
		list1->first= list2->first;
		list1->last= list2->last;
	}
	else {
		((Link *)list1->last)->next= list2->first;
		((Link *)list2->first)->prev= list1->last;
		list1->last= list2->last;
	}
	list2->first= list2->last= 0;
}

int BLI_stringdec(char *string, char *kop, char *start, unsigned short *numlen)
{
	unsigned short len, len2, nums = 0, nume = 0;
	short i, found = 0;

	len2 = len = strlen(string);
	
	if (len > 6) {
		if (BLI_strncasecmp(string + len - 6, ".blend", 6) == 0) len -= 6;
		else if (BLI_strncasecmp(string + len - 6, ".trace", 6) == 0) len -= 6;
	}
	
	if (len > 9) {
		if (BLI_strncasecmp(string + len - 9, ".blend.gz", 9) == 0) len -= 9;
	}
		
	if (len == len2) {
		if (len > 4) {
			/* handle .jf0 en .jf1 for jstreams */
			if (BLI_strncasecmp(string + len - 4, ".jf", 3) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".tga", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".jpg", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".png", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".txt", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".cyc", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".enh", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".rgb", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".psx", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".ble", 4) == 0) len -= 4;
			else if (BLI_strncasecmp(string + len - 4, ".exr", 4) == 0) len -= 4;
		}
	}
	
	for (i = len - 1; i >= 0; i--) {
		if (string[i] == '/') break;
		if (isdigit(string[i])) {
			if (found){
				nums = i;
			}
			else{
				nume = i;
				nums = i;
				found = 1;
			}
		}
		else {
			if (found) break;
		}
	}
	if (found){
		if (start) strcpy(start,&string[nume+1]);
		if (kop) {
			strcpy(kop,string);
			kop[nums]=0;
		}
		if (numlen) *numlen = nume-nums+1;
		return ((int)atoi(&(string[nums])));
	}
	if (start) strcpy(start, string + len);
	if (kop) {
		strncpy(kop, string, len);
		kop[len] = 0;
	}
	if (numlen) *numlen=0;
	return 0;
}


void BLI_stringenc(char *string, char *kop, char *start, unsigned short numlen, int pic)
{
	char numstr[10]="";
	unsigned short len,i;

	strcpy(string,kop);
	
	if (pic>0 || numlen==4) {
		len= sprintf(numstr,"%d",pic);
		
		for(i=len;i<numlen;i++){
			strcat(string,"0");
		}
		strcat(string,numstr);
	}
	strcat(string, start);
}


void BLI_newname(char *name, int add)
{
	char head[128], tail[128];
	int pic;
	unsigned short digits;
	
	pic = BLI_stringdec(name, head, tail, &digits);
	
	/* are we going from 100 -> 99 or from 10 -> 9 */
	if (add < 0 && digits < 4 && digits > 0) {
		int i, exp;
		exp = 1;
		for (i = digits; i > 1; i--) exp *= 10;
		if (pic >= exp && (pic + add) < exp) digits--;
	}
	
	pic += add;
	
	if (digits==4 && pic<0) pic= 0;
	BLI_stringenc(name, head, tail, digits, pic);
}

/* little helper macro for BLI_uniquename */
#ifndef GIVE_STRADDR
	#define GIVE_STRADDR(data, offset) ( ((char *)data) + offset )
#endif

/* Generic function to set a unique name. It is only designed to be used in situations
 * where the name is part of the struct, and also that the name is at most 128 chars long.
 * 
 * For places where this is used, see constraint.c for example...
 *
 * 	name_offs: should be calculated using offsetof(structname, membername) macro from stddef.h
 *	len: maximum length of string (to prevent overflows, etc.)
 *	defname: the name that should be used by default if none is specified already
 */
void BLI_uniquename(ListBase *list, void *vlink, char defname[], short name_offs, short len)
{
	Link *link;
	char tempname[128];
	int	number = 1, exists = 0;
	char *dot;
	
	/* Make sure length can be handled */
	if ((len < 0) || (len > 128))
		return;
	
	/* See if we are given an empty string */
	if (ELEM(NULL, vlink, defname))
		return;
	
	if (GIVE_STRADDR(vlink, name_offs) == '\0') {
		/* give it default name first */
		BLI_strncpy(GIVE_STRADDR(vlink, name_offs), defname, len);
	}
	
	/* See if we even need to do this */
	if (list == NULL)
		return;
	
	for (link = list->first; link; link= link->next) {
		if (link != vlink) {
			if (!strcmp(GIVE_STRADDR(link, name_offs), GIVE_STRADDR(vlink, name_offs))) {
				exists = 1;
				break;
			}
		}
	}
	if (exists == 0)
		return;

	/* Strip off the suffix */
	dot = strchr(GIVE_STRADDR(vlink, name_offs), '.');
	if (dot)
		*dot=0;
	
	for (number = 1; number <= 999; number++) {
		BLI_snprintf(tempname, 128, "%s.%03d", GIVE_STRADDR(vlink, name_offs), number);
		
		exists = 0;
		for (link= list->first; link; link= link->next) {
			if (vlink != link) {
				if (!strcmp(GIVE_STRADDR(link, name_offs), tempname)) {
					exists = 1;
					break;
				}
			}
		}
		if (exists == 0) {
			BLI_strncpy(GIVE_STRADDR(vlink, name_offs), tempname, len);
			return;
		}
	}
}


void BLI_addhead(ListBase *listbase, void *vlink)
{
	Link *link= vlink;

	if (link == NULL) return;
	if (listbase == NULL) return;

	link->next = listbase->first;
	link->prev = NULL;

	if (listbase->first) ((Link *)listbase->first)->prev = link;
	if (listbase->last == NULL) listbase->last = link;
	listbase->first = link;
}


void BLI_addtail(ListBase *listbase, void *vlink)
{
	Link *link= vlink;

	if (link == NULL) return;
	if (listbase == NULL) return;

	link->next = NULL;
	link->prev = listbase->last;

	if (listbase->last) ((Link *)listbase->last)->next = link;
	if (listbase->first == 0) listbase->first = link;
	listbase->last = link;
}


void BLI_remlink(ListBase *listbase, void *vlink)
{
	Link *link= vlink;

	if (link == NULL) return;
	if (listbase == NULL) return;

	if (link->next) link->next->prev = link->prev;
	if (link->prev) link->prev->next = link->next;

	if (listbase->last == link) listbase->last = link->prev;
	if (listbase->first == link) listbase->first = link->next;
}


void BLI_freelinkN(ListBase *listbase, void *vlink)
{
	Link *link= vlink;

	if (link == NULL) return;
	if (listbase == NULL) return;

	BLI_remlink(listbase,link);
	MEM_freeN(link);
}


void BLI_insertlink(ListBase *listbase, void *vprevlink, void *vnewlink)
{
	Link *prevlink= vprevlink;
	Link *newlink= vnewlink;

	/* newlink comes after prevlink */
	if (newlink == NULL) return;
	if (listbase == NULL) return;
	
	/* empty list */
	if (listbase->first == NULL) { 
		
		listbase->first= newlink;
		listbase->last= newlink;
		return;
	}
	
	/* insert before first element */
	if (prevlink == NULL) {	
		newlink->next= listbase->first;
		newlink->prev= 0;
		newlink->next->prev= newlink;
		listbase->first= newlink;
		return;
	}

	/* at end of list */
	if (listbase->last== prevlink) 
		listbase->last = newlink;

	newlink->next= prevlink->next;
	prevlink->next= newlink;
	if (newlink->next) newlink->next->prev= newlink;
	newlink->prev= prevlink;
}

/* This uses insertion sort, so NOT ok for large list */
void BLI_sortlist(ListBase *listbase, int (*cmp)(void *, void *))
{
	Link *current = NULL;
	Link *previous = NULL;
	Link *next = NULL;
	
	if (cmp == NULL) return;
	if (listbase == NULL) return;

	if (listbase->first != listbase->last)
	{
		for( previous = listbase->first, current = previous->next; current; current = next )
		{
			next = current->next;
			previous = current->prev;
			
			BLI_remlink(listbase, current);
			
			while(previous && cmp(previous, current) == 1)
			{
				previous = previous->prev;
			}
			
			BLI_insertlinkafter(listbase, previous, current);
		}
	}
}

void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink)
{
	Link *prevlink= vprevlink;
	Link *newlink= vnewlink;

	/* newlink before nextlink */
	if (newlink == NULL) return;
	if (listbase == NULL) return;

	/* empty list */
	if (listbase->first == NULL) { 
		listbase->first= newlink;
		listbase->last= newlink;
		return;
	}
	
	/* insert at head of list */
	if (prevlink == NULL) {	
		newlink->prev = NULL;
		newlink->next = listbase->first;
		((Link *)listbase->first)->prev = newlink;
		listbase->first = newlink;
		return;
	}

	/* at end of list */
	if (listbase->last == prevlink) 
		listbase->last = newlink;

	newlink->next = prevlink->next;
	newlink->prev = prevlink;
	prevlink->next = newlink;
	if (newlink->next) newlink->next->prev = newlink;
}

void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink)
{
	Link *nextlink= vnextlink;
	Link *newlink= vnewlink;

	/* newlink before nextlink */
	if (newlink == NULL) return;
	if (listbase == NULL) return;

	/* empty list */
	if (listbase->first == NULL) { 
		listbase->first= newlink;
		listbase->last= newlink;
		return;
	}
	
	/* insert at end of list */
	if (nextlink == NULL) {	
		newlink->prev= listbase->last;
		newlink->next= 0;
		((Link *)listbase->last)->next= newlink;
		listbase->last= newlink;
		return;
	}

	/* at beginning of list */
	if (listbase->first== nextlink) 
		listbase->first = newlink;

	newlink->next= nextlink;
	newlink->prev= nextlink->prev;
	nextlink->prev= newlink;
	if (newlink->prev) newlink->prev->next= newlink;
}


void BLI_freelist(ListBase *listbase)
{
	Link *link, *next;

	if (listbase == NULL) 
		return;
	
	link= listbase->first;
	while (link) {
		next= link->next;
		free(link);
		link= next;
	}
	
	listbase->first= NULL;
	listbase->last= NULL;
}

void BLI_freelistN(ListBase *listbase)
{
	Link *link, *next;

	if (listbase == NULL) return;
	
	link= listbase->first;
	while (link) {
		next= link->next;
		MEM_freeN(link);
		link= next;
	}
	
	listbase->first= NULL;
	listbase->last= NULL;
}


int BLI_countlist(ListBase *listbase)
{
	Link *link;
	int count = 0;
	
	if (listbase) {
		link = listbase->first;
		while (link) {
			count++;
			link= link->next;
		}
	}
	return count;
}

void *BLI_findlink(ListBase *listbase, int number)
{
	Link *link = NULL;

	if (number >= 0) {
		link = listbase->first;
		while (link != NULL && number != 0) {
			number--;
			link = link->next;
		}
	}

	return link;
}

int BLI_findindex(ListBase *listbase, void *vlink)
{
	Link *link= NULL;
	int number= 0;
	
	if (listbase == NULL) return -1;
	if (vlink == NULL) return -1;
	
	link= listbase->first;
	while (link) {
		if (link == vlink)
			return number;
		
		number++;
		link= link->next;
	}
	
	return -1;
}

/*=====================================================================================*/
/* Methods for access array (realloc) */
/*=====================================================================================*/

/* remove item with index */
static void rem_array_item(struct DynamicArray *da, unsigned int index)
{
	da->items[index]=NULL;
	da->count--;
	if(index==da->last_item_index){
		while((!da->items[da->last_item_index]) && (da->last_item_index>0)){
			da->last_item_index--;
		}
	}
}

/* add array (if needed, then realloc) */
static void add_array_item(struct DynamicArray *da, void *item, unsigned int index)
{
	/* realloc of access array */
	if(da->max_item_index < index){
		unsigned int i, max = da->max_item_index;
		void **nitems;

		do {
			da->max_item_index += PAGE_SIZE;	/* OS can allocate only PAGE_SIZE Bytes */
		} while(da->max_item_index<=index);

		nitems = (void**)MEM_mallocN(sizeof(void*)*(da->max_item_index+1), "dlist access array");
		for(i=0;i<=max;i++)
			nitems[i] = da->items[i];

		/* set rest pointers to the NULL */
		for(i=max+1; i<=da->max_item_index; i++)
			nitems[i]=NULL;

		MEM_freeN(da->items);		/* free old access array */
		da->items = nitems;
	}

	da->items[index] = item;
	da->count++;
	if(index > da->last_item_index) da->last_item_index = index;
}

/* free access array */
static void destroy_array(DynamicArray *da)
{
	da->count=0;
	da->last_item_index=0;
	da->max_item_index=0;
	MEM_freeN(da->items);
	da->items = NULL;
}

/* initialize dynamic array */
static void init_array(DynamicArray *da)
{
	unsigned int i;

	da->count=0;
	da->last_item_index=0;
	da->max_item_index = PAGE_SIZE-1;
	da->items = (void*)MEM_mallocN(sizeof(void*)*(da->max_item_index+1), "dlist access array");
	for(i=0; i<=da->max_item_index; i++) da->items[i]=NULL;
}

/* reinitialize dynamic array */
static void reinit_array(DynamicArray *da)
{
	destroy_array(da);
	init_array(da);
}

/*=====================================================================================*/
/* Methods for two way dynamic list with access array */
/*=====================================================================================*/

/* create new two way dynamic list with access array from two way dynamic list
 * it doesn't copy any items to new array or something like this It is strongly
 * recomended to use BLI_dlist_ methods for adding/removing items from dynamic list
 * unless you can end with inconsistence system !!! */
DynamicList *BLI_dlist_from_listbase(ListBase *lb)
{
	DynamicList *dlist;
	Link *item;
	int i=0, count;
	
	if(!lb) return NULL;
	
	count = BLI_countlist(lb);

	dlist = MEM_mallocN(sizeof(DynamicList), "temp dynamic list");
	/* ListBase stuff */
	dlist->lb.first = lb->first;
	dlist->lb.last = lb->last;
	/* access array stuff */
	dlist->da.count=count;
	dlist->da.max_item_index = count-1;
	dlist->da.last_item_index = count -1;
	dlist->da.items = (void*)MEM_mallocN(sizeof(void*)*count, "temp dlist access array");

	item = (Link*)lb->first;
	while(item){
		dlist->da.items[i] = (void*)item;
		item = item->next;
		i++;
	}

	/* to prevent you of using original ListBase :-) */
	lb->first = lb->last = NULL;

	return dlist;
}

/* take out ListBase from DynamicList and destroy all temporary structures of DynamicList */
ListBase *BLI_listbase_from_dlist(DynamicList *dlist, ListBase *lb)
{
	if(!dlist) return NULL;

	if(!lb) lb = (ListBase*)MEM_mallocN(sizeof(ListBase), "ListBase");
	
	lb->first = dlist->lb.first;
	lb->last = dlist->lb.last;

	/* free all items of access array */
	MEM_freeN(dlist->da.items);
	/* free DynamicList*/
	MEM_freeN(dlist);

	return lb;
}

/* return pointer at item from th dynamic list with access array */
void *BLI_dlist_find_link(DynamicList *dlist, unsigned int index)
{
	if(!dlist || !dlist->da.items) return NULL;

	if((index <= dlist->da.last_item_index) && (index >= 0) && (dlist->da.count>0)){
      		return dlist->da.items[index];
	}
	else {
		return NULL;
	}
}

/* return count of items in the dynamic list with access array */
unsigned int BLI_count_items(DynamicList *dlist)
{
	if(!dlist) return 0;

	return dlist->da.count;
}

/* free item from the dynamic list with access array */
void BLI_dlist_free_item(DynamicList *dlist, unsigned int index)
{
	if(!dlist || !dlist->da.items) return;
	
	if((index <= dlist->da.last_item_index) && (dlist->da.items[index])){
		BLI_freelinkN(&(dlist->lb), dlist->da.items[index]);
		rem_array_item(&(dlist->da), index);
	}
}

/* remove item from the dynamic list with access array */
void BLI_dlist_rem_item(DynamicList *dlist, unsigned int index)
{
	if(!dlist || !dlist->da.items) return;
	
	if((index <= dlist->da.last_item_index) && (dlist->da.items[index])){
		BLI_remlink(&(dlist->lb), dlist->da.items[index]);
		rem_array_item(&(dlist->da), index);
	}
}

/* add item to the dynamic list with access array (index) */
void* BLI_dlist_add_item_index(DynamicList *dlist, void *item, unsigned int index)
{
	if(!dlist || !dlist->da.items) return NULL;

	if((index <= dlist->da.max_item_index) && (dlist->da.items[index])) {
		/* you can't place item at used index */
		return NULL;
	}
	else {
		add_array_item(&(dlist->da), item, index);
		BLI_addtail(&(dlist->lb), item);
		return item;
	}
}

/* destroy dynamic list with access array */
void BLI_dlist_destroy(DynamicList *dlist)
{
	if(!dlist) return;

	BLI_freelistN(&(dlist->lb));
	destroy_array(&(dlist->da));
}

/* initialize dynamic list with access array */
void BLI_dlist_init(DynamicList *dlist)
{
	if(!dlist) return;

	dlist->lb.first = NULL;
	dlist->lb.last = NULL;

	init_array(&(dlist->da));
}

/* reinitialize dynamic list with acces array */
void BLI_dlist_reinit(DynamicList *dlist)
{
	if(!dlist) return;
	
	BLI_freelistN(&(dlist->lb));
	reinit_array(&(dlist->da));
}

/*=====================================================================================*/

char *BLI_strdupn(const char *str, int len) {
	char *n= MEM_mallocN(len+1, "strdup");
	memcpy(n, str, len);
	n[len]= '\0';
	
	return n;
}
char *BLI_strdup(const char *str) {
	return BLI_strdupn(str, strlen(str));
}

char *BLI_strncpy(char *dst, const char *src, int maxncpy) {
	int srclen= strlen(src);
	int cpylen= (srclen>(maxncpy-1))?(maxncpy-1):srclen;
	
	memcpy(dst, src, cpylen);
	dst[cpylen]= '\0';
	
	return dst;
}

int BLI_snprintf(char *buffer, size_t count, const char *format, ...)
{
	int n;
	va_list arg;

	va_start(arg, format);
	n = vsnprintf(buffer, count, format, arg);
	
	if (n != -1 && n < count) {
		buffer[n] = '\0';
	} else {
		buffer[count-1] = '\0';
	}
	
	va_end(arg);
	return n;
}

int BLI_streq(char *a, char *b) {
	return (strcmp(a, b)==0);
}
int BLI_strcaseeq(char *a, char *b) {
	return (BLI_strcasecmp(a, b)==0);
}

/* ******************** string encoding ***************** */

/* This is quite an ugly function... its purpose is to
 * take the dir name, make it absolute, and clean it up, replacing
 * excess file entry stuff (like /tmp/../tmp/../)
 * note that dir isn't protected for max string names... 
 * 
 * If relbase is NULL then its ignored
 */

void BLI_cleanup_dir(const char *relabase, char *dir)
{
	BLI_cleanup_file(relabase, dir);
	BLI_add_slash(dir);

}

void BLI_cleanup_file(const char *relabase, char *dir)
{
	short a;
	char *start, *eind;
	if (relabase) {
		BLI_convertstringcode(dir, relabase);
	} else {
		if (dir[0]=='/' && dir[1]=='/') {
			if (dir[2]== '\0') {
				return; /* path is "//" - cant clean it */
			}
			dir = dir+2; /* skip the first // */
		}
	}
	
	/* Note
	 *   memmove( start, eind, strlen(eind)+1 );
	 * is the same as
	 *   strcpy( start, eind ); 
	 * except strcpy should not be used because there is overlap,
	  * so use memmove's slightly more obscure syntax - Campbell
	 */
	
#ifdef WIN32
	
	/* Note, this should really be moved to the file selector,
	 * since this function is used in many areas */
	if(strcmp(dir, ".")==0) {	/* happens for example in FILE_MAIN */
	   get_default_root(dir);
	   return;
	}	

	while ( (start = strstr(dir, "\\..\\")) ) {
		eind = start + strlen("\\..\\") - 1;
		a = start-dir-1;
		while (a>0) {
			if (dir[a] == '\\') break;
			a--;
		}
		if (a<0) {
			break;
		} else {
			memmove( dir+a, eind, strlen(eind)+1 );
		}
	}

	while ( (start = strstr(dir,"\\.\\")) ){
		eind = start + strlen("\\.\\") - 1;
		memmove( start, eind, strlen(eind)+1 );
	}

	while ( (start = strstr(dir,"\\\\" )) ){
		eind = start + strlen("\\\\") - 1;
		memmove( start, eind, strlen(eind)+1 );
	}

	if((a = strlen(dir))){				/* remove the '\\' at the end */
		while(a>0 && dir[a-1] == '\\'){
			a--;
			dir[a] = 0;
		}
	}
#else
	if(dir[0]=='.') {	/* happens, for example in FILE_MAIN */
	   dir[0]= '/';
	   dir[1]= 0;
	   return;
	}	

	while ( (start = strstr(dir, "/../")) ) {
		eind = start + strlen("/../") - 1;
		a = start-dir-1;
		while (a>0) {
			if (dir[a] == '/') break;
			a--;
		}
		if (a<0) {
			break;
		} else {
			memmove( dir+a, eind, strlen(eind)+1 );
		}
	}

	while ( (start = strstr(dir,"/./")) ){
		eind = start + strlen("/./") - 1;
		memmove( start, eind, strlen(eind)+1 );
	}

	while ( (start = strstr(dir,"//" )) ){
		eind = start + strlen("//") - 1;
		memmove( start, eind, strlen(eind)+1 );
	}

	if( (a = strlen(dir)) ){				/* remove all '/' at the end */
		while(dir[a-1] == '/'){
			a--;
			dir[a] = 0;
			if (a<=0) break;
		}
	}
#endif
}


void BLI_makestringcode(const char *relfile, char *file)
{
	char * p;
	char * q;
	char * lslash;
	char temp[FILE_MAXDIR+FILE_MAXFILE];
	char res[FILE_MAXDIR+FILE_MAXFILE];
	
	/* if file is already relative, bail out */
	if(file[0]=='/' && file[1]=='/') return;
	
	/* also bail out if relative path is not set */
	if (relfile[0] == 0) return;

#ifdef WIN32 
	if (strlen(relfile) > 2 && relfile[1] != ':') {
		char* ptemp;
		/* fix missing volume name in relative base,
		   can happen with old .Blog files */
		get_default_root(temp);
		ptemp = &temp[2];
		if (relfile[0] != '\\' && relfile[0] != '/') {
			ptemp++;
		}
		BLI_strncpy(ptemp, relfile, FILE_MAXDIR + FILE_MAXFILE-3);
	} else {
		BLI_strncpy(temp, relfile, FILE_MAXDIR + FILE_MAXFILE);
	}

	if (strlen(file) > 2) {
		if ( temp[1] == ':' && file[1] == ':' && temp[0] != file[0] )
			return;
	}
#else
	BLI_strncpy(temp, relfile, FILE_MAX);
#endif

	BLI_char_switch(temp, '\\', '/');
	BLI_char_switch(file, '\\', '/');
	
	/* remove /./ which confuse the following slash counting... */
	BLI_cleanup_file(NULL, file);
	BLI_cleanup_file(NULL, temp);
	
	/* the last slash in the file indicates where the path part ends */
	lslash = BLI_last_slash(temp);

	if (lslash) 
	{	
		/* find the prefix of the filename that is equal for both filenames.
		   This is replaced by the two slashes at the beginning */
		p = temp;
		q = file;
		while (*p == *q) {
			++p; ++q;
		}
		/* we might have passed the slash when the beginning of a dir matches 
		   so we rewind. Only check on the actual filename
		*/
		if (*q != '/') {
			while ( (q >= file) && (*q != '/') ) { --q; --p; }
		} 
		else if (*p != '/') {
			while ( (p >= temp) && (*p != '/') ) { --p; --q; }
		}
		
		strcpy(res,	"//");

		/* p now points to the slash that is at the beginning of the part
		   where the path is different from the relative path. 
		   We count the number of directories we need to go up in the
		   hierarchy to arrive at the common 'prefix' of the path
		*/			
		while (p && p < lslash)	{
			if (*p == '/') 
				strcat(res,	"../");
			++p;
		}

		strcat(res, q+1); /* don't copy the slash at the beginning */
		
#ifdef	WIN32
		BLI_char_switch(res+2, '/', '\\');
#endif
		strcpy(file, res);
	}
}

int BLI_parent_dir(char *path)
{
#ifdef WIN32
	static char *parent_dir="..\\";
#else
	static char *parent_dir="../";
#endif
	char tmp[FILE_MAXDIR+FILE_MAXFILE+4];
	BLI_strncpy(tmp, path, sizeof(tmp));
	BLI_add_slash(tmp);
	strcat(tmp, parent_dir);
	BLI_cleanup_dir(NULL, tmp);
 	
	if (!BLI_testextensie(tmp, parent_dir)) {
		BLI_strncpy(path, tmp, sizeof(tmp));	
		return 1;
	} else {
		return 0;
	}
}

int BLI_convertstringframe(char *path, int frame)
{
	int ch_sta, ch_end, i;
	/* Insert current frame: file### -> file001 */
	ch_sta = ch_end = 0;
	for (i = 0; path[i] != '\0'; i++) {
		if (path[i] == '\\' || path[i] == '/') {
			ch_end = 0; /* this is a directory name, dont use any hashes we found */
		} else if (path[i] == '#') {
			ch_sta = i;
			ch_end = ch_sta+1;
			while (path[ch_end] == '#') {
				ch_end++;
			}
			i = ch_end-1; /* keep searching */
			
			/* dont break, there may be a slash after this that invalidates the previous #'s */
		}
	}
	if (ch_end) { /* warning, ch_end is the last # +1 */
		/* Add the frame number? */
		short numlen, hashlen;
		char tmp[FILE_MAX];
		
		char format[16]; /* 6 is realistically the maxframe (300000), so 8 should be enough, but 16 to be safe. */
		if (((ch_end-1)-ch_sta) >= 16) {
			ch_end = ch_sta+15; /* disallow values longer then 'format' can hold */
		}
		
		strcpy(tmp, path);
		
		numlen = 1 + (int)log10((double)frame); /* this is the number of chars in the number */
		hashlen = ch_end - ch_sta;
		
		sprintf(format, "%d", frame);
		
		if (numlen==hashlen) { /* simple case */
			memcpy(tmp+ch_sta, format, numlen);
		} else if (numlen < hashlen) {
			memcpy(tmp+ch_sta + (hashlen-numlen), format, numlen); /*dont copy the string terminator */
			memset(tmp+ch_sta, '0', hashlen-numlen);
		} else {
			/* number is longer then number of #'s */
			if (tmp[ch_end] == '\0') { /* hashes are last, no need to move any string*/
				/* bad juju - not testing string length here :/ */
				memcpy(tmp+ch_sta, format, numlen+1); /* add 1 to get the string terminator \0 */
			} else {
				/* we need to move the end characters, reuse i */
				int j;
				
				i = strlen(tmp); /* +1 to copy the string terminator */
				j = i + (numlen-hashlen); /* from/to */
				
				while (i >= ch_end) {
					tmp[j] = tmp[i]; 
					i--;
					j--;
				}
				memcpy(tmp + ch_sta, format, numlen);
			}
		}	
		strcpy(path, tmp);
		return 1;
	}
	return 0;
}


int BLI_convertstringcode(char *path, const char *basepath)
{
	int wasrelative = (strncmp(path, "//", 2)==0);
	char tmp[FILE_MAX];
	char base[FILE_MAX];
#ifdef WIN32
	char vol[3] = {'\0', '\0', '\0'};

	BLI_strncpy(vol, path, 3);
	/* we are checking here if we have an absolute path that is not in the current
	   blend file as a lib main - we are basically checking for the case that a 
	   UNIX root '/' is passed.
	*/
	if (!wasrelative && (vol[1] != ':' && (vol[0] == '\0' || vol[0] == '/' || vol[0] == '\\'))) {
		char *p = path;
		get_default_root(tmp);
		// get rid of the slashes at the beginning of the path
		while (*p == '\\' || *p == '/') {
			p++;
		}
		strcat(tmp, p);
	}
	else {
		BLI_strncpy(tmp, path, FILE_MAX);
	}
#else
	BLI_strncpy(tmp, path, FILE_MAX);
	
	/* Check for loading a windows path on a posix system
	 * in this case, there is no use in trying C:/ since it 
	 * will never exist on a unix os.
	 * 
	 * Add a / prefix and lowercase the driveletter, remove the :
	 * C:\foo.JPG -> /c/foo.JPG */
	
	if (isalpha(tmp[0]) && tmp[1] == ':' && (tmp[2]=='\\' || tmp[2]=='/') ) {
		tmp[1] = tolower(tmp[0]); /* replace ':' with driveletter */
		tmp[0] = '/'; 
		/* '\' the slash will be converted later */
	}
	
#endif

	BLI_strncpy(base, basepath, FILE_MAX);
	
	BLI_cleanup_file(NULL, base);
	
	/* push slashes into unix mode - strings entering this part are
	   potentially messed up: having both back- and forward slashes.
	   Here we push into one conform direction, and at the end we
	   push them into the system specific dir. This ensures uniformity
	   of paths and solving some problems (and prevent potential future
	   ones) -jesterKing. */
	BLI_char_switch(tmp, '\\', '/');
	BLI_char_switch(base, '\\', '/');	

	/* Paths starting with // will get the blend file as their base,
	 * this isnt standard in any os but is uesed in blender all over the place */
	if (wasrelative) {
		char *lslash= BLI_last_slash(base);
		if (lslash) {
			int baselen= (int) (lslash-base) + 1;
			/* use path for for temp storage here, we copy back over it right away */
			BLI_strncpy(path, tmp+2, FILE_MAX);
			
			memcpy(tmp, base, baselen);
			strcpy(tmp+baselen, path);
			strcpy(path, tmp);
		} else {
			strcpy(path, tmp+2);
		}
	} else {
		strcpy(path, tmp);
	}
	
	if (path[0]!='\0') {
		if ( path[strlen(path)-1]=='/') {
			BLI_cleanup_dir(NULL, path);
		} else {
			BLI_cleanup_file(NULL, path);
		}
	}
	
#ifdef WIN32
	/* skip first two chars, which in case of
	   absolute path will be drive:/blabla and
	   in case of relpath //blabla/. So relpath
	   // will be retained, rest will be nice and
	   shiny win32 backward slashes :) -jesterKing
	*/
	BLI_char_switch(path+2, '/', '\\');
#endif
	
	return wasrelative;
}


/*
 * Should only be done with command line paths.
 * this is NOT somthing blenders internal paths support like the // prefix
 */
int BLI_convertstringcwd(char *path)
{
	int wasrelative = 1;
	int filelen = strlen(path);
	
#ifdef WIN32
	if (filelen >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
		wasrelative = 0;
#else
	if (filelen >= 2 && path[0] == '/')
		wasrelative = 0;
#endif
	
	if (wasrelative==1) {
		char cwd[FILE_MAXDIR + FILE_MAXFILE];
		BLI_getwdN(cwd); /* incase the full path to the blend isnt used */
		
		if (cwd[0] == '\0') {
			printf( "Could not get the current working directory - $PWD for an unknown reason.");
		} else {
			/* uses the blend path relative to cwd important for loading relative linked files.
			*
			* cwd should contain c:\ etc on win32 so the relbase can be NULL
			* relbase being NULL also prevents // being misunderstood as relative to the current
			* blend file which isnt a feature we want to use in this case since were dealing
			* with a path from the command line, rather then from inside Blender */
			
			char origpath[FILE_MAXDIR + FILE_MAXFILE];
			BLI_strncpy(origpath, path, FILE_MAXDIR + FILE_MAXFILE);
			
			BLI_make_file_string(NULL, path, cwd, origpath); 
		}
	}
	
	return wasrelative;
}


/* copy di to fi, filename only */
void BLI_splitdirstring(char *di, char *fi)
{
	char *lslash= BLI_last_slash(di);

	if (lslash) {
		BLI_strncpy(fi, lslash+1, FILE_MAXFILE);
		*(lslash+1)=0;
	} else {
		BLI_strncpy(fi, di, FILE_MAXFILE);
		di[0]= 0;
	}
}

char *BLI_gethome(void) {
	#ifdef __BeOS
		return "/boot/home/";		/* BeOS 4.5: doubleclick at icon doesnt give home env */

	#elif !defined(WIN32)
		return getenv("HOME");

	#else /* Windows */
		char * ret;
		static char dir[512];
		static char appdatapath[MAXPATHLEN];
		HRESULT hResult;

		/* Check for %HOME% env var */

		ret = getenv("HOME");
		if(ret) {
			sprintf(dir, "%s\\.blender", ret);
			if (BLI_exists(dir)) return dir;
		}

		/* else, check install dir (path containing blender.exe) */

		BLI_getInstallationDir(dir);

		if (BLI_exists(dir))
		{
			strcat(dir,"\\.blender");
			if (BLI_exists(dir)) return(dir);
		}

				
		/* add user profile support for WIN 2K / NT */
		hResult = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdatapath);
		
		if (hResult == S_OK)
		{
			if (BLI_exists(appdatapath)) { /* from fop, also below... */
				sprintf(dir, "%s\\Blender Foundation\\Blender", appdatapath);
				BLI_recurdir_fileops(dir);
				if (BLI_exists(dir)) {
					strcat(dir,"\\.blender");
					if(BLI_exists(dir)) return(dir);
				}
			}
			hResult = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdatapath);
			if (hResult == S_OK)
			{
				if (BLI_exists(appdatapath)) 
				{ /* from fop, also below... */
					sprintf(dir, "%s\\Blender Foundation\\Blender", appdatapath);
					BLI_recurdir_fileops(dir);
					if (BLI_exists(dir)) {
						strcat(dir,"\\.blender");
						if(BLI_exists(dir)) return(dir);
					}
				}
			}
		}
#if 0
		ret = getenv("USERPROFILE");
		if (ret) {
			if (BLI_exists(ret)) { /* from fop, also below...  */
				sprintf(dir, "%s\\Application Data\\Blender Foundation\\Blender", ret);
				BLI_recurdir_fileops(dir);
				if (BLI_exists(dir)) {
					strcat(dir,"\\.blender");
					if(BLI_exists(dir)) return(dir);
				}
			}
		}
#endif

		/* 
		   Saving in the Windows dir is less than desirable. 
		   Use as a last resort ONLY! (aphex)
		*/
		
		ret = getenv("WINDOWS");		
		if (ret) {
			if(BLI_exists(ret)) return ret;
		}

		ret = getenv("WINDIR");	
		if (ret) {
			if(BLI_exists(ret)) return ret;
		}
		
		return "C:\\Temp";	/* sheesh! bad, bad, bad! (aphex) */
	#endif
}

void BLI_clean(char *path)
{
	if(path==0) return;
#ifdef WIN32
	if(path && strlen(path)>2) {
		BLI_char_switch(path+2, '/', '\\');
	}
#else
	BLI_char_switch(path, '\\', '/');
#endif
}

void BLI_char_switch(char *string, char from, char to) 
{
	if(string==0) return;
	while (*string != 0) {
		if (*string == from) *string = to;
		string++;
	}
}

void BLI_make_exist(char *dir) {
	int a;

	#ifdef WIN32
		BLI_char_switch(dir, '/', '\\');
	#else
		BLI_char_switch(dir, '\\', '/');
	#endif	
	
	a = strlen(dir);
	
#ifdef WIN32	
	while(BLI_exists(dir) == 0){
		a --;
		while(dir[a] != '\\'){
			a--;
			if (a <= 0) break;
		}
		if (a >= 0) dir[a+1] = 0;
		else {
			/* defaulting to drive (usually 'C:') of Windows installation */
			get_default_root(dir);
			break;
		}
	}
#else
	while(BLI_exist(dir) == 0){
		a --;
		while(dir[a] != '/'){
			a--;
			if (a <= 0) break;
		}
		if (a >= 0) dir[a+1] = 0;
		else {
			strcpy(dir,"/");
			break;
		}
	}
#endif
}

void BLI_make_existing_file(char *name)
{
	char di[FILE_MAXDIR], fi[FILE_MAXFILE];
	
	strcpy(di, name);
	BLI_splitdirstring(di, fi);
	
	/* test exist */
	if (BLI_exists(di) == 0) {
		BLI_recurdir_fileops(di);
	}
}


void BLI_make_file_string(const char *relabase, char *string,  const char *dir, const char *file)
{
	int sl;

	if (!string || !dir || !file) return; /* We don't want any NULLs */
	
	string[0]= 0; /* ton */

	/* we first push all slashes into unix mode, just to make sure we don't get
	   any mess with slashes later on. -jesterKing */
	/* constant strings can be passed for those parameters - don't change them - elubie */
	/*
	BLI_char_switch(relabase, '\\', '/');
	BLI_char_switch(dir, '\\', '/');
	BLI_char_switch(file, '\\', '/');
	*/

	/* Resolve relative references */	
	if (relabase && dir[0] == '/' && dir[1] == '/') {
		char *lslash;
		
		/* Get the file name, chop everything past the last slash (ie. the filename) */
		strcpy(string, relabase);
		
		lslash= (strrchr(string, '/')>strrchr(string, '\\'))?strrchr(string, '/'):strrchr(string, '\\');
		
		if(lslash) *(lslash+1)= 0;

		dir+=2; /* Skip over the relative reference */
	}
#ifdef WIN32
	else {
		if (strlen(dir) >= 2 && dir[1] == ':' ) {
			BLI_strncpy(string, dir, 3);
			dir += 2;
		}
		else { /* no drive specified */
			/* first option: get the drive from the relabase if it has one */
			if (relabase && strlen(relabase) >= 2 && relabase[1] == ':' ) {
				BLI_strncpy(string, relabase, 3);	
				string[2] = '\\';
				string[3] = '\0';
			}
			else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
				get_default_root(string);
			}
			
			/* ignore leading slashes */
			while (*dir == '/' || *dir == '\\') dir++;
		}
	}
#endif

	strcat(string, dir);

	/* Make sure string ends in one (and only one) slash */	
	/* first trim all slashes from the end of the string */
	sl = strlen(string);
	while (sl>0 && ( string[sl-1] == '/' || string[sl-1] == '\\') ) {
		string[sl-1] = '\0';
		sl--;
	}
	/* since we've now removed all slashes, put back one slash at the end. */
	strcat(string, "/");
	
	while (*file && (*file == '/' || *file == '\\')) /* Trim slashes from the front of file */
		file++;
		
	strcat (string, file);
	
	/* Push all slashes to the system preferred direction */
	BLI_clean(string);
}

int BLI_testextensie(const char *str, const char *ext)
{
	short a, b;
	int retval;

	a= strlen(str);
	b= strlen(ext);

	if(a==0 || b==0 || b>=a) {
		retval = 0;
	} else if (BLI_strcasecmp(ext, str + a - b)) {
		retval = 0;	
	} else {
		retval = 1;
	}

	return (retval);
}

/*
 * This is a simple version of BLI_split_dirfile that has the following advantages...
 * 
 * Converts "/foo/bar.txt" to "/foo/" and "bar.txt"
 * - wont change 'string'
 * - wont create any directories
 * - dosnt use CWD, or deal with relative paths.
 * - Only fill's in *dir and *file when they are non NULL
 * */
void BLI_split_dirfile_basic(const char *string, char *dir, char *file)
{
	int lslash=0, i = 0;
	for (i=0; string[i]!='\0'; i++) {
		if (string[i]=='\\' || string[i]=='/')
			lslash = i+1;
	}
	if (dir) {
		if (lslash) {
			BLI_strncpy( dir, string, lslash+1); /* +1 to include the slash and the last char */
		} else {
			dir[0] = '\0';
		}
	}
	
	if (file) {
		strcpy( file, string+lslash);
	}
}


/* Warning,
 * - May modify 'string' variable
 * - May create the directory if it dosnt exist
 * if this is not needed use BLI_split_dirfile_basic(...)
 */
void BLI_split_dirfile(char *string, char *dir, char *file)
{
	int a;
#ifdef WIN32
	int sl;
	short is_relative = 0;
	char path[FILE_MAX];
#endif

	dir[0]= 0;
	file[0]= 0;

#ifdef WIN32
	BLI_strncpy(path, string, FILE_MAX);
	BLI_char_switch(path, '/', '\\'); /* make sure we have a valid path format */
	sl = strlen(path);
	if (sl) {
		int len;
		if (path[0] == '/' || path[0] == '\\') { 
			BLI_strncpy(dir, path, FILE_MAXDIR);
			if (sl > 1 && path[0] == '\\' && path[1] == '\\') is_relative = 1;
		} else if (sl > 2 && path[1] == ':' && path[2] == '\\') {
			BLI_strncpy(dir, path, FILE_MAXDIR);
		} else {
			BLI_getwdN(dir);
			strcat(dir,"\\");
			strcat(dir,path);
			BLI_strncpy(path,dir,FILE_MAXDIR+FILE_MAXFILE);
		}
		
		// BLI_exist doesn't recognize a slashed dirname as a dir
		//  check if a trailing slash exists, and remove it. Do not do this
		//  when we are already at root. -jesterKing
		a = strlen(dir);
		if(a>=4 && dir[a-1]=='\\') dir[a-1] = 0;

		if (is_relative) {
			printf("WARNING: BLI_split_dirfile needs absolute dir\n");
		}
		else {
			BLI_make_exist(dir);
		}

		if (S_ISDIR(BLI_exist(dir))) {

			/* copy from end of string into file, to ensure filename itself isn't truncated 
			if string is too long. (aphex) */

			len = FILE_MAXFILE - strlen(path);

			if (len < 0)
				BLI_strncpy(file,path + abs(len),FILE_MAXFILE);
			else
				BLI_strncpy(file,path,FILE_MAXFILE);
		    
			if (strrchr(path,'\\')) {
				BLI_strncpy(file,strrchr(path,'\\')+1,FILE_MAXFILE);
			}
			
			if ( (a = strlen(dir)) ) {
				if (dir[a-1] != '\\') strcat(dir,"\\");
			}
		}
		else {
			a = strlen(dir) - 1;
			while(a>0 && dir[a] != '\\') a--;
			dir[a + 1] = 0;
			BLI_strncpy(file, path + strlen(dir),FILE_MAXFILE);
		}

	}
	else {
		/* defaulting to first valid drive hoping it's not empty CD and DVD drives */
		get_default_root(dir);
		file[0]=0;
	}
#else
	if (strlen(string)) {
		if (string[0] == '/') { 
			strcpy(dir, string);
		} else if (string[1] == ':' && string[2] == '\\') {
			string+=2;
			strcpy(dir, string);
		} else {
			BLI_getwdN(dir);
			strcat(dir,"/");
			strcat(dir,string);
			strcpy((char *)string,dir);
		}

		BLI_make_exist(dir);
			
		if (S_ISDIR(BLI_exist(dir))) {
			strcpy(file,string + strlen(dir));

			if (strrchr(file,'/')) strcpy(file,strrchr(file,'/')+1);
		
			if ( (a = strlen(dir)) ) {
				if (dir[a-1] != '/') strcat(dir,"/");
			}
		}
		else {
			a = strlen(dir) - 1;
			while(dir[a] != '/') a--;
			dir[a + 1] = 0;
			strcpy(file, string + strlen(dir));
		}
	}
	else {
		BLI_getwdN(dir);
		strcat(dir, "/");
		file[0] = 0;
	}
#endif
}

/* simple appending of filename to dir, does not check for valid path! */
void BLI_join_dirfile(char *string, const char *dir, const char *file)
{
	int sl_dir = strlen(dir);
	BLI_strncpy(string, dir, FILE_MAX);
	if (sl_dir > FILE_MAX-1) sl_dir = FILE_MAX-1;
	
	/* only add seperator if needed */
#ifdef WIN32
	if (string[sl_dir-1] != '\\') {
		string[sl_dir] = '\\';
		sl_dir++;
	}
#else
	if (string[sl_dir-1] != '/') {
		string[sl_dir] = '/';
		sl_dir++;
	}
#endif
	
	if (sl_dir <FILE_MAX) {
		BLI_strncpy(string + sl_dir, file, FILE_MAX-sl_dir);
	}
}

static int add_win32_extension(char *name)
{
	int retval = 0;
	int type;

	type = BLI_exist(name);
	if ((type == 0) || S_ISDIR(type)) {
#ifdef _WIN32
		char filename[FILE_MAXDIR+FILE_MAXFILE];
		char ext[FILE_MAXDIR+FILE_MAXFILE];
		char *extensions = getenv("PATHEXT");
		if (extensions) {
			char *temp;
			do {
				strcpy(filename, name);
				temp = strstr(extensions, ";");
				if (temp) {
					strncpy(ext, extensions, temp - extensions);
					ext[temp - extensions] = 0;
					extensions = temp + 1;
					strcat(filename, ext);
				} else {
					strcat(filename, extensions);
				}

				type = BLI_exist(filename);
				if (type && (! S_ISDIR(type))) {
					retval = 1;
					strcpy(name, filename);
					break;
				}
			} while (temp);
		}
#endif
	} else {
		retval = 1;
	}

	return (retval);
}

void BLI_where_am_i(char *fullname, const char *name)
{
	char filename[FILE_MAXDIR+FILE_MAXFILE];
	char *path = NULL, *temp;
	int len;
#ifdef _WIN32
	char *seperator = ";";
	char *slash = "\\";
#else
	char *seperator = ":";
	char *slash = "/";
#endif

	
#ifdef __linux__
	/* linux uses binreloc since argv[0] is not relyable, call br_init( NULL ) first */
	path = br_find_exe( NULL );
	if (path) {
		strcpy(fullname, path);
		free(path);
		return;
	}
#endif
	
	/* unix and non linux */
	if (name && fullname && strlen(name)) {
		strcpy(fullname, name);
		if (name[0] == '.') {
			// relative path, prepend cwd
			BLI_getwdN(fullname);
			len = strlen(fullname);
			if (len && fullname[len -1] != slash[0]) {
				strcat(fullname, slash);
			}
			strcat(fullname, name);
			add_win32_extension(fullname);
		} else if (BLI_last_slash(name)) {
			// full path
			strcpy(fullname, name);
			add_win32_extension(fullname);
		} else {
			// search for binary in $PATH
			path = getenv("PATH");
			if (path) {
				do {
					temp = strstr(path, seperator);
					if (temp) {
						strncpy(filename, path, temp - path);
						filename[temp - path] = 0;
						path = temp + 1;
					} else {
						strncpy(filename, path, sizeof(filename));
					}
					len = strlen(filename);
					if (len && filename[len - 1] != slash[0]) {
						strcat(filename, slash);
					}
					strcat(filename, name);
					if (add_win32_extension(filename)) {
						strcpy(fullname, filename);
						break;
					}
				} while (temp);
			}
		}
#ifndef NDEBUG
		if (strcmp(name, fullname)) {
			printf("guessing '%s' == '%s'\n", name, fullname);
		}
#endif

#ifdef _WIN32
		// in windows change long filename to short filename because
		// win2k doesn't know how to parse a commandline with lots of
		// spaces and double-quotes. There's another solution to this
		// with spawnv(P_WAIT, bprogname, argv) instead of system() but
		// that's even uglier
		GetShortPathName(fullname, fullname, FILE_MAXDIR+FILE_MAXFILE);
#ifndef NDEBUG
		printf("Shortname = '%s'\n", fullname);
#endif
#endif
	}
}

void BLI_where_is_temp(char *fullname, int usertemp)
{
	fullname[0] = '\0';
	
	if (usertemp && BLI_exists(U.tempdir)) {
		strcpy(fullname, U.tempdir);
	}
	
	
#ifdef WIN32
	if (fullname[0] == '\0') {
		char *tmp = getenv("TEMP"); /* Windows */
		if (tmp && BLI_exists(tmp)) {
			strcpy(fullname, tmp);
		}
	}
#else
	/* Other OS's - Try TMP and TMPDIR */
	if (fullname[0] == '\0') {
		char *tmp = getenv("TMP");
		if (tmp && BLI_exists(tmp)) {
			strcpy(fullname, tmp);
		}
	}
	
	if (fullname[0] == '\0') {
		char *tmp = getenv("TMPDIR");
		if (tmp && BLI_exists(tmp)) {
			strcpy(fullname, tmp);
		}
	}
#endif	
	
	if (fullname[0] == '\0') {
		strcpy(fullname, "/tmp/");
	} else {
		/* add a trailing slash if needed */
		BLI_add_slash(fullname);
	}
}

/* 
 * returns absolute path to the app bundle
 * only useful on OS X 
 */
#ifdef __APPLE__
char* BLI_getbundle(void) {
	CFURLRef bundleURL;
	CFStringRef pathStr;
	static char path[MAXPATHLEN];
	CFBundleRef mainBundle = CFBundleGetMainBundle();

	bundleURL = CFBundleCopyBundleURL(mainBundle);
	pathStr = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
	CFStringGetCString(pathStr, path, MAXPATHLEN, kCFStringEncodingASCII);
	return path;
}
#endif

/* strcasestr not available in MSVC */
char *BLI_strcasestr(const char *s, const char *find)
{
    register char c, sc;
    register size_t len;
	
    if ((c = *find++) != 0) {
		c= tolower(c);
		len = strlen(find);
		do {
			do {
				if ((sc = *s++) == 0)
					return (NULL);
				sc= tolower(sc);
			} while (sc != c);
		} while (BLI_strncasecmp(s, find, len) != 0);
		s--;
    }
    return ((char *) s);
}


int BLI_strcasecmp(const char *s1, const char *s2) {
	int i;

	for (i=0; ; i++) {
		char c1 = tolower(s1[i]);
		char c2 = tolower(s2[i]);

		if (c1<c2) {
			return -1;
		} else if (c1>c2) {
			return 1;
		} else if (c1==0) {
			break;
		}
	}

	return 0;
}

int BLI_strncasecmp(const char *s1, const char *s2, int n) {
	int i;

	for (i=0; i<n; i++) {
		char c1 = tolower(s1[i]);
		char c2 = tolower(s2[i]);

		if (c1<c2) {
			return -1;
		} else if (c1>c2) {
			return 1;
		} else if (c1==0) {
			break;
		}
	}

	return 0;
}


#ifdef WITH_ICONV
#include "iconv.h"
#include "localcharset.h"

void BLI_string_to_utf8(char *original, char *utf_8, const char *code)
{
	size_t inbytesleft=strlen(original);
	size_t outbytesleft=512;
	size_t rv=0;
	iconv_t cd;
	
	if (NULL == code) {
		code = locale_charset();
	}
	cd=iconv_open("UTF-8", code);

	if (cd == (iconv_t)(-1)) {
		printf("iconv_open Error");
		*utf_8='\0';
		return ;
	}
	rv=iconv(cd, &original, &inbytesleft, &utf_8, &outbytesleft);
	if (rv == (size_t) -1) {
		printf("iconv Error\n");
		return ;
	}
	*utf_8 = '\0';
	iconv_close(cd);
}
#endif // WITH_ICONV

void BLI_timestr(double _time, char *str)
{
	/* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
	int  hr= ( (int)  _time) / (60*60);
	int min= (((int)  _time) / 60 ) % 60;
	int sec= ( (int) (_time)) % 60;
	int hun= ( (int) (_time   * 100.0)) % 100;
	
	if (hr) {
		sprintf(str, "%.2d:%.2d:%.2d.%.2d",hr,min,sec,hun);
	} else {
		sprintf(str, "%.2d:%.2d.%.2d",min,sec,hun);
	}
	
	str[11]=0;
}

/* ************** 64 bits magic, trick to support up to 32 gig of address space *************** */
/*                only works for malloced pointers (8 aligned)                   */

#ifdef __LP64__ 

#if defined(WIN32) && !defined(FREE_WINDOWS)
#define PMASK		0x07FFFFFFFFi64
#else
#define PMASK		0x07FFFFFFFFll
#endif


int BLI_int_from_pointer(void *poin)
{
	intptr_t lval= (intptr_t)poin;
	
	return (int)(lval>>3);
}

void *BLI_pointer_from_int(int val)
{
	static int firsttime= 1;
	static intptr_t basevalue= 0;
	
	if(firsttime) {
		void *poin= malloc(10000);
		basevalue= (intptr_t)poin;
		basevalue &= ~PMASK;
		printf("base: %d pointer %p\n", basevalue, poin); /* debug */
		firsttime= 0;
		free(poin);
	}
	return (void *)(basevalue | (((intptr_t)val)<<3));
}

#else

int BLI_int_from_pointer(void *poin)
{
	return (int)poin;
}
void *BLI_pointer_from_int(int val)
{
	return (void *)val;
}

#endif

