1 /*********************************************************************
2 *
3 * KasumiConfiguration.cxx
4 *
5 * Kasumi - a management tool for a private dictionary of anthy
6 *
7 * Copyright (C) 2004-2006 Takashi Nakamoto
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 *
24 *********************************************************************/
25
26 #include <cstdlib>
27 #include <iostream>
28 #include <fstream>
29 #include <map>
30 #include <list>
31 #include <getopt.h> /* for getopt_long() */
32 #include <anthy/dicutil.h>
33
34 #include "KasumiConfiguration.hxx"
35 #include "KasumiException.hxx"
36 #include "KasumiString.hxx"
37 #include "KasumiWordType.hxx"
38 #include "intl.h"
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 using namespace std;
45
46 // if you want to add a new configuration settings, do the following:
47 // 1. Set default value: Add a line to loadDefaultProperties() method
48 // like;
49 // config[string("NewKey")] = string("DefaultValue");
50 // 2. Add check routine for validating the setting in checkValidity method.
51 // If it accepts only an integer value, it is the best to add the new key's
52 // name to intValueKeyNames list like;
53 // intValueKeyNames.push_back("NewKey");
54 // Or if it is a shortcut key setting, add the new key's name to keyName
55 // list like:
56 // keyName.push_back("NewKey");
57 // 3. If the setting may be set by command line arguments, add some routines
58 // to loadConfigurationFromArgument method
59
KasumiConfiguration(int argc,char * argv[])60 KasumiConfiguration::KasumiConfiguration(int argc, char *argv[])
61 throw(KasumiException){
62
63 try{
64 loadDefaultProperties();
65
66 // ~/.kasumi must be encoded in EUC-JP
67
68 char *home = getenv("HOME");
69 if(home == NULL){
70 throw KasumiException(string("Cannot find $HOME environment variable."),
71 STDERR,
72 KILL);
73 }
74
75 ConfFileName = string(home) + "/.kasumi";
76
77 loadConfigurationFile();
78 }catch(KasumiException e){
79 throw e;
80 }
81
82 loadConfigurationFromArgument(argc, argv);
83 checkValidity();
84 }
85
~KasumiConfiguration()86 KasumiConfiguration::~KasumiConfiguration(){
87 saveConfiguration();
88 }
89
loadDefaultProperties()90 void KasumiConfiguration::loadDefaultProperties() throw(KasumiException){
91 char *home = getenv("HOME");
92 if(home == NULL){
93 throw KasumiException(string("Cannot find $HOME environment variable."),
94 STDERR,
95
96 KILL);
97 }
98
99 config[string("StartupMode")] = string("MANAGE");
100 config[string("DefaultFrequency")] = string("500");
101 config[string("MaxFrequency")] = string("1000");
102 config[string("MinFrequency")] = string("1");
103 // config[string("QuitShortcutKey")] = string("Ctrl+Q");
104 // config[string("StoreShortcutKey")] = string("Ctrl+S");
105 // config[string("NewWordShortcutKey")] = string("Ctrl+N");
106 // config[string("RemoveShortcutKey")] = string("Ctrl+R");
107 // config[string("AddShortcutKey")] = string("Ctrl+A");
108 // config[string("AddingModeShortcutKey")] = string("Ctrl+J");
109 // config[string("ManageModeShortcutKey")] = string("Ctrl+M");
110 config[string("DefaultSpelling")] = string("");
111 config[string("DefaultSound")] = string("");
112 config[string("DefaultWordType")] = string("#T35");
113 config[string("DefaultAddingSpelling")] = string("");
114 config[string("DefaultAddingSound")] = string("");
115 config[string("DefaultAddingWordType")] = string("#T35");
116 // config[string("DefaultWindowPosX")] = string("-1");
117 // config[string("DefaultWindowPosY")] = string("-1");
118 config[string("ImportSelectedText")] = string("true");
119 #ifdef HAS_ANTHY_DICUTIL_SET_ENCODING
120 config[string("UseEUCJP")] = string("false");
121 #else // HAS_ANTHY_DICUTIL_SET_ENCODING
122 config[string("UseEUCJP")] = string("true");
123 #endif // HAS_ANTHY_DICUTIL_SET_ENCODING
124 }
125
loadConfigurationFromArgument(int argc,char * argv[])126 void KasumiConfiguration::loadConfigurationFromArgument(int argc, char *argv[])
127 throw(KasumiException){
128 int option_index = 0;
129 static struct option long_options[] = {
130 {"help", no_argument, NULL, 'h'},
131 {"version", no_argument, NULL, 'v'},
132 {"add", no_argument, NULL, 'a'},
133 {"exclusive", no_argument, NULL, 'e'},
134 {"manage", no_argument, NULL, 'm'},
135 {"sound", required_argument, NULL, 's'},
136 {"spelling", required_argument, NULL, 't'},
137 {"wordclass", required_argument, NULL, 'w'},
138 // {"x", required_argument, NULL, 'x'},
139 // {"y", required_argument, NULL, 'y'},
140 {"import", no_argument, NULL, 'i'},
141 {"ignore", no_argument, NULL, 'I'},
142 #ifdef HAS_ANTHY_DICUTIL_SET_ENCODING
143 {"eucjp", no_argument, NULL, 'E'},
144 #endif // HAS_ANTHY_DICUTIL_SET_ENCODING
145 {0,0,0,0}
146 };
147
148 string message;
149 int c;
150 while(1){
151 // c = getopt_long(argc, argv, "hvamiIns:t:w:x:y:", long_options, &option_index);
152 #ifdef HAS_ANTHY_DICUTIL_SET_ENCODING
153 c = getopt_long(argc, argv, "hvaemiInEs:t:w:", long_options, &option_index);
154 #else // HAS_ANTHY_DICUTIL_SET_ENCODING
155 c = getopt_long(argc, argv, "hvaemiIns:t:w:", long_options, &option_index);
156 #endif // HAS_ANTHY_DICUTIL_SET_ENCODING
157 if(c == -1) break; // no more argument
158
159 switch(c){
160 case 'h':
161 setPropertyValue(string("StartupMode"),string("HELP"));
162 break;
163 case 'v':
164 setPropertyValue(string("StartupMode"),string("VERSION"));
165 break;
166 case 'a':
167 setPropertyValue(string("StartupMode"),string("ADD"));
168 break;
169 case 'm':
170 setPropertyValue(string("StartupMode"),string("MANAGE"));
171 break;
172 case 'e':
173 setPropertyValue(string("StartupMode"),string("EXCLUSIVE"));
174 break;
175 case 's':
176 setPropertyValue(string("DefaultAddingSound"),string(optarg));
177 break;
178 case 't':
179 setPropertyValue(string("DefaultAddingSpelling"),string(optarg));
180 break;
181 case 'w':
182 setPropertyValue(string("DefaultAddingWordType"),string(optarg));
183 break;
184 // case 'x':
185 // setPropertyValue(string("DefaultWindowPosX"),string(optarg));
186 // break;
187 // case 'y':
188 // setPropertyValue(string("DefaultWindowPosY"),string(optarg));
189 // break;
190 case 'i':
191 setPropertyValue(string("ImportSelectedText"),string("true"));
192 break;
193 case 'I':
194 setPropertyValue(string("ImportSelectedText"),string("false"));
195 break;
196 case 'E':
197 setPropertyValue(string("UseEUCJP"),string("true"));
198 break;
199 case '?':
200 case ':':
201 message = string("Invalid argument error. Try '") + argv[0] +
202 string(" --help' for more information.");
203 throw KasumiException(message, STDERR, KILL);
204 break;
205 }
206 }
207
208 if(optind < argc){
209 message = string("Found non-option argument '") + argv[optind] +
210 string("'. Try '") + argv[0] +
211 string(" --help' for more information.");
212 throw KasumiException(message, STDERR, KILL);
213 }
214 }
215
216
loadConfigurationFile()217 void KasumiConfiguration::loadConfigurationFile()
218 throw(KasumiException){
219
220 int line = 0;
221 string Contents = string();
222 KasumiString Buffer;
223
224 ifstream ConfFile(ConfFileName.c_str());
225
226 if(!ConfFile.is_open()){
227 return; // do not load configuration file
228 }
229
230 // analyze Kasumi Configuration file reading each line
231 while(getline(ConfFile, Buffer, '\n')){
232 line++;
233
234 if(Buffer.isCommentLine()){
235 // commented line; nothing to do
236 }else if(Buffer.isEmptyLine()){
237 // empty line; nothing to do
238 }else if(Buffer.isKeyValLine()){
239 config[Buffer.getKey()] = Buffer.getVal();
240 }else{
241 // not classfied line; configuration file is invalid!
242 string message = ConfFileName + string(":") + int2str(line)
243 + string(": invalid entry in configuration file.");
244 throw KasumiException(message, STDERR, KILL);
245 }
246 }
247 }
248
249 // ToDo: implement saveConfiguration method
saveConfiguration()250 void KasumiConfiguration::saveConfiguration()
251 throw(KasumiException){
252
253 }
254
checkValidity()255 void KasumiConfiguration::checkValidity()
256 throw(KasumiException){
257
258 if(config[string("StartupMode")] != string("MANAGE") &&
259 config[string("StartupMode")] != string("ADD") &&
260 config[string("StartupMode")] != string("EXCLUSIVE") &&
261 config[string("StartupMode")] != string("HELP") &&
262 config[string("StartupMode")] != string("VERSION")){
263 string message("StartupMode variable must be \"MANAGE\", \"EXCLUSIVE\" or \"ADD\"");
264 throw KasumiException(message, STDERR, KILL);
265 }
266
267 // check conrresponding settings being an integer
268 list<string> intValueKeyNames;
269
270 intValueKeyNames.push_back(string("DefaultFrequency"));
271 intValueKeyNames.push_back(string("MaxFrequency"));
272 intValueKeyNames.push_back(string("MinFrequency"));
273 // intValueKeyNames.push_back(string("DefaultWindowPosX"));
274 // intValueKeyNames.push_back(string("DefaultWindowPosY"));
275
276 while(!intValueKeyNames.empty()){
277 string keyName = intValueKeyNames.front();
278 intValueKeyNames.pop_front();
279
280 if(!isInt(config[keyName])){
281 string message = keyName + string(" variable must be an integer");
282 throw KasumiException(message, STDERR, KILL);
283 }
284 }
285
286 // check integer value are suitable
287 int def = str2int(config[string("DefaultFrequency")]);
288 int max = str2int(config[string("MaxFrequency")]);
289 int min = str2int(config[string("MinFrequency")]);
290 if(min < 1){
291 throw KasumiException(string("MinFrequency must be greater than 0"),
292 STDERR, KILL);
293 }else if(max < min){
294 throw KasumiException(string("MinFrequency must not be greater than MaxFrequency."),
295 STDERR, KILL);
296 }else if(def > max){
297 throw KasumiException(string("DefaultFrequency must not be greater than MaxFrequency"),
298 STDERR, KILL);
299 }else if(def < min){
300 throw KasumiException(string("DefaultFrequency must not be less than MinFrequency"),
301 STDERR, KILL);
302 }
303
304 // int x = str2int(config[string("DefaultWindowPosX")]);
305 // int y = str2int(config[string("DefaultWindowPosY")]);
306 // if(x < -1){
307 // throw KasumiException(string("DefaultWindowPosX must be -1 or more"),
308 // STDERR, KILL);
309 // }else if(y < -1){
310 // throw KasumiException(string("DefaultWindowPosY must be -1 or more"),
311 // STDERR, KILL);
312 // }
313
314 // check key configurations
315 // throws exeption if there is an invalid key or duplication
316 // map<string,string> registeredKey;
317 // list<string> keyNames;
318
319 // keyNames.push_back(string("QuitShortcutKey"));
320 // keyNames.push_back(string("StoreShortcutKey"));
321 // keyNames.push_back(string("NewWordShortcutKey"));
322 // keyNames.push_back(string("RemoveShortcutKey"));
323 // keyNames.push_back(string("AddShortcutKey"));
324 // keyNames.push_back(string("AddingModeShortcutKey"));
325 // keyNames.push_back(string("ManageModeShortcutKey"));
326
327 // while(!keyNames.empty()){
328 // string keyName = keyNames.front();
329 // keyNames.pop_front();
330 //
331 // string shortKey = config[string(keyName)];
332 // if(!isValidShortcutKey(shortKey)){
333 // string message = string("Invalid shortcut key configuration for ") +
334 // keyName + string(": ") + shortKey;
335 // throw KasumiException(message, STDERR, KILL);
336 // }
337 // if(registeredKey.find(shortKey) == registeredKey.end()){
338 // registeredKey.insert(make_pair(shortKey,keyName));
339 // }else{
340 // string message = string("Failed to set ") + keyName + string(" variable; ") + shortKey + string(" has been already registered as ") + registeredKey[shortKey];
341 // throw KasumiException(message, STDERR, KILL);
342 // }
343 // }
344
345 // check WordType configuration
346 list<string> keyForWordType;
347 map<string,bool> validWordType;
348
349 keyForWordType.push_back(string("DefaultWordType"));
350 keyForWordType.push_back(string("DefaultAddingWordType"));
351
352 WordTypeList::iterator p = KasumiWordType::beginWordTypeList();
353 while(p != KasumiWordType::endWordTypeList())
354 {
355 validWordType.insert(make_pair((*p)->getCannaTab(),true));
356 p++;
357 }
358
359 while(!keyForWordType.empty()){
360 string keyName = keyForWordType.front();
361 keyForWordType.pop_front();
362 string val = config[keyName];
363
364 if(validWordType.find(val) == validWordType.end()){
365 string message = val + string(" is an invalid word type for ") + keyName;
366 throw KasumiException(message, STDERR, KILL);
367 }
368 }
369
370 // check conrresponding settings being an boolean
371 list<string> booleanValueKeyNames;
372
373 booleanValueKeyNames.push_back(string("ImportSelectedText"));
374
375 while(!booleanValueKeyNames.empty()){
376 string keyName = booleanValueKeyNames.front();
377 booleanValueKeyNames.pop_front();
378
379 if(config[keyName] != "true" && config[keyName] != "false"){
380 throw KasumiException(keyName + string(" variable must be a boolean"),
381 STDERR, KILL);
382 }
383 }
384
385 // no check for:
386 // DefaultSpelling
387 // DefaultSound
388 // DefaultAddingSpelling
389 // DefaultAddingSound
390 // ToDo: confirm default sounds do not have invalid character
391 }
392
setPropertyValue(const string & name,const string & value)393 void KasumiConfiguration::setPropertyValue(const string &name, const string &value){
394 map<string,string>::iterator p;
395
396 p = config.find(name);
397
398 if(p == config.end()){
399 cerr << "error: you cannot set " << name << " property." << endl;
400 exit(1);
401 }
402
403 config[name] = value;
404 }
405
getPropertyValue(const string & name)406 string KasumiConfiguration::getPropertyValue(const string &name){
407 map<string,string>::iterator p;
408
409 p = config.find(name);
410
411 if(p == config.end()){
412 cerr << "error: " << name << " property has not been set yet." << endl;
413 exit(1);
414 }
415
416 return p->second;
417 }
418
getPropertyValueByInt(const string & name)419 int KasumiConfiguration::getPropertyValueByInt(const string &name){
420 map<string,string>::iterator p;
421
422 p = config.find(name);
423
424 if(p == config.end()){
425 cerr << "error: " << name << " property has not been set yet." << endl;
426 exit(1);
427 }
428
429 return str2int(p->second);
430 }
431
getPropertyValueByBool(const string & name)432 bool KasumiConfiguration::getPropertyValueByBool(const string &name){
433 map<string,string>::iterator p;
434
435 p = config.find(name);
436 if(p == config.end()){
437 cerr << "error: " << name << " property has not been set yet." << endl;
438 exit(1);
439 }
440
441 if(p->second == "true"){
442 return true;
443 } else{
444 return false;
445 }
446 }
447
448 /*
449 bool isValidShortcutKey(const string &key){
450 string::size_type i;
451
452 i = key.find("+",0);
453 string shortkey = key.substr(i+1);
454 if(shortkey.length() != 1){
455 return false;
456 }
457 char c = shortkey.c_str()[0];
458 if((c < 'A' || c > 'Z') && (c < '0' || c > '9')){
459 return false;
460 }
461
462 i = key.find("+",0);
463 if(i == key.npos){
464 return true;
465 }
466 string mask = key.substr(0,i);
467 if(mask == "Ctrl"){
468 return true;
469 }else if(mask == "Alt"){
470 return true;
471 }
472
473 return false;
474 }
475 */
476