1 // This file is part of The New Aspell
2 // Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license
3 // version 2.0 or 2.1. You should have received a copy of the LGPL
4 // license along with this library if you did not you can find
5 // it at http://www.gnu.org/.
8 //#define DEBUG {fprintf(stderr,"File: %s(%i)\n",__FILE__,__LINE__);}
21 #ifdef HAVE_LANGINFO_CODESET
22 # include <langinfo.h>
26 #include "asc_ctype.hpp"
29 #include "file_util.hpp"
30 #include "fstream.hpp"
31 #include "getdata.hpp"
32 #include "itemize.hpp"
33 #include "mutable_container.hpp"
34 #include "posib_err.hpp"
35 #include "string_map.hpp"
36 #include "stack_ptr.hpp"
37 #include "char_vector.hpp"
38 #include "convert.hpp"
39 #include "vararray.hpp"
40 #include "string_list.hpp"
44 #include "iostream.hpp"
46 #define DEFAULT_LANG "en_US"
48 // NOTE: All filter options are now stored with he "f-" prefix. However
49 // during lookup, the non prefix version is also recognized.
51 // The "place_holder" field in Entry and the "Vector<int>" parameter of
52 // commit_all are there to deal with the fact than when spacing
53 // options on the command line such as "--key what" it can not be
54 // determined if "what" should be a the value of "key" or if it should
55 // be treated as an independent arg. This is because "key" may
56 // be a filter option. Filter options KeyInfo are not loaded until
57 // after a commit which is not done at the time the options are being
58 // read in from the command line. (If the command line arguments are
59 // read in after the other settings are read in and committed than any
60 // options setting any of the config files will be ignored. Thus the
61 // command line must be parsed and options must be added in an
62 // uncommitted state). So the solution is to assume it is an
63 // independent arg until told otherwise, the position in the arg array
64 // is stored along with the value in the "place_holder" field. When
65 // the config class is finally committed and it is determined that
66 // "what" is really a value for key the stored arg position is pushed
67 // onto the Vector<int> so it can be removed from the arg array. In
68 // the case of a "lset-*" this will happen in multiple config
69 // "Entry"s, so care is taken to only add the arg position once.
73 const char * const keyinfo_type_name[4] = {
74 N_("string"), N_("integer"), N_("boolean"), N_("list")
77 const int Config::num_parms_[9] = {1, 1, 0, 0, 0,
80 typedef Notifier * NotifierPtr;
82 Config::Config(ParmStr name,
83 const KeyInfo * mainbegin,
84 const KeyInfo * mainend)
86 , first_(0), insert_point_(&first_), others_(0)
87 , committed_(true), attached_(false)
88 , md_info_list_index(-1)
89 , settings_read_in_(false)
91 , filter_mode_notifier(0)
93 keyinfo_begin = mainbegin;
94 keyinfo_end = mainend;
103 Config::Config(const Config & other)
108 Config & Config::operator= (const Config & other)
115 Config * Config::clone() const {
116 return new Config(*this);
119 void Config::assign(const Config * other) {
120 *this = *(const Config *)(other);
123 void Config::copy(const Config & other)
125 assert(other.others_ == 0);
130 committed_ = other.committed_;
131 attached_ = other.attached_;
132 settings_read_in_ = other.settings_read_in_;
134 keyinfo_begin = other.keyinfo_begin;
135 keyinfo_end = other.keyinfo_end;
136 extra_begin = other.extra_begin;
137 extra_end = other.extra_end;
138 filter_modules = other.filter_modules;
141 filter_modules_ptrs = other.filter_modules_ptrs;
142 for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin();
143 i != filter_modules_ptrs.end();
148 md_info_list_index = other.md_info_list_index;
151 Entry * const * src = &other.first_;
152 Entry * * dest = &first_;
155 *dest = new Entry(**src);
156 if (src == other.insert_point_)
157 insert_point_ = dest;
158 src = &((*src)->next);
159 dest = &((*dest)->next);
161 if (insert_point_ == 0)
162 insert_point_ = dest;
165 Vector<Notifier *>::const_iterator i = other.notifier_list.begin();
166 Vector<Notifier *>::const_iterator end = other.notifier_list.end();
168 for(; i != end; ++i) {
169 Notifier * tmp = (*i)->clone(this);
171 notifier_list.push_back(tmp);
178 Entry * tmp = first_->next;
184 Entry * tmp = others_->next;
189 Vector<Notifier *>::iterator i = notifier_list.begin();
190 Vector<Notifier *>::iterator end = notifier_list.end();
192 for(; i != end; ++i) {
197 notifier_list.clear();
200 filter_modules.clear();
201 for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin();
202 i != filter_modules_ptrs.end();
205 filter_modules_ptrs.clear();
209 void Config::set_filter_modules(const ConfigModule * modbegin,
210 const ConfigModule * modend)
212 assert(filter_modules_ptrs.empty());
213 filter_modules.clear();
214 filter_modules.assign(modbegin, modend);
217 void Config::set_extra(const KeyInfo * begin,
233 NotifierEnumeration * Config::notifiers() const
235 return new NotifierEnumeration(notifier_list);
238 bool Config::add_notifier(Notifier * n)
240 Vector<Notifier *>::iterator i = notifier_list.begin();
241 Vector<Notifier *>::iterator end = notifier_list.end();
243 while (i != end && *i != n)
252 notifier_list.push_back(n);
258 bool Config::remove_notifier(const Notifier * n)
260 Vector<Notifier *>::iterator i = notifier_list.begin();
261 Vector<Notifier *>::iterator end = notifier_list.end();
263 while (i != end && *i != n)
273 notifier_list.erase(i);
279 bool Config::replace_notifier(const Notifier * o,
282 Vector<Notifier *>::iterator i = notifier_list.begin();
283 Vector<Notifier *>::iterator end = notifier_list.end();
285 while (i != end && *i != o)
305 const Config::Entry * Config::lookup(const char * key) const
307 const Entry * res = 0;
308 const Entry * cur = first_;
311 if (cur->key == key && cur->action != NoOp) res = cur;
315 if (!res || res->action == Reset) return 0;
319 bool Config::have(ParmStr key) const
321 PosibErr<const KeyInfo *> pe = keyinfo(key);
322 if (pe.has_err()) {pe.ignore_err(); return false;}
323 return lookup(pe.data->name);
326 PosibErr<String> Config::retrieve(ParmStr key) const
328 RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
329 if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name);
331 const Entry * cur = lookup(ki->name);
333 return cur ? cur->value : get_default(ki);
336 PosibErr<String> Config::retrieve_any(ParmStr key) const
338 RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
340 if (ki->type != KeyInfoList) {
341 const Entry * cur = lookup(ki->name);
342 return cur ? cur->value : get_default(ki);
345 RET_ON_ERR(retrieve_list(key, &sl));
346 StringListEnumeration els = sl.elements_obj();
349 while ( (s = els.next()) != 0 ) {
358 PosibErr<bool> Config::retrieve_bool(ParmStr key) const
360 RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
361 if (ki->type != KeyInfoBool) return make_err(key_not_bool, ki->name);
363 const Entry * cur = lookup(ki->name);
365 String value(cur ? cur->value : get_default(ki));
367 if (value == "false") return false;
371 PosibErr<int> Config::retrieve_int(ParmStr key) const
373 assert(committed_); // otherwise the value may not be an integer
374 // as it has not been verified.
376 RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
377 if (ki->type != KeyInfoInt) return make_err(key_not_int, ki->name);
379 const Entry * cur = lookup(ki->name);
381 String value(cur ? cur->value : get_default(ki));
383 return atoi(value.str());
386 void Config::lookup_list(const KeyInfo * ki,
387 MutableContainer & m,
388 bool include_default) const
390 const Entry * cur = first_;
391 const Entry * first_to_use = 0;
394 if (cur->key == ki->name &&
395 (first_to_use == 0 ||
396 cur->action == Reset || cur->action == Set
397 || cur->action == ListClear))
404 if (include_default &&
406 !(cur->action == Set || cur->action == ListClear)))
408 String def = get_default(ki);
409 separate_list(def, m, true);
412 if (cur && cur->action == Reset) {
416 if (cur && cur->action == Set) {
417 if (!include_default) m.clear();
422 if (cur && cur->action == ListClear) {
423 if (!include_default) m.clear();
428 if (cur->key == ki->name) {
429 if (cur->action == ListAdd)
431 else if (cur->action == ListRemove)
432 m.remove(cur->value);
438 PosibErr<void> Config::retrieve_list(ParmStr key,
439 MutableContainer * m) const
441 RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
442 if (ki->type != KeyInfoList) return make_err(key_not_list, ki->name);
444 lookup_list(ki, *m, true);
449 static const KeyInfo * find(ParmStr key,
454 if (strcmp(key, i->name) == 0)
461 static const ConfigModule * find(ParmStr key,
462 const ConfigModule * i,
463 const ConfigModule * end)
466 if (strcmp(key, i->name) == 0)
473 PosibErr<const KeyInfo *> Config::keyinfo(ParmStr key) const
475 typedef PosibErr<const KeyInfo *> Ret;
478 i = acommon::find(key, keyinfo_begin, keyinfo_end);
479 if (i != keyinfo_end) return Ret(i);
481 i = acommon::find(key, extra_begin, extra_end);
482 if (i != extra_end) return Ret(i);
484 const char * s = strncmp(key, "f-", 2) == 0 ? key + 2 : key.str();
485 const char * h = strchr(s, '-');
486 if (h == 0) goto err;
489 const ConfigModule * j = acommon::find(k,
490 filter_modules.pbegin(),
491 filter_modules.pend());
493 if (j == filter_modules.pend() && load_filter_hook && committed_) {
494 // FIXME: This isn't quite right
495 PosibErrBase pe = load_filter_hook(const_cast<Config *>(this), k);
498 filter_modules.pbegin(),
499 filter_modules.pend());
502 if (j == filter_modules.pend()) goto err;
504 i = acommon::find(key, j->begin, j->end);
505 if (i != j->end) return Ret(i);
507 if (strncmp(key, "f-", 2) != 0) k = "f-";
510 i = acommon::find(k, j->begin, j->end);
511 if (i != j->end) return Ret(i);
514 return Ret().prim_err(unknown_key, key);
517 static bool proc_locale_str(ParmStr lang, String & final_str)
519 if (lang == 0) return false;
520 const char * i = lang;
521 if (!(asc_islower(i[0]) && asc_islower(i[1]))) return false;
522 final_str.assign(i, 2);
524 if (! (i[0] == '_' || i[0] == '-')) return true;
526 if (!(asc_isupper(i[0]) && asc_isupper(i[1]))) return true;
528 final_str.append(i, 2);
532 static void get_lang_env(String & str)
534 if (proc_locale_str(getenv("LC_MESSAGES"), str)) return;
535 if (proc_locale_str(getenv("LANG"), str)) return;
536 if (proc_locale_str(getenv("LANGUAGE"), str)) return;
542 static void get_lang(String & final_str)
544 // FIXME: THIS IS NOT THREAD SAFE
545 String locale = setlocale (LC_ALL, NULL);
547 setlocale (LC_ALL, "");
548 const char * lang = setlocale (LC_MESSAGES, NULL);
549 bool res = proc_locale_str(lang, final_str);
551 setlocale(LC_MESSAGES, locale.c_str());
553 get_lang_env(final_str);
558 static inline void get_lang(String & str)
565 #if defined USE_LOCALE && defined HAVE_LANGINFO_CODESET
567 static inline void get_encoding(const Config & c, String & final_str)
569 const char * codeset = nl_langinfo(CODESET);
570 if (ascii_encoding(c, codeset)) codeset = "none";
576 static inline void get_encoding(const Config &, String & final_str)
583 String Config::get_default(const KeyInfo * ki) const
585 bool in_replace = false;
588 const char * i = ki->def;
589 if (*i == '!') { // special cases
592 if (strcmp(i, "lang") == 0) {
595 if (entry = lookup("actual-lang"), entry) {
597 } else if (have("master")) {
598 final_str = "<unknown>";
603 } else if (strcmp(i, "encoding") == 0) {
605 get_encoding(*this, final_str);
607 } else if (strcmp(i, "special") == 0) {
613 abort(); // this should not happen
617 } else for(; *i; ++i) {
627 } else { // in_replace
629 if (*i == '/' || *i == ':' || *i == '|' || *i == '#' || *i == '^') {
633 while (*i != '\0' && *i != '>') second += *i++;
635 String s1 = retrieve(replace);
636 String s2 = retrieve(second);
637 final_str += add_possible_dir(s1, s2);
638 } else if (sep == ':') {
639 String s1 = retrieve(replace);
640 final_str += add_possible_dir(s1, second);
641 } else if (sep == '#') {
642 String s1 = retrieve(replace);
643 assert(second.size() == 1);
645 while (s != s1.size() && s1[s] != second[0]) ++s;
646 final_str.append(s1, s);
647 } else if (sep == '^') {
648 String s1 = retrieve(replace);
649 String s2 = retrieve(second);
650 final_str += figure_out_dir(s1, s2);
651 } else { // sep == '|'
652 assert(replace[0] == '$');
653 const char * env = getenv(replace.c_str()+1);
654 final_str += env ? env : second;
659 } else if (*i == '>') {
661 final_str += retrieve(replace).data;
677 PosibErr<String> Config::get_default(ParmStr key) const
679 RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
680 return get_default(ki);
685 #define TEST(v,l,a) \
687 if (len == l && memcmp(s, v, l) == 0) { \
688 if (action) *action = a; \
693 const char * Config::base_name(const char * s, Action * action)
695 if (action) *action = Set;
696 const char * c = strchr(s, '-');
698 unsigned len = c - s;
699 TEST("reset", 5, Reset);
700 TEST("enable", 6, Enable);
701 TEST("dont", 4, Disable);
702 TEST("disable", 7, Disable);
703 TEST("lset", 4, ListSet);
704 TEST("rem", 3, ListRemove);
705 TEST("remove", 6, ListRemove);
706 TEST("add", 3, ListAdd);
707 TEST("clear", 5, ListClear);
713 void separate_list(ParmStr value, AddableContainer & out, bool do_unescape)
715 unsigned len = value.size();
717 VARARRAY(char, buf, len + 1);
718 memcpy(buf, value, len + 1);
722 char * end = buf + len;
726 if (do_unescape) while (*s == ' ' || *s == '\t') ++s;
730 if (do_unescape && *s == '\\') {
732 if (*s == '\0') break;
736 if (*s == ':') break;
737 if (!do_unescape || (*s != ' ' && *s != '\t')) e = s;
744 if (do_unescape) unescape(b);
752 void combine_list(String & res, const StringList & in)
755 StringListEnumeration els = in.elements_obj();
757 while ( (s = els.next()) != 0)
766 if (res.back() == ':') res.pop_back();
769 struct ListAddHelper : public AddableContainer
772 Config::Entry * orig_entry;
773 PosibErr<bool> add(ParmStr val);
776 PosibErr<bool> ListAddHelper::add(ParmStr val)
778 Config::Entry * entry = new Config::Entry(*orig_entry);
780 entry->action = Config::ListAdd;
785 void Config::replace_internal(ParmStr key, ParmStr value)
787 Entry * entry = new Entry;
789 entry->value = value;
791 entry->next = *insert_point_;
792 *insert_point_ = entry;
793 insert_point_ = &entry->next;
796 PosibErr<void> Config::replace(ParmStr key, ParmStr value)
798 Entry * entry = new Entry;
800 entry->value = value;
804 PosibErr<void> Config::remove(ParmStr key)
806 Entry * entry = new Entry;
808 entry->action = Reset;
812 PosibErr<void> Config::set(Entry * entry0, bool do_unescape)
814 StackPtr<Entry> entry(entry0);
816 if (entry->action == NoOp)
817 entry->key = base_name(entry->key.str(), &entry->action);
819 if (num_parms(entry->action) == 0 && !entry->value.empty())
821 if (entry->place_holder == -1) {
822 switch (entry->action) {
824 return make_err(no_value_reset, entry->key);
826 return make_err(no_value_enable, entry->key);
828 return make_err(no_value_disable, entry->key);
830 return make_err(no_value_clear, entry->key);
832 abort(); // this shouldn't happen
835 entry->place_holder = -1;
839 if (entry->action != ListSet) {
841 switch (entry->action) {
843 entry->value = "true";
847 entry->value = "false";
853 if (do_unescape) unescape(entry->value.mstr());
855 entry->next = *insert_point_;
856 *insert_point_ = entry;
857 insert_point_ = &entry->next;
859 if (committed_) RET_ON_ERR(commit(entry0)); // entry0 == entry
861 } else { // action == ListSet
863 Entry * ent = new Entry;
864 ent->key = entry->key;
865 ent->action = ListClear;
868 ListAddHelper helper;
869 helper.config = this;
870 helper.orig_entry = entry;
872 separate_list(entry->value.str(), helper, do_unescape);
877 PosibErr<void> Config::merge(const Config & other)
879 const Entry * src = other.first_;
882 Entry * entry = new Entry(*src);
883 entry->next = *insert_point_;
884 *insert_point_ = entry;
885 insert_point_ = &entry->next;
886 if (committed_) RET_ON_ERR(commit(entry));
892 void Config::lang_config_merge(const Config & other,
893 int which, ParmStr data_encoding)
896 to_utf8.setup(*this, data_encoding, "utf-8", NormTo);
897 const Entry * src = other.first_;
898 Entry * * ip = &first_;
901 const KeyInfo * l_ki = other.keyinfo(src->key);
902 if (l_ki->other_data == which) {
903 const KeyInfo * c_ki = keyinfo(src->key);
904 Entry * entry = new Entry(*src);
905 if (c_ki->flags & KEYINFO_UTF8)
906 entry->value = to_utf8(entry->value);
916 #define NOTIFY_ALL(fun) \
918 Vector<Notifier *>::iterator i = notifier_list.begin(); \
919 Vector<Notifier *>::iterator end = notifier_list.end(); \
921 RET_ON_ERR((*i)->fun); \
926 PosibErr<int> Config::commit(Entry * entry, Conv * conv)
928 PosibErr<const KeyInfo *> pe = keyinfo(entry->key);
930 if (pe.has_err()) goto error;
932 const KeyInfo * ki = pe;
934 entry->key = ki->name;
936 // FIXME: This is the correct thing to do but it causes problems
937 // with changing a filter mode in "pipe" mode and probably
939 //if (attached_ && !(ki->flags & KEYINFO_MAY_CHANGE)) {
940 // pe = make_err(cant_change_value, entry->key);
944 int place_holder = entry->place_holder;
946 if (conv && ki->flags & KEYINFO_UTF8)
947 entry->value = (*conv)(entry->value);
949 if (ki->type != KeyInfoList && list_action(entry->action)) {
950 pe = make_err(key_not_list, entry->key);
954 assert(ki->def != 0); // if null this key should never have values
955 // directly added to it
956 String value(entry->action == Reset ? get_default(ki) : entry->value);
964 if (value.empty() || entry->place_holder != -1) {
965 // if entry->place_holder != -1 than IGNORE the value no
967 entry->value = "true";
970 } else if (value == "true") {
972 } else if (value == "false") {
975 pe = make_err(bad_value, entry->key, value,
976 /* TRANSLATORS: "true" and "false" are literal
977 * values and should not be translated.*/
978 _("either \"true\" or \"false\""));
982 NOTIFY_ALL(item_updated(ki, val));
985 } case KeyInfoString:
987 NOTIFY_ALL(item_updated(ki, value));
994 if (sscanf(value.str(), "%i", &num) == 1 && num >= 0) {
995 NOTIFY_ALL(item_updated(ki, num));
997 pe = make_err(bad_value, entry->key, value, _("a positive integer"));
1005 NOTIFY_ALL(list_updated(ki));
1009 return place_holder;
1012 entry->action = NoOp;
1013 if (!entry->file.empty())
1014 return pe.with_file(entry->file, entry->line_num);
1016 return (PosibErrBase &)pe;
1022 /////////////////////////////////////////////////////////////////////
1023 /////////////////////////////////////////////////////////////////////
1025 class PossibleElementsEmul : public KeyInfoEnumeration
1029 bool include_modules;
1030 bool module_changed;
1033 const ConfigModule * m;
1035 PossibleElementsEmul(const Config * d, bool ic, bool im)
1036 : include_extra(ic), include_modules(im),
1037 module_changed(false), cd(d), i(d->keyinfo_begin), m(0) {}
1039 KeyInfoEnumeration * clone() const {
1040 return new PossibleElementsEmul(*this);
1043 void assign(const KeyInfoEnumeration * other) {
1044 *this = *(const PossibleElementsEmul *)(other);
1047 virtual bool active_filter_module_changed(void) {
1048 return module_changed;
1051 const char * active_filter_module_name(void){
1057 virtual const char * active_filter_module_desc(void) {
1063 const KeyInfo * next() {
1064 if (i == cd->keyinfo_end) {
1066 i = cd->extra_begin;
1071 module_changed = false;
1072 if (i == cd->extra_end) {
1073 m = cd->filter_modules.pbegin();
1074 if (!include_modules || m == cd->filter_modules.pend()) return 0;
1077 module_changed = true;
1085 if (m == cd->filter_modules.pend()){
1089 while (i == m->end) {
1091 if (m == cd->filter_modules.pend()) return 0;
1094 module_changed = true;
1101 bool at_end() const {
1102 return (m == cd->filter_modules.pend());
1106 KeyInfoEnumeration *
1107 Config::possible_elements(bool include_extra, bool include_modules) const
1109 return new PossibleElementsEmul(this, include_extra, include_modules);
1112 struct ListDefaultDump : public AddableContainer
1116 const char * first_prefix;
1117 unsigned num_blanks;
1118 ListDefaultDump(OStream & o);
1119 PosibErr<bool> add(ParmStr d);
1122 ListDefaultDump::ListDefaultDump(OStream & o)
1123 : out(o), first(false)
1125 first_prefix = _("# default: ");
1126 num_blanks = strlen(first_prefix - 1);
1129 PosibErr<bool> ListDefaultDump::add(ParmStr d)
1132 out.write(first_prefix);
1135 for (unsigned i = 0; i != num_blanks; ++i)
1138 VARARRAY(char, buf, d.size() * 2 + 1);
1145 class ListDump : public MutableContainer
1150 ListDump(OStream & o, ParmStr n)
1151 : out(o), name(n) {}
1152 PosibErr<bool> add(ParmStr d);
1153 PosibErr<bool> remove(ParmStr d);
1154 PosibErr<void> clear();
1157 PosibErr<bool> ListDump::add(ParmStr d) {
1158 VARARRAY(char, buf, d.size() * 2 + 1);
1160 out.printf("add-%s %s\n", name, buf);
1163 PosibErr<bool> ListDump::remove(ParmStr d) {
1164 VARARRAY(char, buf, d.size() * 2 + 1);
1166 out.printf("remove-%s %s\n", name, buf);
1169 PosibErr<void> ListDump::clear() {
1170 out.printf("clear-%s\n", name);
1174 void Config::write_to_stream(OStream & out,
1177 KeyInfoEnumeration * els = possible_elements(include_extra);
1184 while ((i = els->next()) != 0) {
1185 if (i->desc == 0) continue;
1187 if (els->active_filter_module_changed()) {
1189 "#######################################################################\n"
1194 "# configured as follows:\n"
1196 els->active_filter_module_name(),
1197 _(els->active_filter_module_desc()));
1203 obuf.printf("# %s (%s)\n# %s\n",
1204 i->name, _(keyinfo_type_name[i->type]), _(i->desc));
1206 if (i->type != KeyInfoList) {
1207 buf.resize(strlen(i->def) * 2 + 1);
1208 escape(buf.data(), i->def);
1209 obuf.printf("# default: %s", buf.data());
1210 def = get_default(i);
1211 if (def != i->def) {
1212 buf.resize(def.size() * 2 + 1);
1213 escape(buf.data(), def.str());
1214 obuf.printf(" = %s", buf.data());
1217 const Entry * entry = lookup(i->name);
1220 buf.resize(entry->value.size() * 2 + 1);
1221 escape(buf.data(), entry->value.str());
1222 obuf.printf("%s %s\n", i->name, buf.data());
1225 unsigned s = obuf.size();
1226 ListDump ld(obuf, i->name);
1227 lookup_list(i, ld, false);
1228 have_value = s != obuf.size();
1232 if (!(i->flags & KEYINFO_HIDDEN) || have_value)
1238 PosibErr<void> Config::read_in(IStream & in, ParmStr id)
1242 while (getdata_pair(in, dp, buf)) {
1244 Entry * entry = new Entry;
1245 entry->key = dp.key;
1246 entry->value = dp.value;
1248 entry->line_num = dp.line_num;
1249 RET_ON_ERR(set(entry, true));
1254 PosibErr<void> Config::read_in_file(ParmStr file) {
1256 RET_ON_ERR(in.open(file, "r"));
1257 return read_in(in, file);
1260 PosibErr<void> Config::read_in_string(ParmStr str, const char * what) {
1261 StringIStream in(str);
1262 return read_in(in, what);
1266 PosibErr<bool> Config::read_in_settings(const Config * other)
1268 if (settings_read_in_) return false;
1270 bool was_committed = committed_;
1271 set_committed_state(false);
1273 if (other && other->settings_read_in_) {
1276 del(); // to clean up any notifiers and similar stuff
1281 if (other) merge(*other);
1283 const char * env = getenv("ASPELL_CONF");
1285 insert_point_ = &first_;
1286 RET_ON_ERR(read_in_string(env, _("ASPELL_CONF env var")));
1290 insert_point_ = &first_;
1291 PosibErrBase pe = read_in_file(retrieve("per-conf-path"));
1292 if (pe.has_err() && !pe.has_err(cant_read_file)) return pe;
1296 insert_point_ = &first_;
1297 PosibErrBase pe = read_in_file(retrieve("conf-path"));
1298 if (pe.has_err() && !pe.has_err(cant_read_file)) return pe;
1302 RET_ON_ERR(commit_all());
1304 settings_read_in_ = true;
1310 PosibErr<void> Config::commit_all(Vector<int> * phs, const char * codeset)
1315 insert_point_ = &first_;
1318 RET_ON_ERR(to_utf8.setup(*this, codeset, "utf-8", NormTo));
1320 *insert_point_ = others_;
1321 others_ = others_->next;
1322 (*insert_point_)->next = 0;
1323 RET_ON_ERR_SET(commit(*insert_point_, codeset ? &to_utf8 : 0), int, place_holder);
1324 if (phs && place_holder != -1 && (phs->empty() || phs->back() != place_holder))
1325 phs->push_back(place_holder);
1326 insert_point_ = &((*insert_point_)->next);
1331 PosibErr<void> Config::set_committed_state(bool val) {
1332 if (val && !committed_) {
1333 RET_ON_ERR(commit_all());
1334 } else if (!val && committed_) {
1342 #ifdef ENABLE_WIN32_RELOCATABLE
1343 # define HOME_DIR "<prefix>"
1344 # define PERSONAL "<lang>.pws"
1345 # define REPL "<lang>.prepl"
1347 # define HOME_DIR "<$HOME|./>"
1348 # define PERSONAL ".aspell.<lang>.pws"
1349 # define REPL ".aspell.<lang>.prepl"
1352 static const KeyInfo config_keys[] = {
1353 // the description should be under 50 chars
1354 {"actual-dict-dir", KeyInfoString, "<dict-dir^master>", 0}
1355 , {"actual-lang", KeyInfoString, "", 0}
1356 , {"conf", KeyInfoString, "aspell.conf",
1357 /* TRANSLATORS: The remaing strings in config.cpp should be kept
1358 under 50 characters, begin with a lower case character and not
1359 include any trailing punctuation marks. */
1360 N_("main configuration file")}
1361 , {"conf-dir", KeyInfoString, CONF_DIR,
1362 N_("location of main configuration file")}
1363 , {"conf-path", KeyInfoString, "<conf-dir/conf>", 0}
1364 , {"data-dir", KeyInfoString, DATA_DIR,
1365 N_("location of language data files")}
1366 , {"dict-alias", KeyInfoList, "",
1367 N_("create dictionary aliases")}
1368 , {"dict-dir", KeyInfoString, DICT_DIR,
1369 N_("location of the main word list")}
1370 , {"encoding", KeyInfoString, "!encoding",
1371 N_("encoding to expect data to be in"), KEYINFO_COMMON}
1372 , {"filter", KeyInfoList , "url",
1373 N_("add or removes a filter"), KEYINFO_MAY_CHANGE}
1374 , {"filter-path", KeyInfoList, DICT_DIR,
1375 N_("path(s) aspell looks for filters")}
1376 //, {"option-path", KeyInfoList, DATA_DIR,
1377 // N_("path(s) aspell looks for options descriptions")}
1378 , {"mode", KeyInfoString, "url",
1379 N_("filter mode"), KEYINFO_COMMON}
1380 , {"extra-dicts", KeyInfoList, "",
1381 N_("extra dictionaries to use")}
1382 , {"home-dir", KeyInfoString, HOME_DIR,
1383 N_("location for personal files")}
1384 , {"ignore", KeyInfoInt , "1",
1385 N_("ignore words <= n chars"), KEYINFO_MAY_CHANGE}
1386 , {"ignore-accents" , KeyInfoBool, "false",
1387 /* TRANSLATORS: It is OK if this is longer than 50 chars */
1388 N_("ignore accents when checking words -- CURRENTLY IGNORED"), KEYINFO_MAY_CHANGE | KEYINFO_HIDDEN}
1389 , {"ignore-case", KeyInfoBool , "false",
1390 N_("ignore case when checking words"), KEYINFO_MAY_CHANGE}
1391 , {"ignore-repl", KeyInfoBool , "false",
1392 N_("ignore commands to store replacement pairs"), KEYINFO_MAY_CHANGE}
1393 , {"jargon", KeyInfoString, "",
1394 N_("extra information for the word list"), KEYINFO_HIDDEN}
1395 , {"keyboard", KeyInfoString, "standard",
1396 N_("keyboard definition to use for typo analysis")}
1397 , {"lang", KeyInfoString, "<language-tag>",
1398 N_("language code"), KEYINFO_COMMON}
1399 , {"language-tag", KeyInfoString, "!lang",
1400 N_("deprecated, use lang instead"), KEYINFO_HIDDEN}
1401 , {"local-data-dir", KeyInfoString, "<actual-dict-dir>",
1402 N_("location of local language data files") }
1403 , {"master", KeyInfoString, "<lang>",
1404 N_("base name of the main dictionary to use"), KEYINFO_COMMON}
1405 , {"master-flags", KeyInfoString, "", 0}
1406 , {"master-path", KeyInfoString, "<dict-dir/master>", 0}
1407 , {"module", KeyInfoString, "default",
1408 N_("set module name"), KEYINFO_HIDDEN}
1409 , {"module-search-order", KeyInfoList, "",
1410 N_("search order for modules"), KEYINFO_HIDDEN}
1411 , {"normalize", KeyInfoBool, "true",
1412 N_("enable Unicode normalization")}
1413 , {"norm-required", KeyInfoBool, "false",
1414 N_("Unicode normalization required for current lang")}
1415 , {"norm-form", KeyInfoString, "nfc",
1416 /* TRANSLATORS: the values after the ':' are literal
1417 values and should not be translated. */
1418 N_("Unicode normalization form: none, nfd, nfc, comp")}
1419 , {"norm-strict", KeyInfoBool, "false",
1420 N_("avoid lossy conversions when normalization")}
1421 , {"per-conf", KeyInfoString, ".aspell.conf",
1422 N_("personal configuration file")}
1423 , {"per-conf-path", KeyInfoString, "<home-dir/per-conf>", 0}
1424 , {"personal", KeyInfoString, PERSONAL,
1425 N_("personal dictionary file name")}
1426 , {"personal-path", KeyInfoString, "<home-dir/personal>", 0}
1427 , {"prefix", KeyInfoString, PREFIX,
1428 N_("prefix directory")}
1429 , {"repl", KeyInfoString, REPL,
1430 N_("replacements list file name") }
1431 , {"repl-path", KeyInfoString, "<home-dir/repl>", 0}
1432 , {"run-together", KeyInfoBool, "false",
1433 N_("consider run-together words legal"), KEYINFO_MAY_CHANGE}
1434 , {"run-together-limit", KeyInfoInt, "2",
1435 N_("maximum number that can be strung together"), KEYINFO_MAY_CHANGE}
1436 , {"run-together-min", KeyInfoInt, "3",
1437 N_("minimal length of interior words"), KEYINFO_MAY_CHANGE}
1438 , {"save-repl", KeyInfoBool , "true",
1439 N_("save replacement pairs on save all")}
1440 , {"set-prefix", KeyInfoBool, "true",
1441 N_("set the prefix based on executable location")}
1442 , {"size", KeyInfoString, "+60",
1443 N_("size of the word list")}
1444 , {"spelling", KeyInfoString, "",
1445 N_("no longer used"), KEYINFO_HIDDEN}
1446 , {"sug-mode", KeyInfoString, "normal",
1447 N_("suggestion mode"), KEYINFO_MAY_CHANGE | KEYINFO_COMMON}
1448 , {"sug-edit-dist", KeyInfoInt, "1",
1449 /* TRANSLATORS: "sug-mode" is a literal value and should not be
1451 N_("edit distance to use, override sug-mode default")}
1452 , {"sug-typo-analysis", KeyInfoBool, "true",
1453 N_("use typo analysis, override sug-mode default")}
1454 , {"sug-repl-table", KeyInfoBool, "true",
1455 N_("use replacement tables, override sug-mode default")}
1456 , {"sug-split-char", KeyInfoList, "\\ :-",
1457 N_("characters to insert when a word is split"), KEYINFO_UTF8}
1458 , {"use-other-dicts", KeyInfoBool, "true",
1459 N_("use personal, replacement & session dictionaries")}
1460 , {"variety", KeyInfoList, "",
1461 N_("extra information for the word list")}
1462 , {"word-list-path", KeyInfoList, DATA_DIR,
1463 N_("search path for word list information files"), KEYINFO_HIDDEN}
1464 , {"warn", KeyInfoBool, "true",
1465 N_("enable warnings")}
1469 // These options are generally used when creating dictionaries
1470 // and may also be specified in the language data file
1473 , {"affix-char", KeyInfoString, "/", // FIXME: Implement
1474 /* TRANSLATORS: It is OK if this is longer than 50 chars */
1475 N_("indicator for affix flags in word lists -- CURRENTLY IGNORED"), KEYINFO_UTF8 | KEYINFO_HIDDEN}
1476 , {"affix-compress", KeyInfoBool, "false",
1477 N_("use affix compression when creating dictionaries")}
1478 , {"clean-affixes", KeyInfoBool, "true",
1479 N_("remove invalid affix flags")}
1480 , {"clean-words", KeyInfoBool, "false",
1481 N_("attempts to clean words so that they are valid")}
1482 , {"invisible-soundslike", KeyInfoBool, "false",
1483 N_("compute soundslike on demand rather than storing")}
1484 , {"partially-expand", KeyInfoBool, "false",
1485 N_("partially expand affixes for better suggestions")}
1486 , {"skip-invalid-words", KeyInfoBool, "true",
1487 N_("skip invalid words")}
1488 , {"validate-affixes", KeyInfoBool, "true",
1489 N_("check if affix flags are valid")}
1490 , {"validate-words", KeyInfoBool, "true",
1491 N_("check if words are valid")}
1494 // These options are specific to the "aspell" utility. They are
1495 // here so that they can be specified in configuration files.
1497 , {"backup", KeyInfoBool, "true",
1498 N_("create a backup file by appending \".bak\"")}
1499 , {"byte-offsets", KeyInfoBool, "false",
1500 N_("use byte offsets instead of character offsets")}
1501 , {"guess", KeyInfoBool, "false",
1502 N_("create missing root/affix combinations"), KEYINFO_MAY_CHANGE}
1503 , {"keymapping", KeyInfoString, "aspell",
1504 N_("keymapping for check mode: \"aspell\" or \"ispell\"")}
1505 , {"reverse", KeyInfoBool, "false",
1506 N_("reverse the order of the suggest list")}
1507 , {"suggest", KeyInfoBool, "true",
1508 N_("suggest possible replacements"), KEYINFO_MAY_CHANGE}
1509 , {"time" , KeyInfoBool, "false",
1510 N_("time load time and suggest time in pipe mode"), KEYINFO_MAY_CHANGE}
1513 const KeyInfo * config_impl_keys_begin = config_keys;
1514 const KeyInfo * config_impl_keys_end
1515 = config_keys + sizeof(config_keys)/sizeof(KeyInfo);
1517 Config * new_basic_config() {
1518 aspell_gettext_init();
1519 return new Config("aspell",
1520 config_impl_keys_begin,
1521 config_impl_keys_end);