1 /*
2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
5 *
6 * Empire is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * ---
20 *
21 * See files README, COPYING and CREDITS in the root of the source
22 * tree for related information and legal notices. It is expected
23 * that future projects/authors will amend these files as needed.
24 *
25 * ---
26 *
27 * nstreval.c: evaluate compiled values
28 *
29 * Known contributors to this file:
30 * Dave Pare, 1989
31 * Steve McClure, 1997
32 * Markus Armbruster, 2004-2016
33 */
34
35 #include <config.h>
36
37 #include <limits.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include "nat.h"
41 #include "nsc.h"
42 #include "optlist.h"
43
44 /*
45 * Initialize @val to symbolic value for selector @ca with index @idx.
46 * Return @val.
47 */
48 struct valstr *
nstr_mksymval(struct valstr * val,struct castr * ca,int idx)49 nstr_mksymval(struct valstr *val, struct castr *ca, int idx)
50 {
51 val->val_type = ca->ca_type;
52 val->val_cat = NSC_OFF;
53 val->val_as.sym.off = ca->ca_off;
54 val->val_as.sym.len = ca->ca_len;
55 val->val_as.sym.idx = idx;
56 val->val_as.sym.get = ca->ca_get;
57 val->val_as.sym.hidden = ca->ca_flags & NSC_HIDDEN;
58 return val;
59 }
60
61 /*
62 * Evaluate @val.
63 * If @val has category NSC_OFF, read the value from the context object
64 * @ptr, and translate it for country @cnum (coordinate system and
65 * contact status). No translation when @cnum is NATID_BAD.
66 * @ptr points to a context object of the type that was used to compile
67 * the value.
68 * Unless @want is NSC_NOTYPE, coerce the value to promoted value type
69 * @want. @val must be coercible.
70 * The result's type is promoted on success, NSC_NOTYPE on error.
71 * In either case, the category is NSC_VAL.
72 * Return @val.
73 */
74 struct valstr *
nstr_eval(struct valstr * val,natid cnum,void * ptr,enum nsc_type want)75 nstr_eval(struct valstr *val, natid cnum, void *ptr, enum nsc_type want)
76 {
77 char *memb_ptr;
78 enum nsc_type valtype;
79 int idx, hidden;
80 coord c;
81 struct natstr *natp;
82 struct relatstr *relp;
83
84 if (CANT_HAPPEN(want != NSC_NOTYPE && !NSC_IS_PROMOTED(want)))
85 want = nstr_promote(want);
86
87 switch (val->val_cat) {
88 case NSC_VAL:
89 valtype = val->val_type;
90 if (CANT_HAPPEN(!NSC_IS_PROMOTED(valtype)))
91 valtype = nstr_promote(valtype);
92 break;
93 case NSC_OFF:
94 if (val->val_as.sym.get) {
95 natp = getnatp(cnum);
96 do {
97 ptr = val->val_as.sym.get(val, natp, ptr);
98 } while (ptr && val->val_as.sym.get);
99 if (!ptr) {
100 valtype = val->val_type;
101 val->val_cat = NSC_VAL;
102 break;
103 }
104 }
105
106 valtype = NSC_LONG;
107 memb_ptr = ptr;
108 memb_ptr += val->val_as.sym.off;
109 idx = val->val_as.sym.idx;
110 hidden = val->val_as.sym.hidden;
111 val->val_cat = NSC_VAL;
112 switch (val->val_type) {
113 case NSC_CHAR:
114 val->val_as.lng = ((signed char *)memb_ptr)[idx];
115 break;
116 case NSC_UCHAR:
117 val->val_as.lng = ((unsigned char *)memb_ptr)[idx];
118 break;
119 case NSC_SHORT:
120 val->val_as.lng = ((short *)memb_ptr)[idx];
121 break;
122 case NSC_USHORT:
123 val->val_as.lng = ((unsigned short *)memb_ptr)[idx];
124 break;
125 case NSC_INT:
126 val->val_as.lng = ((int *)memb_ptr)[idx];
127 break;
128 case NSC_LONG:
129 val->val_as.lng = ((long *)memb_ptr)[idx];
130 break;
131 case NSC_XCOORD:
132 c = ((short *)memb_ptr)[idx];
133 if (cnum == NATID_BAD) {
134 /* FIXME use variant of xrel() that takes orig instead of nation */
135 if (c >= WORLD_X / 2)
136 c -= WORLD_X;
137 } else
138 c = xrel(getnatp(cnum), c);
139 val->val_as.lng = c;
140 break;
141 case NSC_YCOORD:
142 c = ((short *)memb_ptr)[idx];
143 if (cnum == NATID_BAD) {
144 /* FIXME use variant of yrel() that takes orig instead of nation */
145 if (c >= WORLD_Y / 2)
146 c -= WORLD_Y;
147 } else
148 c = yrel(getnatp(cnum), c);
149 val->val_as.lng = c;
150 break;
151 case NSC_FLOAT:
152 val->val_as.dbl = ((float *)memb_ptr)[idx];
153 valtype = NSC_DOUBLE;
154 break;
155 case NSC_DOUBLE:
156 val->val_as.dbl = ((double *)memb_ptr)[idx];
157 valtype = NSC_DOUBLE;
158 break;
159 case NSC_STRINGY:
160 CANT_HAPPEN(idx);
161 val->val_as.str.maxsz = val->val_as.sym.len;
162 val->val_as.str.base = (char *)memb_ptr;
163 valtype = NSC_STRING;
164 break;
165 case NSC_STRING:
166 val->val_as.str.base = ((char **)memb_ptr)[idx];
167 val->val_as.str.maxsz = INT_MAX;
168 /* really SIZE_MAX, but that's C99 */
169 valtype = NSC_STRING;
170 break;
171 case NSC_TIME:
172 val->val_as.lng = ((time_t *)memb_ptr)[idx];
173 break;
174 default:
175 CANT_REACH();
176 valtype = NSC_NOTYPE;
177 }
178
179 if (hidden) {
180 if (CANT_HAPPEN(hidden && valtype != NSC_LONG))
181 break; /* not implemented */
182 relp = ptr;
183 if (CANT_HAPPEN(relp->ef_type != EF_RELAT))
184 break; /* only defined for nation selectors */
185 if (!opt_HIDDEN || cnum == NATID_BAD
186 || getnatp(cnum)->nat_stat == STAT_GOD)
187 break;
188 if (!in_contact(cnum, idx) || !in_contact(relp->rel_uid, idx))
189 val->val_as.lng = -1;
190 }
191 break;
192 default:
193 CANT_REACH();
194 valtype = NSC_NOTYPE;
195 }
196
197 /* coerce */
198 if (valtype == want)
199 ;
200 else if (want == NSC_DOUBLE) {
201 if (valtype == NSC_LONG) {
202 valtype = want;
203 val->val_as.dbl = val->val_as.lng;
204 }
205 }
206
207 if (CANT_HAPPEN(valtype != want && want != NSC_NOTYPE))
208 valtype = NSC_NOTYPE;
209
210 val->val_type = valtype;
211 return val;
212 }
213
214 /*
215 * Promote @valtype.
216 * If @valtype is an integer type, return NSC_LONG.
217 * If @valtype is a floating-point type, return NSC_DOUBLE.
218 * If @valtype is a string type, return NSC_STRING.
219 */
220 int
nstr_promote(int valtype)221 nstr_promote(int valtype)
222 {
223 switch (valtype) {
224 case NSC_LONG:
225 case NSC_DOUBLE:
226 case NSC_STRING:
227 break;
228 case NSC_CHAR:
229 case NSC_UCHAR:
230 case NSC_SHORT:
231 case NSC_USHORT:
232 case NSC_INT:
233 case NSC_XCOORD:
234 case NSC_YCOORD:
235 case NSC_TIME:
236 valtype = NSC_LONG;
237 break;
238 case NSC_FLOAT:
239 valtype = NSC_DOUBLE;
240 break;
241 case NSC_STRINGY:
242 valtype = NSC_STRING;
243 break;
244 default:
245 CANT_REACH();
246 valtype = NSC_NOTYPE;
247 }
248 return valtype;
249 }
250
251 char *
symbol_by_value(int key,struct symbol * table)252 symbol_by_value(int key, struct symbol *table)
253 {
254 int i;
255
256 for (i = 0; table[i].name; i++)
257 if (key == table[i].value)
258 return table[i].name;
259
260 return NULL;
261 }
262
263 int
symbol_set_fmt(char * buf,size_t sz,int flags,struct symbol symtab[],char * sep,int all)264 symbol_set_fmt(char *buf, size_t sz, int flags, struct symbol symtab[],
265 char *sep, int all)
266 {
267 char *pfx = "";
268 int n, i;
269 char *p;
270
271 if (buf && sz)
272 buf[0] = 0;
273 n = 0;
274 for (i = 0; i < 32; i++) {
275 if (!(flags & bit(i)))
276 continue;
277 p = symbol_by_value(bit(i), symtab);
278 if (p)
279 n += snprintf(buf + n, sz - n, "%s%s", pfx, p);
280 else if (all)
281 n += snprintf(buf + n, sz - n, "%s#%d", pfx, i);
282 if ((size_t)n >= sz)
283 sz = n;
284 pfx = sep;
285 }
286
287 CANT_HAPPEN((size_t)n >= sz && buf);
288 return n;
289 }
290