dunst/src/option_parser.c
Benedikt Heine f4fb95c827 Do not make explicit NULL checks on elements
Most of the NULL checks are actually doubly false logic. Turning the
logic around and removing the NULL check makes the program easier to
read.
2018-07-10 13:23:12 +02:00

570 lines
16 KiB
C

/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
#include "option_parser.h"
#include <glib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dunst.h"
#include "log.h"
#include "utils.h"
typedef struct _entry_t {
char *key;
char *value;
} entry_t;
typedef struct _section_t {
char *name;
int entry_count;
entry_t *entries;
} section_t;
static int section_count = 0;
static section_t *sections;
static section_t *new_section(const char *name);
static section_t *get_section(const char *name);
static void add_entry(const char *section_name, const char *key, const char *value);
static const char *get_value(const char *section, const char *key);
static char *clean_value(const char *value);
static int cmdline_argc;
static char **cmdline_argv;
static char *usage_str = NULL;
static void cmdline_usage_append(const char *key, const char *type, const char *description);
static int cmdline_find_option(const char *key);
section_t *new_section(const char *name)
{
for (int i = 0; i < section_count; i++) {
if (!strcmp(name, sections[i].name)) {
DIE("Duplicated section in dunstrc detected.");
}
}
section_count++;
sections = g_realloc(sections, sizeof(section_t) * section_count);
sections[section_count - 1].name = g_strdup(name);
sections[section_count - 1].entries = NULL;
sections[section_count - 1].entry_count = 0;
return &sections[section_count - 1];
}
void free_ini(void)
{
for (int i = 0; i < section_count; i++) {
for (int j = 0; j < sections[i].entry_count; j++) {
g_free(sections[i].entries[j].key);
g_free(sections[i].entries[j].value);
}
g_free(sections[i].entries);
g_free(sections[i].name);
}
g_clear_pointer(&sections, g_free);
section_count = 0;
}
section_t *get_section(const char *name)
{
for (int i = 0; i < section_count; i++) {
if (strcmp(sections[i].name, name) == 0)
return &sections[i];
}
return NULL;
}
void add_entry(const char *section_name, const char *key, const char *value)
{
section_t *s = get_section(section_name);
if (!s)
s = new_section(section_name);
s->entry_count++;
int len = s->entry_count;
s->entries = g_realloc(s->entries, sizeof(entry_t) * len);
s->entries[s->entry_count - 1].key = g_strdup(key);
s->entries[s->entry_count - 1].value = clean_value(value);
}
const char *get_value(const char *section, const char *key)
{
section_t *s = get_section(section);
if (!s) {
return NULL;
}
for (int i = 0; i < s->entry_count; i++) {
if (strcmp(s->entries[i].key, key) == 0) {
return s->entries[i].value;
}
}
return NULL;
}
char *ini_get_path(const char *section, const char *key, const char *def)
{
return string_to_path(ini_get_string(section, key, def));
}
char *ini_get_string(const char *section, const char *key, const char *def)
{
const char *value = get_value(section, key);
if (value)
return g_strdup(value);
return def ? g_strdup(def) : NULL;
}
gint64 ini_get_time(const char *section, const char *key, gint64 def)
{
const char *timestring = get_value(section, key);
gint64 val = def;
if (timestring) {
val = string_to_time(timestring);
}
return val;
}
int ini_get_int(const char *section, const char *key, int def)
{
const char *value = get_value(section, key);
if (value)
return atoi(value);
else
return def;
}
double ini_get_double(const char *section, const char *key, double def)
{
const char *value = get_value(section, key);
if (value)
return atof(value);
else
return def;
}
bool ini_is_set(const char *ini_section, const char *ini_key)
{
return get_value(ini_section, ini_key) != NULL;
}
const char *next_section(const char *section)
{
if (section_count == 0)
return NULL;
if (!section)
return sections[0].name;
for (int i = 0; i < section_count; i++) {
if (strcmp(section, sections[i].name) == 0) {
if (i + 1 >= section_count)
return NULL;
else
return sections[i + 1].name;
}
}
return NULL;
}
int ini_get_bool(const char *section, const char *key, int def)
{
const char *value = get_value(section, key);
if (value) {
switch (value[0]) {
case 'y':
case 'Y':
case 't':
case 'T':
case '1':
return true;
case 'n':
case 'N':
case 'f':
case 'F':
case '0':
return false;
default:
return def;
}
} else {
return def;
}
}
char *clean_value(const char *value)
{
char *s;
if (value[0] == '"')
s = g_strdup(value + 1);
else
s = g_strdup(value);
if (s[strlen(s) - 1] == '"')
s[strlen(s) - 1] = '\0';
return s;
}
int load_ini_file(FILE *fp)
{
if (!fp)
return 1;
char *line = NULL;
size_t line_len = 0;
int line_num = 0;
char *current_section = NULL;
while (getline(&line, &line_len, fp) != -1) {
line_num++;
char *start = g_strstrip(line);
if (*start == ';' || *start == '#' || strlen(start) == 0)
continue;
if (*start == '[') {
char *end = strchr(start + 1, ']');
if (!end) {
LOG_W("Invalid config file at line %d: Missing ']'.", line_num);
continue;
}
*end = '\0';
g_free(current_section);
current_section = (g_strdup(start + 1));
new_section(current_section);
continue;
}
char *equal = strchr(start + 1, '=');
if (!equal) {
LOG_W("Invalid config file at line %d: Missing '='.", line_num);
continue;
}
*equal = '\0';
char *key = g_strstrip(start);
char *value = g_strstrip(equal + 1);
char *quote = strchr(value, '"');
if (quote) {
char *closing_quote = strchr(quote + 1, '"');
if (!closing_quote) {
LOG_W("Invalid config file at line %d: Missing '\"'.", line_num);
continue;
}
} else {
char *comment = strpbrk(value, "#;");
if (comment)
*comment = '\0';
}
value = g_strstrip(value);
if (!current_section) {
LOG_W("Invalid config file at line %d: Key value pair without a section.", line_num);
continue;
}
add_entry(current_section, key, value);
}
free(line);
g_free(current_section);
return 0;
}
void cmdline_load(int argc, char *argv[])
{
cmdline_argc = argc;
cmdline_argv = argv;
}
int cmdline_find_option(const char *key)
{
if (!key) {
return -1;
}
char *key1 = g_strdup(key);
char *key2 = strchr(key1, '/');
if (key2) {
*key2 = '\0';
key2++;
}
/* look for first key */
for (int i = 0; i < cmdline_argc; i++) {
if (strcmp(key1, cmdline_argv[i]) == 0) {
g_free(key1);
return i;
}
}
/* look for second key if one was specified */
if (key2) {
for (int i = 0; i < cmdline_argc; i++) {
if (strcmp(key2, cmdline_argv[i]) == 0) {
g_free(key1);
return i;
}
}
}
g_free(key1);
return -1;
}
static const char *cmdline_get_value(const char *key)
{
int idx = cmdline_find_option(key);
if (idx < 0) {
return NULL;
}
if (idx + 1 >= cmdline_argc) {
/* the argument is missing */
LOG_W("%s: Missing argument. Ignoring.", key);
return NULL;
}
return cmdline_argv[idx + 1];
}
char *cmdline_get_string(const char *key, const char *def, const char *description)
{
cmdline_usage_append(key, "string", description);
const char *str = cmdline_get_value(key);
if (str)
return g_strdup(str);
if (def)
return g_strdup(def);
else
return NULL;
}
char *cmdline_get_path(const char *key, const char *def, const char *description)
{
cmdline_usage_append(key, "string", description);
const char *str = cmdline_get_value(key);
if (str)
return string_to_path(g_strdup(str));
else
return string_to_path(g_strdup(def));
}
gint64 cmdline_get_time(const char *key, gint64 def, const char *description)
{
cmdline_usage_append(key, "time", description);
const char *timestring = cmdline_get_value(key);
gint64 val = def;
if (timestring) {
val = string_to_time(timestring);
}
return val;
}
int cmdline_get_int(const char *key, int def, const char *description)
{
cmdline_usage_append(key, "int", description);
const char *str = cmdline_get_value(key);
if (str)
return atoi(str);
else
return def;
}
double cmdline_get_double(const char *key, double def, const char *description)
{
cmdline_usage_append(key, "double", description);
const char *str = cmdline_get_value(key);
if (str)
return atof(str);
else
return def;
}
int cmdline_get_bool(const char *key, int def, const char *description)
{
cmdline_usage_append(key, "", description);
int idx = cmdline_find_option(key);
if (idx > 0)
return true;
else
return def;
}
bool cmdline_is_set(const char *key)
{
return cmdline_get_value(key) != NULL;
}
char *option_get_path(const char *ini_section,
const char *ini_key,
const char *cmdline_key,
const char *def,
const char *description)
{
char *val = NULL;
if (cmdline_key) {
val = cmdline_get_path(cmdline_key, NULL, description);
}
if (val) {
return val;
} else {
return ini_get_path(ini_section, ini_key, def);
}
}
char *option_get_string(const char *ini_section,
const char *ini_key,
const char *cmdline_key,
const char *def,
const char *description)
{
char *val = NULL;
if (cmdline_key) {
val = cmdline_get_string(cmdline_key, NULL, description);
}
if (val) {
return val;
} else {
return ini_get_string(ini_section, ini_key, def);
}
}
gint64 option_get_time(const char *ini_section,
const char *ini_key,
const char *cmdline_key,
gint64 def,
const char *description)
{
gint64 ini_val = ini_get_time(ini_section, ini_key, def);
return cmdline_get_time(cmdline_key, ini_val, description);
}
int option_get_int(const char *ini_section,
const char *ini_key,
const char *cmdline_key,
int def,
const char *description)
{
/* *str is only used to check wether the cmdline option is actually set. */
const char *str = cmdline_get_value(cmdline_key);
/* we call cmdline_get_int even when the option isn't set in order to
* add the usage info */
int val = cmdline_get_int(cmdline_key, def, description);
if (!str)
return ini_get_int(ini_section, ini_key, def);
else
return val;
}
double option_get_double(const char *ini_section,
const char *ini_key,
const char *cmdline_key,
double def,
const char *description)
{
const char *str = cmdline_get_value(cmdline_key);
double val = cmdline_get_double(cmdline_key, def, description);
if (!str)
return ini_get_double(ini_section, ini_key, def);
else
return val;
}
int option_get_bool(const char *ini_section,
const char *ini_key,
const char *cmdline_key,
int def,
const char *description)
{
int val = false;
if (cmdline_key)
val = cmdline_get_bool(cmdline_key, false, description);
if (cmdline_key && val) {
/* this can only be true if the value has been set,
* so we can return */
return true;
}
return ini_get_bool(ini_section, ini_key, def);
}
void cmdline_usage_append(const char *key, const char *type, const char *description)
{
char *key_type;
if (type && strlen(type) > 0)
key_type = g_strdup_printf("%s (%s)", key, type);
else
key_type = g_strdup(key);
if (!usage_str) {
usage_str =
g_strdup_printf("%-40s - %s\n", key_type, description);
g_free(key_type);
return;
}
char *tmp;
tmp =
g_strdup_printf("%s%-40s - %s\n", usage_str, key_type, description);
g_free(key_type);
g_free(usage_str);
usage_str = tmp;
}
const char *cmdline_create_usage(void)
{
return usage_str;
}
/* see option_parser.h */
enum behavior_fullscreen parse_enum_fullscreen(const char *string, enum behavior_fullscreen def)
{
if (!string)
return def;
if (strcmp(string, "show") == 0)
return FS_SHOW;
else if (strcmp(string, "delay") == 0)
return FS_DELAY;
else if (strcmp(string, "pushback") == 0)
return FS_PUSHBACK;
else {
LOG_W("Unknown fullscreen value: '%s'\n", string);
return def;
}
}
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */