/* Copyright (C) 1996,1997,1998,1999 by Salvador E. Tropea (SET),
   see copyrigh file for details */
#include <ceditint.h>
#include <stdio.h>
#include <string.h>

#define Uses_TCEditor_Internal
#define Uses_TCEditor_External
#define Uses_TCEditor
#include <ceditor.h>

#ifdef SUP_PCRE
#include <pcre.h>
#endif
#include <dyncat.h>

static void PCREInitCompiler();
static void PCREStopCompiler();
static pcre *PCRECompileRegEx(char *text);
static int PCREDoSearch(char *search, int len, pcre *CompiledPCRE);

ccIndex *ConvTable=0;

static void ReplaceCRby0(char *s)
{
 for (; *s && *s!='\n' && *s!='\r'; s++);
 *s=0;
}

static char *MoveAfterEqual(char *s)
{
 for (; *s && *s!='='; s++);
 if (*s) s++;
 for (; *s && ucisspace(*s); s++);
 return s;
}

static void GetUpTo(int len, char *s, char *d, int &l)
{
 if (*s=='"')
   {
    for (s++,l=0; *s && *s!='\n' && *s!='\r' && *s!='"' && l<len; s++,l++)
       {
        if (*s=='\\' && *s && *s!='\n' && *s!='\r')
           s++;
        d[l]=*s;
       }
    return;
   }
 for (l=0; *s && *s!='\n' && *s!='\r' && l<len; s++,l++) d[l]=*s;
}

static void FillTableWith(unsigned char *s, ushort *t, int mask)
{
 for (; *s && *s!='\n' && *s!='\r'; s++) t[*s]|=mask;
}

/**[txh]********************************************************************

  Description:
  Converts a syntax value (index in the syntax hl array) into an index in
the alphabetic collection.

***************************************************************************/

ccIndex SHLConvValToPos(int a)
{
 if (!ConvTable) return a;
 return ConvTable[a];
}

/**[txh]********************************************************************

  Description:
  Converts an index in the sorted collection of syntax hl names in a syntax
value (index in the syntax hl array).

***************************************************************************/

int SHLConvPosToVal(ccIndex a)
{
 if (!ConvTable) return a;

 int i=0;
 while (ConvTable[i]!=a) i++;
 return i;
}

static void PutInTables(int len, char *s, unsigned flag, ushort *Table, int Case)
{
 int i;

 if (len)
   {
    if (!Case)
      {
       for (i=0; i<len; i++)
           s[i]=uctoupper(s[i]);
       Table[uctolower(*s)]|=flag;
      }
    Table[*s]|=flag;
   }
}

int LoadSyntaxHighLightFile(char *name, strSHL *&hl, TStringCollection *list,int &Cant)
{
 FILE *f;
 char b[maxSHLFileWidth];
 int  defs,def,end,i,isCase;
 char *pos,*s;

 Cant=0;
 if ((f=fopen(name,"rb"))==NULL)
    return 1;

 // Meassure the number of definitions
 for (defs=0; !feof(f); )
    {
     fgets(b,maxSHLFileWidth,f);
     if (!feof(f) && strncasecmp(b,"End",3)==0)
        defs++;
    }

 Cant=defs;
 if (!defs)
    return 2;

 // Allocate enough memory
 hl=new strSHL[defs];
 if (!hl)
    return 3;
 memset(hl,0,sizeof(strSHL)*defs);
 ConvTable=new ccIndex[defs];
 if (!ConvTable)
    return 3;

 PCREInitCompiler();
 // Load and parse all
 rewind(f);
 for (def=0; def<defs && !feof(f); def++)
    {
     hl[def].Keywords=new TStringCollection(48,12);
     isCase=0;
     if (!hl[def].Keywords)
        return 3;
     for (i=0; i<256; i++)
         if (ucisalnum(i) || i=='_')
            hl[def].SymbolT[i]|=shl_INSNAME | shl_BEGNAME;
     do
       {
        do
          {
           fgets(b,maxSHLFileWidth,f);
          }
        while (*b=='#' || ucisspace(*b));

        if (strncasecmp(b,"NameMatch",9)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           if (*pos)
              hl[def].NameMatch=PCRECompileRegEx(pos);
          }
        else
        if (strncasecmp(b,"FullNameMatch",13)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           if (*pos)
              hl[def].PathMatch=PCRECompileRegEx(pos);
          }
        else
        if (strncasecmp(b,"Name",4)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           char *s=newStr(pos);
           hl[def].Name=s;
           list->insert(s);
          }
        else
        if (strncasecmp(b,"Files",5)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           hl[def].Extensions=newStr(pos);
          }
        else
        if (strncasecmp(b,"EmacsModes",10)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           hl[def].EmacsModes=newStr(pos);
          }
        else
        if (strncasecmp(b,"ShellScript",11)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           hl[def].ShellScript=newStr(pos);
          }
        else
        if (strncasecmp(b,"OpenComment1",12)==0)
          {
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].OpenCom1,hl[def].lOpenCom1);
           PutInTables(hl[def].lOpenCom1,hl[def].OpenCom1,shl_OPC1,hl[def].SymbolT,isCase);
          }
        else
        if (strncasecmp(b,"CloseComment1",13)==0)
          {
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].CloseCom1,hl[def].lCloseCom1);
           PutInTables(hl[def].lCloseCom1,hl[def].CloseCom1,shl_CLOSE1,hl[def].SymbolT,isCase);
          }
        else
        if (strncasecmp(b,"OpenComment2",12)==0)
          {
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].OpenCom2,hl[def].lOpenCom2);
           PutInTables(hl[def].lOpenCom2,hl[def].OpenCom2,shl_OPC2,hl[def].SymbolT,isCase);
          }
        else
        if (strncasecmp(b,"CloseComment2",13)==0)
          {
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].CloseCom2,hl[def].lCloseCom2);
           PutInTables(hl[def].lCloseCom2,hl[def].CloseCom2,shl_CLOSE2,hl[def].SymbolT,isCase);
          }
        else
        if (strncasecmp(b,"EOLComment1",11)==0)
          {
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].EOLCom1,hl[def].lEOLCom1);
           PutInTables(hl[def].lEOLCom1,hl[def].EOLCom1,shl_EOL,hl[def].SymbolT,isCase);
          }
        else
        if (strncasecmp(b,"EOLComment2",11)==0)
          {
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].EOLCom2,hl[def].lEOLCom2);
           PutInTables(hl[def].lEOLCom2,hl[def].EOLCom2,shl_EOL,hl[def].SymbolT,isCase);
          }
        else
        if (strncasecmp(b,"HexMarker",9)==0)
          {
           int i_l;
           pos=MoveAfterEqual(b);
           GetUpTo(4,pos,hl[def].HexStart,hl[def].lHexStart);
           if (!isCase)
              for (i_l=0; i_l<4; i_l++)
                  hl[def].HexStart[i_l]=uctoupper(hl[def].HexStart[i_l]);
          }
        else
        if (strncasecmp(b,"Symbols1",8)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_SYM1);
          }
        else
        if (strncasecmp(b,"Symbols2",8)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_SYM2);
          }
        else
        if (strncasecmp(b,"String1",7)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_STR1);
          }
        else
        if (strncasecmp(b,"String2",7)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_STR2);
          }
        else
        if (strncasecmp(b,"String3",7)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_STR3);
          }
        else
        if (strncasecmp(b,"ShortString",11)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_CHAR);
          }
        else
        if (strncasecmp(b,"Escape",6)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos && *pos!='\r' && *pos!='\n')
              hl[def].Escape=*pos;
          }
        else
        if (strncasecmp(b,"Preprocessor",12)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos && *pos!='\r' && *pos!='\n')
              hl[def].Preprocessor=*pos;
          }
        else
        if (strncasecmp(b,"Preprocessor2",12)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos && *pos!='\r' && *pos!='\n')
              hl[def].Preprocessor2=*pos;
          }
        else
        if (strncasecmp(b,"Case",4)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
             {
              isCase=1;
              hl[def].Flags1|=FG1_CaseSensitive;
             }
          }
        else
        if (strncasecmp(b,"PartialKeywords",15)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
             {
              isCase=1;
              hl[def].Flags1|=FG1_PartialKeyword;
             }
          }
        else
        if (strncasecmp(b,"RelaxNumberCheck",16)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags1|=FG1_RelaxNumbers;
          }
        else
        if (strncasecmp(b,"UseInternal",11)==0)
          {
           int i=0;
           pos=MoveAfterEqual(b);
           i=atoi(pos); // Another bug in egcs 1.1.2, if I declare int i; before pos=.. thinks it can be used unitialized
           if (i<0 || i>3)
              i=0;
           TCEditor::SHLTableUse[i]=def;
           hl[def].UseInternal=i;
          }
        else
        if (strncasecmp(b,"Keywords",8)==0)
          {
           pos=MoveAfterEqual(b);
           end=0;
           while (!end)
             {
              for (s=pos; *s && *s!='\n' && *s!='\r' && *s!=','; s++);
              if (*s!=',')
                 end=1;
              if (s!=pos)
                {
                 *s=0;
                 char *t=newStr(pos);
                 if (!isCase)
                    strlwr(t); // Make sure they are lower case if the language is not
                               // case sensitive, the seachs assumes it
                 hl[def].Keywords->insert(t);
                }
              pos=s+1;
             }
          }
        else
        if (strncasecmp(b,"AllowedInsideNames",18)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_INSNAME);
          }
        else
        if (strncasecmp(b,"CanStartAName",13)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_BEGNAME);
          }
        else
        if (strncasecmp(b,"SpecialSymbolCont",17)==0)
          {//Before SpecialSymbol or we will confuse it ;-)
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_SPC);
          }
        else
        if (strncasecmp(b,"SpecialSymbol",13)==0)
          {
           pos=MoveAfterEqual(b);
           FillTableWith((uchar *)pos,hl[def].SymbolT,shl_SP);
          }
        else
        if (strncasecmp(b,"PMacros",7)==0)
          {
           pos=MoveAfterEqual(b);
           ReplaceCRby0(pos);
           hl[def].PMacros=newStr(pos);
          }
        else
        if (strncasecmp(b,"NoCheckNumbers",14)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags1|=FG1_NoNumbers;
          }
        else
        if (strncasecmp(b,"EOLCInFirstCol1",15)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags1|=FG1_EOLCInFirstCol1;
          }
        else
        if (strncasecmp(b,"EOLCInFirstCol2",15)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags1|=FG1_EOLCInFirstCol2;
          }
        else
        if (strncasecmp(b,"EOLCInFirstCol",14)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags1|=FG1_EOLCInFirstCol1 |  FG1_EOLCInFirstCol2;
          }
        else
        if (strncasecmp(b,"EOLCInFirstUse1",15)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags1|=FG1_EOLCInFirstUse1;
          }
        else
        if (strncasecmp(b,"EOLCInFirstUse2",15)==0)
          {
           pos=MoveAfterEqual(b);
           if (*pos=='1')
              hl[def].Flags2|=FG2_EOLCInFirstUse2;
          }
       }
     while (!feof(f) && strncasecmp(b,"End",3)!=0);
    }
 fclose(f);

 PCREStopCompiler();
 for (def=0; def<defs; def++)
    {
     SETSECreateTables(hl[def].Search,hl[def].Flags1 & FG1_CaseSensitive,hl[def].Keywords);
     list->search(hl[def].Name,ConvTable[def]);
    }
 LoadDefaultOpts(hl,Cant);

 return 0;
}

// That's just to deallocate all the memory allocated and owned by this module
void UnLoadSyntaxHighLightFile(strSHL *&hl, TStringCollection *list, int &Cant)
{
 int i;

 for (i=0; i<Cant; i++)
    {// Collections
     destroy(hl[i].PM);
     destroy(hl[i].Keywords);
     destroy(hl[i].UserWords);
     // Various strings
     delete[] hl[i].Extensions;
     delete[] hl[i].EmacsModes;
     delete[] hl[i].ShellScript;
     delete[] hl[i].PMacros;
     // That's allocated in editorfo.cc by SETSECreateTables
     delete[] hl[i].Search.firstLetters;
     delete[] hl[i].SearchUserWords.firstLetters;
     // PCREs
     free(hl[i].NameMatch);
     free(hl[i].PathMatch);
    }
 delete[] hl;
 destroy(list);
 Cant=0;
 delete[] ConvTable;
 ConvTable=0;
 // PCRE compiler memory
 PCREInitCompiler();
}

#define MaxExtension 80
extern char *strncpyZ(char *dest, char *orig, int size);

static
int TakeExtension(char *file, char *ext)
{
 char *s=file,*e=0,*e2=0;
 int wasExt=0;

 for (; *s; s++)
     if (*s=='/' || *s=='\\')
        wasExt=0;
     else
        if (*s=='.')
          {
           wasExt++;
           e2=e;
           e=s;
          }

 if (!wasExt)
    return 0;

 // If the extension contains gz and we have 2 extensions use the other
 // it works nice with LFNs, SFNs have 1 extension
 if (wasExt>1 && strstr(e,"gz"))
    strncpyZ(ext,e2+1,min(MaxExtension,e-e2));
 else
    strncpyZ(ext,e+1,MaxExtension);
 return 1;
}

static
int TakeCommentLowLev(char *buffer, int l, char *ext, int &tab_width)
{
 int i;
 char buf[MaxExtension];

 if (l<7)
    return 0;
 l-=2;

 char *s=buffer;
 int p1=-1,p2=-1;

 for (i=0; i<l; i++)
    {
     if (s[i]=='-' && s[i+1]=='*' && s[i+2]=='-')
       {
        if (p1==-1)
          {
           p1=i;
           i+=2;
          }
        else
          if (p2==-1)
            {
             p2=i;
             break;
            }
       }
    }
 if (p2==-1)
    return 0;

 for (p1+=3; ucisspace(s[p1]); p1++);
 for (p2--;  ucisspace(s[p2]); p2--);

 l=p2-p1+2;
 if (l<2)
    return 0;

 if (l>MaxExtension)
    l=MaxExtension;

 strncpyZ(ext,&s[p1],l);
 strcpy(buf,ext);
 // Make a little bit of parsing here
 s=strstr(buf,"mode:");
 if (s)
   {
    s+=5;
    for (;*s && ucisspace(*s); s++);
    char *end=s;
    for (;*end && (ucisalnum(*end) || *end=='-' || *end=='-'); end++);
    *end=0;
    strcpy(ext,s);
    *end=';';
   }
 s=strstr(buf,"tab-width:");
 if (s)
   {
    s+=10;
    for (;*s && ucisspace(*s); s++);
    char *end;
    int temp=strtol(s,&end,0);
    if (temp>0 && temp<32) // Tabs>32 looks like an error, no?
       tab_width=temp;
   }
 return 1;
}

const int searchFromStart=1000, searchAtEnd=3000;

static
int TakeCommentEmacs(char *buffer, int lenBuf, char *ext, int &tab_width)
{
 int len,start;

 /* Don't borther with empty buffers */
 if (!buffer || lenBuf<7)
    return 0;

 /* First 1Kb or available */
 start=0;
 len=searchFromStart;
 if (lenBuf<len)
    len=lenBuf;

 if (TakeCommentLowLev(buffer+start,len,ext,tab_width))
    return 1;

 /* If we searched in all the buffer give up */
 if (lenBuf<=searchFromStart)
    return 0;

 /* Last 3Kb or available */
 start=lenBuf-searchAtEnd;
 if (start<searchFromStart)
    start=searchFromStart;

 return TakeCommentLowLev(buffer+start,lenBuf-start,ext,tab_width);
}


static
int TakeLocalVarLowLev(char *buffer, int lenBuf, char *ext)
{
 unsigned lv,mode,end,i;
 char *s=buffer;

 if (!buffer || lenBuf<25)
    return 0;

 // Search these magic words, quit if one isn't there, ensure they are in the right
 // order
 lv=TCEditor_scan(s,lenBuf,"Local Variables:");
 if (lv==sfSearchFailed)
    return 0;

 lenBuf-=lv;
 s+=lv;
 if (lenBuf<5)
    return 0;
 mode=TCEditor_scan(s,lenBuf,"mode:");
 if (mode==sfSearchFailed)
    return 0;

 lenBuf-=mode;
 s+=mode;
 if (lenBuf<5)
    return 0;
 end=TCEditor_scan(s,lenBuf,"End:");
 if (end==sfSearchFailed)
    return 0;

 s=buffer+lv+mode+5;
 for (i=0; i<MaxExtension-1 && s[i]!='\n' && s[i]!='\r'; i++)
     ext[i]=s[i];
 ext[i]=0;
 return 1;
}

static
int TakeCommentLocalVars(char *buffer, int lenBuf, char *ext)
{
 int start;

 /* Last 3Kb or available */
 start=lenBuf-searchAtEnd;
 if (start<0)
    start=0;

 return TakeLocalVarLowLev(buffer+start,lenBuf-start,ext);
}

static
int TakeComment(char *buffer, int lenBuf, char *ext, int &tab_width)
{
 if (TakeCommentEmacs(buffer,lenBuf,ext,tab_width))
    return 1;
 return TakeCommentLocalVars(buffer,lenBuf,ext);
}

static
int TakeScriptPrg(char *buffer, int lenBuf, char *ext)
{
 // I assume it must start with #!
 if (lenBuf<3 || *buffer!='#' || buffer[1]!='!')
    return 0;
 char *end=buffer+lenBuf;
 *ext=0;
 // Kill any space before the path (also /)
 for (buffer+=2; buffer<end && (ucisspace(*buffer) || *buffer=='/'); buffer++);
 do
   {
    int i;
    if (*buffer=='/')
       buffer++;
    for (i=0; buffer<end && *buffer!='/' && *buffer!=' ' && IsntEOL(*buffer); buffer++)
        if (i<MaxExtension-1)
           ext[i++]=*buffer;
    ext[i]=0;
   }
 while (*buffer=='/');
 return *ext;
}

static
int IsInList(char *list, char *val, int noCase=0)
{
 int l=strlen(list)+1;

 char buf[l];
 strcpy(buf,list); // Because strtok fucks the string

 char *tok=strtok(buf,",");

 if (noCase)
   {
    while (tok)
      {
       if (strcasecmp(tok,val)==0)
          return 1;
       tok=strtok(NULL,",");
      }
   }
 else
   {
    while (tok)
      {
       if (strcmp(tok,val)==0)
          return 1;
       tok=strtok(NULL,",");
      }
   }
 return 0;
}

extern const char *GetVariable(const char *variable);
const int extExtension=1, extEmacsModes=2, extShellScript=3;

static
int SearchSHLForIt(TCEditor &e, char *Extension, int &index, int type)
{
 char EnvName[MaxExtension+10];
 const char *v;
 int i;

 if (Extension[0]==0)
    return 0;
 else
   {
    if (TCEditor::SHLGenList)
      {// Search if the user overwritten the roules with an enviro. var.
       strcpy(EnvName,"SET_SHL.");
       strcat(EnvName,Extension);
       if ((v=GetVariable(EnvName))!=NULL)
         {
          ccIndex posAlpha;
          if (TCEditor::SHLGenList->search((void *)v,posAlpha))
            {
             index=SHLConvPosToVal(posAlpha);
             return 1;
            }
         }
      }
    // User defined
    for (i=0; i<e.SHLCant; i++)
       {
        switch (type)
          {
           case extExtension:
                if (e.SHLArray[i].Extensions &&
                    IsInList(e.SHLArray[i].Extensions,Extension))
                  {
                   index=i;
                   return 1;
                  }
                break;
           case extEmacsModes:
                if (e.SHLArray[i].EmacsModes &&
                    IsInList(e.SHLArray[i].EmacsModes,Extension,1))
                  {
                   index=i;
                   return 1;
                  }
                break;
           case extShellScript:
                if (e.SHLArray[i].ShellScript &&
                    IsInList(e.SHLArray[i].ShellScript,Extension))
                  {
                   index=i;
                   return 1;
                  }
                break;
          }
       }
   }
 return 0;
}

const int typeFullName=0,typeName=1;

static
int SearchByName(TCEditor &e, char *fileName, int &index, int type)
{
 char *name=fileName;
 if (type==typeName)
   {
    char *name=strrchr(fileName,'/');
    if (!name)
       name=fileName;
    else
       name++;
   }
 int len=strlen(name),i;
 for (i=0; i<e.SHLCant; i++)
    {
     pcre *regex=type==typeFullName ? e.SHLArray[i].PathMatch : e.SHLArray[i].NameMatch;
     if (regex && PCREDoSearch(name,len,regex))
       {
        index=i;
        return 1;
       }
    }
 return 0;
}

int SHLSelect(TCEditor &e, char *buffer, int lenBuf)
{
 int i,found=0,tab_width=-1;
 char Extension[MaxExtension];
 Extension[0] = 0; // this must be initialized !! (RH)

 // In case the editor isn't locked is better to lock it to avoid multiple
 // redraws, for example because a SHL change and a tab size change
 e.lock();
 int oldTab=e.tabSize;
 e.SHLValueSelected=-1;
 // First try using Emacs style syntax highlight methode
 if (TakeComment(buffer,lenBuf,Extension,tab_width))
    found=SearchSHLForIt(e,Extension,i,extEmacsModes);

 // If not present then check if the file is a UNIX script
 if (!found && TakeScriptPrg(buffer,lenBuf,Extension))
    found=SearchSHLForIt(e,Extension,i,extShellScript);

 // Try RegEx with the full name
 if (!found)
    found=SearchByName(e,e.fileName,i,typeFullName);

 // Try RegEx with the name
 if (!found)
    found=SearchByName(e,e.fileName,i,typeName);

 // If not present then try with the extension
 if (!found)
   {
    TakeExtension(e.fileName,Extension);
    found=SearchSHLForIt(e,Extension,i,extExtension);
   }

 dflOptions *dflOps;
 if (found)
   {
    if (e.SHLArray[i].UseInternal)
       e.SetHighlightTo((shlState)e.SHLArray[i].UseInternal);
    else
       e.SetHighlightTo(shlGenericSyntax,i);
    e.SHLValueSelected=i;
    dflOps=&e.SHLArray[i].df;
   }
 else
   {
    e.SetHighlightTo(shlNoSyntax);
    dflOps=&e.dflOps;
   }
 // Transfer the default options
 uint16 lo=e.CompactFlags();
 lo&=dflOps->resetOpts;
 lo|=dflOps->setOpts;
 e.ExpandFlags(lo);
 if (dflOps->tabSize)
   {
    e.tabSize=dflOps->tabSize;
    e.update(ufView);
   }
 if (dflOps->wrapCol)
    e.WrapCol=dflOps->wrapCol;

 if (tab_width>0 && tab_width!=oldTab)
   {
    e.tabSize=tab_width;
    e.update(ufView);
   }
 e.unlock();

 return found;
}

void SHLTransferDefaultsNewFile(TCEditor &e)
{
 dflOptions *dflOps=&e.dflOps;
 uint16 lo=e.CompactFlags();
 lo&=dflOps->resetOpts;
 lo|=dflOps->setOpts;
 e.ExpandFlags(lo);
 if (dflOps->tabSize)
   {
    e.tabSize=dflOps->tabSize;
    e.update(ufView);
   }
 if (dflOps->wrapCol)
    e.WrapCol=dflOps->wrapCol;
}

char *SHLNameOf(unsigned number)
{
 if (number<(unsigned)TCEditor::SHLCant)
    return TCEditor::SHLArray[number].Name;
 return "";
}


int SHLNumberOf(char *name)
{
 int i;

 for (i=0; i<TCEditor::SHLCant; i++)
    {
     if (strcmp(TCEditor::SHLArray[i].Name,name)==0)
        return i;
    }
 return -1;
}

char *SHLConstructEmacsModeComment(TCEditor &e)
{
 int shl=e.SHLValueSelected;
 if (shl<0 || shl>=e.SHLCant || !e.SHLArray[shl].EmacsModes)
    return 0;

 char *list=e.SHLArray[shl].EmacsModes;
 char *tok=strtok(list,",");
 if (!tok)
    return 0;

 int useEOL=e.SHLArray[shl].lEOLCom1!=0;
 if (!useEOL && (!e.SHLArray[shl].lOpenCom1 || !e.SHLArray[shl].lCloseCom1))
    return 0;

 DynStrCatStruct cat;
 if (useEOL)
    DynStrCatInit(&cat,e.SHLArray[shl].EOLCom1,e.SHLArray[shl].lEOLCom1);
 else
    DynStrCatInit(&cat,e.SHLArray[shl].OpenCom1,e.SHLArray[shl].lOpenCom1);
 DynStrCat(&cat," -""*- mode:");
 DynStrCat(&cat,tok);
 char buf[32];
 int l=sprintf(buf,"; tab-width: %d -""*- ",e.tabSize);
 DynStrCat(&cat,buf,l);
 if (!useEOL)
    DynStrCat(&cat,e.SHLArray[shl].CloseCom1,e.SHLArray[shl].lCloseCom1);
 DynStrCat(&cat,(char *)crlf);

 return cat.str;
}

PMacroStr *SHLSearchPMTrigger(char *trg);

/************************ Regular expressions file matching stuff *******************/
#ifdef SUP_PCRE
static int PCREMaxMatchs=0;
static int *PCREMatchs=0;

/**[txh]********************************************************************

  Description:
  Initialize the matchs array to 0. Called before starting to compile the
expressions.

***************************************************************************/

static
void PCREInitCompiler()
{
 PCREMaxMatchs=0;
 delete[] PCREMatchs;
 PCREMatchs=0;
}

/**[txh]********************************************************************

  Description:
  Allocates memory for the matchs array. Called after compiling all the
expressions and before executing any of them.

***************************************************************************/

static
void PCREStopCompiler()
{
 PCREMatchs=new int[PCREMaxMatchs];
}

/**[txh]********************************************************************

  Description:
  Compiles a RegEx.

  Return: A pointer to the compiled RegEx or 0 if error.

***************************************************************************/

static
pcre *PCRECompileRegEx(char *text)
{
 const char *error;
 int   errorOffset;
 pcre *ret=pcre_compile(text,0,&error,&errorOffset,0);
 if (!ret)
    return 0;

 int matchs=(pcre_info(ret,0,0)+1)*3;
 if (matchs>PCREMaxMatchs)
    PCREMaxMatchs=matchs;

 return ret;
}

static
int PCREDoSearch(char *search, int len, pcre *CompiledPCRE)
{
 int hits=pcre_exec(CompiledPCRE,0,search,len,PCRE206 0,PCREMatchs,PCREMaxMatchs);

 return hits>0;
}
#else
// Dummies
static void PCREInitCompiler() {}
static void PCREStopCompiler() {}
static pcre *PCRECompileRegEx(char *) {}
static int PCREDoSearch(char *, int , pcre *) {}
#endif
/********************** End Regular expressions file matching stuff *****************/

