/************************************************************************/
/*                                                                      */
/* RCPP - Resource Compiler Pre-Processor for NT system                 */
/*                                                                      */
/* GETFLAGS.C - Parse Command Line Flags                                */
/*                                                                      */
/* 27-Nov-90 w-BrianM  Update for NT from PM SDK RCPP                   */
/*                                                                      */
/************************************************************************/
#include "rc.h"


/************************************************************************/
/* Define function specific macros and global vars                      */
/************************************************************************/
static WCHAR     *ErrString;   /* Store string pointer in case of error */


/************************************************************************/
/* Local Function Prototypes                                            */
/************************************************************************/
int getnumber   (WCHAR *);
int isita       (WCHAR *, WCHAR);
void substr     (struct cmdtab *, WCHAR *, int);
int tailmatch   (WCHAR *, WCHAR *);



/************************************************************************
 *      crack_cmd(table, string, func, dup)
 *              set flags determined by the string based on table.
 *              func will get the next word.
 *              if dup is set, any strings stored away will get pstrduped
 * see getflags.h for specific matching and setting operators
 *
 *  for flags which take parameters, a 'char' following the flag where 'char' is
 *  '#' : says the parameter string may be separated from the option.
 *              ie, "-M#" accepts "-Mabc" and "-M abc"
 *  '*' : says the parameter must be concatenated with the flag
 *              ie, "-A*" accepts only "-Axyz" not "-A xyz"
 *  if neither is specified a space is required between parameter and flag
 *              ie, "-o" accepts only "-o file" and not "-ofile"
 *
 * Modified by:         Dave Weil                       D001
 *                              recognize '-' and '/' as equivalent on MSDOS
 *
 ************************************************************************/

int
crack_cmd(
    struct cmdtab *tab,
    WCHAR *string,
    WCHAR *(*next)(void),
    int _dup
    )
{
    register WCHAR      *format, *str;

    if (!string) {
        return(0);
    }

    ErrString = string;
    for (; tab->type; tab++)            /* for each format */ {
        format = tab->format;
        str = string;
        for (; ; )                              /* scan the string */
            switch (*format) {
                /*  optional space between flag and parameter  */
                case L'#':
                    if ( !*str ) {
                        substr(tab, (*next)(), _dup);
                    } else {
                        substr(tab, str, _dup);
                    }
                    return(tab->retval);
                    break;

                /*  no space allowed between flag and parameter  */
                case L'*':
                    if (*str && tailmatch(format, str))
                        substr(tab, str, _dup);
                    else
                        goto notmatch;
                    return(tab->retval);
                    break;

                /*  space required between flag and parameter  */
                case 0:
                    if (*str) {                         /*  str left, no good  */
                        goto notmatch;
                    } else if (tab->type & TAKESARG) {  /*  if it takes an arg  */
                        substr(tab, (*next)(), _dup);
                    } else {                            /*  doesn't want an arg  */
                        substr(tab, (WCHAR *)0, _dup);
                    }
                    return(tab->retval);
                    break;
                case L'-':
                    if (L'-' == *str) {
                        str++;
                        format++;
                        continue;
                    } else {
                        goto notmatch;
                    }

                default:
                    if (*format++ == *str++)
                        continue;
                    goto notmatch;
            }
notmatch:
        ;
    }
    return(0);
}


/************************************************************************/
/* set the appropriate flag(s).  called only when we know we have a match */
/************************************************************************/
void
substr(
    struct cmdtab *tab,
    register WCHAR *str,
    int _dup
    )
{
    register struct subtab *q;
    LIST * list;
    WCHAR        *string = str;

    switch (tab->type) {
        case FLAG:
            *(int *)(tab->flag) = 1;
            return;
        case UNFLAG:
            *(int *)(tab->flag) = 0;
            return;
        case NOVSTR:
            if (*(WCHAR **)(tab->flag)) {
                /* before we print it out in the error message get rid of the
                 * arg specifier (e.g. #) at the end of the format.
                 */
//                string = _wcsdup(tab->format);
//                string[wcslen(string)-1] = L'\0';
//
// message 1046 doesn't exist and don't know what it should be
//            Msg_Temp = GET_MSG(1046);
//            SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, string, *(WCHAR **)(tab->flag), str);
                Msg_Temp = GET_MSG(1000);
                fatal(1000);
                return;
            }
            /* fall through */
        case STRING:
            *(WCHAR **)(tab->flag) = (_dup ? _wcsdup(str) : str);
            return;
        case NUMBER:
            *(int *)(tab->flag) = getnumber (str);
            return;
        case PSHSTR:
            list = (LIST * )(tab->flag);
            if (list->li_top > 0)
                list->li_defns[--list->li_top] = (_dup ? _wcsdup(str) : str);
            else {
                Msg_Temp = GET_MSG(1047);
                SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, tab->format, str);
                fatal(1047);
            }
            return;
        case SUBSTR:
            for ( ; *str; ++str) {  /*  walk the substring  */
                for (q = (struct subtab *)tab->flag; q->letter; q++) {
                    /*
                                    **  for every member in the table
                                    */
                    if (*str == (WCHAR)q->letter)
                        switch (q->type) {
                        case FLAG:
                            *(q->flag) = 1;
                            goto got_letter;
                        case UNFLAG:
                            *(q->flag) = 0;
                            goto got_letter;
                        default:
                            goto got_letter;
                        }
                }
got_letter:
                if (!q->letter) {
                    Msg_Temp = GET_MSG(1048);
                    SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, *str, ErrString);
                    fatal(1048);
                }
            }
            return;
        default:
            return;
    }
}


/************************************************************************/
/* Parse the string and return a number 0 <= x < 0xffff (64K)           */
/************************************************************************/
int
getnumber (
    WCHAR *str
    )
{
    long        i = 0;
    WCHAR       *ptr = str;

    for (; iswspace(*ptr); ptr++)
        ;
    if (!iswdigit(*ptr) || (((i = wcstol(ptr, NULL, 10)) >= 65535) ||  i < 0)) {
        Msg_Temp = GET_MSG(1049);
        SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, str);
        fatal(1049);            /* invalid numerical argument, 'str' */
    }
    return ((int) i);
}


/************************************************************************/
/*  is the letter in the string?                                        */
/************************************************************************/
int
isita (
    register WCHAR *str,
    register WCHAR let
    )
{
    if (str)
        while (*str)
            if (*str++ == let)
                return(1);
    return(0);
}


/************************************************************************/
/* compare a tail format (as in *.c) with a string.  if there is no     */
/* tail, anything matches.  (null strings are detected elsewhere)       */
/* the current implementation only allows one wild card                 */
/************************************************************************/
int
tailmatch (
    WCHAR *format,
    WCHAR *str
    )
{
    register WCHAR      *f = format;
    register WCHAR      *s = str;

    if (f[1] == 0)      /*  wild card is the last thing in the format, it matches */
        return(1);
    while (f[1])                /*  find char in front of null in format  */
        f++;
    while (s[1])                /*  find char in front of null in string to check  */
        s++;
    while (*s == *f) {  /*  check chars walking towards front */
        s--;
        f--;
    }
    /*
**  if we're back at the beginning of the format
**  and
**  the string is either at the beginning or somewhere inside
**  then we have a match.
**
**  ex format == "*.c", str == "file.c"
**      at this point *f = '*' and *s == 'e', since we've kicked out of the above
**  loop. since f==format and s>=str this is a match.
**  but if format == "*.c" and str == "file.asm" then
**  *f == 'c' and *s = 'm', f != format and no match.
*/
    return((f == format) && (s >= str));
}
