/* GNUPLOT - stdfn.c */

/*[
 * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/


/* This module collects various functions, which were previously scattered
 * all over the place. In a future implementation of gnuplot, each of
 * these functions will probably reside in their own file in a subdirectory.
 * - Lars Hecking
 */

#include "stdfn.h"

#ifdef WIN32
/* the WIN32 API has a Sleep function that does not consume CPU cycles */
#include <windows.h>
#endif


/*
 * ANSI C functions
 */

/* memcpy() */

#ifndef HAVE_MEMCPY
# ifndef HAVE_BCOPY
/*
 * cheap and slow version of memcpy() in case you don't have one
 */

char *
memcpy(char *dest, char *src, size_t len)
{
    while (len--)
	*dest++ = *src++;

    return dest;
}
# endif				/* !HAVE_BCOPY */
#endif /* HAVE_MEMCPY */

/* memset ()
 *
 * Since we want to use memset, we have to map a possibly nonzero fill byte
 * to the bzero function. The following defined might seem a bit odd, but I
 * think this is the only possible way.
 */

#ifndef HAVE_MEMSET
# ifdef HAVE_BZERO
#  define memset(s, b, l) \
do {                      \
  assert((b)==0);         \
  bzero((s), (l));        \
} while(0)
#  else
#  define memset NO_MEMSET_OR_BZERO
# endif /* HAVE_BZERO */
#endif /* HAVE_MEMSET */


/* strerror() */
#ifndef HAVE_STRERROR

char *
strerror(int no)
{
    static char res_str[30];

    if (no > sys_nerr) {
	sprintf(res_str, "unknown errno %d", no);
	return res_str;
    } else {
	return sys_errlist[no];
    }
}
#endif /* HAVE_STRERROR */


/* strstr() */
#ifndef HAVE_STRSTR


#endif /* HAVE_STRSTR */


#ifdef __PUREC__
/*
 * a substitute for PureC's buggy sscanf.
 * this uses the normal sscanf and fixes the following bugs:
 * - whitespace in format matches whitespace in string, but doesn't
 *   require any. ( "%f , %f" scans "1,2" correctly )
 * - the ignore value feature works (*). this created an address error
 *   in PureC.
 */

#include <stdarg.h>

int
purec_sscanf(const char *string, const char *format,...)
{
    va_list args;
    int cnt = 0;
    char onefmt[256];
    char buffer[256];
    const char *f = format;
    const char *s = string;
    char *f2;
    char ch;
    int ignore;
    void *p;
    int *ip;
    int pos;

    va_start(args, format);
    while (*f && *s) {
	ch = *f++;
	if (ch != '%') {
	    if (isspace((unsigned char) ch)) {
		/* match any number of whitespace */
		while (isspace((unsigned char) *s))
		    s++;
	    } else {
		/* match exactly the character ch */
		if (*s != ch)
		    goto finish;
		s++;
	    }
	} else {
	    /* we have got a '%' */
	    ch = *f++;
	    if (ch == '%') {
		/* match exactly % */
		if (*s != ch)
		    goto finish;
		s++;
	    } else {
		f2 = onefmt;
		*f2++ = '%';
		*f2++ = ch;
		ignore = 0;
		if (ch == '*') {
		    ignore = 1;
		    ch = f2[-1] = *f++;
		}
		while (isdigit((unsigned char) ch)) {
		    ch = *f2++ = *f++;
		}
		if (ch == 'l' || ch == 'L' || ch == 'h') {
		    ch = *f2++ = *f++;
		}
		switch (ch) {
		case '[':
		    while (ch && ch != ']') {
			ch = *f2++ = *f++;
		    }
		    if (!ch)
			goto error;
		    break;
		case 'e':
		case 'f':
		case 'g':
		case 'd':
		case 'o':
		case 'i':
		case 'u':
		case 'x':
		case 'c':
		case 's':
		case 'p':
		case 'n':	/* special case handled below */
		    break;
		default:
		    goto error;
		}
		if (ch != 'n') {
		    strcpy(f2, "%n");
		    if (ignore) {
			p = buffer;
		    } else {
			p = va_arg(args, void *);
		    }
		    switch (sscanf(s, onefmt, p, &pos)) {
		    case EOF:
			goto error;
		    case 0:
			goto finish;
		    }
		    if (!ignore)
			cnt++;
		    s += pos;
		} else {
		    if (!ignore) {
			ip = va_arg(args, int *);
			*ip = (int) (s - string);
		    }
		}
	    }
	}
    }

    if (!*f)
	goto finish;

  error:
    cnt = EOF;
  finish:
    va_end(args);
    return cnt;
}

/* use the substitute now. I know this is dirty trick, but it works. */
#define sscanf purec_sscanf

#endif /* __PUREC__ */


/*
 * POSIX functions
 */

#ifndef HAVE_SLEEP
/* The implementation below does not even come close
 * to what is required by POSIX.1, but I suppose
 * it doesn't really matter on these systems. lh
 */


#ifdef AMIGA_SC_6_1
#include <proto/dos.h>
#endif

unsigned int
sleep(unsigned int delay)
{
#if defined(MSDOS) || defined(_Windows) || defined(DOS386) || defined(AMIGA_AC_5)
# if !(defined(__TURBOC__) || defined(__EMX__) || defined(DJGPP)) || defined(_Windows)	/* Turbo C already has sleep() */
    /* kludge to provide sleep() for msc 5.1 */
    unsigned long time_is_up;

    time_is_up = time(NULL) + (unsigned long) delay;
    while (time(NULL) < time_is_up)
	/* wait */ ;
# endif /* !__TURBOC__ ... */
#endif /* MSDOS ... */

#ifdef AMIGA_SC_6_1
    Delay(50 * delay);
#endif

#ifdef WIN32
    Sleep((DWORD) delay * 1000);
#endif

    return 0;
}

#endif /* HAVE_SLEEP */


/*
 * Other common functions
 */

/*****************************************************************
    portable implementation of strnicmp (hopefully)
*****************************************************************/
#ifndef HAVE_STRCASECMP
# ifndef HAVE_STRICMP

/* return (see MSVC documentation and strcasecmp()):
 *  -1  if str1 < str2
 *   0  if str1 == str2
 *   1  if str1 > str2
 */
int
gp_stricmp(const char *s1, const char *s2)
{
    unsigned char c1, c2;

    do {
	c1 = *s1++;
	if (islower(c1))
	    c1 = toupper(c1);
	c2 = *s2++;
	if (islower(c2))
	    c2 = toupper(c2);
    } while (c1 == c2 && c1 && c2);

    if (c1 == c2)
	return 0;
    if (c1 == '\0' || c1 > c2)
	return 1;
    return -1;
}
# endif				/* !HAVE_STRCASECMP */
#endif /* !HAVE_STRNICMP */

/*****************************************************************
    portable implementation of strnicmp (hopefully)
*****************************************************************/

#ifndef HAVE_STRNCASECMP
# ifndef HAVE_STRNICMP

int
gp_strnicmp(const char *s1, const char *s2, size_t n)
{
    unsigned char c1, c2;

    if (n == 0)
	return 0;

    do {
	c1 = *s1++;
	if (islower(c1))
	    c1 = toupper(c1);
	c2 = *s2++;
	if (islower(c2))
	    c2 = toupper(c2);
    } while (c1 == c2 && c1 && c2 && --n > 0);

    if (n == 0 || c1 == c2)
	return 0;
    if (c1 == '\0' || c1 > c2)
	return 1;
    return -1;
}
# endif				/* !HAVE_STRNCASECMP */
#endif /* !HAVE_STRNICMP */


/* Safe, '\0'-terminated version of strncpy()
 * safe_strncpy(dest, src, n), where n = sizeof(dest)
 * This is basically the old fit.c(copy_max) function
 */
char *
safe_strncpy(char *d, const char *s, size_t n)
{
    char *ret;

    ret = strncpy(d, s, n);
    if (strlen(s) >= n)
	d[n > 0 ? n - 1 : 0] = NUL;

    return ret;
}

#ifndef HAVE_STRCSPN
/*
 * our own substitute for strcspn()
 * return the length of the inital segment of str1
 * consisting of characters not in str2
 * returns strlen(str1) if none of the characters
 * from str2 are in str1
 * based in misc.c(instring) */
size_t
gp_strcspn(const char *str1, const char *str2)
{
    char *s;
    size_t pos;

    if (!str1 || !str2)
	return 0;
    pos = strlen(str1);
    while (*str2++)
	if (s = strchr(str1, *str2))
	    if ((s - str1) < pos)
		pos = s - str1;
    return (pos);
}
#endif /* !HAVE_STRCSPN */

double
gp_strtod(const char *str, char **endptr)
{
#if (0)  /* replace with test for platforms with broken strtod() */
    int used;
    double d;
    int n = sscanf(str, "%lf%n", &d, &used);
    if (n < 1)
	*endptr = (char *)str;
    else
	*endptr = (char *)(str + used);
    return d;
#else
    return strtod(str,endptr);
#endif
}
