/* * Copyright (c) 2004-2005 Sergey Lyubka * All rights reserved * * "THE BEER-WARE LICENSE" (Revision 42): * Sergey Lyubka wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. */ /* * Downloaded Sat Nov 5 17:43:06 CET 2011 at * http://slre.sourceforge.net/1.0/slre.c */ #ifdef SLRE_TEST #include #include #include #include #include #else #include #include #endif /* SLRE_TEST */ #include #include enum {END, BRANCH, ANY, EXACT, ANYOF, ANYBUT, OPEN, CLOSE, BOL, EOL, STAR, PLUS, STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT}; #ifdef SLRE_TEST static struct { const char *name; int narg; const char *flags; } opcodes[] = { {"END", 0, ""}, /* End of code block or program */ {"BRANCH", 2, "oo"}, /* Alternative operator, "|" */ {"ANY", 0, ""}, /* Match any character, "." */ {"EXACT", 2, "d"}, /* Match exact string */ {"ANYOF", 2, "D"}, /* Match any from set, "[]" */ {"ANYBUT", 2, "D"}, /* Match any but from set, "[^]"*/ {"OPEN ", 1, "i"}, /* Capture start, "(" */ {"CLOSE", 1, "i"}, /* Capture end, ")" */ {"BOL", 0, ""}, /* Beginning of string, "^" */ {"EOL", 0, ""}, /* End of string, "$" */ {"STAR", 1, "o"}, /* Match zero or more times "*" */ {"PLUS", 1, "o"}, /* Match one or more times, "+" */ {"STARQ", 1, "o"}, /* Non-greedy STAR, "*?" */ {"PLUSQ", 1, "o"}, /* Non-greedy PLUS, "+?" */ {"QUEST", 1, "o"}, /* Match zero or one time, "?" */ {"SPACE", 0, ""}, /* Match whitespace, "\s" */ {"NONSPACE", 0, ""}, /* Match non-space, "\S" */ {"DIGIT", 0, ""} /* Match digit, "\d" */ }; #endif /* SLRE_TEST */ /* * Commands and operands are all unsigned char (1 byte long). All code offsets * are relative to current address, and positive (always point forward). Data * offsets are absolute. Commands with operands: * * BRANCH offset1 offset2 * Try to match the code block that follows the BRANCH instruction * (code block ends with END). If no match, try to match code block that * starts at offset1. If either of these match, jump to offset2. * * EXACT data_offset data_length * Try to match exact string. String is recorded in data section from * data_offset, and has length data_length. * * OPEN capture_number * CLOSE capture_number * If the user have passed 'struct cap' array for captures, OPEN * records the beginning of the matched substring (cap->ptr), CLOSE * sets the length (cap->len) for respective capture_number. * * STAR code_offset * PLUS code_offset * QUEST code_offset * *, +, ?, respectively. Try to gobble as much as possible from the * matched buffer, until code block that follows these instructions * matches. When the longest possible string is matched, * jump to code_offset * * STARQ, PLUSQ are non-greedy versions of STAR and PLUS. */ static const char *meta_chars = "|.^$*+?()[\\"; #ifdef SLRE_TEST static void print_character_set(FILE *fp, const unsigned char *p, int len) { int i; for (i = 0; i < len; i++) { if (i > 0) (void) fputc(',', fp); if (p[i] == 0) { i++; if (p[i] == 0) (void) fprintf(fp, "\\x%02x", p[i]); else (void) fprintf(fp, "%s", opcodes[p[i]].name); } else if (isprint(p[i])) { (void) fputc(p[i], fp); } else { (void) fprintf(fp, "\\x%02x", p[i]); } } } void slre_dump(const struct slre *r, FILE *fp) { int i, j, ch, op, pc; for (pc = 0; pc < r->code_size; pc++) { op = r->code[pc]; (void) fprintf(fp, "%3d %s ", pc, opcodes[op].name); for (i = 0; opcodes[op].flags[i] != '\0'; i++) switch (opcodes[op].flags[i]) { case 'i': (void) fprintf(fp, "%d ", r->code[pc + 1]); pc++; break; case 'o': (void) fprintf(fp, "%d ", pc + r->code[pc + 1] - i); pc++; break; case 'D': print_character_set(fp, r->data + r->code[pc + 1], r->code[pc + 2]); pc += 2; break; case 'd': (void) fputc('"', fp); for (j = 0; j < r->code[pc + 2]; j++) { ch = r->data[r->code[pc + 1] + j]; if (isprint(ch)) { (void) fputc(ch, fp); } else { (void) fprintf(fp, "\\x%02x", ch); } } (void) fputc('"', fp); pc += 2; break; } (void) fputc('\n', fp); } } #endif /* SLRE_TEST */ static void set_jump_offset(struct slre *r, int pc, int offset) { assert(offset < r->code_size); if (r->code_size - offset > 0xff) r->err_str = "Jump offset is too big"; else r->code[pc] = (unsigned char) (r->code_size - offset); } static void emit(struct slre *r, int code) { if (r->code_size >= (int) (sizeof(r->code) / sizeof(r->code[0]))) r->err_str = "RE is too long (code overflow)"; else r->code[r->code_size++] = (unsigned char) code; } static void store_char_in_data(struct slre *r, int ch) { if (r->data_size >= (int) sizeof(r->data)) r->err_str = "RE is too long (data overflow)"; else r->data[r->data_size++] = ch; } static void exact(struct slre *r, const char **re) { int old_data_size = r->data_size; while (**re != '\0' && (strchr(meta_chars, **re)) == NULL) store_char_in_data(r, *(*re)++); emit(r, EXACT); emit(r, old_data_size); emit(r, r->data_size - old_data_size); } static int get_escape_char(const char **re) { int res; switch (*(*re)++) { case 'n': res = '\n'; break; case 'r': res = '\r'; break; case 't': res = '\t'; break; case '0': res = 0; break; case 'S': res = NONSPACE << 8; break; case 's': res = SPACE << 8; break; case 'd': res = DIGIT << 8; break; default: res = (*re)[-1]; break; } return res; } static void anyof(struct slre *r, const char **re) { int esc, old_data_size = r->data_size, op = ANYOF; if (**re == '^') { op = ANYBUT; (*re)++; } while (**re != '\0') switch (*(*re)++) { case ']': emit(r, op); emit(r, old_data_size); emit(r, r->data_size - old_data_size); return; /* NOTREACHED */ break; case '\\': esc = get_escape_char(re); if ((esc & 0xff) == 0) { store_char_in_data(r, 0); store_char_in_data(r, esc >> 8); } else { store_char_in_data(r, esc); } break; default: store_char_in_data(r, (*re)[-1]); break; } r->err_str = "No closing ']' bracket"; } static void relocate(struct slre *r, int begin, int shift) { emit(r, END); memmove(r->code + begin + shift, r->code + begin, r->code_size - begin); r->code_size += shift; } static void quantifier(struct slre *r, int prev, int op) { if (r->code[prev] == EXACT && r->code[prev + 2] > 1) { r->code[prev + 2]--; emit(r, EXACT); emit(r, r->code[prev + 1] + r->code[prev + 2]); emit(r, 1); prev = r->code_size - 3; } relocate(r, prev, 2); r->code[prev] = op; set_jump_offset(r, prev + 1, prev); } static void exact_one_char(struct slre *r, int ch) { emit(r, EXACT); emit(r, r->data_size); emit(r, 1); store_char_in_data(r, ch); } static void fixup_branch(struct slre *r, int fixup) { if (fixup > 0) { emit(r, END); set_jump_offset(r, fixup, fixup - 2); } } static void compile(struct slre *r, const char **re) { int op, esc, branch_start, last_op, fixup, cap_no, level; fixup = 0; level = r->num_caps; branch_start = last_op = r->code_size; for (;;) switch (*(*re)++) { case '\0': (*re)--; return; /* NOTREACHED */ break; case '^': emit(r, BOL); break; case '$': emit(r, EOL); break; case '.': last_op = r->code_size; emit(r, ANY); break; case '[': last_op = r->code_size; anyof(r, re); break; case '\\': last_op = r->code_size; esc = get_escape_char(re); if (esc & 0xff00) emit(r, esc >> 8); else exact_one_char(r, esc); break; case '(': last_op = r->code_size; cap_no = ++r->num_caps; emit(r, OPEN); emit(r, cap_no); compile(r, re); if (*(*re)++ != ')') { r->err_str = "No closing bracket"; return; } emit(r, CLOSE); emit(r, cap_no); break; case ')': (*re)--; fixup_branch(r, fixup); if (level == 0) { r->err_str = "Unbalanced brackets"; return; } return; /* NOTREACHED */ break; case '+': case '*': op = (*re)[-1] == '*' ? STAR : PLUS; if (**re == '?') { (*re)++; op = op == STAR ? STARQ : PLUSQ; } quantifier(r, last_op, op); break; case '?': quantifier(r, last_op, QUEST); break; case '|': fixup_branch(r, fixup); relocate(r, branch_start, 3); r->code[branch_start] = BRANCH; set_jump_offset(r, branch_start + 1, branch_start); fixup = branch_start + 2; r->code[fixup] = 0xff; break; default: (*re)--; last_op = r->code_size; exact(r, re); break; } } int slre_compile(struct slre *r, const char *re) { r->err_str = NULL; r->code_size = r->data_size = r->num_caps = r->anchored = 0; if (*re == '^') r->anchored++; emit(r, OPEN); /* This will capture what matches full RE */ emit(r, 0); while (*re != '\0') compile(r, &re); if (r->code[2] == BRANCH) fixup_branch(r, 4); emit(r, CLOSE); emit(r, 0); emit(r, END); return (r->err_str == NULL ? 1 : 0); } static int match(const struct slre *, int, const char *, int, int *, struct cap *); static void loop_greedy(const struct slre *r, int pc, const char *s, int len, int *ofs) { int saved_offset, matched_offset; saved_offset = matched_offset = *ofs; while (match(r, pc + 2, s, len, ofs, NULL)) { saved_offset = *ofs; if (match(r, pc + r->code[pc + 1], s, len, ofs, NULL)) matched_offset = saved_offset; *ofs = saved_offset; } *ofs = matched_offset; } static void loop_non_greedy(const struct slre *r, int pc, const char *s, int len, int *ofs) { int saved_offset = *ofs; while (match(r, pc + 2, s, len, ofs, NULL)) { saved_offset = *ofs; if (match(r, pc + r->code[pc + 1], s, len, ofs, NULL)) break; } *ofs = saved_offset; } static int is_any_of(const unsigned char *p, int len, const char *s, int *ofs) { int i, ch; ch = s[*ofs]; for (i = 0; i < len; i++) if (p[i] == ch) { (*ofs)++; return 1; } return 0; } static int is_any_but(const unsigned char *p, int len, const char *s, int *ofs) { int i, ch; ch = s[*ofs]; for (i = 0; i < len; i++) { if (p[i] == ch) return 0; } (*ofs)++; return 1; } static int match(const struct slre *r, int pc, const char *s, int len, int *ofs, struct cap *caps) { int n, saved_offset, res = 1; while (res && r->code[pc] != END) { assert(pc < r->code_size); assert(pc < (int) (sizeof(r->code) / sizeof(r->code[0]))); switch (r->code[pc]) { case BRANCH: saved_offset = *ofs; res = match(r, pc + 3, s, len, ofs, caps); if (res == 0) { *ofs = saved_offset; res = match(r, pc + r->code[pc + 1], s, len, ofs, caps); } pc += r->code[pc + 2]; break; case EXACT: res = 0; n = r->code[pc + 2]; /* String length */ if (n <= len - *ofs && !memcmp(s + *ofs, r->data + r->code[pc + 1], n)) { (*ofs) += n; res = 1; } pc += 3; break; case QUEST: res = 1; saved_offset = *ofs; if (!match(r, pc + 2, s, len, ofs, caps)) *ofs = saved_offset; pc += r->code[pc + 1]; break; case STAR: res = 1; loop_greedy(r, pc, s, len, ofs); pc += r->code[pc + 1]; break; case STARQ: res = 1; loop_non_greedy(r, pc, s, len, ofs); pc += r->code[pc + 1]; break; case PLUS: res = match(r, pc + 2, s, len, ofs, caps); if (res == 0) break; loop_greedy(r, pc, s, len, ofs); pc += r->code[pc + 1]; break; case PLUSQ: res = match(r, pc + 2, s, len, ofs, caps); if (res == 0) break; loop_non_greedy(r, pc, s, len, ofs); pc += r->code[pc + 1]; break; case SPACE: res = 0; if (*ofs < len && isspace(((unsigned char *)s)[*ofs])) { (*ofs)++; res = 1; } pc++; break; case NONSPACE: res = 0; if (*ofs < len && !isspace(((unsigned char *)s)[*ofs])) { (*ofs)++; res = 1; } pc++; break; case DIGIT: res = 0; if (*ofs < len && isdigit(((unsigned char *)s)[*ofs])) { (*ofs)++; res = 1; } pc++; break; case ANY: res = 0; if (*ofs < len) { (*ofs)++; res = 1; } pc++; break; case ANYOF: res = 0; if (*ofs < len) res = is_any_of(r->data + r->code[pc + 1], r->code[pc + 2], s, ofs); pc += 3; break; case ANYBUT: res = 0; if (*ofs < len) res = is_any_but(r->data + r->code[pc + 1], r->code[pc + 2], s, ofs); pc += 3; break; case BOL: res = *ofs == 0 ? 1 : 0; pc++; break; case EOL: res = *ofs == len ? 1 : 0; pc++; break; case OPEN: if (caps != NULL) caps[r->code[pc + 1]].ptr = s + *ofs; pc += 2; break; case CLOSE: if (caps != NULL) caps[r->code[pc + 1]].len = (s + *ofs) - caps[r->code[pc + 1]].ptr; pc += 2; break; case END: pc++; break; default: printf("unknown cmd (%d) at %d\n", r->code[pc], pc); assert(0); break; } } return res; } int slre_match(const struct slre *r, const char *buf, int len, struct cap *caps) { int i, ofs = 0, res = 0; if (r->anchored) { res = match(r, 0, buf, len, &ofs, caps); } else { for (i = 0; i < len && res == 0; i++) { ofs = i; res = match(r, 0, buf, len, &ofs, caps); } } return res; } #ifdef SLRE_TEST #define N_CAPS 5 int main(int argc, char *argv[]) { struct slre slre; struct cap caps[N_CAPS]; unsigned char data[1 * 1024 * 1024]; FILE *fp; int i, res, len; if (argc < 2) { fprintf(stderr, "Usage: %s 'slre' \n", argv[0]); return 1; } fp = fopen(argv[2], "rb"); if (fp == NULL) { fprintf(stderr, "Error: cannot open %s:%s\n", argv[2], strerror(errno)); return 1; } if (!slre_compile(&slre, argv[1])) { fprintf(stderr, "Error compiling slre: %s\n", slre.err_str); return 1; } slre_dump(&slre, stderr); while (fgets(data, sizeof(data), fp) != NULL) { len = strlen(data); if ((len > 0) && (data[len-1] == '\n')) { data[len-1] = '\0'; --len; } printf("Data = \"%s\"\n", data); (void) memset(caps, 0, sizeof(caps)); res = 0; res = slre_match(&slre, data, len, caps); printf("Result [%d]: %d\n", i, res); for (i = 0; i < N_CAPS; i++) { if (caps[i].len > 0) { printf("Substring %d: len=%d [%.*s]\n", i, caps[i].len, caps[i].len, caps[i].ptr); } } printf("----------------------------------------------------\n"); } (void) fclose(fp); return 0; } #endif /* SLRE_TEST */