ARB
aisc_eval.c
Go to the documentation of this file.
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 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 
34 char *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 
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 
CONSTEXPR_INLINE bool is_SPACE_LF_EOS(char c)
Definition: aisc_inline.h:27
const char * get_path() const
Definition: aisc_location.h:44
char * read_aisc_file(const char *path, const Location *loc)
Definition: aisc.c:22
const char * formatted(const char *format,...)
Definition: aisc_commands.c:65
char * strduplen(const char *s, int len)
Definition: aisc_inline.h:42
char * get_var_string(const Data &data, char *var, bool allow_missing_var)
Definition: aisc_var_ref.c:147
static HelixNrInfo * start
#define print_error(code_or_loc, err)
Definition: aisc_def.h:47
#define printf_error(code_or_loc, format, arg)
Definition: aisc_def.h:50
static long fileLen[GB_MAX_MAPPED_FILES]
Definition: admap.cxx:703
#define NULp
Definition: cxxforward.h:97
#define offset(field)
Definition: GLwDrawA.c:73
int get_linenr() const
Definition: aisc_location.h:45
#define aisc_assert(cond)
Definition: aisc_def.h:11
void SKIP_SPACE_LF(const char *&var)
Definition: aisc_inline.h:32