diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/app/rdsamp.c wfdb-10.4.25/app/rdsamp.c
--- wfdb-10.4.24/app/rdsamp.c	2009-04-30 15:30:35.000000000 -0400
+++ wfdb-10.4.25/app/rdsamp.c	2009-11-06 12:13:54.000000000 -0500
@@ -1,5 +1,5 @@
 /* file: rdsamp.c	G. Moody	 23 June 1983
-			Last revised:   20 April 2009
+			Last revised:   6 November 2009
 
 -------------------------------------------------------------------------------
 rdsamp: Print an arbitrary number of samples from each signal
@@ -44,9 +44,10 @@
 int argc;
 char *argv[];
 {
-    char *record = NULL, *search = NULL, *prog_name();
-    int highres = 0, i, isiglist, nsig, nosig = 0, pflag = 0, s, *sig = NULL,
-        timeunits = SECONDS, vflag = 0;
+    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;
     WFDB_Frequency freq;
     WFDB_Sample *v;
     WFDB_Siginfo *si;
@@ -56,6 +57,9 @@
     pname = prog_name(argv[0]);
     for (i = 1 ; i < argc; i++) {
 	if (*argv[i] == '-') switch (*(argv[i]+1)) {
+	  case 'c':	/* output in CSV format */
+	    cflag = 1;
+	    break;
 	  case 'f':	/* starting time */
 	    if (++i >= argc) {
 		(void)fprintf(stderr, "%s: time must follow -f\n", pname);
@@ -78,14 +82,6 @@
 	    }
 	    maxl = i;
 	    break;
-	  case 'r':	/* record name */
-	    if (++i >= argc) {
-		(void)fprintf(stderr, "%s: record name must follow -r\n",
-			      pname);
-		exit(1);
-	    }
-	    record = argv[i];
-	    break;
 	  case 'P':	/* output in high-precision physical units */
 	    ++pflag;	/* (fall through to case 'p') */
 	  case 'p':	/* output in physical units specified */
@@ -97,6 +93,14 @@
 	    else if (*(argv[i]+2) == 'S') timeunits = SAMPLES;
 	    else timeunits = SECONDS;
 	    break;
+	  case 'r':	/* record name */
+	    if (++i >= argc) {
+		(void)fprintf(stderr, "%s: record name must follow -r\n",
+			      pname);
+		exit(1);
+	    }
+	    record = argv[i];
+	    break;
 	  case 's':	/* signal list follows */
 	    isiglist = i+1; /* index of first argument containing a signal # */
 	    while (i+1 < argc && *argv[i+1] != '-') {
@@ -118,7 +122,6 @@
 	    }
 	    search = argv[i];
 	    break;
-	      
 	  case 't':	/* end time */
 	    if (++i >= argc) {
 		(void)fprintf(stderr, "%s: time must follow -t\n",pname);
@@ -164,12 +167,10 @@
     if (to > 0L && (to = strtim(argv[to])) < 0L)
 	to = -to;
     if (nosig) {		/* print samples only from specified signals */
-#ifndef lint
 	if ((sig = (int *)malloc((unsigned)nosig*sizeof(int))) == NULL) {
 	    (void)fprintf(stderr, "%s: insufficient memory\n", pname);
 	    exit(2);
 	}
-#endif
 	for (i = 0; i < nosig; i++) {
 	    if ((s = findsig(argv[isiglist+i])) < 0) {
 		(void)fprintf(stderr, "%s: can't read signal '%s'\n", pname,
@@ -181,12 +182,10 @@
 	nsig = nosig;
     }
     else {			/* print samples from all signals */
-#ifndef lint
 	if ((sig = (int *)malloc((unsigned)nsig*sizeof(int))) == NULL) {
 	    (void)fprintf(stderr, "%s: insufficient memory\n", pname);
 	    exit(2);
 	}
-#endif
 	for (i = 0; i < nsig; i++)
 	    sig[i] = i;
     }
@@ -204,142 +203,252 @@
     if (maxl && (to == 0L || to > from + maxl))
 	to = from + maxl;
 
-    /* Print column headers if '-v' option selected. */
-    if (vflag) {
-	char *p, *r, *t;
-	int j, l;
+    /* Adjust timeunits if starting time is undefined. */
+    if (timeunits == TIMSTR || timeunits == HHMMSS) {
+	char *p = timstr(0L);
+	if (*p != '[') timeunits = HHMMSS;
+	else if (strlen(p) < 16) timeunits = SHORTTIMSTR;
+	else if (freq > 1.0) timeunits = MSTIMSTR;
+    }
+    if (timeunits == HOURS) freq *= 3600.;
+    else if (timeunits == MINUTES) freq *= 60.;
 
-	if (pflag == 0) (void)printf("       sample #");
-	else if (timeunits == SAMPLES) (void)printf("sample interval");
-	else if (timeunits == TIMSTR || timeunits == HHMMSS) {
-	    p = timstr(0L);
-	    if (*p != '[')
-		timeunits = HHMMSS;
-	    else if (strlen(p) < 16)
-		timeunits = SHORTTIMSTR;
-	    if (timeunits == HHMMSS)
-		printf("   Elapsed time");
-	    else if (timeunits == SHORTTIMSTR)
-		printf("     Time");
+    /* Set formats for output. */
+    if (cflag) {  /* CSV output selected */
+	snfmt = ",'%s'";
+	if (pflag) { 	/* output in physical units */
+	    switch (timeunits) {
+	      case SAMPLES:     tnfmt = "'sample interval'";
+		                sprintf(tustr, "'%g sec'", 1./freq);
+				tufmt = tustr; break;
+	      case SHORTTIMSTR: tnfmt = "'Time'";
+		                tufmt = "'hh:mm:ss.mmm'"; break;
+	      case TIMSTR:      tnfmt = "'Time and date'";
+		                tufmt = "'hh:mm:ss dd/mm/yyyy'"; break;
+	      case MSTIMSTR:    tnfmt = "'Time and date'";
+		                tufmt = "'hh:mm:ss.mmm dd/mm/yyyy'"; break;
+	      case HHMMSS:      tnfmt = "'Elapsed time'";
+		                tufmt = "'hh:mm:ss.mmm'"; break;
+	      case HOURS:       tnfmt = "'Elapsed time'";
+		                tufmt = "'hours'"; break;
+	      case MINUTES:     tnfmt = "'Elapsed time'";
+		                tufmt = "'minutes'"; break;
+	      default:
+	      case SECONDS:     tnfmt = "'Elapsed time'";
+		                tufmt = "'seconds'"; break;
+	    }
+	    invalid = ",-";
+	    if (pflag > 1)	/* output in high-precision physical units */
+		vfmt = ",%.8lf";
+	    else
+		vfmt = ",%.3lf";
+	}
+	else {	/* output in raw units */
+	    tnfmt = "'sample #'";
+	    //	    sprintf(tufmt, "'%g sec'", 1./freq);
+	    tfmt = "%ld";
+	    vfmt = ",%d";
+	}
+    }
+    else {	/* output in tab-separated columns selected */
+	if (pflag) {	/* output in physical units */
+	    switch (timeunits) {
+	      case SAMPLES:     tnfmt = "sample interval";
+		                sprintf(speriod, "(%g", 1./freq);
+                                speriod[10] = '\0';
+		                sprintf(tustr, "%10s sec)", speriod);
+				tufmt = tustr; break;
+	      case SHORTTIMSTR: tnfmt = "     Time";
+		                tufmt = "(hh:mm:ss.mmm)"; break;
+	      case TIMSTR:	tnfmt = "   Time      Date    ";
+		                tufmt = "(hh:mm:ss dd/mm/yyyy)"; break;
+	      case MSTIMSTR:    tnfmt = "      Time      Date    ";
+		                tufmt = "(hh:mm:ss.mmm dd/mm/yyyy)"; break;
+	      case HHMMSS:      tnfmt = "   Elapsed time";
+		                tufmt = "   hh:mm:ss.mmm"; break;
+	      case HOURS:       tnfmt = "   Elapsed time";
+		                tufmt = "        (hours)"; break;
+	      case MINUTES:     tnfmt = "   Elapsed time";
+		                tufmt = "      (minutes)"; break;
+	      default:
+	      case SECONDS:     tnfmt = "   Elapsed time";
+		                tufmt = "      (seconds)"; break;
+	    }
+	    if (pflag > 1) {	/* output in high-precision physical units */
+		snfmt = "\t%15s";
+		invalid = "\t              -";
+		vfmt = "\t%15.8lf";
+	    }
 	    else {
-		if (freq > 1.0) {
-		    timeunits = MSTIMSTR;
-		    (void)printf("   ");
-		}
-		(void)printf("   Time      Date    ");
+		snfmt = "\t%7s";
+		invalid = "\t      -";
+		vfmt = "\t%7.3lf";
 	    }
 	}
-	else (void)printf("   Elapsed time");
-	if ((t = malloc((strlen(record)+30) * sizeof(char))) == NULL) {
-	    fprintf(stderr, "%s: insufficient memory\n", pname);
-	    exit(2);
+	else {	/* output in raw units */
+	    snfmt = "\t%7s";
+	    tnfmt = "       sample #";
+	    tfmt = "%15ld";
+	    vfmt = "\t%7d";
 	}
-	for (r = record+strlen(record); r > record; r--)
-	    if (*r == '/') {
-		r++;
-		break;
-	    }
+    }
+
+    /* Print column headers if '-v' option selected. */
+    if (vflag) {
+	char *p, *t;
+	int j, l;
+
+	(void)printf(tnfmt);
+
 	for (i = 0; i < nsig; i++) {
-	    /* Check if a default signal description was provided.  Note that
-	       if the description comes from the header file, the record name
-	       may include path information that might not match that in
-	       'record' (if any);  so we compare only the final part of the
-	       description (p+j below) against the expected description (t). */
-	    (void)sprintf(t, "%s, signal %d", r, sig[i]);
+	    /* Check if a default signal description was provided by looking
+	       for the string ", signal " in the desc field.  If so, replace it
+	       with a shorter string. */
 	    p = si[sig[i]].desc;
-	    j = strlen(p) - strlen(t);
-	    if (j > 0 && strcmp(p+j, t) == 0) {
-		(void)sprintf(t, "sig %d", sig[i]);
-		p = t;
-	    }
-	    l = strlen(p);
-	    if (pflag > 1) {
-		if (l > 15) p += l - 15;
-		(void)printf("\t%15s", p);
+	    if (strstr(p, ", signal ")) {
+		char *t;
+		if (t = malloc(10*sizeof(char))) {
+		    (void)sprintf(t, "sig %d", sig[i]);
+		    p = t;
+		}
 	    }
-	    else {
-		if (l > 7) p+= l - 7;
-		(void)printf("\t%7s", p);
+	    if (cflag == 0) {
+		l = strlen(p);
+		if (pflag > 1) {
+		    if (l > 15) p += l - 15;
+		}
+		else {
+		    if (l > 7) p+= l - 7;
+		}
 	    }
+	    else
+		p = escapify(p);
+	    (void)printf(snfmt, p);
 	}
+    
 	(void)printf("\n");
     }
 
     /* Print data in physical units if '-p' option selected. */
     if (pflag) {
-	char *p, *fmt = pflag > 1 ?  "\t%15.8lf" : "\t%7.3f";
+	char *p;
 
-	if (timeunits == HOURS) freq *= 3600.;
-	else if (timeunits == MINUTES) freq *= 60.;
 
 	/* Print units as a second line of column headers if '-v' selected. */
 	if (vflag) {
 	    char s[12];
 
-	    switch (timeunits) {
- 	    case TIMSTR:      (void)printf("(hh:mm:ss dd/mm/yyyy)"); break;
- 	    case MSTIMSTR:    (void)printf("(hh:mm:ss.mmm dd/mm/yyyy)"); break;
-	    case SHORTTIMSTR: (void)printf("(hh:mm:ss.mmm)"); break;
-	    case HHMMSS:      (void)printf("   hh:mm:ss.mmm"); break;
-	    case HOURS:       (void)printf("        (hours)"); break;
-	    case MINUTES:     (void)printf("      (minutes)"); break;
-	    default:
-	    case SECONDS:     (void)printf("      (seconds)"); break;
-	    case SAMPLES:     (void)sprintf(s, "(%g", 1./freq);
-			      (void)printf("%10s sec)", s);
-	    }
+	    (void)printf(tufmt);
+
 	    for (i = 0; i < nsig; i++) {
-		char ustring[16];
-		int len;
-	
 		p = si[sig[i]].units;
 		if (p == NULL) p = "mV";
-		len = strlen(p);
-		if (pflag > 1) { if (len > 13) len = 13; }
-		else if (len > 5) len = 5;
-		ustring[0] = '(';
-		strncpy(ustring+1, p, len);
-		ustring[len+1] = '\0';
-		(void)printf(pflag > 1 ? "\t%14s)" : "\t%6s)", ustring);    
+		if (cflag == 0) {
+		    char ustring[16];
+		    int len;
+	
+		    len = strlen(p);
+		    if (pflag > 1) { if (len > 13) len = 13; }
+		    else if (len > 5) len = 5;
+		    ustring[0] = '(';
+		    strncpy(ustring+1, p, len);
+		    ustring[len+1] = '\0';
+		    (void)printf(pflag > 1 ? "\t%14s)" : "\t%6s)", ustring);    
+		}
+		else {
+		    p = escapify(p);
+		    (void)printf(",'%s'", p);
+		}
 	    }
+
 	    (void)printf("\n");
 	}
 
 	while ((to == 0L || from < to) && getvec(v) >= 0) {
-	    switch (timeunits) {
-	      case TIMSTR:   (void)printf("%s", timstr(-from)); break;
-	      case SHORTTIMSTR:
-	      case MSTIMSTR: (void)printf("%s", mstimstr(-from)); break;
-	      case HHMMSS:   (void)printf("%15s", from == 0L ?
-					  "0:00.000" : mstimstr(from)); break;
-	      case SAMPLES:  (void)printf("%15ld", from); break;
-	      default:
-	      case SECONDS:  (void)printf("%15.3lf", (double)from/freq); break;
-	      case MINUTES:  (void)printf("%15.5lf", (double)from/freq); break;
-	      case HOURS:    (void)printf("%15.7lf", (double)from/freq); break;
+	    if (cflag == 0) {
+	      switch (timeunits) {
+	        case TIMSTR:   (void)printf("%s", timstr(-from)); break;
+	        case SHORTTIMSTR:
+	        case MSTIMSTR: (void)printf("%s", mstimstr(-from)); break;
+	        case HHMMSS:   (void)printf("%15s", from == 0L ?
+					    "0:00.000" : mstimstr(from)); break;
+	        case SAMPLES:  (void)printf("%15ld", from); break;
+	        default:
+	        case SECONDS:  (void)printf("%15.3lf",(double)from/freq); break;
+	        case MINUTES:  (void)printf("%15.5lf",(double)from/freq); break;
+	        case HOURS:    (void)printf("%15.7lf",(double)from/freq); break;
+	      }
+	    }
+	    else {
+	      switch (timeunits) {
+	        case TIMSTR:
+		    for (p = timstr(-from); *p == ' '; p++)
+			;
+		    (void)printf("'%s'", p); break;
+	        case SHORTTIMSTR:
+	        case MSTIMSTR:
+		    for (p = mstimstr(-from); *p == ' '; p++)
+			;
+		    (void)printf("'%s'", p); break;
+	        case HHMMSS:
+		    if (from == 0L) printf("'0:00.000'");
+		    else {
+			for (p = mstimstr(from); *p == ' '; p++)
+			    ;
+			(void)printf("'%s'", p); break;
+		    }
+		    break;
+	        case SAMPLES:  (void)printf("%ld", from); break;
+	        default:
+	        case SECONDS:  (void)printf("%.3lf",(double)from/freq); break;
+	        case MINUTES:  (void)printf("%.5lf",(double)from/freq); break;
+	        case HOURS:    (void)printf("%.7lf",(double)from/freq); break;
+	      }
 	    }
+
 	    from++;
 	    for (i = 0; i < nsig; i++) {
 		if (v[sig[i]] != WFDB_INVALID_SAMPLE)
-		    (void)printf(fmt,
+		    (void)printf(vfmt,
 		    (double)(v[sig[i]] - si[sig[i]].baseline)/si[sig[i]].gain);
 		else
-		    (void)printf(pflag > 1 ? "\t%15s" : "\t%7s", "-");
+		    (void)printf(invalid);
 	    }
 	    (void)printf("\n");
 	}
     }
 
-    else {
+    else {	/* output in raw units */
 	while ((to == 0L || from < to) && getvec(v) >= 0) {
-	    (void)printf("%15ld", from++);
+	    (void)printf(tfmt, from++);
 	    for (i = 0; i < nsig; i++)
-		(void)printf("\t%7d", v[sig[i]]);
+		(void)printf(vfmt, v[sig[i]]);
 	    (void)printf("\n");
 	}
     }
 
-    exit(0);	/*NOTREACHED*/
+    exit(0);
+}
+
+char *escapify(char *s)
+{
+    char *p = s, *q = s, *r;
+    int c = 0;
+
+    while (*p) {
+	if (*p == '\'' || *p == '\\')
+	    c++;
+	p++;
+    }
+    if (c > 0 && (p = r = calloc(p-s+c, sizeof(char))) != NULL) {
+	while (*q) {
+	    if (*q == '\'' || *q == '\\')
+		*q++ = '\\';
+	    *p++ = *q;
+	}
+	q = r;
+    }
+    return (q);
 }
 
 char *prog_name(s)
@@ -365,6 +474,7 @@
 static char *help_strings[] = {
  "usage: %s -r RECORD [OPTIONS ...]\n",
  "where RECORD is the name of the input record, and OPTIONS may include:",
+ " -c          use CSV (comma-separated value) output format",
  " -f TIME     begin at specified time",
  " -h          print this usage summary",
  " -H          read multifrequency signals in high resolution mode",
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/app/sigamp.c wfdb-10.4.25/app/sigamp.c
--- wfdb-10.4.24/app/sigamp.c	2006-02-25 22:00:46.000000000 -0500
+++ wfdb-10.4.25/app/sigamp.c	2009-11-13 12:03:06.000000000 -0500
@@ -1,9 +1,9 @@
 /* file: sigamp.c	G. Moody	30 November 1991
-			Last revised:	25 February 2006
+			Last revised:	13 November 2009
 
 -------------------------------------------------------------------------------
 sigamp: Measure signal amplitudes
-Copyright (C) 1991-2006 George B. Moody
+Copyright (C) 1991-2009 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
@@ -36,28 +36,42 @@
 #include <wfdb/ecgmap.h>
 
 #define NMAX 300
+#define _STRING(X) #X
+#define STRING(X) _STRING(X)
+
+/* values for timeunits */
+#define SECONDS     1
+#define MINUTES     2
+#define HOURS       3
+#define TIMSTR      4
+#define MSTIMSTR    5
+#define SHORTTIMSTR 6
+#define HHMMSS	    7
+#define SAMPLES     8
 
 char *pname;
 int namp;
 int nsig;
 int pflag;		/* if non-zero, print physical units */
+int qflag;		/* if non-zero, suppress output of trimmed means */
+int timeunits = SECONDS;
 int vflag;		/* if non-zero, print individual measurements */
 int *v0, *vmax, *vmin, *vv;
 double **amp, *vmean, *vsum;
 long dt1, dt2, dtw;
+WFDB_Anninfo ai;
+WFDB_Frequency sfreq;
+WFDB_Siginfo *si;
+WFDB_Time from = 0L, to = 0L, t;
 
-main(argc, argv)
-int argc;
-char *argv[];
+main(int argc, char **argv)
 {
-    char *p, *record = NULL, *prog_name();
-    int i, j, jlow, jhigh, nmax = NMAX, ampcmp(), getptp(), getrms();
-    long from = 0L, to = 0L, t;
-    static int gvmode = 0;
-    static WFDB_Siginfo *si;
-    static WFDB_Anninfo ai;
-    void help();
+    char *p, *record = NULL, *prog_name(char *s);
+    int gvmode = 0, i, j, jlow, jhigh, nmax = NMAX,
+	ampcmp(), getptp(WFDB_Time t), getrms(WFDB_Time t);
+    void help(void), printamp(WFDB_Time t);
 
+    /* Interpret command-line arguments. */
     pname = prog_name(argv[0]);
     for (i = 1; i < argc; i++) {
 	if (*argv[i] == '-') switch(*(argv[i]+1)) {
@@ -106,6 +120,15 @@
 	    break;
 	  case 'p':
 	    pflag = 1;
+	    if (*(argv[i]+2) == 'd') timeunits = TIMSTR;
+	    else if (*(argv[i]+2) == 'e') timeunits = HHMMSS;
+	    else if (*(argv[i]+2) == 'h') timeunits = HOURS;
+	    else if (*(argv[i]+2) == 'm') timeunits = MINUTES;
+	    else if (*(argv[i]+2) == 'S') timeunits = SAMPLES;
+	    else timeunits = SECONDS;
+	    break;
+          case 'q':
+	    qflag = 1;
 	    break;
 	  case 'r':
 	    if (++i >= argc) {
@@ -154,6 +177,7 @@
 	exit(1);
     }
 
+    /* Finish initialization. */
     if (gvmode == 0 && (p = getenv("WFDBGVMODE")))
 	gvmode = atoi(p);
     setgvmode(gvmode|WFDB_GVPAD);
@@ -177,13 +201,26 @@
 	    (void)fprintf(stderr, "%s: insufficient memory\n", pname);
 	    exit(3);
 	}
-    }    
+    }
+    if ((sfreq = sampfreq(NULL)) <= 0.0)
+	sfreq = WFDB_DEFFREQ;
+
+    /* Adjust timeunits if starting time is undefined. */
+    if (timeunits == TIMSTR || timeunits == HHMMSS) {
+	char *p = timstr(0L);
+	if (*p != '[') timeunits = HHMMSS;
+    }
+    if (timeunits == HOURS) sfreq *= 3600.;
+    else if (timeunits == MINUTES) sfreq *= 60.;
+
     if (from > 0L) from = strtim(argv[(int)from]);
     if (to > 0L) to = strtim(argv[(int)to]);
     if (from < 0L) from = -from;
     if (to < 0L) to = -to;
     if (from >= to) to = strtim("e");
-    if (ai.name) {
+
+    /* Collect amplitudes, print them if qflag or vflag set. */
+    if (ai.name) {	/* windows are relative to annotations */
 	WFDB_Annotation annot;
 
 	if (dtw > 0L) {
@@ -214,21 +251,12 @@
 	       (to == 0L || annot.time < to)) {
 	    if (map1(annot.anntyp) == NORMAL) {
 		if (getptp(annot.time) < 0) break;
-		if (vflag) {
-		    (void)printf("%s", mstimstr(annot.time));
-		    if (!pflag)
-			for (i = 0; i < nsig; i++)
-			    (void)printf("\t%g", amp[i][namp]);
-		    else
-			for (i = 0; i < nsig; i++)
-			    (void)printf("\t%g", amp[i][namp]/si[i].gain);
-		    (void)printf("\n");
-		}
+		if (vflag || qflag) printamp(annot.time);
 		namp++;
 	    }
 	}
     }
-    else {
+    else {		/* windows are consecutive nonoverlapping segments */
 	if (dt1 > 0L) {
 	    (void)fprintf(stderr,
 			  "%s: -d option must be used with -a;\n", pname);
@@ -239,40 +267,31 @@
 	if (from > 0L && isigsettime(from) < 0L) exit(2);
 	for (t = from; namp < nmax && (to == 0L || t < to); namp++, t += dtw) {
 	    if (getrms(t) < 0) break;
-	    if (vflag) {
-		(void)printf("%s", mstimstr(t));
-		if (!pflag)
-		    for (i = 0; i < nsig; i++)
-			(void)printf("\t%g", amp[i][namp]);
-		else
-		    for (i = 0; i < nsig; i++)
-			(void)printf("\t%g", amp[i][namp]/si[i].gain);
-		(void)printf("\n");
-	    }
-	}
+	    if (vflag || qflag) printamp(t);
+	}	
     }
-    jlow = namp/20;
-    jhigh = namp - jlow;
-    if (vflag)
-	(void)printf("Trimmed mean");
-    for (i = 0; i < nsig; i++) {
-	double a;
 
-	qsort((char*)amp[i], namp, sizeof(double), ampcmp);
-	for (a = 0.0, j = jlow; j < jhigh; j++)
-	    a += amp[i][j];
-	a /= jhigh - jlow;
-	if (!pflag)
-	    (void)printf("\t%g", a);
-	else
-	    (void)printf("\t%g", a/si[i].gain);
+    /* Calculate trimmed means of collected amplitudes unless -q option set. */
+    if (qflag == 0) {
+	jlow = namp/20;
+	jhigh = namp - jlow;
+	if (vflag)
+	    (void)printf("Trimmed mean");
+	for (i = 0; i < nsig; i++) {
+	    double a;
+
+	    qsort((char*)amp[i], namp, sizeof(double), ampcmp);
+	    for (a = 0.0, j = jlow; j < jhigh; j++)
+		a += amp[i][j];
+	    a /= jhigh - jlow;
+	    (void)printf("\t%g", pflag ? a/si[i].gain : a);
+	}
+	(void)printf("\n");
     }
-    (void)printf("\n");
-    exit(0); /*NOTREACHED*/
+    exit(0);
 }
 
-int getrms(t)
-long t;
+int getrms(WFDB_Time t)
 {
     int i, v;
     long tt;
@@ -316,8 +335,7 @@
     return (0);
 }
 
-int getptp(t)
-long t;
+int getptp(WFDB_Time t)
 {
     int i;
     long tt;
@@ -343,16 +361,30 @@
     return (0);
 }
 
-int ampcmp(p1, p2)
-double *p1, *p2;
+void printamp(WFDB_Time t)
+{
+    int i;
+
+    switch (timeunits) {
+      case TIMSTR:  printf("%s",mstimstr(-t)); break;
+      case HHMMSS:    printf("%s", mstimstr(t)); break;
+      case SAMPLES:   printf("%ld", t); break;
+      default:	    printf("%lf", t/sfreq); break;
+    }
+    for (i = 0; i < nsig; i++)
+	(void)printf("\t%g", pflag ? amp[i][namp]/si[i].gain :
+		     amp[i][namp]);
+    (void)printf("\n");
+}
+
+int ampcmp(double *p1, double *p2)
 {
     if (*p1 > *p2) return (1);
     else if (*p1 == *p2) return (0);
     else return (-1);
 }
 
-char *prog_name(s)
-char *s;
+char *prog_name(char *s)
 {
     char *p = s + strlen(s);
 
@@ -381,17 +413,25 @@
  " -f TIME     begin at specified time",
  " -h          print this usage summary",
  " -H          read multifrequency signals in high resolution mode",
- " -n NMAX     make up to NMAX measurements per signal (default: 300)",
-				   /* default NMAX is defined as 300 above */
+ " -n NMAX     make up to NMAX measurements per signal (default: NMAX = "
+     STRING(NMAX) ")",
  " -p          print results in physical units (default: ADC units)",
+ "              -p may be followed by a character to choose a time format:",
+ "  -pd         print time of day and date if known",
+ "  -pe         print elapsed time as <hours>:<minutes>:<seconds>",
+ "  -ph         print elapsed time in hours",
+ "  -pm         print elapsed time in minutes",
+ "  -ps         print elapsed time in seconds (default)",
+ "  -pS         print elapsed time in sample intervals",
+ " -q          quick mode: print individual measurements only",
  " -t TIME     stop at specified time",
- " -v          verbose mode: print individual measurements",
+ " -v          verbose mode: print individual measurements and trimmed means",
  " -w DTW      set RMS amplitude measurement window",
  "              default: DTW = 1 (second)",
 NULL
 };
 
-void help()
+void help(void)
 {
     int i;
 
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/app/wrsamp.c wfdb-10.4.25/app/wrsamp.c
--- wfdb-10.4.24/app/wrsamp.c	2009-01-20 23:07:42.000000000 -0500
+++ wfdb-10.4.25/app/wrsamp.c	2010-01-21 15:11:33.000000000 -0500
@@ -1,8 +1,9 @@
 /* file: wrsamp.c	G. Moody	10 August 1993
-			Last revised:   20 January 2009
+			Last revised:  21 January 2010
+
 -------------------------------------------------------------------------------
 wrsamp: Select fields or columns from a file and generate a WFDB record
-Copyright (C) 1993-2009 George B. Moody
+Copyright (C) 1993-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
@@ -22,8 +23,6 @@
 please visit PhysioNet (http://www.physionet.org/).
 _______________________________________________________________________________
 
-Portions of this program were derived from the `field' utility described in
-"The UNIX System" by S.R. Bourne, pp. 227-9 (Addison-Wesley, 1983).
 */
 
 #include <stdio.h>
@@ -33,27 +32,208 @@
 /* The following definition yields dither with a triangular PDF in (-1,1). */
 #define DITHER	        (((double)rand() + (double)rand())/RAND_MAX - 1.0)
 
-#define isfsep(c)	((fsep && ((c)==fsep)) || ((c)==' ' || (c)=='\t'))
+/* Dynamic memory allocation macros */
+#define MEMERR(P, N, S) \
+    { wfdb_error("WFDB: can't allocate (%ld*%ld) bytes for %s\n", \
+	     (size_t)N, (size_t)S, #P);			      \
+      exit(1); }
+#define SFREE(P) { if (P) { free (P); P = 0; } }
+#define SUALLOC(P, N, S) { if (!(P = calloc((N), (S)))) MEMERR(P, (N), (S)); }
+#define SALLOC(P, N, S) { SFREE(P); SUALLOC(P, (N), (S)) }
+#define SREALLOC(P, N, S) { if (!(P = realloc(P, (N)*(S)))) MEMERR(P,(N),(S)); }
+#define SSTRCPY(P, Q) { if (Q) { \
+	 SALLOC(P, (size_t)strlen(Q)+1,1); strcpy(P, Q); } }
 
 char *pname;
 
+char *read_line(FILE *ifile, char rsep)
+{
+    static char *buf;
+    int c;
+    static size_t i = 0, length = 0;
+
+    while ((c = getc(ifile)) != rsep) {
+	if (i >= length) {
+	    if (length == 0) length = 512;
+	    length *= 2;
+	    SREALLOC(buf, length+2, sizeof(char));
+	}
+ 	if (c == EOF) {
+	    free(buf);
+	    return (buf = NULL);
+	}
+	buf[i++] = c;
+    }
+    buf[i] = '\0';
+    i = 0;
+    return (buf);
+}
+
+int line_has_alpha(char *line)
+{
+    char *p;
+
+    for (p = line; *p; p++)
+    	if (('A' <= *p && *p <= 'Z') || ('a' <= *p && *p <= 'z'))
+	    return (1);
+    return (0);
+}
+
+int line_has_tab(char *line)
+{
+    char *p;
+
+    for (p = line; *p; p++)
+    	if (*p == '\t')
+	    return (1);
+    return (0);
+}
+
+struct parsemode {
+    char *delim;	/* characters that delimit tokens */
+    char collapse;	/* if non-zero, collapse consecutive delimiters */
+    char esc;		/* if non-zero, next character is literal unless null */
+    char *quotepair[];	/* pairs of characters that open and close tokens */
+};
+
+struct tokenarray {
+    int ntokens;	/* number of tokens */
+    int maxtokens;	/* number of allocated token pointers */
+    char *token[];	/* token pointers */
+};
+
+typedef struct parsemode Parsemode;
+typedef struct tokenarray Tokenarray;
+
+/* The function parseline() parses its first argument, a null-terminated string
+('line'), into a Tokenarray ('ta').  On return, the token pointers (ta->token[])
+address locations within 'line', and the character that immediately follows
+each token is replaced with a null.
+
+The second argument of parseline, ta, is a pointer to a Tokenarray (defined
+above).  If ta is NULL on entry, parseline allocates and returns a Tokenarray
+that is sufficiently large to accomodate all of the tokens in 'line'.
+Otherwise, parseline returns the number of tokens found in ta->ntokens, and the
+token array on return contains either ta->ntokens or ta->maxtokens valid
+elements, whichever is smaller.  If ta->ntokens > ta->maxtokens, the token
+array contains pointers to the first ta->maxtokens tokens.
+
+The third and final argument of parseline, pmode, is a pointer to a Parsemode
+(also defined above).  The caller can set up pmode to specify which characters
+in 'line' are token delimiters, which pairs of characters can be used to quote
+a token that may have embedded delimiters, whether to treat consecutive
+delimiters as if they surround an empty token or as a single delimiter, and
+which character acts as an escape (to cause the next character to be treated as
+a literal character in a token, rather than as a delimiter, quote, or escape
+character).
+
+If pmode is NULL, parseline behaves as if called with pmode = defpmode.  If
+pmode->delim is NULL, parseline sets pmode->delim = defpmode.delim.
+*/
+
+Parsemode defpmode = {
+  " \t\r\n,", /* delimiter characters can be space, tab, CR, LF, or comma */
+  1,	/* collapse consecutive delimiters */
+  '\\',	/* treat any character following a backslash as a literal */
+  { "''", "\"\"", "()", "[]", "{}", "<>", NULL } /* quote characters */
+};
+
+Tokenarray *parseline(char *line, Tokenarray *ta, Parsemode *pmode)
+{
+    int i, n = 0, state = 0;
+    char d, *p = line-1, *q = NULL;
+
+    if (ta == NULL) {
+	int m = (strlen(line) + 1)/2;
+	if (ta = (Tokenarray *)malloc(sizeof(int)*2 + sizeof(char *)*m))
+	    ta->maxtokens = m;
+	else
+	    return (NULL);
+    }
+
+    if (pmode == NULL)
+	pmode = &defpmode;
+    else if (pmode->delim == NULL)
+	pmode->delim = defpmode.delim;
+
+    while (*(++p)) {	/* for each character in the line */
+
+	/* is *p an escape character? */
+	if (pmode->esc && *p == pmode->esc) {
+	    if (*(p+1) == '\0')
+		break;
+	    if (state == 0) { /* start a new token */
+		state = 1;
+		ta->token[n++] = p;
+	    }
+	    p++;	/* include the next character in the token */
+	    continue;
+	}
+
+	/* is *p the character needed to complete a quoted string? */
+	if (q) {
+	    if (*p == *q) { *p = '\0'; q = NULL; }
+	    continue;
+	}
+
+	/* is *p a delimiter character? */
+	i = 0;
+        while (d = pmode->delim[i++]) {
+	    if (*p == d) {
+		*p = '\0';	/* replace delimiter with null */
+		if (state == 0) { /* not in a token */
+		    if (pmode->collapse == 0)
+			ta->token[n++] = p;	/* count an empty token */
+		}
+		state = 0;
+		break;
+	    }
+	}
+
+	/* is *p an open-quote character? */
+	i = 0;
+	while (q = pmode->quotepair[i++]) {  /* q is an open-quote character */
+	    if (*p == *q) { /* *p is first character of a quoted string */
+		if (state == 0) { /* start a new token */
+		    ta->token[n++] = p+1;
+		    state = 1;
+		}
+		q++; /* *q is now the matching close-quote character */
+		break;
+	    }
+	}
+
+	if (d == '\0' && q == NULL) { 	/* p must be part of a token */
+	    if (state == 0) {
+		ta->token[n++] = p;	/* start a new token */
+		state = 1;
+	    }
+	}
+    }
+
+    ta->ntokens = n;
+    return (ta);
+}
+
 main(argc, argv)
 int argc;
 char *argv[];
 {
-    char **ap, *cp, **desc, **fp = NULL, fsep = '\0', *ifname = "(stdin)",
-	*l = NULL, ofname[40], *p, *record = NULL, rsep = '\n', *prog_name();
-    char *gain = "", *scale = "";
+    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];
     double freq = WFDB_DEFFREQ, *scalef, v;
 #ifndef atof
     double atof();
 #endif
-    int c, cf = 0, dflag = 0, *fv = NULL, i, lmax = 1024, mf;
+    int c, cf = 0, dflag = 0, format = 16, *fv = NULL, i, labels, mf, zflag = 0;
     FILE *ifile = stdin;
     long t = 0L, t0 = 0L, t1 = 0L;
 #ifndef atol
     long atol();
 #endif
+    Tokenarray *ta;
     WFDB_Sample *vout;
     WFDB_Siginfo *si;
     unsigned int nf = 0;
@@ -113,11 +293,8 @@
 	    ifname = argv[i];
 	    break;
 	  case 'l':
-	    if (++i >= argc || (lmax = atoi(argv[i])) < 1) {
-		(void)fprintf(stderr, "%s: max line length must follow -l\n",
-			      pname);
-		exit(1);
-	    }
+	    if (++i >= argc) --i;
+	    (void)fprintf(stderr, "%s: -l is obsolete, ignored\n", pname);
 	    break;
 	  case 'o':
 	    if (++i >= argc) {
@@ -127,6 +304,14 @@
 	    }
 	    record = argv[i];
 	    break;
+	  case 'O':
+	    if (++i >= argc) {
+		(void)fprintf(stderr, "%s: output format must follow -O\n",
+			      pname);
+		exit(1);
+	    }
+	    format = atoi(argv[i]);
+	    break;
 	  case 'r':
 	    if (++i >= argc) {
 		(void)fprintf(stderr, "%s: line separator must follow -r\n",
@@ -137,11 +322,12 @@
 	    break;
 	  case 's':
 	    if (++i >= argc) {
-		(void)fprintf(stderr, "%s: field separator must follow -s\n",
+		(void)fprintf(stderr, "%s: field separator(s) must follow -s\n",
 			      pname);
 		exit(1);
 	    }
-	    fsep = argv[i][0];
+	    sflag = 1;
+	    defpmode.delim = argv[i];
 	    break;
 	  case 't':
 	    if (++i >= argc || (t1 = atol(argv[i])) <= 0L) {
@@ -158,6 +344,9 @@
 	    }
 	    scale = argv[i];
 	    break;
+          case 'z':	 /* ignore column 0 */
+	    zflag = 1;
+	    break;
 	  default:
 	    (void)fprintf(stderr, "%s: unrecognized option %s\n", pname,
 			  argv[i]);
@@ -165,59 +354,88 @@
 	}
     }
 
-    /* allocate storage for arrays */
-#ifndef lint
-    l = calloc(lmax, sizeof(char));
-    fp = (char **)calloc(lmax, sizeof(char *));
-    fv = (int *)calloc(argc - i, sizeof(int));
-#endif
-    if (l == NULL || fv == NULL || fp == NULL) {
-	(void)fprintf(stderr, "%s: insufficient memory\n", pname);
+    /* read the first line of the input file */
+    if ((line = read_line(ifile, rsep)) == NULL) {
+	if (rsep != '\n') {
+	    if (record == NULL)
+		(void)fprintf(stderr,
+			      "%s: use -o to specify the output record name",
+			      pname);
+	    else
+		(void)fprintf(stderr,
+		  "%s: no record separators in input\n"
+		  "Try specifying a different separator after the -r option.\n",
+			      pname);
+	}
+	else
+	    (void)fprintf(stderr, "%s: no newlines in input\n", pname);
 	exit(3);
     }
-
-    /* read arguments into fv[...] */
-    while (i < argc) {
-	if (sscanf(argv[i++], "%d", &fv[nf]) != 1 ||
-	    fv[nf] < 0 || fv[nf] >= lmax) {
-	    (void)fprintf(stderr, "%s: unrecognized argument %s\n",
-			  pname, argv[--i]);
-	    exit(1);
+ 
+    /* 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, NULL);
+
+    /* 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) {
+		(void)fprintf(stderr, "%s: unrecognized argument %s\n",
+			      pname, argv[--i]);
+		exit(1);
+	    }
+	    nf++;
 	}
-	nf++;
     }
-
-    if (nf < 1) {
-	help();
-	exit(1);
+    /* 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; j <= nf; i++, j++)
+	    fv[i] = j;
     }
 
-    if ((vout = malloc(nf * sizeof(WFDB_Sample))) == NULL ||
-	(si = malloc(nf * sizeof(WFDB_Siginfo))) == NULL ||
-	(desc = malloc(nf * sizeof(char *))) == NULL ||
-	(scalef = malloc(nf * sizeof(double))) == NULL) {
-	(void)fprintf(stderr, "%s: insufficient memory\n", pname);
-	exit(2);
-    }
-    /* open output file */
+    /* allocate arrays */
+    SUALLOC(vout, nf, sizeof(WFDB_Sample));
+    SUALLOC(si, nf, sizeof(WFDB_Siginfo));
+    SUALLOC(scalef, nf, sizeof(double));
+
+     /* open the output record */
     if (record == NULL)
 	(void)sprintf(ofname, "-");
     else
 	(void)sprintf(ofname, "%s.dat", record);
     for (i = 0; i < nf; i++) {
 	si[i].fname = ofname;
-	if ((desc[i] = malloc((strlen(ifname)+20) * sizeof(char))) == NULL) {
-	    (void)fprintf(stderr, "%s: insufficient memory\n", pname);
-	    exit(2);
+	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);
+	}
+	else {
+	    char tdesc[16];
+
+	    (void)sprintf(tdesc, "column %d", fv[i]);
+	    SSTRCPY(si[i].desc, tdesc);
 	}
-	if (ifile == stdin)
-	    (void)sprintf(desc[i], "column %d", fv[i]);
-	else
-	    (void)sprintf(desc[i], "%s, column %d", ifname, fv[i]);
-	si[i].desc = desc[i];
 	si[i].units = "";
 	si[i].group = 0;
-	si[i].fmt = 16;
+	si[i].fmt = format;
 	si[i].spf = 1;
 	si[i].bsize = 0;
 	si[i].adcres = WFDB_DEFRES;
@@ -238,81 +456,86 @@
 	    while (*scale != '\0' && *scale != ' ')
 		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)) {
+	    free(ta);
+	    ta = parseline(line, NULL, 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)
 	exit(2);
 
-    /* read and copy input */
-    nf--;
-    cp = l;
-    ap = fp;
-    *ap++ = cp;
-    do {
-	c = getc(ifile);
-	if (cp >= l + lmax) {
-	    (void)fprintf(stderr,
-			  "%s: line %ld truncated (> %d bytes)\n",
-			  pname, t, lmax);
-	    while (c != rsep && c!= EOF)
-		c = getc(ifile);
-	}
-	if (c == rsep || c == EOF) {
-	    if (cp == l && c == EOF) break;
-	    *cp++ = 0;
-	    mf = ap - fp;
-	    if (cf) {
-		if (cf < 0) cf = mf;
-		else if (cf != mf)
-		    (void)fprintf(stderr,
-				  "%s: line %ld has incorrect field count\n",
-				  pname, t);
-	    }
-	    if (t >= t0) {	/* write data from this line */
-		for (i = 0; i <= nf; i++) {
-		    if ((p = fp[fv[i]]) == NULL) {
-			(void)fprintf(stderr,
-				      "%s: line %ld, column %d missing\n",
-				      pname, t, fv[i]);
-			vout[i] = WFDB_INVALID_SAMPLE;
-		    }
-		    else if (sscanf(p, "%lf", &v) != 1) {
-			if (strcmp(p, "-") != 0)
-			    (void)fprintf(stderr,
-			      "%s: line %ld, column %d improperly formatted\n",
-				      pname, t, fv[i]);
-			vout[i] = WFDB_INVALID_SAMPLE;
-		    }
-		    else {
-			v *= scalef[i];
-			if (dflag) v += DITHER;
-			if (v >= 0) vout[i] = (WFDB_Sample)(v + 0.5);
-			else vout[i] = (WFDB_Sample)(v - 0.5);
-		    }
-		}
-		if (putvec(vout) < 0) break;
-		for (i = 0; i <= nf; i++)
-		    fp[fv[i]] = NULL;
-	    }
-	    if (c == EOF || (++t >= t1 && t1 > 0L)) break;
-	    cp = l;
-	    ap = fp;
-	    *ap++ = cp;
-	}
-	else if (isfsep(c)) {
-	    if (cp > l) {
-		*cp++ = 0;
-		*ap++ = cp;
-	    }
-	    do {
-		c = getc(ifile);
-	    } while (c != rsep && c != EOF && isfsep(c));
-	    (void)ungetc(c, ifile);
+    /* skip any unwanted samples at the beginning */
+    for (t = 0; t < t0; t++)
+	line = read_line(ifile, rsep);
+
+    /* pick up base time if it's there */
+    if (zflag) {
+	if (*line == '[') {
+	    strncpy(btime, line+1, 23);
+	    setbasetime(btime);
+	}
+	else if (*(line+1) == '[') {
+	    strncpy(btime, line+2, 23);
+	    setbasetime(btime);
 	}
-	else *cp++ = c;
-    } while (c != EOF);
+    }
+
+    /* read and copy samples */
+    while (line != NULL && (t1 == 0L || t++ < t1)) {
+	free(ta);
+	ta = parseline(line, NULL, NULL);
+	for (i = 0; i < nf; i++) {
+	    double v;
+
+	    if (sscanf(ta->token[fv[i]], "%lf", &v) == 1) {
+		//	    if (strcmp(ta->token[fv[i]], "-")) {
+		v *= scalef[i];
+		if (dflag) v+= DITHER;
+		if (v >= 0) vout[i] = (WFDB_Sample)(v + 0.5);
+		else vout[i] = (WFDB_Sample)(v - 0.5);
+	    }
+	    else
+		vout[i] = WFDB_INVALID_SAMPLE;
+	}
+	if (putvec(vout) < 0) break;
+	line = read_line(ifile, rsep);
+    }
+
+    /* write the header */
+    (void)setsampfreq(freq);
     if (record != NULL)
 	(void)newheader(record);
     wfdbquit();
-    exit(0);	/*NOTREACHED*/
+    exit(0);
 }
 
 char *prog_name(s)
@@ -336,7 +559,7 @@
 }
 
 static char *help_strings[] = {
- "usage: %s [OPTIONS ...] COLUMN [COLUMN ...]\n",
+ "usage: %s [OPTIONS ...] [COLUMN ...]\n",
  "where COLUMN selects a field to be copied (leftmost field is column 0),",
  "and OPTIONS may include:",
  " -c          check that each input line contains the same number of fields",
@@ -346,18 +569,34 @@
  " -G GAIN     specify gain(s) to be written to header file (default: 200)",
  " -h          print this usage summary",
  " -i FILE     read input from FILE (default: standard input)",
- " -l LEN      read up to LEN characters in each line (default: 1024)",
  " -o RECORD   save output in RECORD.dat, and generate a header file for",
  "              RECORD (default: write to standard output in format 16, do",
  "              not generate a header file)",
- " -r RSEP     interpret RSEP as the input line separator (default: \\n)",
- " -s FSEP     interpret FSEP as the input field separator (default: space",
- "              or tab)",
+ " -O FORMAT   save output in the specified FORMAT (default: 16)",
+ " -r RSEP     interpret RSEP as the input line separator (default: linefeed)",
+ " -s FSEP     interpret any character in FSEP as an input field separator",
+ "             (default: space, tab, carriage-return, or comma)  ",
  " -t N        stop copying at line N (default: end of input file)",
  " -x SCALE    multiply inputs by SCALE factor(s) (default: 1)",
+ " -z          don't copy column 0 unless explicitly specified",
+ "",
+ "The input is a text file with a sample of each signal on each line. Samples",
+ "can be separated by tabs, spaces, commas, or any combination of these.",
+ "Consecutive separators are equivalent to single separators.",
+ "",  
  "To specify different GAIN or SCALE values for each output signal, provide",
  "a quoted list of values, e.g., -G \"100 50\" defines the gain for signal 0",
  "as 100, and the gain for signal 1 (and any additional signals) as 50.",
+ "",
+ "If the first input line contains any alphabetic characters, it is assumed",
+ "to contain column headings, which are copied as signal descriptions into",
+ "RECORD.hea.  If the second input line also contains alphabetic characters,",
+ "it is assumed to specify the physical units for each column, which are",
+ "copied as units strings into RECORD.hea.  The first line containing samples",
+ "is line 0.",
+ "",
+ "If no COLUMN is specified, all fields are copied in order; column 0 can be",
+ "omitted in this case by using the '-z' option.",
 NULL
 };
 
@@ -366,6 +605,14 @@
     int i;
 
     (void)fprintf(stderr, help_strings[0], pname);
-    for (i = 1; help_strings[i] != NULL; i++)
+    for (i = 1; help_strings[i] != NULL; i++) {
 	(void)fprintf(stderr, "%s\n", help_strings[i]);
+	if (i % 23 == 0) {
+	    char b[5];
+	    (void)fprintf(stderr, "--More--");
+	    (void)fgets(b, 5, stdin);
+	    (void)fprintf(stderr, "\033[A\033[2K");  /* erase "--More--";
+						      assumes ANSI terminal */
+	}
+    }
 }
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/checkpkg/appcheck wfdb-10.4.25/checkpkg/appcheck
--- wfdb-10.4.24/checkpkg/appcheck	2007-12-27 12:15:09.000000000 -0500
+++ wfdb-10.4.25/checkpkg/appcheck	2010-01-21 14:30:01.000000000 -0500
@@ -1,6 +1,6 @@
 #!/bin/sh
 # file: appcheck		G. Moody       7 September 2001
-#                               Last revised:  27 December 2007
+#                               Last revised:  21 January 2010
 #
 # This script checks the basic functionality of most of the WFDB applications
 # in the 'app' directory.  These programs are not (yet) tested by this script:
@@ -111,7 +111,7 @@
 
 echo Testing wrsamp ...
 F=100w.dat
-wrsamp -o 100w -f 2 -F 360 -x 200 1 2 <wrsamp.in
+wrsamp -o 100w -F 360 -O 212 -x 200 1 2 <wrsamp.in
 if ( ./checkfile $F )
 then
     PASS=`expr $PASS + 1`
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/checkpkg/expected/100w.dat wfdb-10.4.25/checkpkg/expected/100w.dat
--- wfdb-10.4.24/checkpkg/expected/100w.dat	2002-11-22 14:24:58.000000000 -0500
+++ wfdb-10.4.25/checkpkg/expected/100w.dat	2010-01-21 14:34:32.000000000 -0500
@@ -1 +1 @@
-²ÿÉÿ±ÿËÿ²ÿÇÿ¯ÿÄÿ¯ÿÃÿ°ÿÇÿµÿÊÿ´ÿÈÿ²ÿÉÿ±ÿÆÿ±ÿÈÿ²ÿÆÿ²ÿÈÿ³ÿÈÿ¯ÿÇÿ°ÿÇÿ¯ÿÇÿ±ÿÅÿ²ÿÉÿ±ÿÈÿ²ÿÆÿ°ÿÅÿ¯ÿÅÿ¯ÿÇÿ²ÿÉÿ²ÿÅÿ±ÿÃÿ°ÿÃÿ°ÿÀÿ±ÿÃÿ´ÿÅÿ¶ÿÄÿ´ÿÄÿ³ÿÀÿ±ÿÀÿ²ÿÂÿ´ÿÃÿ´ÿÁÿ³ÿ¾ÿ²ÿ»ÿ²ÿ¹ÿ°ÿ¼ÿ°ÿ½ÿ¯ÿºÿ­ÿ¶ÿ¬ÿ´ÿªÿ´ÿªÿµÿ«ÿ³ÿ¬ÿ¯ÿªÿ¯ÿ§ÿ¬ÿ£ÿªÿ¥ÿ­ÿ¨ÿ®ÿ«ÿ­ÿ©ÿ«ÿ¦ÿ«ÿ¨ÿ«ÿ§ÿ¯ÿ«ÿ³ÿ«ÿ²ÿ«ÿ³ÿªÿ³ÿªÿ´ÿ­ÿºÿ­ÿÀÿ³ÿÁÿµÿÁÿ³ÿÃÿ¶ÿÃÿ¸ÿÊÿºÿÎÿ½ÿÑÿ½ÿÑÿ¿ÿÏÿÀÿÐÿ¿ÿÑÿÃÿÕÿÅÿÔÿÄÿÔÿÆÿÓÿÅÿÔÿÆÿÕÿÆÿ×ÿÇÿÕÿÇÿÕÿÇÿÓÿÆÿÓÿÆÿÕÿÇÿÚÿÊÿÙÿÊÿÖÿÈÿ×ÿÆÿ×ÿÇÿØÿÉÿ×ÿÉÿ×ÿÉÿ×ÿÆÿÕÿÃÿÖÿÃÿÖÿÄÿØÿÈÿØÿÆÿ×ÿÃÿÔÿÁÿÖÿÂÿØÿÅÿØÿÆÿÙÿÄÿÕÿÃÿÕÿÃÿÔÿÂÿÖÿÄÿÖÿÅÿÖÿÃÿÕÿÃÿÓÿÁÿÒÿÂÿÕÿÂÿÕÿÅÿÕÿÄÿÔÿÃÿÒÿÂÿÔÿÃÿ×ÿÃÿØÿÅÿÔÿÄÿÓÿÁÿÐÿ¿ÿÒÿÀÿÒÿÀÿÕÿÁÿÓÿÂÿÔÿ¿ÿÑÿ½ÿÓÿ¾ÿÓÿÀÿÖÿÀÿÓÿÂÿÓÿ¾ÿÓÿ¾ÿÒÿ¿ÿÔÿÂÿÖÿÃÿÔÿÃÿÕÿÀÿÒÿ¾ÿÑÿ¿ÿÕÿÄÿÖÿÂÿÕÿÃÿÓÿÁÿÓÿ¾ÿÑÿ¾ÿÒÿ¾ÿÕÿÀÿÓÿ¿ÿÔÿ¿ÿÒÿ½ÿÓÿ¿ÿÔÿÁÿÖÿÂÿÕÿÁÿÔÿÀÿÔÿ¿ÿÓÿÁÿÖÿÂÿ×ÿÃÿÕÿÂÿÓÿÁÿÒÿÀÿÐÿÁÿÓÿÂÿ×ÿÅÿÕÿÅÿÕÿÃÿÔÿÂÿÒÿÄÿÓÿÈÿÕÿÉÿÓÿËÿÔÿÉÿÔÿÉÿÓÿÌÿÙÿÐÿÙÿÐÿ×ÿÑÿØÿÐÿØÿÑÿÙÿÓÿÚÿÕÿÞÿ×ÿÞÿ×ÿÜÿÖÿÜÿÕÿÛÿÔÿÞÿÔÿßÿÕÿÜÿÔÿÝÿÔÿÛÿÒÿÝÿÒÿÞÿÐÿâÿÖÿâÿÔÿáÿÒÿÜÿÍÿÜÿÑÿÞÿÖÿÝÿÚÿÜÿ×ÿÚÿÓÿÙÿÌÿØÿÉÿÚÿÈÿÛÿÇÿØÿÃÿÙÿÀÿÕÿÁÿÖÿ½ÿØÿ¿ÿÙÿ½ÿ×ÿÀÿÔÿ¿ÿÕÿÀÿÓÿ¿ÿÓÿ¿ÿÖÿÂÿ×ÿÃÿÖÿÀÿÓÿÀÿÒÿ¾ÿÕÿÀÿØÿÀÿÕÿ¾ÿÔÿ¾ÿÓÿ¸ÿÖÿ¹ÿÖÿ¾ÿØÿ¿ÿÖÿ¿ÿÕÿ½ÿÓÿºÿÑÿ¼ÿÐÿ»ÿÍÿ¹ÿÆÿ³ÿÂÿ«ÿ½ÿ¦ÿ¸ÿ¢ÿºÿŸÿÇÿšÿÝÿ™ÿöÿ¥ÿ ¿ÿ& ßÿ?  V 2 m g ƒ ” ƒ ± e » * § îÿi Éÿ ÆÿÒÿËÿ«ÿÎÿ¥ÿÍÿ¬ÿÒÿ´ÿÒÿ¸ÿÎÿ·ÿÍÿµÿÍÿ´ÿÎÿ¹ÿÑÿ¹ÿÎÿ¶ÿÎÿµÿËÿ³ÿÍÿ´ÿÎÿ·ÿÐÿ¶ÿÏÿ¶ÿÍÿ·ÿÍÿ´ÿËÿ¶ÿÐÿµÿÐÿ·ÿÍÿ¶ÿÍÿ´ÿËÿµÿÌÿ·ÿÎÿ¸ÿÍÿ¸ÿÍÿ¹ÿÍÿ¹ÿÍÿ¶ÿÎÿ·ÿÏÿ¹ÿÓÿºÿÒÿºÿÏÿºÿÍÿ¸ÿÌÿ¸ÿÏÿ¸ÿÏÿºÿÏÿ¹ÿÐÿ·ÿÎÿ¶ÿÏÿ¸ÿÏÿºÿÐÿ¹ÿÐÿºÿÎÿ¹ÿËÿ¶ÿÍÿµÿÐÿ¸ÿÐÿºÿÐÿ»ÿÍÿºÿÌÿ¶ÿÌÿ¶ÿÍÿ¹ÿÍÿºÿÎÿ»ÿÌÿ¸ÿÉÿ¶ÿÈÿ·ÿÈÿ¸ÿÊÿºÿËÿ¹ÿÇÿ·ÿÆÿ·ÿÈÿ¶ÿÈÿ·ÿÉÿºÿÅÿºÿÃÿ¸ÿÂÿ¸ÿÂÿ¹ÿÀÿ»ÿÃÿºÿÀÿ»ÿ»ÿ¹ÿºÿ¶ÿ¹ÿ·ÿ¹ÿ¹ÿºÿ¸ÿºÿ·ÿ¸ÿ·ÿ´ÿ´ÿ³ÿ³ÿ´ÿµÿ´ÿ¶ÿµÿµÿ³ÿ³ÿ±ÿ±ÿ´ÿµÿ²ÿ
\ No newline at end of file
+²ÿÉ±ÿË²ÿÇ¯ÿÄ¯ÿÃ°ÿÇµÿÊ´ÿÈ²ÿÉ±ÿÆ±ÿÈ²ÿÆ²ÿÈ³ÿÈ¯ÿÇ°ÿÇ¯ÿÇ±ÿÅ²ÿÉ±ÿÈ²ÿÆ°ÿÅ¯ÿÅ¯ÿÇ²ÿÉ²ÿÅ±ÿÃ°ÿÃ°ÿÀ±ÿÃ´ÿÅ¶ÿÄ´ÿÄ³ÿÀ±ÿÀ²ÿÂ´ÿÃ´ÿÁ³ÿ¾²ÿ»²ÿ¹°ÿ¼°ÿ½¯ÿº­ÿ¶¬ÿ´ªÿ´ªÿµ«ÿ³¬ÿ¯ªÿ¯§ÿ¬£ÿª¥ÿ­¨ÿ®«ÿ­©ÿ«¦ÿ«¨ÿ«§ÿ¯«ÿ³«ÿ²«ÿ³ªÿ³ªÿ´­ÿº­ÿÀ³ÿÁµÿÁ³ÿÃ¶ÿÃ¸ÿÊºÿÎ½ÿÑ½ÿÑ¿ÿÏÀÿÐ¿ÿÑÃÿÕÅÿÔÄÿÔÆÿÓÅÿÔÆÿÕÆÿ×ÇÿÕÇÿÕÇÿÓÆÿÓÆÿÕÇÿÚÊÿÙÊÿÖÈÿ×Æÿ×ÇÿØÉÿ×Éÿ×Éÿ×ÆÿÕÃÿÖÃÿÖÄÿØÈÿØÆÿ×ÃÿÔÁÿÖÂÿØÅÿØÆÿÙÄÿÕÃÿÕÃÿÔÂÿÖÄÿÖÅÿÖÃÿÕÃÿÓÁÿÒÂÿÕÂÿÕÅÿÕÄÿÔÃÿÒÂÿÔÃÿ×ÃÿØÅÿÔÄÿÓÁÿÐ¿ÿÒÀÿÒÀÿÕÁÿÓÂÿÔ¿ÿÑ½ÿÓ¾ÿÓÀÿÖÀÿÓÂÿÓ¾ÿÓ¾ÿÒ¿ÿÔÂÿÖÃÿÔÃÿÕÀÿÒ¾ÿÑ¿ÿÕÄÿÖÂÿÕÃÿÓÁÿÓ¾ÿÑ¾ÿÒ¾ÿÕÀÿÓ¿ÿÔ¿ÿÒ½ÿÓ¿ÿÔÁÿÖÂÿÕÁÿÔÀÿÔ¿ÿÓÁÿÖÂÿ×ÃÿÕÂÿÓÁÿÒÀÿÐÁÿÓÂÿ×ÅÿÕÅÿÕÃÿÔÂÿÒÄÿÓÈÿÕÉÿÓËÿÔÉÿÔÉÿÓÌÿÙÐÿÙÐÿ×ÑÿØÐÿØÑÿÙÓÿÚÕÿÞ×ÿÞ×ÿÜÖÿÜÕÿÛÔÿÞÔÿßÕÿÜÔÿÝÔÿÛÒÿÝÒÿÞÐÿâÖÿâÔÿáÒÿÜÍÿÜÑÿÞÖÿÝÚÿÜ×ÿÚÓÿÙÌÿØÉÿÚÈÿÛÇÿØÃÿÙÀÿÕÁÿÖ½ÿØ¿ÿÙ½ÿ×ÀÿÔ¿ÿÕÀÿÓ¿ÿÓ¿ÿÖÂÿ×ÃÿÖÀÿÓÀÿÒ¾ÿÕÀÿØÀÿÕ¾ÿÔ¾ÿÓ¸ÿÖ¹ÿÖ¾ÿØ¿ÿÖ¿ÿÕ½ÿÓºÿÑ¼ÿÐ»ÿÍ¹ÿÆ³ÿÂ«ÿ½¦ÿ¸¢ÿºŸÿÇšÿÝ™ÿö¥¿&ß? V2 mg ƒ” ƒ± e» *§ðîiðÉðÆÒÿË«ÿÎ¥ÿÍ¬ÿÒ´ÿÒ¸ÿÎ·ÿÍµÿÍ´ÿÎ¹ÿÑ¹ÿÎ¶ÿÎµÿË³ÿÍ´ÿÎ·ÿÐ¶ÿÏ¶ÿÍ·ÿÍ´ÿË¶ÿÐµÿÐ·ÿÍ¶ÿÍ´ÿËµÿÌ·ÿÎ¸ÿÍ¸ÿÍ¹ÿÍ¹ÿÍ¶ÿÎ·ÿÏ¹ÿÓºÿÒºÿÏºÿÍ¸ÿÌ¸ÿÏ¸ÿÏºÿÏ¹ÿÐ·ÿÎ¶ÿÏ¸ÿÏºÿÐ¹ÿÐºÿÎ¹ÿË¶ÿÍµÿÐ¸ÿÐºÿÐ»ÿÍºÿÌ¶ÿÌ¶ÿÍ¹ÿÍºÿÎ»ÿÌ¸ÿÉ¶ÿÈ·ÿÈ¸ÿÊºÿË¹ÿÇ·ÿÆ·ÿÈ¶ÿÈ·ÿÉºÿÅºÿÃ¸ÿÂ¸ÿÂ¹ÿÀ»ÿÃºÿÀ»ÿ»¹ÿº¶ÿ¹·ÿ¹¹ÿº¸ÿº·ÿ¸·ÿ´´ÿ³³ÿ´µÿ´¶ÿµµÿ³³ÿ±±ÿ´µÿ²
\ No newline at end of file
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/checkpkg/expected/100w.hea wfdb-10.4.25/checkpkg/expected/100w.hea
--- wfdb-10.4.24/checkpkg/expected/100w.hea	2002-11-22 14:24:58.000000000 -0500
+++ wfdb-10.4.25/checkpkg/expected/100w.hea	2010-01-21 14:34:32.000000000 -0500
@@ -1,3 +1,3 @@
 100w 2 360 360
-100w.dat 16 200 12 0 -78 -22875 0 column 1
-100w.dat 16 200 12 0 -55 -17147 0 column 2
+100w.dat 212 200/mV 12 0 -78 -22875 0 MLII
+100w.dat 212 200/mV 12 0 -55 -17147 0 V5
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/doc/wag-src/rdsamp.1 wfdb-10.4.25/doc/wag-src/rdsamp.1
--- wfdb-10.4.24/doc/wag-src/rdsamp.1	2009-10-28 11:40:39.000000000 -0400
+++ wfdb-10.4.25/doc/wag-src/rdsamp.1	2009-11-06 12:18:06.000000000 -0500
@@ -1,17 +1,22 @@
-.TH RDSAMP 1 "28 October 2009" "WFDB 10.4.24" "WFDB Applications Guide"
+.TH RDSAMP 1 "6 November 2009" "WFDB 10.4.25" "WFDB Applications Guide"
 .SH NAME
 rdsamp \- read WFDB signal files
 .SH SYNOPSIS
 \fBrdsamp -r\fR \fIrecord\fR [ \fIoptions\fR ... ]
 .SH DESCRIPTION
-\fBrdsamp\fR reads signal files for the specified \fIrecord\fR and writes the
-samples as decimal numbers on the standard output.  If no \fIoptions\fR are
-provided, \fBrdsamp\fR starts at the beginning of the record and prints all
-samples.  Each line of output contains the sample number and samples from each
-signal, beginning with channel 0, separated by tabs.
+\fBrdsamp\fR reads signal files for the specified \fIrecord\fR and
+writes the samples as decimal numbers on the standard output.  If no
+\fIoptions\fR are provided, \fBrdsamp\fR starts at the beginning of
+the record and prints all samples.  By default, each line of output
+contains the sample number and samples from each signal, beginning
+with channel 0, separated by tabs.
 .PP
 \fIOptions\fR include:
 .TP
+\fB-c\fR
+Produce output in CSV (comma-separated value) format (default: write output in
+tab-separated columns). 
+.TP
 \fB-f\fR \fItime\fR
 Begin at the specified \fItime\fR.  By default, \fBrdsamp\fR starts at the
 beginning of the record.
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/doc/wag-src/sigamp.1 wfdb-10.4.25/doc/wag-src/sigamp.1
--- wfdb-10.4.24/doc/wag-src/sigamp.1	2006-02-25 22:23:55.000000000 -0500
+++ wfdb-10.4.25/doc/wag-src/sigamp.1	2010-01-21 11:30:15.000000000 -0500
@@ -1,4 +1,4 @@
-.TH SIGAMP 1 "25 February 2006" "WFDB 10.4.0" "WFDB Applications Guide"
+.TH SIGAMP 1 "12 January 2006" "WFDB 10.4.24" "WFDB Applications Guide"
 .SH NAME
 sigamp \- measure signal amplitudes of a WFDB record
 .SH SYNOPSIS
@@ -41,7 +41,17 @@
 Make up to \fInmax\fR measurements on each signal (default: 300).
 .TP
 \fB-p\fR
-Print results in physical units (default: ADC units).
+Print results in physical units (default: ADC
+units). \fB-p\fR may be followed by a single character to specifify a
+time format (used with \fB-q\fR and \fB-v\fR when printing individual
+measurements); choices are \fB-pd\fR (time of day and date if known),
+\fB-pe\fR (elapsed time in hours, minutes, and seconds), \fB-ph\fR
+(elapsed time in hours), \fB-pm\fR (elapsed time in minutes),
+\fB-ps\fR (elapsed time in seconds (default)), \fB-pS\fR (elapsed time
+in sample intervals).
+.TP
+\fB-q\fR
+Quick mode: print individual measurements only.
 .TP
 \fB-t\fR \fItime\fR
 Process until the specified \fItime\fR in \fIrecord\fR (default: the end of
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/doc/wag-src/wrsamp.1 wfdb-10.4.25/doc/wag-src/wrsamp.1
--- wfdb-10.4.24/doc/wag-src/wrsamp.1	2008-04-18 12:43:34.000000000 -0400
+++ wfdb-10.4.25/doc/wag-src/wrsamp.1	2010-01-21 15:21:28.000000000 -0500
@@ -1,29 +1,47 @@
-.TH WRSAMP 1 "18 April 2008" "WFDB 10.4.7" "WFDB Applications Guide"
+.TH WRSAMP 1 "21 January 2010" "WFDB 10.4.25" "WFDB Applications Guide"
 .SH NAME
 wrsamp \- write WFDB signal files
 .SH SYNOPSIS
-\fBwrsamp\fR [ \fIoptions\fR ... ] \fIcolumn\fR ...
+\fBwrsamp\fR [ \fIoptions\fR ... ] [ \fIcolumn\fR ... ]
 .SH DESCRIPTION
 \fBwrsamp\fR reads text-format input and writes the specified \fIcolumns\fR in
 WFDB signal file format 16 (see \fBsignal\fR(5)), either to the standard output
-or to a disk file (see the \fB-o\fR option below).
+or to a disk file (see the \fB-o\fR option below).  If no \fIcolumns\fR are
+specified, all columns are written (but see the \fB-z\fR option below).
 .PP
-Normally, \fBwrsamp\fR's input is line- and column-oriented, with \fIline
-separator characters\fR (usually ASCII linefeeds) separating input lines, and
-\fIfield separator characters\fR (usually spaces or tabs) separating columns
-within each line.  Columns need not be of constant width; the only requirement
-is that one or more field separator characters appear between adjacent columns.
-The output of \fBrdsamp\fR(1) is an example of an acceptable input format.
-.PP
-Lines are identified by line number.  The first line of input is line 0.
-Similarly, columns are identified by column number, and the leftmost column is
-column 0.  Columns may be selected in any order, and any given column may be
-selected more than once, or omitted.  The order of \fIcolumn\fR arguments
-determines the order of the signals in the output (data from the first
-\fIcolumn\fR specified are written as signal 0, etc.)  If an entry in a
-specified column is missing or improperly formatted, \fBwrsamp\fR produces a
-warning message identifying the line and column numbers, and inserts a zero in
-the output in place of the missing or improperly formatted sample.
+Normally, \fBwrsamp\fR's input is line- and column-oriented, with
+\fIline separator characters\fR (usually ASCII linefeeds) separating
+input lines, and \fIfield separator characters\fR (usually tabs,
+spaces, or commas) separating columns within each line.  Columns need
+not be of constant width; the only requirement is that one or more
+field separator characters appear between adjacent columns.  The
+output of \fBrdsamp\fR(1) is an example of an acceptable input format,
+as is CSV (comma-separated value) format.
+.PP
+If the first input line contains any alphabetic character, it is
+assumed to contain signal names (column headings), and these are
+copied to the output header file (see the \fB-o\fR option below).  In
+this case, if the second input line also contains any alphabetic
+character, it is assumed to contain unit names (i.e., the names of the
+physical units of each signal), and these are also copied to the
+output header file.  Spaces embedded within unit names are written
+as underscores in the header file.
+.PP
+Lines are identified by line number.  The first line of input not
+containing any alphabetic character is line 0.  Similarly, columns are
+identified by column number, and the leftmost column is column 0.
+Columns may be selected in any order, and any given column may be
+selected more than once, or omitted.  The order of \fIcolumn\fR
+arguments determines the order of the signals in the output (data from
+the first \fIcolumn\fR specified are written as signal 0, etc.)  If an
+entry in a specified column is "-" (i.e., flagged as missing or
+invalid), or if an entry in a specified column is any other
+non-numeric value, \fBwrsamp\fR records it as an invalid sample in its
+output.
+.PP
+If line 0 appears to begin with a timestamp (a field of the form
+\fI[hh:mm:ss.sss dd/mm/yyyy]\fR), \fBwrsamp\fR records it as the
+base time (starting time) in the output header file.
 .PP
 \fIOptions\fR include:
 .TP
@@ -72,11 +90,15 @@
 truncated (with a warning message identifying the line number of the offending
 line).
 .TP
-\fB-o\fR \fIrecord\fR
-Write the signal file in the current directory as \fIrecord\fB.dat\fR, and
-create a header file in the current directory for the specified \fIrecord\fR.
-By default, \fIwrsamp\fR writes the signal file to its standard output in
-format 16 (see \fBsignal\fR(5)), and does not create a header file.
+\fB-o\fR \fIrecord\fR Write the signal file in the current directory
+as \fIrecord\fB.dat\fR, and create a header file in the current
+directory for the specified \fIrecord\fR.  By default, \fIwrsamp\fR
+writes the signal file to its standard output, and does not create a
+header file.
+.TP
+\fB-O\fR \fIformat\fR
+Write the signal file in the specified \fIformat\fR (default: 16).  See
+\fBsignal\fR(5) for descriptions and names of available formats.
 .TP
 \fB-r\fR \fIc\fR
 Interpret \fIc\fR as the input line separator (default: \\n, the ASCII linefeed
@@ -97,6 +119,9 @@
 Multiply all input samples by \fIn\fR (default: 1) before writing them to the
 output signal file.  To specify different scaling factors for each signal,
 provide a quoted list of values in place of \fIn\fR (see the examples below).
+.TP
+\fB-z\fR
+Don't copy column 0 unless explicitly specified.
 .SH ENVIRONMENT
 .PP
 It may be necessary to set and export the shell variable \fBWFDB\fR (see
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/doc/wpg-src/wpg0.tex wfdb-10.4.25/doc/wpg-src/wpg0.tex
--- wfdb-10.4.24/doc/wpg-src/wpg0.tex	2009-08-07 11:52:52.000000000 -0400
+++ wfdb-10.4.25/doc/wpg-src/wpg0.tex	2010-01-21 17:51:02.000000000 -0500
@@ -510,6 +510,15 @@
 WFDB Software Package distribution, for information on any more recent
 changes that may not be described here.
 
+@unnumberedsubsec Changes in version 10.4.25 (21 January 2009)
+
+In WFDB library versions 10.4.5 through 10.4.24, strtim("e") did not
+work properly when the open input record was an EDF file and high
+resolution input mode had not been selected.  This problem has been
+corrected.
+
+(WFDB library version 10.4.24 was identical to version 10.4.23.)
+
 @unnumberedsubsec Changes in version 10.4.23 (7 August 2009)
 
 Several changes in @file{lib/signal.c} eliminate unintended interactions that
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/install-wave32 wfdb-10.4.25/install-wave32
--- wfdb-10.4.24/install-wave32	2009-05-12 12:23:30.000000000 -0400
+++ wfdb-10.4.25/install-wave32	2010-01-07 17:02:37.000000000 -0500
@@ -1,25 +1,35 @@
 #! /bin/sh
 # file: install-wave32		G. Moody	7 October 2008
-#				Last revised:  13 January 2009 for Fedora 10
+#				Last revised:   7 January 2010 for Fedora 12
 # Build and install WAVE on 64-bit Linux
 #
-# This script has been tested on 64-bit Fedora 10 running on an Intel Core2
-# CPU.  The previous version (find it in the sources for wfdb-10.4.11) was
-# tested on 64-bit Fedora 9, also on an Intel Core2 CPU.  The methods used here
-# (but not this script itself) have also been tested on 64-bit Fedora 7 and 8
-# running on both AMD and Intel 64-bit CPUs. It requires root (superuser)
-# permissions and must be run from within the top-level WFDB source directory
-# (the directory that also contains the 'configure' script).
+# *** Fedora 12 users: if this script fails, see the note at the end! ***
 #
 # WAVE must be compiled as a 32-bit application, because it depends on the
 # XView toolkit, which does not support 64-bit mode (and most likely, never
-# will), and 64-bit applications cannot use 32-bit libraries.  Furthermore,
-# 32-bit applications cannot use 64-bit libraries, so it is necessary to
-# install 32-bit versions of all of the libraries needed by WAVE, as well as
-# the .h ("include") files associated with these libraries.  These components
-# fall into three categories:
+# will), and 64-bit applications cannot use the 32-bit XView libraries.
+# Furthermore, 32-bit applications cannot use 64-bit libraries, so it is
+# necessary to install 32-bit versions of all of the libraries needed by WAVE,
+# as well as the .h ("include") files associated with these libraries.
+
+# This script automates the process of building a 32-bit WAVE binary.  On
+# Fedora Linux, it should be able to install all of the prerequisites (beyond
+# those already installed in order to compile the rest of the WFDB package in
+# 64-bit mode) before compiling and installing WAVE.  To do this, it should be
+# sufficient to run this script with root (superuser) permissions from within
+# the top-level WFDB source directory (the directory that also contains the
+# 'configure' script).  It is harmless to rerun this script if the
+# prerequisites have been installed already.
+
+# On other versions of Linux, you will need to install the first two groups of
+# prerequisites in some other way before running the final part of this script
+# as root, using the -q option.  See the notes below for hints.
+
+# The prerequisites needed in order to compile WAVE on 64-bit Fedora fall into
+# three groups:
+#
+#  1. 32-bit libraries and font packages available from Fedora repositories
 #
-#  1. 32-bit libraries available from Fedora repositories
 #     These include the standard C library, the X11 client libraries, the X11
 #     pixmap libraries, the libcurl (HTTP client) libraries, and their
 #     respective developer's toolkits.  The easiest way to install these on
@@ -27,25 +37,49 @@
 #     names in other Linux distributions, and "yum" itself may not be available
 #     as a package manager in some distributions.  These commands are safe to
 #     run even if any or all of these packages are already installed.
+#
+#     If you use a Debian-based Linux, such as Ubuntu, it can be difficult to
+#     force apt-get to install 32-bit packages, especially if like-named 64-bit
+#     packages have been installed already.  Google on "getlibs" for an
+#     alternative.
 
 if [ "x$1" != "x-q" ]
 then
-    yum -y update libgcc.i386 glibc-devel.i386 libX11-devel.i386 \
-	libXpm-devel.i386 libcurl-devel.i386 xorg-x11-fonts-misc
-    yum -y install libgcc.i386 glibc-devel.i386 libX11-devel.i386 \
-	libXpm-devel.i386 libcurl-devel.i386 xorg-x11-fonts-misc
-fi
 
-# On Debian-based distributions, such as Ubuntu, it's awkward to force apt-get
-# to install 32-bit packages, especially if like-named 64-bit packages have
-# been installed already.  Google on "getlibs" for an alternative.
+#     The next several lines attempt to determine if this script is being run
+#     under Fedora 12 or a later version, in order to determine the names of
+#     the 32-bit packages to be installed.  This code has not been tested on
+#     other Linux distributions, but it will assume that the Fedora 12 package
+#     names are the correct ones unless it recognizes Fedora 7, 8, 9, 10, or
+#     11.  Until Fedora 12, 32-bit packages are named with 'i386' suffixes
+#     (although in some cases they have been compiled for i586 or i686
+#     architectures).  In Fedora 12, 32-bit packages are compiled and named as
+#     i686 packages.
+
+    FV=`cat /etc/fedora-release | cut -d " " -f 3`
+    case $FV in
+	7|8|9|10|11) X86=i386 ;;
+	*) X86=i686 ;;
+    esac
+    yum -y install libgcc.$X86 glibc-devel.$X86 libX11-devel.$X86 \
+	libXpm-devel.$X86 libcurl-devel.$X86
+    yum -y xorg-x11-fonts-misc \
+	xorg-x11-fonts-100dpi xorg-x11-fonts-ISO8859-1-100dpi \
+	xorg-x11-fonts-75dpi xorg-x11-fonts-ISO8859-1-75dpi
+fi
 
 #  2. XView libraries available from PhysioNet
+#
 #     These are available as RPMs for Fedora and other RPM-based distributions,
 #     and in binary and source tarballs for other distributions.  By far the
 #     easiest way to install them on Fedora is using the RPM command below.
 #     Again, this command is safe even if any or all of these are already
 #     installed.
+#
+#     On Debian or Ubuntu, simply run
+#          apt-get install xviewg-dev
+#     instead of the rpm commands below (since 32-bit XView is in the Debian
+#     repositories).
 
 if [ "x$1" != "x-q" ]
 then
@@ -54,13 +88,10 @@
 	http://physionet.org/physiotools/xview/i386-Fedora/xview-devel-3.2p1.4-21.1.fc8.i386.rpm
 fi
 
+#  3. The 32-bit version of the WFDB library
 #
-# On Debian or Ubuntu, simply run
-#	apt-get install xviewg-dev
-# instead of the rpm commands above (32-bit XView is in the Debian repositories).
-
-#  3. the 32-bit version of the WFDB library
-#     This is easily compiled and installed by the following commands:
+#     This is easily compiled and installed on any platform by the following
+#     commands:
 
 make clean
 ./configure -m32
@@ -80,3 +111,34 @@
 # Clean up intermediate binaries and other temporary files.
 cd ..
 make clean
+
+
+# ****************************************************************************
+#
+# On Fedora 12, at the time this script was written, the most recent 32-bit
+# version of libcurl-devel was older than the most recent 64-bit version.
+# In this situation (and presumably in similar situations in which a 32-bit
+# package is "stale") the first yum command above fails.  This is a bug
+# in yum that may be fixed in a future release;  and it is also likely that
+# libcurl-devel.i686 will be updated in the future, so you may not encounter
+# this problem.  If you do, here's the work-around:
+#
+# 1. Download the 32-bit rpm that yum refuses to install.
+#
+# 2. Attempt to install it using a command of the form
+#      rpm -ivh --force *.i686.rpm
+#    If this is successful, rerun this script.
+#
+# 3. If rpm complained that one or more dependencies were missing, use yum to
+#    install them, then repeat step 2.
+#
+# Iterate until successful.  In the case of libcurl-devel, it was necessary
+# to use these commands (the yum commands could have been combined into one):
+#
+#    yum -y install libgcc.i686 libX11-devel.i686 install glibc-devel.i686
+#    yum -y install libXpm-devel.i686
+#    yum -y install cyrus-sasl-lib.i686 keyutils-libs.i686 krb5-libs.i686
+#    yum -y install libidn.i686 libssh2.i686 ncurses-libs.i686 nspr.i686
+#    yum -y install nss.i686 nss-softokn.i686 openldap.i686
+#    rpm -ivh --force libcurl-7.19.7-2.fc12.i686.rpm 
+#    rpm -ivh --force libcurl-devel-7.19.7-2.fc12.i686.rpm 
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/lib/signal.c wfdb-10.4.25/lib/signal.c
--- wfdb-10.4.24/lib/signal.c	2009-06-12 16:18:02.000000000 -0400
+++ wfdb-10.4.25/lib/signal.c	2010-01-19 13:05:25.000000000 -0500
@@ -1,10 +1,10 @@
 /* file: signal.c	G. Moody	13 April 1989
-			Last revised:   12 June 2009	wfdblib 10.4.22
+			Last revised:   19 January 2010	wfdblib 10.4.24
 WFDB library functions for signals
 
 _______________________________________________________________________________
 wfdb: a library for reading and writing annotated waveforms (time series data)
-Copyright (C) 1989-2009 George B. Moody
+Copyright (C) 1989-2010 George B. Moody
 
 This library is free software; you can redistribute it and/or modify it under
 the terms of the GNU Library General Public License as published by the Free
@@ -749,7 +749,7 @@
     cfreq = ffreq; /* set sampling and counter frequencies to match */
     sfreq = ffreq * ispfmax;
     if (getafreq() == 0.0) setafreq(sfreq);
-    gvmode &= WFDB_HIGHRES;
+    gvmode |= WFDB_HIGHRES;
     sprintf(buf, "%02d:%02d:%02d %02d/%02d/%04d",
 	    hour, minute, second, day, month, year);
     setbasetime(buf);
diff -Naur --exclude Makefile --exclude info wfdb-10.4.24/lib/wfdb.h wfdb-10.4.25/lib/wfdb.h
--- wfdb-10.4.24/lib/wfdb.h	2009-10-28 12:32:03.000000000 -0400
+++ wfdb-10.4.25/lib/wfdb.h	2010-01-21 20:49:21.000000000 -0500
@@ -33,7 +33,7 @@
 /* WFDB library version. */
 #define WFDB_MAJOR   10
 #define WFDB_MINOR   4
-#define WFDB_RELEASE 24
+#define WFDB_RELEASE 25
 #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.4.24/NEWS wfdb-10.4.25/NEWS
--- wfdb-10.4.24/NEWS	2009-10-28 13:35:40.000000000 -0400
+++ wfdb-10.4.25/NEWS	2010-01-21 21:19:00.000000000 -0500
@@ -1,3 +1,24 @@
+10.4.25 (21 January 2010):
+	WFDB application 'rdsamp' can now write CSV (comma-separated value)
+	format output using its new '-c' option, and 'wrsamp' can read CSV
+	format input.  If no input columns are specified, 'wrsamp' converts
+        all of them.  If the input contains signal names, units, or timestamps,
+        'wrsamp' saves them in the header file it creates.  'wrsamp' also has
+        two new options ('-O', to select the output format, and '-z', to
+        suppress output of column 0).
+
+	WFDB application 'sigamp' has new options for printing individual
+	measurements.
+
+	The script 'install-wave32', used to build and install a 32-bit version
+        of WAVE on some 64-bit platforms, has been updated so that it can be
+	used on Fedora 12 (as well as on previously supported platforms).
+
+	In WFDB library versions 10.4.5 through 10.4.24, strtim("e") did not
+	work properly when the open input record was an EDF file and high
+	resolution input mode had not been selected.  This problem has been
+	corrected.
+
 10.4.24 (28 October 2009):
 	A possible string overflow in nst.c has been corrected.  Thanks to
 	Virginia Faro-Maza for finding and reporting this problem.
