/*****************************************************************************/
/* (C) Copyright, B. EICHBGERGER, All rights reserved.                       */
/*                                                                           */
/* This program remains the exclusive property of B. EICHBERGER              */
/* may not be sold, used, copied, displayed or modified without the written  */
/* consent of Burkhard Eichberger, P.O.Box 4767, Boulder, Colorado 80306     */
/*****************************************************************************/
#include <stdio.h>
#include <sys\types.h>
#include <sys\stat.h>
#include "def.h"
#define EXPARG_M
#include "exparg.h"
#include "def_s.h"
#include "dir.h"

VAR_EXT;

int system(const char *);

#define MAXBUF 100
#define MAXLEAD 20

#define FN_EXP "~"
#define FN_CFG "dx.cfg"
#define CHR_SYS   '`'
#define CHR_QUOTE '"'
#define CHR_INV   '!'
#define CHR_SEP   ';'
#define CHR_PIPE  '^'
#define CHR_IN    '<'
#define CHR_OUT   '>'
#define CHR_ENV1  '%'
#define CHR_ENV2  '$'
#define CHR_LST   '@'

int do_arg_xinv = OFF;
char do_arg_sep = CHR_SEP;
char do_arg_pipe = CHR_PIPE;
char do_arg_in   = CHR_IN;
char do_arg_out  = CHR_OUT;
char do_arg_dir[MAXFILNAM+1];   /* last directory */
char do_arg_lead[MAXLEAD+1];    /* leadin command */

int do_arg_ins(char *szNew);
int do_fillst(char *szArg);
int do_getenv(char *szArg,char *szOut);
int do_arg_bld(char *szArg);
int do_arg_sys(char *szCmd,char *szTail);

/****** PROCESS ARGUMENT EXPANSION ******************************************/
/* Input:                                                                   */
/* ac        = Number of arguments on command line                          */
/* av        = Argument Vector                                              */
/*                                                                          */
/* Output:                                                                  */
/* 0          = OK                                                          */
/* -1         = Cannot open database                                        */
/* -2         = Cannot open window environment                              */
/* -3         = Cannot open getinp environment                              */
/* -4         = Cannot get sellst RAM                                       */
/* -5         = Not enough RAM                                              */
/* -6         = Missing string flex file                                    */
/* -7         = Escape at initial screen                                    */
/****************************************************************************/
int do_arg(int ac,char *av[])
{
FILE *fcfg;
register int i, i0, i1;
char buf1[100+1];
char bufcmd[128+1];
char fncfg[MAXFILNAM+1];
char c, c1, c2, cout;
char *arg, *p, *p0, *pout;
char *ptail;
int xend;
int xsys;

    /****** INIT ************************************************************/
    argc = 0;
    MEMZAP0(do_arg_dir,sizeof(do_arg_dir));
    
    /****** PROCESS PROGRAM NAME ********************************************/
    do_arg_ins(av[0]);

    /****** PROCESS ARGUMENTS ***********************************************/
    for (i=1; i < ac; i++)
        {
        /****** GET ARGUMENT ************************************************/
        arg = av[i];

        /****** PROCESS FILE LIST *******************************************/
        if (do_fillst(arg) == 0)
             {
             continue;
             }

        /****** PROCESS ENVIRONMENT *****************************************/
        if (do_getenv(arg,buf1) == 0)
             {
             do_arg_bld(buf1);
             continue;
             }

        /****** CHECK FOR BACK QUOTE SYSTEM CALLS `cmd` *********************/
        xsys = OFF;
        p = arg;
        pout = do_arg_lead;
        while ((c = *p++) != '\0')
            {
            if (c == CHR_SYS) { xsys = ON; break; }
            *pout++ = c;
            }
        *pout = '\0';

        /****** REGULAR PLAIN ARGUMENT **************************************/
        if (xsys == OFF)
            {
            do_arg_bld(arg);
            continue;
            }
        p0 = p;

        /****** PROCESS THE CONFIG FILE *************************************/
        strncpy(fncfg,av[0],sizeof(fncfg));
        p = fncfg + strlen(fncfg) - 1;
        while (*p != '\\') *p-- = '\0';
        p++;
        strcat(p,FN_CFG);
        if ((fcfg = fopen(fncfg,"rb")) != NULL)
            {
            while (fgetl(bufcmd,sizeof(bufcmd)-1,fcfg) != EOF)
                {
                p = bufcmd;
                cout = '\0';
                while ((c = *p++) != '\0')
                    {
                    if (c != '=')  continue;
                    if (*p != '\\') { cout = *p; break; }
                    c1 = shtoi(p[1]);
                    c2 = shtoi(p[2]);
                    if ((c1 < 0) || (c2 < 0)) 
                        {
                        FERR"exp: Invalid hex number in '%s': %s\n",
                            fncfg,bufcmd);
                        break;
                        }
                    cout = (c1<<4) + c2;
                    break;
                    }
                if (cout == '\0') continue;
if (memcmp(bufcmd,"PIPE="  ,5) == 0) { do_arg_pipe = cout; continue; }
if (memcmp(bufcmd,"CMDSEP=",7) == 0) { do_arg_sep  = cout; continue; }
if (memcmp(bufcmd,"IN="    ,3) == 0) { do_arg_in   = cout; continue; }
if (memcmp(bufcmd,"OUT="   ,4) == 0) { do_arg_out  = cout; continue; }
                FERR"exp: Incorrect command in '%s': %s\n",fncfg,bufcmd);
                }
            fclose(fcfg);
            }

        /****** JOIN COMMANDS UNTIL CLOSING BACK QUOTE **********************/
        xend = OFF;
        pout = bufcmd;
        i0 = i;
        do  {
            if (i == i0) p = p0;    /* start after leadin */
            else         p = av[i]; /* regular argument */
            if (*p == CHR_SYS) p++; /* skip leading backquote ` */
            if (do_getenv(p,buf1) == 0) p = buf1;
            i1 = strlen(p) - 1;
            ptail = p;
            while (*ptail != '\0')
                {
                if (*ptail != CHR_SYS) { ptail++; continue; }
                *ptail++ = '\0'; /* cut ` out */
                xend = ON;
                break;
                }
            while ((c = *p++) != '\0')
                {
                if (c == do_arg_pipe) c = '|';
                if (c == do_arg_in  ) c = '<';
                if (c == do_arg_out ) c = '>';
                *pout++ = c;
                }
            if (xend == ON) break;
            *pout++ = ' ';
            i++;
            } while (i < ac);
        *pout = '\0';
        do_arg_sys(bufcmd,ptail);
        }

    /****** CLEANUP *********************************************************/
    unlink(EXP_FN_LST);
    for (i=0; i < MAXDIRLEV; i++) dir_close();
    return(argc);
}

/****** BUILD NEW ARGUMENT INTO TABLE ***************************************/
/* Input:                                                                   */
/* argtxt    = Text of new argument                                         */
/*                                                                          */
/* Output:                                                                  */
/* 0          = Inserted                                                    */
/* -1         = Directory contains meta char                                */
/* -2         = Cannot change to new directory                              */
/****************************************************************************/
do_arg_bld(argtxt)
char *argtxt;
{
register char c, *p, *p0, *pin, *pout;
char *pexp;
char *pexpinv;
char bufdir[MAXFILNAM+1];
char bufarg[MAXFILNAM+1];
char bufin[MAXFILNAM+1];
char fn[MAXFILNAM+1];
char rdbu[MAXFILNAM+1];
char bufexp[100];
char bufexp_[100];
char bufexpinv[100];
char bufexpinv_[100];
int nlen;
int ncolon;
int col;
int xexp;
int xfile;
int xdir;
int xdrv;
int xcopy;
int xsamedir;                 /* same directory as last argument one */
int xroot;                    /* ON: root directory exception */
int nhit;
int ret_, ret;
int i, i1, i2;

    /****** CHECK IF ARGUMENT CONTAINS EXPRESSION(S) ************************/
    pin = p0 = argtxt;
    xexp = OFF;
    xfile = ON;
    xcopy = OFF;
    pexpinv = NULL;
    c = *pin;
    if (c == '\'') { xcopy = ON; p0++; }
    pin = p0 + strlen(p0) - 1;
    if ((*pin == '\'') || (*pin == '`')) *pin = '\0'; /* strip trailing '` */
    if (access(argtxt,ACC_RD) == 0) xcopy = ON;

    /****** CHECK IF TEXT CONTAINS NO META CHARACTER ************************/
    if (xcopy == ON)
        {
NOEXP:
        /****** INSERT ROOT DIR & CONVERT DOUBLE \\ TO \ ********************/
        p = p0;
        pout = bufarg;
        i = 0;
        ncolon = 0;
        while ((c = *p++) != '\0')
            {
            *pout++ = c;
            if (c == ':') ncolon++;
            if ((i++ == 1) && (c == ':') && ((*p != '/') && (*p != '\\')))
                *pout++ = '/';
            }
        *pout = '\0';
        if (ncolon > 1) strncpy(bufarg,p0,sizeof(bufarg));
        return(do_arg_ins(bufarg));
        }

    /****** INIT ************************************************************/
    MEMZAP0(bufdir,sizeof(bufdir));
    MEMZAP0(bufexp,sizeof(bufexp));

    /****** SKIP LEADING BLANKS *********************************************/
    if (*p0 == CHR_INV)
        {
        do_arg_xinv = ON;
        p0++;
        strfld(p0,CHR_INV,1,1,bufin,sizeof(bufin)); /* get invert expr */
        if (strfld(p0,CHR_INV,2,2,bufexpinv,sizeof(bufexpinv)) > 0)
            {
            /****** TRANSLATE THE SHELL EXPRESSION TO REGULAR EXPRESSION ****/
            pin = bufexpinv;
            pout = bufexpinv_;
            *pout++ = '^';
            while ((c = *pin++) != '\0')
                {
                /****** ESCAPE **********************************************/
                if (c == '\\') { *pout++ = *pin++; continue; }
        
                /****** STAR TO: ANY CHAR REPEATED ANY AMOUNT ***************/
                if (c == '*') { *pout++ = '.';  *pout++ = '*'; continue; }
        
                /****** QUESTION MARK TO: SINGLE CHARACTER ******************/
                if (c == '?') { *pout++ = '.';                 continue; }
        
                /****** DOT TO: ACTUAL CHARACTER DOT ************************/
                if (c == '.') { *pout++ = '\\'; *pout++ = c;   continue; }
                *pout++ = c;
                }
            *pout++ = '$';
            *pout = '\0';
        
            /****** COMPILER REGULAR EXPRESSION *****************************/
if (getenv("REDEBUG") != NULL) FERR"INV: in: %s out: %s\n",bufexpinv,bufexpinv_);
            pexpinv = regcmp(bufexpinv_,NULL);
            }
        }
    else{
        do_arg_xinv = OFF;
        strncpy(bufin,p0,sizeof(bufin));
        }
    pin = bufin;
    pout = bufdir;
    while (*pin == ' ') pin++;  /* skip leading blanks */

    /****** TRANSLATE \ TO / AND INSERT ROOT / AFTER D: *********************/
    p = pin;
    pout = bufarg;
    i = 0;
    ncolon = 0;
    while ((c = *p++) != '\0')
        {
        /****** CHECK FOR RE ESCAPE *****************************************/
        if ((c == '/') && (*p == '\\'))
            {
            *pout++ = '\\';
            *pout++ = *++p;
            p++;
            continue;
            }

        /****** CHECK FOR DIRECTORY SEPARATOR *******************************/
        if (*p == '\\') *p = '/';

        /****** COPY ********************************************************/
        *pout++ = c;

        /****** CHECK FOR DRIVE : *******************************************/
        if (c == ':') ncolon++;

        /****** INSERT ROOT DIRECTORY IF DRIVE ******************************/
        if ((i++ == 1) && (c == ':') && ((*p != '/') && (*p != '\\')))
            *pout++ = '/';
        }
    *pout = '\0';
    if (ncolon > 1) strncpy(bufarg,pin,sizeof(bufarg));

    /****** SEPARATE DIRECTORY FROM FILE/EXPRESSION *************************/
    p0 = bufarg;
    nlen = strlen(p0);
    pin = p0 + nlen - 1;
    xdir = xdrv = OFF;
    col = nlen;
    while (pin >= p0)
        {
        c = *pin--;
        col--;
        if (c == '/') { xdir = ON; break; }
        }

    /****** CHECK IF DIRECTORY FOUND ****************************************/
    if (xdir == ON)
        {
        MEMCPY(bufdir,p0,col);
        MEMCPY(bufexp,p0+col+1,nlen-col);
        }
    else{
        sprintf(bufdir,".");
        MEMCPY(bufexp,p0,nlen);
        }

    /****** CHECK IF META CHARACTER ON DIRECTORY ****************************/
    pin = bufdir;
    xexp = OFF;
    while ((c = *pin++) != '\0')
        {
        switch (c)
        {
        case '*':
        case '?':
        case '[':
        case ']':
        case '^':
        case '$':
            xexp = ON;
            break;
        }
        if (xexp == ON) { p0 = argtxt; goto NOEXP; }
        }

    /****** GET ALL FILES ***************************************************/
    xsamedir = xroot = OFF;
    if (bufdir[0] == '\0') /* straight root exception */
        {
        xroot = ON;
        bufdir[0] = '/'; bufdir[1] = '\0';
        }
    if ((bufdir[1] == ':') && (bufdir[2] == '\0')) /* drive root exception */
        {
        xroot = ON;
        bufdir[2] = '/'; bufdir[3] = '\0';
        }
    if ((i1 = strlen(bufdir)) == (i2 = strlen(do_arg_dir)))
        {
        if (memcmp(bufdir,do_arg_dir,i1) == 0) xsamedir = ON;
        }
    if (xsamedir == OFF)
        {
        MEMCPY(do_arg_dir,bufdir,sizeof(do_arg_dir));
        dir_close();
        if (dir_open(bufdir) < 1) { p0 = argtxt; goto NOEXP; }
        }
    else{
        dir_rewind();
        }

    /****** TRANSLATE THE SHELL EXPRESSION TO REGULAR EXPRESSION ************/
    pin = bufexp;
    pout = bufexp_;
    *pout++ = '^';
    while ((c = *pin++) != '\0')
        {
        /****** ESCAPE ******************************************************/
        if (c == '\\') { *pout++ = *pin++; continue; }

        /****** STAR TO: ANY CHAR REPEATED ANY AMOUNT ***********************/
        if (c == '*') { *pout++ = '.';  *pout++ = '*'; continue; }

        /****** QUESTION MARK TO: SINGLE CHARACTER **************************/
        if (c == '?') { *pout++ = '.';                 continue; }

        /****** DOT TO: ACTUAL CHARACTER DOT ********************************/
        if (c == '.') { *pout++ = '\\'; *pout++ = c;   continue; }
        *pout++ = c;
        }
    *pout++ = '$';
    *pout = '\0';


    /****** COMPILER REGULAR EXPRESSION *************************************/
if (getenv("REDEBUG") != NULL) FERR"in: %s out: %s\n",bufexp,bufexp_);
    if ((pexp = regcmp(bufexp_,NULL)) == NULL) { p0 = argtxt; goto NOEXP; }

    /****** SCAN FILENAMES **************************************************/
    nhit = 0;
    while (dir_read(fn) == 0)
        {
        if ((fn[0] == '.') && (fn[1] == '\0')) continue;
        if ((fn[0] == '.') && (fn[1] == '.')) continue;
        if (pexpinv == NULL)
            {
            ret_ = regchk(fn,pexp);
            if (do_arg_xinv == OFF)
                {
                if (ret_ != 0) continue;
                }
            else{
                if (ret_ == 0) continue;
                }
            }
        else{
            /****** FIRST CHECK IF HIT ***************************************/
            ret_ = regchk(fn,pexpinv);
            if (ret_ != 0) continue;

            /****** THEN CHECK IF NOT INVERSE EXPR ***************************/
            ret_ = regchk(fn,pexp);
            if (ret_ == 0) continue;
            }
        nhit++;
        if ((bufdir[0] == '\0') || (memcmp(bufdir,".",2) == 0))
            {
            do_arg_ins(fn);
            continue;
            }
        if (xroot == OFF) sprintf(rdbu,"%s/%s",bufdir,fn);
        else              sprintf(rdbu,"%s%s",bufdir,fn);
        do_arg_ins(rdbu);
        }
    free_(pexp);
    if (nhit == 0) { p0 = argtxt; goto NOEXP; }
    ret = 0;

    return(ret);
}

/****** INSERT NEW ARGUMENT INTO TABLE **************************************/
/* Input:                                                                   */
/* argtxt    = Text of new argument                                         */
/*                                                                          */
/* Output:                                                                  */
/* 0          = Inserted                                                    */
/* -1         = Out of memory                                               */
/****************************************************************************/
do_arg_ins(argtxt)
char *argtxt;
{
register char c, *pin;
int xfile;
int nlen;
        
    /****** CHECK IF FIRST TIME ******************************************/
    if (argc == 0)
        {
        MEMZAP0(argv,sizeof(argv));
        if ((argv_p = malloc_(MAXARGVLEN,"argv")) == NULL) return(-1);
        argv_nwr = 0;
        }

    /****** CHECK IF FILE ************************************************/
    if (access(argtxt,ACC_RD) == 0) xfile = ON;
    else                            xfile = OFF;

    /****** CHECK MEMORY *************************************************/
    nlen = strlen(argtxt) + 1;
    if ((argv_nwr+nlen) > MAXARGVLEN)
        {
        if ((argv_p = malloc_(MAXARGVLEN,"argv")) == NULL) return(-1);
        argv_nwr = 0;
        }

    /****** INSERT NEW ARGUMENT ******************************************/
    pin = argtxt;
    if (argc >= MAXARGV)
        {
        if (argc == MAXARGV) { FERR"More than %d arguments\n",MAXARGV); argc++;}
        return(-3);
        }
    argv[argc++] = argv_p;
    while ((c = *pin++) != '\0')
        {
        /****** TRANSLATE CHARACTER TO UNIX STYLE IF FILE/DIRECTORY ******/
        if (xfile == ON)
           {
           if (c == '\\') c = '/';
           else           c = tolower(c);
           }
        *argv_p++ = c;
        }
    *argv_p++ = '\0';
    argv_nwr += nlen;
    return(0);
}
/****** PROCESS SYSTEM CALL *************************************************/
/* Input:                                                                   */
/* argtxt    = Text of new argument                                         */
/* tail      = tail text (if any)                                           */
/*                                                                          */
/* Output:                                                                  */
/* 0          = Inserted                                                    */
/* -1         = Out of memory                                               */
/****************************************************************************/
do_arg_sys(argtxt,tail)
char *argtxt, *tail;
{
FILE *fin;
register char c;
char bufcmd[128+1];
char rdbu[MAXBUF+1];
char *p;
int xredirect;
int nmax, i;

    /****** INIT ************************************************************/
    unlink(FN_EXP);

    /****** RUN COMMANDS ****************************************************/
    i = 1;
    while (strfld(argtxt,do_arg_sep,i,i,bufcmd,sizeof(bufcmd)) > 0)
        {
        i++;
        xredirect = OFF;
        p = bufcmd;
        while ((c = *p++) != '\0')
            {
            if (c != '>') continue;
            xredirect = ON;
            break;
            }
        if (xredirect == OFF)
            {
            strcat(bufcmd,">>");
            strcat(bufcmd,FN_EXP);
            }
        system(bufcmd);
        }

    /****** GET RESULT ******************************************************/
    if ((fin = fopen(FN_EXP,"rb")) == NULL) return(-1);
    nmax = 0;
    while (fgetl(rdbu,MAXBUF,fin) != EOF) nmax++;
    rewind(fin);
    i = 0;
    while (fgetl(rdbu,MAXBUF,fin) != EOF)
        {
        if (i++ == 0) sprintf(p = bufcmd,"%s%s",do_arg_lead,rdbu);
        else          p = rdbu;
        if (i == nmax) strcat(p,tail);
        do_arg_bld(p);
        }
    fclose(fin);
    unlink(FN_EXP);
    return(0);
}

/****** RETRIEVE ENVIRONMENT ************************************************/
/* Input:                                                                   */
/* arg       = argument                                                     */
/* out       = text output                                                  */
/*                                                                          */
/* Output:                                                                  */
/* 0          = out filled with environment                                 */
/* 1          = no environment variable given                               */
/* -1         = environment not found                                       */
/****************************************************************************/
do_getenv(arg,out)
char *arg, *out;
{
char buf1[100];
char *p;
char *penv;

    if ((arg[0] != CHR_ENV1) && (arg[0] != CHR_ENV2)) return(1);
    strncpy(buf1,arg,sizeof(buf1));
    p = buf1 + strlen(buf1) - 1;
    if (*p == CHR_ENV1) *p = '\0'; /* cancel trailing % */
    if (*p == CHR_SYS ) *p = '\0'; /* cancel trailing ` */
    strupp(buf1);
    *out = '\0';
    if ((penv = getenv(buf1+1)) == NULL) return(0);
    strcpy(out,penv);
    return(0);
}

/****** PROCESS FILE LIST ***************************************************/
/* Input:                                                                   */
/* arg       = argument                                                     */
/* out       = text output                                                  */
/*                                                                          */
/* Output:                                                                  */
/* 0          = out filled with environment                                 */
/* 1          = no environment variable given                               */
/* -1         = environment not found                                       */
/****************************************************************************/
do_fillst(char *arg)
{
FILE *fin;
char rdbu[MAXBUF+1];

    fin = NULL;
    if (arg[0] != CHR_LST) return(1);

    if ((fin = fopen(arg+1,"rb")) == NULL)
        {
        FERR"ARG: Cannot open file '%s' (%s)\n",arg+1,SYSERR);
        return(0); /* so that it is not passed on */
        }
    while (fgetl(rdbu,MAXBUF,fin) != EOF)
        {
        do_arg_bld(rdbu);
        }
    
    if (fin != NULL)
        fclose(fin);
    return(0);
}
