/*
 *	Various utilities
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *		(C) 2001 Marek 'Marx' Grac
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#include "types.h"

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

/*
 * return minimum/maximum of two numbers
 */
unsigned int uns_min(unsigned int a, unsigned int b)
{
	return a < b ? a : b;
}

signed int int_min(signed int a, signed int b)
{
	return a < b ? a : b;
}

signed long int long_max(signed long int a, signed long int b)
{
	return a > b ? a : b;
}

signed long int long_min(signed long int a, signed long int b)
{
	return a < b ? a : b;
}

off_t off_max(off_t a, off_t b)
{
	return a > b ? a : b;
}

off_t off_min(off_t a, off_t b)
{
	return a < b ? a : b;
}

ptrdiff_t diff_max(ptrdiff_t a, ptrdiff_t b)
{
	return a > b ? a : b;
}

ptrdiff_t diff_min(ptrdiff_t a, ptrdiff_t b)
{
	return a < b ? a : b;
}

/* Versions of 'read' and 'write' which automatically retry when interrupted */
ptrdiff_t joe_read(int fd, void *buf, ptrdiff_t size)
{
	ptrdiff_t rt;

	do {
		rt = read(fd, buf, (size_t)size);
	} while (rt < 0 && errno == EINTR);
	return rt;
}

ptrdiff_t joe_write(int fd, const void *buf, ptrdiff_t size)
{
	ptrdiff_t rt;

	do {
		rt = write(fd, buf, (size_t)size);
	} while (rt < 0 && errno == EINTR);
	return rt;
}

int joe_ioctl(int fd, unsigned long req, void *ptr)
{
	int rt;
	do {
#ifdef __CYGWIN__
		rt = ioctl(fd, (int)req, ptr);
#else
		rt = ioctl(fd, req, ptr);
#endif
	} while (rt == -1 && errno == EINTR);
	return rt;
}

/* Heap checking versions of malloc() */

/* #define HEAP_CHECK 1 */

#ifdef HEAP_CHECK

struct mcheck {
	struct mcheck *next;
	int state;		/* 0=malloced, 1=free */
	int size;		/* size in bytes */
	int magic;
};

struct mcheck *first;
struct mcheck *last;

void check_malloc()
{
	struct mcheck *m;
	int y = 0;
	for(m=first;m;m=m->next) {
		char *ptr = (char *)m+SIZEOF(struct mcheck);
		int x;
		if(m->magic!=0x55AA55AA) {
			printf("corrupt heap: head %x\n",ptr);
			*(int *)0=0;
		}
		for(x=0;x!=16384;++x)
			if(ptr[x+m->size]!=0xAA) {
				printf("Corrupt heap: tail %x\n",ptr);
				*(int *)0=0;
			}
		if(m->state)
			for(x=0;x!=m->size;++x)
				if(ptr[x]!=0x41) {
					printf("Corrupt heap: modified free block %x\n",ptr);
					*(int *)0=0;
				}
		++y;
	}
}

void *joe_malloc(int size)
{
	struct mcheck *m = (struct mcheck *)malloc(size+SIZEOF(struct mcheck)+16384);
	char *ptr = (char *)m+SIZEOF(struct mcheck);
	int x;
	m->state = 0;
	m->size = size;
	m->magic=0x55AA55AA;
	m->next = 0;

	if(!size) {
		printf("0 passed to malloc\n");
		*(int *)0=0;
	}
	for(x=0;x!=16384;++x)
		ptr[x+size]=0xAA;
	if(first) {
		last->next = m;
		last = m;
	} else {
		first = last = m;
	}
	check_malloc();
	/* printf("malloc size=%d addr=%x\n",size,ptr); fflush(stdout); */
	return ptr;
}

void *joe_calloc(int nmemb, int size)
{
	int sz = nmemb*size;
	int x;
	char *ptr;
	ptr = joe_malloc(sz);
	for(x=0;x!=sz;++x)
		ptr[x] = 0;
	return ptr;
}

void *joe_realloc(void *ptr, int size)
{
	struct mcheck *n;
	char *np;
	struct mcheck *m = (struct mcheck *)((char *)ptr-SIZEOF(struct mcheck));

	if(!size) {
		printf("0 passed to realloc\n");
		*(int *)0=0;
	}

	np = joe_malloc(size);

	n = (struct mcheck *)(np-SIZEOF(struct mcheck));

	if(m->size>size)
		memcpy(np,ptr,size);
	else
		memcpy(np,ptr,m->size);

	joe_free(ptr);

	return np;
}

void joe_free(void *ptr)
{
	struct mcheck *m = (struct mcheck *)((char *)ptr-SIZEOF(struct mcheck));
	int x;
	if (m->magic!=0x55AA55AA) {
		printf("Free non-malloc block %x\n",ptr);
		*(int *)0=0x0;
	}
	if (m->state) {
		printf("Double-free %x\n",ptr);
		*(int *)0=0x0;
	}
	for(x=0;x!=m->size;++x)
		((char *)ptr)[x]=0x41;
	m->state = 1;
	check_malloc();
	/* printf("free=%x\n",ptr); */
}

#else

/* Normal malloc() */

void *joe_malloc(ptrdiff_t size)
{
	void *p = malloc((size_t)size);
	if (!p)
		ttsig(-1);
	return p;
}

void *joe_calloc(ptrdiff_t nmemb,ptrdiff_t size)
{
	void *p = calloc((size_t)nmemb, (size_t)size);
	if (!p)
		ttsig(-1);
	return p;
}

void *joe_realloc(void *ptr,ptrdiff_t size)
{
	void *p = realloc(ptr, (size_t)size);
	if (!p)
		ttsig(-1);
	return p;
}

void joe_free(void *ptr)
{
	free(ptr);
}

#endif

/* Cast insensitive comparison: Only if you know for sure that the text is ASCII */

int zicmp(const char *a, const char *b)
{
	char ca;
	char cb;
	while (*a && *b) {
		ca = *a;
		cb = *b;
		if (ca >= 'a' && ca <= 'z')
			ca = (char)(ca + ('A' - 'a'));
		if (cb >= 'a' && cb <= 'z')
			cb = (char)(cb + ('A' - 'a'));
		if (ca > cb)
			return 1;
		if (ca < cb)
			return -1;
		++a;
		++b;
	}
	if (*a)
		return 1;
	if (*b)
		return -1;
	return 0;
}

/* Tags file string matcher */
/* a can be something like: "class::subclass::member"
 * b can be any of these and will still match:
 *    member
 *    ::member
 *    subclass::member
 *    ::subclass::member
 *    class::subclass::member
 */

int zmcmp(const char *a, const char *b)
{
	ptrdiff_t x = zlen(a);
	do {
		if (a[x] == ':' && a[x + 1] == ':')
			if ((b[0] == ':' && !strcmp(a + x, b)) || !strcmp(a + x + 2, b))
				return 0;
	} while (x--);
	return strcmp(a, b);
}

int zncmp(const char *a, const char *b, ptrdiff_t len)
{
	return strncmp(a, b, (size_t)len);
}


char *zdup(const char *bf)
{
	ptrdiff_t size = zlen(bf);
	char *p = (char *)joe_malloc(size+1);
	memcpy(p, bf, (size_t)(size + 1));
	return p;
}

#if 0
char *zcpy(char *a, char *b)
{
	strcpy(a,b);
	return a;
}
#endif

char *zncpy(char *a, const char *b, ptrdiff_t len)
{
	strncpy(a,b,(size_t)len);
	return a;
}

/* Copy b into buffer a of length len.  A will always end up NUL terminated. */

char *zlcpy(char *a, ptrdiff_t len, const char *b)
{
	char *org = a;
	if (!len) {
		fprintf(stderr, "zlcpy called with len == 0\n");
		exit(1);
	}
	--len;
	while (len && *b) {
		*a++ = *b++;
		--len;
	}
	*a = 0;
	return org;
}

void *mcpy(void *a, const void *b, ptrdiff_t len)
{
	if (len)
		memcpy(a, b, (size_t)len);
	return a;
}

#if 0
char *zcat(char *a, char *b)
{
	strcat(a,b);
	return a;
}
#endif

char *zlcat(char *a, ptrdiff_t siz, const char *b)
{
	char *org = a;
	while (*a && siz) {
		++a;
		--siz;
	}
	zlcpy(a, siz, b);
	return org;
}

/* Convert ASCII number to off_t */

off_t zhtoo(const char *s)
{
	off_t val = 0;
	int flg = 0;
	if (s) {
		if (*s == '-') {
			++s;
			flg = 1;
		} else if (*s == '+') {
			++s;
		}
		while ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) {
			if (*s >= '0' && *s <= '9')
				val = val * 16 + (off_t)(*s - '0');
			else if (*s >= 'a' && *s <= 'f')
				val = val * 16 + (off_t)(*s - 'a' + 10);
			else if (*s >= 'A' && *s <= 'F')
				val = val * 16 + (off_t)(*s - 'A' + 10);
			++s;
		}
	}
	if (flg)
		return -val;
	else
		return val;
}

off_t ztoo(const char *s)
{
	off_t val = 0;
	int flg = 0;
	if (s) {
		if (*s == '-') {
			++s;
			flg = 1;
		} else if (*s == '+') { /* Why is this needed? */
			++s;
		}
		if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
			s += 2;
			while ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) {
				if (*s >= '0' && *s <= '9')
					val = val * 16 + (off_t)(*s - '0');
				else if (*s >= 'a' && *s <= 'f')
					val = val * 16 + (off_t)(*s - 'a' + 10);
				else if (*s >= 'A' && *s <= 'F')
					val = val * 16 + (off_t)(*s - 'A' + 10);
				++s;
			}
		} else if (s[0] == '0') {
			while (*s >= '0' && *s <= '7') {
				val = val * 8 + (off_t)(*s - '0');
				++s;
			}
		} else {
			while (*s >= '0' && *s <= '9') {
				val = val * 10 + (off_t)(*s - '0');
				++s;
			}
		}
	}
	if (flg)
		return -val;
	else
		return val;
}

long ztol(const char *s)
{
	off_t val = ztoo(s);
	return (long)val;
}

int ztoi(const char *s)
{
	off_t val = ztoo(s);
	return (int)val;
}

ptrdiff_t ztodiff(const char *s)
{
	off_t val = ztoo(s);
	return (ptrdiff_t)val;
}

long zhtol(const char *s)
{
	off_t val = zhtoo(s);
	return (long)val;
}

int zhtoi(const char *s)
{
	off_t val = zhtoo(s);
	return (int)val;
}

ptrdiff_t zhtodiff(const char *s)
{
	off_t val = zhtoo(s);
	return (ptrdiff_t)val;
}

/* Compare zero-terminated strings of ints */

int Zcmp(const int *a, const int *b)
{
	while (*a && *b && *a == *b) {
		++a;
		++b;
	}
	if (*a > *b)
		return 1;
	else if (*a < *b)
		return -1;
	else
		return 0;
}

/* Copy b into buffer a of length len.  A will always end up NUL terminated. */

int *Zlcpy(int *a, ptrdiff_t len, const int *b)
{
	int *org = a;
	if (!len) {
		fprintf(stderr, "Zlcpy called with len == 0\n");
		exit(1);
	}
	--len;
	while (len && *b) {
		*a++ = *b++;
		--len;
	}
	*a = 0;
	return org;
}

/* Convert ints to chars */

char *Ztoz(char *a, ptrdiff_t len, const int *b)
{
	char *org = a;
	if (!len) {
		fprintf(stderr, "Ztoz called with len == 0\n");
		exit(1);
	}
	--len;
	while (len && *b) {
		*a++ = TO_CHAR_OK(*b++);
		--len;
	}
	*a = 0;
	return org;
}

/* Convert ints to utf8 */

char *Ztoutf8(char *a, ptrdiff_t len, const int *b)
{
	char *org = a;
	if (!len) {
		fprintf(stderr, "Ztoz called with len == 0\n");
		exit(1);
	}
	--len;
	while (len && *b) {
		char bf[8];
		ptrdiff_t enc = utf8_encode(bf, *b++);
		ptrdiff_t x;
		if (enc < len) {
			for (x = 0; x != enc; ++x) {
				*a++ = bf[x];
				--len;
			}
		}
	}
	*a = 0;
	return org;
}

/* Length of an int string */

ptrdiff_t Zlen(const int *s)
{
	ptrdiff_t len = 0;
	for (;;) {
		if (!s[0]) return 0 + len;
		if (!s[1]) return 1 + len;
		if (!s[2]) return 2 + len;
		if (!s[3]) return 3 + len;
		if (!s[4]) return 4 + len;
		if (!s[5]) return 5 + len;
		if (!s[6]) return 6 + len;
		if (!s[7]) return 7 + len;
		s += 8;
		len += 8;
	}
}

/* Duplicate an int string */

int *Zdup(const int *bf)
{
	ptrdiff_t size = (1 + Zlen(bf)) * SIZEOF(int);
	int *p = (int *)joe_malloc(size);
	memcpy(p, bf, (size_t)size);
	return p;
}

#ifndef SIG_ERR
#define SIG_ERR ((sighandler_t) -1)
#endif

/* wrapper to hide signal interface differences */
int joe_set_signal(int signum, sighandler_t handler)
{
	int retval;
#ifdef HAVE_SIGACTION
	struct sigaction sact;

	mset((char *)&sact, 0, SIZEOF(sact));
	sact.sa_handler = handler;
#ifdef SA_INTERRUPT
	sact.sa_flags = SA_INTERRUPT;
#endif
	retval = sigaction(signum, &sact, NULL);
#elif defined(HAVE_SIGVEC)
	struct sigvec svec;

	mset(&svec, 0, SIZEOF(svec));
	svec.sv_handler = handler;
#ifdef HAVE_SV_INTERRUPT
	svec.sv_flags = SV_INTERRUPT;
#endif
	retval = sigvec(signum, &svec, NULL);
#else
	retval = (signal(signum, handler) != SIG_ERR) ? 0 : -1;
#ifdef HAVE_SIGINTERRUPT
	siginterrupt(signum, 1);
#endif
#endif
	return(retval);
}

/* Helpful little parsing utilities */

/* Skip whitespace and return first non-whitespace character or zero for end of line */

int parse_ws(const char **pp,int cmt)
{
	const char *p = *pp;
	while (*p==' ' || *p=='\t')
		++p;
	if (*p == cmt || *p == '\n' || *p == '\r') {
		while (*p)
			++p;
	}
/* 	if (*p=='\r' || *p=='\n' || *p==cmt)
		*p = 0; */
	*pp = p;
	return *p;
}

/* Parse whitespace, but leave pointer at line ending */

int parse_wsn(const char **pp,int cmt)
{
	const char *p = *pp;
	while (*p==' ' || *p=='\t')
		++p;
	if (*p == cmt) {
		while (*p && *p != '\r' && *p != '\n')
			++p;
	}
	*pp = p;
	return *p;
}

/* Skip whitespace, including line endings */

int parse_wsl(const char **pp,int cmt)
{
	const char *p = *pp;
	for (;;) {
		while (*p==' ' || *p=='\t' || *p == '\r' || *p == '\n')
			++p;
		if (*p == cmt) {
			while (*p && *p != '\r' && *p != '\n')
				++p;
		} else {
			break;
		}
	}
	*pp = p;
	return *p;
}

/* Parse an identifier into a buffer.  Identifier is truncated to a maximum of len-1 chars. */

int parse_ident(const char * *pp, char *buf, ptrdiff_t len)
{
	const char *p = *pp;
	const char *q;
	int c;

	q = p;
	if (joe_isalpha_(utf8_map, (c = utf8_decode_fwrd(&q, NULL)))) {
		do {
			char bf[8];
			ptrdiff_t enc = utf8_encode(bf, c);
			ptrdiff_t x;
			if (enc + 1 <= len) {
				for (x = 0; x != enc; ++x) {
					*buf++ = bf[x];
					--len;
				}
				*buf = 0;
			}
			p = q;
		} while (joe_isalnum_(utf8_map, (c = utf8_decode_fwrd(&q, NULL))));
		*pp = p;
		return 0;
	} else
		return -1;
}

/* Parse to next whitespace */

int parse_tows(const char * *pp, char *buf)
{
	const char *p = *pp;
	while (*p && *p!=' ' && *p!='\t' && *p!='\n' && *p!='\r' && *p!='#')
		*buf++ = *p++;

	*pp = p;
	*buf = 0;
	return 0;
}

/* Parse over a specific keyword */

int parse_kw(const char * *pp, const char *kw)
{
	const char *p = *pp;
	while(*kw && *kw==*p)
		++kw, ++p;
	if(!*kw && !(joe_isalnum_(utf8_map, utf8_decode_string(p)))) {
		*pp = p;
		return 0;
	} else
		return -1;
}

/* Parse a field (same as parse_kw, but string must be terminated with whitespace) */

int parse_field(const char **pp, const char *kw)
{
	const char *p = *pp;
	while(*kw && *kw==*p)
		++kw, ++p;
	if(!*kw && (!*p || *p==' ' || *p=='\t' || *p=='#' || *p=='\n' || *p=='\r')) {
		*pp = p;
		return 0;
	} else
		return -1;
}

/* Parse a specific character */

int parse_char(const char **pp, char c)
{
	const char *p = *pp;
	if (*p == c) {
		*pp = p+1;
		return 0;
	} else
		return -1;
}

/* Parse an integer.  Returns 0 for success. */

const char *skip_digits(const char *p)
{
	if (*p == '-')
		++p;
	else if (*p == '+') /* Because ztoo has it */
		++p;
	if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) {
		p += 2;
		while ((*p >= '0' && *p <= '9') || ((*p & ~32) >= 'A' && (*p & ~32) <= 'F'))
			++p;
	} else if (*p == '0')
		while (*p >= '0' && *p <= '7')
			++p;
	else
		while(*p>='0' && *p<='9')
			++p;
	return p;
}

int parse_int(const char **pp, int *buf)
{
	const char *p = *pp;
	if ((*p>='0' && *p<='9') || *p=='-') {
		*buf = ztoi(p);
		*pp = skip_digits(p);
		return 0;
	} else
		return -1;
}

int parse_diff(const char **pp, ptrdiff_t *buf)
{
	const char *p = *pp;
	if ((*p>='0' && *p<='9') || *p=='-') {
		*buf = ztodiff(p);
		*pp = skip_digits(p);
		return 0;
	} else
		return -1;
}

/* Parse a long */

int parse_off_t(const char * *pp, off_t *buf)
{
	const char *p = *pp;
	off_t val = 0;
	int flg = 0;
	if ((*p>='0' && *p<='9') || *p=='-') {
		if(*p=='-') {
			++p;
			flg = 1;
		}
		while(*p >= '0' && *p <= '9') {
			val = val * 10 + (int)(*p - '0');
			++p;
		}
		if (flg)
			val = -val;
		*buf = val;
		*pp = p;
		return 0;
	} else
		return -1;
}

/* Parse a string of the form "xxxxx" into a fixed-length buffer.  The
 * address of the buffer is 'buf'.  The length of this buffer is 'len'.  A
 * terminating NUL is added to the parsed string.  If the string is larger
 * than the buffer, the string is truncated.
 *
 * C string escape sequences are handled.
 *
 * 'p' holds an address of the input string pointer.  The pointer
 * is updated to point right after the parsed string if the function
 * succeeds.
 *
 * Returns the length of the string (not including the added NUL), or
 * -1 if there is no string or if the input ended before the terminating ".
 */

ptrdiff_t parse_string(const char **pp, char *buf, ptrdiff_t len)
{
	char *start = buf;
	const char *p= *pp;
	if(*p=='\"') {
		++p;
		while(len > 1 && *p && *p!='\"') {
			int c = escape(0, &p, NULL, NULL);
			*buf++ = TO_CHAR_OK(c);
			--len;
		}
		*buf = 0;
		while(*p && *p!='\"') {
			escape(0, &p, NULL, NULL);
		}
		if(*p == '\"') {
			*pp = p + 1;
			return buf - start;
		}
	}
	return -1;
}

ptrdiff_t parse_Zstring(const char **pp, int *buf, ptrdiff_t len)
{
	int *start = buf;
	const char *p= *pp;
	if(*p=='\"') {
		++p;
		while(len > 1 && *p && *p!='\"') {
			int c = escape(1, &p, NULL, NULL);
			*buf++ = c;
			--len;
		}
		*buf = 0;
		while(*p && *p!='\"') {
			escape(1, &p, NULL, NULL);
		}
		if(*p == '\"') {
			*pp = p + 1;
			return buf - start;
		}
	}
	return -1;
}

/* Emit a string with escape sequences */

#ifdef junk

/* Used originally for printing macros */

void emit_string(FILE *f,const char *s,ptrdiff_t len)
{
	char buf[8];
	char *p, *q;
	fputc('\"',f);
	while(len) {
		p = unescape(buf,*s++);
		for(q=buf;q!=p;++q)
			fputc(*q,f);
		--len;
	}
	fputc('\"',f);
}
#endif

/* Emit a string */

void emit_string(FILE *f,const char *s,ptrdiff_t len)
{
	fputc('"',f);
	while(len) {
		if (*s=='"' || *s=='\\')
			fputc('\\',f), fputc(*s,f);
		else if(*s=='\n')
			fputc('\\',f), fputc('n',f);
		else if(*s=='\r')
			fputc('\\',f), fputc('r',f);
		else if(*s==0)
			fputc('\\',f), fputc('0',f), fputc('0',f), fputc('0',f);
		else
			fputc(*s,f);
		++s;
		--len;
	}
	fputc('"',f);
}

/* Parse a character range: a-z */

int parse_range(const char * *pp, int *first, int *second)
{
	const char *p= *pp;
	int a, b;
	if(!*p)
		return -1;
	if(*p=='\\' && p[1]) {
		++p;
		if(*p=='n')
			a = '\n';
		else if(*p=='t')
  			a = '\t';
		else
			a = *(const unsigned char *)p;
		++p;
	} else
		a = *(const unsigned char *)p++;
	if(*p=='-' && p[1]) {
		++p;
		if(*p=='\\' && p[1]) {
			++p;
			if(*p=='n')
				b = '\n';
			else if(*p=='t')
				b = '\t';
			else
				b = *(const unsigned char *)p;
			++p;
		} else
			b = *(const unsigned char *)p++;
	} else
		b = a;
	*first = a;
	*second = b;
	*pp = p;
	return 0;
}

int parse_class(const char * *pp, struct interval **array, ptrdiff_t *size)
{
	static struct interval simple;
	const char *p= *pp;
	struct Cclass *cat = NULL;
	int a, b;
	if(!*p)
		return -1;

	a = escape(1, &p, NULL, &cat);

	if (a == -256 && cat) {
		*array = cat->intervals;
		*size = cat->len;
		*pp = p;
		return 0;
	}

	if(*p == '-' && p[1] && p[1] != '"') {
		++p;
		b = escape(1, &p, NULL, NULL);
	} else
		b = a;


	if (b < a)
		b = a;
	simple.first = a;
	simple.last = b;
	*array = &simple;
	*size = 1;
	*pp = p;
	return 0;
}

void jsort(void *base, ptrdiff_t num, ptrdiff_t size, int (*compar)(const void *a, const void *b))
{
	qsort(base, (size_t)num, (size_t)size, compar);
}

off_t oabs(off_t a)
{
	if (a < 0)
		return -a;
	else
		return a;
}
