Bookmarking things: xml_loc and xml_getloc

Previous: xml_replace and xml_replacecontent: Replacing an element with another. ] [ Top: index ] [ Next: Working with attributes: xml_set and xml_attrval ]

The XML bookmarking feature of the XMLAPI may well be unique among XML libraries. I haven't done any real searching, but I've never heard of anything like it. What it allows us to do is take an XML snippet and derive a locator for it, then use that locator at a later date to find the snippet again. The advantage over explicit naming is that we needn't think of naming things in advance; the disadvantage is that the locator is naturally position-dependent, so that if the XML document changes we may end up finding a different snippet than the one we started with. Then again, maybe that's not such a horrible problem; if our application says we need the first data item, then a locator ".data(0)" is exactly what we need.

There are two forms to the location function, xml_loc and xml_locf. The first takes a literal locator, while the second is a printf-style formatted function. The only formatting directives it recognizes, however, are %s for strings and %d for integers. Anything else will be ignored.

There are also two forms for the locator finding function: xml_getloc and xml_getlocbuf. The first takes an explicit buffer for the locator to be retrieved, while the second builds the buffer using malloc and returns it. The result must be freed when the caller is done with it.
 
XMLAPI XML * xml_loc (XML * start, const char * loc)
{
   char * mark;
   const char * attrval;
   char piece[64];
   int i;
   int count;

   if (!loc) return (start);
   if (!*loc) return (start);

   if (*loc == '.') return (xml_loc (xml_first (start), loc + 1));

   while (start && start->name == NULL) start = xml_next (start);
   if (!start) return (NULL);

   while (*loc == ' ') loc++;
   i = 0;
   while (*loc && *loc != '.') piece[i++] = *loc++;
   piece[i] = '\0';
   if (*loc) loc++;
   while (*loc == ' ') loc++;

   mark = strchr (piece, ']');
   if (mark) *mark = '\0';
   mark = strchr (piece, '(');
   if (mark) {
      *mark++ = '\0';
      count = atoi (mark);
      mark = NULL;
   } else {
      count = 0;
      mark = strchr (piece, '[');
      if (mark) {
         *mark++ = '\0';
      }
   }

   while (start) {
      if (start->name == NULL) {
         start = xml_next (start);
         continue;
      }
      if (strcmp (start->name, piece)) {
         start = xml_next (start);
         continue;
      }
      if (count) {
         count --;
         start = xml_next (start);
         continue;
      }
      if (!mark) {
         if (*loc) return (xml_loc (xml_first (start), loc));
         return (start);
      }
      attrval = xml_attrval(start, "id");
      if (attrval) {
         if (strcmp (attrval, mark)) {
            start = xml_next (start);
            continue;
         }
         if (*loc) return (xml_loc (xml_first(start), loc));
         return (start);
      }
      attrval = xml_attrval(start, "name");
      if (attrval) {
         if (strcmp (attrval, mark)) {
            start = xml_next (start);
            continue;
         }
         if (*loc) return (xml_loc (xml_first(start), loc));
         return (start);
      }
   }
   return (NULL);
}

XMLAPI XML * xml_locf (XML *start, const char * loc, ...)
{
   va_list args;
   char * locator;
   XML * found;

   va_start (args, loc);
   locator = _xml_string_format (loc, args);
   va_end (args);

   found = xml_loc (start, locator);
   FREE (locator);
   return (found);
}
Building our locator is recursive. We build our parent's locator, append a dot, and qualify it. We've got two flavors of this function; one takes a buffer and size, and the other builds the buffer as it goes using _xml_string_tackon
 
XMLAPI void xml_getloc (XML * xml, char *loc, int len)
{
   int s;
   int count;
   XML * sib;
   if (xml->parent != NULL) {
      xml_getloc (xml->parent, loc, len);
   } else {
      *loc = '\0';
   }
   s = strlen (loc);
   if (s > 0 && s < len-1) { strcat (loc, "."); s++; }
   len -= s;
   loc += s;
   if (strlen(xml->name) < len) {
      strcpy (loc, xml->name);
   } else {
      strncpy (loc, xml->name, len-1);
      loc[len-1] = '\0';
   }
   if (xml->parent == NULL) return;
   sib = xml_first(xml->parent);
   count = 0;
   while (sib != xml && sib != NULL) {
      if (sib->name != NULL) {
         if (!strcmp (sib->name, xml->name)) count ++;
      }
      sib = xml_next(sib);
   }
   if (count > 0 && s > 4) {
      strcat (loc, "(");
      sprintf (loc + strlen(loc), "%d", count);
      strcat (loc, ")");
   }
}
The build-as-you-go version needs a helper function to hide its recursion baggage.
 
char * _xml_getlocbuf_buf (XML * xml, char *buffer, int *cursize, int *curptr)
{
   int s;
   int count;
   XML * sib;
   char countbuf[sizeof(int) * 3 + 1];

   if (xml->parent != NULL) {
      _xml_getlocbuf_buf (xml->parent, buffer, cursize, curptr);
      buffer = _xml_string_tackon (buffer, cursize, curptr, ".");
   }

   buffer = _xml_string_tackon (buffer, cursize, curptr, xml->name);

   if (xml->parent == NULL) return (buffer);

   sib = xml_first(xml->parent);
   count = 0;
   while (sib != xml && sib != NULL) {
      if (sib->name != NULL) {
         if (!strcmp (sib->name, xml->name)) count ++;
      }
      sib = xml_next(sib);
   }
   if (count > 0) {
      buffer = _xml_string_tackon (buffer, cursize, curptr, "(");
      sprintf (countbuf, "%d", count);
      buffer = _xml_string_tackon (buffer, cursize, curptr, countbuf);
      buffer = _xml_string_tackon (buffer, cursize, curptr, ")");
   }

   return (buffer);
}

XMLAPI char * xml_getlocbuf (XML * xml)
{
   char * buf = (char *) MALLOC (256);
   int  cursize = 256;
   int  curptr = 0;

   *buf = '\0';
   return (_xml_getlocbuf_buf (xml, buf, &cursize, &curptr));
}
Previous: xml_replace and xml_replacecontent: Replacing an element with another. ] [ Top: index ] [ Next: Working with attributes: xml_set and xml_attrval ]


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