diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/app/sampfreq.c wfdb-10.5.7/app/sampfreq.c --- wfdb-10.5.6/app/sampfreq.c 2001-10-05 01:03:00.000000000 -0400 +++ wfdb-10.5.7/app/sampfreq.c 2010-12-05 09:37:28.000000000 -0500 @@ -33,10 +33,16 @@ char **argv; { if (argc == 3 && strcmp(argv[1], "-H") == 0) { - setgvmode(WFDB_HIGHRES); - (void)isigopen(argv[2], NULL, 0); - printf("%g\n", sampfreq(NULL)); - exit(0); + int nsig; + WFDB_Siginfo *si; + + if ((nsig = isigopen(argv[2], NULL, 0)) > 0) { + SUALLOC(si, nsig, sizeof(WFDB_Siginfo)); + isigopen(argv[2], si, nsig); + setgvmode(WFDB_HIGHRES); + printf("%g\n", sampfreq(NULL)); + exit(0); + } } else if (argc == 2) { printf("%g\n", sampfreq(argv[1])); diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/conf/linux-slib.def wfdb-10.5.7/conf/linux-slib.def --- wfdb-10.5.6/conf/linux-slib.def 2010-05-13 11:01:37.000000000 -0400 +++ wfdb-10.5.7/conf/linux-slib.def 2010-12-16 18:18:10.000000000 -0500 @@ -1,5 +1,5 @@ # file: linux-slib.def G. Moody 31 May 2000 -# Last revised: 13 May 2010 +# Last revised: 12 December 2010 # This section contains settings suitable for generating an ELF-format shared # library under Linux. @@ -126,11 +126,12 @@ lib-post-install: -(cd $(LIBDIR); ln -sf $(WFDBLIB) $(WFDBLIB_SONAME)) -(cd $(LIBDIR); ln -sf $(WFDBLIB_SONAME) $(WFDBLIB_BASENAME)) - -([ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled && /sbin/restorecon $(LIBDIR)/$(WFDBLIB)) - @$(LDCONFIG) || echo Warning: "$(LDCONFIG)" was unsuccessful + -([$EUID -eq 0 -a -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled && /sbin/restorecon $(LIBDIR)/$(WFDBLIB)) + -([$EUID -eq 0] && ($(LDCONFIG) 2>/dev/null || \ + echo Warning: "$(LDCONFIG)" was unsuccessful)) lib-post-uninstall: rm -f $(LIBDIR)/$(WFDBLIB_BASENAME) rm -f $(LIBDIR)/$(WFDBLIB_SONAME) - @$(LDCONFIG) || echo Warning: "$(LDCONFIG)" was unsuccessful + -@$(LDCONFIG) || echo Warning: "$(LDCONFIG)" was unsuccessful #______________________________________________________________________________ diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/doc/wag-src/fixag.sed wfdb-10.5.7/doc/wag-src/fixag.sed --- wfdb-10.5.6/doc/wag-src/fixag.sed 2006-08-23 15:25:29.000000000 -0400 +++ wfdb-10.5.7/doc/wag-src/fixag.sed 2010-12-11 10:15:24.000000000 -0500 @@ -85,7 +85,7 @@ s+>WFDB Programmer's Guide<+>WFDB Programmer's Guide<+ s+>WFDB Applications Guide<+>WFDB Applications Guide<+ s+>WAVE User's Guide<+>WAVE User's Guide<+ -s+>plt Tutorial and Cookbook<+>plt Tutorial and Cookbook<+ +s+>plt Tutorial and Cookbook<+>plt Tutorial and Cookbook<+ s+Table of Contents+
Next: NEXTPAGE Up: WFDB Applications Guide Previous: PREVPAGEOn This Page
+ s+Table of Contents+
Next: NEXTPAGE Up: WFDB Applications Guide Previous: PREVPAGEOn This Page
+ s++

Up: WFDB Applications Guide


Please e-mail your comments and suggestions to webmaster@physionet.org, or post them to:

PhysioNet
MIT Room E25-505A
77 Massachusetts Avenue
Cambridge, MA 02139 USA

Updated LONGDATE+ diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/doc/wpg-src/wpg0.tex wfdb-10.5.7/doc/wpg-src/wpg0.tex --- wfdb-10.5.6/doc/wpg-src/wpg0.tex 2010-11-29 19:24:38.000000000 -0500 +++ wfdb-10.5.7/doc/wpg-src/wpg0.tex 2010-12-16 19:36:15.000000000 -0500 @@ -3339,6 +3339,7 @@ conversion parameters. * setwfdb:: Dynamically changing the database path. * getwfdb:: Reading the database path. +* resetwfdb:: Restoring the initial database path. * wfdbfile:: Obtaining the pathname of a WFDB file. * wfdbflush:: Flushing buffered output annotations and samples. @@ -3561,6 +3562,8 @@ @itemize @bullet @item +the WFDB path (in versions 10.5.7 and later) +@item the factors used for converting between samples, seconds, and counter values (reset to 1), the base time (reset to 0, i.e., midnight), and the base counter value (reset to 0); @@ -4061,7 +4064,7 @@ for an example of the use of @code{setwfdb}. @c @group -@node getwfdb, wfdbfile, setwfdb, miscellaneous functions +@node getwfdb, resetwfdb, setwfdb, miscellaneous functions @unnumberedsubsec getwfdb @findex getwfdb @cindex database path (reading) @@ -4102,6 +4105,22 @@ as @samp{\\}.) @c @group +@node resetwfdb, wfdbfile, getwfdb, miscellaneous functions +@unnumberedsubsec resetwfdb +@findex resetwfdb (10.5.7) +@cindex WFDB path (resetting) + +@example +void resetwfdb(void) +@end example +@noindent +@c @end group + +@noindent +This function restores the WFDB path to its initial value (either the +first value returned by @code{getwfdb} in the current process, or @code{NULL}). + +@c @group @node wfdbfile, wfdbflush, getwfdb, miscellaneous functions @unnumberedsubsec wfdbfile @findex wfdbfile (4.3) @@ -8982,6 +9001,22 @@ changes that may not be described here. @unnumberedsec WFDB 10.5 + +@unnumberedsubsec Changes in version 10.5.6 (16 December 2010) + +When opening records with the same name in different directories successively +within a single process, the persistence of WFDB path changes made by WFDB +library function @code{wfdb_addtopath()} interfered with locating the correct +files in the second and subsequent records. The solution included addition of +a new WFDB library function, @code{resetwfdb()}, which restores the WFDB path +to the value returned by the first invocation of @code{getwfdb()} in the +current process (or @code{NULL} if @code{getwfdb()} has not been invoked); +library function @code{wfdbquit()} now invokes @code{resetwfdb()}. In +addition, the safe-string copy macro @code{SSTRCPY} (defined in @code{wfdb.h}) +now properly handles the case of copying null pointers. Thanks to Benjamin +Moody for identifying the problem, providing test inputs, and contributions to +the solution. + @unnumberedsubsec Changes in version 10.5.6 (29 November 2010) WFDB records with names of the form @file{nnn/nnn} can now be identified diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/lib/Makefile.tpl wfdb-10.5.7/lib/Makefile.tpl --- wfdb-10.5.6/lib/Makefile.tpl 2009-05-14 10:42:24.000000000 -0400 +++ wfdb-10.5.7/lib/Makefile.tpl 2010-12-12 13:20:15.000000000 -0500 @@ -1,5 +1,5 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 14 May 2009 +# Last revised: 12 December 2010 # This section of the Makefile should not need to be changed. INCLUDES = $(INCDIR)/wfdb/wfdb.h $(INCDIR)/wfdb/wfdblib.h \ @@ -19,7 +19,7 @@ $(MAKE) all cp $(WFDBLIB) $(LIBDIR) $(SETLPERMISSIONS) $(LIBDIR)/$(WFDBLIB) - $(MAKE) lib-post-install + $(MAKE) lib-post-install 2>/dev/null # 'make collect': retrieve the installed WFDB library and headers collect: diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/lib/wfdb.h wfdb-10.5.7/lib/wfdb.h --- wfdb-10.5.6/lib/wfdb.h 2010-11-29 21:22:52.000000000 -0500 +++ wfdb-10.5.7/lib/wfdb.h 2010-12-17 06:12:23.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdb.h G. Moody 13 June 1983 - Last revised: 29 November 2010 wfdblib 10.5.6 + Last revised: 16 December 2010 wfdblib 10.5.7 WFDB library type, constant, structure, and function interface definitions _______________________________________________________________________________ wfdb: a library for reading and writing annotated waveforms (time series data) @@ -32,7 +32,7 @@ /* WFDB library version. */ #define WFDB_MAJOR 10 #define WFDB_MINOR 5 -#define WFDB_RELEASE 6 +#define WFDB_RELEASE 7 #define WFDB_NETFILES 1 /* if 1, library includes code for HTTP, FTP clients */ #define WFDB_NETFILES_LIBCURL 1 @@ -348,6 +348,7 @@ extern FSTRING wfdberror(void); extern FVOID setwfdb(char *database_path_string); extern FSTRING getwfdb(void); +extern FVOID resetwfdb(void); extern FINT setibsize(int input_buffer_size); extern FINT setobsize(int output_buffer_size); extern FSTRING wfdbfile(char *file_type, char *record); diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/lib/wfdb.h0 wfdb-10.5.7/lib/wfdb.h0 --- wfdb-10.5.6/lib/wfdb.h0 2010-11-29 12:00:20.000000000 -0500 +++ wfdb-10.5.7/lib/wfdb.h0 2010-12-16 18:55:23.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdb.h G. Moody 13 June 1983 - Last revised: 29 November 2010 wfdblib 10.5.6 + Last revised: 16 December 2010 wfdblib 10.5.7 WFDB library type, constant, structure, and function interface definitions _______________________________________________________________________________ wfdb: a library for reading and writing annotated waveforms (time series data) @@ -348,6 +348,7 @@ extern FSTRING wfdberror(void); extern FVOID setwfdb(char *database_path_string); extern FSTRING getwfdb(void); +extern FVOID resetwfdb(void); extern FINT setibsize(int input_buffer_size); extern FINT setobsize(int output_buffer_size); extern FSTRING wfdbfile(char *file_type, char *record); diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/lib/wfdbinit.c wfdb-10.5.7/lib/wfdbinit.c --- wfdb-10.5.6/lib/wfdbinit.c 2008-01-11 13:38:13.000000000 -0500 +++ wfdb-10.5.7/lib/wfdbinit.c 2010-12-16 18:37:29.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdbinit.c G. Moody 23 May 1983 - Last revised: 11 January 2008 wfdblib 10.4.5 + Last revised: 16 December 2010 wfdblib 10.5.7 WFDB library functions wfdbinit, wfdbquit, and wfdbflush _______________________________________________________________________________ wfdb: a library for reading and writing annotated waveforms (time series data) @@ -46,6 +46,7 @@ { wfdb_anclose(); /* close annotation files, reset variables */ wfdb_sigclose(); /* close signals, reset variables */ + resetwfdb(); /* restore the WFDB path */ wfdb_sampquit(); /* release sample data buffer */ #if WFDB_NETFILES wfdb_wwwquit(); /* release resources allocated by libcurl or libwww */ diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/lib/wfdbio.c wfdb-10.5.7/lib/wfdbio.c --- wfdb-10.5.6/lib/wfdbio.c 2010-11-29 21:16:46.000000000 -0500 +++ wfdb-10.5.7/lib/wfdbio.c 2010-12-16 19:04:25.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdbio.c G. Moody 18 November 1988 - Last revised: 29 November 2010 wfdblib 10.5.6 + Last revised: 16 December 2010 wfdblib 10.5.7 Low-level I/O functions for the WFDB library _______________________________________________________________________________ @@ -33,6 +33,7 @@ This file contains definitions of the following WFDB library functions: getwfdb (returns the database path string) setwfdb (sets the database path string) + resetwfdb [10.5.7] (restores the database path to its initial value) wfdbquiet (suppresses WFDB library error messages) wfdbverbose [4.0] (enables WFDB library error messages) wfdberror [4.5] (returns the most recent WFDB library error message) @@ -169,7 +170,16 @@ specified (local) FILE (using wfdb_getiwfdb); such files may be nested up to 10 levels. */ -static char *wfdbpath = NULL; +static char *wfdbpath = NULL, *wfdbpath_init = NULL; + +/* resetwfdb is called by wfdbquit, and can be called within an application, +to restore the WFDB path to the value that was returned by the first call +to getwfdb (or NULL if getwfdb was not called). */ + +FVOID resetwfdb(void) +{ + SSTRCPY(wfdbpath, wfdbpath_init); +} FSTRING getwfdb(void) { @@ -178,6 +188,7 @@ if (p == NULL) p = DEFWFDB; if (*p == '@') p = wfdb_getiwfdb(p); + SSTRCPY(wfdbpath_init, p); setwfdb(p); } return (wfdbpath); diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/NEWS wfdb-10.5.7/NEWS --- wfdb-10.5.6/NEWS 2010-11-29 22:07:06.000000000 -0500 +++ wfdb-10.5.7/NEWS 2010-12-25 08:55:35.259522974 -0500 @@ -1,3 +1,20 @@ +10.5.7 (12 December 2010): + When opening records with the same name in different directories + successively within a single process, the persistence of WFDB path + changes made by WFDB library function wfdb_addtopath interfered with + locating the correct files in the second and subsequent records. + The solution included addition of a new WFDB library function, + resetwfdb, which restores the WFDB path to the value returned by + the first invocation of getwfdb in the current process (or NULL if + getwfdb has not been invoked); library function wfdbquit now invokes + resetwfdb. In addition, the safe-string copy macro SSTRCPY (defined + in wfdb.h) now properly handles the case of copying null pointers. + Thanks to Benjamin Moody for identifying the problem, providing test + inputs, and contributions to the solution. + + The -H option of app/sampfreq.c did not work in recent releases because + of changes in the WFDB library; it works again in 10.5.7. + 10.5.6 (29 November 2010): WFDB records with names of the form 'nnn/nnn' can now be identified using the short form 'nnn/' in applications built using the WFDB library diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/wfdbmap/map-record wfdb-10.5.7/wfdbmap/map-record --- wfdb-10.5.6/wfdbmap/map-record 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/wfdbmap/map-record 2010-07-30 15:37:34.000000000 -0400 @@ -0,0 +1,12 @@ +#! /bin/bash + +# make a map of a WFDB record + +RL=$1 +RS=`basename $RL` +shift + +wfdbmap -r $RL $* >$RS.script +bash $RS.script >$RS.plt 2>/dev/null +lwcat -strip -eps $RS.plt >$RS.ps +convert -density 150 $RS.ps $RS.png diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/wfdbmap/README wfdb-10.5.7/wfdbmap/README --- wfdb-10.5.6/wfdbmap/README 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/wfdbmap/README 2010-11-04 10:28:34.000000000 -0400 @@ -0,0 +1,38 @@ +file: README G. Moody 30 July 2010 + +Software for creating 'maps' of PhysioBank records + +To build and install the software in this directory, first install the +WFDB and plt software packages from PhysioNet, ImageMagick (from +www.imagemagick.org), then return to this directory and type 'make'. + +This directory contains 'wfdbmap', a program that reads a WFDB record, +optionally including one or more associated annotation files, and generates a +script containing embedded data. When the script is run, it creates (using +'plt' and 'lwcat', from the plt package) a PostScript-format 'map' of the WFDB +record and its annotations. The maps displayed by the PhysioBank ATM are +created in this way. + +Also in this directory is 'map-record', a shell script that illustrates how +'wfdbmap' and (indirectly) 'plt are used to create a map, and how to convert +the PostScript map into a PNG-format map (using 'convert' from ImageMagick). +For example, to make a map of mitdb/200 and its associated 'atr' annotations, +run the command: + map-record mitdb/200 -a atr +The outputs of this command are 200.ps and 200.png. If other annotation files +are available, their annotator names can be given as additional command-line +arguments: + map-record RECORD -a ANN1 ANN2 ANN3 ... + +*** A MAJOR LIMITATION of this version of wfdbmap is that it cannot properly +map segments of a variable-layout multisegment record unless all of the +.hea files for that record are local (i.e., they can't be retrieved from a +web server as needed). For the ATM, this is not a limitation at all, since +the ATM is mapping local records on physionet.org. For general use, it's +a serious limitation, since variable-layout multisegment records are often +the records for which a map would be most useful. + +The symptoms of this problem are a warning message of the form: + wfdbmap: can't open http://physionet.org/physiobank/database/... +and an output map that shows all signals as missing. (The annotations are +mapped properly, however.) diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/wfdbmap/signal-colors.h wfdb-10.5.7/wfdbmap/signal-colors.h --- wfdb-10.5.6/wfdbmap/signal-colors.h 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/wfdbmap/signal-colors.h 2009-04-09 16:44:18.000000000 -0400 @@ -0,0 +1,311 @@ +struct sigcolor { + char *color; + char *class; + char *name; +} ctab[] = { +"red", "ECG", "Abdomen_1", +"red", "ECG", "Abdomen_2", +"red", "ECG", "Abdomen_3", +"red", "ECG", "Abdomen_4", +"green", "Resp", "Abdomen [ABMV]", +"green", "Resp", "ABDO RES", +"blue", "BP", "ABP", +"blue", "BP", "ABP_1/2", +"blue", "BP", "ABP_2/2", +"blue", "BP", "ABPDias", +"blue", "BP", "ABPMean", +"blue", "BP", "ABPSys", +"blue", "BP", "AOBP", +"blue", "BP", "AOBP_1/2", +"blue", "BP", "AOBP_2/2", +"blue", "BP", "AOBP Dias", +"blue", "BP", "AOBP Mean", +"blue", "BP", "AOBP Sys", +"blue", "BP", "NBP_1/2", +"blue", "BP", "NBP_2/2", +"blue", "BP", "NBP", +"blue", "BP", "NBPDias", +"blue", "BP", "NBPMean", +"blue", "BP", "NBPSys", +"blue", "BP", "ABP Dias", +"blue", "BP", "ABP Mean", +"blue", "BP", "ABP Sys", +"blue", "BP", "NBP Dias", +"blue", "BP", "NBP Mean", +"blue", "BP", "NBP Sys", +"blue", "BP", "ABP ", +"red", "ECG", "A-I", +"green", "Resp", "AIRFLOW", +"blue", "BP", "ART", +"blue", "BP", "ART_1/2", +"blue", "BP", "ART Dias", +"blue", "BP", "ART Mean", +"blue", "BP", "ART Sys", +"blue", "BP", "ART ", +"blue", "BP", "ART 1", +"blue", "BP", "ART1 ", +"blue", "BP", "ART^M ", +"red", "ECG", "A-S", +"red", "ECG", "avf", +"red", "ECG", "aVF", +"red", "ECG", "AVF", +"red", "ECG", "AVF+", +"red", "ECG", "avl", +"red", "ECG", "aVL", +"red", "ECG", "AVL", +"red", "ECG", "avr", +"red", "ECG", "aVR", +"red", "ECG", "AVR", +"darkgreen", "Temp", "BLOODT", +"darkgreen", "Temp", "BLOODT_1/3", +"darkgreen", "Temp", "BLOODT_2/3", +"darkgreen", "Temp", "BLOODT_3/3", +"blue", "BP", "BP", +"blue", "BP", " BP", +"pink", "noise", "BW noise, signal 0", +"pink", "noise", "BW noise, signal 1", +"darkred", "CO", "CO", +"darkred", "CO", "CO_1/2", +"darkred", "CO", "CO_1/3", +"darkred", "CO", "CO_2/2", +"darkred", "CO", "CO_2/3", +"darkred", "CO", "CO_3/3", +"purple", "CO2", "C02", +"blue", "BP", "CVP", +"blue", "BP", "CVP_1/2", +"blue", "BP", "CVP_1/3", +"blue", "BP", "CVP_2/2", +"blue", "BP", "CVP_2/3", +"blue", "BP", "CVP_3/3", +"red", "ECG", "CC5", +"red", "ECG", "chan 1", +"red", "ECG", "chan 2", +"red", "ECG", "chan 3", +"red", "ECG", "CM2", +"red", "ECG", "CM4", +"red", "ECG", "CM5", +"purple", "CO2", "Co2", +"purple", "CO2", "CO2", +"purple", "CO2", "CO2^M ", +"red", "ECG", "CS12", +"red", "ECG", "CS34", +"red", "ECG", "CS56", +"red", "ECG", "CS78", +"red", "ECG", "CS90", +"blue", "BP", "CVP", +"blue", "BP", "CVP 3", +"red", "ECG", "D3", +"red", "ECG", "D4", +"red", "ECG", "ECG", +"red", "ECG", " ECG", +"red", "ECG", "ECG0", +"red", "ECG", "ECG1", +"red", "ECG", "ECG 1", +"red", "ECG", "ECG 2", +"red", "ECG", "ECG 3", +"red", "ECG", "ECG AVF", +"red", "ECG", "ECG [ECG1]", +"red", "ECG", "ECG F", +"red", "ECG", "ECG I", +"red", "ECG", "ECG II", +"red", "ECG", "ECG III", +"red", "ECG", "ECG lead 1", +"red", "ECG", "ECG lead 2", +"red", "ECG", "ECG lead 3", +"red", "ECG", "ECG Lead AVF", +"red", "ECG", "ECG lead AVL", +"red", "ECG", "ECG lead I", +"red", "ECG", "ECG Lead I", +"red", "ECG", "ECG lead II", +"red", "ECG", "ECG lead II ", +"red", "ECG", "ECG LeadII", +"red", "ECG", "ECG Lead II", +"red", "ECG", "ECG lead III", +"red", "ECG", "ECG Lead III", +"red", "ECG", "ECG lead V", +"red", "ECG", "ECG Lead V", +"red", "ECG", "ECG Lead V ", +"red", "ECG", "ECG Lead V3", +"red", "ECG", "ECG Lead V4", +"red", "ECG", "ECG lead V5", +"red", "ECG", "ECG Lead V5", +"red", "ECG", "ECG lead V6", +"red", "ECG", "ECG MCL", +"red", "ECG", "ECG signal 0", +"red", "ECG", "ECG signal 1", +"red", "ECG", "ECG V", +"red", "ECG", "ECG V3", +"red", "ECG", "ECG V Lead", +"grey", "Annot", "EDF Annotations", +"yellow", "EEG", "EEG", +"yellow", "EEG", "EEG C3-A2 [C3A2]", +"yellow", "EEG", "EEG (C3-O1)", +"yellow", "EEG", "EEG (C4-A1)", +"yellow", "EEG", "EEG C4-A1 [C4A1]", +"yellow", "EEG", "EEG Fpz-Cz", +"yellow", "EEG", "EEG (O2-A1)", +"yellow", "EEG", "EEG Pz-Oz", +"yellow", "EEG", "EEG(sec)", +"orange", "EMG", "EMG", +"orange", "EMG", "EMG-Chin [EMYG]", +"orange", "EMG", "EMG submental", +"orange", "EMG", "EMG Submental", +"pink", "noise", "EM noise, signal 0", +"pink", "noise", "EM noise, signal 1", +"lightblue", "EOG", "EOG", +"lightblue", "EOG", "EOG E1-A1 [EOGL]", +"lightblue", "EOG", "EOG E2-A1 [EOGR]", +"lightblue", "EOG", "EOG horizontal", +"lightblue", "EOG", "EOG(L)", +"lightblue", "EOG", "EOG(R)", +"lightblue", "EOG", "EOG (right)", +"red", "ECG", "E-S", +"grey", "Annot", "Event marker", +"green", "Resp", "Flow [AFLO]", +"grey", "Annot", "Hypnogram", +"red", "ECG", "i", +"red", "ECG", "I", +"red", "ECG", "I ", +"red", "ECG", "I+", +"blue", "BP", "ICP", +"grey", "Annot", "ID+Sync+Error", +"red", "ECG", "ii", +"red", "ECG", "II", +"red", "ECG", "II ", +"red", "ECG", "II+", +"red", "ECG", "iii", +"red", "ECG", "III", +"red", "ECG", "III ", +"red", "ECG", "III+", +"blue", "BP", "LAP", +"red", "ECG", "lead I", +"red", "ECG", "lead II", +"red", "ECG", "lead V", +"pink", "noise", "MA noise, signal 0", +"pink", "noise", "MA noise, signal 1", +"red", "ECG", "MCL1", +"red", "ECG", "MCL1 ", +"red", "ECG", "MCL1+", +"red", "ECG", "ML2", +"red", "ECG", "ML5", +"red", "ECG", "MLI", +"red", "ECG", "MLII", +"red", "ECG", "MLIII", +"red", "ECG", "mod.V1", +"red", "ECG", "MV2", +"red", "ECG", "MV2 ", +"blue", "BP", "P1", +"blue", "BP", "P1 Dias", +"blue", "BP", "P1 Mean", +"blue", "BP", "P1 Sys", +"blue", "BP", "PA", +"blue", "BP", "PAP", +"blue", "BP", "PAP_1/2", +"blue", "BP", "PAP_2/2", +"blue", "BP", "PAP ", +"blue", "BP", "PAP Dias", +"blue", "BP", "PAP Mean", +"blue", "BP", "PAP Sys", +"blue", "BP", "PAP 2", +"blue", "BP", "PAWP", +"blue", "BP", "PAWP_1/2", +"blue", "BP", "PAWP_1/3", +"blue", "BP", "PAWP_2/2", +"blue", "BP", "PAWP_2/3", +"blue", "BP", "PAWP_3/3", +"darkblue", "PLETH", "PLETH", +"darkblue", "PLETH", "PLETH ", +"grey", "Pos", "Position", +"orangered", "HR", "HR", +"orangered", "HR", "HR_1/2", +"orangered", "HR", "HR_1/3", +"orangered", "HR", "HR_2/2", +"orangered", "HR", "HR_2/3", +"orangered", "HR", "HR_3/3", +"orangered", "HR", "PR", +"blue", "BP", "Pressure", +"blue", "BP", "Pressure ", +"blue", "BP", "Pressure1", +"blue", "BP", "Pressure 1", +"blue", "BP", "Pressure 2", +"blue", "BP", "Pressure 3", +"blue", "BP", "Pressure 4", +"orangered", "HR", "Pulse", +"orangered", "HR", "PULSE", +"orangered", "HR", "PULSE_1/2", +"orangered", "HR", "PULSE_1/3", +"orangered", "HR", "PULSE_2/2", +"orangered", "HR", "PULSE_2/3", +"orangered", "HR", "PULSE_3/3", +"orangered", "HR", "PVC Rate per Minute", +"orangered", "HR", "PVC Rate per Minute_1/2", +"orangered", "HR", "PVC Rate per Minute_1/3", +"orangered", "HR", "PVC Rate per Minute_2/3", +"orangered", "HR", "PVC Rate per Minute_3/3", +"blue", "BP", "RAP", +"green", "Resp", "RESP", +"green", "Resp", "RESP_1/2", +"green", "Resp", "RESP_1/3", +"green", "Resp", "RESP_2/2", +"green", "Resp", "RESP_2/3", +"green", "Resp", "RESP_3/3", +"green", "Resp", " RESP", +"green", "Resp", "RESP ", +"green", "Resp", "Resp A", +"green", "Resp", "Resp (abdomen)", +"green", "Resp", "Resp (abdominal)", +"green", "Resp", "Resp C", +"green", "Resp", "Resp (chest)", +"green", "Resp", "Resp Imp", +"green", "Resp", "Resp. Imp.", +"green", "Resp", "Resp.Imp.", +"green", "Resp", "RESP IMP", +"green", "Resp", "Resp Inp.", +"green", "Resp", "Resp N", +"green", "Resp", "Resp (nasal)", +"green", "Resp", "Resp oro-nasal", +"green", "Resp", "Resp (sum)", +"darkred", "O2", "SaO2", +"darkred", "O2", "SaO2 [OSAT]", +"darkred", "O2", "SO2", +"grey", "Sound", "Sound", +"darkred", "O2", "SpO2", +"darkred", "O2", "SpO2_1/2", +"darkred", "O2", "SpO2_1/3", +"darkred", "O2", "SpO2_2/2", +"darkred", "O2", "SpO2_2/3", +"darkred", "O2", "SpO2_3/3", +"green", "Resp", "Sum", +"magenta", "SV", "SV", +"darkgreen", "Temp", "Temp body", +"green", "Resp", "Thorax_1", +"green", "Resp", "Thorax_2", +"green", "Resp", "Thorax [CHMV]", +"green", "Resp", "THOR RES", +"blue", "BP", "UAP", +"blue", "BP", "UAP Dias", +"blue", "BP", "UAP Mean", +"blue", "BP", "UAP Sys", +"red", "ECG", "V", +"red", "ECG", "V ", +"red", "ECG", "V+", +"red", "ECG", "v1", +"red", "ECG", "V1", +"red", "ECG", "V1-V2", +"red", "ECG", "v2", +"red", "ECG", "V2", +"red", "ECG", "V2-V3", +"red", "ECG", "v3", +"red", "ECG", "V3", +"red", "ECG", "v4", +"red", "ECG", "V4", +"red", "ECG", "V4-V5", +"red", "ECG", "v5", +"red", "ECG", "V5", +"red", "ECG", "v6", +"red", "ECG", "V6", +"red", "ECG", "vx", +"red", "ECG", "vy", +"red", "ECG", "vz", +NULL, NULL, NULL +}; diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/wfdbmap/wfdbmap.c wfdb-10.5.7/wfdbmap/wfdbmap.c --- wfdb-10.5.6/wfdbmap/wfdbmap.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/wfdbmap/wfdbmap.c 2009-03-30 02:22:56.000000000 -0400 @@ -0,0 +1,469 @@ +/* file: wfdbmap.c G. Moody 22 March 2009 + Last revised: 30 March 2009 + +------------------------------------------------------------------------------- +wfdbmap: generates a 'plt' script to make a PostScript map of a WFDB record +Copyright (C) 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 +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 +#include +#include +#include + +char *pname; +WFDB_Anninfo *ai = NULL; +WFDB_Siginfo *si = NULL; +int mflag = 0; +int spm; + +main(argc, argv) +int argc; +char *argv[]; +{ + char *record = NULL, *prog_name(); + int i, j, length, **map = NULL, nann = 0, nsig; + void help(); + void map_sig(char *record, WFDB_Siginfo *si, int nsig,int **map,int length); + void map_ann(char *record, WFDB_Anninfo *ai, int nann,int **map,int length); + void write_map(int **map, int nsig, int nann, int length); + void write_script(int **map, int nsig, int nann, int length); + + pname = prog_name(argv[0]); + + /* Interpret command-line options. */ + for (i = 1; i < argc; i++) { + if (*argv[i] == '-') switch (*(argv[i]+1)) { + case 'a': /* annotators follow */ + if (++i >= argc) { + (void)fprintf(stderr, "%s: annotators must follow -a\n", + pname); + exit(1); + } + /* how may annotators are there? */ + for (j = i; j < argc && *argv[j] != '-'; j++) + ; + nann = j - i; + /* allocate *ai and initialize it */ + if (nann > 0) { + if ((ai = malloc(nann * sizeof(WFDB_Anninfo))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + exit(2); + } + for (j = 0; j < nann; j++) { + ai[j].name = argv[i++]; + ai[j].stat = WFDB_READ; + } + } + i--; + break; + case 'h': /* print usage summary and quit */ + help(); + exit(0); + break; + case 'm': /* print map only */ + mflag = 1; + break; + case 'r': /* input record name follows */ + if (++i >= argc) { + (void)fprintf(stderr, + "%s: input record name must follow -r\n", + pname); + exit(1); + } + record = argv[i]; + break; + default: + (void)fprintf(stderr, "%s: unrecognized option %s\n", + pname, argv[i]); + exit(1); + } + else { + (void)fprintf(stderr, "%s: unrecognized argument %s\n", + pname, argv[i]); + exit(1); + } + } + if (record == NULL) { + help(); + exit(1); + } + + nsig = isigopen(record, NULL, 0); + if (nsig > 0) { + if ((si = malloc(nsig * sizeof(WFDB_Siginfo))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + if (ai) { wfdbquit(); free(ai); } + exit(2); + } + if ((nsig = isigopen(record, si, nsig)) < 0) + nsig = 0; + } + spm = strtim("60"); + length = (strtim("e") + (spm-1))/spm; /* number of minutes in record */ + + if (length < 1 && nsig > 0) { + /* calculate the length of the record from the signal file size */ + char *p; + double fs = 0; + FILE *ifile; + long bpm; + + for (i = 0; i < nsig && si[i].group == 0; i++) + switch (si[i].fmt) { + case 8: + case 80: fs += si[i].spf; break; + case 310: + case 311: fs += 1.33333333*si[i].spf; break; + case 212: fs += 1.5*si[i].spf; break; + default: fs += 2*si[i].spf; break; + } + bpm = fs * spm + 0.5; /* bytes per minute */ + p = wfdbfile(si[i].fname, NULL); + ifile = fopen(p, "r"); + fseek(ifile, 0L, SEEK_END); + length = ftell(ifile)/bpm; + fclose(ifile); + } + + if (length < 1) { /* try to get length from annotation file(s) */ + int alen; + WFDB_Annotation annot; + WFDB_Time t; + + for (i = 0; i < nann; i++) { + if (annopen(record, &ai[i], 1) < 0) continue; + while (getann(0, &annot) >= 0) + t = annot.time; + alen = (t + (spm-1))/spm; + if (alen > length) length = alen; + } + } + + if (length > 60000) /* 1000 hours > 41 days */ + length = 60000; /* truncate extremely long records */ + + if (length < 1) { + fprintf(stderr, "%s: can't map record %s (length unspecified)\n", + pname, record); + exit(1); + } + + if ((map = malloc((nsig + 4*nann) * sizeof(int *))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + wfdbquit(); + if (ai) free(ai); + if (si) free(si); + exit(2); + } + for (i = 0; i < nsig + 4*nann; i++) + if ((map[i] = calloc(length, sizeof(int))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + while (--i > 0) + if (map[i]) free(map[i]); + free(map); + wfdbquit(); + if (ai) free(ai); + if (si) free(si); + exit(2); + } + + if (nsig > 0) map_sig(record, si, nsig, map, length); + if (nann > 0) map_ann(record, ai, nann, &map[nsig], length); + + if (mflag) { + printf("T"); + for (i = 0; i < nsig; i++) + printf("\t%s", si[i].desc); + for (i = 0; i < nann; i++) + printf("\t%s\tQRS\tEctopic\tVE", ai[i].name); + printf("\n"); + write_map(map, nsig, nann, length); + } + else + write_script(map, nsig, nann, length); + + for (i = 0; i < nsig + 4*nann; i++) + free(map[i]); + free(map); + wfdbquit(); + if (ai) free(ai); + if (si) free(si); + + exit(0); +} + +void map_sig(char *record, WFDB_Siginfo *si, int nsig, int **map, int length) +{ + if (si[0].nsamp == strtim("e") || si[0].nsamp != 0) { + /* fixed-layout record */ + int i, t; + for (i = 0; i < nsig; i++) + for (t = 0; t < length; t++) + map[i][t] = 1; + } + else { /* variable-layout record */ + char buf[256], *d, *p, *q, *r, *hfname, *shfname; + long m, m0, mf, spm = strtim("60"), t = 0, tf; + FILE *ifile, *sfile; + + p = wfdbfile("hea", record); + hfname = calloc(strlen(p) + 1, 1); + strcpy(hfname, p); + if ((ifile = fopen(hfname, "r")) == NULL) { + fprintf(stderr, "%s: can't open %s\n", pname, hfname); + free(hfname); + return; + } + for (d = p + strlen(p); d > p; d--) + if (*(d-1) == '/') { + *d = '\0'; + break; + } + shfname = calloc(strlen(hfname) + 16, 1); + strcpy(shfname, p); + d = shfname + strlen(shfname); /* d points to first char after '/' */ + fgets(buf, sizeof(buf), ifile); /* read and ignore first two lines */ + fgets(buf, sizeof(buf), ifile); + + m0 = 0; + while (fgets(buf, sizeof(buf), ifile)) {/* read a segment descriptor */ + char *tp; + + if (buf[0] == '~') { /* segment is null (all signals off) */ + t += atol(buf+2); + m0 = t/spm; + continue; + } + for (tp = buf+1; *tp != ' '; tp++) + ; + *tp = '\0'; + tf = t + atol(tp+1); + if ((mf = tf/spm) > length) mf = length; + sprintf(d, "%s.hea", buf); + if (sfile = fopen(shfname, "r")) {/* open the segment header file */ + char sbuf[256]; + int i; + + fgets(sbuf, sizeof(sbuf), sfile);/* read & ignore first line */ + + while ((p = fgets(sbuf, sizeof(sbuf), sfile)) && *sbuf != '#') { + /* signal description line */ + for (q = sbuf, i = 0; *q; q++) + if (*q == ' ' && ++i == 8) break; + q++; + *(q + strlen(q) - 2) = '\0'; + for (i = 0; i < nsig; i++) + if (strcmp(si[i].desc, q) == 0) + for (m = m0; m < mf; m++) + map[i][m] = 1; + } + fclose(sfile); + t = tf; + m0 = t/spm; + } + } + fclose(ifile); + free(shfname); + free(hfname); + } +} + +int *namax; + +void map_ann(char *record, WFDB_Anninfo *ai, int nann, int **map, int length) +{ + int i; + + namax = malloc(nann * sizeof(int)); + wfdbquiet(); /* suppress warnings if an annotator can't be opened */ + for (i = 0; i < nann; i++) { + char tstring[10]; + int minutes, *na, *nq, *ne, *nv; + WFDB_Annotation annot; + WFDB_Time end_of_epoch; + + na = &map[4*i][0]; + namax[i] = 1; + + /* Open the annotators one at a time, and do not quit if any fail + to open. */ + if (annopen(record, &ai[i], 1) < 0) continue; + minutes = end_of_epoch = 0; + iannsettime(1L); + while (getann(0, &annot) >= 0) { + if (annot.time > end_of_epoch) { + if (*na > namax[i]) namax[i] = *na; + minutes = annot.time/spm; + na = &map[4*i][minutes]; + nq = &map[4*i + 1][minutes]; + ne = &map[4*i + 2][minutes]; + nv = &map[4*i + 3][minutes]; + if (++minutes > length) + break; /* stop at the end of the record */ + sprintf(tstring, "%d", 60*minutes); + end_of_epoch = strtim(tstring); + } + switch (annot.anntyp) { + case PVC: + case FUSION: + case VESC: + case RONT: + case FLWAV: (*nv)++; /* fall through, no break! */ + case APC: + case ABERR: + case NPC: + case SVPB: + case NESC: + case AESC: + case SVESC: (*ne)++; /* fall through, no break! */ + case NORMAL: + case LBBB: + case RBBB: + case PACE: + case UNKNOWN: + case BBB: + case LEARN: + case PFUS: (*nq)++; /* fall through, no break! */ + default: (*na)++; break; + } + } + if (*na > namax[i]) namax[i] = *na; + } + return; +} + +void write_map(int **map, int nsig, int nann, int length) +{ + int i, imax = nsig + 4*nann - 1, t; + + for (t = 0; t < length; t++) { + if (mflag == 0) printf("%d\t0\t", t); /* x0, y0 for histogram plots */ + printf("%d\t", t+1); + for (i = 0; i < imax; i++) + printf("%d\t", map[i][t]); + printf("%d\n", map[i][t]); + } +} + +#include "signal-colors.h" + +char *color(char *type) +{ + int i; + + for (i = 0; ctab[i].name != NULL; i++) + if (strcmp(type, ctab[i].name) == 0) return (ctab[i].color); + return ("grey"); +} + +void write_script(int **map, int nsig, int nann, int length) +{ + char *p, tstring[2][30]; + double ylow, ymax; + int i, j; + WFDB_Time tf = strtim("e"); + + strcpy(tstring[0], timstr(0)); + for (p = tstring[0]; *p == ' '; p++) + ; + if (tf) strcpy(tstring[1], timstr(-tf)); + else sprintf(tstring[1], "%d:%02d:00", length/60, length%60); + + ymax = 2*(nsig + 2*nann + 1); + ylow = 0.01 * ymax; + + printf("#! /bin/sh\n" + "cat >map.txt <= 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); +} + +static char *help_strings[] = { + "usage: %s -r RECORD -a ANNOTATOR [OPTIONS ...]\n", + "where RECORD and ANNOTATOR specify the input, and OPTIONS may include:", + " -h print this usage summary", +NULL +}; + +void help() +{ + int i; + + (void)fprintf(stderr, help_strings[0], pname); + for (i = 1; help_strings[i] != NULL; i++) + (void)fprintf(stderr, "%s\n", help_strings[i]); +} diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/xml/annxml.c wfdb-10.5.7/xml/annxml.c --- wfdb-10.5.6/xml/annxml.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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 +#include +#include + +#define WFDBXMLPROLOG "\n" \ + "\n" \ + "\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, "<"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '&') fprintf(ofile, "&"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '"') fprintf(ofile, """); + else if (*p == '\'') fprintf(ofile, "'"); + else fprintf(ofile, "%c", *p); + p++; + } + fprintf(ofile, "", 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, "\n", annotator, record); + + process_start(mstimstr(0)); + + (void)fprintf(ofile, "%.12g\n", + sfreq); + + while (getann(0, &annot) >= 0) + process_annotation(); + + process_anntab(); + + fprintf(ofile, "\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, "\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, "%d\n", year); + } + if (month > 0) fprintf(ofile, "%d\n", month); + if (day > 0) fprintf(ofile, "%d\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, "%g\n", hour); + if (minute >= 0) fprintf(ofile, "%g\n", minute); + if (second >= 0) fprintf(ofile, "%g\n", second); + fprintf(ofile, "\n"); + } +} + +static long anncount[ACMAX+1]; + +void process_anntab() +{ + int i; + + fprintf(ofile, ""); + for (i = 0; i <= ACMAX; i++) { + if (anncount[i]) { + fprintf(ofile, "%d", i); + output_xml(ofile, "anncode", annstr(i)); + output_xml(ofile, "anndescription", anndesc(i)); + fprintf(ofile, "%ld\n", anncount[i]); + } + } + fprintf(ofile, ""); +} + +void process_annotation() +{ + fprintf(ofile, "%s", + annot.time, annstr(annot.anntyp)); + if (annot.subtyp) fprintf(ofile, "%d", annot.subtyp); + if (annot.chan) fprintf(ofile, "%d", annot.chan); + if (annot.num) fprintf(ofile, "%d", annot.num); + if (annot.aux) output_xml(ofile, "aux", annot.aux+1); + fprintf(ofile, "\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.6/xml/heaxml.c wfdb-10.5.7/xml/heaxml.c --- wfdb-10.5.6/xml/heaxml.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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 +#include + +#define WFDBXMLPROLOG "\n" \ + "\n" \ + "\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, "<"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '&') fprintf(ofile, "&"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '"') fprintf(ofile, """); + else if (*p == '\'') fprintf(ofile, "'"); + else fprintf(ofile, "%c", *p); + p++; + } + fprintf(ofile, "\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, "\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\n\n", i); + process_start(nextts); + fprintf(ofile, "%ld\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\n", sp[i].recname); + setgvmode(WFDB_LOWRES); + t = strtim("e"); + strcpy(nextts, mstimstr(-t)); + process_record(); + } + fprintf(ofile, "\n"); + } + } + fprintf(ofile, "\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\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, "%g\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, "%s\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, "%g\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, "%s\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, "\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, "\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, "%d\n", year); + } + if (month > 0) fprintf(ofile, "%d\n", month); + if (day > 0) fprintf(ofile, "%d\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, "%g\n", hour); + if (minute >= 0) fprintf(ofile, "%g\n", minute); + if (second >= 0) fprintf(ofile, "%g\n", second); + fprintf(ofile, "\n"); + } +} + +int process_record(void) +{ + char *p; + double cfreq, sfreq; + int i, skew; + FILE *ifile; + + /* Process and output info. */ + process_info(); + + /* Output the section if the starting time is specified. */ + process_start(mstimstr(0L)); + + if (nsig < 1 || t > 0L) + (void)fprintf(ofile, "%ld\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, "%ld\n", t); + } + + (void)fprintf(ofile, "%.12g\n", + sfreq = sampfreq(NULL)); + (void)fprintf(ofile, "%d\n", nsig); + cfreq = getcfreq(); + if (sfreq != cfreq) { + double basecount = getbasecount(); + fprintf(ofile, " 0.0) fprintf(ofile, " basecount=\"%g\"", basecount); + fprintf(ofile, ">%.12g\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, "\n\n"); + fprintf(ofile, "\n%s\n\n", + s[i].fmt ? s[i].fname : "[none]"); + if (plen = wfdbgetstart(i)) + fprintf(ofile, "%ld\n", plen); + } + fprintf(ofile, "\n%s\n", + s[i].desc); + fprintf(ofile, "%.12g\n", + s[i].gain == 0. ? WFDB_DEFGAIN : s[i].gain); + fprintf(ofile, "%s\n", + s[i].units ? s[i].units : "mV"); + fprintf(ofile, "%d\n", s[i].initval); + fprintf(ofile, "%d\n", s[i].fmt); + if (s[i].spf > 1) + fprintf(ofile,"%d\n", s[i].spf); + if (skew = wfdbgetskew(i)) + fprintf(ofile, "%ld\n", skew); + fprintf(ofile, "%d\n", s[i].bsize); + fprintf(ofile, "%d\n", s[i].adcres); + fprintf(ofile, "%d\n", s[i].adczero); + fprintf(ofile, "%d\n", s[i].baseline); + fprintf(ofile, "%d\n", s[i].cksum); + fprintf(ofile, "\n\n"); + } + } + if (nsig > 0) fprintf(ofile, "\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.6/xml/Makefile.top wfdb-10.5.7/xml/Makefile.top --- wfdb-10.5.6/xml/Makefile.top 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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.6/xml/Makefile.tpl wfdb-10.5.7/xml/Makefile.tpl --- wfdb-10.5.6/xml/Makefile.tpl 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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.6/xml/README wfdb-10.5.7/xml/README --- wfdb-10.5.6/xml/README 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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.6/xml/wfdb.dtd wfdb-10.5.7/xml/wfdb.dtd --- wfdb-10.5.6/xml/wfdb.dtd 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/xml/wfdb.dtd 2010-10-06 21:58:53.000000000 -0400 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/xml/wfdb.xsl wfdb-10.5.7/xml/wfdb.xsl --- wfdb-10.5.6/xml/wfdb.xsl 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/xml/wfdb.xsl 2010-07-01 21:51:51.000000000 -0400 @@ -0,0 +1,231 @@ + + + + + + + + + + + + +Description of record <xsl:value-of select="@name"/> + +

+Description of record +

+

General information

+

+ + +Sampling frequency: + +samples per signal per second
+ +Counter frequency: + +counts per second (starting from +)
+
+Signals:

+ + + + + + + + + + + +Record <xsl:value-of select="@record"/>, +Annotator <xsl:value-of select="@annotator"/> + +

+Record , +Annotator +

+ +

+ + +Time resolution: +ticks per second
+Annotations: + + + + + + + + +
CountType (Description)
Total
+

+
+ + + + + + + + + + + +
TimeTypeSubChanNumNote
+ + + +
+ + + + + + +() + + + + + + + + + + + + + + + + + +

Notes

+

+Age: + + () + years + +
+Sex: + +male +female + + +
+Diagnoses: + +; + + +
+Medications: + +; + + () + +
+ +
+
+

+
+ + +Start: + +// at :: +
+
+ + +Duration: +:: +( sample intervals)
+
+ + + + +

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.

+
+ +

Signal file:

+ +Preamble: bytes
+
+
+
+ + +

Signal :

+

+Gain: +adu/
+Initial value: adu
+ +Storage format:
+
+ +Samples per frame:
+
+ +Skew: sample intervals
+
+ADC resolution: bits
+ +ADC zero: adu
+
+ +Baseline: adu
+
+ +Block size:
+
+ +Checksum: + +

+
+
+ + +

Segment :

+

+ + + + +Sampling frequency: + +samples per signal per second
+ +Counter frequency: + +counts per second (starting from +)
+
+Signals: + +
+ + + + +
+

+ + +
+ + diff -Naur --exclude Makefile --exclude info wfdb-10.5.6/xml/xmlann.c wfdb-10.5.7/xml/xmlann.c --- wfdb-10.5.6/xml/xmlann.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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 , +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 +#include +#include +#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.6/xml/xmlhea.c wfdb-10.5.7/xml/xmlhea.c --- wfdb-10.5.6/xml/xmlhea.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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 , +as specified by 'wfdb.dtd'; see '100s.hea.xml' for a sample in this format. +The inverse transformation can be performed by 'heaxml'. +*/ + +#include +#include +#include +#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, " : %s : %s", age, sex); + if (dx == 0) sprintf(info + strlen(info), " : ?"); + if (dx == 1 && strlen(diagnosis[0]) < 78 - (strlen(info) + 14)) { + sprintf(info + strlen(info), " : %s", diagnosis[0]); + dx = 0; + } + if (rx == 0) sprintf(info + strlen(info), " : ?"); + if (rx == 1 && strlen(medication[0]) < 78 - (strlen(info) + 16)) { + sprintf(info + strlen(info), " : %s", medication[0]); + rx = 0; + } + putinfo(info); + if (dx) + for (i = 0; i < dx; i++) + writeinfo(": ", diagnosis[i]); + if (rx) + for (i = 0; i < rx; i++) + writeinfo(": ", 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.6/xml/xmlproc.h wfdb-10.5.7/xml/xmlproc.h --- wfdb-10.5.6/xml/xmlproc.h 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.7/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 + +/* 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 . */ +#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"); +}