gchar *
g_utf8_strescape (const gchar * utf8_source, const gchar * exceptions)
{
  GString *result;
  guchar excmap[256];
  gunichar c;

  g_return_val_if_fail (utf8_source != NULL, NULL);
  g_return_val_if_fail (g_utf8_validate (utf8_source, -1, NULL), NULL);

  memset (excmap, 0, 256);
  if (exceptions)
    {
      guchar *e = (guchar *) exceptions;

      while (*e)
	{
	  excmap[*e] = 1;
	  e++;
	}
    }

  result = g_string_new (NULL);
  while ((c = g_utf8_get_char (p)))
    {
      if (c < sizeof (excmap) && excmap[c])
	g_string_append_c (result, c);
      else
	{
	  switch (c)
	    {
	    case '\b':
	      g_string_append (result, "\\b");
	      break;
	    case '\f':
	      g_string_append (result, "\\f");
	      break;
	    case '\n':
	      g_string_append (result, "\\n");
	      break;
	    case '\r':
	      g_string_append (result, "\\r");
	      break;
	    case '\t':
	      g_string_append (result, "\\t");
	      break;
	    case '\v':
	      g_string_append (result, "\\v");
	      break;
	    case '\\':
	      g_string_append (result, "\\\\");
	      break;
	    case '"':
	      g_string_append (result, "\\\"");
	      break;
	    default:
	      g_string_append_unichar (result, c);
	    }
	}
      p = g_utf8_find_next_char (p, NULL);
    }
  return g_string_free (result, FALSE);
}
