#include // printf(), scanf() // getc(), fgets() // FILE, fopen(), fclose(), rewind() // fgetpos(), fsetpos(), fpos_t #include //exit() #define FALSE (0) #define TRUE (!FALSE) int main(int argc, char *argv[]) { int c; int charcount, linecount; int lastc; FILE *fp, *log; int i; int age1, age2; int eolflag; fpos_t fp_pos; int line; char **string; // same as char *string[] except that's not allowed. log = stdout; // log goes to screen until redirected below. fprintf(log, "There were %i terms on the command line.\n", argc); fprintf(log, "They were:\n"); for(i=0; i 4) { fprintf(log, "There were sufficient items to perform comparison.\n"); sscanf(argv[2], "%i", &age1); sscanf(argv[4], "%i", &age2); if(age1 == age2) fprintf(log, "%s is the same age as %s\n", argv[1], argv[3]); if(age1 < age2) fprintf(log, "%s is %i year(s) older than %s\n", argv[3], age2-age1, argv[1]); if(age1 > age2) fprintf(log, "%s is %i year(s) older than %s\n", argv[1], age1-age2, argv[3]); } if(argc > 5) // Assuming 5th item is logfile name { log = fopen(argv[5], "wt"); if(NULL == log) { log = stdout; fprintf(log, "Error opening log %s as requested.\n", argv[5]); fprintf(log, "Using Standard Output device instead.\n"); } else { fprintf(log, "Using log %s as requested.\n", argv[5]); } } else { log = stdout; fprintf(log, "No log file name supplied on command line.\n"); fprintf(log, "Using Standard Output device instead.\n"); } fp = fopen("names.txt", "rt"); if(NULL == fp) { fprintf(log, "Input File failed to open.\n"); fprintf(log, "Program terminating.\n"); printf("You idiot!"); exit(1); } fprintf(log, "Input File opened successfully.\n"); // Note that there are two common ways for a file to end. // The first is that there is a newline character at the // end of the last line and the other is that there isn't. // To get the proper count for the number of lines, you // need to detect both situations. The easiest way it to // remember what the last character read was. Then, after // the loop exits, if the last character was NOT a newline, // then the EOF not only marks the end of the file, but the // end of the last line of data as well and the count needs // to be incremented. fprintf(log, "Scanning file for length and line count.\n"); for(linecount = charcount = 0; EOF != (c = fgetc(fp)); charcount++) { if('\n' == c) linecount++; printf("%c", c); lastc = c; } if('\n' != lastc) linecount++; fprintf(log, " The file contained %i characters.\n", charcount); fprintf(log, " The file contained %i lines.\n", linecount); // TASK: Allocate sufficient memory to hold the pointers to the strings. string = (char **) malloc(linecount*sizeof(char *)); // malloc() returns a (void *) which simply means that it is a pointer // without any claim as to what type of data it is pointing to. // The variable 'file' on the other hand is declared as being a pointer // to value that is a pointer to a char, hence it is of type char **. // The typecast above is there to suppress any compiler warnings that // might get generated concerning suspicious pointer conversions and // also documents what type of memory we are requesting. // Since our last operation (counting the lines in the file) has placed // the file cursor at the end of the file, we need to reset it to the // beginning. There are three ways to do this: // // 1) We could simply close and reopen the file. // 2) Use the function rewind(fp) to go to the beginning. // 3) Use the function fseek(fp, 0, SEEK_SET). // // The first is a lot of overhead while the third is generally // associated with operations on binary files - although it's use here // is perfectly legal. The second, on the other hand, really matches // the logic of what we are trying to accomplish. rewind(fp); // So at this point we have enough memory to store enough string // pointers to handle every line of text in the file, but as of now // those pointers themselves are uninitialized garbage. The next step // is to go line by line and: // // 1) Determine how long the line is. // 2) Allocate enough memory for that string. // 3) Store the pointer to that string in the array. // 4) Read the string and store it in the allocated space. // Since we have to know how long each line is before we allocate the // memory for it and we have to allocate the memory before we can read // in and store the actual line, we have to make two passes. What we // need to do is, at the beginning of a line, remember where we are // in the file. Then count forward until we find the newline character // or EOF to see how long the line is. At that point we can allocate // the memory and then jump back to the beginning of the line and // read in the data. // To do the jumping back and forth, we can use fgetpos() to store // the value of the file pointer in a variable of type fpos_t (which // on our system happens to be an alias for an unsigned long). This // value can then be used in a subsequent call to fsetpos() to jump // back to that same point. fprintf(log, "Commencing read in of data.\n"); for(line = 0; line < linecount; line++) { fprintf(log, " Line # %3i: ", line); fgetpos(fp, &fp_pos); eolflag = FALSE; // End-of-Line flag charcount = 0; for(charcount = 0; !eolflag; charcount++) { c = fgetc(fp); if((EOF == c) || ('\n' == c)) eolflag = TRUE; } // Notice that charcount DOES include the newline/EOF character. fprintf(log, "Length: %3i ", charcount); fsetpos(fp, &fp_pos); string[line] = (char *) malloc(charcount); fgets(string[line], charcount, fp); // This should have read everything BUT the newline/EOF character. // So we will now read that character in so as to get rid of it // and verify that everything is going as planned. c = fgetc(fp); if( !((EOF == c) || ('\n' == c)) ) { printf("Error in string alignments during read in.\n"); exit(2); } fprintf(log, "(\\n' || EOF found were expected)\n"); } fclose(fp); fprintf(log, "Input file closed.\n"); // Now we will print out the lines that were read in. We have to // tack on our own newline character since we stripped this from // the strings. fprintf(log, "Printing out contents of string array to screen.\n"); for(line=0; line