/*
 * This code was taken from the libstrfunc project, http://lionet.info
 * All rights reserved.
 */
/*-
 * Copyright (c) 1999, 2000, 2001 Lev Walkin <vlm@lionet.info>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: ncnf_sf_lite.c,v 1.1 2005/05/26 12:08:19 vlm Exp $
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>

#include "ncnf_sf_lite.h"

#define	sf_malloc	malloc
#define	sf_calloc	calloc
#define	sf_realloc	realloc

/*
 * Create or initialize empty string list.
 */
ncnf_sf_svect *
ncnf_sf_sinit() {
	ncnf_sf_svect *sl;

	sl=(ncnf_sf_svect *)sf_calloc(1, sizeof(ncnf_sf_svect));
	if(!sl)
		return NULL;

	sl->listlen = 4;

	/* Place for data chunks */
	sl->list = (char **)sf_malloc(sizeof(char *) * sl->listlen);
	if(!sl->list) {
		free(sl);
		return NULL;
	}

	/* Place for data lengths */
	sl->lens = (size_t *)sf_malloc(sizeof(size_t) * sl->listlen);
	if(!sl->lens) {
		free(sl->list);
		free(sl);
		return NULL;
	}

	sl->list[0] = NULL;
	sl->lens[0] = 0;

	return sl;
};

/* frees any strings inside the ncnf_sf_svect object */
void
ncnf_sf_sclear(ncnf_sf_svect *sl) {

	if(sl == NULL)
		return;

	if(sl->list) {
		while(sl->count--) {
			if(sl->list[sl->count])
				free(sl->list[sl->count]);
		}
		*(sl->list) = NULL;
		free(sl->list);
		sl->list = 0;
	}

	if(sl->lens) {
		free(sl->lens);
	}

	sl->lens = 0;
	sl->count = 0;
	sl->listlen = 0;
}

void
ncnf_sf_sfree(ncnf_sf_svect *sl) {
	if(!sl)
		return;

	if(sl->list) {
		if(sl->count > 0)
			while(sl->count--) {
				void *p = sl->list[sl->count];
				if(p) free(p);
			}
		free(sl->list);
	};

	if(sl->lens)
		free(sl->lens);

	free(sl);
};

/* Basic adding function */
int
_sf_add_internal(ncnf_sf_svect *s, void *msg, size_t len) {

	if(s->count + 1 >= s->listlen) {
		void *z;
		size_t newlistlen;

		if((newlistlen = s->listlen << 2) == 0)
			newlistlen = 4;

		z = sf_realloc(s->list, sizeof(char *) * newlistlen);
		if(z)
			s->list = (char **)z;
		else
			return -1;

		z = sf_realloc(s->lens, sizeof(size_t) * newlistlen);
		if(z)
			s->lens = (size_t *)z;
		else
			return -1;
		s->listlen = newlistlen;
	}

	s->list[s->count] = msg;
	s->lens[s->count] = len;

	s->count++;
	s->list[s->count] = NULL;
	s->lens[s->count] = 0;

	return 0;
}


/* Add a string to the end of the ncnf_sf_svect structure */
int
ncnf_sf_sadd2(ncnf_sf_svect *s, const char *msg, size_t len) {
	char *tmp;

	if(!s || !msg)
		return -1;

	tmp = (char *)sf_malloc(len+1);
	if(!tmp)
		return -1;
	memcpy(tmp, msg, len);
	tmp[len] = 0;

	if(_sf_add_internal(s, tmp, len) == -1) {
		free(tmp);
		return -1;
	}

	return 0;
};

int
ncnf_sf_sadd(ncnf_sf_svect *s, const char *msg) {
	if(!s || !msg)
		return -1;
	return ncnf_sf_sadd2(s, msg, strlen(msg));
}


/* delete i'th string from the list */
int
ncnf_sf_sdel(ncnf_sf_svect *s, size_t n) {
	if(!s)
		return -1;

	if(n >= s->count)
		return s->count;

	free(s->list[n]);

	for(s->count--; n <= s->count; n++) {
		s->list[n] = s->list[n+1];
		s->lens[n] = s->lens[n+1];
	};

	return s->count;
}

ssize_t
ncnf_sf_sfind(ncnf_sf_svect *sl, const char *what) {
	size_t n;
	size_t wlen;
	char cw;

	if(!sl || !sl->count || !what)
		return -1;

	wlen=strlen(what);
	cw = *what;

	for(n=0; n < sl->count; n++) {

		if(sl->lens[n] != wlen)
			continue;

		if(wlen) {
			if(
				(sl->list[n][0] == cw)
				&& !strcmp(sl->list[n], what)
			) return n;
		} else {
			return n;
		};

	};

	return -1;
}

/****************/
/* SPLIT STRING */
/****************/

/* split string and add its contents to the ncnf_sf_svect structure */
/* Split: full version */
int
ncnf_sf_splitf(ncnf_sf_svect *s, const char *msg, const char *dlm, int flags) {
	const char *p = msg;
	const char *w = NULL;
	int tokens = 0;
	int dlen;
	char cp, cd;

	if(!s || !p) {
		errno = EINVAL;
		return -1;
	}

	if(!dlm) {
		if(flags & 4)
			flags &= ~4;

		if(flags)
			/* Default to parse /etc/password-like strings */
			dlm = ":";
		else
			/* Default to split on common whitespaces */
			dlm = " \t\n\r";
	}
	cd = *dlm;
	dlen = strlen(dlm);

	if(flags & 2) {

		for(; (cp = *p); p++) {
			if((cd == cp) && (strncmp(p, dlm, dlen) == 0)) {
				if(w) {
					if(ncnf_sf_sadd2(s, w, p-w) == -1) {
						while(tokens--)	/* cleanup */
							ncnf_sf_sdel(s, s->count - 1);
						return -1;
					}
					w = NULL;
					tokens++;
				} else if(flags & 1) {
					if(ncnf_sf_sadd2(s, "", 0) == -1) {
						while(tokens--)	/* cleanup */
							ncnf_sf_sdel(s, s->count - 1);
						return -1;
					};
					tokens++;
				}
				p += dlen - 1;
			} else {
				if(!w)
					w = p;
			}
		}

	} else {

		for(; (cp = *p); p++) {
			if((cd == cp) || memchr(dlm, cp, dlen)) {
				if(w) {
					if(ncnf_sf_sadd2(s, w, p-w) == -1) {
						while(tokens--)
							ncnf_sf_sdel(s, s->count -1);
						return -1;
					};
					w = NULL;
					tokens++;
				} else if(flags & 1) {
					if(ncnf_sf_sadd2(s, "", 0) == -1) {
						while(tokens--)
							ncnf_sf_sdel(s, s->count -1);
						return -1;
					}
					tokens++;
				}
			} else {
				if(!w)
					w = p;
			}
		}

	}

	if(w) {
		ncnf_sf_sadd2(s, w, p-w);
		tokens++;
	}

	return tokens;
}

ncnf_sf_svect *
ncnf_sf_split(const char *msg, const char *delim, int flags) {
	ncnf_sf_svect *sl;

	sl = ncnf_sf_sinit();
	if(!sl)
		return NULL;

	if(ncnf_sf_splitf(sl, msg, delim, flags) == -1) {
		ncnf_sf_sfree(sl);
		return NULL;
	}

	return sl;
}

static char *_sf_sjoin_buf = NULL;

char *
ncnf_sf_sjoin(ncnf_sf_svect *s, const char *delimiter) {
	char *buf;
	int flen;
	int k, need;
	char *p;

	if(s == NULL || s->count == 0) {
		if(_sf_sjoin_buf) free(_sf_sjoin_buf);
		return (_sf_sjoin_buf = strdup(""));
	}

	if(delimiter) {
		flen = strlen(delimiter);
	} else {
		delimiter = "";
		flen = 0;
	}

	for(need = 0, k = 0; k < s->count; k++)
		need += ((ssize_t)s->lens[k] >= 0
				? s->lens[k] : strlen(s->list[k]))
			+ (k?flen:0);

	buf = (char *)sf_malloc(need + 1);
	if(buf == NULL)
		/* ENOMEM? */
		return NULL;

	p=buf;

	for(k = 0; k < s->count; k++) {
		if(k) {
			memcpy(p, delimiter, flen);
			p += flen;
		}
		if((need = s->lens[k]) < 0)
			need = strlen(s->list[k]);
		memcpy(p, s->list[k], need);
		p += need;
	}
	*p = 0;

	if(_sf_sjoin_buf) free(_sf_sjoin_buf);
	return (_sf_sjoin_buf=buf);
}


syntax highlighted by Code2HTML, v. 0.9.1