[syslog-ng]template() and template_escape() extension for all destination drivers

Achim Gsell achim@cybercity.ch
Thu, 30 Jan 2003 19:42:14 +0100


--------------Boundary-00=_EMIJ9GU8G94XFM4QI2NW
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 8bit

Hi,

On Wed, Oct 30, 2002 at 20:47:55+0100, Balazs Scheidler wrote:

> I came to having some time to integrate pending patches, and found your
> patch. I have a couple of comments, which needs to be fixed before releasing
> a syslog-ng with these parts integrated.
>
> * expand_macro & friends should be declared in a header file instead of
>   declaring it in every referencing module.  maybe a new file called
>   macros.c would be a good way of resolving this problem.

> * expand_macros returns a newly allocated string, which must be freed, so
>   constructs like this cause memory leaks:

> +               if (self->template_output)
> +                       A_WRITE_STRING(&self->dest_buf->super,
> +                                      c_format("<%i>%S\n",
> +                                               msg->pri,
> expand_macros(self->cfg, self->template_output, self->template_escape, msg)));
>
> c_format() has an option to free an ol_string by specifying 'f' like this:
> 
> c_format("%fS", expand_macros()) ...

> Otherwise your patch looks ok to me. If you want to have it integrated prior
> to 1.6.0 is being released, please send a modified diff.

In the attachment you find a revised version of the patch. The functions
"expand_macros()" & friend are now declared in "macros.h" and coded in 
"macros.c". The memory leaks are fixed ...

Achim
--------------Boundary-00=_EMIJ9GU8G94XFM4QI2NW
Content-Type: text/x-diff;
  charset="us-ascii";
  name="template.diff"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="template.diff"

diff -urN syslog-ng-1.5.25/src/Makefile.am syslog-ng-1.5.25.template-patch/src/Makefile.am
--- syslog-ng-1.5.25/src/Makefile.am	2001-07-08 19:04:21.000000000 +0200
+++ syslog-ng-1.5.25.template-patch/src/Makefile.am	2003-01-29 08:54:04.000000000 +0100
@@ -23,7 +23,8 @@
 	afremctrl.h \
 	utils.h \
 	syslog-names.h \
-	nscache.h
+	nscache.h \
+	macros.h
 
 dist_x_files = \
 	cfgfile.h.x \
@@ -71,7 +72,8 @@
 	afremctrl.c \
 	nscache.c \
 	utils.c \
-	syslog-names.c
+	syslog-names.c \
+	macros.c
 
 syslog_ng_LDADD = @LIBOBJS@
 
diff -urN syslog-ng-1.5.25/src/affile.c syslog-ng-1.5.25.template-patch/src/affile.c
--- syslog-ng-1.5.25/src/affile.c	2003-01-08 10:31:37.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/affile.c	2003-01-29 09:53:04.000000000 +0100
@@ -63,6 +63,7 @@
 #include "center.h"
 #include "resource.h"
 #include "cfgfile.h"
+#include "macros.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -85,9 +86,6 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-struct ol_string *
-expand_macros(struct syslog_config *cfg, struct ol_string *template, int template_escape, struct log_info *msg);
-
 
 static int do_open_file(struct ol_string *name, int flags, 
                         int uid, int gid, int mode, 
@@ -314,12 +312,19 @@
 	CAST(affile_dest_writer, self, c);
 
 	if (self->dest) {
-		struct ol_string *l;
-		if (self->owner->template_output)
-			l = expand_macros(self->owner->cfg, self->owner->template_output, self->owner->template_escape, msg);
-		else
-			l = c_format("%S %S %S\n", msg->date, msg->host, msg->msg);
-		A_WRITE_STRING(&self->dest->buffer->super, l);
+		struct ol_string *msg_line;
+
+		if (self->owner->template_output) {
+			msg_line = expand_macros(
+					   self->owner->cfg,
+					   self->owner->template_output,
+					   self->owner->template_escape, msg);
+		} else {
+			msg_line = c_format("%S %S %S\n",
+					    msg->date,
+					    msg->host, msg->msg);
+		}
+		A_WRITE_STRING(&self->dest->buffer->super, msg_line);
 		if (self->reap)
 			io_callout_set_timeout(self->reap, self->time_reap);
 	}
@@ -484,452 +489,6 @@
 }
 
 
-
-#define MAX_MACRO_ARGS        32
-#define MAX_EXPANDED_MACRO    2048
-
-struct macro_def {
-	char *name;
-	int id;
-};
-
-#define M_FACILITY 0
-#define M_LEVEL    1
-#define M_TAG      2
-
-#define M_DATE     3
-#define M_FULLDATE 4
-#define M_ISODATE  5
-#define M_YEAR     6
-#define M_MONTH    7
-#define M_DAY      8
-#define M_HOUR     9
-#define M_MIN      10
-#define M_SEC      11
-#define M_WEEKDAY  12
-#define M_TZOFFSET 13
-#define M_TZ       14
-#define M_UNIXTIME 15
-
-#define M_DATE_RECVD     16
-#define M_FULLDATE_RECVD 17
-#define M_ISODATE_RECVD  18
-#define M_YEAR_RECVD     19
-#define M_MONTH_RECVD    20
-#define M_DAY_RECVD      21
-#define M_HOUR_RECVD     22
-#define M_MIN_RECVD      23
-#define M_SEC_RECVD      24
-#define M_WEEKDAY_RECVD  25
-#define M_TZOFFSET_RECVD 26
-#define M_TZ_RECVD       27
-#define M_UNIXTIME_RECVD 28
-
-#define M_DATE_STAMP     30
-#define M_FULLDATE_STAMP 31
-#define M_ISODATE_STAMP  32
-#define M_YEAR_STAMP     33
-#define M_MONTH_STAMP    34
-#define M_DAY_STAMP      35
-#define M_HOUR_STAMP     36
-#define M_MIN_STAMP      37
-#define M_SEC_STAMP      38
-#define M_WEEKDAY_STAMP  39
-#define M_TZOFFSET_STAMP 40
-#define M_TZ_STAMP       41
-#define M_UNIXTIME_STAMP 42
-
-#define M_FULLHOST       43
-#define M_HOST           44
-#define M_FULLHOST_FROM  45
-#define M_HOST_FROM      46
-#define M_PROGRAM        47
-
-#define M_MESSAGE        48
-
-#define M_SOURCE_IP      49
-
-int append_string(char **dest, int *left, char *str, int length, int escape)
-{
-	int l;
-  
-	if (!escape) {
-		l = MIN(length, *left - 1);
-		strncpy(*dest, str, l);
-	}
-	else {
-		char *p;
-      
-		l = 0;
-		for (p = str; length && ((*left - l) > 1); p++, length--, l++) {
-			if (*p == '\'' || *p == '\"' || *p == '\\') {
-				if ((*left - l) < 3)
-					break;
-				*(*dest + l) = '\\';
-				*(*dest + l + 1) = *p;
-				l++;
-			}
-			else {
-				*(*dest + l) = *p;
-			}
-		}
-	}
-	return l;
-}
-
-void expand_macro(struct syslog_config *cfg, int id, int escape, char **dest, unsigned int *left, struct log_info *msg)
-{
-	int length = 0;
-	
-	switch (id) {
-	case M_FACILITY: {
-		/* facility */
-		char *n = syslog_lookup_value(msg->pri & LOG_FACMASK, sl_facilities);
-		if (n) {
-			length = append_string(dest, left, n, strlen(n), 0);
-		}
-		else {
-			length = snprintf(*dest, *left, "%x", (msg->pri & LOG_FACMASK) >> 3);
-		}
-		break;
-	}
-	case M_LEVEL: {
-		/* level */
-		char *n = syslog_lookup_value(msg->pri & LOG_PRIMASK, sl_levels);
-		if (n) {
-			length = append_string(dest, left, n, strlen(n), 0);
-		}
-		else {
-			/* should never happen */
-			length = snprintf(*dest, *left, "%d", msg->pri & LOG_PRIMASK);
-		}
-
-		break;
-	}
-	case M_TAG: {
-		length = snprintf(*dest, *left, "%02x", msg->pri);
-		break;
-	}
-	case M_SOURCE_IP: {
- 		char *ip;
- 		
-		if (msg->saddr) {
-	 		CAST(inet_address_info, addr, msg->saddr);
- 		
-	 		ip = inet_ntoa(addr->sa.sin_addr);
-		}
-		else {
-			ip = "127.0.0.1";
-		}
-		length = append_string(dest, left, ip, strlen(ip), escape);
-		break;
-	}
-	case M_FULLHOST_FROM:
-	case M_FULLHOST: {
-		struct ol_string *host = (id == M_FULLHOST ? msg->host : msg->host_from);
-		/* full hostname */
-		length = append_string(dest, left, host->data, host->length, escape);
-		break;
-	}
-	case M_HOST_FROM:
-	case M_HOST: {
-		/* host */
-		struct ol_string *host = (id == M_HOST ? msg->host : msg->host_from);
-		UINT8 *p1;
-		UINT8 *p2;
-		int remaining;
-		
-		p1 = memchr(host->data, '@', host->length);
-		if (p1) 
-			p1++; 
-		else 
-			p1 = host->data;
-                remaining = host->length - (p1 - host->data);
-		p2 = memchr(p1, '/', remaining);
-		if (p2) {
-			length = MIN((unsigned int) (p2 - p1), *left);
-		}
-		else {
-			length = MIN(*left, (unsigned int) (host->length - (p1 - host->data)));
-		}
-		length = append_string(dest, left, p1, length, escape);
-		break;
-	}
-	case M_PROGRAM: {
-		/* program */
-		if (msg->program) {
-			length = append_string(dest, left, msg->program->data, msg->program->length, escape);
-		}
-		break;
-	}
-	case M_FULLDATE_RECVD:
-	case M_ISODATE_RECVD:
-	case M_WEEKDAY_RECVD:
-	case M_DATE_RECVD:
-	case M_YEAR_RECVD:
-	case M_MONTH_RECVD:
-	case M_DAY_RECVD:
-	case M_HOUR_RECVD:
-	case M_MIN_RECVD:
-	case M_SEC_RECVD:
-	case M_TZOFFSET_RECVD:
-	case M_TZ_RECVD:
-	case M_UNIXTIME_RECVD:
-
-	case M_FULLDATE_STAMP:
-	case M_ISODATE_STAMP:
-	case M_WEEKDAY_STAMP:
-	case M_DATE_STAMP:
-	case M_YEAR_STAMP:
-	case M_MONTH_STAMP:
-	case M_DAY_STAMP:
-	case M_HOUR_STAMP:
-	case M_MIN_STAMP:
-	case M_SEC_STAMP:
-	case M_TZOFFSET_STAMP:
-	case M_TZ_STAMP:
-	case M_UNIXTIME_STAMP:
-
-	case M_FULLDATE:
-	case M_ISODATE:
-	case M_WEEKDAY:
-	case M_DATE:
-	case M_YEAR:
-	case M_MONTH:
-	case M_DAY: 
-	case M_HOUR:
-	case M_MIN:
-	case M_SEC:
-	case M_TZOFFSET:
-	case M_TZ:
-	case M_UNIXTIME: {
-		/* year, month, day */
-		struct tm *tm;
-		time_t unixtime;
-
-	       	switch(id) {
-	       	case M_FULLDATE_RECVD:
-	       	case M_ISODATE_RECVD: 
-	       	case M_WEEKDAY_RECVD: 
-	       	case M_DATE_RECVD:    
-	       	case M_YEAR_RECVD:    
-	       	case M_MONTH_RECVD:   
-	       	case M_DAY_RECVD:     
-	       	case M_HOUR_RECVD:    
-	       	case M_MIN_RECVD:     
-	       	case M_SEC_RECVD:     
-		case M_TZOFFSET_RECVD:
-		case M_TZ_RECVD:
-	       	case M_UNIXTIME_RECVD:     
-			unixtime = msg->recvd;
-			break;
-		case M_FULLDATE_STAMP:
-		case M_ISODATE_STAMP: 
-		case M_WEEKDAY_STAMP: 
-		case M_DATE_STAMP:    
-		case M_YEAR_STAMP:    
-		case M_MONTH_STAMP:   
-		case M_DAY_STAMP:     
-		case M_HOUR_STAMP:    
-		case M_MIN_STAMP:     
-		case M_SEC_STAMP:     
-		case M_TZOFFSET_STAMP:
-		case M_TZ_STAMP:
-		case M_UNIXTIME_STAMP:     
-			unixtime = msg->stamp;
-	                break;
-		default:
- 		        if (cfg->use_time_recvd)
-				unixtime = msg->recvd;
- 		        else
-				unixtime = msg->stamp;
- 		        break;
-		}
-		
-		tm = localtime(&unixtime);
-
-		switch (id) {
-		case M_WEEKDAY:
- 	        case M_WEEKDAY_RECVD:
- 	        case M_WEEKDAY_STAMP:
-	                length = strftime(*dest, *left - 1, "%a", tm);			
-			break;
-		case M_YEAR:
-		case M_YEAR_RECVD:
- 	        case M_YEAR_STAMP:
-			length = snprintf(*dest, *left, "%04d", tm->tm_year + 1900);
-			break;
-		case M_MONTH:
- 	        case M_MONTH_RECVD:
- 	        case M_MONTH_STAMP:
-			length = snprintf(*dest, *left, "%02d", tm->tm_mon + 1);
-			break;
-		case M_DAY:
- 		case M_DAY_RECVD:
- 	        case M_DAY_STAMP:  
-			length = snprintf(*dest, *left, "%02d", tm->tm_mday);
-			break;
-		case M_HOUR:
-		case M_HOUR_RECVD:
- 	        case M_HOUR_STAMP:
-			length = snprintf(*dest, *left, "%02d", tm->tm_hour);
-			break;
-		case M_MIN:
- 		case M_MIN_RECVD:
-		case M_MIN_STAMP:
-			length = snprintf(*dest, *left, "%02d", tm->tm_min);
-			break;
-		case M_SEC:
-		case M_SEC_RECVD:
-		case M_SEC_STAMP:
-			length = snprintf(*dest, *left, "%02d", tm->tm_sec);
-			break;
-		case M_ISODATE:
- 		case M_ISODATE_RECVD:
-		case M_ISODATE_STAMP:
-	                length = strftime(*dest, *left - 1, "%Y-%m-%dT%H:%M:%S%z", tm);
-	                break;
-	        case M_FULLDATE:
- 	        case M_FULLDATE_RECVD:
- 	        case M_FULLDATE_STAMP:
-	                length = strftime(*dest, *left - 1, "%Y %h %e %H:%M:%S", tm);
-	        	break;
-	        case M_DATE:
- 	        case M_DATE_RECVD:
- 	        case M_DATE_STAMP:
-	                length = strftime(*dest, *left - 1, "%h %e %H:%M:%S", tm);
-	                break;
-	        case M_TZOFFSET:
- 	        case M_TZOFFSET_RECVD:
- 	        case M_TZOFFSET_STAMP:
-			length = strftime(*dest, *left -1, "%z", tm);
-			break;
-	        case M_TZ:
- 	        case M_TZ_RECVD:
- 	        case M_TZ_STAMP:
-			length = strftime(*dest, *left -1, "%Z", tm);
-			break;
-	        case M_UNIXTIME:
- 	        case M_UNIXTIME_RECVD:
- 	        case M_UNIXTIME_STAMP:
-			length = snprintf(*dest, *left, "%ld", (long) unixtime);
-	                break;
-		}
-		break;
-	}
-	case M_MESSAGE: {
-		/* message */
-		length = append_string(dest, left, msg->msg->data, msg->msg->length, escape);
-		break;
-	}
-	default:
-		break;
-	}
-	if (length < 0 || length > *left) 
-		length = *left;
-
-	*left -= length;
-	*dest += length;
-}
-
-struct ol_string *
-expand_macros(struct syslog_config *cfg, struct ol_string *template, int template_escape, struct log_info *msg)
-{
-	static struct macro_def macros[] = {
-		{ "FACILITY", M_FACILITY },
-		{ "PRIORITY", M_LEVEL },
-		{ "LEVEL", M_LEVEL },
-		{ "TAG", M_TAG },
-
-		{ "DATE", M_DATE },
-		{ "FULLDATE", M_FULLDATE },
-		{ "ISODATE", M_ISODATE },
-		{ "YEAR", M_YEAR },
-		{ "MONTH", M_MONTH },
-		{ "DAY", M_DAY },
-		{ "HOUR", M_HOUR },
-		{ "MIN", M_MIN },
-		{ "SEC", M_SEC },
-		{ "WEEKDAY", M_WEEKDAY },
-		{ "UNIXTIME", M_UNIXTIME },
-		{ "TZOFFSET", M_TZOFFSET },
-		{ "TZ", M_TZ },
-
- 		{ "R_DATE", M_DATE_RECVD },
- 		{ "R_FULLDATE", M_FULLDATE_RECVD },
- 		{ "R_ISODATE", M_ISODATE_RECVD },
- 		{ "R_YEAR", M_YEAR_RECVD },
- 		{ "R_MONTH", M_MONTH_RECVD },
- 		{ "R_DAY", M_DAY_RECVD },
- 		{ "R_HOUR", M_HOUR_RECVD },
- 		{ "R_MIN", M_MIN_RECVD },
- 		{ "R_SEC", M_SEC_RECVD },
- 		{ "R_WEEKDAY", M_WEEKDAY_RECVD },
-		{ "R_UNIXTIME", M_UNIXTIME_RECVD },
-		{ "R_TZOFFSET", M_TZOFFSET_RECVD },
-		{ "R_TZ", M_TZ_RECVD },
- 
- 		{ "S_DATE", M_DATE_STAMP },
- 		{ "S_FULLDATE", M_FULLDATE_STAMP },
- 		{ "S_ISODATE", M_ISODATE_STAMP },
- 		{ "S_YEAR", M_YEAR_STAMP },
- 		{ "S_MONTH", M_MONTH_STAMP },
- 		{ "S_DAY", M_DAY_STAMP },
- 		{ "S_HOUR", M_HOUR_STAMP },
- 		{ "S_MIN", M_MIN_STAMP },
- 		{ "S_SEC", M_SEC_STAMP },
- 		{ "S_WEEKDAY", M_WEEKDAY_STAMP },
- 		{ "S_UNIXTIME", M_UNIXTIME_STAMP },
- 		{ "S_TZOFFSET", M_TZOFFSET_STAMP },
- 		{ "S_TZ", M_TZ_STAMP },
-		
-		{ "HOST_FROM", M_HOST_FROM },
-		{ "FULLHOST_FROM", M_FULLHOST_FROM },
-		{ "HOST", M_HOST },
-		{ "FULLHOST", M_FULLHOST },
-
-		{ "PROGRAM", M_PROGRAM },
-		{ "MSG", M_MESSAGE },
-		{ "MESSAGE", M_MESSAGE },
-		{ "SOURCEIP", M_SOURCE_IP }
-	};
-	char format[cfg->log_msg_size + 1], *format_ptr = format;
-	unsigned int left = sizeof(format) - 1;
-	unsigned int i, j;
-
-	i = 0;
-	while (left && (i < template->length)) {
-
-		if (template->data[i] == '$') {
-			/* beginning of a macro */
-			for (j = 0; j < (sizeof(macros) / sizeof(struct macro_def)); j++) {
-				if (strncmp(macros[j].name, &template->data[i + 1], strlen(macros[j].name)) == 0) {
-					break;
-				}
-			}
-			if (j == (sizeof(macros) / sizeof(struct macro_def))) {
-				i++;
-				while ((template->data[i] >= 'A' && 
-					template->data[i] <= 'Z') ||
-				       template->data[i] == '_') 
-					i++;
-			}
-			else {
-				i += strlen(macros[j].name) + 1;
-				expand_macro(cfg, macros[j].id, template_escape, &format_ptr, &left, msg);
-			}
-		}
-		else {
-			*format_ptr = template->data[i];
-			format_ptr++;
-			i++;
-			left--;
-		}
-	}
-	*format_ptr = 0;
-	return c_format_cstring("%z", format);
-}
-
 /* FIXME: a binary search would be nicer */
 static struct affile_dest_writer *
 affile_find_writer(struct affile_dest *self, 
diff -urN syslog-ng-1.5.25/src/afinet.c syslog-ng-1.5.25.template-patch/src/afinet.c
--- syslog-ng-1.5.25/src/afinet.c	2003-01-08 10:31:37.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afinet.c	2003-01-29 11:52:09.000000000 +0100
@@ -39,6 +39,7 @@
 #endif
 
 #include "afinet.c.x"
+#include "macros.h"
 
 void inet_address_setip(struct address_info **a, const char *ip)
 {
@@ -386,6 +387,9 @@
        (dest_buf object abstract_buffer)
        (sync_freq simple UINT32)
        (conn_fd object connect_fd)
+       (template_fname string)
+       (template_output string)
+       (template_escape . int)
        (cfg object syslog_config)))
 */
 /* af_inet destination */
@@ -422,6 +426,20 @@
 	inet_address_setport(&dest->bind_addr, port, service, proto);
 }
 
+void afinet_dest_set_template(struct log_dest_driver *c, char *t)
+{
+	CAST(afinet_dest, self, c);
+ 
+	self->template_output = c_format("%z", t);
+}
+
+void afinet_dest_set_template_escape(struct log_dest_driver *c, int enable)
+{
+	CAST(afinet_dest, self, c);
+
+	self->template_escape = enable;
+}
+
 void afinet_dest_set_destport(struct log_dest_driver *c, UINT32 port, 
 			      const char *service, const char *proto)
 {
@@ -527,7 +545,21 @@
 	CAST(afinet_dest, self, c);
 
 	if (self->dest_buf) {
-		A_WRITE_STRING(&self->dest_buf->super, c_format("<%i>%S %S %S\n", msg->pri, msg->date, msg->host, msg->msg));
+		struct ol_string *msg_line;
+
+		if (self->template_output) {
+			msg_line = c_format("<%i>%fS",
+					    msg->pri,
+					    expand_macros(
+						    self->cfg,
+						    self->template_output,
+						    self->template_escape, msg));
+		} else {
+                	msg_line = c_format("<%i>%S %S %S\n",
+					    msg->pri,
+					    msg->date, msg->host, msg->msg);
+		}
+		A_WRITE_STRING(&self->dest_buf->super, msg_line);
 	}
 	log_info_free(msg);
 }
diff -urN syslog-ng-1.5.25/src/afinet.c.x syslog-ng-1.5.25.template-patch/src/afinet.c.x
--- syslog-ng-1.5.25/src/afinet.c.x	2003-01-08 16:38:39.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afinet.c.x	2003-01-29 11:57:37.000000000 +0100
@@ -33,6 +33,9 @@
   struct abstract_buffer *dest_buf;
   UINT32 sync_freq;
   struct connect_fd *conn_fd;
+  struct ol_string *template_fname;
+  struct ol_string *template_output;
+  int template_escape;
   struct syslog_config *cfg;
 };
 extern struct ol_class afinet_dest_class;
@@ -49,11 +52,18 @@
   mark((struct ol_object *) i->cfg);
 }
 
+static void do_afinet_dest_free(struct ol_object *o)
+{
+  struct afinet_dest *i = (struct afinet_dest *) o;
+  ol_string_free(i->template_fname);
+  ol_string_free(i->template_output);
+}
+
 struct ol_class afinet_dest_class =
 { STATIC_HEADER,
   &afsocket_dest_class, "afinet_dest", sizeof(struct afinet_dest),
   do_afinet_dest_mark,
-  NULL
+  do_afinet_dest_free
 };
 #endif /* !CLASS_DECLARE */
 
diff -urN syslog-ng-1.5.25/src/afinter.c syslog-ng-1.5.25.template-patch/src/afinter.c
--- syslog-ng-1.5.25/src/afinter.c	2002-04-26 11:43:54.000000000 +0200
+++ syslog-ng-1.5.25.template-patch/src/afinter.c	2003-01-29 08:31:50.000000000 +0100
@@ -82,6 +82,9 @@
 {
 	CAST(log_source_driver, self, c);
 	cfg->internal = self->super.next;
+	if (assigned_configuration == NULL) {
+		assigned_configuration = cfg;
+	}
 	return ST_OK | ST_GOON;
 }
 
diff -urN syslog-ng-1.5.25/src/afprogram.c syslog-ng-1.5.25.template-patch/src/afprogram.c
--- syslog-ng-1.5.25/src/afprogram.c	2001-07-08 19:04:21.000000000 +0200
+++ syslog-ng-1.5.25.template-patch/src/afprogram.c	2003-01-29 12:04:32.000000000 +0100
@@ -37,6 +37,7 @@
 #include <fcntl.h>
 
 #include "afprogram.c.x"
+#include "macros.h"
 
 /* CLASS:
    (class
@@ -44,7 +45,11 @@
      (super log_dest_driver)
      (vars
        (progname string)
-       (dest object io_fd)))
+       (dest object io_fd)
+       (template_fname string)
+       (template_output string)
+       (template_escape . int)
+       (cfg object syslog_config)))
 */
 
 /* CLASS:
@@ -55,6 +60,22 @@
        (pid simple pid_t)
        (dest object io_fd)))
 */
+void
+afprogram_dest_set_template(struct log_dest_driver *c, char *t)
+{
+	CAST(afprogram_dest, self, c);
+ 
+	self->template_output = c_format("%z", t);
+}
+
+void
+afprogram_dest_set_template_escape(struct log_dest_driver *c, int enable)
+{
+	CAST(afprogram_dest, self, c);
+
+	self->template_escape = enable;
+}
+
 
 static void do_kill_child(struct resource *c)
 {
@@ -94,6 +115,7 @@
 	}
 	else {
 		NEW(afprogram_child, prg);
+		self->cfg = cfg;
 
 		prg->super.kill = do_kill_child;
 		prg->pid = pid;
@@ -109,18 +131,31 @@
 	return ST_OK | ST_GOON;
 }
 
-static void do_handle_afprogram_dest(struct log_handler *c, 
+
+
+static void
+do_handle_afprogram_dest(struct log_handler *c, 
 				     struct log_info *msg)
 {
         CAST(afprogram_dest, self, c);
 
-        if (self->dest)
-                A_WRITE_STRING(&self->dest->buffer->super, 
-			       c_format("<%i>%S %S %S\n", 
-					msg->pri, 
-					msg->date, 
-					msg->host, 
-					msg->msg));
+        if (self->dest) {
+		struct ol_string *msg_line;
+
+		if (self->template_output) {
+			msg_line = c_format("<%i>%fS",
+					    msg->pri,
+					    expand_macros(
+						    self->cfg,
+						    self->template_output,
+						    self->template_escape, msg));
+		} else {
+			msg_line = c_format("<%i>%S %S %S\n",
+					    msg->pri,
+					    msg->date, msg->host, msg->msg);
+		}
+                A_WRITE_STRING(&self->dest->buffer->super, msg_line);
+	} 
         log_info_free(msg);
 }
 
diff -urN syslog-ng-1.5.25/src/afprogram.c.x syslog-ng-1.5.25.template-patch/src/afprogram.c.x
--- syslog-ng-1.5.25/src/afprogram.c.x	2003-01-08 10:11:26.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afprogram.c.x	2003-01-29 12:04:35.000000000 +0100
@@ -4,6 +4,10 @@
   struct log_dest_driver super;
   struct ol_string *progname;
   struct io_fd *dest;
+  struct ol_string *template_fname;
+  struct ol_string *template_output;
+  int template_escape;
+  struct syslog_config *cfg;
 };
 extern struct ol_class afprogram_dest_class;
 #endif /* !CLASS_DEFINE */
@@ -14,12 +18,15 @@
 {
   struct afprogram_dest *i = (struct afprogram_dest *) o;
   mark((struct ol_object *) i->dest);
+  mark((struct ol_object *) i->cfg);
 }
 
 static void do_afprogram_dest_free(struct ol_object *o)
 {
   struct afprogram_dest *i = (struct afprogram_dest *) o;
   ol_string_free(i->progname);
+  ol_string_free(i->template_fname);
+  ol_string_free(i->template_output);
 }
 
 struct ol_class afprogram_dest_class =
diff -urN syslog-ng-1.5.25/src/afprogram.h syslog-ng-1.5.25.template-patch/src/afprogram.h
--- syslog-ng-1.5.25/src/afprogram.h	2001-02-25 13:00:22.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afprogram.h	2003-01-29 08:31:50.000000000 +0100
@@ -29,5 +29,7 @@
 #include "syslog-ng.h"
 
 struct log_dest_driver *make_afprogram_dest(const char *cmd);
+void afprogram_dest_set_template(struct log_dest_driver *c, char *t);
+void afprogram_dest_set_template_escape(struct log_dest_driver *c, int enable);
 
 #endif
diff -urN syslog-ng-1.5.25/src/afsocket.h syslog-ng-1.5.25.template-patch/src/afsocket.h
--- syslog-ng-1.5.25/src/afsocket.h	2001-04-14 11:10:34.000000000 +0200
+++ syslog-ng-1.5.25.template-patch/src/afsocket.h	2003-01-29 08:31:50.000000000 +0100
@@ -138,12 +138,16 @@
 void afinet_dest_set_mac(struct log_dest_driver *c, UINT32 value);
 void afinet_dest_set_encrypt(struct log_dest_driver *c, UINT32 value);
 void afinet_dest_set_syncfreq(struct log_dest_driver *c, UINT32 value);
+void afinet_dest_set_template(struct log_dest_driver *c, char *t);
+void afinet_dest_set_template_escape(struct log_dest_driver *c, int enable);
 
 struct log_source_driver *
 make_afunix_source(struct address_info *bind_addr, UINT32 flags);
 
 struct log_dest_driver *
 make_afunix_dest(struct address_info *dest_addr, UINT32 flags);
+void afunix_dest_set_template(struct log_dest_driver *c, char *t);
+void afunix_dest_set_template_escape(struct log_dest_driver *c, int enable);
 
 struct log_source_driver *
 make_afinet_source(struct address_info *bind_addr, UINT32 flags);
diff -urN syslog-ng-1.5.25/src/afunix.c syslog-ng-1.5.25.template-patch/src/afunix.c
--- syslog-ng-1.5.25/src/afunix.c	2003-01-08 10:31:37.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afunix.c	2003-01-30 14:47:01.000000000 +0100
@@ -40,6 +40,8 @@
 #include <assert.h>
 
 #include "afunix.c.x"
+#include "macros.h"
+
 
 /* af_unix source connection */
 
@@ -316,16 +318,51 @@
      (vars
        (dest object io_fd)
        (dest_buf object abstract_buffer)
+       (template_fname string)
+       (template_output string)
+       (template_escape . int)
        (cfg object syslog_config)))
 */
 
-void do_handle_afunix_dest(struct log_handler *c, struct log_info *msg)
+void
+afunix_dest_set_template(struct log_dest_driver *c, char *t)
+{
+	CAST(afunix_dest, self, c);
+ 
+	self->template_output = c_format("%z", t);
+}
+
+void
+afunix_dest_set_template_escape(struct log_dest_driver *c, int enable)
+{
+	CAST(afunix_dest, self, c);
+
+	self->template_escape = enable;
+}
+
+static void
+do_handle_afunix_dest(struct log_handler *c, struct log_info *msg)
 {
 	CAST(afunix_dest, self, c);
 
 	if (self->dest_buf) {
-		A_WRITE_STRING(&self->dest_buf->super, c_format("<%i>%S %S%c", msg->pri, msg->date, msg->msg, 0));
+		struct ol_string *msg_line;
+
+		if (self->template_output) {
+			msg_line = c_format("<%i>%fS",
+					    msg->pri,
+					    expand_macros(
+						    self->cfg,
+						    self->template_output,
+						    self->template_escape, msg));
+		} else {
+                	msg_line = c_format("<%i>%S %S %S\n",
+					    msg->pri,
+					    msg->date, msg->host, msg->msg);
+		}                
+		A_WRITE_STRING(&self->dest_buf->super, msg_line);
 	}
+
 	log_info_free(msg);
 }
 
diff -urN syslog-ng-1.5.25/src/afunix.c.x syslog-ng-1.5.25.template-patch/src/afunix.c.x
--- syslog-ng-1.5.25/src/afunix.c.x	2003-01-08 16:38:38.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afunix.c.x	2003-01-30 14:47:02.000000000 +0100
@@ -34,6 +34,9 @@
   struct afsocket_dest super;
   struct io_fd *dest;
   struct abstract_buffer *dest_buf;
+  struct ol_string *template_fname;
+  struct ol_string *template_output;
+  int template_escape;
   struct syslog_config *cfg;
 };
 extern struct ol_class afunix_dest_class;
@@ -49,11 +52,18 @@
   mark((struct ol_object *) i->cfg);
 }
 
+static void do_afunix_dest_free(struct ol_object *o)
+{
+  struct afunix_dest *i = (struct afunix_dest *) o;
+  ol_string_free(i->template_fname);
+  ol_string_free(i->template_output);
+}
+
 struct ol_class afunix_dest_class =
 { STATIC_HEADER,
   &afsocket_dest_class, "afunix_dest", sizeof(struct afunix_dest),
   do_afunix_dest_mark,
-  NULL
+  do_afunix_dest_free
 };
 #endif /* !CLASS_DECLARE */
 
diff -urN syslog-ng-1.5.25/src/afuser.c syslog-ng-1.5.25.template-patch/src/afuser.c
--- syslog-ng-1.5.25/src/afuser.c	2001-07-08 18:07:27.000000000 +0200
+++ syslog-ng-1.5.25.template-patch/src/afuser.c	2003-01-29 12:04:30.000000000 +0100
@@ -36,30 +36,66 @@
 #include <utmp.h>
 
 #include "afuser.c.x"
+#include "macros.h"
 
 /* CLASS:
    (class
      (name afuser_dest)
      (super log_dest_driver)
      (vars
-       (username string)))
+       (username string)
+       (template_fname string)
+       (template_output string)
+       (template_escape . int)
+       (cfg object syslog_config)))
 */
 
+void
+afuser_dest_set_template(struct log_dest_driver *c, char *t)
+{
+	CAST(afuser_dest, self, c);
+ 
+	self->template_output = c_format("%z", t);
+}
+
+void
+afuser_dest_set_template_escape(struct log_dest_driver *c, int enable)
+{
+	CAST(afuser_dest, self, c);
+
+	self->template_escape = enable;
+}
+
 static int 
 do_init_afuser_dest(struct log_handler *c, 
 		    struct syslog_config *cfg, 
 		    struct persistent_config *persistent)
 {
+	CAST(afuser_dest, self, c);
+
+	self->cfg = cfg;
+
 	return ST_OK | ST_GOON;
 }
 
-static void do_handle_afuser_log(struct log_handler *c, struct log_info *msg)
+static void
+do_handle_afuser_log(struct log_handler *c, struct log_info *msg)
 {
 	CAST(afuser_dest, self, c);
 	struct utmp *ut;
 	struct ol_string *msg_line;
 
-	msg_line = c_format("%S %S %S\n", msg->date, msg->host, msg->msg);
+	if (self->template_output) {
+		msg_line = c_format("%fS",
+				    expand_macros(
+					    self->cfg,
+					    self->template_output,
+					    self->template_escape, msg));
+	} else {
+		msg_line = c_format("%S %S %S\n",
+				    msg->date, msg->host, msg->msg);
+	}
+
 	while ((ut = getutent())) {
 #if HAVE_MODERN_UTMP
 		if (ut->ut_type == USER_PROCESS &&
diff -urN syslog-ng-1.5.25/src/afuser.c.x syslog-ng-1.5.25.template-patch/src/afuser.c.x
--- syslog-ng-1.5.25/src/afuser.c.x	2003-01-08 10:11:25.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afuser.c.x	2003-01-29 12:04:34.000000000 +0100
@@ -3,21 +3,34 @@
 {
   struct log_dest_driver super;
   struct ol_string *username;
+  struct ol_string *template_fname;
+  struct ol_string *template_output;
+  int template_escape;
+  struct syslog_config *cfg;
 };
 extern struct ol_class afuser_dest_class;
 #endif /* !CLASS_DEFINE */
 
 #ifndef CLASS_DECLARE
+static void do_afuser_dest_mark(struct ol_object *o, 
+void (*mark)(struct ol_object *o))
+{
+  struct afuser_dest *i = (struct afuser_dest *) o;
+  mark((struct ol_object *) i->cfg);
+}
+
 static void do_afuser_dest_free(struct ol_object *o)
 {
   struct afuser_dest *i = (struct afuser_dest *) o;
   ol_string_free(i->username);
+  ol_string_free(i->template_fname);
+  ol_string_free(i->template_output);
 }
 
 struct ol_class afuser_dest_class =
 { STATIC_HEADER,
   &log_dest_driver_class, "afuser_dest", sizeof(struct afuser_dest),
-  NULL,
+  do_afuser_dest_mark,
   do_afuser_dest_free
 };
 #endif /* !CLASS_DECLARE */
diff -urN syslog-ng-1.5.25/src/afuser.h syslog-ng-1.5.25.template-patch/src/afuser.h
--- syslog-ng-1.5.25/src/afuser.h	2001-02-25 13:00:22.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/afuser.h	2003-01-29 08:31:50.000000000 +0100
@@ -29,5 +29,7 @@
 #include "syslog-ng.h"
 
 struct log_dest_driver *make_afuser_dest(const char *username);
+void afuser_dest_set_template(struct log_dest_driver *c, char *t);
+void afuser_dest_set_template_escape(struct log_dest_driver *c, int enable);
 
 #endif
diff -urN syslog-ng-1.5.25/src/cfg-grammar.h syslog-ng-1.5.25.template-patch/src/cfg-grammar.h
--- syslog-ng-1.5.25/src/cfg-grammar.h	2003-01-06 09:45:10.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/cfg-grammar.h	2003-01-29 08:42:09.000000000 +0100
@@ -53,7 +53,7 @@
 # define	KW_DIR_OWNER	296
 # define	KW_DIR_GROUP	297
 # define	KW_DIR_PERM	298
-# define	KW_FILE_TEMPLATE	299
+# define	KW_TEMPLATE	299
 # define	KW_TEMPLATE_ESCAPE	300
 # define	KW_OWNER	301
 # define	KW_GROUP	302
diff -urN syslog-ng-1.5.25/src/cfg-grammar.y syslog-ng-1.5.25.template-patch/src/cfg-grammar.y
--- syslog-ng-1.5.25/src/cfg-grammar.y	2003-01-08 10:31:37.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/cfg-grammar.y	2003-01-29 08:31:50.000000000 +0100
@@ -80,7 +80,7 @@
 %token KW_TIME_REOPEN KW_TIME_REAP KW_USE_TIME_RECVD
 %token KW_USE_DNS KW_USE_FQDN KW_GC_BUSY_THRESHOLD 
 %token KW_GC_IDLE_THRESHOLD KW_CREATE_DIRS 
-%token KW_DIR_OWNER KW_DIR_GROUP KW_DIR_PERM KW_FILE_TEMPLATE KW_TEMPLATE_ESCAPE
+%token KW_DIR_OWNER KW_DIR_GROUP KW_DIR_PERM KW_TEMPLATE KW_TEMPLATE_ESCAPE
 %token KW_OWNER KW_GROUP KW_PERM KW_KEEP_ALIVE KW_MAX_CONNECTIONS
 %token KW_LOCALIP KW_IP KW_LOCALPORT KW_PORT KW_DESTPORT 
 %token KW_COMPRESS KW_MAC KW_AUTH KW_ENCRYPT
@@ -136,7 +136,9 @@
 %type	<ptr> dest_afinet_udp_params
 %type	<ptr> dest_afinet_tcp_params
 %type   <ptr> dest_afuser
+%type   <ptr> dest_afuser_params
 %type   <ptr> dest_afprogram
+%type   <ptr> dest_afprogram_params
 %type   <ptr> dest_afremctrl
 
 %type	<ptr> log_items
@@ -408,7 +410,7 @@
 	| KW_DIR_PERM '(' NUMBER ')'		{ affile_set_dir_perm(last_dest_driver, $3); }
 	| KW_CREATE_DIRS '(' yesno ')'		{ affile_set_create_dirs(last_dest_driver, $3); }
 	| KW_REMOVE_IF_OLDER '(' NUMBER ')'	{ affile_set_remove_if_older(last_dest_driver, $3); }
-	| KW_FILE_TEMPLATE '(' string ')'       { affile_set_file_template(last_dest_driver, $3); free($3); }
+	| KW_TEMPLATE '(' string ')'            { affile_set_file_template(last_dest_driver, $3); free($3); }
 	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ affile_set_template_escape(last_dest_driver, $3); }
 	| KW_FSYNC '(' yesno ')'		{ affile_set_fsync(last_dest_driver, $3); }
 	;
@@ -436,7 +438,7 @@
 	: KW_OWNER '(' string ')'		{ affile_set_owner(last_dest_driver, $3); free($3); }
 	| KW_GROUP '(' string ')'		{ affile_set_group(last_dest_driver, $3); free($3); }
 	| KW_PERM '(' NUMBER ')'		{ affile_set_perm(last_dest_driver, $3); }
-	| KW_FILE_TEMPLATE '(' string ')'       { affile_set_file_template(last_dest_driver, $3); free($3); }
+	| KW_TEMPLATE '(' string ')'            { affile_set_file_template(last_dest_driver, $3); free($3); }
 	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ affile_set_template_escape(last_dest_driver, $3); }
 	;
 
@@ -448,23 +450,35 @@
 	| KW_TCP '(' dest_afinet_tcp_params ')'			{ $$ = $3; } 
 	;
 
+/* options are common between dgram & stream */
+dest_afunix_option
+	: KW_TEMPLATE '(' string ')'            { afunix_dest_set_template(last_dest_driver, $3); free($3); }
+	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ afunix_dest_set_template_escape(last_dest_driver, $3); }
+	; 
+
+dest_afunix_options
+	: dest_afunix_options dest_afunix_option
+	|
+	;
+
 dest_afunix_dgram_params
-	: string				{ $$ = make_afunix_dest(
+	: string				{ last_dest_driver = make_afunix_dest(
 							&make_unix_address_c($1)->super, 
 							AFSOCKET_DGRAM);
 							free($1);
 						}
+	dest_afunix_options			{ $$ = last_dest_driver; }
 	;
 
 dest_afunix_stream_params
-	: string				{ $$ = make_afunix_dest(
+	: string				{ last_dest_driver = make_afunix_dest(
 							&make_unix_address_c($1)->super, 
 							AFSOCKET_STREAM);
 							free($1);
 						}
+	dest_afunix_options			{ $$ = last_dest_driver; }
 	;
 
-
 dest_afinet_udp_params
 	: string 	
 	  { 
@@ -486,6 +500,8 @@
 	| KW_LOCALPORT '(' NUMBER ')'		{ afinet_dest_set_localport(last_dest_driver, $3, NULL, NULL); }
 	| KW_PORT '(' NUMBER ')'		{ afinet_dest_set_destport(last_dest_driver, $3, NULL, NULL); }
 	| KW_DESTPORT '(' NUMBER ')'		{ afinet_dest_set_destport(last_dest_driver, $3, NULL, NULL); }
+	| KW_TEMPLATE '(' string ')'            { afinet_dest_set_template(last_dest_driver, $3); free($3); }
+	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ afinet_dest_set_template_escape(last_dest_driver, $3); }
 	;
 
 dest_afinet_udp_option
@@ -522,13 +538,42 @@
 	| KW_SYNC_FREQ '(' NUMBER ')'		{ afinet_dest_set_syncfreq(last_dest_driver, $3); }
 	;
 
-
 dest_afuser
-	: KW_USER '(' string ')'		{ $$ = make_afuser_dest($3); free($3); }
+	: KW_USER '(' dest_afuser_params ')'	{ $$ = $3; }
+	;
+
+dest_afuser_params
+	: string				{ last_dest_driver = make_afuser_dest($1); free($1); }
+	dest_afuser_options			{ $$ = last_dest_driver; }
+	;
+
+dest_afuser_option
+	: KW_TEMPLATE '(' string ')'            { afuser_dest_set_template(last_dest_driver, $3); free($3); }
+	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ afuser_dest_set_template_escape(last_dest_driver, $3); }
+	; 
+
+dest_afuser_options
+	: dest_afuser_options dest_afuser_option
+	|
 	;
 
 dest_afprogram
-	: KW_PROGRAM '(' string ')' 		{ $$ = make_afprogram_dest($3); free($3); }
+	: KW_PROGRAM '(' dest_afprogram_params ')'	{ $$ = $3; }
+	;
+
+dest_afprogram_params
+	: string				{ last_dest_driver = make_afprogram_dest($1); free($1); }
+	dest_afprogram_options			{ $$ = last_dest_driver; }
+	;
+
+dest_afprogram_option
+	: KW_TEMPLATE '(' string ')'            { afprogram_dest_set_template(last_dest_driver, $3); free($3); }
+	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ afprogram_dest_set_template_escape(last_dest_driver, $3); }
+	; 
+
+dest_afprogram_options
+	: dest_afprogram_options dest_afprogram_option
+	|
 	;
 
 dest_afremctrl
diff -urN syslog-ng-1.5.25/src/cfg-lex.c syslog-ng-1.5.25.template-patch/src/cfg-lex.c
--- syslog-ng-1.5.25/src/cfg-lex.c	2003-01-08 16:38:35.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/cfg-lex.c	2003-01-29 08:31:50.000000000 +0100
@@ -485,7 +485,7 @@
 	{ "dir_owner",		KW_DIR_OWNER },
 	{ "dir_group",		KW_DIR_GROUP },
         { "dir_perm",           KW_DIR_PERM },
-        { "template",           KW_FILE_TEMPLATE },
+        { "template",           KW_TEMPLATE },
         { "template-escape",	KW_TEMPLATE_ESCAPE },
         { "template_escape",	KW_TEMPLATE_ESCAPE },
  	{ "keep-alive",         KW_KEEP_ALIVE },
diff -urN syslog-ng-1.5.25/src/macros.c syslog-ng-1.5.25.template-patch/src/macros.c
--- syslog-ng-1.5.25/src/macros.c	1970-01-01 01:00:00.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/macros.c	2003-01-29 10:09:06.000000000 +0100
@@ -0,0 +1,518 @@
+/***************************************************************************
+ *
+ * Copyright (c) 1999 Balázs Scheidler
+ * Copyright (c) 1999-2001 BalaBit IT Ltd.
+ * 
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Inspired by nsyslog, originally written by Darren Reed.
+ *
+ * $Id:$
+ *
+ ***************************************************************************/
+
+/*
+ * Several patches to the macro expansion function were contributed
+ * by Gert Menke.
+ *
+ * Portions Copyright (c) 2002 by Gert Menke
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The patch to add the "template" and "template_escape" options to all
+ * destination drivers was contributed by Achim Gsell.
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include "format.h"
+#include "cfgfile.h"
+#include "macros.h"
+#include "syslog-names.h"
+
+
+#define MAX_MACRO_ARGS        32
+#define MAX_EXPANDED_MACRO    2048
+
+struct macro_def {
+	char *name;
+	int id;
+};
+
+#define M_FACILITY 0
+#define M_LEVEL    1
+#define M_TAG      2
+
+#define M_DATE     3
+#define M_FULLDATE 4
+#define M_ISODATE  5
+#define M_YEAR     6
+#define M_MONTH    7
+#define M_DAY      8
+#define M_HOUR     9
+#define M_MIN      10
+#define M_SEC      11
+#define M_WEEKDAY  12
+#define M_TZOFFSET 13
+#define M_TZ       14
+#define M_UNIXTIME 15
+
+#define M_DATE_RECVD     16
+#define M_FULLDATE_RECVD 17
+#define M_ISODATE_RECVD  18
+#define M_YEAR_RECVD     19
+#define M_MONTH_RECVD    20
+#define M_DAY_RECVD      21
+#define M_HOUR_RECVD     22
+#define M_MIN_RECVD      23
+#define M_SEC_RECVD      24
+#define M_WEEKDAY_RECVD  25
+#define M_TZOFFSET_RECVD 26
+#define M_TZ_RECVD       27
+#define M_UNIXTIME_RECVD 28
+
+#define M_DATE_STAMP     30
+#define M_FULLDATE_STAMP 31
+#define M_ISODATE_STAMP  32
+#define M_YEAR_STAMP     33
+#define M_MONTH_STAMP    34
+#define M_DAY_STAMP      35
+#define M_HOUR_STAMP     36
+#define M_MIN_STAMP      37
+#define M_SEC_STAMP      38
+#define M_WEEKDAY_STAMP  39
+#define M_TZOFFSET_STAMP 40
+#define M_TZ_STAMP       41
+#define M_UNIXTIME_STAMP 42
+
+#define M_FULLHOST       43
+#define M_HOST           44
+#define M_FULLHOST_FROM  45
+#define M_HOST_FROM      46
+#define M_PROGRAM        47
+
+#define M_MESSAGE        48
+
+#define M_SOURCE_IP      49
+
+static int
+append_string(char **dest, int *left, char *str, int length, int escape)
+{
+	int l;
+  
+	if (!escape) {
+		l = MIN(length, *left - 1);
+		strncpy(*dest, str, l);
+	}
+	else {
+		char *p;
+      
+		l = 0;
+		for (p = str; length && ((*left - l) > 1); p++, length--, l++) {
+			if (*p == '\'' || *p == '\"' || *p == '\\') {
+				if ((*left - l) < 3)
+					break;
+				*(*dest + l) = '\\';
+				*(*dest + l + 1) = *p;
+				l++;
+			}
+			else {
+				*(*dest + l) = *p;
+			}
+		}
+	}
+	return l;
+}
+
+
+static void
+expand_macro(struct syslog_config *cfg, int id, int escape, char **dest, unsigned int *left, struct log_info *msg)
+{
+	int length = 0;
+	
+	switch (id) {
+	case M_FACILITY: {
+		/* facility */
+		char *n = syslog_lookup_value(msg->pri & LOG_FACMASK, sl_facilities);
+		if (n) {
+			length = append_string(dest, left, n, strlen(n), 0);
+		}
+		else {
+			length = snprintf(*dest, *left, "%x", (msg->pri & LOG_FACMASK) >> 3);
+		}
+		break;
+	}
+	case M_LEVEL: {
+		/* level */
+		char *n = syslog_lookup_value(msg->pri & LOG_PRIMASK, sl_levels);
+		if (n) {
+			length = append_string(dest, left, n, strlen(n), 0);
+		}
+		else {
+			/* should never happen */
+			length = snprintf(*dest, *left, "%d", msg->pri & LOG_PRIMASK);
+		}
+
+		break;
+	}
+	case M_TAG: {
+		length = snprintf(*dest, *left, "%02x", msg->pri);
+		break;
+	}
+	case M_SOURCE_IP: {
+ 		char *ip;
+ 		
+		if (msg->saddr) {
+	 		CAST(inet_address_info, addr, msg->saddr);
+ 		
+	 		ip = inet_ntoa(addr->sa.sin_addr);
+		}
+		else {
+			ip = "127.0.0.1";
+		}
+		length = append_string(dest, left, ip, strlen(ip), escape);
+		break;
+	}
+	case M_FULLHOST_FROM:
+	case M_FULLHOST: {
+		struct ol_string *host = (id == M_FULLHOST ? msg->host : msg->host_from);
+		/* full hostname */
+		length = append_string(dest, left, host->data, host->length, escape);
+		break;
+	}
+	case M_HOST_FROM:
+	case M_HOST: {
+		/* host */
+		struct ol_string *host = (id == M_HOST ? msg->host : msg->host_from);
+		UINT8 *p1;
+		UINT8 *p2;
+		int remaining;
+		
+		p1 = memchr(host->data, '@', host->length);
+		if (p1) 
+			p1++; 
+		else 
+			p1 = host->data;
+                remaining = host->length - (p1 - host->data);
+		p2 = memchr(p1, '/', remaining);
+		if (p2) {
+			length = MIN((unsigned int) (p2 - p1), *left);
+		}
+		else {
+			length = MIN(*left, (unsigned int) (host->length - (p1 - host->data)));
+		}
+		length = append_string(dest, left, p1, length, escape);
+		break;
+	}
+	case M_PROGRAM: {
+		/* program */
+		if (msg->program) {
+			length = append_string(dest, left, msg->program->data, msg->program->length, escape);
+		}
+		break;
+	}
+	case M_FULLDATE_RECVD:
+	case M_ISODATE_RECVD:
+	case M_WEEKDAY_RECVD:
+	case M_DATE_RECVD:
+	case M_YEAR_RECVD:
+	case M_MONTH_RECVD:
+	case M_DAY_RECVD:
+	case M_HOUR_RECVD:
+	case M_MIN_RECVD:
+	case M_SEC_RECVD:
+	case M_TZOFFSET_RECVD:
+	case M_TZ_RECVD:
+	case M_UNIXTIME_RECVD:
+
+	case M_FULLDATE_STAMP:
+	case M_ISODATE_STAMP:
+	case M_WEEKDAY_STAMP:
+	case M_DATE_STAMP:
+	case M_YEAR_STAMP:
+	case M_MONTH_STAMP:
+	case M_DAY_STAMP:
+	case M_HOUR_STAMP:
+	case M_MIN_STAMP:
+	case M_SEC_STAMP:
+	case M_TZOFFSET_STAMP:
+	case M_TZ_STAMP:
+	case M_UNIXTIME_STAMP:
+
+	case M_FULLDATE:
+	case M_ISODATE:
+	case M_WEEKDAY:
+	case M_DATE:
+	case M_YEAR:
+	case M_MONTH:
+	case M_DAY: 
+	case M_HOUR:
+	case M_MIN:
+	case M_SEC:
+	case M_TZOFFSET:
+	case M_TZ:
+	case M_UNIXTIME: {
+		/* year, month, day */
+		struct tm *tm;
+		time_t unixtime;
+
+	       	switch(id) {
+	       	case M_FULLDATE_RECVD:
+	       	case M_ISODATE_RECVD: 
+	       	case M_WEEKDAY_RECVD: 
+	       	case M_DATE_RECVD:    
+	       	case M_YEAR_RECVD:    
+	       	case M_MONTH_RECVD:   
+	       	case M_DAY_RECVD:     
+	       	case M_HOUR_RECVD:    
+	       	case M_MIN_RECVD:     
+	       	case M_SEC_RECVD:     
+		case M_TZOFFSET_RECVD:
+		case M_TZ_RECVD:
+	       	case M_UNIXTIME_RECVD:     
+			unixtime = msg->recvd;
+			break;
+		case M_FULLDATE_STAMP:
+		case M_ISODATE_STAMP: 
+		case M_WEEKDAY_STAMP: 
+		case M_DATE_STAMP:    
+		case M_YEAR_STAMP:    
+		case M_MONTH_STAMP:   
+		case M_DAY_STAMP:     
+		case M_HOUR_STAMP:    
+		case M_MIN_STAMP:     
+		case M_SEC_STAMP:     
+		case M_TZOFFSET_STAMP:
+		case M_TZ_STAMP:
+		case M_UNIXTIME_STAMP:     
+			unixtime = msg->stamp;
+	                break;
+		default:
+ 		        if (cfg->use_time_recvd)
+				unixtime = msg->recvd;
+ 		        else
+				unixtime = msg->stamp;
+ 		        break;
+		}
+		
+		tm = localtime(&unixtime);
+
+		switch (id) {
+		case M_WEEKDAY:
+ 	        case M_WEEKDAY_RECVD:
+ 	        case M_WEEKDAY_STAMP:
+	                length = strftime(*dest, *left - 1, "%a", tm);			
+			break;
+		case M_YEAR:
+		case M_YEAR_RECVD:
+ 	        case M_YEAR_STAMP:
+			length = snprintf(*dest, *left, "%04d", tm->tm_year + 1900);
+			break;
+		case M_MONTH:
+ 	        case M_MONTH_RECVD:
+ 	        case M_MONTH_STAMP:
+			length = snprintf(*dest, *left, "%02d", tm->tm_mon + 1);
+			break;
+		case M_DAY:
+ 		case M_DAY_RECVD:
+ 	        case M_DAY_STAMP:  
+			length = snprintf(*dest, *left, "%02d", tm->tm_mday);
+			break;
+		case M_HOUR:
+		case M_HOUR_RECVD:
+ 	        case M_HOUR_STAMP:
+			length = snprintf(*dest, *left, "%02d", tm->tm_hour);
+			break;
+		case M_MIN:
+ 		case M_MIN_RECVD:
+		case M_MIN_STAMP:
+			length = snprintf(*dest, *left, "%02d", tm->tm_min);
+			break;
+		case M_SEC:
+		case M_SEC_RECVD:
+		case M_SEC_STAMP:
+			length = snprintf(*dest, *left, "%02d", tm->tm_sec);
+			break;
+		case M_ISODATE:
+ 		case M_ISODATE_RECVD:
+		case M_ISODATE_STAMP:
+	                length = strftime(*dest, *left - 1, "%Y-%m-%dT%H:%M:%S%z", tm);
+	                break;
+	        case M_FULLDATE:
+ 	        case M_FULLDATE_RECVD:
+ 	        case M_FULLDATE_STAMP:
+	                length = strftime(*dest, *left - 1, "%Y %h %e %H:%M:%S", tm);
+	        	break;
+	        case M_DATE:
+ 	        case M_DATE_RECVD:
+ 	        case M_DATE_STAMP:
+	                length = strftime(*dest, *left - 1, "%h %e %H:%M:%S", tm);
+	                break;
+	        case M_TZOFFSET:
+ 	        case M_TZOFFSET_RECVD:
+ 	        case M_TZOFFSET_STAMP:
+			length = strftime(*dest, *left -1, "%z", tm);
+			break;
+	        case M_TZ:
+ 	        case M_TZ_RECVD:
+ 	        case M_TZ_STAMP:
+			length = strftime(*dest, *left -1, "%Z", tm);
+			break;
+	        case M_UNIXTIME:
+ 	        case M_UNIXTIME_RECVD:
+ 	        case M_UNIXTIME_STAMP:
+			length = snprintf(*dest, *left, "%ld", (long) unixtime);
+	                break;
+		}
+		break;
+	}
+	case M_MESSAGE: {
+		/* message */
+		length = append_string(dest, left, msg->msg->data, msg->msg->length, escape);
+		break;
+	}
+	default:
+		break;
+	}
+	if (length < 0 || length > *left) 
+		length = *left;
+
+	*left -= length;
+	*dest += length;
+}
+
+struct ol_string *
+expand_macros(struct syslog_config *cfg, struct ol_string *template, int template_escape, struct log_info *msg)
+{
+	static struct macro_def macros[] = {
+		{ "FACILITY", M_FACILITY },
+		{ "PRIORITY", M_LEVEL },
+		{ "LEVEL", M_LEVEL },
+		{ "TAG", M_TAG },
+
+		{ "DATE", M_DATE },
+		{ "FULLDATE", M_FULLDATE },
+		{ "ISODATE", M_ISODATE },
+		{ "YEAR", M_YEAR },
+		{ "MONTH", M_MONTH },
+		{ "DAY", M_DAY },
+		{ "HOUR", M_HOUR },
+		{ "MIN", M_MIN },
+		{ "SEC", M_SEC },
+		{ "WEEKDAY", M_WEEKDAY },		{ "UNIXTIME", M_UNIXTIME },
+		{ "TZOFFSET", M_TZOFFSET },
+		{ "TZ", M_TZ },
+
+ 		{ "R_DATE", M_DATE_RECVD },
+ 		{ "R_FULLDATE", M_FULLDATE_RECVD },
+ 		{ "R_ISODATE", M_ISODATE_RECVD },
+ 		{ "R_YEAR", M_YEAR_RECVD },
+ 		{ "R_MONTH", M_MONTH_RECVD },
+ 		{ "R_DAY", M_DAY_RECVD },
+ 		{ "R_HOUR", M_HOUR_RECVD },
+ 		{ "R_MIN", M_MIN_RECVD },
+ 		{ "R_SEC", M_SEC_RECVD },
+ 		{ "R_WEEKDAY", M_WEEKDAY_RECVD },
+		{ "R_UNIXTIME", M_UNIXTIME_RECVD },
+		{ "R_TZOFFSET", M_TZOFFSET_RECVD },
+		{ "R_TZ", M_TZ_RECVD },
+ 
+ 		{ "S_DATE", M_DATE_STAMP },
+ 		{ "S_FULLDATE", M_FULLDATE_STAMP },
+ 		{ "S_ISODATE", M_ISODATE_STAMP },
+ 		{ "S_YEAR", M_YEAR_STAMP },
+ 		{ "S_MONTH", M_MONTH_STAMP },
+ 		{ "S_DAY", M_DAY_STAMP },
+ 		{ "S_HOUR", M_HOUR_STAMP },
+ 		{ "S_MIN", M_MIN_STAMP },
+ 		{ "S_SEC", M_SEC_STAMP },
+ 		{ "S_WEEKDAY", M_WEEKDAY_STAMP },
+ 		{ "S_UNIXTIME", M_UNIXTIME_STAMP },
+ 		{ "S_TZOFFSET", M_TZOFFSET_STAMP },
+ 		{ "S_TZ", M_TZ_STAMP },
+		
+		{ "HOST_FROM", M_HOST_FROM },
+		{ "FULLHOST_FROM", M_FULLHOST_FROM },
+		{ "HOST", M_HOST },
+		{ "FULLHOST", M_FULLHOST },
+
+		{ "PROGRAM", M_PROGRAM },
+		{ "MSG", M_MESSAGE },
+		{ "MESSAGE", M_MESSAGE },
+		{ "SOURCEIP", M_SOURCE_IP }
+	};
+	char format[cfg->log_msg_size + 1], *format_ptr = format;
+	unsigned int left = sizeof(format) - 1;
+	unsigned int i, j;
+
+	i = 0;
+	while (left && (i < template->length)) {
+
+		if (template->data[i] == '$') {
+			/* beginning of a macro */
+			for (j = 0; j < (sizeof(macros) / sizeof(struct macro_def)); j++) {
+				if (strncmp(macros[j].name, &template->data[i + 1], strlen(macros[j].name)) == 0) {
+					break;
+				}
+			}
+			if (j == (sizeof(macros) / sizeof(struct macro_def))) {
+				i++;
+				while ((template->data[i] >= 'A' && 
+					template->data[i] <= 'Z') ||
+				       template->data[i] == '_') 
+					i++;
+			}
+			else {
+				i += strlen(macros[j].name) + 1;
+				expand_macro(cfg, macros[j].id, template_escape, &format_ptr, &left, msg);
+			}
+		}
+		else {
+			*format_ptr = template->data[i];
+			format_ptr++;
+			i++;
+			left--;
+		}
+	}
+	*format_ptr = 0;
+	return c_format_cstring("%z", format);
+}
diff -urN syslog-ng-1.5.25/src/macros.h syslog-ng-1.5.25.template-patch/src/macros.h
--- syslog-ng-1.5.25/src/macros.h	1970-01-01 01:00:00.000000000 +0100
+++ syslog-ng-1.5.25.template-patch/src/macros.h	2003-01-29 08:42:09.000000000 +0100
@@ -0,0 +1,31 @@
+/***************************************************************************
+ *
+ * Copyright (c) 
+ * 
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Inspired by nsyslog, originally written by Darren Reed.
+ *
+ * $Id:$
+ *
+ ***************************************************************************/
+
+#ifndef __MACROS_H
+#define __MACROS_H
+
+struct ol_string *
+expand_macros(struct syslog_config *cfg, struct ol_string *template, int template_escape, struct log_info *msg);
+
+#endif

--------------Boundary-00=_EMIJ9GU8G94XFM4QI2NW--