//
// Created by xajhu on 2021/8/3 0003.
//
#include <string.h>
#include <stdlib.h>
#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L
#include <openssl/crypto.h>
#else
#include <openssl/opensslv.h>
#endif

#include "args/argtable3.h"
#include "cmdline.h"
#include "user_errno.h"
#include "task_manager.h"
#include "init.h"
#include "crypto.h"
#include "misc.h"
#include "config.h"
#include "s2j/s2j.h"
#include "msg_queue.h"

#define REG_ICASE (ARG_REX_ICASE)

typedef void (*HelpCmdCb)(void *, const char *, void *);
typedef int (*ProcessCmdCb)(void **, const char *, void *);

typedef struct __ARG_TBL_INFO {
    void        *argTbl;
    int          nArgItem;
    int         *pErrCode;
    ProcessCmdCb pCmd;
    HelpCmdCb    pHelp;
    const char  *pDesc;
} ARG_TBL_INFO, *PARG_TBL_INFO;

static PARG_TBL_INFO g_pArgTbl       = NULL;
static unsigned int  g_nArgTbl       = 0;
static const char   *g_toolsKey[][2] = {
      {"cfg_all", "get all configuration information"},
      {"mem_info", "show the memory alloc information"},
      {"sys_info", "show the system and hardware information"},
};

static int get_reg_cmd(void *pTbl[]) {
    return ((arg_rex_t *)pTbl[0])->count;
}

static void *get_arg_err(void *pTbl[], PARG_TBL_INFO pInfo) {
    return pTbl[pInfo->nArgItem - 1];
}

static arg_lit_t *get_help_cmd(void *pTbl[], PARG_TBL_INFO pInfo) {
    return pTbl[pInfo->nArgItem - 2];
}

static void show_help(const char *pAppName) {
    int i;
    for (i = 0; i < g_nArgTbl; i++) {
        if (i == 0) {
            printf("Usage: \033[1;33m%s", pAppName);
        } else {
            printf("       \033[1;33m%s", pAppName);
        }
        arg_print_syntax(stdout, g_pArgTbl[i].argTbl, "\n");
    }

    printf("\033[0m\nThe Intrusion Detection Systems agent application\n");
    printf("\nMandatory arguments to long options are mandatory for short options too.\n");
    arg_print_glossary(stdout, g_pArgTbl[i - 1].argTbl, "      \033[1;32m%-30s %s\033[0m\n");
    printf("\nSupport subcommand: \n");

    for (i = 0; i < g_nArgTbl - 1; i++) {
        if (g_pArgTbl[i].pDesc) {
            printf("%s", g_pArgTbl[i].pDesc);
            arg_print_glossary(stdout, g_pArgTbl[i].argTbl, "      %-30s %s\n");
        }
    }

    printf("\n\nTry '\033[1;31m%s --help\033[0m' for more information.\n", pAppName);
}

static void cmdn_help(void *pTbl, const char *pName, void *pErr) {
    if (pErr != NULL) {
        printf("\033[1;31m");
        arg_print_errors(stdout, pErr, "");
        printf("\033[0m\n");
    }

    printf("usage : %s", pName);
    arg_print_syntax(stdout, pTbl, "\n");
    printf("\nMandatory arguments to long options are mandatory for short options too.\n");
    arg_print_glossary(stdout, pTbl, "%-30s %s\n");
}

static void cmd2_help(void *pTbl, const char *pName, void *pErr) {
    int i;

    cmdn_help(pTbl, pName, pErr);

    printf("\n\nSupport keys:\n");
    printf("  \033[1;4;35mConfiguration\033[0m:\n");

    for (i = 0; i < ARRAY_SIZE(g_toolsKey); i++) {
        printf("    \033[4m%s\033[0m:\t %s\n", g_toolsKey[i][0], g_toolsKey[i][1]);
    }
}

static int on_cmd1(void *pTbl[], const char *pName, void *pInfo) {
    int           ret;
    PARG_TBL_INFO pArg     = (PARG_TBL_INFO)pInfo;
    const char   *pCfgFile = ((arg_file_t *)pTbl[1])->filename[0];
    const char   *pCfgDir  = ((arg_file_t *)pTbl[2])->filename[0];
    const char   *pKey     = ((arg_str_t *)pTbl[3])->sval[0];
    int           help     = get_help_cmd(pTbl, pArg)->count;

    if (pArg->pHelp && help > 0) {
        pArg->pHelp(pArg->argTbl, pName, NULL);
        return ERR_SUCCESS;
    }

    if ((ret = user_init(pCfgFile, pCfgDir, pKey, 0)) != ERR_SUCCESS) {
        printf("System init error: %d\n", ret);
        return ret;
    }

    return ret;
}

static cJSON *s2j_mq_msg(void *pObj) {
    PMQ_CMD_MSG pMsg = (PMQ_CMD_MSG)pObj;
    s2j_create_json_obj(pJsonObj);

    s2j_json_set_basic_element(pJsonObj, pMsg, string, cmd);
    s2j_json_set_basic_element(pJsonObj, pMsg, string, key);
    s2j_json_set_basic_element(pJsonObj, pMsg, string, value);

    return pJsonObj;
}

static int on_cmd2(void *pTbl[], const char *pName, void *pInfo) {
    int           ret, i;
    PARG_TBL_INFO pArg = (PARG_TBL_INFO)pInfo;
    MQ_CMD_MSG    msg;
    cJSON        *pJsonObj;
    const char   *pJsonStr;

    const char *pSvr = ((arg_str_t *)pTbl[1])->sval[0];
    int         help = get_help_cmd(pTbl, pArg)->count;

    if (pArg->pHelp && help > 0) {
        pArg->pHelp(pArg->argTbl, pName, NULL);
        return ERR_SUCCESS;
    }

    memset(&msg, 0, sizeof(MQ_CMD_MSG));

    strncpy(msg.cmd, ((arg_str_t *)pTbl[2])->sval[0], MAX_PATH - 1);
    strncpy(msg.key, ((arg_str_t *)pTbl[3])->sval[0], MAX_PATH - 1);
    strncpy(msg.value, ((arg_str_t *)pTbl[4])->sval[0], MAX_PATH - 1);

    if (strcmp(msg.cmd, "get") != 0 && strcmp(msg.cmd, "set") != 0) {
        printf("Error command: %s\n", msg.cmd);
        return -ERR_INPUT_PARAMS;
    }

    ret = FALSE;
    for (i = 0; i < ARRAY_SIZE(g_toolsKey); i++) {
        if (strcmp(g_toolsKey[i][0], msg.key) == 0) {
            ret = TRUE;
            break;
        }
    }

    if (ret == FALSE) {
        printf("Error key: %s\n", msg.key);
        return -ERR_INPUT_PARAMS;
    }

    pJsonObj = s2j_mq_msg(&msg);

    if (pJsonObj == NULL) {
        return -ERR_JSON_CREAT_OBJ;
    }

    pJsonStr = cJSON_PrintUnformatted(pJsonObj);
    ret      = mq_cmd_run(pSvr, pJsonStr);

    cJSON_Delete(pJsonObj);
    free((void *)pJsonStr);
    return ret;
}

static int on_cmd3(void *pTbl[], const char *pName, void *pInfo) {
    const char   *pRet;
    PARG_TBL_INFO pArg   = (PARG_TBL_INFO)pInfo;
    int           type   = ((arg_lit_t *)pTbl[1])->count;
    const char   *pValue = ((arg_str_t *)pTbl[2])->sval[0];
    int           help   = get_help_cmd(pTbl, pArg)->count;

    if (pArg->pHelp && help > 0) {
        pArg->pHelp(pArg->argTbl, pName, NULL);
        return ERR_SUCCESS;
    }

    if (type == 0) {
        pRet = base64_encode((unsigned char *)pValue, strlen(pValue));
    } else {
        pRet = (const char *)base64_decode(pValue, NULL);
    }

    if (pRet == NULL) {
        return type == 0 ? -ERR_EVP_ENCRYPTION : -ERR_EVP_DECRYPTION;
    }

    printf("Base64 %s: {%s} --> [%s]\n", type == 0 ? "encode" : "decode", pValue, pRet);

    free((void *)pRet);
    return ERR_SUCCESS;
}

static int on_cmd4(void *pTbl[], const char *pName, void *pInfo) {
    char         *pHashValue = NULL;
    PARG_TBL_INFO pArg       = (PARG_TBL_INFO)pInfo;
    const char   *type       = ((arg_str_t *)pTbl[1])->sval[0];
    const char   *pFile      = ((arg_file_t *)pTbl[2])->filename[0];
    int           help       = get_help_cmd(pTbl, pArg)->count;

    if (pArg->pHelp && help > 0) {
        pArg->pHelp(pArg->argTbl, pName, NULL);
        return ERR_SUCCESS;
    }

    if (strcmp(type, "sha256") == 0) {
        hash_digest_file(HASH_TYPE_SHA256, pFile, &pHashValue);
    } else if (strcmp(type, "sha1") == 0) {
        hash_digest_file(HASH_TYPE_SHA1, pFile, &pHashValue);
    } else if (strcmp(type, "md5") == 0) {
        hash_digest_file(HASH_TYPE_MD5, pFile, &pHashValue);
    } else {
        printf("Unsupported type: %s\n", type);
        return -ERR_UNSUP_EVP_TYPE;
    }

    printf("%s %s hash value: [%s]\n", type, pFile, pHashValue);

    free(pHashValue);
    return ERR_SUCCESS;
}

static int on_cmd5(void *pTbl[], const char *pName, void *pInfo) {
    int            algType;
    const char    *strRet;
    unsigned char *buf;
    int            outSize = 0;
    PARG_TBL_INFO  pArg    = (PARG_TBL_INFO)pInfo;
    int            encrypt = ((arg_lit_t *)pTbl[1])->count;
    const char    *alg     = ((arg_str_t *)pTbl[2])->sval[0];
    const char    *key     = ((arg_str_t *)pTbl[3])->sval[0];
    const char    *data    = ((arg_str_t *)pTbl[4])->sval[0];
    int            help    = get_help_cmd(pTbl, pArg)->count;

    if (pArg->pHelp && help > 0) {
        pArg->pHelp(pArg->argTbl, pName, NULL);
        return ERR_SUCCESS;
    }

    if (strcmp(alg, "3des_ecb") == 0) {
        algType = DES3_ECB_PKCS7PADDING;
    } else if (strcmp(alg, "3des_cbc") == 0) {
        algType = DES3_CBC_PKCS7PADDING;
    } else if (strcmp(alg, "3des_ofb") == 0) {
        algType = DES3_OFB_PKCS7PADDING;
    } else if (strcmp(alg, "aes128_ecb") == 0) {
        algType = AES128_ECB_PKCS7PADDING;
    } else if (strcmp(alg, "aes128_ecb_sha1") == 0) {
        algType = AES128_ECB_PKCS7PADDING_SHA1PRNG;
    } else if (strcmp(alg, "aes128_cbc") == 0) {
        algType = AES128_CBC_PKCS7PADDING;
    } else if (strcmp(alg, "aes128_ofb") == 0) {
        algType = AES128_OFB_PKCS7PADDING;
    } else if (strcmp(alg, "aes192_ecb") == 0) {
        algType = AES192_ECB_PKCS7PADDING;
    } else if (strcmp(alg, "aes192_cbc") == 0) {
        algType = AES192_CBC_PKCS7PADDING;
    } else if (strcmp(alg, "aes192_ofb") == 0) {
        algType = AES192_OFB_PKCS7PADDING;
    } else if (strcmp(alg, "aes256_ecb") == 0) {
        algType = AES256_ECB_PKCS7PADDING;
    } else if (strcmp(alg, "aes256_cbc") == 0) {
        algType = AES256_CBC_PKCS7PADDING;
    } else if (strcmp(alg, "aes256_ofb") == 0) {
        algType = AES256_OFB_PKCS7PADDING;
    } else {
        printf("Unsupported cipher algorithms: %s\n", alg);
        return -ERR_UNSUP_EVP_TYPE;
    }

    if (encrypt > 0) {
        if (symmetric_encrypto(algType, (unsigned char *)data, strlen(data), &buf, &outSize, key) != ERR_SUCCESS) {
            printf("Unsupported aes algorithms: %s\n", alg);
            return -ERR_EVP_ENCRYPTION;
        } else {
            strRet = base64_encode((unsigned char *)buf, outSize);
        }
    } else {
        unsigned char *pBase64 = (unsigned char *)base64_decode(data, (unsigned int *)&outSize);

        if (pBase64 == NULL) {
            printf("Base64 decode error: %s\n", data);
            return -ERR_EVP_DECRYPTION;
        }

        if (symmetric_decrypto(algType, pBase64, outSize, &buf, &outSize, key) != ERR_SUCCESS) {
            printf("Unsupported aes algorithms: %s\n", alg);
            free((void *)pBase64);
            return -ERR_EVP_DECRYPTION;
        } else {
            buf[outSize] = 0;
            strRet       = strdup((const char *)buf);
        }
    }

    printf("AES %s: {%s} --> [%s]\n", encrypt > 0 ? "encryption" : "decryption", data, strRet);

    free(buf);
    free((void *)strRet);
    return ERR_SUCCESS;
}

static int on_cmd6(void *pTbl[], const char *pName, void *pInfo) {
    unsigned char *buf;
    int            outSize = 0;
    PARG_TBL_INFO  pArg    = (PARG_TBL_INFO)pInfo;
    const char    *pKeygen = get_config_keygen();
    const char    *pKey    = ((arg_str_t *)pTbl[1])->sval[0];
    int            help    = get_help_cmd(pTbl, pArg)->count;

    if (pArg->pHelp && help > 0) {
        pArg->pHelp(pArg->argTbl, pName, NULL);
        return ERR_SUCCESS;
    }

    if (symmetric_encrypto(DES3_CBC_PKCS7PADDING, (unsigned char *)pKey, strlen(pKey), &buf, &outSize, pKeygen)
        != ERR_SUCCESS) {
        free((void *)pKeygen);
        return -ERR_EVP_ENCRYPTION;
    } else {
        const char *strRet = base64_encode((unsigned char *)buf, outSize);
        printf("Key: [%s]\n", strRet);
        printf("Keygen: {%s} --> [%s]\n", pKey, strRet);
        free((void *)strRet);
    }

    free(buf);
    free((void *)pKeygen);
    return ERR_SUCCESS;
}

static int on_cmd_(void *pTbl[], const char *pName, void *pInfo) {
    PARG_TBL_INFO pArg    = (PARG_TBL_INFO)pInfo;
    int           help    = get_help_cmd(pTbl, pArg)->count;
    int           version = ((arg_lit_t *)pTbl[0])->count;

    if (help > 0) {
        show_help(pName);
    } else if (version > 0) {
        printf("Application information: %s (Build: %s %s  GCC Ver:%s) With %lu(bits) OS\n",
               BUILD_VERSION,
               __DATE__,
               __TIME__,
               __VERSION__,
               sizeof(int *) * 8);

#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L
        printf("OpenSSL information:     %s (%s, %s)\n",
               OPENSSL_VERSION_TEXT,
               SSLeay_version(SSLEAY_BUILT_ON),
               SSLeay_version(SSLEAY_PLATFORM));
#else
        printf("OpenSSL information:     %s (%s, %s)\n",
               OPENSSL_VERSION_TEXT,
               OpenSSL_version(OPENSSL_BUILT_ON),
               OpenSSL_version(OPENSSL_PLATFORM));
#endif
    }

    return ERR_SUCCESS;
}

int menu_run(int argc, char **argv) {
    int         i, flag = FALSE;
    const char *pAppName = basename_v2(argv[0]);

    /*****************************************************************
    * 系统服务启动参数
    ******************************************************************/
    // clang-format off
    arg_rex_t  *cmd1       = arg_rex1(NULL, NULL, "start", NULL, REG_ICASE, NULL);
    arg_file_t *cfgfile    = arg_file1("c", "config", "<file>", "\tSpecify the current application configuration file path");
    arg_file_t *cfgdir     = arg_file1("d", "directory", "<dir>", "\tSpecify the configuration directory path");
    arg_str_t  *key1       = arg_str0("k", "key", "Key", "\tSpecify the configuration file cryptographic key.");
    arg_lit_t  *helpCmd1   = arg_lit0("h", "help", NULL);
    arg_end_t  *end1       = arg_end(20);
    void       *pArgTbl1[] = {cmd1, cfgfile, cfgdir, key1, helpCmd1, end1};
    int         errCode1   = 0;

    arg_rex_t *cmd2       = arg_rex1(NULL, NULL, "if", NULL, REG_ICASE, NULL);
    arg_str_t *server     = arg_str1("s", "server", "<svr>", "\tMessage queue server address.");
    arg_str_t *type       = arg_str1("t", "command", "<get/set>", "\tMessage queue command.");
    arg_str_t *key2       = arg_str1("k", "key", "<key>", "\tSystem predefine command \033[1;7mKEY\033[0m value.");
    arg_str_t *value2     = arg_str0("v", "value", "value", "\tValue of key when command is need");
    arg_lit_t *helpCmd2   = arg_lit0("h", "help", NULL);
    arg_end_t *end2       = arg_end(20);
    void      *pArgTbl2[] = {cmd2, server, type, key2, value2, helpCmd2, end2};
    int        errCode2   = 0;

    arg_rex_t *cmd3       = arg_rex1(NULL, NULL, "base64", NULL, REG_ICASE, NULL);
    arg_lit_t *operate3   = arg_litn("d", "operate", 0, ARG_MAX_FLAG, "\tBase64 decode, otherwise mean base64 encode");
    arg_str_t *value3     = arg_str0(NULL, NULL, "<base64 string>", NULL);
    arg_lit_t *helpCmd3   = arg_lit0("h", "help", NULL);
    arg_end_t *end3       = arg_end(20);
    void      *pArgTbl3[] = {cmd3, operate3, value3, helpCmd3, end3};
    int        errCode3   = 0;

    arg_rex_t  *cmd4       = arg_rex1(NULL, NULL, "hash", NULL, REG_ICASE, NULL);
    arg_file_t *file4      = arg_file0(NULL, NULL, "<file>", NULL);
    arg_str_t  *alg4       = arg_str0("a", "algorithms", "algorithms", "\tChose \033[1;7mHASH\033[0m algorithms.");
    arg_lit_t  *helpCmd4   = arg_lit0("h", "help", NULL);
    arg_end_t  *end4       = arg_end(20);
    void       *pArgTbl4[] = {cmd4,
                        alg4,
                        file4,
                        arg_rem(NULL, "\tSupport algorithms:"),
                        arg_rem(NULL, "\t  \033[4mmd5\033[0m:    file md5 value"),
                        arg_rem(NULL, "\t  \033[4msha1\033[0m:   file sha1 value"),
                        arg_rem(NULL, "\t  \033[4msha256\033[0m: file sha256 value"),
                        helpCmd4,
                        end4};
    int         errCode4   = 0;

    arg_rex_t *cmd5       = arg_rex1(NULL, NULL, "cipher", NULL, REG_ICASE, NULL);
    arg_lit_t *operate5   = arg_litn("e", "encrypt", 0, ARG_MAX_FLAG, "\tEncryption, Ignore means decryption.");
    arg_str_t *alg5       = arg_str0("a", "algorithms", "algorithms", "\tChose \033[1;7mCryptography\033[0m algorithms.");
    arg_str_t *key5       = arg_str0("k", "key", "Key", "\tCryptographic key");
    arg_str_t *value5     = arg_str0(NULL, NULL, "<AES string>", NULL);
    arg_lit_t *helpCmd5   = arg_lit0("h", "help", NULL);
    arg_end_t *end5       = arg_end(20);
    void      *pArgTbl5[] = {cmd5,
                        operate5,
                        alg5,
                        key5,
                        value5,
                        arg_rem(NULL, "\tSupport algorithms:"),
                        arg_rem(NULL, "\t  \033[4m3des_ecb\033[0m:         3DES ECB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4m3des_cbc\033[0m:         3DES CBC with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4m3des_ofb\033[0m:         3DES OFB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes128_ecb\033[0m:       128 bit AES ECB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes128_ecb_sha1\033[0m:  128 bit AES ECB and used SHA1 key with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes128_cbc\033[0m:       128 bit AES CBC with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes128_ofb\033[0m:       128 bit AES OFB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes192_ecb\033[0m:       192 bit AES ECB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes192_cbc\033[0m:       192 bit AES CBC with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes192_ofb\033[0m:       192 bit AES OFB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes256_ecb\033[0m:       256 bit AES ECB with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes256_cbc\033[0m:       256 bit AES CBC with PKCS7 padding"),
                        arg_rem(NULL, "\t  \033[4maes256_ofb\033[0m:       256 bit AES OFB with PKCS7 padding"),
                        helpCmd5,
                        end5};
    int        errCode5   = 0;

    arg_rex_t *cmd6       = arg_rex1(NULL, NULL, "config", NULL, REG_ICASE, NULL);
    arg_str_t *keyGen     = arg_str0("k", "keygen", "<Key>", "\tCreate configuration file cryptographic key.");
    arg_lit_t *helpCmd6   = arg_lit0("h", "help", NULL);
    arg_end_t *end6       = arg_end(20);
    void      *pArgTbl6[] = {cmd6, keyGen, helpCmd6, end6};
    int        errCode6   = 0;

    /*****************************************************************
    * 系统帮助参数定义
    ******************************************************************/
    arg_lit_t *help_      = arg_lit0("h", "help", "\tDisplay this help and exit");
    arg_lit_t *version_   = arg_lit0("v", "version", "\tDisplay application version information and exit");
    arg_end_t *end_       = arg_end(20);
    void      *pArgTbl_[] = {version_, help_, end_};
    int        errCode_   = 0;

    ARG_TBL_INFO argTblInfo[] = {
        {pArgTbl1, ARRAY_SIZE(pArgTbl1), &errCode1, on_cmd1, cmdn_help, "\r    \033[1;4;36mstart\033[0m:   \033[1mRun agent service normally.\033[0m\n"},
        {pArgTbl2, ARRAY_SIZE(pArgTbl2), &errCode2, on_cmd2, cmd2_help, "\n    \033[1;4;36mif\033[0m:   \033[1mRun agent interface tools.\033[0m\n"},
        {pArgTbl3, ARRAY_SIZE(pArgTbl3), &errCode3, on_cmd3, cmdn_help, "\n    \033[1;4;36mbase64\033[0m:  \033[1mString base64 encode/decode.\033[0m\n"},
        {pArgTbl4, ARRAY_SIZE(pArgTbl4), &errCode4, on_cmd4, cmdn_help, "\n    \033[1;4;36mhash\033[0m:    \033[1mCalculate file's hash value.\033[0m\n"},
        {pArgTbl5, ARRAY_SIZE(pArgTbl5), &errCode5, on_cmd5, cmdn_help, "\n    \033[1;4;36mcipher\033[0m:  \033[1mSymmetric cryptography for string.\033[0m\n"},
        {pArgTbl6, ARRAY_SIZE(pArgTbl6), &errCode6, on_cmd6, cmdn_help, "\n    \033[1;4;36mconfig\033[0m:  \033[1mConfiguration file tools.\033[0m\n"},
        {pArgTbl_, ARRAY_SIZE(pArgTbl_), &errCode_, on_cmd_, NULL, NULL},
    };

    // clang-format on

    g_pArgTbl = argTblInfo;
    g_nArgTbl = ARRAY_SIZE(argTblInfo);

    for (i = 0; i < g_nArgTbl; i++) {
        if (arg_nullcheck(argTblInfo[i].argTbl) != 0) {
            printf("%s insufficient memory\n", pAppName);
            return -ERR_SYS_INIT;
        }
    }

    cfgfile->filename[0] = "-";

    for (i = 0; i < g_nArgTbl; i++) {
        *argTblInfo[i].pErrCode = arg_parse(argc, argv, argTblInfo[i].argTbl);
    }

    for (i = 0; i < g_nArgTbl; i++) {
        PARG_TBL_INFO pArg = &argTblInfo[i];
        if (*pArg->pErrCode == 0 && pArg->pCmd) {
            if (pArg->pCmd(pArg->argTbl, pAppName, pArg) != ERR_SUCCESS) {
                pArg->pHelp(pArg->argTbl, pAppName, NULL);
            }

            flag = TRUE;
        }
    }

    if (flag == FALSE) {
        for (i = 0; i < g_nArgTbl - 1; i++) {
            PARG_TBL_INFO pArg = &argTblInfo[i];

            if (*pArg->pErrCode != 0) {
                if (get_reg_cmd(pArg->argTbl) > 0) {
                    int help = get_help_cmd(pArg->argTbl, pArg)->count;
                    pArg->pHelp(pArg->argTbl, pAppName, help > 0 ? NULL : get_arg_err(pArg->argTbl, pArg));
                    flag = TRUE;
                    break;
                }
            }
        }

        if (flag == FALSE) {
            show_help(pAppName);
        }
    }

    for (i = 0; i < g_nArgTbl; i++) {
        arg_freetable(argTblInfo[i].argTbl, argTblInfo[i].nArgItem);
    }

    return ERR_SUCCESS;
}