📄 clif.c
字号:
/* Copyright (c) 2000, 2003 Dmitry Butskoy <buc@citadel.stu.neva.ru> License: LGPL v2.1 or any later See COPYING.LIB for the status of this software.*/#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdarg.h>#include "clif.h"#if 1 /* Bad idea, anyway... */#define MAX_ARGC_NUMBER 256typedef unsigned char _CLIF_index;#else#define MAX_ARGC_NUMBER (4096 / 5 + 1) /* POSIX ARG_MAX >= 4096 ... */typedef unsigned short _CLIF_index;#endif/* This is needed for some print info functions. This is ugly for thread-safe (is it really actual on program invoking?), and for several CLIF_parse_cmdline invoking... But foo on this. Yeah...*/static struct { int argc; char **argv; CLIF_option *option_list; CLIF_argument *argument_list; unsigned int parse_flags;} curr = { 0, }; static void err_report (const char *format, ...) { va_list ap; if (curr.parse_flags & CLIF_SILENT) return; va_start (ap, format); vfprintf (stderr, format, ap); va_end (ap); fprintf (stderr, "\n"); return;}/* info generation stuff... */#define SHORT_PLUS_MINUS "+/-"#define LONG_PLUS_MINUS "++/--"#define EXCL_DLM " | "static char *show_short (const CLIF_option *optn) { static char buf[80]; char *p = buf; unsigned int flags = optn->flags | curr.parse_flags; if (optn->function_plus) { if (!optn->function) *p++ = '+'; else { strcpy (p, SHORT_PLUS_MINUS); p += sizeof (SHORT_PLUS_MINUS) - 1; } } else *p++ = '-'; *p++ = optn->short_opt[0]; if (optn->arg_name) { char *endp = buf + sizeof (buf) - sizeof (",...]"); const char *s; if (!(flags & _CLIF_STRICT_JOIN_ARG)) *p++ = ' '; if (flags & CLIF_OPTARG) *p++ = '['; s = optn->arg_name; while (*s && p < endp) *p++ = *s++; if (flags & CLIF_SEVERAL) { strcpy (p, ",..."); p += sizeof (",...") - 1; /* last '\0' ... */ } if (flags & CLIF_OPTARG) *p++ = ']'; } *p = '\0'; return buf;} static char *show_long (const CLIF_option *optn) { static char buf[80]; char *p = buf; char *endp; const char *s; unsigned int flags = optn->flags | curr.parse_flags; if (!(flags & _CLIF_STRICT_KEYWORD)) { if (!(flags & _CLIF_STRICT_ONEDASH)) { if (optn->function_plus) { if (!optn->function) { *p++ = '+'; *p++ = '+'; } else { strcpy (p, LONG_PLUS_MINUS); p += sizeof (LONG_PLUS_MINUS) - 1; } } else { *p++ = '-'; *p++ = '-'; } } else { if (optn->function_plus) { if (!optn->function) *p++ = '+'; else { strcpy (p, SHORT_PLUS_MINUS); p += sizeof (SHORT_PLUS_MINUS) - 1; } } else *p++ = '-'; } } s = optn->long_opt; endp = buf + sizeof (buf) - sizeof (" ["); while (*s && p < endp) *p++ = *s++; if (optn->arg_name) { if (flags & _CLIF_STRICT_NOEQUAL) { *p++ = ' '; if (flags & CLIF_OPTARG) *p++ = '['; } else { if (flags & CLIF_OPTARG) *p++ = '['; *p++ = '='; } s = optn->arg_name; endp = buf + sizeof (buf) - sizeof (",...]"); while (*s && p < endp) *p++ = *s++; if (flags & CLIF_SEVERAL) { strcpy (p, ",..."); p += sizeof (",...") - 1; /* last '\0' ... */ } if (flags & CLIF_OPTARG) *p++ = ']'; } *p = '\0'; return buf;}static char *show_excl (const CLIF_option *option_list, int *cnt_p) { static char buf[256]; const CLIF_option *optn; char *p = buf; char *endp = buf + sizeof (buf) - sizeof (EXCL_DLM); int excl_cnt = 0; *p = '\0'; if (cnt_p) *cnt_p = 0; if (!option_list) return buf; for (optn = option_list; optn->short_opt || optn->long_opt; optn++) { char *s; if (!(optn->flags & CLIF_EXCL)) continue; if (optn->short_opt) s = show_short (optn); else s = show_long (optn); if (excl_cnt > 0) { /* i.e., second etc... */ strcpy (p, EXCL_DLM); p += sizeof (EXCL_DLM) - 1; } while (*s && p < endp) *p++ = *s++; excl_cnt++; } *p = '\0'; if (cnt_p) *cnt_p = excl_cnt; return buf;}static int is_keyword (const CLIF_option *optn) { unsigned int flags = optn->flags | curr.parse_flags; return (flags & _CLIF_STRICT_KEYWORD) != 0;}static void err_bad_opt (const char *arg, char c, int n) { char sym = (*arg == '+') ? '+' : '-'; if (c) err_report ("Bad option `%c%c' (argc %d)", sym, c, n); else { char *p = strchr (arg, '='); const char *type = (*arg == sym) ? "option" : "keyword"; if (p) err_report ("Bad %s `%s' (with arg `%s') (argc %d)", type, arg, p + 1, n); else err_report ("Bad %s `%s' (argc %d)", type, arg, n); }}static void err_bad_arg (const CLIF_option *optn, char c, int n) { CLIF_option tmp = *optn; char ss[80]; char *s; tmp.arg_name = NULL; if (c) { s = show_short (&tmp); /* always without arg... */ strncpy (ss, s, sizeof (ss)); s = show_short (optn); } else { s = show_long (&tmp); /* always without arg... */ strncpy (ss, s, sizeof (ss)); s = show_long (optn); } err_report ("%s `%s' (argc %d) requires an argument: `%s'", (c || !is_keyword (optn)) ? "Option" : "Keyword", ss, n, s);} static void err_bad_res (const CLIF_option *optn, char c, const char *opt_arg, int n) { CLIF_option tmp = *optn; char *ss; const char *type; tmp.arg_name = NULL; if (c) { ss = show_short (&tmp); type = "option"; } else { ss = show_long (&tmp); type = is_keyword (optn) ? "keyword" : "option"; } if (optn->arg_name) err_report ("Cannot handle `%s' %s with arg `%s' (argc %d)", ss, type, opt_arg, n); else err_report ("Cannot handle `%s' %s (argc %d)", ss, type, n);}static void err_bad_excl (const CLIF_option *optn, char c, int n) { CLIF_option tmp = *optn; char *ss; char *excl = show_excl (curr.option_list, 0); /* Note: show_(short|long)() nested!!! */ tmp.arg_name = NULL; if (c) ss = show_short (&tmp); else ss = show_long (&tmp); err_report ("%s `%s' (argc %d): Only one of:\n %s\n" "may be specified.", (c || !is_keyword (optn)) ? "Option" : "Keyword", ss, n, excl);}static CLIF_option *find_long (char *arg, char **arg_p, unsigned int match, unsigned int nomatch) { CLIF_option *optn; CLIF_option *abbrev = NULL; char *abbrev_arg = NULL; int abbrev_found = 0; for (optn = curr.option_list; optn->short_opt || optn->long_opt; optn++ ) { char *a; const char *o; unsigned int flags; if (!optn->long_opt) continue; flags = curr.parse_flags | optn->flags; if (flags & nomatch) continue; if (match && !(flags & match)) continue; /* XXX: optimize it */ for (a = arg, o = optn->long_opt; *o && *a == *o; a++, o++) ; if (*a == '\0' || (*a == '=' && optn->arg_name && !(flags & _CLIF_STRICT_NOEQUAL)) ) { /* looks like end of option... */ if (!*o) { /* explicit match found */ if (*a == '=' && arg_p) *arg_p = a + 1; return optn; } if ((flags & CLIF_ABBREV) && (a - arg >= CLIF_MIN_ABBREV) ) { if (!abbrev_found) { abbrev_found = 1; abbrev = optn; if (*a == '=') abbrev_arg = a + 1; } else /* several possibility case... */ abbrev = NULL; } } } if (abbrev) { /* implicit match found */ if (abbrev_arg && arg_p) *arg_p = abbrev_arg; return abbrev; } else /* no match found */ return NULL;}static int check_sym (const CLIF_option *optn, char sym) { if (sym == '+') { if (!optn->function_plus) return -1; } else if (sym == '-') { if (!optn->function && optn->function_plus) return -1; } return 0;}static int call_function (CLIF_option *optn, char *opt_arg, char sym) { int (*function) (CLIF_option *, char *); function = (sym == '+') ? optn->function_plus : optn->function; if (!function) return 0; if (opt_arg && ((optn->flags | curr.parse_flags) & CLIF_SEVERAL)) { char tmp[80]; char *t; char *endt = tmp + sizeof (tmp); while (*opt_arg) { t = tmp; while (t < endt && *opt_arg && *opt_arg != ' ' && *opt_arg != '\t' && *opt_arg != ',' ) *t++ = *opt_arg++; if (t >= endt) return -1; *t = '\0'; if (function (optn, tmp) < 0) return -1; while (*opt_arg == ' ' || *opt_arg == '\t' || *opt_arg == ',') opt_arg++; } return 0; } return function (optn, opt_arg);}int CLIF_parse_cmdline (int argc, char *argv[], CLIF_option *option_list, CLIF_argument *argument_list, unsigned int parse_flags) { int i, j; CLIF_option *optn; CLIF_argument *argm; int num_args = 0; int num_argm = 0, strict_beg = 0, strict_end = 0; _CLIF_index arg_n[MAX_ARGC_NUMBER]; unsigned int dirty_flags = 0; int dirty_plus = 0; int exclusive_cnt = 0; int posix = getenv ("POSIXLY_CORRECT") != NULL || (parse_flags & CLIF_POSIX); curr.argc = argc; curr.argv = argv; curr.option_list = option_list; curr.argument_list = argument_list; curr.parse_flags = parse_flags; if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) { CLIF_current_help (); exit (0); } /* Scan argument_list for check and some info. */ if (argument_list) { enum stages { STRICT_BEG, OPTIONAL, STRICT_END }; int stage = STRICT_BEG; for (argm = argument_list; argm->name; argm++) { if (argm->flags & CLIF_STRICT) { if (stage == STRICT_BEG) strict_beg++; else if (stage == OPTIONAL) { stage = STRICT_END; strict_end++; } else if (stage == STRICT_END) strict_end++; } else { if (stage == STRICT_BEG) stage = OPTIONAL; else if (stage == STRICT_END) { err_report ("Incorrect argument list set in program " "source: more than one optional area."); return -1; } } num_argm++; } } /* Scan option_list for some info. */ if (option_list) { dirty_flags = parse_flags; for (optn = option_list; optn->short_opt || optn->long_opt; optn++ ) { dirty_flags |= optn->flags; if (optn->function_plus) dirty_plus = 1; } } if (dirty_flags & CLIF_EXCL) exclusive_cnt = 1; /* only one is allowed... */ /* Go ! Store arguments, parse options. */ for (i = 1; i < argc; i++) { char *arg = argv[i]; char *opt_arg = NULL; char sym = '-'; if (!option_list) goto handle_arg; if (*arg == '+' && dirty_plus) sym = '+'; if (*arg != sym) { /* argument or keyword */ if (dirty_flags & CLIF_MAY_KEYWORD) { optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0); if (optn) goto long_found; } if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) { /* ugly... */ parse_flags &= ~CLIF_FIRST_GROUP; dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */ goto handle_short; } /* else it is an argument */ goto handle_arg; } else if (*++arg == sym) { /* `--' - long option */ arg++; if (*arg == sym || /* `---' - let it be not option... */ (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH)) ) { arg -= 2; goto handle_arg; /* not option anyway */ } optn = find_long (arg, &opt_arg, 0, _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH); if (optn) goto long_found; /* XXX: May be allow only for `--', not `++' too... */ if (!*arg && sym == '-') { /* `--' and no empty longoption */ option_list = NULL; /* POSIX way... */ continue; } /* XXX: or treat as an argument sometimes??? */ err_bad_opt (argv[i], 0, i); return -1; } else { /* short option, or several short options... */ if (dirty_flags & CLIF_MAY_ONEDASH) { optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0); if (optn) goto long_found; } if (!*arg) { /* POSIX say: only "stdout specification"... */ arg--; goto handle_arg; } goto handle_short; } long_found: if (check_sym (optn, sym) < 0) { /* Oops... */ err_bad_opt (argv[i], 0, i); return -1; } if (optn->flags & CLIF_EXCL) { if (!exclusive_cnt) { err_bad_excl (optn, 0, i); return -1; } exclusive_cnt--; } if (optn->arg_name && !opt_arg) { unsigned int flags = optn->flags | parse_flags; if (++i >= argc || !(flags & CLIF_MAY_NOEQUAL) ) { /* missing opt arg */ i--; if (!(flags & CLIF_OPTARG)) { err_bad_arg (optn, 0, i); return -1; } opt_arg = NULL; } else opt_arg = argv[i]; } if (call_function (optn, opt_arg, sym) < 0) { err_bad_res (optn, 0, opt_arg, i); return -1; } if (optn->flags & CLIF_EXIT) exit (0); continue; handle_arg: if (argument_list) { if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */ arg_n[num_args++] = i; } else { err_report ("`%s' (argc %d): arguments are not allowed", argv[i], i); return -1; } /* POSIX say: No more options after args... */ if (posix) option_list = NULL; /* geniously... */ continue; handle_short: opt_arg = NULL; do { for (optn = option_list; optn->short_opt || optn->long_opt; optn++ ) { if (optn->short_opt && optn->short_opt[0] == *arg) break; } if (!optn->short_opt || check_sym (optn, sym) < 0 ) { err_bad_opt (argv[i], *arg, i); return -1; } if (optn->flags & CLIF_EXCL) { if (!exclusive_cnt) { err_bad_excl (optn, *arg, i); return -1; } exclusive_cnt--; } if (optn->arg_name) { unsigned int flags = parse_flags | optn->flags; if (arg[1] == '\0') { /* a last one */ /* POSIX say: an option with arg cannot be grouped. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -