Task: Read a configuration file

C: #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>

#define rosetta_uint8_t unsigned char

#define FALSE 0
#define TRUE 1

#define CONFIGS_TO_READ 5
#define INI_ARRAY_DELIMITER ','


struct configs {
	char *fullname;
	char *favouritefruit;
	rosetta_uint8_t needspeeling;
	rosetta_uint8_t seedsremoved;
	char **otherfamily;
	size_t otherfamily_len;
	size_t _configs_left_;
};

static char ** make_array (size_t * arrlen, const char * src, const size_t buffsize, IniFormat ini_format) {
 
	
	*arrlen = ini_array_get_length(src, INI_ARRAY_DELIMITER, ini_format);
	char ** const dest = *arrlen ? (char **) malloc(*arrlen * sizeof(char *) + buffsize) : NULL;
	if (!dest) { return NULL; }
	memcpy(dest + *arrlen, src, buffsize);
	char * iter = (char *) (dest + *arrlen);
	for (size_t idx = 0; idx < *arrlen; idx++) {
		dest[idx] = ini_array_release(&iter, INI_ARRAY_DELIMITER, ini_format);
		ini_string_parse(dest[idx], ini_format);
	}
	return dest;

}

static int configs_member_handler (IniDispatch *this, void *v_confs) {

	struct configs *confs = (struct configs *) v_confs;

	if (this->type != INI_KEY) {

		return 0;

	}

	if (ini_string_match_si("FULLNAME", this->data, this->format)) {

		if (confs->fullname) { return 0; }
		this->v_len = ini_string_parse(this->value, this->format); 
		confs->fullname = strndup(this->value, this->v_len);
		confs->_configs_left_--;

	} else if (ini_string_match_si("FAVOURITEFRUIT", this->data, this->format)) {

		if (confs->favouritefruit) { return 0; }
		this->v_len = ini_string_parse(this->value, this->format); 
		confs->favouritefruit = strndup(this->value, this->v_len);
		confs->_configs_left_--;

	} else if (ini_string_match_si("NEEDSPEELING", this->data, this->format)) {

		if (~confs->needspeeling & 0x80) { return 0; }
		confs->needspeeling = ini_get_bool(this->value, TRUE);
		confs->_configs_left_--;

	} else if (ini_string_match_si("SEEDSREMOVED", this->data, this->format)) {

		if (~confs->seedsremoved & 0x80) { return 0; }
		confs->seedsremoved = ini_get_bool(this->value, TRUE);
		confs->_configs_left_--;

	} else if (!confs->otherfamily && ini_string_match_si("OTHERFAMILY", this->data, this->format)) {

		if (confs->otherfamily) { return 0; }
		this->v_len = ini_array_collapse(this->value, INI_ARRAY_DELIMITER, this->format); 
		confs->otherfamily = make_array(&confs->otherfamily_len, this->value, this->v_len + 1, this->format);
		confs->_configs_left_--;

	}

	
	return !confs->_configs_left_;

}

static int populate_configs (struct configs * confs) {

	
	IniFormat config_format = {
		.delimiter_symbol = INI_ANY_SPACE,
		.case_sensitive = FALSE,
		.semicolon_marker = INI_IGNORE,
		.hash_marker = INI_IGNORE,
		.multiline_nodes = INI_NO_MULTILINE,
		.section_paths = INI_NO_SECTIONS,
		.no_single_quotes = FALSE,
		.no_double_quotes = FALSE,
		.no_spaces_in_names = TRUE,
		.implicit_is_not_empty = TRUE,
		.do_not_collapse_values = FALSE,
		.preserve_empty_quotes = FALSE,
		.disabled_after_space = TRUE,
		.disabled_can_be_implicit = FALSE
	};

	*confs = (struct configs) { NULL, NULL, 0x80, 0x80, NULL, 0, CONFIGS_TO_READ };

	if (load_ini_path("rosetta.conf", config_format, NULL, configs_member_handler, confs) & CONFINI_ERROR) {

		fprintf(stderr, "Sorry, something went wrong :-(\n");
		return 1;

	}

	confs->needspeeling &= 0x7F;
	confs->seedsremoved &= 0x7F;

	return 0;

}

int main () {

	struct configs confs;

	ini_global_set_implicit_value("YES", 0);

	if (populate_configs(&confs)) {

		return 1;

	}

	

	printf(

		"Full name: %s\n"
		"Favorite fruit: %s\n"
		"Need spelling: %s\n"
		"Seeds removed: %s\n",

		confs.fullname,
		confs.favouritefruit,
		confs.needspeeling ? "True" : "False",
		confs.seedsremoved ? "True" : "False"

	);

	for (size_t idx = 0; idx < confs.otherfamily_len; idx++) {

		printf("Other family[%d]: %s\n", idx, confs.otherfamily[idx]);

	}

	

	#define FREE_NON_NULL(PTR) if (PTR) { free(PTR); }

	FREE_NON_NULL(confs.fullname);
	FREE_NON_NULL(confs.favouritefruit);
	FREE_NON_NULL(confs.otherfamily);

	return 0;

}









COBOL:        identification division.
       program-id. ReadConfiguration.

       environment division.
       configuration section.
       repository.
           function all intrinsic.

       input-output section.
       file-control.
           select config-file     assign to "Configuration.txt"
                                  organization line sequential.
       data division.
       file section.

       fd  config-file.
       01  config-record          pic is x(128).

       working-storage section.
       77  idx                    pic 9(3).
       77  pos                    pic 9(3).
       77  last-pos               pic 9(3).
       77  config-key             pic x(32).
       77  config-value           pic x(64).
       77  multi-value            pic x(64).
       77  full-name              pic x(64).
       77  favourite-fruit        pic x(64).
       77  other-family           pic x(64) occurs 10.
       77  need-speeling          pic x(5) value "false".
       77  seeds-removed          pic x(5) value "false".

       procedure division.
       main.
           open input config-file
           perform until exit
              read config-file
                 at end
                    exit perform
              end-read  
              move trim(config-record) to config-record
              if config-record(1:1) = "#" or ";" or spaces
                 exit perform cycle
              end-if
              unstring config-record delimited by spaces into config-key
              move trim(config-record(length(trim(config-key)) + 1:)) to config-value
              if config-value(1:1) = "="
                 move trim(config-value(2:)) to config-value
              end-if
              evaluate upper-case(config-key)
                 when "FULLNAME"
                    move config-value to full-name
                 when "FAVOURITEFRUIT"
                    move config-value to favourite-fruit
                 when "NEEDSPEELING"
                    if config-value = spaces
                       move "true" to config-value
                    end-if
                    if config-value = "true" or "false"
                       move config-value to need-speeling
                    end-if
                 when "SEEDSREMOVED"
                    if config-value = spaces
                       move "true" to config-value
                    end-if,
                    if config-value = "true" or "false"
                       move config-value to seeds-removed
                    end-if
                 when "OTHERFAMILY"
                    move 1 to idx, pos
                    perform until exit
                       unstring config-value delimited by "," into multi-value with pointer pos
                          on overflow
                             move trim(multi-value) to other-family(idx)
                             move pos to last-pos
                          not on overflow
                             if config-value(last-pos:) <> spaces
                                move trim(config-value(last-pos:)) to other-family(idx)
                             end-if,
                             exit perform
                       end-unstring
                       add 1 to idx
                    end-perform
              end-evaluate
           end-perform
           close config-file

           display "fullname = " full-name
           display "favouritefruit = " favourite-fruit
           display "needspeeling = " need-speeling
           display "seedsremoved = " seeds-removed
           perform varying idx from 1 by 1 until idx > 10
              if other-family(idx) <> low-values
                 display "otherfamily(" idx ") = " other-family(idx)
              end-if
           end-perform
           .