Commit message: Fix various bugs found by OSS-Fuze.
CVE: CVE-2019-17544
CWE: CWE-125
Vulnerability description: libaspell.a in GNU Aspell before 0.60.8 has a stack-based buffer over-read in acommon::unescape in common/getdata.cpp via an isolated \ character.

Project: aspell
File: config.cpp

  PosibErr<void> Config::read_in_string(ParmStr str, const char * what) {
    StringIStream in(str);
    return read_in(in, what);
  }

Project: aspell
File: config.cpp

  void Config::set_extra(const KeyInfo * begin, 
			       const KeyInfo * end) 
  {
    extra_begin = begin;
    extra_end   = end;
  }

Project: aspell
File: getdata.cpp

  void remove_comments(String & buf)
  {
    char * p = buf.mstr();
    char * b = p;
    while (*p && *p != '#') ++p;
    if (*p == '#') {--p; while (p >= b && asc_isspace(*p)) --p; ++p;}
    buf.resize(p - b);
  }

Project: aspell
File: config.cpp

  PosibErr<bool> ListDefaultDump::add(ParmStr d) 
  {
    if (first) {
      out.write(first_prefix);
    } else {
      out.put('#');
      for (unsigned i = 0; i != num_blanks; ++i)
        out.put(' ');
    }
    VARARRAY(char, buf, d.size() * 2 + 1);
    escape(buf, d);
    out.printl(buf);
    first = false;
    return true;
  }

Project: aspell
File: getdata.cpp

  bool getline(IStream & in, DataPair & d, String & buf)
  {
    if (!in.getline(buf)) return false;
    d.value.str  = buf.mstr();
    d.value.size = buf.size();
    return true;
  }

Project: aspell
File: getdata.cpp

  char * unescape(char * dest, const char * src)
  {
    while (*src) {
      if (*src == '\\' && src[1]) {
	++src;
	switch (*src) {
	case 'n': *dest = '\n'; break;
	case 'r': *dest = '\r'; break;
	case 't': *dest = '\t'; break;
	case 'f': *dest = '\f'; break;
	case 'v': *dest = '\v'; break;
	default: *dest = *src;
	}
      } else {
	*dest = *src;
      }
      ++src;
      ++dest;
    }
    *dest = '\0';
    return dest;
  }

Project: aspell
File: config.cpp

  void Config::copy(const Config & other)
  {
    assert(other.others_ == 0);
    others_ = 0;

    name_ = other.name_;

    committed_ = other.committed_;
    attached_ = other.attached_;
    settings_read_in_ = other.settings_read_in_;

    keyinfo_begin = other.keyinfo_begin;
    keyinfo_end   = other.keyinfo_end;
    extra_begin   = other.extra_begin;
    extra_end     = other.extra_end;
    filter_modules = other.filter_modules;

#ifdef HAVE_LIBDL
    filter_modules_ptrs = other.filter_modules_ptrs;
    for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin();
         i != filter_modules_ptrs.end();
         ++i)
      (*i)->copy();
#endif

    md_info_list_index = other.md_info_list_index;

    insert_point_ = 0;
    Entry * const * src  = &other.first_;
    Entry * * dest = &first_;
    while (*src) 
    {
      *dest = new Entry(**src);
      if (src == other.insert_point_)
        insert_point_ = dest;
      src  = &((*src)->next);
      dest = &((*dest)->next);
    }
    if (insert_point_ == 0)
      insert_point_ = dest;
    *dest = 0;

    Vector<Notifier *>::const_iterator i   = other.notifier_list.begin();
    Vector<Notifier *>::const_iterator end = other.notifier_list.end();

    for(; i != end; ++i) {
      Notifier * tmp = (*i)->clone(this);
      if (tmp != 0)
	notifier_list.push_back(tmp);
    }
  }

Project: aspell
File: config.cpp

  bool Config::replace_notifier(const Notifier * o, 
				      Notifier * n) 
  {
    Vector<Notifier *>::iterator i   = notifier_list.begin();
    Vector<Notifier *>::iterator end = notifier_list.end();

    while (i != end && *i != o)
      ++i;

    if (i == end) {
    
      return false;
    
    } else {

      delete *i;
      *i = n;
      return true;

    }
  }

Project: aspell
File: config.cpp

  String Config::get_default(const KeyInfo * ki) const
  {
    bool   in_replace = false;
    String final_str;
    String replace;
    const char * i = ki->def;
    if (*i == '!') { // special cases
      ++i;
    
      if (strcmp(i, "lang") == 0) {
        
        const Entry * entry;
        if (entry = lookup("actual-lang"), entry) {
          return entry->value;
        } else if (have("master")) {
	  final_str = "<unknown>";
	} else {
	  get_lang(final_str);
	}
	
      } else if (strcmp(i, "encoding") == 0) {

        get_encoding(*this, final_str);

      } else if (strcmp(i, "special") == 0) {

	// do nothing

      } else {
      
	abort(); // this should not happen
      
      }
    
    } else for(; *i; ++i) {
    
      if (!in_replace) {

	if (*i == '<') {
	  in_replace = true;
	} else {
	  final_str += *i;
	}

      } else { // in_replace
      
	if (*i == '/' || *i == ':' || *i == '|' || *i == '#' || *i == '^') {
	  char sep = *i;
	  String second;
	  ++i;
	  while (*i != '\0' && *i != '>') second += *i++;
	  if (sep == '/') {
	    String s1 = retrieve(replace);
	    String s2 = retrieve(second);
	    final_str += add_possible_dir(s1, s2);
	  } else if (sep == ':') {
	    String s1 = retrieve(replace);
	    final_str += add_possible_dir(s1, second);
	  } else if (sep == '#') {
	    String s1 = retrieve(replace);
	    assert(second.size() == 1);
	    unsigned int s = 0;
	    while (s != s1.size() && s1[s] != second[0]) ++s;
	    final_str.append(s1, s);
	  } else if (sep == '^') {
	    String s1 = retrieve(replace);
	    String s2 = retrieve(second);
	    final_str += figure_out_dir(s1, s2);
	  } else { // sep == '|'
	    assert(replace[0] == '$');
	    const char * env = getenv(replace.c_str()+1);
	    final_str += env ? env : second;
	  }
	  replace = "";
	  in_replace = false;

	} else if (*i == '>') {

	  final_str += retrieve(replace).data;
	  replace = "";
	  in_replace = false;

	} else {

	  replace += *i;

	}

      }
      
    }
    return final_str;
  }

Project: aspell
File: config.cpp

  Config::~Config() {
    del();
  }

Project: aspell
File: config.cpp

  PosibErr<int> Config::retrieve_int(ParmStr key) const
  {
    assert(committed_); // otherwise the value may not be an integer
                        // as it has not been verified.

    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
    if (ki->type != KeyInfoInt) return make_err(key_not_int, ki->name);

    const Entry * cur = lookup(ki->name);

    String value(cur ? cur->value : get_default(ki));

    return atoi(value.str());
  }

Project: aspell
File: config.cpp

  PosibErr<bool> Config::retrieve_bool(ParmStr key) const
  {
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
    if (ki->type != KeyInfoBool) return make_err(key_not_bool, ki->name);

    const Entry * cur = lookup(ki->name);

    String value(cur ? cur->value : get_default(ki));

    if (value == "false") return false;
    else                  return true;
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::set(Entry * entry0, bool do_unescape)
  {
    StackPtr<Entry> entry(entry0);

    if (entry->action == NoOp)
      entry->key = base_name(entry->key.str(), &entry->action);

    if (num_parms(entry->action) == 0 && !entry->value.empty()) 
    {
      if (entry->place_holder == -1) {
        switch (entry->action) {
        case Reset:
          return make_err(no_value_reset, entry->key);
        case Enable:
          return make_err(no_value_enable, entry->key);
        case Disable:
          return make_err(no_value_disable, entry->key);
        case ListClear:
          return make_err(no_value_clear, entry->key);
        default:
          abort(); // this shouldn't happen
        }
      } else {
        entry->place_holder = -1;
      }
    }

    if (entry->action != ListSet) {

      switch (entry->action) {
      case Enable:
        entry->value = "true";
        entry->action = Set;
        break;
      case Disable:
        entry->value = "false";
        entry->action = Set;
        break;
      default:
        ;
      }
      if (do_unescape) unescape(entry->value.mstr());

      entry->next = *insert_point_;
      *insert_point_ = entry;
      insert_point_ = &entry->next;
      entry.release();
      if (committed_) RET_ON_ERR(commit(entry0)); // entry0 == entry
      
    } else { // action == ListSet

      Entry * ent = new Entry;
      ent->key = entry->key;
      ent->action = ListClear;
      set(ent);

      ListAddHelper helper;
      helper.config = this;
      helper.orig_entry = entry;

      separate_list(entry->value.str(), helper, do_unescape);
    }
    return no_err;
  }

Project: aspell
File: config.cpp

    const char * active_filter_module_name(void){
      if (m != 0)
        return m->name;
      return "";
    }

Project: aspell
File: config.cpp

  bool Config::add_notifier(Notifier * n) 
  {
    Vector<Notifier *>::iterator i   = notifier_list.begin();
    Vector<Notifier *>::iterator end = notifier_list.end();

    while (i != end && *i != n)
      ++i;

    if (i != end) {
    
      return false;
    
    } else {

      notifier_list.push_back(n);
      return true;

    }
  }

Project: aspell
File: config.cpp

    void assign(const KeyInfoEnumeration * other) {
      *this = *(const PossibleElementsEmul *)(other);
    }

Project: aspell
File: config.cpp

    const KeyInfo * next() {
      if (i == cd->keyinfo_end) {
	if (include_extra)
	  i = cd->extra_begin;
	else
	  i = cd->extra_end;
      }
      
      module_changed = false;
      if (i == cd->extra_end) {
	m = cd->filter_modules.pbegin();
	if (!include_modules || m == cd->filter_modules.pend()) return 0;
	else {
          i = m->begin;
          module_changed = true;
        }
      }

      if (m == 0){
	return i++;
      }

      if (m == cd->filter_modules.pend()){
	return 0;
      }

      while (i == m->end) {
	++m;
	if (m == cd->filter_modules.pend()) return 0;
	else {
          i = m->begin;
          module_changed = true;
        }
      }

      return i++;
    }

Project: aspell
File: config.cpp

  Config * new_basic_config() { 
    aspell_gettext_init();
    return new Config("aspell",
		      config_impl_keys_begin,
		      config_impl_keys_end);
  }

Project: aspell
File: file_util.cpp

  bool remove_file(ParmString name) {
    return remove(name) == 0;
  }

Project: aspell
File: getdata.cpp

  void to_lower(char * str)
  {
    for (; *str; str++) *str = asc_tolower(*str);
  }

Project: aspell
File: config.cpp

  ListDefaultDump::ListDefaultDump(OStream & o) 
    : out(o), first(false)
  {
    first_prefix = _("# default: ");
    num_blanks = strlen(first_prefix) - 1;
  }

Project: aspell
File: file_util.cpp

  PosibErr<bool> open_file_writelock(FStream & inout, ParmString file) {
    typedef PosibErr<bool> Ret;
#ifndef USE_FILE_LOCKS
    bool exists = file_exists(file);
#endif
    {
     Ret pe = inout.open(file, "r+");
     if (pe.get_err() != 0)
       pe = inout.open(file, "w+");
     if (pe.has_err())
       return pe;
    }
#ifdef USE_FILE_LOCKS
    int fd = inout.file_no();
    struct flock fl;
    fl.l_type   = F_WRLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start  = 0;
    fl.l_len    = 0;
    fcntl(fd, F_SETLKW, &fl); // ignore errors
    struct stat s;
    fstat(fd, &s);
    return s.st_size != 0;
#else
    return exists;
#endif
  }

Project: aspell
File: config.cpp

  Config::Config(const Config & other) 
  {
    copy(other);
  }

Project: aspell
File: config.cpp

  PosibErr<bool> ListDump::add(ParmStr d) {
    VARARRAY(char, buf, d.size() * 2 + 1);
    escape(buf, d);
    out.printf("add-%s %s\n", name, buf);
    return true;
  }

Project: aspell
File: config.cpp

  void combine_list(String & res, const StringList & in)
  {
    res.clear();
    StringListEnumeration els = in.elements_obj();
    const char * s = 0;
    while ( (s = els.next()) != 0) 
    {
      for (; *s; ++s) {
        if (*s == ':')
          res.append('\\');
        res.append(*s);
      }
      res.append(':');
    }
    if (!res.empty() && res.back() == ':') res.pop_back();
  }

Project: aspell
File: getdata.cpp

  bool split(DataPair & d)
  {
    char * p   = d.value;
    char * end = p + d.value.size;
    d.key.str = p;
    while (p != end) {
      ++p;
      if ((*p == ' ' || *p == '\t') && *(p-1) != '\\') break;
    }
    d.key.size = p - d.key.str;
    *p = 0;
    if (p != end) {
      ++p;
      while (p != end && (*p == ' ' || *p == '\t')) ++p;
    }
    d.value.str = p;
    d.value.size = end - p;
    return d.key.size != 0;
  }

Project: aspell
File: file_util.cpp

  void truncate_file(FStream & f, ParmString name) {
#ifdef USE_FILE_LOCKS
    f.restart();
    ftruncate(f.file_no(),0);
#else
    f.close();
    f.open(name, "w+");
#endif
  }

Project: aspell
File: config.cpp

  void Config::assign(const Config * other) {
    *this = *(const Config *)(other);
  }

Project: aspell
File: file_util.cpp

  const char * PathBrowser::next()
  {
    if (dir_handle == 0) goto get_next_dir;
  begin: {
      struct dirent * entry = readdir((DIR *)dir_handle);
      if (entry == 0) goto try_again;
      const char * name = entry->d_name;
      unsigned name_len = strlen(name);
      if (suffix.size() != 0 && 
          !(name_len > suffix.size() 
            && memcmp(name + name_len - suffix.size(), suffix.str(), suffix.size()) == 0))
        goto begin;
      path = dir;
      if (path.back() != '/') path += '/';
      path += name;
    }
    return path.str();
  try_again:
    if (dir_handle) closedir((DIR *)dir_handle);
    dir_handle = 0;
  get_next_dir:
    dir = els->next();
    if (!dir) return 0;
    dir_handle = opendir(dir);
    if (dir_handle == 0) goto try_again;
    goto begin;
  }

Project: aspell
File: config.cpp

  const char * Config::base_name(const char * s, Action * action)
  {
    if (action) *action = Set;
    const char * c = strchr(s, '-');
    if (!c) return s;
    unsigned len = c - s;
    TEST("reset",   5, Reset);
    TEST("enable",  6, Enable);
    TEST("dont",    4, Disable);
    TEST("disable", 7, Disable);
    TEST("lset",    4, ListSet);
    TEST("rem",     3, ListRemove);
    TEST("remove",  6, ListRemove);
    TEST("add",     3, ListAdd);
    TEST("clear",   5, ListClear);
    return s;
  }

Project: aspell
File: config.cpp

  bool Config::have(ParmStr key) const 
  {
    PosibErr<const KeyInfo *> pe = keyinfo(key);
    if (pe.has_err()) {pe.ignore_err(); return false;}
    return lookup(pe.data->name);
  }

Project: aspell
File: config.cpp

  static inline void get_lang(String & str) 
  {
    get_lang_env(str);
  }

Project: aspell
File: getdata.cpp

  bool escape(char * dest, const char * src, size_t limit, const char * others)
  {
    const char * begin = src;
    const char * end = dest + limit;
    if (asc_isspace(*src)) {
      if (dest == end) return false;
      *dest++ = '\\';
      if (dest == end) return false;
      *dest++ = *src++;
    }
    while (*src) {
      if (dest == end) return false;
      switch (*src) {
      case '\n': *dest++ = '\\'; *dest = 'n'; break;
      case '\r': *dest++ = '\\'; *dest = 'r'; break;
      case '\t': *dest++ = '\\'; *dest = 't'; break;
      case '\f': *dest++ = '\\'; *dest = 'f'; break;
      case '\v': *dest++ = '\\'; *dest = 'v'; break;
      case '\\': *dest++ = '\\'; *dest = '\\'; break;
      case '#' : *dest++ = '\\'; *dest = '#'; break;
      default:
	if (others && strchr(others, *src)) *dest++ = '\\';
	*dest = *src;
      }
      ++src;
      ++dest;
    }
    if (src > begin + 1 && asc_isspace(src[-1])) {
      --dest;
      *dest++ = '\\';
      if (dest == end) return false;
      *dest++ = src[-1];
    }
    *dest = '\0';
    return true;
  }

Project: aspell
File: config.cpp

  static const ConfigModule * find(ParmStr key, 
				   const ConfigModule * i, 
				   const ConfigModule * end) 
  {
    while (i != end) {
      if (strcmp(key, i->name) == 0)
	return i;
      ++i;
    }
    return i;
  }

Project: aspell
File: getdata.cpp

  char * get_nb_line(IStream & in, String & buf)
  {
    char * p;
    // get first non blank line
    do {
      if (!in.getline(buf)) return 0;
      p = buf.mstr();
      while (*p == ' ' || *p == '\t') ++p;
    } while (*p == '#' || *p == '\0');
    return p;
  }

Project: aspell
File: config.cpp

  PosibErr<bool> ListDump::remove(ParmStr d) {
    VARARRAY(char, buf, d.size() * 2 + 1);
    escape(buf, d);
    out.printf("remove-%s %s\n", name, buf);
    return true;
  }

Project: aspell
File: config.cpp

  Config::Config(ParmStr name,
		 const KeyInfo * mainbegin, 
		 const KeyInfo * mainend)
    : name_(name)
    , first_(0), insert_point_(&first_), others_(0)
    , committed_(true), attached_(false)
    , md_info_list_index(-1)
    , settings_read_in_(false)
    , load_filter_hook(0)
    , filter_mode_notifier(0)
  {
    keyinfo_begin = mainbegin;
    keyinfo_end   = mainend;
    extra_begin = 0;
    extra_end   = 0;
  }

Project: aspell
File: config.cpp

  static const KeyInfo * find(ParmStr key, 
			      const KeyInfo * i, 
			      const KeyInfo * end) 
  {
    while (i != end) {
      if (strcmp(key, i->name) == 0)
	return i;
      ++i;
    }
    return i;
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::set_committed_state(bool val) {
    if (val && !committed_) {
      RET_ON_ERR(commit_all());
    } else if (!val && committed_) {
      assert(empty());
      committed_ = false;
    }
    return no_err;
  }

Project: aspell
File: config.cpp

  Config & Config::operator= (const Config & other)
  {
    del();
    copy(other);
    return *this;
  }

Project: aspell
File: config.cpp

  void Config::write_to_stream(OStream & out, 
			       bool include_extra) 
  {
    KeyInfoEnumeration * els = possible_elements(include_extra);
    const KeyInfo * i;
    String buf;
    String obuf;
    String def;
    bool have_value;

    while ((i = els->next()) != 0) {
      if (i->desc == 0) continue;

      if (els->active_filter_module_changed()) {
        out.printf(_("\n"
                     "#######################################################################\n"
                     "#\n"
                     "# Filter: %s\n"
                     "#   %s\n"
                     "#\n"
                     "# configured as follows:\n"
                     "\n"),
                   els->active_filter_module_name(),
                   _(els->active_filter_module_desc()));
      }

      obuf.clear();
      have_value = false;

      obuf.printf("# %s (%s)\n#   %s\n",
                  i->name, _(keyinfo_type_name[i->type]), _(i->desc));
      if (i->def != 0) {
	if (i->type != KeyInfoList) {
          buf.resize(strlen(i->def) * 2 + 1);
          escape(buf.data(), i->def);
          obuf.printf("# default: %s", buf.data());
          def = get_default(i);
          if (def != i->def) {
            buf.resize(def.size() * 2 + 1);
            escape(buf.data(), def.str());
            obuf.printf(" = %s", buf.data());
          }
          obuf << '\n';
          const Entry * entry = lookup(i->name);
	  if (entry) {
            have_value = true;
            buf.resize(entry->value.size() * 2 + 1);
            escape(buf.data(), entry->value.str());
	    obuf.printf("%s %s\n", i->name, buf.data());
          }
	} else {
          unsigned s = obuf.size();
          ListDump ld(obuf, i->name);
          lookup_list(i, ld, false);
          have_value = s != obuf.size();
	}
      }
      obuf << '\n';
      if (!(i->flags & KEYINFO_HIDDEN) || have_value)
        out.write(obuf);
    }
    delete els;
  }

Project: aspell
File: config.cpp

  Config::possible_elements(bool include_extra, bool include_modules) const
  {
    return new PossibleElementsEmul(this, include_extra, include_modules);
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::remove(ParmStr key)
  {
    Entry * entry = new Entry;
    entry->key = key;
    entry->action = Reset;
    return set(entry);
  }

Project: aspell
File: file_util.cpp

  bool file_exists(ParmString name) {
    return ACCESS(name, F_OK) == 0;
  }

Project: aspell
File: file_util.cpp

  const char * get_file_name(const char * path) {
    const char * file_name;
    if (path != 0) {
      file_name = strrchr(path,'/');
      if (file_name == 0)
        file_name = path;
    } else {
      file_name = 0;
    }
    return file_name;
  }

Project: aspell
File: config.cpp

  PosibErr<void> ListDump::clear() {
    out.printf("clear-%s\n", name);
    return no_err;
  }

Project: aspell
File: config.cpp

  void Config::lang_config_merge(const Config & other,
                                 int which, ParmStr data_encoding)
  {
    Conv to_utf8;
    to_utf8.setup(*this, data_encoding, "utf-8", NormTo);
    const Entry * src  = other.first_;
    Entry * * ip = &first_;
    while (src)
    {
      const KeyInfo * l_ki = other.keyinfo(src->key);
      if (l_ki->other_data == which) {
        const KeyInfo * c_ki = keyinfo(src->key);
        Entry * entry = new Entry(*src);
        if (c_ki->flags & KEYINFO_UTF8)
          entry->value = to_utf8(entry->value);
        entry->next = *ip;
        *ip = entry;
        ip = &entry->next;
      }
      src = src->next;
    }
  }

Project: aspell
File: file_util.cpp

  String figure_out_dir(ParmString dir, ParmString file)
  {
    String temp;
    int s = file.size() - 1;
    while (s != -1 && file[s] != '/') --s;
    if (need_dir(file)) {
      temp += dir;
      temp += '/';
    }
    if (s != -1) {
      temp.append(file, s);
    }
    return temp;
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::retrieve_list(ParmStr key, 
				       MutableContainer * m) const
  {
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
    if (ki->type != KeyInfoList) return make_err(key_not_list, ki->name);

    lookup_list(ki, *m, true);

    return no_err;
  }

Project: aspell
File: config.cpp

  void Config::lookup_list(const KeyInfo * ki,
                           MutableContainer & m,
                           bool include_default) const
  {
    const Entry * cur = first_;
    const Entry * first_to_use = 0;

    while (cur) {
      if (cur->key == ki->name && 
          (first_to_use == 0 || 
           cur->action == Reset || cur->action == Set 
           || cur->action == ListClear)) 
        first_to_use = cur;
      cur = cur->next;
    }

    cur = first_to_use;

    if (include_default && 
        (!cur || 
         !(cur->action == Set || cur->action == ListClear)))
    {
      String def = get_default(ki);
      separate_list(def, m, true);
    }

    if (cur && cur->action == Reset) {
      cur = cur->next;
    }

    if (cur && cur->action == Set) {
      if (!include_default) m.clear();
      m.add(cur->value);
      cur = cur->next;
    }

    if (cur && cur->action == ListClear) {
      if (!include_default) m.clear();
      cur = cur->next;
    }

    while (cur) {
      if (cur->key == ki->name) {
        if (cur->action == ListAdd)
          m.add(cur->value);
        else if (cur->action == ListRemove)
          m.remove(cur->value);
      }
      cur = cur->next;
    }
  }

Project: aspell
File: config.cpp

  static void get_lang(String & final_str) 
  {
    // FIXME: THIS IS NOT THREAD SAFE
    String locale = setlocale (LC_ALL, NULL);
    if (locale == "C")
      setlocale (LC_ALL, "");
    const char * lang = setlocale (LC_MESSAGES, NULL);
    bool res = proc_locale_str(lang, final_str);
    if (locale == "C")
      setlocale(LC_MESSAGES, locale.c_str());
    if (!res)
      get_lang_env(final_str);
  }

Project: aspell
File: file_util.cpp

  unsigned find_file(const Config * config, const char * option, String & filename)
  {
    StringList sl;
    config->retrieve_list(option, &sl);
    return find_file(sl, filename);
  }

Project: aspell
File: config.cpp

  PosibErr<bool> Config::read_in_settings(const Config * other)
  {
    if (settings_read_in_) return false;

    bool was_committed = committed_;
    set_committed_state(false);

    if (other && other->settings_read_in_) {

      assert(empty());
      del(); // to clean up any notifiers and similar stuff
      copy(*other);

    } else {

      if (other) merge(*other);

      const char * env = getenv("ASPELL_CONF");
      if (env != 0) { 
        insert_point_ = &first_;
        RET_ON_ERR(read_in_string(env, _("ASPELL_CONF env var")));
      }
      
      {
      insert_point_ = &first_;
      PosibErrBase pe = read_in_file(retrieve("per-conf-path"));
      if (pe.has_err() && !pe.has_err(cant_read_file)) return pe;
      }
      
      {
        insert_point_ = &first_;
        PosibErrBase pe = read_in_file(retrieve("conf-path"));
        if (pe.has_err() && !pe.has_err(cant_read_file)) return pe;
      }

      if (was_committed)
        RET_ON_ERR(commit_all());

      settings_read_in_ = true;
    }

    return true;
  }

Project: aspell
File: config.cpp

  static inline void get_encoding(const Config &, String & final_str)
  {
    final_str = "none";
  }

Project: aspell
File: config.cpp

  void Config::del()
  {
    while (first_) {
      Entry * tmp = first_->next;
      delete first_;
      first_ = tmp;
    }

    while (others_) {
      Entry * tmp = others_->next;
      delete first_;
      others_ = tmp;
    }

    Vector<Notifier *>::iterator i   = notifier_list.begin();
    Vector<Notifier *>::iterator end = notifier_list.end();

    for(; i != end; ++i) {
      delete (*i);
      *i = 0;
    }
    
    notifier_list.clear();

#ifdef HAVE_LIBDL
    filter_modules.clear();
    for (Vector<Cacheable *>::iterator i = filter_modules_ptrs.begin();
         i != filter_modules_ptrs.end();
         ++i)
      (*i)->release();
    filter_modules_ptrs.clear();
#endif
  }

Project: aspell
File: config.cpp

  static inline void get_encoding(const Config & c, String & final_str)
  {
    const char * codeset = nl_langinfo(CODESET);
    if (ascii_encoding(c, codeset)) codeset = "none";
    final_str = codeset;
  }

Project: aspell
File: config.cpp

  PosibErr<String> Config::retrieve(ParmStr key) const
  {
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
    if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name);

    const Entry * cur = lookup(ki->name);

    return cur ? cur->value : get_default(ki);
  }

Project: aspell
File: getdata.cpp

  bool getdata_pair(IStream & in, DataPair & d, String & buf)
  {
    char * p;

    // get first non blank line and count all read ones
    do {
      buf.clear();
      buf.append('\0'); // to avoid some special cases
      if (!in.append_line(buf)) return false;
      d.line_num++;
      p = buf.mstr() + 1;
      while (*p == ' ' || *p == '\t') ++p;
    } while (*p == '#' || *p == '\0');

    // get key
    d.key.str = p;
    while (*p != '\0' &&
           ((*p != ' ' && *p != '\t' && *p != '#') || *(p-1) == '\\')) ++p;
    d.key.size = p - d.key.str;

    // figure out if there is a value and add terminate key
    d.value.str = p; // in case there is no value
    d.value.size = 0;
    if (*p == '#' || *p == '\0') {*p = '\0'; return true;}
    *p = '\0';

    // skip any whitespace
    ++p;
    while (*p == ' ' || *p == '\t') ++p;
    if (*p == '\0' || *p == '#') {return true;}

    // get value
    d.value.str = p;
    while (*p != '\0' && (*p != '#' || *(p-1) == '\\')) ++p;
    
    // remove trailing white space and terminate value
    --p;
    while (*p == ' ' || *p == '\t') --p;
    if (*p == '\\' && *(p + 1) != '\0') ++p;
    ++p;
    d.value.size = p - d.value.str;
    *p = '\0';

    return true;
  }

Project: aspell
File: config.cpp

    bool at_end() const {
      return (m == cd->filter_modules.pend());
    }

Project: aspell
File: config.cpp

  NotifierEnumeration * Config::notifiers() const 
  {
    return new NotifierEnumeration(notifier_list);
  }

Project: aspell
File: config.cpp

  bool Config::remove_notifier(const Notifier * n) 
  {
    Vector<Notifier *>::iterator i   = notifier_list.begin();
    Vector<Notifier *>::iterator end = notifier_list.end();

    while (i != end && *i != n)
      ++i;

    if (i == end) {
    
      return false;
    
    } else {

      delete *i;
      notifier_list.erase(i);
      return true;

    }
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::replace(ParmStr key, ParmStr value)
  {
    Entry * entry = new Entry;
    entry->key = key;
    entry->value = value;
    return set(entry);
  }

Project: aspell
File: config.cpp

  static bool proc_locale_str(ParmStr lang, String & final_str)
  {
    if (lang == 0) return false;
    const char * i = lang;
    if (!(asc_islower(i[0]) && asc_islower(i[1]))) return false;
    final_str.assign(i, 2);
    i += 2;
    if (! (i[0] == '_' || i[0] == '-')) return true;
    i += 1;
    if (!(asc_isupper(i[0]) && asc_isupper(i[1]))) return true;
    final_str += '_';
    final_str.append(i, 2);
    return true;
  }

Project: aspell
File: config.cpp

  Config * Config::clone() const {
    return new Config(*this);
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::merge(const Config & other)
  {
    const Entry * src  = other.first_;
    while (src) 
    {
      Entry * entry = new Entry(*src);
      entry->next = *insert_point_;
      *insert_point_ = entry;
      insert_point_ = &entry->next;
      if (committed_) RET_ON_ERR(commit(entry));
      src = src->next;
    }
    return no_err;
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::read_in_file(ParmStr file) {
    FStream in;
    RET_ON_ERR(in.open(file, "r"));
    return read_in(in, file);
  }

Project: aspell
File: config.cpp

    KeyInfoEnumeration * clone() const {
      return new PossibleElementsEmul(*this);
    }

Project: aspell
File: file_util.cpp

  PathBrowser::PathBrowser(const StringList & sl, const char * suf)
    : dir_handle(0)
  {
    els = sl.elements();
    suffix = suf;
  }

Project: aspell
File: config.cpp

    PossibleElementsEmul(const Config * d, bool ic, bool im)
      : include_extra(ic), include_modules(im), 
        module_changed(false), cd(d), i(d->keyinfo_begin), m(0) {}

Project: aspell
File: config.cpp

  static void get_lang_env(String & str) 
  {
    if (proc_locale_str(getenv("LC_MESSAGES"), str)) return;
    if (proc_locale_str(getenv("LANG"), str)) return;
    if (proc_locale_str(getenv("LANGUAGE"), str)) return;
    str = DEFAULT_LANG;
  }

Project: aspell
File: config.cpp

  PosibErr<String> Config::get_default(ParmStr key) const
  {
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
    return get_default(ki);
  }

Project: aspell
File: config.cpp

    virtual const char * active_filter_module_desc(void) {
      if (m != 0)
        return m->desc;
      return "";
    }

Project: aspell
File: file_util.cpp

  time_t get_modification_time(FStream & f) {
    struct stat s;
    fstat(f.file_no(), &s);
    return s.st_mtime;
  }

Project: aspell
File: file_util.cpp

  unsigned find_file(const StringList & sl, String & filename)
  {
    StringListEnumeration els = sl.elements_obj();
    const char * dir;
    String path;
    while ( (dir = els.next()) != 0 ) 
    {
      path = dir;
      if (path.empty()) continue;
      if (path.back() != '/') path += '/';
      unsigned dir_len = path.size();
      path += filename;
      if (file_exists(path)) {
        filename.swap(path);
        return dir_len;
      }
    }
    return 0;
  }

Project: aspell
File: getdata.cpp

  void to_lower(String & res, const char * str)
  {
    for (; *str; str++) res += asc_tolower(*str);
  }

Project: aspell
File: getdata.cpp

  void init(ParmString str, DataPair & d, String & buf)
  {
    const char * s = str;
    while (*s == ' ' || *s == '\t') ++s;
    size_t l = str.size() - (s - str);
    buf.assign(s, l);
    d.value.str  = buf.mstr();
    d.value.size = l;
  }

Project: aspell
File: config.cpp

  void Config::replace_internal(ParmStr key, ParmStr value)
  {
    Entry * entry = new Entry;
    entry->key = key;
    entry->value = value;
    entry->action = Set;
    entry->next = *insert_point_;
    *insert_point_ = entry;
    insert_point_ = &entry->next;
  }

Project: aspell
File: config.cpp

  const Config::Entry * Config::lookup(const char * key) const
  {
    const Entry * res = 0;
    const Entry * cur = first_;

    while (cur) {
      if (cur->key == key && cur->action != NoOp)  res = cur;
      cur = cur->next;
    }

    if (!res || res->action == Reset) return 0;
    return res;
  }

Project: aspell
File: config.cpp

  void Config::set_filter_modules(const ConfigModule * modbegin, 
				  const ConfigModule * modend)
  {
    assert(filter_modules_ptrs.empty());
    filter_modules.clear();
    filter_modules.assign(modbegin, modend);
  }

Project: aspell
File: config.cpp

  void separate_list(ParmStr value, AddableContainer & out, bool do_unescape)
  {
    unsigned len = value.size();
    
    VARARRAY(char, buf, len + 1);
    memcpy(buf, value, len + 1);
    
    len = strlen(buf);
    char * s = buf;
    char * end = buf + len;
      
    while (s < end)
    {
      if (do_unescape) while (*s == ' ' || *s == '\t') ++s;
      char * b = s;
      char * e = s;
      while (*s != '\0') {
        if (do_unescape && *s == '\\') {
          ++s;
          if (*s == '\0') break;
          e = s;
          ++s;
        } else {
          if (*s == ':') break;
          if (!do_unescape || (*s != ' ' && *s != '\t')) e = s;
          ++s;
        }
      }
      if (s != b) {
        ++e;
        *e = '\0';
        if (do_unescape) unescape(b);
      
        out.add(b);
      }
      ++s;
    }
  }

Project: aspell
File: file_util.cpp

  PosibErr<void> open_file_readlock(FStream & in, ParmString file) {
    RET_ON_ERR(in.open(file, "r"));
#ifdef USE_FILE_LOCKS
    int fd = in.file_no();
    struct flock fl;
    fl.l_type   = F_RDLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start  = 0;
    fl.l_len    = 0;
    fcntl(fd, F_SETLKW, &fl); // ignore errors
#endif
    return no_err;
  }

Project: aspell
File: config.cpp

  PosibErr<String> Config::retrieve_any(ParmStr key) const
  {
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);

    if (ki->type != KeyInfoList) {
      const Entry * cur = lookup(ki->name);
      return cur ? cur->value : get_default(ki);
    } else {
      StringList sl;
      RET_ON_ERR(retrieve_list(key, &sl));
      StringListEnumeration els = sl.elements_obj();
      const char * s;
      String val;
      while ( (s = els.next()) != 0 ) {
        val += s;
        val += '\n';
      }
      val.pop_back();
      return val;
    }
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::read_in(IStream & in, ParmStr id) 
  {
    String buf;
    DataPair dp;
    while (getdata_pair(in, dp, buf)) {
      to_lower(dp.key);
      Entry * entry = new Entry;
      entry->key = dp.key;
      entry->value = dp.value;
      entry->file = id;
      entry->line_num = dp.line_num;
      RET_ON_ERR(set(entry, true));
    }
    return no_err;
  }

Project: aspell
File: file_util.cpp

  String add_possible_dir(ParmString dir, ParmString file) {
    if (need_dir(file)) {
      String path;
      path += dir;
      path += '/';
      path += file;
      return path;
    } else {
      return file;
    }
  }

Project: aspell
File: file_util.cpp

  PathBrowser::~PathBrowser() 
  {
    delete els;
    if (dir_handle) closedir((DIR *)dir_handle);
  }

Project: aspell
File: config.cpp

  PosibErr<const KeyInfo *> Config::keyinfo(ParmStr key) const
  {
    typedef PosibErr<const KeyInfo *> Ret;
    {
      const KeyInfo * i;
      i = acommon::find(key, keyinfo_begin, keyinfo_end);
      if (i != keyinfo_end) return Ret(i);
      
      i = acommon::find(key, extra_begin, extra_end);
      if (i != extra_end) return Ret(i);
      
      const char * s = strncmp(key, "f-", 2) == 0 ? key + 2 : key.str();
      const char * h = strchr(s, '-');
      if (h == 0) goto err;

      String k(s, h - s);
      const ConfigModule * j = acommon::find(k,
					     filter_modules.pbegin(),
					     filter_modules.pend());
      
      if (j == filter_modules.pend() && load_filter_hook && committed_) {
        // FIXME: This isn't quite right
        PosibErrBase pe = load_filter_hook(const_cast<Config *>(this), k);
        pe.ignore_err();
        j = acommon::find(k,
                          filter_modules.pbegin(),
                          filter_modules.pend());
      }

      if (j == filter_modules.pend()) goto err;

      i = acommon::find(key, j->begin, j->end);
      if (i != j->end) return Ret(i);
      
      if (strncmp(key, "f-", 2) != 0) k = "f-";
      else                            k = "";
      k += key;
      i = acommon::find(k, j->begin, j->end);
      if (i != j->end) return Ret(i);
    }
  err:  
    return Ret().prim_err(unknown_key, key);
  }

Project: aspell
File: config.cpp

  PosibErr<int> Config::commit(Entry * entry, Conv * conv) 
  {
    PosibErr<const KeyInfo *> pe = keyinfo(entry->key);
    {
      if (pe.has_err()) goto error;
      
      const KeyInfo * ki = pe;

      entry->key = ki->name;
      
      // FIXME: This is the correct thing to do but it causes problems
      //        with changing a filter mode in "pipe" mode and probably
      //        elsewhere.
      //if (attached_ && !(ki->flags & KEYINFO_MAY_CHANGE)) {
      //  pe = make_err(cant_change_value, entry->key);
      //  goto error;
      //}

      int place_holder = entry->place_holder;
      
      if (conv && ki->flags & KEYINFO_UTF8)
        entry->value = (*conv)(entry->value);

      if (ki->type != KeyInfoList && list_action(entry->action)) {
        pe = make_err(key_not_list, entry->key);
        goto error;
      }
      
      assert(ki->def != 0); // if null this key should never have values
      // directly added to it
      String value(entry->action == Reset ? get_default(ki) : entry->value);
      
      switch (ki->type) {
        
      case KeyInfoBool: {

        bool val;
      
        if  (value.empty() || entry->place_holder != -1) {
          // if entry->place_holder != -1 than IGNORE the value no
          // matter what it is
          entry->value = "true";
          val = true;
          place_holder = -1;
        } else if (value == "true") {
          val = true;
        } else if (value == "false") {
          val = false;
        } else {
          pe = make_err(bad_value, entry->key, value,
                        /* TRANSLATORS: "true" and "false" are literal
                         * values and should not be translated.*/
                        _("either \"true\" or \"false\""));
          goto error;
        }

        NOTIFY_ALL(item_updated(ki, val));
        break;
        
      } case KeyInfoString:
        
        NOTIFY_ALL(item_updated(ki, value));
        break;
        
      case KeyInfoInt: 
      {
        int num;
        
        if (sscanf(value.str(), "%i", &num) == 1 && num >= 0) {
          NOTIFY_ALL(item_updated(ki, num));
        } else {
          pe = make_err(bad_value, entry->key, value, _("a positive integer"));
          goto error;
        }
        
        break;
      }
      case KeyInfoList:
        
        NOTIFY_ALL(list_updated(ki));
        break;
        
      }
      return place_holder;
    }
  error:
    entry->action = NoOp;
    if (!entry->file.empty())
      return pe.with_file(entry->file, entry->line_num);
    else
      return (PosibErrBase &)pe;
  }

Project: aspell
File: file_util.cpp

  bool rename_file(ParmString orig_name, ParmString new_name)
  {
    remove(new_name);
    return rename(orig_name, new_name) == 0;
  }

Project: aspell
File: config.cpp

  PosibErr<bool> ListAddHelper::add(ParmStr val)
  {
    Config::Entry * entry = new Config::Entry(*orig_entry);
    entry->value = val;
    entry->action = Config::ListAdd;
    config->set(entry);
    return true;
  }

Project: aspell
File: config.cpp

    ListDump(OStream & o, ParmStr n) 
      : out(o), name(n) {}

Project: aspell
File: file_util.cpp

  bool need_dir(ParmString file) {
    if (file[0] == '/' || (file[0] == '.' && file[1] == '/')
#ifdef WIN32
        || (asc_isalpha(file[0]) && file[1] == ':')
        || file[0] == '\\' || (file[0] == '.' && file[1] == '\\')
#endif
      )
      return false;
    else
      return true;
  }

Project: aspell
File: config.cpp

  PosibErr<void> Config::commit_all(Vector<int> * phs, const char * codeset)
  {
    committed_ = true;
    others_ = first_;
    first_ = 0;
    insert_point_ = &first_;
    Conv to_utf8;
    if (codeset)
      RET_ON_ERR(to_utf8.setup(*this, codeset, "utf-8", NormTo));
    while (others_) {
      *insert_point_ = others_;
      others_ = others_->next;
      (*insert_point_)->next = 0;
      RET_ON_ERR_SET(commit(*insert_point_, codeset ? &to_utf8 : 0), int, place_holder);
      if (phs && place_holder != -1 && (phs->empty() || phs->back() != place_holder))
        phs->push_back(place_holder);
      insert_point_ = &((*insert_point_)->next);
    }
    return no_err;
  }

Project: aspell
File: config.cpp

    virtual bool active_filter_module_changed(void) {
      return module_changed;
    }