<commit_before>#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define MAXDATA 512
#define MAXROWS 100

void die(const char *msg) {
  if (errno) perror(msg); else printf("Error: %s\n", msg);
  exit(1);
}

struct Address {
  int id;
  int set;
  char name[MAXDATA];
  char email[MAXDATA];
};

void Address_print(struct Address *addr) {
  printf("%d %s %s\n", addr->id, addr->name, addr->email);
}

struct Connection {
  FILE *file;
  struct Database *db;
};

struct Database {
  struct Address rows[MAXROWS];
};

void Database_load(struct Connection *conn) {
  // read one entry of size Database from file
  size_t entries_read = fread(conn->db, sizeof(struct Database), 1, conn->file);
  if (entries_read != 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_create(struct Connection *conn) {
  for (int i = 0; i < MAXROWS; i++) {
    // init addr by creating prototype
    struct Address addr = { .id = i, .set = 0 };
    conn->db->rows[i] = addr;
  }
}

void Database_write(struct Connection *conn) {
  rewind(conn->file);
  int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
  if (rc != 1) die("Failed to write to database");

  rc = fflush(conn->file);
  if (rc == -1) die("Failed to flush database");
}

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, MAXDATA);
  if (!res) die("Name copy failed");

  res = strncpy(addr->email, email, MAXDATA);
  if (!res) die("Email copy failed");
}

void Database_get(struct Connection *conn, int id) {
  struct Address *addr = &conn->db->rows[id];
  if (!addr->set) die("id not set");
  Address_print(addr);
}

void Database_delete(struct Connection *conn, int id) {
  // ensures that all fields but .id and .set are initialized to 0
  // assignment handles memcpy under the hood
  conn->db->rows[id] = (struct Address){ .id = id, .set = 0 };
}

void Database_list(struct Connection *conn) {
  struct Database *db = conn->db;
  for (int i = 0; i < MAXROWS; i++) {
    struct Address *addr = &db->rows[i];
    if (addr->set) Address_print(addr);<commit_msg>fixing strncpy null terminate bug<commit_after>#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define MAXDATA 512
#define MAXROWS 100

void die(const char *msg) {
  if (errno) perror(msg); else printf("Error: %s\n", msg);
  exit(1);
}

char *strncpy_term(char *dst, const char *src, const size_t len) {
  // Wrapper that fixes strncpy null terminator problem
  // From: http://www.cplusplus.com/reference/cstring/strncpy/
  // No null-character is implicitly appended at the end of destination if source is longer than num.
  // Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).

  char *res = strncpy(dst, src, len);
  if (res && len > 0) dst[len -1 ] = '\0';
  return res;
}

struct Address {
  int id;
  int set;
  char name[MAXDATA];
  char email[MAXDATA];
};

void Address_print(struct Address *addr) {
  printf("%d %s %s\n", addr->id, addr->name, addr->email);
}

struct Connection {
  FILE *file;
  struct Database *db;
};

struct Database {
  struct Address rows[MAXROWS];
};

void Database_load(struct Connection *conn) {
  // read one entry of size Database from file
  size_t entries_read = fread(conn->db, sizeof(struct Database), 1, conn->file);
  if (entries_read != 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_create(struct Connection *conn) {
  for (int i = 0; i < MAXROWS; i++) {
    // init addr by creating prototype
    struct Address addr = { .id = i, .set = 0 };
    conn->db->rows[i] = addr;
  }
}

void Database_write(struct Connection *conn) {
  rewind(conn->file);
  int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
  if (rc != 1) die("Failed to write to database");

  rc = fflush(conn->file);
  if (rc == -1) die("Failed to flush database");
}


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_term(addr->name, name, MAXDATA);
  if (!res) die("Name copy failed");

  res = strncpy_term(addr->email, email, MAXDATA);
  if (!res) die("Email copy failed");
}

void Database_get(struct Connection *conn, int id) {
  struct Address *addr = &conn->db->rows[id];
  if (!addr->set) die("id not set");
  Address_print(addr);
}

void Database_delete(struct Connection *conn, int id) {
  // ensures that all fields but .id and .set are initialized to 0
  // assignment handles memcpy under the hood
  conn->db->rows[id] = (struct Address){ .id = id, .set = 0 };
}

void Database_list(struct Connection *conn) {
  struct Database *db = conn->db;
  for (int i = 0; i < MAXROWS; i++) {
    struct Address *addr = &db->rows[i];
    if (addr->set) Address_print(addr);
{'fraction_non_alphanumeric': 0.15829603845516327, 'fraction_numerical': 0.006464445549477871, 'mean_word_length': 3.8661290322580646, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 76, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2058647', 'n_tokens_mistral': 2079, 'n_tokens_neox': 2015, 'n_words': 667}