US"exists",
US"first_delivery",
US"forall",
+ US"forall_json",
+ US"forall_jsons",
US"forany",
+ US"forany_json",
+ US"forany_jsons",
US"ge",
US"gei",
US"gt",
ECOND_EXISTS,
ECOND_FIRST_DELIVERY,
ECOND_FORALL,
+ ECOND_FORALL_JSON,
+ ECOND_FORALL_JSONS,
ECOND_FORANY,
+ ECOND_FORANY_JSON,
+ ECOND_FORANY_JSONS,
ECOND_STR_GE,
ECOND_STR_GEI,
ECOND_STR_GT,
{ "tls_in_bits", vtype_int, &tls_in.bits },
{ "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified },
{ "tls_in_cipher", vtype_stringptr, &tls_in.cipher },
+ { "tls_in_cipher_std", vtype_stringptr, &tls_in.cipher_stdname },
{ "tls_in_ocsp", vtype_int, &tls_in.ocsp },
{ "tls_in_ourcert", vtype_cert, &tls_in.ourcert },
{ "tls_in_peercert", vtype_cert, &tls_in.peercert },
{ "tls_out_bits", vtype_int, &tls_out.bits },
{ "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified },
{ "tls_out_cipher", vtype_stringptr, &tls_out.cipher },
+ { "tls_out_cipher_std", vtype_stringptr, &tls_out.cipher_stdname },
#ifdef SUPPORT_DANE
{ "tls_out_dane", vtype_bool, &tls_out.dane_verified },
#endif
expand_getcertele(uschar * field, uschar * certvar)
{
var_entry * vp;
-certfield * cp;
if (!(vp = find_var_ent(certvar)))
{
if (*field >= '0' && *field <= '9')
return tls_cert_ext_by_oid(*(void **)vp->value, field, 0);
-for(cp = certfields;
- cp < certfields + nelem(certfields);
- cp++)
+for (certfield * cp = certfields;
+ cp < certfields + nelem(certfields);
+ cp++)
if (Ustrncmp(cp->name, field, cp->namelen) == 0)
{
uschar * modifier = *(field += cp->namelen) == ','
BOOL found = !name;
int len = name ? Ustrlen(name) : 0;
BOOL comma = FALSE;
-header_line * h;
gstring * g = NULL;
-for (h = header_list; h; h = h->next)
+for (header_line * h = header_list; h; h = h->next)
if (h->type != htype_old && h->text) /* NULL => Received: placeholder */
if (!name || (len <= h->slen && strncmpic(name, h->text, len) == 0))
{
{
uschar * s;
gstring * g = NULL;
-int i;
if (!f.enable_dollar_recipients) return NULL;
-for (i = 0; i < recipients_count; i++)
+for (int i = 0; i < recipients_count; i++)
{
s = recipients_list[i].address;
g = string_append2_listele_n(g, US", ", s, Ustrlen(s));
case vtype_pspace:
{
int inodes;
- sprintf(CS var_buffer, "%d",
+ sprintf(CS var_buffer, PR_EXIM_ARITH,
receive_statvfs(val == (void *)TRUE, &inodes));
}
return var_buffer;
read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping,
BOOL check_end, uschar *name, BOOL *resetok)
{
-int i;
const uschar *s = *sptr;
while (isspace(*s)) s++;
-for (i = 0; i < n; i++)
+for (int i = 0; i < n; i++)
{
if (*s != '{')
{
+/* Return pointer to dewrapped string, with enclosing specified chars removed.
+The given string is modified on return. Leading whitespace is skipped while
+looking for the opening wrap character, then the rest is scanned for the trailing
+(non-escaped) wrap character. A backslash in the string will act as an escape.
+
+A nul is written over the trailing wrap, and a pointer to the char after the
+leading wrap is returned.
+
+Arguments:
+ s String for de-wrapping
+ wrap Two-char string, the first being the opener, second the closer wrapping
+ character
+Return:
+ Pointer to de-wrapped string, or NULL on error (with expand_string_message set).
+*/
+
+static uschar *
+dewrap(uschar * s, const uschar * wrap)
+{
+uschar * p = s;
+unsigned depth = 0;
+BOOL quotesmode = wrap[0] == wrap[1];
+
+while (isspace(*p)) p++;
+
+if (*p == *wrap)
+ {
+ s = ++p;
+ wrap++;
+ while (*p)
+ {
+ if (*p == '\\') p++;
+ else if (!quotesmode && *p == wrap[-1]) depth++;
+ else if (*p == *wrap)
+ if (depth == 0)
+ {
+ *p = '\0';
+ return s;
+ }
+ else
+ depth--;
+ p++;
+ }
+ }
+expand_string_message = string_sprintf("missing '%c'", *wrap);
+return NULL;
+}
+
+
+/* Pull off the leading array or object element, returning
+a copy in an allocated string. Update the list pointer.
+
+The element may itself be an abject or array.
+Return NULL when the list is empty.
+*/
+
+static uschar *
+json_nextinlist(const uschar ** list)
+{
+unsigned array_depth = 0, object_depth = 0;
+const uschar * s = *list, * item;
+
+while (isspace(*s)) s++;
+
+for (item = s;
+ *s && (*s != ',' || array_depth != 0 || object_depth != 0);
+ s++)
+ switch (*s)
+ {
+ case '[': array_depth++; break;
+ case ']': array_depth--; break;
+ case '{': object_depth++; break;
+ case '}': object_depth--; break;
+ }
+*list = *s ? s+1 : s;
+if (item == s) return NULL;
+item = string_copyn(item, s - item);
+DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item);
+return US item;
+}
+
+
+
/*************************************************
* Read and evaluate a condition *
*************************************************/
BOOL tempcond, combined_cond;
BOOL *subcondptr;
BOOL sub2_honour_dollar = TRUE;
-int i, rc, cond_type, roffset;
+BOOL is_forany, is_json, is_jsons;
+int rc, cond_type, roffset;
int_eximarith_t num[2];
struct stat statbuf;
uschar name[256];
case ECOND_STR_GE:
case ECOND_STR_GEI:
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
{
/* Sometimes, we don't expand substrings; too many insecure configurations
created using match_address{}{} and friends, where the second param
if (sublen == 24)
{
- uschar *coded = b64encode(digest, 16);
+ uschar *coded = b64encode(CUS digest, 16);
DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+5);
tempcond = (Ustrcmp(coded, sub[1]+5) == 0);
}
else if (sublen == 32)
{
- int i;
uschar coded[36];
- for (i = 0; i < 16; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
+ for (int i = 0; i < 16; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
coded[32] = 0;
DEBUG(D_auth) debug_printf("crypteq: using MD5+hex hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+5);
if (sublen == 28)
{
- uschar *coded = b64encode(digest, 20);
+ uschar *coded = b64encode(CUS digest, 20);
DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+6);
tempcond = (Ustrcmp(coded, sub[1]+6) == 0);
}
else if (sublen == 40)
{
- int i;
uschar coded[44];
- for (i = 0; i < 20; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
+ for (int i = 0; i < 20; i++) sprintf(CS (coded+2*i), "%02X", digest[i]);
coded[40] = 0;
DEBUG(D_auth) debug_printf("crypteq: using SHA1+hex hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+6);
/* forall/forany: iterates a condition with different values */
- case ECOND_FORALL:
- case ECOND_FORANY:
+ case ECOND_FORALL: is_forany = FALSE; is_json = FALSE; is_jsons = FALSE; goto FORMANY;
+ case ECOND_FORANY: is_forany = TRUE; is_json = FALSE; is_jsons = FALSE; goto FORMANY;
+ case ECOND_FORALL_JSON: is_forany = FALSE; is_json = TRUE; is_jsons = FALSE; goto FORMANY;
+ case ECOND_FORANY_JSON: is_forany = TRUE; is_json = TRUE; is_jsons = FALSE; goto FORMANY;
+ case ECOND_FORALL_JSONS: is_forany = FALSE; is_json = TRUE; is_jsons = TRUE; goto FORMANY;
+ case ECOND_FORANY_JSONS: is_forany = TRUE; is_json = TRUE; is_jsons = TRUE; goto FORMANY;
+
+ FORMANY:
{
const uschar * list;
int sep = 0;
return NULL;
}
- if (yield != NULL) *yield = !testfor;
+ if (yield) *yield = !testfor;
list = sub[0];
- while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL)
+ if (is_json) list = dewrap(string_copy(list), US"[]");
+ while ((iterate_item = is_json
+ ? json_nextinlist(&list) : string_nextinlist(&list, &sep, NULL, 0)))
{
+ if (is_jsons)
+ if (!(iterate_item = dewrap(iterate_item, US"\"\"")))
+ {
+ expand_string_message =
+ string_sprintf("%s wrapping string result for extract jsons",
+ expand_string_message);
+ iterate_item = save_iterate_item;
+ return NULL;
+ }
+
DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, iterate_item);
if (!eval_condition(sub[1], resetok, &tempcond))
{
DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", name,
tempcond? "true":"false");
- if (yield != NULL) *yield = (tempcond == testfor);
- if (tempcond == (cond_type == ECOND_FORANY)) break;
+ if (yield) *yield = (tempcond == testfor);
+ if (tempcond == is_forany) break;
}
iterate_item = save_iterate_item;
static int
save_expand_strings(uschar **save_expand_nstring, int *save_expand_nlength)
{
-int i;
-for (i = 0; i <= expand_nmax; i++)
+for (int i = 0; i <= expand_nmax; i++)
{
save_expand_nstring[i] = expand_nstring[i];
save_expand_nlength[i] = expand_nlength[i];
restore_expand_strings(int save_expand_nmax, uschar **save_expand_nstring,
int *save_expand_nlength)
{
-int i;
expand_nmax = save_expand_nmax;
-for (i = 0; i <= expand_nmax; i++)
+for (int i = 0; i <= expand_nmax; i++)
{
expand_nstring[i] = save_expand_nstring[i];
expand_nlength[i] = save_expand_nlength[i];
{
gstring * hash_source;
uschar * p;
-int i;
hctx h;
uschar innerhash[20];
uschar finalhash[20];
memset(innerkey, 0x36, 64);
memset(outerkey, 0x5c, 64);
-for (i = 0; i < Ustrlen(key); i++)
+for (int i = 0; i < Ustrlen(key); i++)
{
innerkey[i] ^= key[i];
outerkey[i] ^= key[i];
chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash);
p = finalhash_hex;
-for (i = 0; i < 3; i++)
+for (int i = 0; i < 3; i++)
{
*p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
*p++ = hex_digits[finalhash[i] & 0x0f];
cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol)
{
int rc;
-uschar * s;
uschar buffer[1024];
+/*XXX could we read direct into a pre-grown string? */
+
while ((rc = tls_read(tls_ctx, buffer, sizeof(buffer))) > 0)
- for (s = buffer; rc--; s++)
+ for (uschar * s = buffer; rc--; s++)
yield = eol && *s == '\n'
? string_cat(yield, eol) : string_catn(yield, s, 1);
-/* Return pointer to dewrapped string, with enclosing specified chars removed.
-The given string is modified on return. Leading whitespace is skipped while
-looking for the opening wrap character, then the rest is scanned for the trailing
-(non-escaped) wrap character. A backslash in the string will act as an escape.
-
-A nul is written over the trailing wrap, and a pointer to the char after the
-leading wrap is returned.
-
-Arguments:
- s String for de-wrapping
- wrap Two-char string, the first being the opener, second the closer wrapping
- character
-Return:
- Pointer to de-wrapped string, or NULL on error (with expand_string_message set).
-*/
-
-static uschar *
-dewrap(uschar * s, const uschar * wrap)
-{
-uschar * p = s;
-unsigned depth = 0;
-BOOL quotesmode = wrap[0] == wrap[1];
-
-while (isspace(*p)) p++;
-
-if (*p == *wrap)
- {
- s = ++p;
- wrap++;
- while (*p)
- {
- if (*p == '\\') p++;
- else if (!quotesmode && *p == wrap[-1]) depth++;
- else if (*p == *wrap)
- if (depth == 0)
- {
- *p = '\0';
- return s;
- }
- else
- depth--;
- p++;
- }
- }
-expand_string_message = string_sprintf("missing '%c'", *wrap);
-return NULL;
-}
-
-
-/* Pull off the leading array or object element, returning
-a copy in an allocated string. Update the list pointer.
-
-The element may itself be an abject or array.
-*/
-
-uschar *
-json_nextinlist(const uschar ** list)
-{
-unsigned array_depth = 0, object_depth = 0;
-const uschar * s = *list, * item;
-
-while (isspace(*s)) s++;
-
-for (item = s;
- *s && (*s != ',' || array_depth != 0 || object_depth != 0);
- s++)
- switch (*s)
- {
- case '[': array_depth++; break;
- case ']': array_depth--; break;
- case '{': object_depth++; break;
- case '}': object_depth--; break;
- }
-*list = *s ? s+1 : s;
-item = string_copyn(item, s - item);
-DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item);
-return US item;
-}
-
-
-
/*************************************************
* Expand string *
*************************************************/
(void)sscanf(CS now,"%u",&inow);
(void)sscanf(CS daystamp,"%u",&iexpire);
- /* When "iexpire" is < 7, a "flip" has occured.
+ /* When "iexpire" is < 7, a "flip" has occurred.
Adjust "inow" accordingly. */
if ( (iexpire < 7) && (inow >= 993) ) inow = 0;
case EITEM_NHASH:
case EITEM_SUBSTR:
{
- int i;
int len;
uschar *ret;
int val[2] = { 0, -1 };
}
}
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++) if (sub[i])
{
- if (sub[i] == NULL) continue;
val[i] = (int)Ustrtol(sub[i], &ret, 10);
if (*ret != 0 || (i != 0 && val[i] < 0))
{
md5 md5_base;
hctx sha1_ctx;
void *use_base;
- int type, i;
+ int type;
int hashlen; /* Number of octets for the hash algorithm's output */
int hashblocklen; /* Number of octets the hash algorithm processes */
uschar *keyptr, *p;
memset(innerkey, 0x36, hashblocklen);
memset(outerkey, 0x5c, hashblocklen);
- for (i = 0; i < keylen; i++)
+ for (int i = 0; i < keylen; i++)
{
innerkey[i] ^= keyptr[i];
outerkey[i] ^= keyptr[i];
/* Encode the final hash as a hex string */
p = finalhash_hex;
- for (i = 0; i < hashlen; i++)
+ for (int i = 0; i < hashlen; i++)
{
*p++ = hex_digits[(finalhash[i] & 0xf0) >> 4];
*p++ = hex_digits[finalhash[i] & 0x0f];
int ovector[3*(EXPAND_MAXN+1)];
int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra,
PCRE_EOPT | emptyopt, ovector, nelem(ovector));
- int nn;
uschar *insert;
/* No match - if we previously set PCRE_NOTEMPTY after a null match, this
if (n == 0) n = EXPAND_MAXN + 1;
expand_nmax = 0;
- for (nn = 0; nn < n*2; nn += 2)
+ for (int nn = 0; nn < n*2; nn += 2)
{
expand_nstring[expand_nmax] = subject + ovector[nn];
expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
case EITEM_EXTRACT:
{
- int i;
- int j;
int field_number = 1;
BOOL field_number_set = FALSE;
uschar *save_lookup_value = lookup_value;
uschar *sub[3];
int save_expand_nmax =
save_expand_strings(save_expand_nstring, save_expand_nlength);
- enum {extract_basic, extract_json} fmt = extract_basic;
+
+ /* On reflection the original behaviour of extract-json for a string
+ result, leaving it quoted, was a mistake. But it was already published,
+ hence the addition of jsons. In a future major version, make json
+ work like josons, and withdraw jsons. */
+
+ enum {extract_basic, extract_json, extract_jsons} fmt = extract_basic;
while (isspace(*s)) s++;
/* Check for a format-variant specifier */
if (*s != '{') /*}*/
- {
- if (Ustrncmp(s, "json", 4) == 0) {fmt = extract_json; s += 4;}
- }
+ if (Ustrncmp(s, "json", 4) == 0)
+ if (*(s += 4) == 's')
+ {fmt = extract_jsons; s++;}
+ else
+ fmt = extract_json;
/* While skipping we cannot rely on the data for expansions being
available (eg. $item) hence cannot decide on numeric vs. keyed.
if (skipping)
{
- for (j = 5; j > 0 && *s == '{'; j--) /*'}'*/
+ for (int j = 5; j > 0 && *s == '{'; j--) /*'}'*/
{
if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
goto EXPAND_FAILED; /*'{'*/
}
}
- else for (i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */
+ else for (int i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */
{
while (isspace(*s)) s++;
if (*s == '{') /*'}'*/
if (*p == 0)
{
field_number *= x;
- if (fmt != extract_json) j = 3; /* Need 3 args */
+ if (fmt == extract_basic) j = 3; /* Need 3 args */
field_number_set = TRUE;
}
}
break;
case extract_json:
+ case extract_jsons:
{
uschar * s, * item;
const uschar * list;
}
while (field_number > 0 && (item = json_nextinlist(&list)))
field_number--;
- s = item;
- lookup_value = s;
- while (*s) s++;
- while (--s >= lookup_value && isspace(*s)) *s = '\0';
+ if ((lookup_value = s = item))
+ {
+ while (*s) s++;
+ while (--s >= lookup_value && isspace(*s)) *s = '\0';
+ }
}
else
{
}
}
}
+
+ if ( fmt == extract_jsons
+ && lookup_value
+ && !(lookup_value = dewrap(lookup_value, US"\"\"")))
+ {
+ expand_string_message =
+ string_sprintf("%s wrapping string result for extract jsons",
+ expand_string_message);
+ goto EXPAND_FAILED_CURLY;
+ }
+ break; /* json/s */
}
/* If no string follows, $value gets substituted; otherwise there can
case EITEM_LISTEXTRACT:
{
- int i;
int field_number = 1;
uschar *save_lookup_value = lookup_value;
uschar *sub[2];
/* Read the field & list arguments */
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
{
while (isspace(*s)) s++;
- if (*s != '{') /*}*/
+ if (*s != '{') /*'}'*/
{
expand_string_message = string_sprintf(
"missing '{' for arg %d of listextract", i+1);
/* Look up the dynamically loaded object handle in the tree. If it isn't
found, dlopen() the file and put the handle in the tree for next time. */
- t = tree_search(dlobj_anchor, argv[0]);
- if (t == NULL)
+ if (!(t = tree_search(dlobj_anchor, argv[0])))
{
void *handle = dlopen(CS argv[0], RTLD_LAZY);
if (handle == NULL)
{
md5 base;
uschar digest[16];
- int j;
md5_start(&base);
md5_end(&base, sub, Ustrlen(sub), digest);
- for (j = 0; j < 16; j++)
+ for (int j = 0; j < 16; j++)
yield = string_fmt_append(yield, "%02x", digest[j]);
}
continue;
{
hctx h;
uschar digest[20];
- int j;
sha1_start(&h);
sha1_end(&h, sub, Ustrlen(sub), digest);
- for (j = 0; j < 20; j++)
+ for (int j = 0; j < 20; j++)
yield = string_fmt_append(yield, "%02X", digest[j]);
}
continue;
uschar *out = sub;
uschar *enc;
- for (enc = sub; *enc != 0; enc++)
+ for (enc = sub; *enc; enc++)
{
if (!isxdigit(*enc))
{
if (isdigit(c)) c -= '0';
else c = toupper(c) - 'A' + 10;
if (b == -1)
- {
b = c << 4;
- }
else
{
*out++ = b | c;
}
}
- enc = b64encode(sub, out - sub);
+ enc = b64encode(CUS sub, out - sub);
yield = string_cat(yield, enc);
continue;
}
case EOP_ESCAPE8BIT:
{
- const uschar * s = sub;
uschar c;
- for (s = sub; (c = *s); s++)
+ for (const uschar * s = sub; (c = *s); s++)
yield = c < 127 && c != '\\'
? string_catn(yield, s, 1)
: string_fmt_append(yield, "\\%03o", c);
continue;
}
- /* Handle time period formating */
+ /* Handle time period formatting */
case EOP_TIME_EVAL:
{
#ifdef SUPPORT_TLS
uschar * s = vp && *(void **)vp->value
? tls_cert_der_b64(*(void **)vp->value)
- : b64encode(sub, Ustrlen(sub));
+ : b64encode(CUS sub, Ustrlen(sub));
#else
- uschar * s = b64encode(sub, Ustrlen(sub));
+ uschar * s = b64encode(CUS sub, Ustrlen(sub));
#endif
yield = string_cat(yield, s);
continue;
{
uschar smode[12];
uschar **modetable[3];
- int i;
mode_t mode;
struct stat st;
modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid;
modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid;
- for (i = 0; i < 3; i++)
+ for (int i = 0; i < 3; i++)
{
memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3);
mode >>= 3;
{
err_ctx e = { .region_start = ptr, .region_end = US ptr + len,
.var_name = NULL, .var_data = NULL };
-int i;
-var_entry * v;
/* check acl_ variables */
tree_walk(acl_var_c, assert_variable_notin, &e);
tree_walk(acl_var_m, assert_variable_notin, &e);
/* check auth<n> variables */
-for (i = 0; i < AUTH_VARS; i++) if (auth_vars[i])
+for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i])
assert_variable_notin(US"auth<n>", auth_vars[i], &e);
/* check regex<n> variables */
-for (i = 0; i < REGEX_VARS; i++) if (regex_vars[i])
+for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i])
assert_variable_notin(US"regex<n>", regex_vars[i], &e);
/* check known-name variables */
-for (v = var_table; v < var_table + var_table_size; v++)
+for (var_entry * v = var_table; v < var_table + var_table_size; v++)
if (v->type == vtype_stringptr)
assert_variable_notin(US v->name, *(USS v->value), &e);
if (n == 0) n = EXPAND_MAXN + 1;
if (yield)
{
- int nn;
- expand_nmax = (setup < 0)? 0 : setup + 1;
- for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2)
+ expand_nmax = setup < 0 ? 0 : setup + 1;
+ for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
{
expand_nstring[expand_nmax] = subject + ovector[nn];
expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
int main(int argc, uschar **argv)
{
-int i;
uschar buffer[1024];
debug_selector = D_v;
debug_fd = fileno(debug_file);
big_buffer = malloc(big_buffer_size);
-for (i = 1; i < argc; i++)
+for (int i = 1; i < argc; i++)
{
if (argv[i][0] == '+')
{