diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/bxb.c wfdb-10.5.5/app/bxb.c
--- wfdb-10.5.4/app/bxb.c	2002-11-24 15:15:04.000000000 -0500
+++ wfdb-10.5.5/app/bxb.c	2010-08-11 08:51:18.000000000 -0400
@@ -1,9 +1,9 @@
 /* file: bxb.c		G. Moody	14 December 1987
-			Last revised:	24 November 2002
+			Last revised:	 10 August 2010
 
 -------------------------------------------------------------------------------
 bxb: ANSI/AAMI-standard beat-by-beat annotation file comparator
-Copyright (C) 2002 George B. Moody
+Copyright (C) 2010 George B. Moody
 
 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
@@ -1074,7 +1074,13 @@
     sstat("      F missed in shutdown", "%6.2f", Fx, Fn+Fs+Fv+Ff+Fq+Fo+Fx);
     if (fflag == 1 || fflag == 3 || fflag == 4 || fflag == 6)
 	(void)fprintf(sfile, "       Total shutdown time: ");
-    (void)fprintf(sfile, "%5ld seconds\n", shut_down);
+    if (fflag != 2 && fflag != 5)
+        (void)fprintf(sfile, "%5ld seconds\n", shut_down);
+    else 
+        (void)fprintf(sfile, "%5ld seconds %ld %ld %ld %ld %ld\n", shut_down,
+		      Nn+Ns+Nv+Nf+Nq+No+Nx, Sn+Ss+Sv+Sf+Sq+So+Sx,
+		      Vn+Vs+Vv+Vf+Vq+Vo+Vx, Fn+Fs+Fv+Ff+Fq+Fo+Fx,
+		      Qn+Qs+Qv+Qf+Qq+Qo+Qx);
 }
 
 static char *help_strings[] = {
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/ecgeval.c wfdb-10.5.5/app/ecgeval.c
--- wfdb-10.5.4/app/ecgeval.c	2006-11-30 10:09:01.000000000 -0500
+++ wfdb-10.5.5/app/ecgeval.c	2010-07-27 13:52:41.000000000 -0400
@@ -1,9 +1,9 @@
 /* file: ecgeval.c	G. Moody	22 March 1992
-			Last revised:  30 November 2006
+			Last revised:   27 July 2010
 
 -------------------------------------------------------------------------------
 ecgeval: Generate and run a script of commands to compare sets of annotations
-Copyright (C) 1992-2006 George B. Moody
+Copyright (C) 1992-2010 George B. Moody
 
 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
@@ -384,17 +384,19 @@
 	    getans(tans, 20);
 	    if (tans[0] == 'a') {
 		if ((sfile = fopen(scriptname, "a")) == NULL)
-		    (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n");
+		    (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n",
+				  scriptname);
 	    }
 	    else if (tans[0] == 'r') {
 		if ((sfile = fopen(scriptname, "w")) == NULL)
-		    (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n");
+		    (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n",
+				  scriptname);
 	    }
 	    else
 		sfile = NULL;
 	}
 	else if ((sfile = fopen(scriptname, "w")) == NULL)
-	    (void)fprintf(stderr, "Sorry, I can't create `%s'.\n");
+	    (void)fprintf(stderr, "Sorry, I can't create `%s'.\n", scriptname);
 
 	if (sfile == NULL)
 	    scriptname[0] = '\0';
@@ -424,17 +426,19 @@
 	    getans(tans, 20);
 	    if (tans[0] == 'a') {
 		if ((rfile = fopen(reportname, "a")) == NULL)
-		    (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n");
+		    (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n",
+				  reportname);
 	    }
 	    else if (tans[0] == 'r') {
 		if ((rfile = fopen(reportname, "w")) == NULL)
-		    (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n");
+		    (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n",
+				  reportname);
 	    }
 	    else
 		rfile = NULL;
 	}
 	else if ((rfile = fopen(reportname, "w")) == NULL)
-	    (void)fprintf(stderr, "Sorry, I can't create `%s'.\n");
+	    (void)fprintf(stderr, "Sorry, I can't create `%s'.\n", reportname);
 
 	if (rfile == NULL)
 	    reportname[0] = '\0';
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/psfd.c wfdb-10.5.5/app/psfd.c
--- wfdb-10.5.4/app/psfd.c	2009-05-27 15:47:52.000000000 -0400
+++ wfdb-10.5.5/app/psfd.c	2010-07-27 13:53:48.000000000 -0400
@@ -1,9 +1,9 @@
 /* file: psfd.c		G. Moody         9 August 1988
-			Last revised:	  27 May 2009
+			Last revised:	  27 July 2010
 
 -------------------------------------------------------------------------------
 psfd: Produces annotated full-disclosure ECG plots on a PostScript device
-Copyright (C) 1988-2009 George B. Moody
+Copyright (C) 1988-2010 George B. Moody
 
 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
@@ -379,7 +379,7 @@
 		if (Mflag < 0 || Mflag > 3) {
 		    (void)fprintf(stderr,
 			  "%s: incorrect format (%d) specified after -M\n",
-				  pname);
+				  pname, Mflag);
 		    exit(1);
 		}
 	    }
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/rdsamp.c wfdb-10.5.5/app/rdsamp.c
--- wfdb-10.5.4/app/rdsamp.c	2010-07-02 00:39:48.000000000 -0400
+++ wfdb-10.5.5/app/rdsamp.c	2010-10-06 22:01:25.000000000 -0400
@@ -1,5 +1,5 @@
 /* file: rdsamp.c	G. Moody	 23 June 1983
-			Last revised:   16 March 2010
+			Last revised:	6 October 2010
 
 -------------------------------------------------------------------------------
 rdsamp: Print an arbitrary number of samples from each signal
@@ -38,6 +38,12 @@
 #define HHMMSS	    7
 #define SAMPLES     8
 
+#define WFDBXMLPROLOG  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<?xml-stylesheet type=\"text/xsl\"" \
+ " href=\"wfdb.xsl\"?>\n" \
+ "<!DOCTYPE wfdbsampleset PUBLIC \"-//PhysioNet//DTD WFDB 1.0//EN\"" \
+ " \"http://physionet.org/physiobank/database/XML/wfdb.dtd\">\n"
+
 char *pname;
 
 main(argc, argv)
@@ -47,7 +53,7 @@
     char *record = NULL, *search = NULL, *escapify(), *prog_name();
     char *invalid, *snfmt, *tfmt, *tnfmt, *tufmt, *vfmt, speriod[16], tustr[16];
     int cflag = 0, highres = 0, i, isiglist, nsig, nosig = 0, pflag = 0, s,
-	*sig = NULL, timeunits = SECONDS, vflag = 0;
+	*sig = NULL, timeunits = SECONDS, vflag = 0, xflag = 0;
     WFDB_Frequency freq;
     WFDB_Sample *v;
     WFDB_Siginfo *si;
@@ -132,6 +138,9 @@
 	  case 'v':	/* verbose output -- include column headings */
 	    vflag = 1;
 	    break;
+	  case 'X':	/* output in WFDB-XML format */
+	    xflag = cflag = vflag = 1; /* format is CSV inside an XML wrapper */
+	    break;
 	  default:
 	    (void)fprintf(stderr, "%s: unrecognized option %s\n", pname,
 			  argv[i]);
@@ -245,7 +254,6 @@
 	}
 	else {	/* output in raw units */
 	    tnfmt = "'sample #'";
-	    //	    sprintf(tufmt, "'%g sec'", 1./freq);
 	    tfmt = "%ld";
 	    vfmt = ",%d";
 	}
@@ -293,12 +301,20 @@
 	}
     }
 
+    /* Print WFDB-XML prolog if '-x' option selected. */
+    if (xflag) {
+	printf(WFDBXMLPROLOG);
+	printf("<wfdbsampleset>\n"
+	       "<samplingfrequency>%g</samplingfrequency>\n"
+	       "<signals>%d</signals>\n<description>",
+	       freq, nsig);
+    }
     /* Print column headers if '-v' option selected. */
     if (vflag) {
 	char *p, *t;
 	int j, l;
 
-	(void)printf(tnfmt);
+	(void)printf("%s", tnfmt);
 
 	for (i = 0; i < nsig; i++) {
 	    /* Check if a default signal description was provided by looking
@@ -325,7 +341,7 @@
 		p = escapify(p);
 	    (void)printf(snfmt, p);
 	}
-    
+	if (xflag) (void)printf("</description>");
 	(void)printf("\n");
     }
 
@@ -338,7 +354,8 @@
 	if (vflag) {
 	    char s[12];
 
-	    (void)printf(tufmt);
+	    if (xflag) (void)printf("<units>");
+	    (void)printf("%s", tufmt);
 
 	    for (i = 0; i < nsig; i++) {
 		p = si[sig[i]].units;
@@ -360,10 +377,11 @@
 		    (void)printf(",'%s'", p);
 		}
 	    }
-
+	    if (xflag) (void)printf("</units>");
 	    (void)printf("\n");
 	}
 
+	if (xflag) (void)printf("<samplevectors>\n", nsig+1);
 	while ((to == 0L || from < to) && getvec(v) >= 0) {
 	    if (cflag == 0) {
 	      switch (timeunits) {
@@ -412,13 +430,14 @@
 		    (void)printf(vfmt,
 		     ((double)v[sig[i]] - si[sig[i]].baseline)/si[sig[i]].gain);
 		else
-		    (void)printf(invalid);
+		    (void)printf("%s", invalid);
 	    }
 	    (void)printf("\n");
 	}
     }
 
     else {	/* output in raw units */
+	if (xflag) (void)printf("<samplevectors>\n", nsig+1);
 	while ((to == 0L || from < to) && getvec(v) >= 0) {
 	    (void)printf(tfmt, from++);
 	    for (i = 0; i < nsig; i++)
@@ -427,6 +446,9 @@
 	}
     }
 
+    if (xflag)		/* print trailer if WFDB-XML output was selected */
+	printf("</samplevectors>\n</wfdbsampleset>\n");
+
     exit(0);
 }
 
@@ -494,6 +516,7 @@
  "		the time specified with -f, and begin printing then",
  " -t TIME     stop at specified time",
  " -v          print column headings",
+ " -X          output in WFDB-XML format",
 NULL
 };
 
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/snip.c wfdb-10.5.5/app/snip.c
--- wfdb-10.5.4/app/snip.c	2009-04-17 01:50:17.000000000 -0400
+++ wfdb-10.5.5/app/snip.c	2010-07-27 13:59:43.000000000 -0400
@@ -1,8 +1,8 @@
 /* file: snip.c		G. Moody	30 July 1989
-			Last revised:	17 April 2009
+			Last revised:	27 July 2010
 -------------------------------------------------------------------------------
 snip: Copy an excerpt of a database record
-Copyright (C) 1989-2009 George B. Moody
+Copyright (C) 1989-2010 George B. Moody
 
 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
@@ -341,7 +341,7 @@
 	wfdb_fgets(buf, sizeof(buf), ihfile);
 
 	/* Start writing the master output header. */
-	fprintf(ohfile, "%s/%d %d %.12g %d", nrec, nseg, nsig, sfreq, to-from);
+	fprintf(ohfile, "%s/%d %d %.12g %ld", nrec, nseg, nsig, sfreq, to-from);
 	if (tstring[0]) fprintf(ohfile, " %s", tstring);
 	fprintf(ohfile, "\r\n");
 
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/sortann.c wfdb-10.5.5/app/sortann.c
--- wfdb-10.5.4/app/sortann.c	2009-02-04 10:54:28.000000000 -0500
+++ wfdb-10.5.5/app/sortann.c	2010-10-04 14:06:44.000000000 -0400
@@ -1,8 +1,8 @@
 /* file sortann.c	G. Moody	 7 April 1997
-			Last revised:	4 February 2009
+			Last revised:	4 October 2010
 -------------------------------------------------------------------------------
 sortann: Rearrange annotations in canonical order
-Copyright (C) 1997-2009 George B. Moody
+Copyright (C) 1997-2010 George B. Moody
 
 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
@@ -162,9 +162,9 @@
 	exit(1);
     }
 
-    /* By setting WFDBNOSORT, we ensure that wfdbquit won't invoke this program
+    /* By setting WFDBANNSORT, we ensure that wfdbquit won't invoke this program
        recursively if something goes wrong. */
-    putenv("WFDBNOSORT=1");
+    putenv("WFDBANNSORT=0");
 
     if ((sps = sampfreq(record)) < 0.)
 	(void)setsampfreq(sps = WFDB_DEFFREQ);
@@ -245,9 +245,12 @@
 	(newp->annotation).aux = p;
     }
     if (lastp == &annlist ||
-	pa->chan > (lastp->annotation).chan ||
-	pa->num > (lastp->annotation).num ||
-	pa->time > (lastp->annotation).time) {
+	pa->time > (lastp->annotation).time ||
+	(pa->time == (lastp->annotation).time &&
+	 pa->num > (lastp->annotation).num) ||
+	(pa->time == (lastp->annotation).time &&
+	 pa->num == (lastp->annotation).num &&
+	 pa->chan > (lastp->annotation).chan)) {
 	/* this annotation is in order -- add to end of list */
 	newp->prev = lastp;
 	lastp->next = newp;
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/sumstats.c wfdb-10.5.5/app/sumstats.c
--- wfdb-10.5.4/app/sumstats.c	2005-08-19 21:25:59.000000000 -0400
+++ wfdb-10.5.5/app/sumstats.c	2010-08-10 22:28:17.000000000 -0400
@@ -1,8 +1,8 @@
 /* file: sumstats.c	G. Moody	17 August 1989
-   			Last revised:	19 August 2005
+   			Last revised:	10 August 2010
 -------------------------------------------------------------------------------
 sumstats: Derive aggregate statistics from bxb, rxr, or epic line-format output
-Copyright (C) 1989-2005 George B. Moody
+Copyright (C) 1989-2010 George B. Moody
 
 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
@@ -34,7 +34,8 @@
 #include <stdio.h>
 #include <wfdb/wfdb.h>
 
-static int nrec, NQS, NQP, NVS, NVP, NVF, NSVS, NSVP, NRRE;
+static int nrec, Nrec, Vrec, Frec;
+static int NQS, NQP, NVS, NVP, NVF, NSVS, NSVP, NRRE;
 static long Nn, Ns, Nv, No, Nx,
 	    Sn, Ss, Sv, So, Sx,
 	    Vn, Vs, Vv, Vo, Vx,
@@ -45,8 +46,9 @@
 static long CTS, CFN, CTP, CFP, STS, SFN, STP, SFP, LTS, LFN, LTP, LFP;
 static long ETS, EFN, ETP, EFP;
 static long NCS, NCP, NSS, NSP, NLS, NLP, NES, NEP, NDS, NDP;
+static long NT, VT, FT, QT;
 static long detected_episode_length, overlap, total_episode_length;
-static double CQS, CQP, CVS, CVP, CVF, CSVS, CSVP, CRRE;
+static double CQS, CQP, CVS, CVP, CVF, CSVS, CSVP, CRRE, CBM, CNM, CVM, CFM;
 static double CCS, CCP, CSS, CSP, CLS, CLP, CES, CEP, CDS, CDP, CERR, CMREF;
 char *pname;		/* name by which this program was invoked */
 
@@ -178,6 +180,18 @@
 	(void)printf("______________\n");
 	(void)printf("Sum   %4ld %4ld %4ld %4ld", Nx, Vx, Fx, Qx);
 	(void)printf("                               %4ld seconds\n", ST);
+	(void)printf("Gross                    ");
+        pstat(" %6.2f", (double)Nx+Vx+Fx+Qx, (double)(NT+VT+FT+QT));
+	pstat(" %6.2f", (double)Nx, (double)(NT));
+	pstat(" %6.2f", (double)Vx, (double)(VT));
+	pstat(" %6.2f", (double)Fx, (double)(FT));
+	(void)printf("\n");
+	(void)printf("Average                  ");
+	pstat(" %6.2f", CBM, (double)nrec);
+	pstat(" %6.2f", CNM, (double)Nrec);
+	pstat(" %6.2f", CVM, (double)Vrec);
+	pstat(" %6.2f", CFM, (double)Frec);
+	(void)printf("\n");
 	break;
       case 3:	/* rxr VE run-by-run table */
       case 6:	/* rxr SVE run-by-run table */
@@ -287,7 +301,8 @@
     static char rts[20], tts[20];
     static double rre, ds, dp, err, mref;
     static int cts, cfn, ctp, cfp, sts, sfn, stp, sfp, lts, lfn, ltp, lfp;
-    static int ets, efn, etp, efp;
+    static int dummy, nt, vt, ft, qt;
+    static long ets, efn, etp, efp;
     static long nn, sn, vn, fn, on, ns, ss, vs, fs, os;
     static long nv, sv, vv, fv, ov, no, so, vo, fo;
     static long nx, sx, vx, fx, qx, st;
@@ -326,11 +341,19 @@
 	return (1);
       case 2:	/* bxb -l shutdown report */
 	st = -1L;
-	(void)sscanf(s, "%s%ld%ld%ld%ld%s%s%s%s%ld", rec,
-	       &nx, &vx, &fx, &qx, mb, mn, mv, mf, &st);
+	(void)sscanf(s, "%s%ld%ld%ld%ld%s%s%s%s%ld seconds %ld%ld%ld%ld%ld",
+		     rec, &nx, &vx, &fx, &qx, mb, mn, mv, mf, &st,
+		     &nt, &dummy, &vt, &ft, &qt);
 	if (st < 0L) return (0);
 	Nx += nx; Vx += vx; Fx += fx; Qx += qx; ST += st;
-	nrec++;
+	NT += nt; VT += vt; FT += ft; QT += qt;
+	if (nt + vt + ft + qt) {
+	    CBM += (nx + vx + fx + qx)/(double)(nt + vt + ft + qt); nrec++;
+	    if (nt) { CNM += nx/(double)nt; Nrec++; NT += nt; }
+	    if (vt) { CVM += vx/(double)vt; Vrec++; VT += vt; }
+	    if (ft) { CFM += fx/(double)ft; Frec++; FT += ft; }
+	    QT += qt;
+	}
 	return (1);
       case 3:	/* rxr run-by-run report */
       case 6:
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/wrann.c wfdb-10.5.5/app/wrann.c
--- wfdb-10.5.4/app/wrann.c	2009-08-06 14:56:35.000000000 -0400
+++ wfdb-10.5.5/app/wrann.c	2010-07-27 14:06:58.000000000 -0400
@@ -1,9 +1,9 @@
 /* file wrann.c		G. Moody	 6 July 1983
-			Last revised:   6 August 2009
+			Last revised:	27 July 2010
 
 -------------------------------------------------------------------------------
 wrann: Translate an ASCII file in 'rdann' output format to an annotation file
-Copyright (C) 1983-2009 George B. Moody
+Copyright (C) 1983-2010 George B. Moody
 
 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
@@ -128,11 +128,11 @@
 	/* Create an aux string if needed. */
 	if (*annstr || ap) {
 	    if (*annstr && ap)
-		sprintf(a+1, "%s %s\0", annstr, ap+1);
+		sprintf(a+1, "%s %s", annstr, ap+1);
 	    else if (*annstr)
-		sprintf(a+1, "%s\0", annstr);
+		sprintf(a+1, "%s", annstr);
 	    else
-		sprintf(a+1, "%s\0", ap+1);
+		sprintf(a+1, "%s", ap+1);
 	    *a = strlen(a+1);
 	    annot.aux = a;
 	}
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/wrsamp.c wfdb-10.5.5/app/wrsamp.c
--- wfdb-10.5.4/app/wrsamp.c	2010-07-02 00:17:38.000000000 -0400
+++ wfdb-10.5.5/app/wrsamp.c	2010-10-06 22:04:19.000000000 -0400
@@ -1,5 +1,5 @@
 /* file: wrsamp.c	G. Moody	10 August 1993
-			Last revised:    1 July 2010
+			Last revised:   6 October 2010
 
 -------------------------------------------------------------------------------
 wrsamp: Select fields or columns from a file and generate a WFDB record
@@ -33,6 +33,8 @@
 #define DITHER	        (((double)rand() + (double)rand())/RAND_MAX - 1.0)
 
 char *pname;
+unsigned int ncols = 0;
+unsigned int nosig = 0;
 
 char *read_line(FILE *ifile, char rsep)
 {
@@ -130,6 +132,8 @@
     int i, m = (strlen(line) + 1)/2, n = 0, state = 0;
     char d, *p, *q = NULL;
 
+    if (m < nosig) m = nosig;
+    if (m < ncols) m = ncols;
     SSTRCPY(tbuf, line);
     p = tbuf-1;
     SREALLOC(ta, sizeof(int)*2 + sizeof(char *)*m, 1);
@@ -205,13 +209,13 @@
 {
     char **ap, *cp, **desc, *gain = "", *ifname = "(stdin)",
 	*line = NULL, ofname[40], *p, *record = NULL, rsep = '\n',
-	*scale = "", sflag = 0, trim = 0, **units, *prog_name();
-    static char btime[25];
+	*scale = "", sflag = 0, trim = 0, Xflag = 0, **units, *prog_name();
+    static char btime[25], **dstrings, **ustrings;
     double freq = WFDB_DEFFREQ, *scalef, v;
 #ifndef atof
     double atof();
 #endif
-    int c, cf = 0, dflag = 0, format = 16, *fv = NULL, i, labels, mf, zflag = 0;
+    int c, cf = 0, dflag = 0, format = 16, *fv = NULL, i, j, mf, zflag = 0;
     FILE *ifile = stdin;
     long t = 0L, t0 = 0L, t1 = 0L;
 #ifndef atol
@@ -220,7 +224,6 @@
     Tokenarray *ta;
     WFDB_Sample *vout;
     WFDB_Siginfo *si;
-    unsigned int nf = 0;
     void help();
 
     pname = prog_name(argv[0]);
@@ -338,7 +341,11 @@
 	}
     }
 
-    /* read the first line of the input file */
+    nosig = argc - i;	/* any remaining arguments are column numbers */
+
+    /* Determine the number of input columns, and (if present) the signal
+       names and the units for each signal.  Start by reading the first line
+       of the input file. */
     if ((line = read_line(ifile, rsep)) == NULL) {
 	if (rsep != '\n') {
 	    if (record == NULL)
@@ -355,69 +362,130 @@
 	    (void)fprintf(stderr, "%s: no newlines in input\n", pname);
 	exit(3);
     }
- 
-    /* unless -s was given, note if it contains any tab characters */
-    if (sflag == 0 && line_has_tab(line))
-	defpmode.delim = "\t";
-
-    /* note if it contains any alphabetic characters */
-    labels = line_has_alpha(line);
-
-    /* parse it into tokens */
-    ta = parseline(line, NULL);
+    /* Recognize WFDB-XML format if present. */
+    if (strncmp(line, "<?xml ", 6) == 0) {
+	Xflag = zflag = 1; /* ignore column 0 */
+	do {
+	    if ((line = read_line(ifile, rsep)) == NULL) {
+		(void)fprintf(stderr,
+			      "%s: XML input, but no <wfdbsampleset> found\n",
+			      pname);
+		exit(1);
+	    }
+	} while (strcmp(line, "<wfdbsampleset>"));
+	do {
+	    if ((line = read_line(ifile, rsep)) == NULL) {
+		(void)fprintf(stderr, "%s: empty <wfdbsampleset>\n", pname);
+		exit(1);
+	    }
+	    if (strncmp(line, "<samplingfrequency>", 19) == 0)
+		sscanf(line+19, "%lf", &freq);
+	    else if (strncmp(line, "<description>", 13) == 0) {
+		line[strlen(line)-14] = '\0';	/* strip </description> */
+		ta = parseline(line+13, NULL);	/* skip <description> */
+		SUALLOC(dstrings, ta->ntokens, sizeof(char *));
+		for (i = 0; i < ta->ntokens; i++)
+		    SSTRCPY(dstrings[i], ta->token[i]);
+		ncols = ta->ntokens;
+	    }
+	    else if (strncmp(line, "<units>", 7) == 0) {
+		line[strlen(line)-8] = '\0';	/* strip </units> */
+		ta = parseline(line+7, NULL);	/* skip <units> */
+		SUALLOC(ustrings, ta->ntokens, sizeof(char *));
+		for (i = 0; i < ta->ntokens; i++)
+		    SSTRCPY(ustrings[i], ta->token[i]);
+	    }
+	} while (strcmp(line, "<samplevectors>"));
+	if ((line = read_line(ifile, rsep)) == NULL) {
+	    (void)fprintf(stderr, "%s: empty <wfdbsampleset>\n", pname);
+	    exit(1);
+	}
+    }		 
 
+    else {	/* non-XML input */
+	/* Unless -s was given, note if line 0 contains any tab characters. */
+	if (sflag == 0 && line_has_tab(line))
+	    defpmode.delim = "\t";
+	/* If it contains any alphabetic characters, save the signal names. */
+	if (line_has_alpha(line)) {
+	    ta = parseline(line, NULL);
+	    SALLOC(dstrings, ta->ntokens, sizeof(char *));
+	    for (i = 0; i < ta->ntokens; i++) {
+		while (*(ta->token[i]) == ' ')
+		    (ta->token[i])++;
+		SSTRCPY(dstrings[i], ta->token[i]);
+	    }
+	    ncols = ta->ntokens;
+	    /* Read the second line. */
+	    if ((line = read_line(ifile, rsep)) == NULL) {
+		(void)fprintf(stderr, "%s: no data\n", pname);
+		exit(1);
+	    }
+	    /* If it has any alphabetic characters, save the unit strings. */
+	    else if (line_has_alpha(line)) {
+		ta = parseline(line, NULL);
+		SALLOC(ustrings, ta->ntokens, sizeof(char *));
+		for (i = 0; i < ta->ntokens; i++) {
+		    while (*(ta->token[i]) == ' ')
+			(ta->token[i])++;
+		    if (*(ta->token[i]) == '(')
+		        (ta->token[i])++;
+		    SSTRCPY(ustrings[i], ta->token[i]);
+		}
+		/* Read the third line. */
+		if ((line = read_line(ifile, rsep)) == NULL) {
+		    (void)fprintf(stderr, "%s: no data\n", pname);
+		    exit(1);
+		}
+	    }
+	}
+    }
+ 
     /* read selected column numbers into fv[...] */
-    if (i < argc) {
-	SUALLOC(fv, argc - i, sizeof(int));
-	while (i < argc) {
-	    if (sscanf(argv[i++], "%d", &fv[nf]) != 1 ||
-		fv[nf] < 0 || fv[nf] >= ta->ntokens) {
+    if (nosig) {
+	SUALLOC(fv, nosig, sizeof(int));
+	for (i = argc - nosig, j = 0; i < argc; i++, j++) {
+	    if (sscanf(argv[i], "%d", &fv[j]) != 1 ||
+		fv[j] < 0 || fv[j] >= ncols) {
 		(void)fprintf(stderr, "%s: unrecognized argument %s\n",
-			      pname, argv[--i]);
+			      pname, argv[i]);
 		exit(1);
 	    }
-	    nf++;
 	}
     }
-    /* if no columns were specified, copy all columns (or all except 0) */
-    else {
-	int i, j;
-
-	nf = ta->ntokens - zflag;
-	SUALLOC(fv, nf, sizeof(int));
-	for (i = 0, j = zflag; i < nf; i++, j++)
+    else { /* copy all column numbers (or all except 0) into fv[...] */
+	nosig = ncols - zflag;
+	SUALLOC(fv, nosig, sizeof(int));
+	for (i = 0, j = zflag; i < nosig; i++, j++)
 	    fv[i] = j;
     }
 
-    /* allocate arrays */
-    SUALLOC(vout, nf, sizeof(WFDB_Sample));
-    SUALLOC(si, nf, sizeof(WFDB_Siginfo));
-    SUALLOC(scalef, nf, sizeof(double));
+    SUALLOC(vout, nosig, sizeof(WFDB_Sample));
+    SUALLOC(si, nosig, sizeof(WFDB_Siginfo));
+    SUALLOC(scalef, nosig, sizeof(double));
 
-     /* open the output record */
+    /* open the output record */
     if (record == NULL)
 	(void)sprintf(ofname, "-");
     else
 	(void)sprintf(ofname, "%s.dat", record);
-    for (i = 0; i < nf; i++) {
+ 
+    for (i = 0; i < nosig; i++) {
 	si[i].fname = ofname;
-	si[i].desc = NULL;
-	if (labels) { /* set the signal descriptions from the column headings */
-	    char *p = ta->token[fv[i]], *q;
-
-	    while (*p == ' ') p++;
-	    q = p + strlen(p);
-	    while (*(q-1) == ' ') q--;
-	    *q = '\0';
-	    SSTRCPY(si[i].desc, p);
+	if (dstrings) {
+	    SSTRCPY(si[i].desc, dstrings[fv[i]]);
 	}
 	else {
 	    char tdesc[16];
-
-	    (void)sprintf(tdesc, "olumn %d", fv[i]);
+	    
+	    (void)sprintf(tdesc, "col %d", fv[i]);
 	    SSTRCPY(si[i].desc, tdesc);
 	}
-	si[i].units = "";
+	if (ustrings) {
+	    SSTRCPY(si[i].units, ustrings[fv[i]]);
+	}
+	else
+	    si[i].units = "";
 	si[i].group = 0;
 	si[i].fmt = format;
 	si[i].spf = 1;
@@ -449,39 +517,11 @@
 		scale++;
     }
 
-    if (labels) {	/* read the second line of input */
-	if ((line = read_line(ifile, rsep)) == NULL) {
-	    (void)fprintf(stderr, "%s: no input data\n", pname);
-	    exit(4);
-	}
-	if (line_has_alpha(line)) {
-	    ta = parseline(line, NULL);
-	    /* Copy units strings after trimming any surrounding spaces,
-	       parentheses, or brackets, and after replacing embedded spaces
-	       with underscores. */
-	    for (i = 0; i < nf; i++) {
-		char *p = ta->token[fv[i]], *q;
-
-		while (*p == ' ') p++;
-		if (*p == '(' || *p == '[') p++;
-		q = p + strlen(p);
-		while (*(q-1) == ' ') q--;
-		if (*(q-1) == ')' || *(q-1) == ']') q--;
-		*q = '\0';
-		while (--q > p)
-		    if (*q == ' ') *q = '_';
-		si[i].units = NULL;
-		SSTRCPY(si[i].units, p);
-	    }
-	    line = read_line(ifile, rsep);
-	}
-    }
-
     /* discard any additional lines containing text */
     while (line_has_alpha(line))
 	line = read_line(ifile, rsep);
 
-    if (osigfopen(si, nf) < nf || setsampfreq(freq) < 0)
+    if (osigfopen(si, nosig) < nosig || setsampfreq(freq) < 0)
 	exit(2);
 
     /* skip any unwanted samples at the beginning */
@@ -503,7 +543,8 @@
     /* read and copy samples */
     while (line != NULL && (t1 == 0L || t++ < t1)) {
 	ta = parseline(line, NULL);
-	for (i = 0; i < nf; i++) {
+
+	for (i = 0; i < nosig; i++) {
 	    double v;
 
 	    if (sscanf(ta->token[fv[i]], "%lf", &v) == 1) {
@@ -517,7 +558,16 @@
 		vout[i] = WFDB_INVALID_SAMPLE;
 	}
 	if (putvec(vout) < 0) break;
+
 	line = read_line(ifile, rsep);
+	if (line && Xflag && strncmp(line, "</samplevectors>", 16) == 0) {
+	    if (line = read_line(ifile, rsep))
+		if (strcmp(line, "</wfdbsampleset>")) {
+		    fprintf(stderr, 
+			  "%s: (warning) unexpected EOF in XML input\n", pname);
+		}
+	    break;
+	}
     }
 
     /* write the header */
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/check-manifest wfdb-10.5.5/check-manifest
--- wfdb-10.5.4/check-manifest	2005-08-12 01:53:16.000000000 -0400
+++ wfdb-10.5.5/check-manifest	2010-07-27 17:17:18.000000000 -0400
@@ -1,14 +1,16 @@
 #!/bin/sh
 # file: check-manifest		G. Moody	14 December 2001
-#				Last revised:    1 November 2002
+#				Last revised:     27 July 2010
 # Verify tarball contents
 
 PACKAGE=$1
-sort -f <../${PACKAGE}-MANIFEST >../sort.$$
+sort -f <MANIFEST | grep . | grep -v ${PACKAGE} >../expected-MANIFEST
+sort -f <../${PACKAGE}-MANIFEST | grep . | sed 's+/$++' | \
+ grep -v ${PACKAGE} >../sort.$$
 mv ../sort.$$ ../${PACKAGE}-MANIFEST
-if diff MANIFEST ../${PACKAGE}-MANIFEST
+if diff ../expected-MANIFEST ../${PACKAGE}-MANIFEST
 then
-  rm -f ../${PACKAGE}-MANIFEST
+  rm -f ../expected-MANIFEST ../${PACKAGE}-MANIFEST
   cat <<EOF
 
 The contents of ${PACKAGE} match the MANIFEST -- no errors were detected.
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/conf/darwin.def wfdb-10.5.5/conf/darwin.def
--- wfdb-10.5.4/conf/darwin.def	2010-05-13 11:04:24.000000000 -0400
+++ wfdb-10.5.5/conf/darwin.def	2010-07-27 15:02:30.000000000 -0400
@@ -1,5 +1,5 @@
 # file: darwin.def	G. Moody and I. Henry	14 November 2002
-#				Last revised: 	  13 May 2010
+#				Last revised: 	  27 July 2010
 # 'make' definitions for compiling the WFDB Software Package under Darwin
 #
 # Based on 'freebsd.def'.
@@ -74,7 +74,7 @@
 
 # CFLAGS is the set of C compiler options.  CFLAGS should always include
 # CCDEFS.
-CFLAGS = $(MFLAGS) -g -O $(CCDEFS) $(LC) -I$(INCDIR)
+CFLAGS = $(MFLAGS) -g -O $(CCDEFS) $(LC) -I$(INCDIR) -arch i386 -arch x86_64 -arch ppc
 
 # LDFLAGS is appended to the C compiler command line to specify loading the
 # WFDB library.
@@ -146,7 +146,7 @@
 XLIBDIR = $(XHOME)/lib
 
 # WCFLAGS is the set of C compiler options to use when compiling WAVE.
-WCFLAGS = $(CFLAGS) -I$(OWINCDIR) -I$(XINCDIR)
+WCFLAGS = $(CFLAGS) -I$(OWINCDIR) -I$(XINCDIR) -arch i386
 
 # HELPOBJ can be set to "help.o" if you wish to recompile the XView spot help
 # functions in "wave/help.c" (recommended under Linux).
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/conf/darwin-slib.def wfdb-10.5.5/conf/darwin-slib.def
--- wfdb-10.5.4/conf/darwin-slib.def	2010-05-13 11:04:10.000000000 -0400
+++ wfdb-10.5.5/conf/darwin-slib.def	2010-07-27 15:02:57.000000000 -0400
@@ -1,5 +1,5 @@
 # file: darwin-slib.def	I. Henry and G. Moody	14 November 2002
-#				Last revised:	  13 May 2010
+#				Last revised:	  27 July 2010
 # Based on 'freebsd-slib.def'.
 
 # This section contains settings suitable for generating a shared library under
@@ -68,7 +68,7 @@
 
 # CFLAGS is the set of C compiler options used when compiling the shared
 # library.  CFLAGS should always include CCDEFS.
-CFLAGS = $(MFLAGS) -fPIC -fno-common -g -O $(CCDEFS) $(LC) -I$(INCDIR)
+CFLAGS = $(MFLAGS) -fPIC -fno-common -g -O $(CCDEFS) $(LC) -I$(INCDIR) -arch i386 -arch x86_64 -arch ppc
 
 # WFDBLIB_BASENAME is the name, without version numbers, of the alternate
 # library.  WFDBLIB_SONAME is the shared object name ("soname") of the
@@ -93,6 +93,7 @@
 # components have been compiled separately;  the list of *.o files that
 # make up the library will be appended to BUILDLIB.
 BUILDLIB = gcc $(MFLAGS) -dynamiclib \
+ -arch i386 -arch x86_64 -arch ppc \
  -install_name $(LIBDIR)/$(WFDBLIB_SONAME) \
  -compatibility_version $(MAJOR).$(MINOR) \
  -current_version $(MAJOR).$(MINOR).$(RELEASE) \
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/configure wfdb-10.5.5/configure
--- wfdb-10.5.4/configure	2008-11-19 14:52:23.000000000 -0500
+++ wfdb-10.5.5/configure	2010-10-06 22:34:31.000000000 -0400
@@ -1,6 +1,6 @@
 #! /bin/sh
 # file: configure             G. Moody         24 May 2000
-#                             Last revised:  19 November 2008
+#                             Last revised:   6 October 2010
 # Configuration script for the WFDB Software Package
 
 # This script was not generated using 'autoconf'.  If you can implement
@@ -474,7 +474,7 @@
     cat lib/Makefile.top conf/site.def lib/Makefile.tpl >lib/Makefile
 fi
 for D in app checkpkg convert data doc doc/wag-src doc/wpg-src doc/wug-src \
-         examples fortran psd wave waverc .
+         examples fortran psd wave waverc xml .
   do
   if [ -s $D/Makefile.top ]
       then
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/a2m.c wfdb-10.5.5/convert/a2m.c
--- wfdb-10.5.4/convert/a2m.c	2002-07-24 23:15:46.000000000 -0400
+++ wfdb-10.5.5/convert/a2m.c	2010-07-27 14:10:16.000000000 -0400
@@ -1,9 +1,9 @@
 /* file: a2m.c		G. Moody        9 June 1983
-			Last revised:  24 July 2002
+			Last revised:  27 July 2010
 
 -------------------------------------------------------------------------------
 a2m: Convert an AHA format annotation file to MIT format
-Copyright (C) 2002 George B. Moody
+Copyright (C) 1983-2010 George B. Moody
 
 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
@@ -135,7 +135,8 @@
 	    break;
 	  case 't':	/* file type follows */
 	    if (++i >= argc || (type = atoi(argv[i])) < 0 || type > 3) {
-		(void)fprintf(stderr, "%s: file type (0-3) must follow -t\n");
+		(void)fprintf(stderr, "%s: file type (0-3) must follow -t\n",
+			      pname);
 		exit(1);
 	    }
 	    break;
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/ahaecg2mit.c wfdb-10.5.5/convert/ahaecg2mit.c
--- wfdb-10.5.4/convert/ahaecg2mit.c	2008-07-23 15:03:14.000000000 -0400
+++ wfdb-10.5.5/convert/ahaecg2mit.c	2010-08-24 23:15:06.000000000 -0400
@@ -1,5 +1,5 @@
 /* file: ahaecg2mit.c		G. Moody		7 May 2008
-
+.				Last revised:	       24 August 2010
 Convert a *.ecg file from an AHA Database DVD to WFDB-compatible format
 */
 
@@ -10,7 +10,7 @@
 main(int argc, char **argv)
 {
     char *p, *record;
-    int i, sflag;
+    int i, sflag = 0;
     FILE *ifile;
     void process(char *r, FILE *f);
 
@@ -84,6 +84,6 @@
     }
     (void)newheader(record);
     wfdbquit();
-    fprintf(stderr, "wrote %s.atr, $s.dat, and %s.hea\n", record,record,record);
+    fprintf(stderr, "wrote %s.atr, %s.dat, and %s.hea\n", record,record,record);
     return;
 }
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/mit2edf.c wfdb-10.5.5/convert/mit2edf.c
--- wfdb-10.5.4/convert/mit2edf.c	2009-03-14 23:44:35.000000000 -0400
+++ wfdb-10.5.5/convert/mit2edf.c	2010-07-27 14:13:11.000000000 -0400
@@ -1,8 +1,8 @@
 /* file: mit2edf.c		G. Moody	2 November 2002
-				Last revised:    14 March 2009
+				Last revised:    27 July 2010
 -------------------------------------------------------------------------------
 Convert MIT format header and signal files to EDF (European Data Format) file
-Copyright (C) 2002-2009 George B. Moody
+Copyright (C) 2002-2010 George B. Moody
 
 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
@@ -328,7 +328,7 @@
 
     /* Number of samples per block. */
     for (i = 0; i < nsig; i++, p += 8) {
-	sprintf(buf, "%d", frames_per_block * si[i].spf);
+	sprintf(buf, "%ld", frames_per_block * si[i].spf);
 	strncpy(p, buf, strlen(buf));
     }
 
@@ -345,7 +345,7 @@
 	if (header[i] < 32 || header[i] > 126)
 	    fprintf(stderr,
 		    "WARNING (%s): output contains an invalid character, %d,"
-		    " at byte %ld\n", pname, header[i], i);
+		    " at byte %d\n", pname, header[i], i);
 
     /* In verbose mode, summarize what we've done so far. */
     if (vflag) {
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/mit2wav.c wfdb-10.5.5/convert/mit2wav.c
--- wfdb-10.5.4/convert/mit2wav.c	2009-06-10 12:21:03.000000000 -0400
+++ wfdb-10.5.5/convert/mit2wav.c	2010-07-27 14:14:36.000000000 -0400
@@ -1,8 +1,8 @@
 /* file: mit2wav.c	G. Moody	12 February 2003
-
+			Last revised:	  27 July 2010
 -------------------------------------------------------------------------------
 mit2wav: Convert WFDB format signal file(s) to .wav format
-Copyright (C) 2003 George B. Moody
+Copyright (C) 2003-2010 George B. Moody
 
 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
@@ -254,14 +254,15 @@
     (void)strcpy(tfname, "wavXXXXXX");
     (void)mkstemp(tfname);
     if ((ofile = fopen(tfname, "wb")) == NULL) {
-	(void)fprintf(stderr, "%s: can't create temporary file %s\n", tfname);
+	(void)fprintf(stderr, "%s: can't create temporary file %s\n",
+		      pname, tfname);
 	exit(1);
     }
     
     /* Write the header and format chunks, and the first 8 bytes of the
        data chunk, to the temporary file. */
     if (fwrite("RIFF", 1, 4, ofile) != 4) {
-	fprintf(stderr, "%s: can't write to %s\n", ofname);
+	fprintf(stderr, "%s: can't write to %s\n", pname, ofname);
 	exit(2);
     }
     out32(nsamp*framelen + 36);  /* nsamp*framelen sample bytes, and 36 more
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/wfdb2mat.c wfdb-10.5.5/convert/wfdb2mat.c
--- wfdb-10.5.4/convert/wfdb2mat.c	2010-03-16 11:37:26.000000000 -0400
+++ wfdb-10.5.5/convert/wfdb2mat.c	2010-07-27 14:16:20.000000000 -0400
@@ -1,5 +1,5 @@
 /* file: wfdb2mat.c	G. Moody	26 February 2009
-			Last revised:	  16 March 2010
+			Last revised:	  27 July 2010
 -------------------------------------------------------------------------------
 wfdb2mat: Convert (all or part of) a WFDB signal file to Matlab .mat format
 Copyright (C) 2009-2010 George B. Moody
@@ -337,7 +337,7 @@
 
     /* Summarize the contents of the .mat file. */
     printf("%s\n", p);
-    printf("val has %d row%s (signal%s) and %d column%s (sample%s/signal)\n",
+    printf("val has %d row%s (signal%s) and %ld column%s (sample%s/signal)\n",
 	   nosig, nosig == 1 ? "" : "s", nosig == 1 ? "" : "s",
 	   to-from, to == from+1 ? "" : "s", to == from+1 ? "" : "s");
     printf("Duration: %s\n", timstr(to-from));
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/doc/wag-src/rdsamp.1 wfdb-10.5.5/doc/wag-src/rdsamp.1
--- wfdb-10.5.4/doc/wag-src/rdsamp.1	2009-11-06 12:18:06.000000000 -0500
+++ wfdb-10.5.5/doc/wag-src/rdsamp.1	2010-08-25 23:31:06.000000000 -0400
@@ -1,4 +1,4 @@
-.TH RDSAMP 1 "6 November 2009" "WFDB 10.4.25" "WFDB Applications Guide"
+.TH RDSAMP 1 "25 August 2010" "WFDB 10.5.5" "WFDB Applications Guide"
 .SH NAME
 rdsamp \- read WFDB signal files
 .SH SYNOPSIS
@@ -91,6 +91,11 @@
 columns identifiable).  Names of units are shortened when necessary by
 omitting the final characters, since the initial characters are
 usually most important for distinguishing different units.
+.TP
+\fB-X\fR
+Produce output in WFDB-XML format (same as the CSV format produced using
+the \fB-c\fR option, but wrapped within an XML header and trailer).  This
+format is recognized and parsed automatically by \fBwrsamp\fR.
 
 .SH ENVIRONMENT
 .PP
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/lib/wfdb.h wfdb-10.5.5/lib/wfdb.h
--- wfdb-10.5.4/lib/wfdb.h	2010-07-14 01:28:54.000000000 -0400
+++ wfdb-10.5.5/lib/wfdb.h	2010-10-06 22:45:29.000000000 -0400
@@ -32,7 +32,7 @@
 /* WFDB library version. */
 #define WFDB_MAJOR   10
 #define WFDB_MINOR   5
-#define WFDB_RELEASE 4
+#define WFDB_RELEASE 5
 #define WFDB_NETFILES 1	/* if 1, library includes code for HTTP, FTP clients */
 #define WFDB_NETFILES_LIBCURL 1
 
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/Makefile.tpl wfdb-10.5.5/Makefile.tpl
--- wfdb-10.5.4/Makefile.tpl	2009-10-28 13:19:00.000000000 -0400
+++ wfdb-10.5.5/Makefile.tpl	2010-10-06 22:30:57.000000000 -0400
@@ -1,5 +1,5 @@
 # file: Makefile.tpl		G. Moody	 24 May 2000
-#				Last revised:    10 October 2008
+#				Last revised:   6 October 2010
 # This section of the Makefile should not need to be changed.
 
 # 'make' or 'make all': compile the WFDB applications without installing them
@@ -16,6 +16,7 @@
 	cd psd;      $(MAKE) install
 	-( cd wave;  $(MAKE) install )
 	cd waverc;   $(MAKE) install
+	-( cd xml;   $(MAKE) install )
 	test -d doc && ( cd doc; $(MAKE) install )
 
 # 'make collect': collect the installed files into /tmp/wfdb/
@@ -28,6 +29,7 @@
 	cd psd;      $(MAKE) collect
 	-( cd wave;  $(MAKE) collect )
 	cd waverc;   $(MAKE) collect
+	-( cd xml;   $(MAKE) collect )
 	test -d doc && ( cd doc; $(MAKE) collect )
 
 uninstall:	config.cache
@@ -39,6 +41,7 @@
 	cd psd;      $(MAKE) uninstall
 	cd wave;     $(MAKE) uninstall
 	cd waverc;   $(MAKE) uninstall
+	cd xml;      $(MAKE) uninstall
 	test -d doc && ( cd doc; $(MAKE) uninstall )
 	./uninstall.sh $(WFDBROOT)
 
@@ -54,6 +57,7 @@
 	cd psd;      $(MAKE) clean
 	cd wave;     $(MAKE) clean
 	cd waverc;   $(MAKE) clean
+	cd xml;	     $(MAKE) clean
 	test -d doc && ( cd doc; $(MAKE) clean )
 	cd conf; rm -f *~ prompt site.def site-slib.def
 	rm -f *~ config.cache */*.exe $(PACKAGE)-*.spec
@@ -116,9 +120,11 @@
 	rm -f ../$(PACKAGE)-MANIFEST ../$(PACKAGE).tar.gz \
 	  ../$(PACKAGE)-no-docs.tar.gz
 	cd lib; $(SETPERMISSIONS) *.h
-	cd ..; tar --create --file $(PACKAGE).tar.gz --verbose --gzip \
-          '--exclude=$(PACKAGE)/*CVS' $(PACKAGE) | sed s+${PACKAGE}/++ | \
-	  tee $(PACKAGE)-MANIFEST
+	cd ..; export COPYFILE_DISABLE=true; \
+	  tar --create --file $(PACKAGE).tar.gz --verbose --gzip \
+          '--exclude=$(PACKAGE)/*CVS' $(PACKAGE) 2>&1 | \
+	  sed "s+^a ++" | sed s+${PACKAGE}/++ | \
+          tee $(PACKAGE)-MANIFEST
 	cd ..; tar --create --file $(PACKAGE)-no-docs.tar.gz \
 	  --verbose --gzip \
           '--exclude=$(PACKAGE)/*doc' \
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/MANIFEST wfdb-10.5.5/MANIFEST
--- wfdb-10.5.4/MANIFEST	2009-05-03 15:22:59.000000000 -0400
+++ wfdb-10.5.5/MANIFEST	2010-10-06 22:38:59.000000000 -0400
@@ -1,5 +1,4 @@
-
-app/
+app
 app/12lead.pro
 app/ann2rr.c
 app/bxb.c
@@ -55,10 +54,10 @@
 app/wrsamp.c
 app/xform.c
 check-manifest
-checkpkg/
+checkpkg
 checkpkg/appcheck
 checkpkg/checkfile
-checkpkg/expected/
+checkpkg/expected
 checkpkg/expected/100a.nguess
 checkpkg/expected/100s.a2r
 checkpkg/expected/100s.mix
@@ -97,7 +96,7 @@
 checkpkg/expected/sumann.out
 checkpkg/expected/sumstats.out
 checkpkg/expected/tach.out
-checkpkg/expected/udb/
+checkpkg/expected/udb
 checkpkg/expected/udb/100z.hea
 checkpkg/expected/wfd00001.dat
 checkpkg/expected/wfd00001.hea
@@ -113,7 +112,7 @@
 checkpkg/expected/xform.dat
 checkpkg/expected/xform.hea
 checkpkg/expected/xform.wabp
-checkpkg/input/
+checkpkg/input
 checkpkg/input/100x.hea
 checkpkg/input/ecgeval
 checkpkg/input/sumstats
@@ -123,8 +122,8 @@
 checkpkg/Makefile
 checkpkg/Makefile.top
 checkpkg/Makefile.tpl
-checkpkg/mitdb/
-conf/
+checkpkg/mitdb
+conf
 conf/archname
 conf/collect.sh
 conf/cygwin.def
@@ -150,7 +149,7 @@
 conf/solaris.def
 conf/solaris-slib.def
 conf/version.def
-convert/
+convert
 convert/a2m.c
 convert/ad2m.c
 convert/ahaconvert
@@ -171,7 +170,7 @@
 convert/wav2mit.c
 convert/wfdb2mat.c
 COPYING
-data/
+data
 data/100a.atr
 data/100a.hea
 data/100s.atr
@@ -194,7 +193,7 @@
 data/multi.hea
 data/nstlist
 data/null.hea
-data/pipe/
+data/pipe
 data/pipe/16x10.hea
 data/pipe/16x11.hea
 data/pipe/16x12.hea
@@ -228,7 +227,7 @@
 data/pipe/8x8.hea
 data/pipe/8x9.hea
 data/README
-data/tape/
+data/tape
 data/tape/10240.hea
 data/tape/1024.hea
 data/tape/4096.hea
@@ -238,13 +237,13 @@
 data/tape/mittape.hea
 data/wfdbcal
 data/wfdbpath.mac
-doc/
+doc
 doc/Makefile
 doc/Makefile.top
 doc/Makefile.tpl
-doc/misc/
+doc/misc
 doc/misc/foot.ht0
-doc/misc/icons/
+doc/misc/icons
 doc/misc/icons/contents.png
 doc/misc/icons/cross_ref.png
 doc/misc/icons/foot.png
@@ -257,8 +256,8 @@
 doc/misc/icons/up.png
 doc/misc/index.ht0
 doc/README
-doc/wag/
-doc/wag-src/
+doc/wag
+doc/wag-src
 doc/wag-src/a2m.1
 doc/wag-src/ann2rr.1
 doc/wag-src/annot.5
@@ -353,10 +352,10 @@
 doc/wag-src/wrsamp.1
 doc/wag-src/xform.1
 doc/wag-src/xview.7
-doc/wpg/
-doc/wpg/info/
+doc/wpg
+doc/wpg/info
 doc/wpg/info/README.info
-doc/wpg-src/
+doc/wpg-src
 doc/wpg-src/ctotexi.c
 doc/wpg-src/dir.top
 doc/wpg-src/dir.wpg
@@ -372,25 +371,25 @@
 doc/wpg-src/wpg.cover
 doc/wpg-src/wpg.hlp
 doc/wpg-src/wpg.ht0
-doc/wug/
-doc/wug-src/
+doc/wug
+doc/wug-src
 doc/wug-src/fancybox.perl
 doc/wug-src/.latex2html-init
 doc/wug-src/Makefile
 doc/wug-src/Makefile.top
 doc/wug-src/Makefile.tpl
 doc/wug-src/README
-doc/wug-src/wave/
-doc/wug-src/wave/misc/
+doc/wug-src/wave
+doc/wug-src/wave/misc
 doc/wug-src/wave/misc/example.xws
 doc/wug-src/wave/misc/html.sty
 doc/wug-src/wave/misc/pstoimg
 doc/wug-src/wave/misc/wave.inf
-doc/wug-src/wave/png/
+doc/wug-src/wave/png
 doc/wug-src/wave/png/chart2.png
 doc/wug-src/wave/png/fulldisc.png
 doc/wug-src/wave/png/wave.png
-doc/wug-src/wave/ppm/
+doc/wug-src/wave/ppm
 doc/wug-src/wave/ppm/allow-edit.ppm.gz
 doc/wug-src/wave/ppm/analysis-commands.ppm.gz
 doc/wug-src/wave/ppm/analyze-window.ppm.gz
@@ -432,11 +431,11 @@
 doc/wug-src/wave/ppm/view-window.ppm.gz
 doc/wug-src/wave/ppm/wave-icon.ppm.gz
 doc/wug-src/wave/ppm/wave-menu.ppm.gz
-doc/wug-src/wave/ps/
+doc/wug-src/wave/ps
 doc/wug-src/wave/ps/chart1.ps
 doc/wug-src/wave/ps/chart2.ps
 doc/wug-src/wave/ps/fulldisc.ps
-doc/wug-src/wave/scripts/
+doc/wug-src/wave/scripts
 doc/wug-src/wave/scripts/dossify-html
 doc/wug-src/wave/scripts/fixinfo
 doc/wug-src/wave/scripts/fixlinks
@@ -446,7 +445,7 @@
 doc/wug-src/wave/scripts/wugfigures
 doc/wug-src/wug0.tex
 doc/wug-src/wug.cover
-examples/
+examples
 examples/example10.c
 examples/example1.c
 examples/example2.c
@@ -471,7 +470,7 @@
 examples/refhr.c
 examples/stdev.c
 examples/wfdbversion.c
-fortran/
+fortran
 fortran/example.f
 fortran/fsamples.f
 fortran/Makefile
@@ -482,7 +481,7 @@
 INSTALL
 install.sh
 install-wave32
-lib/
+lib
 lib/annot.c
 lib/calib.c
 lib/COPYING.LIB
@@ -504,7 +503,7 @@
 Makefile.tpl
 MANIFEST
 NEWS
-psd/
+psd
 psd/coherence.c
 psd/fft.c
 psd/hrfft
@@ -524,7 +523,7 @@
 README.NETFILES
 README.WINDOWS
 uninstall.sh
-wave/
+wave
 wave/analysis.hlp
 wave/analyze.c
 wave/annot.c
@@ -551,7 +550,7 @@
 wave/modepan.c
 wave/nomake
 wave/printing.hlp
-waverc/
+waverc
 waverc/Makefile
 waverc/Makefile.top
 waverc/Makefile.tpl
@@ -577,3 +576,15 @@
 wave/xvwave.c
 wave/xvwave.h
 wfdb.spec
+xml
+xml/annxml.c
+xml/heaxml.c
+xml/Makefile
+xml/Makefile.top
+xml/Makefile.tpl
+xml/README
+xml/wfdb.dtd
+xml/wfdb.xsl
+xml/xmlann.c
+xml/xmlhea.c
+xml/xmlproc.h
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/NEWS wfdb-10.5.5/NEWS
--- wfdb-10.5.4/NEWS	2010-07-14 02:29:21.000000000 -0400
+++ wfdb-10.5.5/NEWS	2010-10-06 23:37:51.000000000 -0400
@@ -1,3 +1,40 @@
+10.5.5 (6 October 2010):
+	WFDB-compatible signals can now be converted to a simple XML format
+	using rdsamp's new '-X' option, and XML files in this format can be
+	converted to WFDB-compatible format using wrsamp (which recognizes
+	this format automatically, requiring no special option).  A document
+	type description of this format, and of XML formats for annotation
+	and header files, is available at
+		http://physionet.org/physiobank/database/XML/wfdb.dtd
+	This file, together with a set of applications (annxml, heaxml, xmlann,
+        and xmlhea) that convert WFDB-compatible annotation and header files
+	to and from these XML formats, are included in the 'xml' subdirectory
+	of this package.
+
+	Since version 10.4.14, app/sortann has not properly sorted annotations
+        containing non-zero 'chan' or 'num' fields.  This bug has now been
+	fixed;  thanks to Jesus Maria Rodriguez Presedo for reporting it and
+	for providing a test case.
+
+	Changes in conf/darwin.def and conf/darwin-slib.def allow creation
+	of multi-architecture (i386, x86_64, and ppc) binaries on Mac OS X.
+
+	Virginia Faro-Maza pointed out that sumstats did not derive aggregate
+	shutdown statistics described in ANSI/AAMI EC57, and that bxb did not
+	preserve the raw data needed to derive these statistics.  Both bxb.c
+	and sumstats.c have been revised in this release to remedy these
+	omissions.
+
+	An uninitialized flag may have made ahaecg2mit behave as if its -s
+ 	option was always given, and an informational message contained
+	a formatting error;  both problems have been corrected, thanks to
+	a report from Isaac Henry.
+
+        Errors in the formatting of a variety of error and warning messages
+	have been corrected in app/ecgeval.c, app/psfd.c, app/rdsamp.c,
+	app/snip.c, app/sumstats.c, app/wrann.c, convert/mit2edf.c,
+	convert/mit2wav.c, convert/wfdb2mat.c, and psd/lomb.c.
+
 10.5.4 (13 July 2010):
         Function getseginfo() has been introduced in WFDB library version
 	10.5.4, to allow applications to obtain information about the
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/psd/lomb.c wfdb-10.5.5/psd/lomb.c
--- wfdb-10.5.4/psd/lomb.c	2005-06-10 09:52:21.000000000 -0400
+++ wfdb-10.5.5/psd/lomb.c	2010-07-27 14:20:51.000000000 -0400
@@ -1,9 +1,8 @@
 /* file: lomb.c		G. Moody	12 February 1992
-			Last revised:	  10 June 2005
-
+			Last revised:	  27 July 2010
 -------------------------------------------------------------------------------
 lomb: Lomb periodogram of real data
-Copyright (C) 1992-2005 George B. Moody
+Copyright (C) 1992-2010 George B. Moody
 
 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
@@ -469,34 +468,34 @@
 
 	    if (64 * nmaxt * sizeof(float) < nmax) {
 		fprintf(stderr,
-		      "%s: insufficient memory, truncating input at row %d\n",
+		      "%s: insufficient memory, truncating input at row %lu\n",
 		      pname, npts);
 	        break;
 	    }
 	    if ((xt = (float *)realloc(x, nmaxt * sizeof(float))) == NULL) {
 		fprintf(stderr,
-		      "%s: insufficient memory, truncating input at row %d\n",
+		      "%s: insufficient memory, truncating input at row %lu\n",
 		      pname, npts);
 	        break;
 	    }
 	    x = xt;
 	    if ((yt = (float *)realloc(y, nmaxt * sizeof(float))) == NULL) {
 		fprintf(stderr,
-		      "%s: insufficient memory, truncating input at row %d\n",
+		      "%s: insufficient memory, truncating input at row %lu\n",
 		      pname, npts);
 	        break;
 	    }
 	    y = yt;
 	    if ((w1t = (float *)realloc(wk1,64*nmaxt*sizeof(float))) == NULL){
 		fprintf(stderr,
-		      "%s: insufficient memory, truncating input at row %d\n",
+		      "%s: insufficient memory, truncating input at row %lu\n",
 		      pname, npts);
 	        break;
 	    }
 	    wk1 = w1t;
 	    if ((w2t = (float *)realloc(wk2,64*nmaxt*sizeof(float))) == NULL){
 		fprintf(stderr,
-		      "%s: insufficient memory, truncating input at row %d\n",
+		      "%s: insufficient memory, truncating input at row %lu\n",
 		      pname, npts);
 	        break;
 	    }
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/wfdb.spec wfdb-10.5.5/wfdb.spec
--- wfdb-10.5.4/wfdb.spec	2009-05-03 23:39:12.000000000 -0400
+++ wfdb-10.5.5/wfdb.spec	2010-10-06 22:45:00.000000000 -0400
@@ -15,6 +15,9 @@
 BuildRoot: /var/tmp/%{name}-root
 
 %changelog
+* Wed Oct 6 2010 George B Moody <george@mit.edu>
+- added annxml, heaxml, xmlann, xmlhea
+
 * Sun May 3 2009 George B Moody <george@mit.edu>
 - moved wfdb-config from devel to apps
 
@@ -139,6 +142,7 @@
 %{_bindir}/ahaconvert
 %{_bindir}/ahaecg2mit
 %{_bindir}/ann2rr
+%{_bindir}/annxml
 %{_bindir}/bxb
 %{_bindir}/calsig
 %{_bindir}/coherence
@@ -148,6 +152,7 @@
 %{_bindir}/epicmp
 %{_bindir}/fft
 %{_bindir}/fir
+%{_bindir}/heaxml
 %{_bindir}/hrfft
 %{_bindir}/hrlomb
 %{_bindir}/hrmem
@@ -208,6 +213,8 @@
 %{_bindir}/wrann
 %{_bindir}/wrsamp
 %{_bindir}/xform
+%{_bindir}/xmlann
+%{_bindir}/xmlhea
 %{_libdir}/ps
 %{_mandir}
 
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/annxml.c wfdb-10.5.5/xml/annxml.c
--- wfdb-10.5.4/xml/annxml.c	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/annxml.c	2010-07-01 21:22:14.000000000 -0400
@@ -0,0 +1,212 @@
+/* file: annxml.c	G. Moody	28 June 2010
+
+-------------------------------------------------------------------------------
+heaxml: Convert a WFDB annotation file to XML format
+Copyright (C) 2010 George B. Moody
+
+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 even 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.
+
+You may contact the author by e-mail (george@mit.edu) or postal mail
+(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
+please visit PhysioNet (http://www.physionet.org/).
+_______________________________________________________________________________
+
+*/
+
+
+#include <stdio.h>
+#include <wfdb/wfdb.h>
+#include <wfdb/ecgcodes.h>
+
+#define WFDBXMLPROLOG  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<?xml-stylesheet type=\"text/xsl\"" \
+ " href=\"wfdb.xsl\"?>\n" \
+ "<!DOCTYPE wfdbannotationset PUBLIC \"-//PhysioNet//DTD WFDB 1.0//EN\"" \
+ " \"http://physionet.org/physiobank/database/XML/wfdb.dtd\">\n"
+
+char *token(char *p)
+{
+  if (p) {
+    while (*p && *p != ' ' && *p != '\t' && *p != '\n')
+      p++;	/* find whitespace */
+    while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
+      p++;	/* find first non-whitespace */
+    if (*p == '\0') p = NULL;
+  }
+  return (p);
+}
+
+void output_xml(FILE *ofile, char *tag, char *p)
+{
+  if (p) {
+    fprintf(ofile, "<%s>", tag);
+    while (*p) {
+      if (*p == '<') fprintf(ofile, "&lt;");
+      else if (*p == '>') fprintf(ofile, "&gt;");
+      else if (*p == '&') fprintf(ofile, "&amp;");
+      else if (*p == '>') fprintf(ofile, "&gt;");
+      else if (*p == '"') fprintf(ofile, "&quot;");
+      else if (*p == '\'') fprintf(ofile, "&apos;");
+      else fprintf(ofile, "%c", *p);
+      p++;
+    }
+    fprintf(ofile, "</%s>", tag);
+  }
+  return;
+}
+
+int nsig;
+FILE *ofile;
+WFDB_Annotation annot;
+WFDB_Frequency sfreq;
+
+void process_start(char *tstring);
+void process_anntab(void);
+void process_annotation(void);
+
+main(int argc, char **argv)
+{
+  char *annotator, *ofname, *p, *pname, *record, *prog_name();
+    WFDB_Anninfo ai;
+
+    pname = prog_name(argv[0]);
+    if (argc < 3) {
+      (void)fprintf(stderr, "usage: %s RECORD ANNOTATOR\n", pname);
+      exit(1);
+    }
+    record = argv[1];
+    annotator = argv[2];
+
+    /* Discover the number of signals defined in the header. */
+    if ((nsig = isigopen(record, NULL, 0)) < 0) exit(2);
+    sfreq = sampfreq(record);
+
+    /* Open the input annotation file. */
+    ai.name = annotator;
+    ai.stat = WFDB_READ;
+    if (annopen(record, &ai, 1) < 0)
+      exit(3);
+
+    /* The name of the output file is of the form 'RECORD.ANNOTATOR.xml'.  Any
+       directory separators (/) in the record name are replaced by hyphens (-)
+       in the output file name, so that the output file is always written into
+       the current directory. */
+    ofname = calloc(strlen(record)+strlen(annotator)+6, sizeof(char));
+    sprintf(ofname, "%s.%s.xml", record, annotator);
+    for (p = ofname; *p; p++)
+      if (*p == '/') *p = '-';
+
+    /* Open the output file and write the XML prolog. */
+    if ((ofile = fopen(ofname, "wt")) == NULL) {
+      fprintf(stderr, "%s: can't create %s\n", pname, ofname);
+      exit(4);
+    }
+    fprintf(ofile, WFDBXMLPROLOG);
+
+    (void)fprintf(ofile, "<wfdbannotationset annotator=\"%s\""
+		  " record=\"%s\">\n", annotator, record);
+
+    process_start(mstimstr(0));
+
+    (void)fprintf(ofile, "<samplingfrequency>%.12g</samplingfrequency>\n",
+		  sfreq);
+
+    while (getann(0, &annot) >= 0)
+      process_annotation();
+
+    process_anntab();
+
+    fprintf(ofile, "</wfdbannotationset>\n");
+    wfdbquit();
+    exit(0);
+}
+
+void process_start(char *p)
+{
+    if (*p == '[') {
+      int day = -1, month = -1, year = -1;
+      double hour = -1.0, minute = -1.0, second = -1.0;
+
+      fprintf(ofile, "<start>\n");
+      sscanf(p+1, "%lf:%lf:%lf %d/%d/%d",
+	     &hour, &minute, &second, &day, &month, &year);
+      if (year >= 0) {
+	if (year < 100) year += 1900;
+	if (year < 1975) year += 100;
+	fprintf(ofile, "<year>%d</year>\n", year);
+      }
+      if (month > 0) fprintf(ofile, "<month>%d</month>\n", month);
+      if (day > 0) fprintf(ofile, "<day>%d</day>\n", day);
+
+      if (second < 0) { /* incomplete start time in MM:SS or SS format */
+	if (minute < 0) { second = hour; hour = -1; } /* SS format */
+	else { second = minute; minute = hour; hour = -1; } /* MM:SS */
+      }
+
+      if (hour >= 0) fprintf(ofile, "<hour>%g</hour>\n", hour);
+      if (minute >= 0) fprintf(ofile, "<minute>%g</minute>\n", minute);
+      if (second >= 0) fprintf(ofile, "<second>%g</second>\n", second);
+      fprintf(ofile, "</start>\n");
+    }
+}
+
+static long anncount[ACMAX+1];
+
+void process_anntab()
+{
+  int i;
+
+  fprintf(ofile, "<anntab>");
+  for (i = 0; i <= ACMAX; i++) {
+    if (anncount[i]) {
+      fprintf(ofile, "<anntabentry><anntype>%d</anntype>", i);
+      output_xml(ofile, "anncode", annstr(i));
+      output_xml(ofile, "anndescription", anndesc(i));
+      fprintf(ofile, "<anncount>%ld</anncount></anntabentry>\n", anncount[i]);
+    }
+  }
+  fprintf(ofile, "</anntab>");
+}
+
+void process_annotation()
+{
+  fprintf(ofile, "<annotation><time>%ld</time><anncode>%s</anncode>",
+	  annot.time, annstr(annot.anntyp));
+  if (annot.subtyp) fprintf(ofile, "<subtype>%d</subtype>", annot.subtyp);
+  if (annot.chan) fprintf(ofile, "<chan>%d</chan>", annot.chan);
+  if (annot.num) fprintf(ofile, "<num>%d</num>", annot.num);
+  if (annot.aux) output_xml(ofile, "aux", annot.aux+1);
+  fprintf(ofile, "</annotation>\n");
+  anncount[annot.anntyp]++;
+}
+
+char *prog_name(s)
+char *s;
+{
+    char *p = s + strlen(s);
+
+#ifdef MSDOS
+    while (p >= s && *p != '\\' && *p != ':') {
+	if (*p == '.')
+	    *p = '\0';		/* strip off extension */
+	if ('A' <= *p && *p <= 'Z')
+	    *p += 'a' - 'A';	/* convert to lower case */
+	p--;
+    }
+#else
+    while (p >= s && *p != '/')
+	p--;
+#endif
+    return (p+1);
+}
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/heaxml.c wfdb-10.5.5/xml/heaxml.c
--- wfdb-10.5.4/xml/heaxml.c	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/heaxml.c	2010-07-01 17:58:42.000000000 -0400
@@ -0,0 +1,400 @@
+/* file: heaxml.c	G. Moody	28 June 2010
+			Last revised:	 1 July 2010
+-------------------------------------------------------------------------------
+heaxml: Convert a WFDB .hea (header) file to XML format
+Copyright (C) 2010 George B. Moody
+
+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 even 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.
+
+You may contact the author by e-mail (george@mit.edu) or postal mail
+(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
+please visit PhysioNet (http://www.physionet.org/).
+_______________________________________________________________________________
+
+*/
+
+
+#include <stdio.h>
+#include <wfdb/wfdb.h>
+
+#define WFDBXMLPROLOG  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
+ "<?xml-stylesheet type=\"text/xsl\"" \
+ " href=\"wfdb.xsl\"?>\n" \
+ "<!DOCTYPE wfdbrecord PUBLIC \"-//PhysioNet//DTD WFDB 1.0//EN\"" \
+ " \"http://physionet.org/physiobank/database/XML/wfdb.dtd\">\n"
+
+char *token(char *p)
+{
+  if (p) {
+    while (*p && *p != ' ' && *p != '\t' && *p != '\n')
+      p++;	/* find whitespace */
+    while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
+      p++;	/* find first non-whitespace */
+    if (*p == '\0') p = NULL;
+  }
+  return (p);
+}
+
+void output_xml(FILE *ofile, char *tag, char *p)
+{
+  if (p) {
+    fprintf(ofile, "<%s>", tag);
+    while (*p) {
+      if (*p == '<') fprintf(ofile, "&lt;");
+      else if (*p == '>') fprintf(ofile, "&gt;");
+      else if (*p == '&') fprintf(ofile, "&amp;");
+      else if (*p == '>') fprintf(ofile, "&gt;");
+      else if (*p == '"') fprintf(ofile, "&quot;");
+      else if (*p == '\'') fprintf(ofile, "&apos;");
+      else fprintf(ofile, "%c", *p);
+      p++;
+    }
+    fprintf(ofile, "</%s>\n", tag);
+  }
+  return;
+}
+
+int nsig;
+FILE *ofile;
+WFDB_Siginfo *s;
+WFDB_Time t;
+
+void process_start(char *tstring);
+
+main(int argc, char **argv)
+{
+    char *ofname, *info, *p, *pname, *record, *prog_name();
+    int i, in_msrec = 0, nsegments;
+    WFDB_Seginfo *seg, *sp;
+
+    pname = prog_name(argv[0]);
+    if (argc < 2) {
+      (void)fprintf(stderr, "usage: %s RECORD\n", pname);
+      exit(1);
+    }
+    record = argv[1];
+
+    /* Discover the number of signals defined in the header. */
+    if ((nsig = isigopen(record, NULL, 0)) < 0) exit(2);
+
+    /* The name of the output file is the record name with an
+       appended ".hea.xml".  Any directory separators (/) in
+       the record name are replaced by hyphens (-) in the
+       output file name, so that the output file is always
+       written into the current directory. */
+    ofname = calloc(strlen(record)+9, sizeof(char));
+    strcpy(ofname, record);
+    for (p = ofname; *p; p++)
+      if (*p == '/') *p = '-';
+    strcat(ofname, ".hea.xml");
+
+    /* Open the output file and write the XML prolog. */
+    if ((ofile = fopen(ofname, "wt")) == NULL) {
+      fprintf(stderr, "%s: can't create %s\n", pname, ofname);
+      exit(3);
+    }
+    fprintf(ofile, WFDBXMLPROLOG);
+
+    /* Allocate storage for nsig signal information structures. */
+    if (nsig > 0 && (s = malloc(nsig * sizeof(WFDB_Siginfo))) == NULL) {
+	fprintf(stderr, "%s: insufficient memory\n", pname);
+	exit(4);
+    }
+    nsig = isigopen(record, s, -nsig);
+
+    (void)fprintf(ofile, "<wfdbrecord name=\"%s\">\n\n", record);
+    setgvmode(WFDB_LOWRES);
+    t = strtim("e");
+    if (nsig > 0 && (s[0].fmt == 0 || s[0].nsamp != 0) && s[0].nsamp != t) {
+ 	in_msrec = 1;
+	nsegments = getseginfo(&sp);
+	seg = calloc(nsegments, sizeof(WFDB_Seginfo));
+	for (i = 0; i < nsegments; i++)
+	  seg[i] = sp[i];
+	sp = seg;
+    }
+
+    process_record();
+
+    if (in_msrec) {
+      static char *segname, nextts[30];
+
+      segname = calloc(strlen(record)+20, sizeof(char));
+      for (p = record + strlen(record) - 1; p > record && *p != '/'; p--)
+	;
+      *p = '\0';
+      /* If segment 0 has 0 samples, it's a layout segment, and the information
+	 in it has already been output above, so skip it; otherwise, it's a
+	 regular segment, so process it.  The initialization of i in the for
+	 loop below tests for this. */
+      for (i = sp[0].nsamp ? 0 : 1; i < nsegments; i++) {
+	fprintf(stderr, "segment %d: %s\n", i, sp[i].recname);
+	if (strcmp("~", sp[i].recname) == 0) {
+	  fprintf(ofile, "\n<segment name=\"gap_%d\">\n<gap>\n", i);
+	  process_start(nextts);
+	  fprintf(ofile, "<length>%ld</length></gap>\n", sp[i].nsamp);
+	}
+	else {
+	  sprintf(segname, "%s/%s", record, sp[i].recname);
+	  wfdbquit();
+	  nsig = isigopen(segname, NULL, 0);
+	  nsig = isigopen(segname, s, -nsig);
+	  fprintf(ofile, "\n<segment name=\"%s\">\n", sp[i].recname);
+	  setgvmode(WFDB_LOWRES);
+	  t = strtim("e");
+	  strcpy(nextts, mstimstr(-t));
+	  process_record();
+	}
+	fprintf(ofile, "</segment>\n");
+      }
+    }
+    fprintf(ofile, "</wfdbrecord>\n");
+    exit(0);
+}
+
+void process_info(void)
+{
+    char *info, *p;
+
+    if (info = getinfo((char *)NULL)) {
+      double age = -1.0;
+      char *sex = NULL;
+
+      fprintf(ofile, "\n<info>\n");
+      /* Find the first non-space in the first info string. */
+      for (p = info; *p && *p == ' '; p++)
+	;
+      if ('0' <= *p && *p <= '9') {
+	/* If the first token of the first info string is numeric, the
+	   current .hea file does not have tagged info, and the first
+	   and second tokens are the age and sex; and the second info
+	   string (if present) contains the medications. Handle this
+	   case first. */
+	sscanf(p, "%lf", &age);
+	if (age >= 0) fprintf(ofile, "<age>%g</age>\n", age);
+	p = token(p); /* go to the next token */
+	if (p && (*p == 'm' || *p == 'M')) sex = "M";
+	else if (p && (*p == 'f' || *p == 'F')) sex = "F";
+	if (sex) fprintf(ofile, "<sex>%s</sex>\n", sex);
+	/* If there are any more tokens, save them as 'extra' info. */
+	if (p = token(p))
+	  output_xml(ofile, "extra", p);
+	if (info = getinfo((char *)NULL)) {
+	  output_xml(ofile, "medication", info);
+	  info = getinfo((char *)NULL);
+	}
+      }
+      /* process standard (tagged) info */
+      while (info) {
+	if (age < 0) {
+	  if ((p = strstr(info, "age")) || (p = strstr(info, "Age"))) {
+	    if (p = token(p)) {
+	      sscanf(p, "%lf", &age);
+	      if (age >= 0) fprintf(ofile, "<age>%g</age>\n", age);
+	    }
+	    /* Additional tagged data may follow age.  Continue processing
+	       the remainder of this info string below. */
+	    if (!(info = token(p)))
+	      /* If there is nothing else, get the next info if any. */
+	      info = getinfo((char *)NULL);
+	  }
+	}
+	if (sex == NULL) {
+	  if (info &&
+	      ((p = strstr(info, "sex"))||(p = strstr(info, "Sex")))) {
+	    if ((p = token(p)) && (*p == 'm' || *p == 'M')) sex = "M";
+	    else if (p && (*p == 'f' || *p == 'F')) sex = "F";
+	    if (sex) fprintf(ofile, "<sex>%s</sex>\n", sex);
+	    /* Additional tagged data may follow sex.  Continue processing
+	       the remainder of this info string. */
+	    if (!(info = token(p)))
+		/* If there is nothing else, get the next info if any. */
+	      info = getinfo((char *)NULL);
+	  }
+	}
+	/* Diagnoses may be present in more than one info string. */
+	if (info && *info &&
+	    ((p=strstr(info,"diagnos")) || (p=strstr(info,"Diagnos")))) {
+	  if ((p = token(p)) == NULL)
+	    /* If nothing follows the 'diagnosis' tag, assume the next info
+	       is the diagnosis. */
+	    p = getinfo((char *)NULL);
+	  if (p) {
+	    output_xml(ofile, "diagnosis", p);
+	  /* This info has been consumed;  get the next info if any. */
+	    info = getinfo((char *)NULL);
+	    continue;
+	  }
+	}
+	if (info && *info && 
+	    ((p=strstr(info,"medication"))||(p=strstr(info,"Medication")))) {
+	  if ((p = token(p)) == NULL)
+	    /* If nothing follows the 'medication' tag, assume the next info
+	       is the medication. */
+	    p = getinfo((char *)NULL);
+	  if (p) {
+	    output_xml(ofile, "medication", p);
+	    /* This info has been consumed;  get the next info if any. */
+	    info = getinfo((char *)NULL);
+	    continue;
+	  }
+	}
+	/* Process any info that was not identified above. */
+	if (info && *info)
+	  output_xml(ofile, "other", info);
+	info = getinfo((char *)NULL);
+      }
+      fprintf(ofile, "</info>\n");
+    }
+}
+
+void process_start(char *p)
+{
+    if (*p == '[') {
+      int day = -1, month = -1, year = -1;
+      double hour = -1.0, minute = -1.0, second = -1.0;
+
+      fprintf(ofile, "<start>\n");
+      sscanf(p+1, "%lf:%lf:%lf %d/%d/%d",
+	     &hour, &minute, &second, &day, &month, &year);
+      if (year >= 0) {
+	if (year < 100) year += 1900;
+	if (year < 1975) year += 100;
+	fprintf(ofile, "<year>%d</year>\n", year);
+      }
+      if (month > 0) fprintf(ofile, "<month>%d</month>\n", month);
+      if (day > 0) fprintf(ofile, "<day>%d</day>\n", day);
+
+      if (second < 0) { /* incomplete start time in MM:SS or SS format */
+	if (minute < 0) { second = hour; hour = -1; } /* SS format */
+	else { second = minute; minute = hour; hour = -1; } /* MM:SS */
+      }
+
+      if (hour >= 0) fprintf(ofile, "<hour>%g</hour>\n", hour);
+      if (minute >= 0) fprintf(ofile, "<minute>%g</minute>\n", minute);
+      if (second >= 0) fprintf(ofile, "<second>%g</second>\n", second);
+      fprintf(ofile, "</start>\n");
+    }
+}
+
+int process_record(void)
+{
+    char *p;
+    double cfreq, sfreq;
+    int i, skew;
+    FILE *ifile;
+
+    /* Process and output info. */
+    process_info();
+
+    /* Output the <start> section if the starting time is specified. */
+    process_start(mstimstr(0L));
+
+    if (nsig < 1 || t > 0L)
+      (void)fprintf(ofile, "<length>%ld</length>\n", t);
+    else if (s[0].fmt && (ifile = fopen(s[0].fname, "rb")) &&
+	     (fseek(ifile, 0L, 2) == 0)) {
+	int framesize = 0;
+	long nbytes = ftell(ifile) - wfdbgetstart(0);
+
+	fclose(ifile);
+	for (i = 0; i < nsig && s[i].group == 0; i++)
+	    framesize += s[i].spf;
+	switch (s[0].fmt) {
+	  case 8:
+	  case 80:
+	    t = nbytes / framesize;
+	    break;
+	  default:
+	  case 16:
+	  case 61:
+	  case 160:
+	    t = nbytes / (2*framesize);
+	    break;
+	  case 212:
+	    t = (2L * nbytes) / (3*framesize);
+	    break;
+	  case 310:
+	  case 311:
+	    t = (3L * nbytes) / (4*framesize);
+	    break;
+	}
+      (void)fprintf(ofile, "<length>%ld</length>\n", t);
+    }
+
+    (void)fprintf(ofile, "<samplingfrequency>%.12g</samplingfrequency>\n",
+		  sfreq = sampfreq(NULL));
+    (void)fprintf(ofile, "<signals>%d</signals>\n", nsig);
+    cfreq = getcfreq();
+    if (sfreq != cfreq) {
+      double basecount = getbasecount();
+      fprintf(ofile, "<counterfrequency");
+      if (basecount > 0.0) fprintf(ofile, " basecount=\"%g\"", basecount);
+      fprintf(ofile, ">%.12g</counterfrequency>\n", cfreq);
+    }
+
+    if (nsig > 0) { 
+      for (i = 0; i < nsig; i++) {
+	if (i == 0 || s[i].group != s[i-1].group) {
+	  long plen;
+
+	  if (i > 0) fprintf(ofile, "</signalfile>\n\n");
+	  fprintf(ofile, "<signalfile>\n<filename>%s</filename>\n\n",
+		  s[i].fmt ? s[i].fname : "[none]");
+	  if (plen = wfdbgetstart(i))
+	    fprintf(ofile, "<preamblelength>%ld</preamblelength>\n", plen);
+	}
+	fprintf(ofile, "<signal>\n<description>%s</description>\n",
+		s[i].desc);
+	fprintf(ofile, "<gain>%.12g</gain>\n",
+		s[i].gain == 0. ? WFDB_DEFGAIN : s[i].gain);
+	fprintf(ofile, "<units>%s</units>\n",
+		s[i].units ? s[i].units : "mV");
+	fprintf(ofile, "<initialvalue>%d</initialvalue>\n", s[i].initval);
+	fprintf(ofile, "<storageformat>%d</storageformat>\n", s[i].fmt);
+	if (s[i].spf > 1)
+	  fprintf(ofile,"<samplesperframe>%d</samplesperframe>\n", s[i].spf);
+	if (skew = wfdbgetskew(i))
+	  fprintf(ofile, "<skew>%ld</skew>\n", skew);
+	fprintf(ofile, "<blocksize>%d</blocksize>\n", s[i].bsize);
+	fprintf(ofile, "<adcresolution>%d</adcresolution>\n", s[i].adcres);
+	fprintf(ofile, "<adczero>%d</adczero>\n", s[i].adczero);
+	fprintf(ofile, "<baseline>%d</baseline>\n", s[i].baseline);
+	fprintf(ofile, "<checksum>%d</checksum>\n", s[i].cksum);
+	fprintf(ofile, "</signal>\n\n");
+      }
+    }
+    if (nsig > 0) fprintf(ofile, "</signalfile>\n\n");
+    return (0);
+}
+
+char *prog_name(s)
+char *s;
+{
+    char *p = s + strlen(s);
+
+#ifdef MSDOS
+    while (p >= s && *p != '\\' && *p != ':') {
+	if (*p == '.')
+	    *p = '\0';		/* strip off extension */
+	if ('A' <= *p && *p <= 'Z')
+	    *p += 'a' - 'A';	/* convert to lower case */
+	p--;
+    }
+#else
+    while (p >= s && *p != '/')
+	p--;
+#endif
+    return (p+1);
+}
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/Makefile.top wfdb-10.5.5/xml/Makefile.top
--- wfdb-10.5.4/xml/Makefile.top	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/Makefile.top	2010-10-06 22:18:20.000000000 -0400
@@ -0,0 +1,30 @@
+# file: Makefile	G. Moody	 22 August 2010
+#
+# 'make' description file for WFDB-XML applications
+#
+# -----------------------------------------------------------------------------
+# WFDB-XML applications: programs for conversion between WFDB and XML formats
+# Copyright (C) 2010 George B. Moody
+#
+# These programs are free software; you can redistribute them and/or modify
+# them 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.
+#
+# These programs are distributed in the hope that they will be useful, but
+# WITHOUT ANY WARRANTY; without even 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
+# these programs; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# You may contact the author by e-mail (george@mit.edu) or postal mail
+# (MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
+# please visit PhysioNet (http://www.physionet.org/).
+# _____________________________________________________________________________
+#
+# All of the converters require the WFDB library, and those that read
+# XML also require libexpat (http://expat.sourceforge.net).
+
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/Makefile.tpl wfdb-10.5.5/xml/Makefile.tpl
--- wfdb-10.5.4/xml/Makefile.tpl	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/Makefile.tpl	2010-10-06 22:26:19.000000000 -0400
@@ -0,0 +1,50 @@
+# file: Makefile.tpl		G. Moody	  22 August 2010
+#
+# This section of the Makefile should not need to be changed.
+
+CFILES = annxml.c heaxml.c xmlann.c xmlhea.c
+HFILES = xmlproc.h
+MFILES = Makefile
+XFILES = annxml heaxml xmlann xmlhea
+
+# General rule for compiling C sources into executable files.  This is
+# redundant for most versions of `make', but at least one System V version
+# needs it.
+.c:
+	$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
+
+# `make all': build applications
+all:	$(XFILES)
+	$(STRIP) $(XFILES)
+
+# `make' or `make install':  build and install applications
+install:	all $(BINDIR)
+	$(SETXPERMISSIONS) $(XFILES)
+	../install.sh $(BINDIR) $(XFILES)
+
+# 'make collect': retrieve the installed applications
+collect:
+	../conf/collect.sh $(BINDIR) $(XFILES)
+
+uninstall:
+	../uninstall.sh $(BINDIR) $(XFILES)
+
+# Create directories for installation if necessary.
+$(BINDIR):
+	mkdir -p $(BINDIR); $(SETDPERMISSIONS) $(BINDIR)
+
+# `make clean':  remove intermediate and backup files
+clean:
+	rm -f $(XFILES) *.o *~
+
+# `make listing':  print a listing of WFDB-XML applications sources
+listing:
+	$(PRINT) README $(MFILES) $(CFILES) $(HFILES)
+
+# Rules for compiling WFDB-XML applications that require non-standard options
+
+xmlann:		xmlann.c xmlproc.h
+	$(CC) $(CFLAGS) xmlann.c -o $@ $(LDFLAGS) -lexpat
+
+xmlhea:		xmlhea.c xmlproc.h
+	$(CC) $(CFLAGS) xmlhea.c -o $@ $(LDFLAGS) -lexpat
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/README wfdb-10.5.5/xml/README
--- wfdb-10.5.4/xml/README	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/README	2010-10-06 21:51:04.000000000 -0400
@@ -0,0 +1,86 @@
+file: README		G. Moody	2 July 2010
+      			Last revised:  6 October 2010
+
+This directory contains software for interchange between WFDB native formats
+and XML formats.  It includes:
+
+wfdb.dtd	a DTD for WFDB-XML, an XML schema for
+		expressing the contents of WFDB
+		header and annotation files
+
+wfdb.xsl	an XSLT style sheet that a web browser
+		(or other XSLT engine) can use to transform
+		a WFDB-XML header or annotation file into
+		a human-readable representation of its
+ 		contents
+
+heaxml.c	a C program that converts a native
+		WFDB .hea file into a WFDB-XML file
+
+annxml.c	a C program that converts a native
+		WFDB annotation file into a WFDB-XML file
+
+xmlhea.c	a C program that converts a WFDB-XML header
+		file into a native WFDB .hea file
+
+xmlann.c	a C program that converts a WFDB-XML annotation
+		file into a native WFDB annotation file
+
+xmlproc.h	generic functions for processing XML files, shared by
+		xmlhea and xmlann
+
+Makefile	a 'make' description file for automating the
+		process of compiling and installing this software
+
+A copy of the most recent version of wfdb.dtd is posted on PhysioNet at
+   http://physionet.org/physiobank/database/XML/wfdb.dtd
+This on-line copy is referenced in WFDB-XML files generated by heaxml
+and annxml.
+
+Note that these programs require the WFDB library version 10.5.4 or
+later, and that those that read WFDB-XML files also require libexpat
+(http://expat.sourceforge.net/).  To compile them, install gcc, make,
+the WFDB software package, and libexpat if you have not already done so,
+then run 'make' from within this directory.  If you don't have 'make',
+you can compile these programs manually;  see 'Makefile' for the commands
+needed to do so.
+
+It is not necessary to download the native WFDB files in order to use
+heaxml and annxml, since they can obtain their inputs directly from
+the PhysioNet web server via HTTP.
+
+For example, these commands:
+    heaxml mitdb/200
+    annxml mitdb/200 atr
+produce the WFDB-XML files
+    mitdb-200.hea.xml
+    mitdb-200.atr.xml
+in the current directory.
+
+If you put a copy of wfdb.xsl in the same directory with these output
+files, you should be able to view the HTML generated from them via
+wfdb.xsl by opening them with Firefox or another web browser that
+incorporates an XSLT engine.
+
+Try running heaxml on a mimicdb or mimic2db record, for
+example like this:
+    heaxml mimic2db/a40006/a40006
+This command produces
+    mimic2db-a40006-a40006.hea.xml
+which includes information about each segment in the
+multi-segment record as well as a summary of the entire
+record.  The inverse operation, which can be performed by reading
+the .xml file using xmlhea, generates a complete set of segment
+headers together with the master header in this case.
+
+Warning: the XML outputs generated by these commands are huge.  There is a
+good reason that I don't keep these data in XML format on PhysioBank!
+
+There are no 'datxml' or 'xmldat' applications to convert signal files
+to and from WFDB-XML format, because the standard 'rdsamp' and 'wrsamp'
+can perform these conversions.  Use rdsamp's '-X' option to generate
+WFDB-XML output;  wrsamp can recognize WFDB-XML input automatically, so
+no special option is required.  WFDB-XML signal format is the CSV format
+that can be produced by rdsamp's '-c' option, wrapped within an XML header
+and trailer.
+
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/wfdb.dtd wfdb-10.5.5/xml/wfdb.dtd
--- wfdb-10.5.4/xml/wfdb.dtd	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/wfdb.dtd	2010-10-06 21:58:53.000000000 -0400
@@ -0,0 +1,71 @@
+<!-- file: wfdb.dtd   G. Moody <george@mit.edu>    25 August 2010 (v1.0) -->
+<!ELEMENT wfdbrecord (info*, start?, length?,
+	  samplingfrequency, counterfrequency?, signals, signalfile*, segment*)>
+<!ATTLIST wfdbrecord name CDATA #REQUIRED>
+<!ELEMENT start (year?, month?, day?, hour, minute, second?)>
+<!ELEMENT year (#PCDATA)>
+<!ELEMENT month (#PCDATA)>
+<!ELEMENT day (#PCDATA)>
+<!ELEMENT hour (#PCDATA)>
+<!ELEMENT minute (#PCDATA)>
+<!ELEMENT second (#PCDATA)>
+<!ATTLIST length units CDATA "sample intervals">
+<!ELEMENT length (#PCDATA)>
+<!ATTLIST samplingfrequency units CDATA "Hz">
+<!ELEMENT samplingfrequency (#PCDATA)>
+<!ATTLIST counterfrequency basecount CDATA "0">
+<!ATTLIST counterfrequency units CDATA "Hz">
+<!ELEMENT counterfrequency (#PCDATA)>
+<!ELEMENT signals (#PCDATA)>
+<!ELEMENT signalfile (filename, preamblelength?, signal*)>
+<!ELEMENT filename (#PCDATA)>
+<!ELEMENT preamblelength (#PCDATA)>
+<!ELEMENT signal (description?, gain?, units?, initialvalue?, storageformat,
+	  samplesperframe?, skew?, blocksize?, adcresolution?, adczero?,
+	  baseline?, checksum?)>
+<!ELEMENT description (#PCDATA)>
+<!ELEMENT gain (#PCDATA)>
+<!ELEMENT units (#PCDATA)>
+<!ELEMENT initialvalue (#PCDATA)>
+<!ELEMENT storageformat (#PCDATA)>
+<!ELEMENT samplesperframe (#PCDATA)>
+<!ELEMENT skew (#PCDATA)>
+<!ELEMENT blocksize (#PCDATA)>
+<!ELEMENT adcresolution (#PCDATA)>
+<!ELEMENT adczero (#PCDATA)>
+<!ELEMENT baseline (#PCDATA)>
+<!ELEMENT checksum (#PCDATA)>
+<!ELEMENT info (age?, sex?, extra?, diagnosis*, medication*, other*)>
+<!ELEMENT age (#PCDATA)>
+<!ELEMENT sex (#PCDATA)>
+<!ELEMENT extra (#PCDATA)>
+<!ELEMENT diagnosis (#PCDATA)>
+<!ATTLIST medication dosage CDATA #IMPLIED>
+<!ELEMENT medication (#PCDATA)>
+<!ELEMENT other (#PCDATA)>
+<!ELEMENT segment (info*, start?, length?, gap?,
+	  samplingfrequency?, counterfrequency?, signals?, signalfile*)>
+<!ATTLIST segment name CDATA #REQUIRED>
+<!ELEMENT gap (start?, length)>
+<!ATTLIST gap name CDATA #IMPLIED>
+<!ELEMENT wfdbannotationset (start?, length?, samplingfrequency?, annotation*,
+	  anntab?)>
+<!ATTLIST wfdbannotationset annotator CDATA #REQUIRED
+	  record CDATA #REQUIRED>
+<!ELEMENT anntab (anntabentry*)>
+<!ELEMENT anntabentry (anntype, anncode, anndescription, anncount?)>
+<!ELEMENT annotation (time, anncode, subtype?, chan?, num?, aux?, dur?, url?)>
+<!ELEMENT anntype (#PCDATA)>
+<!ELEMENT anncode (#PCDATA)>
+<!ELEMENT anndescription (#PCDATA)>
+<!ELEMENT anncount (#PCDATA)>
+<!ELEMENT time (#PCDATA)>
+<!ELEMENT subtype (#PCDATA)>
+<!ELEMENT chan (#PCDATA)>
+<!ELEMENT num (#PCDATA)>
+<!ELEMENT aux (#PCDATA)>
+<!ELEMENT url (#PCDATA)>
+<!ELEMENT dur (#PCDATA)>
+<!ELEMENT wfdbsampleset (start?, samplingfrequency?, signals?,
+	  description, units?, samplevectors*)>
+<!ELEMENT samplevectors (#PCDATA)>
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/wfdb.xsl wfdb-10.5.5/xml/wfdb.xsl
--- wfdb-10.5.4/xml/wfdb.xsl	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/wfdb.xsl	2010-07-01 21:51:51.000000000 -0400
@@ -0,0 +1,231 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:output method="html"/>
+
+<xsl:template match="/">
+<xsl:apply-templates select="wfdbrecord"/>
+<xsl:apply-templates select="wfdbannotationset"/>
+</xsl:template>
+
+<xsl:template match="wfdbrecord">
+<html>
+<head><title>
+Description of record <xsl:value-of select="@name"/>
+</title></head>
+<body><h1>
+Description of record <xsl:value-of select="@name"/>
+</h1>
+<h2>General information</h2>
+<p>
+<xsl:apply-templates select="start"/>
+<xsl:apply-templates select="length"/>
+<b>Sampling frequency: </b>
+<xsl:value-of select="samplingfrequency"/>
+samples per signal per second<br/>
+<xsl:if test="counterfrequency">
+<b>Counter frequency: </b>
+<xsl:value-of select="counterfrequency"/>
+counts per second (starting from 
+<xsl:value-of select="counterfrequency/@basecount"/>)<br/>
+</xsl:if>
+<b>Signals: </b><xsl:value-of select="signals"/></p>
+
+<xsl:apply-templates select="info"/>
+<xsl:apply-templates select="signalfile"/>
+<xsl:apply-templates select="segment"/>
+</body>
+</html>
+</xsl:template>
+
+<xsl:template match="wfdbannotationset">
+<html>
+<head><title>
+Record <xsl:value-of select="@record"/>,
+Annotator <xsl:value-of select="@annotator"/>
+</title></head>
+<body><h1>
+Record <xsl:value-of select="@record"/>,
+Annotator <xsl:value-of select="@annotator"/>
+</h1>
+
+<p>
+<xsl:apply-templates select="start"/>
+<xsl:apply-templates select="length"/>
+<b>Time resolution: </b><xsl:value-of select="samplingfrequency"/>
+ticks per second<br/>
+<b>Annotations:</b>
+<table width="90%"><tr>
+<td width="20%" align="right"><b>Count</b></td>
+<td width="4%"></td>
+<td><b>Type (Description)</b></td>
+</tr>
+<xsl:apply-templates select="anntab/anntabentry"/>
+<tr><td align="right"><b><xsl:value-of select="count(//annotation)"/></b></td><td></td>
+<td><b>Total</b></td></tr>
+</table>
+</p>
+<hr/>
+<table width="90%"><tr>
+<td width="20%" align="right"><b>Time</b></td>
+<td width="4%"></td>
+<td width="10%"><b>Type</b></td>
+<td width="8%"><b>Sub</b></td>
+<td width="8%"><b>Chan</b></td>
+<td width="8%"><b>Num</b></td>
+<td width="4%"></td>
+<td><b>Note</b></td>
+</tr>
+<xsl:apply-templates select="annotation"/>
+</table>
+
+</body>
+</html>
+</xsl:template>
+
+<xsl:template match="anntabentry">
+<tr>
+<td align="right"><xsl:value-of select="anncount"/></td>
+<td></td>
+<td><xsl:value-of select="anncode"/>
+(<xsl:value-of select="anndescription"/>)</td>
+</tr>
+</xsl:template>
+
+<xsl:template match="annotation">
+<tr>
+<td align="right"><xsl:value-of select="time"/></td>
+<td></td>
+<td><xsl:value-of select="anncode"/></td>
+<td><xsl:value-of select="subtype"/></td>
+<td><xsl:value-of select="chan"/></td>
+<td><xsl:value-of select="num"/></td>
+<td></td>
+<td><xsl:value-of select="aux"/></td>
+</tr>
+</xsl:template>
+
+<xsl:template match="info">
+<h3>Notes</h3>
+<p>
+<xsl:if test="age"><b>Age: </b><xsl:value-of select="age"/>
+<xsl:choose>
+<xsl:when test="age/@units"> (<xsl:value-of select="age/@units"/>)</xsl:when>
+<xsl:otherwise> years</xsl:otherwise>
+</xsl:choose>
+<br/></xsl:if>
+<xsl:if test="sex"><b>Sex: </b>
+<xsl:choose>
+<xsl:when test="sex='M' or sex='m'">male</xsl:when>
+<xsl:when test="sex='F' or sex='f'">female</xsl:when>
+<xsl:otherwise><xsl:value-of select="sex"/></xsl:otherwise>
+</xsl:choose>
+<br/></xsl:if>
+<xsl:if test="diagnosis"><b>Diagnoses: </b>
+<xsl:for-each select="diagnosis">
+<xsl:if test="position()&gt;1">; </xsl:if>
+<xsl:value-of select="."/>
+</xsl:for-each>
+<br/></xsl:if>
+<xsl:if test="medication"><b>Medications: </b>
+<xsl:for-each select="medication">
+<xsl:if test="position()&gt;1">; </xsl:if>
+<xsl:value-of select="."/>
+<xsl:if test="@dosage"> (<xsl:value-of select="@dosage"/>)</xsl:if>
+</xsl:for-each>
+<br/></xsl:if>
+<xsl:for-each select="other">
+<xsl:value-of select="."/><br/>
+</xsl:for-each>
+</p>
+</xsl:template>
+
+<xsl:template match="start">
+<b>Start: </b>
+<xsl:if test="year">
+<xsl:value-of select="year"/>/<xsl:value-of select="format-number(month, '00')"/>/<xsl:value-of select="format-number(day, '00')"/> at </xsl:if><xsl:value-of select="format-number(hour, '00')"/>:<xsl:value-of select="format-number(minute, '00')"/>:<xsl:value-of select="format-number(second, '00')"/>
+<br/>
+</xsl:template>
+
+<xsl:template match="length">
+<b>Duration: </b>
+<xsl:value-of select="format-number(floor(. div /wfdbrecord/samplingfrequency div 3600), '0')"/>:<xsl:value-of select="format-number(floor(. div /wfdbrecord/samplingfrequency mod 3600 div 60), '00')"/>:<xsl:value-of select="format-number(. div /wfdbrecord/samplingfrequency mod 60, '00.###')"/>
+(<xsl:value-of select="."/> sample intervals)<br/>
+</xsl:template>
+
+<xsl:template match="signalfile">
+<xsl:choose>
+<xsl:when test="filename='[none]'">
+<p><b>Each of the signals listed below appears in at least one segment
+of this record.  See the details for each segment (following this
+list of available signals) for additional information.</b></p> 
+</xsl:when>
+<xsl:otherwise>
+<h2>Signal file: <xsl:value-of select="filename"/></h2>
+<xsl:if test="preamblelength">
+<b>Preamble: </b><xsl:value-of select="preamblelength"/> bytes<br/>
+</xsl:if>
+</xsl:otherwise>
+</xsl:choose>
+
+<xsl:for-each select="signal">
+<h3>Signal <xsl:value-of select="position() - 1"/>: <xsl:value-of select="description"/></h3>
+<p>
+<b>Gain: </b><xsl:value-of select="gain"/>
+adu/<xsl:value-of select="units"/><br/>
+<b>Initial value: </b><xsl:value-of select="initialvalue"/> adu<br/>
+<xsl:if test="storageformat!='0'">
+<b>Storage format: </b><xsl:value-of select="storageformat"/><br/>
+</xsl:if>
+<xsl:if test="samplesperframe">
+<b>Samples per frame: </b><xsl:value-of select="samplesperframe"/><br/>
+</xsl:if>
+<xsl:if test="skew">
+<b>Skew: </b><xsl:value-of select="skew"/> sample intervals<br/>
+</xsl:if>
+<b>ADC resolution: </b><xsl:value-of select="adcresolution"/> bits<br/>
+<xsl:if test="adczero!=0">
+<b>ADC zero: </b><xsl:value-of select="adczero"/> adu<br/>
+</xsl:if>
+<xsl:if test="baseline!=0">
+<b>Baseline: </b><xsl:value-of select="baseline"/> adu<br/>
+</xsl:if>
+<xsl:if test="blocksize&gt;0">
+<b>Block size: </b><xsl:value-of select="blocksize"/><br/>
+</xsl:if>
+<xsl:if test="storageformat!='0'">
+<b>Checksum: </b><xsl:value-of select="checksum"/>
+</xsl:if>
+</p>
+</xsl:for-each>
+</xsl:template>
+
+<xsl:template match="segment">
+<hr/><h2>Segment <xsl:value-of select="position()-1"/>: <xsl:value-of select="@name"/></h2>
+<p>
+<xsl:choose>
+<xsl:when test="not(gap)">
+<xsl:apply-templates select="start"/>
+<xsl:apply-templates select="length"/>
+<b>Sampling frequency: </b>
+<xsl:value-of select="samplingfrequency"/>
+samples per signal per second<br/>
+<xsl:if test="counterfrequency">
+<b>Counter frequency: </b>
+<xsl:value-of select="counterfrequency"/>
+counts per second (starting from 
+<xsl:value-of select="counterfrequency/@basecount"/>)<br/>
+</xsl:if>
+<b>Signals: </b>
+<xsl:value-of select="signals"/>
+</xsl:when>
+<xsl:when test="gap">
+<xsl:apply-templates select="gap/start"/>
+<xsl:apply-templates select="gap/length"/>
+</xsl:when>
+</xsl:choose>
+</p>
+<xsl:apply-templates select="signalfile"/>
+
+</xsl:template>
+
+</xsl:stylesheet>
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/xmlann.c wfdb-10.5.5/xml/xmlann.c
--- wfdb-10.5.4/xml/xmlann.c	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/xmlann.c	2010-08-22 23:16:47.000000000 -0400
@@ -0,0 +1,188 @@
+/* file: xmlann.c	G. Moody	22 August 2010
+
+-------------------------------------------------------------------------------
+xmlann: Convert a WFDB-XML file to a WFDB-compatible annotation file
+Copyright (C) 2010 George B. Moody
+
+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 even 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.
+
+You may contact the author by e-mail (george@mit.edu) or postal mail
+(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
+please visit PhysioNet (http://www.physionet.org/).
+_______________________________________________________________________________
+
+The input to xmlann should be an XML file containing a <wfdbannotationset>,
+as specified by 'wfdb.dtd';  see '100s.atr.xml' for a sample in this format.
+The inverse transformation can be performed by 'annxml'.
+
+TODO: collect anntabentry items and write anntab (rearrange in annhea
+so this can be done in one pass).  Optionally write a simple header
+file and include sampling frequency/length/start attributes in it.
+Define and handle new annotation dur and url items.  */
+
+#include <stdio.h>
+#include <string.h>
+#include <wfdb/wfdb.h>
+#include "xmlproc.h"	/* provides main(), process(), DATALEN, qflag, vflag */
+
+char *content;
+int depth, plen, output_not_open;
+
+WFDB_Anninfo ai;
+WFDB_Annotation a;
+char *record, *rec;
+double sps;
+
+void XMLCALL start(void *data, const char *el, const char **attr)
+{
+  int i;
+
+  sprintf(data + strlen(data), "/%s", el);
+  if (vflag) {
+      printf("\n%s", data);
+      for (i = 0; attr[i]; i += 2)
+	  printf(" %s='%s'", attr[i], attr[i + 1]);
+      fflush(stdout);
+  }
+
+  if (strcmp("wfdbannotationset", el) == 0) {
+      if (depth) {
+	  fprintf(stderr, "Malformed input: wfdbannotationset not at root\n");
+	  //	  exit(1);
+      }
+      for (i = 0; attr[i]; i += 2) {
+	  if (attr[i] && strcmp("annotator", attr[i]) == 0)
+	      SSTRCPY(ai.name, attr[i+1]);
+	  if (attr[i] && strcmp("record", attr[i]) == 0)
+	      SSTRCPY(record, attr[i+1]);
+      }
+      if (ai.name == NULL || record == NULL) {
+	  fprintf(stderr, "Malformed input: wfdbannotationset is missing a"
+		  " required annotator or record attribute\n");
+	  //	  exit(1);
+      }
+      for (rec = record + strlen(record); rec > record; rec--)
+	  if (*rec == '/') { rec++; break; }
+      output_not_open = 1;
+  }
+  depth++;
+}
+
+void XMLCALL middle(void *data, const char *el, int len)
+{
+    if (plen == 0) {	/* not in the same element as last time */
+        if (len == 1 && *el == '\n')
+	    return;  /* ignore newlines outside of tags */
+	SALLOC(content, plen = len + 1, sizeof(char));
+	strncpy(content, el, len);
+    }
+    else {
+	SREALLOC(content, plen + len, sizeof(char));
+	strncpy(content + plen - 1, el, len);
+	plen += len;
+    }
+    if (vflag) { printf("\t|%s|", content); fflush(stdout); }
+}
+ 
+void XMLCALL end(void *data, const char *el)
+{
+  char *p;
+  int i;
+  long t;
+
+  depth--;
+  if (depth == 0) {
+      if (strcmp("wfdbannotationset", el) == 0) {
+	  wfdbquit();
+	  if (!qflag) printf("%s.%s\n", rec, ai.name);
+      }
+      else if (record) {
+	  fprintf(stderr, "Malformed input ends without /wfdbannotationset\n");
+	  //	  exit(1);
+      }
+      else {
+	  fprintf(stderr, "No wfdbannotationset in input\n");
+	  //	  exit(1);
+      }
+  }
+  else if (strcmp("samplingfrequency", el) == 0)
+      sscanf(content, "%lf", &sps);
+  else if (strcmp("/wfdbannotationset/annotation", data) == 0) {
+      if (output_not_open) {
+	  if (sps > 0) setafreq(sps);
+	  ai.stat = WFDB_WRITE;
+	  annopen(rec, &ai, 1);
+	  output_not_open = 0;
+      }
+      putann(0, &a);
+      a.anntyp = a.subtyp = a.chan = a.num = 0;
+      a.aux = NULL;
+  }
+  else if (strcmp("/wfdbannotationset/annotation/time", data) == 0) {
+      sscanf(content, "%ld", &t);
+      a.time = t;
+  }
+  else if (strcmp("/wfdbannotationset/annotation/anncode", data) == 0) {
+      a.anntyp = strann(content);
+  }
+  else if (strcmp("/wfdbannotationset/annotation/subtype", data) == 0) {
+      sscanf(content, "%d", &i);
+      a.subtyp = i;
+  }
+  else if (strcmp("/wfdbannotationset/annotation/chan", data) == 0) {
+      sscanf(content, "%d", &i);
+      a.chan = i;
+  }
+  else if (strcmp("/wfdbannotationset/annotation/num", data) == 0) {
+      sscanf(content, "%d", &i);
+      a.num = i;
+  }
+  else if (strcmp("/wfdbannotationset/annotation/aux", data) == 0) {
+      static char auxbuf[256];
+
+      if ((i = strlen(content)) > 254) { i = 254; }
+      a.aux = auxbuf;
+      auxbuf[0] = i;
+      strncpy(auxbuf+1, content, i);
+      auxbuf[i+1] = '\0';
+  }
+
+  plen = 0;
+  for (p = data + strlen(data) - 1; p > (char *)data; p--)
+      if (*p == '/') { *p = '\0'; break; }
+  if (vflag) { printf("."); fflush(stdout); }
+}
+
+void cleanup()
+{
+    content = NULL;
+    depth = plen = output_not_open = 0;
+    ai.name = NULL;
+    a.anntyp = a.subtyp = a.chan = a.num = 0;
+    a.aux = record = rec = NULL;
+    sps = 0;
+}
+
+void help()
+{
+    fprintf(stderr, "usage: %s [ OPTION ...] [ FILE ... ]\n", pname);
+    fprintf(stderr, " OPTIONs may include:\n"
+	    "  -h   print this usage summary\n"
+	    "  -q   quiet mode (print errors only)\n"
+	    "  -v   verbose mode\n"
+	    " FILE is the name of a WFDB-XML annotation file to be converted\n"
+	    " into one or more WFDB annotation files, or '-' to convert the\n"
+	    " standard input.\n");
+    exit(1);
+}
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/xmlhea.c wfdb-10.5.5/xml/xmlhea.c
--- wfdb-10.5.4/xml/xmlhea.c	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/xmlhea.c	2010-08-22 23:19:23.000000000 -0400
@@ -0,0 +1,463 @@
+/* file: xmlhea.c	G. Moody	20 August 2010
+			Last revised:	22 August 2010
+-------------------------------------------------------------------------------
+xmlhea: Convert an XML file to a WFDB-compatible .hea (header) file
+Copyright (C) 2010 George B. Moody
+
+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 even 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.
+
+You may contact the author by e-mail (george@mit.edu) or postal mail
+(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
+please visit PhysioNet (http://www.physionet.org/).
+_______________________________________________________________________________
+
+The input to this program should be an XML file containing a <wfdbrecord>,
+as specified by 'wfdb.dtd';  see '100s.hea.xml' for a sample in this format.
+The inverse transformation can be performed by 'heaxml'.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <wfdb/wfdb.h>
+#include "xmlproc.h"	/* provides main(), process(), DATALEN, qflag, vflag */
+
+void write_header(char *recname);
+void writeinfo(char *tag, char *data);
+
+char *content, *dp;
+int depth, plen;
+
+WFDB_Siginfo *si;
+char *record, *rec, *sfname, *age, *sex, **diagnosis, *extra, **medication,
+    **other;
+double sps, cps, cbase, second;
+int nseg, nsig, sig, dx, rx, ox;
+int year, month, day, hour, minute;
+long length, offset;
+WFDB_Seginfo *segi;
+
+int mnsig;
+double msps, mcps, mcbase, msecond;
+int myear, mmonth, mday, mhour, mminute;
+long mlength;
+
+struct asiginfo {
+    char *fname;
+    int fmt;
+    int skew;
+    long offset;
+} *sa;
+static char info[254], *ip;
+
+void XMLCALL start(void *data, const char *el, const char **attr)
+{
+  int i;
+
+  sprintf(data + strlen(data), "/%s", el);
+  if (vflag) {
+      printf("\n%s", data);
+      for (i = 0; attr[i]; i += 2)
+	  printf(" %s='%s'", attr[i], attr[i + 1]);
+      fflush(stdout);
+  }
+
+  if (depth == 0)
+      dp = data;
+  if (strcmp("wfdbrecord", el) == 0) {
+      if (depth) {
+	  fprintf(stderr, "Malformed input: wfdbrecord not at root\n");
+	  //	  exit(1);
+      }
+      if (!attr[0] || strcmp("name", attr[0])) {
+	  fprintf(stderr, "Malformed input: no name for wfdbrecord\n");
+	  //	  exit(1);
+      }
+      dp = data + strlen(data);
+      SSTRCPY(record, attr[1]);
+      for (rec = record + strlen(record); rec > record; rec--)
+	  if (*rec == '/') { rec++; break; }
+      SUALLOC(sfname, strlen(rec)+5, sizeof(char));
+      sprintf(sfname, "%s.dat", rec);    /* default signal file name */
+  }
+  if (strcmp("counterfrequency", el) == 0) {
+      for (i = 0; attr[i]; i += 2) {
+	  if (strcmp(attr[i], "basecount") == 0)
+	      sscanf(attr[i+1], "%lf", &cbase);
+      }
+  }
+  if (strcmp("/wfdbrecord/segment", data) ==  0) {
+      dp = data + strlen(data);
+      if (!attr[0] || strcmp("name", attr[0])) {
+	  fprintf(stderr, "Malformed input: no name for segment %d\n", nseg);
+	  //	  exit(1);
+      }
+      if (nseg == 0) {
+	  SUALLOC(segi, 1, sizeof(WFDB_Seginfo));
+
+	  mnsig = nsig; msps = sps; mcps = cps; mcbase = cbase;
+	  myear = year; mmonth = month; mday = day;
+	  mhour = hour; mminute = minute; msecond = second;
+	  mlength = length;
+
+	  if (si && strcmp(sfname, "[none]") == 0) {
+	      char *layout_rec;
+
+	      SUALLOC(layout_rec, strlen(rec) + 8, sizeof(char));
+	      sprintf(layout_rec, "%s_layout", rec);
+	      strncpy(segi[0].recname, layout_rec, WFDB_MAXRNL);
+	      for (i = 0; i < nsig; i++) {
+		  SSTRCPY(sa[i].fname,"~");
+		  segi[0].nsamp = si[i].nsamp = si[i].cksum = 0;
+	      }
+	      write_header(layout_rec);
+	      nseg = 1;
+	  }
+      }
+      if (nseg > 0) {
+	  SREALLOC(segi, nseg+1, sizeof(WFDB_Seginfo));
+      }
+      strncpy(segi[nseg].recname, attr[1], WFDB_MAXRNL);
+      sig = dx = rx = ox = 0;
+      SFREE(diagnosis); SFREE(medication); SFREE(other);
+  }
+
+  depth++;
+}
+
+void XMLCALL middle(void *data, const char *el, int len)
+{
+    if (plen == 0) {	/* not in the same element as last time */
+        if (len == 1 && *el == '\n')
+	    return;  /* ignore newlines outside of tags */
+	SALLOC(content, plen = len + 1, sizeof(char));
+	strncpy(content, el, len);
+    }
+    else {
+	SREALLOC(content, plen + len, sizeof(char));
+	strncpy(content + plen - 1, el, len);
+	plen += len;
+    }
+    if (vflag) { printf("\t|%s|", content); fflush(stdout); }
+}
+ 
+void XMLCALL end(void *data, const char *el)
+{
+  char *p;
+
+  depth--;
+  if (depth == 0) {
+      if (strcmp("wfdbrecord", el) == 0) {
+	  if (nseg == 0)
+	      write_header(rec);
+	  else {
+	      char *ofname;
+	      FILE *ofile;
+	      int i;
+
+	      SUALLOC(ofname, strlen(rec)+5, sizeof(char));
+	      sprintf(ofname, "%s.hea", rec);
+	      ofile = fopen(ofname, "wt");
+	      fprintf(ofile, "%s/%d %d %.12g", rec, nseg, mnsig, msps);
+	      if (mcps) {
+		  fprintf(ofile, "%.12g", mcps);
+		  if (mcbase)
+		      fprintf(ofile, "(%.12g)", mcbase);
+	      }
+	      fprintf(ofile, " %ld %02d:%02d:%02d",
+		      mlength, mhour, mminute, (int)msecond);
+	      if (i = ((int)(msecond*1000. + 0.5)) % 1000)
+		  fprintf(ofile, ".%03d", i);
+	      if (myear || mmonth || mday)
+		  fprintf(ofile, " %02d/%02d/%d", mday, mmonth, myear);
+	      fprintf(ofile, "\r\n");
+	      for (i = 0; i < nseg; i++)
+		  fprintf(ofile, "%s %ld\r\n", segi[i].recname, segi[i].nsamp);
+	      if (!qflag) printf("%s\n", ofname);
+	  }
+      }
+      else if (record) {
+	  fprintf(stderr, "Malformed input ends without /wfdbrecord\n");
+	  //	  exit(1);
+      }
+      else {
+	  fprintf(stderr, "No wfdbrecord in input\n");
+	  //	  exit(1);
+      }
+  }
+  else if (depth == 1) {
+      if (strcmp("length", el) == 0)
+	  sscanf(content, "%ld", &length);
+      else if (strcmp("samplingfrequency", el) == 0)
+	  sscanf(content, "%lf", &sps);
+      else if (strcmp("counterfrequency", el) == 0)
+	  sscanf(content, "%lf", &cps);
+      else if (strcmp("signals", el) == 0) {
+	  sscanf(content, "%ld", &nsig);
+	  SUALLOC(si, sizeof(WFDB_Siginfo), nsig);
+	  SUALLOC(sa, sizeof(struct asiginfo), nsig);
+      }
+      else if (strcmp("segment", el) == 0) {
+	  if (strcmp(segi[nseg].recname, "~"))
+	      write_header(segi[nseg].recname);
+	  nseg++;
+      }
+  }
+
+  else if (strcmp("/wfdbrecord/segment/length", data) == 0) {
+      sscanf(content, "%ld", &length);
+      segi[nseg].nsamp = length;
+  }
+  else if (strcmp("/wfdbrecord/segment/gap/length", data) == 0) {
+      strcpy(segi[nseg].recname, "~");
+      sscanf(content, "%ld", &length);
+      segi[nseg].nsamp = length;
+  }
+  else if (strcmp("/wfdbrecord/segment/signals", data) == 0)
+      sscanf(content, "%ld", &nsig);
+  
+  else if (strcmp("/info/age", dp) == 0) {
+      SSTRCPY(age, content);
+  }
+  else if (strcmp("/info/sex", dp) == 0) {
+      SSTRCPY(sex, content);
+  }
+  else if (strcmp("/info/extra", dp) == 0) {
+      SSTRCPY(extra, content);
+  }
+  else if (strcmp("/info/diagnosis", dp) == 0) {
+      if (dx == 0) { SUALLOC(diagnosis, 1, sizeof(char *)); }
+      else { SREALLOC(diagnosis, dx+1, sizeof(char *)); }
+      if (diagnosis[dx]) {
+	  SREALLOC(diagnosis[dx], strlen(content)+1, sizeof(char));
+      }
+      else {
+	  SUALLOC(diagnosis[dx], strlen(content)+1, sizeof(char));
+      }
+      strcpy(diagnosis[dx++], content);
+  }
+  else if (strcmp("/info/medication", dp) == 0) {
+      if (rx == 0) { SUALLOC(medication, 1, sizeof(char *)); }
+      else { SREALLOC(medication, rx+1, sizeof(char *)); }
+      if (medication[rx]) {
+	  SREALLOC(medication[rx], strlen(content)+1, sizeof(char));
+      }
+      else {
+	  SUALLOC(medication[rx], strlen(content)+1, sizeof(char));
+      }
+      strcpy(medication[rx++], content);
+  }
+  else if (strcmp("/info/other", dp) == 0) {
+      if (ox == 0) { SUALLOC(other, 1, sizeof(char *)); }
+      else { SREALLOC(other, ox+1, sizeof(char *)); }
+      SUALLOC(other[ox], strlen(content)+1, sizeof(char));
+      strcpy(other[ox++], content);
+  }
+
+  else if (strcmp("/start/year", dp) == 0) {
+      sscanf(content, "%ld", &year);
+  }
+  else if (strcmp("/start/month", dp) == 0) {
+      sscanf(content, "%ld", &month);
+  }
+  else if (strcmp("/start/day", dp) == 0) {
+      sscanf(content, "%ld", &day);
+  }
+  else if (strcmp("/start/hour", dp) == 0) {
+      sscanf(content, "%ld", &hour);
+  }
+  else if (strcmp("/start/minute", dp) == 0) {
+      sscanf(content, "%ld", &minute);
+  }
+  else if (strcmp("/start/second", dp) == 0) {
+      sscanf(content, "%lf", &second);
+  }
+
+  else if (strcmp("/signalfile/filename", dp) == 0) {
+      SSTRCPY(sfname, content);
+  }
+  else if (strcmp("/signalfile/preamblelength", dp) == 0) {
+      sscanf(content, "%ld", &offset);
+  }
+  else if (strcmp("/signalfile/signal", dp) == 0) {
+      SSTRCPY(sa[sig].fname, sfname);
+      sa[sig].offset = offset;
+      si[sig].nsamp = length;
+      sig++;
+  }
+  else if (strcmp("/signalfile/signal/description", dp) == 0) {
+      SSTRCPY(si[sig].desc, content);
+  }
+  else if (strcmp("/signalfile/signal/gain", dp) == 0) {
+      sscanf(content, "%lf", &(si[sig].gain));
+  }
+  else if (strcmp("/signalfile/signal/units", dp) == 0) {
+      SSTRCPY(si[sig].units, content);
+  }
+  else if (strcmp("/signalfile/signal/initialvalue", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].initval));
+  }
+  else if (strcmp("/signalfile/signal/storageformat", dp) == 0) {
+      sscanf(content, "%d", &sa[sig].fmt);
+  }
+  else if (strcmp("/signalfile/signal/blocksize", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].bsize));
+  }
+  else if (strcmp("/signalfile/signal/adcresolution", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].adcres));
+  }
+  else if (strcmp("/signalfile/signal/adczero", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].adczero));
+  }
+  else if (strcmp("/signalfile/signal/baseline", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].baseline));
+  }
+  else if (strcmp("/signalfile/signal/checksum", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].cksum));
+  }
+  else if (strcmp("/signalfile/signal/samplesperframe", dp) == 0) {
+      sscanf(content, "%d", &(si[sig].spf));
+  }
+  else if (strcmp("/signalfile/signal/skew", dp) == 0) {
+      sscanf(content, "%d", &sa[sig].skew);
+  }
+
+  plen = 0;
+  for (p = data + strlen(data) - 1; p > (char *)data; p--)
+      if (*p == '/') { *p = '\0'; break; }
+  if (vflag) { printf("."); fflush(stdout); }
+}
+
+void write_header(char *recname)
+{
+    int i, dxlen, rxlen, oxlen;
+
+    if (!qflag) printf("%s.hea\n", recname);
+    if (sps > 0)
+	setsampfreq(sps);
+    if (cps > 0) {
+	setcfreq(cps);
+	if (cbase > 0) setbasecount(cbase);
+    }
+    if (year || month || day || hour || minute || second) {
+	char tstart[24];
+	if (year || month || day)
+	    sprintf(tstart, "%02d:%02d:%g %02d/%02d/%d",
+		    hour, minute, second, day, month, year);
+	else
+	    sprintf(tstart, "%02d:%02d:%g", hour, minute, second);
+	setbasetime(tstart);
+    }
+
+    /* IMPORTANT: Before invoking osigopen, si[*].fname is set to "~", and
+       si[*].fmt is set to 0, to ensure that osigfopen does not actually create
+       (empty) output signal files (which would destroy any existing files of
+       the same names).  It's necessary to call osigfopen in order to allow
+       skews and offsets to be set, however.  si[*].fname and si[*].fmt must be
+       set to their intended values (which have been preserved in sa[*]) after
+       calling osigfopen and before calling setheader.
+    */
+    for (i = 0; i < nsig; i++) {
+	si[i].fname = "~";
+	si[i].fmt = 0;
+    }
+     if (osigfopen(si, nsig) == nsig) {
+	for (i = 0; i < nsig; i++) {
+	    si[i].fname = sa[i].fname;
+	    si[i].fmt = sa[i].fmt;
+	    wfdbsetskew(i, sa[i].skew);
+	    wfdbsetstart(i, sa[i].offset);
+	}
+    }
+    setheader(recname, si, nsig);
+
+    /* After calling setheader, the info strings are written to the output. */
+    if (age == NULL || strlen(age) > 5) age = "?";
+    if (sex == NULL || strlen(sex) > 20) sex = "?";
+    sprintf(info, " <age>: %s <sex>: %s", age, sex);
+    if (dx == 0) sprintf(info + strlen(info), " <diagnoses>: ?");
+    if (dx == 1 && strlen(diagnosis[0]) < 78 - (strlen(info) + 14)) {
+	sprintf(info + strlen(info), " <diagnoses>: %s", diagnosis[0]);
+	dx = 0;
+    }
+    if (rx == 0) sprintf(info + strlen(info), " <medications>: ?");
+    if (rx == 1 && strlen(medication[0]) < 78 - (strlen(info) + 16)) {
+	sprintf(info + strlen(info), " <medications>: %s", medication[0]);
+	rx = 0;
+    }
+    putinfo(info);
+    if (dx)
+	for (i = 0; i < dx; i++)
+	    writeinfo("<diagnoses>: ", diagnosis[i]);
+    if (rx)
+	for (i = 0; i < rx; i++)
+	    writeinfo("<medications>: ", medication[i]);
+    if (extra)
+	writeinfo(" ", extra);
+    if (ox > 1)
+	for (i = 0; i < ox; i++)
+	    writeinfo("", other[i]);
+
+    wfdbquit();	/* flush any pending writes and close files */
+}
+
+void writeinfo(char *tag, char *data)
+{
+    char info[80];
+    int len = strlen(tag) + strlen(data);
+
+    while (len >= 80) {
+	strcpy(info, tag);
+	strncat(info, data, 79-strlen(tag));
+	putinfo(info);
+	data += 79-strlen(tag);
+	tag = "# ";
+    }
+    strcpy(info, tag);
+    strcat(info, data);
+    putinfo(info);
+}
+
+void cleanup()
+{
+    content = dp = NULL;
+    depth = plen = 0;
+    si = NULL;
+    record = rec = sfname = age = sex = extra = NULL;
+    diagnosis = medication = other = NULL;
+    sps = cps = cbase = second = 0;
+    nseg = nsig = sig = dx = rx = ox = 0;
+    year = month = day = hour = minute = 0;
+    length = offset = 0;
+    segi = NULL;
+    mnsig = 0;
+    msps = mcps = mcbase = msecond = 0;
+    myear = mmonth = mday = mhour = mminute = 0;
+    mlength = 0;
+    sa = NULL;
+    info[0] = '\0';
+    ip = NULL;
+}
+
+void help()
+{
+    fprintf(stderr, "usage: %s [ OPTION ...] [ FILE ... ]\n", pname);
+    fprintf(stderr, " OPTIONs may include:\n"
+	    "  -h   print this usage summary\n"
+	    "  -q   quiet mode (print errors only)\n"
+	    "  -v   verbose mode\n"
+	    " FILE is the name of a WFDB-XML header file to be converted\n"
+	    " into one or more WFDB .hea files, or '-' to convert the\n"
+	    " standard input.\n");
+    exit(1);
+}
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/xmlproc.h wfdb-10.5.5/xml/xmlproc.h
--- wfdb-10.5.4/xml/xmlproc.h	1969-12-31 19:00:00.000000000 -0500
+++ wfdb-10.5.5/xml/xmlproc.h	2010-08-22 18:58:26.000000000 -0400
@@ -0,0 +1,130 @@
+/* file: xmlproc.h	G. Moody	20 August 2010
+			Last revised:	22 August 2010
+-------------------------------------------------------------------------------
+xmlproc: generic functions for processing XML files
+Copyright (C) 2010 George B. Moody
+
+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 even 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.
+
+You may contact the author by e-mail (george@mit.edu) or postal mail
+(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
+please visit PhysioNet (http://www.physionet.org/).
+_______________________________________________________________________________
+
+This code should be compiled together with one of xmlhea.c, xmlann.c, or
+xmldat.c, which provide implementations of the callback functions start(),
+middle(), and end() for processing WFDB-XML header, annotation, and signal
+files respectively, as well as the functions cleanup() and help().
+*/
+
+#include <expat.h>
+
+/* functions including callbacks defined in xmlhea.c, xmlann.c, xmldat.c */
+void XMLCALL start(void *data, const char *el, const char **attr);
+void XMLCALL middle(void *data, const char *el, int len);
+void XMLCALL end(void *data, const char *el);
+void cleanup(void);
+void help(void);
+
+#define DATALEN	1024	/* max length of *data passed among callbacks,
+			   in characters */
+
+char *pname;
+int qflag, vflag;
+
+int main(int argc, char **argv)
+{
+    FILE *ifile;
+    int i;
+    void process(FILE *ifile);
+
+    pname = argv[0];
+    if (argc < 2) help();
+    for (i = 1; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    switch (argv[i][1]) {
+	    case '\0': process(stdin); break;
+	    case 'h': help(); break;
+	    case 'q': qflag = 1; break;
+	    case 'v': vflag = 1; break;
+	    default: fprintf(stderr, "%s: unrecognized option %s\n",
+			     argv[0], argv[i]);
+		exit(1);
+		break;
+	    }
+	}
+	else {
+	    ifile = fopen(argv[i], "rt");
+	    if (ifile) {
+		process(ifile);
+		fclose(ifile);
+	    }
+	    else {
+		fprintf(stderr, "%s: can't open %s\n", argv[0], argv[i]);
+		exit(1);
+	    }
+	}	
+    }
+    exit(0);
+}
+
+/* Definitions needed by process().  XML_LARGE_SIZE and XML_USE_MSC_EXTENSIONS
+   may be defined in <expat.h>. */
+#ifdef XML_LARGE_SIZE
+#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
+#define XML_FMT_INT_MOD "I64"
+#else
+#define XML_FMT_INT_MOD "ll"
+#endif
+#else
+#define XML_FMT_INT_MOD "l"
+#endif
+
+#define BUFLEN		8192
+
+void process(FILE *ifile)
+{
+    int done = 0, len;
+    static char buf[BUFLEN], userdata[DATALEN];
+    XML_Parser p = XML_ParserCreate(NULL);
+
+    if (! p) {
+	fprintf(stderr, "Couldn't allocate memory for parser\n");
+	exit(2);
+    }
+
+    XML_SetUserData(p, userdata);
+    XML_SetElementHandler(p, start, end);
+    XML_SetCharacterDataHandler(p, middle);
+
+    do {
+	len = (int)fread(buf, 1, BUFLEN, ifile);
+	if (ferror(ifile)) {
+	    fprintf(stderr, "Read error\n");
+	    exit(-1);
+	}
+	done = feof(ifile);
+
+	if (XML_Parse(p, buf, len, done) == XML_STATUS_ERROR) {
+	    fprintf(stderr, "Parse error at line %" XML_FMT_INT_MOD "u:\n%s\n",
+		    XML_GetCurrentLineNumber(p),
+		    XML_ErrorString(XML_GetErrorCode(p)));
+	    exit(-1);
+	}
+    } while (!done);
+    XML_ParserFree(p);
+    cleanup();
+    userdata[0] = '\0';
+    if (vflag) printf("\n");
+}
