Commit 1cf89cc7 authored by Philip Carns's avatar Philip Carns

copy over misc. utility code from CODES repo

parent 507b01d9
/*
* Copyright (C) 2013, University of Chicago
*
* See COPYRIGHT notice in top-level directory.
*/
#ifndef CODES_H
#define CODES_H
#include <ross.h>
#include <assert.h>
#if 0
#define codes_event_new tw_event_new
#else
static inline tw_event * codes_event_new(
tw_lpid dest_gid,
tw_stime offset_ts,
tw_lp * sender)
{
tw_stime abs_ts = offset_ts + tw_now(sender);
assert(abs_ts < g_tw_ts_end);
//printf("codes_event_new() abs_ts: %.9f\n", abs_ts);
return(tw_event_new(dest_gid, offset_ts, sender));
}
#endif
/* TODO: validate what value we should use here */
/* Modeled latency for communication between local software components and
* communication between daemons and hardware devices. Measured in
* nanoseconds.
*/
#define CODES_MEAN_LOCAL_LATENCY 0.01
static inline tw_stime codes_local_latency(tw_lp *lp)
{
tw_stime tmp;
tmp = tw_rand_exponential(lp->rng, CODES_MEAN_LOCAL_LATENCY);
return(tmp);
}
static inline void codes_local_latency_reverse(tw_lp *lp)
{
tw_rand_reverse_unif(lp->rng);
return;
}
#endif /* CODES_H */
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* End:
*
* vim: ft=c ts=8 sts=4 sw=4 expandtab
*/
/*
* Copyright (C) 2011, University of Chicago
*
* See COPYRIGHT notice in top-level directory.
*/
/* SUMMARY:
* CODES custom mapping file for ROSS
*/
#include "configuration.h"
#include "codes.h"
#include "lp-type-lookup.h"
#define MAX_NAME_LENGTH 256
/* number of LPs assigned to the current PE (abstraction of MPI rank) */
static int lps_for_this_pe = 0;
/* data structure to hold configuration */
configuration_t config;
/* char arrays for holding lp type name and group name*/
char local_grp_name[MAX_NAME_LENGTH], local_lp_name[MAX_NAME_LENGTH];
/* Returns number of LPs on the current PE */
int codes_mapping_get_lps_for_pe(void);
/* Takes the global LP ID and returns the rank (PE id) on which the LP is mapped.*/
tw_peid codes_mapping( tw_lpid gid);
/* loads the configuration file and sets up the number of LPs on each PE. */
void codes_mapping_setup(char* filepath);
/* Takes the group name , type name, rep ID and offset (for that lp type + repetition) and then returns the global LP ID. */
void codes_mapping_get_lp_id(char* grp_name, char* lp_type_name, int rep_id, int offset, tw_lpid* gid);
/* takes the LP ID and returns its grp name and index, lp type name and ID, repetition ID and the offset of the LP
* (for multiple LPs in a repetition). */
void codes_mapping_get_lp_info(tw_lpid gid, char* grp_name, int* grp_id, int* lp_type_id, char* lp_type_name, int* grp_rep_id, int* offset);
/* assigns local and global lp ids for ROSS. */
void codes_mapping_init(void);
/* Takes the global LP ID, maps it to the local LP ID and returns the LP.
* lps have global and local LP IDs.
* global LP IDs are unique across all PEs, local LP IDs are unique within a PE. */
tw_lp * codes_mapping_to_lp( tw_lpid lpid);
#ifndef __CONFIGURATION_H__
#define __CONFIGURATION_H__
#include <stdint.h>
#include <stdlib.h>
#define CONFIGURATION_MAX_NAME 256
#define CONFIGURATION_MAX_GROUPS 10
#define CONFIGURATION_MAX_TYPES 10
typedef struct config_lptype_s
{
char name[CONFIGURATION_MAX_NAME];
uint64_t count;
} config_lptype_t;
typedef struct config_lpgroup_s
{
char name[CONFIGURATION_MAX_NAME];
uint64_t repetitions;
config_lptype_t lptypes[CONFIGURATION_MAX_TYPES];
uint64_t lptypes_count;
} config_lpgroup_t;
typedef struct configuration_s
{
config_lpgroup_t lpgroups[CONFIGURATION_MAX_GROUPS];
uint64_t lpgroups_count;
} configuration_t;
int configuration_load (const char * filepath, configuration_t *config);
int configuration_dump (configuration_t *config);
#endif
#ifndef DOTCONF_H
#define DOTCONF_H
#ifdef __cplusplus
extern "C" {
#endif
/* stdio.h should be included by the application - as the manual page says */
#ifndef _STDIO_H
#include <stdio.h> /* needed for FILE* */
#endif
#ifdef WIN32
# ifndef R_OK
#define R_OK 0
# endif
#endif
/* some buffersize definitions */
#define CFG_BUFSIZE 4096 /* max length of one line */
#define CFG_MAX_OPTION 32 /* max length of any option name */
#define CFG_MAX_VALUE 4064 /* max length of any options value */
#define CFG_MAX_FILENAME 256 /* max length of a filename */
#define CFG_VALUES 16 /* max # of arguments an option takes */
#define CFG_INCLUDEPATH_ENV "DC_INCLUDEPATH"
#define WILDCARDS "*?" /* list of supported wild-card characters */
/* constants for type of option */
#define ARG_TOGGLE 0 /* TOGGLE on,off; yes,no; 1, 0; */
#define ARG_INT 1 /* callback wants an integer */
#define ARG_STR 2 /* callback expects a \0 terminated str */
#define ARG_LIST 3 /* wants list of strings */
#define ARG_NAME 4 /* wants option name plus ARG_LIST stuff */
#define ARG_RAW 5 /* wants raw argument data */
#define ARG_NONE 6 /* does not expect ANY args */
#define CTX_ALL 0 /* context: option can be used anywhere */
/* for convenience of terminating the dotconf_options list */
#define LAST_OPTION { "", 0, NULL, NULL }
#define LAST_CONTEXT_OPTION { "", 0, NULL, NULL, 0 }
#define DOTCONF_CB(__name) const char *__name(command_t *cmd, \
context_t *ctx)
#define FUNC_ERRORHANDLER(_name) int _name(configfile_t * configfile, \
int type, long dc_errno, const char *msg)
/* some flags that change the runtime behaviour of dotconf */
#define NONE 0
#define CASE_INSENSITIVE 1<<0 /* match option names case insensitive */
#define DONT_SUBSTITUTE 1<<1 /* do not call substitute_env after read_arg */
#define NO_INLINE_COMMENTS 1<<2 /* do not allow inline comments */
#define DUPLICATE_OPTION_NAMES 1<<3 /* allow for duplicate option names */
/* syslog style errors as suggested by Sander Steffann <sander@steffann.nl> */
#ifdef HAVE_SYSLOG
#include <syslog.h>
#define DCLOG_EMERG LOG_EMERG /* system is unusable */
#define DCLOG_ALERT LOG_ALERT /* action must be taken immediately */
#define DCLOG_CRIT LOG_CRIT /* critical conditions */
#define DCLOG_ERR LOG_ERR /* error conditions */
#define DCLOG_WARNING LOG_WARNING /* warning conditions */
#define DCLOG_NOTICE LOG_NOTICE /* normal but significant condition */
#define DCLOG_INFO LOG_INFO /* informational */
#define DCLOG_DEBUG LOG_DEBUG /* debug-level messages */
#define DCLOG_LEVELMASK LOG_PRIMASK /* mask off the level value */
#else /* HAVE_SYSLOG */
#define DCLOG_EMERG 0 /* system is unusable */
#define DCLOG_ALERT 1 /* action must be taken immediately */
#define DCLOG_CRIT 2 /* critical conditions */
#define DCLOG_ERR 3 /* error conditions */
#define DCLOG_WARNING 4 /* warning conditions */
#define DCLOG_NOTICE 5 /* normal but significant condition */
#define DCLOG_INFO 6 /* informational */
#define DCLOG_DEBUG 7 /* debug-level messages */
#define DCLOG_LEVELMASK 7 /* mask off the level value */
#endif /* HAVE_SYSLOG */
/* callback types for dotconf_callback */
/* error constants */
#define ERR_NOERROR 0x0000
#define ERR_PARSE_ERROR 0x0001
#define ERR_UNKNOWN_OPTION 0x0002
#define ERR_WRONG_ARG_COUNT 0x0003
#define ERR_INCLUDE_ERROR 0x0004
#define ERR_NOACCESS 0x0005
#define ERR_USER 0x1000 /* base for userdefined errno's */
/* i needed this to check an ARG_LIST entry if it's toggled in one of my apps; maybe you do too */
#define CFG_TOGGLED(_val) ( (_val[0] == 'Y' \
|| _val[0] == 'y') \
|| (_val[0] == '1') \
|| ((_val[0] == 'o' \
|| _val[0] == 'O') \
&& (_val[1] == 'n' \
|| _val[1] == 'N')))
enum callback_types
{
ERROR_HANDLER = 1,
CONTEXT_CHECKER
};
typedef enum callback_types callback_types;
typedef struct configfile_t configfile_t;
typedef struct configoption_t configoption_t;
typedef struct configoption_t ConfigOption;
typedef struct command_t command_t;
typedef void context_t;
typedef void info_t;
typedef const char *(*dotconf_callback_t)(command_t *, context_t *);
typedef int (*dotconf_errorhandler_t)(configfile_t *, int, unsigned long, const char *);
typedef const char *(*dotconf_contextchecker_t)(command_t *, unsigned long);
struct configfile_t
{
/* ------ the fields in configfile_t are provided to the app via command_t's ; READ ONLY! --- */
FILE *stream;
char eof; /* end of file reached ? */
size_t size; /* file size; cached on-demand for here-documents */
context_t *context;
configoption_t const **config_options;
int config_option_count;
/* ------ misc read-only fields ------------------------------------------------------------- */
char *filename; /* name of file this option was found in */
unsigned long line; /* line number we're currently at */
unsigned long flags; /* runtime flags given to dotconf_open */
char *includepath;
/* ------ some callbacks for interactivity -------------------------------------------------- */
dotconf_errorhandler_t errorhandler;
dotconf_contextchecker_t contextchecker;
int (*cmp_func)(const char *, const char *, size_t);
};
struct configoption_t
{
const char *name; /* name of configuration option */
int type; /* for possible values, see above */
dotconf_callback_t callback; /* callback function */
info_t *info; /* additional info for multi-option callbacks */
unsigned long context; /* context sensitivity flags */
};
struct command_t
{
const char *name; /* name of the command */
configoption_t *option; /* the option as given in the app; READ ONLY */
/* ------ argument data filled in for each line / command ----------------------------------- */
struct {
long value; /* ARG_INT, ARG_TOGGLE */
char *str; /* ARG_STR */
char **list; /* ARG_LIST */
} data;
int arg_count; /* number of arguments (in data.list) */
/* ------ misc context information ---------------------------------------------------------- */
configfile_t *configfile;
context_t *context;
};
/* ------ dotconf_create() - create the configfile_t needed for further dot.conf fun ------------ */
configfile_t *dotconf_create(char *, const configoption_t *, context_t *, unsigned long);
/* ------ dotconf_cleanup() - tidy up behind dotconf_create and the parser dust ----------------- */
void dotconf_cleanup(configfile_t *configfile);
/* ------ dotconf_command_loop() - iterate through each line of file and handle the commands ---- */
int dotconf_command_loop(configfile_t *configfile);
/* ------ dotconf_command_loop_until_error() - like continue_line but return on the first error - */
const char *dotconf_command_loop_until_error(configfile_t *configfile);
/* ------ dotconf_continue_line() - check if line continuation is to be handled ----------------- */
int dotconf_continue_line(char *buffer, size_t length);
/* ------ dotconf_get_next_line() - read in the next line of the configfile_t ------------------- */
int dotconf_get_next_line(char *buffer, size_t bufsize, configfile_t *configfile);
/* ------ dotconf_get_here_document() - read the here document until delimit is found ----------- */
char *dotconf_get_here_document(configfile_t *configfile, const char *delimit);
/* ------ dotconf_invoke_command() - call the callback for command_t ---------------------------- */
const char *dotconf_invoke_command(configfile_t *configfile, command_t *cmd);
/* ------ dotconf_find_command() - iterate through all registered options trying to match ------- */
configoption_t *dotconf_find_command(configfile_t *configfile, const char *command);
/* ------ dotconf_read_arg() - read one argument from the line handling quoting and escaping ---- */
/*
side effects: the char* returned by dotconf_read_arg is malloc() before, hence that pointer
will have to be free()ed later.
*/
char *dotconf_read_arg(configfile_t *configfile, signed char **line);
/* ------ dotconf_handle_command() - parse, substitute, find, invoke the command found in buffer */
const char *dotconf_handle_command(configfile_t *configfile, char *buffer);
/* ------ dotconf_register_option() - add a new option table to the list of commands ------------ */
void dotconf_register_options(configfile_t *configfile, const configoption_t *options);
/* ------ dotconf_warning() - handle the dispatch of error messages of various levels ----------- */
int dotconf_warning(configfile_t *configfile, int level, unsigned long errnum, const char *, ...);
/* ------ dotconf_callback() - register a special callback -------------------------------------- */
void dotconf_callback(configfile_t *configfile, callback_types type, dotconf_callback_t);
/* ------ dotconf_substitute_env() - handle the substitution on environment variables ----------- */
char *dotconf_substitute_env(configfile_t *, char *);
/* ------ internal utility function that verifies if a character is in the WILDCARDS list -- */
int dotconf_is_wild_card(char value);
/* ------ internal utility function that calls the appropriate routine for the wildcard passed in -- */
int dotconf_handle_wild_card(command_t* cmd, char wild_card, char* path, char* pre, char* ext);
/* ------ internal utility function that frees allocated memory from dotcont_find_wild_card -- */
void dotconf_wild_card_cleanup(char* path, char* pre);
/* ------ internal utility function to check for wild cards in file path -- */
/* ------ path and pre must be freed by the developer ( dotconf_wild_card_cleanup) -- */
int dotconf_find_wild_card(char* filename, char* wildcard, char** path, char** pre, char** ext);
/* ------ internal utility function that compares two stings from back to front -- */
int dotconf_strcmp_from_back(const char* s1, const char* s2);
/* ------ internal utility function that determins if a string matches the '?' criteria -- */
int dotconf_question_mark_match(char* dir_name, char* pre, char* ext);
/* ------ internal utility function that determins if a string matches the '*' criteria -- */
int dotconf_star_match(char* dir_name, char* pre, char* ext);
/* ------ internal utility function that determins matches for filenames with -- */
/* ------ a '?' in name and calls the Internal Include function on that filename -- */
int dotconf_handle_question_mark(command_t* cmd, char* path, char* pre, char* ext);
/* ------ internal utility function that determins matches for filenames with -- */
/* ------ a '*' in name and calls the Internal Include function on that filename -- */
int dotconf_handle_star(command_t* cmd, char* path, char* pre, char* ext);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* DOTCONF_H */
/*
* Copyright (C) 2011, University of Chicago
*
* See COPYRIGHT notice in top-level directory.
*/
#ifndef LP_IO_H
#define LP_IO_H
#include <ross.h>
typedef char* lp_io_handle;
#define LP_IO_UNIQ_SUFFIX 1
/* to be called (collectively) before running simulation to prepare the
* output directory.
*/
int lp_io_prepare(char *directory, int flags, lp_io_handle* handle, MPI_Comm comm);
/* to be called within LPs to store a block of data */
int lp_io_write(tw_lpid gid, char* identifier, int size, void* buffer);
/* to be called (collectively) after tw_run() to flush data to disk */
int lp_io_flush(lp_io_handle handle, MPI_Comm comm);
/* retrieves the directory name for a handle */
static inline char* lp_io_handle_to_dir(lp_io_handle handle)
{
return(strdup(handle));
}
#endif /* LP_IO_H */
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* End:
*
* vim: ft=c ts=8 sts=4 sw=4 expandtab
*/
/*
* Copyright (C) 2013, University of Chicago
*
* See COPYRIGHT notice in top-level directory.
*/
#ifndef LP_TYPE_LOOKUP_H
#define LP_TYPE_LOOKUP_H
#include "ross.h"
const tw_lptype* lp_type_lookup(const char* name);
void lp_type_register(const char* name, const tw_lptype* type);
#endif /* LP_TYPE_LOOKUP_H */
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* End:
*
* vim: ft=c ts=8 sts=4 sw=4 expandtab
*/
......@@ -55,7 +55,13 @@ nobase_include_HEADERS = \
codes/codesparser.h \
codes/quickhash.h \
codes/configfile.h \
codes/quicklist.h
codes/quicklist.h \
codes/codes_mapping.h \
codes/lp-type-lookup.h \
codes/codes.h \
codes/configuration.h \
codes/dotconf.h \
codes/lp-io.h
src_libcodes_base_a_SOURCES = \
codes/codesparser.h \
......@@ -90,6 +96,11 @@ src_libcodes_base_a_SOURCES = \
src/util/red-black-tree.c \
src/util/codes-callbacks.h \
src/util/codes-callbacks.c \
src/util/codes_mapping.c \
src/util/lp-type-lookup.c \
src/util/configuration.c \
src/util/dotconf.c \
src/util/lp-io.c \
codes/codeslogging.h \
src/logging/codeslogging.c \
codes/timeline.h \
......
/*
* Copyright (C) 2011, University of Chicago
*
* See COPYRIGHT notice in top-level directory.
*/
/* SUMMARY:
* CODES custom mapping file for ROSS
*/
#include "codes/codes_mapping.h"
int codes_mapping_get_lps_for_pe()
{
return lps_for_this_pe;
}
/* Takes the global LP ID and returns the rank (PE id) on which the LP is mapped */
tw_peid codes_mapping( tw_lpid gid)
{
return gid / lps_for_this_pe;
}
/* This function loads the configuration file and sets up the number of LPs on each PE */
void codes_mapping_setup(char* filepath)
{
/* TODO: Add full file path? This one only runs from current directory */
configuration_load(filepath, &config);
configuration_dump(&config);
int grp, lpt;
int pes = tw_nnodes();
for (grp = 0; grp < config.lpgroups_count; grp++)
{
for (lpt = 0; lpt < config.lpgroups[grp].lptypes_count; lpt++)
lps_for_this_pe += (config.lpgroups[grp].lptypes[lpt].count * config.lpgroups[grp].repetitions);
}
lps_for_this_pe /= pes;
//printf("\n LPs for this PE are %d reps %d ", lps_for_this_pe, config.lpgroups[grp].repetitions);
}
/* This function takes the group ID , type ID and rep ID then returns the global LP ID */
/* TODO: Add string based search for LP group and type names */
void codes_mapping_get_lp_id(char* grp_name, char* lp_type_name, int rep_id, int offset, tw_lpid* gid)
{
int grp, lpt, lpcount = 0, lp_types_count, rep, count_for_this_lpt;
short found = 0;
// Account for all lps in the previous groups
for(grp = 0; grp < config.lpgroups_count; grp++)
{
lp_types_count = config.lpgroups[grp].lptypes_count;
rep = config.lpgroups[grp].repetitions;
if(strcmp(config.lpgroups[grp].name, grp_name) == 0)
{
found = 1;
break;
}
for(lpt = 0; lpt < lp_types_count; lpt++)
lpcount += (rep * config.lpgroups[grp].lptypes[lpt].count);
}
assert(found);
found = 0;
lp_types_count = config.lpgroups[grp].lptypes_count;
// Account for the previous lp types in the current repetition
for(lpt = 0; lpt < lp_types_count; lpt++)
{
count_for_this_lpt = config.lpgroups[grp].lptypes[lpt].count;
if(strcmp(config.lpgroups[grp].lptypes[lpt].name, lp_type_name) == 0)
{
found = 1;
break;
}
lpcount += count_for_this_lpt;
}
assert(found);
// Account for all previous repetitions
for(rep = 0; rep < rep_id; rep++)
{
for(lpt = 0; lpt < lp_types_count; lpt++)
lpcount += config.lpgroups[grp].lptypes[lpt].count;
}
*gid = lpcount + offset;
}
/* This function takes the LP ID and returns its grp index, lp type ID and repetition ID */
void codes_mapping_get_lp_info(tw_lpid gid, char* grp_name, int* grp_id, int* lp_type_id, char* lp_type_name, int* grp_rep_id, int* offset)
{
int grp, lpt, rep, grp_offset, lp_offset, rep_offset;
int lp_tmp_id, lp_types_count, lp_count;
unsigned long grp_lp_count=0;
short found = 0;
/* Find the group id first */
for(grp = 0; grp < config.lpgroups_count; grp++)
{
grp_offset = 0;
rep_offset = 0;
rep = config.lpgroups[grp].repetitions;
lp_types_count = config.lpgroups[grp].lptypes_count;
for(lpt = 0; lpt < lp_types_count; lpt++)
{
lp_count = config.lpgroups[grp].lptypes[lpt].count;
grp_offset += (rep * lp_count);
rep_offset += lp_count;
}
/* Each gid is assigned an increasing number starting from 0th group and 0th lp type
* so we check here if the gid lies within the numeric range of a group */
if(gid >= grp_lp_count && gid < grp_lp_count + grp_offset)
{
*grp_id = grp;
strcpy(local_grp_name, config.lpgroups[grp].name);
lp_offset = gid - grp_lp_count; /* gets the lp offset starting from the point where the group begins */
*grp_rep_id = lp_offset / rep_offset;
lp_tmp_id = lp_offset - (*grp_rep_id * rep_offset);
found = 1;
break;
}
grp_lp_count += grp_offset; /* keep on increasing the group lp count to next group range*/
}
assert(found);
lp_offset = 0;
found = 0; /* reset found for finding LP type */
/* Now we compute the LP type ID here based on the lp offset that we just got */
for(lpt = 0; lpt < lp_types_count; lpt++)
{
lp_count = config.lpgroups[grp].lptypes[lpt].count;
if(lp_tmp_id >= lp_offset && lp_tmp_id < lp_offset + lp_count)
{
*lp_type_id = lpt;
strcpy(local_lp_name, config.lpgroups[grp].lptypes[lpt].name);
*offset = lp_tmp_id - lp_offset;
found = 1;
break;
}
lp_offset += lp_count;
}
assert(found);
strncpy(grp_name, local_grp_name, MAX_NAME_LENGTH);
strncpy(lp_type_name, local_lp_name, MAX_NAME_LENGTH);
//printf("\n gid %d lp type name %s rep_id %d ", gid, lp_type_name, *grp_rep_id);
}
/* This function assigns local and global LP Ids to LPs */
void codes_mapping_init(void)
{
int grp_id, lpt_id, rep_id, offset;
tw_lpid ross_gid, ross_lid; /* ross global and local IDs */
tw_pe * pe;
char lp_type_name[MAX_NAME_LENGTH];
char grp_name[MAX_NAME_LENGTH];
int nkp_per_pe = 16;
tw_lpid lpid, kpid;
/* have 16 kps per pe, this is the optimized configuration for ROSS custom mapping */
for(kpid = 0; kpid < nkp_per_pe; kpid++)
tw_kp_onpe(kpid, g_tw_pe[0]);
int lp_init_range = g_tw_mynode * lps_for_this_pe;
codes_mapping_get_lp_info(lp_init_range, grp_name, &grp_id, &lpt_id, lp_type_name, &rep_id, &offset);
for (lpid = lp_init_range; lpid < lp_init_range + lps_for_this_pe; lpid++)
{
ross_gid = lpid;
ross_lid = lpid - lp_init_range;
kpid = ross_lid % g_tw_nkp;
pe = tw_getpe(kpid % g_tw_npe);
codes_mapping_get_lp_info(ross_gid, grp_name, &grp_id, &lpt_id, lp_type_name, &rep_id, &offset);
tw_lp_onpe(ross_lid , pe, ross_gid);
tw_lp_onkp(g_tw_lp[ross_lid], g_tw_kp[kpid]);
tw_lp_settype(ross_lid, lp_type_lookup(lp_type_name));
}
return;
}
/* This function takes the global LP ID, maps it to the local LP ID and returns the LP
* lps have global and local LP IDs
* global LP IDs are unique across all PEs, local LP IDs are unique within a PE */
tw_lp * codes_mapping_to_lp( tw_lpid lpid)
{
int index = lpid - (g_tw_mynode * lps_for_this_pe);
// printf("\n global id %d index %d lps_before %d lps_offset %d local index %d ", lpid, index, lps_before, g_tw_mynode, local_index);
return g_tw_lp[index];
}
#include <string.h>
#include "codes/configuration.h"
#include "codes/txt_configfile.h"
int configuration_load (const char *filepath, configuration_t *config)
{
char *errstr;
ConfigHandle ch;
SectionHandle sh;
SectionHandle subsh;
SectionEntry se[10];
SectionEntry subse[10];
size_t se_count = 10;
size_t subse_count = 10;
int i, j, lpt;
char data[256];
memset (config, 0, sizeof(*config));
errstr = NULL;
ch = txtfile_openConfig(filepath, &errstr);
cf_openSection(ch, ROOT_SECTION, "LPGROUPS", &sh);
cf_listSection(ch, sh, se, &se_count);
for (i = 0; i < se_count; i++)
{
printf("section: %s type: %d\n", se[i].name, se[i].type);
if (se[i].type == SE_SECTION)
{
subse_count = 10;
cf_openSection(ch, sh, se[i].name, &subsh);
cf_listSection(ch, subsh, subse, &subse_count);
strncpy(config->lpgroups[i].name, se[i].name,
CONFIGURATION_MAX_NAME);
config->lpgroups[i].repetitions = 1;
config->lpgroups_count++;
for (j = 0, lpt = 0; j < subse_count; j++)
{
if (subse[j].type == SE_KEY)
{
cf_getKey(ch, subsh, subse[j].name, data, sizeof(data));
printf("key: %s value: %s\n", subse[j].name, data);
if (strcmp("repetitions", subse[j].name) == 0)
{
config->lpgroups[i].repetitions = atoi(data);
// printf("\n Repetitions: %d ", config->lpgroups[i].repetitions);
}
else
{
// assume these are lptypes and counts