Here is my code. I am stuck on this error. This happens after the command: ./ex17 db.dat c
In gdb, it says that the text file is busy, but it has just been created using the “w” parm in the Database_open() function.
I tried chmod 777 db.dat (which is really bad) and it won’t open. Any ideas?
#include <stdio.h> #include <assert.h> #include <stdlib.h> #include <errno.h> #include <string.h> // define macro variables to use for data size and rows number #define MAX_DATA 511 + '\0' #define MAX_ROWS 100 // this is a structure for an address, which is a data row in the database struct Address { int id; int set; char name[MAX_DATA]; char email[MAX_DATA]; }; // a structure of a database that accepts up to 100 rows struct Database { struct Address rows[MAX_ROWS]; }; // the connection here consists of a file object (.dat) and a Database structure pointer struct Connection { FILE *file; struct Database *db; }; // this is a function that handles errors, and prints a simple error message void die(const char *message) { if (errno) { perror(message); } else { printf("ERROR: %s\n", message); } exit(1); } // this function prints the address from the *addr pointer variable // it prints out each of the fields using pointer notation -> void Address_print(struct Address *addr) { printf("%d %s %s", addr->id, addr->name, addr->email); } // the function loads the database, taking in a connection structure // here we use fread and it takes 4 parms, a void *ptr (conn->db), // the number of memory bytes (nmemb) from the stream pointed to // by the stream (1), and a FILE *stream. void Database_load(struct Connection *conn) { int rc = fread(conn->db, sizeof(struct Database), 1, conn->file); if (rc != 1) { die("Failed to load database."); } } // a function of type Connection structure that takes in a FILE *filename // and a character. The mode decides if it is write-only, read-write, etc... struct Connection *Database_open(const char *filename, char mode) { // here we are creating a conn pointer of type Connection structure // and using malloc to get memory from the computer of the size // of the structure Connection struct Connection *conn = malloc(sizeof(struct Connection)); if (!conn) { die("Memory error."); } // allocate the Database structure size to the conn->db conn->db = malloc(sizeof(struct Database)); if (mode == 'c') { conn->file = fopen(filename, "w"); } else { conn->file = fopen(filename, "r+"); if (conn->file) { Database_load(conn); } } if (!conn->file) { die("Shitballs, failed to open the file."); } return conn; } // here we have a function that closes the database connection // fairly straightforward void Database_close(struct Connection *conn) { if (conn) { if (conn->file) { fclose(conn->file); } if (conn->db) { free(conn->db); } free(conn); } } // this function allows us to write to the database using the // Connection structure void Database_write(struct Connection *conn) { // rewind() accepts a FILE *stream and simply sets the file // position indicator to the beginning of the file taken in // through conn->file rewind(conn->file); // like fread(), this function uses the same parms but instead of // reading, it is writing to the FILE *stream provided in conn->file int rc = fwrite(conn->file, sizeof(struct Database), 1, conn->file); if (rc != 1) { die("Failed to write to database."); } // this function forces a discard of all buffer data from the user space rc = fflush(conn->file); if (rc != 1) { die("Cannot flush the database."); } } // this function creates the database using the Connection structure as a parameter // it creates a prototype addr structure to initialize with the MAX_ROWS and sets the // .id to 0-99 for all rows in the skeleton database void Database_create(struct Connection *conn) { int i = 0; for (i = 0; i < MAX_ROWS; i++) { // make a prototype to initialize it struct Address addr = {.id = i, .set = 0}; // then assign it conn->db->rows[i] = addr; } } // this function sets the id, name, email in the database with the arguments provided // by the user. it is done by using 's' as the second parameter when running the program void Database_set(struct Connection *conn, int id, const char *name, const char *email) { struct Address *addr = &conn->db->rows[id]; if (addr->set) { die("Already set, delete it first."); } addr->set = 1; // WARNING: bug, need to fix this char *res = strncpy(addr->name, name, MAX_DATA); // demonstrate the strncpy bug if (!res) { die("Name copy failed."); } res = strncpy(addr->email, email, MAX_DATA); if (!res) { die("Email copy failed"); } } void Data_set(struct Connection *conn, int id) { struct Address *addr = &conn->db->rows[id]; if(addr->set) { Address_print(addr); } else { die("ID not set."); } } void Database_delete(struct Connection *conn, int id) { struct Address addr = {.id = id, .set = 0}; conn->db->rows[id] = addr; } void Database_list(struct Connection *conn) { int i = 0; struct Database *db = conn->db; for(i = 0; i < MAX_ROWS; i++) { struct Address *current_address = &db->rows[i]; if(current_address->set) { Address_print(current_address); } } } int main(int argc, char *argv[]) { if(argc < 3) { die("USAGE: ex17 <dbfile> <action> [action parms]"); } char *filename = argv[1]; char action = argv[2][0]; struct Connection *conn = Database_open(filename, action); int id = 0; if(argc > 3) { id = atoi(argv[3]); } if(id >= MAX_ROWS) { die("Not that many records."); } switch(action) { case 'c': Database_create(conn); Database_write(conn); break; case 'g': if(argc != 4) { die("Need an id to 'get'"); } case 's': if(argc != 6) { die("Need id, name, and email to 'set'"); } Database_set(conn, id, argv[4], argv[5]); Database_write(conn); break; case 'd': if(argc != 4) { die("Need id to delete."); } Database_delete(conn, id); Database_write(conn); break; case 'l': Database_list(conn); break; default: die("Invalid action: c=create, g=get, s=set, d=del, l=list"); } Database_close(conn); return 0; }