/* This is part of ODS2 written by Paul Nankervis,
 * email address:  Paulnank@au1.ibm.com

 * ODS2 is distributed freely for all members of the
 * VMS community to use. However all derived works
 * must maintain comments in their source to acknowledge
 * the contributions of the original author and
 * subsequent contributors.   This is free software; no
 * warranty is offered,  and while we believe it to be useful,
 * you use it at your own risk.
 */

#if !defined( DEBUG ) && defined( DEBUG_SETCMD )
#define DEBUG DEBUG_SETCMD
#else
#ifndef DEBUG
#define DEBUG 0
#endif
#endif

#include "cmddef.h"

#ifdef USE_LIBEDIT
#include <histedit.h>
extern EditLine *editline;
extern History  *edithist;
#endif

static hlpfunc_t sethelp;

static vmscond_t setcwd( char *newdef );
static vmscond_t setdef( char *newdef );

static vmscond_t savequals( options_t defval, qual_t *qp,
                            int qualc, char **qualv,
                            defqualsp_t *saveloc );
static void set_exit( void );


/******************************************************************* doset() */

#define set_msgdef OPT_GENERIC_1

/* Indexes must match in doset() & sethelp() */

static uint32_t histmax;

static
qual_t setkwds[] = { {"cwd",                  0, 0, NV,
                      "commands set item_name item_cwd"},
                     {"directory_qualifiers", 1, 0, NV,
                      "commands set item_name item_dirquals"},
                     {"copy_qualifiers",      2, 0, NV,
                      "commands set item_name item_copyquals" },
                     {"default",              3, 0, NV,
                      "commands set item_name item_setdef"},
                     {"error_exit",           4, 0, NV,
                      "-commands set item_name item_seterrxit"},
                     {"noerror_exit",         5, 0, NV, NULL },
                     {"message",              6, 0, NV,
                      "commands set item_name item_setmsg"},
                     {"qualifier_style",      7, 0, NV,
                      "commands set item_name item_qualstyle"},
                     {"search_qualifiers",    8, 0, NV,
                      "commands set item_name item_searchquals"},
                     {"verify",               9, 0, NV,
                      "-commands set item_name item_verify"},
                     {"noverify",            10, 0, NV, NULL },
#ifdef USE_LIBEDIT
                     {"key_bindings",        11, 0, NV,
                      "commands set item_name item_key_bindings"},
#endif
                     {"history_limit",       12, 0, DV(&histmax),
                      "commands set item_name item_history_limit"},
                     {"help_file",           13, 0, NV,
                      "commands set item_name item_helpfile"},

                     {NULL,                   0, 0, NV, NULL }
};

static
qual_t setmsgkwdsp[] = {{"facility",         MSG_FACILITY,   0,            NV,
                         "commands set item_name item_msgfac"},
                        {"nofacility",       0,              MSG_FACILITY, NV, NULL},
                        {"severity",         MSG_SEVERITY,   0,            NV,
                         "commands set item_name item_msgsev"},
                        {"noseverity",       0,              MSG_SEVERITY, NV, NULL},
                        {"identification",   MSG_NAME,       0,            NV,
                         "commands set item_name item_msgid"},
                        {"noidentification", 0,              MSG_NAME,     NV, NULL},
                        {"text",             MSG_TEXT,       0,            NV,
                         "commands set item_name item_msgtext"},
                        {"notext",           0,              MSG_TEXT,     NV, NULL},
                        {"default",          set_msgdef,     0,            NV,
                         "commands set item_name item_default"},

                        {NULL,               0,              0,            NV, NULL }
};

static
qual_t setqskwds[] = {{"unix", 1, 0, NV, "commands set value value_unix"},
                      {"dcl",  2, 0, NV, "commands set value value_dcl"},
                      {"vms",  2, 0, NV, NULL },

                      {NULL,   0, 0, NV, NULL }
};
param_t setpars[] = { {"item_name", REQ, KEYWD, PA(setkwds),         "" },
                      {"value"    , CND, KEYWD, sethelp, setqskwds,  "" },

                      {NULL,        0,   0,      NOPA,               NULL },
};

/*********************************************************** doset() */

extern qual_t copyquals[];
extern const int copy_defaults;
extern defqualsp_t copy_defopt;

extern qual_t dirquals[];
extern const int dir_defaults;
extern defqualsp_t dir_defopt;

extern qual_t searchquals[];
extern const int search_defaults;
extern defqualsp_t search_defopt;

extern uint32_t cmd_histsize;

DECL_CMD(set) {
    options_t parnum;
    vmscond_t sts;

    if( $FAILS(sts = checkquals( &parnum, 0, setkwds, -1, argv+1 )) ) {
        return sts;
    }
    switch( parnum ) {
    default:
        return SS$_BADPARAM;
    case 0: /* default */
    case 3: /* current working directory */
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        return (parnum == 0)? setcwd( argv[2] ) : setdef( argv[2] );
    case 1:{ /* directory_qualifiers */
        if( qualc == 0 )
            return printmsg( ODS2_NOQUALS, 0 );
        return savequals( dir_defaults, dirquals,
                          qualc, qualv, &dir_defopt );
    }
    case 2:{ /* copy_qualifiers */
        if( qualc == 0 )
            return printmsg( ODS2_NOQUALS, 0 );
        return savequals( copy_defaults, copyquals,
                          qualc, qualv, &copy_defopt );
    }
    case 4:
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        error_exit = TRUE;
        return SS$_NORMAL;
    case 5:
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        error_exit = FALSE;
        return SS$_NORMAL;
    case 6: { /* message file */
        options_t options;

        if( qualc != 0 ) {
            if( $FAILS(sts = checkquals( &options, get_message_format(),
                       setmsgkwdsp, qualc, qualv )) )
                return sts;
            if( argc > 2 )
                return printmsg( ODS2_NOPAROK, 0 );
            if( options & set_msgdef ) {
                if( (options & ~set_msgdef) != get_message_format() )
                    return printmsg( ODS2_INCONQUAL, 0 );
                return set_message_file( NULL );
            } 
            if( $FAILS(sts = set_message_format( options )) )
                return printmsg( sts, 0 );
            return SS$_NORMAL;
        }
        if( argc > 3 )
            return printmsg( ODS2_TOOMANY, 0, "parameters" );
        if( argc < 3 )
            return printmsg( ODS2_TOOFEW, 0, "parameters" );
       if( $FAILS(sts = set_message_file( argv[2] )) )
            return printmsg(sts, 0 );
        return SS$_NORMAL;
    }
    case 7: { /* qualifier_style */
        options_t par;

        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        if( $FAILS(sts = checkquals( &par, 0, setqskwds, 1, argv+2 )) ) {
            return sts;
        }
        switch(par) {
        default:
            abort();
        case 2:
            qstyle_s = "/";
            break;
        case 1:
            qstyle_s = "-";
            break;
        }
        return SS$_NORMAL;
    }
    case 8:{ /* search_qualifiers */
        if( qualc == 0 )
            return printmsg( ODS2_NOQUALS, 0 );
        return savequals( search_defaults, searchquals,
                          qualc, qualv, &search_defopt );
    }
    case 9:
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        verify_cmd = TRUE;
        return SS$_NORMAL;
    case 10:
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        verify_cmd = FALSE;
        return SS$_NORMAL;
#ifdef USE_LIBEDIT
    case 11: {
        char *av[20];
        int i, ac;

        ac = argc -2;
        for( i = 0; i < 20; i++ ) {
            if( i < ac )
                av[i] = argv[i+2];
            else
                av[i] = NULL;
        }

        el_set( editline, EL_BIND,
                av[0],  av[1],  av[2],  av[3],  av[4],  av[5],  av[6],  av[7],
                av[8],  av[9],  av[10], av[11], av[12], av[13], av[14], av[15],
                av[16], av[17], av[18], NULL );

        return SS$_NORMAL;
    }
#endif
    case 12: {
#ifdef USE_LIBEDIT
        HistEvent ev;

        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        if( histmax < 1 )
            return printmsg( HELP_SETCMDHISTVAL, 0 );
        if( history( edithist, &ev, H_SETSIZE, histmax ) >= 0 ) {
            cmd_histsize = histmax;
        }
#else
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        if( histmax < 1 )
            return printmsg( HELP_SETCMDHISTVAL, 0 );
        cmd_histsize = histmax;
#endif
        return SS$_NORMAL;
    }
    case 13:
        if( qualc )
            return printmsg( ODS2_NOQUALSOK, 0 );
        if( argc > 3 )
            return printmsg( ODS2_TOOMANY, 0, "parameters" );
        if( argc < 3 )
            return printmsg( ODS2_TOOFEW, 0, "parameters" );
        if( $FAILS(sts = sethelpfile( argv[2], NULL, NULL )) )
            return printmsg( sts, 0 );
        return sts;
    } /* switch */
}

/************************************************************ sethelp() */

static const char *sethelp( CMDSETp_t cmd, paramp_t p,
                            int argc, char **argv ) {
    options_t par;
    vmscond_t sts;

    UNUSED( cmd );
    UNUSED( argc );

    if( argc == 1 ) {
        p->helpstr = "";
        p->ptype = KEYWD;
        p->arg = setkwds;
        return getmsg( HELP_SETVALUE, MSG_TEXT );
    }

    if( $FAILS(sts = checkquals( &par, 0, setkwds, -1, argv+1 )) ) {
        return NULL;
    }

    switch( par ) {
    case 0:
        p->helpstr = "commands set item_name type_cwd";
        p->ptype = STRING;
        break;
    case 1:
        p->helpstr = "commands set item_name type_dirquals";
        p->ptype = QUALS;
        p->arg = dirquals;
        break;
    case 2:
        p->helpstr = "commands set item_name type_copyquals";
        p->ptype = QUALS;
        p->arg = copyquals;
        break;
    case 3:
        p->helpstr = "commands set item_name type_setdef";
        p->ptype = FSPEC;
        break;
    case 4:
    case 5:
        p->helpstr = "commands set item_name type_seterrxit";
        p->ptype = NONE;
        break;
    case 6:
        p->helpstr = "commands set item_name type_setmsg";
        p->ptype = QUALS;
        p->arg = setmsgkwdsp;
        break;
    case 7:
        p->helpstr = "commands set item_name type_qualstyle";
        p->ptype = KEYWD;
        p->arg = setqskwds;
        break;
    case 8:
        p->helpstr = "commands set item_name type_searchquals";
        p->ptype = QUALS;
        p->arg = searchquals;
        break;
    case 9:
    case 10:
        p->helpstr = "commands set item_name item_verify";
        p->ptype = NONE;
        break;
#ifdef USE_LIBEDIT
    case 11:
        p->helpstr = "commands set item_name item_key_bindings";
        p->ptype = STRING;
        break;
#endif
    case 12:
        p->helpstr = "commands set item_name item_history_limit";
        p->ptype = STRING;
        break;
    case 13:
        p->helpstr = "commands set item_name item_helpfile";
        p->ptype = NONE;
        break;
    default:
        fprintf( stderr, "Missing help key for param %d\n", par );
        abort();
    }
    return NULL;
}

/*********************************************************** setcwd() */

static vmscond_t setcwd( char *newdef ) {
   if( chdir( newdef ) != 0 ) {
       int err;

       err = errno;
       printmsg( ODS2_SETCWD, 0 );
       return printmsg( ODS2_OSERROR, MSG_CONTINUE, ODS2_SETCWD,
                        strerror( err ) );
   }
   return SS$_NORMAL;
}

/*********************************************************** setdef() */

static int default_set = FALSE;

static vmscond_t setdef( char *newdef ) {
    register vmscond_t sts;
    struct dsc_descriptor defdsc = { 0, 0, 0, NULL };

    defdsc.dsc_a_pointer = (char *) newdef;
    defdsc.dsc_w_length = (unsigned short)strlen( defdsc.dsc_a_pointer );
    if( $SUCCESSFUL(sts = sys_setddir( &defdsc, NULL, NULL )) ) {
        default_set = TRUE;
    } else {
        sts = printmsg( sts, 0 );
    }
    return sts;
}

/*********************************************************** mountdef() */

vmscond_t mountdef( const char *devnam ) {
    char *colon, *buf;
    size_t len;

    if( default_set )
        return SS$_NORMAL;

    len = strlen( devnam );
    buf = (char *) malloc( len  + sizeof( ":[000000]" ) );
    if( buf == NULL ) {
        return printmsg( ODS2_OSERROR, 0, strerror( errno ) );
    }

    colon = strchr( devnam, ':' );
    if( colon != NULL )
        len = (size_t)(colon - devnam);
    memcpy( buf, devnam, len );
    memcpy( buf+len, ":[000000]", sizeof( ":[000000]" ) );
    setdef( buf );
    free( buf );

    return SS$_NORMAL;
}

/************************************************************ savequals() */

static defqualsp_t savedquals = NULL;

static vmscond_t savequals( options_t defval, qual_t *qp,
                            int qualc, char **qualv,
                            defqualsp_t *saveloc ) {
    vmscond_t sts;
    int i;
    size_t len;
    defqualsp_t newq, *old;
    char *dp;

    len = ( offsetof( defquals_t, data ) +
            ( sizeof( char** ) * ((size_t)qualc +1) ) );

    for( i = 0; i < qualc; i++ )
        len += strlen( qualv[i] ) + 1;

    if( (newq = (defqualsp_t) malloc( len )) == NULL )
        return SS$_INSFMEM;

    newq->qualc = qualc;
    newq->qualv = newq->data;

    dp = (char *)(newq->data + qualc + 1);

    for( i = 0; i < qualc; i++ ) {
        newq->qualv[i] = dp;
        len = strlen( qualv[i] ) +1;
        memcpy( dp, qualv[i], len );
        dp += len;
    }
    newq->qualv[i] = NULL;

    if( $FAILS(sts = checkquals( &defval, defval, qp, qualc, qualv )) ) {
        free( newq );
        return sts;
    }

    for( old = &savedquals; *old != NULL; old = &(*old)->next ) {
        defqualsp_t t;

        if( (t = *old) == *saveloc ) {
            *old = t->next;
            free( t );
            break;
        }
    }

    newq->next = savedquals;

    if( savedquals == NULL )
        atexit( set_exit );

    *saveloc =
        savedquals = newq;

    return SS$_NORMAL;
}

/************************************************************ set_exit() */
static void set_exit( void ) {
    defqualsp_t sq;

    while( (sq = savedquals) != NULL ) {
        savedquals = sq->next;
        free( sq );
    }

    return;
}

