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

#define Uses_MsgBox
#define Uses_fpstream
#define Uses_TDialog
#define Uses_SOStack
#define Uses_TDialog
#define Uses_TApplication
#define Uses_TDeskTop

#define Uses_TSInputLine
#define Uses_TSLabel
#define Uses_TSLabelCheck
#define Uses_TSButton

// First include creates the dependencies
#include <easydia1.h>
#include <ceditor.h>
// Second request the headers
#include <easydiag.h>

#define Uses_SETAppDialogs
#define Uses_SETAppHelper
#define Uses_SETAppVarious
#define Uses_SETAppConst
#include <setapp.h>

#include <edmsg.h>
#include <ctype.h>
#include <dyncat.h>
#include <rhutils.h>
#include <splinman.h>
#include <intermp3.h>
#include <runprog.h>

const int maxCommand=80;

static char     Command[maxCommand]="\x0";
static unsigned Options=0;

const unsigned UseOSScreen=1;

void ConfigureRunCommand(void)
{
 if (!Command[0])
    strcpy(Command,"make");

 struct
 {
  char   ComAux[maxCommand];
  ushort Options;
 } box;
 strcpy(box.ComAux,Command);
 box.Options=(ushort)Options;

 TSViewCol *col=new TSViewCol(new TDialog(TRect(1,1,1,1),_("Command to run")));

 TSLabel *progInput=new TSLabel(_("~E~nter the program name"),new TSInputLine(80,40));
 TSLabel *options=TSLabelCheck(_("~O~ptions"),_("~U~se OS screen to run the program"),0);

 col->insert(2,1,progInput);
 col->insert(2,yTSUnder,options,0,progInput);
 EasyInsertOKCancel(col);

 TDialog *d=col->doIt();
 delete col;
 d->options|=ofCentered;
 d->helpCtx=cmeConfRunCommand;

 if (execDialog(d,&box)!=cmCancel)
   {
    strcpy(Command,box.ComAux);
    Options=box.Options;
   }
}

static SOStack *StackPath;

static
char *ParseFun(char *buf, FileInfo &fI, char *&fileName)
{
 char *endOfName,*endOfLine=0;
 int offset=0;
 int IsLineNumber=0;

 // Look for file name and line number
 // It fails if: The file is absolute and starts with a number
 if (ucisalpha(buf[0]) && buf[1]==':' && (!ucisdigit(buf[2])))
    offset=2;
 endOfName=strchr(buf+offset,':');
 if (endOfName)
    endOfLine=strchr(endOfName+1,':');
 /* Check if the line number is real */
 if (endOfLine && ucisdigit(endOfName[1]))
   {
    char *s;
    for (s=endOfName+2; *s!=':' && ucisdigit(*s); s++);
    IsLineNumber=*s==':';
   }
 if (!endOfName || !endOfLine || !IsLineNumber)
   {
    char *s=strstr(buf,"ntering dir");
    if (s)
      {
       // The people that makes make if funny:
       s=strchr(s,'`');
       if (s)
         {
          char *e=strrchr(s,'\'');
          if (e)
            {
             char ActualPath[PATH_MAX];
             char v=*e;
             *e=0;
             strcpy(ActualPath,s+1);
             *e=v;
             strcat(ActualPath,"/");
             StackPath->addStr(ActualPath);
            }
         }
      }
    else
      {
       s=strstr(buf,"eaving dir");
       if (s)
          StackPath->DestroyTop();
      }
    fI.Line=-1;
    fileName=0;
    return strdup(buf);
   }

 char *actPath=StackPath->GetStrOf(StackPath->GetTopHandle());

 char *ret;
 if (offset || buf[0]=='/' || buf[0]=='\\')
   { // Absolute path
    ret=strdup(buf);
    *endOfName=0;
    fileName=strdup(buf);
   }
 else
   { // Relative path
    DynStrCatStruct msgLine;
    DynStrCatInit(&msgLine,actPath);
    DynStrCat(&msgLine,buf);
    ret=msgLine.str;
   
    DynStrCatStruct File;
    DynStrCatInit(&File,actPath);
    DynStrCat(&File,buf,endOfName-buf);
    fileName=File.str;
   }

 *endOfLine=0;
 fI.Line=atoi(endOfName+1);
 fI.Column=1;

 return ret;
}

static
char *ErrorFile=0;

static
char *nullStr()
{
 char *s;
 s=new char[1];
 *s=0;
 return s;
}

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

  Description:
  Returns the content of the file where the last call to RunExternalProgram
stored the redirected data. @x{RunExternalProgram}. You must pass the
maximun allowed len and the function will return the real len.

  Return:
  A new allocated string even on error. If error ocurred the new string
have 0 lenght.

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

char *RunExternalProgramGetFile(int &len)
{
 char *s;
 long lMax=len;

 len=0;
 if (!ErrorFile)
    return nullStr();
 FILE *f=fopen(ErrorFile,"rb");
 if (!f)
    s=nullStr();
 else
   {
    long lFile;
    fseek(f,0,SEEK_END);
    lFile=ftell(f);
    fseek(f,0,SEEK_SET);
    if (lFile>lMax)
       lFile=lMax;
   
    s=new char[lFile+1];
    if (fread(s,lFile,1,f)!=1)
      {
       delete s;
       s=nullStr();
      }
    else
       len=(int)lFile;
    s[lFile]=0; // Make it ASCIIZ

    fclose(f);
   }

 unlink(ErrorFile);
 ErrorFile=0;
 return s;
}

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

  Description:
  Deletes the file where the redirection is stored.

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

void RunExternalProgramKillFile(void)
{
 if (!ErrorFile)
    return;
 unlink(ErrorFile);
 ErrorFile=0;
}

const char *Running=__("Running %s");
const char *BackEd=__("Back in the editor");

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

  Description:
  Runs an external program passed as argument. The stderr and stdout are
redirected and the result is showed in the message window. If you don't pass
an argument the program specified by the configuration dialog is used.@*
  Valid flags are:@*
  repDontShowDialog: if the argument is null the dialog that tells about
the configuration is not showed.@*
  repDontShowAsMessage: the redirected information isn't showed in the
message box, instead the file is available calling
RunExternalProgramGetFile.@*
  repRestoreScreen: Restore the screen after running the program.@*

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

void RunExternalProgram(char *Program, unsigned flags)
{
 char *s=Command;
 if (Program)
    s=Program;

 if (!s || *s==0)
   {
    if ((flags & repDontShowDialog)==0)
       messageBox(_("You must configure it first in the Options submenu"),mfError | mfOKButton);
    return;
   }

 SaveAllEditors();
 char *err=open_stderr_out();

 if ((flags & repDontShowAsMessage)==0)
   {
    int l=strlen(s)+strlen(_(Running));
    char b[l];
    sprintf(b,_(Running),s);
    EdShowMessage(b,True);
   }

 StackPath=new SOStack();
 char b[PATH_MAX];
 //getwd(b); Declared as dangerous by glibc 2, reported by ld ?!
 getcwd(b,PATH_MAX);
 strcat(b,"/");
 StackPath->addStr(b);

 if ((Options & UseOSScreen) || (flags & repRestoreScreen))
    FullSuspendScreen();

 MP3Suspend;
 system(s);
 MP3Resume;

 if ((Options & UseOSScreen) || (flags & repRestoreScreen))
    FullResumeScreen();

 close_stderr_out();
 if ((flags & repDontShowAsMessage)==0)
   {
    int goBack=0;
    goBack=DumpFileToMessage(err,"From program:",ParseFun);
    SpLinesUpdate();
    ErrorFile=0;
    EdShowMessage(_(BackEd));
    if (goBack)
       EdJumpToMessage(0);
   }
 else
    ErrorFile=err;
 delete StackPath;

 ReLoadModifEditors();
}

void SaveRunCommand(fpstream &s)
{
 if (Command[0])
   {
    s << (char)2;
    s.writeString(Command);
    s << Options ;
    return;
   }
 s << (char)0;
}

void LoadRunCommand(fpstream &s)
{
 char version;

 s >> version;
 if (version)
   {
    s.readString(Command,maxCommand);
    if (version>=2)
       s >> Options;
   }
}
