/*************************************************************************** * Copyright (C) 2008 by Peter Eschright * * petereschright@gmail.com * * * * This source code is presented for informational purposes only. * * Please contact the author for permision for use. * ***************************************************************************/ #include <stdio.h> #include <stdlib.h> #include "user_input.h" /*************************************************************************** * * * user_input.c * * * * A library for simple safe command line input from a user. * * Include functions for inputing most data types, with error * * checking and safety. * * * * * * Usage: * * * * Compile and link with your project, or build seperatly and link. * * * * * * Notes: * * - All functions read in entire input buffer and make sure buffer is * * ready for next input. * * - Maximum read size for all functions is defined by READ_SIZE * * * * * * * * * * * * * * * * * * * * * * * * Error codes-- (defined in user_input.h) * * * * user failed to input desired data type: USER_INPUT_ERROR_BAD_INPUT * * * * user input exceeded allowed size: USER_INPUT_ERROR_TOO_LONG * * * * std input stream was closed: USER_INPUT_ERROR_CLOSED_STREAM * * * * input read failed for other reason: USER_INPUT_ERROR_FAILED_READ * * * ***************************************************************************/ /************************************************** * * * Normalize a string: * * copy normalized str_1 to str_2 * * * * String is trimmed of preceding and trailing * * whitespace. string has internal whitespace * * collapsed to single space character. * * * **************************************************/ void trim_and_normalize(char *str_2,char *str_1){ register char *ptr1 = str_1; register char *ptr2 = str_2; register int state = 0; while(1){ if(state == 0){ while( isspace(*ptr1) ) ++ptr1; state = 1; } else if(state == 1){ do *(ptr2++) = *(ptr1++); while(((*ptr1) != NULL) && !isspace(*ptr1)); if(*ptr1 == '\0'){ break; } state = 2; } else if(state == 2){ while(isspace(*ptr1) && *ptr1 != '\n') ++ptr1; if(*ptr1 == '\n'){ break; } else{ *(ptr2++) = ' '; state = 1; } } } *ptr2 = '\0'; return; } /********************************* * read in an unsigned integer * * return 0 iff sucessful * * return error code if failed * *********************************/ int user_input_uint(unsigned int *get_number){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ int read_return; /* captures return from read */ int buffer_not_clean = 0; /* used to see that all input was read */ int eof_set = 0; /* set to one if eof encountered */ int failed_eof_set = 0; /* set to one if eof encountered during first read used to determine reason for bad read */ int error_set = 0; /* set to one if error encountered */ int failed_error_set = 0; /* set to one if error encountered during first read used to determine reason for bad read */ int input_type_ok = 0; /* set to one iff input was valid */ /* get input from stdin */ if(!fgets(read_str,READ_SIZE,stdin)){ /* if input failed do error checking and return */ if(feof(stdin)){ eof_set = 1; failed_eof_set = 1; } if(ferror(stdin)){ error_set = 1; failed_error_set = 1; } } /* see that all input was read, look for newline */ if(!strstr(read_str,"\n")){ buffer_not_clean = 1; } /* attempt to extract value */ if(!error_set){ input_type_ok = sscanf(read_str,"%u",get_number); } /* clean up messes, order is important */ /* if error set clear error flag */ /* come back if failed to clear error */ retry_error_clear: if(error_set){ clearerr(stdin); error_set = 0; } /* empty buffer if needed */ if(buffer_not_clean){ int detect_eof; while((detect_eof = getchar()) != '\n' && (detect_eof != EOF)); if(detect_eof == EOF){ /* see if another error occured */ if(ferror(stdin)){ error_set = 1; goto retry_error_clear; } /* else assume eof reached ok */ eof_set = 1; } buffer_not_clean = 0; } /* if eof set clear flag */ if(eof_set){ clearerr(stdin); } /* return appropriate value */ if(input_type_ok == 1) return(0); else if(failed_error_set) return(USER_INPUT_ERROR_FAILED_READ); else if(failed_eof_set) return(USER_INPUT_ERROR_CLOSED_STREAM); else return(USER_INPUT_ERROR_BAD_INPUT); } /********************************* * read in an integer * * return 0 iff sucessful * * return error code if failed * *********************************/ int user_input_int(int *get_number){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ int read_return; /* captures return from read */ int buffer_not_clean = 0; /* used to see that all input was read */ int eof_set = 0; /* set to one if eof encountered */ int failed_eof_set = 0; /* set to one if eof encountered during first read used to determine reason for bad read */ int error_set = 0; /* set to one if error encountered */ int failed_error_set = 0; /* set to one if error encountered during first read used to determine reason for bad read */ int input_type_ok = 0; /* set to one iff input was valid */ /* get input from stdin */ if(!fgets(read_str,READ_SIZE,stdin)){ /* if input failed do error checking and return */ if(feof(stdin)){ eof_set = 1; failed_eof_set = 1; } if(ferror(stdin)){ error_set = 1; failed_error_set = 1; } } /* see that all input was read, look for newline */ if(!strstr(read_str,"\n")){ buffer_not_clean = 1; } /* attempt to extract value */ if(!error_set){ input_type_ok = sscanf(read_str,"%d",get_number); } /* clean up messes, order is important */ /* if error set clear error flag */ /* come back if failed to clear error */ retry_error_clear: if(error_set){ clearerr(stdin); error_set = 0; } /* empty buffer if needed */ if(buffer_not_clean){ int detect_eof; while((detect_eof = getchar()) != '\n' && (detect_eof != EOF)); if(detect_eof == EOF){ /* see if another error occured */ if(ferror(stdin)){ error_set = 1; goto retry_error_clear; } /* else assume eof reached ok */ eof_set = 1; } buffer_not_clean = 0; } /* if eof set clear flag */ if(eof_set){ clearerr(stdin); } /* return appropriate value */ if(input_type_ok == 1) return(0); else if(failed_error_set) return(USER_INPUT_ERROR_FAILED_READ); else if(failed_eof_set) return(USER_INPUT_ERROR_CLOSED_STREAM); else return(USER_INPUT_ERROR_BAD_INPUT); } /********************************* * read in an float * * return 0 iff sucessful * * return error code if failed * *********************************/ int user_input_float(float *get_number){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ int read_return; /* captures return from read */ int buffer_not_clean = 0; /* used to see that all input was read */ int eof_set = 0; /* set to one if eof encountered */ int failed_eof_set = 0; /* set to one if eof encountered during first read used to determine reason for bad read */ int error_set = 0; /* set to one if error encountered */ int failed_error_set = 0; /* set to one if error encountered during first read used to determine reason for bad read */ int input_type_ok = 0; /* set to one iff input was valid */ /* get input from stdin */ if(!fgets(read_str,READ_SIZE,stdin)){ /* if input failed do error checking and return */ if(feof(stdin)){ eof_set = 1; failed_eof_set = 1; } if(ferror(stdin)){ error_set = 1; failed_error_set = 1; } } /* see that all input was read, look for newline */ if(!strstr(read_str,"\n")){ buffer_not_clean = 1; } /* attempt to extract value */ if(!error_set){ input_type_ok = sscanf(read_str,"%f",get_number); } /* clean up messes, order is important */ /* if error set clear error flag */ /* come back if failed to clear error */ retry_error_clear: if(error_set){ clearerr(stdin); error_set = 0; } /* empty buffer if needed */ if(buffer_not_clean){ int detect_eof; while((detect_eof = getchar()) != '\n' && (detect_eof != EOF)); if(detect_eof == EOF){ /* see if another error occured */ if(ferror(stdin)){ error_set = 1; goto retry_error_clear; } /* else assume eof reached ok */ eof_set = 1; } buffer_not_clean = 0; } /* if eof set clear flag */ if(eof_set){ clearerr(stdin); } /* return appropriate value */ if(input_type_ok == 1) return(0); else if(failed_error_set) return(USER_INPUT_ERROR_FAILED_READ); else if(failed_eof_set) return(USER_INPUT_ERROR_CLOSED_STREAM); else return(USER_INPUT_ERROR_BAD_INPUT); } /************************************************* * read in an double * * * * Function reads float and casts it to a double. * * return 0 iff sucessful * * return error code if failed * *************************************************/ int user_input_double(double *get_number){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ int read_return; /* captures return from read */ int buffer_not_clean = 0; /* used to see that all input was read */ int eof_set = 0; /* set to one if eof encountered */ int failed_eof_set = 0; /* set to one if eof encountered during first read used to determine reason for bad read */ int error_set = 0; /* set to one if error encountered */ int failed_error_set = 0; /* set to one if error encountered during first read used to determine reason for bad read */ int input_type_ok = 0; /* set to one iff input was valid */ float temp = 0.0; /* get input from stdin */ if(!fgets(read_str,READ_SIZE,stdin)){ /* if input failed do error checking and return */ if(feof(stdin)){ eof_set = 1; failed_eof_set = 1; } if(ferror(stdin)){ error_set = 1; failed_error_set = 1; } } /* see that all input was read, look for newline */ if(!strstr(read_str,"\n")){ buffer_not_clean = 1; } /* attempt to extract value */ if(!error_set){ input_type_ok = sscanf(read_str,"%f",&temp); *get_number = (double)temp; } /* clean up messes, order is important */ /* if error set clear error flag */ /* come back if failed to clear error */ retry_error_clear: if(error_set){ clearerr(stdin); error_set = 0; } /* empty buffer if needed */ if(buffer_not_clean){ int detect_eof; while((detect_eof = getchar()) != '\n' && (detect_eof != EOF)); if(detect_eof == EOF){ /* see if another error occured */ if(ferror(stdin)){ error_set = 1; goto retry_error_clear; } /* else assume eof reached ok */ eof_set = 1; } buffer_not_clean = 0; } /* if eof set clear flag */ if(eof_set){ clearerr(stdin); } /* return appropriate value */ if(input_type_ok == 1) return(0); else if(failed_error_set) return(USER_INPUT_ERROR_FAILED_READ); else if(failed_eof_set) return(USER_INPUT_ERROR_CLOSED_STREAM); else return(USER_INPUT_ERROR_BAD_INPUT); } /********************************* * read in a single char * * return 0 iff sucessful * * return error code if failed * *********************************/ int user_input_char(char *get_c){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ int read_return; /* captures return from read */ int buffer_not_clean = 0; /* used to see that all input was read */ int eof_set = 0; /* set to one if eof encountered */ int failed_eof_set = 0; /* set to one if eof encountered during first read used to determine reason for bad read */ int error_set = 0; /* set to one if error encountered */ int failed_error_set = 0; /* set to one if error encountered during first read used to determine reason for bad read */ int input_type_ok = 0; /* set to one iff input was valid */ /* get input from stdin */ if(!fgets(read_str,READ_SIZE,stdin)){ /* if input failed do error checking and return */ if(feof(stdin)){ eof_set = 1; failed_eof_set = 1; } if(ferror(stdin)){ error_set = 1; failed_error_set = 1; } } /* see that all input was read, look for newline */ if(!strstr(read_str,"\n")){ buffer_not_clean = 1; } /* attempt to extract value */ if(!error_set){ input_type_ok = sscanf(read_str,"%1s",get_c); } /* clean up messes, order is important */ /* if error set clear error flag */ /* come back if failed to clear error */ retry_error_clear: if(error_set){ clearerr(stdin); error_set = 0; } /* empty buffer if needed */ if(buffer_not_clean){ int detect_eof; while((detect_eof = getchar()) != '\n' && (detect_eof != EOF)); if(detect_eof == EOF){ /* see if another error occured */ if(ferror(stdin)){ error_set = 1; goto retry_error_clear; } /* else assume eof reached ok */ eof_set = 1; } buffer_not_clean = 0; } /* if eof set clear flag */ if(eof_set){ clearerr(stdin); } /* return appropriate value */ if(input_type_ok == 1) return(0); else if(failed_error_set) return(USER_INPUT_ERROR_FAILED_READ); else if(failed_eof_set) return(USER_INPUT_ERROR_CLOSED_STREAM); else return(USER_INPUT_ERROR_BAD_INPUT); } /************************************************** * read in a string * * * * String is trimmed of preceding and trailing * * whitespace. * * * * upto size - 1 characters are read. * * USER_INPUT_ERROR_BAD_INPUT is returned if * * input was too long for get_str and remaining * * input is removed from buffer. * * * * return 0 iff sucessful * * return error code if failed * **************************************************/ int user_input_str(char *get_str, int size){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ char trimmed_str[READ_SIZE]; trimmed_str[0] = 0; /* used to hold trimmed version of input */ int read_return; /* captures return from read */ int buffer_not_clean = 0; /* used to see that all input was read */ int eof_set = 0; /* set to one if eof encountered */ int failed_eof_set = 0; /* set to one if eof encountered during first read used to determine reason for bad read */ int error_set = 0; /* set to one if error encountered */ int failed_error_set = 0; /* set to one if error encountered during first read used to determine reason for bad read */ int input_type_ok = 0; /* set to one iff input was valid */ int input_too_long = 0; /* set to one iff input was longer than size size */ /* get input from stdin */ if(!fgets(read_str,READ_SIZE,stdin)){ /* if input failed do error checking and return */ if(feof(stdin)){ eof_set = 1; failed_eof_set = 1; } if(ferror(stdin)){ error_set = 1; failed_error_set = 1; } } /* see that all input was read, look for newline */ if(!strstr(read_str,"\n")){ buffer_not_clean = 1; } /* attempt to extract value */ if(!error_set){ if(buffer_not_clean){ input_type_ok = 0; input_too_long = 1; }else{ /* trim the input */ trim_and_normalize(trimmed_str,read_str); /* make sure size was ok */ if((strlen(trimmed_str) + 1 ) > size){ input_type_ok = 0; input_too_long = 1; } else{ /* looks ok */ strcpy(get_str,trimmed_str); input_type_ok = 1; } } } /* clean up messes, order is important */ /* if error set clear error flag */ /* come back if failed to clear error */ retry_error_clear: if(error_set){ clearerr(stdin); error_set = 0; } /* empty buffer if needed */ if(buffer_not_clean){ int detect_eof; while((detect_eof = getchar()) != '\n' && (detect_eof != EOF)); if(detect_eof == EOF){ /* see if another error occured */ if(ferror(stdin)){ error_set = 1; goto retry_error_clear; } /* else assume eof reached ok */ eof_set = 1; } buffer_not_clean = 0; } /* if eof set clear flag */ if(eof_set){ clearerr(stdin); } /* return appropriate value */ if(input_type_ok == 1) return(0); else if(input_too_long){ get_str[0] = 0; return(USER_INPUT_ERROR_TOO_LONG); } else if(failed_error_set) return(USER_INPUT_ERROR_FAILED_READ); else if(failed_eof_set) return(USER_INPUT_ERROR_CLOSED_STREAM); else return(USER_INPUT_ERROR_BAD_INPUT); } /************************************************** * read in a word, defined as a continuos * * string of non-whitespace characters. * * * * word is trimmed of preceding and trailing * * whitespace. * * * * upto size - 1 characters are read. * * USER_INPUT_ERROR_BAD_INPUT is returned if * * input was too long for get_str and remaining * * input is removed from buffer. * * * * return 0 iff sucessful * * return error code if failed * **************************************************/ int user_input_word(char *get_word, int size){ char read_str[READ_SIZE]; read_str[0] = 0; /* used to read from stdin */ register int read_value = 0; register int i = 0; register char c; read_value = user_input_str(read_str,READ_SIZE); if(read_value){ return(read_value); } c = read_str[i]; while(c && !isspace(c) && (i < (size - 1))){ get_word[i] = c; ++c; ++i; c = read_str[i]; } get_word[i] = '\0'; if(i < size) return(0); else return(USER_INPUT_ERROR_TOO_LONG); } /************************************************** * read in a yes or no response, * * defined as follows * * string of non-whitespace characters. * * * * preceding whitespace and all characters * * first word (see above) are ignored. * * * * answer is set to 1 iff yes or 0 iff no * * or -1 iff error) * * * * The input is then interpreted as follows: * * input interpretation value of answer * * YES yes 1 * * Yes yes 1 * * yes yes 1 * * Y yes 1 * * y yes 1 * * NO no 0 * * No no 0 * * no no 0 * * N no 0 * * n no 0 * * other error -1 * * * * * * USER_INPUT_ERROR_BAD_INPUT is returned if * * input was not of form described * * Unused input is removed from buffer. * * * * return 0 iff sucessful * * return error code if failed * **************************************************/ int user_input_yes_no(int *answer){ char answr_str[8]; answr_str[0] = '\0'; user_input_word(answr_str,8); if( (strcmp("YES",answr_str) == 0) || (strcmp("Yes",answr_str) == 0) || (strcmp("yes",answr_str) == 0) || (strcmp("Y",answr_str) == 0) || (strcmp("y",answr_str) == 0) ){ *answer = 1; return(0); } else if( (strcmp("NO",answr_str) == 0) || (strcmp("No",answr_str) == 0) || (strcmp("no",answr_str) == 0) || (strcmp("N",answr_str) == 0) || (strcmp("n",answr_str) == 0) ){ *answer = 0; return(0); } *answer = -1; return(1); }