view src/dmargs.c @ 2208:90ec1ec89c56

Revamp the palette handling in lib64gfx somewhat, add helper functions to lib64util for handling external palette file options and add support for specifying one of the "internal" palettes or external (.act) palette file to gfxconv and 64vw.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 Jun 2019 05:01:12 +0300
parents bcdea45a14cb
children c801995cbb13
line wrap: on
line source

/*
 * Simple commandline argument processing
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2002-2018 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
/// @file
/// @brief Simple commandline argument processing functions
#include "dmargs.h"


/**
 * Parse and optionally handle the given long or short option argument.
 * @param currArg current argument string
 * @param argIndex pointer to index of current argument in argv[]
 * @param argc number of arguments
 * @param argv argument string array
 * @param opts options list array
 * @param nopts number of elements in options list array
 * @param handle_option function pointer to callback that handles option arguments
 * @param doProcess if TRUE, actually handle the argument, aka call the handle_option() function. if FALSE, only validity of options are checked.
 * @param isLong TRUE if the option is a --long-format one
 */
static BOOL dmArgsProcessOpt(
    char *currArg, int *argIndex,
    int argc, char *argv[],
    const DMOptArg opts[], int nopts,
    BOOL (*handle_option)(int id, char *, char *),
    BOOL doProcess, BOOL isLong)
{
    const DMOptArg *opt = NULL;
    char *optArg = NULL;
    int optIndex;

    for (optIndex = 0; optIndex < nopts; optIndex++)
    {
        const DMOptArg *node = &opts[optIndex];
        if (isLong && node->o_long != NULL)
        {
            if (strcmp(currArg, node->o_long) == 0)
            {
                opt = node;
                optArg = NULL;
                break;
            }

            size_t len = strlen(node->o_long);
            if (strncmp(currArg, node->o_long, len) == 0 &&
                currArg[len] == '=')
            {
                opt = node;
                optArg = (&currArg[len+1] != 0) ? &currArg[len+1] : NULL;
                break;
            }
        }
        else
        if (!isLong && node->o_short != 0)
        {
            if (*currArg == node->o_short)
            {
                opt = node;
                optArg = (currArg[1] != 0) ? &currArg[1] : NULL;
            }
        }
    }

    if (opt != NULL)
    {
        // Check for the possible option argument
        if ((opt->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL)
        {
            if (*argIndex < argc)
            {
                (*argIndex)++;
                optArg = argv[*argIndex];
            }

            if (optArg == NULL)
            {
                dmErrorMsg("Option '%s%s' requires an argument.\n",
                    isLong ? "--" : "-",
                    currArg);
                return FALSE;
            }
        }

        // Option was given succesfully, try to process it
        if (doProcess && !handle_option(opt->id, optArg, currArg))
            return FALSE;
    }
    else
    {
        dmErrorMsg("Unknown %s option '%s%s'\n",
            isLong ? "long" : "short",
            isLong ? "--" : "-",
            currArg);

        return FALSE;
    }

    return TRUE;
}


/**
 * Process given array of commandline arguments, handling short
 * and long options by calling the respective callback functions.
 *
 * @param argc number of arguments
 * @param argv argument list
 * @param opts supported option list array
 * @param nopts number of elements in the option list array
 * @param handle_option callback function
 * @param handle_other callback function
 * @param flags processing flags
 * @return return TRUE if all is well
 */
BOOL dmArgsProcess(int argc, char *argv[],
     const DMOptArg *opts, const int nopts,
     BOOL(*handle_option)(int id, char *, char *),
     BOOL(*handle_other)(char *), const int flags)
{
    int argIndex, handleFlags = flags & OPTH_ONLY_MASK;
    BOOL optionsOK = TRUE, endOfOptions = FALSE;

    for (argIndex = 1; argIndex < argc; argIndex++)
    {
        char *str = argv[argIndex];
        if (*str == '-' && !endOfOptions)
        {
            // Should we process options?
            BOOL doProcess = (handleFlags & OPTH_ONLY_OPTS) || handleFlags == 0;
            BOOL isLong;

            str++;
            if (*str == '-')
            {
                // Check for "--", which ends the options-list
                str++;
                if (*str == 0)
                {
                    endOfOptions = TRUE;
                    continue;
                }

                // We have a long option
                isLong = TRUE;
            }
            else
                isLong = FALSE;

            if (!dmArgsProcessOpt(str, &argIndex, argc, argv,
                opts, nopts, handle_option, doProcess, isLong))
                optionsOK = FALSE;
        }
        else
        if (handleFlags == OPTH_ONLY_OTHER || handleFlags == 0)
        {
            // Was not option argument
            if (handle_other == NULL ||
                (handle_other != NULL && !handle_other(str)))
            {
                dmErrorMsg("Invalid argument '%s'\n", str);
                optionsOK = FALSE;
            }
        }

        // Check if we bail out on invalid argument
        if (!optionsOK && (flags & OPTH_BAILOUT))
            return FALSE;
    }

    return optionsOK;
}


static void dmPad(FILE *outFile, int count)
{
    while (count--)
        fputc(' ', outFile);
}


static void dmPrintWrap(FILE *fh, const char *str, int spad, int rpad, int width)
{
    size_t pos = 0;
    BOOL first = TRUE;

    while (str[pos])
    {
        // Pre-pad line
        int linelen = first ? spad : rpad;
        dmPad(fh, first ? 0 : rpad);
        first = FALSE;

        // Skip whitespace at line start
        while (isspace(str[pos]) || str[pos] == '\n') pos++;

        // Handle each word
        while (str[pos] && str[pos] != '\n')
        {
            size_t next;
            int wlen;
            for (wlen = 0, next = pos; str[next] && !isspace(str[next]) && str[next] != '\n'; next++, wlen++);
//            fprintf(stdout, "X '%c', %d .. linelen=%d/%d, wlen=%d\n", str[pos], pos, linelen, width, wlen);
            if (linelen + wlen < width)
            {
                for (;pos < next; pos++, linelen++)
                    fputc(str[pos], fh);

                if (str[next] == '\n' || str[next] == 0)
                {
                    fprintf(fh, "\n");
                    break;
                }
                else
                {
                    fputc(str[pos], fh);
                    pos++;
                    linelen++;
                }
            }
            else
            {
                fprintf(fh, "\n");
                break;
            }
        }
    }
}


/**
 * Print help for commandline arguments/options
 * @param fh stdio file handle to output to
 * @param opts options list array
 * @param nopts number of elements in options list array
 * @param flags flags (currently unused)
 */
void dmArgsPrintHelp(FILE *fh,
    const DMOptArg *opts, const int nopts,
    const int flags)
{
    int index;
    (void) flags;

    // Print out option list
    for (index = 0; index < nopts; index++)
    {
        const DMOptArg *opt = &opts[index];
        char tmpStr[128];

        // Print short option
        if (opt->o_short != 0)
        {
            snprintf(tmpStr, sizeof(tmpStr),
                "-%c,", opt->o_short);
        }
        else
            tmpStr[0] = 0;

        fprintf(fh, " %-5s", tmpStr);

        // Print long option
        if (opt->o_long != NULL)
        {
            snprintf(tmpStr, sizeof(tmpStr), "--%s%s",
                opt->o_long,
                (opt->flags & OPT_ARGREQ) ? "=ARG" : "");
        }
        else
            tmpStr[0] = 0;

        fprintf(fh, "%-18s", tmpStr);

        dmPrintWrap(fh, opt->desc, 24, 24, 79);
    }
}