1
\$\begingroup\$

I am currently working on a game, and I get the configuration in an .ini file, and I get information with this library https://github.com/benhoyt/inih I would like to improve the readability / performance of this code, as I do not know much about good practices.

include "project.h"
// typedef struct s_server_conf {
// const char *ip;
// unsigned int port;
// } t_server_conf;
// typedef struct s_db_conf {
// const char *host;
// const char *user;
// const char *passwd;
// const char *db;
// unsigned int port;
// } t_db_conf;
// typedef struct s_conf {
// t_server_conf server;
// t_db_conf conf;
// } t_conf;
static int parse_server(t_conf *conf, const char *name, const char *value)
{
 # define MATCH(n) (strcmp(name, n) == 0)
 if (MATCH("ip"))
 conf->server->ip = strdup(value);
 else if (MATCH("port"))
 conf->server->port = atoi(value);
 else
 return (0);
 return (1);
}
static int parse_database(t_conf *conf, const char *name, const char *value)
{
 # define MATCH_NAME(n) (strcmp(name, n) == 0)
 if (MATCH_NAME("host"))
 conf->db->host = (strdup(value));
 else if (MATCH_NAME("user"))
 conf->db->user = (strdup(value));
 else if (MATCH_NAME("passwd"))
 conf->db->passwd = (strdup(value));
 else
 return (0);
 return (1);
}
static int handler(void *conf, const char *section, const char *name, const char *value)
{
 # define MATCH_SECTION(s) (strcmp(section, s) == 0)
 printf("Le nom du champ est %s, la valeur est %s (La section est %s)\n", name, value, section)
;
 if (MATCH_SECTION("database"))
 return (parse_database((t_conf*)conf, name, value));
 if (MATCH_SECTION("server"))
 return (parse_server((t_conf*)conf, name, value));
 return (0);
}
void clear_conf(t_conf *conf)
{
 if (conf->db)
 free(conf->db);
 if (conf->server)
 free(conf->server);
 free(conf);
}
static t_conf *create_conf(void)
{
 t_conf *conf;
 if (!(conf = (t_conf*)malloc(sizeof(t_conf))))
 return (NULL);
 memset(conf, '0円', sizeof(t_conf));
 if (!(conf->db = (t_db_conf*)malloc(sizeof(t_db_conf))))
 {
 clear_conf(conf);
 return (NULL);
 }
 conf->db->port = 3306;
 if (!(conf->server = (t_server_conf*)malloc(sizeof(t_server_conf))))
 {
 clear_conf(conf);
 return (NULL);
 }
 return (conf);
}
t_conf *get_conf(void)
{
 t_conf *conf;
 if (!(conf = create_conf()))
 return (NULL);
 if (ini_parse("settings.ini", handler, conf) < 0)
 {
 printf("Unable to load configuration\n");
 exit (1);
 }
 return (conf);
}
200_success
146k22 gold badges190 silver badges479 bronze badges
asked Apr 26, 2019 at 23:52
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

Design

Rather than bury a magic filename, consider passing it in.

//t_conf *get_conf(void) {
// ... 
// if (ini_parse("settings.ini", handler, conf) < 0)
t_conf *get_conf(const char *ini_file_name) {
 ... 
 if (ini_parse(ini_file_name, handler, conf) < 0)

Maybe add before the if()

 if (ini_file_name == NULL) {
 ini_file_name = "settings.ini";
 }

Devoid of overall comments

Consider comments to at least convey the coding goals.

Allocate to the size of the de-referenced object

... rather than allocate to the size of the type - easier to code right, review and maintain. Cast not needed.

//if (!(conf = (t_conf*)malloc(sizeof(t_conf))))
// return (NULL);
conf = malloc(sizeof *conf);
if (conf == NULL) {
 return NULL;
}
// like-wise
// memset(conf, '0円', sizeof(t_conf));
memset(conf, 0, sizeof *conf);

Use auto formatting

Life is too short for manual formatting. Find and use an auto formatter - customized as needed.

Comments of typedefs not needed

As is now, this code has comments that is not required to match what is truly in "project.h" and as time goes on, can diverge. For code posting here, post a portion of "project.h" instead.

Invalid code

include "project.h" should be #include "project.h"

Hiding objects here is bad

# define MATCH(n) (strcmp(name, n) == 0) brings in name, but not obvious from the call if (MATCH("ip")).

Alternative:

static bool streq(const char *a, const char *b) {
 return strcmp(a,b) == 0;
}
...
if (streq(name, "ip"))

Naked magic number

Buried in code is

conf->db->port = 3306;

Better to have

// near top of code
#define PORT_DEFAULT 3306
// later on
conf->db->port = PORT_DEFAULT;

Unneeded if

Just call free(). free(NULL) is OK.

//if (conf->db)
// free(conf->db);
free(conf->db);

Like free(), I'd expect clear_conf(NULL) to not error

void clear_conf(t_conf *conf) {
 if (conf) { // add 

() not needed in return and others

Many places

// return (conf);
return conf;
// conf->db->host = (strdup(value));
conf->db->host = strdup(value);

Non-standard C

Note: strdup() is not standard, yet ubiquitous.

Missing #include <>

Do not rely on "project.h" to provide them unless that is clearly a project rule.

NULL as name

Given the potential generic use of handler, I'd guard against NULL.

// printf("Le nom du champ est %s, la valeur est %s (La section est %s)\n", name, value, section);
if (name === NULL || value == NULL || section == NULL) {
 tbd();
} else {
 printf("Le nom du champ est %s, la valeur est %s (La section est %s)\n", name, value, section);
}

Further I'd bracket such strings to help detect leading/trailing white spaces in them

 printf("Le nom du champ est \"%s\", la valeur est \"%s\" (La section est \"%s\")\n",
 name, value, section);

Lack of error checking

// what if `value` is "", "abc", "123xyz", "12345678901234567890", ...?
conf->server->port = atoi(value);
// what if allocation returns NULL? 
conf->server->ip = strdup(value);

Respect presentation width

 printf("Le nom du champ est %s, la valeur est %s (La section est %s)\n", name, value, section)
;

vs.

 printf("Le nom du champ est %s, la valeur est %s (La section est %s)\n",
 name, value, section);

Another reason for an auto formatter.

Curious dual language

I'd expect one.

printf("Le nom du champ est %s, la valeur est %s (La section est %s)\n", ...
printf("Unable to load configuration\n");

Conversion changes sign-ness

unsigned port;
...
conf->server->port = atoi(value);

Perhaps a helper function

// return error status
bool to_unsigned(unsigned *u, const char *s) {
 if (s == NULL) {
 return true;
 }
 char *endptr;
 errno = 0;
 unsigned long = strtoul(s, &endptr, 10); 
 if (errno || s == endptr || *endptr || value > UINT_MAX) {
 return true;
 }
 if (u) {
 *u = (unsigned) value;
 }
 return false;
}
if (to_unsigned(&conf->server->port, value)) {
 hanlde_oops();
}
user673679
12.2k2 gold badges34 silver badges65 bronze badges
answered May 3, 2019 at 5:56
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.