/* Copyright (c) 1998,1999 Stanley J. Brooks
 * All rights reserved.
 * 
 * You may redistribute under the terms of either the GNU General Public
 * License or the Perl Artistic License.
 */

#include <ctype.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <slang.h>
#include <sys/types.h>

/* this was just to verify that cp0 will be an slstring
static int is_same(char *cp0)
{
	char *cp1;
	if (!SLang_pop_slstring(&cp1)) {
		SLang_free_slstring(cp1);
		return (cp0 == cp1);
	}
	return 0;
}
*/

static int is_uppers(unsigned char *cp)
{
	unsigned char ch;
	while ((ch=*cp++)) if (!isalpha(ch) || ch != UPPER_CASE(ch)) return 0;
	return 1;
}

static int is_lowers(unsigned char *cp)
{
	unsigned char ch;
	while ((ch=*cp++)) if (!isalpha(ch) || ch != LOWER_CASE(ch)) return 0;
	return 1;
}

/* this one depends on C locale: */
static int is_alnums(unsigned char *cp)
{
	unsigned char ch;
	while ((ch=*cp++)) if (!isalnum(ch)) return 0;
	return 1;
}

static int is_digits(unsigned char *cp)
{
	unsigned char ch;
	while ((ch=*cp++)) if (!isdigit(ch)) return 0;
	return 1;
}

/* [sjb]  new builtin _time(), so remove this... */
static int unix_time(void)
{
	return (int) time(NULL);
}

/* string = cut_list_element(string,string-to-cut,delim)  */
static void cut_list_element(char* cp0, char* cut, int *delim)
{
	char *cp,*out;
	char ch0,ch;
	int len,dl = (char) *delim;

	if (!(cp0 = SLmake_string(cp0))) {
		SLang_verror(SL_MALLOC_ERROR,"Problem making string");
		return;
	}
	len=strlen(cut);
	ch0=*cut;
	for (cp=out=cp0; (ch=*cp); ) {
		if ((!ch0 || (ch==ch0 && !memcmp(cp,cut,len)))
		  && (!cp[len]||cp[len]==dl)) {
			cp+=len;
			if (!*cp++) {
				if (out>cp0) out--;
				break;
			}
		}else{
			while((ch=*cp++) && ch!=dl) *out++=ch;
			if (!ch) break;
			*out++=ch;
		}
	}
	*out='\0';
	SLang_push_malloced_string(cp0);
	/* ^^ this frees it, do don't bother with realloc */
	return;
}

static int SLirc_index(char *cp, int *c)
{
   register char *p=cp;
   register char ch;
   register char ch0=*c;
   
   while ((ch=*p++))
     if (ch==ch0) return p-cp;
   return 0;
}

static int SLirc_rindex(char *cp, int *c)
{
   register char *p=cp;
   char *q=NULL;
   register char ch;
   register char ch0=*c;
   
   while ((ch=*p++))
     if (ch==ch0) q=p;
   if (q) return(q-cp);
   return 0;
}

static void chop_1st(char* cp0)
{
	char *cp;

	cp = cp0;
	if (*cp) cp++;
	SLang_push_string(cp);
	return;
}

static void chop_last(char *cp0)
{
	char *cp1;
	int i;

	i = strlen(cp0);
	if (!i) i++;
	cp1 = (char*) SLmalloc(i);
	if (cp1) {
    if (--i) (void)memcpy(cp1,cp0,i);
		*(cp1+i) = '\0';
		i = SLang_push_malloced_string(cp1);
		if (i != -1) return;
	}
	SLang_verror(SL_MALLOC_ERROR,"Problem making string");
}

/* if input string ends with "\n" or "\r\n", 
 * push string less that.
 * eg: s = chomp(s);
*/
static void chomp(char *cp0)
{
	char *p;
	int r,i;

	p = rindex(cp0,'\n');
	if (!p || p[1])
		i = strlen(p);
	else {
		if (*--p != '\r') p++;
		i = p - cp0;
	}
	p = (char*) SLmalloc(i+1);
	if (p) {
    if (i) (void)memcpy(p,cp0,i);
		p[i] = '\0';
		r = SLang_push_malloced_string(p);
		if (!r) return;
	}
	SLang_verror(SL_MALLOC_ERROR,"Problem making string");
	return;
}

/* url_encode replaces various special chars by their %xx equivs
 *             and ' ' by '+'
 * this is for urlencoded HTTP queries
*/
static void url_encode(char* p)
{
	unsigned char ch;
	char *s0,*s;

	s0 = (char*) SLmalloc(3*strlen(p)+1); /* enough for maximum expansion */
	if (!s0) return;
	s = s0;
	while((ch = *p++)) {
		if (ch == ' ') { *s++ = '+'; continue; }
		if (   (ch >= 'a' && ch <= 'z')
		    || (ch >= 'A' && ch <= 'Z')
		    || (ch >= 0xa0) /* iso-latin-1 chars */
		    || (ch == '_') || (ch == '.') || (ch == '-') || (ch == '$') /* safe */
/*	    || (ch == '!') || (ch == '*') || (ch == '\'') */
/*	    || (ch == '(') || (ch == ')') || (ch == ',')  */
			 )
		{ *s++ = ch; continue;}
		sprintf(s,"%%%.2X",ch); s+=3;
	}
	*s++ = 0;
	(void) SLang_push_malloced_string(s0);
}

static int hexlate(unsigned char ch)
{
	int r;
	if (ch >= '0' && ch <= '9') r = ch - '0'; 
	else {
		ch &= ~0x20;
		if (ch >= 'A' && ch <= 'F') r = ch + 10 - 'A'; 
		else r = -1;
	}
	return r;
}

static void url_decode(char* p)
{
	unsigned char ch;
	char *s0,*s;

	s0 = (char*) SLmalloc(strlen(p)+1); /* enough, can't expand. */
	if (!s0) return;
	s = s0;
	while((ch = *p++)) {
		if (ch == '+') ch = ' ';
		if (ch == '%') {
			int r1,r2;
			if ( ((r1 = hexlate(p[0])) >= 0)
			  && ((r2 = hexlate(p[1])) >= 0)
			  && (r2 += r1<<4) )
			{
				p+=2; ch = r2;
			}
		}
		*s++ = ch;
	}
	*s++ = 0;
	(void) SLang_push_malloced_string(s0);
}

/* s_split(string,delimiter) splits string with respect to delim-char,
 * pushing token strings onto stack, followed by the integer count
 * of the tokens. The first token in the string is pushed LAST, so
 * that it will be the 1st to be popped.
 *
 * My IMPRESSION is that any string on the stack *should* be a
 * private copy in RAM, so it shouldn't be necessary to save & restore
 * the delim's, but who knows? I might be wrong.
 *
 * LATER THOUGHTS: above is true when you pop via SLpop_string,
 * but NOT if SLang_pop_slstring.
 * Also, if the parameter is specified via SLANG_STRING_TYPE in
 * the intrinsic declaration, then the pointer you get is an slstring,
 * which MUST NOT be modified.
 *
 * Something I'm still not clear on: when param is indicated as
 * SLANG_STRING_TYPE in intrinsic declaration, do you still need
 * to do SLang_free_slstring() on it within the subroutine?
 * I assume that answer is "no" because of ... intrin_strcmp() in
 * slang's own slstd.c source-file
*/
static int s_split(char *cp0, int *delim)
{
	char *cp,*cph,dl;
	int pmct=0;

	cp0 = SLmake_string(cp0);
	if (!cp0) {
		SLang_verror(SL_MALLOC_ERROR,"Problem making string");
		return 0;
	}
	dl = (char)(*delim);
	for (cph=cp0+strlen(cp0); cph>cp0; cph=--cp) {
		*cph='\0';
		for (cp=cph; cp>cp0 && *--cp!=dl; ); /* back up to delim */
		if (cp>cp0) cp++;
		SLang_push_string(cp);
		pmct++;
	}
	SLfree(cp0);
	return(pmct);
}

static void v_split(char *cp0,char *delims)
{
	char *cp,*cph,dl;

	cp0 = SLmake_string(cp0);
	if (!cp0) {
		SLang_verror(SL_MALLOC_ERROR,"Problem making string");
		return;
	}
	for(cp=cp0; (dl=*delims++); cp=cph) {
		cph=index(cp,dl);
		if (!cph) break;
		*cph='\0';
		SLang_push_string(cp);
		*cph++=dl; /* restore it! */
	}
	SLang_push_string(cp); /* the last piece */
  SLfree(cp0);

	if (dl) /* fell short */
		do {
			SLang_push_string("");
		} while (*delims++);
}

static int is_prefix(char *cpa, char *cpb)
{
	u_char cha,chb;

	while((chb=*cpb++)) {
		cha=*cpa++;
		if (cha!=chb) return 0;
	}
	return 1;
}

static int is_prefix_i(char *cpa, char *cpb)
{
	u_char cha,chb;

	while((chb=*cpb++)) {
		cha=*cpa++;
		if (LOWER_CASE(cha)!=LOWER_CASE(chb)) return 0;
	}
	return 1;
}

static int streq(char *cpa, char *cpb)
{
	u_char cha,chb;

	while((chb=*cpb++)) {
		cha=*cpa++;
		if (cha!=chb) return 0;
	}
	return (*cpa)?0:1;
}

static int streq_i(char *cpa, char *cpb)
{
	u_char cha,chb;

	while((chb=*cpb++)) {
		cha=*cpa++;
		if (LOWER_CASE(cha)!=LOWER_CASE(chb)) return 0;
	}
	return (*cpa)?0:1;
}

static int u_strspn(char *cpa, char *cpb)
{
	char *pa,*pb;
	char cha,chb;

	pa=cpa;
	while((cha=*pa++)) {
		for (pb = cpb; ((chb=*pb++)) && chb != cha;);
		if (!chb) break;
	}
	return (--pa-cpa);
}

static int u_strcspn(char *cpa, char *cpb)
{
	char *pa,*pb;
	char cha,chb;

	pa=cpa;
	while((cha=*pa++)) {
		for (pb = cpb; ((chb=*pb++)) && chb != cha;);
		if (chb) break;
	}
	return (--pa-cpa);
}

/* The Intrinsics Table for LinkList stuff, and some misc */
static SLang_Intrin_Fun_Type Utils_Fun_Intrinsics[] =
{
	MAKE_INTRINSIC_0("unix_time", unix_time, SLANG_INT_TYPE),
	MAKE_INTRINSIC_S("is_uppers", is_uppers, SLANG_INT_TYPE),
	MAKE_INTRINSIC_S("is_lowers", is_lowers, SLANG_INT_TYPE),
	MAKE_INTRINSIC_S("is_alnums", is_alnums, SLANG_INT_TYPE),
	MAKE_INTRINSIC_S("is_digits", is_digits, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("is_prefix", is_prefix, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("is_prefix_i", is_prefix_i, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("streq", streq, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("streq_i", streq_i, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("strspn", u_strspn, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("strcspn", u_strcspn, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SI("index", SLirc_index, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SI("rindex", SLirc_rindex, SLANG_INT_TYPE),
	MAKE_INTRINSIC_S("chop_1st", chop_1st, SLANG_VOID_TYPE),
	MAKE_INTRINSIC_S("chop_last", chop_last, SLANG_VOID_TYPE),
	MAKE_INTRINSIC_S("chomp", chomp, SLANG_VOID_TYPE),
	MAKE_INTRINSIC_S("url_encode", url_encode, SLANG_VOID_TYPE),
	MAKE_INTRINSIC_S("url_decode", url_decode, SLANG_VOID_TYPE),
	MAKE_INTRINSIC_SSI("cut_list_element", cut_list_element, SLANG_VOID_TYPE),
	MAKE_INTRINSIC_SI("s_split", s_split, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("v_split", v_split, SLANG_VOID_TYPE),
	SLANG_END_TABLE
};

int init_utils_module(void) __attribute__((unused));
int init_utils_module(void)
{
	if (SLdefine_for_ifdef("UTILS")) {
		fprintf(stderr,"utils-module: fail define_for_isdef(UTILS)\n");
		return -1;
	}
	if (SLadd_intrin_fun_table(Utils_Fun_Intrinsics, "Utils")) return -1;
	return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1