/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a * comma-separated list of interface rules (see cf_opt_network_interface_legacy() for the format of * each rule), then parses the regular config array-of-struct style interface option settings so * that both forms are supported. * * @author Andrew Bettison <*****@*****.**> */ int cf_opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node) { if (!node->text) return cf_opt_config_interface_list(listp, node); if (node->nodc) { cf_warn_incompatible_children(node); return CFINCOMPATIBLE; } const char *p; const char *arg = NULL; unsigned n = listp->ac; int result = CFOK; for (p = node->text; n < NELS(listp->av); ++p) { if (*p == '\0' || *p == ',' || isspace(*p)) { if (arg) { int len = p - arg; if (len > 80) { result |= CFSTRINGOVERFLOW; goto bye; } char buf[len + 1]; strncpy(buf, arg, len)[len] = '\0'; int ret = cf_opt_network_interface_legacy(&listp->av[n].value, buf); switch (ret) { case CFERROR: return CFERROR; case CFOK: listp->av[n].key = n; ++n; break; default: { strbuf b = strbuf_alloca(180); strbuf_cf_flag_reason(b, ret); cf_warn_node(node, NULL, "invalid interface rule %s -- %s", alloca_str_toprint(buf), strbuf_str(b)); \ result |= CFSUB(ret); break; } } arg = NULL; } if (!*p) break; } else if (!arg) arg = p; } if (*p) { result |= CFARRAYOVERFLOW; goto bye; } assert(n <= NELS(listp->av)); listp->ac = n; bye: if (listp->ac == 0) result |= CFEMPTY; return result; }
char const * hxmode(int mode) { static char const *modev[] = { "READ", "UPDATE", "CHECK", "REPAIR", "READ,MMAP", "UPDATE,MMAP", "INVALID:CHECK+MMAP", "INVALID:REPAIR+MMAP", "DUBIOUS:READ+MPROTECT", "DUBIOUS:UPDATE+MPROTECT", "INVALID:CHECK+MPROTECT", "INVALID:REPAIR+MPROTECT", "READ+MPROTECT", "UPDATE+MPROTECT", "INVALID:CHECK+MMAP+MPROTECT", "INVALID:REPAIR+MMAP+MPROTECT", "DUBIOUS:READ+FSYNC", "UPDATE,FSYNC", "DUBIOUS:CHECK+FSYNC", "REPAIR,FSYNC", }; return mode < 0 ? hxerror(mode) : mode < NELS(modev) ? modev[mode] : "INVALID"; }
int cf_opt_pattern_list(struct pattern_list *listp, const char *text) { struct pattern_list list; memset(&list, 0, sizeof list); const char *word = NULL; const char *p; for (p = text; ; ++p) { if (!*p || isspace(*p) || *p == ',') { if (word) { size_t len = p - word; if (list.patc >= NELS(list.patv) || len >= sizeof(list.patv[list.patc])) return CFARRAYOVERFLOW; strncpy(list.patv[list.patc++], word, len)[len] = '\0'; word = NULL; } if (!*p) break; } else if (!word) word = p; } assert(word == NULL); if (list.patc == 0) return CFEMPTY; *listp = list; return CFOK; }
char const * hxerror(HXRET ret) { # undef _C # undef _E # define _C , # define _E(x) #x static char const *errv[] = { "ok", HXERRS }; static char num[11]; if (ret > 0 || -ret >= NELS(errv)) return (sprintf(num, "%d", ret), num); return errv[-ret]; }
static int restful_rhizome_bundlelist_json_content_chunk(struct http_request *hr, strbuf b) { httpd_request *r = (httpd_request *) hr; const char *headers[] = { ".token", "_id", "service", "id", "version", "date", ".inserttime", ".author", ".fromhere", "filesize", "filehash", "sender", "recipient", "name" }; switch (r->u.rhlist.phase) { case LIST_HEADER: strbuf_puts(b, "{\n\"header\":["); unsigned i; for (i = 0; i != NELS(headers); ++i) { if (i) strbuf_putc(b, ','); strbuf_json_string(b, headers[i]); } strbuf_puts(b, "],\n\"rows\":["); if (!strbuf_overrun(b)) r->u.rhlist.phase = LIST_ROWS; return 1; case LIST_ROWS: { int ret = rhizome_list_next(&r->u.rhlist.cursor); if (ret == -1) return -1; if (ret == 0) { time_ms_t now; if (r->u.rhlist.cursor.rowid_since == 0 || (now = gettime_ms()) >= r->u.rhlist.end_time) { r->u.rhlist.phase = LIST_END; return 1; } time_ms_t wake_at = now + config.rhizome.api.restful.newsince_poll_ms; if (wake_at > r->u.rhlist.end_time) wake_at = r->u.rhlist.end_time; http_request_pause_response(&r->http, wake_at); return 0; } rhizome_manifest *m = r->u.rhlist.cursor.manifest; assert(m->filesize != RHIZOME_SIZE_UNSET); rhizome_lookup_author(m); if (r->u.rhlist.rowcount != 0) strbuf_putc(b, ','); strbuf_puts(b, "\n["); if (m->rowid > r->u.rhlist.rowid_highest) { strbuf_json_string(b, alloca_list_token(m->rowid)); r->u.rhlist.rowid_highest = m->rowid; } else strbuf_json_null(b); strbuf_putc(b, ','); strbuf_sprintf(b, "%"PRIu64, m->rowid); strbuf_putc(b, ','); strbuf_json_string(b, m->service); strbuf_putc(b, ','); strbuf_json_hex(b, m->cryptoSignPublic.binary, sizeof m->cryptoSignPublic.binary); strbuf_putc(b, ','); strbuf_sprintf(b, "%"PRIu64, m->version); strbuf_putc(b, ','); if (m->has_date) strbuf_sprintf(b, "%"PRItime_ms_t, m->date); else strbuf_json_null(b); strbuf_putc(b, ','); strbuf_sprintf(b, "%"PRItime_ms_t",", m->inserttime); switch (m->authorship) { case AUTHOR_LOCAL: case AUTHOR_AUTHENTIC: strbuf_json_hex(b, m->author.binary, sizeof m->author.binary); strbuf_puts(b, ",1,"); break; default: strbuf_json_null(b); strbuf_puts(b, ",1,"); break; } strbuf_sprintf(b, "%"PRIu64, m->filesize); strbuf_putc(b, ','); strbuf_json_hex(b, m->filesize ? m->filehash.binary : NULL, sizeof m->filehash.binary); strbuf_putc(b, ','); strbuf_json_hex(b, m->has_sender ? m->sender.binary : NULL, sizeof m->sender.binary); strbuf_putc(b, ','); strbuf_json_hex(b, m->has_recipient ? m->recipient.binary : NULL, sizeof m->recipient.binary); strbuf_putc(b, ','); strbuf_json_string(b, m->name); strbuf_puts(b, "]"); if (!strbuf_overrun(b)) { rhizome_list_commit(&r->u.rhlist.cursor); ++r->u.rhlist.rowcount; } } return 1; case LIST_END: strbuf_puts(b, "\n]\n}\n"); if (!strbuf_overrun(b)) r->u.rhlist.phase = LIST_DONE; // fall through... case LIST_DONE: return 0; } abort(); }
/* Returns 0 if a command is matched and parsed, with the results of the parsing in the '*parsed' * structure. * * Returns 1 and logs an error if no command matches the argument list, contents of '*parsed' are * undefined. * * Returns 2 if the argument list is ambiguous, ie, matches more than one command, contents of * '*parsed' are undefined. * * Returns -1 and logs an error if the parsing fails due to an internal error (eg, malformed command * schema), contents of '*parsed' are undefined. * * @author Andrew Bettison <*****@*****.**> */ int cli_parse(const int argc, const char *const *args, const struct cli_schema *commands, struct cli_parsed *parsed) { int ambiguous = 0; int matched_cmd = -1; int cmd; for (cmd = 0; commands[cmd].function; ++cmd) { struct cli_parsed cmdpa; memset(&cmdpa, 0, sizeof cmdpa); cmdpa.commands = commands; cmdpa.cmdi = cmd; cmdpa.args = args; cmdpa.argc = argc; cmdpa.labelc = 0; cmdpa.varargi = -1; const char *pattern = NULL; unsigned arg = 0; unsigned opt = 0; while ((pattern = commands[cmd].words[opt])) { //DEBUGF("cmd=%d opt=%d pattern='%s' args[arg=%d]='%s'", cmd, opt, pattern, arg, arg < argc ? args[arg] : ""); unsigned patlen = strlen(pattern); if (cmdpa.varargi != -1) return WHYF("Internal error: commands[%d].word[%d]=\"%s\" - more words not allowed after \"...\"", cmd, opt, commands[cmd].words[opt]); /* These are the argument matching rules: * * "..." consumes all remaining arguments * * "word" consumes one argument that exactly matches "word", does not label it (this is the * "simple" case in the code below; all other rules label something that matched) * * "word1|word2|...|wordN" consumes one argument that exactly matches "word1" or "word2" etc. * or "wordN", labels it with the matched word (an empty alternative, eg "|word" does not * match an empty argument) * * (as a special case of the above rule, "|word" consumes one argument that exactly matches * "word" and labels it "word", but it appears in the help description as "word") * * "<label>" consumes exactly one argument "ANY", records it with label "label" * * "prefix=<any>" consumes one argument "prefix=ANY" or two arguments "prefix" "ANY", * and records the text matching ANY with label "prefix" * * "prefix <any>" consumes one argyment "prefix ANY" if available or two arguments "prefix" * "ANY", and records the text matching ANY with label "prefix" * * "prefix<any>" consumes one argument "prefixANY", and records the text matching ANY with * label "prefix" * * "[ANY]..." consumes all remaining arguments which match ANY, as defined below * * "[word]" consumes one argument if it exactly matches "word", records it with label * "word" * * "[word1|word2|...|wordN]" consumes one argument if it exactly matches "word1" or "word2" * etc. or "wordN", labels it with the matched word * * "[<label>]" consumes one argument "ANY" if available, records it with label "label" * * "[prefix=<any>]" consumes one argument "prefix=ANY" if available or two arguments * "prefix" "ANY" if available, records the text matching ANY with label "prefix" * * "[prefix <any>]" consumes one argument "prefix ANY" if available or two arguments * "prefix" "ANY" if available, records the text matching ANY with label "prefix" * * "[prefix<any>]" consumes one argument "prefixANY" if available, records the text matching * ANY with label "prefix" */ if (patlen == 3 && pattern[0] == '.' && pattern[1] == '.' && pattern[2] == '.') { cmdpa.varargi = arg; arg = argc; ++opt; } else { int optional = 0; int repeating = 0; if (patlen > 5 && pattern[0] == '[' && pattern[patlen-4] == ']' && pattern[patlen-3] == '.' && pattern[patlen-2] == '.' && pattern[patlen-1] == '.') { optional = repeating = 1; pattern += 1; patlen -= 5; } else if (patlen > 2 && pattern[0] == '[' && pattern[patlen-1] == ']') { optional = 1; pattern += 1; patlen -= 2; } unsigned oarg = arg; const char *text = NULL; const char *label = NULL; unsigned labellen = 0; const char *word = pattern; unsigned wordlen = 0; char simple = 0; unsigned alt = 0; if (patlen && *word == '|') { ++alt; ++word; } if (patlen == 0) return WHYF("Internal error: commands[%d].word[%d]=\"%s\" - empty words not allowed", cmd, opt, commands[cmd].words[opt]); for (; word < &pattern[patlen]; word += wordlen + 1, ++alt) { // Skip over empty "||word" alternative (but still count it). if (*word == '|') return WHYF("Internal error: commands[%d].word[%d]=\"%s\" - empty alternatives not allowed", cmd, opt, commands[cmd].words[opt]); // Find end of "word|" alternative. wordlen = 1; while (&word[wordlen] < &pattern[patlen] && word[wordlen] != '|') ++wordlen; // Skip remaining alternatives if we already got a match. if (text) continue; // Look for a match. const char *prefix = NULL; unsigned prefixlen = 0; char prefixarglen = 0; const char *caret = strchr(word, '<'); if (wordlen > 2 && caret && word[wordlen-1] == '>') { if ((prefixarglen = prefixlen = caret - word)) { prefix = word; if (prefixlen > 1 && (prefix[prefixlen-1] == '=' || prefix[prefixlen-1] == ' ')) --prefixarglen; label = prefix; labellen = prefixarglen; if (arg < argc) { unsigned arglen = strlen(args[arg]); if (arglen >= prefixlen && strncmp(args[arg], prefix, prefixlen) == 0) { text = args[arg++] + prefixlen; } else if (arg + 1 < argc && arglen == prefixarglen && strncmp(args[arg], prefix, prefixarglen) == 0) { ++arg; text = args[arg++]; } } } else { label = &word[1]; labellen = wordlen - 2; if (arg < argc) text = args[arg++]; } } else if (arg < argc && strlen(args[arg]) == wordlen && strncmp(args[arg], word, wordlen) == 0) { simple = 1; text = args[arg]; label = word; labellen = wordlen; ++arg; } } assert(alt > 0); if (arg == oarg && !optional) break; if (labellen && text && (optional || !simple || alt > 1)) { if (cmdpa.labelc >= NELS(cmdpa.labelv)) return WHYF("Internal error: commands[%d].word[%d]=\"%s\" - label limit exceeded", cmd, opt, commands[cmd].words[opt]); cmdpa.labelv[cmdpa.labelc].label = label; cmdpa.labelv[cmdpa.labelc].len = labellen; cmdpa.labelv[cmdpa.labelc].text = text; ++cmdpa.labelc; if (!repeating) ++opt; } else ++opt; } } //DEBUGF("cmd=%d opt=%d args[arg=%d]='%s'", cmd, opt, arg, arg < argc ? args[arg] : ""); if (!pattern && arg == argc) { /* A match! We got through the command definition with no internal errors and all literal args matched and we have a proper number of args. If we have multiple matches, then note that the call is ambiguous. */ if (matched_cmd >= 0) ++ambiguous; if (ambiguous == 1) { NOWHENCE(WHY_argv("Ambiguous command:", argc, args)); NOWHENCE(HINT("Matches the following:")); NOWHENCE(HINT_argv(" ", argc, commands[matched_cmd].words)); } if (ambiguous) NOWHENCE(HINT_argv(" ", argc, commands[cmd].words)); matched_cmd = cmd; *parsed = cmdpa; } } /* Don't process ambiguous calls */ if (ambiguous) return 2; /* Complain if we found no matching calls */ if (matched_cmd < 0) { if (argc) NOWHENCE(WHY_argv("Unknown command:", argc, args)); return 1; } return 0; }