chiark / gitweb /
disorder.h: more consistent approach to function attributes
[disorder] / lib / configuration.c
index e7698ec533da6af5b4b61ac099a530d25c1f05e0..2ef3a5ed310e3ef68b2da000882e99641b07dbc2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004-2010 Richard Kettlewell
+ * Copyright (C) 2004-2011, 2013 Richard Kettlewell
  * Portions copyright (C) 2007 Mark Wooding
  *
  * This program is free software: you can redistribute it and/or modify
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
 #include <ctype.h>
 #include <stddef.h>
-#include <pwd.h>
-#include <langinfo.h>
-#include <pcre.h>
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+#if HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+#if HAVE_PCRE_H
+# include <pcre.h>
+#endif
+#if HAVE_SHLOBJ_H
+# include <Shlobj.h>
+#endif
 #include <signal.h>
 
 #include "rights.h"
 #include "charset.h"
 #include "defs.h"
 #include "printf.h"
-#include "regsub.h"
+#if HAVE_PCRE_H
+# include "regsub.h"
+#endif
 #include "signame.h"
 #include "authhash.h"
 #include "vector.h"
+#if !_WIN32
 #include "uaudio.h"
+#endif
 
 /** @brief Path to config file 
  *
@@ -62,11 +77,13 @@ char *configfile;
  */
 int config_per_user = 1;
 
+#if !_WIN32
 /** @brief Table of audio APIs
  *
  * Only set in server processes.
  */
 const struct uaudio *const *config_uaudio_apis;
+#endif
 
 /** @brief Config file parser state */
 struct config_state {
@@ -200,8 +217,13 @@ static int set_collections(const struct config_state *cs,
   /* Defaults */
   if(!module)
     module = "fs";
+#if HAVE_LANGINFO_H
   if(!encoding)
     encoding = nl_langinfo(CODESET);
+#else
+  if(!encoding)
+    encoding = "ascii";
+#endif
   cl = ADDRESS(cs->config, struct collectionlist);
   ++cl->n;
   cl->s = xrealloc(cl->s, cl->n * sizeof (struct collection));
@@ -343,13 +365,13 @@ static int parse_sample_format(const struct config_state *cs,
                   cs->path, cs->line, t);
     return -1;
   }
-  if(format) format->bits = t;
+  if(format) format->bits = (uint8_t)t;
   switch (*p) {
     case 'l': case 'L': t = ENDIAN_LITTLE; p++; break;
     case 'b': case 'B': t = ENDIAN_BIG; p++; break;
     default: t = ENDIAN_NATIVE; break;
   }
-  if(format) format->endian = t;
+  if(format) format->endian = (uint8_t)t;
   if(*p != '/') {
     disorder_error(errno, "%s:%d: expected `/' after bits-per-sample",
          cs->path, cs->line);
@@ -380,7 +402,7 @@ static int parse_sample_format(const struct config_state *cs,
                   cs->path, cs->line, t);
     return -1;
   }
-  if(format) format->channels = t;
+  if(format) format->channels = (uint8_t)t;
   if(*p) {
     disorder_error(0, "%s:%d: junk after channels", cs->path, cs->line);
     return -1;
@@ -395,6 +417,7 @@ static int set_sample_format(const struct config_state *cs,
                             nvec, vec);
 }
 
+#if HAVE_PCRE_H
 static int set_namepart(const struct config_state *cs,
                        const struct conf *whoami,
                        int nvec, char **vec) {
@@ -480,6 +503,7 @@ static int set_transform(const struct config_state *cs,
   ++tl->n;
   return 0;
 }
+#endif
 
 static int set_rights(const struct config_state *cs,
                      const struct conf *whoami,
@@ -561,6 +585,7 @@ static void free_collectionlist(struct config *c,
   xfree(cll->s);
 }
 
+#if HAVE_PCRE_H
 static void free_namepartlist(struct config *c,
                              const struct conf *whoami) {
   struct namepartlist *npl = ADDRESS(c, struct namepartlist);
@@ -593,6 +618,7 @@ static void free_transformlist(struct config *c,
   }
   xfree(tl->t);
 }
+#endif
 
 static void free_netaddress(struct config *c,
                            const struct conf *whoami) {
@@ -613,8 +639,10 @@ static const struct conftype
   type_stringlist_accum = { set_stringlist_accum, free_stringlistlist },
   type_string_accum = { set_string_accum, free_stringlist },
   type_sample_format = { set_sample_format, free_none },
+#if HAVE_PCRE_H
   type_namepart = { set_namepart, free_namepartlist },
   type_transform = { set_transform, free_transformlist },
+#endif
   type_netaddress = { set_netaddress, free_netaddress },
   type_rights = { set_rights, free_string };
 
@@ -657,7 +685,7 @@ static int validate_isabspath(const struct config_state *cs,
 
   for(n = 0; n < nvec; ++n)
     if(vec[n][0] != '/') {
-      disorder_error(errno, "%s:%d: %s: not an absolute path", 
+      disorder_error(0, "%s:%d: %s: not an absolute path", 
                     cs->path, cs->line, vec[n]);
       return -1;
     }
@@ -731,6 +759,7 @@ static int validate_tracklength(const struct config_state *cs,
 static int validate_non_negative(const struct config_state *cs,
                                 int nvec, char **vec) {
   long n;
+  char errbuf[1024];
 
   if(nvec < 1) {
     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
@@ -741,7 +770,8 @@ static int validate_non_negative(const struct config_state *cs,
     return -1;
   }
   if(xstrtol(&n, vec[0], 0, 0)) {
-    disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
+    disorder_error(0, "%s:%d: %s", cs->path, cs->line,
+                   format_error(ec_errno, errno, errbuf, sizeof errbuf));
     return -1;
   }
   if(n < 0) {
@@ -760,6 +790,7 @@ static int validate_non_negative(const struct config_state *cs,
 static int validate_positive(const struct config_state *cs,
                          int nvec, char **vec) {
   long n;
+  char errbuf[1024];
 
   if(nvec < 1) {
     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
@@ -770,7 +801,8 @@ static int validate_positive(const struct config_state *cs,
     return -1;
   }
   if(xstrtol(&n, vec[0], 0, 0)) {
-    disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
+    disorder_error(0, "%s:%d: %s", cs->path, cs->line,
+                   format_error(ec_errno, errno, errbuf, sizeof errbuf));
     return -1;
   }
   if(n <= 0) {
@@ -780,6 +812,7 @@ static int validate_positive(const struct config_state *cs,
   return 0;
 }
 
+#if !_WIN32
 /** @brief Validate a system username
  * @param cs Configuration state
  * @param nvec Length of (proposed) new value
@@ -795,6 +828,7 @@ static int validate_isauser(const struct config_state *cs,
   }
   return 0;
 }
+#endif
 
 /** @brief Validate a sample format string
  * @param cs Configuration state
@@ -933,6 +967,7 @@ static int validate_algo(const struct config_state attribute((unused)) *cs,
   return 0;
 }
 
+#if !_WIN32
 /** @brief Validate a playback backend name
  * @param cs Configuration state
  * @param nvec Length of (proposed) new value
@@ -961,6 +996,7 @@ static int validate_backend(const struct config_state attribute((unused)) *cs,
   /* In non-server processes we have no idea what's valid */
   return 0;
 }
+#endif
 
 /** @brief Validate a pause mode string
  * @param cs Configuration state
@@ -1011,7 +1047,9 @@ static int validate_destaddr(const struct config_state attribute((unused)) *cs,
 /** @brief All configuration items */
 static const struct conf conf[] = {
   { C(alias),            &type_string,           validate_alias },
+#if !_WIN32
   { C(api),              &type_string,           validate_backend },
+#endif
   { C(authorization_algorithm), &type_string,    validate_algo },
   { C(broadcast),        &type_netaddress,       validate_destaddr },
   { C(broadcast_from),   &type_netaddress,       validate_any },
@@ -1026,14 +1064,18 @@ static const struct conf conf[] = {
   { C(default_rights),   &type_rights,           validate_any },
   { C(device),           &type_string,           validate_any },
   { C(history),          &type_integer,          validate_positive },
+#if !_WIN32
   { C(home),             &type_string,           validate_isabspath },
+#endif
   { C(listen),           &type_netaddress,       validate_any },
   { C(mail_sender),      &type_string,           validate_any },
   { C(mixer),            &type_string,           validate_any },
   { C(mount_rescan),     &type_boolean,          validate_any },
   { C(multicast_loop),   &type_boolean,          validate_any },
   { C(multicast_ttl),    &type_integer,          validate_non_negative },
+#if HAVE_PCRE_H
   { C(namepart),         &type_namepart,         validate_any },
+#endif
   { C(new_bias),         &type_integer,          validate_positive },
   { C(new_bias_age),     &type_integer,          validate_positive },
   { C(new_max),          &type_integer,          validate_positive },
@@ -1055,22 +1097,31 @@ static const struct conf conf[] = {
   { C(remote_userman),   &type_boolean,          validate_any },
   { C(replay_min),       &type_integer,          validate_non_negative },
   { C(rtp_delay_threshold), &type_integer,       validate_positive },
+  { C(rtp_mode),         &type_string,           validate_any },
   { C(rtp_verbose),      &type_boolean,          validate_any },
   { C(sample_format),    &type_sample_format,    validate_sample_format },
   { C(scratch),          &type_string_accum,     validate_isreg },
+#if !_WIN32
   { C(sendmail),         &type_string,           validate_isabspath },
+#endif
   { C(short_display),    &type_integer,          validate_positive },
   { C(signal),           &type_signal,           validate_any },
   { C(smtp_server),      &type_string,           validate_any },
   { C(sox_generation),   &type_integer,          validate_non_negative },
+#if !_WIN32
   { C2(speaker_backend, api),  &type_string,     validate_backend },
+#endif
   { C(speaker_command),  &type_string,           validate_any },
   { C(stopword),         &type_string_accum,     validate_any },
   { C(templates),        &type_string_accum,     validate_isdir },
   { C(tracklength),      &type_stringlist_accum, validate_tracklength },
+#if HAVE_PCRE_H
   { C(transform),        &type_transform,        validate_any },
+#endif
   { C(url),              &type_string,           validate_url },
+#if !_WIN32
   { C(user),             &type_string,           validate_isauser },
+#endif
   { C(username),         &type_string,           validate_any },
 };
 
@@ -1116,6 +1167,7 @@ static int config_set_args(const struct config_state *cs,
   va_list ap;
   struct vector v[1];
   char *s;
+  int rc;
 
   vector_init(v);
   vector_append(v, (char *)which);
@@ -1124,7 +1176,7 @@ static int config_set_args(const struct config_state *cs,
     vector_append(v, s);
   va_end(ap);
   vector_terminate(v);
-  int rc = config_set(cs, v->nvec, v->vec);
+  rc = config_set(cs, v->nvec, v->vec);
   xfree(v->vec);
   return rc;
 }
@@ -1272,8 +1324,10 @@ static const char *const default_players[] = {
  */
 static struct config *config_default(void) {
   struct config *c = xmalloc(sizeof *c);
+#if !_WIN32
   const char *logname;
   struct passwd *pw;
+#endif
   struct config_state cs;
   size_t n;
 
@@ -1282,14 +1336,30 @@ static struct config *config_default(void) {
   cs.config = c;
   /* Strings had better be xstrdup'd as they will get freed at some point. */
   c->history = 60;
+#if !_WIN32
   c->home = xstrdup(pkgstatedir);
+#endif
+#if _WIN32
+  {
+    char buffer[128];
+    DWORD bufsize = sizeof buffer;
+    if(!GetUserNameA(buffer, &bufsize))
+      disorder_fatal(0, "cannot determine our username");
+    c->username = xstrdup(buffer);
+  }
+#else
   if(!(pw = getpwuid(getuid())))
     disorder_fatal(0, "cannot determine our username");
   logname = pw->pw_name;
   c->username = xstrdup(logname);
+#endif
   c->refresh = 15;
   c->refresh_min = 1;
+#ifdef SIGKILL
   c->signal = SIGKILL;
+#else
+  c->signal = SIGTERM;
+#endif
   c->alias = xstrdup("{/artist}{/album}{/title}{ext}");
   c->device = xstrdup("default");
   c->nice_rescan = 10;
@@ -1311,8 +1381,10 @@ static struct config *config_default(void) {
   c->dbversion = 2;
   c->cookie_login_lifetime = 86400;
   c->cookie_key_lifetime = 86400 * 7;
+#if !_WIN32
   if(sendmail_binary[0] && strcmp(sendmail_binary, "none"))
     c->sendmail = xstrdup(sendmail_binary);
+#endif
   c->smtp_server = xstrdup("127.0.0.1");
   c->new_max = 100;
   c->reminder_interval = 600;          /* 10m */
@@ -1338,9 +1410,11 @@ static struct config *config_default(void) {
   c->broadcast_from.af = -1;
   c->listen.af = -1;
   c->connect.af = -1;
+  c->rtp_mode = xstrdup("auto");
   return c;
 }
 
+#if !_WIN32
 /** @brief Construct a filename
  * @param c Configuration
  * @param name Base filename
@@ -1354,11 +1428,14 @@ char *config_get_file2(struct config *c, const char *name) {
   byte_xasprintf(&s, "%s/%s", c->home, name);
   return s;
 }
+#endif
 
 /** @brief Set the default configuration file */
 static void set_configfile(void) {
+#if !_WIN32
   if(!configfile)
     byte_xasprintf(&configfile, "%s/config", pkgconfdir);
+#endif
 }
 
 /** @brief Free a configuration object
@@ -1389,9 +1466,12 @@ void config_free(struct config *c) {
 static void config_postdefaults(struct config *c,
                                int server) {
   struct config_state cs;
+#if HAVE_PCRE_H
   const struct conf *whoami;
   int n;
+#endif
 
+#if HAVE_PCRE_H
   static const char *namepart[][4] = {
     { "title",  "/([0-9]+ *[-:]? *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
     { "title",  "/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort" },
@@ -1409,10 +1489,12 @@ static void config_postdefaults(struct config *c,
     { "dir",   "[[:punct:]]",                           "", "sort", "g", }
   };
 #define NTRANSFORM (int)(sizeof transform / sizeof *transform)
+#endif
 
   cs.path = "<internal>";
   cs.line = 0;
   cs.config = c;
+#if HAVE_PCRE_H
   if(!c->namepart.n) {
     whoami = find("namepart");
     for(n = 0; n < NNAMEPART; ++n)
@@ -1423,14 +1505,17 @@ static void config_postdefaults(struct config *c,
     for(n = 0; n < NTRANSFORM; ++n)
       set_transform(&cs, whoami, 5, (char **)transform[n]);
   }
+#endif
   if(!c->api) {
     if(c->speaker_command)
       c->api = xstrdup("command");
     else if(c->broadcast.af != -1)
       c->api = xstrdup("rtp");
+#if !_WIN32
     else if(config_uaudio_apis)
       c->api = xstrdup(uaudio_default(config_uaudio_apis,
                                       UAUDIO_API_SERVER)->name);
+#endif
     else
       c->api = xstrdup("<none>");
   }
@@ -1477,14 +1562,15 @@ int config_read(int server,
                 const struct config *oldconfig) {
   struct config *c;
   char *privconf;
-  struct passwd *pw;
+  struct passwd *pw = NULL;
 
   set_configfile();
   c = config_default();
-  /* standalone Disobedience installs might not have a global config file */
-  if(access(configfile, F_OK) == 0)
-    if(config_include(c, configfile))
-      return -1;
+  /* standalone client installs might not have a global config file */
+  if(configfile)
+    if(access(configfile, F_OK) == 0)
+      if(config_include(c, configfile))
+        return -1;
   /* if we can read the private config file, do */
   if((privconf = config_private())
      && access(privconf, R_OK) == 0
@@ -1493,6 +1579,7 @@ int config_read(int server,
   xfree(privconf);
   /* if there's a per-user system config file for this user, read it */
   if(config_per_user) {
+#if !_WIN32
     if(!(pw = getpwuid(getuid())))
       disorder_fatal(0, "cannot determine our username");
     if((privconf = config_usersysconf(pw))
@@ -1500,6 +1587,7 @@ int config_read(int server,
        && config_include(c, privconf))
       return -1;
     xfree(privconf);
+#endif
     /* if we have a password file, read it */
     if((privconf = config_userconf(0, pw))
        && access(privconf, F_OK) == 0
@@ -1511,10 +1599,12 @@ int config_read(int server,
   config_postdefaults(c, server);
   if(oldconfig)  {
     int failed = 0;
+#if !_WIN32
     if(strcmp(c->home, oldconfig->home)) {
       disorder_error(0, "'home' cannot be changed without a restart");
       failed = 1;
     }
+#endif
     if(strcmp(c->alias, oldconfig->alias)) {
       disorder_error(0, "'alias' cannot be changed without a restart");
       failed = 1;
@@ -1531,10 +1621,12 @@ int config_read(int server,
       disorder_error(0, "'nice_server' cannot be changed without a restart");
       /* ...but we accept the new config anyway */
     }
+#if HAVE_PCRE_H
     if(namepartlist_compare(&c->namepart, &oldconfig->namepart)) {
       disorder_error(0, "'namepart' settings cannot be changed without a restart");
       failed = 1;
     }
+#endif
     if(stringlist_compare(&c->stopword, &oldconfig->stopword)) {
       disorder_error(0, "'stopword' settings cannot be changed without a restart");
       failed = 1;
@@ -1553,23 +1645,37 @@ int config_read(int server,
 
 /** @brief Return the path to the private configuration file */
 char *config_private(void) {
+#if _WIN32
+  return NULL;
+#else
   char *s;
 
   set_configfile();
   byte_xasprintf(&s, "%s.private", configfile);
   return s;
+#endif
 }
 
 /** @brief Return the path to user's personal configuration file */
 char *config_userconf(const char *home, const struct passwd *pw) {
   char *s;
-
+#if _WIN32
+  wchar_t *wpath = 0;
+  char *appdata;
+  if(SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &wpath) != S_OK)
+    disorder_fatal(0, "error calling SHGetKnownFolderPath");
+  appdata = win_wtomb(wpath);
+  CoTaskMemFree(wpath);
+  byte_xasprintf(&s, "%s\\DisOrder\\passwd", appdata);
+#else
   if(!home && !pw && !(pw = getpwuid(getuid())))
     disorder_fatal(0, "cannot determine our username");
   byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir);
+#endif
   return s;
 }
 
+#if !_WIN32
 /** @brief Return the path to user-specific system configuration */
 char *config_usersysconf(const struct passwd *pw) {
   char *s;
@@ -1589,6 +1695,7 @@ char *config_usersysconf(const struct passwd *pw) {
 char *config_get_file(const char *name) {
   return config_get_file2(config, name);
 }
+#endif
 
 /** @brief Order two stringlists
  * @param a First stringlist
@@ -1612,6 +1719,7 @@ static int stringlist_compare(const struct stringlist *a,
     return 0;
 }
 
+#if HAVE_PCRE_H
 /** @brief Order two namepart definitions
  * @param a First namepart definition
  * @param b Second namepart definition
@@ -1657,13 +1765,15 @@ static int namepartlist_compare(const struct namepartlist *a,
   else
     return 0;
 }
+#endif
 
 /** @brief Verify configuration table.
  * @return The number of problems found
 */
 int config_verify(void) {
   int fails = 0;
-  for(size_t n = 1; n < sizeof conf / sizeof *conf; ++n)
+  size_t n;
+  for(n = 1; n < sizeof conf / sizeof *conf; ++n)
     if(strcmp(conf[n-1].name, conf[n].name) >= 0) {
       fprintf(stderr, "%s >= %s\n", conf[n-1].name, conf[n].name);
       ++fails;