/***************************************************************************
 *                                                                         *
 *   EDTOFF.C                                                              *
 *                                                                         *
 *   Copyright (c) 1993-1996 Galacticomm, Inc.    All rights reserved.     *
 *                                                                         *
 *   Offline editor registration and centralized handling routines, as     *
 *   used by CNF and Menu Tree.                                            *
 *                                                                         *
 *                                               - R. Stein  1/21/93       *
 *                                                                         *
 ***************************************************************************/

#include "stdafx.h"
#include "gcommlib.h"
#include "edtoff.h"
#include "fileutil.h"
#include "CnfAsrt.h"

#define FILREV "$Revision: $"

INT nedts=0;                       // number of editor handlers
LONG ibsize;                       // bytes read in by impbuf()
                                   // set whenever anyone returns EOTRUNC
INT edtskp=0;                      // 1=allow PgUp/PgDn to end edit session
INT edtkey;               // if edtskp and the return value is >= EONOCHG,
                          // then this can be PGUP or PGDN, as appropriate
CHAR edterr[80];            // editor error message

EDTHANDLER **edthandlers=NULL; // array of ptrs to editor handler routines

static BOOL extActive = FALSE;
static HANDLE procHand = INVALID_HANDLE_VALUE;
static HANDLE threadHand = INVALID_HANDLE_VALUE;

#define MAXARG 10                  // Argument list for spawnv()
static CHAR *args[MAXARG+1];       // for use by exeedt()
static INT numargs;                // for use by exeedt()
//static CHAR *textfn;     // set by edtoff(), used by expbuf() and impbuf()
static CString textfn;
static UINT bufSize = 0;
static CHAR *textBuf = NULL;
static const CHAR *tryext[ 3 ] = {"ibm","asc","ans"};

INT exeedt(CHAR *command,CHAR *txtbuf,UINT sizbuf);
INT dosedt(CHAR *command,CHAR *txtbuf,UINT sizbuf);
INT dlload(CHAR *command,CHAR *txtbuf,UINT sizbuf);
INT expbuf(CHAR *txtbuf);
INT impbuf(CHAR *txtbuf,UINT sizbuf);

VOID
regedtoff(                         // register an offline editor
EDTHANDLER *edthdl)    // ptr to editor handler (see EOXXXXX return codes)
{
     edthandlers=(EDTHANDLER **)alcrsz(edthandlers,
                                       sizeof(EDTHANDLER *)*nedts,
                                       sizeof(EDTHANDLER *)*(nedts+1));
     edthandlers[nedts++]=edthdl;
}

VOID
eospawn(VOID)  // register DLL, spawn(.exe), and system() editors handlers
 // be sure to call eospawn() after all other editors have been registered
{
     regedtoff(dosedt);
}

INT
edtoff(                            // edit this stuff
CHAR *command,                     // editor command line (per .MDF file)
CHAR *txtbuf,            // in-place editing buffer (NULL=already on disk)
UINT sizbuf,                    // size of buffer in bytes (includes '\0')
CHAR *tfn)                         // name to use for file, if any
{             // see EOXXXXX return codes (EONOTME means no handler found)
     INT i,rc=EONOTME;

     textfn=tfn;
     edtkey=0;
     strcpy(edterr,"failed");
     for (i=0 ; i < nedts ; i++) {
          if ((rc=(*edthandlers[i])(command,txtbuf,sizbuf)) != EONOTME) {
               break;
          }
     }
     return(rc);
}

INT
exeedt(                            // spawn() to .EXE file editor
CHAR *command,                     // editor command line (per .MDF file)
CHAR *txtbuf,            // in-place editing buffer (NULL=already on disk)
UINT sizbuf)                    // size of buffer in bytes (includes '\0')
{
     CHAR *cp;
     INT rc;

     if (!samend(firstwd(command),".EXE")) {
          return(EONOTME);
     }
     if (!expbuf(txtbuf)) {
          sprintf(edterr,"cannot create \"%s\" for editing",textfn);
          return(EOERROR);
     }
     args[0]=strtok(command," ");
     for (numargs=1 ; numargs < MAXARG ; numargs++) {
          if ((args[numargs]=strtok(NULL," ")) == NULL) {
               break;
          }
     }
     args[numargs]=NULL;
     setwin(NULL,0,0,79,24,1);
     locate(0,24);
     cursiz(GVIDLILCURS);
     if ((rc=_spawnvp(P_WAIT,args[0],(const char *const *)args)) != 0) {
          if (rc == -1) {
               switch (errno) {
               case E2BIG:
                    cp="has too many arguments";
                    break;
               case EINVAL:
                    cp="has a bad argument (or requires more memory)";
                    break;
               case ENOENT:
                    cp="specifies an .EXE file that doesn't exist";
                    break;
               case ENOEXEC:
                    cp="specifies a bad .EXE file";
                    break;
               case ENOMEM:
                    cp="requires more real-mode memory";
                    break;
               default:
                    cp="cannot be executed";
               }
          }
          else {
               cp=spr("got an error level %d",rc);
          }
          strcpy(edterr,cp);
          rc=EOERROR;
     }
     else if (txtbuf == NULL) {
          rc=EOSAVE;
     }
     else if ((rc=impbuf(txtbuf,sizbuf)) == EOERROR) {
          sprintf(edterr,"erased the %s file",textfn);
     }
     return(rc);
}

INT
dosedt(                            // system() to .EXE or .BAT file editor
CHAR *command,                     // editor command line (per .MDF file)
CHAR *txtbuf,                      // in-place editing buffer
UINT sizbuf)                    // size of buffer in bytes (includes '\0')
{                       // must be very last registered editor before dlls
     INT rc=EOERROR;

     if (samend(firstwd(command),".DLL") || samend(firstwd(command),".386")) {
          return(EONOTME);
     }
     if (!expbuf(txtbuf)) {
          sprintf(edterr,"cannot create \"%s\" for editing",textfn);
          return(rc);
     }
//     setwin(NULL,0,0,79,24,1);
//     locate(0,24);
//     cursiz(GVIDLILCURS);

     CString   cmdPath;
     CString   cmdExt;
     CString   cmdRoot;

     GBOOL     useSystem=TRUE;

     if (which(command, cmdPath, cmdRoot, cmdExt))
     {
          cmdExt.MakeLower();

          if (cmdExt==".exe")
          {
               useSystem=FALSE;
          }
     }

     GBOOL     success=FALSE;

     if (useSystem) {
          success=(system(command) == 0);
     }
     else {
          PROCESS_INFORMATION processInfo;
          STARTUPINFO startInfo;

          GetStartupInfo( &startInfo );

          success=CreateProcess( NULL, command,
                                 NULL, NULL,
                                 FALSE, NORMAL_PRIORITY_CLASS,
                                 NULL,
                                 NULL,
                                 &startInfo, &processInfo
                               );

          if (success) 
          {
               procHand = processInfo.hProcess;
               threadHand = processInfo.hThread;
               extActive = TRUE;
               bufSize = sizbuf;
               textBuf = txtbuf;

               return( EOLATER );
          }
     }

     if (! success) {
          if (useSystem)
          {
               strcpy(edterr,"failed");
          }
          else
          {
               FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,
                              NULL, GetLastError(),
                              MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
                              edterr, sizeof( edterr ), NULL
                            );
          }
     }
     else if (txtbuf == NULL) {
          rc=EOSAVE;
     }
     else if ((rc=impbuf(txtbuf,sizbuf)) == EOERROR) {
          sprintf(edterr,"erased the %s file",textfn);
     }
     return(rc);
}

INT
expbuf(                            // export data into a text file
CHAR *txtbuf)                    // data (or NULL=should already be there)
{                              // returns 1=exported 0=error creating file
     FILE *tfp;

     if (txtbuf == NULL) {
          return(1);
     }
     if ((tfp=fopen(textfn,FOPWA)) == NULL) {
          return(0);
     }
     fwrite(txtbuf,1,strlen(txtbuf),tfp);
     fclose(tfp);
     return(1);
}

INT
impbuf(                            // import text from the buffer
CHAR *txtbuf,                     // buffer with old contents still intact
UINT sizbuf)                       // bytes in buffer, including NUL
{                // returns EOERROR=no file or EOTRUNC, EOSAVE, or EONOCHG
     FILE *tfp;
     CHAR *cp;
     INT diff,i,c;

     USHORT    fd = 0;
     USHORT    ft = 0;

     getFileTm( textfn, &ft, &fd );

     ULONG     bestTime = ((ULONG)fd << 16) | (ULONG)ft;

     CString   tryFn( textfn );

     CString   root = tryFn;

     int       dot = root.Find( '.' );

     if (dot >= 0)
     {
          root = root.Left( dot );                    
     }

     for ( int count = 0; count < 3; ++count )
     {
          CString   tempFn( root );
          tempFn += '.';
          tempFn += tryext[ count ];

          USHORT    fd = 0;
          USHORT    ft = 0;

          getFileTm( tempFn, &ft, &fd );

          ULONG     thisTime = ((ULONG)fd << 16) | (ULONG)ft;

          if (thisTime > bestTime)
          {
               tryFn = tempFn;
          }
     }

     tfp = fopen( tryFn, FOPRA );

     if (tfp == NULL)
     {
          ibsize=0L;
          return(EOERROR);
     }

     ibsize=filelength(fileno(tfp));
     cp=txtbuf;
     diff=0;
     for (i=0 ; (c=getc(tfp)) != EOF && i < sizbuf-1 ; i++) {
          if (c != '\0') {
               if (!diff && *cp != c) {
                    diff=1;
               }
               *cp++=c;
          }
     }
     if (!diff && *cp != '\0') {
          diff=1;
     }
     *cp='\0';
     fclose(tfp);
     unlink(textfn);
     unlink(tryFn);
     return(c != EOF ? EOTRUNC : (diff ? EOSAVE : EONOCHG));
}


BOOL Finish( int &rc )
{
     DWORD     exCode;

     CnfAssert( ExternalEditorActive() );

     if (GetExitCodeProcess( procHand, &exCode ))
     {
          if (exCode == STILL_ACTIVE)
          {
               return( FALSE );
          }
     }
     else
     {
          return( FALSE );
     }

     CloseHandle( threadHand );
     CloseHandle( procHand );

     threadHand = procHand = INVALID_HANDLE_VALUE;
     extActive = FALSE;

     if (txtbuf == NULL) {
          rc=EOSAVE;
     }
     else if ((rc=impbuf(textBuf,bufSize)) == EOERROR) {
          sprintf(edterr,"erased the %s file",textfn);
     }

     return( TRUE );
}


BOOL ExternalEditorActive()
{
     return( extActive );
}
