/* vim: set filetype=c:
 *
 * strings.c86  -  MGL runtime string handling routines
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: strings.c86,v 1.9 2007/01/06 18:31:38 gkminix Exp $
 */

#include "rtcommon.h86"



/*
 *****************************************************************************
 *
 * String comparison
 */
#ifdef strcmp
# undef strcmp	/* Undefine any gcc preprocessor defines */
#endif

__INTERNAL(strcmp, bool_t,
		__nptr s1ptr, __nptr s2ptr, short cmptype)
{
  char *s1 = (char *)((int)s1ptr);
  char *s2 = (char *)((int)s2ptr);
  size_t slen, i;

  /*
   * Get length of shortest string and compare all characters
   */
  slen = MIN((size_t)(s1[0]), (size_t)(s2[0]));
  for (i = 1; i <= slen; i++)
	if (s1[i] != s2[i]) {
		switch (cmptype) {
			case RTCMP_EQ:
				return(FALSE);
			case RTCMP_GT:
			case RTCMP_GE:
				return(s1[i] > s2[i]);
			case RTCMP_LT:
			case RTCMP_LE:
				return(s1[i] < s2[i]);
			case RTCMP_NE:
				return(TRUE);
			default:
				break;
		}
		break;
	}

  /*
   * If both strings are the same up until the shortest length, the
   * return value depends on how the length values compare.
   */
  if (i > slen)
	switch (cmptype) {
		case RTCMP_EQ:
			return(s1[0] == s2[0]);
		case RTCMP_GT:
			return(s1[0] > s2[0]);
		case RTCMP_GE:
			return(s1[0] >= s2[0]);
		case RTCMP_LT:
			return(s1[0] < s2[0]);
		case RTCMP_LE:
			return(s1[0] <= s2[0]);
		case RTCMP_NE:
			return(s1[0] != s2[0]);
		default:
			break;
	}

  /*
   * We get here if we got called by a comparison command which doesn't
   * exist. The return instruction is just to make the compiler happy.
   */
  rtint_raise(EINTERNAL);
  return(FALSE);
}


/* Function wrapper */
__PUBLIC(strcmp, 3,
		__A(__nptr, 2),
		__A(__nptr, 1),
		__A(short, 0)
);


/* Export info */
__EXPORT_PROC(strcmp, BOOL, 3, __ARGS(
		__D(STRING, CONST),
		__D(STRING, CONST),
		__D(NUM, NONE)));



/*
 *****************************************************************************
 *
 * Return substring
 */
#ifdef strsub
# undef strsub	/* Undefine any gcc preprocessor defines */
#endif

__INTERNAL(strsub, __nptr,
		__nptr retptr, size_t retsize,
		__nptr s1ptr, short index1, short index2)
{
  char *s1 = (char *)((int)s1ptr);
  char *ret = (char *)((int)retptr);
  size_t slen, i;

  /* Check for valid arguments */
  if (index1 > (size_t)(s1[0]))
	index1 = (size_t)(s1[0]);
  if (index1 < 1)
	index1 = 1;
  if (index2 > (size_t)(s1[0]))
	index2 = (size_t)(s1[0]);
  if (index2 < index1)
	index2 = index1;

  /* Now copy the string */
  if (retsize > 0) {
	slen = index2 - index1 + 1;
	if (slen > (retsize - 1))
		slen = (retsize - 1);
	for (i = 0; i < slen; i++)
		ret[i + 1] = s1[i + index1];
	ret[0] = (char)(slen & 0xff);
  }
  return(retptr);
}


/* Function wrapper */
__PUBLIC(strsub, 5,
		__A(__nptr, 3), __A(size_t, 4),
		__A(__nptr, 2),
		__A(short, 1),
		__A(short, 0)
);


/* Export info */
__EXPORT_PROC(strsub, STRING, 3, __ARGS(
		__D(STRING, CONST),
		__D(NUM, NONE),
		__D(NUM, NONE)));



/*
 *****************************************************************************
 *
 * Concatenate two characters into a string
 */
__INTERNAL(strcatcc, __nptr,
		__nptr retptr, size_t retsize, char c1, char c2)
{
  char *ret = (char *)((int)retptr);

  if (retsize >= 3) {
	ret[0] = (char)2;
	ret[1] = c1;
	ret[2] = c2;
  } else if (retsize == 2) {
	ret[0] = (char)1;
	ret[1] = c1;
  } else if (retsize == 1)
	ret[0] = (char)0;
  return(retptr);
}


/* Function wrapper */
__PUBLIC(strcatcc, 4,
		__A(__nptr, 2), __A(size_t, 3),
		__A(char, 1),
		__A(char, 0)
);


/* Export info */
__EXPORT_PROC(strcatcc, STRING, 2, __ARGS(
		__D(CHAR, NONE),
		__D(CHAR, NONE)));



/*
 *****************************************************************************
 *
 * Concatenate a character and a string
 */
__INTERNAL(strcatcs, __nptr,
		__nptr retptr, size_t retsize, char c1, __nptr s1ptr)
{
  char *ret = (char *)((int)retptr);
  char *s1 = (char *)((int)s1ptr);
  size_t slen, i;

  if (retsize > 0) {
	slen = (size_t)(s1[0]) + 1;
	if (slen > (retsize - 1))
		slen = (retsize - 1);
	if (slen > 0) {
		ret[1] = c1;
		if (slen > 1) {
			for (i = 2; i <= slen; i++)
				ret[i] = s1[i - 1];
		}
	}
	ret[0] = (char)(slen & 0xff);
  }
  return(retptr);
}


/* Function wrapper */
__PUBLIC(strcatcs, 4,
		__A(__nptr, 2), __A(size_t, 3),
		__A(char, 1),
		__A(__nptr, 0)
);


/* Export info */
__EXPORT_PROC(strcatcs, STRING, 2, __ARGS(
		__D(CHAR, NONE),
		__D(STRING, CONST)));



/*
 *****************************************************************************
 *
 * Concatenate a string and a character
 */
__INTERNAL(strcatsc, __nptr,
		__nptr retptr, size_t retsize, __nptr s1ptr, char c1)
{
  char *ret = (char *)((int)retptr);
  char *s1 = (char *)((int)s1ptr);
  size_t slen, i;

  if (retsize > 0) {
	slen = 0;
	for (i = 1; i <= (size_t)(s1[0]); i++)
		if (i <= (retsize - 1)) {
			ret[i] = s1[i];
			slen++;
		}
	i = (size_t)(s1[0]) + 1;
	if (i <= (retsize - 1)) {
		ret[i] = c1;
		slen++;
	}
	ret[0] = (char)(slen & 0xff);
  }
  return(retptr);
}


/* Function wrapper */
__PUBLIC(strcatsc, 4,
		__A(__nptr, 2), __A(size_t, 3),
		__A(__nptr, 1),
		__A(char, 0)
);


/* Export info */
__EXPORT_PROC(strcatsc, STRING, 2, __ARGS(
		__D(STRING, CONST),
		__D(CHAR, NONE)));



/*
 *****************************************************************************
 *
 * Concatenate two strings
 */
#ifdef strcat
# undef strcat	/* Undefine any gcc preprocessor defines */
#endif

__INTERNAL(strcat, __nptr,
		__nptr retptr, size_t retsize, __nptr s1ptr, __nptr s2ptr)
{
  char *ret = (char *)((int)retptr);
  char *s1 = (char *)((int)s1ptr);
  char *s2 = (char *)((int)s2ptr);
  size_t slen, i, j;

  if (retsize > 0) {
	slen = 0;
	for (i = 1, j = 1; j <= (size_t)(s1[0]); i++, j++)
		if (i <= (retsize - 1)) {
			ret[i] = s1[j];
			slen++;
		}
	for (j = 1; j <= (size_t)(s2[0]); i++, j++)
		if (i <= (retsize - 1)) {
			ret[i] = s2[j];
			slen++;
		}
	ret[0] = (char)(slen & 0xff);
  }
  return(retptr);
}


/* Function wrapper */
__PUBLIC(strcat, 4,
		__A(__nptr, 2), __A(size_t, 3),
		__A(__nptr, 1),
		__A(__nptr, 0)
);


/* Export info */
__EXPORT_PROC(strcat, STRING, 2, __ARGS(
		__D(STRING, CONST),
		__D(STRING, CONST)));



/*
 *****************************************************************************
 *
 * Fill a string with a constant value
 */
#ifdef strset
# undef strset	/* Undefine any gcc preprocessor defines */
#endif

__INTERNAL(strset, __nptr,
		__nptr retptr, size_t retsize, char c, size_t num)
{
  char *ret = (char *)((int)retptr);
  size_t i;

  if (retsize > 0) {
	if (num > (retsize - 1))
		num = (retsize - 1);
	for (i = 1; i <= num; i++)
		ret[i] = c;
	ret[0] = (char)(num & 0xff);
  }
  return(retptr);
}


/* Function wrapper */
__PUBLIC(strset, 4,
		__A(__nptr, 2), __A(size_t, 3),
		__A(char, 1),
		__A(size_t, 0)
);


/* Export info */
__EXPORT_PROC(strset, STRING, 2, __ARGS(
		__D(CHAR, NONE),
		__D(NUM, NONE)));



/*
 *****************************************************************************
 *
 * Memory comparison (for non-scalars)
 */
#ifdef memcmp
# undef memcmp	/* Undefine any gcc preprocessor defines */
#endif

__INTERNAL(memcmp, bool_t,
		__nptr s1ptr, size_t s1size,
		__nptr s2ptr, size_t s2size,
		short cmptype)
{
  char *s1 = (char *)((int)s1ptr);
  char *s2 = (char *)((int)s2ptr);
  size_t slen, i;

  /*
   * Determine length and type of comparison
   */
  slen = MIN(s1size, s2size);
  if (cmptype != RTCMP_EQ && cmptype != RTCMP_NE)
	rtint_raise(EINTERNAL);

  /*
   * Compare the memory blocks
   */
  for (i = 0; i < slen; i++)
	if (s1[i] != s2[i])
		return(cmptype == RTCMP_NE);

  return(cmptype == RTCMP_EQ);
}


/* Function wrapper */
__PUBLIC(memcmp, 5,
		__A(__nptr, 3), __A(size_t, 4),
		__A(__nptr, 1), __A(size_t, 2),
		__A(short, 0)
);


/* Export info */
__EXPORT_PROC(memcmp, BOOL, 3, __ARGS(
		__D(ANY, REF),
		__D(ANY, REF),
		__D(NUM, NONE)));

