// This file controls the reading and writing of the file that holds the // aircraft info and reservation data, which is loaded into data structures // when the program starts, and stores back into the file before the // program shuts down. A typical data file looks something like this: // // NAME 4 Twin Comanche <--- another aircraft, with it's own unique id // DESC A light twin engine aircraft. <--- multi-line description // IFR equipped. // RES 5 Doug Sanderson 123456700 123456760 <--- one reservation, with unique // RES 6 Gregg McDonald 123456780 123456820 id, start time, and finish // NAME 7 Piper Cub time // DESC A 2 seat tail dragger. // Slow, but a lot of fun to fly. // Not recommended if crosswind component > 8 kt. // RES 8 Gregg McDonald 123456780 123456820 #include #include #include #include #include #include "errors.h" #include "time_slot.h" #include "reservations.h" #include "aircraft.h" #include "fleet.h" #include "data_file.h" #define MAX_LINE_LEN 256 void file_parser::init() { line_type = AC_NONE; ac_name[0] = '\0'; ac_id = 0; ac_desc[0] = '\0'; user_name[0] = '\0'; res_id = 0; start = 0; finish = 0; } void read_data_file(const char *filename, fleet & the_fleet) { char buffer[MAX_LINE_LEN]; new_aircraft new_ac; char complete_desc[2048]; // big buffer for a possibly multiple // line aircraft description file_parser data_values; time_slot_info new_slot; ifstream data_in; data_in.open(filename); if (! data_in.good() ) { strcpy(fatal_err_function, "read_data_file"); sprintf(fatal_err_msg, "failed to open %s for reading", filename); fatal_error(); } else { new_ac.ac_unique_id = 0; new_ac.name[0] = '\0'; new_ac.description = complete_desc; new_ac.description[0] = '\0'; while ( data_in.getline(buffer, MAX_LINE_LEN-1) ) { // read and parse the next line of the data file data_values.init(); parse_data(data_values, buffer); // Don't add a new aircraft until we also get the description. // // Don't process an aircraft description until we have gathered // up all of the description, which may span several lines, and // therefore require several passes through this "while" loop switch (data_values.line_type) { case AC_NAME: // if we have not yet added the previous aircraft, // add the previous aircraft before we start messing // with this new aircraft if (new_ac.name[0] != '\0') { add_prior_aircraft(new_ac, the_fleet); } // start saving values for this new aircraft we just read from // the input data file new_ac.ac_unique_id = data_values.ac_id; strcpy(new_ac.name, data_values.ac_name); strcpy(new_ac.description, data_values.ac_desc); break; case AC_RESERVATION: // add the aircraft that we have now have // a complete name & description for if (new_ac.name[0] != '\0') { add_prior_aircraft(new_ac, the_fleet); } // add this new reservation new_slot.slot_unique_id = data_values.res_id; strcpy(new_slot.name, data_values.user_name); new_slot.start = data_values.start; new_slot.finish = data_values.finish; the_fleet.add_ac_reservation(new_ac.ac_unique_id, new_slot); break; case AC_DESCRIPTION: // append this line of description to the existing lines // of description if (new_ac.description[0] == '\0') { strcpy(new_ac.description, data_values.ac_desc); } else { strcat(new_ac.description, data_values.ac_desc); } strcat(new_ac.description, "\n"); break; default: strcpy(fatal_err_function, "read_data_file"); sprintf(fatal_err_msg, "unexpected line type %d", data_values.line_type); fatal_error(); } } // Its possible that the last aircraft from the file // had no reservations, and so the last aircraft is // still waiting to be added to the_fleet. If that is // the case, add it now. if (new_ac.name[0] != '\0') { add_prior_aircraft(new_ac, the_fleet); } } } // If we have been been collecting a multi-line description for a new // aircraft, assume that we now have all the required lines, and add // the aircraft. void add_prior_aircraft(new_aircraft & new_ac, fleet & the_fleet) { if (new_ac.ac_unique_id == 0) { strcpy(fatal_err_function, "add_prior_aircraft"); sprintf(fatal_err_msg, "aircraft_unique_id is 0"); fatal_error(); } the_fleet.add_new_ac(new_ac); // Don't mess with the ac_unique_id field. We will need that number when we // start adding reservations for this aircraft. new_ac.name[0] = '\0'; new_ac.description[0] = '\0'; } void parse_data(file_parser & data_values, char *buffer) { if ( strncmp(buffer, prefix_name, strlen(prefix_name) ) == 0 ) { // its an aircraft name parse_data_name(data_values, buffer); } else if ( strncmp(buffer, prefix_reservation, strlen(prefix_reservation) ) == 0 ) { // its an aircraft reservation parse_data_reservation(data_values, buffer); } else { // It must be one of the aircraft description lines, which can span // several lines, and only the first line will have the // DESC prefix parse_data_description(data_values, buffer); } } // This function parses this type of data file line: // // NAME 2714 Twin Comanche void parse_data_name(file_parser & data_values, char *buffer) { char *ptr_id = NULL; char *ptr_name = NULL; // Point ptr_id at the unique id number. ptr_id = strchr(buffer, ' ') + 1; if (ptr_id == NULL) { strcpy(fatal_err_function, "parse_data_name"); sprintf(fatal_err_msg, "invalid aircraft name line: %s", buffer); fatal_error(); } // Point ptr_name at the beginning of the aircraft name. Put a '\0' // character between the id and the name. ptr_name = strchr(ptr_id, ' '); if (ptr_id == NULL) { strcpy(fatal_err_function, "parse_data_name"); sprintf(fatal_err_msg, "invalid aircraft name line: %s", buffer); fatal_error(); } *ptr_name = '\0'; ++ptr_name; // make sure the name isn't too long if ( ( strlen(ptr_name) + 1 ) > AC_NAME_LEN ) { ptr_name[AC_NAME_LEN-1] = '\0'; } // save values data_values.line_type = AC_NAME; data_values.ac_id = atoi(ptr_id); strcpy(data_values.ac_name, ptr_name); } // This function parses one of these types of data file description // lines, which often require several lines to complete: // // DESC A 2 seat tail dragger. // Slow, but a lot of fun to fly. // Not recommended if crosswind component > 8 kt. void parse_data_description(file_parser & data_values, char *buffer) { char *ptr1 = NULL; // Only the first description line will have the DESC prefix data_values.line_type = AC_DESCRIPTION; if ( strncmp(buffer, prefix_description, strlen(prefix_description) ) == 0 ) { // This is apparently the first line of the aircraft description ptr1 = strchr(buffer, ' ') + 1; // remember blank space after // prefix_description strcpy(data_values.ac_desc, ptr1); } else { // This is just another line of the aircraft description strcpy(data_values.ac_desc, buffer); } } // This function parses this type of data file line: // // RES 7654 Doug Sanderson 123456700 123456760 void parse_data_reservation(file_parser & data_values, char *buffer) { char *ptr1 = NULL, // points at unique id number *ptr2 = NULL, // points at name of person with the reservation *ptr3 = NULL, // points at start time *ptr4 = NULL; // points at finish time // position ptr1 after the first blank space ptr1 = strchr(buffer, ' '); ++ptr1; if (ptr1 == NULL) { strcpy(fatal_err_function, "parse_data_reservation"); sprintf(fatal_err_msg, "invalid reservation line: %s", buffer); fatal_error(); } // add a '\0' at the end of ptr1 and position ptr2 ptr2 = strchr(ptr1, ' '); *ptr2 = '\0'; ++ptr2; // The last 2 sub-strings will be the start and finish dates. // Start at the end of the string and work backwards. // // RES 7654 Doug Sanderson 123456700 123456760 // ^^ // || // |+--- position ptr4 here // | // +--- put a '\0' character here ptr4 = strrchr(ptr2, ' '); if (ptr4 == NULL) { strcpy(fatal_err_function, "parse_data_reservation"); sprintf(fatal_err_msg, "invalid reservation %s", buffer); fatal_error(); } *ptr4 = '\0'; ++ptr4; // RES 7654 Doug Sanderson 123456700 // ^^ // || // |+--- position ptr3 here // | // +--- put a '\0' character here ptr3 = strrchr(ptr2, ' '); if (ptr3 == NULL) { strcpy(fatal_err_function, "parse_data_reservation"); sprintf(fatal_err_msg, "invalid reservation %s", buffer); fatal_error(); } *ptr3 = '\0'; ++ptr3; // make sure the reservation name is not too long if ( ( strlen(ptr3) + 1 ) > MAX_NAME_LEN ) { ptr3[MAX_NAME_LEN-1] = '\0'; } // load up the data structure with values data_values.line_type = AC_RESERVATION; data_values.res_id = atoi(ptr1); strcpy(data_values.user_name, ptr2); data_values.start = atoi(ptr3); data_values.finish = atoi(ptr4); } // Save the contents of the_fleet into a file for later reference. void write_data_file(const char *filename, fleet & the_fleet) { char tmp_filename[128]; char sys_command[256]; new_aircraft *ac_array = NULL; int ac_count = 0; all_ac_data ac_data; int i, j; int outcome; strcpy(tmp_filename, filename); strcat(tmp_filename, ".tmp"); // remove old tmp file if it exists ofstream f_out(tmp_filename); if (! f_out.good() ) { strcpy(fatal_err_function, "write_data_file"); sprintf(fatal_err_msg, "failed to open %s for writing", tmp_filename); fatal_error(); } // get list of all aircraft ac_array = the_fleet.find_all_ac(ac_count); for (i = 0; i < ac_count; ++i) { // get all reservations for this aircraft ac_data = the_fleet.find_all_ac_data(ac_array[i].ac_unique_id, outcome); if (outcome != SUCCESSFUL) { strcpy(fatal_err_function, "write_data_file"); sprintf(fatal_err_msg, "find_all_ac_data() returned error code %d", outcome); fatal_error(); } // write the data for this aircraft and all it's reservations into // the data file f_out << "NAME " << ac_data.spec.ac_unique_id << " " << ac_data.spec.name << endl; f_out << "DESC " << ac_data.spec.description; for (j = 0; j < ac_data.res_count; ++j) { f_out << "RES " << ac_data.all_res[j].slot_unique_id << " " << ac_data.all_res[j].name << " " << ac_data.all_res[j].start << " " << ac_data.all_res[j].finish << endl; } // free un-needed memory delete [] ac_data.spec.description; ac_data.spec.description = NULL; delete [] ac_data.all_res; ac_data.all_res = NULL; } // free un-needed memory for (i = 0; i < ac_count; ++i) { delete [] ac_array[i].description; } delete [] ac_array; // close tmp file, delete the dat file, and move the tmp file to the dat file f_out.close(); sprintf(sys_command, "rm %s > /dev/null 2>&1", filename); system(sys_command); sprintf(sys_command, "mv %s %s > /dev/null 2>&1", tmp_filename, filename); system(sys_command); }