/**********************************************************
 *  ENVEDT.C - Editor for Master Environment Variables    *
 *  Jim Kyle, July 8, 1990                                *
 *                                                        *
 *      qcl envedt.c eea.obj envpkg.obj nxtevar.obj       *
 *   or cl envedt.c -MAmx eea.asm envpkg.asm nxtevar.asm  *
 *   or tcc envedt.c eea.asm envpkg.asm nxtevar.asm       *
 *                                                        *
 *   NOTE: THIS FILE CONTAINS CODE THAT WAS ACCIDENTALLY  *
 *   LEFT OUT OF THE VERSION PRINTED IN UNDOCUMENTED DOS  *
 **********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>

#ifndef FP_SEG
#define FP_SEG(f) (*((unsigned *)&(f) + 1))
#endif

#ifndef FP_OFF
#define FP_OFF(f) (*((unsigned *)&(f)))
#endif

#ifndef MK_FP
#define MK_FP(s,o) ((void far *)\
    (((unsigned long)(s) << 16) | (unsigned)(o)))
#endif

extern char far * nxtevar( char far * vptr );
extern char far * mstenvp( void );
extern void max_xy( int *x, int *y );
extern int col( void );
extern int row( void );
extern void setrc( int r, int c );
extern int envsiz( char far * envptr);

char far * menv;                /* pointer to current var */
char far * rest;                /* pointer to next var    */
char far * lstbyt;              /* adr of last byte used  */
char vname[512], *txtptr;       /* working buffer and ptr */
int  nmlen,  insmode = 0,       /* name len, insert flag  */
     max_x,  max_y,             /* screen limits          */
     i,      c,                 /* scratchpads            */
     begrow, begcol,            /* cursor, start of text  */
     currow, curcol,            /*         current loc    */
     endrow,                    /*         end of text    */
     editing,                   /* loop control flag      */
     i_cur, i_max,              /* cur, max i in txtptr   */
     free_env;                  /* bytes free in env      */

void findvar( char * varnam )   /* find var, set txtptr   */
{ nmlen = strlen( varnam );
  txtptr = NULL;                /* present not-found flag */
  while ( *menv )
    { rest = nxtevar( menv );   /* "rest" always next one */
      sprintf( vname, "%Fs", menv );
      if( vname[nmlen] == '=')  /* possible match found   */
        { vname[nmlen] = '\0';
          if (stricmp( vname, varnam ) == 0)
            { txtptr = &vname[nmlen+1];
              vname[nmlen] = '=';
              return;           /* found it, get out now  */
            }
        }
      menv = rest;              /* try again with next    */
    }
}

void calccrsr( void )           /* calc currow, curcol    */
{ begrow = endrow - (i_max / max_x );
  if (( i_max % max_x ) == 0 )
    begrow++;
  begcol = 0;
  currow = begrow + (i_cur / max_x );
  curcol = begcol + (i_cur % max_x );
}

void show_var( void )           /* display var content    */
{ setrc( begrow, begcol );      /* set to start           */
  printf( txtptr );             /* show the string        */
  endrow = row();               /* update end row if scrl */
  if( ! col() &&                /* adjust for line scroll */
      endrow == (max_y-1) )
    endrow--;
  calccrsr();                   /* establish cursor posn  */
}

void do_del( void )
{ for (i=i_cur; txtptr[i]; i++ )/* slide over one to left */
    txtptr[i] = txtptr[i+1];
  if ( i_max && i_cur >= --i_max )       /* decr length   */
    i_cur = (i_max - 1);        /* and adjust if needed   */
  free_env++;                   /* account for freed byte */
  setrc( begrow, begcol );      /* re-display the string  */
  printf( txtptr );
  endrow = row();               /* hold ending point      */
  if( ! col() )                 /* adjust for line wrap   */
    endrow--;                   /*   if now in col 0      */
  putchar( ' ' );               /* erase garbage char     */
  calccrsr();                   /* establish cursor posn  */
}

void dochar( void )
{ if ( free_env < 3 )           /* just beep if no space  */
    { putchar( 7 );
      return;
    }
  if ( insmode )                /* open up a hole for new */
    { if ( --free_env < 3 )     /* decr freespace count   */
        { putchar( 7 );         /*   and if too little is */
          return;               /*   left, beep and quit  */
        }
    for (i = ++i_max; i > i_cur; i--)
      txtptr[i] = txtptr[i-1];
    }
  txtptr[i_cur++] = (char) c;   /* put char down          */
  if ( i_cur >= i_max )         /* check for extending it */
    { txtptr[ ++i_max ] = '\0'; /* set new EOS            */
      if ( --free_env < 3 )     /* decr freespace count   */
        { putchar( 7 );         /*   if too little is     */
          return;               /*   left, beep and quit  */
        }
    }
  show_var();                   /* re-display the string  */
}

int edtxt( void )               /* read kbd, do editing   */
{ int retval;
  begrow = row();
  begcol = 0;
  i_max = strlen( txtptr );     /* set buffer index limit */
  i_cur = 0;                    /* and current index val  */
  show_var();                   /* display the string     */
  for ( editing=1; editing; )   /* main editing loop here */
    { setrc( 0, 70 );           /* status message loc     */
      printf("MODE: %s ", insmode ? "INS" : "REP" );
      setrc( currow, curcol );  /* keep cursor posn curr  */
      switch( c = getch() )
        { case 0:               /* function key or keypad */
            switch( getch() )
              { case 30:        /* Alt-A, redisplay       */
                  show_var();   /* re-display the string  */
                  break;
                case 32:        /* Alt-D, delete variable */
                  printf("\nDELETE this variable (Y/N)? ");
                  if(( getch() & 89 ) == 89 ) /* 89 = 'Y' */
                    { vname[0] = '\0';
                      retval = 1;
                      editing = 0;
                    }
                  break;
                case 71:        /* home, goto first char  */
                  i_cur = 0;
                  calccrsr();   /* establish cursor posn  */
                  break;
                case 72:        /* up arrow               */
                  if ( (i_cur - max_x ) > 0 )
                    i_cur -= max_x;
                  calccrsr();   /* establish cursor posn  */
                  break;
                case 75:        /* left arrow             */
                  if ( i_cur > 0 )
                    i_cur--;
                  calccrsr();   /* establish cursor posn  */
                  break;
                case 77:        /* right arrow            */
                  if ( i_cur < i_max )
                    i_cur++;
                  calccrsr();   /* establish cursor posn  */
                  break;
                case 79:        /* end, goto last char    */
                  i_cur = i_max;
                  calccrsr();   /* establish cursor posn  */
                  break;
                case 80:        /* down arrow             */
                  if ( (i_cur + max_x ) < i_max )
                    i_cur += max_x;
                  calccrsr();   /* establish cursor posn  */
                  break;
                case 82:        /* insert, toggle flag    */
                  insmode = !insmode;
                  break;
                case 83:        /* delete, remove 1 char  */
                  do_del();
                  break;
              }                 /* end of special codes   */
            break;
          case 8:               /* backspace del to left  */
            if (i_cur)
              { i_cur--;        /* back up one first      */
                do_del();       /* then do the delete     */
              }
            break;
          case 13:              /* Enter accepts changes  */
            retval = 1;
            editing = 0;
            break;
          case 27:              /* ESC quits without save */
            retval = 0;
            editing = 0;
            break;
          default:
            if (c >= ' ' && c < 127)
              dochar();         /* handle INS or REP      */
            else
              putchar( 7 );     /* beep on any other char */
        }
    }
  setrc( endrow, 0 );
  return (retval);
}

void putenvbak( void )          /* copies back to env     */
{ char * locptr;
  int save_size;

  save_size = FP_OFF( lstbyt ) - FP_OFF( rest ) + 1;
  locptr = (char *)malloc( save_size );

  for( i=0; i<save_size; i++ )  /* save trailing data     */
    locptr[i] = rest[i];
  for( i=0; vname[i]; i++ )     /* copy edited string     */
    *menv++ = vname[i];
  if( vname[0] )                /* if not deleting...     */
    *menv++ = '\0';             /* ...add EOS byte to var */
  for( i=0; i<save_size; i++)   /* copy in trailing data  */
    *menv++ = locptr[i];
  free( locptr );               /* release save area      */

  printf("\nENVIRONMENT UPDATED." );
}

void doedit( char * varnam )    /* find var, edit, save   */
{ printf("Editing '%s':\n", varnam );
  menv = mstenvp();             /* set starting point     */
  free_env = envsiz(menv) << 4; /* get the size in bytes  */
  findvar( varnam );            /* look for the variable  */
  for( lstbyt=menv; *lstbyt; )  /* menv set by findvar()  */
    lstbyt=nxtevar(lstbyt);     /* locate end of var area */
  if( lstbyt[1] == 1 && lstbyt[2] == 0 )
    { lstbyt += 3;              /* skip loadfile name     */
      while (*lstbyt)
        lstbyt++;
    }
  lstbyt++;
  free_env -= FP_OFF( lstbyt ); /* what's left is free    */
  if ( txtptr == NULL )         /* didn't find the name   */
    { free_env -= (nmlen+1);    /* take out free space    */
      if ( free_env < 5 )
        { puts("Not found, no room to add.");
          return;
        }
      printf( "Not found; add it (Y/N)? " );
      if(( getch() & 89 ) != 89 )       /* 89 = 'Y'       */
        return;
      for ( i=0; i<nmlen; i++ ) /* force to uppercase     */
        vname[i] = (char) toupper( varnam[i] );
      vname[nmlen] = '=';       /* add the equals sign    */
      vname[nmlen+1] = '\0';    /* make content empty     */
      txtptr = &vname[nmlen+1]; /* set text pointer to it */
      putchar( '\n' );          /* start on fresh line    */
      insmode = 1;              /* and in INS mode        */
    }
  printf("Free environment space = %d bytes.\n", free_env );
  if ( edtxt() )                /* do the editing now     */
    putenvbak();                /* copy to master env     */
  else
    printf("\nENVIRONMENT NOT CHANGED." );
  putchar( '\n' );
}

void showvars( void )           /* prints usage message   */
{ puts(" USAGE: ENVEDT varname [[name2] ... ]");
  puts("where varname is the name of an env variable");
  puts("  and name2, etc., are optional added names.");
  puts("Current variable names are:" );
  menv = mstenvp();
  for( i=0; i<8; i++ )
    vname[i] = ' ';
  while ( *menv )               /* get and print names    */
    { sprintf(vname+8, "%Fs", menv );
      for( i=8; vname[i] != '='; i++ )
        /* all done by for()        */ ;
      vname[i] = '\0';
      puts( vname );
      menv = nxtevar( menv );
    }
  puts("Re-run with name(s) of variable(s) to be edited.");
}

void main ( int argc, char **argv )
{ int i;

  if (argc < 2)
    showvars();                 /* list all vars to CRT   */
  else
    { max_xy( &max_x, &max_y ); /* set up screen limits   */
      while ( --argc )          /* process all vars named */
        doedit( *++argv );
    }
}
