/* mRss - Copyright (C) 2005-2007 baku - Andrea Marchesini
 *                                     <baku@ippolita.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#else
#error Use configure; make; make install
#endif

#include "mrss.h"
#include "mrss_internal.h"

static void __mrss_write_string(void (*func)(void *, char *, ...), void *obj,
                                char *str) {
  int i;
  int len;
  char buf[1024];
  int j;

#define __NXML_CHECK_BUF                                                       \
  if (j == sizeof(buf) - 1) {                                                  \
    buf[j] = 0;                                                                \
    func(obj, "%s", buf);                                                      \
    j = 0;                                                                     \
  }

  if (!str)
    return;

  len = strlen(str);

  for (j = i = 0; i < len; i++) {
    if (str[i] == '\r')
      continue;

    else if (str[i] == '<') {
      buf[j++] = '&';
      __NXML_CHECK_BUF;
      buf[j++] = 'l';
      __NXML_CHECK_BUF;
      buf[j++] = 't';
      __NXML_CHECK_BUF;
      buf[j++] = ';';
      __NXML_CHECK_BUF;
    }

    else if (str[i] == '>') {
      buf[j++] = '&';
      __NXML_CHECK_BUF;
      buf[j++] = 'g';
      __NXML_CHECK_BUF;
      buf[j++] = 't';
      __NXML_CHECK_BUF;
      buf[j++] = ';';
      __NXML_CHECK_BUF;
    }

    else if (str[i] == '&') {
      buf[j++] = '&';
      __NXML_CHECK_BUF;
      buf[j++] = 'a';
      __NXML_CHECK_BUF;
      buf[j++] = 'm';
      __NXML_CHECK_BUF;
      buf[j++] = 'p';
      __NXML_CHECK_BUF;
      buf[j++] = ';';
      __NXML_CHECK_BUF;
    }

    else if (str[i] == '\'') {
      buf[j++] = '&';
      __NXML_CHECK_BUF;
      buf[j++] = 'a';
      __NXML_CHECK_BUF;
      buf[j++] = 'p';
      __NXML_CHECK_BUF;
      buf[j++] = 'o';
      __NXML_CHECK_BUF;
      buf[j++] = 's';
      __NXML_CHECK_BUF;
      buf[j++] = ';';
      __NXML_CHECK_BUF;
    }

    else if (str[i] == '\"') {
      buf[j++] = '&';
      __NXML_CHECK_BUF;
      buf[j++] = 'q';
      __NXML_CHECK_BUF;
      buf[j++] = 'u';
      __NXML_CHECK_BUF;
      buf[j++] = 'o';
      __NXML_CHECK_BUF;
      buf[j++] = 't';
      __NXML_CHECK_BUF;
      buf[j++] = ';';
      __NXML_CHECK_BUF;
    }

    else {
      buf[j++] = str[i];
      __NXML_CHECK_BUF;
    }
  }

  if (j) {
    buf[j] = 0;
    func(obj, "%s", buf);
    j = 0;
  }
}

static void __mrss_write_real_image(mrss_t *mrss,
                                    void (*func)(void *, char *, ...),
                                    void *obj) {
  if (mrss->image_title || mrss->image_url || mrss->image_link ||
      mrss->image_width || mrss->image_height || mrss->description) {

    func(obj, "  %s<image>\n", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");

    if (mrss->image_title) {
      func(obj, "    %s<title>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->image_title);
      func(obj, "</title>\n");
    }

    if (mrss->image_url) {
      func(obj, "      %s<url>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->image_url);
      func(obj, "</url>\n");
    }

    if (mrss->image_link) {
      func(obj, "      %s<link>",
           mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->image_link);
      func(obj, "</link>\n");
    }

    if (mrss->version != MRSS_VERSION_1_0) {
      if (mrss->image_width)
        func(obj, "      <width>%d</width>\n", mrss->image_width);

      if (mrss->image_height)
        func(obj, "      <height>%d</height>\n", mrss->image_height);

      if (mrss->image_description) {
        func(obj, "      <description>");
        __mrss_write_string(func, obj, mrss->image_description);
        func(obj, "</description>\n");
      }
    }

    func(obj, "  %s</image>\n", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
  }
}

static void __mrss_write_real_textinput(mrss_t *mrss,
                                        void (*func)(void *, char *, ...),
                                        void *obj) {
  if (mrss->textinput_title || mrss->textinput_description ||
      mrss->textinput_name || mrss->textinput_link) {

    func(obj, "  %s<textinput>\n",
         mrss->version == MRSS_VERSION_1_0 ? "" : "  ");

    if (mrss->textinput_title) {
      func(obj, "    %s<title>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->textinput_title);
      func(obj, "</title>\n");
    }

    if (mrss->textinput_description) {
      func(obj, "    %s<description>",
           mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->textinput_description);
      func(obj, "</description>\n");
    }

    if (mrss->textinput_name) {
      func(obj, "    %s<name>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->textinput_name);
      func(obj, "</name>\n");
    }

    if (mrss->textinput_link) {
      func(obj, "    %s<link>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, mrss->textinput_link);
      func(obj, "</link>\n");
    }

    func(obj, "    </textinput>\n");
  }
}

static void __mrss_write_real_cloud(mrss_t *mrss,
                                    void (*func)(void *, char *, ...),
                                    void *obj) {
  if ((mrss->version == MRSS_VERSION_0_92 ||
       mrss->version == MRSS_VERSION_2_0) &&
      (mrss->cloud || mrss->cloud_domain || mrss->cloud_port ||
       mrss->cloud_path || mrss->cloud_registerProcedure ||
       mrss->cloud_protocol)) {
    func(obj, "    <cloud");

    if (mrss->cloud_domain) {
      func(obj, " domain=\"");
      __mrss_write_string(func, obj, mrss->cloud_domain);
      func(obj, "\"");
    }

    if (mrss->cloud_port)
      func(obj, " port=\"%d\"", mrss->cloud_port);

    if (mrss->cloud_path) {
      func(obj, " path=\"");
      __mrss_write_string(func, obj, mrss->cloud_path);
      func(obj, "\"");
    }

    if (mrss->cloud_registerProcedure) {
      func(obj, " registerProcedure=\"");
      __mrss_write_string(func, obj, mrss->cloud_registerProcedure);
      func(obj, "\"");
    }

    if (mrss->cloud_protocol) {
      func(obj, " protocol=\"");
      __mrss_write_string(func, obj, mrss->cloud_protocol);
      func(obj, "\"");
    }

    if (mrss->cloud)
      func(obj, ">%s</cloud>\n", mrss->cloud);
    else
      func(obj, " />\n", mrss->cloud);
  }
}

static void __mrss_write_real_skipHours(mrss_t *mrss,
                                        void (*func)(void *, char *, ...),
                                        void *obj) {
  if (mrss->skipHours && mrss->version != MRSS_VERSION_1_0) {
    mrss_hour_t *hour;

    func(obj, "    <skipHours>\n");

    hour = mrss->skipHours;
    while (hour) {
      func(obj, "      <hour>");
      __mrss_write_string(func, obj, hour->hour);
      func(obj, "</hour>\n");

      hour = hour->next;
    }

    func(obj, "    </skipHours>\n");
  }
}

static void __mrss_write_real_skipDays(mrss_t *mrss,
                                       void (*func)(void *, char *, ...),
                                       void *obj) {
  if (mrss->skipDays && mrss->version != MRSS_VERSION_1_0) {
    mrss_day_t *day;

    func(obj, "    <skipDays>\n");

    day = mrss->skipDays;
    while (day) {
      func(obj, "      <day>");
      __mrss_write_string(func, obj, day->day);
      func(obj, "</day>\n");
      day = day->next;
    }

    func(obj, "    </skipDays>\n");
  }
}

static void __mrss_write_real_category(mrss_t *mrss, mrss_category_t *category,
                                       void (*func)(void *, char *, ...),
                                       void *obj) {
  if ((mrss->version == MRSS_VERSION_0_92 ||
       mrss->version == MRSS_VERSION_2_0) &&
      mrss->category) {
    while (category) {
      func(obj, "    <category");

      if (category->domain) {
        func(obj, " domain=\"");
        __mrss_write_string(func, obj, category->domain);
        func(obj, "\"");
      }

      func(obj, ">");
      __mrss_write_string(func, obj, category->category);
      func(obj, "</category>\n");

      category = category->next;
    }
  }
}

static void __mrss_write_real_tag(mrss_tag_t *tag,
                                  void (*func)(void *, char *, ...), void *obj,
                                  int index) {
  mrss_attribute_t *attribute;

  struct mrss_ns_t {
    char *ns;
    struct mrss_ns_t *next;
  } *namespaces = NULL, *nstmp;

  int i;

  while (tag) {
    if (tag->ns) {
      if (!(namespaces = calloc(1, sizeof(struct mrss_ns_t))))
        return;

      namespaces->ns = tag->ns;
    }

    for (attribute = tag->attributes; attribute; attribute = attribute->next) {
      if (attribute->ns) {
        for (nstmp = namespaces; nstmp; nstmp = nstmp->next)
          if (!strcmp(nstmp->ns, attribute->ns))
            break;

        if (nstmp)
          continue;

        if (!(nstmp = calloc(1, sizeof(struct mrss_ns_t))))
          return;

        nstmp->ns = attribute->ns;
        nstmp->next = namespaces->next;
        namespaces->next = nstmp;
      }
    }

    for (i = 0; i < index; i++)
      func(obj, "  ");

    if (tag->ns)
      func(obj, "  <ns0:%s", tag->name);
    else
      func(obj, "  <%s", tag->name);

    for (i = 0, nstmp = namespaces; nstmp; nstmp = nstmp->next)
      func(obj, " xmlns:ns%d=\"%s\"", i++, nstmp->ns);

    for (attribute = tag->attributes; attribute; attribute = attribute->next) {
      if (attribute->ns) {
        for (i = 0, nstmp = namespaces; nstmp; i++, nstmp = nstmp->next) {
          if (!strcmp(nstmp->ns, attribute->ns)) {
            func(obj, " ns%d:%s=\"%s\"", i, attribute->name, attribute->value);
            break;
          }
        }
      } else
        func(obj, " %s=\"%s\"", attribute->name, attribute->value);
    }

    if (tag->value) {
      func(obj, ">");
      __mrss_write_string(func, obj, tag->value);
    }

    if (tag->children) {
      if (!tag->value)
        func(obj, ">\n");

      func(obj, "\n");
      __mrss_write_real_tag(tag->children, func, obj, index + 1);
    }

    if (tag->children || tag->value) {
      if (tag->children)
        for (i = 0; i < index; i++)
          func(obj, "  ");

      if (tag->ns)
        func(obj, "</ns0:%s>\n", tag->name);
      else
        func(obj, "</%s>\n", tag->name);

    } else
      func(obj, "/>\n");

    while (namespaces) {
      nstmp = namespaces;
      namespaces = namespaces->next;
      free(nstmp);
    }

    tag = tag->next;
  }
}

static void __mrss_write_real_item(mrss_t *mrss,
                                   void (*func)(void *, char *, ...),
                                   void *obj) {
  mrss_item_t *item = mrss->item;

  while (item) {
    func(obj, "  %s<item>\n", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");

    if (item->title) {
      func(obj, "    %s<title>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, item->title);
      func(obj, "</title>\n");
    }

    if (item->link) {
      func(obj, "    %s<link>", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, item->link);
      func(obj, "</link>\n");
    }

    if (item->description) {
      func(obj, "    %s<description>",
           mrss->version == MRSS_VERSION_1_0 ? "" : "  ");
      __mrss_write_string(func, obj, item->description);
      func(obj, "</description>\n");
    }

    if (mrss->version == MRSS_VERSION_2_0) {
      if (item->author) {
        func(obj, "      <author>");
        __mrss_write_string(func, obj, item->author);
        func(obj, "</author>\n");
      }

      if (item->comments) {
        func(obj, "      <comments>");
        __mrss_write_string(func, obj, item->comments);
        func(obj, "</comments>\n");
      }

      if (item->pubDate) {
        func(obj, "      <pubDate>");
        __mrss_write_string(func, obj, item->pubDate);
        func(obj, "</pubDate>\n");
      }

      if (item->guid) {
        func(obj, "      <guid isPermaLink=\"%s\">",
             item->guid_isPermaLink ? "true" : "false");
        __mrss_write_string(func, obj, item->guid);
        func(obj, "</guid>\n");
      }
    }

    if (mrss->version == MRSS_VERSION_2_0 ||
        mrss->version == MRSS_VERSION_0_92) {
      if (item->source || item->source_url) {
        func(obj, "      <source");

        if (item->source_url) {
          func(obj, " url=\"");
          __mrss_write_string(func, obj, item->source_url);
          func(obj, "\"");
        }

        if (item->source) {
          func(obj, ">");
          __mrss_write_string(func, obj, item->source);
          func(obj, "</source>\n");
        } else
          func(obj, " />\n");
      }
    }

    if (item->enclosure || item->enclosure_length || item->enclosure_type) {
      func(obj, "      <enclosure");

      if (item->enclosure_url) {
        func(obj, " url=\"");
        __mrss_write_string(func, obj, item->enclosure_url);
        func(obj, "\"");
      }

      if (item->enclosure_length)
        func(obj, " length=\"%d\"", item->enclosure_length);

      if (item->enclosure_type) {
        func(obj, " type=\"");
        __mrss_write_string(func, obj, item->enclosure_type);
        func(obj, "\"");
      }

      if (item->enclosure) {
        func(obj, ">");
        __mrss_write_string(func, obj, item->enclosure);
        func(obj, "</enclosure>\n");
      } else
        func(obj, " />\n");
    }

    __mrss_write_real_category(mrss, item->category, func, obj);

    if (item->other_tags)
      __mrss_write_real_tag(item->other_tags, func, obj, 1);

    func(obj, "  %s</item>\n", mrss->version == MRSS_VERSION_1_0 ? "" : "  ");

    item = item->next;
  }
}

static void __mrss_write_real_atom_category(mrss_category_t *category,
                                            void (*func)(void *, char *, ...),
                                            void *obj) {
  while (category) {
    func(obj, "    <category");

    if (category->domain) {
      func(obj, " scheme=\"");
      __mrss_write_string(func, obj, category->domain);
      func(obj, "\"");
    }

    if (category->category) {
      func(obj, " term=\"");
      __mrss_write_string(func, obj, category->category);
      func(obj, "\"");
    }

    if (category->label) {
      func(obj, " label=\"");
      __mrss_write_string(func, obj, category->label);
      func(obj, "\"");
    }

    func(obj, "/>\n");

    category = category->next;
  }
}

static void __mrss_write_real_atom_entry(mrss_t *mrss,
                                         void (*func)(void *, char *, ...),
                                         void *obj) {
  mrss_item_t *item = mrss->item;

  while (item) {
    func(obj, "  <entry>\n");

    if (item->title) {
      func(obj, "    <title");

      if (item->title_type) {
        func(obj, " type=\"");
        __mrss_write_string(func, obj, item->title_type);
        func(obj, "\"");
      }

      func(obj, ">");
      __mrss_write_string(func, obj, item->title);
      func(obj, "</title>\n");
    }

    if (item->link) {
      func(obj, "    <link>");
      __mrss_write_string(func, obj, item->link);
      func(obj, "</link>\n");
    }

    if (item->description) {
      func(obj, "    <summary");

      if (item->description_type) {
        func(obj, " type=\"");
        __mrss_write_string(func, obj, item->description_type);
        func(obj, "\"");
      }

      func(obj, ">");
      __mrss_write_string(func, obj, item->description);
      func(obj, "</summary>\n");
    }

    if (item->copyright) {
      func(obj, "    <rights>");
      __mrss_write_string(func, obj, item->copyright);
      func(obj, "</rights>\n");
    }

    if (item->author) {
      func(obj, "    <author>\n");
      func(obj, "      <name>");
      __mrss_write_string(func, obj, item->author);
      func(obj, "</name>\n");

      if (item->author_email) {
        func(obj, "      <email>");
        __mrss_write_string(func, obj, item->author_email);
        func(obj, "</email>\n");
      }

      if (item->author_uri) {
        func(obj, "      <uri>");
        __mrss_write_string(func, obj, item->author_uri);
        func(obj, "</uri>\n");
      }

      func(obj, "    </author>\n");
    }

    if (item->contributor) {
      func(obj, "    <contributor>\n");
      func(obj, "      <name>");
      __mrss_write_string(func, obj, item->contributor);
      func(obj, "</name>\n");

      if (item->contributor_email) {
        func(obj, "      <email>");
        __mrss_write_string(func, obj, item->contributor_email);
        func(obj, "</email>\n");
      }

      if (item->contributor_uri) {
        func(obj, "      <uri>");
        __mrss_write_string(func, obj, item->contributor_uri);
        func(obj, "</uri>\n");
      }

      func(obj, "    </contributor>\n");
    }

    /* TODO */
    if (mrss->pubDate) {
      if (mrss->version == MRSS_VERSION_ATOM_1_0) {
        func(obj, "  <published>");
        __mrss_write_string(func, obj, item->pubDate);
        func(obj, "</published>\n");
      } else {
        func(obj, "  <issued>");
        __mrss_write_string(func, obj, item->pubDate);
        func(obj, "</issued>\n");
      }
    }

    if (item->guid) {
      func(obj, "    <id>");
      __mrss_write_string(func, obj, item->guid);
      func(obj, "</id>\n");
    }

    __mrss_write_real_atom_category(item->category, func, obj);

    if (item->other_tags)
      __mrss_write_real_tag(item->other_tags, func, obj, 1);

    func(obj, "  </entry>\n");

    item = item->next;
  }
}

static mrss_error_t
__mrss_write_atom(mrss_t *mrss, void (*func)(void *, char *, ...), void *obj) {
  func(obj, "<feed xmlns=\"http://www.w3.org/2005/Atom\"");

  if (mrss->language)
    func(obj, " xml:lang=\"%s\"", mrss->language);

  if (mrss->version == MRSS_VERSION_ATOM_0_3)
    func(obj, " version=\"0.3\"");

  func(obj, ">\n");

  func(obj, "  <title");

  if (mrss->title_type) {
    func(obj, " type=\"");
    __mrss_write_string(func, obj, mrss->title_type);
    func(obj, "\"");
  }

  func(obj, ">");
  __mrss_write_string(func, obj, mrss->title);
  func(obj, "</title>\n");

  if (mrss->description) {
    if (mrss->version == MRSS_VERSION_ATOM_1_0) {
      func(obj, "  <subtitle");

      if (mrss->description_type) {
        func(obj, " type=\"");
        __mrss_write_string(func, obj, mrss->description_type);
        func(obj, "\"");
      }

      func(obj, ">");
      __mrss_write_string(func, obj, mrss->description);
      func(obj, "</subtitle>\n");
    } else {
      func(obj, "  <tagline");

      if (mrss->description_type) {
        func(obj, " type=\"");
        __mrss_write_string(func, obj, mrss->description_type);
        func(obj, "\"");
      }

      func(obj, ">");
      __mrss_write_string(func, obj, mrss->description);
      func(obj, "</tagline>\n");
    }
  }

  if (mrss->link) {
    func(obj, "  <link href=\"");
    __mrss_write_string(func, obj, mrss->link);
    func(obj, "\"/>\n");
  }

  if (mrss->id) {
    func(obj, "  <id>");
    __mrss_write_string(func, obj, mrss->id);
    func(obj, "</id>\n");
  }

  if (mrss->copyright) {
    func(obj, "  <rights");

    if (mrss->copyright_type) {
      func(obj, " type=\"");
      __mrss_write_string(func, obj, mrss->copyright_type);
      func(obj, "\"");
    }

    func(obj, ">");
    __mrss_write_string(func, obj, mrss->copyright);
    func(obj, "</rights>\n");
  }

  /* TODO */
  if (mrss->lastBuildDate) {
    func(obj, "  <updated>");
    __mrss_write_string(func, obj, mrss->lastBuildDate);
    func(obj, "</updated>\n");
  }

  if (mrss->managingeditor) {
    func(obj, "  <author>\n");
    func(obj, "    <name>");
    __mrss_write_string(func, obj, mrss->managingeditor);
    func(obj, "</name>\n");

    if (mrss->managingeditor_email) {
      func(obj, "    <email>");
      __mrss_write_string(func, obj, mrss->managingeditor_email);
      func(obj, "</email>\n");
    }

    if (mrss->managingeditor_uri) {
      func(obj, "    <uri>");
      __mrss_write_string(func, obj, mrss->managingeditor_uri);
      func(obj, "</uri>\n");
    }

    func(obj, "  </author>\n");
  }

  if (mrss->generator) {
    func(obj, "  <generator");

    if (mrss->generator_uri) {
      func(obj, " uri=\"");
      __mrss_write_string(func, obj, mrss->generator_uri);
      func(obj, "\"");
    }

    if (mrss->generator_version) {
      func(obj, " version=\"");
      __mrss_write_string(func, obj, mrss->generator_version);
      func(obj, "\"");
    }

    func(obj, ">");
    __mrss_write_string(func, obj, mrss->generator);
    func(obj, "</generator>\n");
  }

  if (mrss->image_url) {
    func(obj, "  <icon>");
    __mrss_write_string(func, obj, mrss->image_url);
    func(obj, "</icon>\n");
  }

  if (mrss->image_logo) {
    func(obj, "  <logo>");
    __mrss_write_string(func, obj, mrss->image_logo);
    func(obj, "</logo>\n");
  }

  __mrss_write_real_atom_category(mrss->category, func, obj);

  __mrss_write_real_atom_entry(mrss, func, obj);

  if (mrss->other_tags)
    __mrss_write_real_tag(mrss->other_tags, func, obj, 0);

  func(obj, "</feed>\n");

  return MRSS_OK;
}

static mrss_error_t
__mrss_write_real(mrss_t *mrss, void (*func)(void *, char *, ...), void *obj) {
  func(obj, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");

  if (mrss->version == MRSS_VERSION_1_0)
    func(obj, "<rdf:RDF\n"
              "  xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"
              "  xmlns=\"http://purl.org/rss/1.0/\">\n");

  else if (mrss->version == MRSS_VERSION_ATOM_1_0 ||
           mrss->version == MRSS_VERSION_ATOM_0_3)
    return __mrss_write_atom(mrss, func, obj);

  else {
    func(obj, "<rss version=\"");

    switch (mrss->version) {
    case MRSS_VERSION_0_91:
      func(obj, "0.91");
      break;

    case MRSS_VERSION_0_92:
      func(obj, "0.92");
      break;

    case MRSS_VERSION_2_0:
      func(obj, "2.0");
      break;

    default:
      break;
    }

    func(obj, "\">\n");
  }

  if (mrss->version == MRSS_VERSION_1_0 && mrss->about) {
    func(obj, "  <channel rdf:about=\"");
    __mrss_write_string(func, obj, mrss->about);
    func(obj, "\">\n");
  }

  else
    func(obj, "  <channel>\n");

  if (mrss->title) {
    func(obj, "    <title>");
    __mrss_write_string(func, obj, mrss->title);
    func(obj, "</title>\n");
  }

  if (mrss->description) {
    func(obj, "    <description>");
    __mrss_write_string(func, obj, mrss->description);
    func(obj, "</description>\n");
  }

  if (mrss->link) {
    func(obj, "    <link>");
    __mrss_write_string(func, obj, mrss->link);
    func(obj, "</link>\n");
  }

  if (mrss->language && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <language>");
    __mrss_write_string(func, obj, mrss->language);
    func(obj, "</language>\n");
  }

  if (mrss->rating && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <rating>");
    __mrss_write_string(func, obj, mrss->rating);
    func(obj, "</rating>\n");
  }

  if (mrss->copyright && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <copyright>");
    __mrss_write_string(func, obj, mrss->copyright);
    func(obj, "</copyright>\n");
  }

  if (mrss->pubDate && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <pubDate>");
    __mrss_write_string(func, obj, mrss->pubDate);
    func(obj, "</pubDate>\n");
  }

  if (mrss->lastBuildDate && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <lastBuildDate>");
    __mrss_write_string(func, obj, mrss->lastBuildDate);
    func(obj, "</lastBuildDate>\n");
  }

  if (mrss->docs && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <docs>");
    __mrss_write_string(func, obj, mrss->docs);
    func(obj, "</docs>\n");
  }

  if (mrss->managingeditor && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <managingeditor>");
    __mrss_write_string(func, obj, mrss->managingeditor);
    func(obj, "</managingeditor>\n");
  }

  if (mrss->webMaster && mrss->version != MRSS_VERSION_1_0) {
    func(obj, "    <webMaster>");
    __mrss_write_string(func, obj, mrss->webMaster);
    func(obj, "</webMaster>\n");
  }

  if (mrss->version == MRSS_VERSION_2_0) {
    if (mrss->generator) {
      func(obj, "    <generator>");
      __mrss_write_string(func, obj, mrss->generator);
      func(obj, "</generator>\n");
    }

    if (mrss->ttl)
      func(obj, "    <ttl>%d</ttl>\n", mrss->ttl);
  }

  if (mrss->version == MRSS_VERSION_1_0) {
    if (mrss->image_url) {
      func(obj, "    <image rdf:resource=\"");
      __mrss_write_string(func, obj, mrss->image_url);
      func(obj, "\" />\n");
    }

    if (mrss->textinput_link) {
      func(obj, "    <textinput rdf:resource=\"");
      __mrss_write_string(func, obj, mrss->textinput_link);
      func(obj, "\" />\n");
    }

    if (mrss->item) {
      mrss_item_t *item = mrss->item;

      func(obj, "    <items>\n"
                "      <rdf:Seq>\n");
      while (item) {
        func(obj, "        <rdf:li rdf:resource=\"");
        __mrss_write_string(func, obj, item->link);
        func(obj, "\" />\n");
        item = item->next;
      }
      func(obj, "      </rdf:Seq>\n"
                "    </items>\n");
    }

    func(obj, "  </channel>\n");
  }

  __mrss_write_real_image(mrss, func, obj);

  __mrss_write_real_textinput(mrss, func, obj);

  __mrss_write_real_cloud(mrss, func, obj);

  __mrss_write_real_skipHours(mrss, func, obj);

  __mrss_write_real_skipDays(mrss, func, obj);

  __mrss_write_real_category(mrss, mrss->category, func, obj);

  if (mrss->other_tags)
    __mrss_write_real_tag(mrss->other_tags, func, obj, 1);

  __mrss_write_real_item(mrss, func, obj);

  if (mrss->version != MRSS_VERSION_1_0)
    func(obj, "  </channel>\n"
              "</rss>\n");
  else
    func(obj, "</rdf:RDF>\n");

  return MRSS_OK;
}

static void __mrss_file_write(void *obj, char *str, ...) {
  va_list va;

  va_start(va, str);
  vfprintf((FILE *)obj, str, va);
  va_end(va);
}

static void __mrss_buffer_write(void *obj, char *str, ...) {
  va_list va;
  char s[4096];
  int len;
  char **buffer = (char **)obj;

  va_start(va, str);
  len = vsnprintf(s, sizeof(s), str, va);
  va_end(va);

  if (!*buffer) {
    if (!(*buffer = (char *)malloc(sizeof(char) * (len + 1))))
      return;

    strncpy(*buffer, s, len + 1);
  } else {
    if (!(*buffer = (char *)realloc(*buffer, sizeof(char) *
                                                 (strlen(*buffer) + len + 1))))
      return;
    strncat(*buffer, s, len + 1);
  }
}

/*************************** EXTERNAL FUNCTION ******************************/

mrss_error_t mrss_write_file(mrss_t *mrss, char *file) {
  FILE *fl;
  mrss_error_t ret;

  if (!mrss || !file)
    return MRSS_ERR_DATA;

  if (!(fl = fopen(file, "wb")))
    return MRSS_ERR_POSIX;

  ret = __mrss_write_real(mrss, __mrss_file_write, fl);
  fclose(fl);

  return ret;
}

mrss_error_t mrss_write_buffer(mrss_t *mrss, char **buffer) {
  if (!mrss || !buffer)
    return MRSS_ERR_DATA;

  return __mrss_write_real(mrss, __mrss_buffer_write, buffer);
}

/* EOF */
