/*
** SellaNMS
** $Id: util.cxx,v 1.5 2006/01/24 07:08:09 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

static const char rcsid[] = "$Id: util.cxx,v 1.5 2006/01/24 07:08:09 sella Exp $";

#include "util.h"
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <regex.h>
#include <sys/types.h>
#include <stl/slist>
#include <stl/string>

char* trim(char* str) {
	rtrim(str);
	ltrim(str);

	return str;
}

char* rtrim(char* str) {
	for (char *p = str + strlen(str) - 1; p >= str; p--) {
		switch (*p) {
			case ' ':
			case '\n':
			case '\r':
			case '\t':
				*p = '\0';
				break;
			default:
				p = str;
		}
	}
	return str;
}

char* ltrim(char* str) {
	size_t idx, size, len;

	len = strlen(str);
	for (idx = 0; idx < len; idx++) {
		if (str[idx] != ' ' && str[idx] != '\n' && str[idx] != '\r' && str[idx] != '\t') {
			break;
		}
	}
	if (idx > 0) {
		size = len - idx;
		for(size_t i = 0; i < size; i++) {
			str[i] = str[i + idx];
		}
	}

	return str;
}

unsigned short in_cksum(unsigned short* packet, unsigned long size) {
   int nleft = size;
   unsigned short *p = packet;
   unsigned long sum = 0;
   u_short answer = 0;

   while (nleft > 1)  {
       sum += *p++;
       nleft -= 2;
   }

   if (nleft == 1) {
       *(unsigned char *)(&answer) = *(u_char *)p;
       sum += answer;
   }

   sum = (sum >> 16) + (sum & 0xffff);
   sum += (sum >> 16);
   answer = ~sum;

   return answer;
}

void SubStringReplace(string &str, char* orgsubstr, char* newsubstr) {
	int i, len = str.size() + 1024;
	char *ptr, *buf = new char[len];

	while (1) { /* Loop, so we replace multiple copies of the substr. */
		i = SubStringSearch(str.c_str(), orgsubstr);
		if (i < 0) /* Check if orgsubstr is there. */
			break;
		strncpy(buf, str.c_str(), len);
		str.erase(i, str.size() - i);
		str += newsubstr;
		ptr = buf + i + strlen(orgsubstr);
		str += ptr;
	}
	delete[] buf;
}
 
int SubStringSearch(const char *str, const char* substr) {
	/* Returns -1 if not found, or starting index if found. This search is case sensitive.*/
	char *ptr;

	if (strlen(str) < strlen(substr))
		return -1;
	ptr = (char*) str;
	for (size_t i = 0; i <= strlen(str) - strlen(substr); i++) {
		if (ptr[0] == substr[0]) {
			if (!strncmp(ptr, substr, strlen(substr))) {
				return i;
			}
		}
		ptr++;
	}
	return -1;
}

#if 0
void tokenize_string(vector<string>& tokens, const string& str, const string& delimiters = " \n") {
	string::size_type s_pos = str.find_first_not_of(delimiters, 0);
	string::size_type e_pos = str.find_first_of(delimiters, s_pos);

	while (e_pos != string::npos || s_pos != string::npos) {
		tokens.push_back(str.substr(s_pos, e_pos - s_pos));

		/* Next victim. */
		s_pos = str.find_first_not_of(delimiters, e_pos);
		e_pos = str.find_first_of(delimiters, s_pos);
	}
}
#endif

void tokenize_string(slist<string>& tokens, const string& str, const string& delimiters = " \n") {
	string::size_type s_pos = str.find_first_not_of(delimiters, 0);
	string::size_type e_pos = str.find_first_of(delimiters, s_pos);

	while (e_pos != string::npos || s_pos != string::npos) {
		tokens.push_front(str.substr(s_pos, e_pos - s_pos));

		/* Next victim. */
		s_pos = str.find_first_not_of(delimiters, e_pos);
		e_pos = str.find_first_of(delimiters, s_pos);
	}

	tokens.reverse();
}

char *ereg_replace(const char *pattern, const char *replace, const char *str) {
	return _reg_replace(pattern, replace, str, 0, 1);
}

char *eregi_replace(const char *pattern, const char *replace, const char *str) {
	return _reg_replace(pattern, replace, str, 1, 1);
}

/* Modified version of PHP3's function _php3_regreplace from reg.c in PHP v3.0.18. */
/* PHP3 code is dual licensed with GPL v2 and the PHP license. */
char* _reg_replace(const char *pattern, const char *replace, const char *string, int icase, int extended) {
	regex_t re;
	regmatch_t subs[MAX_REGREPLACE_SUBS];

	char *buf, *nbuf, *walkbuf;
	const char *walk;
	int buf_len, pos, tmp, string_len, new_l, err, copts = 0;

	assert(pattern != NULL);
	assert(replace != NULL);
	assert(string != NULL);

	string_len = strlen(string);

	if (icase) {
		copts = REG_ICASE;
	}

	if (extended) {
		copts |= REG_EXTENDED;
	}

	if ((err = regcomp(&re, pattern, copts)) != 0) {
		return NULL;
	}

	/* start with a buffer that is twice the size of the stringo we're doing replacements in */
	buf_len = 2 * string_len + 1;
	buf = (char*) malloc(buf_len * sizeof(char));
	if (!buf) {
		regfree(&re);

		return NULL;
	}

	err = pos = 0;
	buf[0] = '\0';

	while (!err) {
		err = regexec(&re, &string[pos], (size_t) MAX_REGREPLACE_SUBS, subs, (pos ? REG_NOTBOL : 0));

		if (err && err != REG_NOMATCH) {
			regfree(&re);

			return NULL;
		}
		if (!err) {
			new_l = strlen(buf) + subs[0].rm_so;
			walk = replace;
			while (*walk) {
				if ('\\' == *walk && '0' <= walk[1] && '9' >= walk[1] && subs[walk[1] - '0'].rm_so > -1 && subs[walk[1] - '0'].rm_eo > -1) {
					new_l += subs[walk[1] - '0'].rm_eo - subs[walk[1] - '0'].rm_so;
					walk += 2;
				} else {
					new_l++;
					walk++;
				}
			}

			if (new_l + 1 > buf_len) {
				buf_len = 1 + buf_len + 2 * new_l;
				nbuf = (char*) malloc(buf_len);
				strcpy(nbuf, buf);
				free(buf);
				buf = nbuf;
			}
			tmp = strlen(buf);
			/* copy the part of the string before the match */
			strncat(buf, &string[pos], subs[0].rm_so);

			/* copy replacement and backrefs */
			walkbuf = &buf[tmp + subs[0].rm_so];
			walk = replace;
			while (*walk) {
				if ('\\' == *walk && '0' <= walk[1] && '9' >= walk[1] && subs[walk[1] - '0'].rm_so > -1 && subs[walk[1] - '0'].rm_eo > -1) {
					tmp = subs[walk[1] - '0'].rm_eo - subs[walk[1] - '0'].rm_so;
					memcpy (walkbuf, &string[pos + subs[walk[1] - '0'].rm_so], tmp);
					walkbuf += tmp;
					walk += 2;
				} else {
					*walkbuf++ = *walk++;
				}
			}
			*walkbuf = '\0';

			/* and get ready to keep looking for replacements */
			if (subs[0].rm_so == subs[0].rm_eo) {
				if (subs[0].rm_so + pos >= string_len) {
					break;
				}
				new_l = strlen (buf) + 1;
				if (new_l + 1 > buf_len) {
					buf_len = 1 + buf_len + 2 * new_l;
					nbuf = (char*) malloc(buf_len * sizeof(char));
					strcpy(nbuf, buf);
					free(buf);
					buf = nbuf;
				}
				pos += subs[0].rm_eo + 1;
				buf[new_l-1] = string[pos-1];
				buf[new_l] = '\0';
			} else {
				pos += subs[0].rm_eo;
			}
		} else { /* REG_NOMATCH */
			new_l = strlen(buf) + strlen(&string[pos]);
			if (new_l + 1 > buf_len) {
				buf_len = new_l + 1; /* now we know exactly how long it is */
				nbuf = (char*) malloc(buf_len * sizeof(char));
				strcpy(nbuf, buf);
				free(buf);
				buf = nbuf;
			}
			/* stick that last bit of string on our output */
			strcat(buf, &string[pos]);
		}
	}

	regfree(&re);
	buf[new_l] = '\0';

	return (buf);
}

#if 0
bool process_cron_format(vector<string> &cron_v, string &cron) {
	vector<string> correct_order;
	vector<string>::iterator it;

#if 0
	field          allowed values
	-----          --------------
	minute         0-59
	hour           0-23
	day of month   1-31
	month          1-12 (or names, see below)
	day of week    0-7 (0 or 7 is Sun, or use names)
#endif

	correct_order.resize(5);
	for (it = cron_v.begin(); it != cron_v.end(); ++it) {
		if (!it->compare("minute") || !it->compare("min")) {
			correct_order[0] = *it;
		} else if (!it->compare("hour")) {
			correct_order[1] = *it;
		} else if (!it->compare("day-of-month")) {
			correct_order[2] = *it;
		} else if (!it->compare("month") || !it->compare("mon")) {
			correct_order[3] = *it;
		} else if (!it->compare("day-of-week") || !it->compare("day")) {
			correct_order[4] = *it;
		} else if (!it->compare("cron") || !it->compare("crontab")) {
			cron = *it;
			break;
		} else {
			return false;
		}
	}

	return true;
}
#endif

char *dbtstamp_from_unixtstamp(time_t t) {
	char *buf = NULL;
	struct tm *ts = NULL;

	if ((ts = gmtime(&t)) != NULL) {
		buf = new char[16];
		if (buf) {
			snprintf(buf, 16, "%04d%02d%02d%02d%02d%02d", 1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
		}
	}

	return buf;
}

void add_timeval(timeval &dst, timeval src) {
   if ((dst.tv_usec += src.tv_usec) > 1000000) {
      dst.tv_sec++;
      dst.tv_usec -= 1000000;
   }
   dst.tv_sec += src.tv_sec;
}

void subtract_timeval(timeval &dst, timeval src) {
   if ((dst.tv_usec -= src.tv_usec) < 0) {
      dst.tv_sec--;
      dst.tv_usec += 1000000;
   }
   dst.tv_sec -= src.tv_sec;
}

int tvcmp(timeval dst, timeval src) {
   if ((dst.tv_usec -= src.tv_usec) < 0) {
      dst.tv_sec--;
      dst.tv_usec += 1000000;
   }
   dst.tv_sec -= src.tv_sec;

	if (dst.tv_sec < 0) {
		return -1;
	} else if (dst.tv_sec > 0) {
		return 1;
	} else {
		if (dst.tv_usec < 0) {
			return -1;
		} else if (dst.tv_usec > 0) {
			return 1;
		} else {
			return 0;
		}
	}
}

long tvtousec(timeval tv) {
	return (tv.tv_usec + (tv.tv_sec * 1000000));
}

void hexdump(char *p, size_t length) {
	unsigned	counter = 0;
	char		bytes[length];

	memcpy(bytes, p, length);
	counter = 0;

	printf("      00 01 02 03   04 05 06 07\n");
	while (counter < length) {
		printf("      ");
		for (unsigned i = 0; i < 8; i++) {
			if ((counter + i) < length) {
				printf("%02x ", (char)bytes[counter + i]);
			} else {
				printf("   ");
			}
		}
		printf("  ");
		for (unsigned i = 0; i < 8; i++) {
			if ((counter + i) < length) {
				printf("%c", bytes[counter + i]);
			}
		}
		printf("\n");
		counter += 8;
	}
}

RESULT flag_init(int pipes[2]) {
	if (pipe(pipes) == 0) {
		return SUCCESS;
	} else {
		TRACE(TRACE_INTERNAL, "failed to init flag: 0x%x (0x%x / 0x%x)", pipes, pipes[0], pipes[1]);
		return FAILURE;
	}
}

RESULT flag_set(int pipe[2]) {
	char				buffer[1];

	fd_set			rfds;
	struct timeval	tv;

	FD_ZERO(&rfds);
	FD_SET(pipe[0], &rfds);

	tv.tv_sec = 0;
	tv.tv_usec = 0;

	/* Check to see if there is already a set flag in the pipe. */
	if (select(pipe[0] + 1, &rfds, NULL, NULL, &tv) == 0) {
//		TRACE(TRACE_INTERNAL, "set flag: 0x%x (0x%x)", pipe, pipe[0]);
		if (write(pipe[1], buffer, 1) != 1) {
			TRACE(TRACE_INTERNAL, "failed to write flag: 0x%x (0x%x)", pipe, pipe[0]);
		}
	}
	return SUCCESS;
}

RESULT flag_wait(int pipe[2], bool *interrupt, int timeout_secs) {
	char				buffer[1];

	fd_set			rfds;
	struct timeval	tv;
	time_t			timeout = time(NULL) + timeout_secs;
	bool				never = false;

	if (interrupt == NULL) {
		interrupt = &never;
	}

	while (!(*interrupt)) {
		FD_ZERO(&rfds);
		FD_SET(pipe[0], &rfds);

		tv.tv_sec = 0;
		tv.tv_usec = 500000;

		if (select(pipe[0] + 1, &rfds, NULL, NULL, &tv) > 0) {
//			TRACE(TRACE_INTERNAL, "read flag: 0x%x (0x%x)", pipe, pipe[0]);
			read(pipe[0], buffer, 1);
			return SUCCESS;
		}
		if (timeout_secs > 0 && time(NULL) >= timeout) {
			return SUCCESS;
		}
	}
	return FAILURE;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim:
** vim: noet ts=3 sw=3
*/
