17abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <debug.h>
27abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <cmdline.h>
37abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <stdio.h>
47abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <stdlib.h>
57abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <getopt.h>
67abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <string.h>
77abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#include <ctype.h>
87abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
97abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchevextern char *optarg;
107abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchevextern int optind, opterr, optopt;
117abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
127abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchevstatic struct option long_options[] = {
137abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"output",  required_argument, 0, 'o'},
147abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"height",  required_argument, 0, 'h'},
157abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"width",   required_argument, 0, 'w'},
167abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"gray",    no_argument,       0, 'g'},
177abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"type",    required_argument, 0, 't'},
187abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"rotate",  required_argument, 0, 'r'},
197abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"verbose", no_argument,       0, 'V'},
207abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {"help",    no_argument,       0, 1},
217abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    {0, 0, 0, 0},
227abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev};
237abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
247abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev/* This array must parallel long_options[] */
257abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchevstatic const char *descriptions[] = {
267abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "output file",
277abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "image height in pixels",
287abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "image width in pixels",
297abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "process the luma plane only",
307abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "encode as one of { 'ppm', 'rgb', or 'argb' }",
317abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "rotate (90, -90, 180 degrees)",
327abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "print verbose output",
337abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    "print this help screen",
347abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev};
357abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
367abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchevvoid print_help(const char *name) {
377abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    fprintf(stdout,
387abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            "Converts yuv 4:2:0 to rgb24 and generates a PPM file.\n"
397abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            "invokation:\n"
407abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            "\t%s infile --height <height> --width <width> --output <outfile> -t <ppm|grb|argb> [ --gray ] [ --rotate <degrees> ] [ --verbose ]\n"
417abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            "\t%s infile --help\n",
427abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            name, name);
437abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    fprintf(stdout, "options:\n");
447abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    struct option *opt = long_options;
457abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    const char **desc = descriptions;
467abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    while (opt->name) {
477abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        fprintf(stdout, "\t-%c/--%s%s: %s\n",
487abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                isprint(opt->val) ? opt->val : ' ',
497abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                opt->name,
507abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                (opt->has_arg ? " (argument)" : ""),
517abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                *desc);
527abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        opt++;
537abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        desc++;
547abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    }
557abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev}
567abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
577abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchevint get_options(int argc, char **argv,
587abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                char **outfile,
597abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                int *height,
607abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                int *width,
617abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                int *gray,
627abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                char **type,
637abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                int *rotate,
647abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                int *verbose) {
657abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    int c;
667abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
677abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(outfile); *outfile = NULL;
687abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(height); *height = -1;
697abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(width); *width = -1;
707abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(gray); *gray = 0;
717abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(rotate); *rotate = 0;
727abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(verbose); *verbose = 0;
737abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(type); *type = NULL;
747abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
757abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    while (1) {
767abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        /* getopt_long stores the option index here. */
777abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        int option_index = 0;
787abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
797abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        c = getopt_long (argc, argv,
807abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                         "Vgo:h:w:r:t:",
817abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                         long_options,
827abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                         &option_index);
837abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        /* Detect the end of the options. */
847abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        if (c == -1) break;
857abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
867abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        if (isgraph(c)) {
877abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
887abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        }
897abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
907abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#define SET_STRING_OPTION(name) do {                                   \
917abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(optarg);                                                    \
927abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    (*name) = strdup(optarg);                                          \
937abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev} while(0)
947abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
957abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#define SET_INT_OPTION(val) do {                                       \
967abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    ASSERT(optarg);                                                    \
977abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev	if (strlen(optarg) >= 2 && optarg[0] == '0' && optarg[1] == 'x') { \
987abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev			FAILIF(1 != sscanf(optarg+2, "%x", val),                   \
997abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev				   "Expecting a hexadecimal argument!\n");             \
1007abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev	} else {                                                           \
1017abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev		FAILIF(1 != sscanf(optarg, "%d", val),                         \
1027abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev			   "Expecting a decimal argument!\n");                     \
1037abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev	}                                                                  \
1047abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev} while(0)
1057abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
1067abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        switch (c) {
1077abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 0:
1087abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            /* If this option set a flag, do nothing else now. */
1097abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            if (long_options[option_index].flag != 0)
1107abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                break;
1117abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            INFO ("option %s", long_options[option_index].name);
1127abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            if (optarg)
1137abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev                INFO (" with arg %s", optarg);
1147abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            INFO ("\n");
1157abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            break;
1167abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 1: print_help(argv[0]); exit(1); break;
1177abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev		case 'o':
1187abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev			SET_STRING_OPTION(outfile);
1197abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev			break;
1207abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev		case 't':
1217abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev			SET_STRING_OPTION(type);
1227abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev			break;
1237abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 'h':
1247abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            SET_INT_OPTION(height);
1257abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            break;
1267abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 'w':
1277abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            SET_INT_OPTION(width);
1287abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            break;
1297abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 'r':
1307abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            SET_INT_OPTION(rotate);
1317abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            break;
1327abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 'g': *gray = 1; break;
1337abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case 'V': *verbose = 1; break;
1347abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        case '?':
1357abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            /* getopt_long already printed an error message. */
1367abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            break;
1377abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
1387abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#undef SET_STRING_OPTION
1397abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev#undef SET_INT_OPTION
1407abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
1417abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        default:
1427abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev            FAILIF(1, "Unknown option");
1437abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev        }
1447abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    }
1457abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev
1467abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev    return optind;
1477abfb48551224ac2b4a8d1820c023915e31ffb5fIliyan Malchev}
148