From 92f40a30a9403c4371c06cbc6baa54dd2965012b Mon Sep 17 00:00:00 2001 From: nick Date: Sat, 19 Apr 2008 19:25:31 +0000 Subject: [PATCH] xcowsay config file git-svn-id: http://svn.nickg.me.uk/work/xcowsay@349 a97b1542-0b21-0410-a459-e47997c36f34 --- src/Makefile.am | 2 +- src/config_file.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++ src/config_file.h | 23 ++++++ src/xcowsay.c | 3 + xcowsay.6 | 17 ++++ 5 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/config_file.c create mode 100644 src/config_file.h diff --git a/src/Makefile.am b/src/Makefile.am index aa82402..caa469c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,6 +6,6 @@ LDADD = $(XCOWSAY_LIBS) xcowsay_SOURCES = xcowsay.c display_cow.c display_cow.h floating_shape.h \ floating_shape.c settings.h settings.c Cowsay_glue.h xcowsayd.h \ - xcowsayd.c + xcowsayd.c config_file.h config_file.c EXTRA_DIST = xcowat xcowfortune \ No newline at end of file diff --git a/src/config_file.c b/src/config_file.c new file mode 100644 index 0000000..ed1429d --- /dev/null +++ b/src/config_file.c @@ -0,0 +1,194 @@ +/* config_file.c -- Config file parser. + * Copyright (C) 2008 Nick Gasson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "config_file.h" +#include "settings.h" + +typedef enum { tEOF, tTOKEN, tNEWLINE, tEQUALS } token_t; +static const char* tok_names[] = { "EOF", "token", "newline", "'='" }; + +typedef struct { + char *buf; + int len, off; +} strbuf_t; + +static strbuf_t *make_str_buf(int len) +{ + strbuf_t *p = (strbuf_t*)malloc(sizeof(strbuf_t)); + p->len = len; + p->off = 0; + p->buf = malloc(len); + assert(p->buf); + return p; +} + +static void free_str_buf(strbuf_t *sbuf) +{ + free(sbuf->buf); + free(sbuf); +} + +static void push_char(strbuf_t *sbuf, char ch) +{ + if (sbuf->off + 1 == sbuf->len) + return; // Silently truncate too long tokens :S + else { + sbuf->buf[sbuf->off++] = ch; + sbuf->buf[sbuf->off] = '\0'; + } +} + +#define clear_buf(sbuf) sbuf->off = 0 +#define has_chars(sbuf) (sbuf->off > 0) +#define string(sbuf) (sbuf->buf) + +static token_t next_token(FILE *f, strbuf_t* sbuf, int *lineno, jmp_buf *escape) +{ + clear_buf(sbuf); + bool skip_to_eol = false; + for (;;) { + char next = fgetc(f); + if (EOF == next) + return (has_chars(sbuf) && !skip_to_eol) ? tTOKEN : tEOF; + else if ('\n' == next) { + skip_to_eol = false; + if (has_chars(sbuf)) { + ungetc('\n', f); + return tTOKEN; + } + else { + (*lineno)++; + return tNEWLINE; + } + } + else if (!skip_to_eol) { + if (isspace(next)) { + if (has_chars(sbuf)) + return tTOKEN; + } + else if ('=' == next) + return tEQUALS; + else if ('#' == next) + skip_to_eol = true; + else if (isalpha(next) || isdigit(next) || '_' == next) + push_char(sbuf, next); + else { + fprintf(stderr, "Illegal character in xcowsayrc: %c\n", next); + longjmp(*escape, 3); + } + } + } +} + +static void expect(token_t want, token_t got, bool allow_eof, + int lineno, jmp_buf *escape) +{ + if (tEOF == got && allow_eof) + longjmp(*escape, 1); + else if (want != got) { + fprintf(stderr, "xcowsayrc: line %d: Expected %s but found %s\n", + lineno, tok_names[want], tok_names[got]); + longjmp(*escape, 2); + } +} + +static bool is_int_option(const char *s, int *ival) +{ + const char *p = s; + while (*p) { + if (isdigit(*p)) + ++p; + else + return false; + } + *ival = atoi(s); + return true; +} + +static bool is_bool_option(const char *s, bool *bval) +{ + if (strcasecmp(s, "true") == 0) { + *bval = true; + return true; + } + else if (strcasecmp(s, "false") == 0) { + *bval = false; + return true; + } + else + return false; +} + +void parse_config_file(void) +{ + char fname[FILENAME_MAX]; + const char *home = getenv("HOME"); + if (NULL == home) + return; + + snprintf(fname, FILENAME_MAX, "%s/.xcowsayrc", home); + + FILE *frc = fopen(fname, "r"); + if (NULL == frc) + return; + + const int MAX_TOKEN = 256; + strbuf_t *opt_buf = make_str_buf(MAX_TOKEN); + strbuf_t *val_buf = make_str_buf(MAX_TOKEN); + strbuf_t *dummy_buf = make_str_buf(0); + + jmp_buf escape; + if (setjmp(escape) == 0) { + token_t tok; + int lineno = 1; + for (;;) { + tok = next_token(frc, opt_buf, &lineno, &escape); + expect(tTOKEN, tok, true, lineno, &escape); + + tok = next_token(frc, dummy_buf, &lineno, &escape); + expect(tEQUALS, tok, false, lineno, &escape); + + tok = next_token(frc, val_buf, &lineno, &escape); + expect(tTOKEN, tok, false, lineno, &escape); + + int ival; + bool bval; + if (is_int_option(string(val_buf), &ival)) + set_int_option(string(opt_buf), ival); + else if (is_bool_option(string(val_buf), &bval)) + set_bool_option(string(opt_buf), bval); + else + set_string_option(string(opt_buf), string(val_buf)); + + tok = next_token(frc, dummy_buf, &lineno, &escape); + expect(tNEWLINE, tok, true, lineno, &escape); + } + } + + free_str_buf(opt_buf); + free_str_buf(val_buf); + free_str_buf(dummy_buf); + + fclose(frc); +} diff --git a/src/config_file.h b/src/config_file.h new file mode 100644 index 0000000..1e715e7 --- /dev/null +++ b/src/config_file.h @@ -0,0 +1,23 @@ +/* config_file.h -- Config file parser. + * Copyright (C) 2008 Nick Gasson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef INC_CONFIGFILE_H +#define INC_CONFIGFILE_H + +void parse_config_file(); + +#endif diff --git a/src/xcowsay.c b/src/xcowsay.c index fc2abeb..7b2bce1 100644 --- a/src/xcowsay.c +++ b/src/xcowsay.c @@ -25,6 +25,7 @@ #include "display_cow.h" #include "settings.h" #include "xcowsayd.h" +#include "config_file.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -156,6 +157,8 @@ int main(int argc, char **argv) add_string_option("font", DEF_FONT); add_string_option("cow_size", DEF_COW_SIZE); add_string_option("image_base", DEF_IMAGE_BASE); + + parse_config_file(); int c, index = 0, failure = 0; const char *spec = "hvdrt:f:"; diff --git a/xcowsay.6 b/xcowsay.6 index afc9804..6f8712c 100644 --- a/xcowsay.6 +++ b/xcowsay.6 @@ -13,6 +13,7 @@ xcowsay \- Display a cute cow and speech bubble. .RB [ "-f" .RI \| font \|] .RI [\| text \|]... + .SH DESCRIPTION Display a cow with a speech bubble containing some text. If .I text @@ -43,6 +44,22 @@ starts it checks to see if a daemon is running, and if it is, sends a request and returns immediately. Otherwise .B xcowsay will block until the cow has disappeared. + +.SH CONFIGURATION FILE +xcowsay reads a configuration file on startup. Currently the file must +be named .xcowsayrc in your home directory, a system-wide config file +might be added in a later release. + +The configuration file consists of 'option = value' pairs, one per line. The +valid keys are given in the next section. For example, the following line sets +display time to 10 seconds: +.PP +.RS +display_time = 10000 +.RE +.PP +The character '#' begins a comment which lasts until the end of the line. + .SH OPTIONS Note that these options override any settings in the config file. .TP -- 2.39.2