//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       cwargv.cpp
//
//  Contents:   argv parsing api
//
//  History:    02-Oct-1997 pberkman    create
//
//--------------------------------------------------------------------------

#include    "global.hxx"

#include    "cwargv.hxx"
#include    "fparse.hxx"

#define SID_FILES               0

typedef struct ARGVSTORAGE_
{
    DWORD       dwsidOption;
    WCHAR       *pwszOption;

    DWORD       dwsidOptionHelp;
    WCHAR       *pwszOptionHelp;

    BOOL        fHiddenCmd;

    BOOL        fSet;

    DWORD       dwValueType;
    union
    {
        WCHAR   *pwszValue;
        WCHAR   *pwszCmdFile;
        DWORD   dwValue;
        BOOL    fValue;
    };

} ARGVSTORAGE;

cWArgv_::cWArgv_(HINSTANCE hInst0, BOOL *pfFailed, BOOL fChkCmdFile)
{
    pwszThisFilename        = NULL; // don't delete!
    pwszUsageWord           = NULL;
    pwszUsageOptionsText    = NULL;
    pwszUsageCmdFileText    = NULL;
    pwszUsageAddText        = NULL;
    pwszOptionParamText     = NULL;
    pwszUsageString         = NULL;
    pwszNonParamArgBlanks   = NULL;
    fShowHiddenArgs         = FALSE;
    fNonHiddenParamArgs     = FALSE;
    dwLongestArg            = 0;
    this->hInst             = hInst0;
    this->fChkCmdF          = fChkCmdFile;
    pArgs                   = new Stack_(NULL); // no sorting!
    if (pArgs == NULL)
    {
        *pfFailed = TRUE;   
    }
    else
    {
        *pfFailed = FALSE;
    }
}

cWArgv_::~cWArgv_(void)
{
    ARGVSTORAGE *pArg;
    DWORD       dwIdx;

    DELETE_OBJECT(pwszUsageWord);
    DELETE_OBJECT(pwszUsageOptionsText);
    DELETE_OBJECT(pwszUsageCmdFileText);
    DELETE_OBJECT(pwszUsageAddText);
    DELETE_OBJECT(pwszOptionParamText);
    DELETE_OBJECT(pwszUsageString);
    DELETE_OBJECT(pwszNonParamArgBlanks);

    dwIdx = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        DELETE_OBJECT(pArg->pwszOption);
        DELETE_OBJECT(pArg->pwszOptionHelp);

        dwIdx++;
    }

    DELETE_OBJECT(pArgs);
}

void cWArgv_::AddUsageText(DWORD dwsidUsageWord, DWORD dwsidUsageOptions, DWORD dwsidUsageCmdFileText,
                           DWORD dwsidUsageAddText, DWORD dwsidOptionParamText)
{
    WCHAR       wszString[MAX_PATH];

    wszString[0] = NULL;
    LoadStringU(this->hInst, dwsidUsageWord, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pwszUsageWord = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return;
        }

        wcscpy(pwszUsageWord, &wszString[0]);
    }

    wszString[0] = NULL;
    LoadStringU(this->hInst, dwsidUsageOptions, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pwszUsageOptionsText = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return;
        }

        wcscpy(pwszUsageOptionsText, &wszString[0]);
    }

    wszString[0] = NULL;
    LoadStringU(this->hInst, dwsidUsageCmdFileText, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pwszUsageCmdFileText = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return;
        }

        wcscpy(pwszUsageCmdFileText, &wszString[0]);
    }

    wszString[0] = NULL;
    LoadStringU(this->hInst, dwsidOptionParamText, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pwszOptionParamText = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return;
        }

        wcscpy(pwszOptionParamText, &wszString[0]);
    }

    wszString[0] = NULL;
    LoadStringU(this->hInst, dwsidUsageAddText, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pwszUsageAddText = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return;
        }

        wcscpy(pwszUsageAddText, &wszString[0]);
    }
}

WCHAR *cWArgv_::GetUsageString(void)
{
    int i;

    if (pwszUsageString)
    {
        return(pwszUsageString);
    }

    if (!(pwszThisFilename))
    {
        return(NULL);
    }

    DWORD   ccLen;

    ccLen = wcslen(pwszThisFilename);

    if (pwszUsageWord)
    {
        ccLen += wcslen(pwszUsageWord);
    }

    if (pwszUsageOptionsText)
    {
        ccLen += wcslen(pwszUsageOptionsText);
    }

    if (fChkCmdF)
    {
        if (pwszUsageCmdFileText)
        {
            ccLen += wcslen(pwszUsageCmdFileText);
        }
    }

    if (pwszUsageAddText)
    {
        ccLen += wcslen(pwszUsageAddText);
    }

    ARGVSTORAGE *pArg;
    DWORD       dwIdx;

    dwIdx = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption != SID_FILES)
        {
            if (pArg->pwszOption)
            {
                if ((pArg->fHiddenCmd) && (!(fShowHiddenArgs)))
                {
                    dwIdx++;
                    continue;
                }

                ccLen += 6;             // 5 spaces + 1 for '-'
                ccLen += dwLongestArg;  // wcslen(pArg->pwszOption);

                if (((fNonHiddenParamArgs) && !(fShowHiddenArgs)) ||
                    (fShowHiddenArgs))
                {
                    if ((pwszNonParamArgBlanks) && (pwszOptionParamText))
                    {
                        ccLen++;    // space
                        ccLen += wcslen(pwszOptionParamText);
                    }
                }

                if (pArg->pwszOptionHelp)
                {
                    ccLen += 2; // : + space
                    ccLen += wcslen(pArg->pwszOptionHelp);
                }

                ccLen += 2; // cr/lf
            }
        }

        dwIdx++;
    }


    ccLen += 10;

    if (!(pwszUsageString = new WCHAR[ccLen + 1]))
    {
        return(NULL);
    }

    swprintf(pwszUsageString, L"%s: %s %s %s %s\r\n",
                            pwszUsageWord,
                            pwszThisFilename,
                            (pwszUsageOptionsText) ? pwszUsageOptionsText : L"",
                            (pwszUsageCmdFileText && fChkCmdF) ? pwszUsageCmdFileText : L"",
                            (pwszUsageAddText) ? pwszUsageAddText : L"");

    dwIdx = pArgs->Count() - 1;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption != SID_FILES)
        {
            if (pArg->pwszOption)
            {
                if ((pArg->fHiddenCmd) && (!(fShowHiddenArgs)))
                {
                    if (dwIdx == 0)
                    {
                        break;
                    }

                    dwIdx--;
                    continue;
                }

                wcscat(pwszUsageString, L"     -");
                wcscat(pwszUsageString, pArg->pwszOption);

                if ((pArg->dwValueType != WARGV_VALUETYPE_BOOL) &&
                    (pwszOptionParamText))
                {
                    wcscat(pwszUsageString, L" ");
                    wcscat(pwszUsageString, pwszOptionParamText);
                }

                if (pArg->pwszOptionHelp)
                {
                    wcscat(pwszUsageString, L": ");

                    for (i = 0; i < (int)(dwLongestArg - wcslen(pArg->pwszOption)); i++)
                    {
                        wcscat(pwszUsageString, L" ");
                    }

                    if ((pArg->dwValueType == WARGV_VALUETYPE_BOOL) &&
                        (((fNonHiddenParamArgs) && !(fShowHiddenArgs)) || (fShowHiddenArgs)) &&
                        (pwszNonParamArgBlanks))
                    {
                        wcscat(pwszUsageString, pwszNonParamArgBlanks);
                    }

                    wcscat(pwszUsageString, pArg->pwszOptionHelp);
                }

                wcscat(pwszUsageString, L"\r\n");
            }
        }

        if (dwIdx == 0)
        {
            break;
        }

        dwIdx--;
    }

    return(pwszUsageString);
}

BOOL cWArgv_::Add2List(DWORD dwsidOption, DWORD dwsidOptionHelp, DWORD dwValueType, void *pvDefaultValue,
                       BOOL fInternalCmd)
{
    if (!(pArgs))
    {
        return(FALSE);
    }

    ARGVSTORAGE *pArg;
    WCHAR       wszString[MAX_PATH];
    DWORD       i;

    if (!(pArg = (ARGVSTORAGE *)pArgs->Add(sizeof(ARGVSTORAGE))))
    {
        return(FALSE);
    }

    memset(pArg, 0x00, sizeof(ARGVSTORAGE));

    pArg->dwValueType   = dwValueType;

    if (pArg->dwValueType != WARGV_VALUETYPE_BOOL)
    {
        if (!(pwszNonParamArgBlanks))
        {
            if (pwszOptionParamText)
            {
                if (pwszNonParamArgBlanks = new WCHAR[wcslen(pwszOptionParamText) + 2])
                {
                    for (i = 0; i <= (DWORD)wcslen(pwszOptionParamText); i++)
                    {
                        pwszNonParamArgBlanks[i] = L' ';
                    }
                    pwszNonParamArgBlanks[i] = NULL;
                }
            }
        }

        fNonHiddenParamArgs = TRUE;
    }

    pArg->fHiddenCmd    = fInternalCmd;

    pArg->dwsidOption   = dwsidOption;
    wszString[0] = NULL;
    LoadStringU(this->hInst, dwsidOption, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pArg->pwszOption = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return(FALSE);
        }

        wcscpy(pArg->pwszOption, &wszString[0]);

        if ((DWORD)wcslen(&wszString[0]) > dwLongestArg)
        {
            dwLongestArg = wcslen(&wszString[0]);
        }
    }


    pArg->dwsidOptionHelp   = dwsidOptionHelp;
    wszString[0]            = NULL;
    LoadStringU(this->hInst, dwsidOptionHelp, &wszString[0], MAX_PATH);

    if (wszString[0])
    {
        if (!(pArg->pwszOptionHelp = new WCHAR[wcslen(&wszString[0]) + 1]))
        {
            return(FALSE);
        }

        wcscpy(pArg->pwszOptionHelp, &wszString[0]);
    }

    if (pvDefaultValue)
    {
        switch (dwValueType)
        {
            case WARGV_VALUETYPE_BOOL:      pArg->fValue    = (BOOL)((DWORD_PTR)pvDefaultValue);     break;
            case WARGV_VALUETYPE_DWORDD:
            case WARGV_VALUETYPE_DWORDH:    pArg->dwValue   = (DWORD)((DWORD_PTR)pvDefaultValue);    break;
            case WARGV_VALUETYPE_WCHAR:     pArg->pwszValue = (WCHAR *)pvDefaultValue;  break;
            default:
                return(FALSE);
        }
    }

    return(TRUE);
}

BOOL cWArgv_::Fill(int argc, WCHAR **wargv)
{
    if (!(pArgs))
    {
        return(FALSE);
    }

    if (!(pwszThisFilename))
    {
        if (pwszThisFilename = wcsrchr(&wargv[0][0], L'\\'))
        {
            pwszThisFilename++;
        }
        else
        {
            pwszThisFilename    = &wargv[0][0];
        }
    }

    int     i;

    for (i = 1; i < argc; ++i)
    {
        switch (wargv[i][0])
        {
            case L'-':
            case L'/':
                if (wargv[i][1] == L'~')
                {
                    fShowHiddenArgs     = TRUE;
                    return(FALSE);
                }

                i += this->ProcessArg(argc - i, &wargv[i]);
                break;

            case L'@':
                this->ProcessCommandFile(&wargv[i][1]);
                break;

            default:
                this->AddFile(&wargv[i][0]);
                break;
        }
    }

    return(TRUE);
}

int cWArgv_::ProcessArg(int argc, WCHAR **wargv)
{
    ARGVSTORAGE *pArg;
    DWORD       dwIdx;
    DWORD       ccOption;
    WCHAR       *pwszArg;
    int         iRet;

    iRet    = 0;

    pwszArg             = &wargv[0][1]; // skip over - or /

    dwIdx = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->pwszOption)
        {
            ccOption = (DWORD)wcslen(pArg->pwszOption);

            if (_memicmp(pArg->pwszOption, pwszArg, ccOption * sizeof(WCHAR)) == 0)
            {
                pArg->fSet = TRUE;

                switch (pArg->dwValueType)
                {
                    case WARGV_VALUETYPE_BOOL:
                            pArg->fValue = TRUE;

                            return(iRet);

                    case WARGV_VALUETYPE_DWORDH:
                    case WARGV_VALUETYPE_DWORDD:
                            if (!(pwszArg[ccOption]))
                            {
                                pwszArg = &wargv[1][0];
                                iRet++;
                            }
                            else
                            {
                                pwszArg = &wargv[0][ccOption + 1];
                            }

                            if (pArg->dwValueType == WARGV_VALUETYPE_DWORDH)
                            {
                                pArg->dwValue = (DWORD)wcstoul(pwszArg, NULL, 16);
                            }
                            else
                            {
                                pArg->dwValue = (DWORD)wcstoul(pwszArg, NULL, 10);
                            }

                            return(iRet);

                    case WARGV_VALUETYPE_WCHAR:
                            if (!(pwszArg[ccOption]))
                            {
                                pArg->pwszValue = &wargv[1][0];
                                iRet++;
                            }
                            else
                            {
                                pArg->pwszValue = &wargv[0][ccOption];
                            }

                            return(iRet);

                    default:
                            return(iRet);
                }
            }
        }

        dwIdx++;
    }

    return(iRet);
}

BOOL cWArgv_::IsSet(DWORD dwsidOption)
{
    ARGVSTORAGE *pArg;
    DWORD   dwIdx;

    dwIdx = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption == dwsidOption)
        {
            return((pArg->fSet) ? TRUE : FALSE);
        }

        dwIdx++;
    }

    return(FALSE);
}

void *cWArgv_::GetValue(DWORD dwsidOption)
{
    ARGVSTORAGE *pArg;
    DWORD   dwIdx;

    dwIdx = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption == dwsidOption)
        {
            switch (pArg->dwValueType)
            {
                case WARGV_VALUETYPE_BOOL:      return((void *)(UINT_PTR)pArg->fValue);
                case WARGV_VALUETYPE_DWORDD:
                case WARGV_VALUETYPE_DWORDH:    return((void *)(UINT_PTR)pArg->dwValue);
                case WARGV_VALUETYPE_WCHAR:     return((void *)pArg->pwszValue);
                default:
                    return(NULL);
            }
        }

        dwIdx++;
    }

    return(NULL);
}

WCHAR *cWArgv_::GetOptionHelp(DWORD dwsidOption)
{
    if (!(pArgs))
    {
        return(NULL);
    }

    ARGVSTORAGE *pArg;
    DWORD       dwIdx;

    dwIdx   = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption == dwsidOption)
        {
            return(pArg->pwszOptionHelp);
        }

        dwIdx++;
    }

    return(NULL);
}

WCHAR *cWArgv_::GetOption(DWORD dwsidOption)
{
    if (!(pArgs))
    {
        return(NULL);
    }

    ARGVSTORAGE *pArg;
    DWORD       dwIdx;

    dwIdx   = 0;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption == dwsidOption)
        {
            return(pArg->pwszOption);
        }

        dwIdx++;
    }

    return(NULL);
}

WCHAR *cWArgv_::GetFileName(DWORD *pdwidxLast)
{
    if (!(pArgs))
    {
        return(NULL);
    }

    ARGVSTORAGE *pArg;
    DWORD       dwIdx;
    DWORD       dwFIdx;

    dwIdx   = 0;
    dwFIdx  = 1;

    while (pArg = (ARGVSTORAGE *)pArgs->Get(dwIdx))
    {
        if (pArg->dwsidOption == SID_FILES)
        {
            if (!(pdwidxLast) || (dwFIdx > *pdwidxLast))
            {
                return(pArg->pwszValue);
            }

            dwFIdx++;
        }

        dwIdx++;
    }

    return(NULL);
}

BOOL cWArgv_::AddFile(WCHAR *pwszFile)
{
    ARGVSTORAGE *pArg;

    if (!(pArg = (ARGVSTORAGE *)pArgs->Add(sizeof(ARGVSTORAGE))))
    {
        return(FALSE);
    }

    memset(pArg, 0x00, sizeof(ARGVSTORAGE));

    pArg->dwsidOption   = SID_FILES;
    pArg->dwValueType   = WARGV_VALUETYPE_WCHAR;
    pArg->pwszValue     = pwszFile;

    this->StripQuotes(pArg->pwszValue);

    return(TRUE);
}

BOOL cWArgv_::ProcessCommandFile(WCHAR *pwszFile)
{
    if (!(this->fChkCmdF))
    {
        return(FALSE);
    }

    HANDLE      hFile;
    BOOL        fFailed = FALSE;
    fParse_     fp(pwszFile, &fFailed);
    WCHAR       *pwsz;

    if (fFailed)
    {
        return FALSE;
    }

    fp.Reset();

    while (fp.GetNextLine())
    {
        fp.EOLRemove();

        if ((fp.GetCurrentLine()) && (fp.GetCurrentLine()[0]))
        {
            pwsz = fp.GetCurrentLine();
            this->Fill(1, &pwsz);
        }
    }

    return(TRUE);
}

void cWArgv_::StripQuotes(WCHAR *pwszIn)
{
    DWORD   dwSrc;
    DWORD   dwDst;
    DWORD   dwLen;

    dwSrc = 0;
    dwDst = 0;
    dwLen = wcslen(pwszIn);

    while (dwSrc < dwLen)
    {
        if (pwszIn[dwSrc] != L'\"')
        {
            pwszIn[dwDst] = pwszIn[dwSrc];
            dwDst++;
        }
        dwSrc++;
    }

    pwszIn[dwDst] = NULL;
}
