Kjetil Homme's patch for extended macro features (redefinition,
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 5 Apr 2005 13:58:34 +0000 (13:58 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 5 Apr 2005 13:58:34 +0000 (13:58 +0000)
definition between driver and ACL definitions).

doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/ACKNOWLEDGMENTS
src/src/acl.c
src/src/readconf.c

index 87e001f27e6376ca023b526a6faef6598799239d..ff8c2835dc2d89ed69acbafc59cbd02513421aad 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.107 2005/04/04 10:33:49 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.108 2005/04/05 13:58:34 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -119,6 +119,9 @@ PH/20 When checking for unexpected SMTP input at connect time (before writing
 
 PH/21 Added acl_not_smtp_mime to allow for MIME scanning for non-SMTP messages.
 
 
 PH/21 Added acl_not_smtp_mime to allow for MIME scanning for non-SMTP messages.
 
+PH/22 Added support for macro redefinition, and (re)definition in between
+      driver and ACL definitions.
+
 
 A note about Exim versions 4.44 and 4.50
 ----------------------------------------
 
 A note about Exim versions 4.44 and 4.50
 ----------------------------------------
index 6f3ac86d1b3952403b917334ad77777636444e92..b2563f30c0f92e70654da0d30a3844121a40752b 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.32 2005/04/04 10:33:49 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.33 2005/04/05 13:58:34 ph10 Exp $
 
 New Features in Exim
 --------------------
 
 New Features in Exim
 --------------------
@@ -100,6 +100,36 @@ PH/04 There is a new option called acl_not_smtp_mime that allows you to scan
       MIME parts in non-SMTP messages. It operates in exactly the same way as
       acl_smtp_mime
 
       MIME parts in non-SMTP messages. It operates in exactly the same way as
       acl_smtp_mime
 
+PH/05 It is now possible to redefine a macro within the configuration file.
+      The macro must have been previously defined within the configuration (or
+      an included file). A definition on the command line using the -D option
+      causes all definitions and redefinitions within the file to be ignored.
+      In other words, -D overrides any values that are set in the file.
+      Redefinition is specified by using '==' instead of '='. For example:
+
+        MAC1 =  initial value
+        ...
+        MAC1 == updated value
+
+      Redefinition does not alter the order in which the macros are applied to
+      the subsequent lines of the configuration file. It is still the same
+      order in which the macros were originally defined. All that changes is
+      the macro's value. Redefinition makes it possible to accumulate values.
+      For example:
+
+        MAC1 =  initial value
+        ...
+        MAC1 == MAC1 and something added
+
+      This can be helpful in situations where the configuration file is built
+      from a number of other files.
+
+PH/06 Macros may now be defined or redefined between router, transport,
+      authenticator, or ACL definitions, as well as in the main part of the
+      configuration. They may not, however, be changed within an individual
+      driver or ACL, or in the local_scan, retry, or rewrite sections of the
+      configuration.
+
 
 Version 4.50
 ------------
 
 Version 4.50
 ------------
index 2b5426b9d30f45d41ed7c9c60a35b2e15a8c07b8..ce5402eb81abce20ba842844667223496fe461c1 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.18 2005/03/29 14:53:09 ph10 Exp $
+$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.19 2005/04/05 13:58:35 ph10 Exp $
 
 EXIM ACKNOWLEDGEMENTS
 
 
 EXIM ACKNOWLEDGEMENTS
 
@@ -20,7 +20,7 @@ relatively small patches.
 Philip Hazel
 
 Lists created: 20 November 2002
 Philip Hazel
 
 Lists created: 20 November 2002
-Last updated:  29 March 2005
+Last updated:  05 April 2005
 
 
 THE OLD LIST
 
 
 THE OLD LIST
@@ -41,6 +41,7 @@ Yann Golanski             Numerical hash function
 Jason Gunthorpe           IPv6 support (Linux)
 Michael Haardt            LDAP support enhancement
 Steve Haslam              First code for TLS
 Jason Gunthorpe           IPv6 support (Linux)
 Michael Haardt            LDAP support enhancement
 Steve Haslam              First code for TLS
+Kjetil Torgrim Homme      Suggested patch for macro extensions
 John Horne                Proof-reading documentation (repeatedly)
 Pierre Humblet            Cygwin support
 Paul Kelly                MySQL interface
 John Horne                Proof-reading documentation (repeatedly)
 Pierre Humblet            Cygwin support
 Paul Kelly                MySQL interface
index 704e9cb5e096211556873c9631e29afea7d3495a..1d20ff7c7555fc42b60d821bde31e239790b936c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.26 2005/03/29 10:56:48 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.27 2005/04/05 13:58:35 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -589,18 +589,11 @@ while ((s = (*func)()) != NULL)
     s++;
     }
 
     s++;
     }
 
-  /* Read the name of a verb or a condition, or the start of a new ACL */
+  /* Read the name of a verb or a condition, or the start of a new ACL, which
+  can be started by a name, or by a macro definition. */
 
   s = readconf_readname(name, sizeof(name), s);
 
   s = readconf_readname(name, sizeof(name), s);
-  if (*s == ':')
-    {
-    if (negated || name[0] == 0)
-      {
-      *error = string_sprintf("malformed ACL name in \"%s\"", saveline);
-      return NULL;
-      }
-    break;
-    }
+  if (*s == ':' || isupper(name[0] && *s == '=')) return yield;
 
   /* If a verb is unrecognized, it may be another condition or modifier that
   continues the previous verb. */
 
   /* If a verb is unrecognized, it may be another condition or modifier that
   continues the previous verb. */
index 0fe00cf6bcf2df4c7112bff2172c8aa84607e052..cf4048aae030c6633a8728b258e716f7efa1c925 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/readconf.c,v 1.6 2005/04/04 10:33:49 ph10 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.7 2005/04/05 13:58:35 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -435,6 +435,122 @@ return US"";
 
 
 
 
 
 
+/*************************************************
+*       Deal with an assignment to a macro       *
+*************************************************/
+
+/* 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
+replacement text are extracted and stored. Redefinition of existing,
+non-command line, macros is permitted using '==' instead of '='.
+
+Arguments:
+  s            points to the start of the logical line
+
+Returns:       nothing
+*/
+
+static void
+read_macro_assignment(uschar *s)
+{
+uschar name[64];
+int namelen = 0;
+BOOL redef = FALSE;
+macro_item *m;
+macro_item *mlast = NULL;
+
+while (isalnum(*s) || *s == '_')
+  {
+  if (namelen >= sizeof(name) - 1)
+    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+      "macro name too long (maximum is %d characters)", sizeof(name) - 1);
+  name[namelen++] = *s++;
+  }
+name[namelen] = 0;
+
+while (isspace(*s)) s++;
+if (*s++ != '=')
+  log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "malformed macro definition");
+
+if (*s == '=')
+  {
+  redef = TRUE;
+  s++;
+  }
+while (isspace(*s)) s++;
+
+/* If an existing macro of the same name was defined on the command line, we
+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. Note: it is documented that the other way round
+works. */
+
+for (m = macros; m != NULL; m = m->next)
+  {
+  int len = Ustrlen(m->name);
+
+  if (Ustrcmp(m->name, name) == 0)
+    {
+    if (!m->command_line && !redef)
+      log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "macro \"%s\" is already "
+       "defined (use \"==\" if you want to redefine it", name);
+    break;
+    }
+
+  if (len < namelen && Ustrstr(name, m->name) != NULL)
+    log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
+      "a macro because previously defined macro \"%s\" is a substring",
+      name, m->name);
+
+  /* We cannot have this test, because it is documented that a substring
+  macro is permitted (there is even an example).
+  *
+  * if (len > 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);
+  */
+
+  mlast = m;
+  }
+
+/* Check for an overriding command-line definition. */
+
+if (m != NULL && m->command_line) return;
+
+/* Redefinition must refer to an existing macro. */
+
+if (redef)
+  {
+  if (m == NULL)
+    log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "can't redefine an undefined macro "
+      "\"%s\"", name);
+  }
+
+/* We have a new definition. The macro_item structure includes a final vector
+called "name" which is one byte long. Thus, adding "namelen" gives us enough
+room to store the "name" string. */
+
+else
+  {
+  m = store_get(sizeof(macro_item) + namelen);
+  if (macros == NULL) macros = m; else mlast->next = m;
+  Ustrncpy(m->name, name, namelen);
+  m->name[namelen] = 0;
+  m->next = NULL;
+  m->command_line = FALSE;
+  }
+
+/* Set the value of the new or redefined macro */
+
+m->replacement = string_copy(s);
+}
+
+
+
+
 
 /*************************************************
 *            Read configuration line             *
 
 /*************************************************
 *            Read configuration line             *
@@ -2631,65 +2747,7 @@ a macro definition. */
 
 while ((s = get_config_line()) != NULL)
   {
 
 while ((s = get_config_line()) != NULL)
   {
-  if (isupper(s[0]))
-    {
-    macro_item *m;
-    macro_item *mlast = NULL;
-    uschar name[64];
-    int namelen = 0;
-
-    while (isalnum(*s) || *s == '_')
-      {
-      if (namelen >= sizeof(name) - 1)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-          "macro name too long (maximum is %d characters)", sizeof(name) - 1);
-      name[namelen++] = *s++;
-      }
-    name[namelen] = 0;
-    while (isspace(*s)) s++;
-    if (*s++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-      "malformed macro definition");
-    while (isspace(*s)) s++;
-
-    /* If an existing macro of the same name was defined on the command line,
-    we just skip this definition. Otherwise it's an error to attempt to
-    redefine a macro. It is also an error to define a macro whose name begins
-    with the name of a previously-defined macro. */
-
-    for (m = macros; m != NULL; m = m->next)
-      {
-      int len = Ustrlen(m->name);
-
-      if (Ustrcmp(m->name, name) == 0)
-        {
-        if (m->command_line) break;
-        log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "macro \"%s\" is already "
-          "defined", name);
-        }
-
-      if (len < namelen && Ustrstr(name, m->name) != NULL)
-        log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
-          "a macro because previously defined macro \"%s\" is a substring",
-          name, m->name);
-
-      mlast = m;
-      }
-    if (m != NULL) continue;   /* Found an overriding command-line definition */
-
-    m = store_get(sizeof(macro_item) + namelen);
-    m->next = NULL;
-    m->command_line = FALSE;
-    if (mlast == NULL) macros = m; else mlast->next = m;
-
-    /* This use of strcpy() is OK because we have obtained a block of store
-    whose size is based on the length of "name". The definition of the
-    macro_item structure includes a final vector called "name" which is one
-    byte long. Thus, adding "namelen" gives us enough room to store the "name"
-    string. */
-
-    Ustrcpy(m->name, name);
-    m->replacement = string_copy(s);
-    }
+  if (isupper(s[0])) read_macro_assignment(s);
 
   else if (Ustrncmp(s, "domainlist", 10) == 0)
     read_named_list(&domainlist_anchor, &domainlist_count,
 
   else if (Ustrncmp(s, "domainlist", 10) == 0)
     read_named_list(&domainlist_anchor, &domainlist_count,
@@ -3071,16 +3129,33 @@ driver_instance **p = anchor;
 driver_instance *d = NULL;
 uschar *buffer;
 
 driver_instance *d = NULL;
 uschar *buffer;
 
-/* Now process the configuration lines */
-
 while ((buffer = get_config_line()) != NULL)
   {
   uschar name[64];
 while ((buffer = get_config_line()) != NULL)
   {
   uschar name[64];
+  uschar *s;
 
 
-  /* Read the first name on the line and test for the start of a new driver.
-  If this isn't the start of a new driver, the line will be re-read. */
+  /* Read the first name on the line and test for the start of a new driver. A
+  macro definition indicates the end of the previous driver. If this isn't the
+  start of a new driver, the line will be re-read. */
 
 
-  uschar *s = readconf_readname(name, sizeof(name), buffer);
+  s = readconf_readname(name, sizeof(name), buffer);
+
+  /* Handle macro definition, first finishing off the initialization of the
+  previous driver, if any. */
+
+  if (isupper(*name) && *s == '=')
+    {
+    if (d != NULL)
+      {
+      if (d->driver_name == NULL)
+        log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+          "no driver defined for %s \"%s\"", class, d->name);
+      (d->info->init)(d);
+      d = NULL;
+      }
+    read_macro_assignment(buffer);
+    continue;
+    }
 
   /* If the line starts with a name terminated by a colon, we are at the
   start of the definition of a new driver. The rest of the line must be
 
   /* If the line starts with a name terminated by a colon, we are at the
   start of the definition of a new driver. The rest of the line must be
@@ -3128,7 +3203,8 @@ while ((buffer = get_config_line()) != NULL)
     continue;
     }
 
     continue;
     }
 
-  /* Give an error if we have not set up a current driver yet. */
+  /* Not the start of a new driver. Give an error if we have not set up a
+  current driver yet. */
 
   if (d == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
     "%s name missing", class);
 
   if (d == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
     "%s name missing", class);
@@ -3582,7 +3658,8 @@ if (skip)
   return;
   }
 
   return;
   }
 
-/* Read each ACL and add it into the tree */
+/* Read each ACL and add it into the tree. Macro (re)definitions are allowed
+between ACLs. */
 
 acl_line = get_config_line();
 
 
 acl_line = get_config_line();
 
@@ -3593,8 +3670,15 @@ while(acl_line != NULL)
   uschar *error;
 
   p = readconf_readname(name, sizeof(name), acl_line);
   uschar *error;
 
   p = readconf_readname(name, sizeof(name), acl_line);
-  if (*p != ':')
-    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing ACL name");
+  if (isupper(*name) && *p == '=')
+    {
+    read_macro_assignment(acl_line);
+    acl_line = get_config_line();
+    continue;
+    }
+
+  if (*p != ':' || name[0] == 0)
+    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
 
   node = store_get(sizeof(tree_node) + Ustrlen(name));
   Ustrcpy(node->name, name);
 
   node = store_get(sizeof(tree_node) + Ustrlen(name));
   Ustrcpy(node->name, name);