First running version
This commit is contained in:
300
src/gclogger.c
Normal file
300
src/gclogger.c
Normal file
@@ -0,0 +1,300 @@
|
||||
#include "gclogger.h"
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
static volatile bool running = true;
|
||||
|
||||
static int confighandler(void* config, const char* section, const char* name, const char* value) {
|
||||
configuration* pconfig = (configuration*)config;
|
||||
|
||||
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||
if (MATCH("device", "port")) {
|
||||
pconfig->dev_port = strdup(value);
|
||||
} else if (MATCH("device", "baud")) {
|
||||
pconfig->dev_baud = atoi(value);
|
||||
} else if (MATCH("device", "location")) {
|
||||
pconfig->dev_location = strdup(value);
|
||||
} else if (MATCH("device", "latitude")) {
|
||||
pconfig->dev_latitude = atof(value);
|
||||
} else if (MATCH("device", "longitude")) {
|
||||
pconfig->dev_longitude = atof(value);
|
||||
} else if (MATCH("device", "interval")) {
|
||||
pconfig->dev_interval = atoi(value);
|
||||
} else if (MATCH("custlog", "url")) {
|
||||
pconfig->custlog_url = strdup(value);
|
||||
} else if (MATCH("custlog", "id")) {
|
||||
pconfig->custlog_id = strdup(value);
|
||||
} else if (MATCH("custlog", "param_id")) {
|
||||
pconfig->custlog_param_id = strdup(value);
|
||||
} else if (MATCH("custlog", "param_cpm")) {
|
||||
pconfig->custlog_param_cpm = strdup(value);
|
||||
} else if (MATCH("custlog", "param_temp")) {
|
||||
pconfig->custlog_param_temp = strdup(value);
|
||||
} else if (MATCH("custlog", "param_lng")) {
|
||||
pconfig->custlog_param_lng = strdup(value);
|
||||
} else if (MATCH("custlog", "param_lat")) {
|
||||
pconfig->custlog_param_lat = strdup(value);
|
||||
} else if (MATCH("custlog", "param_loc")) {
|
||||
pconfig->custlog_param_loc = strdup(value);
|
||||
} else if (MATCH("custlog", "param_version")) {
|
||||
pconfig->custlog_param_version = strdup(value);
|
||||
} else if (MATCH("custlog", "param_time")) {
|
||||
pconfig->custlog_param_time = strdup(value);
|
||||
} else if (MATCH("csv", "path")) {
|
||||
pconfig->csv_path = strdup(value);
|
||||
} else {
|
||||
return 0; /* unknown section/name, error */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool str_isset(char *str) {
|
||||
return str != NULL && str[0] != '\0';
|
||||
}
|
||||
|
||||
void init_configuration(configuration* config) {
|
||||
config->debug = false;
|
||||
|
||||
config->dev_port = "";
|
||||
config->dev_baud = 115200;
|
||||
config->dev_location = "";
|
||||
config->dev_latitude = 0.0;
|
||||
config->dev_longitude = 0.0;
|
||||
config->dev_interval = 60;
|
||||
config->custlog_url = "";
|
||||
config->custlog_id = "";
|
||||
config->custlog_param_id = "id";
|
||||
config->custlog_param_cpm = "cpm";
|
||||
config->custlog_param_temp = "temp";
|
||||
config->custlog_param_lng = "lng";
|
||||
config->custlog_param_lat = "lat";
|
||||
config->custlog_param_loc = "loc";
|
||||
config->custlog_param_version = "version";
|
||||
config->custlog_param_time = "time";
|
||||
config->csv_path = "";
|
||||
}
|
||||
|
||||
void signal_handler(int sig) {
|
||||
switch (sig) {
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
case SIGQUIT:
|
||||
case SIGHUP:
|
||||
running = false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool send_custlog(const configuration config, int cpm, float temperature, const char *version, struct tm *tm) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
|
||||
curl = curl_easy_init();
|
||||
|
||||
if(curl) {
|
||||
char *url_buffer;
|
||||
size_t url_size;
|
||||
// first get size of final url
|
||||
url_size = snprintf(NULL, 0, "%s?%s=%s&%s=%d&%s=%f&%s=%s&%s=%ld&%s=%f&%s=%f&%s=%s",
|
||||
config.custlog_url,
|
||||
config.custlog_param_id, config.custlog_id,
|
||||
config.custlog_param_cpm, cpm,
|
||||
config.custlog_param_temp, temperature,
|
||||
config.custlog_param_version, version,
|
||||
config.custlog_param_time, mktime(tm),
|
||||
config.custlog_param_lng, config.dev_longitude,
|
||||
config.custlog_param_lat, config.dev_latitude,
|
||||
config.custlog_param_loc, config.dev_location);
|
||||
|
||||
// now allocate buffer and build url
|
||||
url_buffer = (char *)malloc(url_size + 1);
|
||||
snprintf(url_buffer, url_size+1,"%s?%s=%s&%s=%d&%s=%f&%s=%s&%s=%ld&%s=%f&%s=%f&%s=%s",
|
||||
config.custlog_url,
|
||||
config.custlog_param_id, config.custlog_id,
|
||||
config.custlog_param_cpm, cpm,
|
||||
config.custlog_param_temp, temperature,
|
||||
config.custlog_param_version, version,
|
||||
config.custlog_param_time, mktime(tm),
|
||||
config.custlog_param_lng, config.dev_longitude,
|
||||
config.custlog_param_lat, config.dev_latitude,
|
||||
config.custlog_param_loc, config.dev_location);
|
||||
|
||||
printf("final url: %s\n", url_buffer);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url_buffer);
|
||||
|
||||
#ifdef SKIP_PEER_VERIFICATION
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
#endif
|
||||
|
||||
#ifdef SKIP_HOSTNAME_VERIFICATION
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
#endif
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if(res != CURLE_OK)
|
||||
printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
else
|
||||
printf("curl_easy_perform() ok!");
|
||||
|
||||
free(url_buffer);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool send_tocsv(const configuration config, int cpm, float temperature, const char *version, const struct tm *tm) {
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen(config.csv_path, "a")) == NULL) {
|
||||
printf("Error while opening the csv file '%s'.\n", config.csv_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *ts = asctime(tm);
|
||||
ts[strlen(ts) - 1] = 0; // get rid of \n
|
||||
fprintf(fp, "%s,%d,%07.3f,%s\n", ts, cpm, temperature, version);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void show_usage() {
|
||||
printf("Geiger Counter Logger\n");
|
||||
printf("Version %s\n", GCLOGGER_VERSION);
|
||||
printf("Copyright (C) 2018 Christoph Haas, christoph.h@sprinternet.at\n\n");
|
||||
printf("This program comes with ABSOLUTELY NO WARRANTY.\n");
|
||||
printf("This is free software, and you are welcome to redistribute it.\n\n");
|
||||
printf("Usage: gclogger -c <file> [-d]\n");
|
||||
printf(" -c <file> configuration file path\n");
|
||||
printf(" -d enable verbose mode\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int opt = 0;
|
||||
int gc_fd = -1;
|
||||
configuration config;
|
||||
|
||||
// setup signal handlers
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGQUIT, signal_handler);
|
||||
signal(SIGHUP, signal_handler);
|
||||
|
||||
// parse config file
|
||||
init_configuration(&config);
|
||||
while ((opt = getopt(argc, argv, "c:d")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
config.debug = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (ini_parse(optarg, confighandler, &config) < 0) {
|
||||
printf("Can't load config file '%s'\n", optarg);
|
||||
return 1;
|
||||
}
|
||||
printf("Config loaded from '%s': device_port=%s, interval=%d\n",
|
||||
optarg, config.dev_port, config.dev_interval);
|
||||
break;
|
||||
|
||||
default: /* '?' */
|
||||
show_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// additional check if extra/no arguments where given
|
||||
if (optind < argc || argc == 1) {
|
||||
show_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// check if device port is set
|
||||
if(!str_isset(config.dev_port)) {
|
||||
printf("Device port must be set!'\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
gc_fd = gmc_open(config.dev_port, config.dev_baud);
|
||||
|
||||
// check if device connection was successful
|
||||
if(gc_fd == -1) {
|
||||
printf("Connection to device (%s) failed!'\n", config.dev_port);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// read version
|
||||
char version[15] = { 0 };
|
||||
if(gmc_get_version(gc_fd, version) == -1) {
|
||||
printf("Unable to read Geiger counter version!'\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (config.debug) {
|
||||
printf("GC VERSION: %s\n", version);
|
||||
}
|
||||
|
||||
time_t last = time(NULL);
|
||||
int cpm, sum = 0, count = 0, tcount = 0;
|
||||
float temperature, tsum = 0;
|
||||
|
||||
// main loop
|
||||
while (running) {
|
||||
// read cpm
|
||||
if ((cpm = gmc_get_cpm(gc_fd)) > 0) {
|
||||
sum += cpm;
|
||||
count++;
|
||||
}
|
||||
|
||||
// read temperature
|
||||
tsum += gmc_get_temperature(gc_fd);
|
||||
tcount++;
|
||||
|
||||
if (difftime(time(NULL), last) >= config.dev_interval) {
|
||||
if (count > 0) {
|
||||
struct tm *tm = gmtime(&last);
|
||||
cpm = sum / count;
|
||||
temperature = tsum / tcount;
|
||||
|
||||
if (config.debug) {
|
||||
printf("CPM: %d (= %d/%d), TEMP: %07.3f, Timestamp: %s\n", cpm, sum, count, temperature, asctime(tm));
|
||||
}
|
||||
|
||||
// log to custom REST api
|
||||
if (str_isset(config.custlog_url)) {
|
||||
printf("Uploading to %s.\n", config.custlog_url);
|
||||
if(!send_custlog(config, cpm, temperature, version, tm)) {
|
||||
printf("Upload to %s failed.\n", config.custlog_url);
|
||||
}
|
||||
}
|
||||
|
||||
// log to csv
|
||||
if (str_isset(config.csv_path)) {
|
||||
if(!send_tocsv(config, cpm, temperature, version, tm)) {
|
||||
printf("Logging to %s failed.\n", config.csv_path);
|
||||
}
|
||||
}
|
||||
|
||||
time(&last);
|
||||
sum = tsum = count = tcount = 0;
|
||||
} else {
|
||||
printf("Reading ZERO value from Geiger tube.\n");
|
||||
}
|
||||
}
|
||||
|
||||
sleep(1); // sleep one second
|
||||
}
|
||||
|
||||
gmc_close(gc_fd);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
102
src/gmc.c
Normal file
102
src/gmc.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "gmc.h"
|
||||
|
||||
int gmc_open(const char *device, int baud) {
|
||||
int gc_fd = -1;
|
||||
struct termios tio;
|
||||
|
||||
memset(&tio, 0, sizeof(struct termios));
|
||||
tio.c_cflag = CS8 | CREAD | CLOCAL; // 8n1
|
||||
tio.c_cc[VMIN] = 0;
|
||||
tio.c_cc[VTIME] = 5;
|
||||
|
||||
int tio_baud = B115200;
|
||||
switch(baud) {
|
||||
case 9600: tio_baud = B9600; break;
|
||||
case 19200: tio_baud = B19200; break;
|
||||
case 38400: tio_baud = B38400; break;
|
||||
case 57600: tio_baud = B57600; break;
|
||||
case 115200: tio_baud = B115200; break;
|
||||
}
|
||||
|
||||
if ((gc_fd = open(device, O_RDWR)) != -1) {
|
||||
if (cfsetspeed(&tio, tio_baud) == 0) { // set baud speed
|
||||
if (tcsetattr(gc_fd, TCSANOW, &tio) == 0) { // apply baud speed change
|
||||
if (gmc_set_heartbeat_off(gc_fd)) { // disable heartbeat, we use polling
|
||||
return gc_fd;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// something failed
|
||||
close(gc_fd);
|
||||
gc_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return gc_fd;
|
||||
}
|
||||
|
||||
void gmc_close(int device) {
|
||||
close(device);
|
||||
}
|
||||
|
||||
int gmc_get_cpm(int device) {
|
||||
const char cmd[] = "<GETCPM>>";
|
||||
char buf[2] = { 0 };
|
||||
|
||||
if (gmc_write(device, cmd) == (ssize_t) strlen(cmd))
|
||||
gmc_read(device, buf, 2);
|
||||
|
||||
return buf[0] * 256 + buf[1];
|
||||
}
|
||||
|
||||
float gmc_get_temperature(int device) {
|
||||
const char cmd[] = "<GETTEMP>>";
|
||||
char buf[4] = { 0 };
|
||||
|
||||
if (gmc_write(device, cmd) == (ssize_t) strlen(cmd))
|
||||
gmc_read(device, buf, 4);
|
||||
|
||||
int sign = buf[2] == 0 ? 1 : -1;
|
||||
float temp = buf[0];
|
||||
temp += (float) buf[1] / 10;
|
||||
temp = temp * sign;
|
||||
return temp;
|
||||
}
|
||||
|
||||
int gmc_get_version(int device, char *version) {
|
||||
const char cmd[] = "<GETVER>>";
|
||||
|
||||
if (gmc_write(device, cmd) == (ssize_t) strlen(cmd))
|
||||
return gmc_read(device, version, 14);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool gmc_set_heartbeat_off(int device) {
|
||||
const char cmd[] = "<HEARTBEAT0>>";
|
||||
|
||||
if (gmc_write(device, cmd) == (ssize_t) strlen(cmd))
|
||||
return gmc_flush(device);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int gmc_write(int device, const char *cmd) {
|
||||
return write(device, cmd, strlen(cmd));
|
||||
}
|
||||
|
||||
int gmc_read(int device, char *buf, int length) {
|
||||
return read(device, buf, length);
|
||||
}
|
||||
|
||||
bool gmc_flush(int device) {
|
||||
char ch;
|
||||
|
||||
// flush input stream (max 100 bytes)
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (read(device, &ch, 1) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
269
src/ini.c
Normal file
269
src/ini.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ini.h"
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
|
||||
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||
typedef struct {
|
||||
const char* ptr;
|
||||
size_t num_left;
|
||||
} ini_parse_string_ctx;
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
static char* rstrip(char* s)
|
||||
{
|
||||
char* p = s + strlen(s);
|
||||
while (p > s && isspace((unsigned char)(*--p)))
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
static char* lskip(const char* s)
|
||||
{
|
||||
while (*s && isspace((unsigned char)(*s)))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to null at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
strncpy(dest, src, size);
|
||||
dest[size - 1] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user)
|
||||
{
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
int max_line = INI_MAX_LINE;
|
||||
#else
|
||||
char* line;
|
||||
int max_line = INI_INITIAL_ALLOC;
|
||||
#endif
|
||||
#if INI_ALLOW_REALLOC
|
||||
char* new_line;
|
||||
int offset;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)malloc(INI_INITIAL_ALLOC);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INI_HANDLER_LINENO
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||
#else
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, max_line, stream) != NULL) {
|
||||
#if INI_ALLOW_REALLOC
|
||||
offset = strlen(line);
|
||||
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
max_line *= 2;
|
||||
if (max_line > INI_MAX_LINE)
|
||||
max_line = INI_MAX_LINE;
|
||||
new_line = realloc(line, max_line);
|
||||
if (!new_line) {
|
||||
free(line);
|
||||
return -2;
|
||||
}
|
||||
line = new_line;
|
||||
if (reader(line + offset, max_line - offset, stream) == NULL)
|
||||
break;
|
||||
if (max_line >= INI_MAX_LINE)
|
||||
break;
|
||||
offset += strlen(line + offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
#if INI_ALLOW_BOM
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
#endif
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||
/* Start-of-line comment */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = end + 1;
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
#endif
|
||||
value = lskip(value);
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!HANDLER(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
|
||||
#if INI_STOP_ON_FIRST_ERROR
|
||||
if (error)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !INI_USE_STACK
|
||||
free(line);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||
{
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||
{
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* An ini_reader function to read the next line from a string buffer. This
|
||||
is the fgets() equivalent used by ini_parse_string(). */
|
||||
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||
const char* ctx_ptr = ctx->ptr;
|
||||
size_t ctx_num_left = ctx->num_left;
|
||||
char* strp = str;
|
||||
char c;
|
||||
|
||||
if (ctx_num_left == 0 || num < 2)
|
||||
return NULL;
|
||||
|
||||
while (num > 1 && ctx_num_left != 0) {
|
||||
c = *ctx_ptr++;
|
||||
ctx_num_left--;
|
||||
*strp++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
num--;
|
||||
}
|
||||
|
||||
*strp = '\0';
|
||||
ctx->ptr = ctx_ptr;
|
||||
ctx->num_left = ctx_num_left;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||
ini_parse_string_ctx ctx;
|
||||
|
||||
ctx.ptr = string;
|
||||
ctx.num_left = strlen(string);
|
||||
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||
user);
|
||||
}
|
Reference in New Issue
Block a user