Various improvements to options parsing.

This commit is contained in:
LoRd_MuldeR 2022-09-15 00:29:15 +02:00
parent 3f0b4c5788
commit 1b8e9aba42
5 changed files with 169 additions and 106 deletions

View File

@ -13,7 +13,7 @@ else
endif endif
endif endif
CFLAGS = -Wall -std=gnu99 -Ofast -DNDEBUG CFLAGS = -Wall -std=gnu99 -pedantic -Ofast -DNDEBUG
ifneq (,$(XCFLAGS)) ifneq (,$(XCFLAGS))
CFLAGS += $(XCFLAGS) CFLAGS += $(XCFLAGS)

View File

@ -10,17 +10,17 @@ Synopsis:
crc64.exe [OPTIONS] [<file_1> [<file_2> ... <file_n>]] crc64.exe [OPTIONS] [<file_1> [<file_2> ... <file_n>]]
Options: Options:
--help --version Print help screen / show version information -h --help --version Show help screen / show version information
--binary Output the digest in binary format (default is hex-format) -b --binary Output digest in binary format (default is hex-string)
--decimal Output the digest in decimal string format -d --decimal Output digest in decimal string format
--upper-case Print hex-string as upper-case (default is lower-case) -u --upper-case Print digest as upper-case (default is lower-case)
--no-padding Print the digest *without* any leading zeros -p --no-padding Print digest *without* any leading zeros
--silent Suppress error messages -s --silent Suppress error messages
--ignore-errors Ignore I/O errors and proceeed with the next file -e --ignore-errors Ignore I/O errors and proceeed with the next file
--no-flush Do *not* flush the standard output stream after each file -f --no-flush Do *not* flush output stream after each file
--init-with-zero Initialize CRC with zero (default is 0xFFF...FFF) -z --init-with-zero Initialize CRC with 0x000..000 (default is 0xFFF..FFF)
--negate-final Negate the final CRC result -n --negate-final Negate the final CRC result
--self-test Run integreated self-test and exit program -t --self-test Run integrated self-test and exit program
``` ```
One output line is generated per input file: One output line is generated per input file:

234
crc64.c
View File

@ -11,6 +11,7 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <ctype.h>
#include <signal.h> #include <signal.h>
#ifdef _WIN32 #ifdef _WIN32
@ -25,16 +26,17 @@ static const int VERSION_MINOR = 0;
static const int VERSION_PATCH = 2; static const int VERSION_PATCH = 2;
#define OPT_HELPSC 0x001 #define OPT_HELPSC 0x001
#define OPT_BINARY 0x002 #define OPT_VERSNO 0x002
#define OPT_UPPERC 0x004 #define OPT_BINARY 0x004
#define OPT_DECIML 0x008 #define OPT_UPPERC 0x008
#define OPT_NOPADD 0x010 #define OPT_DECIML 0x010
#define OPT_IGNERR 0x020 #define OPT_NOPADD 0x020
#define OPT_SILENT 0x040 #define OPT_IGNERR 0x040
#define OPT_NOFLSH 0x080 #define OPT_SILENT 0x080
#define OPT_ZEROIN 0x100 #define OPT_NOFLSH 0x100
#define OPT_NEGATE 0x200 #define OPT_ZEROIN 0x200
#define OPT_SLFTST 0x400 #define OPT_NEGATE 0x400
#define OPT_SLFTST 0x800
/* ======================================================================== */ /* ======================================================================== */
/* Compiler */ /* Compiler */
@ -45,13 +47,16 @@ static const int VERSION_PATCH = 2;
#define ALIGNED(X) __attribute__((aligned(X))) #define ALIGNED(X) __attribute__((aligned(X)))
#define PURE __attribute__((pure)) #define PURE __attribute__((pure))
#define FORCE_INLINE __attribute__((always_inline)) __inline__ #define FORCE_INLINE __attribute__((always_inline)) __inline__
#define COUNTOF(X) (sizeof(X) / sizeof((X)[0]))
#define ATOMIC_INC(X) __sync_add_and_fetch((X), 1L);
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define ALIGNED(X) __declspec(align(X)) #define ALIGNED(X) __declspec(align(X))
#define PURE #define PURE __declspec(noalias)
#define FORCE_INLINE __forceinline #define FORCE_INLINE __forceinline
#define COUNTOF(X) _countof(X)
#define ATOMIC_INC(X) _InterlockedIncrement((X));
#else #else
#error Unsupported compiler, please fix !!! #error Unsupported compiler, please fix !!!
@ -68,6 +73,8 @@ static const int VERSION_PATCH = 2;
#define CHAR wchar_t #define CHAR wchar_t
#define MAIN wmain #define MAIN wmain
#define STRCASECMP _wcsicmp #define STRCASECMP _wcsicmp
#define ISSPACE iswspace
#define TOLOWER towlower
#define PRINTF wprintf #define PRINTF wprintf
#define FPUTS fputws #define FPUTS fputws
#define FPRINTF fwprintf #define FPRINTF fwprintf
@ -88,6 +95,8 @@ int _dowildcard = -1;
#define CHAR char #define CHAR char
#define MAIN main #define MAIN main
#define STRCASECMP strcasecmp #define STRCASECMP strcasecmp
#define ISSPACE isspace
#define TOLOWER tolower
#define PRINTF printf #define PRINTF printf
#define FPUTS fputs #define FPUTS fputs
#define FPRINTF fprintf #define FPRINTF fprintf
@ -101,13 +110,13 @@ int _dowildcard = -1;
/* Signal handler */ /* Signal handler */
/* ======================================================================== */ /* ======================================================================== */
static int g_interrupted_flag = 0; static long g_aborted = 0L;
static void sigint_handler(const int sig) static void sigint_handler(const int sig)
{ {
if (sig == SIGINT) if (sig == SIGINT)
{ {
g_interrupted_flag = -1; ATOMIC_INC(&g_aborted);
} }
} }
@ -230,7 +239,7 @@ const wchar_t *const STR_STDIN = L"CONIN$";
const char *const STR_STDIN = "/dev/stdin"; const char *const STR_STDIN = "/dev/stdin";
#endif #endif
static int process(const CHAR *const file_name, const int options) static int process_file(const CHAR *const file_name, const int options)
{ {
int retval = EXIT_FAILURE; int retval = EXIT_FAILURE;
FILE *input = NULL; FILE *input = NULL;
@ -280,7 +289,7 @@ static int process(const CHAR *const file_name, const int options)
break; /*ignore the read error*/ break; /*ignore the read error*/
} }
} }
if (g_interrupted_flag) if (g_aborted)
{ {
goto clean_up; /*process was interrupted*/ goto clean_up; /*process was interrupted*/
} }
@ -371,14 +380,13 @@ CRC64_TESTCASE[] =
{ { 0x80459bd12d319927ULL, 0x3377cec7a585e11fULL }, 0x00F4240, "a" }, { { 0x80459bd12d319927ULL, 0x3377cec7a585e11fULL }, 0x00F4240, "a" },
{ { 0x5343b9581532ecbdULL, 0x9c5f7a86307e23ceULL }, 0x1000000, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" }, { { 0x5343b9581532ecbdULL, 0x9c5f7a86307e23ceULL }, 0x1000000, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" },
{ { 0x771555a0a9bdf4cbULL, 0xfbd4c4d2a72a6865ULL }, 0xFFFFFC7, "\x20!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" }, { { 0x771555a0a9bdf4cbULL, 0xfbd4c4d2a72a6865ULL }, 0xFFFFFC7, "\x20!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" },
{ { 0x3514226b46672b42ULL, 0xb9d5b31948f0b7ecULL }, 0xFFFFFC7, ")F9ow<I]PrX5kgbjJ'(`A&\".qHt6|{}-\x20paCe%Q:*,#x0NiMuzUG>V;!_S+dym/\\cL$73@4T?8DO2B[EhnKY^slZ~=RWvf1" }, { { 0x3514226b46672b42ULL, 0xb9d5b31948f0b7ecULL }, 0xFFFFFC7, ")F9ow<I]PrX5kgbjJ'(`A&\".qHt6|{}-\x20paCe%Q:*,#x0NiMuzUG>V;!_S+dym/\\cL$73@4T?8DO2B[EhnKY^slZ~=RWvf1" }
{ { 0x0000000000000000ULL, 0x0000000000000000ULL }, 0x0000000, NULL }
}; };
static int self_test(void) static int self_test(void)
{ {
size_t i, j, k; size_t i, j, k;
for (i = 0U; CRC64_TESTCASE[i].input; ++i) for (i = 0U; i < COUNTOF(CRC64_TESTCASE); ++i)
{ {
const size_t len = strlen(CRC64_TESTCASE[i].input); const size_t len = strlen(CRC64_TESTCASE[i].input);
for (j = 0U; j < 2U; ++j) for (j = 0U; j < 2U; ++j)
@ -387,21 +395,66 @@ static int self_test(void)
for (k = 0U; k < CRC64_TESTCASE[i].loops; ++k) for (k = 0U; k < CRC64_TESTCASE[i].loops; ++k)
{ {
crc = crc64_update(crc, (const uint8_t*)CRC64_TESTCASE[i].input, len); crc = crc64_update(crc, (const uint8_t*)CRC64_TESTCASE[i].input, len);
if (g_aborted)
{
return EXIT_FAILURE;
}
} }
FPRINTF(stderr, T("%016") T(PRIx64) T(" [%s]\n"), crc, (crc == CRC64_TESTCASE[i].expected[j]) ? T("OK") : T("Failed!")); FPRINTF(stderr, T("%016") T(PRIx64) T(" [%s]\n"), crc, (crc == CRC64_TESTCASE[i].expected[j]) ? T("OK") : T("Failed!"));
fflush(stderr); fflush(stderr);
if (crc != CRC64_TESTCASE[i].expected[j]) if (crc != CRC64_TESTCASE[i].expected[j])
{ {
FPRINTF(stderr, T("Expected result was 0x%016") T(PRIx64) T(", but got 0x%016") T(PRIx64) T(" :-(\n"), CRC64_TESTCASE[i].expected[j], crc); FPRINTF(stderr, T("Expected result was 0x%016") T(PRIx64) T(", but got 0x%016") T(PRIx64) T("\n"), CRC64_TESTCASE[i].expected[j], crc);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
} }
FPUTS(T("All tests completed successfully :-)\n"), stderr);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
/* ======================================================================== */
/* Parse options */
/* ======================================================================== */
#define ISALNUM(X) (((X) != T('\0')) && ((((X) >= T('0')) && ((X) <= T('9'))) || (((X) >= T('a')) && ((X) <= T('z'))) || (((X) >= T('A')) && ((X) <= T('Z')))))
static const struct
{
CHAR c;
const CHAR *name;
int flag;
}
CLI_OPTION_NAMES[] =
{
{ T('h'), T("help"), OPT_HELPSC },
{ T('v'), T("version"), OPT_VERSNO },
{ T('b'), T("binary"), OPT_BINARY },
{ T('u'), T("upper-case"), OPT_UPPERC },
{ T('d'), T("decimal"), OPT_DECIML },
{ T('p'), T("no-padding"), OPT_NOPADD },
{ T('e'), T("ignore-errors"), OPT_IGNERR },
{ T('s'), T("silent"), OPT_SILENT },
{ T('f'), T("no-flush"), OPT_NOFLSH },
{ T('z'), T("init-with-zero"), OPT_ZEROIN },
{ T('n'), T("negate-final"), OPT_NEGATE },
{ T('t'), T("self-test"), OPT_SLFTST }
};
static int parse_option(int *const options, const CHAR c, const CHAR *const name)
{
size_t i;
for (i = 0U; i < COUNTOF(CLI_OPTION_NAMES); ++i)
{
if ((c == CLI_OPTION_NAMES[i].c) || (name && (!STRCASECMP(name, CLI_OPTION_NAMES[i].name))))
{
*options |= CLI_OPTION_NAMES[i].flag;
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}
/* ======================================================================== */ /* ======================================================================== */
/* MAIN */ /* MAIN */
/* ======================================================================== */ /* ======================================================================== */
@ -423,98 +476,91 @@ int MAIN(int argc, CHAR *argv[])
sigaction(SIGINT, &act, NULL); sigaction(SIGINT, &act, NULL);
#endif #endif
while ((arg_off < argc) && (argv[arg_off][0] == '-') && (argv[arg_off][1] == '-')) if ((argc > 1) && (!STRCASECMP(argv[1], T("/?"))))
{ {
const CHAR *const arg_val = argv[arg_off++] + 2U; options |= OPT_HELPSC;
if (arg_val[0] != '\0') goto print_help;
}
while ((arg_off < argc) && (argv[arg_off][0] == T('-')) && ((argv[arg_off][1] == T('-')) || ISALNUM(argv[arg_off][1])))
{
const CHAR *arg_ptr = argv[arg_off++] + 1U;
if (*arg_ptr == T('-'))
{ {
if ((!STRCASECMP(arg_val, T("help"))) || (!STRCASECMP(arg_val, T("version")))) if (*(++arg_ptr) != T('\0'))
{ {
options |= OPT_HELPSC; if (parse_option(&options, T('\0'), arg_ptr) != EXIT_SUCCESS)
} {
else if (!STRCASECMP(arg_val, T("binary"))) FPRINTF(stderr, T("Error: Option \"--%s\" is not recognized!\n"), arg_ptr);
{ return EXIT_FAILURE;
options |= OPT_BINARY; }
}
else if (!STRCASECMP(arg_val, T("upper-case")))
{
options |= OPT_UPPERC;
}
else if (!STRCASECMP(arg_val, T("decimal")))
{
options |= OPT_DECIML;
}
else if (!STRCASECMP(arg_val, T("no-padding")))
{
options |= OPT_NOPADD;
}
else if (!STRCASECMP(arg_val, T("ignore-errors")))
{
options |= OPT_IGNERR;
}
else if (!STRCASECMP(arg_val, T("silent")))
{
options |= OPT_SILENT;
}
else if (!STRCASECMP(arg_val, T("no-flush")))
{
options |= OPT_NOFLSH;
}
else if (!STRCASECMP(arg_val, T("init-with-zero")))
{
options |= OPT_ZEROIN;
}
else if (!STRCASECMP(arg_val, T("negate-final")))
{
options |= OPT_NEGATE;
}
else if (!STRCASECMP(arg_val, T("self-test")))
{
options |= OPT_SLFTST;
} }
else else
{ {
FPRINTF(stderr, T("Error: Option \"--%s\" is not recognized!\n"), arg_val); break; /*stop option processing here*/
return EXIT_FAILURE;
} }
} }
else else
{ {
break; /*stop option processing*/ for(; ISALNUM(*arg_ptr); ++arg_ptr)
{
if (parse_option(&options, TOLOWER(*arg_ptr), NULL) != EXIT_SUCCESS)
{
FPRINTF(stderr, T("Error: Option \"-%c\" is not recognized!\n"), *arg_ptr);
return EXIT_FAILURE;
}
}
} }
} }
print_help:
if ((options & OPT_HELPSC) || (options & OPT_VERSNO))
{
FPRINTF(stderr, T("CRC-64 %d.%d.%d [%s]\n"), VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, T(__DATE__));
if (options & OPT_HELPSC)
{
FPUTS(T("\nSynopsis:\n"), stderr);
FPUTS(T(" ") PROGRAM_NAME T(" [OPTIONS] [<file_1> [<file_2> ... <file_n>]]\n\n"), stderr);
FPUTS(T("Options:\n"), stderr);
FPUTS(T(" -h --help --version Show help screen / show version information\n"), stderr);
FPUTS(T(" -b --binary Output digest in binary format (default is hex-string)\n"), stderr);
FPUTS(T(" -d --decimal Output digest in decimal string format\n"), stderr);
FPUTS(T(" -u --upper-case Print digest as upper-case (default is lower-case)\n"), stderr);
FPUTS(T(" -p --no-padding Print digest *without* any leading zeros\n"), stderr);
FPUTS(T(" -s --silent Suppress error messages\n"), stderr);
FPUTS(T(" -e --ignore-errors Ignore I/O errors and proceeed with the next file\n"), stderr);
FPUTS(T(" -f --no-flush Do *not* flush output stream after each file\n"), stderr);
FPUTS(T(" -z --init-with-zero Initialize CRC with 0x000..000 (default is 0xFFF..FFF)\n"), stderr);
FPUTS(T(" -n --negate-final Negate the final CRC result\n"), stderr);
FPUTS(T(" -t --self-test Run integrated self-test and exit program\n\n"), stderr);
}
return EXIT_SUCCESS;
}
if ((options & OPT_BINARY) && (options & OPT_DECIML)) if ((options & OPT_BINARY) && (options & OPT_DECIML))
{ {
FPUTS(T("Error: Binary output and decimal output are mutually exclusive!\n"), stderr); FPUTS(T("Error: Binary output and decimal output are mutually exclusive!\n"), stderr);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (options & OPT_HELPSC)
{
FPRINTF(stderr, T("CRC64 %d.%d.%d [%s]\n\n"), VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, T(__DATE__));
FPUTS(T("Synopsis:\n"), stderr);
FPUTS(T(" ") PROGRAM_NAME T(" [OPTIONS] [<file_1> [<file_2> ... <file_n>]]\n\n"), stderr);
FPUTS(T("Options:\n"), stderr);
FPUTS(T(" --help --version Print help screen / show version information\n"), stderr);
FPUTS(T(" --binary Output the digest in binary format (default is hex-format)\n"), stderr);
FPUTS(T(" --decimal Output the digest in decimal string format\n"), stderr);
FPUTS(T(" --upper-case Print hex-string as upper-case (default is lower-case)\n"), stderr);
FPUTS(T(" --no-padding Print the digest *without* any leading zeros\n"), stderr);
FPUTS(T(" --silent Suppress error messages\n"), stderr);
FPUTS(T(" --ignore-errors Ignore I/O errors and proceeed with the next file\n"), stderr);
FPUTS(T(" --no-flush Do *not* flush the standard output stream after each file\n"), stderr);
FPUTS(T(" --init-with-zero Initialize CRC with zero (default is 0xFFF...FFF)\n"), stderr);
FPUTS(T(" --negate-final Negate the final CRC result\n"), stderr);
FPUTS(T(" --self-test Run integreated self-test and exit program\n\n"), stderr);
return EXIT_SUCCESS;
}
if (options & OPT_SLFTST) if (options & OPT_SLFTST)
{ {
FPUTS(T("Running CRC-64 self-test, please wait...\n"), stderr); FPUTS(T("Running CRC-64 self-test, please wait...\n"), stderr);
fflush(stderr); fflush(stderr);
return self_test(); if (self_test() == EXIT_SUCCESS)
{
FPUTS(T("All tests completed successfully :-)\n"), stderr);
return EXIT_SUCCESS;
}
else
{
if (!(options & OPT_SILENT))
{
FPUTS(g_aborted ? T("Error: Test was interrupted!\n") : T("At least one test has failed :-(\n"), stderr);
}
return EXIT_FAILURE;
}
} }
#ifdef _WIN32 #ifdef _WIN32
@ -523,9 +569,9 @@ int MAIN(int argc, CHAR *argv[])
if (arg_off < argc) if (arg_off < argc)
{ {
while ((arg_off < argc) && (!g_interrupted_flag)) while ((arg_off < argc) && (!g_aborted))
{ {
if (process(argv[arg_off++], options) != EXIT_SUCCESS) if (process_file(argv[arg_off++], options) != EXIT_SUCCESS)
{ {
if (!(options & OPT_IGNERR)) if (!(options & OPT_IGNERR))
{ {
@ -537,7 +583,7 @@ int MAIN(int argc, CHAR *argv[])
} }
else else
{ {
if (process(NULL, options) != EXIT_SUCCESS) if (process_file(NULL, options) != EXIT_SUCCESS)
{ {
if (!(options & OPT_IGNERR)) if (!(options & OPT_IGNERR))
{ {
@ -546,7 +592,7 @@ int MAIN(int argc, CHAR *argv[])
} }
} }
if (g_interrupted_flag && (!(options & OPT_IGNERR))) if (g_aborted && (!(options & OPT_IGNERR)))
{ {
if (!(options & OPT_SILENT)) if (!(options & OPT_SILENT))
{ {

14
win32/crc64.props Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros">
<XPDeprecationWarning>false</XPDeprecationWarning>
</PropertyGroup>
<PropertyGroup />
<ItemDefinitionGroup />
<ItemGroup>
<BuildMacro Include="XPDeprecationWarning">
<Value>$(XPDeprecationWarning)</Value>
</BuildMacro>
</ItemGroup>
</Project>

View File

@ -53,12 +53,15 @@
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="crc64.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="crc64.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets"> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="crc64.props" />
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">