diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 7d2f79d1e0cef7af2bd298930db46af9938e07b0..095d2fc8644dcb9b7c9637533f4f55be41a5c21d 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.5 2003/07/24 15:52:53 petere Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.6 2003/07/31 17:21:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,12 +19,15 @@
 #include "parser/keywords.h"
 
 
+#define supports_grant_options(version) ((version) >= 70400)
+
+static bool parseAclArray(const char *acls, char ***itemarray, int *nitems);
 static bool parseAclItem(const char *item, const char *type, const char *name,
 						 int remoteVersion,
 						 PQExpBuffer grantee, PQExpBuffer grantor,
 						 PQExpBuffer privs, PQExpBuffer privswgo);
+static char *copyAclUserName(PQExpBuffer output, char *input);
 static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
-#define supports_grant_options(version) ((version) >= 70400)
 
 
 /*
@@ -185,8 +188,9 @@ buildACLCommands(const char *name, const char *type,
 				 int remoteVersion,
 				 PQExpBuffer sql)
 {
-	char	   *aclbuf,
-			   *tok;
+	char	  **aclitems;
+	int			naclitems;
+	int			i;
 	PQExpBuffer grantee, grantor, privs, privswgo;
 	PQExpBuffer	firstsql, secondsql;
 	bool		found_owner_privs = false;
@@ -194,6 +198,13 @@ buildACLCommands(const char *name, const char *type,
 	if (strlen(acls) == 0)
 		return true;			/* object has default permissions */
 
+	if (!parseAclArray(acls, &aclitems, &naclitems))
+	{
+		if (aclitems)
+			free(aclitems);
+		return false;
+	}
+
 	grantee = createPQExpBuffer();
 	grantor = createPQExpBuffer();
 	privs = createPQExpBuffer();
@@ -217,27 +228,10 @@ buildACLCommands(const char *name, const char *type,
 	appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM PUBLIC;\n",
 					  type, name);
 
-	/* Make a working copy of acls so we can use strtok */
-	aclbuf = strdup(acls);
-
-	/* Scan comma-separated ACL items */
-	for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ","))
+	/* Scan individual ACL items */
+	for (i = 0; i < naclitems; i++)
 	{
-		size_t toklen;
-
-		/*
-		 * Token may start with '{' and/or '"'.  Actually only the start
-		 * of the string should have '{', but we don't verify that.
-		 */
-		if (*tok == '{')
-			tok++;
-		if (*tok == '"')
-			tok++;
-		toklen = strlen(tok);
-		while (toklen >=0 && (tok[toklen-1] == '"' || tok[toklen-1] == '}'))
-			tok[toklen-- - 1] = '\0';
-
-		if (!parseAclItem(tok, type, name, remoteVersion,
+		if (!parseAclItem(aclitems[i], type, name, remoteVersion,
 						  grantee, grantor, privs, privswgo))
 			return false;
 
@@ -327,7 +321,6 @@ buildACLCommands(const char *name, const char *type,
 						  type, name, fmtId(owner));
 	}
 
-	free(aclbuf);
 	destroyPQExpBuffer(grantee);
 	destroyPQExpBuffer(grantor);
 	destroyPQExpBuffer(privs);
@@ -337,15 +330,105 @@ buildACLCommands(const char *name, const char *type,
 	destroyPQExpBuffer(firstsql);
 	destroyPQExpBuffer(secondsql);
 
+	free(aclitems);
+
 	return true;
 }
 
+/*
+ * Deconstruct an ACL array (or actually any 1-dimensional Postgres array)
+ * into individual items.
+ *
+ * On success, returns true and sets *itemarray and *nitems to describe
+ * an array of individual strings.  On parse failure, returns false;
+ * *itemarray may exist or be NULL.
+ *
+ * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
+ */
+static bool
+parseAclArray(const char *acls, char ***itemarray, int *nitems)
+{
+	int			inputlen;
+	char	  **items;
+	char	   *strings;
+	int			curitem;
+
+	/*
+	 * We expect input in the form of "{item,item,item}" where any item
+	 * is either raw data, or surrounded by double quotes (in which case
+	 * embedded characters including backslashes and quotes are backslashed).
+	 *
+	 * We build the result as an array of pointers followed by the actual
+	 * string data, all in one malloc block for convenience of deallocation.
+	 * The worst-case storage need is not more than one pointer and one
+	 * character for each input character (consider "{,,,,,,,,,,}").
+	 */
+	*itemarray = NULL;
+	*nitems = 0;
+	inputlen = strlen(acls);
+	if (inputlen < 2 || acls[0] != '{' || acls[inputlen-1] != '}')
+		return false;			/* bad input */
+	items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
+	if (items == NULL)
+		return false;			/* out of memory */
+	*itemarray = items;
+	strings = (char *) (items + inputlen);
+
+	acls++;						/* advance over initial '{' */
+	curitem = 0;
+	while (*acls != '}')
+	{
+		if (*acls == '\0')
+			return false;		/* premature end of string */
+		items[curitem] = strings;
+		while (*acls != '}' && *acls != ',')
+		{
+			if (*acls == '\0')
+				return false;		/* premature end of string */
+			if (*acls != '"')
+				*strings++ = *acls++; /* copy unquoted data */
+			else
+			{
+				/* process quoted substring */
+				acls++;
+				while (*acls != '"')
+				{
+					if (*acls == '\0')
+						return false;		/* premature end of string */
+					if (*acls == '\\')
+					{
+						acls++;
+						if (*acls == '\0')
+							return false; /* premature end of string */
+					}
+					*strings++ = *acls++; /* copy quoted data */
+				}
+				acls++;
+			}
+		}
+		*strings++ = '\0';
+		if (*acls == ',')
+			acls++;
+		curitem++;
+	}
+	if (acls[1] != '\0')
+		return false;			/* bogus syntax (embedded '}') */
+	*nitems = curitem;
+	return true;
+}
 
 /*
- * This will take an aclitem string of privilege code letters and
- * parse it into grantee, grantor, and privilege information.  The
- * privilege information is split between privileges with grant option
- * (privswgo) and without (privs).
+ * This will parse an aclitem string, having the general form
+ *		username=privilegecodes/grantor
+ * or
+ *		group groupname=privilegecodes/grantor
+ * (the /grantor part will not be present if pre-7.4 database).
+ *
+ * The returned grantee string will be the dequoted username or groupname
+ * (preceded with "group " in the latter case).  The returned grantor is
+ * the dequoted grantor name or empty.  Privilege characters are decoded
+ * and split between privileges with grant option (privswgo) and without
+ * (privs).
  *
  * Note: for cross-version compatibility, it's important to use ALL when
  * appropriate.
@@ -365,19 +448,19 @@ parseAclItem(const char *item, const char *type, const char *name,
 
 	buf = strdup(item);
 
-	/* user name is string up to = */
-	eqpos = strchr(buf, '=');
-	if (!eqpos)
+	/* user or group name is string up to = */
+	eqpos = copyAclUserName(grantee, buf);
+	if (*eqpos != '=')
 		return false;
-	*eqpos = '\0';
-	printfPQExpBuffer(grantee, "%s", buf);
 
 	/* grantor may be listed after / */
 	slpos = strchr(eqpos + 1, '/');
 	if (slpos)
 	{
-		*slpos = '\0';
-		printfPQExpBuffer(grantor, "%s", slpos + 1);
+		*slpos++ = '\0';
+		slpos = copyAclUserName(grantor, slpos);
+		if (*slpos != '\0')
+			return false;
 	}
 	else
 		resetPQExpBuffer(grantor);
@@ -457,6 +540,38 @@ parseAclItem(const char *item, const char *type, const char *name,
 	return true;
 }
 
+/*
+ * Transfer a user or group name starting at *input into the output buffer,
+ * dequoting if needed.  Returns a pointer to just past the input name.
+ * The name is taken to end at an unquoted '=' or end of string.
+ */
+static char *
+copyAclUserName(PQExpBuffer output, char *input)
+{
+	resetPQExpBuffer(output);
+	while (*input && *input != '=')
+	{
+		if (*input != '"')
+			appendPQExpBufferChar(output, *input++);
+		else
+		{
+			input++;
+			while (*input != '"')
+			{
+				if (*input == '\0')
+					return input; /* really a syntax error... */
+				/*
+				 * There is no quoting convention here, thus we can't cope
+				 * with usernames containing double quotes.  Keep this code
+				 * in sync with putid() in backend's acl.c.
+				 */
+				appendPQExpBufferChar(output, *input++);
+			}
+			input++;
+		}
+	}
+	return input;
+}
 
 /*
  * Append a privilege keyword to a keyword list, inserting comma if needed.