/*
 * xml_stats.c: Funtions used by sadf to display statistics in XML.
 * (C) 1999-2014 by Sebastien GODARD (sysstat <at> orange.fr)
 *
 ***************************************************************************
 * This program is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU General Public License as published  by  the *
 * Free Software Foundation; either version 2 of the License, or (at  your *
 * option) any later version.                                              *
 *                                                                         *
 * This program is distributed in the hope that it  will  be  useful,  but *
 * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
 * for more details.                                                       *
 *                                                                         *
 * You should have received a copy of the GNU General Public License along *
 * with this program; if not, write to the Free Software Foundation, Inc., *
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                   *
 ***************************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "sa.h"
#include "sadf.h"
#include "ioconf.h"
#include "xml_stats.h"

#ifdef USE_NLS
#include <locale.h>
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

extern unsigned int flags;
extern unsigned int dm_major;

/*
 ***************************************************************************
 * Open or close <network> markup.
 *
 * IN:
 * @tab		Number of tabulations.
 * @action	Open or close action.
 ***************************************************************************
 */
void xml_markup_network(int tab, int action)
{
	static int markup_state = CLOSE_XML_MARKUP;

	if (action == markup_state)
		return;
	markup_state = action;

	if (action == OPEN_XML_MARKUP) {
		/* Open markup */
		xprintf(tab, "<network per=\"second\">");
	}
	else {
		/* Close markup */
		xprintf(tab, "</network>");
	}
}

/*
 ***************************************************************************
 * Open or close <power-management> markup.
 *
 * IN:
 * @tab		Number of tabulations.
 * @action	Open or close action.
 ***************************************************************************
 */
void xml_markup_power_management(int tab, int action)
{
	static int markup_state = CLOSE_XML_MARKUP;

	if (action == markup_state)
		return;
	markup_state = action;

	if (action == OPEN_XML_MARKUP) {
		/* Open markup */
		xprintf(tab, "<power-management>");
	}
	else {
		/* Close markup */
		xprintf(tab, "</power-management>");
	}
}

/*
 ***************************************************************************
 * Display CPU statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @g_itv	Interval of time in jiffies mutliplied by the number of
 * 		processors.
 ***************************************************************************
 */
__print_funct_t xml_print_cpu_stats(struct activity *a, int curr, int tab,
				    unsigned long long g_itv)
{
	int i, cpu_offline;
	struct stats_cpu *scc, *scp;
	char cpuno[8];

	if (DISPLAY_CPU_DEF(a->opt_flags)) {
		xprintf(tab++, "<cpu-load>");
	}
	else if (DISPLAY_CPU_ALL(a->opt_flags)) {
		xprintf(tab++, "<cpu-load-all>");
	}

	for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {

		scc = (struct stats_cpu *) ((char *) a->buf[curr]  + i * a->msize);
		scp = (struct stats_cpu *) ((char *) a->buf[!curr] + i * a->msize);

		/* Should current CPU (including CPU "all") be displayed? */
		if (a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))) {

			/* Yes: Display it */
			if (!i) {
				/* This is CPU "all" */
				strcpy(cpuno, "all");
			}
			else {
				sprintf(cpuno, "%d", i - 1);

				/*
				 * If the CPU is offline then it is omited from /proc/stat:
				 * All the fields couldn't have been read and the sum of them is zero.
				 * (Remember that guest/guest_nice times are already included in
				 * user/nice modes.)
				 */
				if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
				     scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
				     scc->cpu_hardirq + scc->cpu_softirq) == 0) {
					/*
					 * Set current struct fields (which have been set to zero)
					 * to values from previous iteration. Hence their values won't
					 * jump from zero when the CPU comes back online.
					 */
					*scc = *scp;

					g_itv = 0;
					cpu_offline = TRUE;
				}
				else {
					/*
					 * Recalculate interval for current proc.
					 * If result is 0 then current CPU is a tickless one.
					 */
					g_itv = get_per_cpu_interval(scc, scp);
					cpu_offline = FALSE;
				}

				if (!g_itv) {
					/* Current CPU is offline or tickless */
					if (DISPLAY_CPU_DEF(a->opt_flags)) {
						xprintf(tab, "<cpu number=\"%d\" "
							"user=\"%.2f\" "
							"nice=\"%.2f\" "
							"system=\"%.2f\" "
							"iowait=\"%.2f\" "
							"steal=\"%.2f\" "
							"idle=\"%.2f\"/>",
							i - 1, 0.0, 0.0, 0.0, 0.0, 0.0,
							cpu_offline ? 0.0 : 100.0);
					}
					else if (DISPLAY_CPU_ALL(a->opt_flags)) {
						xprintf(tab, "<cpu number=\"%d\" "
							"usr=\"%.2f\" "
							"nice=\"%.2f\" "
							"sys=\"%.2f\" "
							"iowait=\"%.2f\" "
							"steal=\"%.2f\" "
							"irq=\"%.2f\" "
							"soft=\"%.2f\" "
							"guest=\"%.2f\" "
							"gnice=\"%.2f\" "
							"idle=\"%.2f\"/>",
							i - 1, 0.0, 0.0, 0.0, 0.0,
							0.0, 0.0, 0.0, 0.0, 0.0,
							cpu_offline ? 0.0 : 100.0);
					}
					continue;
				}
			}

			if (DISPLAY_CPU_DEF(a->opt_flags)) {
				xprintf(tab, "<cpu number=\"%s\" "
					"user=\"%.2f\" "
					"nice=\"%.2f\" "
					"system=\"%.2f\" "
					"iowait=\"%.2f\" "
					"steal=\"%.2f\" "
					"idle=\"%.2f\"/>",
					cpuno,
					ll_sp_value(scp->cpu_user,   scc->cpu_user,   g_itv),
					ll_sp_value(scp->cpu_nice,   scc->cpu_nice,   g_itv),
					ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
						    scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
						    g_itv),
					ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
					ll_sp_value(scp->cpu_steal,  scc->cpu_steal,  g_itv),
					scc->cpu_idle < scp->cpu_idle ?
					0.0 :
					ll_sp_value(scp->cpu_idle,   scc->cpu_idle,   g_itv));
			}
			else if (DISPLAY_CPU_ALL(a->opt_flags)) {
				xprintf(tab, "<cpu number=\"%s\" "
					"usr=\"%.2f\" "
					"nice=\"%.2f\" "
					"sys=\"%.2f\" "
					"iowait=\"%.2f\" "
					"steal=\"%.2f\" "
					"irq=\"%.2f\" "
					"soft=\"%.2f\" "
					"guest=\"%.2f\" "
					"gnice=\"%.2f\" "
					"idle=\"%.2f\"/>",
					cpuno,
					(scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
					0.0 :
					ll_sp_value(scp->cpu_user - scp->cpu_guest,
						    scc->cpu_user - scc->cpu_guest, g_itv),
					(scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
					0.0 :
					ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
						    scc->cpu_nice - scc->cpu_guest_nice, g_itv),
					ll_sp_value(scp->cpu_sys, scc->cpu_sys, g_itv),
					ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
					ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
					ll_sp_value(scp->cpu_hardirq, scc->cpu_hardirq, g_itv),
					ll_sp_value(scp->cpu_softirq, scc->cpu_softirq, g_itv),
					ll_sp_value(scp->cpu_guest, scc->cpu_guest, g_itv),
					ll_sp_value(scp->cpu_guest_nice, scc->cpu_guest_nice, g_itv),
					scc->cpu_idle < scp->cpu_idle ?
					0.0 :
					ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv));
			}
		}
	}

	if (DISPLAY_CPU_DEF(a->opt_flags)) {
		xprintf(--tab, "</cpu-load>");
	}
	else if (DISPLAY_CPU_ALL(a->opt_flags)) {
		xprintf(--tab, "</cpu-load-all>");
	}
}

/*
 ***************************************************************************
 * Display task creation and context switch statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pcsw_stats(struct activity *a, int curr, int tab,
				     unsigned long long itv)
{
	struct stats_pcsw
		*spc = (struct stats_pcsw *) a->buf[curr],
		*spp = (struct stats_pcsw *) a->buf[!curr];

	/* proc/s and cswch/s */
	xprintf(tab, "<process-and-context-switch per=\"second\" "
		"proc=\"%.2f\" "
		"cswch=\"%.2f\"/>",
		S_VALUE(spp->processes, spc->processes, itv),
		S_VALUE(spp->context_switch, spc->context_switch, itv));
}

/*
 ***************************************************************************
 * Display interrupts statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_irq_stats(struct activity *a, int curr, int tab,
				    unsigned long long itv)
{
	int i;
	struct stats_irq *sic, *sip;
	char irqno[8];

	xprintf(tab++, "<interrupts>");
	xprintf(tab++, "<int-global per=\"second\">");

	for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {

		sic = (struct stats_irq *) ((char *) a->buf[curr]  + i * a->msize);
		sip = (struct stats_irq *) ((char *) a->buf[!curr] + i * a->msize);

		/* Should current interrupt (including int "sum") be displayed? */
		if (a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))) {

			/* Yes: Display it */
			if (!i) {
				/* This is interrupt "sum" */
				strcpy(irqno, "sum");
			}
			else {
				sprintf(irqno, "%d", i - 1);
			}

			xprintf(tab, "<irq intr=\"%s\" value=\"%.2f\"/>", irqno,
				S_VALUE(sip->irq_nr, sic->irq_nr, itv));
		}
	}

	xprintf(--tab, "</int-global>");
	xprintf(--tab, "</interrupts>");
}

/*
 ***************************************************************************
 * Display swapping statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_swap_stats(struct activity *a, int curr, int tab,
				     unsigned long long itv)
{
	struct stats_swap
		*ssc = (struct stats_swap *) a->buf[curr],
		*ssp = (struct stats_swap *) a->buf[!curr];

	xprintf(tab, "<swap-pages per=\"second\" "
		"pswpin=\"%.2f\" "
		"pswpout=\"%.2f\"/>",
		S_VALUE(ssp->pswpin,  ssc->pswpin,  itv),
		S_VALUE(ssp->pswpout, ssc->pswpout, itv));
}

/*
 ***************************************************************************
 * Display paging statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_paging_stats(struct activity *a, int curr, int tab,
				       unsigned long long itv)
{
	struct stats_paging
		*spc = (struct stats_paging *) a->buf[curr],
		*spp = (struct stats_paging *) a->buf[!curr];

	xprintf(tab, "<paging per=\"second\" "
		"pgpgin=\"%.2f\" "
		"pgpgout=\"%.2f\" "
		"fault=\"%.2f\" "
		"majflt=\"%.2f\" "
		"pgfree=\"%.2f\" "
		"pgscank=\"%.2f\" "
		"pgscand=\"%.2f\" "
		"pgsteal=\"%.2f\" "
		"vmeff-percent=\"%.2f\"/>",
		S_VALUE(spp->pgpgin,        spc->pgpgin,        itv),
		S_VALUE(spp->pgpgout,       spc->pgpgout,       itv),
		S_VALUE(spp->pgfault,       spc->pgfault,       itv),
		S_VALUE(spp->pgmajfault,    spc->pgmajfault,    itv),
		S_VALUE(spp->pgfree,        spc->pgfree,        itv),
		S_VALUE(spp->pgscan_kswapd, spc->pgscan_kswapd, itv),
		S_VALUE(spp->pgscan_direct, spc->pgscan_direct, itv),
		S_VALUE(spp->pgsteal,       spc->pgsteal,       itv),
		(spc->pgscan_kswapd + spc->pgscan_direct -
		 spp->pgscan_kswapd - spp->pgscan_direct) ?
		SP_VALUE(spp->pgsteal, spc->pgsteal,
			 spc->pgscan_kswapd + spc->pgscan_direct -
			 spp->pgscan_kswapd - spp->pgscan_direct) : 0.0);
}

/*
 ***************************************************************************
 * Display I/O and transfer rate statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_io_stats(struct activity *a, int curr, int tab,
				   unsigned long long itv)
{
	struct stats_io
		*sic = (struct stats_io *) a->buf[curr],
		*sip = (struct stats_io *) a->buf[!curr];

	xprintf(tab, "<io per=\"second\">");

	xprintf(++tab, "<tps>%.2f</tps>",
		S_VALUE(sip->dk_drive, sic->dk_drive, itv));

	xprintf(tab, "<io-reads rtps=\"%.2f\" bread=\"%.2f\"/>",
		S_VALUE(sip->dk_drive_rio,  sic->dk_drive_rio,  itv),
		S_VALUE(sip->dk_drive_rblk, sic->dk_drive_rblk, itv));

	xprintf(tab, "<io-writes wtps=\"%.2f\" bwrtn=\"%.2f\"/>",
		S_VALUE(sip->dk_drive_wio,  sic->dk_drive_wio,  itv),
		S_VALUE(sip->dk_drive_wblk, sic->dk_drive_wblk, itv));

	xprintf(--tab, "</io>");
}

/*
 ***************************************************************************
 * Display memory statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_memory_stats(struct activity *a, int curr, int tab,
				       unsigned long long itv)
{
	struct stats_memory
		*smc = (struct stats_memory *) a->buf[curr],
		*smp = (struct stats_memory *) a->buf[!curr];

	xprintf(tab, "<memory per=\"second\" unit=\"kB\">");

	if (DISPLAY_MEM_AMT(a->opt_flags)) {

		xprintf(++tab, "<memfree>%lu</memfree>",
			smc->frmkb);

		xprintf(tab, "<memused>%lu</memused>",
			smc->tlmkb - smc->frmkb);

		xprintf(tab, "<memused-percent>%.2f</memused-percent>",
			smc->tlmkb ?
			SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) :
			0.0);

		xprintf(tab, "<buffers>%lu</buffers>",
			smc->bufkb);

		xprintf(tab, "<cached>%lu</cached>",
			smc->camkb);

		xprintf(tab, "<commit>%lu</commit>",
			smc->comkb);

		xprintf(tab, "<commit-percent>%.2f</commit-percent>",
			(smc->tlmkb + smc->tlskb) ?
			SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) :
			0.0);

		xprintf(tab, "<active>%lu</active>",
			smc->activekb);

		xprintf(tab, "<inactive>%lu</inactive>",
			smc->inactkb);

		xprintf(tab--, "<dirty>%lu</dirty>",
			smc->dirtykb);
	}

	if (DISPLAY_SWAP(a->opt_flags)) {

		xprintf(++tab, "<swpfree>%lu</swpfree>",
			smc->frskb);

		xprintf(tab, "<swpused>%lu</swpused>",
			smc->tlskb - smc->frskb);

		xprintf(tab, "<swpused-percent>%.2f</swpused-percent>",
			smc->tlskb ?
			SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) :
			0.0);

		xprintf(tab, "<swpcad>%lu</swpcad>",
			smc->caskb);

		xprintf(tab--, "<swpcad-percent>%.2f</swpcad-percent>",
			(smc->tlskb - smc->frskb) ?
			SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) :
			0.0);
	}

	if (DISPLAY_MEMORY(a->opt_flags)) {

		xprintf(++tab, "<frmpg>%.2f</frmpg>",
			S_VALUE((double) KB_TO_PG(smp->frmkb),
				(double) KB_TO_PG(smc->frmkb), itv));

		xprintf(tab, "<bufpg>%.2f</bufpg>",
			S_VALUE((double) KB_TO_PG(smp->bufkb),
				(double) KB_TO_PG(smc->bufkb), itv));

		xprintf(tab--, "<campg>%.2f</campg>",
			S_VALUE((double) KB_TO_PG(smp->camkb),
				(double) KB_TO_PG(smc->camkb), itv));
	}

	xprintf(tab, "</memory>");
}

/*
 ***************************************************************************
 * Display kernel tables statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_ktables_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	struct stats_ktables
		*skc = (struct stats_ktables *) a->buf[curr];

	xprintf(tab, "<kernel "
		"dentunusd=\"%u\" "
		"file-nr=\"%u\" "
		"inode-nr=\"%u\" "
		"pty-nr=\"%u\"/>",
		skc->dentry_stat,
		skc->file_used,
		skc->inode_used,
		skc->pty_nr);
}

/*
 ***************************************************************************
 * Display queue and load statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_queue_stats(struct activity *a, int curr, int tab,
				      unsigned long long itv)
{
	struct stats_queue
		*sqc = (struct stats_queue *) a->buf[curr];

	xprintf(tab, "<queue "
		"runq-sz=\"%lu\" "
		"plist-sz=\"%u\" "
		"ldavg-1=\"%.2f\" "
		"ldavg-5=\"%.2f\" "
		"ldavg-15=\"%.2f\" "
		"blocked=\"%lu\"/>",
		sqc->nr_running,
		sqc->nr_threads,
		(double) sqc->load_avg_1 / 100,
		(double) sqc->load_avg_5 / 100,
		(double) sqc->load_avg_15 / 100,
		sqc->procs_blocked);
}

/*
 ***************************************************************************
 * Display serial lines statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_serial_stats(struct activity *a, int curr, int tab,
				       unsigned long long itv)
{
	int i;
	struct stats_serial *ssc, *ssp;

	xprintf(tab, "<serial per=\"second\">");
	tab++;

	for (i = 0; i < a->nr; i++) {

		ssc = (struct stats_serial *) ((char *) a->buf[curr]  + i * a->msize);
		ssp = (struct stats_serial *) ((char *) a->buf[!curr] + i * a->msize);

		if (ssc->line == 0)
			continue;

		if (ssc->line == ssp->line) {

			xprintf(tab, "<tty line=\"%d\" "
				"rcvin=\"%.2f\" "
				"xmtin=\"%.2f\" "
				"framerr=\"%.2f\" "
				"prtyerr=\"%.2f\" "
				"brk=\"%.2f\" "
				"ovrun=\"%.2f\"/>",
				ssc->line - 1,
				S_VALUE(ssp->rx,      ssc->rx,      itv),
				S_VALUE(ssp->tx,      ssc->tx,      itv),
				S_VALUE(ssp->frame,   ssc->frame,   itv),
				S_VALUE(ssp->parity,  ssc->parity,  itv),
				S_VALUE(ssp->brk,     ssc->brk,     itv),
				S_VALUE(ssp->overrun, ssc->overrun, itv));
		}
	}

	xprintf(--tab, "</serial>");
}

/*
 ***************************************************************************
 * Display disks statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_disk_stats(struct activity *a, int curr, int tab,
				     unsigned long long itv)
{
	int i, j;
	struct stats_disk *sdc,	*sdp;
	struct ext_disk_stats xds;
	char *dev_name, *persist_dev_name;

	xprintf(tab, "<disk per=\"second\">");
	tab++;

	for (i = 0; i < a->nr; i++) {

		sdc = (struct stats_disk *) ((char *) a->buf[curr] + i * a->msize);

		if (!(sdc->major + sdc->minor))
			continue;

		j = check_disk_reg(a, curr, !curr, i);
		sdp = (struct stats_disk *) ((char *) a->buf[!curr] + j * a->msize);

		/* Compute extended statistics values */
		compute_ext_disk_stats(sdc, sdp, itv, &xds);

		dev_name = NULL;
		persist_dev_name = NULL;

		if (DISPLAY_PERSIST_NAME_S(flags)) {
			persist_dev_name = get_persistent_name_from_pretty(get_devname(sdc->major, sdc->minor, TRUE));
		}

		if (persist_dev_name) {
			dev_name = persist_dev_name;
		}
		else {
			if ((USE_PRETTY_OPTION(flags)) && (sdc->major == dm_major)) {
				dev_name = transform_devmapname(sdc->major, sdc->minor);
			}

			if (!dev_name) {
				dev_name = get_devname(sdc->major, sdc->minor,
						       USE_PRETTY_OPTION(flags));
			}
		}

		xprintf(tab, "<disk-device dev=\"%s\" "
			"tps=\"%.2f\" "
			"rd_sec=\"%.2f\" "
			"wr_sec=\"%.2f\" "
			"avgrq-sz=\"%.2f\" "
			"avgqu-sz=\"%.2f\" "
			"await=\"%.2f\" "
			"svctm=\"%.2f\" "
			"util-percent=\"%.2f\"/>",
			/* Confusion possible here between index and minor numbers */
			dev_name,
			S_VALUE(sdp->nr_ios, sdc->nr_ios, itv),
			S_VALUE(sdp->rd_sect, sdc->rd_sect, itv),
			S_VALUE(sdp->wr_sect, sdc->wr_sect, itv),
			/* See iostat for explanations */
			xds.arqsz,
			S_VALUE(sdp->rq_ticks, sdc->rq_ticks, itv) / 1000.0,
			xds.await,
			xds.svctm,
			xds.util / 10.0);
	}

	xprintf(--tab, "</disk>");
}

/*
 ***************************************************************************
 * Display network interfaces statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_dev_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	int i, j;
	struct stats_net_dev *sndc, *sndp;
	double rxkb, txkb, ifutil;

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	for (i = 0; i < a->nr; i++) {

		sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);

		if (!strcmp(sndc->interface, ""))
			continue;

		j = check_net_dev_reg(a, curr, !curr, i);
		sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);

		rxkb = S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv);
		txkb = S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv);
		ifutil = compute_ifutil(sndc, rxkb, txkb);

		xprintf(tab, "<net-dev iface=\"%s\" "
			"rxpck=\"%.2f\" "
			"txpck=\"%.2f\" "
			"rxkB=\"%.2f\" "
			"txkB=\"%.2f\" "
			"rxcmp=\"%.2f\" "
			"txcmp=\"%.2f\" "
			"rxmcst=\"%.2f\" "
			"ifutil-percent=\"%.2f\"/>",
			sndc->interface,
			S_VALUE(sndp->rx_packets,    sndc->rx_packets,    itv),
			S_VALUE(sndp->tx_packets,    sndc->tx_packets,    itv),
			rxkb / 1024,
			txkb / 1024,
			S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
			S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
			S_VALUE(sndp->multicast,     sndc->multicast,     itv),
			ifutil);
	}
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display network interfaces error statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_edev_stats(struct activity *a, int curr, int tab,
					 unsigned long long itv)
{
	int i, j;
	struct stats_net_edev *snedc, *snedp;

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	for (i = 0; i < a->nr; i++) {

		snedc = (struct stats_net_edev *) ((char *) a->buf[curr] + i * a->msize);

		if (!strcmp(snedc->interface, ""))
			continue;

		j = check_net_edev_reg(a, curr, !curr, i);
		snedp = (struct stats_net_edev *) ((char *) a->buf[!curr] + j * a->msize);

		xprintf(tab, "<net-edev iface=\"%s\" "
			"rxerr=\"%.2f\" "
			"txerr=\"%.2f\" "
			"coll=\"%.2f\" "
			"rxdrop=\"%.2f\" "
			"txdrop=\"%.2f\" "
			"txcarr=\"%.2f\" "
			"rxfram=\"%.2f\" "
			"rxfifo=\"%.2f\" "
			"txfifo=\"%.2f\"/>",
			snedc->interface,
			S_VALUE(snedp->rx_errors,         snedc->rx_errors,         itv),
			S_VALUE(snedp->tx_errors,         snedc->tx_errors,         itv),
			S_VALUE(snedp->collisions,        snedc->collisions,        itv),
			S_VALUE(snedp->rx_dropped,        snedc->rx_dropped,        itv),
			S_VALUE(snedp->tx_dropped,        snedc->tx_dropped,        itv),
			S_VALUE(snedp->tx_carrier_errors, snedc->tx_carrier_errors, itv),
			S_VALUE(snedp->rx_frame_errors,   snedc->rx_frame_errors,   itv),
			S_VALUE(snedp->rx_fifo_errors,    snedc->rx_fifo_errors,    itv),
			S_VALUE(snedp->tx_fifo_errors,    snedc->tx_fifo_errors,    itv));
	}
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display NFS client statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_nfs_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	struct stats_net_nfs
		*snnc = (struct stats_net_nfs *) a->buf[curr],
		*snnp = (struct stats_net_nfs *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-nfs "
		"call=\"%.2f\" "
		"retrans=\"%.2f\" "
		"read=\"%.2f\" "
		"write=\"%.2f\" "
		"access=\"%.2f\" "
		"getatt=\"%.2f\"/>",
		S_VALUE(snnp->nfs_rpccnt,     snnc->nfs_rpccnt,     itv),
		S_VALUE(snnp->nfs_rpcretrans, snnc->nfs_rpcretrans, itv),
		S_VALUE(snnp->nfs_readcnt,    snnc->nfs_readcnt,    itv),
		S_VALUE(snnp->nfs_writecnt,   snnc->nfs_writecnt,   itv),
		S_VALUE(snnp->nfs_accesscnt,  snnc->nfs_accesscnt,  itv),
		S_VALUE(snnp->nfs_getattcnt,  snnc->nfs_getattcnt,  itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display NFS server statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_nfsd_stats(struct activity *a, int curr, int tab,
       				   	 unsigned long long itv)
{
	struct stats_net_nfsd
		*snndc = (struct stats_net_nfsd *) a->buf[curr],
		*snndp = (struct stats_net_nfsd *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-nfsd "
		"scall=\"%.2f\" "
		"badcall=\"%.2f\" "
		"packet=\"%.2f\" "
		"udp=\"%.2f\" "
		"tcp=\"%.2f\" "
		"hit=\"%.2f\" "
		"miss=\"%.2f\" "
		"sread=\"%.2f\" "
		"swrite=\"%.2f\" "
		"saccess=\"%.2f\" "
		"sgetatt=\"%.2f\"/>",
		S_VALUE(snndp->nfsd_rpccnt,    snndc->nfsd_rpccnt,    itv),
		S_VALUE(snndp->nfsd_rpcbad,    snndc->nfsd_rpcbad,    itv),
		S_VALUE(snndp->nfsd_netcnt,    snndc->nfsd_netcnt,    itv),
		S_VALUE(snndp->nfsd_netudpcnt, snndc->nfsd_netudpcnt, itv),
		S_VALUE(snndp->nfsd_nettcpcnt, snndc->nfsd_nettcpcnt, itv),
		S_VALUE(snndp->nfsd_rchits,    snndc->nfsd_rchits,    itv),
		S_VALUE(snndp->nfsd_rcmisses,  snndc->nfsd_rcmisses,  itv),
		S_VALUE(snndp->nfsd_readcnt,   snndc->nfsd_readcnt,   itv),
		S_VALUE(snndp->nfsd_writecnt,  snndc->nfsd_writecnt,  itv),
		S_VALUE(snndp->nfsd_accesscnt, snndc->nfsd_accesscnt, itv),
		S_VALUE(snndp->nfsd_getattcnt, snndc->nfsd_getattcnt, itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display network socket statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_sock_stats(struct activity *a, int curr, int tab,
       				   	 unsigned long long itv)
{
	struct stats_net_sock
		*snsc = (struct stats_net_sock *) a->buf[curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-sock "
		"totsck=\"%u\" "
		"tcpsck=\"%u\" "
		"udpsck=\"%u\" "
		"rawsck=\"%u\" "
		"ip-frag=\"%u\" "
		"tcp-tw=\"%u\"/>",
		snsc->sock_inuse,
	       	snsc->tcp_inuse,
	       	snsc->udp_inuse,
       		snsc->raw_inuse,
	       	snsc->frag_inuse,
	       	snsc->tcp_tw);
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display IP network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_ip_stats(struct activity *a, int curr, int tab,
				       unsigned long long itv)
{
	struct stats_net_ip
		*snic = (struct stats_net_ip *) a->buf[curr],
		*snip = (struct stats_net_ip *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-ip "
		"irec=\"%.2f\" "
		"fwddgm=\"%.2f\" "
		"idel=\"%.2f\" "
		"orq=\"%.2f\" "
		"asmrq=\"%.2f\" "
		"asmok=\"%.2f\" "
		"fragok=\"%.2f\" "
		"fragcrt=\"%.2f\"/>",
		S_VALUE(snip->InReceives,    snic->InReceives,    itv),
		S_VALUE(snip->ForwDatagrams, snic->ForwDatagrams, itv),
		S_VALUE(snip->InDelivers,    snic->InDelivers,    itv),
		S_VALUE(snip->OutRequests,   snic->OutRequests,   itv),
		S_VALUE(snip->ReasmReqds,    snic->ReasmReqds,    itv),
		S_VALUE(snip->ReasmOKs,      snic->ReasmOKs,      itv),
		S_VALUE(snip->FragOKs,       snic->FragOKs,       itv),
		S_VALUE(snip->FragCreates,   snic->FragCreates,   itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display IP network error statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_eip_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	struct stats_net_eip
		*sneic = (struct stats_net_eip *) a->buf[curr],
		*sneip = (struct stats_net_eip *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-eip "
		"ihdrerr=\"%.2f\" "
		"iadrerr=\"%.2f\" "
		"iukwnpr=\"%.2f\" "
		"idisc=\"%.2f\" "
		"odisc=\"%.2f\" "
		"onort=\"%.2f\" "
		"asmf=\"%.2f\" "
		"fragf=\"%.2f\"/>",
		S_VALUE(sneip->InHdrErrors,     sneic->InHdrErrors,     itv),
		S_VALUE(sneip->InAddrErrors,    sneic->InAddrErrors,    itv),
		S_VALUE(sneip->InUnknownProtos, sneic->InUnknownProtos, itv),
		S_VALUE(sneip->InDiscards,      sneic->InDiscards,      itv),
		S_VALUE(sneip->OutDiscards,     sneic->OutDiscards,     itv),
		S_VALUE(sneip->OutNoRoutes,     sneic->OutNoRoutes,     itv),
		S_VALUE(sneip->ReasmFails,      sneic->ReasmFails,      itv),
		S_VALUE(sneip->FragFails,       sneic->FragFails,       itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display ICMP network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_icmp_stats(struct activity *a, int curr, int tab,
					 unsigned long long itv)
{
	struct stats_net_icmp
		*snic = (struct stats_net_icmp *) a->buf[curr],
		*snip = (struct stats_net_icmp *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-icmp "
		"imsg=\"%.2f\" "
		"omsg=\"%.2f\" "
		"iech=\"%.2f\" "
		"iechr=\"%.2f\" "
		"oech=\"%.2f\" "
		"oechr=\"%.2f\" "
		"itm=\"%.2f\" "
		"itmr=\"%.2f\" "
		"otm=\"%.2f\" "
		"otmr=\"%.2f\" "
		"iadrmk=\"%.2f\" "
		"iadrmkr=\"%.2f\" "
		"oadrmk=\"%.2f\" "
		"oadrmkr=\"%.2f\"/>",
		S_VALUE(snip->InMsgs,           snic->InMsgs,           itv),
		S_VALUE(snip->OutMsgs,          snic->OutMsgs,          itv),
		S_VALUE(snip->InEchos,          snic->InEchos,          itv),
		S_VALUE(snip->InEchoReps,       snic->InEchoReps,       itv),
		S_VALUE(snip->OutEchos,         snic->OutEchos,         itv),
		S_VALUE(snip->OutEchoReps,      snic->OutEchoReps,      itv),
		S_VALUE(snip->InTimestamps,     snic->InTimestamps,     itv),
		S_VALUE(snip->InTimestampReps,  snic->InTimestampReps,  itv),
		S_VALUE(snip->OutTimestamps,    snic->OutTimestamps,    itv),
		S_VALUE(snip->OutTimestampReps, snic->OutTimestampReps, itv),
		S_VALUE(snip->InAddrMasks,      snic->InAddrMasks,      itv),
		S_VALUE(snip->InAddrMaskReps,   snic->InAddrMaskReps,   itv),
		S_VALUE(snip->OutAddrMasks,     snic->OutAddrMasks,     itv),
		S_VALUE(snip->OutAddrMaskReps,  snic->OutAddrMaskReps,  itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display ICMP error message statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_eicmp_stats(struct activity *a, int curr, int tab,
					  unsigned long long itv)
{
	struct stats_net_eicmp
		*sneic = (struct stats_net_eicmp *) a->buf[curr],
		*sneip = (struct stats_net_eicmp *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-eicmp "
		"ierr=\"%.2f\" "
		"oerr=\"%.2f\" "
		"idstunr=\"%.2f\" "
		"odstunr=\"%.2f\" "
		"itmex=\"%.2f\" "
		"otmex=\"%.2f\" "
		"iparmpb=\"%.2f\" "
		"oparmpb=\"%.2f\" "
		"isrcq=\"%.2f\" "
		"osrcq=\"%.2f\" "
		"iredir=\"%.2f\" "
		"oredir=\"%.2f\"/>",
		S_VALUE(sneip->InErrors,        sneic->InErrors,        itv),
		S_VALUE(sneip->OutErrors,       sneic->OutErrors,       itv),
		S_VALUE(sneip->InDestUnreachs,  sneic->InDestUnreachs,  itv),
		S_VALUE(sneip->OutDestUnreachs, sneic->OutDestUnreachs, itv),
		S_VALUE(sneip->InTimeExcds,     sneic->InTimeExcds,     itv),
		S_VALUE(sneip->OutTimeExcds,    sneic->OutTimeExcds,    itv),
		S_VALUE(sneip->InParmProbs,     sneic->InParmProbs,     itv),
		S_VALUE(sneip->OutParmProbs,    sneic->OutParmProbs,    itv),
		S_VALUE(sneip->InSrcQuenchs,    sneic->InSrcQuenchs,    itv),
		S_VALUE(sneip->OutSrcQuenchs,   sneic->OutSrcQuenchs,   itv),
		S_VALUE(sneip->InRedirects,     sneic->InRedirects,     itv),
		S_VALUE(sneip->OutRedirects,    sneic->OutRedirects,    itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display TCP network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_tcp_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	struct stats_net_tcp
		*sntc = (struct stats_net_tcp *) a->buf[curr],
		*sntp = (struct stats_net_tcp *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-tcp "
		"active=\"%.2f\" "
		"passive=\"%.2f\" "
		"iseg=\"%.2f\" "
		"oseg=\"%.2f\"/>",
		S_VALUE(sntp->ActiveOpens,  sntc->ActiveOpens,  itv),
		S_VALUE(sntp->PassiveOpens, sntc->PassiveOpens, itv),
		S_VALUE(sntp->InSegs,       sntc->InSegs,       itv),
		S_VALUE(sntp->OutSegs,      sntc->OutSegs,      itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display TCP network error statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_etcp_stats(struct activity *a, int curr, int tab,
					 unsigned long long itv)
{
	struct stats_net_etcp
		*snetc = (struct stats_net_etcp *) a->buf[curr],
		*snetp = (struct stats_net_etcp *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-etcp "
		"atmptf=\"%.2f\" "
		"estres=\"%.2f\" "
		"retrans=\"%.2f\" "
		"isegerr=\"%.2f\" "
		"orsts=\"%.2f\"/>",
		S_VALUE(snetp->AttemptFails, snetc->AttemptFails,  itv),
		S_VALUE(snetp->EstabResets,  snetc->EstabResets,  itv),
		S_VALUE(snetp->RetransSegs,  snetc->RetransSegs,  itv),
		S_VALUE(snetp->InErrs,       snetc->InErrs,  itv),
		S_VALUE(snetp->OutRsts,      snetc->OutRsts,  itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display UDP network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_udp_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	struct stats_net_udp
		*snuc = (struct stats_net_udp *) a->buf[curr],
		*snup = (struct stats_net_udp *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-udp "
		"idgm=\"%.2f\" "
		"odgm=\"%.2f\" "
		"noport=\"%.2f\" "
		"idgmerr=\"%.2f\"/>",
		S_VALUE(snup->InDatagrams,  snuc->InDatagrams,  itv),
		S_VALUE(snup->OutDatagrams, snuc->OutDatagrams, itv),
		S_VALUE(snup->NoPorts,      snuc->NoPorts,      itv),
		S_VALUE(snup->InErrors,     snuc->InErrors,     itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display IPv6 network socket statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_sock6_stats(struct activity *a, int curr, int tab,
					  unsigned long long itv)
{
	struct stats_net_sock6
		*snsc = (struct stats_net_sock6 *) a->buf[curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-sock6 "
		"tcp6sck=\"%u\" "
		"udp6sck=\"%u\" "
		"raw6sck=\"%u\" "
		"ip6-frag=\"%u\"/>",
		snsc->tcp6_inuse,
		snsc->udp6_inuse,
		snsc->raw6_inuse,
		snsc->frag6_inuse);
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display IPv6 network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_ip6_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	struct stats_net_ip6
		*snic = (struct stats_net_ip6 *) a->buf[curr],
		*snip = (struct stats_net_ip6 *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-ip6 "
		"irec6=\"%.2f\" "
		"fwddgm6=\"%.2f\" "
		"idel6=\"%.2f\" "
		"orq6=\"%.2f\" "
		"asmrq6=\"%.2f\" "
		"asmok6=\"%.2f\" "
		"imcpck6=\"%.2f\" "
		"omcpck6=\"%.2f\" "
		"fragok6=\"%.2f\" "
		"fragcr6=\"%.2f\"/>",
		S_VALUE(snip->InReceives6,       snic->InReceives6,       itv),
		S_VALUE(snip->OutForwDatagrams6, snic->OutForwDatagrams6, itv),
		S_VALUE(snip->InDelivers6,       snic->InDelivers6,       itv),
		S_VALUE(snip->OutRequests6,      snic->OutRequests6,      itv),
		S_VALUE(snip->ReasmReqds6,       snic->ReasmReqds6,       itv),
		S_VALUE(snip->ReasmOKs6,         snic->ReasmOKs6,         itv),
		S_VALUE(snip->InMcastPkts6,      snic->InMcastPkts6,      itv),
		S_VALUE(snip->OutMcastPkts6,     snic->OutMcastPkts6,     itv),
		S_VALUE(snip->FragOKs6,          snic->FragOKs6,          itv),
		S_VALUE(snip->FragCreates6,      snic->FragCreates6,      itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display IPv6 network error statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_eip6_stats(struct activity *a, int curr, int tab,
					 unsigned long long itv)
{
	struct stats_net_eip6
		*sneic = (struct stats_net_eip6 *) a->buf[curr],
		*sneip = (struct stats_net_eip6 *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-eip6 "
		"ihdrer6=\"%.2f\" "
		"iadrer6=\"%.2f\" "
		"iukwnp6=\"%.2f\" "
		"i2big6=\"%.2f\" "
		"idisc6=\"%.2f\" "
		"odisc6=\"%.2f\" "
		"inort6=\"%.2f\" "
		"onort6=\"%.2f\" "
		"asmf6=\"%.2f\" "
		"fragf6=\"%.2f\" "
		"itrpck6=\"%.2f\"/>",
		S_VALUE(sneip->InHdrErrors6,     sneic->InHdrErrors6,     itv),
		S_VALUE(sneip->InAddrErrors6,    sneic->InAddrErrors6,    itv),
		S_VALUE(sneip->InUnknownProtos6, sneic->InUnknownProtos6, itv),
		S_VALUE(sneip->InTooBigErrors6,  sneic->InTooBigErrors6,  itv),
		S_VALUE(sneip->InDiscards6,      sneic->InDiscards6,      itv),
		S_VALUE(sneip->OutDiscards6,     sneic->OutDiscards6,     itv),
		S_VALUE(sneip->InNoRoutes6,      sneic->InNoRoutes6,      itv),
		S_VALUE(sneip->OutNoRoutes6,     sneic->OutNoRoutes6,     itv),
		S_VALUE(sneip->ReasmFails6,      sneic->ReasmFails6,      itv),
		S_VALUE(sneip->FragFails6,       sneic->FragFails6,       itv),
		S_VALUE(sneip->InTruncatedPkts6, sneic->InTruncatedPkts6, itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display ICMPv6 network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_icmp6_stats(struct activity *a, int curr, int tab,
					  unsigned long long itv)
{
	struct stats_net_icmp6
		*snic = (struct stats_net_icmp6 *) a->buf[curr],
		*snip = (struct stats_net_icmp6 *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-icmp6 "
		"imsg6=\"%.2f\" "
		"omsg6=\"%.2f\" "
		"iech6=\"%.2f\" "
		"iechr6=\"%.2f\" "
		"oechr6=\"%.2f\" "
		"igmbq6=\"%.2f\" "
		"igmbr6=\"%.2f\" "
		"ogmbr6=\"%.2f\" "
		"igmbrd6=\"%.2f\" "
		"ogmbrd6=\"%.2f\" "
		"irtsol6=\"%.2f\" "
		"ortsol6=\"%.2f\" "
		"irtad6=\"%.2f\" "
		"inbsol6=\"%.2f\" "
		"onbsol6=\"%.2f\" "
		"inbad6=\"%.2f\" "
		"onbad6=\"%.2f\"/>",
		S_VALUE(snip->InMsgs6,                    snic->InMsgs6,                    itv),
		S_VALUE(snip->OutMsgs6,                   snic->OutMsgs6,                   itv),
		S_VALUE(snip->InEchos6,                   snic->InEchos6,                   itv),
		S_VALUE(snip->InEchoReplies6,             snic->InEchoReplies6,             itv),
		S_VALUE(snip->OutEchoReplies6,            snic->OutEchoReplies6,            itv),
		S_VALUE(snip->InGroupMembQueries6,        snic->InGroupMembQueries6,        itv),
		S_VALUE(snip->InGroupMembResponses6,      snic->InGroupMembResponses6,      itv),
		S_VALUE(snip->OutGroupMembResponses6,     snic->OutGroupMembResponses6,     itv),
		S_VALUE(snip->InGroupMembReductions6,     snic->InGroupMembReductions6,     itv),
		S_VALUE(snip->OutGroupMembReductions6,    snic->OutGroupMembReductions6,    itv),
		S_VALUE(snip->InRouterSolicits6,          snic->InRouterSolicits6,          itv),
		S_VALUE(snip->OutRouterSolicits6,         snic->OutRouterSolicits6,         itv),
		S_VALUE(snip->InRouterAdvertisements6,    snic->InRouterAdvertisements6,    itv),
		S_VALUE(snip->InNeighborSolicits6,        snic->InNeighborSolicits6,        itv),
		S_VALUE(snip->OutNeighborSolicits6,       snic->OutNeighborSolicits6,       itv),
		S_VALUE(snip->InNeighborAdvertisements6,  snic->InNeighborAdvertisements6,  itv),
		S_VALUE(snip->OutNeighborAdvertisements6, snic->OutNeighborAdvertisements6, itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display ICMPv6 error message statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_eicmp6_stats(struct activity *a, int curr, int tab,
					   unsigned long long itv)
{
	struct stats_net_eicmp6
		*sneic = (struct stats_net_eicmp6 *) a->buf[curr],
		*sneip = (struct stats_net_eicmp6 *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-eicmp6 "
		"ierr6=\"%.2f\" "
		"idtunr6=\"%.2f\" "
		"odtunr6=\"%.2f\" "
		"itmex6=\"%.2f\" "
		"otmex6=\"%.2f\" "
		"iprmpb6=\"%.2f\" "
		"oprmpb6=\"%.2f\" "
		"iredir6=\"%.2f\" "
		"oredir6=\"%.2f\" "
		"ipck2b6=\"%.2f\" "
		"opck2b6=\"%.2f\"/>",
		S_VALUE(sneip->InErrors6,        sneic->InErrors6,        itv),
		S_VALUE(sneip->InDestUnreachs6,  sneic->InDestUnreachs6,  itv),
		S_VALUE(sneip->OutDestUnreachs6, sneic->OutDestUnreachs6, itv),
		S_VALUE(sneip->InTimeExcds6,     sneic->InTimeExcds6,     itv),
		S_VALUE(sneip->OutTimeExcds6,    sneic->OutTimeExcds6,    itv),
		S_VALUE(sneip->InParmProblems6,  sneic->InParmProblems6,  itv),
		S_VALUE(sneip->OutParmProblems6, sneic->OutParmProblems6, itv),
		S_VALUE(sneip->InRedirects6,     sneic->InRedirects6,     itv),
		S_VALUE(sneip->OutRedirects6,    sneic->OutRedirects6,    itv),
		S_VALUE(sneip->InPktTooBigs6,    sneic->InPktTooBigs6,    itv),
		S_VALUE(sneip->OutPktTooBigs6,   sneic->OutPktTooBigs6,   itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display UDPv6 network statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_net_udp6_stats(struct activity *a, int curr, int tab,
					 unsigned long long itv)
{
	struct stats_net_udp6
		*snuc = (struct stats_net_udp6 *) a->buf[curr],
		*snup = (struct stats_net_udp6 *) a->buf[!curr];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_network(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab, "<net-udp6 "
		"idgm6=\"%.2f\" "
		"odgm6=\"%.2f\" "
		"noport6=\"%.2f\" "
		"idgmer6=\"%.2f\"/>",
		S_VALUE(snup->InDatagrams6,  snuc->InDatagrams6,  itv),
		S_VALUE(snup->OutDatagrams6, snuc->OutDatagrams6, itv),
		S_VALUE(snup->NoPorts6,      snuc->NoPorts6,      itv),
		S_VALUE(snup->InErrors6,     snuc->InErrors6,     itv));
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_network(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display CPU frequency statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pwr_cpufreq_stats(struct activity *a, int curr, int tab,
					    unsigned long long itv)
{
	int i;
	struct stats_pwr_cpufreq *spc;
	char cpuno[8];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_power_management(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab++, "<cpu-frequency unit=\"MHz\">");

	for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {

		spc = (struct stats_pwr_cpufreq *) ((char *) a->buf[curr]  + i * a->msize);

		/* Should current CPU (including CPU "all") be displayed? */
		if (a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))) {

			/* Yes: Display it */
			if (!i) {
				/* This is CPU "all" */
				strcpy(cpuno, "all");
			}
			else {
				sprintf(cpuno, "%d", i - 1);
			}

			xprintf(tab, "<cpufreq number=\"%s\" "
				"frequency=\"%.2f\"/>",
				cpuno,
				((double) spc->cpufreq) / 100);
		}
	}

	xprintf(--tab, "</cpu-frequency>");
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_power_management(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display fan statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pwr_fan_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	int i;
	struct stats_pwr_fan *spc;

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_power_management(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab++, "<fan-speed unit=\"rpm\">");

	for (i = 0; i < a->nr; i++) {
		spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);

		xprintf(tab, "<fan number=\"%d\" rpm=\"%llu\" drpm=\"%llu\" device=\"%s\"/>",
			i + 1,
			(unsigned long long) spc->rpm,
			(unsigned long long) (spc->rpm - spc->rpm_min),
			spc->device);
	}

	xprintf(--tab, "</fan-speed>");
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_power_management(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display temperature statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pwr_temp_stats(struct activity *a, int curr, int tab,
					 unsigned long long itv)
{
	int i;
	struct stats_pwr_temp *spc;

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_power_management(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab++, "<temperature unit=\"degree Celsius\">");

	for (i = 0; i < a->nr; i++) {
		spc = (struct stats_pwr_temp *) ((char *) a->buf[curr]  + i * a->msize);

		xprintf(tab, "<temp number=\"%d\" degC=\"%.2f\" percent-temp=\"%.2f\" device=\"%s\"/>",
			i + 1,
			spc->temp,
			(spc->temp_max - spc->temp_min) ?
			(spc->temp - spc->temp_min) / (spc->temp_max - spc->temp_min) * 100 :
			0.0,
			spc->device);
	}

	xprintf(--tab, "</temperature>");
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_power_management(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display voltage inputs statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pwr_in_stats(struct activity *a, int curr, int tab,
				       unsigned long long itv)
{
	int i;
	struct stats_pwr_in *spc;

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_power_management(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab++, "<voltage-input unit=\"V\">");

	for (i = 0; i < a->nr; i++) {
		spc = (struct stats_pwr_in *) ((char *) a->buf[curr]  + i * a->msize);

		xprintf(tab, "<in number=\"%d\" inV=\"%.2f\" percent-in=\"%.2f\" device=\"%s\"/>",
			i,
			spc->in,
			(spc->in_max - spc->in_min) ?
			(spc->in - spc->in_min) / (spc->in_max - spc->in_min) * 100 :
			0.0,
			spc->device);
	}

	xprintf(--tab, "</voltage-input>");
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_power_management(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display huge pages statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_huge_stats(struct activity *a, int curr, int tab,
				     unsigned long long itv)
{
	struct stats_huge
		*smc = (struct stats_huge *) a->buf[curr];

	xprintf(tab, "<hugepages unit=\"kB\">");

	xprintf(++tab, "<hugfree>%lu</hugfree>",
		smc->frhkb);

	xprintf(tab, "<hugused>%lu</hugused>",
		smc->tlhkb - smc->frhkb);

	xprintf(tab--, "<hugused-percent>%.2f</hugused-percent>",
		smc->tlhkb ?
		SP_VALUE(smc->frhkb, smc->tlhkb, smc->tlhkb) :
		0.0);

	xprintf(tab, "</hugepages>");
}

/*
 ***************************************************************************
 * Display weighted CPU frequency statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pwr_wghfreq_stats(struct activity *a, int curr, int tab,
					    unsigned long long itv)
{
	int i, k;
	struct stats_pwr_wghfreq *spc, *spp, *spc_k, *spp_k;
	unsigned long long tis, tisfreq;
	char cpuno[8];

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_power_management(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab++, "<cpu-weighted-frequency unit=\"MHz\">");

	for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {

		spc = (struct stats_pwr_wghfreq *) ((char *) a->buf[curr]  + i * a->msize * a->nr2);
		spp = (struct stats_pwr_wghfreq *) ((char *) a->buf[!curr] + i * a->msize * a->nr2);

		/* Should current CPU (including CPU "all") be displayed? */
		if (a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))) {

			/* Yes... */
			tisfreq = 0;
			tis = 0;

			for (k = 0; k < a->nr2; k++) {

				spc_k = (struct stats_pwr_wghfreq *) ((char *) spc + k * a->msize);
				if (!spc_k->freq)
					break;
				spp_k = (struct stats_pwr_wghfreq *) ((char *) spp + k * a->msize);

				tisfreq += (spc_k->freq / 1000) *
				           (spc_k->time_in_state - spp_k->time_in_state);
				tis     += (spc_k->time_in_state - spp_k->time_in_state);
			}

			if (!i) {
				/* This is CPU "all" */
				strcpy(cpuno, "all");
			}
			else {
				sprintf(cpuno, "%d", i - 1);
			}

			xprintf(tab, "<cpuwfreq number=\"%s\" "
				"weighted-frequency=\"%.2f\"/>",
				cpuno,
				tis ? ((double) tisfreq) / tis : 0.0);
		}
	}

	xprintf(--tab, "</cpu-weighted-frequency>");
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_power_management(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display USB devices statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_pwr_usb_stats(struct activity *a, int curr, int tab,
					unsigned long long itv)
{
	int i;
	struct stats_pwr_usb *suc;

	if (!IS_SELECTED(a->options) || (a->nr <= 0))
		goto close_xml_markup;

	xml_markup_power_management(tab, OPEN_XML_MARKUP);
	tab++;

	xprintf(tab++, "<usb-devices>");

	for (i = 0; i < a->nr; i++) {
		suc = (struct stats_pwr_usb *) ((char *) a->buf[curr]  + i * a->msize);

		if (!suc->bus_nr)
			/* Bus#0 doesn't exist: We are at the end of the list */
			break;

		xprintf(tab, "<usb bus_number=\"%d\" idvendor=\"%x\" idprod=\"%x\" "
			     "maxpower=\"%u\" manufact=\"%s\" product=\"%s\"/>",
			suc->bus_nr,
			suc->vendor_id,
			suc->product_id,
			suc->bmaxpower << 1,
			suc->manufacturer,
			suc->product);
	}

	xprintf(--tab, "</usb-devices>");
	tab--;

close_xml_markup:
	if (CLOSE_MARKUP(a->options)) {
		xml_markup_power_management(tab, CLOSE_XML_MARKUP);
	}
}

/*
 ***************************************************************************
 * Display filesystems statistics in XML.
 *
 * IN:
 * @a		Activity structure with statistics.
 * @curr	Index in array for current sample statistics.
 * @tab		Indentation in XML output.
 * @itv		Interval of time in jiffies.
 ***************************************************************************
 */
__print_funct_t xml_print_filesystem_stats(struct activity *a, int curr, int tab,
					   unsigned long long itv)
{
	int i;
	struct stats_filesystem *sfc;

	xprintf(tab, "<filesystems>");
	tab++;

	for (i = 0; i < a->nr; i++) {

		sfc = (struct stats_filesystem *) ((char *) a->buf[curr] + i * a->msize);

		if (!sfc->f_blocks)
			/* Size of filesystem is null: We are at the end of the list */
			break;

		xprintf(tab, "<filesystem fsname=\"%s\" "
			"MBfsfree=\"%.0f\" "
			"MBfsused=\"%.0f\" "
			"fsused-percent=\"%.2f\" "
			"ufsused-percent=\"%.2f\" "
			"Ifree=\"%llu\" "
			"Iused=\"%llu\" "
			"Iused-percent=\"%.2f\"/>",
			sfc->fs_name,
			(double) sfc->f_bfree / 1024 / 1024,
			(double) (sfc->f_blocks - sfc->f_bfree) / 1024 / 1024,
			/* f_blocks is not null. But test it anyway ;-) */
			sfc->f_blocks ? SP_VALUE(sfc->f_bfree, sfc->f_blocks, sfc->f_blocks)
				      : 0.0,
			sfc->f_blocks ? SP_VALUE(sfc->f_bavail, sfc->f_blocks, sfc->f_blocks)
				      : 0.0,
			sfc->f_ffree,
			sfc->f_files - sfc->f_ffree,
			sfc->f_files ? SP_VALUE(sfc->f_ffree, sfc->f_files, sfc->f_files)
				     : 0.0);
	}

	xprintf(--tab, "</filesystems>");
}
