Added MicroConf class for configuration file parsing.
authorStefan Westerfeld <stefan@space.twc.de>
Sun, 16 May 2010 17:19:55 +0000 (19:19 +0200)
committerStefan Westerfeld <stefan@space.twc.de>
Sun, 16 May 2010 17:19:55 +0000 (19:19 +0200)
src/Makefile.am
src/jacksampler.cc
src/main.cc
src/microconf.cc [new file with mode: 0644]
src/microconf.hh [new file with mode: 0644]

index 4606d3a..e9cae5e 100644 (file)
@@ -2,5 +2,5 @@ AM_CXXFLAGS = $(BSE_CFLAGS) $(JACK_CFLAGS)
 
 bin_PROGRAMS = jacksampler
 
-jacksampler_SOURCES = jacksampler.cc main.cc jacksampler.hh main.hh
+jacksampler_SOURCES = jacksampler.cc main.cc jacksampler.hh main.hh microconf.cc microconf.hh
 jacksampler_LDADD = $(BSE_LIBS) $(JACK_LIBS)
index 791804d..79abecb 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "main.hh"
 #include "jacksampler.hh"
+#include "microconf.hh"
 
 using std::string;
 using std::vector;
@@ -312,43 +313,32 @@ JackSampler::load_note (const Options& options, int note, const char *file_name,
 void
 JackSampler::parse_config (const Options& options, int instrument, const char *name)
 {
-  FILE *config = fopen (name, "r");
+  MicroConf cfg (name);
 
-  char buffer[1024];
-
-  while (fgets (buffer, 1024, config))
+  while (cfg.next())
     {
-      const char *sep = " \t\n";
-      const char *command = strtok (buffer, sep);
-      if (command && strcmp (command, "sample") == 0)
-        {
-          const char *note_str = strtok (NULL, sep);
+      int    note = 0;
+      double d = 0;
+      string file;
 
-          if (note_str)
-            {
-              int note = atoi (note_str);
-              const char *file = strtok (NULL, sep);
-              load_note (options, note, file, instrument);
-              printf ("NOTE %d FILE %s\n", note, file);
-            }
+      if (cfg.command ("sample", note, file))
+        {
+          load_note (options, note, file.c_str(), instrument);
+          printf ("NOTE %d FILE %s\n", note, file.c_str());
         }
-      else if (command && strcmp (command, "release_delay") == 0)
+      else if (cfg.command ("release_delay", d))
         {
-          const char *rd_str = strtok (NULL, sep);
-          if (rd_str)
-            {
-              release_delay_ms = atof (rd_str);
-              printf ("RELEASE_DELAY %f ms\n", release_delay_ms);
-            }
+          release_delay_ms = d;
+          printf ("RELEASE_DELAY %f ms\n", release_delay_ms);
         }
-      else if (command && strcmp (command, "release") == 0)
+      else if (cfg.command ("release", d))
         {
-          const char *rd_str = strtok (NULL, sep);
-          if (rd_str)
-            {
-              release_ms = atof (rd_str);
-              printf ("RELEASE %f ms\n", release_ms);
-            }
+          release_ms = d;
+          printf ("RELEASE %f ms\n", release_ms);
+        }
+      else
+        {
+          cfg.die_if_unknown();
         }
     }
 }
index e4d877c..a62eba1 100644 (file)
@@ -24,7 +24,7 @@
 #include "main.hh"
 #include "jacksampler.hh"
 
-bool
+static bool
 is_newline (char ch)
 {
   return (ch == '\n' || ch == '\r');
diff --git a/src/microconf.cc b/src/microconf.cc
new file mode 100644 (file)
index 0000000..fe431a8
--- /dev/null
@@ -0,0 +1,148 @@
+/* MicroConf - minimal configuration framework
+ * Copyright (C) 2010 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <stdlib.h>
+#include "microconf.hh"
+
+using std::string;
+using std::vector;
+
+MicroConf::MicroConf (const string& filename)
+{
+  cfg_file = fopen (filename.c_str(), "r");
+
+  current_file = filename;
+  current_no = 0;
+}
+
+static bool
+is_newline (char ch)
+{
+  return (ch == '\n' || ch == '\r');
+}
+
+bool
+MicroConf::next()
+{
+  char s[1024];
+
+  if (!fgets (s, 1024, cfg_file))
+    return false; // eof
+
+  current_line = s;
+  current_no++;
+
+  // strip newline at end of input
+  while (!current_line.empty() && is_newline (current_line[current_line.size() - 1]))
+    current_line.resize (current_line.size() - 1);
+
+  tokenize();
+
+  return true;
+}
+
+string
+MicroConf::line()
+{
+  return current_line;
+}
+
+bool
+string_chars (char ch)
+{
+  if ((ch >= 'A' && ch <= 'Z')
+  ||  (ch >= '0' && ch <= '9')
+  ||  (ch >= 'a' && ch <= 'z')
+  ||  (ch == '.')
+  ||  (ch == '/')
+  ||  (ch == '-')
+  ||  (ch == '_'))
+    return true;
+
+  return false;
+}
+
+bool
+white_space (char ch)
+{
+  return (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r');
+}
+
+void
+MicroConf::tokenize()
+{
+  enum { BLANK, STRING, COMMENT } state = BLANK;
+  string s;
+
+  string xline = current_line + '\n';
+  tokens.clear();
+  for (string::const_iterator i = xline.begin(); i != xline.end(); i++)
+    {
+      if (state == BLANK && string_chars (*i))
+        {
+          state = STRING;
+          s += *i;
+        }
+      else if (state == STRING && string_chars (*i))
+        {
+          s += *i;
+        }
+      else if (state == STRING && white_space (*i))
+        {
+          tokens.push_back (s);
+          s = "";
+          state = BLANK;
+        }
+      else if (*i == '#')
+        {
+          state = COMMENT;
+        }
+    }
+}
+
+bool
+MicroConf::convert (const std::string& token, int& arg)
+{
+  arg = atoi (token.c_str());
+  return true;
+}
+
+bool
+MicroConf::convert (const std::string& token, double& arg)
+{
+  arg = atof (token.c_str());
+  return true;
+}
+
+bool
+MicroConf::convert (const std::string& token, std::string& arg)
+{
+  arg = token;
+  return true;
+}
+
+void
+MicroConf::die_if_unknown()
+{
+  if (tokens.size())
+    {
+      fprintf (stderr, "configuration file %s: bad line number %d: %s\n",
+               current_file.c_str(), current_no, current_line.c_str());
+      exit (1);
+    }
+}
diff --git a/src/microconf.hh b/src/microconf.hh
new file mode 100644 (file)
index 0000000..107c44c
--- /dev/null
@@ -0,0 +1,61 @@
+/* MicroConf - minimal configuration framework
+ * Copyright (C) 2010 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <string>
+#include <vector>
+
+#include <stdio.h>
+
+class MicroConf
+{
+private:
+  FILE                    *cfg_file;
+  std::string              current_line;
+  int                      current_no;
+  std::string              current_file;
+  std::vector<std::string> tokens;
+
+  bool convert (const std::string& token, int& arg);
+  bool convert (const std::string& token, double& arg);
+  bool convert (const std::string& token, std::string& arg);
+
+  void tokenize();
+public:
+  MicroConf (const std::string& filename);
+
+  bool next();
+  std::string line();
+  void die_if_unknown();
+
+  template<class T1>
+  bool command (const std::string& cmd, T1& arg1)
+  {
+    if (tokens.size() != 2 || cmd != tokens[0])
+      return false;
+    return convert (tokens[1], arg1);
+  }
+  template<class T1, class T2>
+  bool command (const std::string& cmd, T1& arg1, T2& arg2)
+  {
+    if (tokens.size() != 3 || cmd != tokens[0])
+      return false;
+    return convert (tokens[1], arg1) && convert (tokens[2], arg2);
+  }
+};
+
+