%% options %% module #include "dk4conf.h" #include "dk4types.h" #include "dk4error.h" #include "dk4opt.h" #include "dk4maidddi.h" #include "dk4maidddu.h" #include "dk4maiddsz.h" #include "dk4maiddbl.h" #include "dk4strd.h" #include "dk4fput.h" $!trace-include static const dkChar * const dk4opt_kwnl[] = { $!string-table macro=dkT # # 0 # \n # # 1 ... 5 # ERROR: Option - / -- requires # # 6 ... 11 # boolean size integer unsigned integer double string # # 12 # argument!\n # # 13 14 # Illegal argument text: " "!\n # # 15 # : # # 16 17 # ERROR: No file names expected!\n ERROR: Too many file names!\n # # 18 19 # ERROR: Illegal option: " "!\n # # 20 21 # Warning: Option used multiple times: " ".\n # # 22 23 # Warning: Unused argument in option: " ".\n $!end }; static void dk4opt_log_1( size_t kwind, const dkChar *progname, int logstderr ) { if (0 != logstderr) { dk4fput_initialize_stderr(); if (NULL != progname) { dk4fputs(progname, stderr, NULL); dk4fputs(dk4opt_kwnl[15], stderr, NULL); } dk4fputs(dk4opt_kwnl[kwind], stderr, NULL); fflush(stderr); } } static void dk4opt_log_3( size_t kwi1, size_t kwi2, const dkChar *vartext, const dkChar *progname, int logstderr ) { if (0 != logstderr) { dk4fput_initialize_stderr(); if (NULL != progname) { dk4fputs(progname, stderr, NULL); dk4fputs(dk4opt_kwnl[15], stderr, NULL); } dk4fputs(dk4opt_kwnl[kwi1], stderr, NULL); if (NULL != vartext) { dk4fputs(vartext, stderr, NULL); } dk4fputs(dk4opt_kwnl[kwi2], stderr, NULL); fflush(stderr); } } static void dk4opt_complain_argument( dk4_option_t *opt, const dkChar *valstr, const dkChar *argv0, int progname, int logstderr ) { if (0 != logstderr) { if ((dkT('\0') != opt->spec.c) || (NULL != opt->spec.longopt)) { dk4fput_initialize_stderr(); if ((0 != progname) && (NULL != argv0)) { dk4fputs(argv0, stderr, NULL); dk4fputs(dk4opt_kwnl[15], stderr, NULL); } dk4fputs(dk4opt_kwnl[1], stderr, NULL); if (dkT('\0') != opt->spec.c) { dk4fputs(dk4opt_kwnl[2], stderr, NULL); dk4fputc(opt->spec.c, stderr, NULL); if (NULL != opt->spec.longopt) { dk4fputs(dk4opt_kwnl[3], stderr, NULL); } } if (NULL != opt->spec.longopt) { dk4fputs(dk4opt_kwnl[4], stderr, NULL); dk4fputs(opt->spec.longopt, stderr, NULL); } dk4fputs(dk4opt_kwnl[5], stderr, NULL); switch (opt->spec.argtype) { case DK4_OPT_ARG_BOOL : { dk4fputs(dk4opt_kwnl[6], stderr, NULL); } break; case DK4_OPT_ARG_SIZE : { dk4fputs(dk4opt_kwnl[7], stderr, NULL); } break; case DK4_OPT_ARG_INT : { dk4fputs(dk4opt_kwnl[8], stderr, NULL); } break; case DK4_OPT_ARG_UNSIGNED : { dk4fputs(dk4opt_kwnl[9], stderr, NULL); } break; case DK4_OPT_ARG_DOUBLE : { dk4fputs(dk4opt_kwnl[10], stderr, NULL); } break; } dk4fputs(dk4opt_kwnl[12], stderr, NULL); if (NULL != valstr) { dk4fputs(dk4opt_kwnl[13], stderr, NULL); dk4fputs(valstr, stderr, NULL); dk4fputs(dk4opt_kwnl[14], stderr, NULL); } fflush(stderr); } } } /** Find first position of equal sign in string. Before calling this function we manuall checked there is no equal sign at position 0. @param str String to check. @return Position of first equal sign on success, 0 on error. */ static size_t dk4opt_i_equal_sign_position(const dkChar *str) { size_t back = 0; size_t num = 0; while ((dkT('\0') != *str) && (0 == back)) { if (dkT('=') == *str) { back = num; } else { str++; num++; } } return back; } /** Set argument for one option. @param opt Option to set. @param valstr String containing the value. @param logstderr Flag: Logging to stderr allowed. @param progname Program name or NULL. @return 1 on success, 0 on error. */ static int dk4opt_i_set_value( dk4_option_t *opt, dkChar *valstr, int logstderr, const dkChar *progname ) { const dkChar *ep = NULL; /* First unconvertable char */ double d = 0.0; /* Default double value */ dk4_um_t um = (dk4_um_t)0UL; /* Default unsigned value */ dk4_im_t im = (dk4_im_t)0L; /* Default integer value */ size_t sz = (size_t)0UL; /* Default size value */ int back = 0; /* Result */ $? "+ dk4opt_i_set_value \"%!ds\"", TR_STR(valstr) switch (opt->spec.argtype) { case DK4_OPT_ARG_BOOL : { $? ". boolean" opt->val.b = 0; opt->found = 0; if (0 != dk4str_is_bool(valstr)) { opt->val.b = ((0 != dk4str_is_on(valstr)) ? 1 : 0); opt->found = 1; back = 1; } else { back = dk4ma_input_dk_dec_dk4_im_t(&im, valstr, &ep, 1, NULL); if (0 != back) { opt->val.b = (((dk4_im_t)0L != im) ? 1 : 0); opt->found = 1; } else { /* ERROR: Not a boolean */ dk4opt_complain_argument( opt, valstr, progname, ((NULL != progname) ? 1 : 0), logstderr ); } } } break; case DK4_OPT_ARG_SIZE : { $? ". size" opt->val.s = (size_t)0UL; opt->found = 0; back = dk4ma_input_dk_dec_size_t(&sz, valstr, &ep, 1, NULL); if (0 != back) { opt->val.s = sz; opt->found = 1; } else { /* ERROR: Not a size */ dk4opt_complain_argument( opt, valstr, progname, ((NULL != progname) ? 1 : 0), logstderr ); } } break; case DK4_OPT_ARG_INT : { $? ". int" opt->val.i = (dk4_im_t)0L; opt->found = 0; back = dk4ma_input_dk_dec_dk4_im_t(&im, valstr, &ep, 1, NULL); if (0 != back) { opt->val.i = im; opt->found = 1; } else { /* ERROR: Not an int */ dk4opt_complain_argument( opt, valstr, progname, ((NULL != progname) ? 1 : 0), logstderr ); } } break; case DK4_OPT_ARG_UNSIGNED : { $? ". unsigned" opt->val.u = (dk4_um_t)0UL; opt->found = 0; back = dk4ma_input_dk_dec_dk4_um_t(&um, valstr, &ep, 1, NULL); if (0 != back) { opt->val.u = um; opt->found = 1; } else { /* ERROR: Not an unsigned number */ dk4opt_complain_argument( opt, valstr, progname, ((NULL != progname) ? 1 : 0), logstderr ); } } break; case DK4_OPT_ARG_DOUBLE : { $? ". double" opt->val.d = 0.0; opt->found = 0; back = dk4ma_input_dk_double(&d, valstr, &ep, 1, NULL); if (0 != back) { opt->val.d = d; opt->found = 1; } else { /* ERROR: Not a double */ dk4opt_complain_argument( opt, valstr, progname, ((NULL != progname) ? 1 : 0), logstderr ); } } break; case DK4_OPT_ARG_STRING : { $? ". string" opt->val.t = valstr; opt->found = 1; if (NULL != valstr) { back = 1; } } break; } $? "- dk4opt_i_set_value %d", back return back; } int dk4opt_process_argv( dk4_option_t *optptr, size_t szoptptr, int argc, dkChar *argv[], dkChar **filenames, size_t *nfilenames, int progname, int logstderr, dk4_er_t *erp ) { dkChar *curptr = NULL; /* Current argument to process */ dkChar *ocurptr = NULL; /* Original (complete) argument */ size_t fnused = 0; /* Used elements in filenames */ size_t j = 0; /* Option index to test */ size_t eqpos = 0; /* Equal sign position */ int i = 0; /* Current argument index */ int found = 0; /* Flag: current opt found in set */ int res = 0; /* Operation result */ int back = 0; $? "+ dk4opt_process_argv" if ((NULL != optptr) && (0 < szoptptr)) { $? ". args ok" for (j = 0; j < szoptptr; j++) { optptr[j].found = 0; optptr[j].val.t = NULL; } if ((0 < argc) && (NULL != argv)) { $? ". args ok" if ((NULL != filenames) && (NULL != nfilenames)) { for (fnused = 0; fnused < *nfilenames; fnused++) { filenames[fnused] = NULL; } fnused = 0; } back = 1; i = 0; if (0 != progname) { i = 1; } while(i < argc) { ocurptr = curptr = argv[i]; $? ". processing \"%s\"", curptr if (dkT('-') == *curptr) { curptr++; if (dkT('-') == *curptr) { curptr++; if (dkT('\0' != *curptr)) { if (dkT('=') != *curptr) { eqpos = dk4opt_i_equal_sign_position(curptr); found = 0; $? ". eqpos = %u", (unsigned)eqpos for (j = 0; ((j < szoptptr) && (0 == found)); j++) { if (NULL != optptr[j].spec.longopt) { res = 0; if (0 != eqpos) { if (eqpos == dk4str_len(optptr[j].spec.longopt)) { res = dk4str_ncmp(curptr,optptr[j].spec.longopt,eqpos); } else { res = 1; } } else { res = dk4str_cmp(curptr, optptr[j].spec.longopt); } if (0 == res) { if (0 != optptr[j].found) { /* WARNING: Option used multiple times */ dk4opt_log_3( 20, 21, ocurptr, ((0 != progname) ? argv[0] : NULL), logstderr ); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } found = 1; switch (optptr[j].spec.argtype) { case DK4_OPT_ARG_BOOL : case DK4_OPT_ARG_SIZE : case DK4_OPT_ARG_INT : case DK4_OPT_ARG_UNSIGNED : case DK4_OPT_ARG_DOUBLE : case DK4_OPT_ARG_STRING : { if (0 < eqpos) { curptr = &(curptr[eqpos+1]); if (dkT('\0') == *curptr) { curptr = NULL; } } else { curptr = NULL; } if (NULL == curptr) { if (++i < argc) { curptr = argv[i]; } } if (NULL != curptr) { res = dk4opt_i_set_value( &(optptr[j]), curptr, logstderr, ((0 != progname) ? argv[0] : NULL) ); if (0 == res) { back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { back = 0; /* ERROR: Option required argument */ dk4opt_complain_argument( &(optptr[j]), NULL, argv[0], progname, logstderr ); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } break; default : { optptr[j].found = 1; if (0 < eqpos) { /* WARNING: Option does not use argument */ dk4opt_log_3( 22, 23, ocurptr, ((0 != progname) ? argv[0] : NULL), logstderr ); } } break; } } } } if (0 == found) { back = 0; /* ERROR: Illegal option */ dk4opt_log_3( 18,19,ocurptr,((0 != progname) ? argv[0] : NULL),logstderr ); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { back = 0; /* ERROR: Long option without name */ dk4opt_log_3( 18, 19, ocurptr, ((0 != progname) ? argv[0] : NULL), logstderr ); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { if ((NULL != filenames) && (NULL != nfilenames)) { if (fnused < *nfilenames) { filenames[fnused++] = ocurptr; } else { back = 0; /* ERROR: Too many file names */ dk4opt_log_1(17,((0 != progname) ? argv[0] : NULL),logstderr); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { back = 0; /*ERROR: No file names expected */ dk4opt_log_1(16, ((0 != progname) ? argv[0] : NULL), logstderr); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } } else { if (dkT('\0') != *curptr) { found = 0; for (j = 0; ((j < szoptptr) && (0 == found)); j++) { if (optptr[j].spec.c == *curptr) { if (0 != optptr[j].found) { /* WARNING: Option used multiple times */ dk4opt_log_3( 20,21,ocurptr,((0 != progname) ? argv[0] : NULL),logstderr ); } found = 1; switch(optptr[j].spec.argtype) { case DK4_OPT_ARG_BOOL : case DK4_OPT_ARG_SIZE : case DK4_OPT_ARG_INT : case DK4_OPT_ARG_UNSIGNED : case DK4_OPT_ARG_DOUBLE : case DK4_OPT_ARG_STRING : { curptr++; if (dkT('\0') == *curptr) { curptr = NULL; if (++i < argc) { curptr = argv[i]; } } if (NULL != curptr) { res = dk4opt_i_set_value( &(optptr[j]), curptr, logstderr, ((0 != progname) ? argv[0] : NULL) ); if (0 == res) { back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { back = 0; /* ERROR: Option requires argument */ dk4opt_complain_argument( &(optptr[j]), NULL, argv[0], progname, logstderr ); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } break; default : { optptr[j].found = 1; } break; } } } if (0 == found) { back = 0; /* ERROR: Option not found in specification */ dk4opt_log_3( 18, 19, ocurptr, ((0 != progname) ? argv[0] : NULL), logstderr ); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { if ((NULL != filenames) && (NULL != nfilenames)) { if (fnused < *nfilenames) { filenames[fnused++] = ocurptr; } else { back = 0; /* ERROR: Too many file names */ dk4opt_log_1(17,((0 != progname) ? argv[0] : NULL),logstderr); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { back = 0; /* ERROR: No file names expected */ dk4opt_log_1(16, ((0 != progname) ? argv[0] : NULL), logstderr); dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } } } else { if ((NULL != filenames) && (NULL != nfilenames)) { if (fnused < *nfilenames) { filenames[fnused++] = curptr; } else { back = 0; /* ERROR: Too many file names */ dk4opt_log_1(17, ((0 != progname) ? argv[0] : NULL), logstderr); dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL); } } else { back = 0; /* ERROR: No file names expected */ dk4opt_log_1(16, ((0 != progname) ? argv[0] : NULL), logstderr); dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL); } } i++; } } else { /* ERROR: Arguments */ dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } } else { /* ERROR: Arguments */ dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } if (NULL != nfilenames) { *nfilenames = fnused; } $? "- dk4opt_process_argv %d", back return back; }