Merge commit 'v3.3b1' into diablo-package
authorSteven Luo <steven+maemo@steven676.net>
Sun, 30 May 2010 09:40:58 +0000 (02:40 -0700)
committerSteven Luo <steven+maemo@steven676.net>
Sun, 30 May 2010 09:40:58 +0000 (02:40 -0700)
Changelog
Makefile
README
config-ui/Makefile
config-ui/browser-switchboard-config.c [new file with mode: 0644]
config-ui/browser-switchboard-cp.c
config-ui/save-config.c [new file with mode: 0644]
config.c [new file with mode: 0644]
config.h [new file with mode: 0644]
launcher.c
main.c

index 4eb2902..120ab03 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,13 @@
+version 3.3:
+* add support for Opera Mobile
+* fall back to the built-in default if the user's configured default browser
+  isn't installed
+* introduce a command-line config utility; this is mainly intended to give
+  browser vendors a way to set their browser as the default
+* provide a description for users who don't know what MicroB is; thanks
+  Emanuele Cassioli for pointing out the need
+* major rewrite of configuration loading/saving code
+
 version 3.2:
 * make the "Web" menu entry and /usr/bin/browser open the default browser, and
   provide a new "MicroB" menu entry and /usr/bin/microb script for launching
index 65bb61e..e6e1bb8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ LDFLAGS = -Wl,--as-needed `pkg-config --libs dbus-glib-1` $(EXTRA_LDFLAGS)
 PREFIX = /usr
 
 APP = browser-switchboard
-obj = main.o launcher.o dbus-server-bindings.o configfile.o log.o
+obj = main.o launcher.o dbus-server-bindings.o config.o configfile.o log.o
 
 all:
        @echo 'Usage:'
diff --git a/README b/README
index d7e51d8..2f36e77 100644 (file)
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 BROWSER SWITCHBOARD
-version 3.2
+version 3.3b1
 
 Browser Switchboard is a program which allows you to choose which
 browser to use as the default browser. It supports MicroB, Tear,
@@ -15,7 +15,9 @@ page on garage.maemo.org: https://garage.maemo.org/frs/?group_id=1159
 Quick Start:
 1. Download the binary package: browser-switchboard_X.Y-Z_all.deb
 (where X.Y-Z is the version number, of course).
-2. Install the package using the Application Manager (open the
+2. If you're using a Maemo 5 device, make sure all your MicroB browser
+windows are closed.
+3. Install the package using the Application Manager (open the
 Application Manager, then select Application->Install from file in the
 menu).
 
index 8bc3749..090fc67 100644 (file)
@@ -11,23 +11,27 @@ LDFLAGS_PLUGIN = -shared $(LDFLAGS_HILDON) \
        `pkg-config --libs libosso` `pkg-config --libs hildon-control-panel`
 PREFIX = /usr
 
-other_obj = ../configfile.o
+other_obj = ../configfile.o ../config.o save-config.o
 
 APP = browser-switchboard-cp
+UTIL = browser-switchboard-config
 app_obj = $(APP).app.o $(other_obj)
+util_obj = $(UTIL).o $(other_obj)
 HILDON_APP = $(APP)-hildon
 happ_obj = $(APP).happ.o $(other_obj)
 PLUGIN = lib$(APP).so
-plugin_obj = $(APP).plugin.o ../configfile.plugin.o
+plugin_obj = $(APP).plugin.o ../configfile.plugin.o ../config.plugin.o save-config.plugin.o
 
 all:
        @echo 'Usage:'
        @echo '    make app -- build standalone GTK+ application'
+       @echo '    make util -- build command-line configuration utility'
        @echo '    make diablo-hildon-app -- build standalone Diablo Hildon application'
        @echo '    make diablo-plugin -- build Diablo hildon-control-panel plugin'
        @echo '    make fremantle-hildon-app -- build standalone Fremantle Hildon application'
        @echo '    make fremantle-plugin -- build Fremantle hildon-control-panel plugin'
 app: $(APP)
+util: $(UTIL)
 diablo-hildon-app: $(HILDON_APP)
 diablo-plugin: $(PLUGIN)
 fremantle-hildon-app:
@@ -41,6 +45,9 @@ $(APP): $(app_obj)
 %.app.o: %.c
        $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
 
+$(UTIL): $(util_obj)
+       $(CC) $(CFLAGS) -o $(UTIL) $(util_obj) $(LDFLAGS)
+
 $(HILDON_APP): $(happ_obj)
        $(CC) $(CFLAGS) -o $(HILDON_APP) $(happ_obj) \
                $(LDFLAGS) $(LDFLAGS_HILDON)
@@ -69,6 +76,6 @@ install-plugin: $(PLUGIN)
        install -c -m 0644 $(APP).desktop $(DESTDIR)$(PREFIX)/share/applications/hildon-control-panel
 
 clean:
-       rm -f $(APP) $(HILDON_APP) $(PLUGIN) $(app_obj) $(happ_obj) $(plugin_obj)
+       rm -f $(APP) $(UTIL) $(HILDON_APP) $(PLUGIN) $(app_obj) $(util_obj) $(happ_obj) $(plugin_obj)
 
-.PHONY: strip strip-plugin install install-plugin app diablo-hildon-app diablo-plugin fremantle-hildon-app fremantle-plugin
+.PHONY: strip strip-plugin install install-plugin app util diablo-hildon-app diablo-plugin fremantle-hildon-app fremantle-plugin
diff --git a/config-ui/browser-switchboard-config.c b/config-ui/browser-switchboard-config.c
new file mode 100644 (file)
index 0000000..ec1e509
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * browser-switchboard-config.c -- command-line configuration utility for 
+ * Browser Switchboard
+ * 
+ * Copyright (C) 2009-2010 Steven Luo
+ * 
+ * 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 2, 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include "config.h"
+
+extern struct swb_config_option swb_config_options[];
+
+static int get_config_value(char *name) {
+       struct swb_config cfg;
+       struct swb_config_option *optinfo;
+       ptrdiff_t i;
+       int retval = 1;
+
+       swb_config_init(&cfg);
+
+       if (!swb_config_load(&cfg))
+               return 1;
+
+       for (optinfo = swb_config_options; optinfo->name; ++optinfo) {
+               if (strcmp(name, optinfo->name))
+                       continue;
+
+               i = optinfo - swb_config_options;
+               switch (optinfo->type) {
+                 case SWB_CONFIG_OPT_STRING:
+                       if (*(char **)cfg.entries[i])
+                               printf("%s\n", *(char **)cfg.entries[i]);
+                       break;
+                 case SWB_CONFIG_OPT_INT:
+                       printf("%d\n", *(int *)cfg.entries[i]);
+                       break;
+                 default:
+                       break;
+               }
+               retval = 0;
+               break;
+       }
+
+       swb_config_free(&cfg);
+
+       return retval;
+}
+
+static int set_config_value(char *name, char *value) {
+       struct swb_config cfg;
+       struct swb_config_option *optinfo;
+       ptrdiff_t i;
+       int retval = 1;
+
+       swb_config_init(&cfg);
+
+       if (!swb_config_load(&cfg))
+               return 1;
+
+       for (optinfo = swb_config_options; optinfo->name; ++optinfo) {
+               if (strcmp(name, optinfo->name))
+                       continue;
+
+               i = optinfo - swb_config_options;
+               switch (optinfo->type) {
+                 case SWB_CONFIG_OPT_STRING:
+                       /* Free any existing string */
+                       if (cfg.flags & optinfo->set_mask)
+                               free(*(char **)cfg.entries[i]);
+
+                       if (strlen(value) == 0) {
+                               /* If the new value is empty, clear the config
+                                  setting */
+                               *(char **)cfg.entries[i] = NULL;
+                               cfg.flags &= ~optinfo->set_mask;
+                       } else {
+                               /* Make a copy of the string -- it's not safe
+                                  to free value, which comes from argv */
+                               if (!(*(char **)cfg.entries[i] =
+                                     strdup(value)))
+                                       exit(1);
+                               cfg.flags |= optinfo->set_mask;
+                       }
+                       break;
+                 case SWB_CONFIG_OPT_INT:
+                       if (strlen(value) == 0) {
+                               /* If the new value is empty, clear the config
+                                  setting */
+                               cfg.flags &= ~optinfo->set_mask;
+                       } else {
+                               *(int *)cfg.entries[i] = atoi(value);
+                               cfg.flags |= optinfo->set_mask;
+                       }
+                       break;
+               }
+               retval = 0;
+               break;
+       }
+
+       if (!retval)
+               if (!swb_config_save(&cfg))
+                       retval = 1;
+
+       swb_config_free(&cfg);
+
+       /* Try to send SIGHUP to any running browser-switchboard process
+          This causes it to reread config files if in continuous_mode, or
+          die so that the config will be reloaded on next start otherwise */
+       system("kill -HUP `pidof browser-switchboard` > /dev/null 2>&1");
+
+       return retval;
+}
+
+void usage(void) {
+       printf("Usage:\n");
+       printf("  browser-switchboard-config -b -- Display default browser\n");
+       printf("  browser-switchboard-config -c -- Display command used when default browser is \"other\"\n");
+       printf("  browser-switchboard-config -m -- Display continuous mode setting\n");
+       printf("  browser-switchboard-config -o option -- Display value of option\n");
+       printf("\n");
+       printf("  browser-switchboard-config -s [-b|-c|-m|-o option] value -- Set the selected option to value\n");
+       printf("\n");
+       printf("  browser-switchboard-config -h -- Show this message\n");
+}
+
+int main(int argc, char **argv) {
+       int opt, done = 0;
+       int set = 0;
+       char *selected_opt = NULL;
+       
+       while (!done && (opt = getopt(argc, argv, "hsbcmo:")) != -1) {
+               switch (opt) {
+                 case 'h':
+                       usage();
+                       exit(0);
+                       break;
+                 case 's':
+                       set = 1;
+                       break;
+                 case 'b':
+                       selected_opt = "default_browser";
+                       done = 1;
+                       break;
+                 case 'c':
+                       selected_opt = "other_browser_cmd";
+                       done = 1;
+                       break;
+                 case 'm':
+                       selected_opt = "continuous_mode";
+                       done = 1;
+                       break;
+                 case 'o':
+                       selected_opt = optarg;
+                       done = 1;
+                       break;
+                 default:
+                       usage();
+                       exit(1);
+                       break;
+               }
+       }
+
+       if (!selected_opt) {
+               printf("Must specify one of -b, -c, -m, -o\n");
+               usage();
+               exit(1);
+       }
+
+       if (set) {
+               if (optind >= argc) {
+                       printf("Value to set config option to not provided\n");
+                       usage();
+                       exit(1);
+               }
+               return set_config_value(selected_opt, argv[optind]);
+       } else
+               return get_config_value(selected_opt);
+}
index c34778b..0c768e1 100644 (file)
@@ -51,7 +51,7 @@
 #endif /* HILDON_CP_APPLET */
 #endif /* HILDON */
 
-#include "configfile.h"
+#include "config.h"
 
 #define CONTINUOUS_MODE_DEFAULT 0
 
@@ -64,15 +64,16 @@ struct browser_entry {
        char *displayname;
 };
 struct browser_entry browsers[] = {
-       { "microb", "MicroB" }, /* First entry is the default! */
+       { "microb", "MicroB (stock browser)" }, /* First entry is the default! */
        { "tear", "Tear" },
        { "fennec", "Mobile Firefox (Fennec)" },
+       { "opera", "Opera Mobile" },
        { "midori", "Midori" },
        { "other", "Other" },
        { NULL, NULL },
 };
 
-char *logger_name = NULL;
+struct swb_config orig_cfg;
 
 struct config_widgets {
 #if defined(HILDON) && defined(FREMANTLE)
@@ -151,170 +152,40 @@ static inline void set_other_browser_cmd(char *cmd) {
 }
 
 static void load_config(void) {
-       FILE *fp;
-       int continuous_mode_seen = 0;
-       int default_browser_seen = 0;
-       int other_browser_cmd_seen = 0;
-       struct swb_config_line line;
-
-       if (!(fp = open_config_file()))
-               return;
-
-       /* Parse the config file
-          TODO: should we handle errors differently than EOF? */
-       if (!parse_config_file_begin())
-               goto out;
-       while (!parse_config_file_line(fp, &line)) {
-               if (line.parsed) {
-                       if (!strcmp(line.key, "continuous_mode")) {
-                               if (!continuous_mode_seen) {
-                                       set_continuous_mode(atoi(line.value));
-                                       continuous_mode_seen = 1;
-                               }
-                               free(line.value);
-                       } else if (!strcmp(line.key, "default_browser")) {
-                               if (!default_browser_seen) {
-                                       set_default_browser(line.value);
-                                       default_browser_seen = 1;
-                               }
-                               free(line.value);
-                       } else if (!strcmp(line.key, "other_browser_cmd")) {
-                               if (!other_browser_cmd_seen) {
-                                       set_other_browser_cmd(line.value);
-                                       other_browser_cmd_seen = 1;
-                               }
-                               free(line.value);
-                       } else if (!strcmp(line.key, "logging")) {
-                               if (!logger_name)
-                                       logger_name = line.value;
-                       }
-               }
-               free(line.key);
-       }
-       parse_config_file_end();
-
-out:
-       fclose(fp);
-       return;
+       swb_config_init(&orig_cfg);
+       
+       swb_config_load(&orig_cfg);
+
+       set_continuous_mode(orig_cfg.continuous_mode);
+       set_default_browser(orig_cfg.default_browser);
+       if (orig_cfg.other_browser_cmd)
+               set_other_browser_cmd(orig_cfg.other_browser_cmd);
 }
 
 static void save_config(void) {
-       FILE *fp = NULL, *tmpfp = NULL;
-       char *homedir, *tempfile, *newfile;
-       size_t len;
-       int continuous_mode_seen = 0;
-       int default_browser_seen = 0;
-       int other_browser_cmd_seen = 0;
-       struct swb_config_line line;
-
-       /* If CONFIGFILE_DIR doesn't exist already, try to create it */
-       if (!(homedir = getenv("HOME")))
-               homedir = DEFAULT_HOMEDIR;
-       len = strlen(homedir) + strlen(CONFIGFILE_DIR) + 1;
-       if (!(newfile = calloc(len, sizeof(char))))
-               return;
-       snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_DIR);
-       if (access(newfile, F_OK) == -1 && errno == ENOENT) {
-               mkdir(newfile, 0750);
+       struct swb_config new_cfg;
+
+       swb_config_copy(&new_cfg, &orig_cfg);
+
+       if (get_continuous_mode() != orig_cfg.continuous_mode) {
+               new_cfg.continuous_mode = get_continuous_mode();
+               new_cfg.flags |= SWB_CONFIG_CONTINUOUS_MODE_SET;
        }
-       free(newfile);
-
-       /* Put together the path to the new config file and the tempfile */
-       len = strlen(homedir) + strlen(CONFIGFILE_LOC) + 1;
-       if (!(newfile = calloc(len, sizeof(char))))
-               return;
-       /* 4 = strlen(".tmp") */
-       if (!(tempfile = calloc(len+4, sizeof(char)))) {
-               free(newfile);
-               return;
+       if (strcmp(get_default_browser(), orig_cfg.default_browser)) {
+               new_cfg.default_browser = get_default_browser();
+               new_cfg.flags |= SWB_CONFIG_DEFAULT_BROWSER_SET;
        }
-       snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_LOC);
-       snprintf(tempfile, len+4, "%s%s", newfile, ".tmp");
-
-       /* Open the temporary file for writing */
-       if (!(tmpfp = fopen(tempfile, "w")))
-               /* TODO: report the error somehow? */
-               goto out;
-
-       /* Open the old config file, if it exists */
-       if ((fp = open_config_file()) && parse_config_file_begin()) {
-               /* Copy the old config file over to the new one line by line,
-                  replacing old config values with new ones
-                  TODO: should we handle errors differently than EOF? */
-               while (!parse_config_file_line(fp, &line)) {
-                       if (line.parsed) {
-                               /* Is a config line, print the new value here */
-                               if (!strcmp(line.key, "continuous_mode")) {
-                                       if (!continuous_mode_seen) {
-                                               fprintf(tmpfp, "%s = %d\n",
-                                                       line.key,
-                                                       get_continuous_mode());
-                                               continuous_mode_seen = 1;
-                                       }
-                               } else if (!strcmp(line.key,
-                                                       "default_browser")) {
-                                       if (!default_browser_seen) {
-                                               fprintf(tmpfp, "%s = \"%s\"\n",
-                                                       line.key,
-                                                       get_default_browser());
-                                               default_browser_seen = 1;
-                                       }
-                               } else if (!strcmp(line.key,
-                                                       "other_browser_cmd")) {
-                                       if (!other_browser_cmd_seen &&
-                                           strlen(get_other_browser_cmd())>0) {
-                                               fprintf(tmpfp, "%s = \"%s\"\n",
-                                                       line.key,
-                                                       get_other_browser_cmd());
-                                               other_browser_cmd_seen = 1;
-                                       }
-                               } else if (!strcmp(line.key,
-                                                       "logging")) {
-                                       if (logger_name) {
-                                               fprintf(tmpfp, "%s = \"%s\"\n",
-                                                       line.key,
-                                                       logger_name);
-                                               free(logger_name);
-                                               logger_name = NULL;
-                                       }
-                               }
-                       } else {
-                               /* Just copy the old line over */
-                               fprintf(tmpfp, "%s\n", line.key);
-                       }
-                       free(line.key);
-                       free(line.value);
-               }
-               parse_config_file_end();
+       if (strlen(get_other_browser_cmd()) == 0) {
+               new_cfg.other_browser_cmd = NULL;
+               new_cfg.flags &= ~SWB_CONFIG_OTHER_BROWSER_CMD_SET;
+       } else if (!(orig_cfg.other_browser_cmd &&
+                    !strcmp(get_other_browser_cmd(),
+                            orig_cfg.other_browser_cmd))) {
+               new_cfg.other_browser_cmd = get_other_browser_cmd();
+               new_cfg.flags |= SWB_CONFIG_OTHER_BROWSER_CMD_SET;
        }
 
-       /* If we haven't written them yet, write out the new config values */
-       if (!continuous_mode_seen)
-               fprintf(tmpfp, "%s = %d\n",
-                       "continuous_mode", get_continuous_mode());
-       if (!default_browser_seen)
-               fprintf(tmpfp, "%s = \"%s\"\n",
-                       "default_browser", get_default_browser());
-       if (!other_browser_cmd_seen && strlen(get_other_browser_cmd()) > 0)
-               fprintf(tmpfp, "%s = \"%s\"\n",
-                       "other_browser_cmd", get_other_browser_cmd());
-       if (logger_name)
-               fprintf(tmpfp, "%s = \"%s\"\n",
-                       "logging", logger_name);
-
-       /* Replace the old config file with the new one */
-       fclose(tmpfp);
-       tmpfp = NULL;
-       rename(tempfile, newfile);
-
-out:
-       free(newfile);
-       free(tempfile);
-       if (tmpfp)
-               fclose(tmpfp);
-       if (fp)
-               fclose(fp);
-       return;
+       swb_config_save(&new_cfg);
 }
 
 static void do_reconfig(void) {
diff --git a/config-ui/save-config.c b/config-ui/save-config.c
new file mode 100644 (file)
index 0000000..4415b99
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * save-config.c -- functions to save a Browser Switchboard configuration
+ *
+ * Copyright (C) 2009-2010 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "configfile.h"
+#include "config.h"
+
+extern struct swb_config_option swb_config_options[];
+
+/* Outputs a config file line for the named option to a file descriptor */
+static void swb_config_output_option(FILE *fp, unsigned int *oldcfg_seen,
+                             struct swb_config *cfg, char *name) {
+       struct swb_config_option *opt;
+       ptrdiff_t i;
+       
+       for (opt = swb_config_options; opt->name; ++opt) {
+               if (strcmp(opt->name, name))
+                       continue;
+
+               i = opt - swb_config_options;
+               if (!(*oldcfg_seen & opt->set_mask) &&
+                   (cfg->flags & opt->set_mask)) {
+                       switch (opt->type) {
+                         case SWB_CONFIG_OPT_STRING:
+                               fprintf(fp, "%s = \"%s\"\n",
+                                       opt->name,
+                                       *(char **)cfg->entries[i]);
+                               *oldcfg_seen |= opt->set_mask;
+                               break;
+                         case SWB_CONFIG_OPT_INT:
+                               fprintf(fp, "%s = %d\n",
+                                       opt->name,
+                                       *(int *)cfg->entries[i]);
+                               *oldcfg_seen |= opt->set_mask;
+                               break;
+                       }
+               }
+               break;
+       }
+}
+
+/* Save the settings in the provided swb_config struct to the config file
+   Returns true on success, false otherwise */
+int swb_config_save(struct swb_config *cfg) {
+       FILE *fp = NULL, *tmpfp = NULL;
+       char *homedir, *tempfile, *newfile;
+       size_t len;
+       int retval = 1;
+       struct swb_config_line line;
+       unsigned int oldcfg_seen = 0;
+       int i;
+
+       /* If CONFIGFILE_DIR doesn't exist already, try to create it */
+       if (!(homedir = getenv("HOME")))
+               homedir = DEFAULT_HOMEDIR;
+       len = strlen(homedir) + strlen(CONFIGFILE_DIR) + 1;
+       if (!(newfile = calloc(len, sizeof(char))))
+               return 0;
+       snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_DIR);
+       if (access(newfile, F_OK) == -1 && errno == ENOENT) {
+               mkdir(newfile, 0750);
+       }
+       free(newfile);
+
+       /* Put together the path to the new config file and the tempfile */
+       len = strlen(homedir) + strlen(CONFIGFILE_LOC) + 1;
+       if (!(newfile = calloc(len, sizeof(char))))
+               return 0;
+       /* 4 = strlen(".tmp") */
+       if (!(tempfile = calloc(len+4, sizeof(char)))) {
+               free(newfile);
+               return 0;
+       }
+       snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_LOC);
+       snprintf(tempfile, len+4, "%s%s", newfile, ".tmp");
+
+       /* Open the temporary file for writing */
+       if (!(tmpfp = fopen(tempfile, "w"))) {
+               retval = 0;
+               goto out;
+       }
+
+       /* Open the old config file, if it exists */
+       if ((fp = open_config_file()) && parse_config_file_begin()) {
+               /* Copy the old config file over to the new one line by line,
+                  replacing old config values with new ones
+                  TODO: should we handle errors differently than EOF? */
+               while (!parse_config_file_line(fp, &line)) {
+                       if (line.parsed) {
+                               /* Is a config line, print the new value here */
+                               swb_config_output_option(tmpfp, &oldcfg_seen,
+                                                        cfg, line.key);
+                       } else {
+                               /* Just copy the old line over */
+                               fprintf(tmpfp, "%s\n", line.key);
+                       }
+                       free(line.key);
+                       free(line.value);
+               }
+               parse_config_file_end();
+       }
+
+       /* If we haven't written them yet, write out any new config values */
+       for (i = 0; swb_config_options[i].name; ++i)
+               swb_config_output_option(tmpfp, &oldcfg_seen, cfg,
+                                        swb_config_options[i].name);
+
+       /* Replace the old config file with the new one */
+       fclose(tmpfp);
+       tmpfp = NULL;
+       rename(tempfile, newfile);
+
+out:
+       free(newfile);
+       free(tempfile);
+       if (tmpfp)
+               fclose(tmpfp);
+       if (fp)
+               fclose(fp);
+       return retval;
+}
diff --git a/config.c b/config.c
new file mode 100644 (file)
index 0000000..8928a3d
--- /dev/null
+++ b/config.c
@@ -0,0 +1,161 @@
+/*
+ * config.c -- configuration functions for Browser Switchboard
+ *
+ * Copyright (C) 2009-2010 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "configfile.h"
+#include "config.h"
+
+/* The Browser Switchboard config file options */
+struct swb_config_option swb_config_options[] = {
+       { "continuous_mode", SWB_CONFIG_OPT_INT, SWB_CONFIG_CONTINUOUS_MODE_SET },
+       { "default_browser", SWB_CONFIG_OPT_STRING, SWB_CONFIG_DEFAULT_BROWSER_SET },
+       { "other_browser_cmd", SWB_CONFIG_OPT_STRING, SWB_CONFIG_OTHER_BROWSER_CMD_SET },
+       { "logging", SWB_CONFIG_OPT_STRING, SWB_CONFIG_LOGGING_SET },
+       { NULL, 0, 0 },
+};
+
+/* Browser Switchboard configuration defaults */
+static struct swb_config swb_config_defaults = {
+       .flags = SWB_CONFIG_INITIALIZED,
+       .continuous_mode = 0,
+       .default_browser = "microb",
+       .other_browser_cmd = NULL,
+       .logging = "stdout",
+};
+
+
+/* Copy the contents of an swb_config struct
+   The entries[] array means that the standard copy will not work */
+void swb_config_copy(struct swb_config *dst, struct swb_config *src) {
+       if (!dst || !src)
+               return;
+
+       dst->entries[0] = &(dst->continuous_mode);
+       dst->entries[1] = &(dst->default_browser);
+       dst->entries[2] = &(dst->other_browser_cmd);
+       dst->entries[3] = &(dst->logging);
+
+       dst->flags = src->flags;
+
+       dst->continuous_mode = src->continuous_mode;
+       dst->default_browser = src->default_browser;
+       dst->other_browser_cmd = src->other_browser_cmd;
+       dst->logging = src->logging;
+}
+
+/* Initialize a swb_config struct with configuration defaults */
+void swb_config_init(struct swb_config *cfg) {
+       swb_config_copy(cfg, &swb_config_defaults);
+}
+
+/* Free all heap memory used in an swb_config struct
+   This MUST NOT be done if any of the strings are being used elsewhere! */
+void swb_config_free(struct swb_config *cfg) {
+       int i;
+
+       if (!cfg)
+               return;
+       if (!(cfg->flags & SWB_CONFIG_INITIALIZED))
+               return;
+
+       for (i = 0; swb_config_options[i].name; ++i) {
+               if (cfg->flags & swb_config_options[i].set_mask) {
+                       switch (swb_config_options[i].type) {
+                         case SWB_CONFIG_OPT_STRING:
+                               free(*(char **)cfg->entries[i]);
+                               *(char **)cfg->entries[i] = NULL;
+                               break;
+                         default:
+                               break;
+                       }
+               }
+       }
+
+       cfg->flags = 0;
+}
+
+/* Load a value into the part of a struct swb_config indicated by name */
+static int swb_config_load_option(struct swb_config *cfg,
+                                 char *name, char *value) {
+       struct swb_config_option *opt;
+       ptrdiff_t i;
+       int retval = 0;
+
+       for (opt = swb_config_options; opt->name; ++opt) {
+               if (strcmp(name, opt->name))
+                       continue;
+
+               if (!(cfg->flags & opt->set_mask)) {
+                       i = opt - swb_config_options;
+                       switch (opt->type) {
+                         case SWB_CONFIG_OPT_STRING:
+                               *(char **)cfg->entries[i] = value;
+                               break;
+                         case SWB_CONFIG_OPT_INT:
+                               *(int *)cfg->entries[i] = atoi(value);
+                               free(value);
+                               break;
+                       }
+                       cfg->flags |= opt->set_mask;
+               }
+               retval = 1;
+               break;
+       }
+
+       if (!retval)
+               free(value);
+
+       return retval;
+}
+
+/* Read the config file and load settings into the provided swb_config struct
+   Caller is responsible for freeing allocated strings with free()
+   Returns true on success, false otherwise */
+int swb_config_load(struct swb_config *cfg) {
+       FILE *fp;
+       struct swb_config_line line;
+
+       if (!cfg || !(cfg->flags & SWB_CONFIG_INITIALIZED))
+               return 0;
+
+       if (!(fp = open_config_file()))
+               goto out_noopen;
+
+       /* Parse the config file
+          TODO: should we handle errors differently than EOF? */
+       if (!parse_config_file_begin())
+               goto out;
+       while (!parse_config_file_line(fp, &line)) {
+               if (line.parsed)
+                       swb_config_load_option(cfg, line.key, line.value);
+               free(line.key);
+       }
+       parse_config_file_end();
+
+out:
+       fclose(fp);
+out_noopen:
+       return 1;
+}
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..858aec5
--- /dev/null
+++ b/config.h
@@ -0,0 +1,61 @@
+/*
+ * config.h -- definitions for Browser Switchboard configuration
+ *
+ * Copyright (C) 2009-2010 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#define SWB_CONFIG_INITIALIZED                 0x01
+#define SWB_CONFIG_CONTINUOUS_MODE_SET         0x02
+#define SWB_CONFIG_DEFAULT_BROWSER_SET         0x04
+#define SWB_CONFIG_OTHER_BROWSER_CMD_SET       0x08
+#define SWB_CONFIG_LOGGING_SET                 0x10
+
+struct swb_config {
+       unsigned int flags;
+       /* Array of pointers to the elements of the struct, in the order given
+          in swb_config_options[] */
+       void *entries[4];
+
+       int continuous_mode;
+       char *default_browser;
+       char *other_browser_cmd;
+       char *logging;
+};
+
+struct swb_config_option {
+       char *name;
+       enum {
+               SWB_CONFIG_OPT_STRING,
+               SWB_CONFIG_OPT_INT
+       } type;
+       int set_mask;
+};
+
+void swb_config_copy(struct swb_config *dst, struct swb_config *src);
+void swb_config_init(struct swb_config *cfg);
+void swb_config_free(struct swb_config *cfg);
+
+int swb_config_load(struct swb_config *cfg);
+
+int swb_config_save(struct swb_config *cfg);
+
+#endif /* _CONFIG_H */
index 77f2ded..fe57eb9 100644 (file)
 #include "dbus-server-bindings.h"
 #include "log.h"
 
-#define LAUNCH_DEFAULT_BROWSER launch_microb
+struct browser_launcher {
+       char *name;
+       void (*launcher)(struct swb_context *, char *);
+       char *other_browser_cmd;
+       char *binary;
+};
 
 #ifdef FREMANTLE
 static int microb_started = 0;
@@ -134,7 +139,7 @@ static void launch_tear(struct swb_context *ctx, char *uri) {
                if (!tear_proxy) {
                        if (!(tear_proxy = dbus_g_proxy_new_for_name(
                                                ctx->session_bus,
-                                               "com.nokia.tear",
+                                               "com.nokia.tear",
                                                "/com/nokia/tear",
                                                "com.nokia.Tear"))) {
                                log_msg("Failed to create proxy for com.nokia.Tear D-Bus interface\n");
@@ -386,7 +391,7 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                                /* Event found, stop looking */
                                break;
                        memset(buf, '\0', 256);
-                }
+               }
                inotify_rm_watch(fd, inot_wd);
                close(fd);
 
@@ -595,57 +600,83 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) {
        execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
 }
 
-/* Use launch_other_browser as the default browser launcher, with the string
-   passed in as the other_browser_cmd
-   Resulting other_browser_cmd is always safe to free(), even if a pointer
-   to a string constant is passed in */
-static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) {
-       size_t len = strlen(cmd);
-
-       free(ctx->other_browser_cmd);
-       ctx->other_browser_cmd = calloc(len+1, sizeof(char));
-       if (!ctx->other_browser_cmd) {
-               log_msg("malloc failed!\n");
-               ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
-       } else {
-               ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd,
-                                                cmd, len+1);
-               ctx->default_browser_launcher = launch_other_browser;
+
+/* The list of known browsers and how to launch them */
+static struct browser_launcher browser_launchers[] = {
+       { "microb", launch_microb, NULL, NULL }, /* First entry is the default! */
+       { "tear", launch_tear, NULL, "/usr/bin/tear" },
+       { "fennec", NULL, "fennec %s", "/usr/bin/fennec" },
+       { "opera", NULL, "opera %s", "/usr/bin/opera" },
+       { "midori", NULL, "midori %s", "/usr/bin/midori" },
+       { NULL, NULL, NULL, NULL },
+};
+
+static void use_launcher_as_default(struct swb_context *ctx,
+                                   struct browser_launcher *browser) {
+       if (!ctx || !browser)
+               return;
+
+       if (browser->launcher)
+               ctx->default_browser_launcher = browser->launcher;
+       else if (browser->other_browser_cmd) {
+               free(ctx->other_browser_cmd);
+
+               /* Make a copy of the string constant so that
+                  ctx->other_browser_cmd is safe to free() */
+               ctx->other_browser_cmd = strdup(browser->other_browser_cmd);
+               if (!ctx->other_browser_cmd) {
+                       log_msg("malloc failed!\n");
+                       /* Ideally, we'd configure the built-in default here --
+                          but it's possible we could be called in that path */
+                       exit(1);
+               } else
+                       ctx->default_browser_launcher = launch_other_browser;
        }
+
+       return;
 }
 
 void update_default_browser(struct swb_context *ctx, char *default_browser) {
+       struct browser_launcher *browser;
+
        if (!ctx)
                return;
 
-       if (!default_browser) {
+       /* Configure the built-in default to start -- that way, we can
+          handle errors by just returning */
+       use_launcher_as_default(ctx, &browser_launchers[0]);
+
+       if (!default_browser)
                /* No default_browser configured -- use built-in default */
-               ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
                return;
-       }
 
-       if (!strcmp(default_browser, "tear"))
-               ctx->default_browser_launcher = launch_tear;
-       else if (!strcmp(default_browser, "microb"))
-               ctx->default_browser_launcher = launch_microb;
-       else if (!strcmp(default_browser, "fennec"))
-               /* Cheat and reuse launch_other_browser, since we don't appear
-                  to need to do anything special */
-               use_other_browser_cmd(ctx, "fennec %s");
-       else if (!strcmp(default_browser, "midori"))
-               use_other_browser_cmd(ctx, "midori %s");
-       else if (!strcmp(default_browser, "other")) {
+       /* Go through the list of known browser launchers and use one if
+          it matches */
+       for (browser = browser_launchers; browser->name; ++browser)
+               if (!strcmp(default_browser, browser->name)) {
+                       /* Make sure the user's choice is installed on the
+                          system */
+                       if (browser->binary && access(browser->binary, X_OK)) {
+                               log_msg("%s appears not to be installed\n",
+                                       default_browser);
+                       } else {
+                               use_launcher_as_default(ctx, browser);
+                               return;
+                       }
+               }
+
+       /* Deal with default_browser = "other" */
+       if (!strcmp(default_browser, "other")) {
                if (ctx->other_browser_cmd)
                        ctx->default_browser_launcher = launch_other_browser;
-               else {
+               else
                        log_msg("default_browser is 'other', but no other_browser_cmd set -- using default\n");
-                       ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
-               }
-       } else {
-               log_msg("Unknown default_browser %s, using default",
-                       default_browser);
-               ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
+               return;
        }
+
+       /* Unknown value of default_browser */
+       log_msg("Unknown default_browser %s, using default\n", default_browser);
+       return;
 }
 
 void launch_browser(struct swb_context *ctx, char *uri) {
diff --git a/main.c b/main.c
index 093b535..40823e3 100644 (file)
--- a/main.c
+++ b/main.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <signal.h>
+#include <errno.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <dbus/dbus-glib.h>
 #include "browser-switchboard.h"
 #include "launcher.h"
 #include "dbus-server-bindings.h"
-#include "configfile.h"
+#include "config.h"
 #include "log.h"
 
 struct swb_context ctx;
 
-static void set_config_defaults(struct swb_context *ctx) {
-       if (!ctx)
-               return;
-       free(ctx->other_browser_cmd);
-       ctx->continuous_mode = 0;
-       ctx->default_browser_launcher = NULL;
-       ctx->other_browser_cmd = NULL;
-}
-
 static void waitforzombies(int signalnum) {
        while (waitpid(-1, NULL, WNOHANG) > 0)
                log_msg("Waited for a zombie\n");
 }
 
 static void read_config(int signalnum) {
-       FILE *fp;
-       int continuous_mode_seen = 0;
-       struct swb_config_line line;
-       char *default_browser = NULL, *logger_name = NULL;
-
-       set_config_defaults(&ctx);
-
-       if (!(fp = open_config_file()))
-               goto out_noopen;
-
-       /* Parse the config file
-          TODO: should we handle errors differently than EOF? */
-       if (!parse_config_file_begin())
-               goto out;
-       while (!parse_config_file_line(fp, &line)) {
-               if (line.parsed) {
-                       if (!strcmp(line.key, "continuous_mode")) {
-                               if (!continuous_mode_seen) {
-                                       ctx.continuous_mode = atoi(line.value);
-                                       continuous_mode_seen = 1;
-                               }
-                               free(line.value);
-                       } else if (!strcmp(line.key, "default_browser")) {
-                               if (!default_browser)
-                                       default_browser = line.value;
-                       } else if (!strcmp(line.key, "other_browser_cmd")) {
-                               if (!ctx.other_browser_cmd)
-                                       ctx.other_browser_cmd = line.value;
-                       } else if (!strcmp(line.key, "logging")) {
-                               if (!logger_name)
-                                       logger_name = line.value;
-                       } else {
-                               /* Don't need this line's contents */
-                               free(line.value);
-                       }
-               }
-               free(line.key);
-       }
-       parse_config_file_end();
+       struct swb_config cfg;
 
-out:
-       fclose(fp);
-out_noopen:
-       log_config(logger_name);
-       update_default_browser(&ctx, default_browser);
+       swb_config_init(&cfg);
+
+       swb_config_load(&cfg);
+
+       log_config(cfg.logging);
+       ctx.continuous_mode = cfg.continuous_mode;
+       free(ctx.other_browser_cmd);
+       if (cfg.other_browser_cmd) {
+               if (!(ctx.other_browser_cmd = strdup(cfg.other_browser_cmd))) {
+                       log_perror(errno, "Failed to set other_browser_cmd");
+                       exit(1);
+               }
+       } else
+               ctx.other_browser_cmd = NULL;
+       update_default_browser(&ctx, cfg.default_browser);
 
-       log_msg("continuous_mode: %d\n", ctx.continuous_mode);
-       log_msg("default_browser: '%s'\n",
-               default_browser?default_browser:"NULL");
+       log_msg("continuous_mode: %d\n", cfg.continuous_mode);
+       log_msg("default_browser: '%s'\n", cfg.default_browser);
        log_msg("other_browser_cmd: '%s'\n",
-               ctx.other_browser_cmd?ctx.other_browser_cmd:"NULL");
-       log_msg("logging: '%s'\n",
-               logger_name?logger_name:"NULL");
+               cfg.other_browser_cmd?cfg.other_browser_cmd:"NULL");
+       log_msg("logging: '%s'\n", cfg.logging);
 
-       free(logger_name);
-       free(default_browser);
+       swb_config_free(&cfg);
        return;
 }