RTDECL(int) RTGetOptArgvFromString(char ***ppapszArgv, int *pcArgs, const char *pszCmdLine, const char *pszSeparators)
{
    /*
     * Some input validation.
     */
    AssertPtr(pszCmdLine);
    AssertPtr(pcArgs);
    AssertPtr(ppapszArgv);
    if (!pszSeparators)
        pszSeparators = " \t\n\r";
    else
        AssertPtr(pszSeparators);
    size_t const cchSeparators = strlen(pszSeparators);
    AssertReturn(cchSeparators > 0, VERR_INVALID_PARAMETER);

    /*
     * Parse the command line and chop off it into argv individual argv strings.
     */
    int         rc        = VINF_SUCCESS;
    const char *pszSrc    = pszCmdLine;
    char       *pszDup    = (char *)RTMemAlloc(strlen(pszSrc) + 1);
    char       *pszDst    = pszDup;
    if (!pszDup)
        return VERR_NO_STR_MEMORY;
    char      **papszArgs = NULL;
    unsigned    iArg      = 0;
    while (*pszSrc)
    {
        /* Skip stuff */
        rc = rtGetOptSkipDelimiters(&pszSrc, pszSeparators, cchSeparators);
        if (RT_FAILURE(rc))
            break;
        if (!*pszSrc)
            break;

        /* Start a new entry. */
        if ((iArg % 32) == 0)
        {
            void *pvNew = RTMemRealloc(papszArgs, (iArg + 33) * sizeof(char *));
            if (!pvNew)
            {
                rc = VERR_NO_MEMORY;
                break;
            }
            papszArgs = (char **)pvNew;
        }
        papszArgs[iArg++] = pszDst;

        /* Parse and copy the string over. */
        RTUNICP CpQuote = 0;
        RTUNICP Cp;
        for (;;)
        {
            rc = RTStrGetCpEx(&pszSrc, &Cp);
            if (RT_FAILURE(rc) || !Cp)
                break;
            if (!CpQuote)
            {
                if (Cp == '"' || Cp == '\'')
                    CpQuote = Cp;
                else if (rtGetOptIsCpInSet(Cp, pszSeparators, cchSeparators))
                    break;
                else
                    pszDst = RTStrPutCp(pszDst, Cp);
            }
            else if (CpQuote != Cp)
                pszDst = RTStrPutCp(pszDst, Cp);
            else
                CpQuote = 0;
        }
        *pszDst++ = '\0';
        if (RT_FAILURE(rc) || !Cp)
            break;
    }

    if (RT_FAILURE(rc))
    {
        RTMemFree(pszDup);
        RTMemFree(papszArgs);
        return rc;
    }

    /*
     * Terminate the array.
     * Check for empty string to make sure we've got an array.
     */
    if (iArg == 0)
    {
        RTMemFree(pszDup);
        papszArgs = (char **)RTMemAlloc(1 * sizeof(char *));
        if (!papszArgs)
            return VERR_NO_MEMORY;
    }
    papszArgs[iArg] = NULL;

    *pcArgs     = iArg;
    *ppapszArgv = papszArgs;
    return VINF_SUCCESS;
}
Example #2
0
RTDECL(int) RTGetOptArgvFromString(char ***ppapszArgv, int *pcArgs, const char *pszCmdLine,
                                   uint32_t fFlags, const char *pszSeparators)
{
    /*
     * Some input validation.
     */
    AssertPtr(pszCmdLine);
    AssertPtr(pcArgs);
    AssertPtr(ppapszArgv);
    AssertReturn(   fFlags == RTGETOPTARGV_CNV_QUOTE_BOURNE_SH
                 || fFlags == RTGETOPTARGV_CNV_QUOTE_MS_CRT, VERR_INVALID_FLAGS);
    if (!pszSeparators)
        pszSeparators = " \t\n\r";
    else
        AssertPtr(pszSeparators);
    size_t const cchSeparators = strlen(pszSeparators);
    AssertReturn(cchSeparators > 0, VERR_INVALID_PARAMETER);

    /*
     * Parse the command line and chop off it into argv individual argv strings.
     */
    int         rc        = VINF_SUCCESS;
    const char *pszSrc    = pszCmdLine;
    char       *pszDup    = (char *)RTMemAlloc(strlen(pszSrc) + 1);
    char       *pszDst    = pszDup;
    if (!pszDup)
        return VERR_NO_STR_MEMORY;
    char      **papszArgs = NULL;
    unsigned    iArg      = 0;
    while (*pszSrc)
    {
        /* Skip stuff */
        rc = rtGetOptSkipDelimiters(&pszSrc, pszSeparators, cchSeparators);
        if (RT_FAILURE(rc))
            break;
        if (!*pszSrc)
            break;

        /* Start a new entry. */
        if ((iArg % 32) == 0)
        {
            void *pvNew = RTMemRealloc(papszArgs, (iArg + 33) * sizeof(char *));
            if (!pvNew)
            {
                rc = VERR_NO_MEMORY;
                break;
            }
            papszArgs = (char **)pvNew;
        }
        papszArgs[iArg++] = pszDst;

        /*
         * Parse and copy the string over.
         */
        RTUNICP Cp;
        if ((fFlags & RTGETOPTARGV_CNV_QUOTE_MASK) == RTGETOPTARGV_CNV_QUOTE_BOURNE_SH)
        {
            /*
             * Bourne shell style.
             */
            RTUNICP CpQuote = 0;
            for (;;)
            {
                rc = RTStrGetCpEx(&pszSrc, &Cp);
                if (RT_FAILURE(rc) || !Cp)
                    break;
                if (!CpQuote)
                {
                    if (Cp == '"' || Cp == '\'')
                        CpQuote = Cp;
                    else if (rtGetOptIsCpInSet(Cp, pszSeparators, cchSeparators))
                        break;
                    else if (Cp != '\\')
                        pszDst = RTStrPutCp(pszDst, Cp);
                    else
                    {
                        /* escaped char */
                        rc = RTStrGetCpEx(&pszSrc, &Cp);
                        if (RT_FAILURE(rc) || !Cp)
                            break;
                        pszDst = RTStrPutCp(pszDst, Cp);
                    }
                }
                else if (CpQuote != Cp)
                {
                    if (Cp != '\\' || CpQuote == '\'')
                        pszDst = RTStrPutCp(pszDst, Cp);
                    else
                    {
                        /* escaped char */
                        rc = RTStrGetCpEx(&pszSrc, &Cp);
                        if (RT_FAILURE(rc) || !Cp)
                            break;
                        pszDst = RTStrPutCp(pszDst, Cp);
                    }
                }
                else
                    CpQuote = 0;
            }
        }
        else
        {
            /*
             * Microsoft CRT style.
             */
            Assert((fFlags & RTGETOPTARGV_CNV_QUOTE_MASK) == RTGETOPTARGV_CNV_QUOTE_MS_CRT);
            bool fInQuote = false;
            for (;;)
            {
                rc = RTStrGetCpEx(&pszSrc, &Cp);
                if (RT_FAILURE(rc) || !Cp)
                    break;
                if (Cp == '"')
                    fInQuote = !fInQuote;
                else if (!fInQuote && rtGetOptIsCpInSet(Cp, pszSeparators, cchSeparators))
                    break;
                else if (Cp != '\\')
                    pszDst = RTStrPutCp(pszDst, Cp);
                else
                {
                    /* A backslash sequence is only relevant if followed by
                       a double quote, then it will work like an escape char. */
                    size_t cQuotes = 1;
                    while (*pszSrc == '\\')
                    {
                        cQuotes++;
                        pszSrc++;
                    }
                    if (*pszSrc != '"')
                        /* Not an escape sequence.  */
                        while (cQuotes-- > 0)
                            pszDst = RTStrPutCp(pszDst, '\\');
                    else
                    {
                        /* Escape sequence.  Output half of the slashes.  If odd
                           number, output the escaped double quote . */
                        while (cQuotes >= 2)
                        {
                            pszDst = RTStrPutCp(pszDst, '\\');
                            cQuotes -= 2;
                        }
                        if (!cQuotes)
                            fInQuote = !fInQuote;
                        else
                            pszDst = RTStrPutCp(pszDst, '"');
                        pszSrc++;
                    }
                }
            }
        }

        *pszDst++ = '\0';
        if (RT_FAILURE(rc) || !Cp)
            break;
    }

    if (RT_FAILURE(rc))
    {
        RTMemFree(pszDup);
        RTMemFree(papszArgs);
        return rc;
    }

    /*
     * Terminate the array.
     * Check for empty string to make sure we've got an array.
     */
    if (iArg == 0)
    {
        RTMemFree(pszDup);
        papszArgs = (char **)RTMemAlloc(1 * sizeof(char *));
        if (!papszArgs)
            return VERR_NO_MEMORY;
    }
    papszArgs[iArg] = NULL;

    *pcArgs     = iArg;
    *ppapszArgv = papszArgs;
    return VINF_SUCCESS;
}