
//  ------------------------------------------------------------------
//  GoldED+
//  Copyright (C) 1990-1999 Odinn Sorensen
//  Copyright (C) 1999-2000 Alexander S. Aganichev
//  ------------------------------------------------------------------
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License as
//  published by the Free Software Foundation; either version 2 of the
//  License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307 USA
//  ------------------------------------------------------------------
//  $Id$
//  ------------------------------------------------------------------
//  Common code for the area compile.
//  ------------------------------------------------------------------

#include <golded.h>
#include <gmoprot.h>

//  ------------------------------------------------------------------
//  Fix up the echoid and make default if none specified

void FixEchoid(char* echoid, int type) {

  static uint netnum = 1;
  static uint echonum = 1;
  static uint localnum = 1;

  if (echoid == NULL) {
    LOG_NULL_POINTER;
    LOG.printf("! Illegall call of function: FixEchoid(NULL,%i). File %s line %i ", type, __FILE__, __LINE__);
    PointerErrorExit();
    return;
  }
  if(*echoid == NUL) {
    const char* t = CFG->areaautoid == AUTOID_LONG ? "MAIL" : "";
    if(type & GMB_NET)
      sprintf(echoid, "NET%s%03u", t, netnum++);
    else if(type & GMB_ECHO)
      sprintf(echoid, "ECHO%s%03u", t, echonum++);
    else if(type & GMB_LOCAL)
      sprintf(echoid, "LOCAL%03u", localnum++);
  }
}


//  ------------------------------------------------------------------
//  Sort a group of areas

void AreaList::SortAreaGroup(const char* options, int beginarea, int endarea) {

  if(beginarea != endarea) {
    const char* ptr = striinc("-S", options);
    if(ptr) {
      ptr += 2;
      if(*ptr == '=')
        ptr++;
      Sort(ptr, beginarea, endarea);
    }
  }
}


//  ------------------------------------------------------------------
//  Rename an echoid

void RenameArea(char* echoid)
{
  std::vector<EchoRen>::iterator n = CFG->arearename.begin();
  std::vector<EchoRen>::iterator end = CFG->arearename.end();

  for (; n != end; n++)
  {
    if (strieql(echoid, n->from.c_str()))
    {
      strxcpy(echoid, n->to.c_str(), sizeof(Echo));
      break;
    }
  }
}


//  ------------------------------------------------------------------
//  Check if the echoid is on the exclusion list

bool CheckExcl(char* echoid)
{
  gstrarray::iterator n = CFG->areaexcl.begin();
  gstrarray::iterator end1 = CFG->areaexcl.end();

  for (; n != end1; n++)
  {
    if (strwild(echoid, n->c_str()))
    {
      // Found excl, now check for incl
      gstrarray::iterator x = CFG->areaincl.begin();
      gstrarray::iterator end2 = CFG->areaincl.end();

      for (; x != end2; x++)
      {
        if (strwild(echoid, x->c_str()))
          return false;
      }

      return true;   // Echoid is excluded
    }
  }

  return false;
}


//  ------------------------------------------------------------------
//  Check if the echo is email or news

void CheckEMailOrNews(char* echoid, uint& type)
{
  gstrarray::iterator i = CFG->areaisemail.begin();
  gstrarray::iterator end = CFG->areaisemail.end();

  for (; i != end; i++)
  {
    if (strwild(echoid, i->c_str()))
    {
      type = GMB_EMAIL | GMB_NET;
      break;
    }
  }

  i = CFG->areaisnews.begin();
  end = CFG->areaisnews.end();

  for(; i != end; i++)
  {
    if (strwild(echoid, i->c_str()))
    {
      type = GMB_NEWSGROUP | GMB_ECHO;
      break;
    }
  }
}


//  ------------------------------------------------------------------
//  Add or update area data

void AddNewArea(AreaCfg* aa) {

  AL.AddNewArea(aa);
}


//  ------------------------------------------------------------------
//  Add or update area data

void AddNewArea(AreaCfg& aa) {

  AL.AddNewArea(&aa);
}


//  ------------------------------------------------------------------
//  Add or update area data

void AreaList::AddNewArea(AreaCfg* aa)
{
  if (veryverbose)
  {
    std::string temp;
    STD_PRINT("  fmt=" << aa->basetype << ", eid=\"" << aa->echoid);
    STD_PRINT("\", pth=\"" << aa->path << "\", brd=" << aa->board);
    STD_PRINT(", gid=" << aa->groupid << ", aka=" << aa->aka.make_string(temp));
    STD_PRINTNL(" " << aa->attr.make_string(temp));
  }

  Desc desc;
  char buf[256];
  bool newarea = true;
  static int serial = 0;
  static int net1st = false;

  *desc = NUL;

  // Make sure the path field is 100% correct for the msgbase
  if ((aa->basetype == "FTS1") || (aa->basetype == "OPUS"))
  {
    if(*aa->path == NUL)
      return;
    MapPath(aa->path);
    AddBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #ifndef GMB_NOHUDS
  else if (aa->basetype == "HUDSON")
  {
    if ((aa->board < 1) or (aa->board > 200))  // Ignore areas with invalid numbers
      return;
    gsprintf(PRINTF_DECLARE_BUFFER(aa->path), "%u", aa->board);
  }
  #endif
  #ifndef GMB_NOGOLD
  else if (aa->basetype == "GOLDBASE")
  {
    if ((aa->board < 1) or (aa->board > 500))  // Ignore areas with invalid numbers
      return;
    gsprintf(PRINTF_DECLARE_BUFFER(aa->path), "%u", aa->board);
  }
  #endif
  #ifndef GMB_NOEZY
  else if (aa->basetype == "EZYCOM")
  {
    // Ignore areas with invalid numbers
    if ((aa->board < 1) or (aa->board > 1536))
      return;
    gsprintf(PRINTF_DECLARE_BUFFER(aa->path), "%u", aa->board);
  }
  #endif
  #ifndef GMB_NOWCAT
  else if (aa->basetype == "WILDCAT")
  {
    if(*aa->path == NUL)
      return;
    MapPath(aa->path);
    StripBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #endif
  #ifndef GMB_NOXBBS
  else if (aa->basetype == "ADEPTXBBS")
  {
    if(*aa->path == NUL)
      return;
    MapPath(aa->path);
    StripBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #endif
  #ifndef GMB_NOSQSH
  else if (aa->basetype == "SQUISH")
  {
    if(*aa->path == NUL)
      return;
    MapPath(aa->path);
    StripBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #endif
  #ifndef GMB_NOJAM
  else if (aa->basetype == "JAM")
  {
    if(*aa->path == NUL)
      return;
    MapPath(aa->path);
    StripBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #endif
  #ifndef GMB_NOPCB
  else if (aa->basetype == "PCBOARD")
  {
    MapPath(aa->path);
    StripBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #endif
  #ifndef GMB_NOSMB
  else if (aa->basetype == "SMB")
  {
    if(*aa->path == NUL)
      return;
    MapPath(aa->path);
    StripBackslash(aa->path);
    strschg_environ(aa->path, sizeof(aa->path));
  }
  #endif
  else if (aa->basetype == "SEPARATOR")
  {
  }
  else {
      return;
  }

  // Note msgbase type
  if(not find(basetypes, aa->basetype))
    basetypes.push_back(aa->basetype);

  // Things to do for real areas only
  if(not aa->isseparator()) {

    // If the echoid is missing, try to find it in the echolist
    if(strblank(aa->echoid))
      AFILE->echolist.FindEcho(aa->echoid, aa->path, aa->desc);

    // Fix up the echoid
    FixEchoid(aa->echoid, aa->type);

    // Rename the echoid if necessary
    RenameArea(aa->echoid);

    // Do not accept excluded areas
    if(CheckExcl(aa->echoid))
      return;

    // Fix description
    if(strblank(aa->desc))
      strxcpy(aa->desc, aa->echoid, sizeof(aa->desc));
  }

  // Check if it's email or news
  CheckEMailOrNews(aa->echoid, aa->type);

  // Give each area a unique serial number
  aa->areaid = serial++;

  // Check if we already have the area (dup echoid or path)
  int _currarea = 0;
  area_iterator ap = idx.begin();
  area_iterator end = idx.end();

  for (; ap != end; ap++)
  {
    ++_currarea;
    int eq_echoid  = strieql(aa->echoid, (*ap)->echoid());
    if (eq_echoid)
    {
      newarea = false;
      if (not (*ap)->isseparator())
      {
        if (strblank((*ap)->desc()))
          strxcpy(desc, aa->desc, sizeof(desc));
      }
      break;
    }
    else if(not (*ap)->isseparator())
    {
      bool eq_path    = strieql(aa->path, (*ap)->path());
      bool eq_board   = (aa->board == (*ap)->board());
      bool eq_msgbase = (aa->basetype == (*ap)->basetype());
      bool eq_isfido  = (((aa->basetype == "OPUS") || (aa->basetype == "FTS1")) &&
                         (((*ap)->basetype() == "OPUS") || ((*ap)->basetype() == "FTS1")));

      if (eq_path and eq_board and (eq_msgbase or eq_isfido))
      {
        // We had it already, so override with the new data
        newarea = false;
        if(strblank((*ap)->desc()))
          strxcpy(desc, aa->desc, sizeof(desc));
        break;
      }
    }
  }

  // If first netmail area, set default for AREAFREQTO and AREAREPLYTO
  if((aa->isnet() and not aa->isseparator()) and not net1st) {
    net1st = true;
    if(not *CFG->areafreqto)
      strcpy(CFG->areafreqto, aa->echoid);
    if(not *CFG->areareplyto)
      strcpy(CFG->areareplyto, aa->echoid);
  }

  // Increase area index size if new area
  if(newarea) {
    idx.push_back(NewArea(aa->basetype));
    throw_new(idx[_currarea]);
    ap = idx.end(); --ap;
  }

  // Add aka if not found
  if (aa->aka.net)
  {
    bool found = false;
    std::vector<gaka>::iterator i = CFG->aka.begin();
    std::vector<gaka>::iterator end = CFG->aka.end();

    for (; i != end; i++)
    {
      if (aa->aka == i->addr)
      {
        found = true;
        break;
      }
    }

    if (not found)     // Then add it
      CfgAddress(aa->aka.make_string(buf));  // Add the aka
  }
  else {
    // If address is missing, use main address
    if(not CFG->aka.empty())
      aa->aka = CFG->aka[0].addr;
  }

  // Copy the data
  if(newarea) {
    (*ap)->set_echoid(aa->echoid);
    (*ap)->set_path(aa->path);
    (*ap)->set_board(aa->board);
    (*ap)->set_basetype(aa->basetype);
  }
  (*ap)->set_desc(newarea or strblank(desc) ? aa->desc : desc);
  (*ap)->set_areaid(aa->areaid);
  (*ap)->set_groupid(aa->groupid);
  (*ap)->set_type(aa->type);
  (*ap)->set_aka(aa->aka);
  (*ap)->set_originno(aa->originno);
  (*ap)->set_attr(aa->attr);
  (*ap)->set_scan(aa->scan);
  (*ap)->set_scanexcl(aa->scanexcl);
  (*ap)->set_scanincl(aa->scanincl);
  (*ap)->set_pmscan(aa->pmscan);
  (*ap)->set_pmscanexcl(aa->pmscanexcl);
  (*ap)->set_pmscanincl(aa->pmscanincl);
}


//  ------------------------------------------------------------------
//  Get areas from other programs

void AreaList::GetAreafile(char* value) {

  const word CRC_ECHOLIST = 0xCE70;

  word crcval;
  char* keyword;
  char* options;
  int beginarea, endarea;

  crcval = getkeyvalcrc(&keyword, &value);
  options = throw_strdup(value);

  beginarea = idx.size();

  if(crcval == CRC_ECHOLIST)
    ReadEcholist(value);
  else {
    AFILE->quiet = quiet;
    AFILE->sharemode = CFG->sharemode;
    AFILE->fidomsgtype = CFG->fidomsgtype;
    AFILE->ra2usersbbs = CFG->ra2usersbbs;
    AFILE->squishuserno = CFG->squishuserno;
    AFILE->areapath = CFG->areapath;
    AFILE->pcboardpath = CFG->pcboardpath;

    if(not CFG->aka.empty())
      AFILE->primary_aka = CFG->aka[0].addr;
    else
      AFILE->primary_aka = 0;

    AFILE->attribsnet = CFG->attribsnet;
    AFILE->attribsecho = CFG->attribsecho;
    AFILE->attribsnews = CFG->attribsnews;
    AFILE->attribsemail = CFG->attribsemail;
    AFILE->attribslocal = CFG->attribslocal;

    AFILE->ReadAreafile(crcval, value);

    CFG->ra2usersbbs = AFILE->ra2usersbbs;
    CFG->squishuserno = AFILE->squishuserno;
  }

  endarea = idx.size();
  SortAreaGroup(options, beginarea, endarea);

  throw_free(options);
}


//  ------------------------------------------------------------------
//  Get area definition

void AreaList::GetArea(char* def) {

  AreaCfg aa;
  char* ptr;
  char* tag;
  char* desc;
  char* base;
  char* loc;
  char* attr;

  if(strnieql(def, "-FILE ", 6))  // Check for alternate formats
    GetAreafile(def+6);
  else {
    tag = def;
    desc = strskip_txt(tag);
    *desc = NUL;

    aa.reset();

    // Split up the string definition
    desc = strskip_wht(desc+1);
    switch(*desc) {
      case '\"':
        base = strskip_to(desc+1, '\"')+1;
        break;
      case '\'':
        base = strskip_to(desc+1, '\'')+1;
        break;
      default:
        base = strskip_txt(desc);
    }
    *base = NUL;
    base = strskip_wht(base+1);
    loc = strskip_txt(base);
    *loc = NUL;
    loc = strskip_wht(loc+1);
    attr = strpbrk(loc, " \t");
    if(attr and *attr) {
      *attr = NUL;
      attr = strskip_wht(attr+1);
    }

    // Parse the individual parts

    aa.setechoid(tag);              // Copy the echotag

    StripQuotes(desc);              // Copy description, stripping quotes ("")
    aa.setdesc(desc);

    switch(g_toupper(*base)) {        // Store area info

      case '-':
        aa.basetype = "SEPARATOR";
        break;

      case 'M':
        aa.basetype = "SQUISH";
        aa.setpath(loc);
        break;

      case 'Y':
        aa.basetype = "SMB";
        aa.setpath(loc);
        break;

      case 'H':
      case 'R':
      case 'Q':
        aa.basetype = "HUDSON";
        aa.board = (uint) atoi(loc);
        break;

      case 'G':
        aa.basetype = "GOLDBASE";
        aa.board = (uint) atoi(loc);
        break;

      case 'E':
        aa.basetype = "EZYCOM";
        aa.board = (uint) atoi(loc);
        break;

      case 'J':
        aa.basetype = "JAM";
        aa.setpath(loc);
        break;

      case 'P':
        aa.basetype = "PCBOARD";
        aa.setpath(loc);
        break;

      case 'W':
        aa.basetype = "WILDCAT";
        aa.setpath(loc);
        break;

      case 'X':
        aa.basetype = "ADEPTXBBS";
        aa.setpath(loc);
        break;

      case 'S':
        aa.basetype = "FTS1";
        aa.setpath(loc);
        break;

      case 'F':
      case 'O':
      default:
        aa.basetype = "OPUS";
        aa.setpath(loc);
    }

    aa.type = GMB_ECHO;
    aa.attr = CFG->attribsecho;
    strupr(base);
    if(strchr(base, 'N')) {
      aa.type = GMB_NET;
      aa.attr = CFG->attribsnet;
    }
    if(strchr(base, 'L')) {
      aa.type = GMB_LOCAL;
      aa.attr = CFG->attribslocal;
    }
    if(strchr(base, 'E')) {
      aa.type = GMB_ECHO;
      aa.attr = CFG->attribsecho;
    }
    if(attr) {                      // Get attributes and AKAs
      GetAttribstr(&aa.attr, attr);
      if((ptr = strpbrk(attr, "0123456789")) != NULL) {
        int n = atoi(ptr);
        if((n < 0) or ((uint) n >= CFG->aka.size()))
          n = 0;
        if(not CFG->aka.empty())
          aa.aka = CFG->aka[n].addr;   // Found AKA number
      }
    }

    if(not aa.aka.net and not CFG->aka.empty())
      aa.aka = CFG->aka[0].addr;

    AddNewArea(&aa);
  }
}


//  ------------------------------------------------------------------
//  Get area definition (new)
//
//  Syntax of AREADEF keyword:
//
//    AREADEF <echoid> <"desc"> <group> <type> <msgbase> <path/board> <aka> <(attrs)> ["origin"]
//
//  Examples:
//
//    AREADEF NET.ALL  "Netmail, Line 1" N Net   Opus   R:\NETMAIL\             2:231/77 (PVT K/S)
//    AREADEF TEST     "Testing..."      D Echo  Hudson 67                      .        ()
//    AREADEF 1LOCAL   "Sysop <-> Users" L Local Squish R:\MAX\MSG\LOC\1LOCAL   .        (PVT)     "Your Sysop * The Goldware BBS Line 1"
//    AREADEF DANEINFO "DaneNet Info"    D Echo  Squish R:\MAX\MSG\231\DANEINFO .        (R/O)

bool AreaList::GetAreaFirstPart(AreaCfg& aa, char*& key, char*& val) {

  const word CRC_NET      = 0xEC5E;
  const word CRC_ECHO     = 0xC2D1;
  const word CRC_LOCAL    = 0x4CD5;
  const word CRC_EMAIL    = 0x9C64;
  const word CRC_NEWS     = 0x61F1;

  // Get echoid
  getkeyval(&key, &val);
  aa.setechoid(key);

  // Get description
  getkeyval(&key, &val);
  aa.setdesc(key);

  // Get group (letter)
  getkeyval(&key, &val);
  aa.groupid = getgroup(key);

  // Get type
  word crc = getkeyvalcrc(&key, &val);
  switch(crc) {
    case CRC_NET:
      aa.type = GMB_NET;
      aa.attr = CFG->attribsnet;
      break;
    case CRC_ECHO:
      aa.type = GMB_ECHO;
      aa.attr = CFG->attribsecho;
      break;
    case CRC_LOCAL:
      aa.type = GMB_LOCAL;
      aa.attr = CFG->attribslocal;
      break;
    case CRC_EMAIL:
      aa.type = GMB_EMAIL | GMB_NET;
      aa.attr = CFG->attribsemail;
      break;
    case CRC_NEWS:
      aa.type = GMB_NEWSGROUP | GMB_ECHO;
      aa.attr = CFG->attribsnews;
      break;
    default:
      return false;
  }

  return true;
}


//  ------------------------------------------------------------------

void AreaList::GetAreaSep(char* val) {

  char* key;
  AreaCfg aa;

  aa.reset();
  aa.basetype = "SEPARATOR";

  if(not GetAreaFirstPart(aa, key, val))
    return;

  // Get aka
  getkeyval(&key, &val);
  if(not CFG->aka.empty())               // Seed aka with main address
    aa.aka = CFG->aka[0].addr;

  aa.aka.set(key);

  AddNewArea(&aa);
}


//  ------------------------------------------------------------------

void AreaList::GetAreaDef(char* val) {

  const word CRC_SEP      = 0x2FC6;
  const word CRC_FIDO     = 0x3AC8;
  const word CRC_OPUS     = 0x1254;
  const word CRC_SDMSG    = 0xAEC0;
  const word CRC_FTS1     = 0xEE2A;
  const word CRC_FTSC     = 0xEE58;
  const word CRC_QBBS     = 0x175B;
  const word CRC_HUDSON   = 0xBAB1;
  const word CRC_SQUISH   = 0xFCF6;
  const word CRC_EZYCOM   = 0xC81B;
  const word CRC_JAM      = 0xA8C3;
  const word CRC_GOLD     = 0x6134;
  const word CRC_GOLDBASE = 0x560D;
  const word CRC_PCB      = 0x19B7;
  const word CRC_PCBOARD  = 0x84EC;
  const word CRC_WCAT     = 0xAEDB;
  const word CRC_WILDCAT  = 0x7F3A;
  const word CRC_XBBS     = 0xADC3;
  const word CRC_SMB      = 0x27D4;

  char* key;
  AreaCfg aa;

  aa.reset();

  if(not GetAreaFirstPart(aa, key, val))
    return;

  // Get format
  word crc = getkeyvalcrc(&key, &val);
  getkeyval(&key, &val);        // Get path/board
  switch(crc) {
    case CRC_SEP:
      aa.basetype = "SEPARATOR";
      break;
    case CRC_FIDO:
    case CRC_OPUS:
    case CRC_SDMSG:
      aa.basetype = "OPUS";
      aa.setpath(key);
      break;
    case CRC_FTS1:
    case CRC_FTSC:
      aa.basetype = "FTS1";
      aa.setpath(key);
      break;
    case CRC_QBBS:
    case CRC_HUDSON:
      aa.basetype = "HUDSON";
      aa.board = (uint) atoi(key);
      if((aa.board < 1) or (aa.board > 200)) {
        return;
      }
      break;
    case CRC_GOLD:
    case CRC_GOLDBASE:
      aa.basetype = "GOLDBASE";
      aa.board = atoi(key);
      if((aa.board < 1) or (aa.board > 500)) {
        return;
      }
      break;
    case CRC_SQUISH:
      aa.basetype = "SQUISH";
      aa.setpath(key);
      break;
    case CRC_SMB:
      aa.basetype = "SMB";
      aa.setpath(key);
      break;
    case CRC_EZYCOM:
      aa.basetype = "EZYCOM";
      aa.board = atoi(key);
      break;
    case CRC_JAM:
      aa.basetype = "JAM";
      aa.setpath(key);
      break;
    case CRC_PCB:
    case CRC_PCBOARD:
      aa.basetype = "PCBOARD";
      aa.setpath(key);
      break;
    case CRC_WCAT:
    case CRC_WILDCAT:
      aa.basetype = "WILDCAT";
      aa.setpath(key);
      break;
    case CRC_XBBS:
      aa.basetype = "ADEPTXBBS";
      aa.setpath(key);
      break;
    default:
      return;
  }

  // Get aka
  getkeyval(&key, &val);
  if(not CFG->aka.empty())               // Seed aka with main address
    aa.aka = CFG->aka[0].addr;

  aa.aka.set(key);

  // Get attributes
  getkeyval(&key, &val);
  GetAttribstr(&aa.attr, key);

  // Get origin (optional)
  getkeyval(&key, &val);
  aa.setorigin(key);

  // Add to arealist
  AddNewArea(&aa);
}


//  ------------------------------------------------------------------

void ReadEcholist(char* val) {

  AL.ReadEcholist(val);
}


//  ------------------------------------------------------------------

void getdztoken(char** key, char** val) {

  char* p = *val;

  if ((val == NULL)or(key==NULL)) {
    LOG_NULL_POINTER;
    LOG.printf("! Illegall call of function: getdztoken(%s,%s). File %s line %i ", key?"key":"NULL", val?"val":"NULL", __FILE__, __LINE__);
    PointerErrorExit();
    return;
  }
  *key = p;
  p = strskip_to(p, ',');
  if(*p == ',')
    *p++ = NUL;
  *val = p;
}


//  ------------------------------------------------------------------

void AreaList::ReadEcholist(char* val)
{
  char* key;
  Path file;
  char buf[256], options[80];
  bool is_sqafix = false;
  bool is_dz = false;

  if (val == NULL) {
    LOG_NULL_POINTER;
    LOG.printf("! Illegall call of function: AreaList::ReadEcholist(NULL). File %s line %i ", __FILE__, __LINE__);
    PointerErrorExit();
    return;
  }
  // Get filename or options
  *file = NUL;
  *options = NUL;
  while(*val) {
    getkeyval(&key, &val);
    if(*key == '-') {
      // Get option
      if(strieql(key+1, "SQAFIX"))
        is_sqafix = true;
      else if(strieql(key+1, "DZ"))
        is_dz = true;
      else {
        strcat(options, key);
        strcat(options, " ");
      }
      continue;
    }
    strcpy(file, key);
  }

  if (*file)
  {
    gfile fp(file, "rt", CFG->sharemode);
    if (fp.isopen())
    {
      void (*tok)(char**, char**);

      if(is_dz)
        tok = getdztoken;
      else
        tok = getkeyval;

      if (not quiet)
        STD_PRINTNL("* Reading " << file);

      while (fp.Fgets((val=buf), sizeof(buf)))
      {
        // Get echoid
        if(is_sqafix) {
          tok(&key, &val);
          if(not strieql(key, "EchoArea"))
            continue;
        }
        else if(is_dz) {
          if(not g_isalpha(*val) and (*val != ','))
            continue;
          tok(&key, &val);
        }
        tok(&key, &val);
        if(*key and (*key != ';') and (*key != '%')) {
          if(is_sqafix) {
            char *grp, *desc;
            tok(&grp, &val);
            if((desc = strchr(val, '\"')) != NULL) {
              char *p = strchr(++desc, '\"');
              if(p)
                *p = NUL;
              else
                desc = NULL;
            }

            area_iterator ap = idx.begin();
            area_iterator end = idx.end();

            for (; ap != end; ap++)
            {
              if (strieql(key, (*ap)->echoid()))
              {
                (*ap)->set_groupid(g_toupper(*grp));
                if (desc) (*ap)->set_desc(desc);
                break;
              }
            }
          }
          else {
            char *desc;
            if(is_dz)
              tok(&desc, &val);
            else
              desc = val;

            area_iterator ap = idx.begin();
            area_iterator end = idx.end();

            for (; ap != end; ap++)
            {
              if (strieql(key, (*ap)->echoid()))
              {
                (*ap)->set_desc(desc);
                break;
              }
            }
          }
        }
      }
    }
  }
}


//  ------------------------------------------------------------------

void AreaList::GetAreaDesc(char* val) {

  char* key;
  Area* aa = NULL;

  if (val == NULL) {
    LOG_NULL_POINTER;
    LOG.printf("! Illegall call of function: AreaList::GetAreaDesc(NULL). File %s line %i ", __FILE__, __LINE__);
    PointerErrorExit();
    return;
  }
  // Get echoid and find area
  getkeyval(&key, &val);

  area_iterator ap = idx.begin();
  area_iterator end = idx.end();

  for (; ap != end; ap++)
  {
    if (strieql(key, (*ap)->echoid()))
    {
      aa = *ap;
      break;
    }
  }

  if (aa == NULL)   // Not found, ignore
    return;

  // Get description
  getkeyval(&key, &val);
  aa->set_desc(key);

  // Get group letter (optional)
  getkeyval(&key, &val);
  if(*key) {
    if(*key != '-') {
      aa->set_groupid(getgroup(key));
    }

    // Get aka (optional)
    getkeyval(&key, &val);
    if(*key) {

      if(*key != '-') {
        ftn_addr aka;
        if(not CFG->aka.empty())         // Seed aka with main address
          aka = CFG->aka[0].addr;

        aka.set(key);
        aa->set_aka(aka);
      }

      // Get attributes (optional)
      getkeyval(&key, &val);
      if(*key) {

        if(*key != '-') {
          Attr attr;
          GetAttribstr(&attr, key);
          aa->set_attr(attr);
        }

        // Get origin (optional)
        getkeyval(&key, &val);
        if(*key)
          aa->set_origin(key);
      }
    }
  }
}


//  ------------------------------------------------------------------

Area::Area(gmo_area* a) {

  throw_new(a);

  area = a;
  adat = NULL;
  bookmark = 0;
  marks = 0;
  unread = 0;
  isvalidchg = false;
  isunreadchg = false;
  isreadmark = false;
  isreadpm = false;
  isscanned = false;
  ispmscanned = false;
  istossed = false;
  findfirst = true;
}


//  ------------------------------------------------------------------

Area::~Area() {

  if(isopen())
    Close();

  throw_delete(area);
}


//  ------------------------------------------------------------------

char* MapPath(char* map, bool reverse)
{
  Path buf, cmap;

  if(map==NULL) {
    LOG_NULL_POINTER;
    LOG.printf("! Illegall call of function: MapPath(NULL, %s). File %s line %i ", reverse?"TRUE":"FALSE", __FILE__, __LINE__);
    PointerErrorExit();
    return NULL;
  }
  strxcpy(cmap, map, sizeof(Path));
  if(reverse)
    strchg(cmap, GOLD_WRONG_SLASH_CHR, GOLD_SLASH_CHR);

  std::vector< std::pair<std::string, std::string> >::iterator i = CFG->mappath.begin();
  std::vector< std::pair<std::string, std::string> >::iterator end = CFG->mappath.end();

  for (; i != end; i++)
  {
    const char* p = reverse ? i->second.c_str() : i->first.c_str();
    const char* q = reverse ? i->first.c_str() : i->second.c_str();
    if(strnieql(cmap, p, strlen(p))) {
      strxcpy(buf, map, sizeof(Path));
      strxmerge(map, sizeof(Path), q, buf+strlen(p), NULL);
      char sl1, sl2;
      char* ptr;

      ptr = strpbrk((char *)p, "/\\");
      sl1 = ptr ? *ptr : NUL;
      ptr = strpbrk((char *)q, "/\\");
      sl2 = ptr ? *ptr : NUL;

      if(sl1 and sl2 and (sl1 != sl2))
        strchg(map, sl1, sl2);

      break;
    }
  }
  return map;
}


//  ------------------------------------------------------------------
//  Set area origin

int AreaCfgBase::setorigin(std::string& _origin)
{
  if (not strblank(_origin.c_str()))
  {
    // Check if it already exists
    gstrarray::iterator n = CFG->origin.begin();
    gstrarray::iterator end = CFG->origin.end();

    for (originno = 0; n != end; originno++, n++)
    {
      if (*n == _origin)
        return originno;
    }

    // Not found, so add it
    CfgOrigin(_origin.c_str());
    // originno = CFG->origin.size()-1;
  }

  return originno;
}


//  ------------------------------------------------------------------
//  Set area echoid

const char* AreaCfg::setautoid(const char* _echoid) {

  if (_echoid == NULL) {
    LOG_NULL_POINTER;
    LOG.printf("! Illegall call of function: AreaCfg::setautoid(NULL). File %s line %i ", __FILE__, __LINE__);
    PointerErrorExit();
    return NULL;
  }
  return strxcpy(echoid, CFG->areaautoid ? "" : _echoid, sizeof(Echo));
}


//  ------------------------------------------------------------------
