arg.h
00a2dd39
 #include <stdlib.h>
 #include <stdio.h>
 
 
 struct arg_const {
     char const * name;
     int const value;
 };
 
 
 #ifndef ARG_HELP
 #define ARG_HELP help
 #endif
 #ifndef ARG_VERSION
 #define ARG_VERSION version
 #endif
 #ifndef ARG_USAGE
 #define ARG_USAGE usage
 #endif
 #ifndef ARG_FORMAT_HEADER
 #define ARG_FORMAT_HEADER       "Recognized values for <%s>:\n"
 #endif
 #ifndef ARG_FORMAT_NAME
 #define ARG_FORMAT_NAME         "  %s\n"
 #endif
 #ifndef ARG_FORMAT_POSITIONAL
 #define ARG_FORMAT_POSITIONAL   "No %s specified"
 #endif
 #ifndef ARG_FORMAT_COLON
 #define ARG_FORMAT_COLON        "Option -%c requires an argument"
 #endif
 #ifndef ARG_FORMAT_QUESTIONMARK
 #define ARG_FORMAT_QUESTIONMARK "Unrecognized option: '-%c'"
 #endif
 #ifndef ARG_FORMAT_DEFAULT
 #define ARG_FORMAT_DEFAULT      "Failed to parse option: '-%c'"
 #endif
 #ifndef ARG_FORMAT_FIND
 #define ARG_FORMAT_FIND         "Unrecognized %s: '%s'"
 #endif
 #ifndef ARG_FORMAT_CATCH
 #define ARG_FORMAT_CATCH        "Unrecognized argument: '%s'"
 #endif
 
 #define ARG_PRINT(STREAM, ARG, ARGS) \
     fprintf(STREAM, ARG_FORMAT_HEADER, #ARG); \
     for (size_t argi = 0; argi < sizeof(ARGS) / sizeof(*ARGS); ++argi) { \
         void const * arg = &((char const *)ARGS)[argi * sizeof(*ARGS)]; \
         fprintf(STREAM, ARG_FORMAT_NAME, *(char const **)arg); \
     }
 
 #define ARG_SPECIALS() \
     if (argc > 1) { \
         if ( \
             0 == strcmp(argv[1], "-h") || \
             0 == strcmp(argv[1], "--help") \
         ) { \
             ARG_HELP(stdout); \
             exit(EXIT_SUCCESS); \
         } \
         if ( \
             0 == strcmp(argv[1], "--version") \
         ) { \
             ARG_VERSION(stdout); \
             exit(EXIT_SUCCESS); \
         } \
     }
 
 #define ARG_DECLARE(ARG, DEFAULT) \
     char const * arg_##ARG = DEFAULT;
 
 #define ARG_POSITIONAL(ARG) \
     if (optind >= argc) { \
         fprintf(stderr, ARG_FORMAT_POSITIONAL "\n\n", #ARG); \
         ARG_USAGE(stderr); \
         exit(EXIT_FAILURE); \
     } \
     arg_##ARG = argv[optind++];
 
 #define ARG_GETOPT_BEGIN(OPTSTRING) \
     { \
         int opt; \
         int opterr = 0; \
         while (-1 != (opt = getopt(argc, argv, ":" OPTSTRING))) { \
             switch(opt) {
 
 #define ARG_GETOPT(OPT, ARG) \
                 case OPT: arg_##ARG = argv[optind]; break;
 
 #define ARG_GETOPT_WITH_ARGUMENT(OPT, ARG) \
                 case OPT: arg_##ARG = optarg; break;
 
 #define ARG_GETOPT_END() \
                 case ':': \
                     fprintf(stderr, ARG_FORMAT_COLON "\n\n", optopt); \
                     opterr = 1; \
                     break; \
                 case '?': \
                     fprintf(stderr, ARG_FORMAT_QUESTIONMARK "\n\n", optopt); \
                     opterr = 1; \
                     break; \
                 default: \
                     fprintf(stderr, ARG_FORMAT_DEFAULT "\n\n", opt); \
                     opterr = 1; \
                     break; \
             } \
         } \
         if (opterr) { \
             ARG_USAGE(stderr); \
             exit(EXIT_FAILURE); \
         } \
     }
 
 #define ARG_CONVERT_BOOL(ARG) \
     int ARG = !!arg_##ARG;
 
 #define ARG_CONVERT_NULL(ARG) \
     char const * ARG = *(arg_##ARG) ? (arg_##ARG) : NULL;
 
 #define ARG_CONVERT_FIND(TYPE, ARG, ARGS) \
     struct TYPE const * ARG = NULL; \
     for (size_t argi = 0; argi < sizeof(ARGS) / sizeof(*ARGS); ++argi) { \
         void const * arg = &((char const *)ARGS)[argi * sizeof(*ARGS)]; \
         if (0 == strcmp(arg_##ARG, *(char const **)arg)) \
             ARG = arg; \
     } \
     if (!ARG) { \
         fprintf(stderr, ARG_FORMAT_FIND "\n\n", #ARG, arg_##ARG); \
         ARG_USAGE(stderr); \
         exit(EXIT_FAILURE); \
     } \
 
 #define ARG_CONVERT_FIND_CONST(ARG, ARGS) \
     struct arg_const const * arg_const_##ARG = NULL; \
     for (size_t argi = 0; argi < sizeof(ARGS) / sizeof(*ARGS); ++argi) { \
         void const * arg = &((char const *)ARGS)[argi * sizeof(*ARGS)]; \
         if (0 == strcmp(arg_##ARG, *(char const **)arg)) \
             arg_const_##ARG = arg; \
     } \
     if (!arg_const_##ARG) { \
         fprintf(stderr, ARG_FORMAT_FIND "\n\n", #ARG, arg_##ARG); \
         ARG_USAGE(stderr); \
         exit(EXIT_FAILURE); \
     } \
     int ARG = arg_const_##ARG->value; \
 
 #define ARG_CATCH() \
     if (optind < argc) { \
         fprintf(stderr, ARG_FORMAT_CATCH "\n\n", argv[optind]); \
         ARG_USAGE(stderr); \
         exit(EXIT_FAILURE); \
     }