$OpenBSD: patch-src_log_c,v 1.1 2013/03/28 16:22:32 brad Exp $

Support multi line logging.

--- src/log.c.orig	Tue Aug 17 05:04:38 2010
+++ src/log.c	Wed Mar 27 09:14:49 2013
@@ -263,39 +263,9 @@ int log_error_close(server *srv) {
 	return 0;
 }
 
-int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) {
-	va_list ap;
-
-	switch(srv->errorlog_mode) {
-	case ERRORLOG_PIPE:
-	case ERRORLOG_FILE:
-	case ERRORLOG_FD:
-		if (-1 == srv->errorlog_fd) return 0;
-		/* cache the generated timestamp */
-		if (srv->cur_ts != srv->last_generated_debug_ts) {
-			buffer_prepare_copy(srv->ts_debug_str, 255);
-			strftime(srv->ts_debug_str->ptr, srv->ts_debug_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts)));
-			srv->ts_debug_str->used = strlen(srv->ts_debug_str->ptr) + 1;
-
-			srv->last_generated_debug_ts = srv->cur_ts;
-		}
-
-		buffer_copy_string_buffer(srv->errorlog_buf, srv->ts_debug_str);
-		buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(": ("));
-		break;
-	case ERRORLOG_SYSLOG:
-		/* syslog is generating its own timestamps */
-		buffer_copy_string_len(srv->errorlog_buf, CONST_STR_LEN("("));
-		break;
-	}
-
-	buffer_append_string(srv->errorlog_buf, filename);
-	buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("."));
-	buffer_append_long(srv->errorlog_buf, line);
-	buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(") "));
-
-
-	for(va_start(ap, fmt); *fmt; fmt++) {
+/* lowercase: append space, uppercase: don't */
+static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) {
+	for(; *fmt; fmt++) {
 		int d;
 		char *s;
 		buffer *b;
@@ -304,50 +274,50 @@ int log_error_write(server *srv, const char *filename,
 		switch(*fmt) {
 		case 's':           /* string */
 			s = va_arg(ap, char *);
-			buffer_append_string(srv->errorlog_buf, s);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" "));
+			buffer_append_string(out, s);
+			buffer_append_string_len(out, CONST_STR_LEN(" "));
 			break;
 		case 'b':           /* buffer */
 			b = va_arg(ap, buffer *);
-			buffer_append_string_buffer(srv->errorlog_buf, b);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" "));
+			buffer_append_string_buffer(out, b);
+			buffer_append_string_len(out, CONST_STR_LEN(" "));
 			break;
 		case 'd':           /* int */
 			d = va_arg(ap, int);
-			buffer_append_long(srv->errorlog_buf, d);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" "));
+			buffer_append_long(out, d);
+			buffer_append_string_len(out, CONST_STR_LEN(" "));
 			break;
 		case 'o':           /* off_t */
 			o = va_arg(ap, off_t);
-			buffer_append_off_t(srv->errorlog_buf, o);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" "));
+			buffer_append_off_t(out, o);
+			buffer_append_string_len(out, CONST_STR_LEN(" "));
 			break;
 		case 'x':           /* int (hex) */
 			d = va_arg(ap, int);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("0x"));
-			buffer_append_long_hex(srv->errorlog_buf, d);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" "));
+			buffer_append_string_len(out, CONST_STR_LEN("0x"));
+			buffer_append_long_hex(out, d);
+			buffer_append_string_len(out, CONST_STR_LEN(" "));
 			break;
 		case 'S':           /* string */
 			s = va_arg(ap, char *);
-			buffer_append_string(srv->errorlog_buf, s);
+			buffer_append_string(out, s);
 			break;
 		case 'B':           /* buffer */
 			b = va_arg(ap, buffer *);
-			buffer_append_string_buffer(srv->errorlog_buf, b);
+			buffer_append_string_buffer(out, b);
 			break;
 		case 'D':           /* int */
 			d = va_arg(ap, int);
-			buffer_append_long(srv->errorlog_buf, d);
+			buffer_append_long(out, d);
 			break;
 		case 'O':           /* off_t */
 			o = va_arg(ap, off_t);
-			buffer_append_off_t(srv->errorlog_buf, o);
+			buffer_append_off_t(out, o);
 			break;
 		case 'X':           /* int (hex) */
 			d = va_arg(ap, int);
-			buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("0x"));
-			buffer_append_long_hex(srv->errorlog_buf, d);
+			buffer_append_string_len(out, CONST_STR_LEN("0x"));
+			buffer_append_long_hex(out, d);
 			break;
 		case '(':
 		case ')':
@@ -355,24 +325,110 @@ int log_error_write(server *srv, const char *filename,
 		case '>':
 		case ',':
 		case ' ':
-			buffer_append_string_len(srv->errorlog_buf, fmt, 1);
+			buffer_append_string_len(out, fmt, 1);
 			break;
 		}
 	}
-	va_end(ap);
+}
 
+static int log_buffer_prepare(buffer *b, server *srv, const char *filename, unsigned int line) {
 	switch(srv->errorlog_mode) {
 	case ERRORLOG_PIPE:
 	case ERRORLOG_FILE:
 	case ERRORLOG_FD:
-		buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("\n"));
-		write(srv->errorlog_fd, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1);
+		if (-1 == srv->errorlog_fd) return -1;
+		/* cache the generated timestamp */
+		if (srv->cur_ts != srv->last_generated_debug_ts) {
+			buffer_prepare_copy(srv->ts_debug_str, 255);
+			strftime(srv->ts_debug_str->ptr, srv->ts_debug_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts)));
+			srv->ts_debug_str->used = strlen(srv->ts_debug_str->ptr) + 1;
+
+			srv->last_generated_debug_ts = srv->cur_ts;
+		}
+
+		buffer_copy_string_buffer(b, srv->ts_debug_str);
+		buffer_append_string_len(b, CONST_STR_LEN(": ("));
 		break;
 	case ERRORLOG_SYSLOG:
-		syslog(LOG_ERR, "%s", srv->errorlog_buf->ptr);
+		/* syslog is generating its own timestamps */
+		buffer_copy_string_len(b, CONST_STR_LEN("("));
 		break;
 	}
 
+	buffer_append_string(b, filename);
+	buffer_append_string_len(b, CONST_STR_LEN("."));
+	buffer_append_long(b, line);
+	buffer_append_string_len(b, CONST_STR_LEN(") "));
+
 	return 0;
 }
 
+static void log_write(server *srv, buffer *b) {
+	switch(srv->errorlog_mode) {
+	case ERRORLOG_PIPE:
+	case ERRORLOG_FILE:
+	case ERRORLOG_FD:
+		buffer_append_string_len(b, CONST_STR_LEN("\n"));
+		write(srv->errorlog_fd, b->ptr, b->used - 1);
+		break;
+	case ERRORLOG_SYSLOG:
+		syslog(LOG_ERR, "%s", b->ptr);
+		break;
+	}
+}
+
+int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) {
+	va_list ap;
+
+	if (-1 == log_buffer_prepare(srv->errorlog_buf, srv, filename, line)) return 0;
+
+	va_start(ap, fmt);
+	log_buffer_append_printf(srv->errorlog_buf, fmt, ap);
+	va_end(ap);
+
+	log_write(srv, srv->errorlog_buf);
+
+	return 0;
+}
+
+int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...) {
+	va_list ap;
+	size_t prefix_used;
+	buffer *b = srv->errorlog_buf;
+	char *pos, *end, *current_line;
+
+	if (multiline->used < 2) return 0;
+
+	if (-1 == log_buffer_prepare(b, srv, filename, line)) return 0;
+
+	va_start(ap, fmt);
+	log_buffer_append_printf(b, fmt, ap);
+	va_end(ap);
+
+	prefix_used = b->used;
+
+	current_line = pos = multiline->ptr;
+	end = multiline->ptr + multiline->used;
+
+	for ( ; pos < end ; ++pos) {
+		switch (*pos) {
+		case '\n':
+		case '\r':
+		case '\0': /* handles end of string */
+			if (current_line < pos) {
+				/* truncate to prefix */
+				b->used = prefix_used;
+				b->ptr[b->used - 1] = '\0';
+
+				buffer_append_string_len(b, current_line, pos - current_line);
+				log_write(srv, b);
+			}
+			current_line = pos + 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
