Added "search" architecture
[WebThing.git] / Config.cs
1 using System;
2 using System.IO;
3 using System.Collections;
4 using System.Collections.Generic;
5 using System.Text.RegularExpressions;
6
7 namespace bytex64.WebThing {
8     public struct SearchHandlerPair {
9         public string Shortcut;
10         public string Plugin;
11
12         public SearchHandlerPair(string s, string p) {
13             Shortcut = s;
14             Plugin = p;
15         }
16     }
17
18     public class Config {
19         public static List<string> ConfigPath;
20         public static string ConfigPathOut = null;
21
22         public static Dictionary<string,string> Options;
23         public static Dictionary<string,Dictionary<string,string>> PluginOptions;
24         public static string[] Arguments;
25         public static List<string> Plugins;
26         public static List<SearchHandlerPair> SearchHandlers;
27
28         private static HashSet<string> CommandLineOptions;
29         private static Regex item_re = new Regex(@"^(?:([\w.]+)\.)?(\w+)$");
30         private static Regex file_var_re = new Regex(@"^([\w.]+)\s*=\s*(.*)$");
31         private static Regex file_command_re = new Regex(@"^([\w]+)\s*(.*)$");
32
33         static Config() {
34             Options = new Dictionary<string,string>();
35             PluginOptions = new Dictionary<string,Dictionary<string,string>>();
36             CommandLineOptions = new HashSet<string>();
37             Plugins = new List<string>();
38             SearchHandlers = new List<SearchHandlerPair>();
39
40             // Set up ConfigPath
41             IDictionary Env = Environment.GetEnvironmentVariables();
42             ConfigPath = new List<string>();
43             ConfigPath.Add("/etc/WebThing");
44             if (Env.Contains("HOME")) {
45                 string homepath = Env["HOME"] + "/.config/WebThing";
46                 ConfigPath.Add(homepath);
47                 if (Directory.Exists(homepath))
48                     ConfigPathOut = homepath;
49             }
50             string LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
51             if (LocalAppData != "") {
52                 string homepath = LocalAppData + "/WebThing";
53                 ConfigPath.Add(homepath);
54                 if (Directory.Exists(homepath))
55                     ConfigPathOut = homepath;
56             }
57         }
58
59         private static void ParseOption(string key, string val) {
60             Match m = item_re.Match(key);
61             string plugin = m.Groups[1].Value;
62             string name = m.Groups[2].Value;
63             if (plugin.Length > 0) {   // Plugin Option
64                 if (!PluginOptions.ContainsKey(plugin))
65                     PluginOptions[plugin] = new Dictionary<string,string>();
66                 PluginOptions[plugin][name] = val;
67             } else {                   // Global Option
68                 switch(key.ToLower()) {
69                 case "plugin":
70                     Plugins.Add(val);
71                     break;
72                 default:
73                     Options[key] = val;
74                     break;
75                 }
76             }
77         }
78
79         public static void DumpOptions() {
80             Console.WriteLine("Options:");
81             foreach (string key in Options.Keys)
82                 Console.WriteLine("   {0}\t{1}", key, Options[key]);
83
84             Console.WriteLine("Plugin Options:");
85             foreach (string plugin in PluginOptions.Keys) {
86                 Console.WriteLine("    {0}", plugin);
87                 foreach (string key in PluginOptions[plugin].Keys)
88                     Console.WriteLine("        {0}\t{1}", key, PluginOptions[plugin][key]);
89             }
90
91             Console.WriteLine("Arguments: {0}", String.Join(" ", Arguments));
92         }
93
94         public static void ParseCommandLine() {
95             Queue<string> q = new Queue<string>();
96             foreach (string s in System.Environment.GetCommandLineArgs()) {
97                 q.Enqueue(s);
98             }
99             q.Dequeue();   // Remove self argument
100
101             Regex SimpleOpt = new Regex(@"^-([\w.]+)(?:[:=](.*))?$");
102             Regex LongOpt   = new Regex(@"^--([\w.]+)$");
103             List<string> args = new List<string>();
104
105             while (q.Count > 0) {
106                 string opt = q.Dequeue();
107                 Match m;
108                    
109                 m = SimpleOpt.Match(opt);
110                 if (m.Success) {
111                     string key = m.Groups[1].Value;
112                     string val = m.Groups[2].Value;
113                     if (val.Length > 0) {
114                         ParseOption(key, val);
115                     } else {
116                         ParseOption(key, null);
117                     }
118                     CommandLineOptions.Add(key);
119                     continue;
120                 }
121
122                 m = LongOpt.Match(opt);
123                 if (m.Success) {
124                     string key = m.Groups[1].Value;
125                     if (q.Count > 0) {
126                         string val = q.Peek();
127                         if (!(SimpleOpt.IsMatch(val) || LongOpt.IsMatch(val))) {
128                             q.Dequeue();
129                             ParseOption(key, val);
130                         } else {
131                             ParseOption(key, null);
132                         }
133                     } else {
134                         ParseOption(key, null);
135                     }
136                     CommandLineOptions.Add(key);
137                     continue;
138                 }
139
140                 // else
141
142                 args.Add(opt);
143             }
144             Arguments = args.ToArray();
145         }
146
147         public static void Load() {
148             foreach (string path in ConfigPath) {
149                 if (!Directory.Exists(path)) continue;
150                 string[] ConfigFiles = Directory.GetFiles(path, "*.conf");
151                 foreach (string file in ConfigFiles) {
152                     LoadFile(file);
153                 }
154             }
155             if (Options.ContainsKey("config"))
156                 LoadFile(Options["config"]);
157         }
158
159         public static void LoadFile(string filename) {
160             TextReader f;
161             try {
162                 f = new StreamReader(filename);
163             } catch (FileNotFoundException) {
164                 Console.WriteLine("Could not open configuration file {0}", filename);
165                 return;
166             }
167
168             string line;
169             while ((line = f.ReadLine()) != null) {
170                 Match m;
171
172                 line = line.Trim();
173                 m = file_var_re.Match(line);
174                 if (m.Success) {
175                     string key = m.Groups[1].Value;
176                     if (!CommandLineOptions.Contains(key))
177                         ParseOption(key, m.Groups[2].Value);
178                     continue;
179                 }
180
181                 m = file_command_re.Match(line);
182                 if (m.Success) {
183                     string cmd = m.Groups[1].Value.ToLower();
184                     switch(cmd) {
185                     case "plugin":
186                         Plugins.Add(m.Groups[2].Value);
187                         break;
188                     case "searchhandler":
189                         string[] args = Regex.Split(m.Groups[2].Value, @"\s+");
190                         if (args.Length != 2) {
191                             Console.WriteLine("Expecting two arguments for SearchHandler.");
192                             break;
193                         }
194                         SearchHandlers.Add(new SearchHandlerPair(args[0], args[1]));
195                         break;
196                     default:
197                         Console.WriteLine("Unknown Command in {0}: {1}", filename, line);
198                         break;
199                     }
200                     continue;
201                 }
202             }
203
204             f.Close();
205         }
206
207         public static void SaveFile(string filename) {
208             TextWriter f;
209             try {
210                 f = new StreamWriter(filename);
211             } catch (IOException) {
212                 Console.WriteLine("Could not save configuration to {0}", filename);
213                 return;
214             }
215
216             foreach (string key in Options.Keys) {
217                 if (CommandLineOptions.Contains(key)) continue;
218                 f.WriteLine("{0} = {1}", key, Options[key]);
219             }
220
221             foreach (string plugin in PluginOptions.Keys) {
222                 foreach (string key in PluginOptions[plugin].Keys) {
223                     string pkey = String.Format("{0}.{1}", plugin, key);
224                     if (CommandLineOptions.Contains(pkey)) continue;
225                     f.WriteLine("{0} = {1}", pkey, PluginOptions[plugin][key]);
226                 }
227             }
228
229             f.Close();
230         }
231
232         public static void SaveFile(WebThingPlugin p, string filename) {
233             string plugin_name = p.GetType().FullName;
234             SaveFile(plugin_name, filename);
235         }
236
237         public static void SaveFile(string plugin_name, string filename) {
238             // TODO: Throw exception if plugin does not exist
239             TextWriter f = new StreamWriter(filename);
240
241             foreach (string key in PluginOptions[plugin_name].Keys) {
242                 f.WriteLine("{0}.{1} = {2}", plugin_name, key, PluginOptions[plugin_name][key]);
243             }
244
245             f.Close();
246         }
247
248         // Data parsers
249         public static bool ParseBool(string v) {
250             switch(v.ToLower()) {
251             case "true":
252             case "t":
253             case "1":
254             case "on":
255             case "yes":
256                 return true;
257             }
258             return false;
259         }
260     }
261 }