/*
 * This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

/*
 * list of functions already implemented:
 *
 * size_t xgets(char *STRING, size_t SIZE);
 * int to_long(char *STRING, long int *NUM);
 * int to_double(char *STRING, double *NUM);
 * void str_tolower(char *STRING);
 * void str_toupper(char *STRING);
 * int y_n(char *STRING);
 */

/*
 * function that makes the same thing as gets, but it provides ways of
 * preventing buffer overflows. it also works "flushing" the stdin, not
 * allowing the string to terminate with a '\n' in order to avoid the
 * problems arisen from that. it will read SIZE-1 characters (to put
 * the null character at the end of the string). it returns the number
 * of characters read, -1 for error
 */
size_t xgets(char *STRING, size_t SIZE) 
{
	register int i;
	char ch;

	if (SIZE < 1) { /* checks if the size of buffer to be read is less
						than 1 */
		fprintf(stderr, "Size of buffer should be 1 or more.\n");
		return -1;
	}

	for(i = 0 ; i < SIZE ; i++) {
		ch = getchar(); /* get the chars one by one */
		if( ch == EOF ) return EOF;
		switch (ch) {
			case '\n': /* terminates the reading if it finds a new line */
				STRING[i] = '\0'; /* replaces the ending newline with
									a null characters */
				return (i); /* return the size of buffer read */
			case '\b': /* treats the backspace case */
				if (i > 0) {
					i--;
				}
				break;
			default:
				STRING[i] = ch;
		}
	}

	while ((ch = getchar())) { /* this while loop will flush the output, */
		if (ch == '\n') {      /* preventing the next input to be 'skipped' */
			ch = '\0';         /* if you enter a string bigger than 
									the buffer */
			break;
		}
		else {
			ch = '\0';
		}
	}

	STRING[SIZE-1] = '\0'; /* if the buffer is greater than SIZE, put the the
								null character in the last postion */
	return (SIZE-1);
}

/*
 * convert a string to a long int. you pass the string and long number (as a 
 * pointer) where it will be stored. the funcion returns a status code:
 *   0      if everything has gone well
 *   -1     if the string is NaN
 *   -2     if the number is a fraction
 *   ERANGE if it is out of range
 */
int to_long(char *STRING, long int *NUM)
{
	char *tail;

	/* cuts off the white spaces */
	while(isspace (*STRING)) {
		STRING++;
	}

	/* STRING is empty? */
	if(*STRING == 0)
		return -100; /* NaN */

	errno = 0;
	*NUM = strtol(STRING, &tail, 0);

	if(errno == ERANGE) {
		return(ERANGE); /* number too large, out of range*/
	}
	else if(tail == STRING) {
		return -1; /* NaN */
	}
	/* if we have a period, strtol() will split the string, so 
	 * we have check what's after it */
	else if(*tail++ == '.') {
		/* if it is NaN after the period, then we have a problem! */
		if( *tail >= 'a' ) return -1;

		/* check if a number needs to be rounded our ceiled */
		if( *tail >= '5' && *tail <= '9' ) *NUM = *NUM + 1;

		return -2; /* it was a fraction */
	}
	else if('\0' != *tail) {
		return -1; /* NaN */
	}
	else {
		return 0; /* AGW */
	}
}

/*
 * convert a string to a double. you pass the string and double number (as a 
 * pointer) where it will be stored. the funcion returns a status code:
 *   0      if everything has gone well
 *   -1   if the string is NaN
 *   ERANGE if it is out of range
 */
int to_double(char *STRING, double *NUM)
{
	char *tail;

	/* cuts off the white spaces */
	while(isspace (*STRING)) {
		STRING++;
	}

	/* STRING is empty? */
	if(*STRING == 0)
		return -100; /* NaN */

	errno = 0;
	*NUM = strtod(STRING, &tail);

	if(errno == ERANGE) {
		return(ERANGE); /* number too large, out of range*/
	}
	else if(tail == STRING) {
		return -1; /* NaN */
	}
	else if('\0' != *tail) {
		return -1; /* NaN */
	}
	else {
		return 0; /* AGW */
	}
}

/* lowercase a string, using pre-ISO compatibility,
 * it checks if a character is not already lowercase
 * WARNING: this is a destructive function since it changes
 * the original string. if you really need it, create another
 * one with strdup() or strdupa()
 */
void str_tolower(char *STRING)
{
	int i;
	for( i = 0 ; STRING[i] ; i++ ) {
		if(isupper(STRING[i])) {
			STRING[i] = tolower(STRING[i]);
		}
	}
}

/* uppercase a string, using pre-ISO compatibility,
 * it checks if a character is not already uppercase
 * WARNING: this is a destructive function since it changes
 * the original string. if you really need it, create another
 * one with strdup() or strdupa()
 */
void str_toupper(char *STRING)
{
	int i;
	for( i = 0 ; STRING[i] ; i++ ) {
		if(islower(STRING[i])) {
			STRING[i] = toupper(STRING[i]);
		}
	}
}

/*
 * the function accepts many types of answers. by now, it will take
 * yes or no in many languages. see below what's implemented by now.
 * it will return 1 if 'yes', 0 if 'no' and -1 it if is nothing 
 * that makes sense. it is case insensitive!!!
 *
 * (english, portuguese, japanese, spanish, french, gallo, greek, tagalog)
 * YES: y, yes, s, sim, hai, si, oui, yen, yan, ne, oo
 * NO: n, no, nao, iie, non, nenni, ochi, hindi
 */
int y_n(char *STRING)
{
	char *str;

	/* creates a new string so that we can change it to lowercase
	 * while not changing the argument */
	str = strdup(STRING);

	/* lowercase it */
	str_tolower(str);

	/* check if it is yes or no, or neither of them */
	if( !strcmp(str, "y")   ||
		!strcmp(str, "yes") ||
		!strcmp(str, "s")   ||
		!strcmp(str, "sim") ||
		!strcmp(str, "hai") ||
		!strcmp(str, "si")  ||
		!strcmp(str, "oui") ||
		!strcmp(str, "yen") ||
		!strcmp(str, "yan") ||
		!strcmp(str, "ne")  ||
		!strcmp(str, "oo") ) {
		free(str);
		return 1; /* yes */
	}

	else if( !strcmp(str, "n") ||
		!strcmp(str, "no")     ||
		!strcmp(str, "nao")    ||
		!strcmp(str, "iie")    ||
		!strcmp(str, "non")    ||
		!strcmp(str, "nenni")  ||
		!strcmp(str, "ochi")   ||
		!strcmp(str, "hindi") ) {
		free(str);
		return 0; /* no */
	}
	
	else {
		free(str);
		return -1; /* what the hell? it is nothing! believe me! */
	}
}

