/*
* Empire - A multi-player, client/server Internet based war game.
- * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
- * Ken Stevens, Steve McClure
+ * Copyright (C) 1986-2017, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ * Ken Stevens, Steve McClure, Markus Armbruster
*
- * This program is free software; you can redistribute it and/or modify
+ * Empire is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ---
*
* Known contributors to this file:
* Dave Pare, 1989
* Steve McClure, 1997
- * Markus Armbruster, 2004-2008
+ * Markus Armbruster, 2004-2014
*/
#include <config.h>
#include <limits.h>
#include "file.h"
#include "match.h"
+#include "nsc.h"
#include "player.h"
#include "prototypes.h"
static int nstr_optype(enum nsc_type, enum nsc_type);
/*
- * Compile conditions into array NP[LEN].
+ * Compile conditions into array @np[@len].
* Return number of conditions, or -1 on error.
- * It is an error if there are more than LEN conditions.
- * TYPE is the context type, a file type.
- * STR is the condition string, in Empire syntax, without the leading
+ * It is an error if there are more than @len conditions.
+ * @type is the context type, a file type.
+ * @str is the condition string, in Empire syntax, without the leading
* '?'.
*/
int
np = &dummy;
/* left operand */
+ if (!*cond) {
+ pr("%s -- %scondition expected\n", str, i ? "another " : "");
+ return -1;
+ }
tail = nstr_parse_val(cond, &np->lft);
lft_caidx = nstr_match_ca(&np->lft, ca);
++tail;
/* right operand */
+ if (!*tail) {
+ pr("%s -- operand expected\n", cond);
+ return -1;
+ }
tail = nstr_parse_val(tail, &np->rgt);
rgt_caidx = nstr_match_ca(&np->rgt, ca);
return i + 1;
}
-/* Like strcmp(S1, S2), but limit length of S1 to SZ1 and of S2 to SZ2. */
+/* Like strcmp(S1, S2), but limit length of S1 to SZ1 and of S2 to SZ2. */
static int
strnncmp(char *s1, size_t sz1, char *s2, size_t sz2)
{
int res;
- if (sz1 == sz2) return strncmp(s1, s2, sz2);
- if (sz1 < sz2) return -strnncmp(s2, sz2, s1, sz1);
+ if (sz1 == sz2)
+ return strncmp(s1, s2, sz2);
+ if (sz1 < sz2)
+ return -strnncmp(s2, sz2, s1, sz1);
res = strncmp(s1, s2, sz2);
return res ? res : s1[sz2];
}
: (CANT_REACH(), 0))
/*
- * Evaluate compiled conditions in array NP[NCOND].
+ * Evaluate compiled conditions in array @np[@ncond].
* Return non-zero iff they are all true.
- * PTR points to a context object of the type that was used to compile
+ * @ptr points to a context object of the type that was used to compile
* the conditions.
*/
int
nstr_exec(struct nscstr *np, int ncond, void *ptr)
{
- int i, op, optype, cmp;
+ int i, op, cmp;
+ enum nsc_type optype;
struct valstr lft, rgt;
for (i = 0; i < ncond; ++i) {
if (np[i].lft.val_cat == NSC_NOCAT || np[i].rgt.val_cat == NSC_NOCAT)
return 0;
lft = np[i].lft;
- nstr_exec_val(&lft, player->cnum, ptr, optype);
+ nstr_eval(&lft, player->cnum, ptr, optype);
rgt = np[i].rgt;
- nstr_exec_val(&rgt, player->cnum, ptr, optype);
+ nstr_eval(&rgt, player->cnum, ptr, optype);
+ if (CANT_HAPPEN(lft.val_type != optype || rgt.val_type != optype))
+ return 0;
switch (optype) {
case NSC_LONG:
if (!EVAL(op, lft.val_as.lng, rgt.val_as.lng))
}
/*
- * Parse a value in STR into VAL.
+ * Parse a value in @str into @val.
* Return a pointer to the first character after the value.
* Value is either evaluated into NSC_STRING, NSC_DOUBLE or NSC_LONG,
* or an identifier.
val->val_cat = NSC_VAL;
val->val_as.str.base = str + 1;
val->val_as.str.maxsz = tail - (str + 1);
- if (*tail) ++tail;
+ if (*tail)
+ ++tail;
/* FIXME else unclosed string */
return tail;
}
}
/* funny character, interpret as identifier */
- tail = str+1;
+ tail = CANT_HAPPEN(!*str) ? str : str + 1;
val->val_type = NSC_NOTYPE;
val->val_cat = NSC_ID;
val->val_as.str.base = str;
}
/*
- * Match VAL in table of selector descriptors CA, return index.
+ * Match @val in table of selector descriptors @ca, return index.
* Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
* several.
- * A VAL that is not an identifier doesn't match anything. A null CA
+ * A @val that is not an identifier doesn't match anything. A null @ca
* is considered empty.
*/
static int
}
/*
- * Is identifier VAL the name of the selector given by CA and IDX?
- * Return non-zero if and only if IDX is non-negative and VAL is the
- * name of CA[IDX].
- * IDX must have been obtained from nstr_match_ca(VAL, CA).
+ * Is identifier @val the name of the selector given by @ca and @idx?
+ * Return non-zero if and only if @idx is non-negative and @val is the
+ * name of @ca[@idx].
+ * @idx must have been obtained from nstr_match_ca(@val, @ca).
*/
static int
nstr_is_name_of_ca(struct valstr *val, struct castr *ca, int idx)
{
if (CANT_HAPPEN(val->val_cat != NSC_ID && idx >= 0))
- return 0;
+ return 0;
return idx >= 0 && strlen(ca[idx].ca_name) == val->val_as.str.maxsz;
}
/*
* Do we have two comparable selectors?
- * Check selector descriptors CA[LFT_IDX] (unless LFT_IDX is negative)
- * and CA[RGT_IDX] (unless RGT_IDX is negative). CA may be null when
+ * Check selector descriptors @ca[@lft_idx] (unless @lft_idx is negative)
+ * and @ca[@rgt_idx] (unless @rgt_idx is negative). @ca may be null when
* both are negative.
*/
static int
}
/*
- * Match VAL in a selector's values, return its (non-negative) value.
- * Match values of selector descriptor CA[IDX], provided IDX is not
- * negative. CA may be null when IDX is negative.
+ * Match @val in a selector's values, return its (non-negative) value.
+ * Match values of selector descriptor @ca[@idx], provided @idx is not
+ * negative. @ca may be null when @idx is negative.
* Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
* several.
*/
}
/*
- * Change VAL to resolve identifier to selector.
- * Return VAL on success, NULL on error.
- * No change if VAL is not an identifier.
- * Else change VAL into symbolic value for selector CA[IDX] if IDX >=
+ * Change @val to resolve identifier to selector.
+ * Return @val on success, NULL on error.
+ * No change if @val is not an identifier.
+ * Else change @val into symbolic value for selector @ca[@idx] if @idx >=
* 0, and error if not.
*/
static struct valstr *
return NULL;
}
+ if (CA_IS_ARRAY(&ca[idx])) {
+ pr("%.*s -- not usable here\n",
+ (int)val->val_as.str.maxsz, val->val_as.str.base);
+ val->val_cat = NSC_NOCAT;
+ return NULL;
+ }
+
if ((ca[idx].ca_flags & NSC_DEITY) && !player->god) {
pr("%.*s -- not accessible to mortals\n",
(int)val->val_as.str.maxsz, val->val_as.str.base);
}
/*
- * Change VAL to resolve identifier to value SELVAL for selector CA.
- * Return VAL.
- * VAL must be an identifier, and SELVAL must have been obtained from
- * nstr_match_val(VAL, CA0, IDX), where CA = &CA0[IDX].
+ * Change @val to resolve identifier to value @selval for selector @ca.
+ * Return @val.
+ * @val must be an identifier, and @selval must have been obtained from
+ * nstr_match_val(@val, CA0, @idx), where @ca = &CA0[@IDX].
*/
static struct valstr *
nstr_resolve_val(struct valstr *val, int selval, struct castr *ca)
}
/*
- * Return operator type for operand types LFT, RGT.
+ * Return operator type for operand types @lft, @rgt.
*/
static int
nstr_optype(enum nsc_type lft, enum nsc_type rgt)
lft = nstr_promote(lft);
rgt = nstr_promote(rgt);
if (lft == rgt)
- return lft;
+ return lft;
if (lft == NSC_DOUBLE && rgt == NSC_LONG)
- return NSC_DOUBLE;
+ return NSC_DOUBLE;
if (rgt == NSC_DOUBLE && lft == NSC_LONG)
- return NSC_DOUBLE;
+ return NSC_DOUBLE;
return NSC_NOTYPE;
}
/*
- * Compile a value in STR into VAL.
+ * Compile a value in @str into @val.
* Return a pointer to the first character after the value on success,
* NULL on error.
- * TYPE is the context type, a file type.
- * If STR names an array, VAL simply refers to the element with index
- * zero.
+ * @type is the context type, a file type.
*/
char *
nstr_comp_val(char *str, struct valstr *val, int type)