Hi everyone,
First thanks for your book “Learn C the Hard Way”!
I am French, I hope my english will be clear enought.
I am almost stuck (already) with Exercise 17 and the extra credit 2:“Change the code to accept parameters for MAX_DATA and MAX_ROWS, store them in a Database structure and write that to the file…”
I managed to get the code working with the following declarations:
struct address {
int id;
int set;
char *name;
char *email;
};
struct database {
int max_rows;
int max_data;
struct address **rows;
};
struct connection {
FILE *file;
struct database *db;
};
I am wondering if the struct address **rows is the best way to answer your exercise? (I also managed to do the job with struct address *rows).
Here is my work:
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
struct address {
int id;
int set;
char *name;
char *email;
};
struct database {
int max_rows;
int max_data;
struct address **rows;
};
struct connection {
FILE *file;
struct database *db;
};
void database_create(struct connection *, const int, const int);
void die(const char *);
void address_print(struct address *);
void database_load(struct connection *);
struct connection * database_open(const char *, char);
void database_close(struct connection *);
void database_write(struct connection *);
void database_create(struct connection *, const int, const int);
void die(const char *message)
{
if (errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
void address_print(struct address *addr)
{
printf("%d %s %s\n", addr->id, addr->name, addr->email);
}
void database_load(struct connection *conn)
{
int rc = fread(&conn->db->max_rows, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
rc = fread(&conn->db->max_data, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
database_create(conn, conn->db->max_rows, conn->db->max_data);
int i = 0;
for (i = 0; i < conn->db->max_rows; i++) {
rc = fread(&conn->db->rows[i]->id, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
rc = fread(&conn->db->rows[i]->set, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
rc = fread(conn->db->rows[i]->name, conn->db->max_data * sizeof(char), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
rc = fread(conn->db->rows[i]->email, conn->db->max_data * sizeof(char), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
}
}
struct connection * database_open(const char *filename, char mode)
{
struct connection *conn = malloc(sizeof(struct connection));
if (!conn) {
die("Memory error.");
}
conn->db = malloc(sizeof(struct database));
if (!conn->db) {
die("Memory error.");
}
if (mode == 'c') {
conn->file = fopen(filename, "w");
} else {
conn->file = fopen(filename, "r+");
if (conn->file) {
database_load(conn);
}
}
if (!conn->file) {
die("Failed to open the file.");
}
return conn;
}
void database_close(struct connection *conn)
{
if (conn->file) {
fclose(conn->file);
}
if (conn->db) {
free(conn->db);
}
free(conn);
}
void database_write(struct connection *conn)
{
rewind(conn->file);
int rc = fwrite(&conn->db->max_rows, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
rc = fwrite(&conn->db->max_data, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
int i = 0;
for (i = 0; i < conn->db->max_rows; i++) {
rc = fwrite(&conn->db->rows[i]->id, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
rc = fwrite(&conn->db->rows[i]->set, sizeof(int), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
rc = fwrite(conn->db->rows[i]->name, conn->db->max_data * sizeof(char), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
rc = fwrite(conn->db->rows[i]->email, conn->db->max_data * sizeof(char), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
}
rc = fflush(conn->file);
if (rc == -1) {
die("Cannot flush database.");
}
}
void database_create(struct connection *conn, const int MAX_ROWS, const int MAX_DATA)
{
conn->db->max_rows = MAX_ROWS;
conn->db->max_data = MAX_DATA;
conn->db->rows = (struct address **)malloc(MAX_ROWS * sizeof(struct address *));
int i = 0;
for (i = 0; i < MAX_ROWS; i++) {
conn->db->rows[i] = (struct address *)malloc(sizeof(struct address));
conn->db->rows[i]->name = (char *)malloc(MAX_DATA * sizeof(char));
conn->db->rows[i]->email = (char *)malloc(MAX_DATA * sizeof(char));
conn->db->rows[i]->id = i;
conn->db->rows[i]->set = 0;
}
}
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;
char *res = strncpy(addr->name, name, conn->db->max_data);
addr->name[conn->db->max_data-1] = '\0';
if (!res) {
die("Name copy failed.");
}
res = strncpy(addr->email, email, conn->db->max_data);
addr->email[conn->db->max_data-1] = '\0';
if(!res) {
die("Email copy failed.");
}
}
void database_get(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_list(struct connection *conn)
{
int i = 0;
struct database *db = conn->db;
for (i = 0; i < conn->db->max_rows; i++) {
struct address *cur = db->rows[i];
if (cur->set) {
address_print(cur);
}
}
}
void database_delete(struct connection *conn, int id)
{
conn->db->rows[id]->id = 0;
conn->db->rows[id]->set = 0;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
die("USAGE: ex17 <dbfile> <action> [action param]");
}
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 >= conn->db->max_rows && action != 'c') {
die("There's not that many records.");
}
switch (action) {
case 'c':
database_create(conn, 5, 10);
database_write(conn);
break;
case 'g':
if (argc != 4) {
die("Need an id to get");
}
database_get(conn, id);
break;
case 's':
if (argc != 6) {
die("Need id, name, 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;
}
I still don’t understand why i can’t write/read a struct in one shot when one of the element is a pointer or pointer on pointer…
If you can help me a little bit I would appreciate a lot! I am not so confident with what I read online…
Have a good day!
Richard