Index: trunk/include/regfi.h
===================================================================
--- trunk/include/regfi.h	(revision 125)
+++ trunk/include/regfi.h	(revision 126)
@@ -406,3 +406,6 @@
 		      uint32* cell_length, bool* unalloc);
 
+char* regfi_parse_classname(REGF_FILE* file, uint32 offset,
+			    uint16* name_length, bool strict);
+
 #endif	/* _REGFI_H */
Index: trunk/lib/regfi.c
===================================================================
--- trunk/lib/regfi.c	(revision 125)
+++ trunk/lib/regfi.c	(revision 126)
@@ -1510,5 +1510,6 @@
 
 
-
+/*******************************************************************
+ *******************************************************************/
 REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset, 
 			    uint32 max_size, bool strict)
@@ -1635,39 +1636,60 @@
   ret_val->keyname[ret_val->name_length] = '\0';
 
-
-  /***/
-  
-  if(ret_val->classname_length > 0 
-     && ret_val->classname_off != REGF_OFFSET_NONE 
-     && ret_val->classname_off == (ret_val->classname_off & 0xFFFFFFF8))
-  {
-    ret_val->classname = (char*)zalloc(ret_val->classname_length+1);
-    if(ret_val->classname != NULL)
-    {
-      if(!regfi_parse_cell(file->fd, ret_val->classname_off+REGF_BLOCKSIZE, 
-			   (uint8*)ret_val->classname, ret_val->classname_length,
-			   &cell_length, &unalloc) 
-	 || (cell_length < ret_val->classname_length)
-	 || (strict && unalloc))
+  if(ret_val->classname_off != REGF_OFFSET_NONE)
+  {
+    ret_val->classname 
+      = regfi_parse_classname(file, ret_val->classname_off+REGF_BLOCKSIZE,
+			      &ret_val->classname_length, strict);
+    /*
+    if(strict && ret_val->classname == NULL)
+	return NULL;
+    */
+  }
+
+  return ret_val;
+}
+
+
+/*******************************************************************/
+/* XXX: Not currently validating against hbin length.              */
+/*******************************************************************/
+char* regfi_parse_classname(REGF_FILE* file, uint32 offset, 
+			    uint16* name_length, bool strict)
+{
+  char* ret_val = NULL;
+  uint32 length;
+  uint32 cell_length;
+  bool unalloc = false;
+
+  if(*name_length > 0 && offset != REGF_OFFSET_NONE 
+     && offset == (offset & 0xFFFFFFF8))
+  {    
+    if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+	return NULL;
+
+    if(cell_length < *name_length)
+    {
+      if(strict)
+	return NULL;
+      *name_length = cell_length & 0xFFFFFFF8;
+    }
+    
+    ret_val = (char*)zalloc(*name_length);
+    if(ret_val != NULL)
+    {
+      length = *name_length;
+      if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
+	 || length != *name_length)
       {
-	/* Being careful not to reject the whole key here even when
-	 * strict and things are obviously wrong, since it appears
-	 * they're commonly obviously wrong. 
-	 */
-	free(ret_val->classname);
-	ret_val->classname = NULL;
-	return ret_val;
+	free(ret_val);
+	return NULL;
       }
 
-      ret_val->classname[ret_val->classname_length] = '\0';
       /*printf("==> cell_length=%d, classname_length=%d, max_bytes_subkeyclassname=%d\n", cell_length, ret_val->classname_length, ret_val->max_bytes_subkeyclassname);*/
     }
   }
-  /***/
-
 
   return ret_val;
 }
-
 
 
Index: trunk/src/common.c
===================================================================
--- trunk/src/common.c	(revision 125)
+++ trunk/src/common.c	(revision 126)
@@ -123,9 +123,9 @@
  * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
  * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
- * terminates the string.  Returns the length of the string stored in
+ * terminates the string.  Returns the length of the data written to
  * ascii.  On error, returns a negative errno code.
  */
 static int uni_to_ascii(unsigned char* uni, char* ascii, 
-			unsigned int uni_max, unsigned int ascii_max)
+			uint32 uni_max, uint32 ascii_max)
 {
   char* inbuf = (char*)uni;
@@ -136,5 +136,5 @@
 
   /* Set up conversion descriptor. */
-  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
+  conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
 
   ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
@@ -147,5 +147,50 @@
 
   iconv_close(conv_desc);  
-  return strlen(ascii);
+  return ascii_max-out_len-1;
+}
+
+
+static char* quote_unicode(unsigned char* uni, uint32 length, 
+			   const char* special, char** error_msg)
+{
+  char* ret_val;
+  char* ascii;
+  char* tmp_err;
+  int ret_err;
+  *error_msg = NULL;
+
+  ascii = malloc(length+1);
+  if(ascii == NULL)
+  {
+    *error_msg = (char*)malloc(27);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Memory allocation failure.");
+    return NULL;
+  }
+  
+  ret_err = uni_to_ascii(uni, ascii, length, length+1);
+  if(ret_err < 0)
+  {
+    free(ascii);
+    tmp_err = strerror(-ret_err);
+    *error_msg = (char*)malloc(54+strlen(tmp_err));
+    if(*error_msg == NULL)
+    {
+      free(ascii);
+      return NULL;
+    }
+
+    sprintf(*error_msg, 
+	    "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
+    ret_val = quote_buffer(uni, length, special);
+  }
+  else
+  {
+    ret_val = quote_string(ascii, special);
+    free(ascii);
+  }
+  
+  return ret_val;
 }
 
@@ -166,15 +211,13 @@
   char* asciip;
   char* ascii;
-  unsigned char* cur_str;
-  char* cur_ascii;
+  char* ascii_tmp;
   char* cur_quoted;
-  char* tmp_err;
-  const char* str_type;
+  char* tmp_err = NULL;
+  const char* delim;
   uint32 i;
   uint32 cur_str_len;
-  uint32 ascii_max, cur_str_max;
-  uint32 str_rem, cur_str_rem, alen;
+  uint32 ascii_max;
+  uint32 str_rem, alen;
   int ret_err;
-  unsigned short num_nulls;
 
   if(datap == NULL)
@@ -194,37 +237,21 @@
     /* REG_LINK is a symbolic link, stored as a unicode string. */
   case REG_LINK:
-    ascii_max = sizeof(char)*(len+1);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL)
-      return NULL;
-    
     /* Sometimes values have binary stored in them.  If the unicode
      * conversion fails, just quote it raw.
      */
-    ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
-    if(ret_err < 0)
-    {
-      tmp_err = strerror(-ret_err);
-      str_type = regfi_type_val2str(type);
-      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
-      if(*error_msg == NULL)
-      {
-	free(ascii);
-	return NULL;
-      }
-      sprintf(*error_msg, "Unicode conversion failed on %s field; "
-	       "printing as binary.  Error: %s", str_type, tmp_err);
-      
-      cur_quoted = quote_buffer(datap, len, common_special_chars);
-    }
-    else
-      cur_quoted = quote_string(ascii, common_special_chars);
-    free(ascii);
+    cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
     if(cur_quoted == NULL)
     {
-      *error_msg = (char*)malloc(27+1);
-      if(*error_msg != NULL)
-	strcpy(*error_msg, "Buffer could not be quoted.");
-    }
+      if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+      else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
+      {
+	sprintf(*error_msg, "Buffer could not be quoted due to error: %s", 
+		tmp_err);
+	free(tmp_err);
+      }
+    }
+    else if (tmp_err != NULL)
+      *error_msg = tmp_err;
     return cur_quoted;
     break;
@@ -264,92 +291,56 @@
     break;
     
-
-  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
-   *      redone with fewer malloc calls and better string concatenation.
-   *      Also, gives lame output when "\0\0" is the string.
-   */
   case REG_MULTI_SZ:
     ascii_max = sizeof(char)*(len*4+1);
-    cur_str_max = sizeof(char)*(len+1);
-    cur_str = malloc(cur_str_max);
-    cur_ascii = malloc(cur_str_max);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
-      return NULL;
-
-    /* Reads until it reaches 4 consecutive NULLs, 
-     * which is two nulls in unicode, or until it reaches len, or until we
-     * run out of buffer.  The latter should never happen, but we shouldn't
-     * trust our file to have the right lengths/delimiters.
+    ascii_tmp = malloc(ascii_max);
+    if(ascii_tmp == NULL)
+      return NULL;
+
+    /* Attempt to convert entire string from UTF-16LE to ASCII, 
+     * then parse and quote fields individually.
+     * If this fails, simply quote entire buffer as binary. 
      */
-    asciip = ascii;
-    num_nulls = 0;
-    str_rem = ascii_max;
-    cur_str_rem = cur_str_max;
-    cur_str_len = 0;
-
-    for(i=0; (i < len) && str_rem > 0; i++)
-    {
-      *(cur_str+cur_str_len) = *(datap+i);
-      if(*(cur_str+cur_str_len) == 0)
-	num_nulls++;
-      else
-	num_nulls = 0;
-      cur_str_len++;
-
-      if(num_nulls == 2)
-      {
-	ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
-	if(ret_err < 0)
+    ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
+    if(ret_err < 0)
+    {
+      tmp_err = strerror(-ret_err);
+      *error_msg = (char*)malloc(54+strlen(tmp_err));
+      if(*error_msg == NULL)
+	return NULL;
+      sprintf(*error_msg, "MULTI_SZ unicode conversion"
+	      " failed with '%s'. Quoting as binary.", tmp_err);
+      ascii = quote_buffer(datap, len, subfield_special_chars);
+    }
+    else
+    {
+      ascii = malloc(ascii_max);
+      if(ascii == NULL)
+      {
+	free(ascii_tmp);
+	return NULL;
+      }
+      asciip = ascii;
+      asciip[0] = '\0';
+      str_rem = ascii_max;
+      delim = "";
+      for(i=0; i<ret_err; i+=cur_str_len+1)
+      {
+	cur_str_len = strlen(ascii_tmp+i);
+	if(ascii_tmp[i] != '\0')
 	{
-	  /* XXX: should every sub-field error be enumerated? */
-	  if(*error_msg == NULL)
+	  cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
+	  if(cur_quoted != NULL)
 	  {
-	    tmp_err = strerror(-ret_err);
-	    *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
-	    if(*error_msg == NULL)
-	    {
-	      free(cur_str);
-	      free(cur_ascii);
-	      free(ascii);
-	      return NULL;
-	    }
-	    sprintf(*error_msg, "Unicode conversion failed on at least one "
-		    "MULTI_SZ sub-field; printing as binary.  Error: %s",
-		    tmp_err);
+	    alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
+	    asciip += alen;
+	    str_rem -= alen;
+	    free(cur_quoted);
 	  }
-	  cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
-				    subfield_special_chars);
 	}
-	else
-	  cur_quoted = quote_string(cur_ascii, subfield_special_chars);
-
-	alen = snprintf(asciip, str_rem, "%s", cur_quoted);
-	asciip += alen;
-	str_rem -= alen;
-	free(cur_quoted);
-
-	if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
-	  break;
-	else
-	{
-	  if(str_rem > 0)
-	  {
-	    asciip[0] = '|';
-	    asciip[1] = '\0';
-	    asciip++;
-	    str_rem--;
-	  }
-	  memset(cur_str, 0, cur_str_max);
-	  cur_str_len = 0;
-	  num_nulls = 0;
-	  /* To eliminate leading nulls in subsequent strings. */
-	  i++;
-	}
-      }
-    }
-    *asciip = 0;
-    free(cur_str);
-    free(cur_ascii);
+	delim = "|";
+      }
+    }
+
+    free(ascii_tmp);
     return ascii;
     break;
@@ -357,9 +348,10 @@
   /* XXX: Dont know what to do with these yet, just print as binary... */
   default:
-    /* XXX: It would be really nice if this message somehow included the
-     *      name of the current value we're having trouble with, since
-     *      stderr/stdout don't always sync nicely.
-     */
-    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
+    *error_msg = (char*)malloc(65);
+    if(*error_msg == NULL)
+      return NULL;
+    sprintf(*error_msg,
+	    "Unrecognized registry data type (0x%.8X); quoting as binary.",
+	    type);
     
   case REG_NONE:
Index: trunk/src/reglookup.c
===================================================================
--- trunk/src/reglookup.c	(revision 125)
+++ trunk/src/reglookup.c	(revision 126)
@@ -97,5 +97,4 @@
 	      "Returned error: %s\n", prefix, quoted_name, conv_error);
   }
-  /* XXX: should these always be printed? */
   else if(conv_error != NULL && print_verbose)
     fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
@@ -296,4 +295,5 @@
   char* dacl = NULL;
   char* quoted_classname;
+  char* error_msg = NULL;
   char mtime[20];
   time_t tmp_time[1];
@@ -322,5 +322,27 @@
 
     if(k->classname != NULL)
-      quoted_classname = quote_string(k->classname, key_special_chars);
+    {
+      quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
+				       key_special_chars, &error_msg);
+      if(quoted_classname == NULL)
+      {
+	if(error_msg == NULL)
+	  fprintf(stderr, "ERROR: Could not quote classname"
+		  " for key '%s' due to unknown error.\n", full_path);
+	else
+	{
+	  fprintf(stderr, "ERROR: Could not quote classname"
+		  " for key '%s' due to error: %s\n", full_path, error_msg);
+	  free(error_msg);
+	}
+      }
+      else if (error_msg != NULL)
+      {
+	if(print_verbose)
+	  fprintf(stderr, "WARNING: While converting classname"
+		  " for key '%s': %s.\n", full_path, error_msg);
+	free(error_msg);
+      }
+    }
     else
       quoted_classname = empty_str;
