/*ProfiPacket - packet radio terminal program
  Copyright (C) 1999  Alexander Feigl

  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

  Author:

  Alexander Feigl
  Burachstraße 51

  D-88250 Weingarten

  Mail : Alexander.Feigl@gmx.de
*/


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef __GNUC__
#define _storage_text_ __attribute__ ((section(".text")))
#else
#define _storage_text_
#endif

#include "GUIProtos.h"
#include "global.h"
#include "QSO.h"
#include "PPVersion.h"

#include "Mail.h"


#ifdef AmigaOS

#include <libraries/locale.h>

#include <exec/memory.h>
#include <dos/exall.h>

#include <clib/locale_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#ifdef __GNUC__


extern void *DOSBase;
extern void *SysBase;
extern void *LocaleBase;


#include <inline/dos.h>
#include <inline/exec.h>
#include <inline/locale.h>

#endif

#else /*AmigaOS*/

#include "amigaemu.h"

#endif /*AmigaOS*/

struct LockCall {
     struct LockCall *next;
     unsigned char callpattern[8];
        };

typedef const unsigned char *constr;


const constr _storage_text_ KnownPrograms[] =


  {
   (const constr) "ProfiPacket",
   (const constr) "XPACKET",
   (const constr) "FBB",
   (const constr) "SP",
   (const constr) "TOP",
   (const constr) "GP",
   (const constr) "TE",
   (const constr) "AmiCom",
   (const constr) "AHP",
   (const constr) "AP",
   (const constr) "WinGT",
   (const constr) "STOP",
   (const constr) NULL,
  };



extern void QSO_Cleanup2(void);

static int QSO_AddLockCall(unsigned char *call)
{
 unsigned char *cptr;
 struct LockCall *lcall;
 unsigned llen;


 llen=0;
 cptr=call;
 if (*call=='!') cptr++;
 while (*(cptr++)!='\0') llen++;
 if ( (llen==0) || (llen>6) ) return(FALSE);

 lcall=AllocVec(sizeof(struct LockCall),0);
 if (lcall==NULL) return(FALSE);

 lcall->next=LockCalls;
 CopyStringUpper(lcall->callpattern,call);
 LockCalls=lcall;
 return(TRUE);

}

void QSO_LoadLockCall(void)
 {
  BPTR callfile;
  unsigned char xline[384];
  unsigned char *xlptr;

  if ((callfile=Open("text/calllock.txt",MODE_OLDFILE))==NULL)
   {
    return;
   }


  while ( FGets(callfile,xline,380)!=NULL)
   {

    /* Strip CR*/
    xlptr=xline;
    while (*(xlptr++)!='\0');
    xlptr--;
    xlptr--;
    if (*xlptr=='\n') *(xlptr)='\0';

    /* Check for comment*/
    if ( *xline=='\0') continue;
    if ( *xline==';') continue;

    QSO_AddLockCall(xline);
   }
  Close(callfile);

  return;
 }

int QSO_CheckCall(const unsigned char *call)
 {
  struct LockCall *lcall;

  lcall=LockCalls;
  while (lcall!=NULL)
   {
    int match,antimatch,breakmatch;
    struct LockCall *tcall;
    unsigned char *sptr;
    const unsigned char *cptr;
    tcall=lcall;
    lcall=lcall->next;
    sptr=tcall->callpattern;
    cptr=call;
    match=FALSE;antimatch=FALSE;breakmatch=FALSE;
    if ((*sptr)=='!') {++sptr;antimatch=TRUE;}
    while (1)
     {
      switch (*sptr)
       {
        case '*':
         {
          if (*cptr=='\0') breakmatch=TRUE;
          if (*cptr=='-') breakmatch=TRUE;
          break;
         }
        case '#':
         {
          if ( (*cptr>'9') || (*cptr<'0')) breakmatch=TRUE;
          break;
         }
        case '$':
         {
          if ( (*cptr>'Z') || (*cptr<'A')) breakmatch=TRUE;
          break;
         }
        case '\0':
         {
          if ( (*cptr=='\0') || (*cptr=='-'))
           {
            match=TRUE;
           }
          breakmatch=TRUE;
         }
        default:
         {
          if (*cptr!=*sptr) breakmatch=TRUE;
          break;
         }
       }
      if (breakmatch) break;
      ++sptr;++cptr;
     }


    if ( (antimatch) && (!match) ) return(TRUE);
    if ( (!antimatch) && (match) ) return(TRUE);


   }
  return(FALSE);
 }


void QSO_TransmitSID(unsigned long channel,struct StationID *tsid)
{
 unsigned char SID[128];
 unsigned char *sptr;
 const unsigned char *dptr;
 unsigned long i;

 sptr=SID;

 if (tsid->Program==0) return;

 if (tsid->Version==-1) tsid->Version=PPVersion_Main;
 if (tsid->Revision==-1) tsid->Revision=PPVersion_Revision;
 if (tsid->Subrevision==-1) tsid->Subrevision=PPVersion_Subrevision;
 

 *(sptr++)='[';
 dptr=KnownPrograms[tsid->Program-1];
 while (*dptr!='\0') *(sptr++)=*(dptr++);
 *(sptr++)='-';
 if (tsid->Version>9) *(sptr++)='0'+(tsid->Version/10);
 *(sptr++)='0'+(tsid->Version%10);
 *(sptr++)='.';
 *(sptr++)='0'+(tsid->Revision/10);
 *(sptr++)='0'+(tsid->Revision%10);
 if (tsid->Subrevision)
  {
   *(sptr++)='.';
   *(sptr++)='0'+(tsid->Subrevision/10);
   *(sptr++)='0'+(tsid->Subrevision%10);
  }
 if (tsid->SystemType==SID_SystemType_BBS)
  {
   *(sptr++)='-';
#define AppendCap(mchr,version) \
  if (version>=0) { *(sptr++)=(mchr);\
  if ((version)>0) *(sptr++)='0'+(version);};
  for (i=0;i<26;++i)
   {
    signed char *capptr;
    capptr=(&tsid->AVersion)+i;
    AppendCap('A'+i,*capptr);
   }
  AppendCap('$',tsid->DollarVersion);
#undef AppendCap
  }
 *(sptr++)=']';
 *(sptr++)=13;
 QSO_SendData(sptr-SID,channel,SID);
}

int QSO_ParseSID(unsigned char *ssid,struct StationID *rsid)
{
 unsigned char ProgramName[16];
 unsigned char ProgramVersion[32];
 unsigned char ProgramCaps[32];

 unsigned char *sptr,*dptr;
 unsigned long lens;

 int progcap;

 progcap=FALSE;
 ProgramName[0]='\0';
 ProgramVersion[0]='\0';
 ProgramCaps[0]='\0';

 if (ssid[0]!='[') return (FALSE);


 sptr=ssid+1;
 dptr=ProgramName;

 lens=0;
 while (  (*sptr!='\0') && (*sptr!='-'))
  {
   if (lens<=14)
    {
     lens++;
     *(dptr+1)='\0';
     *(dptr++)=*(sptr);
    }
   ++sptr;
  }
 if (*sptr=='\0') return (FALSE);

 ++sptr;
 dptr=ProgramVersion;
 lens=0;

 while (  (*sptr!='\0') && (*sptr!=']') && (*sptr!='-') )
  {
   if (lens<=30)
    {
     lens++;
     *(dptr+1)='\0';
     *(dptr++)=*(sptr);
    }
   ++sptr;
  }



 if (*sptr=='\0') return (FALSE);
 if (*sptr=='-')
  {
   ++sptr;
   dptr=ProgramCaps;
   lens=0;
   progcap=TRUE;
   while ( (*sptr!='\0') && (*sptr!=']') )
    {
     if (lens<=30)
      {
       lens++;
       *(dptr+1)='\0';
       *(dptr++)=*(sptr);
      }
     ++sptr;
    }
   if (*sptr=='\0') return (FALSE);
  }

 if (progcap==FALSE)
  {
   if ( ( (ProgramVersion[0]>='A') && (ProgramVersion[0]<='Z') ) ||
          (ProgramVersion[0]=='$') )

    {
     sptr=ProgramVersion;
     dptr=ProgramCaps;
     do { *(dptr++)=*(sptr++); } while (*(sptr-1)!='\0');
     ProgramVersion[0]='\0';
     progcap=TRUE;
    }
  }


 rsid->Program=0;
 rsid->SystemType=SID_SystemType_Chat;

  {
   sptr=ProgramName;
   dptr=rsid->ProgramName;
   do {*(dptr++)=*(sptr++); } while (*(sptr-1)!='\0');
  }
 rsid->Version=0;
 rsid->Revision=0;
 rsid->Subrevision=0;

  {
   const unsigned char **lxptr;
   const unsigned char *xptr;
   unsigned long i;

   i=0;
   lxptr=(void *) KnownPrograms;
   while  (  (xptr=*(lxptr++))!=NULL)
    {
     ++i;
     sptr=ProgramName;
     while ( *(sptr++)==*(xptr++) ) if ( *(xptr-1)=='\0')
      {
       rsid->Program=i;
       break;
      }
     if (rsid->Program) break;
    }
  }

 if (ProgramVersion[0]!='\0')
  {
   unsigned long oldstage,stagenum,charnum,todecimal,todecimal2;
   unsigned char *pvptr;

   stagenum=0;
   pvptr=ProgramVersion;
   while (stagenum<3)
    {

     todecimal=0;
     oldstage=stagenum;
     charnum=0;
     while (1)
      {
       if ( *pvptr=='\0')
        {
         pvptr++;
         stagenum=3;
         break;
        }
       if ( *pvptr=='.')
        {
         pvptr++;
         stagenum++;
         break;
        }
       if ( (*pvptr<'0') || (*pvptr>'9'))
        {
         pvptr++;
         stagenum=3;
         break;
        }
       todecimal2= *pvptr - '0';
       todecimal = (todecimal*10)+todecimal2;
       pvptr++;
       charnum++;
      }
     if (charnum==0) break;
     switch (oldstage)
      {
       case 0:
        rsid->Version=todecimal;
        break;
       case 1:
        rsid->Revision=todecimal;
        break;
       case 2:
        rsid->Subrevision=todecimal;
        break;
      }
    }
  }

  {
   signed char *delptr;
   unsigned long i;
   delptr=&rsid->AVersion;
   for (i=0;i<32;++i) *(delptr++)=-1;
  }

 if ( (progcap==TRUE) && ( (ProgramCaps[0]<'0') || (ProgramCaps[0]>'9')))
  {
   unsigned char cap;
   unsigned long version;
    
   rsid->SystemType=SID_SystemType_BBS;
   sptr=ProgramCaps;
   while (*sptr!='\0')
     {
      if ( (  (*sptr<'A') || (*sptr>'Z')) && (*sptr!='$')) return (FALSE);
      cap=*sptr;
      version=0;
       if ( (*(sptr+1)>='0') && (*(sptr+1)<='9') )
        {
         version=*(sptr+1)-'0';
         sptr++;
        }
       sptr++;
       if (cap=='$')
        {
         rsid->DollarVersion=version;
        }
        else
        {
         signed char *capptr;
         capptr=(&rsid->AVersion)+(cap-'A');
         *capptr=version;
        }
      }
  }

  return(TRUE);
 }

 void QSO_HandleSID(struct QSO *tqso,struct StationID *sid)
 {
  tqso->StationID=*sid;
  if (tqso->User!=NULL)
   {
    struct User *user;
    int ident;
    user=tqso->User;
    ident=TRUE;
     {
      unsigned char *p1,*p2;
      unsigned long i;

      p1=(unsigned char *) (&user->StationID);
      p2=(unsigned char *) sid;
      for (i=0;i<sizeof(struct StationID);++i)
       {
        if ( *(p1++)!=*(p2++))
         {
          ident=FALSE;
          break;
         }
       }

     }

    if (ident==FALSE)
     {
      /* Station ID changed - update user database!!!!*/
      user->StationID=*sid;


      /* Update terminal type if not disconnect */

      if (user->Type!=3)
       {
        switch (sid->SystemType)
         {
          case SID_SystemType_Chat:
           {
            /* Chat program - update terminal type */

            switch (sid->Program)
             {
              case SID_Program_ProfiPacket:
                user->Type=1;
                break;

              default:
                user->Type=0;
                break;
             }
            break;
           }

          case SID_SystemType_BBS:
           {
            /* BBS program - update terminal type */

            switch (sid->Program)
             {
              case SID_Program_ProfiPacket:
                user->Type=6;
                break;
              case SID_Program_FBB:
                user->Type=5;
                break;
              default:
                user->Type=4;
                break;
             }
            break;
           }
         }
       }

      QSO_ChangeUser(user);
     }
   }
 }


 void QSO_FillUserdata(struct QSO *qso,unsigned char *name, unsigned char *qth,
                       unsigned char *locator,unsigned char *zipcode,
                       unsigned char *homebbs)
 {
  unsigned char xcall[16];

   {
    unsigned char *sptr,*dptr;
    sptr=qso->Call;
    dptr=xcall;
    while (  (*sptr!='-') && (*sptr!='\0') ) *(dptr++)=*(sptr++);
    *dptr='\0';


    if ( (sptr=MAIL_GetUserName(xcall))!=NULL) CopyString(name,sptr);
    if ( (sptr=MAIL_GetUserQTH(xcall))!=NULL) CopyString(qth,sptr);
    if ( (sptr=MAIL_GetUserZIP(xcall))!=NULL) CopyString(zipcode,sptr);
    if ( (sptr=MAIL_GetUserBBS(xcall))!=NULL) CopyString(homebbs,sptr);

   }

   {
    struct User *xuser;
    if (qso->User!=NULL)
     {
      xuser=qso->User;
      if (xuser->Name[0]!='\0') CopyString(name,xuser->Name);
      if (xuser->QTH[0]!='\0') CopyString(qth,xuser->QTH);
      if (xuser->Locator[0]!='\0') CopyString(locator,xuser->Locator);
     }
   }

 }


 void QSO_WriteUserdata(struct QSO *qso,unsigned char *name, unsigned char *qth,
                        unsigned char *locator,unsigned char *zipcode,
                        unsigned char *homebbs)
 {
  unsigned char xcall[16];
   {
    unsigned char *sptr,*dptr;
    unsigned int compared;
    sptr=qso->Call;
    dptr=xcall;
    compared=0;
    while (  (*sptr!='-') && (*sptr!='\0') ) *(dptr++)=*(sptr++);
    *dptr='\0';


    if ( (sptr=MAIL_GetUserName(xcall))!=NULL) if (CompareString(name,xcall)) ++compared;
    if ( (sptr=MAIL_GetUserQTH(xcall))!=NULL) if (CompareString(qth,xcall)) ++compared;
    if ( (sptr=MAIL_GetUserZIP(xcall))!=NULL) if (CompareString(zipcode,xcall)) ++compared;
    if ( (sptr=MAIL_GetUserBBS(xcall))!=NULL) if (CompareString(homebbs,xcall)) ++compared;
    if (compared!=4) MAIL_ChangeUser(xcall,name,qth,zipcode,homebbs,TRUE);
   }
   {
    struct User *xuser;
    if (qso->User!=NULL)
     {
      xuser=qso->User;
      if (!CompareString(xuser->Name,name))
       {
        CopyStringLimit(xuser->Name,name,32);
        QSO_ChangeUser(xuser);
       }
      if (!CompareString(xuser->QTH,qth))
       {
        CopyStringLimit(xuser->QTH,qth,32);
        QSO_ChangeUser(xuser);
       }

      if (!CompareString(xuser->Locator,locator))
       {
        CopyString(xuser->Locator,locator);
        QSO_ChangeUser(xuser);
       }
     }


   }

 }


void QSO_HandleNameRequest(unsigned long channel,unsigned long flags)
 {

  unsigned char dummybuffer[128];



  if (flags&1)
   {
    unsigned char *cptr;
   CopyString(dummybuffer,"\r#NAM#");
   CopyString(dummybuffer+6,USER_GetSysopName());

   cptr=dummybuffer;
   while (*cptr!='\0') ++cptr;
   *(cptr++)='\r';
   *(cptr)='\0';
   MAIN_ConvertToPC(dummybuffer);
   QSO_TransmitText(dummybuffer,channel,FALSE,FALSE);
  }
 if (flags&2)
  {
   unsigned char *cptr;
   CopyString(dummybuffer,"\r#QTH#");
   CopyString(dummybuffer+6,USER_GetSysopQTH());

   cptr=dummybuffer;
   while (*cptr!='\0') ++cptr;
   *(cptr++)='\r';
   *(cptr)='\0';
   MAIN_ConvertToPC(dummybuffer);
   QSO_TransmitText(dummybuffer,channel,FALSE,FALSE);
  }
 if (flags&4)
  {
   unsigned char *cptr;
   CopyString(dummybuffer,"\r#LOC#");
   CopyString(dummybuffer+6,USER_GetSysopLocator());

   cptr=dummybuffer;
   while (*cptr!='\0') ++cptr;
   *(cptr++)='\r';
   *(cptr)='\0';
   MAIN_ConvertToPC(dummybuffer);
   QSO_TransmitText(dummybuffer,channel,FALSE,FALSE);
  }

}

int QSO_TestVersion (struct StationID *sid,
                     unsigned long ver,
                     unsigned long rev,unsigned long subrev)
 {
  if (ver<sid->Version) return(TRUE);
  if (ver>sid->Version) return(FALSE);
  if (rev<sid->Revision) return (TRUE);
  if (rev>sid->Revision) return(FALSE);
  if (subrev>sid->Subrevision) return(FALSE);
  return(TRUE);
 }

unsigned long QSO_TestNameRequest(unsigned long channel)
 {
  struct QSO *thisqso;
  struct StationID *sid;
  struct User *tuser;
  unsigned long xflag;

  thisqso=QSOs+(channel-1);

  tuser=thisqso->User;
  if (tuser==NULL) return(0);

  sid=&thisqso->StationID;

  xflag=0;

  if ( ( ( sid->Program==SID_Program_ProfiPacket) && (QSO_TestVersion(sid,0,24,11)) ) ||
       ( ( sid->Program==SID_Program_XPacket) && (QSO_TestVersion(sid,1,73,0)) )  |
       ( ( sid->Program==SID_Program_AP) && (QSO_TestVersion(sid,3,28,0)) ) )
   {
    unsigned char dummybuffer[64];
    CopyString(dummybuffer,"\r#REQUESTNAME:---#\r");

    if (*tuser->Name=='\0')
     {
      *(dummybuffer+14)='+';
      xflag|=1;
     }
    if (*tuser->QTH=='\0')
     {
      *(dummybuffer+15)='+';
      xflag|=2;
     }
    if (*tuser->Locator=='\0')
     {
      *(dummybuffer+16)='+';
      xflag|=4;
     }

    if (xflag)
     {
      QSO_TransmitText(dummybuffer,channel,FALSE,FALSE);
     }
   }
  return(xflag);
 }


/******************************LANGUAGE MODULE *************************/

extern void *LocaleBase;
extern struct Locale *Locale;

struct LanguageNode {

     const struct LanguageNode *next;
     const unsigned long LanguageCode;
     const unsigned long LanguageString;
     const unsigned char *LanguageName;
     const unsigned char *LanguageID;
     const unsigned char *LanguageLocale;
        };

struct CatalogCache {
     struct CatalogCache *next;
     unsigned char * CatalogName;
     unsigned long CatalogLCode;
     struct Catalog *Catalog;
        };

/**********************************LANGUAGE DEFINITIONS******************/

extern long int StandardLanguage;

static const struct LanguageNode _storage_text_ Language_Spain =
 {
  (const void *) 0,
  8,
  633,
  (const constr) "Espanñl",
  (const constr) "E",
  (const constr) "espanñol.language"
 };


static const struct LanguageNode _storage_text_ Language_Czech =
 {
  &Language_Spain,
  7,
  632,
  (const constr) "Czech",
  (const constr) "CZ",
  (const constr) "czech.language"
 };



static const struct LanguageNode _storage_text_ Language_Norsk =
 {
  &Language_Czech,
  6,
  577,
  (const constr) "Norsk",
  (const constr) "NOR",
  (const constr) "norsk.language"
 };


static const struct LanguageNode _storage_text_ Language_Polski =
 {
  &Language_Norsk,
  5,
  576,
  (const constr) "Polski",
  (const constr) "PL",
  (const constr) "polski.language"
 };


static const struct LanguageNode _storage_text_ Language_Nederland =
 {
  &Language_Polski,
  4,
  539,
  (const constr) "Nederlands",
  (const constr) "NLD",
  (const constr) "nederlands.language"
 };


static const struct LanguageNode _storage_text_ Language_French =
 {
  &Language_Nederland,
  3,
  505,
  (const constr) "Français",
  (const constr) "FRA",
  (const constr) "français.language"
 };


static const struct LanguageNode _storage_text_ Language_German =
 {
  &Language_French,
  2,
  314,
  (const constr) "Deutsch",
  (const constr) "DEU",
  (const constr) "deutsch.language",
 };


static const struct LanguageNode _storage_text_ Language_English =
 {
  &Language_German,
  1,
  313,
  (const constr) "English",
  (const constr) "EN",
  (const constr) "english.language",
 };

static const struct LanguageNode _storage_text_ Language_Table =
 {
  &Language_English,
  0,
  312,
  (const constr) "Standard",
  (const constr) "STD",
  0
 };


struct Catalog *QSO_GetQSOCatalog(struct LanguageNode *lnod,
                                         unsigned char *cname)
{
 unsigned long lcode;
 struct CatalogCache *xparse;
 const unsigned char *sptr;
 unsigned char *dptr;
 unsigned char xopenname[64];

 if (LocaleBase==NULL) return(NULL);
 lcode = lnod->LanguageCode;
 xparse=CatalogHook;
 while (xparse!=NULL)
  {
   if (xparse->CatalogLCode!=lcode)
    {
     xparse=xparse->next;
     continue;
    }
   sptr=xparse->CatalogName;
   dptr=cname;

  while ( *(sptr++)==*(dptr++) )
    {
     if (*(sptr-1)=='\0') return (xparse->Catalog);
    }
   xparse=xparse->next;
  }
 xparse=AllocVec(sizeof(struct CatalogCache),0);

 if (xparse==NULL) return (NULL);

 xparse->next=CatalogHook;
 xparse->CatalogName=cname;
 xparse->CatalogLCode=lcode;
 sptr=lnod->LanguageLocale;
 dptr=xopenname;
 while ( (*sptr!='.') && (*sptr!='\0') ) *(dptr++)=*(sptr++);
 *(dptr)='\0';
 xparse->Catalog=OpenCatalog(NULL,cname,OC_Language,(unsigned long)
                             xopenname,TAG_DONE);
 CatalogHook=xparse;
 return(xparse->Catalog);
}

 unsigned char *QSO_GetText(struct QSO *qso,unsigned char *txttable,
                            unsigned char *catalogname,
                            unsigned long textnumber)
 {
  signed short *txtnum;
  unsigned char *catalo;
  const struct LanguageNode *lnode;
  struct Catalog *mycat;

  /* get me from the builtin language */
  catalo=txttable-1;
  while (1)
   {
    txtnum= (signed short *) ( (unsigned long) (catalo+1)&0xfffffffe);
    catalo= ((unsigned char *) (txtnum) )+2;
    if ( *txtnum==-1) return ("<<cannot get text>>\r");
    if (*txtnum==textnumber) break;
    while (*(catalo++)!='\0');
   }

  lnode=GetLanguageNode(qso);

  if ( (lnode==NULL) || (lnode->LanguageCode<2) ) return (catalo);

  mycat=QSO_GetQSOCatalog((struct LanguageNode *) lnode,catalogname);
  if (mycat==NULL) return(catalo);

  return GetCatalogStr(mycat,textnumber,catalo);
 }




const struct LanguageNode *GetLanguageTable(void)
 {
  return(&Language_Table);
 }

const struct LanguageNode *GetLanguageNode(const struct QSO *qso)
 {
  const struct LanguageNode *langnode;
  int language;
  language=StandardLanguage;
  if (qso!=NULL)
   {
    if (qso->User!=NULL)
     {
      language=qso->User->Language;
     }
   }
  langnode=GetLanguageTable();
  while (langnode!=NULL)
   {
    if (langnode->LanguageCode==language)
     {
      return(langnode);
     }
   
    langnode=langnode->next;
   }
  return(GetLanguageTable()); 
 }

int GetStandardLanguage(void)
 {
  return(StandardLanguage);
 } 

void InitStandardLanguage(void)
 {
  const struct LanguageNode *lnode;
  StandardLanguage=1;   /* preset with English */
  if (Locale==NULL) return;
  
  lnode=GetLanguageTable();
  if (lnode==0) return;
  lnode=lnode->next;
  
  while (lnode!=NULL)
   {
    if(  (lnode->LanguageLocale!=NULL)
        && CompareString(Locale->loc_LocaleName,lnode->LanguageLocale))
     {
      StandardLanguage=lnode->LanguageCode;
     }
    lnode=lnode->next;
   }
 }

 
unsigned long GetLanguageString(unsigned long lnum)
 {
  const struct LanguageNode *ln;
  
  ln=GetLanguageTable();
  while (ln!=NULL)
   {
    if (lnum==ln->LanguageCode)
     {
      return(ln->LanguageString);
     }
    ln=ln->next;
   }
  return(0);
 }

 /**********************************cleanup*/

void QSO_Cleanup(void)
{
 while (CatalogHook!=NULL)
  {
   struct CatalogCache *tcata;
   tcata=CatalogHook;
   CatalogHook=tcata->next;
   if (tcata->Catalog!=NULL) CloseCatalog(tcata->Catalog);
   FreeVec(tcata);
  }
 while (LockCalls!=NULL)
  {
   struct LockCall *tlcall;
   tlcall=LockCalls;
   LockCalls=tlcall->next;
   FreeVec(tlcall);
  }
 QSO_Cleanup2();
}
 