X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/0311ab059b86ac0b86df9dc35fafb0b21c540a6b..1a7c9a486d397cd58814616923af501282c43a26:/src/src/readconf.c diff --git a/src/src/readconf.c b/src/src/readconf.c index 42bb5afe5..5d519e996 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -592,6 +592,38 @@ return US""; +/************************************************* +* Deal with an assignment to a macro * +*************************************************/ + +/* We have a new definition; append to the list. + +Args: + name Name of the macro. Must be in storage persistent past the call + val Expansion result for the macro. Ditto persistence. +*/ + +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 = name; +m->replacement = val; +if (mlast) + mlast->next = m; +else + macros = m; +mlast = 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 +674,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 { @@ -683,7 +731,7 @@ if (redef) /* We have a new definition. */ else - (void) macro_create(name, s, FALSE); + (void) macro_create(string_copy(name), string_copy(s), FALSE); return TRUE; } @@ -708,6 +756,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 +777,56 @@ 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 != '_') s++; + +/* For each defined macro, scan the line (from after XXX= if present), +replacing all occurrences of the macro. */ *macro_found = FALSE; -while (*s) +for (m = macros; m; m = m->next) { - if (isupper(*s) || *s == '_' && isupper(s[1])) - { - macro_item * m; - unsigned mnum = 0; + uschar * p, *pp; + uschar * t = s; - while ((m = macro_search_largest_prefix(s)) && m->m_number > mnum) - { - uschar * pp; - int moveby; + while ((p = Ustrstr(t, m->name)) != NULL) + { + int moveby; - /* Expand the buffer if necessary */ +/* fprintf(stderr, "%s: matched '%s' in '%s'\n", __FUNCTION__, m->name, ss); */ + /* Expand the buffer if necessary */ - 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; - } + 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; + } - /* 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. */ + /* 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 = 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; + 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 != '_') t++; + *macro_found = TRUE; } - s++; } /* An empty macro replacement at the start of a line could mean that ss no @@ -2810,25 +2863,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 +2892,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))