| 1 | // Institute of Microbiology (Technical University Munich) // |
|---|
| 2 | // http://www.arb-home.de/ // |
|---|
| 3 | |
|---|
| 4 | #include "aisc_eval.h" |
|---|
| 5 | #include "aisc_inline.h" |
|---|
| 6 | #include "aisc_proto.h" |
|---|
| 7 | |
|---|
| 8 | char *Expression::eval_math(char *expr, char op_char) { |
|---|
| 9 | char sep[] = "?,;"; |
|---|
| 10 | sep[0] = op_char; |
|---|
| 11 | |
|---|
| 12 | char *brk = strpbrk(expr, sep); |
|---|
| 13 | if (!brk) { |
|---|
| 14 | print_error(&loc, formatted("Expected to see '%c', ',' or ';' in '%s'", op_char, expr)); |
|---|
| 15 | return NULL; |
|---|
| 16 | } |
|---|
| 17 | |
|---|
| 18 | brk[0] = 0; |
|---|
| 19 | int i1 = atoi(expr); |
|---|
| 20 | brk[0] = op_char; |
|---|
| 21 | int i2 = atoi(brk+1); |
|---|
| 22 | int r; |
|---|
| 23 | switch (op_char) { |
|---|
| 24 | case '+': r = i1+i2; break; |
|---|
| 25 | case '*': r = i1*i2; break; |
|---|
| 26 | default : |
|---|
| 27 | printf_error(&loc, "Unknown operator '%c'", op_char); |
|---|
| 28 | return NULL; |
|---|
| 29 | } |
|---|
| 30 | |
|---|
| 31 | return strdup(formatted("%i", r)); |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | char *Expression::evalPart(int start, int end, int& resLen) { |
|---|
| 35 | aisc_assert(start>0); |
|---|
| 36 | |
|---|
| 37 | char *res = NULL; |
|---|
| 38 | resLen = -1; |
|---|
| 39 | |
|---|
| 40 | char c = ebuffer[end+1]; |
|---|
| 41 | ebuffer[end+1] = 0; |
|---|
| 42 | char *part = ebuffer+start; |
|---|
| 43 | SKIP_SPACE_LF(part); |
|---|
| 44 | |
|---|
| 45 | if (start>end) { // empty "$()" |
|---|
| 46 | res = strdup(""); |
|---|
| 47 | resLen = 0; |
|---|
| 48 | } |
|---|
| 49 | else if (part[0] == '+' || part[0] == '*') { |
|---|
| 50 | res = eval_math(part+1, part[0]); |
|---|
| 51 | } |
|---|
| 52 | else if (part[0] == '#') { |
|---|
| 53 | if (strncmp(part, "#FILE", 5) == 0) { |
|---|
| 54 | char *path = part+5; |
|---|
| 55 | while (is_SPACE_LF_EOS(*path)) ++path; |
|---|
| 56 | |
|---|
| 57 | char *file = read_aisc_file(path, &loc); |
|---|
| 58 | if (!file) { |
|---|
| 59 | printf_error(&loc, "couldn't read file '%s'", path); |
|---|
| 60 | } |
|---|
| 61 | else { |
|---|
| 62 | int fileLen = strlen(file); |
|---|
| 63 | const char *sourcename = loc.get_path(); |
|---|
| 64 | int sourceline = loc.get_linenr(); |
|---|
| 65 | aisc_assert(sourcename); |
|---|
| 66 | |
|---|
| 67 | int buflen = fileLen+strlen(path)+strlen(sourcename)+100; |
|---|
| 68 | res = (char *)malloc(buflen+1); |
|---|
| 69 | |
|---|
| 70 | // Inject code to restore correct code file and line (needed for proper error messages) |
|---|
| 71 | int printed = sprintf(res, "@SETSOURCE %s,%i@%s@SETSOURCE %s,%i@", |
|---|
| 72 | path, 1, |
|---|
| 73 | file, |
|---|
| 74 | sourcename, sourceline); |
|---|
| 75 | if (printed >= buflen) { |
|---|
| 76 | fprintf(stderr, "%s:%i: Error: buffer overflow\n", __FILE__, __LINE__); |
|---|
| 77 | } |
|---|
| 78 | free(file); |
|---|
| 79 | } |
|---|
| 80 | } |
|---|
| 81 | else { |
|---|
| 82 | printf_error(&loc, "unknown eval-command '%s'", part); |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | else { |
|---|
| 86 | res = get_var_string(data, part, allow_missing_ref); |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | ebuffer[end+1] = c; |
|---|
| 90 | |
|---|
| 91 | if (resLen == -1 && res) resLen = strlen(res); |
|---|
| 92 | return res; |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | bool Expression::evalRest(const int offset) { |
|---|
| 96 | // evaluate content of 'ebuffer' starting at offset |
|---|
| 97 | // return true on success |
|---|
| 98 | // may reallocate ebuffer - so old pointers might get invalidated |
|---|
| 99 | |
|---|
| 100 | aisc_assert(strncmp(ebuffer+offset, "$(", 2) == 0); |
|---|
| 101 | |
|---|
| 102 | { |
|---|
| 103 | const char *more_to_eval = strstr(ebuffer+offset+2, "$("); |
|---|
| 104 | if (more_to_eval) { |
|---|
| 105 | if (!evalRest(more_to_eval-ebuffer)) return false; |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | int closing_paren; |
|---|
| 110 | { |
|---|
| 111 | const char *paren = strchr(ebuffer+offset+2, ')'); |
|---|
| 112 | if (!paren) { |
|---|
| 113 | print_error(&loc, "unbalanced parens; missing ')'"); |
|---|
| 114 | return false; |
|---|
| 115 | } |
|---|
| 116 | closing_paren = paren-ebuffer; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | int eval_len; |
|---|
| 120 | char *evaluated; |
|---|
| 121 | |
|---|
| 122 | if (offset>0 && ebuffer[offset-1] == '$') { // quoted "$$(...)" |
|---|
| 123 | eval_len = closing_paren-offset; |
|---|
| 124 | evaluated = strduplen(ebuffer+offset+1, eval_len); |
|---|
| 125 | } |
|---|
| 126 | else { |
|---|
| 127 | evaluated = evalPart(offset+2, closing_paren-1, eval_len); |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | if (!evaluated) { |
|---|
| 131 | return false; |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | int org_len = closing_paren-offset+1; |
|---|
| 135 | |
|---|
| 136 | char *rest = ebuffer+closing_paren+1; |
|---|
| 137 | int restlen = used-closing_paren; |
|---|
| 138 | |
|---|
| 139 | if (eval_len <= org_len) { |
|---|
| 140 | if (eval_len < org_len) { |
|---|
| 141 | aisc_assert(closing_paren+1 <= used); |
|---|
| 142 | memmove(ebuffer+offset+eval_len, rest, restlen); |
|---|
| 143 | used -= (org_len-eval_len); |
|---|
| 144 | } |
|---|
| 145 | memcpy(ebuffer+offset, evaluated, eval_len); |
|---|
| 146 | } |
|---|
| 147 | else { |
|---|
| 148 | int growth = eval_len-org_len; |
|---|
| 149 | if ((used+growth+1)>bufsize) { // need to increase ebuffer size |
|---|
| 150 | int new_bufsize = (used+growth+1)*3/2; |
|---|
| 151 | char *new_linebuf = (char*)malloc(new_bufsize); |
|---|
| 152 | |
|---|
| 153 | memcpy(new_linebuf, ebuffer, offset); |
|---|
| 154 | memcpy(new_linebuf+offset, evaluated, eval_len); |
|---|
| 155 | aisc_assert(closing_paren+1 <= used); |
|---|
| 156 | memcpy(new_linebuf+offset+eval_len, rest, restlen); |
|---|
| 157 | |
|---|
| 158 | free(ebuffer); |
|---|
| 159 | ebuffer = new_linebuf; |
|---|
| 160 | bufsize = new_bufsize; |
|---|
| 161 | } |
|---|
| 162 | else { |
|---|
| 163 | aisc_assert(closing_paren+1 <= used); |
|---|
| 164 | memmove(ebuffer+offset+eval_len, rest, restlen); |
|---|
| 165 | memcpy(ebuffer+offset, evaluated, eval_len); |
|---|
| 166 | } |
|---|
| 167 | used += growth; |
|---|
| 168 | aisc_assert(used<bufsize); |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | aisc_assert(ebuffer[used] == 0); |
|---|
| 172 | |
|---|
| 173 | free(evaluated); |
|---|
| 174 | |
|---|
| 175 | char *ld = strstr(ebuffer+offset, "$("); |
|---|
| 176 | if (ld) { |
|---|
| 177 | int next_offset = ld-ebuffer; |
|---|
| 178 | if (next_offset == offset) { // infinite loop in evaluation |
|---|
| 179 | static int deadlock_offset; |
|---|
| 180 | static int deadlock_count; |
|---|
| 181 | |
|---|
| 182 | if (next_offset == deadlock_offset) { |
|---|
| 183 | ++deadlock_count; |
|---|
| 184 | if (deadlock_count > 50) { // more than 50 evals at same offset in expression |
|---|
| 185 | printf_error(&loc, "detected (endless?) recursive evaluation in expression '%s'", ld); |
|---|
| 186 | return false; |
|---|
| 187 | } |
|---|
| 188 | } |
|---|
| 189 | else { |
|---|
| 190 | deadlock_offset = next_offset; |
|---|
| 191 | deadlock_count = 1; |
|---|
| 192 | } |
|---|
| 193 | } |
|---|
| 194 | return evalRest(next_offset); |
|---|
| 195 | } |
|---|
| 196 | |
|---|
| 197 | return true; |
|---|
| 198 | } |
|---|
| 199 | |
|---|