Start from scratch
This commit is contained in:
parent
86c489f573
commit
243486ec64
@ -1,21 +0,0 @@
|
|||||||
# This file is only necessary for the gitlab to github sync and does not belong to the gclogger code.
|
|
||||||
|
|
||||||
#before_script:
|
|
||||||
# - ssh-keyscan -t rsa,dsa,ecdsa github.com > /etc/ssh/ssh_known_hosts
|
|
||||||
stages:
|
|
||||||
- mirror
|
|
||||||
|
|
||||||
variables:
|
|
||||||
MIRROR_REPOSITORY: "git@github.com:h44z/gclogger.git"
|
|
||||||
|
|
||||||
mirror-github:
|
|
||||||
stage: mirror
|
|
||||||
cache: {}
|
|
||||||
script:
|
|
||||||
# Do a mirror clone of the repository
|
|
||||||
- git clone --mirror $CI_REPOSITORY_URL
|
|
||||||
# Mirror push it to the destination
|
|
||||||
- cd $CI_PROJECT_NAME.git
|
|
||||||
- git push --mirror $MIRROR_REPOSITORY
|
|
||||||
# Cleanup
|
|
||||||
- cd ..; rm -rf $CI_PROJECT_NAME.git
|
|
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018 Christoph Haas
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
35
Makefile
35
Makefile
@ -1,35 +0,0 @@
|
|||||||
# Copyright 2018 Christoph Haas
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it
|
|
||||||
# under the terms of the standard MIT license. See LICENSE for more details.
|
|
||||||
|
|
||||||
EXE = gclogger
|
|
||||||
|
|
||||||
SRC_DIR = src
|
|
||||||
OBJ_DIR = obj
|
|
||||||
|
|
||||||
SRC = $(wildcard $(SRC_DIR)/*.c)
|
|
||||||
OBJ = $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
|
|
||||||
|
|
||||||
CPPFLAGS += -Iinclude
|
|
||||||
CFLAGS += -Wall -Wextra -Os
|
|
||||||
|
|
||||||
#LDFLAGS += -Llib
|
|
||||||
LDLIBS += -lcurl
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: $(EXE)
|
|
||||||
|
|
||||||
# Linking:
|
|
||||||
$(EXE): $(OBJ)
|
|
||||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
|
||||||
|
|
||||||
# Compiling:
|
|
||||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
# Cleaning:
|
|
||||||
clean:
|
|
||||||
$(RM) $(OBJ)
|
|
||||||
$(RM) $(EXE)
|
|
25
README.md
25
README.md
@ -1,25 +0,0 @@
|
|||||||
# Geiger Counter Logger
|
|
||||||
Version 0.1
|
|
||||||
|
|
||||||
Copyright (C) 2018 Christoph Haas, christoph.h@sprinternet.at
|
|
||||||
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY.
|
|
||||||
This is free software, and you are welcome to redistribute it.
|
|
||||||
|
|
||||||
gclogger is a lightweight daemon for reporting your
|
|
||||||
Geiger counter readings to various radiation monitoring websites.
|
|
||||||
|
|
||||||
Tested Geiger counter devices:
|
|
||||||
- GMC-320 and GMC-320 Plus
|
|
||||||
http://www.gqelectronicsllc.com/comersus/store/LeftStart.asp?idCategory=50
|
|
||||||
|
|
||||||
|
|
||||||
## Usage:
|
|
||||||
|
|
||||||
Compile the project using make.
|
|
||||||
|
|
||||||
Then edit the example config file and run gclogger:
|
|
||||||
|
|
||||||
```
|
|
||||||
./gclogger -c gclogger.ini -d
|
|
||||||
```
|
|
@ -1,46 +0,0 @@
|
|||||||
;
|
|
||||||
; Geiger Counter Logger - configuration file
|
|
||||||
;
|
|
||||||
|
|
||||||
; Serial device filename
|
|
||||||
; ------------------------
|
|
||||||
[device]
|
|
||||||
port=/dev/ttyUSB0
|
|
||||||
baud=115200
|
|
||||||
|
|
||||||
; GPS location of your sensor
|
|
||||||
; -----------------------------
|
|
||||||
location=Innsbruck, AT
|
|
||||||
latitude=47.2530
|
|
||||||
longitude=11.3969
|
|
||||||
|
|
||||||
; Polling interval in seconds (60..3600)
|
|
||||||
; ----------------------------------------
|
|
||||||
interval=60
|
|
||||||
|
|
||||||
;
|
|
||||||
; Log portal configurations
|
|
||||||
;
|
|
||||||
; Uncomment sections to enable them
|
|
||||||
;
|
|
||||||
|
|
||||||
; Custom REST logging portal
|
|
||||||
; ----------------------------------------------
|
|
||||||
[custlog]
|
|
||||||
;id=example_device_id
|
|
||||||
;url=https://api.mygclog.com/log
|
|
||||||
;type=GET
|
|
||||||
;param_id=id
|
|
||||||
;param_cpm=cpm
|
|
||||||
;param_temp=temp
|
|
||||||
;param_lng=lng
|
|
||||||
;param_lat=lat
|
|
||||||
;param_loc=loc
|
|
||||||
;param_version=version
|
|
||||||
;param_time=captured_at
|
|
||||||
|
|
||||||
; Log to CSV file
|
|
||||||
; ----------------------------------------------
|
|
||||||
[csv]
|
|
||||||
;path=/tmp/gclog.csv
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Christoph Haas
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the standard MIT license. See LICENSE for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _GCLOGGER_H_
|
|
||||||
#define _GCLOGGER_H_
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
#include "ini.h"
|
|
||||||
#include "gmc.h"
|
|
||||||
|
|
||||||
#define GCLOGGER_VERSION "0.1"
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
bool debug;
|
|
||||||
char* dev_port;
|
|
||||||
int dev_baud;
|
|
||||||
char* dev_location;
|
|
||||||
float dev_latitude;
|
|
||||||
float dev_longitude;
|
|
||||||
int dev_interval;
|
|
||||||
|
|
||||||
char* radmon_user;
|
|
||||||
char* radmon_pass;
|
|
||||||
char* safecast_key;
|
|
||||||
char* safecast_device;
|
|
||||||
char* netc_id;
|
|
||||||
char* custlog_url;
|
|
||||||
char* custlog_type;
|
|
||||||
char* custlog_id;
|
|
||||||
char* custlog_param_id;
|
|
||||||
char* custlog_param_cpm;
|
|
||||||
char* custlog_param_temp;
|
|
||||||
char* custlog_param_lng;
|
|
||||||
char* custlog_param_lat;
|
|
||||||
char* custlog_param_loc;
|
|
||||||
char* custlog_param_version;
|
|
||||||
char* custlog_param_time;
|
|
||||||
char* csv_path;
|
|
||||||
} configuration;
|
|
||||||
|
|
||||||
static int confighandler(void* config, const char* section, const char* name, const char* value);
|
|
||||||
bool str_isset(char *str);
|
|
||||||
void init_configuration(configuration* config);
|
|
||||||
void signal_handler(int sig);
|
|
||||||
bool send_custlog(const configuration config, int cpm, float temperature, const char *version, struct tm *tm);
|
|
||||||
bool send_tocsv(const configuration config, int cpm, float temperature, const char *version, const struct tm *tm);
|
|
||||||
void show_usage();
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Christoph Haas
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the standard MIT license. See LICENSE for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _GC_GMC_H_
|
|
||||||
#define _GC_GMC_H_
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
// GQ Geiger Counter Communication Protocol: http://www.gqelectronicsllc.com/download/GQ-RFC1201.txt
|
|
||||||
|
|
||||||
int gmc_open(const char *device, int baud);
|
|
||||||
void gmc_close(int device);
|
|
||||||
|
|
||||||
int gmc_get_cpm(int device);
|
|
||||||
float gmc_get_temperature(int device);
|
|
||||||
int gmc_get_version(int device, char *buf);
|
|
||||||
|
|
||||||
bool gmc_set_heartbeat_off(int device);
|
|
||||||
|
|
||||||
int gmc_write(int device, const char *cmd);
|
|
||||||
int gmc_read(int device, char *buf, int length);
|
|
||||||
bool gmc_flush(int device);
|
|
||||||
|
|
||||||
#endif
|
|
130
include/ini.h
130
include/ini.h
@ -1,130 +0,0 @@
|
|||||||
/* 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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __INI_H__
|
|
||||||
#define __INI_H__
|
|
||||||
|
|
||||||
/* Make this header file easier to include in C++ code */
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
|
||||||
#ifndef INI_HANDLER_LINENO
|
|
||||||
#define INI_HANDLER_LINENO 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Typedef for prototype of handler function. */
|
|
||||||
#if INI_HANDLER_LINENO
|
|
||||||
typedef int (*ini_handler)(void* user, const char* section,
|
|
||||||
const char* name, const char* value,
|
|
||||||
int lineno);
|
|
||||||
#else
|
|
||||||
typedef int (*ini_handler)(void* user, const char* section,
|
|
||||||
const char* name, const char* value);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Typedef for prototype of fgets-style reader function. */
|
|
||||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
|
||||||
|
|
||||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
|
||||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
|
||||||
is "" if name=value pair parsed before any section heading. name:value
|
|
||||||
pairs are also supported as a concession to Python's configparser.
|
|
||||||
|
|
||||||
For each name=value pair parsed, call handler function with given user
|
|
||||||
pointer as well as section, name, and value (data only valid for duration
|
|
||||||
of handler call). Handler should return nonzero on success, zero on error.
|
|
||||||
|
|
||||||
Returns 0 on success, line number of first error on parse error (doesn't
|
|
||||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
|
||||||
error (only when INI_USE_STACK is zero).
|
|
||||||
*/
|
|
||||||
int ini_parse(const char* filename, ini_handler handler, void* user);
|
|
||||||
|
|
||||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
|
||||||
close the file when it's finished -- the caller must do that. */
|
|
||||||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
|
||||||
|
|
||||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
|
||||||
filename. Used for implementing custom or string-based I/O (see also
|
|
||||||
ini_parse_string). */
|
|
||||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
|
||||||
void* user);
|
|
||||||
|
|
||||||
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
|
||||||
instead of a file. Useful for parsing INI data from a network socket or
|
|
||||||
already in memory. */
|
|
||||||
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
|
||||||
|
|
||||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
|
||||||
configparser. If allowed, ini_parse() will call the handler with the same
|
|
||||||
name for each subsequent line parsed. */
|
|
||||||
#ifndef INI_ALLOW_MULTILINE
|
|
||||||
#define INI_ALLOW_MULTILINE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
|
||||||
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
|
|
||||||
#ifndef INI_ALLOW_BOM
|
|
||||||
#define INI_ALLOW_BOM 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
|
||||||
both ; and # comments at the start of a line by default. */
|
|
||||||
#ifndef INI_START_COMMENT_PREFIXES
|
|
||||||
#define INI_START_COMMENT_PREFIXES ";#"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
|
||||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
|
||||||
Python 3.2+ configparser behaviour. */
|
|
||||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
|
||||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
|
||||||
#endif
|
|
||||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
|
||||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
|
||||||
#ifndef INI_USE_STACK
|
|
||||||
#define INI_USE_STACK 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Maximum line length for any line in INI file (stack or heap). Note that
|
|
||||||
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
|
||||||
#ifndef INI_MAX_LINE
|
|
||||||
#define INI_MAX_LINE 200
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
|
||||||
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
|
||||||
zero. */
|
|
||||||
#ifndef INI_ALLOW_REALLOC
|
|
||||||
#define INI_ALLOW_REALLOC 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
|
||||||
is zero. */
|
|
||||||
#ifndef INI_INITIAL_ALLOC
|
|
||||||
#define INI_INITIAL_ALLOC 200
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Stop parsing on first error (default is to keep parsing). */
|
|
||||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
|
||||||
#define INI_STOP_ON_FIRST_ERROR 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __INI_H__ */
|
|
4
obj/.gitignore
vendored
4
obj/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
# Ignore everything in this directory
|
|
||||||
*
|
|
||||||
# Except this file
|
|
||||||
!.gitignore
|
|
349
src/gclogger.c
349
src/gclogger.c
@ -1,349 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Christoph Haas
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the standard MIT license. See LICENSE for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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", "type")) {
|
|
||||||
pconfig->custlog_type = 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_type = "GET";
|
|
||||||
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 = curl_easy_init();
|
|
||||||
|
|
||||||
if(curl) {
|
|
||||||
char *url_buffer = NULL;
|
|
||||||
size_t url_size;
|
|
||||||
char *post_buffer = NULL;
|
|
||||||
size_t post_size;
|
|
||||||
|
|
||||||
if(strncmp(config.custlog_type, "GET", 3) == 0) {
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
if(config.debug) {
|
|
||||||
printf("Final url: %s\n", url_buffer);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
url_buffer = strdup(config.custlog_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url_buffer);
|
|
||||||
|
|
||||||
if(strncmp(config.custlog_type, "POST", 4) == 0) {
|
|
||||||
// first get size of final url
|
|
||||||
post_size = snprintf(NULL, 0, "%s=%s&%s=%d&%s=%f&%s=%s&%s=%ld&%s=%f&%s=%f&%s=%s",
|
|
||||||
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
|
|
||||||
post_buffer = (char *)malloc(post_size + 1);
|
|
||||||
snprintf(post_buffer, post_size+1,"%s=%s&%s=%d&%s=%f&%s=%s&%s=%ld&%s=%f&%s=%f&%s=%s",
|
|
||||||
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);
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_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);
|
|
||||||
if(post_buffer != NULL)
|
|
||||||
free(post_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);
|
|
||||||
|
|
||||||
// setup curl
|
|
||||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
109
src/gmc.c
109
src/gmc.c
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Christoph Haas
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the standard MIT license. See LICENSE for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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
269
src/ini.c
@ -1,269 +0,0 @@
|
|||||||
/* 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);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user