#include #include #include #include #include #include "sphere.h" #include "utils.h" #define MAXIO 4 typedef struct { FILE *fptr; char *filename; char is_text; char is_read; } Filetable; #ifndef SEEK_SET #define SEEK_SET 0 #endif #define MAXLINE 132 static char io_buff[MAXLINE]; Filetable fat[MAXIO]; FILE *fptr; int file_lun; int is_text; /*....................................................................... Initialise the file allocation table such that all file pointers are NULL. */ void fat_init(void) { int i; for(i=0; i= MAXIO || lun < 1 || fat[lun].fptr == NULL) { lprintf(stderr, "No file assigned with lun: %d\n",lun); return NULL; }; /* Report its text/binary type. */ *is_text = fat[lun].is_text; /* Check that the file is open for data transfer in the required direction. */ if(fat[lun].is_read != want_read && want_read != 2) { if(want_read) lprintf(stderr, "lun %d is write-only.\n",lun); else lprintf(stderr, "lun %d is read-only.\n",lun); return NULL; }; /* No errors - return the required file pointer. */ return fat[lun].fptr; } /*....................................................................... Utility routine to open a new user file and insert it in the file allocation table. It returns the FAT number if succesfull or -1 otherwise. */ int file_open(char read_write_append, char is_text, char *filename) { char mode[4]; int i; /* Find a free slot in the file allocation table. */ for(i=1;i<.>type'. Where the <> denote optional arguments. */ for(; (c=*cptr) != '\0'; cptr++) { switch (c) { case '-': case '+': case ' ': case '0': case '#': was_error = (end_flags && c != '0'); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': end_flags = 1; break; case '.': was_error = was_point; was_point = end_flags = 1; break; case 's': case 'e': case 'f': case 'g': case 'd': case 'i': end_all = 1; break; default: was_error = 1; }; /* Report syntax errors in format specifier. */ if(was_error) { lprintf(stderr, "fprintf(,'%s',...): Illegal format specifier: %s\n",fmt, fmt_ptr); return -1; }; /* When the format specifier has been syntactically checked, check that the specifier type matches the user argument type. Then send the part of the format string starting at the end of the last format specifier and ending at the end of the current specifier, to fprintf together with the pertinent argument. */ if(end_all) { /* First take a copy of the required section of the format string. */ strncpy(io_buff,str_ptr, cptr-str_ptr+1); io_buff[cptr-str_ptr+1]='\0'; /* Check that there is an argument associated with the current specifier. */ if(arg+1 > nargs) { lprintf(stderr, "fprintf(,'%s',...): More specifiers than arguments?\n",fmt); return -1; }; switch (c) { /* Now check coincidence of argument and specifier types before writing the required output. */ case 's': switch (args[arg]->atyp) { case 'c': lprintf(fptr, io_buff, *STRPTR(args[arg])); break; case 'l': lprintf(fptr, io_buff, (*LOGPTR(args[arg])) ? "TRUE":"FALSE"); break; default: was_error = 1; break; }; break; case 'e': case 'f': case 'g': was_error = (args[arg]->atyp != 'f'); if(!was_error) lprintf(fptr, io_buff, *FLTPTR(args[arg])); break; case 'd': case 'i': was_error = (args[arg]->atyp != 'i'); if(!was_error) lprintf(fptr, io_buff, *INTPTR(args[arg])); break; }; /* Report specifier-argument mismatches. */ if(was_error) { lprintf(stderr, "fprintf(,'%s',...): argument specifier %d does not match its argument\n",fmt, arg+1); return -1; }; /* Continue parsing the format string for the next argument. */ arg++; str_ptr=cptr+1; break; }; }; /* Check if the end of the format line was reached before the latest specifier was completed. */ if(c == '\0') { lprintf(stderr, "printf(,'%s',...): Final format specifier incomplete.\n",fmt); return -1; }; break; /* Check for Carriage return and line feed requests. */ case '\\': /* If a valid escape sequence is encounterred - first copy any unwritten preceding string into io_buff[] - write the pre-escape string + the escape sequence, then advance cptr and str_ptr past the escape sequence. */ switch (*(cptr+1)) { case 'n': case 'r': case 't': strncpy(io_buff,str_ptr, cptr-str_ptr); io_buff[cptr-str_ptr]='\0'; switch (*(cptr+1)) { case 'n': lprintf(fptr, "%s\n",io_buff); break; case 'r': lprintf(fptr, "%s\r",io_buff); break; }; str_ptr = (++cptr) + 1; }; break; default: break; }; }; /* The final character of the format string has been parsed, write any remaining un-written output. */ if(cptr-str_ptr > 0) lprintf(fptr, str_ptr); return 0; } /*....................................................................... Query the user. First prompt the user using the string sent as the only argument with '? (y/n): ' appended. Return 1 if the user answers with 'y' or presses carriage return without any string. Otherwise if the user enters 'n' return 0. If anything else is typed re-prompt with 'Please type y (or press return) for yes or n for no: '. On error -1 is returned. */ int ask_user(char prompt[]) { static char *ctst; /* Prompt. */ lprintf(stdout, "%s? (y/n): ",prompt); for (;;) { ctst = fgets(io_buff, MAXLINE, stdin); if(ctst == NULL) { clearerr(stdin); lprintf(stderr, "Aborted due to read error on stdin\n"); return -1; }; /* Check what the user typed. */ if(io_buff[1]=='\n') { switch (io_buff[0]) { case 'y': case 'Y': return 1; case 'n': case 'N': return 0; }; } /* If nothing was typed before new-line the default is true. */ else if(io_buff[0]=='\n') return 1; /* Re-prompt. */ lprintf(stdout, "Please answer y or n. %s? (y/n): ",prompt); }; } /*....................................................................... * Query the user for a string. The user will be presented with the given * prompt, and the provided default value. If the user presses return * without entering anything then the default string will be substituted. * * Input: * prompt char * The string the prompt the user with. * defstr char * The default string to be returned if the user * doesn't enter anything. If this is NULL then * the user will be re-prompted for input. * Output: * return char * The answer string or NULL on error. Note that * this is malloced memory which must be free'd * by the caller when no longer required. */ char *prompt_user(char *prompt, char *defstr) { char *retval; /* A malloc'd copy of the answer string */ char *answer = NULL; /* The user's answer */ /* * Ask the use for input and read the user's reply. * This may take more than one attempt. */ while(!answer) { char *nl; /* A pointer to the newline in io_buff[] */ /* * Prompt for input. */ if(!defstr || *defstr == '\0') lprintf(stdout, "%s: ", prompt); else lprintf(stdout, "%s (%s): ", prompt, defstr); /* * Read the reply. */ if(!fgets(io_buff, MAXLINE, stdin)) { clearerr(stdin); lprintf(stderr, "prompt_user: Aborted due to read error on stdin.\n"); return NULL; }; /* * Find the newline. */ nl = strchr(io_buff, '\n'); /* * String too long? */ if(!nl) { lprintf(stderr, "prompt_user: String too long.\n"); continue; }; /* * Remove the newline character. */ *nl = '\0'; /* * If the string is empty and there is a default string, substitute * the default. */ if(io_buff[0] != '\0') answer = io_buff; else if(defstr) answer = defstr; else answer = NULL; }; /* * Allocate storage for a copy of the string. */ retval = stralloc(strlen(answer)); if(retval) strcpy(retval, answer); return retval; } /*....................................................................... On input skip the next field in the file connected to FILE *fptr. A field is delimited either by white space or by matched quotes (or one quote and a carriage return character. Any preceding and following white space is also skipped. This function is only for use with text files. It is not recommended for use on stdin either. If the end of file mark is intercepted before the start of the required field, -1 is returned. If the end of file mark terminates the required field then 0 is returned. Otherwise the terminating character of the field is returned. */ int skip_field(FILE *fptr) { static int c; /* Find the first non-white space character. */ while((c=fgetc(fptr)) != EOF && (isspace(c) || c == ',') ); if(c==EOF) return -1; /* If that character is a ' then skip everything up to the next ' or carriage return. */ if(c=='\"') while( (c=fgetc(fptr)) != EOF && c != '\n' && c != '\"'); /* Otherwise read up to the next white space. */ else while( !isspace(c=fgetc(fptr)) && c != EOF); /* Check for end of file. */ if(c == EOF) return 0; return c; } /*....................................................................... Read a 1-D array of unknown size from a user file. The return array and its size are required. Reading of the array will be terminated when carriage return or end of file characters are intercepted, when non-numeric characters are intercepted (with the exception of tabs, commas ans spaces) or when the array has been filled up to its declared size. The return value of the function is either the size of the array read or, on error, -1. This function is only for use on text files. */ int input_array(FILE *fptr, float *array, size_t num_el) { int c,i,num_read; /* Skip tabs, commas and spaces. */ for(i=0;i nargs) { lprintf(stderr, "fprintf(,'%s',...): More specifiers than arguments?\n",fmt); return -1; }; switch (*cptr) { case 'f': was_error = args[arg]->atyp != 'f'; break; case 'i': was_error = args[arg]->atyp != 'i'; break; }; if(was_error) { lprintf(stderr, "fprintf(,'%s',...): argument specifier %d does not match its argument\n",fmt, arg+1); return -1; }; /* Skip white space preceding the number. */ while( (c=fgetc(fptr)) != EOF && (isspace(c) || c == ',')); if(c == EOF) { lprintf(stderr, "Reached end of file while searching for start of argument %d of:\n\t%s\n", arg+1, fmt); return -1; }; ungetc(c,fptr); /* Read the number. */ switch (*cptr) { case 'f': /* If the optional width specifier was provided, use it. */ if(num != 0) { sprintf(io_buff, "%%%df",num); c=fscanf(fptr, io_buff, FLTPTR(args[arg])); } else c=fscanf(fptr, "%f",FLTPTR(args[arg])); break; case 'i': if(num != 0) { sprintf(io_buff, "%%%di",num); c=fscanf(fptr, io_buff, INTPTR(args[arg])); } else c=fscanf(fptr, "%i",INTPTR(args[arg])); break; }; /* Check that the read was successful. */ if(c == 0) { lprintf(stderr, "Error in reading argument %d of '%s'\n", arg+1, fmt); return -1; }; /* The current argument has been assigned - prepare for the next one - if any. */ arg++; /* Skip any following white-space up to the next record or up to any new-line character. This is here essentially to make sure that if the last record on a given line is simply followed by spaces, then the line will have been completely read - this is important for reads from stdin where the carriage return would be interpretted by the next read or the compiler. */ for(;;) { c=fgetc(fptr); if(c=='\n' || c==EOF) break; if(!isspace(c) && c != ',') { ungetc(c,fptr); break; }; }; break; /* Read a string from the file. */ case 's': if(arg+1 > nargs) { lprintf(stderr, "fprintf(,'%s',...): More specifiers than arguments?\n",fmt); return -1; }; if(args[arg]->atyp != 'c') { lprintf(stderr, "fprintf(,'%s',...): argument specifier %d does not match its argument\n",fmt, arg+1); return -1; }; /* Skip white space in the file. */ while( (c=fgetc(fptr)) != EOF && (isspace(c) || c == ',')); if(c == EOF) { lprintf(stderr, "Reached end of file while searching for start of argument %d of:\n\t%s\n", arg+1, fmt); return -1; }; /* The string will be read into io_buff first and then copied to the return string. */ io_buff[0] = c; buf_pos = 1; /* The optional number in the format specifier is the minimum number of characters to be read. If this is specified simply read the requested number but stop at line breaks and EOF. */ if(num != 0) { if(num > MAXLINE-1) num = MAXLINE-1; while(buf_pos < num && (c=fgetc(fptr)) != EOF && c != '\n') io_buff[buf_pos++] = c; } /* If the initial character is a ' then read everything up to the next ' or carriage return. */ else if(c == '\"') { buf_pos = 0; while(buf_pos 0); break; /* Skip num lines in the file. */ case 'L': for(;;) if( (c=fgetc(fptr)) == EOF || (c=='\n' && --num <= 0) ) break; break; /* Skip num characters in the file. */ case 'C': do { if(fgetc(fptr) == EOF) break; } while(--num > 0); break; /* The following characters delimited by a matching } should be searched for num times. Copy the string into io_buff first. */ case '{': buf_pos = 0; cptr++; while( (c = *cptr) != '}' && c != '\0') { cptr++; io_buff[buf_pos++] = c; }; if(c == '\0') { lprintf(stderr, "Unmatched '{' in read-format: %s\n", fmt); return -1; }; /* Perform the search num times. */ do { if(buf_pos > 0 && file_search(fptr, io_buff, buf_pos, 0) <= 0) return -1; } while(--num > 0); break; case '\0': return 0; break; /* Unsupported specifier type - report error. */ default: lprintf(stderr, "Un-recognised read-format specifier %c\n",*cptr); return -1; break; }; }; return 0; }