/***
*wild.c - wildcard expander
*
*   Copyright (c) 1985-1992, Microsoft Corporation.  All rights reserved.
*
*Purpose:
*   expands wildcards in argv
*
*   handles '*' (none or more of any char) and '?' (exactly one char)
*
*******************************************************************************/

#include <stddef.h>
#include <string.h>
#include <malloc.h>
#include <register.h>
#include <ctype.h>
#include <msdos.h>
#include <internal.h>


/*
** these are the data structures
**
**     __argv
**     -------     ------
**     |     |---->|    |---->"arg0"
**     -------     ------
**         |    |---->"arg1"
**         ------
**          ....
**         ------
**         |    |---->"argn"
**         ------
**         |NULL|
**         ------
**                   argend
**                   -------
**     -------               |     |
**     |     | __argc            -------
**     -------                  |
**                      |
**  arghead                 V
**  ------     ---------        ----------
**  |    |---->|   |   |----> .... ---->|   |NULL|
**  ------     ---------        ----------
**       |            |
**       V            V
**        "narg0"              "nargn"
*/

#define SLASHCHAR   '\\'
#define FWDSLASHCHAR '/'
#define SLASH       "\\"
#define FWDSLASH    "/"
#define STAR        "*.*"

#define WILDSTRING  "*?"

#ifdef  _WINDOWS
    extern int _near __argc;
    extern char * * _near __argv;
#else
    extern int __argc;
    extern char * * __argv;
#endif

struct argnode {
    char *argptr;
    struct argnode *nextnode;
};

static struct argnode *arghead;
static struct argnode *argend;

static int match (char *, char *);
static int add (char *);
static void sort (struct argnode *);

/***
*int _cwild() - wildcard expander
*
*Purpose:
*    expands wildcard in file specs in argv
*
*    handles '*' (none or more of any char), '?' (exactly one char), and
*    '[string]' (chars which match string chars or between n1 and n2
*    if 'n1-n2' in string inclusive)
*
*Entry:
*
*Exit:
*    returns 0 if successful, -1 if any malloc() calls fail
*    if problems with malloc, the old argc and argv are not touched
*
*Exceptions:
*
*******************************************************************************/

int
_cwild ()
{
    REG1 char **argv = __argv;
    REG2 struct argnode *nodeptr;
    REG3 int argc;
    REG4 char **tmp;
    char *wchar;

    arghead = argend = NULL;

    for (argv = __argv; *argv; argv++)  /* for each arg... */
    if ( *(*argv)++ == '"' )
        /* strip leading quote from quoted arg */
    {
        if (add(*argv))
        return(-1);
    }
    else if (wchar = strpbrk( *argv, WILDSTRING )) {
        /* attempt to expand arg with wildcard */
        if (match( *argv, wchar ))
        return(-1);
    }
    else if (add( *argv ))  /* normal arg, just add */
        return(-1);

    /* count the args */
    for (argc = 0, nodeptr = arghead; nodeptr;
        nodeptr = nodeptr->nextnode, argc++)
        ;

    /* try to get new arg vector */
    if (!(tmp = (char **)malloc(sizeof(char *)*(argc+1))))
    return(-1);

    /* the new arg vector... */
    __argv = tmp;

    /* the new arg count... */
    __argc = argc;

    /* install the new args */
    for (nodeptr = arghead; nodeptr; nodeptr = nodeptr->nextnode)
    *tmp++ = nodeptr->argptr;

    /* the terminal NULL */
    *tmp = NULL;

    /* free up local data */
    for (nodeptr = arghead; nodeptr; nodeptr = arghead) {
    arghead = arghead->nextnode;
    free(nodeptr);
    }

    /* return success */
    return(0);
}


/***
*int match(arg, ptr) - [STATIC]
*
*Purpose:
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

static int
match (arg, ptr)
REG4 char *arg;
REG1 char *ptr;
{
    REG2 char *new;
    REG3 int length;
    char *all;
    REG5 struct argnode *first;
    REG6 int gotone = 0;

    while (ptr != arg && *ptr != SLASHCHAR && *ptr != FWDSLASHCHAR
    && *ptr != ':') {
    /* find first slash or ':' before wildcard */
    ptr--;
    }

    if (*ptr == ':' && ptr != arg+1) /* weird name, just add it as is */
    return(add(arg));

    if (*ptr == SLASHCHAR || *ptr == FWDSLASHCHAR
    || *ptr == ':') /* pathname */
    length = ptr - arg + 1; /* length of dir prefix */

    if (new = _find(arg)) { /* get the first file name */
    first = argend;

    do  { /* got a file name */
        if (strcmp(new, ".") && strcmp(new, "..")) {
        if (*ptr != SLASHCHAR && *ptr != ':'
            && *ptr != FWDSLASHCHAR ) {
            /* current directory; don't need path */
            if (!(arg = _strdup(new)) || add(arg))
            return(-1);
        }
        else    /* add full pathname */
            if (!(all=malloc(length+strlen(new)+1))
            || add(strcpy(strncpy(all,arg,length)+length,new)
            - length))
            return(-1);

        gotone++;
        }

    }
    while (new = _find(NULL));  /* get following files */

    if (gotone) {
        sort(first ? first->nextnode : arghead);
        return(0);
    }
    }

    return(add(arg)); /* no match */
}

/***
*int add(arg) - [STATIC]
*
*Purpose:
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

static int
add (arg)
char *arg;
{
    REG1 struct argnode *nodeptr;

    if (!(nodeptr = (struct argnode *)malloc(sizeof(struct argnode))))
    return(-1);

    nodeptr->argptr = arg;
    nodeptr->nextnode = NULL;

    if (arghead)
    argend->nextnode = nodeptr;
    else
    arghead = nodeptr;

    argend = nodeptr;
    return(0);
}


/***
*void sort(first) - [STATIC]
*
*Purpose:
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

static void
sort (first)
REG2 struct argnode *first;
{
    REG1 struct argnode *nodeptr;
    REG3 char *temp;

    if (first) /* something to sort */
    while (nodeptr = first->nextnode) {
        do  {
        if (strcmp(nodeptr->argptr, first->argptr) < 0) {
            temp = first->argptr;
            first->argptr = nodeptr->argptr;
            nodeptr->argptr = temp;
        }
        }
        while (nodeptr = nodeptr->nextnode);

        first = first->nextnode;
    }
}
