Working with attributes: xml_set and xml_attrval

Previous: Bookmarking things: xml_loc and xml_getloc ] [ Top: index ] [ Next: xml_create: Creating an empty element ]

Setting an attribute is a little complicated. If the attribute is already represented in the element's attribute list, then we free the old value, allocate space for a copy of the new value, and copy it. Otherwise we allocate a new attribute holder and copy both name and value into it.

This function comes in three varieties for your viewing pleasure: xml_set just uses strdup to make a copy of the value you give it, xml_setf treats its second parameter as a printf-style format and builds the value, and xml_setnum takes an integer and formats it into a string. As you can imagine, I did xml_setnum before I even thought of xml_setf.

Oh -- another variant. The xml_set_nodup function can be used to take over management of a malloc'd string buffer. It works exactly the same as xml_set except that the string is not duplicated. Useful for when you save locations obtained from xml_getlocbuf, for instance.

July 18, 2001: Yet another variant: xml_attrcat tacks strings onto the end of already-set attribute values. It uses valsize to grow the allocated buffer accordingly. This is support for a better version of wftk_value_interpret, by the way.

May 13, 2002: Added xml_unset.

Sep 30, 2002: Realized that freeing attr->value before referring to value is stupid if value is or contains attr->value... This was an awfully subtle bug. Makes me wonder how many other really stupid things I'm doing.
 
XMLAPI void xml_set (XML * xml, const char * name, const char * value)
{
   ATTR * attr;
   char * holder;

   if (!xml) return;
   if (!value) value = "";

   attr = xml->attrs;
   while (attr) {
      if (!strcmp (attr->name, name)) break;
      attr = attr->next;
   }

   if (attr) {
      holder = xml_strdup (value);
      FREE ((void *) (attr->value));
      attr->value = holder;
      attr->valsize = strlen (value) + 1;
      return;
   }

   if (xml->attrs == NULL) {
      attr = (ATTR *) MALLOC (sizeof (struct _attr));
      xml->attrs = attr;
   } else {
      attr = xml->attrs;
      while (attr->next) attr = attr->next;
      attr->next = (ATTR *) MALLOC (sizeof (struct _attr));
      attr = attr->next;
   }

   attr->next = NULL;
   attr->name = xml_strdup (name);
   if (value) {
      attr->value = xml_strdup (value);
      attr->valsize = strlen (value) + 1;
   } else {
      attr->value = xml_strdup ("");
      attr->valsize = 1;
   }
}
XMLAPI void xml_setf (XML * xml, const char *name, const char *format, ...)
{
   ATTR * attr;
   va_list args;
   char * value;

   if (!xml) return;
   va_start (args, format);
   value = xml_string_formatv (format, args);
   va_end (args);

   attr = xml->attrs;
   while (attr) {
      if (!strcmp (attr->name, name)) break;
      attr = attr->next;
   }

   if (attr) {
      FREE ((void *) (attr->value));
      attr->value = value;
      attr->valsize = 0;
      return;
   }

   if (xml->attrs == NULL) {
      attr = (ATTR *) MALLOC (sizeof (struct _attr));
      xml->attrs = attr;
   } else {
      attr = xml->attrs;
      while (attr->next) attr = attr->next;
      attr->next = (ATTR *) MALLOC (sizeof (struct _attr));
      attr = attr->next;
   }

   attr->next = NULL;
   attr->name = xml_strdup (name);
   attr->value = value;
   attr->valsize = 0;
}
XMLAPI void xml_setnum (XML * xml, const char *attr, int number)
{
   char buf[sizeof(number) * 3 + 1];
   sprintf (buf, "%d", number);
   xml_set (xml, attr, buf);
}
XMLAPI void xml_set_nodup (XML * xml, const char * name, char * value)
{
   ATTR * attr;

   if (!xml) return;
   if (!value) {
      xml_set (xml, name, "");
      return;
   }

   attr = xml->attrs;
   while (attr) {
      if (!strcmp (attr->name, name)) break;
      attr = attr->next;
   }

   if (attr) {
      FREE ((void *) (attr->value));
      attr->value = value;
      attr->valsize = 0;
      return;
   }

   if (xml->attrs == NULL) {
      attr = (ATTR *) MALLOC (sizeof (struct _attr));
      xml->attrs = attr;
   } else {
      attr = xml->attrs;
      while (attr->next) attr = attr->next;
      attr->next = (ATTR *) MALLOC (sizeof (struct _attr));
      attr = attr->next;
   }

   attr->next = NULL;
   attr->name = xml_strdup (name);
   if (value) {
      attr->value = value;
      attr->valsize = 0;
   } else {
      attr->value = xml_strdup (value);
      attr->valsize = 1;
   }
}
XMLAPI void xml_attrcat (XML * xml, const char * name, const char * value)
{
   ATTR * attr;
   int len;

   attr = xml->attrs;
   while (attr) {
      if (!strcmp (attr->name, name)) break;
      attr = attr->next;
   }

   if (!attr) xml_set (xml, name, value);
   else {
      if (value) {
         len = strlen (attr->value) + strlen (value) + 1;
         if (len > attr->valsize) {
            while (attr->valsize < len) attr->valsize += 256;
            attr->value = (char *) REALLOC (attr->value, attr->valsize);
         }
         strcat (attr->value, value);
      }
   }
}
XMLAPI void xml_attrncat (XML * xml, const char * name, const char * value, int length)
{
   ATTR * attr;
   int len;

   attr = xml->attrs;
   while (attr) {
      if (!strcmp (attr->name, name)) break;
      attr = attr->next;
   }

   if (!attr) {
      xml_set (xml, name, "");
      attr = xml->attrs;
      while (attr) {
         if (!strcmp (attr->name, name)) break;
         attr = attr->next;
      }
   }
   if (!attr) return; /* This is probably a dumb way to handle low-mem situations, but... */

   if (value) {
      len = strlen (attr->value) + length + 1;
      if (len > attr->valsize) {
         while (attr->valsize < len) attr->valsize += 256;
         attr->value = (char *) REALLOC (attr->value, attr->valsize);
      }
      strncat (attr->value, value, length);
   }
}
XMLAPI void xml_unset (XML * xml, const char * name)
{
   ATTR * prev;
   ATTR * attr;

   attr = xml->attrs;
   prev = NULL;
   while (attr) {
      if (attr->name && !strcmp (attr->name, name)) break;
      prev = attr;
      attr = attr->next;
   }

   if (!attr) return;

   if (prev) prev->next = attr->next;
   else      xml->attrs = attr->next;

   FREE ((void *) (attr->value));
   FREE ((void *) (attr));
}
Retrieving a value, on the other hand, is rather simple. So simple, in fact, that we only have two different ways to do it (instead of six or more).

May 26, 2002: A quick and easy pointer into the text value for text chunks. I can't quite believe I've omitted this before.
 
XMLAPI const char * xml_attrval (XML * element,const char * name)
{
   ATTR * attr;

   if (!element) return ("");
   attr = element->attrs;
   while (attr) {
      if (!strcmp (attr->name, name)) return (attr->value);
      attr = attr->next;
   }
   return ("");
}
XMLAPI int xml_attrvalnum (XML * element, const char * name)
{
   return (atoi (xml_attrval (element, name)));
}
XMLAPI const char * xml_textval (XML * element)
{
   ATTR * attr;

   if (!element) return ("");
   if (xml_is_element (element)) return ("");
   attr = element->attrs;
   if (attr) {
      if (attr->value) return (attr->value);
   }
   return ("");
}
Previous: Bookmarking things: xml_loc and xml_getloc ] [ Top: index ] [ Next: xml_create: Creating an empty element ]


This code and documentation are released under the terms of the GNU license. They are copyright (c) 2000-2003, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. This presentation was created using LPML.