/* file: annsort.c G. Moody 18 May 1995 Sort annotations by interval length using user-specified criteria Copyright (C) Massachusetts Institute of Technology 1995. All rights reserved. */ #include #ifdef __STDC__ #include #else extern void exit(); #endif #include #include /* SORT should be a command to sort a file numerically by the third column, writing the output to the standard output. The default given here appropriate for most UNIX systems (check to be sure your `sort' is in `/usr/bin'). The string `%s' is replaced by the pathname of the file before SORT is executed. */ #define SORT "/usr/bin/sort -n +2 %s" /* TFTEMPLATE is a string passed to mktemp, used to generate a unique filename for a temporary file (written by this program and passed to SORT as shown above). Make sure that `/tmp' (or whatever directory you choose) is writeable, and has sufficient space for the temporary file (its size is the same as that of the output file, i.e., about 25 bytes per interval). */ #define TFTEMPLATE "/tmp/asXXXXXX" char *pname; main(argc, argv) int argc; char *argv[]; { char *record = NULL, *prog_name(); double sps; FILE *tfile; int i, j; void help(); static DB_Anninfo ai; static DB_Annotation annot; static DB_Time from, to, t0; static long interval; static int istate; static char flag[3][ACMAX]; static char a0, tfname[14], cmd[50]; /* Read and interpret command-line arguments. */ pname = prog_name(argv[0]); for (i = 1; i < argc; i++) { if (*argv[i] == '-') switch (*(argv[i]+1)) { case 'a': /* annotator follows */ if (++i >= argc) { (void)fprintf(stderr, "%s: annotator must follow -a\n", pname); exit(1); } ai.name = argv[i]; ai.stat = READ; break; case 'b': /* annotation mnemonic(s) follow (interval beginners) */ if (++i >= argc || !isann(j = strann(argv[i]))) { (void)fprintf(stderr, "%s: annotation mnemonic(s) must follow -b\n", pname); exit(1); } flag[0][j] = 1; /* The code above not only checks that there is a mnemonic where there should be one, but also allows for the possibility that there might be a (user-defined) mnemonic beginning with `-'. The following lines pick up any other mnemonics, but assume that arguments beginning with `-' are options, not mnemonics. */ while (++i < argc && argv[i][0] != '-') if (isann(j = strann(argv[i]))) flag[0][j] = 1; if (i == argc || argv[i][0] == '-') i--; flag[0][0] = 1; /* indicate that user has set flag[0][] */ break; case 'e': /* annotation mnemonic(s) follow (interval enders) */ if (++i >= argc || !isann(j = strann(argv[i]))) { (void)fprintf(stderr, "%s: annotation mnemonic(s) must follow -e\n", pname); exit(1); } flag[2][j] = 1; while (++i < argc && argv[i][0] != '-') if (isann(j = strann(argv[i]))) flag[2][j] = 1; if (i == argc || argv[i][0] == '-') i--; flag[2][0] = 1; /* indicate that user has set flag[2][] */ break; case 'f': /* starting time follows */ if (++i >= argc) { (void)fprintf(stderr, "%s: starting time must follow -f\n", pname); exit(1); } from = i; /* to be converted to sample intervals below */ break; case 'h': /* print usage summary and quit */ help(); exit(0); break; case 'i': /* annotation mnemonic(s) follow (types to ignore) */ if (++i >= argc || !isann(j = strann(argv[i]))) { (void)fprintf(stderr, "%s: annotation mnemonic(s) must follow -i\n", pname); exit(1); } flag[1][j] = 1; while (++i < argc && argv[i][0] != '-') if (isann(j = strann(argv[i]))) flag[1][j] = 1; if (i == argc || argv[i][0] == '-') i--; flag[1][0] = 1; /* indicate that user has set flag[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; case 't': /* ending time follows */ if (++i >= argc) { (void)fprintf(stderr, "%s: end time must follow -t\n", pname); exit(1); } to = 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 || ai.name == NULL) { help(); exit(1); } /* Initialize the interval definition if the user did not do so on the command line. flag[0][i] is TRUE (1) if an annotation with anntyp = i can begin an interval of interest. flag[1][i] is TRUE if such an annotation can occur in the middle of an interval of interest (i.e., if such an annotation is to be ignored). flag[2][i] is TRUE if such an annotation can end an interval of interest. By default, intervals may begin and end with any QRS annotation, and all non-QRS annotations are ignored. */ for (i = 0; i < ACMAX; i++) { if (!flag[0][0]) flag[0][i] = isqrs(i); if (!flag[1][0]) flag[1][i] = !flag[0][i]; if (!flag[2][0]) flag[2][i] = flag[0][i]; } if ((sps = sampfreq(record)) < 0.) (void)setsampfreq(sps = DEFFREQ); /* Open the annotation file, and skip ahead if necessary. */ if (annopen(record, &ai, 1) < 0) exit(2); if (from && iannsettime(strtim(argv[(int)from])) < 0) exit(2); if (to) to = strtim(argv[(int)to]); /* Generate a temporary file name for use below. */ (void)strcpy(tfname, TFTEMPLATE); (void)mktemp(tfname); if ((tfile = fopen(tfname, "w")) == NULL) { fprintf(stderr, "%s: can't create temporary file %s\n", pname, tfname); exit(3); } /* Find the next interval of interest, and add it to the interval list. */ while (getann(0, &annot) == 0 && (to == 0L || annot.time <= to)) { if (!isann(annot.anntyp)) continue; /* ignore non-annotations */ switch (istate) { case 0: /* looking for the beginning of an interval */ if (flag[0][annot.anntyp]) { /* this might be what we want */ a0 = annot.anntyp; t0 = annot.time; istate = 1; } break; case 1: /* looking for the end of an interval */ if (flag[2][annot.anntyp]) { /* we found one! */ /* add it to the interval list */ fprintf(tfile, " %s %s-%s %.3lf\n", mstimstr((annot.time+t0)/2), annstr(a0), annstr(annot.anntyp), (annot.time-t0)/sps); } if (flag[2][annot.anntyp] || !flag[1][annot.anntyp]) { if (flag[0][annot.anntyp]) { /* this might be the beginning of another interval of interest; remain in state 1 */ a0 = annot.anntyp; t0 = annot.time; } else /* otherwise resume looking for another beginning */ istate = 0; } break; } } (void)fclose(tfile); /* Sort the interval list. */ sprintf(cmd, SORT, tfname); i = system(cmd); unlink(tfname); exit(i); } 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); } static char *help_strings[] = { "usage: %s -r RECORD -a ANNOTATOR [OPTIONS ...]\n", "where RECORD and ANNOTATOR specify the input, and OPTIONS may include:", " -b TYPE [TYPE ...] begin intervals with annotations of specified TYPEs only", " -e TYPE [TYPE ...] end intervals with annotations of specified TYPEs only", " -f TIME start at specified TIME", " -h print this usage summary", " -i TYPE [TYPE ...] ignore annotations of specified TYPEs only", " -t TIME stop at specified TIME", 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]); }