/* File MSNLIB.C
 * Replacement for C library for use with MS-DOS Kermit.
 *
 *	Copyright (C) 1982, 1999, Trustees of Columbia University in the 
 *	City of New York.
 * Effective 1 July 2011, MS-DOS Kermit should be considered Open
 * Source software under the Revised 3-Clause BSD License, even
 * though the software itself and associated files may carry the old
 * copyright and licensing information.
 * http://www.columbia.edu/kermit/mskermit.html
 *
 * Last edit
 * 12 Jan 1995 v3.14
 *  Authors: J.R.Doupnik, USU, Frank da Cruz, Columbia Univ.
 * Contains:
 * strchr, strcat, strncat, strcpy, strncpy, strlen, strcmp, stricmp, strncmp
 * atoi, itoa, ltoa, isdigit, ntoa.
*/
#include "msntcp.h"
#include "msnlib.h"

#ifndef MSDOS
/*
 In MS-DOS Kermit, these are assembler routines to avoid math library.
*/
#define ourmod(a,b)   (a % b)
#define ourdiv(a, b)  (a / b)
#define ourlmod(a,b)  (a % b)
#define ourldiv(a, b) (a / b)
#endif /* MSDOS */

#ifndef NULL
#define NULL 0
#endif /* NULL */

/*
 By the way, there is probably no point in the next #ifndef,
 because size_t is either built into to the compiler or typedef'd,
 rather than defined.  So the #define below will always happen.
*/
#ifndef size_t
#define size_t int
#endif /* size_t */

int _acrtused;			/* MS C compiler startup file quantity */

/*
  _strchr
  Finds first occurence of character c in string s.
  Returns pointer to it if found, NULL if not found.
*/
byte *
strchr(byte *s, const byte c) {
    while ((*s != (byte)'\0') && (*s != (byte)(c & 0xff))) s++;
    if (*s == '\0') return(NULL);
    else return(s);
}

byte FAR *
strchrf(byte FAR *s, const byte c) {
    while ((*s != (byte)'\0') && (*s != (byte)(c & 0xff))) s++;
    if (*s == '\0') return(NULL);
    else return(s);
}

/*
  _strcat
  Appends entire string s2 to string s1.
  Assumes there is room for s2 after end of s1.
  Returns pointer to s1 or NULL if s1 is a null pointer.
*/
byte *
strcat(byte *s1, byte *s2) {
    register byte *p;

    if (s1 == NULL) return(NULL);
    if (s2 == NULL || *s2 == '\0') return(s1);
    p = s1;				/* Make copy */
    while (*p) p++;			/* Find end */
    while (*p++ = *s2++);		/* Copy thru terminating NUL */
    return(s1);				/* Return original */
}

/*
  _strncat
  Appends up to n chars of string s2 to string s1.
  Returns pointer to string1 or NULL if s1 is a null pointer.
*/
byte *
strncat(byte *s1, byte *s2, size_t n) {
    register byte * p;

    if (s1 == NULL) return(NULL);
    if (s2 == NULL || *s2 == '\0') return(s1);
    p = s1;				/* Copy pointer */
    while (*p) p++;			/* Step to end of s1 */
    while ((*p++ = *s2++) && (--n > 0)); /* Copy up to n bytes of s2 */
    return(s1);				/* Return original pointer */
}

/*
  _strcpy
  Copies s2 to s1, returns pointer to s1 or NULL if s1 was NULL.
*/
byte *
strcpy(byte *s1, byte *s2) {
    register byte *p;

    if (s1 == NULL) return(NULL);
    if (s2 == NULL) s2 = "";
    p = s1;				/* Copy pointer */
    while (*p++ = *s2++);		/* Copy thru terminating NUL */
    return(s1);				/* Return original pointer */
}

/*
  _strncpy
  Copies at most n characters from s2 to to s1, returns pointer to s1.
  Returns s1 or NULL if s1 was NULL.
*/
byte *
strncpy(byte *s1, byte *s2, size_t n) {
    register int s2len;
    register byte *p1;

    if (s1 == NULL) return(NULL);
    if (s2 == NULL) s2 = "";
    if ((s2len = strlen(s2)) > n) s2len = n;
    p1 = s1;

    while (s2len-- > 0)			/* Copy */
      *p1++ = *s2++;
    *p1 = '\0';				/* Terminate */
    return(s1);				/* No need to pad out, one's enuf */
}

/*
  _strlen
  Returns length of null-terminated string not including '\0'
*/
size_t
strlen(byte *s) {
    register int i = 0;

    if (s == NULL) return(0);
    while (*s++) i++;
    return(i);
}

/*
  _strcmp
  Compare null-terminated strings using ASCII values.
  Case matters.  Returns:
  < 0 if s1 < s2,
  = 0 if s1 = s2,
  > 0 if s1 > s2
*/
int
strcmp(byte *s1, byte *s2) {
    if (s1 == NULL) s1 = "";
    if (s2 == NULL) s2 = "";
    do {
	if (*s1 < *s2) return(-1);
	if (*s1 > *s2) return(1);
	if (*s2 == '\0') return(0);
	s2++;
    } while (*s1++);
    return(0);
}

/*
  _stricmp
  Like strcmp but case insenstive
*/
int
stricmp(byte *s1, byte *s2) {
    register byte c1, c2;

    if (s1 == NULL) s1 = "";
    if (s2 == NULL) s2 = "";
    do {
	c1 = *s1; c2 = *s2;
	if ('a' <= c1 && c1 <= 'z') c1 = c1 - (byte)('a' - 'A');
	if ('a' <= c2 && c2 <= 'z') c2 = c2 - (byte)('a' - 'A');
	if (c1 < c2) return(-1);
	if (c1 > c2) return(1);
	if (c2 == '\0') return(0);
	s1++; s2++;
    } while (c1 != '\0');
    return(0);
}

/*
  _strncmp
  Compares at most n characters of strings s1 and s2.
*/
int
strncmp(byte *s1, byte *s2, size_t n) {	

    if (s1 == NULL) s1 = "";
    if (s2 == NULL) s2 = "";
    while (n-- > 0 && *s1) {
	if (*s1 < *s2) return(-1);
	if (*s1 > *s2) return(1);
	s1++; s2++;
    }
    return(0);
}

/*
  _atoi
  Converts decimal numeric string to integer.
  Breaks on first non-digit or end of string.
  Returns integer.
*/
int
atoi(byte *s) {
    register int i, count;
    count = 0;
    for (i = 0; i < 18; i++) {
	if (*s < '0' || *s > '9') break;
	count *= 10;
	count += *s - '0';		/* ascii to binary */
	s++;
    }
    return(count);
}

/*
  _itoa
  Converts integer value to ASCII digits (up to 18 characters long),
  stores in string, null terminated.  Returns NULL on failure,
  pointer to result on success.
*/
byte *
itoa(int value, byte *string, int radix) { /* From K & R */
    int c, j, sign;
    register int i;
    register byte *s;

    if (string == NULL) return(NULL);
    
    s = string;

    if ((sign = value) < 0)		/* Save sign */
      value = - value;			/* Force value positive */
    i = 0;
    do {
	s[i++] = (byte)(ourmod(value, radix) + '0');
    } while ((value = ourdiv(value, radix)) > 0);

    if (sign < 0)
      s[i++] = '-';
    s[i] = '\0';
    j = strlen(s) -1;
    for (i = 0; i < j; i++, j--) {
	c = s[i];
	if (c > '9') c = c - '9' + 'A' -1;
	s[i] = s[j];
	s[j] = (byte)(c & 0xff);
    }
    return(string);
}

/*
  _ltoa
  Like itoa() but using long value ( < 34 ).
*/
byte *
ltoa(long value, byte *string, int radix) { /* K & R */
    int c, j;
    register int i;
    long sign;
    register byte * s;

    if (string == NULL) return(NULL);
    s = string;

    if ((sign = value) < 0) value = - value; /* value to positive*/
    i = 0;
    do {
	s[i++] = (byte)(ourlmod(value, radix) + '0');
    } while ((value = ourldiv(value, radix)) > 0);

    if (sign < 0)
      s[i++] = '-';
    s[i] = '\0';

    j = strlen(s) - 1;
    for (i = 0; i < j; i++, j--) {
	c = s[i];
	if (c > '9') c = c - '9' + 'A' -1;
	s[i] = s[j];
	s[j] = (byte)(c & 0xff);
    }
    return(string);
}

/*
  _isdigit
  Returns 1 if argument is a decimal digit, 0 otherwise.
*/
int
isdigit(const byte c) {
    if ((c & 0xff) < '0' || (c & 0xff) > '9')
      return(0);			/* say is not a digit */
    return(1);
}

/* 
   Convert long val to dotted decimal string at pointer p, does high order
   byte first. Intended to yield dotted decimal IP addresses from longs.
*/
void
ntoa(byte *p, unsigned long val)
{
	register byte *ptr;
	register int i;
	
	ptr = p;
	for (i = 24; i >= 0; i -= 8)
		{
		itoa((int)((val >> i) & 0xff), ptr, 10); /* convert a byte */
		strcat(ptr, ".");			/* dot separator */
		ptr = p + strlen(p);
		}
	*(--ptr) = '\0';			/* remove trailing dot */
}
