Merge branch 'master' into progress_hint

This commit is contained in:
Sascha Kruse 2012-10-08 19:04:44 +02:00
commit eaf0bc8308
9 changed files with 450 additions and 480 deletions

View File

@ -3,7 +3,7 @@
include config.mk
SRC = draw.c dunst.c list.c dunst_dbus.c ini.c utils.c
SRC = draw.c dunst.c list.c dunst_dbus.c utils.c options.c
OBJ = ${SRC:.c=.o}
all: doc options dunst service
@ -24,9 +24,9 @@ config.h:
@echo creating $@ from config.def.h
@cp config.def.h $@
dunst: draw.o dunst.o list.o dunst_dbus.o ini.o utils.o
dunst: draw.o dunst.o list.o dunst_dbus.o utils.o options.o
@echo CC -o $@
@${CC} ${CFLAGS} -o $@ dunst.o draw.o list.o dunst_dbus.o ini.o utils.o ${LDFLAGS}
@${CC} ${CFLAGS} -o $@ dunst.o draw.o list.o dunst_dbus.o options.o utils.o ${LDFLAGS}
clean:
@echo cleaning

378
dunst.c
View File

@ -27,9 +27,12 @@
#include "draw.h"
#include "dunst_dbus.h"
#include "list.h"
#include "ini.h"
#include "utils.h"
#ifndef STATIC_CONFIG
#include "options.h"
#endif
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@ -185,12 +188,12 @@ void warn(const char *text, int urg)
if (n == NULL)
die("Unable to allocate memory", EXIT_FAILURE);
n->appname = "dunst";
n->appname = strdup("dunst");
n->summary = strdup(text);
if (n->summary == NULL)
die("Unable to allocate memory", EXIT_FAILURE);
n->body = "";
n->icon = "";
n->body = strdup("");
n->icon = strdup("");
n->timeout = 0;
n->urgency = urg;
n->progress = 0;
@ -339,7 +342,12 @@ void update_lists()
}
to_move = most_important(notification_queue);
if (!to_move) {
return;
}
n = (notification *) to_move->data;
if (!n)
return;
n->start = now;
/* TODO get notifications pushed back into
@ -358,7 +366,7 @@ void update_lists()
int do_word_wrap(char *source, int max_width)
{
strtrim_end(source);
rstrip(source);
if (!source || strlen(source) == 0)
return 0;
@ -403,39 +411,30 @@ int do_word_wrap(char *source, int max_width)
void update_draw_txt_buf(notification * n, int max_width)
{
strtrim_end(n->msg);
rstrip(n->msg);
char *msg = n->msg;
while(isspace(*msg))
msg++;
if (!n->draw_txt_buf.txt) {
int dup_len = strlen(" () ") + digit_count(INT_MAX);
int msg_len = strlen(msg) + 2; /* 2 == surrounding spaces */
int age_len = strlen(" ( h 60m 60s old ) ")
+ digit_count(INT_MAX); /* could be INT_MAX hours */
int line_length = dup_len + msg_len + age_len;
if (n->draw_txt_buf.txt)
free(n->draw_txt_buf.txt);
line_length += sizeof(" ( more ) ") + digit_count(INT_MAX);
n->draw_txt_buf.txt = calloc(line_length, sizeof(char));
n->draw_txt_buf.bufsize = line_length;
}
char *buf = n->draw_txt_buf.txt;
int bufsize = n->draw_txt_buf.bufsize;
char *next = buf;
memset(buf, '\0', bufsize);
char *buf;
/* print dup_count */
if (n->dup_count > 0) {
snprintf(next, bufsize - strlen(buf), "(%d) ", n->dup_count);
next = buf + strlen(buf);
asprintf(&buf, "(%d)", n->dup_count);
} else {
buf = strdup("");
}
/* print msg */
strncat(buf, msg, bufsize - strlen(buf));
next = buf + strlen(buf);
{
char *new_buf;
asprintf(&new_buf, "%s %s", buf, msg);
free(buf);
buf = new_buf;
}
/* print age */
int hours, minutes, seconds;
@ -446,20 +445,22 @@ void update_draw_txt_buf(notification * n, int max_width)
minutes = t_delta / 60 % 60;
seconds = t_delta % 60;
char *new_buf;
if (hours > 0) {
snprintf(next, bufsize - strlen(buf),
" (%dh %dm %ds old)", hours, minutes, seconds);
asprintf(&new_buf, "%s (%dh %dm %ds old)", buf, hours,
minutes, seconds);
} else if (minutes > 0) {
snprintf(next, bufsize - strlen(buf), " (%dm %ds old)",
minutes, seconds);
asprintf(&new_buf, "%s (%dm %ds old)", buf, minutes, seconds);
} else {
snprintf(next, bufsize - strlen(buf), " (%ds old)",
seconds);
asprintf(&new_buf, "%s (%ds old)", buf, seconds);
}
free(buf);
buf = new_buf;
}
n->draw_txt_buf.line_count =
do_word_wrap(n->draw_txt_buf.txt, max_width);
n->draw_txt_buf.line_count = do_word_wrap(buf, max_width);
n->draw_txt_buf.txt = buf;
}
char *draw_txt_get_line(draw_txt * dt, int line)
@ -1076,10 +1077,10 @@ void init_shortcut(keyboard_shortcut * ks)
str++;
*str = '\0';
str++;
strtrim_end(mod);
rstrip(mod);
ks->mask = ks->mask | string_to_mask(mod);
}
strtrim_end(str);
rstrip(str);
ks->sym = XStringToKeysym(str);
/* find matching keycode for ks->sym */
@ -1204,6 +1205,7 @@ int select_screen(XineramaScreenInfo * info, int info_len)
} else {
int x, y;
assert(f_mode == FOLLOW_MOUSE || f_mode == FOLLOW_KEYBOARD);
Window root = RootWindow(dc->dpy, DefaultScreen(dc->dpy));
if (f_mode == FOLLOW_MOUSE) {
@ -1248,10 +1250,9 @@ void update_screen_info()
{
#ifdef XINERAMA
int n;
int screen = scr.scr;
XineramaScreenInfo *info;
if ((info = XineramaQueryScreens(dc->dpy, &n))) {
screen = select_screen(info, n);
int screen = select_screen(info, n);
if (screen >= n) {
/* invalid monitor, fallback to default */
screen = 0;
@ -1486,26 +1487,6 @@ void parse_cmdline(int argc, char *argv[])
}
#ifndef STATIC_CONFIG
static int dunst_ini_get_boolean(const char *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 False;
}
}
static rule_t *dunst_rules_find_or_create(const char *section)
{
l_node *iter;
@ -1527,158 +1508,6 @@ static rule_t *dunst_rules_find_or_create(const char *section)
return rule;
}
static char *dunst_ini_get_string(const char *value)
{
char *s;
if (value[0] == '"')
s = strdup(value + 1);
else
s = strdup(value);
if (s[strlen(s) - 1] == '"')
s[strlen(s) - 1] = '\0';
return s;
}
static int
dunst_ini_handle(void *user_data, const char *section,
const char *name, const char *value)
{
if (strcmp(section, "global") == 0) {
if (strcmp(name, "font") == 0)
font = dunst_ini_get_string(value);
else if (strcmp(name, "format") == 0)
format = dunst_ini_get_string(value);
else if (strcmp(name, "sort") == 0)
sort = dunst_ini_get_boolean(value);
else if (strcmp(name, "indicate_hidden") == 0)
indicate_hidden = dunst_ini_get_boolean(value);
else if (strcmp(name, "word_wrap") == 0)
word_wrap = dunst_ini_get_boolean(value);
else if (strcmp(name, "idle_threshold") == 0)
idle_threshold = atoi(value);
else if (strcmp(name, "monitor") == 0)
scr.scr = atoi(value);
else if (strcmp(name, "follow") == 0) {
char *c = dunst_ini_get_string(value);
parse_follow_mode(c);
free(c);
} else if (strcmp(name, "geometry") == 0)
geom = dunst_ini_get_string(value);
else if (strcmp(name, "line_height") == 0)
line_height = atoi(value);
else if (strcmp(name, "modifier") == 0) {
deprecated_dunstrc_shortcuts = True;
char *c = dunst_ini_get_string(value);
KeySym mod = string_to_mask(c);
free(c);
close_ks.mask = mod;
close_all_ks.mask = mod;
history_ks.mask = mod;
} else if (strcmp(name, "key") == 0) {
close_ks.str = dunst_ini_get_string(value);
} else if (strcmp(name, "all_key") == 0) {
close_all_ks.str = dunst_ini_get_string(value);
} else if (strcmp(name, "history_key") == 0) {
history_ks.str = dunst_ini_get_string(value);
} else if (strcmp(name, "bounce_freq") == 0) {
bounce_freq = atof(value);
} else if (strcmp(name, "alignment") == 0) {
if (strcmp(value, "left") == 0)
align = left;
else if (strcmp(value, "center") == 0)
align = center;
else if (strcmp(value, "right") == 0)
align = right;
/* FIXME warning on unknown alignment */
} else if (strcmp(name, "show_age_threshold") == 0)
show_age_threshold = atoi(value);
else if (strcmp(name, "sticky_history") == 0)
sticky_history = dunst_ini_get_boolean(value);
else if (strcmp(name, "separator_height") == 0)
separator_height = atoi(value);
else if (strcmp(name, "transparency") == 0)
transparency = atoi(value);
if (strcmp(name, "separator_color") == 0) {
char *str = dunst_ini_get_string(value);
if (strcmp(str, "auto") == 0)
sep_color = AUTO;
else if (strcmp(str, "foreground") == 0)
sep_color = FOREGROUND;
else
fprintf(stderr, "Warning: Unknown separator color\n");
free(str);
}
} else if (strcmp(section, "urgency_low") == 0) {
if (strcmp(name, "background") == 0)
lowbgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "foreground") == 0)
lowfgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0)
timeouts[LOW] = atoi(value);
} else if (strcmp(section, "urgency_normal") == 0) {
if (strcmp(name, "background") == 0)
normbgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "foreground") == 0)
normfgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0)
timeouts[NORM] = atoi(value);
} else if (strcmp(section, "urgency_critical") == 0) {
if (strcmp(name, "background") == 0)
critbgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "foreground") == 0)
critfgcolor = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0)
timeouts[CRIT] = atoi(value);
} else if (strcmp(section, "shortcuts") == 0) {
if (strcmp(name, "close") == 0)
close_ks.str = dunst_ini_get_string(value);
else if (strcmp(name, "close_all") == 0)
close_all_ks.str = dunst_ini_get_string(value);
else if (strcmp(name, "history") == 0)
history_ks.str = dunst_ini_get_string(value);
} else {
rule_t *current_rule = dunst_rules_find_or_create(section);
if (strcmp(name, "appname") == 0)
current_rule->appname = dunst_ini_get_string(value);
else if (strcmp(name, "summary") == 0)
current_rule->summary = dunst_ini_get_string(value);
else if (strcmp(name, "body") == 0)
current_rule->body = dunst_ini_get_string(value);
else if (strcmp(name, "icon") == 0)
current_rule->icon = dunst_ini_get_string(value);
else if (strcmp(name, "timeout") == 0)
current_rule->timeout = atoi(value);
else if (strcmp(name, "urgency") == 0) {
const char *urg = value;
if (strcmp(urg, "low") == 0)
current_rule->urgency = LOW;
else if (strcmp(urg, "normal") == 0)
current_rule->urgency = NORM;
else if (strcmp(urg, "critical") == 0)
current_rule->urgency = CRIT;
else
fprintf(stderr,
"unknown urgency: %s, ignoring\n", urg);
} else if (strcmp(name, "foreground") == 0)
current_rule->fg = dunst_ini_get_string(value);
else if (strcmp(name, "background") == 0)
current_rule->bg = dunst_ini_get_string(value);
else if (strcmp(name, "format") == 0)
current_rule->format = dunst_ini_get_string(value);
}
return 1;
}
void parse_dunstrc(char *cmdline_config_path)
{
@ -1704,15 +1533,133 @@ void parse_dunstrc(char *cmdline_config_path)
}
}
if (ini_parse_file(config_file, dunst_ini_handle, NULL) < 0) {
puts("dunstrc could not be parsed -> skipping\n");
load_ini_file(config_file);
font = ini_get_string("global", "font", font);
format = ini_get_string("global", "format", format);
sort = ini_get_bool("global", "sort", sort);
indicate_hidden = ini_get_bool("global", "indicate_hidden", indicate_hidden);
word_wrap = ini_get_bool("global", "word_wrap", word_wrap);
idle_threshold = ini_get_int("global", "idle_threshold", idle_threshold);
monitor = ini_get_int("global", "monitor", monitor);
{
char *c = ini_get_string("global", "follow", "");
if (strlen(c) > 0) {
parse_follow_mode(c);
free(c);
}
}
geom = ini_get_string("global", "geometry", geom);
line_height = ini_get_int("global", "line_height", line_height);
{
char *c = ini_get_string("global", "modifier", "");
if (strlen(c) > 0) {
deprecated_dunstrc_shortcuts = True;
KeySym mod = string_to_mask(c);
close_ks.mask = mod;
close_all_ks.mask = mod;
close_all_ks.mask = mod;
free(c);
}
}
close_ks.str = ini_get_string("global", "key", close_ks.str);
close_all_ks.str = ini_get_string("global", "key", close_all_ks.str);
history_ks.str = ini_get_string("global", "key", history_ks.str);
bounce_freq = ini_get_double("global", "bounce_freq", bounce_freq);
{
char *c = ini_get_string("global", "alignment", "");
if (strlen(c) > 0) {
if (strcmp(c, "left") == 0)
align = left;
else if (strcmp(c, "center") == 0)
align = center;
else if (strcmp(c, "right") == 0)
align = right;
else
fprintf(stderr, "Warning: unknown allignment\n");
free(c);
}
}
show_age_threshold = ini_get_int("global", "show_age_threshold", show_age_threshold);
sticky_history = ini_get_bool("global", "sticky_history", sticky_history);
separator_height = ini_get_int("global", "separator_height", separator_height);
transparency = ini_get_int("global", "transparency", transparency);
{
char *c = ini_get_string("global", "separator_color", "");
if (strlen(c) > 0) {
if (strcmp(c, "auto") == 0)
sep_color = AUTO;
else if (strcmp(c, "foreground") == 0)
sep_color = FOREGROUND;
else
fprintf(stderr, "Warning: Unknown separator color\n");
free(c);
}
}
lowbgcolor = ini_get_string("urgency_low", "background", lowbgcolor);
lowfgcolor = ini_get_string("urgency_low", "foreground", lowfgcolor);
timeouts[LOW] = ini_get_int("urgency_low", "timeout", timeouts[LOW]);
normbgcolor = ini_get_string("urgency_normal", "background", normbgcolor);
normfgcolor = ini_get_string("urgency_normal", "foreground", normfgcolor);
timeouts[NORM] = ini_get_int("urgency_normal", "timeout", timeouts[NORM]);
critbgcolor = ini_get_string("urgency_critical", "background", critbgcolor);
critfgcolor = ini_get_string("urgency_critical", "foreground", critfgcolor);
timeouts[CRIT] = ini_get_int("urgency_critical", "timeout", timeouts[CRIT]);
close_ks.str = ini_get_string("shortcuts", "close", close_ks.str);
close_all_ks.str = ini_get_string("shortcuts", "close_all", close_all_ks.str);
history_ks.str = ini_get_string("shortcuts", "history", history_ks.str);
char *cur_section = NULL;
for (; cur_section; cur_section = next_section(cur_section)) {
if (strcmp(cur_section, "global") == 0
|| strcmp(cur_section, "shortcuts") == 0
|| strcmp(cur_section, "urgency_low") == 0
|| strcmp(cur_section, "urgency_normal") == 0
|| strcmp(cur_section, "urgency_critical") == 0)
continue;
rule_t *current_rule = dunst_rules_find_or_create(cur_section);
current_rule->appname = ini_get_string(
cur_section, "appname", current_rule->appname);
current_rule->summary = ini_get_string(
cur_section, "summary", current_rule->summary);
current_rule->body = ini_get_string(
cur_section, "body", current_rule->body);
current_rule->icon = ini_get_string(
cur_section, "icon", current_rule->icon);
current_rule->timeout = ini_get_int(
cur_section, "timeout", current_rule->timeout);
{
char *urg = ini_get_string(cur_section, "urgency", "");
if (strlen(urg) > 0) {
if (strcmp(urg, "low") == 0)
current_rule->urgency = LOW;
else if (strcmp(urg, "normal") == 0)
current_rule->urgency = NORM;
else if (strcmp(urg, "critical") == 0)
current_rule->urgency = CRIT;
else
fprintf(stderr,
"unknown urgency: %s, ignoring\n", urg);
free(urg);
}
}
current_rule->fg = ini_get_string(
cur_section, "foreground", current_rule->fg);
current_rule->bg = ini_get_string(
cur_section, "background", current_rule->bg);
current_rule->format = ini_get_string(
cur_section, "format", current_rule->format);
}
fclose(config_file);
free_ini();
xdgWipeHandle(&xdg);
}
#endif /* STATIC_CONFIG */
char *parse_cmdline_for_config_file(int argc, char *argv[])
{
@ -1727,6 +1674,7 @@ char *parse_cmdline_for_config_file(int argc, char *argv[])
}
return NULL;
}
#endif /* STATIC_CONFIG */
int main(int argc, char *argv[])
{

View File

@ -33,7 +33,6 @@ typedef struct _screen_info {
typedef struct _draw_txt {
char *txt;
int line_count;
int bufsize;
} draw_txt;
typedef struct _notification {

176
ini.c
View File

@ -1,176 +0,0 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license. Go to the project
home page for more info:
The "inih" library is distributed under the New BSD license:
Copyright (c) 2009, Brush Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Brush Technology nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
http://code.google.com/p/inih/
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#define MAX_LINE 200
#define MAX_SECTION 50
#define MAX_NAME 50
/* 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(*--p))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace(*s))
s++;
return (char*)s;
}
/* Return pointer to first char c or ';' comment in given string, or pointer to
null at end of string if neither found. ';' must be prefixed by a whitespace
character to register as a comment. */
static char* find_char_or_comment(const char* s, char c)
{
int was_whitespace = 0;
while (*s && *s != c && !(was_whitespace && *s == ';')) {
was_whitespace = isspace(*s);
s++;
}
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_file(FILE* file,
int (*handler)(void*, const char*, const char*,
const char*),
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
char line[MAX_LINE];
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
/* Scan through file line by line */
while (fgets(line, sizeof(line), file) != NULL) {
lineno++;
start = lskip(rstrip(line));
if (*start == ';' || *start == '#') {
/* Per Python ConfigParser, allow '#' comments at start of line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-black 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_char_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 && *start != ';') {
/* Not a comment, must be a name[=:]value pair */
end = find_char_or_comment(start, '=');
if (*end != '=') {
end = find_char_or_comment(start, ':');
}
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
end = find_char_or_comment(value, '\0');
if (*end == ';')
*end = '\0';
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;
}
}
}
return error;
}
/* See documentation in header file. */
int ini_parse(const char* filename,
int (*handler)(void*, const char*, const char*, const char*),
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;
}

83
ini.h
View File

@ -1,83 +0,0 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license. Go to the project
home page for more info:
The "inih" library is distributed under the New BSD license:
Copyright (c) 2009, Brush Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Brush Technology nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
http://code.google.com/p/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>
/* 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), or -1 on file open error.
*/
int ini_parse(const char* filename,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
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,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
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
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

254
options.c Normal file
View File

@ -0,0 +1,254 @@
/* copyright 2012 Sascha Kruse and contributors (see LICENSE for licensing information) */
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include "options.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;
static section_t *sections;
static section_t *new_section(char *name);
static section_t *get_section(char *name);
static void add_entry(char *section_name, char *key, char *value);
static char *get_value(char *section, char *key);
static char *clean_value(char *value);
section_t *new_section(char *name)
{
section_count++;
sections = realloc(sections, sizeof(section_t) *section_count);
sections[section_count - 1].name = 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++) {
free(sections[i].entries[j].key);
free(sections[i].entries[j].value);
}
free(sections[i].entries);
free(sections[i].name);
}
free(sections);
}
section_t *get_section(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(char *section_name, char *key, char *value)
{
section_t *s = get_section(section_name);
if (s == NULL) {
s = new_section(section_name);
}
s->entry_count++;
int len = s->entry_count;
s->entries = realloc(s->entries, sizeof(entry_t) * len);
s->entries[s->entry_count - 1].key = strdup(key);
s->entries[s->entry_count - 1].value = clean_value(value);
}
char *get_value(char *section, 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_string(char *section, char *key, const char *def)
{
char *value = get_value(section, key);
if (value == NULL)
return def;
else
return strdup(value);
}
int ini_get_int(char *section, char *key, int def)
{
char *value = get_value(section, key);
if (value == NULL)
return def;
else
return atoi(value);
}
double ini_get_double(char *section, char *key, int def)
{
char *value = get_value(section, key);
if (value == NULL)
return def;
else
return atof(value);
}
char *next_section(char *section)
{
if (section_count == 0)
return NULL;
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(char *section, char *key, int def)
{
char *value = get_value(section, key);
if (value == NULL)
return def;
else {
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 false;
}
}
}
char *clean_value(char *value)
{
char *s;
if (value[0] == '"')
s = strdup(value + 1);
else
s = strdup(value);
if (s[strlen(s) - 1] == '"')
s[strlen(s) - 1] = '\0';
return s;
}
int load_ini_file(FILE *fp)
{
char line[BUFSIZ];
int line_num = 0;
char *current_section = NULL;
while (fgets(line, sizeof(line), fp) != NULL) {
line_num++;
char *start = lskip(rstrip(line));
if (*start == ';' || *start == '#' || strlen(start) == 0)
continue;
if (*start == '[') {
char *end = strstr(start + 1, "]");
if (!end) {
printf("Warning: invalid config file at line %d\n", line_num);
printf("Missing ']'\n");
continue;
}
*end = '\0';
if (current_section)
free(current_section);
current_section = (strdup(start+1));
new_section(current_section);
continue;
}
char *equal = strstr(start + 1, "=");
if (!equal) {
printf("Warning: invalid config file at line %d\n", line_num);
printf("Missing '='\n");
continue;
}
*equal = '\0';
char *key = rstrip(start);
char *value = lskip(equal+1);
char *quote = strstr(value, "\"");
if (quote) {
char *closing_quote = strstr(quote + 1, "\"");
if (!closing_quote) {
printf("Warning: invalid config file at line %d\n", line_num);
printf("Missing '\"'\n");
continue;
}
closing_quote = '\0';
} else {
char *comment = strstr(value, "#");
if (!comment)
comment = strstr(value, ";");
if (comment)
comment = '\0';
}
value = rstrip(value);
if (!current_section) {
printf("Warning: invalid config file at line: %d\n", line_num);
printf("Key value pair without a section\n");
continue;
}
add_entry(current_section, key, value);
}
if (current_section)
free(current_section);
return 0;
}
/* vim: set ts=8 sw=8 tw=0: */

20
options.h Normal file
View File

@ -0,0 +1,20 @@
/* copyright 2012 Sascha Kruse and contributors (see LICENSE for licensing information) */
#pragma once
#include <stdio.h>
int load_ini_file(FILE *);
char *ini_get_string(char *section, char *key, const char *def);
int ini_get_int(char *section, char *key, int def);
double ini_get_double(char *section, char *key, int def);
int ini_get_bool(char *section, char *key, int def);
void free_ini(void);
/* returns the next known section.
* if section == NULL returns first section.
* returns NULL if no more sections are available
*/
char *next_section(char *section);
/* vim: set ts=8 sw=8 tw=0: */

View File

@ -7,7 +7,7 @@
#include "dunst.h"
void strtrim_end(char *str)
char *rstrip(char *str)
{
char *end;
end = str + strlen(str) - 1;
@ -15,6 +15,13 @@ void strtrim_end(char *str)
*end = '\0';
end--;
}
return str;
}
char *lskip(char *s)
{
for(; *s && isspace(*s); s++);
return s;
}
char *string_replace(const char *needle, const char *replacement,

View File

@ -1,7 +1,8 @@
#ifndef UTIL_H
#define UTIL_H
/* remove spaces before and after str */
void strtrim_end(char *str);
char *rstrip(char *str);
char *lskip(char *str);
/* replace needle with replacement in haystack */
char *string_replace(const char *needle, const char *replacement,