#ifdef __BORLANDC__
#pragma warn -8004
#pragma warn -8058
#pragma warn -8053
#pragma warn -8060
#pragma warn -8012
#pragma warn -8013
#pragma warn -8075
#pragma warn -8080
#endif

#ifdef __XCODE__
#include "xcode_quiet.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#ifdef WIN32
#include <direct.h>
#endif
#ifdef unix
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <dirent.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#include <io.h>
#include <conio.h>
#include <fcntl.h>
#include <windows.h>
#endif
#include <signal.h>

#ifdef WIN32

#define XLPWSTR LPSTR
#define XLPCWSTR LPCSTR

#	ifndef __BORLANDC__
#		include <sys/utime.h>
#	else
#		include <utime.h>
#	endif
#else

#	include <utime.h>

#endif

// This is because on freebsd5 they've included daylight saving in the timezone again, idiots
// can't make up their minds.
#ifdef is_freebsd5
#define IGNORE_ISDST 1
#endif

#ifdef WIN32
#include "windows.h"
#endif
#include "lib.h"
#include "fasttime2.h"
#define DMID 14
#include "debug.h"
#include "tmalloc.h"
#include "debug_marijn.h"

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif

#define emsg EMSG_NOT_IN_LIB_C_uSE_VEMSG
#define imsg EMSG_NOT_IN_LIB_C_uSE_VEMSG


#ifdef WIN32
#ifndef __BORLANDC__
//#define fstat _fstat
#define fileno _fileno
//#define stat _stat
#define mkdir _mkdir
#endif
#endif

#ifndef WIN32
#include <termios.h>

#undef  CMIN
#define CMIN  1

#undef  CTIME
#define CTIME 1

#ifndef SURGEMAIL
int dtrace_fake(char *s);
#define dtrace dtrace_fake
#else
#define dtrace(xxx) trace_add(__FILE__,__LINE__,xxx)
#endif

int do_getch(int echo) {
	static struct termios Otty, Ntty;
	int	error;
	char ch=0;

	tcgetattr(0, &Otty);
	Ntty = Otty;

	Ntty.c_iflag = 0;
	Ntty.c_oflag = 0;
	Ntty.c_lflag &= ~ICANON;

	if (!echo) {
		// disable echoing the char as it is typed
		Ntty.c_lflag &= ~ECHO;
	}
	else {
		// enable echoing the char as it is typed
		Ntty.c_lflag |=  ECHO;
	}

	Ntty.c_cc[VMIN]  = CMIN;
	Ntty.c_cc[VTIME] = CTIME;

#if 0
//use this to flush the input buffer before blocking for new input
#	define FLAG TCSAFLUSH
#else
//use this to return a char from the current input buffer, or block if no input is waiting.
#	define FLAG TCSANOW
#endif

	if (0 == (error = tcsetattr(0, FLAG, &Ntty))) {
		error = read(0, &ch, 1);
		error += tcsetattr(0, FLAG, &Otty);
	}
	return (error == 1 ? (int) ch : -1 );
}
#endif /*WIN32*/

int wild_stats_add(char *src, int line);
char *nullok(char *s)
{
	if (s==NULL) return "";
	return s;
}
int nulloki(int *i)
{
	if (i==NULL) return 0;
	return *i;
}

#ifndef WIN32
#define _tzname tzname
#endif



char *timezone_name(char *bf, int bfsz) {
	strcpy(bf,"");
	if (_tzname[0]==NULL) return bf;
	ncpy(bf,_tzname[0],bfsz-1);
	return bf;
}
int wild_date_sort(WILD_DATA **dlist);

char *timezone_name_short(char *bf, int bfsz) {
	char *p = _tzname[0];
	int i;

	strcpy(bf,"");
	if (p==NULL) return bf;
	for(i = 0; *p && i < bfsz-1; p++) {
		if (sisupper(*p)) bf[i++] = *p;
	}
	bf[i] = '\0';
	return bf;
}

#include <stdarg.h>
#include "vsnprintf.h"

static int (*vemsg)(const char *, ...);
static int (*vimsg)(const char *, ...);

/*
 * This array is designed for mapping upper and lower case letter
 * together for a case independent comparison.  The mappings are
 * based upon ascii character sequences.
 */
const unsigned char charmap[] = {
        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

char charmatch(char c) { return charmap[c]; }

int lib_sprintf( char *buffer, int bfsize, char *format, ... )
{
	int r;

	if (buffer && format && bfsize > 0) {
		va_list arg_list;

		va_start(arg_list, format);
		r = vsnprintf( buffer, bfsize, format, arg_list);
		va_end(arg_list);
		if (r<0 || r>=bfsize) buffer[(r = bfsize-1)] = 0;
		return r;
	}
	return -1;
}

#define lprintf lib_sprintf

int tree_sort(WILD_DATA **dlist);

int system_dir(char *bf, int bfsz) {
	int r;
#ifdef WIN32
	r = GetSystemDirectoryA((XLPWSTR)bf,bfsz);
	for(; r != 0 && bf[r] != '\\'; r--);
	if (r != 0 && bf[r-1] == ':') r++;
	bf[r] = '\0';
#else
	ncpy(bf,"/etc",bfsz-1);
	r = 4;
#endif
	return r;
}
int lib_get_host_port(char *host,char *dflt, int port, char *setting)
{
	char tmp[BFSZ];
	char *s;
	lprintf(host,BFSZ-1,"%s:%d",dflt,port);
	ncpy(tmp,setting,BFSZ-1);
	s = strchr(tmp,',');
	if (s!=NULL) *s = 0;
	s = strchr(tmp,'.');
	if (s!=NULL) {
		ncpy(host,tmp,BFSZ-1);
	} else {
		lprintf(host,BFSZ-1,"%s:%s",dflt,tmp);
	}
	if (strchr(host,':')==NULL) {
		ncpy(tmp,host,BFSZ-1);
		lprintf(host,BFSZ-1,"%s:%s",tmp,port);
	}
//printf("CONNECT TO (%s)\n",host);
	return TRUE;
}
int lib_used_tree(char *path, double *total, int *ntot)
{
	// This does note cope with sub directories! 9actually I think it does
	WILD_HANDLE *h;
	WILD_DATA mydata;
	char full[BFSZ];
	int more;

	more = wild_first(&h, path,&mydata);
	for (;more; more = wild_next(h, &mydata)){
		if (mydata.isnorm && !mydata.isdir) {
			*total += mydata.size;
			(*ntot) ++;
		} else if (mydata.isdir) {
			if (strcmp(mydata.name,".")==0) continue;
			if (strcmp(mydata.name,"..")==0) continue;
			lprintf(full,BFSZ-1,"%s/%s",path,mydata.name);
			lib_used_tree(full,total,ntot);
		}
	}
	wild_end(&h);
	return TRUE;
}
int lib_used_ntree(char *path, double *total, int *nfiles)
{
	// This does note cope with sub directories! 9actually I think it does
	WILD_HANDLE *h;
	WILD_DATA mydata;
	char full[BFSZ];
	int more;

	more = wild_first(&h, path,&mydata);
//	vimsg("path (%s) used_ntree more=%d error=%s\n",path,more,strerror(errno));
	for (;more; more = wild_next(h, &mydata)){
		(*nfiles)++;
//		vimsg("used_ntree: file %s\n",mydata.name);
		if (mydata.isnorm && !mydata.isdir) {
			*total += mydata.size;
		} else if (mydata.isdir) {
			if (strcmp(mydata.name,".")==0) continue;
			if (strcmp(mydata.name,"..")==0) continue;
			lprintf(full,BFSZ-1,"%s/%s",path,mydata.name);
			lib_used_ntree(full,total,nfiles);
		}
	}
	wild_end(&h);
	return TRUE;
}
int lib_tree_count(char *path, char *fname, int *nfiles)
{
	WILD_HANDLE *h;
	WILD_DATA mydata;
	char full[BFSZ];
	int more;

	more = wild_first(&h, path,&mydata);
	for (;more; more = wild_next(h, &mydata)){
		if (mydata.isnorm && !mydata.isdir) {
			if (strcmp(fname,mydata.name)==0) (*nfiles)++;
		} else if (mydata.isdir) {
			if (strcmp(mydata.name,".")==0) continue;
			if (strcmp(mydata.name,"..")==0) continue;
			lprintf(full,BFSZ-1,"%s/%s",path,mydata.name);
			lib_tree_count(full,fname,nfiles);
		}
	}
	wild_end(&h);
	return TRUE;
}
Wordlist *word_folders(char *path)
{
	WILD_HANDLE *h;
	WILD_DATA mydata;
//	char full[BFSZ];
	int more;
	Wordlist *list=NULL;

	more = wild_first(&h, path,&mydata);
	for (;more; more = wild_next(h, &mydata)){
		if (mydata.isdir) {
			if (strcmp(mydata.name,".")==0) continue;
			if (strcmp(mydata.name,"..")==0) continue;
			word_add(&list,mydata.name);
		}
	}
	wild_end(&h);
	return list;
}


int lib_delete_tree(char *path, char *reason, double *total, int age, int *nskip)
{
	// This does note cope with sub directories! 9actually I think it does
	WILD_HANDLE *h;
	WILD_DATA mydata;
	char full[BFSZ];
	int more;

	strcpy(reason,"");
	more = wild_first(&h, path,&mydata);
	for (;more; more = wild_next(h, &mydata)){
		if (mydata.isnorm && !mydata.isdir) {
			lprintf(full,BFSZ-1,"%s/%s",path,mydata.name);
			*total += mydata.size;
			if (age!=0) if (lib_file_age(full) < age) {(*nskip)++; continue;}
			
			if (lib_remove(full)!=0) lprintf(reason,BFSZ-1,"Fail (%s) %s",full,strerror(errno));
			//printf("%s deleted\n",full);
		} else if (mydata.isdir) {
			if (strcmp(mydata.name,".")==0) continue;
			if (strcmp(mydata.name,"..")==0) continue;
			lprintf(full,BFSZ-1,"%s/%s",path,mydata.name);
			//printf("%s deleting...\n",full);
			lib_delete_tree(full,reason,total,age,nskip);
		}
	}
	wild_end(&h);
#ifdef WIN32
#define rmdir _rmdir
#endif
	if (lib_strlen(reason)==0) {
			if (rmdir(path)!=0) lprintf(reason,BFSZ-1,"Fail2 (%s) %s",path,strerror(errno));
	}
	if (lib_strlen(reason)!=0) return FALSE;

	return TRUE;
}

#ifdef WIN32
size_t GetStackUsage()
{
    MEMORY_BASIC_INFORMATION mbi;
	memset(&mbi, 0, sizeof(mbi));
    VirtualQuery(&mbi, &mbi, sizeof(mbi));
    // now mbi.AllocationBase = reserved stack memory base address

    VirtualQuery(mbi.AllocationBase, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe reserved (uncommitted) portion of the stack
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the guard page
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the committed (i.e. accessed) portion of the stack

    return mbi.RegionSize;
}
#else
size_t GetStackUsage()
{
	return 0;
}
#endif

int lib_delete_dir(char *path, char *reason, double *tot)
{
	// This does note cope with sub directories!
	WILD_HANDLE *h;
	WILD_DATA mydata;
	char full[BFSZ];
	int more;

	*tot = 0;
	strcpy(reason,"");
	more = wild_first(&h, path,&mydata);
	while(more){
		if (mydata.isnorm && !mydata.isdir) {
			(*tot) += mydata.size;
			lprintf(full,BFSZ-1,"%s/%s",path,mydata.name);
			if (lib_remove(full)!=0) lprintf(reason,BFSZ-1,"Fail (%s) %s",full,strerror(errno));
		}
		more = wild_next(h, &mydata);
	}
	wild_end(&h);
#ifdef WIN32
#define rmdir _rmdir
#endif
	if (lib_strlen(reason)==0) {
			if (rmdir(path)!=0) lprintf(reason,BFSZ-1,"Fail2 (%s) %s",path,strerror(errno));
	}
	if (lib_strlen(reason)!=0) return FALSE;
	return TRUE;
}


char *lib_base_domain(char *dom, char *tmp)
{
	int want=3;
	char *s;
	int got=0;
	if (strstrnc(dom,".com")!=NULL) want=2;
	if (strstrnc(dom,".bz")!=NULL) want=2;
	if (strstrnc(dom,".biz")!=NULL) want=2;
	if (strstrnc(dom,".net")!=NULL) want=2;
	if (strstrnc(dom,".info")!=NULL) want=2;
	if (strstrnc(dom,".ph")!=NULL) want=2;
	//  a.b.c.d   Find 'want' dots from right and cut....
	s = dom + lib_strlen(dom)-1;
	for (;s>dom;s--) {
		if (*s=='.') got++;
		if (got>=want) break;
	}
	if (*s=='.') s++;
	ncpy(tmp,s,BFSZ-1);
//printf("base domain (%s) want=%d\n",tmp,want);
	return tmp;
}
int lib_compare_base(char *dom1, char *dom2)
{
	char tmp1[BFSZ];
	char tmp2[BFSZ];
	if (strcmpnc(lib_base_domain(dom1,tmp1),lib_base_domain(dom2,tmp2))==0) return TRUE;
	return FALSE;
}

int lib_get_port(int port, char *setting)
{
	char tmp[BFSZ];
	char *s;
	ncpy(tmp,setting,BFSZ-1);
	if (lib_strlen(setting)==0) return port;
	s = strchr(tmp,',');
	if (s!=NULL) *s = 0;

	s = strchr(tmp,':');
	if (s!=NULL) return atoi(s+1);
	if (strchr(tmp,'.')==NULL) return atoi(tmp);
	return port;
}

char *buildtime(char *bf, int bfsz) {
	if (bf) {
		/* code doesn't work, gets time wrong by 1 hr depending on daylight savings
#ifdef WIN32
		IMAGE_DOS_HEADER *DosHeader;
		IMAGE_NT_HEADERS *NTHeader;
		DWORD linktime;

		if (bfsz < 26) {
			bf[0] = '\0';
			return bf;
		}

		DosHeader = (IMAGE_DOS_HEADER*)GetModuleHandle(NULL);
		if (DosHeader && IMAGE_DOS_SIGNATURE == DosHeader->e_magic) {
			NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader + DosHeader->e_lfanew);
			if (IMAGE_NT_SIGNATURE == NTHeader->Signature) {
				long old_timezone;
				struct tm *tm;

				linktime = NTHeader->FileHeader.TimeDateStamp;
				//printf("%d %d %d\n",linktime,_timezone,_daylight);
				//remember timezone, set to NZ time
				old_timezone = _timezone;
				_timezone = -43200;
				tm = gmtime(&linktime);
				if (tm) {
					sprintf(bf,"%.24s",safe_asctime(tm,tmpstr));
					return bf;
				}
				//restore timezone
				_timezone = old_timezone;
			}
		}
#endif
		*/
		if (bfsz < (signed)(lib_strlen(__DATE__)+lib_strlen(__TIME__)+3)) {
			bf[0] = '\0';
			return bf;
		}
		sprintf(bf,"%s %s",__DATE__,__TIME__);
	}
	return bf;
}


/* QSORT compare fns ... */
int qsort_strcmp(const void *arg1, const void *arg2) {
   return strcmp(*(char**) arg1, *(char**)arg2 );
}
int qsort_strcmpnc(const void *arg1, const void *arg2) {
   return strcmpnc(*(char**) arg1, *(char**)arg2 );
}
int qsort_int(const void *arg1, const void *arg2) {
	return *(int *)arg1 - *(int *)arg2;
}

void qreverse(void *xlist, size_t count, size_t size) {
	unsigned char *list = (unsigned char *)xlist;
	unsigned char *tmp = calloc(1,size);
	size_t i;

	for(i = 0; i < count/2; i++) {
		memcpy(tmp,list+(i*size),size);
		memcpy(list+(i*size),list+((count-1-i)*size),size);
		memcpy(list+((count-1-i)*size),tmp,size);
	}

	free(tmp);
}

unsigned char char2byte_array[256] = {
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
	0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

void lib_set_emsg(int (*fn)(const char *, ...)) {
	vemsg = fn;
}
void lib_set_imsg(int (*fn)(const char *, ...)) {
	vimsg = fn;
}
int fclose_nfs(FILE *f)
{
#ifdef is_freebsd
		if (fflush(f)!=0) {
			if (vemsg) vemsg("fflush failed handle %d %s\n",fileno(f),strerror(errno));
		}
		if (fsync(fileno(f))!=0) {
			if (vemsg) vemsg("fsync failed handle %d %s\n",fileno(f),strerror(errno));
		}
#endif
//printf("fclose nfs done\n");
		return fclose(f);
}
char * lib_class_c(char *in, char *out)
{
	char *s;
	int n=0;
	if (out!=in) 	ncpy(out,in,BFSZ-1);
	for (s=out;*s!=0;s++) {
		if (*s=='.') n++;
		if (n==3) *s = 0;
	}
//	if (vimsg) vimsg("class_c (%s)\n",out);
	return out;
}





unsigned int lib_thread_cpu(void) // returns result in  CLOCKS_PER_SEC
{
#ifdef WIN32
	unsigned int lo,hi,tot,r;
	FILETIME crtime,extime,ktime,utime;

	GetProcessTimes(GetCurrentProcess(),&crtime,&extime,&ktime,&utime);
	lo = ktime.dwLowDateTime >> 16;
	hi = ktime.dwHighDateTime << 16;
	tot = lo + hi;
	lo = utime.dwLowDateTime >> 16;
	hi = utime.dwHighDateTime << 16;
	tot += lo + hi;
	r = (int) ((double) tot * .0065536 * CLOCKS_PER_SEC);
	r = r & 0x07fffffff;
	return (int) r;
#else

	// Stupid bloody posix, these timers don't actually work
	// it returns 'elapsed time' not 'cpu time'.
	// there doesn't seem to be any explanation of this fault on the net
	// it just seems to have been incorrectly implemented on most systems
	// - chrisp
	// on osx the macro's are defined but don't actually compile... stupid crap.
	// Chrisp suspects this causes crash on some linux kernals, (not sure);

/*
#ifdef DO_MAIL
#ifndef OSX
#ifdef _POSIX_TIMERS
#ifdef _POSIX_THREAD_CPUTIME
#define THREAD_TIMERS 1
#endif
#endif
#endif
#endif
*/


#ifdef THREAD_TIMERS
panic panic panic, bad code here I thik chrisp
	struct timespec tp;
	clock_gettime(CLOCK_THREAD_CPUTIME_ID,&tp);
	return tp.tv_sec*CLOCKS_PER_SEC + tp.tv_nsec/(1000000000/CLOCKS_PER_SEC);
#else
	//return clock();
	return 0;
#endif

#endif
}





int lib_tail_file(char *inname, char *outname, int want, int append)
{
	char bf[BFSZ];
	FILE *fin,*fout;
	int r;
	int len;
	int sz=0;
	int at=0;

	fin = fopen(inname,"rb");
	if (fin==NULL) {
		if (vemsg!=NULL) vemsg("Cound not open (%s) %s\n",inname,strerror(errno));
		return FALSE;
	}
	if (!append) remove(outname);
	fout = fopen(outname,"ab");
	if (fout==NULL) {
		fclose(fin);
		if (vemsg!=NULL) vemsg("Cound not open (%s) %s\n",outname,strerror(errno));
		return FALSE;
	}
	len = lib_file_osize(fin);
	at = len-want;
	if (at<0) at = 0;
	fseek(fin,at,SEEK_SET);
	if (at>0) fgets(bf,BFSZ-1,fin);

	for (;!feof(fin);) {
		r = (int)(int)fread(bf,1,BFSZ-1,fin);
		if (r<=0) break;
		if (r>0) {
			sz += r;
			(int)fwrite(bf,1,r,fout);
			if (sz>want*2) break;
		}
	}
	fclose(fin);
	fclose(fout);
	return TRUE;
}
char *lib_remove_char(char *mask, char r, char *out)
{
	char *s;

	for (s=out;*mask!=0;mask++) {
		if (*mask==r) continue;
		*s++ = *mask;
		if (s-out>BFSZ-10) break;
	}
	*s++ = 0;
	return out;
}
int lib_filetype(char *fname, char *ext)
{
	// "test.dat"  ".dat"  8, 4
	int n = lib_strlen(fname) - lib_strlen(ext);
	if (n<0) return FALSE;
	if (strcmpnc(fname+n,ext)==0) return TRUE;
	return FALSE;
}

void *safe_memmove_real(void *dst, const void *src, int bytes, void *start, int bfsz, char *file, int line)
{
	stack_ok("memmove");
	if ((char *)dst < (char *)start || bytes < 0 || bytes > bfsz-((char *)dst-(char *)start)) {
		if (vemsg) vemsg("safe_memmove: failed dst=%p src=%p bytes=%d start=%p bfsz=%d %s:%d",dst,src,bytes,start,bfsz,file,line);
		lib_abort();
	}
	return memmove(dst,src,bytes);
}
void *safe_memcpy_real(void *dst, const void *src, int bytes, void *start, int bfsz, char *file, int line)
{
	stack_ok("memcpy");
	if ((char *)dst < (char *)start || bytes < 0 || bytes > bfsz-((char *)dst-(char *)start)) {
		if (vemsg) vemsg("safe_memcpy: failed dst=%p src=%p bytes=%d start=%p bfsz=%d %s:%d",dst,src,bytes,start,bfsz,file,line);
		lib_abort();
	}
	return memcpy(dst,src,bytes);
}

char *system_error(int ecode, char *bf, int bfsz) {
	if (bf) {
		int r;

		*bf = '\0';
#ifdef WIN32
		r = FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			ecode,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR) bf,
			bfsz,
			NULL);
		for(; r != 0 && iseol(bf[r]); r--) bf[r] = '\0';
#else
		ncpy(bf,strerror(ecode),bfsz-1);
#endif
	}
	return bf;
}

void assign(char **to, char *value) {
	if (*to) free(*to);
	*to = strdup(value);
}
int lib_from_two(char *from)
{
	char bf[BFSZ];
	char last[BFSZ];
	char *s;
	char *upto;
	strcpy(last,"");
	ncpy(bf,from,BFSZ-1);
	strlwr(bf);
	
	for (s = mystrtok(bf," \t\"<>",&upto);s!=NULL;s=mystrtok(NULL," \t\"<>",&upto)) {
		char *x = strchr(s,'@');
		if (x==NULL) continue;
		if (strchr(x+1,'@')!=NULL) return TRUE; // two ats, also bad...
		if (strlen(last)>0) if (!lib_same_domain(last,s)) {
			return TRUE;
		}
		ncpy(last,s,BFSZ-1);
	}
	return FALSE;
}

int is_localhost(char *s)
{
	if (strcmp(s,"127.0.0.1")==0) return TRUE;
	if (strcmp(s,"localhost")==0) return TRUE;
	if (strcmp(s,"::ffff:127.0.0.1")==0) return TRUE;
	if (strcmp(s,"::1")==0) return TRUE;
	return FALSE;
}
int remove_rename(char *oldf, char *newf, char *error, int bfsz) { // WARNING WARNING, PARAMTERS ARE BACKWARDS IN THIS FUNCTION,
	int i;
	for (i=0; TRUE; i++) {
		if (remove(oldf) == -1 && errno != ENOENT) {
			if (error) lprintf(error,bfsz,"Failed to remove (%s): %s",oldf,strerror(errno));
			if (i>35) return FALSE;
			lib_msleep(100);
			if (i>10) lib_msleep(500);
			continue;
		}
		break;
	}
	for (i=0; TRUE; i++) {
		if (rename(newf,oldf) == 0) break;
		lib_msleep(200);
		if (i>10) lib_msleep(500);
		if (i>35) {
			if (error) lprintf(error,bfsz,"Failed to rename (%s)->(%s): %s",newf,oldf,strerror(errno));
			return FALSE;
		}
	}
	return TRUE;
}

char *fixsep(char *path, int bfsz) {
	char *p,c=0;
	int l;

	if (!path) return path;

	l = lib_strlen(path);
	for(p = path; *p; p++, l--) {
		if (issep(*p)) {
			if (c == SEP && p != &path[1]) safe_memmove(p,p+1,l--,path,bfsz);
			else *p = SEP;
		}
		c = *p;
	}
	return path;
}
char *strtrm(char *bf) {
	char *s;

	for(s = bf; *s; s++);
	for(; s > bf && sisspace(s[-1]); ) *(--s) = 0;
	for(s = bf; sisspace(*s); s++);

	return s;
}
int lib_min(int a, int b)
{
	if (a<b) return a;
	return b;
}
int path_compare_real(char *path1, char *path2);
int path_compare(char *path1, char *path2)
{
	int r = path_compare_real(path1,path2);
//	if (vimsg) vimsg("path_compare %d (%s) (%s)\n",r,path1,path2);
	return r;
}
int path_compare_real(char *path1, char *path2)
{
	char *s1 = path1;
	char *s2 = path2;
	int r = 0;

	if (!path1 || !path2) return 1;

	for(;; s1++, s2++) {
		if (issep(*s1) && issep(*s2)) r = 0;
		else r = charmap[*s1] - charmap[*s2];

		// chrisp added, if one ends in a slash and the othe doesn't, then they match
		if (*s1==0 && *s2!=0) {
			if (s2[1]==0 && issep(*s2)) {r=0; break;}
		}
		if (*s2==0 && *s1!=0) {
			if (s1[1]==0 && issep(*s1)) {r=0; break;}
		}
		if (r != 0) break;
		if (!*s1) break;
		if (!*s2) break;
		// step past multiple slashes. chrisp change to fix mirroring issue!!!
		for (;issep(s1[0]) && issep(s1[1]);s1++) ;
		for (;issep(s2[0]) && issep(s2[1]);s2++) ;
	}

	return r;
}

int path_ncompare(char *path1, char *path2, int n) {
	char *s1 = path1;
	char *s2 = path2;
	int r = 0;

	if (!path1 || !path2) return 1;

	for(; n > 0; s1++, s2++, n--) {
		if (issep(*s1) && issep(*s2)) r = 0;
		else r = charmap[*s1] - charmap[*s2];
		if (r != 0) break;
		if (!*s1) break;
		if (!*s2) break;
	}

	return r;
}

char *make_path(char *path, char *file, char *result, int bfsz) {
	int left = bfsz,hassep = FALSE;
	char *s,*p = result;

	*p = '\0';
	if (path) {
		for(s = path; *s && --left; s++) {
			if (issep(*s)) {*p++ = SEP; continue;}
			*p++ = *s;
		}
		hassep = issep(*(p-1));
		*p = '\0';
	}
	if (file) {
		if (hassep && issep(*file)) file++;
		if (!hassep && !issep(*file)) *p++ = SEP;
		for(s = file; *s && --left; s++) {
			if (issep(*s)) {*p++ = SEP; continue;}
			*p++ = *s;
		}
		*p = '\0';
	}
	return result;
}
char *lib_spaces(int n, char *tmp)
{
	if (n>BFSZ-3) n = BFSZ-3;
	memset(tmp,' ',n);
	tmp[n] = 0;
	return tmp;
}
char *make_full_path(char *file, char *result, int bfsz) {
	char cur[BFSZ];

	*result = 0;
	if (issep(file[0])) goto ok;
	if (file[0] && file[1] == ':') goto ok;
	lib_getcwd(cur);
	make_path(cur,file,result,bfsz);
ok:
	if (!*result) ncpy(result,file,bfsz-1);
	return result;
}

char *strip_ext(char *file)
{
	char *p;

	for(p = file; *p; p++) {}
	for(; p > file && *p != '.' && !issep(*p); p--) {}
	if (*p == '.') *p = 0;
	return file;
}

// Identical but these functiosn SORT the directory, unix doesn't!!!
int swild_first(SWILD_HANDLE **hh, char *wild, WILD_DATA *data)
{
	int more;
	SWILD_HANDLE *h;
	WILD_DATA *d,mydata;
	*hh = h = calloc(1,sizeof(SWILD_HANDLE));
	more = wild_first(&h->wh,wild,&mydata);
	while(more){
		d = calloc(1,sizeof(WILD_DATA));
		*d = mydata;
		d->next = h->sorted;
		h->sorted = d;
		more = wild_next(h->wh, &mydata);
	}
	wild_end(&h->wh);

	wild_date_sort(&h->sorted);
	h->cur = h->sorted;
	if (h->cur!=NULL) {
		*data = *h->cur;
		h->cur = h->cur->next;
		return TRUE;
	}
	return FALSE;
}
int swild_next(SWILD_HANDLE *h, WILD_DATA *data)
{
	if (h->cur!=NULL) {
		*data = *h->cur;
		h->cur = h->cur->next;
		return TRUE;
	}
	return FALSE;
}
static int r_any;
int lib_remove_enable(void)
{
	r_any = TRUE;
	return TRUE;
}
int lib_remove(char *fname)
{
	if (!r_any) if (strstrnc(fname,"dlist.ini")!=NULL) {
		if (vemsg!=NULL) vemsg("ABORTING DUE TO remove of file in root surgemail directory (%s) please report to chrisp@netwin.co.nz\n",fname);
		lib_abort();
	}
	return remove(fname);
}
int swild_end(SWILD_HANDLE **hh)
{
	SWILD_HANDLE *h = *hh;
	WILD_DATA *d,*next;

	for (d=h->sorted;d!=NULL;d=next) {
		next = d->next;
		free(d);
	}
	free(*hh); *hh = NULL;
	return TRUE;
}

int lib_add_slashif(char *s)
{
	int len = lib_strlen(s);
	len--;
	if (len==0) return FALSE;
	if (s[len]=='/') return TRUE;
	if (s[len]=='\\') return TRUE;
	len++;
	s[len] = '/';
	s[len+1] = 0;
	return FALSE;
}
char *strfind(char *string,char delim,char **next) {
	char *start,*end;

	start = string;
	if (!start && next) start = *next;
	if (!start) return NULL;
	end = strchr(start,delim);
	if (end) *end++ = '\0';
	if (next) *next = end;
	return start;
}

char *comma_number(char *number, int bfsz) {
	char *ptr;
	int i,l,b;

	for(l = b = 0, ptr = number; *ptr; ptr++) {
		if (l || *ptr == '.' || !sisdigit(*ptr)) l++;
		b++;
	}
	for(ptr-= (intptr_t)l+1, i = 1; ptr > number; ptr--, i++, l++) {
		if (i != 3) continue;
		if (*(ptr-1) != ',') {
			if (++b == bfsz-1) return NULL;
			safe_memmove(ptr+1,ptr,l+2,number,bfsz);
			*ptr = ',';
		}
		else ptr--;
		i = 0;
		l++;
	}
	return number;
}

int lib_email_hasheader(char *fname)
{
	char bf[BFSZ],*p;
	FILE *f;

	f = fopen(fname,"r");
	if (f==NULL) return FALSE;
	fgets(bf,BFSZ-1,f);
	fclose(f);
	for(p = bf; *p; p++) {
		if (sisspace(*p)) return FALSE;
		if (*p == ':') return TRUE;
	}
	return FALSE;
}

int lib_iswhite(char c)
{
	if (c==' ') return TRUE;
	if (c=='\t') return TRUE;
	return FALSE;
}
int ncpy_file(char *bf, char *fname, int len)
{
	FILE *f;
	strcpy(bf,"");
	f = fopen(fname,"rb");
	if (f==NULL) return FALSE;
	len = (int)(int)fread(bf,1,len-1,f);
	if (len<0) len = 0;
	bf[len] = 0;
	return TRUE;
}

#ifdef is_linux
#define HAS_STATFS
#endif
#ifdef is_solaris
#define HAS_STATVFS
#endif

#ifdef is_osx
#define HAS_STATFS
#define is_bsdish
#endif
#ifdef is_freebsd
#define HAS_STATFS
#define is_bsdish
#endif

#ifdef WIN32
#	define DONE_FREESPACE 1

int lib_freespace(char *s) /* returrn in k bytes free */
{
	char bf[BFSZ];
	DWORD sc,bs,fc,c;
	bigint kb;

	ncpy(bf,s,BFSZ-1);
	s = strchr(bf,':');
	if (s!=NULL) {s+=2; *s = 0; s = bf;} else {
		/* Try and find end of filer name */
		if (strncmp(bf,"\\\\",2)==0) {
			s = strchr(bf+2,'\\');
			if (s!=NULL) s = strchr(s+1,'\\');
			if (s!=NULL) *s = 0;
			s = bf;
		} else s = "c:\\";
	}
	if (!GetDiskFreeSpaceA((XLPCWSTR)s,&sc,&bs,&fc,&c)) {
		//vemsg("GetDiskFreeSpace call failed, {%s} errcode=%d \n",s,GetLastError());
		return 2000000000;
	}
	// Munge result to avoid overflow, so if total is > 2tbytes just return 2tbytes
	kb = (bigint) sc* (bigint) bs* (bigint)fc /(1024);
	if (kb > 2000000000) return 2000000000;
	return (int) kb;

}
bigint lib_freespace_big(char *s) /* returrn in k bytes free */
{
	char bf[BFSZ];
	DWORD sc,bs,fc,c;
	bigint kb;

	ncpy(bf,s,BFSZ-1);
	s = strchr(bf,':');
	if (s!=NULL) {s+=2; *s = 0; s = bf;} else {
		/* Try and find end of filer name */
		if (strncmp(bf,"\\\\",2)==0) {
			s = strchr(bf+2,'\\');
			if (s!=NULL) s = strchr(s+1,'\\');
			if (s!=NULL) *s = 0;
			s = bf;
		} else s = "c:\\";
	}
	if (!GetDiskFreeSpaceA((XLPCWSTR)s,&sc,&bs,&fc,&c)) {
		return 2000000000;
	}
	// Munge result to avoid overflow, so if total is > 2tbytes just return 2tbytes
	kb = (bigint) sc* (bigint) bs* (bigint)fc /(1024);
	return  kb;

}
bigint lib_totalspace_big(char *s) /* returrn in k bytes free */
{
	char bf[BFSZ];
	DWORD sc,bs,fc,c;
	bigint kb;

	ncpy(bf,s,BFSZ-1);
	s = strchr(bf,':');
	if (s!=NULL) {s+=2; *s = 0; s = bf;} else {
		/* Try and find end of filer name */
		if (strncmp(bf,"\\\\",2)==0) {
			s = strchr(bf+2,'\\');
			if (s!=NULL) s = strchr(s+1,'\\');
			if (s!=NULL) *s = 0;
			s = bf;
		} else s = "c:\\";
	}
	if (!GetDiskFreeSpaceA((XLPCWSTR)s,&sc,&bs,&fc,&c)) {
		return 50000000;
	}
	// Munge result to avoid overflow, so if total is > 2tbytes just return 2tbytes
	kb = (bigint) sc* (bigint) bs* (bigint)c /(1024);
	return  kb;

}

int lib_freespace_debug(char *s, int *r_sc, int *r_bs, int *r_fc) /* returrn in k bytes free */
{
	char bf[BFSZ];
	DWORD sc,bs,fc,c;
	bigint kb;

	ncpy(bf,s,BFSZ-1);
	s = strchr(bf,':');
	if (s!=NULL) {s+=2; *s = 0; s = bf;} else {
		/* Try and find end of filer name */
		if (strncmp(bf,"\\\\",2)==0) {
			s = strchr(bf+2,'\\');
			if (s!=NULL) s = strchr(s+1,'\\');
			if (s!=NULL) *s = 0;
			s = bf;
		} else s = "c:\\";
	}
	if (!GetDiskFreeSpaceA((XLPCWSTR)s,&sc,&bs,&fc,&c)) {
		return 5000000;
	}

	*r_sc = sc;
	*r_bs = bs;
	*r_fc = fc;
	kb = (bigint) sc* (bigint) bs* (bigint)fc /(1024);
	if (kb/1024 > 2000000) return 2000000000;
	return (int) kb;

}

int lib_freespace2(char *s) /* returrn in k bytes free */
{
	char bf[BFSZ];
	DWORD sc,bs,fc,c;
	int mb;
	ncpy(bf,s,BFSZ-1);
	s = strchr(bf,':');
	if (s!=NULL) {s+=2; *s = 0; s = bf;} else {
		/* Try and find end of filer name */
		if (strncmp(bf,"\\\\",2)==0) {
			s = strchr(bf+2,'\\');
			if (s!=NULL) s = strchr(s+1,'\\');
			if (s!=NULL) *s = 0;
			s = bf;
		} else s = "c:\\";
	}
	if (!GetDiskFreeSpaceA((XLPCWSTR)s,&sc,&bs,&fc,&c)) {
		//vemsg("GetDiskFreeSpace call failed, {%s} errcode=%d \n",s,GetLastError());
		return -1;
	}
	// Munge result to avoid overflow, so if total is > 2tbytes just return 2tbytes
	mb = sc*(bs/512)*((fc/2) / 1024);
	if (mb > 2000000) {
		return 2000000000;  // then return 2tbytes. (in k)
	}

	return sc*(bs/512)*(fc/2);  /* Avoid an overflow */
}
#endif

#ifdef HAS_STATFS
#	define DONE_FREESPACE 1
#	ifdef is_bsdish
#	include <limits.h>
#	include <sys/param.h>
#	include <sys/mount.h>
#else
#	include <sys/vfs.h>
#endif
int lib_freespace(char *disk)
{
        struct statfs info;
        int r;
		int mb;
        if (statfs(disk,&info)!=0) {
                return 2200000;
        }
		mb = (info.f_bavail/2000)*(info.f_bsize/500);
		if (mb>2000000) {
			return 2000000000; // return 2tbytes if result would be larger...
		}
        r = (info.f_bavail/20)*(info.f_bsize/50);

        return r;
}
int lib_freespace2(char *disk)
{
        struct statfs info;
        int r;

        if (statfs(disk,&info)!=0) {
                return -1;
        }
        r = (info.f_bavail/20)*(info.f_bsize/50);
        return r;
}
bigint lib_freespace_big(char *disk)
{
        struct statfs info;
        bigint r;

        if (statfs(disk,&info)!=0) {
                return -1;
        }
        r = (bigint) ((bigint) info.f_bavail/20)*((bigint) info.f_bsize/50);
        return r;
}
int lib_freespace_debug(char *disk, int *r_sc, int *r_bs, int *r_fc) /* returrn in k bytes free */
{
        struct statfs info;
        int r;
		int mb;
		*r_sc = *r_bs = *r_fc = 0;
        if (statfs(disk,&info)!=0) {
                return 0;
        }
		*r_sc = 1;
		*r_bs = info.f_bsize;
		*r_fc = info.f_bavail;	
        r = (info.f_bavail/20)*(info.f_bsize/50);
        return r;
}
bigint lib_totalspace_big(char *disk)
{
        struct statfs info;
        bigint r;

        if (statfs(disk,&info)!=0) {
                return -1;
        }
        r = (bigint) ((bigint) info.f_blocks/20)*((bigint) info.f_bsize/50);
        return r;
}
#endif

#ifdef HAS_STATVFS
#	define DONE_FREESPACE 1
#	include <sys/statvfs.h>


int lib_freespace_debug(char *disk, int *r_sc, int *r_bs, int *r_fc) /* returrn in k bytes free */
{
        struct statvfs info;
        int r;
		int mb;
		*r_sc = *r_bs = *r_fc = 0;
        if (statvfs(disk,&info)!=0) {
                return 3000000;
        }
		mb = (info.f_bavail/2000)*(info.f_bsize/500);
		*r_sc = 1;
		*r_bs = info.f_bavail;
		*r_fc = info.f_frsize;

		if (mb>2000000) {
			return 2000000000; // return 2tbytes if result would be larger...
		}
        r = (info.f_bavail/20)*(info.f_frsize/50);
        return r;
}

int lib_freespace(char *disk)
{
        struct statvfs info;
        int r;
		int mb;

        if (statvfs(disk,&info)!=0) {
                return 3000000;
        }
		mb = (info.f_bavail/2000)*(info.f_bsize/500);
		if (mb>2000000) {
			return 2000000000; // return 2tbytes if result would be larger...
		}
        r = (info.f_bavail/20)*(info.f_frsize/50);
        return r;
}
int lib_freespace2(char *disk)
{
        struct statvfs info;
        int r;

        if (statvfs(disk,&info)!=0) {
                return -1;
        }
        r = (info.f_bavail/20)*(info.f_frsize/50);
        return r;
}
bigint lib_freespace_big(char *disk)
{
        struct statvfs info;
        bigint r;

        if (statvfs(disk,&info)!=0) {
                return -1;
        }
        r = (bigint) (info.f_bavail/20)* (bigint) (info.f_frsize/50);
        return r;
}
bigint lib_totalspace_big(char *disk)
{
        struct statvfs info;
        bigint r;

        if (statvfs(disk,&info)!=0) {
                return -1;
        }
        r = (bigint) (info.f_blocks/20)* (bigint) (info.f_frsize/50);
        return r;
}

#endif

#ifndef DONE_FREESPACE
int lib_freespace(char *s)
{
	return 5000000;
}
int lib_freespace2(char *s)
{
	return -1;
}
#endif

void if_free(void **a)
{
	if (*a==NULL) return;
	free(*a);
	*a = NULL;
}
char *lib_mygetenv(char *s)
{
	char *b;
	b = getenv(s);
	if (b==NULL) return "";
	return b;
}
#ifdef WIN32
#define chmod _chmod
#endif
int lib_chmod(char *fname, int mde)
{
	mde = mde & 0777; /* Strip special bits to be safe */
	return chmod(fname,mde);
}
int lib_chown(char *fname, int uid, int gid)
{
#ifndef WIN32
	return chown(fname,uid,gid);
#else
	return TRUE;
#endif
}


#ifdef WIN32
#ifndef __BORLANDC__
//#define _stat stat
//#define _fstat fstat
#endif
#endif
// c:\src\sam\lib.c


#ifndef USE_DFOPEN
#define my_stat(a,b) fd_statx(a,b,__FILE__,__LINE__)
int fd_stat(const char *path, struct stat *buffer, char *source, int line);

int fd_statx(const char *path, struct stat *buffer, char *src, int line)
{
/*	FILE *f = fopen(path,"rb");
	int r;
	if (f==NULL) return -1;
	r = fstat(fileno(f),buffer);
	fclose(f);
	return r;*/

	return stat(path,buffer);
}

#endif



int lib_file_osize_x(FILE *f, char *src, int line)
{
	struct stat bf;
	wild_stats_add(src,line);

	if (f==NULL) return -1;
	if (fstat(fileno(f),&bf)!=0) {return -1;} /* Its nice to know when a file does not exist  */
	return bf.st_size;
}
unsigned long lib_file_osize_unsigned(FILE *f)
{
	struct stat bf;
	if (f==NULL) return 0;

	if (fstat(fileno(f),&bf)!=0) {return 0;}
	return bf.st_size;
}
int lib_file_osizeh_x(int handle, char *src, int line)
{
	struct stat bf;
	wild_stats_add(src,line);


	if (fstat(handle,&bf)!=0) {return -1;} /* Its nice to know when a file does not exist  */
	return bf.st_size;
}
int lib_file_size_r(char *fname, char *src, int line)
{
	struct stat bf;
//printf("lib_Filesize2\n");
	wild_stats_add(src,line);

#ifdef USE_DFOPEN

//	printf("lib_file_sizea %s %s:%d\n",fname,src,line);
	if (fd_stat(fname,&bf,src,line)!=0) {return -1;} /* Its nice to know when a file does not exist  */
#else
//	printf("lib_file_sizeb %s %s:%d\n",fname,src,line);
	if (my_stat(fname,&bf)!=0) {return -1;} /* Its nice to know when a file does not exist  */
#endif
	return bf.st_size;
}
int lib_file_size_true(char *fname)
{
	struct stat bf;

	if (my_stat(fname,&bf)!=0) {return 0;} /* Its nice to return 0 when using this function for quota calculations */
	return bf.st_size;
}

// New function to work out disk usage...
int disk_usage(char *path, double *used, double *total)
{
	bigint tot =  lib_totalspace_big(path); /* returrn in k bytes free */
	bigint f =  lib_freespace_big(path); /* returrn in k bytes free */
	if (tot<0) return FALSE;
	if (f<0) return FALSE;
	*used = (double) (tot-f);
	*total =(double) ( tot);
	return TRUE;
}


unsigned long lib_file_size_unsigned(char *fname)
{
	struct stat bf;


	if (my_stat(fname,&bf)!=0) {return 0;} /* Its nice to return 0 when using this function for quota calculations */
	return bf.st_size;
}
int lib_file_count_lines(char *file, int *lines) {
	char bf[BFSZ];
	FILE *f;

	if (!lines) return FALSE;

	f = fopen(file,"r");
	if (!f) return FALSE;
	for(*lines = 0; fgets(bf,BFSZ,f); (*lines)++) ;
	fclose(f);
	return TRUE;
}
int lib_file_move(char *src, char *dst, char *reason)
{
	int i=0;
	strcpy(reason,"ok");
	for (;i<10;i++) {
		if (rename(src,dst)==0) {
			if (i>0) vimsg("lib_file_move: Succeeded (%s) (%s)\n",src,dst);
			return TRUE;
		}
		lib_msleep(10);
		vimsg("lib_file_move: Trying again (%s) (%s) (%s)\n",src,dst,strerror(errno));
	}
	vimsg("lib_file_move: failed trying copy %s %s %s\n",src,dst,strerror(errno));
	lprintf(reason,BFSZ-1,"lib_file_move: failed trying copy %s %s %s\n",src,dst,strerror(errno));
	return lib_file_copy(src,dst,reason);
}
int lib_file_copy(char *src, char *dst, char *reason)
{
	return lib_file_copy_mkdir(src,dst,reason,FALSE);
}

int lib_copy_open(FILE *fin, FILE *fout, char *reason)
{
	int r = TRUE;
	char bf[12000];
	int k,kk;
	for (;!feof(fin);) {
		k = (int)fread(bf,1,10000,fin);
		if (k<=0) break;
		kk = (int)fwrite(bf,1,k,fout);
		if (kk!=k) {
			sprintf(reason,"copy failed fout %.100s",strerror(errno));
			r = FALSE;
			break;
		}
	}

	return r;
}

int lib_file_copy_mkdir(char *src, char *dst, char *reason, int makedir)
{
	FILE *fin=NULL;
	FILE *fout=NULL;
	int r = TRUE;
	char bf[12000];
	int k,kk;
	fin = fopen(src,"rb");
	if (fin==NULL) {
		sprintf(reason,"src %.100s failed %.100s",src,strerror(errno));
		r = FALSE;
		goto done;
	}
	if (makedir) fout = lib_fopen_create(dst,"wb",0770);
	else fout = fopen(dst,"wb");
	if (fout==NULL) {
		sprintf(reason,"dst %.100s failed fout %.100s",dst,strerror(errno));
		r = FALSE;
		goto done;
	}
	for (;!feof(fin);) {
		k = (int)fread(bf,1,10000,fin);
		if (k<=0) break;
		kk = (int)fwrite(bf,1,k,fout);
		if (kk!=k) {
			sprintf(reason,"copy %.100s %.100s failed fout %.100s",src,dst,strerror(errno));
			r = FALSE;
			break;
		}
	}
done:	if (fin!=NULL) fclose(fin);
	if (fout!=NULL) {
		if (fclose(fout)!=0) {
			sprintf(reason,"copy %.100s %.100s failed fclose() %.100s",src,dst,strerror(errno));
			r = FALSE;
		}
	}
	return r;
}
int lib_file_copy_contains(char *src, char *dst, char *contains, char *reason, char *cause)
{
	FILE *fin=NULL;
	FILE *fout=NULL;
	int r = TRUE;
	char bf[12000];
	strcpy(reason,"");
	fin = fopen(src,"rb");
	if (fin==NULL) {
		sprintf(reason,"src %.100s failed %.100s",src,strerror(errno));
		r = FALSE;
		goto done;
	}
	else fout = fopen(dst,"wb");
	if (fout==NULL) {
		sprintf(reason,"dst %.100s failed fout %.100s",dst,strerror(errno));
		r = FALSE;
		goto done;
	}
	if (strlen(cause)>0) fprintf(fout,"%s\n",cause);
	for (;!feof(fin);) {
		if (fgets(bf,BFSZ-1,fin)==NULL) break;
		if (strstr(bf,contains)!=NULL) fwrite(bf,1,strlen(bf),fout);
	}
done:	if (fin!=NULL) fclose(fin);
	if (fout!=NULL) {
		if (fclose(fout)!=0) {
			sprintf(reason,"copy %.100s %.100s failed fclose() %.100s",src,dst,strerror(errno));
			r = FALSE;
		}
	}
	return r;
}
int lib_file_copy_size(char *src, char *dst, char *reason, int sz, char *prefix)
{
	FILE *fin=NULL;
	FILE *fout=NULL;
	int r = TRUE;
	char bf[12000];
	int k,kk;
	fin = fopen(src,"rb");
	if (fin==NULL) {
		sprintf(reason,"src %.100s failed %.100s",src,strerror(errno));
		r = FALSE;
		goto done;
	}
	fout = lib_fopen_create(dst,"wb",0770);
	if (fout==NULL) {
		sprintf(reason,"dst %.100s failed fout %.100s",dst,strerror(errno));
		r = FALSE;
		goto done;
	}
	if (prefix) if (lib_strlen(prefix)>0) fprintf(fout,"%s",prefix);
	for (;!feof(fin);) {
		int  blk = 10000;
		if (blk>sz) blk = sz;
		if (blk<=0) break;
		k = (int)fread(bf,1,10000,fin);
		if (k<=0) break;
		sz -= k;
		kk = (int)fwrite(bf,1,k,fout);
		if (kk!=k) {
			sprintf(reason,"copy %.100s %.100s failed fout %.100s",src,dst,strerror(errno));
			r = FALSE;
			break;
		}
	}
done:	if (fin!=NULL) fclose(fin);
	if (fout!=NULL) fclose(fout);
	return r;
}
int lib_file_write(char *fname, char *txt)
{
	FILE *f = fopen(fname,"w");
	if (f==NULL) return FALSE;
	fprintf(f,"%s",txt);
	fclose(f);
	return TRUE;
}
int lib_file_read(char *file, char **data, int *size, int max) {
	struct stat finf;
	FILE *fp;
	int get;
	if (!file || !data) return FALSE;

	if (my_stat(file,&finf) == -1) return FALSE;
	get = finf.st_size;
	if (get>max) get = max;
	if (size) *size = get;

	fp = fopen(file,"rb");
	if (!fp) return FALSE;


	*data = calloc(get+1,sizeof(char));
	if (!*data) return FALSE;
	(int)fread(*data,get,1,fp);
	if (ferror(fp)) {
		free(*data);
		return FALSE;
	}
	fclose(fp);
	return TRUE;
}
int lib_append_file(FILE *fout, char *xname)
{
	char bf[BFSZ];
	FILE *fin;
	int r;
	int sz=0;
	int at=0;
	fin = fopen(xname,"r");
	if (fin==NULL) {
		fprintf(fout,"\n----  Cound not append file (%s) %s\n\n",xname,strerror(errno));
		return TRUE;
	}
	for (;!feof(fin);) {
		r = (int)fread(bf,1,BFSZ-1,fin);
		if (r<=0) break;
		if (r>0) {
			sz += r;
			(int)fwrite(bf,1,r,fout);
		}
	}
	fclose(fin);
	return TRUE;
}

int lib_file_compare(char *fn1, char *fn2)
/*
returns true if file contents are identical.
*/
{
	int r1,r2;
	char bf1[BFSZ];
	char bf2[BFSZ];
	int ret=true;
	FILE *f1,*f2;
	f1=fopen(fn1,"rb");
	if (!f1) return false;
	f2=fopen(fn2,"rb");
	if (!f2) {
		fclose(f1);
		return false;
	}
	while(!feof(f1) || !feof(f2)) {
		r1=(int)fread(bf1,1,BFSZ-1,f1);
		r2=(int)fread(bf2,1,BFSZ-1,f2);
		if (r1!=r2) {
			ret=false;
			break;
		}
		if (r1<=0) {
			ret=true;
			break;
		}
		if (memcmp(bf1,bf2,r1)!=0) {
			ret=false;
			break;
		}
	}
	fclose(f1);
	fclose(f2);

	return ret;
}

int lib_read_next_line(FILE *f, char *bf, int bfsz)
{
	bf[0]=0;
	if (!f) return false;
	if (feof(f)) return false;
	if (fgets(bf,bfsz-1,f)) {
		if (bf[0]) {
			strip_eol(bf);
			return true;
		}
	}
	return false;
}

int lib_text_file_compare(char *fn1, char *fn2)
/*
returns true if file contents are identical, ignoring end of line chars
and trailing blank lines

*/
{
	char b1[BFSZ];
	char b2[BFSZ];
	int ret=true;
	FILE *f1,*f2;
	f1=fopen(fn1,"rb");
	if (!f1) return false;
	f2=fopen(fn2,"rb");
	if (!f2) {
		fclose(f1);
		return false;
	}
	while(true) {
		int ok1=lib_read_next_line(f1,b1,BFSZ);
		int ok2=lib_read_next_line(f2,b2,BFSZ);
		if (ok1 && ok2) {
			if (strcmp(b1,b2)!=0) {
				ret=false;
				break;
			}
			continue;
		}
		if (!ok1 && !ok2) {
			break;
		}
		if (!ok1 && b2[0]) {
			ret=false;
			break;
		}
		if (!ok2 && b1[0]) {
			ret=false;
			break;
		}
	}
	fclose(f1);
	fclose(f2);

	return ret;
}

char *lib_addsep(char *s, char *tmp)
{
	int n=lib_strlen(s);
	ncpy(tmp,s,BFSZ-1);
	s = tmp;
	if (n<1) return s;
	if (s[n-1]=='/') return s;
	if (s[n-1]=='\\') return s;
#ifdef WIN32
	s[n++] = '\\';
	s[n] = 0;
#else
	s[n++] = '/';
	s[n] = 0;
#endif
	return s;
}

char *lib_file_name_only(char *full_path_and_name)
{
	char *s2,*s;
	s=full_path_and_name;
	if (!s) return s;

	while(s2=strchr(s,'/')) s=s2+1;
	while(s2=strchr(s,'\\')) s=s2+1;

	return s;
}

char *lib_dir_name_only(char *full_path_and_name, char *bf, int bfsz)
/*
e.g lib_dir_name_only("c:\test\abc\def.txt") returns "c:\test\abc";
*/
{
	char *s2,*s;
	if (!full_path_and_name || full_path_and_name[0]==0) return full_path_and_name;

	ncpy(bf,full_path_and_name,bfsz-1);
	s=bf;

	while(s2=strchr(s,'/')) s=s2+1;
	while(s2=strchr(s,'\\')) s=s2+1;
	*s=0;
	if (s>bf) s[-1]=0;

	return bf;
}

char *lib_get_extension(char *fname)
{
	char *p,*p2;
	p=fname;
	while(true) {
		p2=strchr(p+1,'.');
		if (!p2) break;
		p=p2;
	}
	if (p==fname) return "";
	return p;
}

char *lib_showbinary(unsigned char *buf,int buflen,char *tmp, int bfsz)
{
	int i;
	unsigned char c;
	char *start=tmp;
	for (i=0;i<buflen;i++) {
		if (tmp-start>bfsz-10) break;
		c = buf[i];
		if (sisprint(c) && c>33) {
			*tmp++ = buf[i];
		} else {
			sprintf(tmp,"'%02X'",c);
			tmp += lib_strlen(tmp);
		}
	}
	*tmp = 0;
	return start;
}

char *lib_get_rest(char *out, char *cmd, int n)
{
	int i;
	char *s;
	s = cmd;
	for (i=0; i<n; i++) {
		s = strchr(s,' '); if (s==NULL) break;
		s++;
	}
	if (s==NULL) {
		ncpy(out,"",BFSZ-1);
		return out;
	}
	ncpy(out,s,BFSZ-1);
	return out;
}
char *lib_get_rest2(char *out, char *cmd, int n) // Ignore double spaces...
{
	int i;
	char *s;
	s = cmd;
	for (i=0; i<n; i++) {
		s = strchr(s,' '); if (s==NULL) break;
		for (; sisspace(*s); s++) ;
	}
	if (s==NULL) {
		ncpy(out,"",BFSZ-1);
		return out;
	}
	ncpy(out,s,BFSZ-1);
	return out;
}


void lib_copy_upto(char **src, char c, char *dst, int bfsz) {
	char *s = NULL;
	int i;

	for(s = *src, i = 0; *s && *s != c && i < bfsz-1; s++, i++)
		dst[i] = *s;
	dst[i] = 0;
	*src = s;
}

void lib_copy_upto_any(char **src, char *c, char *dst, int bfsz) {
	char *s = NULL;
	int i;

	for(s = *src, i = 0; *s && !strchr(c,*s) && i < bfsz-1; s++, i++)
		dst[i] = *s;
	dst[i] = 0;
	*src = s;
}
void lib_copy_upto_quoted(char **src, char *c, char *dst, int bfsz)
{
	char *s = *src;
	int i;

	if (*s=='"') {
		i = 0;
		dst[i++] = *s++;
		for( ; *s && *s!='"' && i < bfsz-2; s++, i++)
			dst[i] = *s;
		if (*s) dst[i++]  = *s++; // add the quote.
		dst[i] = 0;
		*src = s;
		return;
	}
	for(s = *src, i = 0; *s && !strchr(c,*s) && i < bfsz-1; s++, i++)
		dst[i] = *s;
	dst[i] = 0;
	*src = s;
}

void lib_skip_any(char **src, char *c) {
	while(**src && strchr(c,**src)) (*src)++;
}

void lib_copy_upto_str(char **src, char *c, char *dst, int bfsz) {
	char *s = *src;
	int len,i;

	len = lib_strlen(c);
	for(s = *src, i = 0; *s && i < bfsz-1; s++, i++) {
		if (strncmp(s,c,len)==0) break;
		dst[i] = *s;
	}
	dst[i] = 0;
	*src = s;
}
char *strclr(char *s)
{
	*s = 0;
	return s;
}
char *lib_get_word_strip(char *out, char *xcmd, int want)
{
	char *s;
	char bf[BFSZ];
	char *ret = lib_get_word(out,xcmd,want);
	if (*ret=='<') {
		ncpy(bf,ret+1,BFSZ-1);
		s = strchr(bf,'>');
		if (s!=NULL) *s = 0;
		ncpy(out,bf,BFSZ-1);
	}
	return ret;

}
char *lib_get_word(char *out, char *xcmd, int want)
{
	char *p[100];
	char cmd[BFSZ];
	int n;
	ncpy(cmd,xcmd,BFSZ-1);
	want--;
	if (want==0) want = 0;
	strcpy(out,"");
	n = token_split(cmd,p," \r\n\t",99);
	if (n<=want) return out;
	ncpy(out,p[want],BFSZ-1);
	return out;
}

char *lib_escape_square(char *subj, char *tmp)
{
	char *out=tmp;
	int i;
	char *s = subj;
	if (s==NULL) return "";
	if (lib_strlen(s)>600) return s;

	for (i=0; i<1000 && *s!=0;i++,s++) {
		if (*s=='[') { strcpy(out,"\\["); out += lib_strlen(out); }
		if (*s==']') { strcpy(out,"\\]"); out += lib_strlen(out); }
		else *out++ = *s;
	}
	*out++ = 0;
    return tmp;
}

char *lib_url_encode(char *tmp, char *url)
{
	return lib_url_encode2(tmp,url);
}
char *lib_url_encode_broken(char *tmp, char *url)  // this one is broken!!!
{
	char *out=tmp;
	int i;
	char *s = url;
	if (s==NULL) return "";
	if (lib_strlen(s)>600) return s;

	for (i=0; i<1000 && *s!=0;i++,s++) {
		if (*s=='<') { strcpy(out,"&lt;"); out += lib_strlen(out); }
		else if (*s=='>') { strcpy(out,"&gt;"); out += lib_strlen(out); }
		else if (*s=='&') { strcpy(out,"&amp;"); out += lib_strlen(out); }
		else if (*s==' ') { strcpy(out,"+"); out += lib_strlen(out); }
		else if (*s=='+') { strcpy(out,"&#43;"); out += lib_strlen(out); }
		else if (*s=='"') { strcpy(out,"&#34;"); out += lib_strlen(out); }
		else *out++ = *s;
	}
	*out++ = 0;
        return tmp;
}
char *lib_url_encode2(char *tmp, char *url)
{
	char *out=  tmp;
	int i;
	unsigned char *s = (unsigned char *) url;
	if (s==NULL) return "";
	if (lib_strlen(s)>600) return s;

	for (i=0; i<1000 && *s!=0;i++,s++) {
		if (*s=='<') { strcpy(out,"&lt;"); out += lib_strlen(out); }
//		else if (*s=='>') { strcpy(out,"&gt;"); out += lib_strlen(out); }
//		else if (*s=='&') { strcpy(out,"&amp;"); out += lib_strlen(out); }
//		else if (*s==' ') { strcpy(out,"+"); out += lib_strlen(out); }
//		else if (*s=='+') { strcpy(out,"&#43;"); out += lib_strlen(out); }
//		else if (*s=='"') { strcpy(out,"&#34;"); out += lib_strlen(out); }

		else if ((*s<32) || (*s>=128) || (*s=='#')||(*s=='`')||(*s==',')||(*s=='\'')||(*s=='"')|| (*s=='\\')|| (*s=='<')|| (*s=='>')|| (*s=='"')|| (*s=='+')|| (*s==' ')|| (*s=='&')) {
			*out++ = '%';
			sprintf(out,"%02X",*s);
			out += lib_strlen(out);
		} else *out++ = *s;
	}
	*out++ = 0;
        return tmp;
}

static char *encode3="#'`,$\\\"><+ &|/;:=?@{}~[]!%)(-_*^.";
char *lib_url_encode3(char *tmp, char *url, int len)			// NOTE: url is NOT null terminated and len specifies how much to process
{
	char *out=  tmp;
	int i;
	unsigned char *s = (unsigned char *) url;
	if (s==NULL) return "";
	if (lib_strlen(s)>600) return s;

	for (i=0; i<len ;i++,s++) {
		if (strchr(encode3,*s)!=NULL || *s>126 || *s<33) {
			*out++ = '%';
			sprintf(out,"%02X",*s);
			out += lib_strlen(out);
		} else *out++ = *s;
		if (out-tmp>BFSZ-10) break;
	}
	*out++ = 0;
    return tmp;
}
char *lib_quote_slash(char *in, char *tmp)
{
	char *out=tmp;
	int i;
	char *s = in;
	if (s==NULL) return "";

	for (i=0; i<BFSZ && *s!=0;i++,s++) {
		if (*s=='"') { strcpy(out,"\\"); out += lib_strlen(out); }
		*out++ = *s;
	}
	*out++ = 0;
    return tmp;
}
char *lib_fts_encode(char *tmp, char *input,int bfsz)
{
	char *s;
	char *out=tmp;
	for (s=input;*s!=0;s++) {
		if (*s==' ') { strcpy(out,"+"); out += lib_strlen(out); }
		else *out++ = *s;
		if (out-tmp>bfsz-3) break;
	}
	*out++ = 0;
    return tmp;
}
char *lib_fts_decode(char *tmp, char *input,int bfsz)
{
	char *s;
	char *out=tmp;
	for (s=input;*s!=0;s++) {
		if (*s=='+') { strcpy(out," "); out += lib_strlen(out); }
		else *out++ = *s;
		if (out-tmp>bfsz-3) break;
	}
	*out++ = 0;
    return tmp;
}
unsigned long fixed_atol(char *s)
{
	char *xend;
	return strtoul(s,&xend,10);
}

char *lib_encode_quote(char *tmp, char *input, int bfsz)
{
	char *out=tmp;
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";

	for (i=0;  *s!=0;i++,s++) {
		if (*s=='&') { strcpy(out,"&amp;"); out += lib_strlen(out); }
		else if (*s=='"') { strcpy(out,"&#34;"); out += lib_strlen(out); }
		else if (*s=='\'') { strcpy(out,"&#39;"); out += lib_strlen(out); }
		else if (*s>126) { lprintf(out,12,"&#%d;",(unsigned int) *s); out += lib_strlen(out); }
		else *out++ = *s;
		if ((out-tmp) > (bfsz-20)) break;
	}
	*out++ = 0;
    return tmp;
}

char *lib_encode_quoteonly(char *tmp, char *input, int bfsz)
{
	char *out=tmp;
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";

	for (i=0;  *s!=0;i++,s++) {
		if (*s=='\\') { strcpy(out,"\\\\"); out += lib_strlen(out); }
		else if (*s=='"') { strcpy(out,"\\\""); out += lib_strlen(out); }
		else *out++ = *s;
		if ((out-tmp) > (bfsz-20)) break;
	}
	*out++ = 0;
    return tmp;
}
char *lib_decode_quoteonly(char *bf, char *s, int bfsz)
{
	int i;

	for(i = 0; i < BFSZ && *s; i++, s++) {
		if (*s == '\\') {
			if (s[1] == '\\') {
				bf[i] = '\\';
				s++;
			}
			else if (s[1] == '"') {
				bf[i] = '"';
				s++;
			}
		}
		else bf[i] = *s;
	}
	bf[i] = '\0';
	return bf;
}
// Trim at first non \encoded first double quote
// Marijn: Write a new function but I thought there was a function already to do this... (related function web_encode_noquot)
char *lib_trim_dquote(char *tmp, char *input, int bfsz)
{
	char *out=tmp, last='\0';
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";
	for (i=0; *s!=0;i++,s++) {
		if (*s=='"' && last!='\\') break;
		*out++ = *s;
		last=*s;
		if ((out-tmp) > (bfsz-20)) break;
	}
	*out++ = 0;
    return tmp;
}
char *lib_trim_squote(char *tmp, char *input, int bfsz)
{
	char *out=tmp, last='\0';
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";
	for (i=0; *s!=0;i++,s++) {
		if (*s=='\'' && last!='\\') break;
		*out++ = *s;
		last=*s;
		if ((out-tmp) > (bfsz-20)) break;
	}
	*out++ = 0;
    return tmp;
}
// Check for any unescape encoded characters
int lib_esc_encoded(char *s)
{
	char last='\0';
	if (s==NULL) return TRUE;
	for (;*s!=0;s++) {
		if ((*s=='\''||*s=='\"') && last!='\\') return FALSE;
		last=*s;
	}
    return TRUE;
}

#define toxdigit(x) (x<='9'?x-'0' : x<='F'?x-'A'+10 : x-'a'+10)
char *lib_decode_exclamation(char *bf, char *s, int bfsz)
{
	int i;
	for(i = 0; i < BFSZ && *s; i++, s++) {
		if (*s == '!')  {
			if (!isxdigit(s[1]) || !isxdigit(s[2])) {
				bf[i] = *s;
				continue;
			}
			bf[i] = toxdigit(s[1])*16 + toxdigit(s[2]);
			s+=2;
		}
		else bf[i] = *s;
	}
	bf[i] = '\0';
	return bf;
}
char *lib_decode_percent(char *bf, char *s, int bfsz)
{
	int i;
	for(i = 0; i < bfsz && *s; i++, s++) {
		if (*s == '%')  {
			if (!isxdigit(s[1]) || !isxdigit(s[2])) {
				bf[i] = *s;
				continue;
			}
			bf[i] = toxdigit(s[1])*16 + toxdigit(s[2]);
			s+=2;
		}
		else bf[i] = *s;
	}
	bf[i] = '\0';
	return bf;
}
char *lib_decode_percent_clean(char *bf, char *s, int bfsz)
{
	int i;
	for(i = 0; i < BFSZ && *s; i++, s++) {
		if (*s == '%')  {
			if (!isxdigit(s[1]) || !isxdigit(s[2])) {
				bf[i] = *s;
				continue;
			}
			bf[i] = toxdigit(s[1])*16 + toxdigit(s[2]);
			if(bf[i]<0x20) i--;	// ignore non printables
			s+=2;
		}
		else bf[i] = *s;
	}
	bf[i] = '\0';
	return bf;
}

char *lib_web_xss(char *tmp, char *input)
{
	char *out=tmp;
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";
//	if (lib_strlen(s)>600) return s;

	for (i=0;  *s!=0;i++,s++) {
		if (*s=='<') continue;
		else if (*s=='>') continue;
		else if (*s=='"') continue;
		else *out++ = *s;
		if ((out-tmp) > (BFSZ-20)) break;
	}
	*out++ = 0;
    return tmp;
}

char *lib_web_encode(char *tmp, char *input, int bfsz)
{
	char *out=tmp;
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";
//	if (lib_strlen(s)>600) return s;

	for (i=0;  *s!=0;i++,s++) {
		if (*s=='<') { strcpy(out,"&lt;"); out += lib_strlen(out); }
		else if (*s=='>') { strcpy(out,"&gt;"); out += lib_strlen(out); }
		else if (*s=='&') { strcpy(out,"&amp;"); out += lib_strlen(out); }
		else if (*s=='+') { strcpy(out,"&#43;"); out += lib_strlen(out); }
		else if (*s=='"') { strcpy(out,"&#34;"); out += lib_strlen(out); }
		else if (*s=='\'') { strcpy(out,"&#39;"); out += lib_strlen(out); }
		else if (*s>126) { lprintf(out,12,"&#%d;",(unsigned int) *s); out += lib_strlen(out); }
		else *out++ = *s;
		if ((out-tmp) > (bfsz-20)) break;
	}
	*out++ = 0;
    return tmp;
}
char *lib_web_encode_8bit(char *tmp, char *input, int bfsz)
{
	char *out=tmp;
	int i;
	unsigned char *s = (unsigned char *) input;
	if (s==NULL) return "";
//	if (lib_strlen(s)>600) return s;

	for (i=0;  *s!=0;i++,s++) {
		if (*s=='<') { strcpy(out,"&lt;"); out += lib_strlen(out); }
		else if (*s=='>') { strcpy(out,"&gt;"); out += lib_strlen(out); }
		else if (*s=='&') { strcpy(out,"&amp;"); out += lib_strlen(out); }
		else if (*s=='+') { strcpy(out,"&#43;"); out += lib_strlen(out); }
		else if (*s=='"') { strcpy(out,"&#34;"); out += lib_strlen(out); }
		else if (*s=='\'') { strcpy(out,"&#39;"); out += lib_strlen(out); }
		else *out++ = *s;
		if ((out-tmp) > (bfsz-20)) break;
	}
	*out++ = 0;
    return tmp;
}
int lib_strip_html(char *in, char *out, int bfsz)
{
	char *s;
	int inhtml = FALSE;
	char *o=out;
	for (s=in;*s!=0;s++) {
		if (*s=='<') inhtml = TRUE;
		if (strncmpnc(s,"&nbsp;",6)==0) {
			*o++ = ' ';
			s+=5;
			continue;
		}
		if (!inhtml) *o++ = *s;
		if (*s=='>') inhtml = FALSE;
		if (o-out>bfsz-10) break;
	}
	*o++ = 0;
	return TRUE;
}
char *lib_strip_text(char *in, char *txt, char *out, int bfsz)
{
	char *s;
	char *o=out;
	int len=(int)strlen(txt);
	for (s=in;*s!=0;s++) {
		if (strncmpnc(s,txt,len)==0) {
			s+=(len-1);
			continue;
		}
		*o++ = *s;
		if (o-out>bfsz-10) break;
	}
	*o++ = 0;
	return out;
}

char *lib_mailto_encode(char *tmp, char *input) // strip quotes as outlook seems to be confused by them.
{
	char *out=tmp;
	int i;
	char *s = input;
	if (s==NULL) return "";
	if (lib_strlen(s)>600) return s;

	for (i=0; i<1000 && *s!=0;i++,s++) {
		if (*s=='<') { strcpy(out,"&lt;"); out += lib_strlen(out); }
		else if (*s=='>') { strcpy(out,"&gt;"); out += lib_strlen(out); }
		else if (*s=='&') { strcpy(out,"&amp;"); out += lib_strlen(out); }
		else if (*s=='+') { strcpy(out,"&#43;"); out += lib_strlen(out); }
		else if (*s=='"') ;
		else *out++ = *s;
	}
	*out++ = 0;
        return tmp;
}
#define LONGLINE 32000
int lib_undotstuff(char *fname)
{
	char *bf = calloc(1,LONGLINE);
	char fname2[BFSZ];
	FILE *fin,*fout;
	lprintf(fname2,BFSZ-1,"%s_new",fname);
	fin = fopen(fname,"rb");
	if (fin==NULL) {
		free(bf);
		return FALSE;
	}
	fout = fopen(fname2,"wb");
	if (fout==NULL) {fclose(fin); free(bf); return FALSE;}
	for (;!feof(fin);) {
		if (fgets(bf,LONGLINE-1,fin)==NULL) break;
		if (*bf=='.') fprintf(fout,"%s",bf+1);
		else fprintf(fout,"%s",bf);
	}
	fclose(fin); fclose(fout);
	remove(fname);
	rename(fname2,fname);
	free(bf);
	return TRUE;
}
char *lib_nntp_escape(char *s, char *tmp)
{
   ncpy(tmp,s,BFSZ-1);
  for (s=tmp;*s!=0;s++) {
    if (sisspace(*s)) *s = '+';
  }
  return tmp;
}

char *lib_url_decode(char *bf, char *s) {
	int i,x;

	for(i = 0; i < BFSZ && *s; i++, s++) {
		if (*s == '+') bf[i] = ' ';
		else if (*s == '&') {
			if (s[1] == '#') {
				x = 0;
				for(s += 2; *s && *s != ';'; s++) {
					if (sisdigit(*s)) x += *s - '0';
					else if (sisupper(*s)) x += (*s - 'A' + 10);
					else x += (*s - 'a' + 10);
					x *= 16;
				}
				bf[i] = x;
			}
			else if (strncmp(s,"&lt;",4)==0) {
				bf[i] = '<';
				s += 3;
			}
			else if (strncmp(s,"&gt;",4)==0) {
				bf[i] = '>';
				s += 3;
			}
			else if (strncmp(s,"&amp;",5)==0) {
				bf[i] = '&';
				s += 4;
			}
		}
		else bf[i] = *s;
	}
	bf[i] = '\0';
	return bf;
}

#ifdef WIN32
static __declspec(thread) char *tls_stack=NULL;
static __declspec(thread) char *tls_name=NULL;
static __declspec(thread) int tls_stacksz=0;
#define STACK_CHECK 1
#else
#ifdef STACK_CHECK
static __thread char *tls_stack=NULL;
static __thread char *tls_name=NULL;
static __thread int tls_stacksz=0;
#else
static char *tls_stack=NULL;
static char *tls_name=NULL;
static int tls_stacksz=0;
#endif
#endif

static long stack_worst;
static size_t stack_usage;
static char *stack_where;
int stack_tot(void)
{
	return stack_worst;
}
char *stack_whence(void)
{
	return stack_where;
}
int stack_tot_usage(void)
{
	return (int) stack_usage;
}
void stack_init(char *name, int stacksz)
{
	char x[100];
	x[1] = 1;
#ifdef STACK_CHECK
	tls_stack = x;
	tls_name = name;
	tls_stacksz = stacksz;
#endif
}
void stack_print(void)
{
	char x[100];
	long tot;
	x[1] = 1;
#ifdef STACK_CHECK
	if (tls_stack==NULL) return;
	tot = (long) (tls_stack - x);
	printf("Stack Depth %d %x\n",tot,tot);
#endif
}
void stack_ok(char *whence)
{
	char x[100];
	long tot;
	x[1] = 1;
#ifdef STACK_CHECK
	if (tls_stack==NULL) return;
	tot = (long) (tls_stack - x);
	if (tot<0) return;
	if (tot>stack_worst) {
		stack_worst = tot;
		stack_usage = GetStackUsage();
		stack_where = tls_name;
		if (tls_stacksz ==0) tls_stacksz = 500000;
		if (stack_worst > (tls_stacksz-100000)) {
			tls_stack = NULL; // to prevent infinite loop
			if (vemsg) vemsg("Stack exceeded limit %d/%d %s\n",stack_worst,tls_stacksz,tls_name);
			lib_abort();
		}
	}
#endif
}

char *lib_clean_angle(char *fname, char *tmp)
{
	char *s;
	char *xtmp=tmp;
	for (s=fname;*s!=0;s++) {
		if (*s=='<') *tmp++ = '{';
		else if (*s=='>') *tmp++ = '}';
		else *tmp++ = *s;
	}
	*tmp++ = 0;
	return xtmp;
}



char *lib_capitalize(char *word, char *tmp)
{
	ncpy(tmp,word,BFSZ-1);
	tmp[0] = toupper(tmp[0]);
	return tmp;
}

char *lib_capitalize_all(char *word, char *tmp)
{
	int space = TRUE;
	char *ptr;

	ncpy(tmp,word,BFSZ-1);
	for(ptr = tmp; *ptr; ptr++) {
		if (*ptr == '_') *ptr = ' ';
		if (sisspace(*ptr)) { space = TRUE; continue; }
		if (!space) continue;
		*ptr = toupper(*ptr);
		space = FALSE;
	}
	return tmp;
}



/*

Cool thing for counting and sorting a 'thing'

  typedef struct ATHING {char *thing; struct ATHING next*; int n;} Athing;


    typedef struct THING {Athing *table[2000]; Athing *sorted;} Thing;

	thing_add(Thing **th, char *xxx);
	n = thing_sort(th);
	char *thing_get(Thing *th, int n, int *x);
	thing_free(Thing **th);

*/









/*
int lib_file_size(char *fname)
{
	FILE *f;
	int r;
	f = fopen(fname,"r");
	if (f==NULL) return -1;
	r = lib_file_osize(f);
	fclose(f);
	return r;
}*/
static char * wild_src[6000];
static int wild_line[6000];
static int wild_rline[6000];
int wild_stats_add(char *src, int rline)
{
	int line = rline % 5005;
	if (line>0) {
		(wild_line[line])++;
		wild_src[line] = src;
		wild_rline[line] = rline;
	}
	return TRUE;
}
int wild_stats_report(void)
{
	FILE *f;
	int i;
	f = fopen("wild.report","w");
	if (f!=NULL) {
		for (i=0; i<6000; i++) {
			if (wild_line[i] > 0) {
				fprintf(f,"%d %s:%d\n",wild_line[i],wild_src[i],wild_rline[i]);
			}
		}
		fclose(f);
	}

	return TRUE;
}
int lib_exists_real(char *fname, char *src, int line)
{
	struct stat st;
	if (fd_stat(fname,&st,src,line)==0) return TRUE;
	return FALSE;
}

int lib_create_file(char *fname)
{
	FILE *f;
	f=fopen(fname,"w");
	if (f) {
		fprintf(f,"x");
		fclose(f);
	}
	return lib_exists(fname);
}
int lib_create_file2(char *fname, char *body)
{
	FILE *f;
	f=fopen(fname,"w");
	if (f) {
		fprintf(f,"%s",body);
		fclose(f);
	}
	return lib_exists(fname);
}
/*
Deal with:
	/aa/xxxx
	/aa/xx*xxx
	/aa/xx*xxx/xx*
	/aa/xx/x*x/yy*
*/
int tree_scan2(Tree_Handle *tree, WILD_DATA **th, char *base, char *wild, char *p[], int upto, int np, int issub);
int tree_base(char *bf, char *base, char *wild)
{
	if (lib_strlen(base)==0) {strcpy(bf,wild); return TRUE;}
	sprintf(bf,"%.500s/%.500s",base,wild);
	if (strncmp(bf,"//",2)==0) sprintf(bf,"%.500s%.500s",base,wild);

	return TRUE;
}
int tree_scan(Tree_Handle *tree,WILD_DATA **th, char *base, char *wild, char *p[], int upto, int np, int issub)
{
	char dir[BFSZ];
	char bf[BFSZ];
	static int lvl;
	int deeper= upto<(np-1);

/*imsg("tree_scan(%s) (%s) %d %d, deeper %d\n",base,wild,upto,np-1,deeper);*/
	tree_base(dir,base,wild);
	if (strchr(wild,'*')==NULL) {
		if (deeper) {
			tree_base(bf,base,wild);
			tree_scan(tree,th,bf,p[upto+1],p,upto+1,np,FALSE);
			return TRUE;
		}
	}
	tree_base(bf,base,wild);
//	if (lib_isdir(bf)) {
//		return tree_scan2(th,bf,"*",p,upto+1,np,issub);
//	} else {
		return tree_scan2(tree,th,base,wild,p,upto,np,issub);
//	}
}
int lib_isdir(char *fname)
{
#ifdef WIN32
    DWORD r;

    r = GetFileAttributesA((XLPCWSTR)fname);
    if (r == -1) return FALSE;
    /*imsg("GetFileAttributes r=[%x][%x]\n",r,FILE_ATTRIBUTE_DIRECTORY);*/
    if (r & FILE_ATTRIBUTE_DIRECTORY) return TRUE;
#else
	struct stat st;

	if (my_stat( fname,&st)!=0) return FALSE;
    /*imsg("lib_isdir [%s][%o]\n",fname,st.st_mode);*/
	if (S_ISDIR(st.st_mode)) return TRUE;
#endif
	return FALSE;
}


int tree_scan2(Tree_Handle *tree, WILD_DATA **th, char *xbase, char *wild, char *p[], int upto, int np, int issub)
{
	int nodirsinlist=FALSE;
	WILD_HANDLE *h;
	WILD_DATA mydata;
	WILD_DATA *d;
	char dir[BFSZ];
	char bf[BFSZ];
	char dontadd[BFSZ];
	int more;
	char base[BFSZ];
	static int lvl;
	int deeper= upto<(np-1);

	lvl++;
	ncpy(base,xbase,BFSZ-1);

	if (tree->access_check!=NULL) {
		if (strstr(wild,"*")!=NULL) {
			if (!tree->access_check(tree->p1,base)) {
/*				imsg("Blocking listing of (%s)\n",base);*/
				return TRUE;
			}
		}
	}
	tree_base(dir,base,wild);

/*
dir    base     wild	newbase		newwild
/a/b   /a	b	/a/b
/a/*   /a	*	/a		*

	*/

	if (strstr(wild,"*")==NULL) {
		if (lib_isdir(dir)) {
			/* imsg("modified base from (%.200s) to (%.200s) \n",base,dir);*/
			ncpy(base,dir,BFSZ-2);
		}
	}

/*	imsg("tree_scan %d (%.500s) (%.500s) %d/%d (%.500s)\n",lvl,base,wild,upto,np,dir);*/
	if (!tree->recursive) if (!deeper) if (lib_isdir(dir)) {
		tree_scan2(tree,th,dir,"*",p,upto+1,np,TRUE);
		lvl--;
		return TRUE;
	}
	ncpy(dontadd,dir,BFSZ-1);
/*imsg("tree_scan, wildfirst (%s)\n",dir);*/


	more = wild_first(&h, dir, &mydata);
	/*if (!more) imsg("wild failed (%.500s) %.500s\n",dir,strerror(errno));*/
	while(more){
/*imsg("%d %s\n",lvl,mydata.name);*/
		if (deeper && !tree->recursive) {
			if (strcmp(mydata.name,".")==0) goto nextone;
			if (strcmp(mydata.name,"..")==0) goto nextone;
			if (mydata.isdir) {
				tree_base(bf,base,mydata.name);
				tree_scan2(tree,th,bf,p[upto+1],p,upto+1,np,FALSE);
			}
		} else {
			if ( (strcmp(mydata.name,".")!=0) &&
				(strcmp(mydata.name,"..")!=0)) {
				if (mydata.isdir) if (upto<40) {
				 if (tree->recursive)  {
					tree_base(bf,base,mydata.name);
					tree_scan2(tree,th,bf,"*",p,upto+1,np,TRUE);
					if (nodirsinlist) goto nextone;
				 } else if ((upto==(np-1))) {
					tree_base(bf,base,mydata.name);
					tree_scan2(tree,th,bf,"*",p,upto+1,np,TRUE);
					if (nodirsinlist) goto nextone;
				 }
				}
			}
			tree_base(bf,base,mydata.name);
			if (strcmpnc(dontadd,bf)==0) {
				//printf("skipping (%s)\n",dontadd);
				//goto nextone;
			}
			d = calloc(1,sizeof(WILD_DATA));
/*imsg("Adding name to list (%s) (%s) (%s) %d %d\n",mydata.name,base,bf,upto,np);*/
			memcpy(d,&mydata,sizeof(WILD_DATA));
			ncpy(d->name,bf,BFSZ-1);
			d->issub = issub;
			d->next = *th;
			*th = d;


		}
nextone:	more = wild_next(h, &mydata);
	}
	wild_end(&h);
	lvl--;
	return TRUE;
}
int lib_del_isok(char *src)
{
	char *s;
	char tmp[BFSZ];
	ncpy(tmp,src,BFSZ-1);
	s = strstr(tmp,"/*"); if (s!=NULL) *s = 0;
	s = strstr(tmp,"\\*"); if (s!=NULL) *s = 0;
	if (lib_strlen(tmp) < 4) {
		if (vemsg) vemsg("do_wild_delete with dangerous input (%s)\n",src);
		lib_abort();
	}
	return TRUE;
}
/*

 dir /aa/*

 dir /aa/bob*

 dir /aa/bob_dir



  */

int tree_first(Tree_Handle **th, char *wild, int rec, int (*access_check)(void *x, char *s), void *p1, WILD_DATA *data)
{
	char xwild[BFSZ];
	char *p[199];
	char first[BFSZ];
	int i;
	int n;
	Tree_Handle *h;

	if (*th!=NULL) {
//		printf("Tree handle was not null\n");
		lib_abort();
	}
	h = *th = calloc(1,sizeof(Tree_Handle));
	ncpy(xwild,wild,BFSZ-1);
	h->access_check = access_check;
	h->p1 = p1;
	h->recursive = rec;

	n = token_split2(xwild,p,"/",99);
	if (lib_strlen(p[0])==0) {
		if (strcmp(wild,"/")==0) {
			n = 1;
			p[0] = "/";
		} else {
			sprintf(first,"/%.500s",p[1]);
			for (i=0; i<n; i++) {
				p[i] = p[i+1];
			}
			p[0] = first;
			n--;
		}
	}
/*	imsg("tree_scan(%.100s)\n",p[0]);*/

	tree_scan(*th,&h->wd, "",p[0],p,0,n,FALSE);

	tree_sort(&h->wd);

	h->cur=h->wd;
	if (h->cur==NULL) return FALSE;
	memcpy(data,h->cur,sizeof(WILD_DATA));
	return TRUE;
}
int tree_simple_do(Tree_Handle **tree, WILD_DATA **th,  char *wild);
int tree_simple(Tree_Handle **th, char *wild, void *p1, WILD_DATA *data)
{
	Tree_Handle *h;

	if (*th!=NULL) {
//		printf("Tree handle was not null\n");
		lib_abort();
	}
	h = *th = calloc(1,sizeof(Tree_Handle));
	h->access_check = NULL;
	h->p1 = p1;
	h->recursive = FALSE;

	tree_simple_do(th,&h->wd,wild);
	tree_sort(&h->wd);

	h->cur=h->wd;
	if (h->cur==NULL) return FALSE;
	memcpy(data,h->cur,sizeof(WILD_DATA));
	return TRUE;
}
int tree_simple_do(Tree_Handle **tree, WILD_DATA **th,  char *wild)
{
	WILD_HANDLE *h=NULL;
	WILD_DATA mydata;
	WILD_DATA *d;
	char bf[BFSZ];
	int more;
	char base[BFSZ];
	char xwild[BFSZ];
	char rest[BFSZ];
	ncpy(xwild,wild,BFSZ-1);
	wild_split(xwild,base,rest);

	more = wild_first(&h,wild,&mydata);
	while(more){
		if (strcmp(mydata.name,".")==0) goto nextone;
		if (strcmp(mydata.name,"..")==0) goto nextone;
		d = calloc(1,sizeof(WILD_DATA));
		tree_base(bf,base,mydata.name);
//printf("Found (%s) (%s) (%s)\n",base,mydata.name,bf);
		memcpy(d,&mydata,sizeof(WILD_DATA));
		ncpy(d->name,bf,BFSZ-1);
		d->issub = FALSE;
		d->next = *th;
		*th = d;
nextone:	more = wild_next(h, &mydata);
	}
	wild_end(&h);
	return TRUE;
}


int tree_cmp(const void *elem1, const void *elem2 )
{
	WILD_DATA *a = *(WILD_DATA **) elem1;
	WILD_DATA *b = *(WILD_DATA **) elem2;
	return -strcmp(a->name,b->name);
}
int wild_date_cmp(const void *elem1, const void *elem2 )
{
	WILD_DATA *a = *(WILD_DATA **) elem1;
	WILD_DATA *b = *(WILD_DATA **) elem2;
	return (int) (-a->time_create+b->time_create);
}
int tree_sort(WILD_DATA **dlist)
{
	/* Sort using a qsort */
	int n=0;
	int i;
	WILD_DATA *d;
	WILD_DATA **ary;
	for (d=*dlist;d!=NULL;d=d->next) n++;
	if (n<2) return TRUE;
	ary = calloc(n,sizeof(WILD_DATA));
	for (i=0,d=*dlist;d!=NULL;d=d->next) {
		ary[i++] = d;
	}
	if (n>1) qsort(ary,n,sizeof(WILD_DATA *),tree_cmp);
	*dlist = NULL;
	for (i=0; i<n; i++) {
		d = ary[i];
		d->next = *dlist;
		*dlist = d;
	}
	free(ary);
	return TRUE;
}
int wild_date_sort(WILD_DATA **dlist)
{
	/* Sort using a qsort */
	int n=0;
	int i;
	WILD_DATA *d;
	WILD_DATA **ary;
	for (d=*dlist;d!=NULL;d=d->next) n++;
	if (n<2) return TRUE;
	ary = calloc(n,sizeof(WILD_DATA));
	for (i=0,d=*dlist;d!=NULL;d=d->next) {
		ary[i++] = d;
	}
	if (n>1) qsort(ary,n,sizeof(WILD_DATA *),wild_date_cmp);
	*dlist = NULL;
	for (i=0; i<n; i++) {
		d = ary[i];
		d->next = *dlist;
		*dlist = d;
	}
	free(ary);
	return TRUE;
}
int tree_next(Tree_Handle *h, WILD_DATA *data)
{
	h->cur = h->cur->next;
	if (h->cur==NULL) return FALSE;
	memcpy(data,h->cur,sizeof(WILD_DATA));
	return TRUE;
}
int tree_reset(Tree_Handle *h, WILD_DATA *data)
{
	h->cur = h->wd;
	if (h->cur==NULL) return FALSE;
	memcpy(data,h->cur,sizeof(WILD_DATA));
	return TRUE;
}
int tree_end(Tree_Handle **hh)
{
	Tree_Handle *h = *hh;
	WILD_DATA *d,*dnext;

	if (*hh==NULL) return TRUE;
	for (d=h->wd;d!=NULL;d=dnext) {
		dnext = d->next;
		free(d);
	}
	free(*hh);
	*hh = NULL;
	return TRUE;
}
int tree_tot_subdir(WILD_DATA *h, char *path)
{
	double tot=0;
	char xpath[BFSZ];
	char xname[BFSZ];
	for (;h!=NULL;h=h->next) {
		if (h->issub) if (h->isnorm) {
			lib_path_split(h->name,xpath,xname);
			if (lib_strlen(xpath)>0) if (xpath[lib_strlen(xpath)-1]=='/') xpath[lib_strlen(xpath)-1] = 0;
			if (strcmp(path,xpath)==0) {
				tot += h->size;
			}
		}
	}
	return (int) tot/1000;
}
#ifdef wild_first
#undef wild_first
#endif


int wild_first_safe(WILD_HANDLE **hh, char *wild, WILD_DATA *data, char *src, int line)
{
	// scan entire list and store in structure...
	int more;
	int nfound=0;
	WILD_HANDLE *h;
	WILD_DATA *d,mydata;
	more = wild_first_r(hh,wild,&mydata,src,line);
	h = *hh;
	while(more){
		d = calloc(1,sizeof(WILD_DATA));
		*d = mydata;
		d->next = h->list;
		h->list = d;
		nfound++;
		more = wild_next_r(h, &mydata,src,line);
	}

	if (h) h->cur = h->list;
	return wild_next_safe(h,data,src,line);
}
int wild_next_safe(WILD_HANDLE *h, WILD_DATA *data, char *src, int line)
{
	if (h==NULL) return FALSE;
	if (h->cur!=NULL) {
		*data = *h->cur;
		h->cur = h->cur->next;
		return TRUE;
	}
	return FALSE;

}
int wild_end_safe(WILD_HANDLE **hh)
{
	WILD_HANDLE *h=NULL;
	// If list of files in handle then free them...
	WILD_DATA *w,*next;
	if (hh!=NULL) h = *hh;
	if (h!=NULL) {
		for (w=h->list;w!=NULL;w=next) {
			next = w->next;
			free(w);
		}
		h->list = NULL;
	}
	wild_end_r(hh);
	return TRUE;
}






#define MAXTIMEIT 30000
typedef struct  WTIMEIT {char *src; int msec; int count; } Wtimeit;
static Wtimeit wtimeit[MAXTIMEIT];
int wild_first_time(WILD_HANDLE **hh, char *xwild, WILD_DATA *data, char *src, int line)
{
	int r;
	int diff;
	int msec=lib_msec_now();
	r = wild_first_r(hh,xwild,data,src,line);
	diff = lib_msec_now()-msec;
	if (diff<0) diff = 0;
	if (line>=0 && line<MAXTIMEIT) {
		wtimeit[line].src = src;
		wtimeit[line].msec += diff;
		wtimeit[line].count++;
	}

	return r;
}
int wild_dump(void)
{
	FILE *f;
	int i;
	f = fopen("wild.stats","w");
	if (f==NULL) return FALSE;
	for (i=0; i<MAXTIMEIT; i++) {
		if (wtimeit[i].count>0) fprintf(f,"%8d %8d %s:%d\n",wtimeit[i].msec,wtimeit[i].count,wtimeit[i].src,i);
	}
	fclose(f);
	return TRUE;
}
#ifdef win32
#define stat _stat
#endif
int lib_stat_x(char *fname, int *sz, time_t *time_write, int *isdir, char *src, int line)
{
   struct stat buf;

   wild_stats_add(src,line);
   if (stat(fname,&buf)!=0) {
		return FALSE;
   }
   *sz = buf.st_size;
   *time_write = buf.st_ctime;
   *isdir = S_ISDIR(buf.st_mode);
   return TRUE;
}

int wild_skip(WILD_DATA *data)
{
	if (data->name[0]==0) return true;
	if (strcmp(data->name,"..")==0) return true;
	if (strcmp(data->name,".")==0) return true;
	return false;
}

#ifdef WIN32
#include <io.h>

int wild_first_r(WILD_HANDLE **hh, char *xwild, WILD_DATA *data, char *src, int line)
{
	WILD_HANDLE *h;
	struct _finddata_t c_file;
	char wild[BFSZ];
	ncpy(wild,xwild,200);
	if (lib_strlen(wild)==2) if (wild[1]==':') strcat(wild,"\\*");
	if (lib_isdir(wild)) {
		ncat(wild,"\\*",BFSZ-1);
	}
	h = *hh  = malloc(sizeof(WILD_HANDLE));
	memset(h,0,sizeof(WILD_HANDLE));
	memset(data,0,sizeof(WILD_DATA));
	/*imsg("wild_first (%.400s)\n",wild);*/
	h->h = fd_findfirst(wild,&c_file,src,line);
	if (h->h== -1L) {
/*		if (lib_exists(wild)) {

		} else {

		}*/
		free(h);
		*hh = NULL;
		return FALSE;
	}
	wild_count(1);
	data->isdir = (c_file.attrib & _A_SUBDIR);
	data->isnorm = TRUE;
	if (data->isdir) data->isnorm = FALSE;
	if (c_file.attrib & _A_HIDDEN) data->isnorm = FALSE;
	ncpy(data->name,c_file.name,LIB_BFSZ-1);
	data->time_create = c_file.time_create;
	data->time_access = c_file.time_access;
	data->time_write = c_file.time_write;
	data->size = c_file.size;

	return TRUE;
}

int wild_next_r(WILD_HANDLE *h, WILD_DATA *data, char *src, int line)
{
	struct _finddata_t c_file;
#ifdef USE_DFOPEN
	int r;
	r = fd_findnext(h->h, &c_file, src, line);
#else
	int r  = _findnext(h->h,&c_file);
#endif

/*
#ifdef __BORLANDC__
	r = !r;
#endif
*/
	if (!h->fast) wild_stats_add(src,line);
	if (r!=0) return FALSE;
	memset(data,0,sizeof(WILD_DATA));
	data->isdir = (c_file.attrib & _A_SUBDIR);
	data->isnorm = TRUE;
	if (data->isdir) data->isnorm = FALSE;
	if (c_file.attrib & _A_HIDDEN) data->isnorm = FALSE;
	ncpy(data->name,c_file.name,LIB_BFSZ-1);
	data->time_create = c_file.time_create;
	data->time_access = c_file.time_access;
	data->time_write = c_file.time_write;
	data->size = c_file.size;

	data->gid = 0;
	data->uid = 0;
	data->mode = 0;
	data->islink = FALSE;
	data->nlinks = 1;


	strcpy(data->symlink,"");
/*
	if (h->fast) { // FOR DEBUGGING ONLY we can emulate the behaviour on unix.
		data->size = 0;
		data->time_write = 0;
		data->isnorm = TRUE; // Lets hope it is.
		if (strcmp(data->name,".")==0) data->isnorm =FALSE;
		if (strcmp(data->name,"..")==0) data->isnorm =FALSE;
		return TRUE;
	}
	*/
	return TRUE;
}
int wild_end_r(WILD_HANDLE **hh)
{
	WILD_HANDLE *h = *hh;
	if (h!=NULL) wild_count(-1);
	if (h!=NULL) _findclose(h->h);
	if (h!=NULL) free(h);
	*hh = NULL;
	return TRUE;
}
#else
/*
Examples
	/a/b/c*.*	/a/b  c*.*
	/a/b/c/*.*	/a/b/c *.*
	/a/b/c		/a/b/c *.*  If (!isdir()) then return 'c'
*/
int wild_first_r(WILD_HANDLE **hh, char *wild, WILD_DATA *data, char *src, int line)
{
	int ret=FALSE;
	WILD_HANDLE *h;
	char w[LIB_BFSZ];
	char path[LIB_BFSZ];
	*hh = h = malloc(sizeof(WILD_HANDLE));
	memset(h,0,sizeof(WILD_HANDLE));
	memset(data,0,sizeof(WILD_DATA));


	wild_split(wild,path,w);
	/* emsg("wild_first (%s) (%s) (%s) \n",wild,path,w);*/

//if (vimsg) vimsg("wild_first (%s) (%s) (%s) \n",wild,path,w);
	h->h = opendir(path);
	if (h->h== NULL) {
		/* emsg("opendir failed (%s) (%s)\n",path,strerror(errno)); */
		free(h);
		*hh = NULL;
		return FALSE;
	}
	h->path = strdup(path);
	h->wild = strdup(w);
	ret = wild_next_r(h,data,src,line);
	wild_count(1);
	if (!ret) {
		wild_end_r(hh);
		return FALSE;
	}
	return ret;
}
int wild_next_r(WILD_HANDLE *h, WILD_DATA *data, char *src, int line)
{
	struct dirent *dval,aval;
	char tmp[BFSZ];
	char dresult[3000 + sizeof(struct dirent)];
	struct stat st;
	int r;
	memset(data,0,sizeof(WILD_DATA));
	if (!h->fast) wild_stats_add(src,line);
again:
	safe_readdir(h->h,dresult,&dval);
	if (dval==NULL) {
		/* emsg("readdir failed no more files"); */
		return FALSE;
	}

	/* Now do a wild match on the file name, then the path */
	if (!match_wild(h->wild,dval->d_name)) goto again;

	/* Now stat the file so we can fill in the details */
	sprintf(tmp,"%.400s/%.400s",h->path,dval->d_name);
	sprintf(data->name,"%.400s",dval->d_name);
	if (h->fast) {
		data->size = 0;
		data->time_write = 0;
		data->isnorm = TRUE; // Lets hope it is.
		if (strcmp(dval->d_name,".")==0) data->isnorm =FALSE;
		if (strcmp(dval->d_name,"..")==0) data->isnorm =FALSE;
		return TRUE;
	}
	if (lstat(tmp,&st)!=0) {
		/* emsg("Could not stat (%.500s) (%.500s)\n",data->name,strerror(errno)); */
		goto again;
	}

	data->gid = st.st_gid;
	data->uid = st.st_uid;
	data->mode = st.st_mode;
	data->nlinks = st.st_nlink;
	data->dev = st.st_dev;
	data->inode = st.st_ino;
	data->islink = S_ISLNK(st.st_mode);
	if (data->islink) {
		strcpy(data->symlink,"readlink_failed");
		r = readlink(tmp,data->symlink,LIB_BFSZ-1);
		if (r>=0) data->symlink[r] = 0;
	}

	data->isdir = S_ISDIR(st.st_mode);
	data->isnorm = S_ISREG(st.st_mode);
	if (data->isdir) data->isnorm = FALSE;
	data->time_create = st.st_ctime;
	data->time_access = st.st_atime;
	data->time_write = st.st_mtime;
	data->size = st.st_size;
	return TRUE;
}
int wild_end_r(WILD_HANDLE **hh)
{
	WILD_HANDLE *h = *hh;
	if (h==NULL) return TRUE;
	if (h!=NULL) {
		wild_count(-1);
		closedir(h->h); h->h = 0;
		if (h->path) free(h->path); h->path = NULL;
		if (h->wild) free(h->wild); h->wild = NULL;
		free(h);
	}
	*hh = NULL;
	return TRUE;
}

#endif
int wild_split(char *in, char *path, char *wild)
{
	char bf[BFSZ];
	char *s;
	char *last=NULL;
	ncpy(bf,in,BFSZ-1); in = bf;

	if (lib_strlen(bf)>1) if (bf[lib_strlen(bf)-1]=='/') bf[lib_strlen(bf)-1] = 0;
	if (lib_strlen(bf)>1) if (bf[lib_strlen(bf)-1]=='\\') bf[lib_strlen(bf)-1] = 0;
	for (s=in; *s!=0; s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) {
#ifdef WIN32
		strcpy(path,"");
#else
		strcpy(path,".");
#endif
		ncpy(wild,bf,BFSZ-1);
		return TRUE;
	}
	/*
	Added back in, but without wild card, this fixes directory listing of
	/home
	*/
	if (lib_is_dir(bf)) {
		ncpy(path,bf,BFSZ-1);
		ncpy(wild,"*",BFSZ-1);
		return TRUE;
	}
	*last = 0;
	ncpy(path,bf,BFSZ-1);
	ncpy(wild,last+1,BFSZ-1);
	return TRUE;
}
static char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static char *months_full[]={"January","February","March","April","May","June","July","August","September","October","November","December"};
static char *sdays[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static char *xdays[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

char *lib_month_name(int idx_between_0_and_11)
{
	if (idx_between_0_and_11 < 0 || idx_between_0_and_11 > 11) return NULL;
	return months[idx_between_0_and_11];
}
char *lib_month_name_full(int idx_between_0_and_11)
{
	if (idx_between_0_and_11 < 0 || idx_between_0_and_11 > 11) return NULL;
	return months_full[idx_between_0_and_11];
}
char *lib_weekday_name(int idx_between_0_Sunday_and_6_Saturday)
{
	if (idx_between_0_Sunday_and_6_Saturday < 0 || idx_between_0_Sunday_and_6_Saturday > 6) return NULL;
	return xdays[idx_between_0_Sunday_and_6_Saturday];
}
char *lib_hour_name(int hour_between_0_and_24, char *bf)
{
	if (hour_between_0_and_24<0 || hour_between_0_and_24>24) return NULL;
	if (hour_between_0_and_24==0) return "12 am";
	if (hour_between_0_and_24==12) return "12 pm";
	if (hour_between_0_and_24==24) return "12 am";
	if (hour_between_0_and_24<12) {
		sprintf(bf,"%d am",hour_between_0_and_24);
		return bf;
	}
	sprintf(bf,"%d pm",hour_between_0_and_24-12);
	return bf;
}

char *lib_24hour_name(int hour_between_0_and_24, char *bf)
{
	if (hour_between_0_and_24<0 || hour_between_0_and_24>24) return NULL;
	sprintf(bf,"%02d:00",hour_between_0_and_24);
	return bf;
}

char *lib_not_null(char *txt)
{
	if (txt) return txt;
	return "";
}

char *lib_minute_name(int minute_between_0_and_59, char *bf)
{
	if (minute_between_0_and_59<0 || minute_between_0_and_59>59) return NULL;
	if (minute_between_0_and_59==0) return ":00";
	if (minute_between_0_and_59<10) {
		sprintf(bf,":0%d",minute_between_0_and_59);
		return bf;
	}
	sprintf(bf,":%d",minute_between_0_and_59);
	return bf;
}
char *strprint(char *line, char *tmp)
{
	char *s;
	char *out=tmp;
	for (s=line;*s!=0;s++) {
		if (*s=='\r') {
			*out++ = 'C';
			*out++ = 'R';
		} else if (*s=='\n') {
			*out++ = 'L';
			*out++ = 'F';
		} else if (*s<32) {
			*out++ = '\\';
			*out++ = *s + ' ';
		} else *out++ = *s;
		if (out-tmp>900) break;
	}
	*out++ = 0;
	return tmp;
}
char *interval_english[14] = {
		"second","seconds",
		"minute","minutes",
		"hour","hours",
		"day","days",
		"week","weeks",
		"month","months",
		"year","years"
};

char *lib_time_interval(int seconds, char *bf, int display_exact)
{
	return lib_time_interval_ex(seconds,bf,display_exact,interval_english);
}

char *lib_time_interval_ex(int seconds, char *bf, int display_exact, char *strings[]) {
	int minutes,hours,days,weeks,months,years;

	if (seconds<=0) {
		//return "0 minutes";
		sprintf(bf,"0 %s",strings[I_MINS]);
		return bf;
	}

	if (seconds%60!=0 && (display_exact || seconds<300)) {
		/*if (seconds==1) return "1 second";
		sprintf(bf,"%d seconds",seconds);*/
		if (seconds==1) sprintf(bf,"1 %s",strings[I_SEC]);
		else sprintf(bf,"%d %s",seconds,strings[I_SECS]);
		return bf;
	}
	minutes=(seconds+29)/60;

	if (minutes%60!=0 && (display_exact || minutes<300)) {
		/*if (minutes==1) return "1 minute";
		sprintf(bf,"%d minutes",minutes);*/
		if (minutes==1) sprintf(bf,"1 %s",strings[I_MIN]);
		else sprintf(bf,"%d %s",minutes,strings[I_MINS]);
		return bf;
	}
	hours=(minutes+29)/60;
	if (hours%24!=0 && (display_exact || hours<96)) {
		/*if (hours==1) return "1 hour";
		sprintf(bf,"%d hours",hours);*/
		if (hours==1) sprintf(bf,"1 %s",strings[I_HR]);
		else sprintf(bf,"%d %s",hours,strings[I_HRS]);
		return bf;
	}
	days=(hours+11)/24;
	if (days%7!=0 && (display_exact || days<50)) {
		/*if (days==1) return "1 day";
		sprintf(bf,"%d days",days);*/
		if (days==1) sprintf(bf,"1 %s",strings[I_DAY]);
		else sprintf(bf,"%d %s",days,strings[I_DAYS]);
		return bf;
	}
	weeks=(days+3)/7;
	if (weeks<8 || display_exact) {
		/*if (weeks==1) return "1 week";
		sprintf(bf,"%d weeks",weeks);*/
		if (weeks==1) sprintf(bf,"1 %s",strings[I_WK]);
		else sprintf(bf,"%d %s",weeks,strings[I_WKS]);
		return bf;
	}
	months=(days+14)/30;
	if (months%12!=0 && (display_exact || months<36)) {
		/*if (months==1) return "1 month";
		sprintf(bf,"%d months",months);*/
		if (months==1) sprintf(bf,"1 %s",strings[I_MON]);
		else sprintf(bf,"%d %s",months,strings[I_MONS]);
		return bf;
	}
	years=(days+180)/365;
	/*if (years==1) return "1 year";
	sprintf(bf,"%d years",years);*/
	if (years==1) sprintf(bf,"1 %s",strings[I_YR]);
	else sprintf(bf,"%d %s",years,strings[I_YRS]);
	return bf;
}

char *lib_file_date(char *bf,time_t t)
{
	struct tm *x;
	struct tm atm;
	time_t now=time(NULL);
	int thisyear;
	x = safe_localtime(&now,&atm);
	thisyear = x->tm_year;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	if (x->tm_year==thisyear) {
		sprintf(bf,"%.500s %2d %2d:%02d",months[x->tm_mon],x->tm_mday,x->tm_hour,x->tm_min);
	} else {
		sprintf(bf,"%.500s %2d  %4d",months[x->tm_mon],x->tm_mday,x->tm_year+1900);
	}
	return bf;
}
char *lib_hourmin(char *tmp)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	sprintf(tmp,"%02d:%02d",xt->tm_hour,xt->tm_min);
	return tmp;
}

int lib_hour(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return xt->tm_hour;
}
int lib_minutes(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return xt->tm_min;
}
int lib_mday(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return xt->tm_mday;
}
int lib_yday(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return xt->tm_yday;
}
static char *weekdays[]={
		"Sunday"
		,"Monday"
		,"Tuesday"
		,"Wednesday"
		,"Thursday"
		,"Friday"
		,"Saturday"
};
char *lib_dayofweek(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return weekdays[xt->tm_wday];
}
int lib_month(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return xt->tm_mon;
}
int lib_year(void)
{
	time_t x;
	struct tm atm;
	struct tm *xt;
	x = time(NULL);
	xt = safe_localtime(&x,&atm);
	return xt->tm_year+1900;
}
int lib_year_at(time_t x)
{
	struct tm atm;
	struct tm *xt;
	xt = safe_localtime(&x,&atm);
	return xt->tm_year+1900;
}
#ifndef INT_MAX
#define INT_MAX 2147483647
#endif

int int_time(time_t *x)
{
	time_t r = time(x);
	if (r>INT_MAX) r -= INT_MAX; // so it cycles again from zero nicely...
	return (int) r;
}
char *lib_time(char *bf,time_t t)
{
	struct tm atm;
	struct tm *x;
	time_t now=time(NULL);
	int thisyear;
	x = safe_localtime(&now,&atm);
	thisyear = x->tm_year;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	sprintf(bf,"%02d:%02d",x->tm_hour,x->tm_min);
	return bf;
}
/*
		{
			int r;
			TIME_ZONE_INFORMATION tzi;

			r = GetTimeZoneInformation(&tzi);
			if (r==TIME_ZONE_ID_DAYLIGHT) 	mlink_printf(l," Daylight saving in operations\r\n");
			mlink_printf(l," Windows Timezone (%dmin, or %d hours daylight (%s) standard (%s) day=%d std=%d\r\n"
			,tzi.Bias
			,tzi.Bias/60
			,tzi.DaylightName
			,tzi.StandardName
			,tzi.DaylightBias/60
			,tzi.StandardBias/60);
		}


*/

static int my_timezone;
static int force_timezone;
int lib_set_timezone(double x) // IN HOURS...
{
	force_timezone = (int) (-x*60*60);
	lib_timezone_fix();
	return TRUE;
}
int lib_timezone_fix(void)
{
		if (force_timezone!=0) {
			my_timezone = force_timezone;
			return TRUE;
		}
#ifdef WIN32
		{
			int r;
			int tz;
			static int done;
			TIME_ZONE_INFORMATION tzi;

			if (!done) my_timezone = _timezone;
			r = GetTimeZoneInformation(&tzi);
			tz = tzi.Bias*60;
			if (r==TIME_ZONE_ID_DAYLIGHT) {
				tz += tzi.DaylightBias*60;
			} else {
			}
			//if (!done) if (tz!=my_timezone) if (vemsg) vemsg("Correcting timezone %d -> %d\n",my_timezone/60/60,tz/60/60);

			//if (vemsg) if (tz!=my_timezone) vemsg("TIMEZONE:  timezone %d -> %d\n",my_timezone/60/60,tz/60/60);
		//	printf("TIMEZONE:  timezone %d -> %d\n",my_timezone/60/60,tz/60/60);

			my_timezone = tz;
			done = TRUE;
		}
#endif
		return TRUE;
}
int lib_timezone_raw(void);
static int fake_isdst;
int lib_set_isdst(void)
{
	fake_isdst = TRUE;
	return TRUE;
}

int lib_timezone(void)
{
	int tz = lib_timezone_raw();
	struct tm atm,*x;
	time_t t = time(NULL);
	x = safe_localtime(&t,&atm);
	if (x==NULL) return tz;
#ifndef IGNORE_ISDST
#ifndef WIN32
	if (x->tm_isdst>0) tz -= 60*60;
	if (fake_isdst) tz -= 60*60;
#endif
#endif
	return tz;
}
int lib_timezone_win(void)
{
	int tz = lib_timezone_raw();
	struct tm atm,*x;
	time_t t = time(NULL);
	x = safe_localtime(&t,&atm);
	if (x==NULL) return tz;
#ifndef WIN32
	if (x->tm_isdst>0) tz -= 60*60;
#endif
	return tz;
}
int lib_timezone_nodaylight(void)
{
	int tz = lib_timezone_raw();
	struct tm atm,*x;
	time_t t = time(NULL);
	x = safe_localtime(&t,&atm);
	if (x==NULL) return tz;
	return tz;
}
int lib_timezone_raw(void)
{
	static int done;
	if (!done) lib_timezone_fix();
	done = TRUE;
#ifdef WIN32
	return my_timezone;
//	return _timezone;
#else
#ifdef TZ_INTM // macosx and freebsd*
	{
		time_t t = time(NULL);
		struct tm atm;
		struct tm *tm;
		tm = safe_localtime(&t,&atm);
		return -tm->tm_gmtoff;
	}
#else // linux variants.
	return timezone;
#endif
#endif
}
char *lib_timezone_string(char *zone)  // 4.5  --> 4:30
{
	float offset;
	char *min="00";
	offset  = (float) (-lib_timezone());
	offset /= (60*60.0);
	if (offset!=(int)offset) min = "30";
	if (offset<0) {
		sprintf(zone,"-%02d%s",(int) -offset,min);
	} else {
		sprintf(zone,"+%02d%s",(int) offset,min);
	}
	return zone;
}

static char mtzone[BFSZ];
char *lib_mail_timezone(char *s)
{
	if (s) ncpy(mtzone,s,100);
	return mtzone;
}
char *lib_mail_timezone_get(struct tm *x, char *zone, int bfsz)
{
	if (mtzone[0]) ncpy(zone,mtzone,bfsz-1);
	else {
		lib_timezone_string(zone);
	}
	return zone;
}
int lib_tz_set(void)
{
#ifdef WIN32
	_tzset();
#else
	tzset();
#endif
	return TRUE;
}
char *lib_mail_time(char *tmp, time_t t)
{
	/* Thu, 21 May 1998 05:33:22 -0700*/
	struct tm atm;
	struct tm *x;
	char zone[BFSZ];

	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	lib_mail_timezone_get(x,zone,BFSZ);
	sprintf(tmp,"%s, %02d %s %d %02d:%02d:%02d %s",
		sdays[x->tm_wday],x->tm_mday,months[x->tm_mon]
		,x->tm_year+1900,x->tm_hour,x->tm_min,x->tm_sec,zone);
	return tmp;
}
char *lib_imap_time(char *tmp, time_t t)
{
	/* Thu, 21 May 1998 05:33:22 -0700*/
	struct tm atm;
	struct tm *x;
	char tmp2[BFSZ];
	char zone[BFSZ];
	char *s;

	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	lib_mail_timezone_get(x,zone,BFSZ);
	s = strchr(zone,'(');
	if (s!=NULL) *s = 0;
	ncpy(zone,lib_trim_spaces(zone,tmp2),BFSZ-1);

	sprintf(tmp,"%02d-%s-%d %02d:%02d:%02d %s",
		x->tm_mday,months[x->tm_mon]
		,x->tm_year+1900,x->tm_hour,x->tm_min,x->tm_sec,zone);
	return tmp;
}

char *lib_nice_display_format_datetime(char *tmp, time_t t)
{
	/* Thursday, 21 Janurary 1998 at 5:33 am*/
	struct tm atm;
	struct tm *x;
	int hour;
	char *ampm;
	*tmp=0;
	if (t==0) return tmp;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;
	ampm="am";
	if (hour==0) {hour=12;}
	else if (hour==12) {ampm="pm";}
	else if (hour>12) {ampm="pm";hour-=12;}

	sprintf(tmp,"%s, %02d %s %d at %02d:%02d %s",
		xdays[x->tm_wday],x->tm_mday,months_full[x->tm_mon]
		,x->tm_year+1900,hour,x->tm_min,ampm);
	return tmp;
}

char *lib_nice_display_format_datetime2(char *tmp, time_t t, int day_first)
{
	/* 21/1/1998 5:33 am */
	struct tm atm;
	struct tm *x;
	int hour;
	char *ampm;
	*tmp=0;
	if (t==0) return tmp;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;
	ampm="am";
	if (hour==0) {hour=12;}
	else if (hour==12) {ampm="pm";}
	else if (hour>12) {ampm="pm";hour-=12;}

	if (day_first) {
		sprintf(tmp,"%d/%d/%d %02d:%02d %s"
			,x->tm_mday,x->tm_mon+1
			,x->tm_year+1900,hour,x->tm_min,ampm);
	}
	else {
		sprintf(tmp,"%d/%d/%d %02d:%02d %s"
			,x->tm_mon+1,x->tm_mday
			,x->tm_year+1900,hour,x->tm_min,ampm);
	}
	return tmp;
}

char *lib_nice_daterange_compact(char *tmp, time_t t, time_t t2)
{
	/*
	eg:
	Thu 21 Jan 5am - 5:33am, 2017
	Thu 21 Jan - Fri 22 Jan, 2017
	*/
	struct tm atm, atm2;
	struct tm *x, *x2;
	int hour, hour2;
	char *ampm, *ampm2;
	char time1[BFSZ], time2[BFSZ];
	*tmp=0;
	if (t==0 || t2==0) return tmp;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;
	ampm="am";
	if (hour==0) {hour=12;}
	else if (hour==12) {ampm="pm";}
	else if (hour>12) {ampm="pm";hour-=12;}

	x2 = safe_localtime(&t2,&atm2);
	if (x2==NULL) return "Bad Time    ";
	hour2=x2->tm_hour;
	ampm2="am";
	if (hour2==0) {hour2=12;}
	else if (hour2==12) {ampm2="pm";}
	else if (hour2>12) {ampm2="pm";hour2-=12;}

	if(x->tm_min==0){
		lprintf(time1,BFSZ-1,"%d%s",hour,ampm);
	}else{
		lprintf(time1,BFSZ-1,"%d:%02d%s",hour,x->tm_min,ampm);
	}
	if(x2->tm_min==0){
		lprintf(time2,BFSZ-1,"%d%s",hour2,ampm2);
	}else{
		lprintf(time2,BFSZ-1,"%d:%02d%s",hour2,x2->tm_min,ampm2);
	}

	if(x->tm_mday==x2->tm_mday && x->tm_mon==x2->tm_mon){
		sprintf(tmp,"%s %d %s, %d %s - %s",
			sdays[x->tm_wday],x->tm_mday,months[x->tm_mon],x->tm_year+1900,time1,time2);
	}else if(x->tm_year==x2->tm_year){
		sprintf(tmp,"%s %d %s - %s %d %s, %d",
			sdays[x->tm_wday],x->tm_mday,months[x->tm_mon],
			sdays[x2->tm_wday],x2->tm_mday,months[x2->tm_mon],
			x->tm_year+1900);
	}else{
		sprintf(tmp,"%s %d %s, %d - %s %d %s, %d",
			sdays[x->tm_wday],x->tm_mday,months[x->tm_mon],x->tm_year+1900,
			sdays[x2->tm_wday],x2->tm_mday,months[x2->tm_mon],x2->tm_year+1900);
	
	}
	return tmp;
}


char *lib_nice_display_format_datetime_gmt(char *tmp, time_t t)
{
	/* Thursday, 21 January 1998 at 5:33 am*/
	struct tm atm;
	struct tm *x;
	int hour;
	char *ampm;
	*tmp=0;
	if (t==0) return tmp;
	x = safe_gmtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;
	ampm="am";
	if (hour==0) {hour=12;}
	else if (hour==12) {ampm="pm";}
	else if (hour>12) {ampm="pm";hour-=12;}

	sprintf(tmp,"%s, %02d %s %d at %02d:%02d %s",
		xdays[x->tm_wday],x->tm_mday,months_full[x->tm_mon]
		,x->tm_year+1900,hour,x->tm_min,ampm);
	return tmp;
}

char *lib_nice_display_format_date(char *tmp, time_t t)
{
	/* Thursday, 21 Janurary 1998 */
	struct tm atm;
	struct tm *x;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";

	sprintf(tmp,"%s, %02d %s %d",
		xdays[x->tm_wday],x->tm_mday,months_full[x->tm_mon]
		,x->tm_year+1900);
	return tmp;
}
char *lib_date_simple(char *tmp, time_t t)
{
	/* Thursday, 21 Janurary 1998 */
	struct tm atm;
	struct tm *x;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";

	sprintf(tmp,"%02d %s %d",
		x->tm_mday,months[x->tm_mon]
		,x->tm_year+1900);
	return tmp;
}
char *lib_date_simple3(char *tmp, time_t t)
{
	/* 11-Mar-2020 */
	struct tm atm;
	struct tm *x;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";

	sprintf(tmp,"%02d-%s-%d",
		x->tm_mday,months[x->tm_mon]
		,x->tm_year+1900);
	return tmp;
}
char *lib_date_short(char *tmp, time_t t)
{
	/* Thursday, 21 Janurary 1998 */
	struct tm atm;
	struct tm *x;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";

	sprintf(tmp,"%02d%02d%02d%02d",x->tm_year-100,x->tm_mon+1,x->tm_mday,x->tm_hour);
	return tmp;
}
char *lib_date_path(char *tmp, time_t t)
{
	/* Thursday, 21 Janurary 1998 */
	struct tm atm;
	struct tm *x;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";

	sprintf(tmp,"%02d%02d%02d",x->tm_year-100,x->tm_mon+1,x->tm_mday);
	return tmp;
}


char *lib_nice_display_format_time(char *tmp, time_t t)
{
	/* 5:33 am*/
	struct tm atm;
	struct tm *x;
	int hour;
	char *ampm;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;
	ampm="am";
	if (hour==0) {hour=12;}
	else if (hour==12) {ampm="pm";}
	else if (hour>12) {ampm="pm";hour-=12;}

	sprintf(tmp,"%02d:%02d %s",
		hour,x->tm_min,ampm);
	return tmp;
}

char *lib_short_time(char *tmp, time_t t)
{
	/* 5:33 am*/
	struct tm atm;
	struct tm *x;
	int hour;
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;

	sprintf(tmp,"%02d %02d:%02d",
		x->tm_mday,hour,x->tm_min);
	return tmp;
}

char *lib_from_time(char *tmp)
{
	/* Thu, 21 May 1998 05:33:22 -0700*/
	struct tm atm;
	struct tm *x;
	time_t t= time(NULL);
	x = safe_localtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	sprintf(tmp,"%s %s %02d %02d:%02d:%02d %d",
		sdays[x->tm_wday],months[x->tm_mon],x->tm_mday
		,x->tm_hour,x->tm_min,x->tm_sec,x->tm_year+1900);
	return tmp;
}

char *lib_nice_display_format_gmtime(char *tmp, time_t t)
{
	/* 5:33 am*/
	struct tm atm;
	struct tm *x;
	int hour;
	char *ampm;
	x = safe_gmtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";
	hour=x->tm_hour;
	ampm="am";
	if (hour==0) {hour=12;}
	else if (hour==12) {ampm="pm";}
	else if (hour>12) {ampm="pm";hour-=12;}

	sprintf(tmp,"%d:%02d %s",
		hour,x->tm_min,ampm);
	return tmp;
}
char *lib_nice_display_format_gmdate(char *tmp, time_t t)
{
	/* Thursday, 21 Janurary 1998 */
	struct tm atm;
	struct tm *x;
	x = safe_gmtime(&t,&atm);
	if (x==NULL) return "Bad Time    ";

	sprintf(tmp,"%s, %d %s %d",
		xdays[x->tm_wday],x->tm_mday,months_full[x->tm_mon]
		,x->tm_year+1900);
	return tmp;
}
char *lib_notify_date(time_t t_evt, char *bf, int offset)
{
	time_t t_now, t_tom;
	struct tm tm_now, tm_evt, tm_tom;
	char tmp[BFSZ];
	
	if (t_evt<0) t_evt=0;
	t_evt += offset;
	t_now = time(NULL) + offset;
	t_tom = time(NULL) + 24*60*60 + offset;

	safe_gmtime(&t_evt,&tm_evt);
	safe_gmtime(&t_now,&tm_now);
	safe_gmtime(&t_tom,&tm_tom);

	if(tm_evt.tm_year==tm_now.tm_year && tm_evt.tm_mon==tm_now.tm_mon && tm_evt.tm_mday==tm_now.tm_mday){
		lib_nice_display_format_gmtime(bf,t_evt);	// 3:15 pm
	}else{
		lib_nice_display_format_gmdate(tmp,t_evt);
		if(tm_evt.tm_year==tm_tom.tm_year && tm_evt.tm_mon==tm_tom.tm_mon && tm_evt.tm_mday==tm_tom.tm_mday){
			lprintf(bf,BFSZ,"Tomorrow %s", tmp);	// Tomorrow Thursday, 26 September 2019
		}else{
			ncpy(bf,tmp,BFSZ-1);					// Thursday, 26 September 2019
		}
	}
	return bf;	
}

/*
#ifdef WIN32
#define NOTZALLOC 1
#endif
#ifdef NOTZALLOC
typedef struct TIMEZONET {} timezone_t

#endif

char *lib_time_in_zone(time_t now, char *tzname, char *result)
{
	struct tm *tm;
	struct tm mytm;
	char bf[BFSZ];
	timezone_t tz = tzalloc(tzname);

	long tzgetgmtoff(timezone_t tz, int isdst);
	tm = localtime_rz(tz,now,&mytm);
	char *name = tzgetname(tz,tm->tm_isdst);	
	lprintf(result,BFSZ-1,"%s %s %02d %02d:%02d %s",
		sdays[x->tm_wday],months[x->tm_mon],x->tm_mday
		,x->tm_hour,x->tm_min,name);
	tzfree(tz);
	return result;
}
*/




/* typedef struct BITMAP {char *mask; int sz;} Bitmap;*/
Bitmap *bit_new_real(int sz, char *src, int line)
{
#ifndef USE_DMALLOC
	Bitmap *b = calloc(sizeof(Bitmap),1);
	b->mask = calloc(1,(sz/8)+10);
#else
	Bitmap *b = d_callocid(DMID,sizeof(Bitmap),1,src,line);
	b->mask = d_callocid(DMID,1,(sz/8)+10,src,line);
#endif
	b->sz = sz;
	return b;
}
int bit_info(Bitmap *b, int *x, int *y)
{
	int ns=0;
	int i;
	*x = *y = 0;
	if (b==NULL) return TRUE;
	*y = b->sz;
	for (i=0; i<b->sz; i++) {
		if (bit_isset(b,i)) ns++;
	}
	*x = ns;
	return TRUE;
}
int bit_info_r(Bitmap *b, int *x, int *y)
{
	int ns=0;
	int i;
	*x = *y = 0;
	if (b==NULL) return TRUE;
	*y = b->sz;
	for (i=0; i<b->sz; i++) {
		if (bit_isset_r(b,i)) ns++;
	}
	*x = ns;
	return TRUE;
}
int bit_info_r2(Bitmap *b, int *x, int *y, int *z)
{
	int ns=0;
	int i;
	*z = 0;
	*x = *y = 0;
	if (b==NULL) return TRUE;
	*y = b->sz;
	for (i=0; i<b->sz; i++) {
		if (bit_isset_r(b,i)) ns++;
		else *z = i;
	}
	*x = ns;
	return TRUE;
}
void bit_clear(Bitmap *b)
{
	memset(b->mask,0,b->sz/8+1);
}
int bit_copy(Bitmap *b, char *data, int bytes)
{
	int i;
	if (bytes>((b->sz+7)/8)) bytes = ((b->sz+7)/8);
	if (vemsg) vemsg("bit_copy: bytes = %d, bitmasksize (%d) \n",bytes,((b->sz+7)/8));
	for (i=0; i<bytes; i++) {
		b->mask[i] = data[i];
//		vimsg("bit_copy [%d] %02x\n",i,(int) data[i]);
	}
	return TRUE;
}
int bit_set_r(Bitmap *b, int x, int v)
{
//	unsigned char *m = (unsigned char *) b->mask;
	int r = x % 8;
	r = 7 - r; // reverse order of bits in bit field.
	if (b==NULL) return FALSE;
	if (x<0) return FALSE;
	if (x>=b->sz) return FALSE;
	x = x / 8;
	if (v) b->mask[x] |= (1 << r);
	else b->mask[x] &= (0xff - (1 << r));
	return TRUE;
}
int lib_symlink(char *fname, char *symlink)
{
	FILE *f;
	char *s;
	char bf[BFSZ];
	ncpy(symlink,fname,BFSZ-1);
	f = fopen(symlink,"r");
	if (f==NULL) return FALSE;
	if (fgets(bf,BFSZ-1,f)==NULL) {
		fclose(f);
		return FALSE;
	}
	fclose(f);
	if (strncmpnc(bf,"#symlink:",9)==0) {
		ncpy(symlink,bf+9,BFSZ-1);
	} else return FALSE; // no symlink...
	s = strchr(symlink,'\n'); if (s!=NULL) *s = 0;
	s = strchr(symlink,'\r'); if (s!=NULL) *s = 0;
	return TRUE;
}


int bit_set(Bitmap *b, int x, int v)
{
//	unsigned char *m = (unsigned char *) b->mask;
	int r = x % 8;
	if (b==NULL) return FALSE;
	if (x<0) return FALSE;
	if (x>=b->sz) return FALSE;
	x = x / 8;
	if (v) b->mask[x] |= (1 << r);
	else b->mask[x] &= (0xff - (1 << r));
	return TRUE;
}
int bit_isset_r(Bitmap *b, int x)
{
//	unsigned char *m;
	int r = x % 8;
	r = 7 - r; // reverse order of bits in bit field.
	if (b==NULL) return FALSE;
	if (x<0) return FALSE;
	if (x>=b->sz) return FALSE;
//	m = (unsigned char *) b->mask;
	x = x / 8;
	return ((b->mask[x] & (1 << r))!=0);
}
int bit_isset(Bitmap *b, int x)
{
//	unsigned char *m;
	int r = x % 8;
	if (b==NULL) return FALSE;
	if (x<0) return FALSE;
	if (x>=b->sz) return FALSE;
//	m = (unsigned char *) b->mask;
	x = x / 8;
	return ((b->mask[x] & (1 << r))!=0);
}
int bit_free(Bitmap *b)
{
	if (b==NULL) return TRUE;
	free(b->mask);
	b->mask = NULL;
	free(b);
	return TRUE;
}
int lib_elapsed(time_t was)
{
	return (int) (time(NULL)-was);
}
int  lib_file_age(char *fname)
{
	time_t t = lib_mtime(fname);
	return (int) (time(NULL)-t);
}
void lib_touch(char *fname)
{
	FILE *f;
	f = fopen(fname,"w");
	if (f!=NULL) fclose(f);
}
time_t lib_mtime(char *fname)
{
	struct stat st;
	if (my_stat(fname,&st)!=0) return 0;

	return st.st_mtime;
}
time_t lib_ctime(char *fname)
{
	struct stat st;
	if (my_stat(fname,&st)!=0) return 0;

	return st.st_ctime;
}

void lib_set_mtime(char *fname, time_t t)
{
	struct utimbuf xt;
	xt.actime=t;
	xt.modtime=t;
	utime(fname,&xt);
}


int lib_stat_isdir(char *fname)
{
	struct stat st;
	if (my_stat(fname,&st)!=0) return -1; /* -1 on error*/
#ifdef WIN32
	return ((st.st_mode & _S_IFDIR) != 0)?1:0;  /* 1 on IS_DIR, 0 on NOT DIR */
#else
	return ((st.st_mode &  S_IFDIR) != 0)?1:0;  /* 1 on IS_DIR, 0 on NOT DIR */
#endif
}
time_t lib_mtime_open(FILE *f)
{
	struct stat st;
//printf("fstat2 mtime\n");
	if (fstat(fileno(f),&st)!=0) return 0;

	return st.st_mtime;
}
/* date_from="26/Jun/2000" */
int lib_month_find(char *s)
{
	int i;
	if (sisdigit(*s)) return atoi(s)-1;
	for (i=0; i<12; i++) {
		if (strncmpnc(months[i],s,3)==0) return i;
	}
	return 0;
}
int lib_gmt_offset_at_broken(time_t t)
/*
returns local offset in seconds from GMT at given time.
*/
{
	time_t t1=t,t2=t;
	time_t r1,r2;
	struct tm x1,x2;
	struct tm *tm1,*tm2;
	if ((int) t==-1) return 0;
	tm1 = safe_gmtime(&t1,&x1);
	if (tm1==NULL) return 0;
	if ((intptr_t) tm1==-1) return 0;
	r1=mktime(tm1);
	tm2 = safe_localtime(&t2,&x2);
	if (tm2==NULL) return 0;
	if ((intptr_t) tm2==-1) return 0;
	r2=mktime(tm2);
	return (int) (r2-r1);
}
int show_tm(char *thing, struct tm *t)
{
	printf("%s: %d/%d/%d  %d:%d %s\n",thing,t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_isdst ? "DAYLIGHT" : "Normal");
	return TRUE;
}
int lib_gmt_offset_at(time_t t)
{
	time_t t1=t,t2=t;
	time_t r1,r2;
	struct tm x1,x2;
	struct tm *tm1,*tm2;
	if ((int) t==-1) return 0;
	tm1 = safe_gmtime(&t1,&x1);
	if (tm1==NULL) return 0;
	if ((intptr_t) tm1==-1) return 0;
	//show_tm("gmtime",tm1);
	//tm1->tm_isdst = -1;
	r1=mktime(tm1);
	tm2 = safe_localtime(&t2,&x2);
	if (tm2==NULL) return 0;
	if ((intptr_t) tm2==-1) return 0;
	//show_tm("localtime",tm2);
	tm2->tm_isdst = -1;
	r2=mktime(tm2);
	//printf("offset_at2: Isdaylight = %d, difference is %d %d\n",tm2->tm_isdst,r2-r1,(r2-r1)/(60*60));
	return (int) (r2-r1);
}
int lib_gmt_offset_now(void)
{
	time_t t1=time(NULL); //,t2=time(NULL);
	time_t r1;
	struct tm x1;
	struct tm *tm1;
	tm1 = safe_gmtime(&t1,&x1);
	if (tm1==NULL) return 0;
	if ((intptr_t) tm1==-1) return 0;
	//show_tm("gmtime",tm1);
	r1=mktime(tm1);
//	printf("gmtime=%d, time=%d\n",r1,time(NULL));
	return (int) (time(NULL)-r1);
}

time_t web_date_gmt(char *dte)
{
	/*
	Mon, 13 May 2002 16:09:02 <timezone>
	this function handles any timezone and returns a GMT value

	Marijn: WARNING!!! This function only seems to works if <timezone> matches the timezone running the code??
	*/
	time_t t;

	if (lib_strlen(dte)<29) return 0;

	t = web_date_get(dte);
	t += lib_timezone(); //returns value to shift from local to GMT
	t += ((atoi(&dte[26])/100)*3600); //apply given timezone

	return t;
}
time_t lib_date_xml(char *dte)
{
	/*
	2006-01-01T12:00:00.000Z
	*/
	struct tm x;
	if (lib_strlen(dte)<19) return 0;
	memset(&x,0,sizeof(struct tm));
	x.tm_sec = atoi(dte+17);
	x.tm_min = atoi(dte+14);
	x.tm_hour = atoi(dte+11);
	x.tm_mday = atoi(dte+8);
	x.tm_mon = lib_month_find(dte+5);
	x.tm_year = atoi(dte) - 1900;
	x.tm_isdst = -1;
	return mktime(&x)-lib_timezone();
}


time_t web_date_get_correct(char *dte)
{
	/*
	Mon, 13 May 2002 16:09:02 GMT
	this function expects GMT and returns a GMT value
	*/
	time_t ret;
	//vimsg("web_date_get_correct(%s)\n",dte);
	if (lib_strlen(dte)<29) return 0;
	if (!sisdigit(dte[23])) return 0;
	if (!sisdigit(dte[20])) return 0;
	if (!sisdigit(dte[17])) return 0;
	if (!sisdigit(dte[5])) return 0;
	if (!sisdigit(dte[12])) return 0;
	if (dte[26]!='G' || dte[27]!='M' || dte[28]!='T') return 0;
	ret=web_date_get(dte);
	//vimsg("date_correct (%d)\n",ret);
	return ret+lib_gmt_offset_at(ret);
}


time_t web_date_get(char *dte)
{
	/*
	Mon, 13 May 2002 16:09:02 <timezone>
	WARNING - this function ignores the timezone part and mktime returns a local time value!
	*/
	struct tm x;
	memset(&x,0,sizeof(struct tm));
	x.tm_sec = atoi(dte+23);
	x.tm_min = atoi(dte+20);
	x.tm_hour = atoi(dte+17);
	x.tm_mday = atoi(dte+5);
	x.tm_mon = lib_month_find(dte+8);
	x.tm_year = atoi(dte+12) - 1900;
	x.tm_isdst = -1;
	return mktime(&x);
}

// email date format is:   [day, ]dayofmonth MONTHNAME year

time_t lib_date_get(char *xdte)
{
	char dte[500];
	struct tm x;
	char *s;
	int y;
	char *s2;
	char day[200];
	char mon[200];
	char year[200];
	memset(&x,0,sizeof(struct tm));
	ncpy(dte,xdte,100);
	s = strchr(dte,'/');
	if (s==NULL) return 0;
	*s = 0;
	ncpy(day,dte,20);
	s++;
	s2 = strchr(s,'/');
	if (s2==NULL) return 0;
	*s2 = 0;
	ncpy(mon,s,100);
	ncpy(year,s2+1,100);
	y = atoi(year);
	if (y<50) y = y + 100; // 08 = 108
	if (y>200) y -= 1900;
	x.tm_mon = lib_month_find(mon);
	if (atoi(mon)>0) x.tm_mon = atoi(mon)-1;
	x.tm_year = y;
	x.tm_mday = atoi(dte);
	return mktime(&x);
}
time_t lib_date_dashed(char *xdte)
{
	char dte[500];
	struct tm x;
	char *s;
	int y;
	char *s2;
	char day[200];
	char mon[200];
	char year[200];
	memset(&x,0,sizeof(struct tm));
	ncpy(dte,xdte,100);
	s = strchr(dte,'-');
	if (s==NULL) return 0;
	*s = 0;
	ncpy(day,dte,20);
	s++;
	s2 = strchr(s,'-');
	if (s2==NULL) return 0;
	*s2 = 0;
	ncpy(mon,s,100);
	ncpy(year,s2+1,100);
	y = atoi(year);
	if (y<50) y += 100;
	if (y>200) y -= 1900;
	x.tm_mon = lib_month_find(mon);
	x.tm_year = y;
	x.tm_mday = atoi(dte);
//	printf("mktime month=%d year=%d day=%d\n",x.tm_mon,x.tm_year,x.tm_mday);
	return mktime(&x);
}
char *lib_ftp_time(time_t t)
{
	static char bf[LIB_BFSZ];
	struct tm atm;
	struct tm *x;
	x = safe_gmtime(&t,&atm);
	sprintf(bf,"%d%02d%02d%02d%02d%02d"
		,x->tm_year+1900
		,x->tm_mon+1
		,x->tm_mday
		,x->tm_hour
		,x->tm_min
		,x->tm_sec);
	return bf;
}

char *lib_web_date_at(char *bf, time_t t)
{
	/*char xx[BFSZ];*/
	/*char tmpstr[BFSZ];*/
	struct tm atm;
	/*char hour[BFSZ];*/
	struct tm *tt;
	tt = safe_gmtime(&t,&atm);
	/*strcpy(xx,safe_ctime(&t,tmpstr));
	ncpy(hour,xx+11,8);*/
	sprintf(bf,"%.3s, %02d %.500s %d %02d:%02d:%02d GMT",xdays[tt->tm_wday],tt->tm_mday,months[tt->tm_mon],tt->tm_year+1900,tt->tm_hour,tt->tm_min,tt->tm_sec);
	return bf;
	/* Date: Saturday, 27-Sep-97 21:28:07 GMT*/
}

char *lib_webdav_date_at(char *bf, time_t t)
{
	/*char xx[BFSZ];*/
	/*char tmpstr[BFSZ];*/
	struct tm atm;
	/*char hour[BFSZ];*/
	struct tm *tt;
	tt = safe_gmtime(&t,&atm);
	/*strcpy(xx,safe_ctime(&t,tmpstr));
	ncpy(hour,xx+11,8);*/
	sprintf(bf,"%04d-%02d-%02dT%02d:%02d:%02d-00:00"
		,tt->tm_year+1900
		,tt->tm_mon+1
		,tt->tm_mday
		,tt->tm_hour
		,tt->tm_min
		,tt->tm_sec);
	return bf;
	/* Date: Saturday, 27-Sep-97 21:28:07 GMT*/
}
//1997-12-01T18:27:21-08:00

char *lib_web_date(char *bf)
{
	return lib_web_date_at(bf,time(NULL));
}

static int vms_timezone;
#ifdef GMTVARS
static time_t tts;
static struct tm *ttm;
#endif
void lib_tzinit(void)
{
#ifdef USEGMTOFF
	struct tm atm;
#else
#ifdef HAS_DST
	struct tm atm;
#endif
#endif
        static int done_init=FALSE;
        if (!done_init) {
                done_init = TRUE;
        } else return;
#ifdef USEGMTOFF
               tts = time(NULL);
               ttm = safe_localtime(&tts,&atm);
               vms_timezone = (int) (ttm->tm_gmtoff);
               vms_timezone = -vms_timezone;
#else
#ifdef HAS_DST
		{
			time_t timer;
			struct tm *t;
			static int offset = 0;
			static int done;
			if (!done) {
				done = TRUE;
				timer = time(NULL);
				t = safe_localtime(&timer,&atm);
				if (t->tm_isdst) offset = 60*60;
			}
			vms_timezone -= offset;
		}
#else
#endif

#endif
}
time_t lib_gmt_to_local(time_t x)
{
        lib_tzinit();
        return x-vms_timezone;
}
time_t lib_local_to_gmt(time_t x)
{
        lib_tzinit();
        return x+vms_timezone;
}
char *lib_web_date2(time_t t, char *bf)
{
	/*char xx[BFSZ];*/
	/*char tmpstr[BFSZ];*/
	/*char hour[BFSZ];*/
	struct tm atm;
	struct tm *tt;
	tt = safe_gmtime(&t,&atm);
	/*
	strcpy(xx,safe_ctime(&t,tmpstr));
	ncpy(hour,xx+11,8);*/
	sprintf(bf,"%.3s, %02d %.500s %d %02d:%02d:%02d GMT",xdays[tt->tm_wday],tt->tm_mday,months[tt->tm_mon],tt->tm_year+1900,tt->tm_hour,tt->tm_min,tt->tm_sec);
	return bf;
	/* We generate   Date: Saturday, 27-Sep-1997 21:28:07 GMT*/
	/* Apache makes  Date: Mon, 15 Apr 2002 03:46:09 GMT */
	/* Changed to match apache */
}
char *lib_log_date(time_t t, char *bf)
{
	struct tm atm;
	struct tm *tt;
	tt = safe_gmtime(&t,&atm);
	sprintf(bf,"%d/%s/%d:%02d:%02d:%02d GMT",tt->tm_mday,months[tt->tm_mon],tt->tm_year+1900,tt->tm_hour,tt->tm_min,tt->tm_sec);
	return bf;
}
char *lib_short_date(time_t t, char *bf)
{
	struct tm atm;
	struct tm *tt;
	tt = safe_gmtime(&t,&atm);
	sprintf(bf,"%d/%s/%d %02d:%02d:%02d",tt->tm_mday,months[tt->tm_mon],tt->tm_year-100,tt->tm_hour,tt->tm_min,tt->tm_sec);
	return bf;
}
char *lib_short_date_local(time_t t, char *bf)
{
	struct tm atm;
	struct tm *tt;
	tt = safe_localtime(&t,&atm);
	sprintf(bf,"%d/%s/%d %02d:%02d:%02d",tt->tm_mday,months[tt->tm_mon],tt->tm_year-100,tt->tm_hour,tt->tm_min,tt->tm_sec);
	return bf;
}
char *lib_shorter_date(time_t t, char *bf)
{
	struct tm atm;
	struct tm *tt;
//printf("Time convert %d\n",t);
	tt = safe_localtime(&t,&atm);
	sprintf(bf,"%d/%s/%d",tt->tm_mday,months[tt->tm_mon],1900+tt->tm_year);
	return bf;
}
char *lib_dash_date(time_t t, char *bf)
{
	struct tm atm;
	struct tm *tt;
	tt = safe_localtime(&t,&atm);
	sprintf(bf,"%d-%02d-%02d",1900+tt->tm_year,tt->tm_mon+1,tt->tm_mday);
	return bf;
}


// Do some simplification of date formatting
// MW: I don't much like the actual structure of the function yet but it will do for now ;-)
char *lib_date_basic(time_t t_msg, char *bf, int offset, char *date_format, char *time_format)
{
	time_t t_now;
	struct tm tm_now,tm_msg;

	if (t_msg<0) t_msg=0;

	t_now=time(NULL);

	if (time_format==NULL)	time_format="%#I:%M %p"; 
	if (date_format==NULL)	date_format="%d/%m/%Y";

	t_msg+=offset;
	t_now+=offset;

	safe_gmtime(&t_msg,&tm_msg);
	safe_gmtime(&t_now,&tm_now);

	if(tm_msg.tm_year==tm_now.tm_year){
		if (tm_msg.tm_mon==tm_now.tm_mon && tm_msg.tm_mday==tm_now.tm_mday){
			// Display as "time today"
			strftime(bf,BFSZ,time_format,&tm_msg);
		}else{
			// display as "day of mon"
			if(lib_starts_with(date_format,"%m/%d"))
				lprintf(bf,BFSZ,"%s %d",months[tm_msg.tm_mon],tm_msg.tm_mday);
			else
				lprintf(bf,BFSZ,"%d %s",tm_msg.tm_mday,months[tm_msg.tm_mon]);
		}
	}else{
		//Display as "day mon year" ie. d/m/y or m/d/y
		strftime(bf,BFSZ,date_format,&tm_msg);
	}
	return bf;
}

/* This make ssolaris use select instead of nanosleep, it might avoid a lockup, and it
might be more efficient, but I am not sure. It's a shot in the dark.
*/
#ifdef XXISSOLARIS
#define SLEEP_SELECT 1
#endif

#ifdef XXSLEEP_SELECT
int tcp_msleep(int msec)
{
	static fd_set readfd,writefd,exceptfd;
	struct timeval timeout;
	int nc;
	timeout.tv_sec = msec/1000; /*Wake up once a second*/
	timeout.tv_usec = (msec%1000) * 1000 ;
	nc = select(1,&readfd,&writefd,&exceptfd,&timeout);
	return TRUE;
}

int lib_usleep(int x)
{
	return tcp_msleep(x/1000);
}
#else
#ifndef WIN32
void lib_usleep(int x)
{
#ifndef NONANO
	struct timespec rqtp;
	rqtp.tv_sec = x/1000000;
	rqtp.tv_nsec = (x%1000000)*1000;
	nanosleep(&rqtp,NULL);
#else
	usleep(x);
#endif
}
#endif
#endif

//#define SLEEPHASH 100
//static char *sleep_file[SLEEPHASH];
//static int sleep_line[SLEEPHASH];
//static int sleep_hits[SLEEPHASH];
void lib_msleep(int x) // , char *file, int line
{

#ifdef WIN32
	Sleep(x);
#else
	lib_usleep(x*1000);
#endif
}

char *nstrcat(char *existing_text, char *text_to_append, int existing_text_bfsz)
{
	char *ret=existing_text;
	int len=lib_strlen(existing_text);
	existing_text+=len;
	existing_text_bfsz-=len;
	if (existing_text_bfsz>0) {
		ncpy(existing_text,text_to_append,existing_text_bfsz-1);
	}
	return ret;
}

char *mystrtok(char *start, char *toks, char **upto)
{
	char *s=start;
	if (start!=NULL && upto) *upto = NULL;
	if (s==NULL && upto) s = *upto;
	if (s==NULL) return NULL;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)==NULL) break;
	}
	if (*s==0) return NULL;
	start = s;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)!=NULL) break;
	}
	if (*s==0) {
		if (upto) *upto = NULL;
		return start;
	}
	*s = 0;
	if (upto) *upto = s+1;
	return start;
}
int str_vappend(char **s, char *add)
{
	int n = lib_strlen(*s);
	*s = realloc(*s,n+lib_strlen(add)+1);
	strcpy(*s + n,add);
	return TRUE;
}
char *lib_trim_crlf(char *bf)
{
	int len;
	len = lib_strlen(bf);
	for (;len>1;len--) {
		if (bf[len-1]=='\r') {bf[len-1] = 0; continue;}
		if (bf[len-1]=='\n') {bf[len-1] = 0; continue;}
		break;
	}
	return bf;
}
char *lib_trim_eol(char *bf)
{
	char *s;
	int len;
	s = strstr(bf,"\r\n");
	if (s!=NULL) *s = 0;
	len = lib_strlen(bf);
	if (len>0) if (bf[len-1]==' ') bf[len-1] = 0;
	return bf;
}

char *lib_etrim(char *email, char *tmp)
{
	char *s;
	if (email==NULL) {
		strcpy(tmp,"");
		return tmp;
	}
	if (email[0]!='<') {
		ncpy(tmp,email,BFSZ-1);
		return tmp;
	}
	ncpy(tmp,email+1,BFSZ-1);
	s = strchr(tmp,'>');
	if (s!=NULL) *s = 0;
	return tmp;
}
char *lib_etrimlow(char *email, char *tmp)
{
	char *s;
	if (email==NULL) {
		strcpy(tmp,"");
		return tmp;
	}
	if (email[0]!='<') {
		ncpy(tmp,email,BFSZ-1);
		return tmp;
	}
	ncpy(tmp,email+1,BFSZ-1);
	s = strchr(tmp,'>');
	if (s!=NULL) *s = 0;
	s = strchr(tmp,'@');
	if (s!=NULL) strlwr(s);
	return tmp;
}
char *lib_trimmore(char *email, char *tmp)
{
	char *upto=NULL;
	char *s;
	ncpy(tmp,email,BFSZ-1);
	s = mystrtok(tmp,"<> \t\r\n",&upto);
	if (s!=NULL) return s;
	return email;
}


char *lib_trim_qspaces(char *s, char *tmp)
{
	int n;
	for (;*s!=0;s++) {
		if (*s==' ') continue;
		if (*s=='\t') continue;
		if (*s=='"') continue;
		break;
	}
	ncpy(tmp,s,BFSZ-10);
	for (n=lib_strlen(tmp)-1;n>0;n--) {
		if (tmp[n] == ' ') continue;
		if (tmp[n] == '\t') continue;
		if (tmp[n] == '"') continue;
		break;
	}
	tmp[n+1] = 0;
	return tmp;
}
#define BIGBF 36000
char *file_strdup(char *s)
{
	// If it looks like a file, read file, append lines (with comma's) and strdup result.
	char bf[BFSZ];
	FILE *f;
	if (lib_strlen(s)>1) if (s[1]==':') goto dofile;
	if (s[0]=='/') goto dofile;
	if (s[0]=='\\') goto dofile;
	return strdup(s);
dofile:
	{
		f = fopen(s,"rb");
		if (f==NULL) {
			if (vimsg) vimsg("Unable to open (%s) %s\n",s,strerror(errno));
			return strdup(s);
		}
		{
			char *out = calloc(1,BIGBF);
			char *ret;
			strcpy(out,"");
			for (;!feof(f);) {
				if (fgets(bf,BFSZ-1,f)==NULL) break;
				if (lib_strlen(out)>0) ncat(out,",",BIGBF-1);
				ncat(out,bf,BIGBF-1);
			}
			fclose(f);
			ret = strdup(out);
			free(out);
			return ret;
		}
	}
}
char *lib_crlf_to_spaces(char *tmp)
{
	char *s;
	for (s=tmp;*s!=0;s++) {
		if (*s=='\r') *s = ' ';
		if (*s=='\n') *s = ' ';
		if (*s=='\t') *s = ' ';
	}
	return tmp;
}
char *lib_trim_spaces(char *s, char *tmp)
{
	int n;
	for (;*s!=0;s++) {
		if (*s==' ') continue;
		if (*s=='\t') continue;
		break;
	}
	ncpy(tmp,s,BFSZ-10);
	for (n=lib_strlen(tmp)-1;n>0;n--) {
		if (tmp[n] == ' ') continue;
		if (tmp[n] == '\t') continue;
		break;
	}
	tmp[n+1] = 0;
	return tmp;
}
char *lib_nospaces(char *s, char *tmp)
{
	char *out=tmp;
	for (;*s!=0;s++) {
		if (*s==' ') continue;
		if (*s=='\t') continue;
		*out++ = *s;
		if (out-tmp>BFSZ-2) break;
	}
	*out = 0;
	return tmp;
}
// like lib_trim_spaces, but not BFSZ limited
char *lib_trim_spaces2(char *s, char *tmp)
{
	int n;
	for (;*s!=0;s++) {
		if (*s==' ') continue;
		if (*s=='\t') continue;
		break;
	}
	strcpy(tmp,s);
	for (n=lib_strlen(tmp)-1;n>0;n--) {
		if (tmp[n] == ' ') continue;
		if (tmp[n] == '\t') continue;
		break;
	}
	tmp[n+1] = 0;
	return tmp;
}
int lib_trim_spaces_end(char *s)
{
	int n;
	for (n=lib_strlen(s)-1;n>=0;n--) {
		if (s[n] == ' ') continue;
		if (s[n] == '\t') continue;
		break;
	}
	s[n+1] = 0;
	return n+1;
}
int lib_str_hash(char *s, int max) //cannot be changed as is used for odd things...
{
	unsigned int total=0;
	int r;
	int i;
	if (vemsg!=NULL) if (s==NULL) vemsg("lib_str_hash called with NULL parameter\n");
	for (i=0; *s!=0; ) {
		total += *s++;
		if (*s==0) break;
		total += *s++ << 8;
		if (*s==0) break;
		total += *s++ << 16;
		if (*s==0) break;
		total += *s++ << 24;
		if (*s==0) break;
	}
    r = total % max;
    if (r<0) return -r;
    return r;
}
int lib_int_hash(int key, int max)
{
	key += (key << 12);
	key ^= (key >> 22);
	key += (key << 4);
	key ^= (key >> 9);
	key += (key << 10);
	key ^= (key >> 2);
	key += (key << 7);
	key ^= (key >> 12);
	key=key % max;
	if (key<0) key=-key;
	return key;
}

/* MUST NOT BE CHANGED, USED BY ENCRYPT/DECRYPT FUNCTIONS */
int lib_str_hash_old(char *s, int max)
{
        int total=0;
        int r;
        int i;
        for (i=0;*s!=0;s++,i++) {
		total = total << 3;
                total += *s;
        }
        r = total % max;
        if (r<0) return -r;
        return r;
}
int token_split(char *s, char *p[], char *sep, int tmax)
{
        int n=0;

        for (;*s!=0;) {
                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
                p[n++] = s;
                s += strcspn(s,sep);  /* Find end of word */
                if (*s==0) break;
                *s = 0; s++;
                /*if ((n+2)>=tmax) {*/
				if ((n+1)==tmax) {
                        break;
                }
        }
        p[n] = NULL;
        return n;
}
// "name here" <email@domain>
// name here <email@domain>
// name here email@domain
// full group as email
// name her [email@domain]

int lib_split_email_name(char *name_and_email, char *email_bf, char *name_bf, int bfsz)
{
	char bf[BFSZ], *s, *p[30];
	int i;
	int np;
	strcpy(email_bf,""); strcpy(name_bf,"");
	ncpy(bf,name_and_email,BFSZ-1);
	np = token_split(bf,p," \"<>[]",20);
	if (np<=0) return FALSE;
	for (i=0; i<np-1;i++) {
		if (i>0) ncat(name_bf," ",bfsz-1);
		ncat(name_bf,p[i],bfsz-1);
	}
	ncpy(email_bf,p[np-1],bfsz-1);
	s=strchr(email_bf,'?');
	if(s) *s=0;
	if(strchr(email_bf,'@')==NULL && strchr(name_and_email,'<')==NULL){
		strcpy(name_bf,"");
		ncpy(email_bf,name_and_email,BFSZ-1);
	}
	return TRUE;
}
int lib_simple_wash(char *s)
{
	for (;*s!=0;s++) {
		if (*s=='<') *s = 0;
		if (*s=='>') *s = 0;
	}
	return TRUE;
}
int lib_wash_file(char *given, char *out)
{
	char ext[BFSZ];
	char *last=NULL;
	char *s;
	static int v;
	ncpy(ext,given,BFSZ-1);
	for (s=given;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last!=NULL) ncpy(ext,last+1,BFSZ-1);
	for (s=ext;*s!=0;s++) {
		if (*s==' ') *s = '_';
		if (*s=='.') continue;
		if (*s=='_') continue;
		if (*s=='-') continue;
		if (!sisalnum(*s)) *s = '_';
	}
	ncpy(out,ext,BFSZ-1);
	return TRUE;
}


int lib_split_email(char *email, char *user, char *dom)
{
	char *s;
	ncpy(user,email,200);
	ncpy(dom,"",10);
	if (user[0]=='<') ncpy(user,email+1,200);
	s = strchr(user,'>');
	if (s!=NULL) *s = 0;
	s = strchr(user,'@');
	if (s==NULL) return FALSE;
	*s = 0;
	ncpy(dom,s+1,200);
	strlwr(dom);
	return TRUE;
}
int lib_same_domain(char *email1, char *email2)
{
	char user1[BFSZ];
	char dom1[BFSZ];
	char user2[BFSZ];
	char dom2[BFSZ];

	if (!lib_split_email(email1,user1,dom1)) ncpy(dom1,email1,BFSZ-1);
	if (!lib_split_email(email2,user2,dom2)) ncpy(dom2,email2,BFSZ-1);
	if (strcmpnc(dom1,dom2)==0) return TRUE;
	return FALSE;
}
int lib_is_domain(char *email1, char *dom2)
{
	char user1[BFSZ];
	char dom1[BFSZ];
	if (!lib_split_email(email1,user1,dom1)) ncpy(dom1,email1,BFSZ-1);
	if (strcmpnc(dom1,dom2)==0) return TRUE;
	return FALSE;
}
int imap_token_split(char *s, char *p[], char *sep, int tmax)
{
        int n=0;

        for (;*s!=0;) {
				int inbrk=FALSE;
                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
                p[n++] = s;
				for (;*s!=0;s++) {
					if (!inbrk) if (*s==' ') break;
					if (*s=='[') inbrk = TRUE;
					if (*s==']') inbrk = FALSE;
				}
                if (*s==0) break;
                *s = 0; s++;
                if ((n+2)>=tmax) {
					break;
                }
        }
        p[n] = NULL;
        return n;
}
int lib_str_set(char **a, char *s)
{
	if (*a) free(*a);
	*a = strdup(s);
	return TRUE;
}
int quoted_token_split(char *s, char *p[], char *sep, int tmax)
{
        int n=0;

        for (;*s!=0;) {
                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
                p[n++] = s;
				//if (*s == '\"'){s++; while(*s!='\"' && *s != '\0') {s++;} if (*s!='\"') s++;}
				if (*s == '\"') { for(s++; *s && *s != '\"'; s++); if (*s == '\"') s++; }
                s += strcspn(s,sep);  /* Find end of word */
                if (*s==0) break;
                *s = 0; s++;
                if ((n+2)>=tmax) {
					break;
                }
        }
        p[n] = NULL;
        return n;
}
char *lib_unquote(char *s, char *tmp)
{
	ncpy(tmp,s,BFSZ-1);
	if (*s!='"') return tmp;
	ncpy(tmp,s+1,BFSZ-1);
	s = strchr(tmp,'"');
	if (s!=NULL) *s = 0;
	return tmp;
}
char *lib_unsquote(char *s, char *tmp)
{
	ncpy(tmp,s,BFSZ-1);
	if (*s!='\'') return tmp;
	ncpy(tmp,s+1,BFSZ-1);
	s = strchr(tmp,'\'');
	if (s!=NULL) *s = 0;
	return tmp;
}
char *lib_slash_encode(char *str, char *tmp)
{
	char *start = tmp;
	char *s;
	for (s=str;*s!=0;s++) {
		if (*s=='"') *tmp++ = '\\';
		if (*s=='\\') *tmp++ = '\\';
		*tmp++ = *s;
		if (tmp-start>=BFSZ-2) break;
	}
	*tmp++ = 0;
	return start;
}
int token_quoted_good(char *s, char *p[], char *end, char *sep, int tmax)
{
		// This one handles  abc,"def",ghi "jkl" mno,
        int n=0;

        for (;*s!=0;) {
				for (;*s!=0;s++) {	 // step over the seperator.
					if (*s==':') break;
					if (*s==';') break;
					if (strchr(sep,*s)==NULL) break;
				}
//                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
				end[n] = 0;
                p[n++] = s;

				for (;*s!=0;s++) {
	//printf("Search for seperator %s\n",s);
					if (strchr(sep,*s)!=NULL) break;
					if (*s == '\"') { for(s++; *s && *s != '\"'; s++); if (*s == '\"') s++; }
					if (*s == '<') { for(s++; *s && *s != '>'; s++); if (*s == '>') s++; }
					if (*s==0) break;
					if (strchr(sep,*s)!=NULL) break;
				}
				end[n-1] = *s; // save the end of token char.

                if (*s==0) break;
                *s = 0; s++;
                if ((n+2)>=tmax) {
					break;
                }
        }
        p[n] = NULL;
        return n;
}
// like token_quoted_good, but turns {from:"Anita Morgan" flag:star folders:recent}
//		 into {from:"Anita Morgan"} {flag:star} {folders:recent}
// instead of {from:"Anita Morgan" flag:star} {folders:recent}
int token_quoted_better(char *s, char *p[], char *sep, int tmax)
{
        int n=0;
        for (;*s!=0;) {
                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
                p[n++] = s;
				for (;*s!=0;s++) {
					if (strchr(sep,*s)!=NULL) break;
					if (*s == '\"') { for(s++; *s && *s != '\"'; s++); if (*s == '\"') s++; }
					if (strchr(sep,*s)!=NULL) break;
					if (*s==0) break;
				}
                if (*s==0) break;
                *s = 0; s++;
                if ((n+2)>=tmax) {
					break;
                }
        }
        p[n] = NULL;
        return n;
}

void header_split(char *line, char *header, char *value)
{
	char *s;
	strcpy(header,"");
	strcpy(value,"");
	ncpy(header,line,BFSZ-1);
	s = strchr(header,':');
	if (s==NULL) return;
	*s = 0;
	s++;
	if (*s==' ') s++;
	ncpy(value,s,BFSZ-1);
}



int lib_token_split_quoted(char *s, char **names, char **values, int max_pairs)
/*
Used to split stuff encoded with lib_token_encode_quoted

splits a list of optionally quoted name/pair values.
eturned values are pointers into original string, so original input string is
corrupted by this function.
e.g. a1="test value" a2=another a3=more a4="test value with a \"quoted\" string and a \\ character"
*/
{
    int n=0,quoted;
	char *var;
	char *o;
	char *out_start;

    for (;*s!=0;) {
            s += strspn(s," ");  /* Skip spaces */
            if (*s==0) break;
            var = s;
            s += strcspn(s,"=");  /* Find end of word */
			if (*s==0) break;
			*s = 0; s++;
			if (*s=='"') {
				s++;
				quoted=1;
			}else{
				quoted=0;
			}
			out_start=s;
			o=s;
			for (;*s!=0;s++) {
				if (*s=='"') break;
				if (*s==' ' && !quoted) break;
				if (*s=='\\' && quoted) {
					s++;
					*o++ = *s;
				} else {
					*o++ = *s;
				}
			}
			if (*s) s++;
			*o++ = 0;
			if (n<max_pairs) {
				names[n]=var;
				values[n]=out_start;
				n++;
			}
    }
    return n;
}
int lib_token_split_quoted_keep(char *s, char **names, char **values, int max_pairs)
/*
like lib_token_split_quoted but keeps encoding

e.g. a1="test value" a2=another a3=more a4="test value with a \"quoted\" string and a \\ character"
results in a4=test value with a \"quoted\" string and a \\ character
as opposed to lib_token_split_quoted:
results in a4=test value with a "quoted" string and a \ character
*/
{
    int n=0,quoted;
	char *var;
	char *o;
	char *out_start;

    for (;*s!=0;) {
            s += strspn(s," ");  /* Skip spaces */
            if (*s==0) break;
            var = s;
            s += strcspn(s,"=");  /* Find end of word */
			if (*s==0) break;
			*s = 0; s++;
			if (*s=='"') {
				s++;
				quoted=1;
			}else{
				quoted=0;
			}
			out_start=s;
			o=s;
			for (;*s!=0;s++) {
				if (*s=='"') break;
				if (*s==' ' && !quoted) break;
				if (*s=='\\' && quoted) {
					*o++ = *s++;
					*o++ = *s;
				} else {
					*o++ = *s;
				}
			}
			if (*s) s++;
			*o++ = 0;
			if (n<max_pairs) {
				names[n]=var;
				values[n]=out_start;
				n++;
			}
    }
    return n;
}
char *lib_token_find(char *name,char *names[], char *values[],int size)
{
	int i;
	for(i=0;i<size;i++){
		if(names[i]==NULL) break;
		if (strcmpnc(names[i],name)==0 && *values[i]!='\0') {
			return values[i];
		}
	}
	return NULL;
}
// remove commas say in the case of: a="b", b="c", d="e"
int remove_unquoted_chars(char *s, char *remove)
{
	int n=0;

	for (;*s!=0;) {
		s += strspn(s," ");  /* Skip spaces */
		if (*s==0) break;
		s += strcspn(s,"=");  /* Find end of word */
		if (*s==0) break;
		s++;
		if (*s=='"') {
			s++;
			for (;*s!=0;s++) {
				if (*s=='"') {s++; break;}
			}
			if(strchr(remove,*s)) {
				*s=' ';
				s++;
			}
		}
	}
	return TRUE;
}

int quoted_token_split_rm_quot(char *s, char *p[], char *sep, int tmax)
{ /*  removes the quotes from the tokens  */
        int n=0;

        for (;*s!=0;) {
                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
                p[n++] = s;
				if (*s == '\"') {
					p[n-1]++; s++;
					while(*s!='\"' && *s != '\0') {
						s++;
					}
					if (*s!='\"') {
						if (*s!=0) s++;
					} else {
						if (*s!=0) {*s='\0';s++;}
					}
				}
                s += strcspn(s,sep);  /* Find end of word */
                if (*s==0) break;
                *s = 0; s++;
                if ((n+2)>=tmax) {
                        break;
                }
        }
        p[n] = NULL;
        return n;
}
int quoted_token_split2_rm_quot(char *s, char *p[], char *sep, int tmax)
{ /*  removes the quotes from the tokens  */
        int n=0;

        for (;*s!=0;) {
                /* s += strspn(s,sep);  Skip spaces */
                if (*s==0) break;
                p[n] = s;
				if (*s == '\"'){p[n]++; s++; while(*s!='\"' && *s != '\0') {s++;} if (*s=='\"') {*s='\0';s++;}}
				n++;
                s += strcspn(s,sep);  /* Find end of word */
                if (*s==0) break;
                *s = 0; s++;
                if ((n+2)>=tmax) {
                        break;
                }
        }
        p[n] = NULL;
        return n;
}
/* with this one, two separators next to each other = another field.*/
int token_split2(char *s, char *p[], char *sep, int tmax)
{
        int n=0;

        for (;*s!=0;) {
                p[n++] = s;
                s = strchr(s,*sep);
                if (s==NULL) break;
                *s = 0;
                s++;
                if ((n+5)>=tmax) {
//                        printf("Too many tokens in this header line\n");
                        break;
                }

        }
        p[n] = NULL;
        return n;
}

/* Dynamic buffers that increase in size when you need to add more to it.*/

DBuf *dbuf_new_size(int inc) {
	DBuf *d=malloc(sizeof(DBuf));
	d->inc=inc;
	d->size=d->inc;
	d->len=0;
	d->d=malloc(d->size+1);
	d->d[0]=0;
	return d;
}

DBuf *dbuf_new(void) {
	return dbuf_new_size(DBUF_INC);
}

void dbuf_free(DBuf *d) {
	if (d) free(d->d);
	free(d);
}

void dbuf_addl(DBuf *d,char *bf, int len) {
	int dl;
	char *bf2;
	if (d->len+len>d->size) {
		/*dmsg("resizing dbuf (%d,%d)\n",d->len+len,d->size);*/
		dl=len+d->len-d->size;
		if (dl<d->inc) dl=d->inc;
		if (dl<d->size*4) dl=d->size*4; /* Add to 1.3e - quadruple size to make faster. */
		d->size=d->size+dl;
		bf2=malloc(d->size+1);
		memcpy(bf2,d->d,d->len);
		free(d->d);
		d->d=bf2;
	}
	memcpy(d->d+d->len,bf,len);
	d->len+=len;
	d->d[d->len]=0;
}

void dbuf_add(DBuf *d, char *bf)
{
	/*dmsg("dbuf add {%s}\n",bf);*/
	dbuf_addl(d,bf,lib_strlen(bf));
}

void dbuf_addc(DBuf *d, char c)
{
	char c2;
	if (d->len+1>d->size) {
		c2=c;
		dbuf_addl(d,&c2,1);
		return;
	}
	d->d[d->len]=c;
	d->len++;
	d->d[d->len]=0;
}

void dbuf_addf(DBuf *d,char *fmt,...) {
	char tmp[BFSZ];

	va_list args;
	va_start(args,fmt);
	vsnprintf(tmp,BFSZ-1,fmt,args);
	tmp[BFSZ-1]=0;
	va_end(args);
	dbuf_add(d,tmp);
}

char *dbuf_bf(DBuf *d) {
	d->d[d->len]=0;
	return d->d;
}


int lib_token_comma(char *s, char *p[], int tmax)
{
        int n=0;
	char *c;
	char *sep="\r\n \t";
	char *sepc="\r\n \t,";

        for (;*s!=0;) {
                s += strspn(s,sepc);  /* Skip spaces */
                if (*s==0) break;
                p[n++] = s;
                s += strcspn(s,sepc);  /* Find end of word */
                if (*s==0) break;
		/* If next non space, is a comma, then add a comma to the p[] array */
		for (c=s;*c!=0;c++) {
			if (sisspace(*c)) continue;
			if (*c==',') {
				p[n++] = ",";
				break;
			}
			break;
		}
                *s = 0; s++;
                if ((n+2)>=tmax) {
			break;
                }
        }
        p[n] = NULL;
        return n;
}

char *ncpy_fast(char *dst, char *src, int len)
{
	char *xdst = dst;
	int xlen = len;
	int srclen;
	if (len<100)
		return ncpy(dst,src,len);

	if (src==NULL) {
		char *s=NULL;
		if (vemsg!=NULL) vemsg("ABORTING DUE TO NULL PARAMTER TO NCPY\n");
		*s = 1;
		lib_abort();
	}
	srclen=lib_strlen(src);
	if (srclen<100)
		return ncpy(dst,src,len);

	if (srclen>len) srclen=len;
	if (srclen>0)
		memcpy(dst,src,srclen);
	dst[srclen]=0;
	return xdst;
}
char *ncpy2(char *dst, char *src, int len, int bflen)
{
	if (len>bflen) len = bflen;
	return ncpy(dst,src,len);
}
char *ncpy_debug(char *dst, char *src, int len, char *fname, int line)
{
	int slen = lib_strlen(src); // This is invalid if src is not null terminated.
	if (len<0) {
		vemsg("Crashing because 'ncpy' given negative length (%.100s) %d  %s:%d\n",src,len,fname,line);
		lib_abort();
	}
	if (slen<len-1) return strcpy(dst,src);
	return ncpy_real(dst,src,len,fname,line,len+1);
}
char *strncpy_safe(char *dst, char *src, int len, int dstlen)
{
	char *s;
	if (len<0) return dst;
	if (len>dstlen) len = dstlen;
	s =  ncpy(dst,src,len);
	return s;
}
char *strncat_safe(char *dst, char *src, int len, int dstlen)
{
	char *s;
	int upto = (int)strlen(dst);
	if (len<0) return dst;
	if (len+upto>dstlen) len = dstlen-upto;
	s =  ncpy(dst+upto,src,len);
	return s;
}
// idential to ncpy, but don't warn about bfsz...
char *ncpy_realok(char *dst, char *src, int len, char *fname, int line)
{
        char *xdst = dst;
        int xlen = len;
		if (len<0) {
			vemsg("ncpy_real, Crashing because 'ncpy' given negative length (%.100s) %d %s:%d\n",src,len,fname,line);
			lib_abort();
		}

		if (src==NULL) {
			char *s=NULL;
			if (vemsg!=NULL) vemsg("ABORTING DUE TO NULL PARAMTER TO NCPY %s:%d\n",fname,line);
			*s = 1;
			lib_abort();
		}
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        //xdst[xlen] = 0;  // suspect this one is still causing a crash...
        return xdst;
}
static char *bad_fname;
static int bad_len,bad_line;
int ncpy_idle(void) // this logging line can be removed at some point...
{
	if (bad_fname!=NULL) {
		if (vemsg) vemsg("ncpy called with suspect BFSZ, %s:%d %d (this can be ignored)\n",bad_fname,bad_line,bad_len);
		bad_fname = NULL;
	}
	return TRUE;
}
static FILE *nflog;
#define NCHASH 20000
static int nc_line[NCHASH];
static char *nc_file[NCHASH];
static int nc_hits[NCHASH];
static time_t nc_last[NCHASH];
static int dodebug_ncpy=FALSE;
int lib_ncpy_debug_start(int x)
{
#ifdef USE_DMALLOC
	dodebug_ncpy = x;
	if (x) {
		d_set_malloc_clean(); // clean all mallocs, bad performance hit...
		nflog = fopen("ncpy.log","w");
	}
#endif
	return TRUE;
}
char *lib_file_only(char *fname, char *tmp)
{
	char *s;
	char *last=NULL;
	ncpy(tmp,fname,BFSZ-1);
	for (s=fname;*s!=0;s++) {
		if (*s=='/') last = s+1;
		if (*s=='\\') last = s+1;
	}
	if (last!=NULL) ncpy(tmp,last,BFSZ-1);
	return tmp;
}
/*			int h = line%NCHASH;
			nc_hits[h]++;
			nc_file[h] = fname;
			nc_line[h] = line;
			nc_last[h] = time(NULL);
*/
int lib_ncpy_debug_write(void)
{
	int h;
	time_t now = time(NULL);
	if (!dodebug_ncpy) return TRUE;
//	printf("Writing ncpy.log\n");
	for (h=0; h<NCHASH;h++) {
		if (nc_file[h]!=NULL) {
//			printf("found one %s:%d\n",nc_file[h],nc_line[h]);
			if (nflog!=NULL) fprintf(nflog,"%d %d %s:%d\n",nc_hits[h],(int) (now-nc_last[h]),nc_file[h],nc_line[h]);
			vimsg("ncpy_unsafe %d %d %s:%d\n",nc_hits[h],(int) (now-nc_last[h]),nc_file[h],nc_line[h]);
		}
	}
	fflush(nflog);
	return TRUE;
}
int lib_ncpy_debug_flush(void)
{
	if (!dodebug_ncpy) return TRUE;
	if (nflog!=NULL) fflush(nflog);
	return TRUE;
}
char *ncpy_real(char *dst, char *src, int len, char *fname, int line, int size)
{
        char *xdst = dst;
        int xlen = len;
		static int fsz;

		if (dodebug_ncpy) {
			if ((size<=len) && (size>8) ) {
				if (size>8) { // so not a pointer, this is probably a real bug!
					vemsg("ncpy_unsafe_bug %s:%d %d %d\n",fname,line,size,len);
//					printf("ncpy_unsafe_bug %s:%d %d %d\n",fname,line,size,len);
				}
				if (len==1000) {
					vemsg("ncpy_unsafe_suspect %s:%d %d %d\n",fname,line,size,len);
//					printf("ncpy_unsafe_suspect %s:%d %d %d\n",fname,line,size,len);
				}
			}
		}


		//		stack_ok("ncpy");
		if (len<0) {
			vemsg("ncpy_real, Crashing because 'ncpy' given negative length (%.100s) %d %s:%d\n",src,len,fname,line);
			lib_abort();
		}

		if (src==NULL) {
			char *s=NULL;
			if (vemsg!=NULL) vemsg("ABORTING DUE TO NULL PARAMTER TO NCPY %s:%d\n",fname,line);
			*s = 1;
			lib_abort();
		}
// DEBUGGING CODE:
//		if (len==100) printf("%s:%d debug=%d size %d len %d\n",fname,line,dodebug_ncpy,size,len);
#ifdef USE_DMALLOC
		if (dodebug_ncpy) {
			if (size<=len) { // so if possibly wrong...
				// Lets scan for end of malloc block.
				if (d_malloc_dodgy(dst,len+1,FALSE)) {
					vemsg("ncpy_unsafe: End of malloc region found %s:%d %d %d\n",fname,line,len,size);
				}
				memset(dst,1,len); // Wipe out entire destination, to trigger fault if one exists???
			}
		}
#endif

        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
	//	if (len==BFSZ && (dst-xdst)<BFSZ-2) if (vemsg) vemsg("ncpy_real called with BFSZ %s:%d\n",fname,line,len);
		if (xlen==BFSZ ) {
			bad_line = line;
			bad_fname = fname;
			bad_len = (int) (dst-xdst);
		}
		if (dodebug_ncpy) {
	        xdst[xlen] = 0;
		}
        return xdst;
}
char *lib_nullok(char *s)
{
	stack_ok("nullok");
	if (s==NULL) return "";
	return s;
}
char *ncpy_nullok(char *dst, char *src, int len)
{
	if (src==NULL) src = "";
	return ncpy(dst,src,len);
}
char *ncat(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;
	int r = lib_strlen(dst);

	len -= r;
	dst += r;
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}
// Like ncat but allows you to specify a separator
char *ncat_sep(char *dst, char *sep, char *src, int len)
{
	char *xdst = dst;
	int xlen = len;
	int r = lib_strlen(dst);
	if(lib_strlen(src)==0) return dst;
	len -= r;
	dst += r;
	if(r>0)for (;(*sep !=0) && (len>0); ) {*dst++  = *sep++; len--;}
	for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
	*dst++ = 0;
	xdst[xlen] = 0;
	return xdst;
}
char *strclean(char *src, char *txt, int len)
{
	char *s;
	src[len] = 0;
	for(s=src;*s;s++){
		if(strchr(txt,*s)){
			*s='\0';
			break;
		}
	}
	return src;
}

#define xtolower(_c)    ( (_c)-'A'+'a' )
#define xtoupper(_c)    ( (_c)-'a'+'A' )

char *mystrupr(char *s)
{
        char *ss=s;
        for (;*s!=0;s++) if (sislower(*s)) *s = xtoupper(*s);
        return ss;
}
char *int_to_str(int x, char *tmp)
{
	sprintf(tmp,"%d",x);
	return tmp;
}

#ifndef SQLAUTH // sql library seems to have a function of the same name. 
time_t str_to_time(char *n) // naming conflict, so renamed, will probably break something...
{
	char *end=NULL;
#ifdef WIN32

#ifndef __BORLANDC__
	return (time_t) _strtoi64(n,&end,10);
#else
	return atoi(n);
#endif

#else
	return strtoll(n,&end,10);
#endif
}
#endif

char *time_to_str(time_t x, char *tmp)
{
#ifdef IS64
	sprintf(tmp,"%lld",x); // NEED TO CONFIRM THIS WORKS ON WINDOWS NOW...
#else
	sprintf(tmp,"%d",x);
#endif
	return tmp;
}

char *lib_strdup(char **dst, char *src)
{
	if (*dst!=NULL) free(*dst);
	*dst = strdup(src);
	return *dst;
}
int lib_strlen(const char *s)
{
	return (int) strlen(s);
}
char *mystrlwr(char *s)
{
        char *ss=s;
		stack_ok("nullok");
        for (;*s!=0;s++) if (sisupper(*s)) if (*s>0) *s = xtolower(*s);
        return ss;
}
int lib_has_upper(char *s)
{
	for (;*s!=0;s++) if (sisupper(*s)) return TRUE;
	return FALSE;
}

void str_lines_start(Strlines *v, char *s, int len)
{
	v->start = s;
	v->upto = s;
	v->len = len;
	v->savedp = NULL;
	v->lastret = NULL;
	v->debug = FALSE;
}
char *str_lines_next_eol(Strlines *v, int *goteol)
{
	char *s = v->upto;
	char *start=s;
	char *end = v->start + v->len;
	if (v->savedp!=NULL) *v->savedp = v->saved;
	for (;s<end;s++) {
		if (*s!='\n') continue;
		*goteol = TRUE;
		if (s==v->upto || s[-1]!='\r') {
			v->saved = s[0];
			v->savedp = s;
			s[0] = 0;
			v->upto = s+1;
			return start;
		}
		v->saved = s[-1];
		v->savedp = s-1;
		s[-1] = 0;
		v->upto = s+1;
		return start;
	}
	*goteol = FALSE;
	if (lib_strlen(start)>0) {
		v->upto = s;
		return start;
	}
	return NULL;
}
char *str_lines_next(Strlines *v)
{
	int eol;
	return str_lines_next_eol(v,&eol);
}
char *str_lines_rest(Strlines *v, int *len)
{
	char *s = v->upto;
	char *start=s;
	char *end = v->start + v->len;

	if (v->savedp!=NULL) {
		if (v->debug) vimsg("str_lines_rest: storing saved %d\n", v->saved);
		*v->savedp = v->saved;
	}
	v->savedp = NULL;
	*len = (int)(end-start);
	if (v->debug) vimsg("str_lines_rest: sending  %d\n",*len);
	return s;
}
char *str_lines_next_crlf(Strlines *v, int *goteol)
{
	char *s = v->upto;
	char *start=s;
	char *end = v->start + v->len;
	*goteol = TRUE;
	if (v->savedp!=NULL) *v->savedp = v->saved;
	v->savedp = NULL;
	for (;s<end;s++) {
		if (*s=='\n') {
			if (s==v->upto) continue;
			if (s[-1]=='\r') {
				s++;
				v->saved = s[0];
				v->savedp = s;
				s[0] = 0;
				v->upto = s;
				v->lastret = start;
				return start;
			}
		}
	}
	if (end<=start) return NULL;
	*goteol = FALSE;
	if (v->lastret==start) return NULL; // prevent loops.
	v->lastret = start;
	return start;
}
char *str_lines_next_bin(Strlines *v, int *len)
{
	char *s = v->upto;
	char *start=s;
	char *end = v->start + v->len;
	(*len) = 0;
	if (s>=end) return NULL;
	for (;s<end;s++) {
		(*len) ++;
		if ((*s=='\n') || (*len > 5000)) {
			v->upto = start+(*len);
			return start;
		}
	}
	v->upto = start+(*len);
	return start;
}

char *str_lines_next_any(Strlines *v, int *goteol, int *ended_with_cr, char *tmp)
{
	char *s = v->upto;
	char *start=s;
	char *end = v->start + v->len;
	int lastended = *ended_with_cr;

	/* added by chrisp, so that ended_with_cr is not cleared at end of buffer */
	if (end<=start) {
		//vimsg("str_lines_next_any, end<=start %p %p\n",end,start);
		return NULL;
	}
	if (v->debug) vimsg("str_lines_next_any:  upto=%d len=%d \n",(int)(v->upto-v->start),(int)(end - v->upto));

	*ended_with_cr = FALSE;
	*goteol = TRUE;
	if (v->savedp!=NULL) {
		*v->savedp = v->saved;
		if (v->debug) vimsg("str_lines_next_any: savedp = saved %d\n",v->saved);
	}
	v->savedp = NULL;
	for (;s<end;s++) {
		if (*s=='\n') {
			if (v->debug) vimsg("str_lines_next_any: found LF\n");
			if (s==v->upto) {
				if (lastended) {start++; continue;}
				else {
					v->upto = s + 1;
					if (v->debug) vimsg("str_lines_next_any: found LF, returning blank line\n");
					return "\r\n";
				}
			}
			if (s[-1]=='\r') {
				s++;
				v->saved = s[0];
				v->savedp = s;
				s[0] = 0;
				v->upto = s;
				if (v->debug) vimsg("str_lines_next_any: found CR before LF, setting saved...\n");
				return start;
			} else {
				*s = 0;
				v->upto = s+1;
				ncpy(tmp,start,BFSZ-1);
				ncat(tmp,"\r\n",BFSZ-1);
				if (v->debug) vimsg("str_lines_next_any: found LF appending crlf, set upto -1....\n");
				return tmp;
			}
		}
		if (*s=='\r') if (s[1]!='\n') {
			*s = 0;
			v->upto = s+1;
			ncpy(tmp,start,BFSZ-1);
			ncat(tmp,"\r\n",BFSZ-1);
			if (v->debug) vimsg("str_lines_next_any: found CR with no LF after, append crlf.\n");
			if (s+1==end) {
				if (v->debug) vimsg("str_lines_next_any: SET ENDED_WITH_CR.\n");
				*ended_with_cr = TRUE;
			}
			return tmp;
		}
	}
	if (end<=start) {
		if (v->debug) vimsg("str_lines_next_any2, end<=start %p %p\n",end,start);
		return NULL;
	}
	*goteol = FALSE;
	if (v->lastret==start) {
		if (v->debug) vimsg("str_lines_next_any3, end<=start %p %p\n",end,start);
		return NULL; // prevent loops.
	}
	v->lastret = start;
	if (v->debug) vimsg("str_lines_next_any: strange finish lastret=start.\n");
	return start;
}

void str_lines_end(Strlines *v)
{
	if (v->savedp!=NULL) *v->savedp = v->saved;
	v->savedp = NULL;
}
char *lib_remove_before(char *in, char *tostrip)
{
	char bf[BFSZ];
	char *s = strstrnc(in,tostrip);
	if (s!=NULL) {
		ncpy(bf,s+lib_strlen(tostrip),BFSZ-1);
		ncpy(in,bf,BFSZ-1);
	}
	return in;
}
char *lib_remove_string(char *s, char *tostrip)
{
	char bf[BFSZ];
	char *o;
	int len = lib_strlen(tostrip);
	char *start=s;
	for (o=bf;*s!=0;s++) {
		if (strncmpnc(s,tostrip,len)==0) {
			if (s[len]==' ' || s[len]=='\n' || s[len]=='\r' || s[len]==0 ) {
				if (s==start || s[-1]==' ') {
					s += len + 1;
					s--;
					if (*s==0) break;
					continue;
				}
			}
		}
		*o++ = *s;
	}
	*o++ = 0;
	ncpy(start,bf,BFSZ-1);
	return start;
}
char *lib_strip(char *s, char *tostrip)
{
	char bf[BFSZ];
	char *o;
	char *start=s;
	for (o=bf;*s!=0;s++) {
		if (strchr(tostrip,*s)!=NULL) continue;
		*o++ = *s;
	}
	*o++ = 0;
	ncpy(start,bf,BFSZ-1);
	return start;
}
char *lib_strip_crlf(char *s)
{
	char *start =s;
	s = strchr(start,'\r');
	if (s!=NULL) *s = 0;
	s = strchr(start,'\n');
	if (s!=NULL) *s = 0;
	return start;
}
int lib_strip_crlf_x(char *s)
{
	int x=FALSE;
	char *start=s;
	s = strchr(start,'\r');
	if (s!=NULL) {*s = 0; x=TRUE;}
	s = strchr(start,'\n');
	if (s!=NULL) {*s = 0; x=TRUE;}
	return x;
}
char *lib_strip_crlf_atend(char *s)
{
	char *start=s;

	for(s;*s;s++){
		if(*s=='\r' || *s=='\n'){
			if( (*s=='\r' && *(s+1)=='\n' && *(s+2)==0) || (*(s+1)==0) ){
				*s=0;
				break;
			}
		}
	}
	return start;
}
char *lib_trim_end(char *s)
{
	char *p;
	if(!s || !*s) return s;
	p=s;
	for(; *p; p++);
	for(; sisspace((unsigned char) *p) || iseol((unsigned char) *p); p--) {
		if(p<s) break;
		*p = '\0';
	}
	return s;
}
int sisspace(int c)
{
	if (c<0) return FALSE;
	return isspace(c);
}
int sisascii(int c)
{
	if (c<0) return FALSE;
	return isascii(c);
}
int sisdigit(int c)
{
	if (c<0) return FALSE;
	return (isdigit(c));
}
int sisalnum(int c)
{
	if (c<0) return FALSE;
	return (isalnum(c));
}
int sisalpha(int c)
{
	if (c<0) return FALSE;
	return (isalpha(c));
}
int sisupper(int c)
{
	if (c<0) return FALSE;
	return (isupper(c));
}
int sislower(int c)
{
	if (c<0) return FALSE;
	return (islower(c));
}
int sisprint(int c)
{
	if (c<0) return FALSE;
	return (isprint(c));
}
char *lib_truncate(char *s, int n)
{
	if((int) lib_strlen(s)>n){
		s[n]=0;
		strcat(s,"...");
	}
	return s;
}
char *strstrnc_modify(char *s1, char *s2)
{
	// This version is allowed to modify 's1' , S2 IS ASSUMED TO BE IN LOWER CASE ALREADY...
	strlwr(s1);
	return strstr(s1,s2);
}
char *strstrnc(char *s1, char *s2)
{
	char *a,*a1,*a2;
	char b1 = tolower(*s2);
	char b2 = toupper(*s2);
	int n = lib_strlen(s2);

	if (n==0) return NULL;
	for (a=s1;a!=NULL;) {
		a1 = strchr(a,b1);
		a2 = strchr(a,b2);
		if (a2) {
			if (a1) {
				if (a2<a1) a1 = a2;
			} else {
				a1 = a2;
			}
		}
		if (a1==NULL) break;
		if (strncmpnc(a1,s2,n)==0) return a1;
		a = a1+1;
	}
	return NULL;
}
int strstr_test(char *a1, char *a2)
{
	if (strstrnc(a1,a2)!=strstrnc_old(a1,a2)) printf("FAILED TESTS (%s) (%s) %p %p\n",a1,a2,strstrnc(a1,a2),strstrnc_old(a1,a2));
	if (strstrnc(a1,a2)!=strstrnc_old(a1,a2)) vemsg("FAILED TESTS (%s) (%s) %p %p\n",a1,a2,strstrnc(a1,a2),strstrnc_old(a1,a2));
	return TRUE;
}
char *strstrnc_old(char *s1, char *s2)
{
	int n = lib_strlen(s2);
	if (n==0) return NULL;
	for (;*s1!=0;s1++) {
		if (strncmpnc(s1,s2,n)==0) return s1;
	}
	return NULL;
}
int lib_str_hash_nc(char *s, int max, int width)
{
	unsigned int total=0;
    const unsigned char *cm = charmap;
	int r;
	int i;
	for (i=0; i<width;) {
		total += cm[*s++]; i++;
		if (i>=width) break;
		total += cm[*s++] << 8;  i++;
		if (i>=width) break;
		total += cm[*s++] << 16;  i++;
		if (i>=width) break;
		total += cm[*s++] << 24;  i++;
		if (i>=width) break;
	}
    r = total % max;
    if (r<0) return -r;
    return r;
}



int strncmp_strlen(char *txt, char *start) /* same as strncmp(txt,start,lib_strlen(txt)) */
{
	int len=lib_strlen(start);
	if (len<1) return true;
	return strncmp(txt,start,len);
}

int strnicmp_strlen(char *txt, char *start)  /* same as strnicmp(txt,start,lib_strlen(txt)) */
{
	int len=lib_strlen(start);
	if (len<1) return true;
	return strnicmp(txt,start,len);
}

int lib_starts_with_nc(char *txt, char *start)
{
	return strnicmp_strlen(txt,start)==0;
}

int lib_starts_with(char *txt, char *start)
{
	while(*start) {
		if (*start++!=*txt++) return false;
	}
	return true;
}
char *lib_strchr_2nd(char *str, char c)
{
	char *s;
	s=strchr(str,c);
	if(!s || !(s+1)) return NULL;
	s++;
	s=strchr(s,c);
	if(!s || !(s+1)) return NULL;
	return s;
}


int lib_ends_with(char *txt, char *end)
{
	int len1=lib_strlen(txt);
	int len2=lib_strlen(end);
	if (len2>len1) return 0;
	return strcmp(txt+(len1-len2),end)==0;
}

int lib_ends_with_nc(char *txt, char *end)
{
	int len1=lib_strlen(txt);
	int len2=lib_strlen(end);
	if (len2>len1) return 0;
	return stricmp(txt+(len1-len2),end)==0;
}

char *lib_first_single(char *txt, char c)
{
	char *ret=txt, last=0;
	while(*txt) {
		if (*txt==c && last!=c && *(txt+1)!=c) return txt;
		last=*txt;
		txt++;
	}
	return ret;
}

int strcmpnc_slashfirst(char *s1, char *s2)
{
        const unsigned char *cm = charmap,
             *us1 = (const unsigned char *)s1,
             *us2 = (const unsigned char *)s2;
		unsigned char tmp1, tmp2;

		while (cm[*us1] == cm[*us2++]){
			if (*us1++ == '\0')
                        return (0);
		}

		// A sort where the resulting order is: foo, foo/bar, foo!bar, foo$bar, foobar
		//							instead of: foo, foo!bar, foo$bar, foo/bar, foobar
		tmp1=cm[*us1];
		tmp2=cm[*--us2];
		if(tmp1=='/') tmp1=1;
		if(tmp2=='/') tmp2=1;

		return (tmp1 - tmp2);
}


char *lib_to_forward_slash(char *txt)
{
	char *ret=txt;
	while(*txt) {
		if (*txt=='\\') *txt='/';
		txt++;
	}
	return ret;
}
char *lib_cleanup_slash(char *xfilename)
{
	char bf[BFSZ];
	char *s,*out=bf;
	int isslash,lastslash;
	lastslash = FALSE;
	for (s=xfilename;*s!=0;s++) {
		isslash = FALSE;
		if (*s=='/') isslash = TRUE;
		if (*s=='\\') isslash = TRUE;
		if (isslash) if (lastslash) continue;
		*out++ = *s;
		lastslash = isslash;
	}
	*out++ = 0;
	ncpy(xfilename,bf,BFSZ-1);
	return xfilename;
}
char *lib_cleanup_dotslash(char *xfilename) // remove /a/b/./c
{
	char bf[BFSZ];
	char *s,*out=bf;
	for (s=xfilename;*s!=0;s++) {
		if (strncmp(s,"/./",3)==0)  s+=2;
		else if (strncmp(s,"\\.\\",3)==0)  s+=2;
		*out++ = *s;
	}
	*out++ = 0;
	ncpy(xfilename,bf,BFSZ-1);
	return xfilename;
}
char *lib_cleanup_dotdot(char *xfilename2) // remove /a/b/./c and change a/b/../d to a/d
{
	char bf[BFSZ];
	char *s,*out=bf;
	char xfilename[BFSZ];

	ncpy(xfilename,xfilename2,BFSZ-1);
	lib_to_unix_slash(xfilename);

	for (s=xfilename;*s!=0;s++) {
		if (strcmp(s,"/.")==0)  break; // skip the rest
		else if (strcmp(s,"/..")==0)  {goto upone;}
		else if (strncmp(s,"/./",3)==0)  s+=2;
		else if (strncmp(s,"/../",4)==0)  {
upone:		s+=3;
			// remove end of *out
			if (out>bf) for (out--;out>bf;out--) {
				if (*out=='/') break;
			}
			if (out<bf) out = bf;
		}
		else if (strncmp(s,"\\.\\",3)==0)  s+=2;
		if (out-bf>BFSZ-10) break;
		*out++ = *s;

	}
	*out++ = 0;
	ncpy(xfilename2,bf,BFSZ-1);
	return xfilename2;
}

char *lib_to_unix_slash(char *txt)
{
	char *ret=txt;
	while(*txt) {
		if (*txt=='\\') *txt='/';
		txt++;
	}
	return ret;
}

char *lib_to_back_slash(char *txt)
{
	char *ret=txt;
	while(*txt) {
		if (*txt=='/') *txt='\\';
		txt++;
	}
	return ret;
}

char *lib_to_file_system_slash(char *txt)
{
	if (txt==NULL) return txt;
#ifdef WIN32
	return lib_to_back_slash(txt);
#else
	return lib_to_forward_slash(txt);
#endif
}

int lib_file_names_equal(char *f1, char *f2)
{
	while(*f1 && *f2) {
		if (*f1!=*f2) {
			if ((*f1!='\\' && *f1!='/') || (*f2!='\\' && *f2!='/')) return false;
		}
		f1++;
		f2++;
	}
	return true;
}

char *lib_trim_dots(char *txt, int n, char *bf, int bfsz)
{
	int len;
	ncpy(bf,txt,bfsz-1);
	if (n>bfsz-1) n=bfsz-1;
	len=lib_strlen(bf);
	if (len>n && n>3) {
		bf[n]=0;
		bf[n-1]='.';
		bf[n-2]='.';
		bf[n-3]='.';
	}
	return bf;

}
char *lib_trim_slash(char *txt)
{
	int len;
	len = lib_strlen(txt);
	if (len>0) if (txt[len-1] == '/') txt[len-1] = 0;
	if (len>0) if (txt[len-1] == '\\') txt[len-1] = 0;
	return txt;
}

char *lib_trim_dot(char *txt)
{
	int len;
	len = lib_strlen(txt);
	if (len>0) if (txt[len-1] == '.') txt[len-1] = 0;
	return txt;
}

int lib_count_slash(char *txt)
{
	int count=0;
	char *p=txt;
	while(*p) {
		if (*p=='/') count++;
		if (*p=='\\') count++;
		p++;
	}
	return count;
}
int lib_contains_slash(char *txt)
{
	char *p=txt;
	while(*p) {
		if (*p=='/' || *p=='\\') return TRUE;
		p++;
	}
	return FALSE;
}


#ifndef strdup
#ifndef WIN32
char *strdup(const char *s)
{
	char *n = malloc(lib_strlen(s)+1);
	strcpy(n,s);
	return n;
}
#endif
#endif
#ifdef isunix
#define NEED_STRLWR 1
#endif

#ifdef WIN32x
#define NEED_STRLWR
#endif

#ifdef NEED_STRLWR
#ifdef NOUPPER
#define _tolower(_c)    ( (_c)-'A'+'a' )
#define _toupper(_c)    ( (_c)-'a'+'A' )
#endif

char *strupr(char *s)
{
        char *ss=s;
        for (;*s!=0;s++) if (sislower(*s)) *s = _toupper(*s);
        return ss;
}
char *strlwr(char *s)
{
        char *ss=s;
        for (;*s!=0;s++) if (sisupper(*s)) if (*s>0) *s = _tolower(*s);
        return ss;
}

#endif

int istrue(char *string) {
	if (!string || !string[0]) return FALSE;
	if (strcmpnc(string,"true") == 0) return TRUE;
	if (strcmpnc(string,"yes") == 0) return TRUE;
	if (strcmpnc(string,"y") == 0) return TRUE;
	if (strcmpnc(string,"on") == 0) return TRUE;
	if (strcmpnc(string,"checked") == 0) return TRUE;
	if (strcmpnc(string,"selected") == 0) return TRUE;
	return FALSE;
}

int isfalse(char *string) {
	if (!string || !string[0]) return FALSE;
	if (strcmpnc(string,"false") == 0) return TRUE;
	if (strcmpnc(string,"no") == 0) return TRUE;
	if (strcmpnc(string,"n") == 0) return TRUE;
	if (strcmpnc(string,"off") == 0) return TRUE;
    return FALSE;
}
int lib_write_file_string(char *fname, char *data)
{
	FILE *f;
	f = fopen(fname,"wb");
	if (f!=NULL) {
		(int)fwrite(data,1,lib_strlen(data),f);
		fclose(f);
	}
	return TRUE;
}
char *lib_read_file_string(char *fname, char *data, int sz)
{
	FILE *f;
	strcpy(data,"");
	f = fopen(fname,"rb");
	if (f!=NULL) {
		size_t n = fread(data,1,sz-1,f);
		if (n>=sz) n = sz-1;
		if (n>0) data[n] = 0;
		fclose(f);
	}
	return data;
}
int lib_match_list(char *xwild, char *xgrp2)
{
        char bf[1000];
        char grp[1000];
        int m=TRUE,lastm;
        char *s,*p,*w;
        int done,len;

	if (lib_strlen(xwild)==0) return TRUE;
		if (xgrp2==NULL) return FALSE;
        ncpy(grp,xgrp2,1000-10); strlwr(grp);
        lastm = FALSE;
        p = xwild;
        done = FALSE;
        for (;!done;) {
                s = strchr(p,',');
                if (s==NULL) {s = xwild + lib_strlen(xwild); done = TRUE;}
                if (*p==0) break;
                len = (int)(s - p);
                if (len>100) len = 100;
                strncpy(bf,p,len); bf[len] = 0;
                strlwr(bf); w = bf;

                m = TRUE;
                if (*w=='!') {
                        w++;
                        m = FALSE;
                }
                if (match_wild_or_ip(w,grp)) { lastm = m; }

                p = s + 1;
        }
        return lastm;
}
int lib_match_anyof(char *list, char *thing)
{
        char grp[BFSZ];
		char *s;
		char *upto;

        ncpy(grp,list,1000-10);
		for (s = mystrtok(grp,", ",&upto);s!=NULL;s = mystrtok(NULL,", ",&upto)) {
			if (strcmpnc(s,thing)==0) return TRUE;
		}
		return FALSE;
}


int lib_match_groups(char *xwild, char *xgrp2, int *isat)
{
	char bf[1000];
	char grp[1000];
	int m=TRUE,lastm;
	char *s,*p,*w;
	int done,len;
	int mat,lastat;
	mat = lastat = FALSE;
	if (lib_strlen(xwild)==0) return TRUE;
	ncpy(grp,xgrp2,1000-10); strlwr(grp);
	lastm = FALSE;
	p = xwild;
	done = FALSE;
	for (;!done;) {
		s = strchr(p,',');
		if (s==NULL) {s = xwild + lib_strlen(xwild); done = TRUE;}
		if (*p==0) break;
		len = (int)(s - p);
		if (len>100) len = 100;
		strncpy(bf,p,len); bf[len] = 0;
		strlwr(bf); w = bf;
		m = TRUE;
		if (*w=='!') {
			w++;
			m = FALSE;
		}
		mat = FALSE;
		if (*w=='@') {
			w++;
			m = FALSE;
			mat = TRUE;
		}
		if (match_wild(w,grp)) {
			lastm = m; lastat = mat;
		}
		p = s + 1;
	}
	*isat = lastat;
	return lastm;
}
/* Trim leading and trailing spaces before matching */
int lib_match_list_trim_dfalse(char *xwild, char *xgrp2)
{
	if (xwild==NULL) return TRUE;
	if (lib_strlen(xwild)==0) return FALSE;
	return lib_match_list_trim_dtrue(xwild,xgrp2);
}
int lib_match_list_trim_dtrue(char *xwild, char *xgrp2)
{
        char bf[1000];
        char grp[1000];
	char trimw[BFSZ];
        int m=TRUE,lastm;
        char *s,*p,*w;
        int done,len;

	if (lib_strlen(xwild)==0) return TRUE;
        ncpy(grp,xgrp2,1000-10); strlwr(grp);
        lastm = FALSE;
        p = xwild;
        done = FALSE;
        for (;!done;) {
                s = strchr(p,',');
                if (s==NULL) {s = xwild + lib_strlen(xwild); done = TRUE;}
                if (*p==0) break;
                len = (int)(s - p);
                if (len>100) len = 100;
                strncpy(bf,p,len); bf[len] = 0;
                strlwr(bf); w = bf;

                m = TRUE;
                if (*w=='!') {
                        w++;
                        m = FALSE;
                }
				lib_trim_spaces(w,trimw);

                if (match_wild_or_ip(trimw,grp)) { lastm = m; }

                p = s + 1;
        }
        return lastm;
}

char *lib_ip_fname(char *ip, char *tmp)
{
	char *s;
	ncpy(tmp,ip,BFSZ-1);
	for (s=tmp;*s!=0;s++) {
		if (*s==':') *s = '_';
	}
	return tmp;
}

/* CHTML MODULE STARTS */

Chtml *chtml_open(char *fname)
{
	Chtml *h = calloc(1,sizeof(Chtml));
	h->f = fopen(fname,"r");
	if (h->f==NULL) {
		free(h);
		return NULL;
	}
	return h;
}
char *chtml_find_token(char *bf,char c)
{
	char *s;
	for (s=bf;*s!=0;s++) {
		if (*s=='"') { // then search for end quote.
			for (;*s!=0;s++) if (*s=='"') break;
		}
		if (*s=='\'') { // then search for end quote.
			for (;*s!=0;s++) if (*s=='\'') break;
		}
		if (*s==c) break;
		if (*s==0) break;
	}
	if (*s==c) return s;
	return NULL;
}
int chtml_read(Chtml *h, char *out, int bfsz)
{
	int n = h->len;
	int got=0;
	char saved;
	char *token=NULL;
	char *end=NULL;
	char *end2;
	int gotmore=FALSE;

	strcpy(out,"");

	// Get at least 500 bytes in buffer.
	for (;!feof(h->f);) {
		int want = BFSZ-n;
		if (want>500) want = 500;
		if (n>500) break; // we have enough
		gotmore=TRUE;
		got = (int)fread(h->bf+n,1,want,h->f);
		if (got<0) break;
		n += got;
		h->bf[n] = 0;
	}
	h->len = n;

//if (gotmore) 	vemsg("Input Buffer (%s) length %d\n",h->bf,n);
	// Send everything till beginning of next token... (or end of last token)

	token = chtml_find_token(h->bf,'<');
	if (token==NULL) token = h->bf;
	if (token==h->bf) {
		end = chtml_find_token(token,'>');
		end2 = chtml_find_token(token+1,'<');


		if (end2) end2--;  // the char before hand so we don't cut the beginning one off the enxt string...
		if (end==NULL) end = end2; //
		if (end && end2) if (end2<end) end = end2;
		if (end==NULL) end = token + lib_strlen(token)-1;
	} else {
		token--;
		end = chtml_find_token(token,'>');
		if (end) if (end<token) token = end;
		end = token;
		token = h->bf;
		if (end==NULL) end = token + lib_strlen(token)-1;
	}
	end++;
	if (end<=token) return FALSE;
	saved = *end; *end = 0;
	ncpy(out,token,bfsz-1);
	// Leave the rest in the buffer...
	*end = saved;
	memmove(h->bf,end,lib_strlen(end)+1);
	h->len = lib_strlen(h->bf);

	return TRUE;  // We have sent everything from beginning of < to >
}
int chtml_free(Chtml **ph)
{
	if (*ph) {
		Chtml *h = *ph;
		if (h->f) fclose(h->f); h->f = NULL;
		free(*ph);
	}
	*ph = NULL;
	return TRUE;
}
/* CHTML MODULE ENDS */


/* RFC compliant email address stripping function */
int rfc_raw_email(char *email, char *raw, int bfsz) {
	static char *special = "<>()[]\\.,;:@\"";
	int quoted,dotnum,number,i;
	char *ptr,*str;

	if (!raw) return FALSE;
	*raw = 0;

	if (!email) return FALSE;

	ptr = strrchr(email,'\"');
	if (!ptr) ptr = email;

	str = strchr(ptr,'@');
	if (!str) str = strchr(email,'@');
	if (!str || str == email) return FALSE;
	ptr = str--;
	quoted = FALSE;
	if (*str == '\"') {
		if (str == email) return FALSE;
		quoted = TRUE;
		str--;
	}

	for(; TRUE; str--) {
		if (quoted) {
			if (*str == '\"') {
				if (str == email || *(str-1) != '\\') {
					str++;
					break;
				}
				str--;
			}
		}
		else {
			if (*str != '.' && (*str <= 0x1F || *str == 0x7F || *str == ' ' || strchr(special,*str)) ) {
				if (str == email || *(str-1) != '\\') {
					str++;
					break;
				}
				str--;
			}
		}
		if (str == email) break;
	}

	dotnum = FALSE;
	number = FALSE;

	ptr++;
	if (*ptr == '[') {
		dotnum = TRUE;
		ptr++;
	}
	if (*ptr == '#') {
		number = TRUE;
		ptr++;
	}

	for(; *ptr; ptr++) {
		if (*ptr == '>') break;

		if (dotnum) {
			if (*ptr == ']') break;
			if (!sisdigit(*ptr) && *ptr != '.') {
				break;
			}
		}
		else if (number) {
			if (!sisdigit(*ptr)) {
				break;
			}
		}
		else {
			if (*ptr != '.' && *ptr != '-' && !sisalnum(*ptr) && (*ptr != '_') && (*ptr != '*') ) {
				break;
			}
		}
	}

	for(i = 0; str != ptr && i < bfsz; ) {
		raw[i++] = *str++;
	}

	raw[i] = '\0';
	return TRUE;
}

char *lib_fix_email2(char *str2)
{
	char tmp[BFSZ];
	lib_fix_email(str2,tmp);
	ncpy(str2,tmp,BFSZ-1);
	return str2;
}
char *lib_fix_email(char *str2, char *buf)
{
	char *word_str;
	char *s;
	char *c;
	char str[LIB_BFSZ];
	ncpy(str,str2,LIB_BFSZ-1);
	strlwr(str);
	word_str = mystrtok(str," \n\t<>",&c);
	for (;word_str!=NULL;) {
		if (strchr(word_str,'@')!=NULL){
			sprintf(buf,"%.500s",word_str);
			if (buf[0]=='"') { /* Strip invalid quotes around address */
				s = strchr(buf+1,'"');
				if (s!=NULL) *s = 0;
				ncpy(buf,buf+1,BFSZ-1);
			}
			return buf;
		}
		word_str = mystrtok(NULL," <>\n\t",&c);
	}
	if (strncmp(str2,"<>",2)==0) {
		ncpy(buf,"<>",10);
		return buf;
	}
	ncpy(buf,str2,BFSZ-1);
	return str2;
}
char *lib_first_email(char *str2, char *buf)
{
	char *word_str;
	char *s;
	char *c;
	char str[LIB_BFSZ];
	ncpy(str,str2,LIB_BFSZ-1);
	strlwr(str);
	word_str = mystrtok(str," \n\t<>,",&c);
	for (;word_str!=NULL;) {
		if (strchr(word_str,'@')!=NULL){
			sprintf(buf,"%.500s",word_str);
			if (buf[0]=='"') { /* Strip invalid quotes around address */
				s = strchr(buf+1,'"');
				if (s!=NULL) *s = 0;
				ncpy(buf,buf+1,BFSZ-1);
			}
			return buf;
		}
		word_str = mystrtok(NULL," <>,\n\t",&c);
	}
	if (strncmp(str2,"<>",2)==0) {
		ncpy(buf,"<>",10);
		return buf;
	}
	ncpy(buf,str2,BFSZ-1);
	return str2;
}
char *lib_first_emailorgroup(char *str2, char *buf)
{
	char *word_str;
	char *s;
	char *c;
	char str[LIB_BFSZ];
	ncpy(str,str2,LIB_BFSZ-1);
	strlwr(str);
	word_str = mystrtok(str," \n\t<>,",&c);
	for (;word_str!=NULL;) {
		if (strchr(word_str,'@')!=NULL){
			sprintf(buf,"%.500s",word_str);
			if (buf[0]=='"') { /* Strip invalid quotes around address */
				s = strchr(buf+1,'"');
				if (s!=NULL) *s = 0;
				ncpy(buf,buf+1,BFSZ-1);
			}
			return buf;
		}
		if (strchr(word_str,':')!=NULL) { // group name
			ncpy(buf,word_str,BFSZ-1);
			s = strchr(buf,':');
			if (s!=NULL) *s = 0;
			return buf;
		}
		word_str = mystrtok(NULL," <>,\n\t",&c);
	}
	if (strncmp(str2,"<>",2)==0) {
		ncpy(buf,"<>",10);
		return buf;
	}
	ncpy(buf,str2,BFSZ-1);
	return str2;
}
char *lib_strip_angle(char *email, char *tmp)
{
	char *s;
	if (*email=='<') email++;
	ncpy(tmp,email,200);
	s = strchr(tmp,'>');
	if (s!=NULL) *s = 0;
	return tmp;
}
char *lib_strip_square(char *email, char *tmp)
{
	char *s;
	if (*email=='[') email++;
	ncpy(tmp,email,200);
	s = strchr(tmp,']');
	if (s!=NULL) *s = 0;
	return tmp;
}


int lib_mkdir_x(char *s, int mode, char *src, int line)
{
	int r;
	int silent=FALSE;
   if (strncmp(s,"\\\\",2)==0) silent = TRUE;

#ifdef WIN32
	r = mkdir(s);
#else
	r = mkdir(s,mode);
#endif
	/* Did it fail but still achieve what we wanted */
#ifdef __BORLANDC__
	if (r == -1 && errno == EACCES) {
#else
	if (r == -1 && errno == EEXIST) {
#endif
		struct stat finf;

		if (my_stat(s,&finf) == 0 && finf.st_mode & S_IFDIR)
			r = 0;
	}
	/* Report failure */
	if (r == -1 && vimsg) {
		if (!silent) {
			vimsg("Failed to create dir %s:%d '%s', %s\n",src,line,s,strerror(errno));
		}
	}
	return r;
}
int lib_mkdir_all(char *s, int mode)
/*
Create's directory structure for given file.
*/
{
   char bf[BFSZ];
   int r;

   ncpy(bf,s,BFSZ-1);
   if (lib_strlen(s)<2) return FALSE;
   if (bf[1] == ':') s = bf+3;
   else s = bf+2;
   for (;*s!=0; s++)  {
      if ((*s=='/') || (*s == '\\'))  {
         *s = 0;
         r = lib_mkdir(bf,mode);
         *s = '/';
      }
   }
   r = lib_mkdir(bf,mode);
   return r != -1;
}
int lib_create_path(char *s, int mode)
/*
Create's directory structure for given file, not including file itself.
*/
{
   char bf[BFSZ];
   int r=0;

   ncpy(bf,s,BFSZ-1);
   if (lib_strlen(s)<2) return FALSE;
   if (bf[1] == ':') s = bf+3;
   else s = bf+2;
   for (;*s!=0; s++)  {
      if ((*s=='/') || (*s == '\\'))  {
         *s = 0;
         r = lib_mkdir(bf,mode);
         *s = '/';
      }
   }
   return r != -1;
}
int lib_create_dir_x(char *s, int mode, int(*cb)(void *,char *), void *p, char *src, int line)
/*
Create's directory structure for given directory.
*/

{
   char bf[BFSZ];
   int r;
   char *orig=s;

   ncpy(bf,s,BFSZ-1);
   if (lib_strlen(s)<2) return FALSE;
   if (bf[1] == ':') s = bf+3;
   else s = bf+2;
   for (;*s!=0; s++)  {
      if ((*s=='/') || (*s == '\\'))  {
		  int skip=FALSE;
         *s = 0;
		 if (strncmp(bf,"\\\\",2)==0) {
			if (strchr(bf+2,'\\')==NULL)
			if (strchr(bf+2,'/')==NULL) skip = TRUE;
		 }
		 r = 0;

		 if (!skip) {
			 r = lib_mkdir_x(bf,mode,src,line);
			 if (r==0){
//				 if (vimsg) vimsg("lib_create_dir: Created (%s) successfully mode (%o)\n",bf,mode);
		 	 }
			 if (r == 0 && cb){
				 cb(p,bf);
			 }else{
//  chrisp removed infinite loop that can occur if this is called during emsg_dox() function...
//				 if (errno!=EEXIST) if(vimsg) vimsg("lib_create_dir: Failure for (%s) was (%s) %s\n",bf,orig,strerror(errno));
		 	 }
		 }
         *s = '/';
      }
   }
   return r != -1;
}

int lib_create_missing_x(char *s, int mode, int(*cb)(void *,char *), void *p, char *src, int line)
{
	struct stat finf;
	char bf[BFSZ];
	int skip;
	int r;

	ncpy(bf,s,BFSZ-1);

	if (lib_strlen(s)<2) return FALSE;

	if (bf[1] == ':') s = bf+3;
	else s = bf+2;

	for ( ; *s; s++)  {
		if (!issep(*s)) continue;
		skip = FALSE;

		*s = 0;
		if (strncmp(bf,"\\\\",2)==0) {
			if (!strchr(bf+2,'\\') && !strchr(bf+2,'/'))
				skip = TRUE;
		}

		r = 0;

		if (my_stat(bf,&finf) == 0 && finf.st_mode & S_IFDIR)
			skip = TRUE;

		if (!skip) {
			r = lib_mkdir_x(bf,mode,src,line);
			if (r == 0 && cb) cb(p,bf);
		}
		*s = SEP;
	}

	return r != -1;
}

int lib_fix_mode(char *s, int mode, int lastn) // This only fixes the 'last' path in the list
{
	// This function should work with any value for lastn, but in truth it works for 1 and 2 :-)
   char bf[BFSZ];
   int r;
   char last[BFSZ];
   char last2[BFSZ];
   strcpy(last,"");
   strcpy(last2,"");

   // fix the unix mode on all the directories
   ncpy(bf,s,BFSZ-1);
   if (lib_strlen(s)<2) return FALSE;
   if (bf[1] == ':') s = bf+3;
   else s = bf+2;
   for (;*s!=0; s++)  {
      if ((*s=='/') || (*s == '\\'))  {
		  int skip=FALSE;
         *s = 0;
		 if (strncmp(bf,"\\\\",2)==0) {
			if (strchr(bf+2,'\\')==NULL)
			if (strchr(bf+2,'/')==NULL) skip = TRUE;
		 }
		 r = 0;
		 if (!skip) {
			 ncpy(last2,last,BFSZ-1);
			 ncpy(last,bf,BFSZ-1);
		 }
         *s = '/';
      }
   }
	if (lastn==2) if (lib_strlen(last2)>0) r = lib_chmod(last2,mode);
   if (lib_strlen(last)>0) r = lib_chmod(last,mode);
   return r != -1;
}

int lib_var_replace(char *line, char *out, int outlen, char *var, char *val)
{
	char *s;
	for (s=line;*s!=0;s++) {
		if (strncmp(s,var,lib_strlen(var))==0) {
			outlen -= lib_strlen(var);
			if (outlen<=0) break;
			strcpy(out,val);
			out += lib_strlen(out);
			s += lib_strlen(var)-1;
			continue;
		}
		outlen--;
		if (outlen<1) break;
		*out++ = *s;
	}
	*out++ = 0;
	return TRUE;
}
#ifdef unix
char *strupr(char *s)
{
	return mystrupr(s);
}
char *strlwr(char *s)
{
	return mystrlwr(s);
}
#endif
int lib_is_dir(char *fname)
{
	struct stat bf;

	if (my_stat(fname,&bf)==-1) {
		return FALSE;
	}
	if (bf.st_mode & S_IFDIR) return TRUE;
	return FALSE;
}

int lib_is_file(char *fname)
{
	struct stat bf;

	if (my_stat(fname,&bf)==-1) {
		return FALSE;
	}
	if (bf.st_mode & S_IFREG) return TRUE;
	return FALSE;
}

int lib_is_symlink(char *fname)
{
	struct stat bf;

	if (my_stat(fname,&bf)==-1) {
		return FALSE;
	}
#ifdef unix
	if (S_ISLNK(bf.st_mode)) return TRUE;
#endif
	return FALSE;
}
int lib_chdir(char *s) // setcwd set_cwd
{
#ifdef WIN32
/*	SetCurrentDirectory(s); */
	return chdir(s);
#else
	return chdir(s);
#endif
}
char *lib_getcwd(char *tmp)
{
	strcpy(tmp,"none");
#ifdef WIN32
	GetCurrentDirectoryA(100,(XLPWSTR) tmp);
#else
	getcwd(tmp,100);
#endif
	return tmp;
}
/* Match wild code must be last */
/* Match wild code must be last */
/* Match wild code must be last */
/* Match wild code must be last */

#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif

#define TRUE			1
#define FALSE			0
#define ABORT			-1
#define NEGATE_CLASS		'^'


int domatch_ex(char *text, char *p, int nocase,  char **found, int imap)
{
    register int	last;
    register int	matched;
    register int	reverse;

	if (!imap) if (found) *found = text; // may not be true of course.
    for ( ; *p; text++, p++) {
		if (*text == '\0' && *p != '*')
			return ABORT;
		switch (*p) {
		case '\\':
			/* Literal match with following character. */
			p++;
			/* FALLTHROUGH */
		default:
normal:
			if (nocase ? charmap[*text] != charmap[*p] : *text != *p)
				return FALSE;
			/*if (*text != *p)
				return FALSE;*/
			continue;
		case '?': /* Match anything. */
			continue;
		case '%': // imap only
			if (imap) {
				while (*++p == '%') /* Consecutive stars act just like one. */
					continue;

				while (*text) {
					text++;
					if ((matched = domatch_ex(text, p, nocase,NULL,imap)) != FALSE) {
						if (!imap) if (found) *found = text-1;
						return matched;
					}
					if (*text=='/') goto cont;

				}
				return ABORT;
cont:
				continue;
			} else goto normal;
		case '*':
			while (*++p == '*') /* Consecutive stars act just like one. */
				continue;

			if (*p == '\0') {/* Trailing star matches everything. */
				if (imap) {
					if (found) *found = text; // tell it where we found the last *
				}
				return TRUE;
			}

			while (*text) {
				if ((matched = domatch_ex(text++, p, nocase,NULL,imap)) != FALSE) {
					if (!imap) if (found) *found = text-1;
					return matched;
				}
			}

			return ABORT;

			/* RMH [] not modified to be case insensitive,
			       does not really make sense? */
		case '[':
			reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
			if (reverse) /* Inverted character class. */
				p++;

			matched = FALSE;

			if ((p[1] == ']' || p[1] == '-') && *++p == *text)
				matched = TRUE;

			for (last = *p; *++p && *p != ']'; last = *p) {
				if (*p == '-' && p[1] != ']'
					? *text <= *++p && *text >= last : *text == *p)
					matched = TRUE;
			}

			if (matched == reverse)
				return FALSE;
			continue;
		}
    }
    return *text == '\0';
}


int domatch_ex_imap(char *text, char *p, int nocase,  char **found, int imap)
{
    register int	last;
    register int	matched;
    register int	reverse;

	if (!imap) if (found) *found = text; // may not be true of course.
    for ( ; *p; text++, p++) {
		if (*text == '\0' && *p != '*')
			return ABORT;
		switch (*p) {
		case '\\':
			/* Literal match with following character. */
			p++;
			/* FALLTHROUGH */
		default:
normal:
			if (nocase ? charmap[*text] != charmap[*p] : *text != *p)
				return FALSE;
			/*if (*text != *p)
				return FALSE;*/
			continue;
		case '?': /* Match anything. */
			continue;
		case '%': // imap only
			if (imap) {
				while (*++p == '%') /* Consecutive stars act just like one. */
					continue;

				while (*text) {
					text++;
					if ((matched = domatch_ex_imap(text, p, nocase,found,imap)) != FALSE) {
						if (!imap) if (found) *found = text-1;
						return matched;
					}
					if (*text=='/') goto cont;

				}
				return ABORT;
cont:
				continue;
			} else goto normal;
		case '*':
			while (*++p == '*') /* Consecutive stars act just like one. */
				continue;

			if (*p == '\0') {/* Trailing star matches everything. */
				if (imap) {
					if (found) *found = text; // tell it where we found the last *
				}
				return TRUE;
			}

			while (*text) {
				if ((matched = domatch_ex_imap(text++, p, nocase,found,imap)) != FALSE) {
					if (!imap) if (found) *found = text-1;
					return matched;
				}
			}

			return ABORT;

			/* RMH [] not modified to be case insensitive,
			       does not really make sense? */
		case '[':
			reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
			if (reverse) /* Inverted character class. */
				p++;

			matched = FALSE;

			if ((p[1] == ']' || p[1] == '-') && *++p == *text)
				matched = TRUE;

			for (last = *p; *++p && *p != ']'; last = *p) {
				if (*p == '-' && p[1] != ']'
					? *text <= *++p && *text >= last : *text == *p)
					matched = TRUE;
			}

			if (matched == reverse)
				return FALSE;
			continue;
		}
    }
    return *text == '\0';
}

int match_wild_ipnum(char *xiprange, char *xipnum)
{
	char *pr[200];
	char *pn[200];
	int nr,nn,n;
	int r1,r2;
	int i;
	char *m;
	char iprange[BFSZ];
	char ipnum[BFSZ];
	ncpy(iprange,xiprange,BFSZ-1);
	ncpy(ipnum,xipnum,BFSZ-1);
	nr = token_split(iprange,pr,".",100);
	nn = token_split(ipnum,pn,".",100);
	if (nr!=nn) return FALSE;
	if (nr!=4) return FALSE;
	for (i=0; i<4; i++) {
		m = strchr(pr[i],'-');
		if (m!=NULL) {
			r1 = atoi(pr[i]);
			r2 = atoi(m+1);
			n = atoi(pn[i]);
			if (n<r1) return FALSE;
			if (n>r2) return FALSE;
		} else if (!match_wild(pr[i],pn[i])) return FALSE;
	}
	return TRUE;
}
int match_aton(char *ip)
{
	unsigned int x = 0;
	char *s;
	x = atoi(ip)<<24;
	s = strchr(ip,'.');	if (s!=NULL) s++;	if (s==NULL) return 0;
	x += atoi(s)<<16;
	s = strchr(s,'.');	if (s!=NULL) s++;	if (s==NULL) return 0;
	x += atoi(s)<<8;
	s = strchr(s,'.');	if (s!=NULL) s++;	if (s==NULL) return 0;
	x += atoi(s);
	return x;
}


/* luhn.c - Luhn algorithm test program */


int luhn_check_number(char *number)
{
    int i, sum, ch, num, twoup, len;

    len = (int)strlen(number);
    sum = 0;
    twoup = 0;
    for (i = len - 1; i >= 0; --i) {
        ch = number[i];
        num = (ch >= '0' && ch <= '9') ? ch - '0' : 0;
        if (twoup) {
            num += num;
            if (num > 9) num = (num % 10) + 1;
        }
        sum += num;
        twoup = ++twoup & 1;
    }
    sum = 10 - (sum % 10);
    if (sum == 10) sum = 0;
    return (sum == 0) ? 1 : 0;
}


int lib_lastchar(char *s, char c)
{
	if (lib_strlen(s)==0) return FALSE;
	if (s[lib_strlen(s)-1] == c) return TRUE;
	return FALSE;
}
int match_cidr(char *ip, char *net, int cidr)
{
	unsigned int a,b;
	unsigned int mask;

	if (!lib_isip(ip)) return FALSE; // cannot match if not ip number...
	if (cidr==0) return TRUE;
	a = match_aton(ip);
	b = match_aton(net);
	mask = 0xffffffff << (32-cidr);
	if ((a&mask) == (b&mask)) {
		//printf("cidr did matched %x %x   %x %x cidr=%d\n",a,b,a&mask,b&mask,cidr);
		return TRUE;
	}
	//printf("cidr nomatch %x %x   %x %x cidr=%d\n",a,b,a&mask,b&mask,cidr);
	return FALSE;
}

int match_cidr_strings(char *wild, char *ip)
{
	char *s;
	char net[BFSZ];
	ncpy(net,wild,BFSZ-1);
	s = strchr(net,'/'); 	if (s!=NULL) *s = 0;
	if (s==NULL) s = "/32";
	return match_cidr(ip,net,atoi(s+1));
}
//int tcp_cidr_ipv6(char *address, char *cidr)

static int (*vtcp_cidr_ipv6)(char *, char *);
void lib_set_cidr(int (*fn)(char *, char *))
{
	vtcp_cidr_ipv6 = fn;
}


int match_wild_or_ip(char *wild, char *text)
{
	char *s;
	char tmp[BFSZ];


	if (*text==':' || *text=='[') { // Cope with ipv6 on linux
		s = strstrnc(text,"::ffff:");
		if (s!=NULL) {
			ncpy(tmp,s+7,BFSZ-1);
			s = strchr(tmp,']');
			if (s!=NULL) *s = 0;
			if (match_wild(wild,tmp)) return TRUE;
			if (strchr(tmp,'/')!=NULL) if (match_cidr_strings(wild,tmp)) return TRUE; //added to allow cidr for old ipv4 addresses...
		}
	}
	s = strchr(wild,'/');
	if (s!=NULL && strchr(wild,':')!=NULL) {
		int r;
		if (vtcp_cidr_ipv6) {
			r = vtcp_cidr_ipv6(text,wild);
			if (r) return TRUE;
		}
		return match_wild(wild,text);
	}

	if (!sisdigit(wild[0])) return match_wild(wild,text);

	s = strchr(wild,'/');
	if (s!=NULL) if (strchr(wild,':')==NULL) {
		return match_cidr_strings(wild,text);
	}
	s = strchr(wild,'-');
	if (s==NULL) return match_wild(wild,text);
	if (match_wild_ipnum(wild,text)) return TRUE;
	return match_wild(wild,text);
}

int match_wild(char *wild,char *text)
{
    if (wild[0] == '*' && wild[1] == '\0') return TRUE;
    return domatch_ex(text, wild, FALSE,NULL,FALSE) == TRUE;
}
int match_wild_imap(char *wild,char *text, char **found)
{
    if (wild[0] == '*' && wild[1] == '\0') return TRUE;
    return domatch_ex_imap(text, wild, FALSE,found,TRUE) == TRUE;
}

int lib_split_and_match_wild(char *wild_list, char *text)
{
	char bf[BFSZ],*p[100];
	int n;
	if (wild_list[0] == '*' && wild_list[1] == '\0') return TRUE;
	if (wild_list[0] == '\0') return TRUE;
	ncpy(bf,wild_list,BFSZ-1);
	n = token_split(bf,p," ,",99);
	for(n--; n >= 0; n--) {
		if (match_wild(p[n],text)) return TRUE;
	}
	return FALSE;
}
int lib_split_and_match_wild_long(char *wild_list, char *text)
{
	char *p[100];
	int n;
	char *bf;
	if (wild_list[0] == '*' && wild_list[1] == '\0') return TRUE;
	if (wild_list[0] == '\0') return TRUE;
	bf = strdup(wild_list);
//	ncpy(bf,wild_list,BFSZ-1);
	n = token_split(bf,p," ,",99);
	for(n--; n >= 0; n--) {
		if (match_wild(p[n],text)) {
			free(bf);
			return TRUE;
		}
	}
	free(bf);
	return FALSE;
}
int lib_split_comma_match_wild(char *wild_list, char *text)
{
	char bf[BFSZ],*p[100];
	int n;
	if (wild_list[0] == '*' && wild_list[1] == '\0') return TRUE;
	if (wild_list[0] == '\0') return TRUE;
	ncpy(bf,wild_list,BFSZ-1);
	n = token_split(bf,p,",",99);
	for(n--; n >= 0; n--) {
		char nospace[BFSZ];
		lib_trim_spaces(p[n],nospace);
		if (match_wild(nospace,text)) return TRUE;
	}
	return FALSE;
}

int match_wildnc(char *wild,char *text)
{
    if (wild[0] == '*' && wild[1] == '\0') return TRUE;
    return domatch_ex(text, wild, TRUE,NULL,FALSE) == TRUE;
}
int match_wildnc_at(char *wild,char *text, char **found)
{
    if (wild[0] == '*' && wild[1] == '\0') return TRUE;
    return domatch_ex(text, wild, TRUE,found,FALSE) == TRUE;
}

int split_email(char *email, char *fullname, char *address, int bfsz) {
	char bf[BFSZ];
	char *pp[20];
	char *p;
	int i,np;

	*fullname = *address = 0;

	ncpy(bf,email,BFSZ-1);
	np = quoted_token_split(bf,pp,"<> \t\r\n",20);
	for(i = 0; i < np; i++) {
		if (pp[i][0] == '\"') {
			ncpy(fullname,&pp[i][1],bfsz-1);
			p = &fullname[lib_strlen(fullname)-1];
			if (*p == '\"') *p = 0;
		}
		if (strchr(pp[i],'@')) {
			ncpy(address,pp[i],bfsz-1);
		}
	}

	if (!*fullname) {
		for(i = 0; i < np; i++) {
			if (!strchr(pp[i],'@')) {
				if (*fullname) ncat(fullname," ",bfsz-1);
				ncat(fullname,pp[i],bfsz-1);
			}
		}
	}

	return TRUE;
}
int lib_match_email(char *em1, char *em2)
{
	char bf1[BFSZ],bf2[BFSZ];
	if (strcmpnc(lib_fix_email(em1,bf1),lib_fix_email(em2,bf2))==0) {
		return TRUE;
	}
	return FALSE;
}
int match_email(char *match,char *email)
{
	return match_email_real(match,email,FALSE);
}
int match_email_real(char *match,char *email,int exact)
{
	char fullname[BFSZ];
	char address[BFSZ];
	int isw=FALSE;

	if (exact) isw = FALSE;
	else {
		if (match[0] == '*' && match[1] == '\0') return TRUE;
		if (strchr(match,'*')) isw = TRUE;
	}

	if (exact) {    if (strcmp(email,match)==0) return TRUE; }
	else if (isw) { if (domatch_ex(email,match,FALSE,NULL,FALSE) == TRUE) return TRUE; }
	else {          if (strstr(email,match)) return TRUE; }

	split_email(email,fullname,address,BFSZ);

	if (exact) {    if (strcmp(fullname,match)==0) return TRUE; }
	else if (isw) { if (domatch_ex(fullname,match,FALSE,NULL,FALSE) == TRUE) return TRUE; }
	else {          if (strstr(fullname,match)) return TRUE; }

	if (exact) {    if (strcmp(address,match)==0) return TRUE; }
	else if (isw) { if (domatch_ex(address,match,FALSE,NULL,FALSE) == TRUE) return TRUE; }
	else {          if (strstr(address,match)) return TRUE; }

	return FALSE;
}

int match_emailnc(char *match,char *email)
{
	return match_emailnc_real(match,email,FALSE);
}
int match_emailnc_real(char *match,char *email,int exact)
{
	char fullname[BFSZ];
	char address[BFSZ];
	char *s;
	int isw;

	if (exact) isw = FALSE;
	else {
		if (match[0] == '*' && match[1] == '\0') return TRUE;
		else if ((s = strchr(match,'*'))) {
			if (s[1] == '*') isw = FALSE;
			else isw = TRUE;
		}
		else if (strchr(match,'?')) isw = TRUE;
		else isw = FALSE;
	}

	if (exact) {    if (strcmpnc(email,match)==0) return TRUE; }
	else if (isw) { if (domatch_ex(email,match,TRUE,NULL,FALSE) == TRUE) return TRUE; }
	else {          if (strstrnc(email,match)) return TRUE; }

	split_email(email,fullname,address,BFSZ);

	if (exact) {    if (strcmpnc(fullname,match)==0) return TRUE; }
	else if (isw) { if (domatch_ex(fullname,match,TRUE,NULL,FALSE) == TRUE) return TRUE; }
	else {          if (strstrnc(fullname,match)) return TRUE; }

	if (exact) {    if (strcmpnc(address,match)==0) return TRUE; }
	else if (isw) { if (domatch_ex(address,match,TRUE,NULL,FALSE) == TRUE) return TRUE; }
	else {          if (strstrnc(address,match)) return TRUE; }

	return FALSE;
}

//FWD: something
//RE: something
//RE[x]: something
//FW: something
//SV: something
//[something]
//(fwd) something
//Spam:****, something
//
int strip_subj(char *subj, char *result, int bfsz) {
	char *p,*s;
	int i;

	i = 0;
	for(p = subj; *p && i < bfsz-1; ) {
		if (strncmpnc(p,"re:",3)==0) p += 3;
		else if (strncmpnc(p,"sv:",3)==0) p += 3;
		else if (strncmpnc(p,"fw:",3)==0) p += 3;
		else if (strncmpnc(p,"fwd:",4)==0) p += 4;
		else if (strncmpnc(p,"(fwd)",5)==0) p += 5;
		else if (strncmpnc(p,"re[",3)==0) {
			for(s = &p[3]; *s && *s != ']'; s++);
			if (*s == ']') p = s+1;
			else result[i++] = *p++;
		}
		else {
			result[i++] = *p++;
		}
	}
	result[i] = 0;
	return i;
}

int match_subjnc(char *match, char *subj) {
	return match_subjnc_real(match,subj,FALSE);
}
int match_subjnc_real(char *match, char *subj, int exact) {
	char bf[BFSZ];

	strip_subj(subj,bf,BFSZ);
	if (exact) return strcmpnc(subj,match)==0;
	if (strchr(match,'*') || strchr(match,'?'))
		return match_wild(match,bf);
	return strstrnc(bf,match)!=NULL;
}

/* Match wild code must be last */

/* ftp://ftp.entwinsit.com/pub/dnews/xxx.exe */
int lib_url_split_host(char *url, char *host, char *fname)
{
	char bf[BFSZ];
	char *s;
	char *rest;
	//*port = 80;
	strcpy(fname,"");
	strcpy(host,"");
	ncpy(bf,url,BFSZ-1); url = bf;
	//if (strstrnc(url,"https:")!=NULL) *port = 443;
	s = strstr(url,"//");
	if (s!=NULL) {
		rest = s+2;
		ncpy(host,rest,BFSZ-1);
		s = strchr(host,'/');
		if (s==NULL) {
			s = strchr(host,'?');
			if (s!=NULL) {
				ncpy(fname,s,BFSZ-1);
				*s = 0;
			}
		} else {
			ncpy(fname,s,BFSZ-1);
			*s = 0;
		}
	} else {
		ncpy(host,url,BFSZ-1);
		s = strchr(host,'/');
		if (s!=NULL) *s = 0;

		s = strchr(url,'/');
		if (s==NULL) return TRUE;
		ncpy(fname,s,BFSZ-1);
	}
/*	s = strchr(host,':');
	if (s!=NULL) {
		*s = 0;
		*port = atoi(s+1);
	}
	*/
	return TRUE;
}
char *lib_url_enable(char *xin, char *tmp)
{
	char *s;
	char was;
	char *e;
	char in[BFSZ];
	char *out=tmp;
	ncpy(in,xin,BFSZ-1);
	for (s=in;*s!=0;s++) {
		if (out-tmp>BFSZ-100) break;
		if (strncmp(s,"http:",5)==0 || strncmp(s,"https:",6)==0) {
			for (e=s;*e!=0;e++) {
				if (*e==',') break;
				if (*e==' ') break;
			}
			was = *e;
			*e = 0;
			*out = 0;
			lprintf(out,BFSZ-1-lib_strlen(tmp),"<a href=\"%s\">%s</a>",s,s);
			*e = was;
			out += lib_strlen(out);
			s = e-1;
		} else {
			*out++ = *s;
		}
	}
	*out++ = 0;
	return tmp;
}

//		http://host:port/path/file
//		//host:port/path/file
//		/path/file
//		path/file
int lib_url_split_more(char *url, char *host, char *path, char *fname)
{
	char bf[BFSZ];
	char *s;
	char *last;
	strcpy(fname,"");
	strcpy(host,"");
	strcpy(path,"");

	if (strncmpnc(url,"http:",5)==0) {
		ncpy(bf,url+5,BFSZ-1);
	} else ncpy(bf,url,BFSZ-1);

	if (strncmp(bf,"//",2)==0) {
		ncpy(host,bf+2,BFSZ-1);
		s = strchr(host,'/');
		if (s!=NULL) {
			ncpy(bf,s,BFSZ-1);
			*s = 0;
		} else {
			ncpy(bf,"",BFSZ-1);
		}
	}
	if (*bf=='/') {
		ncpy(path,bf,BFSZ-1);
		last = NULL;
		for (s=path;*s!=0;s++) if (*s=='/') last = s;
		if (last!=NULL) {
			*last = 0;
			ncpy(fname,last+1,BFSZ-1);
		} else {
			ncpy(fname,path,BFSZ-1);
			ncpy(path,"",BFSZ-1);
		}
	} else {
		ncpy(fname,bf,BFSZ-1);
	}
	if (lib_strlen(path)>0) ncat(path,"/",BFSZ-1);
//	printf("path (%s) fname (%s)\n",path,fname);
	return TRUE;
}
int lib_url_split(char *url, char *protocol, char *host, char *xdir, char *fname)
{
	char bf[BFSZ];
	char *s;
	char *rest;
	char *last;
	strcpy(fname,"");
	strcpy(xdir,"");
	ncpy(bf,url,BFSZ-1); url = bf;
	s = strstr(url,"//");
	if (s==NULL) return FALSE;
	*s = 0;
	strcpy(protocol,bf);
	rest = s+2;
	s = strchr(rest,'/');
	if (s!=NULL) {
		*s = 0;
		strcpy(host,rest);
		rest = ++s;
		/* Now find last slash in line */
		last = s-1;
		for (s=rest;*s!=0;s++) if (*s=='/') last = s;
		if (last<rest) strcpy(xdir,"");
		else {
			*last = 0;
			strcpy(xdir,rest);
		}
		rest = last+1;
		strcpy(fname,rest);
	} else {
		strcpy(host,rest);
		s = strchr(host,'?');
		if (s==NULL) return FALSE;
		*s = 0;
		rest = ++s;
		/* Now find last slash in line */
		last = s-1;
		for (s=rest;*s!=0;s++) if (*s=='/') last = s;
		if (last<rest) strcpy(xdir,"");
		else {
			*last = 0;
			strcpy(xdir,rest);
		}
		rest = last+1;
		strcpy(fname,rest);
	}
	return TRUE;
}
// http[s]://host.name:port/rest...
int lib_url_split2(char *url, char *protocol, char *host, char *port, char *rest)
{
	char *s;
	strcpy(port,""); strcpy(rest,""); strcpy(host,"");
	ncpy(protocol,url,BFSZ-1);
	s = strstr(protocol,"//");
	if (s==NULL) return FALSE;
	*s = 0;
	ncpy(host,s+2,BFSZ-1);

	s = strstr(host,"/");
	if (s!=NULL) {
		ncpy(rest,s,BFSZ-1);
		*s = 0;
	}

	s = strstr(host,":");
	if (s!=NULL) {
		ncpy(port,s,BFSZ-1);
		*s = 0;
	}
	return TRUE;
}
/*
Chrisp just modified lib_path_split so it would work correctly with
mixed file names, e.g.
	   xxx/aaa/bb\ddd

*/
int lib_path_split(char *in, char *path, char *fname)
{
	char bf[LIB_BFSZ];
	char *s;
	char *last=NULL;
	ncpy(bf,in,LIB_BFSZ-1); in = bf;


	for (s=bf;*s!=0;s++) if (*s=='/' || *s=='\\') last = s;
	//if (last==NULL) for (s=bf;*s!=0;s++) if (*s=='\\') last = s;
	if (last==NULL) {
		strcpy(path,"");
		strcpy(fname,bf);
		return TRUE;
	}
	*last = 0;
	strcpy(path,bf);
	strcat(path,"/");
	strcpy(fname,last+1);
	return TRUE;
}
/* This version doesn't put trailing slashes on path */
int lib_path_split2(char *in, char *path, char *fname)
{
	char bf[LIB_BFSZ];
	char *s;
	char *last=NULL;
	ncpy(bf,in,LIB_BFSZ-1); in = bf;


	for (s=bf;*s!=0;s++) if (*s=='/'  || *s=='\\') last = s;
	//if (last==NULL) for (s=bf;*s!=0;s++) if (*s=='\\') last = s;
	if (last==NULL) {
		strcpy(path,"");
		strcpy(fname,bf);
		return TRUE;
	}
	*last = 0;
	strcpy(path,bf);
	strcpy(fname,last+1);
	return TRUE;
}
int lib_fullpath(char *in)
{
#ifdef WIN32
	if (in[1]==':') return TRUE;
#endif
	return (issep(in[0]));
}

int lib_path_trailing(char *in, char *dir)
{
	char bf[LIB_BFSZ];
	char *s;
	char *last=NULL;
	ncpy(bf,in,LIB_BFSZ-1);

	if (bf[0]=='\0'){
		strcpy(dir,"");
		return TRUE;
	}
	for (s=bf+lib_strlen(bf)-1; s>=bf&&issep(*s); s--)
		*s='\0';

	for (;s>=bf&&!issep(*s);s--);
	strcpy(dir,s+1);
	return TRUE;
}

char *lib_path_pfromend(char *path, int parts)
{
	char *s;
	int len;
	if(path=='\0') return path;
	len=lib_strlen(path);
	if(len==0 || parts==0) return path;
	for (s=path+len-1;s>path;s--){
		if (*s!='/' && *s!='\\') continue;
		parts--;
		if(parts>0) continue;
		break;
	}
	return s;
}


int lib_encrypt(char *bf, int len, char *key)
{
	unsigned char *s = (unsigned char *) bf;
	int i;
	srand(lib_str_hash_old(key,100000));
	for (i=0; i<len; i++) {
		s[i] = (s[i] - rand())%256;
	}
	return TRUE;
}
int lib_decrypt(char *bf, int len, char *key)
{
	int i;
	unsigned char *s = (unsigned char *) bf;
	srand(lib_str_hash_old(key,100000));
	for (i=0; i<len; i++) {
		s[i] = (s[i] + rand())%256;
	}
	return TRUE;
}
char *lib_crypt_fake(char *key, char *salt)
{
	/* One way encryption of key using salt to perterb */
	/* This function is only intended to make stealing passwords difficult, the password file should still be protected */
	static char result[BFSZ];
	int i;

	int start = salt[0] + salt[1]*255;
	result[0] = salt[0];
	result[1] = salt[1];
	for (i=0; i<(int) lib_strlen(key); i++) {
		result[i+2] = ((key[i] * (start+1)) % 40) + 'A';
		start += key[i];
		start *= 3;
		start = start % 32000;
	}
	result[lib_strlen(key)+2] = 0;
	return result;
}

#ifdef WIN32
char *lib_crypt(char *key, char *salt)
{
	/* One way encryption of key using salt to perterb */
	/* This function is only intended to make stealing passwords difficult, the password file should still be protected */
	static char result[BFSZ];
	int i;

	int start = salt[0] + salt[1]*255;
	result[0] = salt[0];
	result[1] = salt[1];
	for (i=0; i<(int) lib_strlen(key); i++) {
		result[i+2] = ((key[i] * (start+1)) % 40) + 'A';
		start += key[i];
		start *= 3;
		start = start % 32000;
	}
	result[lib_strlen(key)+2] = 0;
	return result;
}
#else

char *crypt(const char *key, const char *salt);
char *lib_crypt(char *key, char *salt)
{
	char *s = crypt(key,salt);
	if (s==NULL) return lib_crypt_fake(key,salt); // just to make sure we return something that won't match by accident.
	return s;
}
#endif
char *lib_guess_content(char *fname)
{
	char bf[BFSZ];
	ncpy(bf,fname,100);
	strlwr(bf);
	if (strstr(bf,".jpg")!=NULL) return "image/JPEG";
	if (strstr(bf,".jpeg")!=NULL) return "image/JPEG";
	if (strstr(bf,".gif")!=NULL) return "image/GIF";
    if (strstr(bf,".png")!=NULL) return "image/PNG";
    if (strstr(bf,".svg")!=NULL) return "image/svg+xml";
	if (strstr(bf,".htm")!=NULL) return "text/html;";
	if (strstr(bf,".sgml")!=NULL) return "text/sgml";
	if (strstr(bf,"rss.xml")!=NULL) return "application/rss+xml";
	if (strstr(bf,".xml")!=NULL) return "application/xml";
	if (strstr(bf,".css")!=NULL) return "text/css";
	if (strstr(bf,".txt")!=NULL) return "text/plain";
	if (strstr(bf,".js")!=NULL) return "text/javascript";
	if (strstr(bf,".wml")!=NULL) return "text/vnd.wap.wml";
	if (strstr(bf,".wap")!=NULL) return "text/vnd.wap.wml";
	if (strstr(bf,".exe")!=NULL) return "application/octet-stream";
	if (strstr(bf,".doc")!=NULL) return "application/octet-stream";
	if (strstr(bf,".xls")!=NULL) return "application/octet-stream";
	if (strstr(bf,".com")!=NULL) return "application/octet-stream";
	if (strstr(bf,".zip")!=NULL) return "application/x-zip-compressed";
	if (strstr(bf,".jnlp")!=NULL) return "application/x-java-jnlp-file";
	if (strstr(bf,".jar")!=NULL) return "application/java-archive";
	if (strstr(bf,".torrent")!=NULL) return "application/x-bittorrent";
	if (strstr(bf,".tor")!=NULL) return "application/x-bittorrent";
	if (strstr(bf,".eml")!=NULL) return "message/rfc822";
	if (strstr(bf,".vcf")!=NULL) return "text/x-vcard";
	if (strstr(bf,".csv")!=NULL) return "text/csv";
	if (strstr(bf,".swf")!=NULL) return "application/x-shockwave-flash";
	if (strstr(bf,".pdf")!=NULL) return "application/pdf";
	if (strstrnc(bf,".mobileconfig")!=NULL) return "application/x-apple-aspen-config";
	if (strstr(bf,".ics")!=NULL) return "application/ics";
	if (strstr(bf,".mp4")!=NULL) return "video/mp4";

	return "application/octet-stream";
}
char *lib_guess_suffix(char *fname)
{
	char bf[BFSZ];
	ncpy(bf,fname,100);
	strlwr(bf);
	if (strstr(bf,"application/pdf")!=NULL) return ".pdf";
	if (strstr(bf,"image/JPEG")!=NULL) return ".jpg";
	if (strstr(bf,"image/GIF")!=NULL) return ".gif";
    if (strstr(bf,"image/PNG")!=NULL) return ".png";
	if (strstr(bf,"text")!=NULL) return ".htm";
	if (strstr(bf,"application/x-zip-compressed")!=NULL) return ".zip";

	return ".unknown";
}


char *strip_eol(char *s)
{
	char *ss=s;
	for (;*s!=0;s++) {
		if (*s=='\n' || *s=='\r') {
			*s = 0;
			break;
		}
	}
	return ss;
}
int image_too_big(int x, int y)
{
	if ((x>3600) && (y>3600)) { // Decreased from 5000 in previous version... people should not send huge images!
		if (vemsg) vemsg("Warning: large image processing (%d x %d)\n",x,y); // changed in 5.0j-2, prior to that it was 5000
																			 // upped from 3000 to 3500 in 6.0b8 as was catching too many
		return TRUE;
	}
	return FALSE;
}
int image_too_bigfile(char *fname, int big_jpg)
{
	char bf[BFSZ], *ext;
	int sz = lib_file_size(fname);
	//vimsg("image_too_bigfile(%s) %d\n",fname,sz);

	if (sz>700000) if (vemsg) vemsg("Warning large image processing (%s)\n",fname);
	if (sz<4000000) return FALSE;

	if(big_jpg){
		strlwr(ncpy(bf,fname,BFSZ-1));
		ext=lib_get_extension(fname);
		if(strcmp(ext,".jpg")==0 || strcmp(ext,".jpeg")==0){
			if (vemsg) vemsg("Large JPG image allowed (%s)\n",fname);
			return FALSE;
		}
	}

	if (vemsg) vemsg("Image too large so not processed (%s)\n",fname);
	return TRUE;
}
char *lib_strip_quotes(char *s, char *tmp)
{
	if (*s!='"') return s;
	s++;
	ncpy(tmp,s,100);
	s = strchr(tmp,'"');
	if (s!=NULL) *s = 0;
	return tmp;
}
char *lib_get_quote(char *s, char *bf)
{
        if (*s=='"') {
                ncpy(bf,s+1,BFSZ-1);
                s = strchr(bf,'"');
                if (s!=NULL) *s = 0;
        } else {
                ncpy(bf,s,BFSZ-1);
                s = strchr(bf,' ');if (s!=NULL) *s = 0;
                s = strchr(bf,';');if (s!=NULL) *s = 0;
                s = strchr(bf,',');if (s!=NULL) *s = 0;
                s = strchr(bf,'\n');if (s!=NULL) *s = 0;
                s = strchr(bf,'\r');if (s!=NULL) *s = 0;
        }
        return bf;
}
char *lib_make_password(char *tmp, int len)
{
	char *start=tmp;
	while (len>0) {
		*tmp++ = '0' + abs(myrand()%10);
		len --;
	}
	*tmp = 0;
	return start;
}
char *lib_cr_decode(char *s, char *tmp, int bfsz)
{
	char *o=tmp;
	for (;*s!=0;s++) {
		if (o-tmp>bfsz-10) break;
		if (*s=='\\') {
			s++;
			if (*s=='\\') {*o++ = *s; continue;}
//			if (*s=='"') {*o++ = *s; continue;}
			if (*s=='n') {*o++ = '\r';*o++ = '\n'; continue;}
			if (*s==0) break; // Bug fix by Matt to prevent crashing on bad input.
		} else *o++ = *s;
	}
	*o = 0;
	return tmp;
}
char *lib_cr_decode2(char *s, char *tmp, int bfsz)
{
	char *o=tmp;
	for (;*s!=0;s++) {
		if (o-tmp>bfsz-10) break;
		if (*s=='\\') {
			s++;
			if (*s=='\\') {*o++ = *s; continue;}
			if (*s=='n') {*o++ = '\n'; continue;}
			if (*s==0) break; // Bug fix by Matt to prevent crashing on bad input.
		} else *o++ = *s;
	}
	*o = 0;
	return tmp;
}
char *lib_bar_decode(char *s, char *tmp, int bfsz)
{
	char *o=tmp;
	for (;*s!=0;s++) {
		if (o-tmp>bfsz-10) break;
		if (*s=='\\') {
			s++;
			if (*s=='\\') {*o++ = *s; continue;}
			if (*s=='n') {*o++ = '\r';*o++ = '\n'; continue;}
			if (*s=='b') {*o++ = '|'; continue;}
			if (*s==0) break; // Bug fix by Matt to prevent crashing on bad input.
		} else *o++ = *s;
	}
	*o = 0;
	return tmp;
}
char *lib_cr_encode(char *s, char *tmp, int bfsz)
{
	char *o=tmp;
	for (;*s!=0;s++) {
		if (o-tmp>bfsz-5) break;
		if (*s=='\r') {
			;
		} else if (*s=='\\') {
			*o++ = '\\';
			*o++ = '\\';
		} else if (*s=='\n') {
			*o++ = '\\';
			*o++ = 'n';
		} else *o++ = *s;
	}
	*o = 0;
	return tmp;
}
char *lib_bar_encode(char *s, char *tmp, int bfsz)
{
	char *o=tmp;
	for (;*s!=0;s++) {
		if (o-tmp>bfsz-5) break;
		if (*s=='\r') {
			;
		} else if (*s=='\\') {
			*o++ = '\\';
			*o++ = '\\';
		} else if (*s=='\n') {
			*o++ = '\\';
			*o++ = 'n';
		} else if (*s=='|') {
			*o++ = '\\';
			*o++ = 'b';
		} else *o++ = *s;
	}
	*o = 0;
	return tmp;
}
char *lib_bar_clean(char *s, char *tmp, int bfsz)
{
    char *o=tmp;
    for (;*s!=0;s++) {
        if (o-tmp>bfsz-5) break;
        if (*s=='|') {
            s++; // chrisp removed meaningless *
        } else *o++ = *s;
    }
    *o = 0;
    return tmp;
}

char *lib_clean_fname(char *in)
{
	char *p;
	for (p=in;*p;p++){
		if(*p=='\\' || *p=='/' || *p==':' || *p=='*' || *p=='?' || *p=='<' || *p=='>' || *p=='|' || *p=='.')
			*p='_';
	}
	return in;
}
char *lib_clean_fname2(char *in)
{
	char *p,*pout;
	for (p=pout=in;*p;p++,pout++){
		if(*p=='\\' || *p=='/' || *p==':' || *p=='*' || *p=='?' || *p=='<' || *p=='>' || *p=='|' || *p=='.' || *p==' '){
			*pout='_';
		}else{
			*pout=*p;
		}
		if(pout>in && *(pout-1)=='_' && *pout=='_') pout--;
	}
	*pout=0;
	return in;
}

char *lib_pad_right(char *src, int width, char *tmp)
{
	int rest= width - lib_strlen(src);
	strcpy(tmp,src);
	if (rest<0) return tmp;
	memset(tmp+lib_strlen(src),' ',rest);
	tmp[width] = 0;
	return tmp;

}
/* Unix emulation functions */
#ifdef unix
int GetTickCount64(void)
{
	return GetTickCount();
}
int GetTickCount(void)
{
	static int start_sec;
	struct timeval tv;
	struct timezone tz;
	gettimeofday(&tv,&tz);
	if (start_sec==0) start_sec = tv.tv_sec;
	return (tv.tv_sec - start_sec)*1000 + tv.tv_usec/1000;
}

#endif

char *unix_mode_show(int mode, int isdir, int islink, char *tmp)
{
	/* Construct mode string from number */
	char *or,*ow,*ox;
	char *gr,*gw,*gx;
	char *ar,*aw,*ax;
	char *type="-";
	or = ow = ox = "-";
	gr = gw = gx = "-";
	ar = aw = ax = "-";
	if (mode & 0400) or = "r";
	if (mode & 0200) ow = "w";
	if (mode & 0100) ox = "x";

	if (mode & 040) gr = "r";
	if (mode & 020) gw = "w";
	if (mode & 010) gx = "x";

	if (mode & 04) ar = "r";
	if (mode & 02) aw = "w";
	if (mode & 01) ax = "x";

	if (mode & 01000) ax = "t";
	if (mode & 02000) gx = "s";
	if (mode & 04000) ox = "s";

	if (isdir) type = "d";
	if (islink) type = "l";
	sprintf(tmp,"%s%s%s%s%s%s%s%s%s%s",type,or,ow,ox,gr,gw,gx,ar,aw,ax);
	return tmp;
}
int cvt_lf_crlf(char *bf, int r,  char *tmp)
{
	char *s=bf;
	char *o=tmp;
	if (r<0) return r;
	for (;r>0;s++,r--) {
		if (*s=='\n') *o++ = '\r';
		*o++ = *s;
	}
	*o = 0;
	return (int)(o-tmp);
}
int cvt_crlf_lf(char *bf, int r)
{
	char *s=bf;
	char *o=bf;

	if (r<0) return r;
	bf[r] = 0;
	for (;r>0;s++,r--) {
		if (*s=='\r') {
			if (s[1] == '\n') continue;
			if (r==1) continue; /*Strip if next char might be lf */
		}
		*o++ = *s;
	}
	*o = 0;
	return (int)(o-bf);
}

int lib_fix_crlf(char *bf, int r,  char *tmp)
{
	char *s=bf;
	char *o=tmp;
	for (;*s && r>0;) {
		if ((*s=='\r') && (s[1]=='\n')) {
			*o++ = *s++;
			*o++ = *s++;
		} else if (*s=='\r' && s[1]!=0) {
			*o++ = '\r';
			*o++ = '\n';
			s++;
		} else if (*s=='\n' && s>bf) {
			*o++ = '\r';
			*o++ = '\n';
			s++;
		} else {
			*o++ = *s++;
		}
	}
	*o = 0;
	return (int)(o-tmp);
}

#ifdef WIN32
int lib_detach(void)
{
	return TRUE;
}
#else
#ifndef _PATH_DEVNULL
#define _PATH_DEVNULL "/dev/null"
#endif

int lib_detach(void)
{
        int fd;
	int noclose=0;
	fflush(NULL);
        switch (fork()) {
        case -1:
                return (-1);
        case 0:
                break;
        default:
                _exit(0);
        }

        if (setsid() == -1) {
                return (-1);
	}

        if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                (void)dup2(fd, STDIN_FILENO);
                (void)dup2(fd, STDOUT_FILENO);
                (void)dup2(fd, STDERR_FILENO);
                if (fd > 2)
                        (void)close (fd);
        }
        return (0);
}

#endif

int word_cmp(const void *p1, const void *p2)
{
	Wordlist *pp1 =  * ((Wordlist **)p1);
	Wordlist *pp2 =  * ((Wordlist **)p2);

	return strcmpnc(pp1->word,pp2->word);
}
int word_sort(Wordlist **pp)
{
	int n=0;
	int i=0;
	Wordlist *p,**array;
	for(p=*pp;p;p=p->next) {
		n++;
	}
	if (n<=1) return n;
	array=malloc(n*sizeof(Wordlist *));
	for(p=*pp;p;p=p->next) {
		array[i++]=p;
	}
	qsort(array,n,sizeof(Wordlist *),(int (*)(const void *, const void *))word_cmp);

	for(i=0;i<n-1;i++) {
		array[i]->next=array[i+1];
	}
	array[n-1]->next=NULL;
	*pp=array[0];

	free(array);
	return n;
}
int hword_new(Hwordlist **x)
{
	*x = calloc(1,sizeof(Hwordlist));
	return TRUE;
}
int hword_free(Hwordlist **xx)
{
	Hwordlist *x = *xx;
	Wentry *w,*next;
	int i;
	for (i=0; i<HWORDHASH; i++) {
		for (w=x->table[i];w!=NULL;w=next) {
			next = w->next;
			free(w->word);
			free(w);
		}
	}
	free(*xx);
	*xx = NULL;
	return TRUE;
}
int hword_pop(Hwordlist *x, int *at, char *word)
{
	Wentry *w;
	int i;
again:
	for (i= *at; i<HWORDHASH; i++) {
		w=x->table[i];
		if (w!=NULL) {
			x->table[i] = w->next;
			ncpy(word,w->word,BFSZ-1);
			free(w->word);
			free(w);
			*at = i+1;
			return TRUE;
		}
	}
	if (*at==0) return FALSE;
	*at = 0;
	goto again;
}
int hword_add(Hwordlist *x, char *word)
{
	int h = lib_str_hash(word,HWORDHASH);
	Wentry *w;
	w = calloc(1,sizeof(Wentry));
	w->word = strdup(word);
	w->next = x->table[h];
	x->table[h] = w;
	return TRUE;
}
int hword_find(Hwordlist *x, char *word)
{
	int h = lib_str_hash(word,HWORDHASH);
	Wentry *w;
	for (w=x->table[h];w!=NULL;w=w->next) {
		if (strcmp(w->word,word)==0) return TRUE;
	}
	return FALSE;
}

int word_copy_real(Wordlist **dst, Wordlist *src, char *file, int line)
{
	for (;src!=NULL;src=src->next) {
		word_add_real(dst,src->word,file,line,FALSE);
	}
	return TRUE;
}
int word_add_nodup_real(Wordlist **w, char *name, char *src, int line)
{
	if (word_find(*w,name)) return TRUE;
	return word_add_real(w,name,src,line,false);
}
int word_add_end(Wordlist **w, char *name)
{
	return word_add_real(w,name,__FILE__,__LINE__,TRUE);
}
int word_add_real(Wordlist **w, char *name, char *file, int line, int atend)
{
	Wordlist *g=NULL;
//printf("Word add (%s) (%d) (%s)\n",file,line,name);
//if (*name==1) abort();//*g=(void *) 0;

#ifdef USE_DMALLOC
	g = d_callocid(DMID,1,sizeof(Wordlist),file,line);
	g->word = d_strdupid(DMID,name,file,line);
#else
	g = calloc(1,sizeof(Wordlist));
	g->word = strdup(name);
#endif
	if (atend) {
		Wordlist *end;
		for(end = *w; end && end->next; end = end->next);
		if (end) {
			g->next = NULL;
			end->next = g;
			return TRUE;
		}
	}
	g->next = *w;
	*w = g;
	return TRUE;
}

int word_del(Wordlist **ww, char *contains)
{
	Wordlist *w,*prev = NULL;

	for (w = *ww ;w != NULL; prev = w, w = w->next) {
		if (strstr(w->word,contains)!=NULL) {
			if (prev) prev->next = w->next;
			else *ww = w->next;
			free(w->word);
			free(w);
			return TRUE;
		}
	}
	return FALSE;
}
int word_delete(Wordlist **ww, char *exact)
{
	Wordlist *w,*prev = NULL;

	for (w = *ww ;w != NULL; prev = w, w = w->next) {
		if (strcmp(w->word,exact)==0) {
			if (prev) prev->next = w->next;
			else *ww = w->next;
			free(w->word);
			free(w);
			return TRUE;
		}
	}
	return FALSE;
}
int time_diff(time_t then)
{
	return (int) (time(NULL)-then);
}
int word_remove_real(Wordlist **ww, char *s, int (*fn)(char *, char *))
{
	Wordlist *w,*prev = NULL;

	for (w = *ww ;w != NULL; prev = w, w = w->next) {
		if (fn(w->word,s)==0) {
			if (prev) prev->next = w->next;
			else *ww = w->next;
			free(w->word);
			free(w);
			return TRUE;
		}
	}
	return FALSE;
}
char *lib_web_encode_js(char *out,char *in,int outbfsz)
{
	char *pin,*pout;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) return in;
		if (*pin=='\n') {strcpy(pout,"\\n"); pout+=2; continue;}
		if (*pin < 32) {
			continue; // remove control characters
		}
		if ((unsigned char)*pin >127) {
			continue; // remove high ascii characters
		}
		switch(*pin) {
		case '\\': strcpy(pout,"\\\\"); pout+=2; break;
		case '\'': strcpy(pout,"\\\'"); pout+=2; break;
		case '"':  strcpy(pout,"\\\""); pout+=2; break;
		case '\r' : break;
		case '\n': strcpy(pout,"\\n"); pout+=2; break;
		default:
			*pout++=*pin;
			break;
		}
	}
	*pout++='\0';
	return out;
}

int word_add_at(Wordlist **w, Wordlist *at, char *name, int replace)
{
	Wordlist *g;
	// printf("Word add (%s) (%d) (%s)\n",file,line,name);
#ifdef USE_DMALLOC
	g = d_callocid(DMID,1,sizeof(Wordlist),__FILE__,__LINE__);
	g->word = d_strdupid(DMID,name,__FILE__,__LINE__);
#else
	g = calloc(1,sizeof(Wordlist));
	g->word = strdup(name);
#endif
	if (at) {
		Wordlist *p, *last;
		for(p = *w, last=NULL; p && p->next; p = p->next){
			if (p==at) break;
			last=p;
		}
		if (p) {
			g->next = p->next;
			if(!replace){
				p->next=g;
			}else if (last){
				last->next=g;
			}else{
				*w=g;
			}
			if(replace){
				free(p->word);
				free(p);
			}
			return TRUE;
		}
	}
	g->next = *w;
	*w = g;
	return TRUE;
}


int word_find(Wordlist *w, char *s)
{
	for (;w!=NULL;w=w->next) {
		if (strcmp(w->word,s)==0) return TRUE;
	}
	return FALSE;
}
int word_findnc(Wordlist *w, char *s)
{
	for (;w!=NULL;w=w->next) {
		if (strcmpnc(w->word,s)==0) return TRUE;
	}
	return FALSE;
}
Wordlist *word_loadfile(char *file) {
	Wordlist *lines = NULL;
	char bf[BFSZ],*p,*n;
	FILE *fp;

	fp = fopen(file,"r");
	if (!fp) return NULL;
	while(fgets(bf,BFSZ-1,fp)) {
		p = mystrtok(bf,"\r\n",&n);
		if (!p) p = "";
		word_add(&lines,p);
	}
	fclose(fp);
	return lines;
}
char * word_geti(Wordlist *w, int i)
{
	Wordlist *start=w;
	if (w==NULL) return "";
	for (;i>0 && w!=NULL;w=w->next) i--;
	if (w==NULL) return start->word;
	return w->word;
}
int word_count(Wordlist *w)
{
	int n=0;
	for (;w!=NULL;w=w->next) n++;
	return n;
}
int word_savefile(Wordlist *lines, char *file) {
	Wordlist *ptr;
	int count = 0;
	FILE *fp;

	fp = fopen(file,"w");
	if (!fp) return count;
	for(ptr = lines; ptr; ptr = ptr->next) {
		fprintf(fp,"%s\n",ptr->word);
		count++;
	}
	fclose(fp);
	return count;
}
void word_reverse_real(Wordlist **list, char *file, int line) {
	Wordlist *newlist = NULL,*p;

	if (!list || !*list) return;
	for(p = *list; p; p = p->next) {
		word_add_real(&newlist,p->word,file,line,FALSE);
	}
	word_free(list);
	*list = newlist;
}
int word_tok(Wordlist **list, char *string, char *toks) {
	char bf[BFSZ],*next,*p;
	int r;

	r = 0;
	ncpy(bf,string,BFSZ-1);
	for(p = mystrtok(bf,toks,&next); p; p = mystrtok(NULL,toks,&next)) {
		word_add(list,p);
		r++;
	}
	return r;
}
int word_free(Wordlist **w)
{
	Wordlist *g,*gnext;
	if (w==NULL) return TRUE;
	for (g=*w;g!=NULL;g=gnext) {
		gnext = g->next;
		if (g->word!=NULL) free(g->word);
		free(g);
	}
	*w = NULL;
	return TRUE;
}
int word_print(char *info, Wordlist *w)
{
	for (;w!=NULL;w=w->next) printf("%s: %s\n",info,w->word);
	return TRUE;
}
int lib_iswindows(void)
{
#ifdef WIN32
	return TRUE;
#else
	return FALSE;
#endif
}

int lib_isnt(void)
{
#ifdef WIN32
	int r;
	OSVERSIONINFO info;
	info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	r = GetVersionEx(&info);
	if (!r) {
		printf("getversionex failed %d\n",GetLastError());
		return FALSE;
	}
	/*printf("platform id = %d %d\n",info.dwPlatformId,VER_PLATFORM_WIN32_NT);*/
	if (info.dwPlatformId==VER_PLATFORM_WIN32_NT) return TRUE;
	return FALSE;
#else
	return FALSE;
#endif
}
int lib_isnt351(void)
{
#ifdef WIN32
	int r;
	OSVERSIONINFO info;
	info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	r = GetVersionEx(&info);
	if (!r) {
		printf("getversionex failed %d\n",GetLastError());
		return FALSE;
	}
	/*printf("platform id = %d %d\n",info.dwPlatformId,VER_PLATFORM_WIN32_NT);*/
	if (info.dwPlatformId==VER_PLATFORM_WIN32_NT) {
		if (info.dwMajorVersion==3) return FALSE;	// should check minor too.
		return TRUE;
	}
	return FALSE;
#else
	return FALSE;
#endif
}

char *lib_cdrive(void)
{
	static char bf[BFSZ];
#ifdef WIN32
	system_dir(bf,200);
	bf[2] = 0;
#else
	return "";
#endif
	return bf;
}

static char *weekday[]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", };
char *lib_strdate(char *bf)
{
        time_t now;
        struct tm *t;
	struct tm atm;
	char zone[BFSZ];
        time(&now);
        t = safe_localtime(&now,&atm);
	lib_timezone_string(zone);


	if (t==NULL) return "LOCALTIMEnull";
	    sprintf(bf,"%d %s %02d %02d:%02d:%02d %s",
	        t->tm_mday,months[t->tm_mon],t->tm_year+1900,
                t->tm_hour,t->tm_min,t->tm_sec,zone);
        return bf;
}

char *win_slash(char *in, char *out)
{
	char *s;
	if (out!=in) strcpy(out,in);
	for (s=out;*s!=0;s++) {
		if (*s=='/') *s = '\\';
	}
	return out;
}
char *unix_slash(char *in, char *out)
{
	char *s;
	if (out!=in) strcpy(out,in);
	for (s=out;*s!=0;s++) {
		if (*s=='\\') *s = '/';
	}
	return out;
}
char *std_slash(char *in, char *out)
{
	char *s;
	if (out!=in) ncpy(out,in,BFSZ-1);
	for (s=out;*s!=0;s++) {
		if (*s=='\\') *s = '/';
	}
	return out;
}
char *lib_single_slash(char *in, char *out)
{
	char *s;
	int last=FALSE;
	char *xout=out;
	for (s=in;*s!=0;s++) {
		if (last) if (*s=='/') continue;
		if (last) if (*s=='\\') continue;
		if (*s=='/' || *s=='\\') last = TRUE; else last=FALSE;
		*out++ = *s;
	}
	*out++ = 0;
	return xout;
}
char *os_slash(char *in, char *out)
{

#ifdef WIN32
	return win_slash(in,out);
#else
	return unix_slash(in,out);
#endif
}
char *lib_encode_utf8(unsigned int code, char *result)
{
	unsigned char *str = (unsigned char *) result;
	if (code >= 0x80) {
//printf("code %x converted: ",code);
			*str++ = 0xc0 + (code /64); //  1001 1110
//printf("%x ",str[-1]);
			*str++ = 0x80 + (code % 64); // 3 = 11, f = 1111
//p/rintf("%x \n",str[-1]);
	}	else {
			*str++ = code;
	}
	*str = '\0';

	return result;
}


/* Not sure when this would be sensible */
int utf8_valid(const unsigned char *buf, unsigned int len)
{
 const unsigned char *endbuf = buf + len;
 unsigned char byte2mask=0x00, c;
 int trailing = 0;  // trailing (continuation) bytes to follow

 while (buf != endbuf)
 {
   c = *buf++;
   if (trailing)
    if ((c&0xC0) == 0x80)  // Does trailing byte follow UTF-8 format?
    {if (byte2mask)        // Need to check 2nd byte for proper range?
      if (c&byte2mask)     // Are appropriate bits set?
       byte2mask=0x00;
      else
       return 0;
     trailing--; }
    else
     return 0;
   else
    if ((c&0x80) == 0x00)  continue;      // valid 1 byte UTF-8
    else if ((c&0xE0) == 0xC0)            // valid 2 byte UTF-8
          if (c&0x1E)                     // Is UTF-8 byte in
                                          // proper range?
           trailing =1;
          else
           return 0;
    else if ((c&0xF0) == 0xE0)           // valid 3 byte UTF-8
          {if (!(c&0x0F))                // Is UTF-8 byte in
                                         // proper range?
            byte2mask=0x20;              // If not set mask
                                         // to check next byte
            trailing = 2;}
    else if ((c&0xF8) == 0xF0)           // valid 4 byte UTF-8
          {if (!(c&0x07))                // Is UTF-8 byte in
                                         // proper range?
            byte2mask=0x30;              // If not set mask
                                         // to check next byte
            trailing = 3;}
    else if ((c&0xFC) == 0xF8)           // valid 5 byte UTF-8
          {if (!(c&0x03))                // Is UTF-8 byte in
                                         // proper range?
            byte2mask=0x38;              // If not set mask
                                         // to check next byte
            trailing = 4;}
    else if ((c&0xFE) == 0xFC)           // valid 6 byte UTF-8
          {if (!(c&0x01))                // Is UTF-8 byte in
                                         // proper range?
            byte2mask=0x3C;              // If not set mask
                                         // to check next byte
            trailing = 5;}
    else  return 0;
 }
  return trailing == 0;
}
#undef feof
int feof_checked(FILE *x)
{
	return feof(x);
}
int start_shell(char *path)
{
#ifdef WIN32
	intptr_t ri;
	HINSTANCE r;
	r=ShellExecuteA(NULL,(XLPCWSTR) "open",(XLPCWSTR)path,(XLPWSTR)"",(XLPWSTR)"",SW_SHOW);
	ri=(intptr_t) r;
	if (ri<32) return FALSE;
	return TRUE;
#else
	return FALSE;
#endif
}
int lib_istrue(char *s)
{
	if (tolower(*s)=='y') return TRUE;
	if (tolower(*s)=='t') return TRUE;
	return FALSE;
}
FILE *lib_fopen_create(char *fname, char *mode, int fmod)
{
	FILE *f;
	f = fopen(fname,mode);
	if (f==NULL) {
		if (errno==ENOENT) {
			lib_create_dir(fname,fmod);
			f = fopen(fname,mode);
		}
	}
	return f;
}

int token_quote(char *s, char *p[], int tmax)
{
        int n=0;
	char *sep=" \t";
	char *sep2="= \t";

        for (;*s!=0;) {
                s += strspn(s,sep);  /* Skip spaces */
                if (*s==0) break;
		if (*s=='"') {
			s++;
	                p[n++] = s;
			for (;*s!=0;s++) {
				if (*s=='"') break;
				if (*s=='\\') if (s[1]!=0) s++;
			}
			goto dorest;
		}
		if (*s=='=') {
	                p[n++] = "=";
			goto dorest;
		}
                p[n++] = s;
                s += strcspn(s,sep2);  /* Find end of word */
		if (*s=='=') {
			p[n++] = "=";
		}
dorest:
                if (*s==0) break;
                *s = 0; s++;
                if ((n+1)>=tmax) {
/*                        imsg("Too many tokens in this header line\n");*/
                        break;
                }
        }
        p[n] = NULL;
        return n;
}
unsigned long hex_to_ulong(char * text)
{
	unsigned long res;
	unsigned char c;

	if (text==NULL) return(-1);

	res=0;
loop:
	c=*text; text++;
	if ((c>='0')&(c<='9'))
		{
		res=(res<<4)+((long)(c-48) & 0x0F);
		goto loop;
		}
	if ((c>='A')&(c<='F'))
		{
		res=(res<<4)+((long)(c-55) & 0x0F);
		goto loop;
		}
	if ((c>='a')&(c<='f'))
		{
		res=(res<<4)+((long)(c-87) & 0x0F);
		goto loop;
		}
	return(res);
}

int lib_hex_digit(int c)
{
	if ((c>='0') && (c<='9')) return c - '0';
	if ((c>='a') && (c<='f')) return c - 'a' + 10;
	if ((c>='A') && (c<='F')) return c - 'A' + 10;
	return 0;
}
char lib_make_hex_digit(int f)
{
	if (f>=0 && f<=9) return '0'+f;
	if (f>=10 && f<=15) return 'A'+(f-10);
	return '0';
}

unsigned int lib_xtoi(char *s)
{
	unsigned int x;
	sscanf(s,"%x",&x);
	return x;
}
char *lib_hex_to_bin(char *hex, unsigned char *bin, int *outlen)
{
	int out=0;
	int i;
	int len = lib_strlen(hex);
	for (i=0; i<len; i+=2) {
		bin[out++] = lib_hex_digit(hex[i]) * 16 + lib_hex_digit(hex[i+1]);
	}
	bin[out++] = 0;
	*outlen = out-1;
	return bin;
}
char *lib_bin_to_hex(char *bin, int binlen, char *out, int outlen)
{
	unsigned char *ubin=(unsigned char *) bin;
	int i;
	int z;
	char *out_start = out;
	for (i=0; i<binlen; i++,ubin++)  {
	      sprintf(out,"%02x",(unsigned int) *ubin);
	      z = lib_strlen(out);
	      out += z;
	      outlen -= z;
	      if (outlen<20) break;
	 }
	*out = 0;
	 return out_start;
}
char *nice_num(int d, char *tmp) // was 1024
{
	sprintf(tmp,"%d",d);
	if (d>5000) sprintf(tmp,"%dk",d/1000);
	if (d>5000000) sprintf(tmp,"%dmb",d/1000/1000);
	return tmp;
}
char *nice_double(double d, char *tmp) // was 1024
{
	sprintf(tmp,"%d",(int) d);
	if (d>5000) sprintf(tmp,"%dk",(int)(d/1000));
	if (d>5000000) sprintf(tmp,"%dmb",(int) (d/1000/1000));
	return tmp;
}
char *nice_speed(int d, char *tmp)
{
	char tmp2[BFSZ];
	sprintf(tmp,"%s per second",nice_num(d,tmp2));
	if (d==0) 	sprintf(tmp,"unlimited");

	return tmp;
}
typedef struct HCOUNT {char *filename; int line; int isopen;} Hcount;
static Hcount hlist[2000];
static FILE *hcountf;
int hcount_init(void)
{
//	hcountf = spawnfopen("hcount.log","w");
	return TRUE;
}
int hcount_open_real(int n, char *filename, int line)
{
	if (n>=2000) return TRUE;
	if (n<0) return TRUE;
	if (hcountf==NULL) return FALSE;
//fprintf(hcountf,"close %d %s:%d\n",n,filename,line);
	if (hlist[n].isopen) hcount_log(n,filename,line,"is already open");
	hlist[n].isopen = TRUE;
	hlist[n].line = line;
	hlist[n].filename = filename;
	return TRUE;
}
int hcount_close_real(int n, char *filename, int line)
{
	if (n>=2000) return TRUE;
	if (n<0) return TRUE;
	if (hcountf==NULL) return FALSE;
//fprintf(hcountf,"close %d %s:%d\n",n,filename,line);
	if (!hlist[n].isopen) hcount_log(n,filename,line,"is already closed");
	hlist[n].isopen = FALSE;
	hlist[n].line = line;
	hlist[n].filename = filename;
	return TRUE;
}
int hcount_report(void)
{
	int i;
	for (i=0; i<2000; i++) {
			if (hlist[i].isopen) hcount_log(i,hlist[i].filename,hlist[i].line,"still open");
	}
	return TRUE;
}
int lib_msec_now(void)
{
	static int base;
	if (base==0) base = (int) (time(NULL)-(24*60*60));
#ifdef WIN32
	{
		SYSTEMTIME systime;
		GetSystemTime(&systime);
		return (int) (((time(NULL)-base)*1000)+(systime.wMilliseconds));
	}
#else
#ifndef NO_TIMEOFDAY
	{
		struct timeval tsec;
		struct timezone xtzone;
		gettimeofday(&tsec,&xtzone);
		return ((tsec.tv_sec-base)*1000)+(tsec.tv_usec/1000);
	}
#else
	return (time(NULL)-base)*1000;
#endif
#endif
}


int hcount_log(int n, char *filename, int line, char *reason)
{
	char bf[BFSZ];
	char tmp[BFSZ];
	static int busy;
	if (hcountf==NULL) return FALSE;
	if (busy) return FALSE;
	busy = TRUE;
	sprintf(bf,"hcount: %d %s %d %s\n",n,filename,line,reason);
	if (line!=hlist[n].line) if (hlist[n].filename!=NULL) 	fprintf(hcountf,"%s was (%s:%d)",lib_emsg_time(tmp),hlist[n].filename,hlist[n].line);

	fprintf(hcountf,"%s %s",lib_emsg_time(tmp),bf);
	if (vemsg!=NULL) vemsg("%s",bf);
	fflush(hcountf);
	if (lib_file_osize(hcountf)>1000000) {
//		spawnfclose(hcountf);
//		hcountf = NULL;
	}
	busy = FALSE;
	return FALSE;
}

int string_to_secs_type(char *s)
{
	while(sisdigit(*s) || sisspace(*s) || ispunct(*s)) s++;
	if (charmap[*s] == charmap['y']) return STS_YEARS;
	if (charmap[*s] == charmap['w']) return STS_WEEKS;
	if (charmap[*s] == charmap['d']) return STS_DAYS;
	if (charmap[*s] == charmap['h']) return STS_HOURS;
	if (charmap[*s] == charmap['m']) {
		if (s[1] && charmap[s[1]] == charmap['o']) return STS_MONTHS;
		return STS_MINS;
	}
	return STS_SECS;
}

int string_to_secs(char *s)
{
	int ival;

	while(sisspace(*s)) s++;
	ival = atoi(s);
	switch(string_to_secs_type(s)) {
	case STS_MONTHS: //not completely accurate
		ival *= 30;
		goto days;

	case STS_YEARS:  //not completely accurate
		ival *= 365;
		goto days;
//		ival *= 52;  chrisp improved it to make it more accurate...
	case STS_WEEKS:
		ival *= 7;
	case STS_DAYS:
days:	ival *= 24;
	case STS_HOURS:
		ival *= 60;
	case STS_MINS:
		ival *= 60;
	default:
		break;
	}
	return ival;
}

int nice_atoi(char *s)
{
	char tmp[BFSZ];
	char rest[BFSZ];
	int n = atoi(s);
	ncpy(tmp,s,100);
	strcpy(rest,"");
	s = strchr(tmp,' '); {
		if (s!=NULL) {
			ncpy(rest,s+1,BFSZ-1);
			strlwr(rest);
			*s = 0;
		}
	}
	s = strchr(tmp,'\t'); if (s!=NULL) *s = 0;
	strlwr(tmp);
//	if (strstr(tmp,"gig")!=NULL) return n*1000000000;
//	if (strstr(tmp,"mb")!=NULL) return  n*1000000;
//	if (strstr(tmp,"m")!=NULL) return   n*1000000;
//	if (strstr(tmp,"k")!=NULL) return   n*1000;
	if (strstr(tmp,"gig")!=NULL) return n*1000*1000*1000;
	if (strstr(tmp,"gb")!=NULL) return n*1000*1000*1000;
	if (strstr(tmp,"mb")!=NULL) return  n*1000*1000;
	if (strstr(tmp,"m")!=NULL) return   n*1000*1000;
	if (strstr(tmp,"kb")!=NULL) return   n*1000;
	if (strstr(tmp,"k")!=NULL) return   n*1000;

	// This allows "23 mb" etc...
	if (strcmp(rest,"gig")==0) return n*1000*100*100;
	if (strcmp(rest,"gb")==0) return n*1000*1000*1000;
	if (strcmp(rest,"mb")==0) return  n*1000*1000;
	if (strcmp(rest,"m")==0) return   n*1000*1000;
	if (strcmp(rest,"kb")==0) return   n*1000;
	if (strcmp(rest,"k")==0) return   n*1000;
/* changed by chrisp at user request. to 1024 instead of 1000.. */
	return n;
}
int nice_atoi_mb(char *s)
{
	char tmp[BFSZ];
	int n = atoi(s);
	ncpy(tmp,s,100);
	s = strchr(tmp,' '); if (s!=NULL) *s = 0;
	s = strchr(tmp,'\t'); if (s!=NULL) *s = 0;
	strlwr(tmp);
	if (strstr(tmp,"gig")!=NULL) return n*1000;
	if (strstr(tmp,"gb")!=NULL) return n*1000;
	if (strstr(tmp,"g")!=NULL) return n*1000;
	if (strstr(tmp,"mb")!=NULL) return  n;
	if (strstr(tmp,"m")!=NULL) return   n;
	if (strstr(tmp,"kb")!=NULL) return   n/1000;
	if (strstr(tmp,"k")!=NULL) return   n/1000;
	return n;
}
double nice_atof(char *s)
{
	char tmp[BFSZ];
	double n = atof(s);
	ncpy(tmp,s,100);
	s = strchr(tmp,' '); if (s!=NULL) *s = 0;
	s = strchr(tmp,'\t'); if (s!=NULL) *s = 0;
	strlwr(tmp);
	if (strstr(tmp,"gig")!=NULL) return n*1000*1000*1000;
	if (strstr(tmp,"gb")!=NULL) return n*1000*1000*1000;
	if (strstr(tmp,"g")!=NULL) return n*1000*1000*1000;
	if (strstr(tmp,"mb")!=NULL) return  n*1000*1000;
	if (strstr(tmp,"m")!=NULL) return   n*1000*1000;
	if (strstr(tmp,"kb")!=NULL) return   n*1000;
	if (strstr(tmp,"k")!=NULL) return   n*1000;
	return n;
}
char* lib_itoa(char *s, int i)
{
	if (i/(1000*1000*1000)>20)		sprintf(s,"%dgig",i/(1000*1000*1000));
	else if (i/(1000*1000*1000)>0)	sprintf(s,"%.1fgig",i/(1000.0*1000*1000));
	else if (i/(1000*1000)>20)		sprintf(s,"%dmb",i/(1000*1000));
	else if (i/(1000*1000)>0)		sprintf(s,"%.1fmb",i/(1000.0*1000));
	else if (i/(1000)>20)			sprintf(s,"%dkb",i/(1000));
	else if (i/(1000)>0)			sprintf(s,"%.1fkb",i/(1000.0));
	else sprintf(s,"%d",i);
	return s;
}
char* lib_itoa_nob(char *s, int i)
{
	if (i/(1000*1000*1000)>20)		sprintf(s,"%dg",i/(1000*1000*1000));
	else if (i/(1000*1000*1000)>0)	sprintf(s,"%.1fg",i/(1000.0*1000*1000));
	else if (i/(1000*1000)>20)		sprintf(s,"%dm",i/(1000*1000));
	else if (i/(1000*1000)>0)		sprintf(s,"%.1fm",i/(1000.0*1000));
	else if (i/(1000)>20)			sprintf(s,"%dk",i/(1000));
	else if (i/(1000)>0)			sprintf(s,"%.1fk",i/(1000.0));
	else sprintf(s,"%d",i);
	return s;
}
char *lib_perseck(char *s, double f)
{
	char tmp[BFSZ];
	sprintf(s,"%s/s",lib_ftoa(tmp,f));
	return s;
}
char *lib_persec(char *s, double f)
{
	sprintf(s,"%3.1f/s",f);
	return s;
}
char *lib_percent(char *s, double f)
{
	sprintf(s,"%2.0f%%",f);
	return s;
}
char* lib_ftoa(char *s, double f)
{
	if (f/(1000*1000*1000)>20)		sprintf(s,"%dgb",(int)(f/(1000*1000*1000)));
	else if (f/(1000*1000*1000)>=1)	sprintf(s,"%.1fgb",f/(1000.0*1000*1000));
	else if (f/(1000*1000)>20)		sprintf(s,"%dmb",(int)(f/(1000*1000)));
	else if (f/(1000*1000)>=1)		sprintf(s,"%.1fmb",f/(1000.0*1000));
	else if (f/(1000)>20)			sprintf(s,"%dkb",(int)(f/(1000)));
	else if (f/(1000)>=1)			sprintf(s,"%.1fkb",f/(1000.0));
	else sprintf(s,"%d",(int)f);
	return s;
}
char* lib_ftoa_nob(char *s, double f)
{
	if (f/(1000*1000*1000)>20)		sprintf(s,"%dg",(int)(f/(1000*1000*1000)));
	else if (f/(1000*1000*1000)>=1)	sprintf(s,"%.1fg",f/(1000.0*1000*1000));
	else if (f/(1000*1000)>20)		sprintf(s,"%dm",(int)(f/(1000*1000)));
	else if (f/(1000*1000)>=1)		sprintf(s,"%.1fm",f/(1000.0*1000));
	else if (f/(1000)>20)			sprintf(s,"%dk",(int)(f/(1000)));
	else if (f/(1000)>=1)			sprintf(s,"%.1fk",f/(1024.0));
	else sprintf(s,"%d",(int)f);
	return s;
}
static char startstop_logfile[BFSZ];
int lib_set_startstop_file(char *s)
{
	ncpy(startstop_logfile,s,BFSZ-1);
	return TRUE;
}
char *lib_startstop_logfile(void)
{
	if (lib_strlen(startstop_logfile)==0) return "startstop.log";
	return startstop_logfile;
}
int lib_isip(char *s)
{
	int rval=1;
	char *t;

	t=s;
	if(*t=='[')
		t++;
	for(;*t!=0;t++)
	{
		if(((*t>='0')&&(*t<='9'))||(*t=='.')||(*t==':'))
			;
		else
		{
			if((*t!=']')||(*(t+1)!=0))
				rval=0;
		}
	}

	return rval;
}
int lib_isquoted(char *s)
{
	if(*s!='"') return FALSE;
	s++;
	if(!*s) return FALSE;
	for(;*s;s++);
	if(*(s-1)!='"') return FALSE;
	return TRUE;
}


#ifdef abort
#undef abort
#endif
static int (*lib_abort_fn)(void);
static int (*lib_abort_fn2)(char *src, int line);
int lib_abort_do(int (*fn)(void))
{
	lib_abort_fn = fn;
	return TRUE;
}
int lib_abort_do2(int (*fn)(char *src, int line))
{
	lib_abort_fn2 = fn;
	return TRUE;
}

static int need_mutex=TRUE;
int lib_fopen_mutex(int x)
{
	need_mutex = x;
	return TRUE;
}
char *lib_logtime(char *tstr)
{
	char tmpstr[BFSZ];
        time_t t;
	char *ccc;

	time(&t);
	ccc = safe_ctime(&t,tmpstr);
	if (ccc==NULL) ccc = "badtimebadtimebadtime";
    strcpy(tstr,ccc+8);
    tstr[11] = 0;
	return tstr;
}
char *lib_logtime2(char *tstr)	// Always zero prefixed!
{
	char tmpstr[BFSZ];
        time_t t;
	char *ccc;

	time(&t);
	ccc = safe_ctime(&t,tmpstr);
	if (ccc==NULL) ccc = "badtimebadtimebadtime";
    strcpy(tstr,ccc+8);
    tstr[11] = 0;
	if(tstr[0]==' ') tstr[0]='0';
	return tstr;
}

static int indian = 1; /* This should change in some way */

int lib_ncrc32_noalign(char *xin,int len)
{
	char bf[BFSZ];
	if (len>BFSZ-1) len = BFSZ-1;
	memcpy(bf,xin,len);
	return lib_ncrc32(bf,len);
}
int lib_ncrc32(char *xin,int len)
{
	/* calculate crc value for string.  Stop at len characters */
	// NOTE DO NOT CHANGE THIS FUNCTION WITHOUT TALKING TO CHRISP, IT WILL BREAK SURGENEWS
	unsigned int dx;
	int r;
	unsigned short *in;
	unsigned int ax = 0;
	unsigned short *al;
	unsigned short *ah;
	unsigned short *dl;
	unsigned short *dh;

	len = len >> 1;
	if (*xin==0) return 0;
	if ((((intptr_t) xin)&1) == 1) xin++;
	in = (unsigned short *) xin;

	ax = 0xffffffff;
	al = (unsigned short *) &ax;
	ah = al+indian;
	dl = (unsigned short *) &dx;
	dh = dl+indian;
	for (;(*in!=0);in++,xin+=2,--len) {
		dx = ax;
		*dl = *al ^ *in;
		*dl = *dl ^ (*dl / 256);
		*ah = *dl * 256;
		*dh = *dh ^ *ah;
		*al = *dl;
		*ah = 0;
		ax *= 1024;
		ax = ax ^ dx;
		if (xin[1] == 0) break;
		if (xin[2] == 0) break;
		if (len<2) break;
	}
	r = (int) ax;
	if (r<0) r = -r;
	return r;
}
char *hms(char *tmp, time_t t)
{
	int h;
	int m;
	int s;
	int isnow=FALSE;
	if (t<0) isnow = TRUE;
	s = t % 60;
	t = t / 60;
	m = t % 60;
	t = t / 60;
	h = (int) t;
	if (h>100000) {
		ncpy(tmp,"--:--:--",20);
		return tmp;
	}
	if (isnow) {
		ncpy(tmp,"(now)   ",20);
		return tmp;
	}
	sprintf(tmp,"%02d:%02d:%02d",h,m,s);
	if (h>99) { // make sure it displays it! 
		sprintf(tmp,"%d:%02d",h,m);
	}
	return tmp;
}

#ifdef NEED_SETENV
	int setenv(const char *label, const char *value, int overwrite) {
		if (label) {
			int length = 0;
			char *data;

			if (!overwrite && getenv(label)) return FALSE;
			length += lib_strlen(label);
			if (value) length += lib_strlen(value);
			data = calloc(1,length+2);
			if (value) sprintf(data,"%s=%s",label,value);
			else sprintf(data,"%s=",label);
			putenv(data);
			return TRUE;
		}
		return FALSE;
	}
#endif

#ifdef NEED_UNSETENV
	void unsetenv(const char *label) {
		if (label) {
			char *data;

			data = calloc(1,lib_strlen(label)+2);
			sprintf(data,"%s=",label);
			putenv(data);
		}
	}
#endif



int lib_good_sms(char *sms) {
	int len;

	if (*sms == '+') sms++;
	len = lib_strlen(sms);
	for(; *sms; ) {
		if (*sms == ' ' || *sms == '-') {
			memmove(sms,sms+1,len--);
			continue;
		}
		if (!sisdigit(*sms)) return FALSE;
		sms++, len--;
	}
	return TRUE;
}

//Articles is Saggy Pix + Movies (w/ indexes)!!!-ele0205DS-095522100.jpg (1/1) [102/114]
//Subject: Gary the Rat. DivX5 720x540 RGB cable cap. [49 of 49] - "0105 #005 - Mergers and Acquisitions (TR-Wobbler).vol081+20.PAR2" yEnc (27/28)
int lib_parts_subject(char *subject, char *subj, char *fname, int *parti, int *partmax)
{
	/* Search for ( or [ from right hand side */
	char *s;
	int i;
	int roundyi = 0;
	char *roundy=NULL;
	int noslash = TRUE;

	*partmax = 1; *parti = 1;
	strcpy(fname,"file.dat");

	for (i=lib_strlen(subject)-1;i>=0; i--) {
		s = subject + i;
		if (*s=='(') {roundy = s; roundyi = i; break;}
	}

	for (i=lib_strlen(subject)-1;i>=0; i--) {
		s = subject + i;
		if (noslash) {
			if (*s=='/') noslash = FALSE;
		} else {
			if (*s=='(') goto found_brace;
			if (*s=='[') goto found_brace;
		}
	}
	/*imsg("No left bracket found\n");*/
	return FALSE;

	/* */
found_brace:
	// Lets go a litte bit further back, and see if we can find a roundy brace.

	if (roundy!=NULL) if (roundy+10>s) {
		s = roundy;
		i = roundyi;
	}
	if (i>BFSZ-10) {
		/*imsg("subject too long\n");*/
		return FALSE;
	}
	ncpy(subj,subject,i);

	*parti = atoi(s+1);
	s = strchr(s,'/');
	if (s==NULL) {
		/*imsg("No slash found\n");*/
		return FALSE;
	}
	*partmax = atoi(s+1);
	if (*parti<0) {
		/*imsg("parti less than zero\n");*/
		return FALSE;
	}
	if (*parti>*partmax) {
		/*imsg("parti < than max %d %d\n",*parti,*partmax);*/
		return FALSE;
	}
	if (*partmax>1000) { /* Added to prevent loops */
		return FALSE;
	}
	if (*partmax<1) { /* Added to prevent loops */
		return FALSE;
	}
	/* File name is last word of subj */
	for (i=lib_strlen(subj)-1;i>0;i--) if (subj[i]=='.') break;
	for (;i>=0;i--) if (subj[i]==' ') { i++; break;}
	ncpy(fname,subj+i,100);
	s = strchr(fname,' '); if (s!=NULL) *s = 0;
	return TRUE;
}
char *lib_unescape(char *s, char *bf, int tmpsz)  /*  ~ = ' ' */
{
        char *out;
        if (s==NULL) return "";
        for (out=bf; *s != 0; s++) {
			if (out-bf+10>BFSZ) break;
            if (*s=='+') *out++ = ' ';
            else if (*s=='\\') { s++; *out++ = *s; }
            else *out++ = *s;
        }
        *out++ = 0;
        return bf;
}


static int do_full_time;
int lib_emsg_time_setfull(int x)
{
	do_full_time = x;
	return TRUE;
}
char *lib_daymon_time(char *tstr)
{
	char tmpstr[BFSZ];
	char *ccc;
	char *s;
	time_t t;
	time(&t);
	ccc = safe_ctime(&t,tmpstr);
	if (ccc==NULL) ccc = "badtimebadtimebadtime";
    ncpy(tstr,ccc,BFSZ-1);
	s=strchr(tstr,'\n');
	if (s) *s=0;
	return tstr;
}
#ifdef EMSG_MILLISEC
#ifndef WIN32
#ifndef OSX
#include <sys/timeb.h>
#endif
#endif
#endif

char *lib_emsg_time(char *tstr)
{
	time_t t;
	char tmpstr[BFSZ];
	char *ccc;
	int msec=0;

	if (do_full_time) return lib_emsg_time2(tstr);
	{
#ifdef EMSG_MILLISEC
	{
#	ifdef OSX
		struct timeval the_time;
		struct timezone tzp;
		gettimeofday(&the_time, &tzp);
		msec = the_time.tv_usec / 1000;
#	else
#		ifdef WIN32
		struct _timeb timebuffer;
		_ftime( &timebuffer );
#		else
		struct timeb timebuffer;
		ftime( &timebuffer );
#		endif
		msec = timebuffer.millitm;
#	endif
	}
#else

#	ifdef WIN32
	SYSTEMTIME systime;
	GetSystemTime(&systime);
	msec = systime.wMilliseconds;
#	endif

#endif
	time(&t);
	ccc = safe_ctime(&t,tmpstr);
	if (ccc==NULL) ccc = "badtimebadtimebadtime";
	strcpy(tstr,ccc+8);
	tstr[11] = 0;
	sprintf(tstr+11,".%.02d",msec/10);

	return tstr;
	}
}
double time_double(void)
{
	time_t t;
	int msec=0;

#ifdef EMSG_MILLISEC
	{
#	ifdef OSX
		struct timeval the_time;
		struct timezone tzp;
		gettimeofday(&the_time, &tzp);
		msec = the_time.tv_usec / 1000;
#	else
#		ifdef WIN32
		struct _timeb timebuffer;
		_ftime( &timebuffer );
#		else
		struct timeb timebuffer;
		ftime( &timebuffer );
#		endif
		msec = timebuffer.millitm;
#	endif
	}
#else

#	ifdef WIN32
	SYSTEMTIME systime;
	GetSystemTime(&systime);
	msec = systime.wMilliseconds;
#	endif

#endif
	time(&t);
	return t + msec/1000.0;
}

char *lib_emsg_time2(char *tstr)
{
	time_t t;
	struct tm atm;
	int msec=0;
#ifdef EMSG_MILLISEC
	{
#	ifdef OSX
		struct timeval the_time;
		struct timezone tzp;
		gettimeofday(&the_time, &tzp);
		msec = the_time.tv_usec / 1000;
#	else
#		ifdef WIN32
		struct _timeb timebuffer;
		_ftime( &timebuffer );
#		else
		struct timeb timebuffer;
		ftime( &timebuffer );
#		endif
		msec = timebuffer.millitm;
#	endif
	}
#else

#	ifdef WIN32
	SYSTEMTIME systime;
	GetSystemTime(&systime);
	msec = systime.wMilliseconds;
#	endif

#endif
	time(&t);
	safe_localtime(&t,&atm);
	sprintf(tstr,"%04d-%02d-%02d %02d:%02d:%02d",atm.tm_year+1900, atm.tm_mon+1, atm.tm_mday, atm.tm_hour, atm.tm_min, atm.tm_sec);
	sprintf(tstr+19,".%.02d",msec/10);

	return tstr;
}


char *lib_comma(int num, char *ret, int bfsz)
{
	return lib_comma_real(num,',',ret,bfsz);
}

char *lib_comma_real(int num, char comma, char *ret, int bfsz)
/*
Convert num to a string with commas where appropriate.
*/
{
	char bf[100];
	char bf2[100];
	int l;
	char *p1,*p2;

	sprintf(bf,"%d",num);

	l=lib_strlen(bf);
	p1=bf;
	p2=bf2;
	while(l>0) {
		*(p2++)=*(p1++);
		l--;
		if (l>0 && l%3==0)
			*(p2++)=comma;
	}
	*p2=0;
	ncpy(ret,bf2,bfsz-1);
	return ret;
}
char *lib_double_string(double bytes, char *ret, int bfsz)
{
	return lib_double_string_real(bytes,',',ret,bfsz);
}

char *lib_double_string_real(double bytes, char comma, char *ret, int bfsz)
{
	char bf2[BFSZ];
	if (bytes<10000) {
		ncpy(ret,lib_comma_real((int) bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," byte");
		if (bytes!=1) strcpy(ret+lib_strlen(ret),"s");
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		ncpy(ret,lib_comma_real((int) bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," KB");
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		ncpy(ret,lib_comma_real((int)  bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," MB");
		return ret;
	}
	bytes/=1000;
	ncpy(ret,lib_comma_real((int)  bytes,comma,bf2,BFSZ),bfsz-10);
	strcpy(ret+lib_strlen(ret)," GB");
	return ret;
}

char *lib_bytes_string(int bytes, char *ret, int bfsz)
{
	return lib_bytes_string_real(bytes,',',ret,bfsz);
}

char *lib_bytes_string_real(int bytes, char comma, char *ret, int bfsz)
{
	char bf2[BFSZ];
	if (bytes<10000) {
		ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," byte");
		if (bytes!=1) strcpy(ret+lib_strlen(ret),"s");
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," KB");
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," MB");
		return ret;
	}
	bytes/=1000;
	ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
	strcpy(ret+lib_strlen(ret)," GB");
	return ret;
}

char *lib_bytes_string2(int bytes, char *ret, int bfsz)
{
	return lib_bytes_string2_real(bytes,',',ret,bfsz);
}

char *lib_bytes_string2_real(int bytes, char comma, char *ret, int bfsz)
{
	char bf2[BFSZ];
	if (bytes<10000) {
		ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," B");
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," KB");
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
		strcpy(ret+lib_strlen(ret)," MB");
		return ret;
	}
	bytes/=1000;
	ncpy(ret,lib_comma_real(bytes,comma,bf2,BFSZ),bfsz-10);
	strcpy(ret+lib_strlen(ret)," GB");
	return ret;
}
char *lib_bytes_string_simple(int bytes, char *ret, int bfsz)
{
	if (bytes<10000) {
		lprintf(ret,bfsz-1,"%d B",bytes);
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		lprintf(ret,bfsz-1,"%d KB",bytes);
		return ret;
	}
	bytes/=1000;
	if (bytes<10000) {
		lprintf(ret,bfsz-1,"%d MB",bytes);
		return ret;
	}
	bytes/=1000;
	lprintf(ret,bfsz-1,"%d GB",bytes);
	return ret;
}

char *lib_bytes_string_basic(int bytes, char *ret, int bfsz, int multiplier)
{
	char tmp[BFSZ];
	if (multiplier==1024) goto kb;
	if (multiplier==1000) goto kb;
	if (bytes<1000) {
//	Keep the display of message size in the surgeweb interface as plain as possible
//		lprintf(ret,bfsz,"%dB",bytes);
		lprintf(ret,bfsz,"1 KB");
		return ret;
	}
	if (bytes<2000) {
		lprintf(tmp,BFSZ,"%1.1f",(float)bytes/1000);
		if(tmp[2]=='0') tmp[1]='\0';
		lprintf(ret,bfsz,"%s KB",tmp);
		return ret;
	}
	bytes/=1000;
kb:
	if (bytes<1000) {
		lprintf(ret,bfsz,"%d KB",bytes);
		return ret;
	}
	if (bytes<2000) {
		lprintf(tmp,BFSZ,"%1.1f",(float)bytes/1000);
		if(tmp[2]=='0') tmp[1]='\0';
		lprintf(ret,bfsz,"%s MB",tmp);
		return ret;
	}
	bytes/=1000;
	if (bytes<1000) {	// intentionally slightly low, means 1gb quota is disaplayed correctly
		lprintf(ret,bfsz,"%d MB",bytes);
		return ret;
	}
	if (bytes<9999) {
		lprintf(tmp,BFSZ,"%1.1f",(float)bytes/1000);
		if(tmp[2]=='0') tmp[1]='\0';
		lprintf(ret,bfsz,"%s GB",tmp);
		return ret;
	}
	bytes/=1000;
	lprintf(ret,bfsz,"%d GB",bytes);
	return ret;
}



int lib_bytes_parse(char *s)
/*
Parses strings of the form
1,234 bytes
12 mb
2 Gbytes
40megabytes

returning number of bytes.
*/
{
	int b;
	char *p;
	b=0;

	p=s;
	while(*p) {
		if (sisdigit(*p)) {
			b*=10;
			b+=(*p)-'0';
		}
		p++;
	}

	if (strchr(s,'k') || strchr(s,'K')) b*=1000;
	else if (strchr(s,'m') || strchr(s,'M')) b*=(1000*1000);
	else if (strchr(s,'g') || strchr(s,'G')) b*=(1000*1000*1000);
	return b;
}

int lib_time_interval_parse(char *s)
/*
Parses strings of the form
1 second
5.2 mins
2hours
3 weeks
4months
and returns number of seconds.

We just look at first/second letter(s) after digits/decimal points stop.
If it's
mo , *60*60*24*30
m , *60
h , *60*60
d , *60*60*24
*/
{
	double v;
	v=atof(s);
	while (*s && (sisdigit(*s) || *s==' ' || *s=='\t' ||  *s=='.'))
		s++;
	if (*s=='m' || *s=='M') {
		if (*(s+1)=='o' || (*(s+1)=='O'))
			v=v*60*60*24*30;/* Months*/
		else
			v=v*60; /* minutes */
	}
	else if (*s=='h' || *s=='H') v=v*60*60; /*hours*/
	else if (*s=='d' || *s=='D') v=v*60*60*24; /*days*/
	else if (*s=='w' || *s=='W') v=v*60*60*24*7; /*weeks*/
	else if (*s=='y' || *s=='Y') v=v*60*60*24*365; /*years*/

	if (v>2147483646) v=2147483646;
	return ((int) v);

}

int lib_contains_dot_dot(char *fname)
/*
Checks if fname contains a filename or path consisting of just ..
Use instead of doing strstr(fname,"..") since file names like "F.A.Q..url" are valid names
*/
{
	char *p;
	for(p=fname;*p;p++) {
		if (p==fname || p[-1]=='\\' || p[-1]=='/') { /* if start of fname or previous char was a path-sep */
			if (p[0]=='.' && p[1]=='.') {
				if (p[2]=='/' || p[2]=='\\' || p[2]=='\0') return TRUE;
			}
		}
	}
	return FALSE;
}

int lib_count_dot_dot(char *fname)
/*
Checks how many .. pairs there are in a string
*/
{
	char *p;
	int count=0;
	for(p=fname;*p;p++) {
		if (p==fname || p[-1]=='\\' || p[-1]=='/') { /* if start of fname or previous char was a path-sep */
			if (p[0]=='.' && p[1]=='.') {
				if (p[2]=='/' || p[2]=='\\' || p[2]=='\0') count++;
			}
		}
	}
	return count;
}
int lib_count_chr(char *txt, int ch)
{
	char *p;
	int count=0;
	for(p=txt;*p;p++) {
		if (*p==ch) count++;
	}
	return count;
}


int lib_remove_with_retry(char *f)
{
	time_t start;
	if (lib_remove(f)==0) return 0;
	if (!lib_exists(f)) return 0;
	for(start=time(NULL);time(NULL)-start<5;) {
		lib_msleep(100);
		if (!lib_exists(f)) return 0;
		if (remove(f)==0) return 0;
	}
	return 1;
}

int lib_rename_with_retry(char *src, char *dst)
{
	time_t start;
	int ret;
	ret=rename(src,dst);
	if (ret==0) return 0;
	if (!lib_exists(src)) return 1;
	start=time(NULL);
	while(start>time(NULL)-5) {
		lib_msleep(100);
		ret=rename(src,dst);
		if (ret==0) return 0;
	}
	return ret;
}
int lib_identical(char *file1, char *file2)
{
	int ret=FALSE;
	char bf1[BFSZ];
	char bf2[BFSZ];
	size_t l1,l2;
	FILE *f1,*f2;
	f1 = fopen(file1,"rb");
	f2 = fopen(file2,"rb");
	if (f1==NULL) goto failed;
	if (f2==NULL) goto failed;
	for (;!feof(f1);) {
		l1 = fread(bf1,1,BFSZ,f1);
		l2 = fread(bf2,1,BFSZ,f2);
		if (l1!=l2) goto failed;
		if (memcmp(bf1,bf2,l1)!=0) goto failed;
	}
	ret = TRUE;
failed:
	if (f1) fclose(f1);
	if (f2) fclose(f2);
	return ret;
}

int dtrace_fake(char *s)
{
	return TRUE;
}
int lib_freespace_mb(char *s)
{
	return (int) (lib_freespace_big(s)/1024);
}
char *lib_remove_trailing_path_sep(char *fname)
{
	int len=lib_strlen(fname);
	if (len>0 && issep(fname[len-1]))
		fname[len-1]=0;
	return fname;
}

int lib_clear_file(char *fname)
{
	FILE *f;
	f=fopen(fname,"w");
	if (f) {
		fclose(f);
		return true;
	}
	return false;
}


char *strchr_last(char *txt, char c)
{
	char *last=NULL;
	while(*txt) {
		if (*txt==c) last=txt;
		txt++;
	}
	return last;
}
char *strchr_slast(char *txt, char c)
{
	char *s=txt;
	while(*s)s++;
	for(;s>txt;s--){
		if(*s==c) break;
	}
	for(s--;s>txt;s--){
		if(*s==c) break;
	}
	return s;
}

char *lib_replace(char *in, char *out, int outbfsz, char *from, char *to)
/*
Replace all occurances of "from" in "in" with "to". outputting to "out"
*/
{
	int flen=lib_strlen(from);
	int tlen=lib_strlen(to);
	char *ret=out;
	while(*in && outbfsz>tlen+2) {
		if (strncmp(in,from,flen)==0) {
			ncpy(out,to,outbfsz-1);
			in+=flen;
			out+=tlen;
			outbfsz-=tlen;
		}
		else {
			*out=*in;
			out++;
			outbfsz--;
			in++;
		}
	}
	*out=0;
	return ret;
}




int lib_replace_in_file(char *fname, char *src, char *dst)
{
	FILE *fin,*fout;
	char bf[BFSZ], bf2[BFSZ];
	char fname2[BFSZ];
	lprintf(fname2,BFSZ-1,"%s_new",fname);
	fin = fopen(fname,"rb");
	if (fin==NULL) return FALSE;
	fout = fopen(fname2,"wb");
	if (fout==NULL) {fclose(fin); return FALSE;}
	for (;!feof(fin);) {
		if (fgets(bf,BFSZ-1,fin)==NULL) break;
		lib_replace(bf,bf2,BFSZ-1,src,dst);
		fprintf(fout,"%s",bf2);
	}
	fclose(fin); fclose(fout);
	remove(fname);
	rename(fname2,fname);
	return TRUE;
}





time_t lib_parsedate_dayonly(char *rec)
{
	char bf[BFSZ];
	char *s;
	time_t r;
	// disregard time and timezone...
	ncpy(bf,rec,BFSZ-1);
	s = strchr(bf,':');
	if (s!=NULL) {
		for (;(s>bf) && (*s!=' ');s--) ;
		*s = 0;
	}
	r = lib_parsedate(bf);
	r += lib_timezone();
	return r;
}
char *last_strstr(const char *haystack, const char *needle)
{
    char *result = NULL;
    if (*needle == '\0')
        return (char *) haystack;

    for (;;) {
        char *p = strstr(haystack, needle);
        if (p == NULL)
            break;
        result = p;
        haystack = p + 1;
    }

    return result;
}


//22-Feb-2008 20:00:00 +0200
//22 Feb 2008 02:00:00 +0200
time_t lib_parsedate(char *rec)
{
	struct tm t;
	char bf[200];
	char *s;
	int tzone=0;
	int y;
	char *h;
//	char tmp3[BFSZ];
	time_t rt;

	ncpy(bf,rec,199); s = bf;
	memset(&t,0,sizeof(t));
	s = mystrtok(bf,"-, \n\t",&h);
	if (s==NULL) return 0;
	if (!sisdigit(*s)) {
		/* Must be optional Day, ignore it!! */
		s = mystrtok(NULL,"-, \n\t",&h); if (s==NULL) return 0;
	}
	t.tm_mday = atoi(s);
	if (t.tm_mday==0) return 0;
	s = mystrtok(NULL,"- \n\t",&h); if (s==NULL) return 0;
	t.tm_mon = lib_month_find(s); /* returns 0-11 */
	if (t.tm_mon<0) return 0;
	s = mystrtok(NULL,"- \n\t",&h); if (s==NULL) return 0;
	y = atoi(s);
	if (y<50) y += 100;  /* Allow 00 = 2000*/
	if (y>200) y -= 1900; /* Allow 2010 = 110 */
	t.tm_year = y;
	if (t.tm_year==0) return 0;
	s = mystrtok(NULL," \r\n\t",&h); if (s==NULL) goto done;
	if (!lib_get_hour(s,&t.tm_hour,&t.tm_min,&t.tm_sec)) goto done;

	s = mystrtok(NULL," \r\n\t",&h);
	if (s==NULL) { /* Should have been a time zone, lets assume GMT */
		tzone = 0;
	} else {
		tzone = lib_get_zone(s);
	}

//printf("got zone %d\n",tzone);
	if (s!=NULL) s = mystrtok(NULL," \r\n\t",&h);
	if (s!=NULL) tzone += lib_get_zone(s); /*Offset from time zone*/
	if (tzone > (60*60*27) ) tzone = 0;
	if (tzone < (-60*60*27) ) tzone = 0;
//printf("Tzone given (%d)\n",tzone/(60*60));
    t.tm_hour -= (tzone/(60*60));
    t.tm_min -= ((tzone % (60*60))/60);  // chrisp changed these to negatives...
done:
	//t.tm_isdst = -1;
	//show_tm("mktime1",&t);
	rt = mktime(&t);
	//show_tm("mktime2",&t);
	if (rt == -1) if (t.tm_year>99)  { /* Try and skip past daylight saving black holes */
		t.tm_hour+=2;
		rt = mktime(&t);
	}
//	rt -= lib_timezone_nodaylight(); // this one seemed right. but on windows, during nz daylight saving, it was  wrong.
//	rt += lib_gmt_offset_at(rt);
	rt += lib_gmt_offset_now();
//	printf("offset now %d\n",lib_gmt_offset_now());
	// when was the other one wrong ?
	//rt -= lib_timezone_win(); // this one seemed right.

	// Lets log the output
//	printf("%s converted to %d = %s (timezone %d)\n",rec,rt,lib_mail_time(tmp3,rt),lib_gmt_offset_now()/60);
//	printf("%s converted to %d = %s (timezone %d)\n",rec,rt,lib_mail_time(tmp3,rt),lib_timezone_nodaylight()/60);
	return rt;
}
int lib_get_hour(char *rec, int *hour, int *min, int *sec)
{
	char hours[200],bf[50];
	char *s2,*s3;

	*hour = 0; *min = 0; *sec = 0;
	ncpy(hours,rec,190);
	s2 = strchr(hours,':');
	if (s2==NULL) { /* Maybe it's 4 digit format, no colon's */
		if (lib_strlen(hours)==2) {
			*hour = atoi(hours); return TRUE;
		}
		if (lib_strlen(hours)==3) {
			ncpy(bf,hours,1);
		 	*hour = atoi(bf); return TRUE;
		}
		if (lib_strlen(hours)!=4) return FALSE;
		ncpy(bf,hours,2); *hour = atoi(bf);
		ncpy(bf,hours+2,2); *min = atoi(bf);
		*sec = 0;
		return TRUE;
	}
	s3 = strchr(s2+1,':');
	*hour = atoi(hours);
	*min = atoi(s2+1);
	*sec = 0;
	if (s3!=NULL) *sec = atoi(s3+1);
	return TRUE;
}
int lib_get_zone(char *rec) /* return time zone offset in seconds, GMT = 0 */
{
	int tsign = 1;
	int h,m,sec;

	if (*rec=='+') rec++;
	if (*rec=='-') {tsign = -1; rec++;}
	if (sisdigit(*rec)) {
		if (!lib_get_hour(rec,&h,&m,&sec)) return 0;
		return tsign*(h*60*60+m*60+sec);
	} else {
		return lib_tzone_find(rec);
	}
}

typedef struct {char *name; int type; int value;} TABLE;
#define tZONE 0
#define tDAYZONE 1
#define HOUR(x) (x*60)

static TABLE	tzone_table[] = {
    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
    { "ces",	tDAYZONE,  -HOUR(2) },	/* Central European Summer */
    { "cest",	tDAYZONE,  -HOUR(2) },	/* Central European Summer */
    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
    { "mezt",	tDAYZONE,  -HOUR(2) },	/* Middle European Summer */
    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
    { "msd",	tDAYZONE,  -HOUR(4) },	/* Moscow Summer */
    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
    { "wadt",	tDAYZONE,  -HOUR(9) },	/* West Australian Daylight */
    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
    { "kdt",	tZONE,     -HOUR(10) },	/* Korean Daylight */
    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
    { "nzdt",	tDAYZONE,  -HOUR(13) },	/* New Zealand Daylight */
    { NULL,	tDAYZONE,  0 }	/* End of table */
};

int lib_tzone_find(char *s)
{
	char bf[100];
	int i;
	ncpy(bf,s,30);
	strlwr(bf);
	if (lib_strlen(bf)==1) { /* Military time zones, could have sign wrong*/
		if (*bf>='a' && *bf<='m') return - (int) ((bf[0]-'a'+1)*60);
		if (*bf>='n' && *bf<='y') return (bf[0]-'n'+1)*60;
		return 0;
	}
	for (i=0; tzone_table[i].name!=NULL; i++) {
		if (strcmp(bf,tzone_table[i].name)==0)
			return tzone_table[i].value*60;
	}
	return 0;
}


static int max_handle;
int lib_max_handle(void)
{
	return max_handle;
}


void lib_assert_failed(char *file, int line)
{
	if (vemsg) vemsg("assertion failed in %s:%d\n",file,line);
	lib_abort();
}

// This function must precede lib.h as it uses the handle stdio...
char *read_line(char *bf, int bfsz, int hide) {
	unsigned char c;
	int i;

	for(i = 0; i < bfsz; ) {
		c = getch();
		if (c == 0xE0) { c = getch(); continue;	}
		if (c == 0x3) {
			raise(SIGINT);
			continue;
		}
		if (c == '\b' || c == 0x7F) {
			if (i) {
				i--;
				fprintf(stdout,"\b \b");
				fflush(stdout);
			}
			continue;
		}
		if (c == '\n' || c == '\r') {
			fputc('\n',stdout);
			fflush(stdout);
			break;
		}
		if (!sisprint(c)) continue;
		bf[i++] = c;
		if (hide) fputc('*',stdout);
		else fputc(c,stdout);
		fflush(stdout);
	}
	bf[i] = '\0';
	return bf;
}


int lib_split_name(char *s, char *first, char *last)
{
	char *p;

	p=strchr(s,',');
	if(p){	// "Last, First" syntax
		ncpy(last,s,BFSZ-1);
		p = strchr_last(last,',');
		if(p && *(p+1)){
			*p='\0'; p++;
			for(;*p==' ';p++);
			ncpy(first,p,BFSZ-1);	//QQQ
		}else{
			ncpy(first,"",BFSZ-1);
		}
	}else{	// "First Last" syntax
		ncpy(first,s,BFSZ-1);
		p = strchr_last(first,' ');
		if(p && p+1){
			*p='\0'; p++;
			ncpy(last,p,BFSZ-1);	//QQQ
		}else{
			ncpy(last,"",BFSZ-1);
		}
	}
	return TRUE;
}

int lib_space_before(char *bf, char *s)
{
	char *p;
	for (p=bf;p<s;p++){
		if (sisspace(*p)){
			return TRUE;
		}
	}
	return FALSE;
}

#include "thread.h"
#ifdef DO_THREADS
static Mutex *m;
#endif 
int lib_mutex_wait(void)
{
#ifdef DO_THREADS
		if (m==NULL) m = th_mutex_new("liblib");
		if (!th_mutex_wait(m,300000)) { /* 10 minutes */
			vemsg("lib_mutex_wait fialing:\n");
			lib_abort();
		}
#endif
	return TRUE;
}
void lib_mutex_release(void)
{
#ifdef DO_THREADS
	if (!th_mutex_release(m)) {
		vemsg("Cannot free mutex, fatal problem\n");
		lib_abort();
	}
#endif
}

static int wildused;
int wild_count(int n)
{
	lib_mutex_wait();
	wildused+=n;
	lib_mutex_release();
	return wildused;
}











static unsigned int m_w;
static unsigned int m_z;

void my_setseed(unsigned int u, unsigned int v)
{
    if (u != 0) m_w = u; 
    if (v != 0) m_z = v;
}

unsigned int my_randint(void)
{
	//lib_mutex_wait();  Strictly speaking should use a mutx, but worst case is we over-rwrite mz or mw and that's ok too...
    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
	//lib_mutex_release();
    return (m_z << 16) + m_w;
}
int myrand(void)
{
	unsigned int r = my_randint() % 32767;
	return r;
}














int safei(int i, int max, char *tag)
{
	if (i<0) {
		if (vemsg) vemsg("safei abort[%s] i=%d max=%d\n",tag,i,max);
		lib_abort();
	}
	if (i>=max) {
		if (vemsg) vemsg("safei abort2[%s] i=%d max=%d\n",tag,i,max);
		lib_abort();
	}
    return i;
}

/*
add new code here
*/


/*


#ifdef EMSG_MILLISEC
	{
#	ifdef OSX
		struct timeval the_time;
		struct timezone tzp;
		gettimeofday(&the_time, &tzp);
		msec = the_time.tv_usec / 1000;
#	else
#		ifdef WIN32
		struct _timeb timebuffer;
		_ftime( &timebuffer );
#		else
		struct timeb timebuffer;
		ftime( &timebuffer );
#		endif
		msec = timebuffer.millitm;
#	endif
	}
#else

#	ifdef WIN32
	SYSTEMTIME systime;
	GetSystemTime(&systime);
	msec = systime.wMilliseconds;
#	endif

#endif









*/

/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
#ifdef WIN32
#include <io.h>
#include <fcntl.h>
/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
//#ifndef USE_SOL
FILE* spawnfopen(const char *filename, const char *mode)
{
	int h;
	int commodeset = 0;
	int scanset    = 0;
	int whileflag, pmode;
	int flags = 0;


	const char *xmode=mode;
	switch (*mode) {
	case ('r'):
		flags = O_RDONLY;
		break;
	case ('w'):
		flags = O_WRONLY | O_CREAT | O_TRUNC;
		break;
	case ('a'):
		flags = O_WRONLY | O_CREAT | O_APPEND;
		break;
	default:
		return(NULL);
	}

	/* There can be up to three more optional mode characters:
	(1) A single '+' character,
	(2) One of 't' and 'b' and
	(3) One of 'c' and 'n'.
	*/

	whileflag=1;

	while(*++mode && whileflag)
	switch(*mode) {

	case ('+'):
		if (flags & O_RDWR)
			whileflag=0;
		else {
			flags |= O_RDWR;
			flags &= ~(O_RDONLY | O_WRONLY);
		}
		break;

	case ('b'):
		if (flags & (O_TEXT | O_BINARY))
			whileflag=0;
		else
			flags |= O_BINARY;
		break;

	case ('t'):
		if (flags & (O_TEXT | O_BINARY))
			whileflag=0;
		else
			flags |= O_TEXT;
		break;

	case ('c'):
		if (commodeset)
			whileflag=0;
		else {
			commodeset = 1;
		}
		break;

	case ('n'):
		if (commodeset)
			whileflag=0;
		else {
			commodeset = 1;
		}
		break;
#ifndef __BORLANDC__
	case ('S'):
		if (scanset)
			whileflag=0;
		else {
		scanset = 1;
			flags |= _O_SEQUENTIAL;
		}
		break;

	case ('R'):
		if (scanset)
			whileflag=0;
		else {
			scanset = 1;
			flags |= _O_RANDOM;
		}
		break;

	case ('T'):
		if (flags & _O_SHORT_LIVED)
			whileflag=0;
		else
			flags |= _O_SHORT_LIVED;
		break;

	case ('D'):
		if (flags & _O_TEMPORARY)
			whileflag=0;
		else
			flags |= _O_TEMPORARY;
		break;
#endif
	default:
		whileflag=0;
		break;
	}

	if ( (flags & O_CREAT) && (flags & O_RDONLY) ) pmode = S_IREAD; //# BUG, ZERO VALUED fLAG logical operation fails.  
	else pmode = S_IREAD | S_IWRITE;

	flags |= O_NOINHERIT;
	h = open(filename, flags, pmode);
	if (h<0) return NULL;
	if (h>max_handle) max_handle = h;
#ifdef USE_DFOPEN
//	dfopen_hist(h,-10,__FILE__,__LINE__);
#endif

	return _fdopen(h, xmode);
}
/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
int spawnfclose_x(FILE *f, char * src, int line)
{
#undef fclose
#ifdef USE_DFOPEN
//	dfopen_hist(fileno(f),15,src,line);
#endif
	return fclose(f);
}
//#endif
/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/

#else
void lib_close_on_exec(int fd);
#ifdef fopen
#undef fopen
#endif
#ifdef fclose
#undef fclose
#endif


/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
FILE* spawnfopen(const char *filename, const char *mode)
{
	FILE *f;
	int m=FALSE;
	int h;
	/* IF setegid is system wide, then all fopens must only occur when we have the mutex */
#ifdef DO_THREADS
#ifdef BAD_SETEGID
#include "../dftp/common/mutex.h"
	if (need_mutex) {mutex_wait(M_SETEGID); m = TRUE;}
#endif
 #endif /*DO_THREADS*/
	f = fopen(filename,mode);
#ifdef DO_THREADS
#ifdef BAD_SETEGID
	if (m) mutex_release(M_SETEGID);
#endif
#endif /*DO_THREADS*/
	if (f==NULL) return f;
    lib_close_on_exec(fileno(f));
	h = fileno(f);
	if (h>max_handle) max_handle = h;

#ifdef USE_DFOPEN
//	dfopen_hist(fileno(f),-10,__FILE__,__LINE__);
#endif
			;
	return f;
}
/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
void lib_close_on_exec(int fd)
{
       if (fcntl(fd,F_SETFD,1)==-1){
       }
}
/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
int spawnfclose_x(FILE *f, char *src, int line)
{
	int r;
	int n = fileno(f);
#ifdef USE_DFOPEN
//	dfopen_hist(fileno(f),15,src,line);
#endif
	r = fclose(f);
	if (r!=0) if (errno==EBADF) if (vemsg) vemsg("dfclose %s:%d h=%d error %s\n",src,line,n,strerror(errno));
	return r;
}
#endif

/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
int spawnfcloseh(FILE *f)
{
	if (f!=NULL) hcount_close(fileno(f));
	return spawnfclose(f);
}
int strcmpnc_test(char *a1, char *a2)
{
	if (strcmpnc(a1,a2)!=strcmpnc_old(a1,a2)) printf("FAILED STRCMPNC TESTS (%s) (%s) %d %d\n",a1,a2,strcmpnc(a1,a2),strcmpnc_old(a1,a2));
	if (strcmpnc(a1,a2)!=strcmpnc_old(a1,a2)) vemsg("FAILED STRCMPNC TESTS (%s) (%s) %d %d\n",a1,a2,strcmpnc(a1,a2),strcmpnc_old(a1,a2));
	return TRUE;
}

#ifdef strcmpnc
#undef strcmpnc
#undef strncmpnc
#endif

int strcmpnc(char *s1, char *s2)
{
        const unsigned char *cm = charmap,
             *us1 = (const unsigned char *)s1,
             *us2 = (const unsigned char *)s2;

        while (cm[*us1] == cm[*us2++])
                if (*us1++ == '\0')
                        return (0);
        return (cm[*us1] - cm[*--us2]);
}
int strcmpnc_old(char *s1, char *s2)
{
        const unsigned char *cm = charmap,
             *us1 = (const unsigned char *)s1,
             *us2 = (const unsigned char *)s2;

        while (cm[*us1] == cm[*us2++])
                if (*us1++ == '\0')
                        return (0);
        return (cm[*us1] - cm[*--us2]);
}
int strncmpnc(const char *s1, const char *s2, size_t n)
{
    const unsigned char *cm = charmap,
         *us1 = (const unsigned char *)s1,
         *us2 = (const unsigned char *)s2;

    while (cm[*us1] == cm[*us2++]) {
		n--;
		if (*us1++ == '\0')
			return (0);
		if (n==0) return 0;
	}
    return (cm[*us1] - cm[*--us2]);
}

#ifdef FAST_TIME
#undef time
#undef gettimeofday
#undef GetSystemTime
static int use_fast=FALSE;
static time_t saved_time;
#ifndef WIN32
static struct timeval saved_tv;
static struct timezone saved_tz;
#else
static SYSTEMTIME saved_systime;
#endif
int fast_store(int useit)
{
	use_fast = useit;
	saved_time = time(NULL);
#ifndef WIN32
	gettimeofday(&saved_tv,NULL);
#else
	GetSystemTime(&saved_systime);
#endif
	return TRUE;
}
time_t fast_time(time_t *tloc)
{
	if (use_fast) {
		if (tloc) *tloc = saved_time;
		return saved_time;
	}
	return time(tloc);
}
#ifndef WIN32
int fast_gettimeofday(struct timeval *tv, struct timezone *tz)
{
	if (use_fast) {
		if (tv!=NULL) memcpy(tv,&saved_tv,sizeof(struct timeval));
		return 0;
	}
	return gettimeofday(tv,tz);
}
#else
int fast_GetSystemTime(SYSTEMTIME *systime)
{
	if (use_fast) {
		if (systime!=NULL) memcpy(systime,&saved_systime,sizeof(SYSTEMTIME));
		return 0;
	}
	GetSystemTime(systime);
	return 0;
}
#endif

#endif


// so the following code will not use dfopen and will work during crash of dfopen :-)
#undef fopen
#undef fclose
void lib_abort_real(char *src, int line)
{
	FILE *f;
	static int done;

	if (!done) {
		done = TRUE;
		f = fopen(lib_startstop_logfile(),"a");
		if (f!=NULL) {
			fprintf(f,"lib_abort called %s:%d %p %p\n",src,line,lib_abort_fn2,lib_abort_fn);
			fclose(f);
		}
		if (lib_abort_fn2!=NULL) lib_abort_fn2(src,line);
		if (lib_abort_fn!=NULL) lib_abort_fn();
	}
#ifdef WIN32
	{
		char *s=NULL;
		fflush(NULL);
		*s = 0;
	}
	abort();
//	exit(0);
#else
	abort();
#endif
}


/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/
#define DONE_INCLUDES 1
#include "safelib.c"



/* DO NOT ADD CODE WITH FCLOSE UNDER HERE, IT WILL BREAK;*/


