diff --git a/README.md b/README.md index 94e7bda..eab9d9f 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,20 @@ HiColor has a Git-style CLI. The actions `encode` and `decode` convert images between PNG and HiColor's own image format. `quantize` round-trips an image through the converter and outputs a normal PNG. Use it to create images that look high-color but aren't. `info` displays information about a HiColor file: version (`5` for 15-bit or `6` for 16), width, and height. ```none -HiColor +HiColor 0.4.0 Create 15/16-bit color RGB images. usage: - hicolor (encode|decode|quantize) [options] src [dest] - hicolor info file - hicolor version - hicolor (help|-h|--help) + hicolor (encode|quantize) [-5|-6] [-n] [--] [] + hicolor (decode []|info |version|help|-h|--help) + +commands: + encode convert PNG to HiColor + decode convert HiColor to PNG + quantize quantize PNG to PNG + info print file version and resolution + version print program version + help print this help message options: -5, --15-bit 15-bit color diff --git a/cli.c b/cli.c index 5828a93..11d394d 100644 --- a/cli.c +++ b/cli.c @@ -327,8 +327,8 @@ void usage(FILE* output) fprintf( output, "usage:\n" - " hicolor (encode|decode|quantize) [-5|-6] [-n] []\n" - " hicolor (info |version|help|-h|--help)\n" + " hicolor (encode|quantize) [-5|-6] [-n] [--] []\n" + " hicolor (decode []|info |version|help|-h|--help)\n" ); } @@ -381,7 +381,7 @@ bool str_prefix(const char* ref, const char* str) } typedef enum command { - ENCODE, DECODE, QUANTIZE + ENCODE, DECODE, QUANTIZE, INFO, VERSION } command; int main(int argc, char** argv) @@ -389,99 +389,111 @@ int main(int argc, char** argv) command opt_command = ENCODE; bool opt_dither = true; hicolor_version opt_version = HICOLOR_VERSION_6; - char* opt_src; - char* opt_dest; + char* arg_src; + char* arg_dest; + bool allow_opts = true; + int min_pos_args = 1; + int max_pos_args = 2; - if (argc == 2 && str_prefix("version", argv[1])) { - version(); - return 0; + if (argc <= 1) { + help(); + return 1; } - if (argc == 2 - && (str_prefix("help", argv[1]) - || strcmp(argv[1], "-h") == 0 - || strcmp(argv[1], "--help") == 0)) { + if (str_prefix("help", argv[1]) + || strcmp(argv[1], "-h") == 0 + || strcmp(argv[1], "--help") == 0) { help(); return 0; } - if (argc == 3 && str_prefix("info", argv[1])) { - return !hicolor_print_info(argv[2]); - } - - if (argc < 3) { - fprintf(stderr, HICOLOR_CLI_ERROR "too few arguments\n"); - usage(stderr); - return 1; - } - int i = 1; if (str_prefix("encode", argv[i])) { opt_command = ENCODE; } else if (str_prefix("decode", argv[i])) { + allow_opts = false; opt_command = DECODE; } else if (str_prefix("quantize", argv[i])) { opt_command = QUANTIZE; + } else if (str_prefix("info", argv[i])) { + allow_opts = false; + max_pos_args = 1; + opt_command = INFO; + } else if (str_prefix("version", argv[i])) { + allow_opts = false; + min_pos_args = 0; + max_pos_args = 0; + opt_command = VERSION; } else { fprintf(stderr, HICOLOR_CLI_ERROR "invalid command\n"); usage(stderr); return 1; } + i++; - while (i < argc && argv[i][0] == '-') { - if (strcmp(argv[i], "--") == 0) { + if (allow_opts) { + while (i < argc && argv[i][0] == '-') { + if (strcmp(argv[i], "--") == 0) { + i++; + break; + } else if (strcmp(argv[i], "-5") == 0 + || strcmp(argv[i], "--15-bit") == 0) { + opt_version = HICOLOR_VERSION_5; + } else if (strcmp(argv[i], "-6") == 0 + || strcmp(argv[i], "--16-bit") == 0) { + opt_version = HICOLOR_VERSION_6; + } else if (strcmp(argv[i], "-n") == 0 + || strcmp(argv[i], "--no-dither") == 0) { + opt_dither = false; + } + i++; - break; - } else if (strcmp(argv[i], "-5") == 0 - || strcmp(argv[i], "--15-bit") == 0) { - opt_version = HICOLOR_VERSION_5; - } else if (strcmp(argv[i], "-6") == 0 - || strcmp(argv[i], "--16-bit") == 0) { - opt_version = HICOLOR_VERSION_6; - } else if (strcmp(argv[i], "-n") == 0 - || strcmp(argv[i], "--no-dither") == 0) { - opt_dither = false; } - - i++; } - if (i >= argc) { + int rem_args = argc - i; + + if (rem_args < min_pos_args) { fprintf(stderr, HICOLOR_CLI_ERROR "too few arguments\n"); usage(stderr); return 1; } - opt_src = argv[i]; + if (rem_args > max_pos_args) { + fprintf(stderr, HICOLOR_CLI_ERROR "too many arguments\n"); + usage(stderr); + return 1; + } + + arg_src = argv[i]; i++; if (i == argc) { - opt_dest = malloc(strlen(opt_src) + 5); - if (opt_dest == NULL) return HICOLOR_CLI_NO_MEMORY_EXIT_CODE; + arg_dest = malloc(strlen(arg_src) + 5); + if (arg_dest == NULL) return HICOLOR_CLI_NO_MEMORY_EXIT_CODE; sprintf( - opt_dest, + arg_dest, opt_command == ENCODE ? "%s.hic" : "%s.png", - opt_src + arg_src ); } else { - opt_dest = argv[i]; + arg_dest = argv[i]; } i++; - if (i < argc) { - fprintf(stderr, HICOLOR_CLI_ERROR "too many arguments\n"); - usage(stderr); - return 1; - } - switch (opt_command) { case ENCODE: - return !png_to_hicolor(opt_version, opt_dither, opt_src, opt_dest); + return !png_to_hicolor(opt_version, opt_dither, arg_src, arg_dest); case DECODE: - return !hicolor_to_png(opt_src, opt_dest); + return !hicolor_to_png(arg_src, arg_dest); case QUANTIZE: - return !png_quantize(opt_version, opt_dither, opt_src, opt_dest); + return !png_quantize(opt_version, opt_dither, arg_src, arg_dest); + case INFO: + return !hicolor_print_info(arg_src); + case VERSION: + version(); + return 0; } } diff --git a/tests/hicolor.test b/tests/hicolor.test index 8baea7d..b5cab29 100755 --- a/tests/hicolor.test +++ b/tests/hicolor.test @@ -43,6 +43,10 @@ tcltest::test version-1.1 {} -body { hicolor version } -match regexp -result {\d+\.\d+\.\d+} +tcltest::test version-1.2 {} -body { + hicolor version file.hi5 +} -returnCodes error -match glob -result {*too many arg*} + tcltest::test version-2.1 {} -body { set action version set output [hicolor $action] @@ -57,17 +61,17 @@ tcltest::test version-2.1 {} -body { tcltest::test help-1.1 {} -body { hicolor help -} -match glob -result *usage:* +} -match glob -result *options:* tcltest::test help-1.2 {} -body { hicolor -h -} -match glob -result *usage:* +} -match glob -result *options:* tcltest::test help-1.3 {} -body { hicolor --help -} -match glob -result *usage:* +} -match glob -result *options:* tcltest::test encode-1.1 {} -body { @@ -129,6 +133,11 @@ tcltest::test encode-2.7 {encode flags} -body { hicolor encode -n -n -n -n -n photo.png } -result {} +tcltest::test encode-2.8 {encode flags} -body { + hicolor encode -6 -n -5 photo.png + hicolor info photo.png.hic +} -result {5 640 427} + tcltest::test encode-3.1 {bad input} -body { hicolor encode truncated.png @@ -157,6 +166,10 @@ tcltest::test decode-1.3 {bad input} -body { hicolor decode [file tail [info script]] } -returnCodes error -match glob -result {*bad magic*} +tcltest::test decode-1.4 {bad input} -body { + hicolor decode -5 photo.hi5 +} -returnCodes error -match glob -result *error:* + tcltest::test quantize-1.1 {} -body { hicolor quantize photo.png photo.16-bit.png @@ -181,6 +194,26 @@ tcltest::test quantize-2.3 {bad input} -body { } -returnCodes error -match glob -result {error: can't load PNG file*} +tcltest::test invalid-command-1.1 {} -body { + hicolor -5 src.png +} -returnCodes error -match glob -result {error: invalid command*} + +tcltest::test invalid-command-1.2 {} -body { + hicolor encoder +} -returnCodes error -match glob -result {error: invalid command*} + + +tcltest::test no-arguments-1.1 {} -body { + hicolor +} -returnCodes error -match glob -result *options:* + + +tcltest::test dash-dash-1.1 {} -body { + hicolor encode -- --no-such-file +} -returnCodes error -match glob -result {error: source image "--no-such-file"\ + doesn't exist} + + tcltest::test data-integrity-1.1 {roundtrip} -constraints gm -body { hicolor decode photo.hi5 temp.png exec gm compare -metric rmse photo.png temp.png