X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/2be324ee17454ae5c5a32a3533aced861d103074..d3e58fcb87faf7131a2712fcfaef200ffd191f05:/src/src/readconf.c diff --git a/src/src/readconf.c b/src/src/readconf.c index acf64570a..658719dff 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2017 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading the configuration file, and for displaying @@ -195,7 +195,9 @@ static optionlist optionlist_config[] = { { "local_from_prefix", opt_stringptr, &local_from_prefix }, { "local_from_suffix", opt_stringptr, &local_from_suffix }, { "local_interfaces", opt_stringptr, &local_interfaces }, +#ifdef HAVE_LOCAL_SCAN { "local_scan_timeout", opt_time, &local_scan_timeout }, +#endif { "local_sender_retain", opt_bool, &local_sender_retain }, { "localhost_number", opt_stringptr, &host_number_string }, { "log_file_path", opt_stringptr, &log_file_path }, @@ -592,6 +594,40 @@ return US""; +/************************************************* +* Deal with an assignment to a macro * +*************************************************/ + +/* We have a new definition; append to the list. + +Args: + name Name of the macro; will be copied + val Expansion result for the macro; will be copied +*/ + +macro_item * +macro_create(const uschar * name, const uschar * val, BOOL command_line) +{ +macro_item * m = store_get(sizeof(macro_item)); + +/* fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val); */ +m->next = NULL; +m->command_line = command_line; +m->namelen = Ustrlen(name); +m->replen = Ustrlen(val); +m->name = string_copy(name); +m->replacement = string_copy(val); +if (mlast) + mlast->next = m; +else + macros = m; +mlast = m; +if (!macros_user) + macros_user = m; +return m; +} + + /* This function is called when a line that starts with an upper case letter is encountered. The argument "line" should contain a complete logical line, and start with the first letter of the macro name. The macro name and the @@ -642,37 +678,53 @@ while (isspace(*s)) s++; just skip this definition. It's an error to attempt to redefine a macro without redef set to TRUE, or to redefine a macro when it hasn't been defined earlier. It is also an error to define a macro whose name begins with the name of a -previously defined macro. +previously defined macro. This is the requirement that make using a tree +for macros hard; we must check all macros for the substring. Perhaps a +sorted list, and a bsearch, would work? Note: it is documented that the other way round works. */ -if ((m = macro_search_prefix(name))) +for (m = macros; m; m = m->next) { - if (m->namelen < namelen) /* substring match */ + if (Ustrcmp(m->name, name) == 0) { - log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as " - "a macro because previously defined macro \"%s\" is a substring", - name, m->tnode.name); - return FALSE; + if (!m->command_line && !redef) + { + log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already " + "defined (use \"==\" if you want to redefine it", name); + return FALSE; + } + break; } - /* exact match */ - if (!m->command_line && !redef) + + if (m->namelen < namelen && Ustrstr(name, m->name) != NULL) { - log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already " - "defined (use \"==\" if you want to redefine it", name); + log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as " + "a macro because previously defined macro \"%s\" is a substring", + name, m->name); return FALSE; } - if (m->command_line) /* overriding cmdline definition */ - return TRUE; + /* We cannot have this test, because it is documented that a substring + macro is permitted (there is even an example). + * + * if (m->namelen > namelen && Ustrstr(m->name, name) != NULL) + * log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as " + * "a macro because it is a substring of previously defined macro \"%s\"", + * name, m->name); + */ } +/* Check for an overriding command-line definition. */ + +if (m && m->command_line) return TRUE; + /* Redefinition must refer to an existing macro. */ if (redef) if (m) { m->replen = Ustrlen(s); - m->tnode.data.ptr = string_copy(s); + m->replacement = string_copy(s); } else { @@ -693,7 +745,7 @@ return TRUE; /* Process line for macros. The line is in big_buffer starting at offset len. Expand big_buffer if needed. Handle definitions of new macros, and -imacro expansions, rewriting the line in thw buffer. +macro expansions, rewriting the line in the buffer. Arguments: len Offset in buffer of start of line @@ -708,6 +760,7 @@ macros_expand(int len, int * newlen, BOOL * macro_found) { uschar * ss = big_buffer + len; uschar * s; +macro_item * m; /* Find the true start of the physical line - leading spaces are always ignored. */ @@ -728,52 +781,60 @@ if (len == 0 && isupper(*s)) if (*s != '=') s = ss; /* Not a macro definition */ } -/* Scan the line (from after XXX= if present), replacing any macros. Rescan -after replacement for any later-defined macros. */ +/* Skip leading chars which cannot start a macro name, to avoid multiple +pointless rescans in Ustrstr calls. */ + +while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++; + +/* For each defined macro, scan the line (from after XXX= if present), +replacing all occurrences of the macro. */ *macro_found = FALSE; -while (*s) +if (*s) for (m = *s == '_' ? macros : macros_user; m; m = m->next) { - if (isupper(*s) || *s == '_' && isupper(s[1])) - { - macro_item * m; - unsigned mnum = 0; + uschar * p, *pp; + uschar * t; - while ((m = macro_search_largest_prefix(s)) && m->m_number > mnum) - { - uschar * pp; - int moveby; + while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++; + if (!*s) break; - /* Expand the buffer if necessary */ + t = s; + while ((p = Ustrstr(t, m->name)) != NULL) + { + int moveby; - while (*newlen - m->namelen + m->replen + 1 > big_buffer_size) - { - int newsize = big_buffer_size + BIG_BUFFER_SIZE; - uschar *newbuffer = store_malloc(newsize); - memcpy(newbuffer, big_buffer, *newlen + 1); - s = newbuffer + (s - big_buffer); - ss = newbuffer + (ss - big_buffer); - big_buffer_size = newsize; - store_free(big_buffer); - big_buffer = newbuffer; - } +/* fprintf(stderr, "%s: matched '%s' in '%s'\n", __FUNCTION__, m->name, ss); */ + /* Expand the buffer if necessary */ - /* Shuffle the remaining characters up or down in the buffer before - copying in the replacement text. Don't rescan the replacement for this - same macro. */ + while (*newlen - m->namelen + m->replen + 1 > big_buffer_size) + { + int newsize = big_buffer_size + BIG_BUFFER_SIZE; + uschar *newbuffer = store_malloc(newsize); + memcpy(newbuffer, big_buffer, *newlen + 1); + p = newbuffer + (p - big_buffer); + s = newbuffer + (s - big_buffer); + ss = newbuffer + (ss - big_buffer); + t = newbuffer + (t - big_buffer); + big_buffer_size = newsize; + store_free(big_buffer); + big_buffer = newbuffer; + } - pp = s + m->namelen; - if ((moveby = m->replen - m->namelen) != 0) - { - memmove(s + m->replen, pp, (big_buffer + *newlen) - pp + 1); - *newlen += moveby; - } - Ustrncpy(s, m->tnode.data.ptr, m->replen); - *macro_found = TRUE; - mnum = m->m_number; + /* Shuffle the remaining characters up or down in the buffer before + copying in the replacement text. Don't rescan the replacement for this + same macro. */ + + pp = p + m->namelen; + if ((moveby = m->replen - m->namelen) != 0) + { + memmove(p + m->replen, pp, (big_buffer + *newlen) - pp + 1); + *newlen += moveby; } + Ustrncpy(p, m->replacement, m->replen); + t = p + m->replen; + while (*t && !isupper(*t) && !(*t == '_' && isupper(t[1]))) t++; + *macro_found = TRUE; } - s++; } /* An empty macro replacement at the start of a line could mean that ss no @@ -2810,25 +2871,26 @@ else if (Ustrcmp(type, "macro") == 0) fprintf(stderr, "exim: permission denied\n"); return FALSE; } - - if (name) - if ((m = macro_search(name))) - macro_print(m->tnode.name, m->tnode.data.ptr, (void *)(long)names_only); - else + for (m = macros; m; m = m->next) + if (!name || Ustrcmp(name, m->name) == 0) { - printf("%s %s not found\n", type, name); - return FALSE; + if (names_only) + printf("%s\n", CS m->name); + else + printf("%s=%s\n", CS m->name, CS m->replacement); + if (name) + return TRUE; } - else - tree_walk(tree_macros, macro_print, (void *)(long)names_only); + if (!name) return TRUE; - return TRUE; + printf("%s %s not found\n", type, name); + return FALSE; } if (names_only) { for (; d; d = d->next) printf("%s\n", CS d->name); - return TRUE;; + return TRUE; } /* Either search for a given driver, or print all of them */ @@ -2838,8 +2900,7 @@ for (; d; d = d->next) BOOL rc = FALSE; if (!name) printf("\n%s %s:\n", d->name, type); - else if (Ustrcmp(d->name, name) != 0) - continue; + else if (Ustrcmp(d->name, name) != 0) continue; for (ol = ol2; ol < ol2 + size; ol++) if (!(ol->type & opt_hidden))