--- e-vcard.c.orig	2005-08-10 13:06:32.000000000 +0100
+++ e-vcard.c.rawk2	2005-08-11 10:30:34.000000000 +0100
@@ -125,75 +127,28 @@
 	return vcard_type;
 }
 
-
-
-static char*
-fold_lines (char *buf)
-{
-	GString *str = g_string_new ("");
-	char *p = buf;
-	char *next, *next2;
-
-	/* we're pretty liberal with line folding here.  We handle
-	   lines folded with \r\n<WS>... and \n\r<WS>... and
-	   \n<WS>... We also turn single \r's and \n's not followed by
-	   WS into \r\n's. */
-	while (*p) {
-		if (*p == '\r' || *p == '\n') {
-			next = g_utf8_next_char (p);
-			if (*next == '\n' || *next == '\r') {
-				next2 = g_utf8_next_char (next);
-				if (*next2 == ' ' || *next2 == '\t') {
-					p = g_utf8_next_char (next2);
-				}
-				else {
-					g_string_append (str, CRLF);
-					p = g_utf8_next_char (next);
-				}
-			}
-			else if (*next == ' ' || *next == '\t') {
-				p = g_utf8_next_char (next);
-			}
-			else {
-				g_string_append (str, CRLF);
-				p = g_utf8_next_char (p);
-			}
-		}
-		else {
-			g_string_append_unichar (str, g_utf8_get_char (p));
-			p = g_utf8_next_char (p);
-		}
-	}
-
-	g_free (buf);
-
-	return g_string_free (str, FALSE);
-}
-
-/* skip forward until we hit the CRLF, or \0 */
 static void
-skip_to_next_line (char **p)
+skip_to_next_line (const char **p)
 {
-	char *lp;
-	lp = *p;
+	const char *lp = *p;
 
-	while (*lp != '\r' && *lp != '\0')
-		lp = g_utf8_next_char (lp);
+	while (*lp != '\r' && *lp != '\n' && *lp != '\0')
+		lp++;
 
 	if (*lp == '\r') {
-		lp = g_utf8_next_char (lp); /* \n */
-		lp = g_utf8_next_char (lp); /* start of the next line */
+		if (*++lp == '\n')
+			lp++;
 	}
-
+	
 	*p = lp;
 }
 
 /* skip forward until we hit a character in @s, CRLF, or \0.  leave *p
    pointing at the character that causes us to stop */
 static void
-skip_until (char **p, char *s)
+skip_until (const char **p, char *s)
 {
-	char *lp;
+	const char *lp;
 
 	lp = *p;
 
@@ -215,26 +170,64 @@
 	*p = lp;
 }
 
+#define WRITE_CHUNK \
+	if (chunk_start) { \
+		g_string_append_len (str, chunk_start, lp - chunk_start); \
+		chunk_start = NULL; \
+	}
+
+#define UNFOLD(p)				       \
+	else if (*p == '\r' || *p == '\n') {	       \
+		p++;				       \
+		if (*p == '\n' || *p == '\r') {	       \
+			p++;			       \
+			if (*p == ' ' || *p == '\t') { \
+				p++;		       \
+			} else {		       \
+				break;		       \
+			}			       \
+		} else if (*p == ' ' || *p == '\t') {  \
+			p++;			       \
+		} else {			       \
+			break;			       \
+		}				       \
+	}
+
+#define UNFOLD_CHUNK(p)				       \
+	else if (*p == '\r' || *p == '\n') {	       \
+		WRITE_CHUNK;			       \
+		p++;				       \
+		if (*p == '\n' || *p == '\r') {	       \
+			p++;			       \
+			if (*p == ' ' || *p == '\t') { \
+				p++;		       \
+			} else {		       \
+				break;		       \
+			}			       \
+		} else if (*p == ' ' || *p == '\t') {  \
+			p++;			       \
+		} else {			       \
+			break;			       \
+		}				       \
+	}
+
+
 static void
-read_attribute_value (EVCardAttribute *attr, char **p, gboolean quoted_printable)
+read_attribute_value (EVCardAttribute *attr, const char **p, gboolean quoted_printable)
 {
-	char *lp = *p;
+	const char *lp = *p;
 	GString *str;
+	const char *chunk_start = NULL;
 
 	/* read in the value */
-	str = g_string_new ("");
-	while (*lp != '\r' && *lp != '\0') {
+	str = g_string_sized_new (16);
+	while (*lp) {
+		//g_printerr("(%c)", *lp);
 		if (*lp == '=' && quoted_printable) {
 			char a, b;
+			WRITE_CHUNK;
 			if ((a = *(++lp)) == '\0') break;
 			if ((b = *(++lp)) == '\0') break;
-			if (a == '\r' && b == '\n') {
-				/* it was a = at the end of the line,
-				 * just ignore this and continue
-				 * parsing on the next line.  yay for
-				 * 2 kinds of line folding
-				 */
-			}
 			else if (isxdigit(a) && isxdigit (b)) {
 				gunichar c;
 
@@ -244,7 +237,7 @@
 				c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
 					| ((b>='a'?b-'a'+10:b-'0')&0x0f);
 				
-				g_string_append_unichar (str, c);
+				g_string_insert_unichar (str, -1, c);
 			}
 			else 
 				{
@@ -255,6 +248,7 @@
 			lp++;
 		}
 		else if (*lp == '\\') {
+			WRITE_CHUNK;
 			/* convert back to the non-escaped version of
 			   the characters */
 			lp = g_utf8_next_char(lp);
@@ -271,50 +265,51 @@
 			default:
 				g_warning ("invalid escape, passing it through");
 				g_string_append_c (str, '\\');
-				g_string_append_unichar (str, g_utf8_get_char(lp));
+				g_string_insert_unichar (str, -1, g_utf8_get_char(lp));
 				break;
 			}
 			lp = g_utf8_next_char(lp);
 		}
 		else if ((*lp == ';') ||
 			 (*lp == ',' && !strcmp (attr->name, "CATEGORIES"))) {
+			WRITE_CHUNK;
 			e_vcard_attribute_add_value (attr, str->str);
 			g_string_assign (str, "");
 			lp = g_utf8_next_char(lp);
 		}
+		UNFOLD_CHUNK(lp)
 		else {
-			g_string_append_unichar (str, g_utf8_get_char (lp));
+			if (chunk_start == NULL)
+				chunk_start = lp;
+
+			//g_string_insert_unichar (str, -1, g_utf8_get_char (lp));
 			lp = g_utf8_next_char(lp);
 		}
 	}
+	WRITE_CHUNK;
 	if (str) {
 		e_vcard_attribute_add_value (attr, str->str);
 		g_string_free (str, TRUE);
 	}
 
-	if (*lp == '\r') {
-		lp = g_utf8_next_char (lp); /* \n */
-		lp = g_utf8_next_char (lp); /* start of the next line */
-	}
-
 	*p = lp;
 }
 
 static void
-read_attribute_params (EVCardAttribute *attr, char **p, gboolean *quoted_printable)
+read_attribute_params (EVCardAttribute *attr, const char **p, gboolean *quoted_printable)
 {
-	char *lp = *p;
+	const char *lp = *p;
 	GString *str;
 	EVCardAttributeParam *param = NULL;
 	gboolean in_quote = FALSE;
-	str = g_string_new ("");
+	str = g_string_sized_new (16);
 	while (*lp != '\0') {
 		if (*lp == '"') {
 			in_quote = !in_quote;
 			lp = g_utf8_next_char (lp);
 		}
 		else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_') {
-			g_string_append_unichar (str, g_utf8_get_char (lp));
+			g_string_insert_unichar (str, -1, g_utf8_get_char (lp));
 			lp = g_utf8_next_char (lp);
 		}
 		/* accumulate until we hit the '=' or ';'.  If we hit
@@ -427,6 +422,7 @@
 			if (colon)
 				break;
 		}
+		UNFOLD (lp)
 		else {
 			g_warning ("invalid character found in parameter spec");
 			g_string_assign (str, "");
@@ -449,19 +445,20 @@
 /* reads an entire attribute from the input buffer, leaving p pointing
    at the start of the next line (past the \r\n) */
 static EVCardAttribute*
-read_attribute (char **p)
+read_attribute (const char **p)
 {
 	char *attr_group = NULL;
 	char *attr_name = NULL;
 	EVCardAttribute *attr = NULL;
 	GString *str;
-	char *lp = *p;
+	const char *lp = *p;
+	char c;
 	gboolean is_qp = FALSE;
-
+	
 	/* first read in the group/name */
-	str = g_string_new ("");
-	while (*lp != '\r' && *lp != '\0') {
-		if (*lp == ':' || *lp == ';') {
+	str = g_string_sized_new (16);
+	while ((c = *lp)) {
+		if (c == ':' || c == ';') {
 			if (str->len != 0) {
 				/* we've got a name, break out to the value/attribute parsing */
 				attr_name = g_string_free (str, FALSE);
@@ -481,23 +478,23 @@
 				goto lose;
 			}
 		}
-		else if (*lp == '.') {
+		else if (c == '.') {
 			if (attr_group) {
 				g_warning ("extra `.' in attribute specification.  ignoring extra group `%s'",
 					   str->str);
-				g_string_free (str, TRUE);
-				str = g_string_new ("");
+				g_string_assign (str, "");
 			}
 			if (str->len != 0) {
 				attr_group = g_string_free (str, FALSE);
-				str = g_string_new ("");
+				g_string_assign (str, "");
 			}
 		}
-		else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_') {
-			g_string_append_unichar (str, g_utf8_get_char (lp));
+		else if (g_unichar_isalnum (g_utf8_get_char (lp)) || c == '-' || c == '_') {
+			g_string_insert_unichar (str, -1, g_utf8_get_char (lp));
 		}
+		UNFOLD (lp)
 		else {
-			g_warning ("invalid character found in attribute group/name");
+			g_warning ("invalid character '%c' found in attribute group/name", c);
 			g_string_free (str, TRUE);
 			*p = lp;
 			skip_to_next_line(p);
@@ -508,7 +505,6 @@
 	}
 
 	if (!attr_name) {
-		skip_to_next_line (p);
 		goto lose;
 	}
 
@@ -531,8 +527,9 @@
 
 	*p = lp;
 
-	if (!attr->values)
+	if (!attr->values) {
 		goto lose;
+	}
 
 	return attr;
  lose:
@@ -541,34 +538,15 @@
 	return NULL;
 }
 
-/* we try to be as forgiving as we possibly can here - this isn't a
- * validator.  Almost nothing is considered a fatal error.  We always
- * try to return *something*.
- */
 static void
 parse (EVCard *evc, const char *str)
 {
-	char *buf = g_strdup (str);
-	char *p, *end;
 	EVCardAttribute *attr;
 
-	/* first validate the string is valid utf8 */
-	if (!g_utf8_validate (buf, -1, (const char **)&end)) {
-		/* if the string isn't valid, we parse as much as we can from it */
-		g_warning ("invalid utf8 passed to EVCard.  Limping along.");
-		*end = '\0';
-	}
+	if (str == NULL)
+		return;
 	
-	buf = fold_lines (buf);
-
-	d(printf ("BEFORE FOLDING:\n"));
-	d(printf (str));
-	d(printf ("\n\nAFTER FOLDING:\n"));
-	d(printf (buf));
-
-	p = buf;
-
-	attr = read_attribute (&p);
+	attr = read_attribute (&str);
 	if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
 		g_warning ("vcard began without a BEGIN:VCARD\n");
 	}
@@ -576,25 +554,20 @@
 		e_vcard_attribute_free (attr);
 	else if (attr)
 		e_vcard_add_attribute (evc, attr);
-
-	while (*p) {
-		EVCardAttribute *next_attr = read_attribute (&p);
-
-		if (next_attr) {
-			if (g_ascii_strcasecmp (next_attr->name, "end"))
-				e_vcard_add_attribute (evc, next_attr);
-			attr = next_attr;
-		}
+	
+	while ((attr = read_attribute (&str))) {
+		//g_printerr("Got attribute %s\n", attr->name);
+		if (g_ascii_strcasecmp (attr->name, "end") != 0)
+			e_vcard_add_attribute (evc, attr);
+		else
+			break;
 	}
 
-	if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
+	if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end"))
 		g_warning ("vcard ended without END:VCARD\n");
-	}
-
+	
 	if (attr && !g_ascii_strcasecmp (attr->name, "end"))
 		e_vcard_attribute_free (attr);
-
-	g_free (buf);
 }
 
 /**
@@ -745,9 +718,9 @@
 	GList *l;
 	GList *v;
 
-	GString *str = g_string_new ("");
+	GString *str;
 
-	g_string_append (str, "BEGIN:VCARD" CRLF);
+	str = g_string_new ("BEGIN:VCARD" CRLF);
 
 	/* we hardcode the version (since we're outputting to a
 	   specific version) and ignore any version attributes the
