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

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

  memset (excmap, 0, sizeof excmap);
  if (exceptions)
    {
      while (*exceptions)
	{
	  excmap[(guchar) *exceptions] = 1;
	  exceptions++;
	}
    }

  result = g_string_new (NULL);
  while (*p)
    {
      gunichar 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:
	      if (c < ' ')
		{
		  g_string_append (result, "\\0");
		  g_string_append_c (result, '0' + ((c >> 3) & 07));
		  g_string_append_c (result, '0' + (c & 07));
		}
	      else
		g_string_append_unichar (result, c);
	    }
	}
      p = g_utf8_next_char (p);
    }
  return g_string_free (result, FALSE);
}
