/** * <p> * The given string contains a list of substrings separated by the * specified delimiter characters. The substrings may contain quoted * strings and/or contain backslash-escaped characters. The common * backslash escape sequences are supported and return their ASCII * values. * </p> * * @param string * A list represented as a string. * * @param delims * A set of delimiter characters. * * @param flags * * TOKEN_KEEP_EMPTY * * If false, then a run of one or more delimeters is treated as a * single delimeter separating tokens. Otherwise each delimeter * separates a token that may be empty. * * string true false * ------------------------------------------- * [a,b,c] [a] [b] [c] [a] [b] [c] * [a,,c] [a] [] [c] [a] [c] * [a,,] [a] [] [] [a] * [,,] [] [] [] (null) * [] [] (null) * * TOKEN_KEEP_BACKSLASH * * The token might have backslash escapes that are suppose to be * part of the token, like a regex string /RE/ where you need to * keep any "\/" between the open and closing slashes. We still * need to recognise escapes and not convert them to a literal. * * TOKEN_IGNORE_QUOTES * * Disable any special processing of quoted substrings; quotes * are treated as literals. * * TOKEN_KEEP_ASIS * * Shorthand for TOKEN_KEEP_BACKSLASH | TOKEN_IGNORE_QUOTES. * * TOKEN_KEEP_BRACKETS * * Split strings with brackets, keeping the open and close: * parenthesis, "(" and ")"; angle brackets, "<" and ">"; square * brackets, "[" and "]"; and/or braces, "{" and "}" grouped * together. Both open and close brackets must in the set of * delimiters. For example: * * string delims vector * ------------------------------------------- * "a{b}c" "{}" "a", "{b}", "c" * "a{{b}}c" "{}" "a", "{{b}}", "c" * "a{{b\{c}}d" "{}" "a", "{{b{c}}", "d" * "a{{b[(<c}}d" "{}" "a", "{{b[(<c}}", "d" * "a{b{c}{d}e}f" "{}" "a", "{b{c}{d}e}", "f" * "<>a{b<c>d}<e>" "{}<>" "<>", "a", "{b<c>d}", "<e>", "" * * @return * A vector of C strings. */ Vector TextSplit(const char *string, const char *delims, int flags) { char *token; Vector list; if ((list = VectorCreate(5)) == NULL) return NULL; VectorSetDestroyEntry(list, free); while ((token = TokenNext(string, &string, delims, flags)) != NULL) (void) VectorAdd(list, token); return list; }
static void do_command(struct pattern_rule *rule, const char *command, const char *line, regmatch_t *parens) { int err; char *cmd, *expand; /* Parse field="..." */ cmd = TokenNext(strchr(command, '=')+1, NULL, "\n", TOKEN_KEEP_ASIS); /* Replace #n matched sub-expressions. */ expand = replace_references('#', cmd, line, parens, rule->re.re_nsub); if (expand != NULL) { if ((err = system(expand)) < 0) (void) fprintf(stderr, "%s\n\t%s (%d)\n", expand, strerror(errno), errno); else if (0 < err) (void) fprintf(stderr, "%s\n\tcommand error %d\n", expand, err); free(expand); } free(cmd); }
/* * Pattern Rule Grammar * -------------------- * * line := blank | "#" comment | rule * * rule := pattern *[ whitespace ] actions * * whitespace := SPACE | TAB * * pattern := "/" extended_regex "/" * * actions := action [ ";" actions ] * * action := thread | limit | report | on_exceed | on_expire * * quote := "'" | '"' * * index := number | quote text quote * * indices := index [ "," index ] * * thread := "t=" indices * * limit := "l=" indices "," max "/" period [ unit ] * * report := "r=" mail *[ "," mail ] * * on_exceed := "c=" quoted_shell_command * * on_expire := "C=" quoted_shell_command * * quoted_shell_command := quote text quote */ static int init_rule(char *line) { Vector fields; const char *next; struct pattern_rule *rule; int i, rc = -1, err; char *field, error[128]; if (*line != '/') goto error0; if ((rule = calloc(1, sizeof (*rule))) == NULL) goto error0; if ((rule->pattern = TokenNext(line, &next, "/", TOKEN_KEEP_ASIS)) == NULL) goto error1; if ((fields = TextSplit(next, ";", TOKEN_KEEP_ASIS)) == NULL) goto error1; if ((err = regcomp(&rule->re, rule->pattern, REG_EXTENDED|REG_NEWLINE)) != 0) { (void) regerror(err, &rule->re, error, sizeof (error)); (void) fprintf(stderr, "pattern /%s/: %s (%d)\n", rule->pattern, error, err); goto error2; } /* Assume new previously unknown pattern. */ if ((err = sqlite3_bind_text(ctx.insert_pattern, 1, rule->pattern, -1, SQLITE_STATIC)) != SQLITE_OK) { sql_error(__FILE__, __LINE__, ctx.insert_pattern); goto error2; } if ((err = sql_step(ctx.db, ctx.insert_pattern)) != SQLITE_DONE) { sql_error(__FILE__, __LINE__, ctx.insert_pattern); goto error2; } rule->id_pattern = sqlite3_last_insert_rowid(ctx.db); (void) sqlite3_clear_bindings(ctx.insert_pattern); sqlite3_reset(ctx.insert_pattern); /* Last insert rowid is zero if no row was inserted, thus it * already exists and we need to find the pattern number (OID). */ if (rule->id_pattern == 0) { if ((err = sqlite3_bind_text(ctx.find_pattern, 1, rule->pattern, -1, SQLITE_STATIC)) != SQLITE_OK) { sql_error(__FILE__, __LINE__, ctx.find_pattern); goto error2; } if ((err = sql_step(ctx.db, ctx.find_pattern)) != SQLITE_ROW) { sql_error(__FILE__, __LINE__, ctx.find_pattern); goto error2; } if ((rule->id_pattern = sqlite3_column_int64(ctx.find_pattern, 0)) == 0) { sql_error(__FILE__, __LINE__, ctx.find_pattern); goto error2; } (void) sqlite3_clear_bindings(ctx.find_pattern); sqlite3_reset(ctx.find_pattern); } for (i = 0; i < VectorLength(fields); i++) { field = VectorGet(fields, i); field += strspn(field, " \t"); switch (*field) { case 'c': if (field[1] == '=') { free(rule->on_exceed); rule->on_exceed = VectorReplace(fields, i, NULL); } break; case 'C': if (field[1] == '=') { free(rule->on_expire); rule->on_expire = VectorReplace(fields, i, NULL); } break; case 'r': if (field[1] == '=') { free(rule->report); rule->report = VectorReplace(fields, i, NULL); } break; case 't': if (field[1] == '=') rule->thread = strtol(field+2, NULL, 10); break; case 'l': if (field[1] == '=') continue; } (void) VectorRemove(fields, i--); } field[strcspn(field, "\r\n")] = '\0'; /* What remains of fields should be an empty vector or an array of l= actions. */ rule->limits = (char **) VectorBase(fields); fields = NULL; rule->node.data = rule; rule->node.free = free_rule; listInsertAfter(&pattern_rules, NULL, &rule->node); rc = 0; error2: VectorDestroy(fields); error1: if (rc != 0) free_rule(rule); error0: return rc; }