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

  Busca Funciones (BuFun), Copyright (c) 1996-2013 by Salvador E. Tropea (SET)

  int SelectFunctionToJump(char *b, unsigned l)

  Is the external entry point, this routine calls to one routine that parses
the C source searching for functions, the methode is heuristic and doesn't
detect all the possible cases, in particular the K & R edition I style of
functions (totally obsolete).
  The routine creates a StringCollection with the functions found and then
creates a dialog box to select one. If the user select a function the
routine returns the line where the function starts if there are no functions
or the user choose cancel the routine returns -1.
  The parameters passed to the routine are:
  char *b: The buffer where the routines will search.
  unsigned l: The length of the buffer.

  Defining the label TEST you can test the module as an standalone program.

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

// That's the first include because is used to configure the editor.
#include "ceditint.h"

#define Uses_stdio
#define Uses_ctype
#define Uses_string
#define Uses_stdlib
#define Uses_itoa

#define Uses_MsgBox
#define Uses_TLabel
#define Uses_TScrollBar
#define Uses_TRect
#define Uses_TButton
#define Uses_TDialog
#define Uses_TEvent
#define Uses_TApplication
#define Uses_TVCodePage

#define Uses_TCEditor_External // For mode constants
#define Uses_TCEditor_Commands // For the cmcJumpToFunction context

#define Uses_TSSOSSortedListBox
#define Uses_TSLabel
#define Uses_TSButton
#define Uses_TSHzGroup
#define Uses_TSStaticText
#include <easydia1.h>
#include <ceditor.h>
#include <easydiag.h>

#define Uses_TGrowDialog
#define Uses_SOStack
#define Uses_TNoCaseSOSStringCollection
#define Uses_TSOSSortedListBox
#include <settvuti.h>

#define Uses_SETAppConst
#include <setapp.h>
#include <edmsg.h>
#include <bufun.h>
#include <splinman.h>

extern ushort execDialog(TDialog *d, void *data);

//#define TEST

inline int IsWordChar(char c)
{
 return c=='_' || TVCodePage::isAlpha(c);
}

char bfBuffer[MaxLenWith0];
char bfNomFun[MaxLenWith0];
char bfTempNomFun[MaxLenWith0];
char bfReturn[MaxLenWith0];
static char Alone;
static int Used,UsedNom,UsedRet,LastRetIndex;
static int Line,LineFun;
static bool LastRetWasChar;
typedef struct
{
 int line;
 ccIndex index;
} stItem;
const int aNothing=0, aWord=1, aChar=2, aPrepro=3;

#ifdef TEST
static FILE *in;
#else
static char *buffer;
static unsigned IndexB;
static unsigned BufLen;
#endif

static int TakeWord(int TakeOneCharToo=0);


#ifdef TEST
static int GetAChar()
{
 char c=getc(in);
 if (c=='\n')
    Line++;
 return c;
}

static void UnGetAChar(int c)
{
 ungetc(c,in);
 if (c=='\n')
    Line--;
}
#else
static int GetAChar()
{
 if (IndexB>=BufLen)
    return EOF;
 int c=(unsigned char)buffer[IndexB++];
 if (c=='\n')
    Line++;
 return c;
}

static void UnGetAChar(int c)
{
 IndexB--;
 if (c=='\n')
    Line--;
}
#endif


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

  Description:
  Puts the next word in Buffer. It skips white spaces, comments,
preprocesor lines, etc. So only words that can be interesting are collected.
If TakeOneCharToo!=0 the routine also collects single chars in Alone global
variable.

  Return:
  aNothing=0 nothing found.
  aWord=1    a word.
  aChar=2    a single character.
  aPrepro=3  a preprocessor line. The buffer contains the #xxx that started
  the line.

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

static
int TakeWord(int TakeOneCharToo)
{
 int c;
 char last;

 do
  {
   c=GetAChar();
   if (c==EOF)
      return aNothing;
   if (IsWordChar(c))
     {
      Used=0;
      do
       {
        bfBuffer[Used++]=c;
        c=GetAChar();
       }
      while (c!=EOF && Used<MaxLen && (TVCodePage::isAlNum(c) || c==':' || c=='_' || c=='~'));
      bfBuffer[Used]=0;
      if (c!=EOF)
         UnGetAChar(c);
      if (Used==0)
         return aNothing;
      return 1;
     }
   else
   if (ucisspace(c))
     {
      do
       {
        c=GetAChar();
       }
      while (c!=EOF && ucisspace(c));
      UnGetAChar(c);
     }
   else
     {
      switch (c)
        {
         case '\"':
              do
               {
                c=GetAChar();
                if (c=='\\')
                  {
                   c=GetAChar();
                   if (c!=EOF) c=0;
                  }
               }
              while (c!=EOF && c!='\"');
              break;

         case '\'':
              do
               {
                c=GetAChar();
                if (c=='\\')
                  {
                   c=GetAChar();
                   if (c!=EOF) c=0;
                  }
               }
              while (c!=EOF && c!='\'');
              break;

         case '/':
              c=GetAChar();
              if (c=='/')
                {
                 do
                  {
                   do
                    {
                     last=c;
                     c=GetAChar();
                    }
                   while (c!=EOF && c!='\n' && c!='\r');
                   if (c=='\r') c=GetAChar();
                  }
                 while (last=='\\');
                }
              else
                if (c=='*')
                  {
                   do
                    {
                     c=GetAChar();
                     if (c=='*')
                       {
                        c=GetAChar();
                        if (c=='/')
                           break;
                        UnGetAChar(c);
                       }
                    }
                   while (c!=EOF);
                  }
              break;

         case '#':
              Used=0;
              do
               {
                bfBuffer[Used++]=c;
                c=GetAChar();
               }
              while (c!=EOF && Used<MaxLen && TVCodePage::isAlpha(c));
              bfBuffer[Used]=0;
              if (c==EOF || Used==0)
                 return aNothing;
              UnGetAChar(c);
              do
               {
                do
                 {
                  last=c;
                  c=GetAChar();
                 }
                while (c!=EOF && c!='\n' && c!='\r');
                if (c=='\r') c=GetAChar();
               }
              while (last=='\\');
              return aPrepro;


         default:
              // Is a single char
              if (TakeOneCharToo)
                {
                 Alone=c;
                 return aChar;
                }
        }
     }
  }
 while (c!=EOF);

 return aNothing;
}

const int maxPreproStates=64;

static int SearchBalance(char ref, char ref2)
{
 int r,bal=1;
 int preproLevel=0;
 static char preproStates[maxPreproStates];

 preproStates[0]=0;
 do
  {
   r=TakeWord(1);
   if (r==2)
     {
      if (preproStates[preproLevel]) continue;
      if (Alone==ref)
         bal--;
      else
         if (Alone==ref2) bal++;
     }
   else if (r==3)
     {
      if (strncmp(bfBuffer,"#if",3)==0)
        {
         preproLevel++;
         if (preproLevel==maxPreproStates) preproLevel--;
         preproStates[preproLevel]=0;
        }
      else if (strncmp(bfBuffer,"#else",5)==0 || strncmp(bfBuffer,"#elif",5)==0)
        {
         preproStates[preproLevel]=1;
        }
      else if (strncmp(bfBuffer,"#endif",6)==0)
        {
         preproLevel--;
         if (preproLevel<0) preproLevel=0;
        }
     }
   if (!bal) break;
  }
 while (r);

 return r;
}


static
int SearchBalanceCopy(char ref, char ref2)
{
 int r,bal=1;

 if (Used<MaxLen)
    bfNomFun[Used++]=ref2;
 UsedNom=Used;
 do
  {
   r=TakeWord(1);
   if (r==2)
     {
      if (Alone==ref)
         bal--;
      else
         if (Alone==ref2) bal++;
      if (UsedNom<MaxLen)
        {
         if (Alone==')' && bfNomFun[UsedNom-1]==' ')
            bfNomFun[UsedNom-1]=Alone;
         else
         if (Alone==',' && bfNomFun[UsedNom-1]==' ')
           {
            bfNomFun[UsedNom-1]=Alone;
            bfNomFun[UsedNom++]=' ';
           }
         else
            bfNomFun[UsedNom++]=Alone;
        }
     }
   else
     if (r==1)
       {
        if (UsedNom+Used<MaxLen_1)
          {
           strcpy(&bfNomFun[UsedNom],bfBuffer);
           UsedNom+=Used;
           bfNomFun[UsedNom++]=' ';
          }
       }
   if (!bal) break;
  }
 while (r);
 bfNomFun[UsedNom++]=0;

 return r;
}

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

  Description:
  Clears the buffer where we collect the return value.
  
***************************************************************************/

static
void ReturnBuffReset()
{
 bfReturn[0]=0;
 UsedRet=0;
 LastRetWasChar=false;
 LastRetIndex=0;
}

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

  Description:
  Adds a word to the buffer where we collect the return value.
  
***************************************************************************/

static
void ReturnBufAdd(const char *buf, int len)
{
 if (UsedRet+len+1<MaxLen)
   {
    LastRetIndex=UsedRet;
    if (UsedRet)
      {
       bfReturn[UsedRet]=' ';
       UsedRet++;
      }
    memcpy(bfReturn+UsedRet,buf,len+1);
    UsedRet+=len;
   }
 LastRetWasChar=false;
}

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

  Description:
  Adds a single char to the buffer where we collect the return value.
  
***************************************************************************/

static
void ReturnBufAdd(char character)
{
 if (UsedRet+2<MaxLen)
   {
    LastRetIndex=UsedRet;
    if (!LastRetWasChar && UsedRet && character!=';')
      {
       bfReturn[UsedRet]=' ';
       UsedRet++;
      }
    bfReturn[UsedRet]=character;
    UsedRet++;
    bfReturn[UsedRet]=0;
   }
 LastRetWasChar=true;
 if (character==';')
    ReturnBuffReset();
}

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

  Description:
  Removes the last addition to the buffer where we collect the return value.
  
***************************************************************************/

static
void ReturnBufUndo()
{
 UsedRet=LastRetIndex;
 bfReturn[UsedRet]=0;
}

static
int SearchCFuncs(char *b, unsigned l, int mode, tAddFunc AddFunc)
{
 int funs=0;
 int opLen=0;
 char opBuf[8];
 int rearrageName=mode & modeBFClassSep;
 mode&=modeBFFunctions; // Only function/prototype setting

 buffer=b;
 IndexB=0;
 BufLen=l;
 ReturnBuffReset();

 Line=1;
 int r;
 do
  {
   r=TakeWord(1);
   if (!r) break;
   if (r==aChar)
     {// Collect the stuff before the function name
      ReturnBufAdd(Alone);
      if (Alone==';') // End of sentence, discard the collected stuff
         ReturnBuffReset();
      continue;
     }
   if (r==aPrepro) continue;
   // Collect the stuff before the function name
   ReturnBufAdd(bfBuffer,Used);
   if (strcmp(bfBuffer,"operator")==0 || (Used>10 && strcmp(bfBuffer+Used-10,"::operator")==0))
     { // C++ operators are a special case.
      // Here we collect the name of the operator, that's symbols until
      // the ( [We can't get fooled by () operator because we always skip
      // the first]
      opLen=1;
      TakeWord(1);
      opBuf[0]=Alone;
      do
        {
         r=TakeWord(1);
         if (r==2 && opLen<4 && Alone!='(')
            opBuf[opLen++]=Alone;
        }
      while (r==2 && Alone!='(');
      opBuf[opLen]=0;
     }
   else
     { // Not an operator, it can be the return type of a function or any
       // other thing. Collect the last word before anything that isn't a
       // word. That's potentially the name of a function
      do
       {
        r=TakeWord(1);
        // Collect the stuff before the function name
        if (r==aWord)
          {
           ReturnBufAdd(bfBuffer,Used);
          }
        else if (r==aChar)
          {
           if (Alone=='(') // Discard the function name
              ReturnBufUndo();
           else
              ReturnBufAdd(Alone);
          }
       }
      while (r==aWord);
      if (!r) break;
     }
   // Ok, we should have the name in Buffer and Alone should be (
   if (Alone=='(')
     {
      strcpy(bfNomFun,bfBuffer);
      if (opLen)
        { // operators
         strcat(bfNomFun," ");
         strcat(bfNomFun,opBuf);
         strcat(bfNomFun," ");
         Used+=opLen+2;
         opLen=0;
        }
      LineFun=Line;
      // Collect all the parameters
      r=SearchBalanceCopy(')','(');
      if (!r) break;
      // Now we should find: (a) a { if that's a function or (b) a ; if that's
      // a prototype.
      do
        {
         r=TakeWord(1);
        }
      while (r==3 && bfBuffer[0]=='#');
      if (!r) break;

      int SearchOpen=0,SkipCatch=0;
      int Eureka=0,isProto=0;

      // Another special case for C++ const member atributes
      // For: proto const ...
      if (r==1 && strcmp(bfBuffer,"const")==0)
         r=TakeWord(1);
      // List of exceptions that can be thrown
      // For: proto throw(...) ...
      if (r==1 && strcmp(bfBuffer,"throw")==0)
        {
         r=TakeWord(1);
         if (r==2 && Alone=='(') // throw(
           {
            r=SearchBalance(')','(');
            if (!r) break;
            // throw(...)
            r=TakeWord(1);
           }
        }

      if (mode==modeBFPrototypes && r==2 && Alone==';')
        {// Looking for a prototype
         Eureka=isProto=1;
        }

      if (!isProto)
        {// Looking for a function
         if (r==2)
           {
            if (Alone=='{')
               Eureka=1;
            else
              if (Alone==':') // Special case for C++ constructors with
                              // initialization lists.
                 SearchOpen=Eureka=1;
           }
         else
           {
            // A bizarre case: try/catch not inside the body
            if (strcmp(bfBuffer,"try")==0)
               SkipCatch=SearchOpen=Eureka=1;
           }
        }
      if (Eureka)
        {
         // Delay the insertion until we know the line of ending
         int used=UsedNom;
         if (mode!=modeBFPrototypes || isProto)
            strcpy(bfTempNomFun,bfNomFun);
            
         if (!isProto)
           {// If the { was delayed look now for it
            if (SearchOpen)
              {
               do
                {
                 r=TakeWord(1);
                 if (r==2 && Alone=='{') break;
                }
               while (r);
               if (!r) break;
              }
            // Skip the body of the function
            r=SearchBalance('}','{');
            if (!r) break;
           }
         // We found a `try', we should find a `catch'.
         // If found skip it adding to the function body these lines.
         if (SkipCatch)
           {
            unsigned pIndexB=IndexB;
            int pLine=Line;
            do
              {
               r=TakeWord(0);
              }
            while (r==3);
            if (r==1 && (strcmp(bfBuffer,"catch")==0))
              {
               r=TakeWord(1);
               if (r==2 && Alone=='(')
                 {
                  if (SearchBalance(')','('))
                    {
                     r=TakeWord(1);
                     if (r==2 && Alone=='{')
                        SearchBalance('}','{');
                    }
                 }
              }
            else
              {
               IndexB=pIndexB;
               Line=pLine;
              }
           }

         // Now insert it
         if (mode!=modeBFPrototypes || isProto)
           {
            //stkHandler s=StrDup(bfTempNomFun,LineFun,Line,used,stk,rearrageName);
            //FunList->insert(s);
            char *member=NULL, *aux;

            // Look for the member name. But only in the function name.
            aux=strchr(bfTempNomFun,'(');
            if (aux)
              {
               *aux=0;
               // Look for the *last* :: (namespace::class::member)
               // Pointed out by Andris.
               member=strrchr(bfTempNomFun,':');
               if (member && member!=bfTempNomFun && member[-1]==':')
                  member--;
               else
                  member=NULL;
               *aux='(';
              }
           
            if (!rearrageName || member==NULL)
              {
               strcpy(bfNomFun,bfTempNomFun);
              }
            else
              { // Change class::member to member (class)
               used++;
               char *source=member+2,*dest=bfNomFun;
               for (; *source; source++,dest++) *dest=*source;
               *(dest++)=' ';
               *(dest++)='(';
               char *s=bfTempNomFun;
               for (;s<member; s++,dest++) *dest=*s;
               *(dest++)=')';
               *dest=0;
              }
            AddFunc(bfNomFun,used,LineFun,Line,bfReturn,UsedRet+1);
            //printf("Function: %s, Line %d, Line end %d Retorna: %s\n",bfTempNomFun,LineFun,Line,bfReturn);
            ReturnBuffReset();
            funs++;
           }
         #ifdef TEST
         printf("Function: %s, Line %d, Line end %d\n",bfTempNomFun,LineFun,Line);
         #endif
        }
     }
  }
 while(r);

 return funs;
}


static TNoCaseSOSStringCollection *glFunList;
static SOStack *glStk;

// A copy of this function is used in txhgen.cc, any update here should be
// reflected there until I find a better solution.
inline
void AlignLen(int &len)
{
 if (len & 3)
    len+=4-(len & 3);
}

static
void StrDup(char *s, int len, int line, int lineEnd, char *ret, int lRet)
{
 stkHandler h;
 char *d,b[64];
 ccIndex ind;
 int differentiate=0, lenRet, lenFun;

 lenFun=len;
 if (glFunList->Search(s,ind))
   {// Already there
    itoa(line,b,10);
    len+=strlen(b)+1;
    differentiate=1;
   }

 AlignLen(len);
 // We will make a copy including the return value, just to avoid breaking
 // the rest.
 lenRet=lRet;
 if (ret==NULL)
    lenRet=0;
 else
   {
    lenRet+=lenFun-1;
    if (TVCodePage::isAlNum(ret[lRet-1]))
       lenRet++;
    AlignLen(lenRet);
   }
 h=glStk->alloc(len+sizeof(int)*3+lenRet);
 d=glStk->GetStrOf(h);
 strcpy(d,s);
 if (differentiate)
   {
    strcat(d," ");
    strcat(d,b);
   }
 int *l=(int *)&d[len];
 l[0]=line;
 l[1]=lineEnd;
 l[2]=lenRet;
 if (lenRet)
   {
    char *dest=d+len+sizeof(int)*3;
    //char *proto=dest;
    memcpy(dest,ret,lRet);
    dest+=lRet-2;
    if (TVCodePage::isAlNum(*dest))
      {
       dest[1]=' ';
       dest++;
      }
    dest++;
    memcpy(dest,s,lenFun);
    //printf("Added prototype: %s\n",proto);
   }
 glFunList->insert(h);
}

static stFuncsSHL FuncsAvail[]=
{
 {"Clipper 5.x",SearchClipperFuncs},
 {"Perl",SearchPerlFuncs},
 {"Syntax Highlight File",SearchSHLDefs},
 {"Texinfo source",SearchTxiSecs},
 {"80x86 asm (AT&T syntax)",SearchAsmLabels},
 {"80x86 asm (Intel syntax)",SearchAsmLabels},
 {"Netwide Assembler",SearchAsmLabels},
 {"8x51 asm",SearchAsmLabels},
 {"PICs asm",SearchAsmLabels},
 {"PDP11 asm",SearchAsmLabels},
 {"PHP",SearchPHPFuncs},
 {"Python",SearchPythonSymbols},
 {"VHDL",SearchVHDLStuff},
 {"PMacros file",SearchPMDefs},
 {"KICAD Library",SearchKICADLib},
 {"Makefile",SearchMakeLabels},
 {"HTML",SearchHTMLAnchors},
 {"DocBook (SGML)",SearchSGMLIDs},
 {"USB Descriptors",SearchUSB},
 {0,SearchCFuncs}
};

static
int SearchFuncs(char *b, unsigned l, TNoCaseSOSStringCollection *FunList,
                SOStack *stk, int mode, const char *shl)
{
 if (!shl) return 0;
 glFunList=FunList;
 glStk=stk;
 int i=0;
 //FunList->duplicates=True;
 while (FuncsAvail[i].shl)
   {
    if (!strcmp(FuncsAvail[i].shl,shl))
       return FuncsAvail[i].func(b,l,mode,StrDup);
    i++;
   }
 return FuncsAvail[i].func(b,l,mode,StrDup);
}

#ifndef TEST

class TLFuns : public TGrowDialog
{
public:
 TLFuns( TRect r, const char *name, int extraOptions=0 ) :
      TWindowInit( &TLFuns::initFrame ),
      TGrowDialog(r,name,extraOptions) {};
 void handleEvent(TEvent& event);
};

void TLFuns::handleEvent(TEvent& event)
{
 TDialog::handleEvent(event);
 if ( event.what == evCommand || event.what == evBroadcast)
   {
    switch ( event.message.command )
      {
       // a button
       case cmListItemSelected:
            endModal(event.message.command);
            clearEvent(event);
            break;
       case cmeZoom:
            event.message.command=cmZoom;
            TDialog::handleEvent(event);
            break;
       default:
           break;
      }
   }
}

static TDialog *createDialog( )
{
 TLFuns *d=new TLFuns(TRect(1,1,1,1),__("Jump to function"));
 TSViewCol *col=new TSViewCol(d);

 TRect r=TApplication::deskTop->getExtent();
 int h=r.b.y-r.a.y-10;
 int w=r.b.x-r.a.x-15;

 TSSOSSortedListBox *ListaH=new TSSOSSortedListBox(w,h,tsslbVertical|tsslbHorizontal,1,256);
 ((TListViewer *)ListaH->view)->handleSpace=False;
 ListaH->view->growMode=gfMoveBottomCorner;
 TSLabel *lista=new TSLabel(__("List of functions"),ListaH);

 col->insert(xTSLeft,yTSUp,lista);

 TSButton *ok=new TSButton(__("O~K~"),cmOK,bfDefault);
 TSButton *cancel=new TSButton(__("Cancel"),cmCancel);
 TSButton *browse=new TSButton(__("~B~rowse"),cmYes);
 ok->view->growMode=cancel->view->growMode=browse->view->growMode=gfGrowAll;
 TSHzGroup *but123=MakeHzGroup(ok,cancel,browse,NULL);
 col->insert(xTSCenter,yTSDown,but123);
 col->doItCenter(cmcJumpToFunction);
 delete col;
 return d;
}

static
char *GetNameLine(TNoCaseSOSStringCollection *FunList, int i, int *line)
{
 char *s=FunList->atStr(i);
 int length=strlen(s)+1;
 AlignLen(length);
 *line=*(int *)(s+length);
 return s;
}

static
int cmpStItem(const void *e1, const void *e2)
{
 stItem *i1=(stItem *)e1,*i2=(stItem *)e2;
 return i1->line-i2->line;
}

static
int AskSortList()
{
 TSViewCol *col=new TSViewCol(__("Sort mode"));

 col->insert(xTSCenter,yTSUp,new TSStaticText(__("Sort the functions")));
 col->insert(xTSCenter,yTSDown,
             new TSHzGroup(new TSButton(__("~A~lphabetically"),cmYes,bfDefault),
                           new TSButton(__("by ~L~ine"),cmNo)));
 TDialog *d=col->doItCenter(cmcJumpToFunction);
 delete col;
 return execDialog(d,0);
}

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

  Description:
  Searchs functions and prototypes and ask for jumping.

  Include: ced_exte
  Return: Line to jump or -1

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

int SelectFunctionToJump(char *b, unsigned l, char *word, int mode,
                         char *fileName, const char *shl)
{
 SOStack stk;
 TNoCaseSOSStringCollection *FunList = new TNoCaseSOSStringCollection(20,5,&stk);

 int funcs=SearchFuncs(b,l,FunList,&stk,mode|modeBFClassSep,shl);

 struct TListBoxRec
 {
  TCollection *items;
  ccIndex selection;
 } br;

 br.items=FunList;
 br.selection=0;

 if (!funcs)
   {
    messageBox(__("Hmmm ... I can't find any function, are you sure?"),mfError | mfOKButton);
    delete FunList;
    return -1;
   }
 else
   {
    if (word)
      {
       FunList->Search(word,br.selection);
       if (br.selection>=FunList->getCount())
          br.selection=FunList->getCount()-1;
      }
    int ret=execDialog(createDialog(),&br);
    if (ret==cmCancel)
      {
       delete FunList;
       return -1;
      }
    // The Browse button have cmYes as command, just for commodity
    if (ret==cmYes)
      {
       ret=AskSortList();
       if (ret==cmCancel)
         {
          delete FunList;
          return -1;
         }
       TProgram::deskTop->lock();
       char *aux=TVIntl::getTextNew(__("Functions:"));
       EdShowMessage(aux,True);
       DeleteArray(aux);
       int i,c=FunList->getCount();
       FileInfo fInfo;
       fInfo.Column=0; fInfo.offset=fInfo.len=-1;
       if (ret==cmYes)
         {
          br.selection=0;
          // Add to the message window
          for (i=0; i<c; i++)
             {
              char *s=GetNameLine(FunList,i,&fInfo.Line);
              EdShowMessageFile(s,fInfo,fileName);
             }
         }
       else
         {
          // Sort by line number
          stItem *s=new stItem[c];
          for (i=0; i<c; i++)
             {
              GetNameLine(FunList,i,&s[i].line);
              s[i].index=i;
             }
          qsort(s,c,sizeof(stItem),cmpStItem);
          br.selection=s[0].index;
          // Add to the message window
          for (i=0; i<c; i++)
             {
              char *name=GetNameLine(FunList,s[i].index,&fInfo.Line);
              EdShowMessageFile(name,fInfo,fileName);
             }
          DeleteArray(s);
         }
       SpLinesUpdate();
       EdJumpToMessage(0);
       TProgram::deskTop->unlock();
      }
   }

 int line;
 GetNameLine(FunList,br.selection,&line);
 delete FunList;

 return line;
}

int CreateFunctionList(char *b, unsigned l, SOStack &stk,
                       TNoCaseSOSStringCollection *FunList, unsigned ops,
                       const char *shl)
{
 int funcs=SearchFuncs(b,l,FunList,&stk,modeBFFunctions | ops,shl);
 return funcs==0;
}

static SOStack *stkBufun=0;
static TNoCaseSOSStringCollection *lstBufun=0;
static const char *FileName=0;
static unsigned FileID=0, numFuncs;

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

  Description:
  Destroys a list of functions created with CreateFunctionList. You should
call it if you are sure the user won't need the list again.
@x{CreateFunctionList}.

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

void DestroyFunctionList()
{
 CLY_destroy(lstBufun);
 delete stkBufun;
 DeleteArray(FileName);
 lstBufun=0;
 stkBufun=0;
 FileName=0;
 FileID=0;
}

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

  Description:
  Creates a function list for this file. The information is hold internally
and the file name is memorized. That's done so if you call it twice for the
same file it won't create the list twice. In addition a number that changes
each time the file is modified should be suplied. In this way the list will
be computed if the file was changed. You must pass the pointer to the buffer
containing the code and the length.@p
  Is safe to call this function even when another list is cached because it
won't leak memory, the old list will be destroyed.

  Return:
  The number of functions found.

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

int CreateFunctionList(char *b, unsigned l, const char *fileName, unsigned ID,
                       const char *shl)
{// Check if that's the one we have cached
 if (stkBufun && FileName && strcmp(FileName,fileName)==0 && FileID==ID)
    return numFuncs;
 DestroyFunctionList();
 stkBufun=new SOStack;
 lstBufun=new TNoCaseSOSStringCollection(20,5,stkBufun);
 FileName=newStr(fileName);
 FileID=ID;
 numFuncs=CreateFunctionList(b,l,*stkBufun,lstBufun,modeBFClassSep,shl);
 return numFuncs;
}

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

  Description:
  Searchs the name of the function that contains the provided line number.
It uses the list created by CreateFunctionList, so you must call it before.
@x{CreateFunctionList}.

  Return:
  !=0 => start, end and name were filled with the starting line of the
function, end line and the name of the function. The pointer to the name
of the function shouldn't be altered or released.

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

int SearchFunctionByLine(int line, int &start, int &end, char *&name)
{
 if (!lstBufun) return 0;
 int i,c=lstBufun->getCount(),len,lineS,lineE,*l;

 // ToDo: a binary search, they are sorted!
 for (i=0; i<c; i++)
    {
     char *s=lstBufun->atStr(i);
     len=strlen(s)+1;
     AlignLen(len);
     l=(int *)(s+len);
     lineS=l[0];
     lineE=l[1];
     if (lineE==-1)
       {
        if (i+1<c)
          {
           char *s=lstBufun->atStr(i+1);
           len=strlen(s)+1;
           AlignLen(len);
           l=(int *)(s+len);
           lineE=l[0]-1;
          }
        else
           lineE=INT_MAX;
       }
     if (line>=lineS && line<lineE)
       {
        start=lineS; end=lineE; name=s;
        return 1;
       }
    }
 return 0;
}

#else
static void writeStr(void *p, void *)
{
 char *s=(char *)p;
 int l=strlen(s)+1;
 AlignLen(l);
 printf("%s %d\n",s,*(int *)(s+l));
}

int main(int argc, char *argv[])
{
 if (argc!=2)
    return 1;
 in=fopen(argv[1],"rb");

 TStringCollection *FunList = new TStringCollection(20,5);

 int funcs=SearchFuncs(FunList);

 printf("\n\nLista ordenada de las funciones: (%d)\n\n",funs);
 FunList->forEach(writeStr,NULL);
 return 0;
}
#endif
