/* file: annxml.c G. Moody 28 June 2010 Last revised: 27 April 2020 ------------------------------------------------------------------------------- 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, see . You may contact the author by e-mail (wfdb@physionet.org) 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); }