url.SetCharAt(' ',offset);
url.SetCharAt('\"',offset);
BookmarkParser::ParseBookmark(const nsString &aLine, const nsCOMPtr<nsIRDFContainer> &aContainer,				nsIRDFResource *nodeType, nsIRDFResource **bookmarkNode){	NS_PRECONDITION(aContainer != nsnull, "null ptr");	if (! aContainer)		return NS_ERROR_NULL_POINTER;	PRInt32 start = aLine.Find(kHREFEquals, PR_TRUE);	NS_ASSERTION(start >= 0, "no 'HREF=\"' string: how'd we get here?");	if (start < 0)		return NS_ERROR_UNEXPECTED;	// 1. Crop out the URL	// Skip past the first double-quote	start += (sizeof(kHREFEquals) - 1);	// ...and find the next so we can chop the URL.	PRInt32 end = aLine.FindChar(PRUnichar('"'), PR_FALSE,start);	NS_ASSERTION(end >= 0, "unterminated string");	if (end < 0)		return NS_ERROR_UNEXPECTED;	nsAutoString url;	aLine.Mid(url, start, end - start);	{		// Now do properly replace %22's (this is what 4.5 did, anyway...)		static const char kEscape22[] = "%22";		PRInt32 offset;		while ((offset = url.Find(kEscape22)) >= 0)		{			url.SetCharAt(' ',offset);			url.Cut(offset + 1, sizeof(kEscape22) - 2);		}	}	// XXX At this point, the URL may be relative. 4.5 called into	// netlib to make an absolute URL, and there was some magic	// "relative_URL" parameter that got sent down as well. We punt on	// that stuff.	// 2. Parse the name	start = aLine.FindChar(PRUnichar('>'), PR_FALSE,end + 1); // 'end' still points to the end of the URL	if (start < 0)	{		NS_WARNING("open anchor tag not terminated");		return NS_ERROR_UNEXPECTED;	}		nsAutoString name;	aLine.Right(name, aLine.Length() - (start + 1));	end = name.Find(kCloseAnchor, PR_TRUE);	if (end < 0)	{		NS_WARNING("anchor tag not terminated");		return NS_ERROR_UNEXPECTED;	}	name.Truncate(end);	Unescape(name);	// 3. Parse the target	nsAutoString target;	start = aLine.Find(kTargetEquals, PR_TRUE);	if (start >= 0) {		start += (sizeof(kTargetEquals) - 1);		end = aLine.FindChar(PRUnichar('"'), PR_FALSE,start);		aLine.Mid(target, start, end - start);	}	// 4. Parse the addition date	PRInt32 addDate = 0;	{		nsAutoString s;		ParseAttribute(aLine, kAddDateEquals, sizeof(kAddDateEquals) - 1, s);		if (s.Length() > 0) {			PRInt32 err;			addDate = s.ToInteger(&err); // ignored.		}	}	// 5. Parse the last visit date	PRInt32 lastVisitDate = 0;	{		nsAutoString s;		ParseAttribute(aLine, kLastVisitEquals, sizeof(kLastVisitEquals) - 1, s);		if (s.Length() > 0) {			PRInt32 err;			lastVisitDate = s.ToInteger(&err); // ignored.		}	}	// 6. Parse the last modified date	PRInt32 lastModifiedDate = 0;	{		nsAutoString s;		ParseAttribute(aLine, kLastModifiedEquals, sizeof(kLastModifiedEquals) - 1, s);		if (s.Length() > 0) {			PRInt32 err;			lastModifiedDate = s.ToInteger(&err); // ignored.		}	}	// 7. Parse the shortcut URL	nsAutoString	shortcut;	ParseAttribute(aLine, kShortcutURLEquals, sizeof(kShortcutURLEquals) -1, shortcut);	// 8. Parse the schedule	nsAutoString	schedule;	ParseAttribute(aLine, kScheduleEquals, sizeof(kScheduleEquals) -1, schedule);	// 9. Parse the last ping date	PRInt32 lastPingDate = 0;	{		nsAutoString s;		ParseAttribute(aLine, kLastPingEquals, sizeof(kLastPingEquals) - 1, s);		if (s.Length() > 0) {			PRInt32 err;			lastPingDate = s.ToInteger(&err); // ignored.		}	}	// 10. Parse the ping ETag	nsAutoString	pingETag;	ParseAttribute(aLine, kPingETagEquals, sizeof(kPingETagEquals) -1, pingETag);	// 11. Parse the ping LastMod date	nsAutoString	pingLastMod;	ParseAttribute(aLine, kPingLastModEquals, sizeof(kPingLastModEquals) -1, pingLastMod);	// 12. Parse the Ping Content Length	nsAutoString	pingContentLength;	ParseAttribute(aLine, kPingContentLenEquals, sizeof(kPingContentLenEquals) -1, pingContentLength);	// 13. Parse the Ping Status	nsAutoString	pingStatus;	ParseAttribute(aLine, kPingStatusEquals, sizeof(kPingStatusEquals) -1, pingStatus);	// Dunno. 4.5 did it, so will we.	if (!lastModifiedDate)		lastModifiedDate = lastVisitDate;	// There was some other cruft here to deal with aliases, but we ignore them thanks for RDF	nsresult rv = NS_ERROR_OUT_OF_MEMORY; // in case ToNewCString() fails	char *cURL = url.ToNewCString();	if (cURL)	{		char *cShortcutURL = shortcut.ToNewCString();	// Note: can be null		rv = AddBookmark(aContainer, cURL, name.GetUnicode(), addDate, lastVisitDate,				lastModifiedDate, cShortcutURL, nodeType, bookmarkNode);		if (NS_SUCCEEDED(rv))		{			// save schedule			if (schedule.Length() > 0)			{				nsCOMPtr<nsIRDFLiteral>	scheduleLiteral;				if (NS_SUCCEEDED(rv = gRDF->GetLiteral(schedule.GetUnicode(),								    getter_AddRefs(scheduleLiteral))))				{					rv = mDataSource->Assert(*bookmarkNode, kWEB_Schedule, scheduleLiteral, PR_TRUE);					if (rv != NS_RDF_ASSERTION_ACCEPTED)					{						NS_ERROR("unable to set bookmark schedule");					}				}				else				{					NS_ERROR("unable to get literal for bookmark schedule");				}			}			// last ping date			AssertTime(*bookmarkNode, kWEB_LastPingDate, lastPingDate);							// save ping ETag			if (pingETag.Length() > 0)			{				PRInt32		offset;				// Note: don't allow quotes in etag				while ((offset = pingETag.FindChar('\"')) >= 0)				{					pingETag.Cut(offset, 1);				}				nsCOMPtr<nsIRDFLiteral>	pingLiteral;				if (NS_SUCCEEDED(rv = gRDF->GetLiteral(pingETag.GetUnicode(),								    getter_AddRefs(pingLiteral))))				{					rv = mDataSource->Assert(*bookmarkNode, kWEB_LastPingETag, pingLiteral, PR_TRUE);					if (rv != NS_RDF_ASSERTION_ACCEPTED)					{						NS_ERROR("unable to set ping etag");					}				}				else				{					NS_ERROR("unable to get literal for ping etag");				}			}			// save ping Last Mod date			if (pingLastMod.Length() > 0)			{				nsCOMPtr<nsIRDFLiteral>	pingLastModLiteral;				if (NS_SUCCEEDED(rv = gRDF->GetLiteral(pingLastMod.GetUnicode(),								    getter_AddRefs(pingLastModLiteral))))				{					rv = mDataSource->Assert(*bookmarkNode, kWEB_LastPingModDate, pingLastModLiteral, PR_TRUE);					if (rv != NS_RDF_ASSERTION_ACCEPTED)					{						NS_ERROR("unable to set ping last mod");					}				}				else				{					NS_ERROR("unable to get literal for ping last mod");				}			}			// save ping Content Length date			if (pingContentLength.Length() > 0)			{				nsCOMPtr<nsIRDFLiteral>	pingContentLengthLiteral;				if (NS_SUCCEEDED(rv = gRDF->GetLiteral(pingContentLength.GetUnicode(),								    getter_AddRefs(pingContentLengthLiteral))))				{					rv = mDataSource->Assert(*bookmarkNode, kWEB_LastPingContentLen, pingContentLengthLiteral, PR_TRUE);					if (rv != NS_RDF_ASSERTION_ACCEPTED)					{						NS_ERROR("unable to set ping content length");					}				}				else				{					NS_ERROR("unable to get literal for ping content length");				}			}			// save ping status			if (pingStatus.Length() > 0)			{				nsCOMPtr<nsIRDFLiteral>	pingStatusLiteral;				if (NS_SUCCEEDED(rv = gRDF->GetLiteral(pingStatus.GetUnicode(),								    getter_AddRefs(pingStatusLiteral))))				{					rv = mDataSource->Assert(*bookmarkNode, kWEB_Status, pingStatusLiteral, PR_TRUE);					if (rv != NS_RDF_ASSERTION_ACCEPTED)					{						NS_ERROR("unable to set ping status");					}				}				else				{					NS_ERROR("unable to get literal for ping status");				}			}		}		if (cShortcutURL)		{			nsCRT::free(cShortcutURL);		}		nsCRT::free(cURL);	}	return(rv);}