source: branches/tree/AISC/aisc_eval.c

Last change on this file was 18732, checked in by westram, 3 years ago
File size: 5.8 KB
Line 
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
8char *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 NULp;
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 NULp;
29    }
30
31    return strdup(formatted("%i", r));
32}
33
34char *Expression::evalPart(int start, int end, int& resLen) {
35    aisc_assert(start>0);
36
37    char *res = NULp;
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
95bool 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
Note: See TracBrowser for help on using the repository browser.