/***************************************************************************
 *                                                                         *
 *   EDTOFF.C                                                              *
 *                                                                         *
 *   Copyright (c) 1993-1997 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 "gcomm.h"
#include "edtoff.h"
#include "protstuf.h"
#ifndef UNIX
#include "process.h"
#include "errno.h"
#endif

#define FILREV "$Revision: 7 $"

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 EXPORT edterr[80];            /* editor error message                 */

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

#define MAXARG 10                  /* Argument list for spawnv()           */
#ifndef UNIX
static CHAR *args[MAXARG+1];       /* for use by exeedt()                  */
static INT numargs;                /* for use by exeedt()                  */
#endif
static CHAR *textfn;     /* set by edtoff(), used by expbuf() and impbuf() */

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 */
{
#ifndef UNIX
     regedtoff(exeedt);
#endif // UNIX
     regedtoff(dosedt);
#ifdef GCDOS
     regedtoff(dlload);
#endif // GCDOS
}

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);
}

#ifndef UNIX
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],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);
}
#endif

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);
     if (system(command) != 0) {
          sprintf(edterr,"failed");
     }
     else if (txtbuf == NULL) {
          rc=EOSAVE;
     }
     else if ((rc=impbuf(txtbuf,sizbuf)) == EOERROR) {
          sprintf(edterr,"erased the %s file",textfn);
     }
     return(rc);
}

#ifdef GCDOS
INT
dlload(                            /* loader for DLL-requiring command stgs*/
CHAR *command,                     /* editor command line (per .MDF file)  */
CHAR *txtbuf,                      /* in-place editing buffer              */
UINT sizbuf)                    /* size of buffer in bytes (includes '\0') */
{           /* will always return EONOTME, but will load DLLs as necessary */
     HMODULE handle;
     CHAR *modnam;

     (VOID)txtbuf;
     (VOID)sizbuf;
     modnam=firstwd(command);
     if (samend(modnam,".DLL") || samend(modnam,".386")) {
          if (samend(modnam,".DLL")) {
               modnam[strlen(modnam)-4]='\0';
          }
          if (DosGetModHandle(modnam,&handle) != 0) {
               loaddll(modnam);
               initdlls();
          }
     }
     return(EONOTME);
}
#endif // GCDOS

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;

     if ((tfp=fopen(textfn,FOPRA)) == 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);
     return(c != EOF ? EOTRUNC : (diff ? EOSAVE : EONOCHG));
}
