/* file: rdacexp.c B. Lin and G. Moody August 1994 Last revised: 22 February 1996 Copyright (C) Massachusetts Institute of Technology 1996. All rights reserved. This program is intended as a simple example to show how to read data obtained from the Access component of the MIMIC database without using Microsoft Access itself. It can be compiled and run under MS-DOS or UNIX using any ANSI/ISO or K&R C compiler; it should be trivial to port it to other environments for which a C compiler is available. The program can be used interactively as is, or you may customize it for use in your own applications. `rdacexp' reads text files exported by Microsoft Access from the `mimic.mdb' database. This program does not read the `mimic.mdb' file directly (unfortunately this is not possible, since the license agreement for Microsoft Access forbids reverse-engineering to decipher the format of `mdb' files). To generate a set of files of the required types, open `mimic.mdb' using Microsoft Access, choose a subject (i.e., a patient, identified by a MIMIC record number), and use the `Export data' button on the Data View menu of the `mimic.mdb' database. The generated files may be read directly by this program if you specify `ex-' as the patient name, but in general they should be renamed by replacing `ex-' in the filenames with the MIMIC record number for the chosen subject. The CD-ROM already contains files generated and renamed in this way for the records in the `mimicdb' directory, so you do not need Microsoft Access to get started. */ #include #include #include /* Assorted standard C library constants that may not be defined in the usual places. These definitions are needed under SunOS 4.1.x. */ #ifndef SEEK_SET #include #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif #include "menu.h" /* defines names of input files and menus used by this program */ /* Function prototypes. */ #ifdef __STDC__ /* for ANSI/ISO C compilers */ int get_column_number(char *buf, char *heading); int number_of_rows(char *buf); int number_of_columns(char *buf); char *head_ith_element(char *buf, int column_number); char *tail_ith_element(char *buf, int column_number, int number_of_columns); char *head_element(char *buf, char *heading); char *tail_element(char *buf, char *heading); char *head(char *buf, char *heading, int row_number); char *tail(char *buf, char *heading, int row_number); int intro_menu(void); int menu(struct menu_entry *menu, int menu_len); void read_data_file(char *subject); void prompt(void); #else /* for K&R C compilers */ int get_column_number(), number_of_rows(), number_of_columns(), intro_menu(), menu(); char *head_ith_element(), *tail_ith_element(), *head_element(), *tail_element(), *head(), *tail(); void read_data_file(), prompt(); #endif /* If running under MS-DOS, assume that the user has no scrollback, and prompt for input before allowing data to scroll off-screen. Otherwise, assume that the user is running in an xterm or equivalent and can scroll back as needed. Note that not all MS-DOS C compilers define MSDOS or __MSDOS__, so you may need to do so manually when compiling this program. */ void prompt() { #if (defined(MSDOS) || defined(__MSDOS__)) char junk[3]; printf("Press for more: "); fgets(junk, 3, stdin); #else ; #endif } /* get_column_number returns the column number of the heading specified in str (the first column is column number 0), by searching in the first line of buf for an exact match to str. This function does not modify buf or str. buf must be in the format of: [tab]...[newline] [tab]...[newline] ... */ int get_column_number(buf, str) char *buf; char *str; { char *tmp, *ptr_to_heading; int count = 0; ptr_to_heading = strstr(buf, str); if (ptr_to_heading == NULL) { printf("Error, badly formatted file\n"); exit(EXIT_FAILURE); } tmp = buf; for ( ; tmp < ptr_to_heading-1; count++) { /* The -1 is to compensate for the quotation mark */ tmp = strchr(tmp, '\t'); tmp++; } return(count); } /* number_of_columns() and number_of_rows() return what their names indicate. Neither modifies buf. */ int number_of_columns(buf) char *buf; { return(get_column_number(buf, "\n")); } int number_of_rows(buf) char *buf; { char *tmp; int heading_count, count=6; heading_count = number_of_columns(buf); tmp = strchr(buf, '\n'); for (count = 1; tmp != NULL; count++) { if (count%heading_count == 0) count++; tmp = strchr(tmp, '\t'); if (tmp == NULL) break; tmp++; } return(count/heading_count); } /* head_ith_element() and tail_ith_element() return pointers to the beginning and end of the indexed heading value obtained by get_column_number(). They return NULL if index is < 0. They do not modify buf or index. */ char *head_ith_element(buf, index) char *buf; int index; { char *tmp; int count; if (index < 0) return(NULL); tmp = buf; for (count = 0; count < index; count++) { tmp = strchr(tmp, '\t'); tmp++; } return(tmp); } char *tail_ith_element(buf, index, number_columns) char *buf; int index; int number_columns; { char *ptr; char *tmp; ptr = head_ith_element(buf, index); if (index == number_columns-1) { tmp = strchr(ptr, '\n'); if (tmp == NULL) { tmp = strchr(ptr, EOF); } } else tmp = strchr(ptr, '\t'); return(tmp); } char *head_element(buf, heading) char *buf; char *heading; { int index; char *beginning_of_data; beginning_of_data = strchr(buf, '\n'); index = get_column_number(buf, heading); return(head_ith_element(beginning_of_data, index)); } char *tail_element(buf, heading) char *buf; char *heading; { char *ptr; char *tmp; int index; ptr = head_element(buf, heading); index = get_column_number(buf, heading); if (index == number_of_columns(buf)-1) { tmp = strchr(ptr, '\n'); if (tmp == NULL) tmp = strchr(ptr, EOF); } else tmp = strchr(ptr, '\t'); return(tmp); } /* head() and tail() require valid heading names and record numbers; they return pointers to the beginning and end of the requested element in buf, without modifying the contents of buf or heading. */ char *head(buf, heading, record_number) char *buf; char *heading; int record_number; { char *tmp; int rows, count, index, number_columns; rows = number_of_rows(buf); index = get_column_number(buf, heading); number_columns = number_of_columns(buf); tmp = buf; if (record_number > rows || record_number <= 0) return(NULL); for (count=record_number; count != 0; count--) { tmp = tail_ith_element(tmp, number_columns-1, number_columns); tmp++; } return(head_ith_element(tmp, index)); } char *tail(buf, heading, record_number) char *buf; char *heading; int record_number; { char *ptr, *tmp; int index; ptr = head(buf, heading, record_number); index = get_column_number(buf, heading); if (index == number_of_columns(buf)-1) { tmp = strchr(ptr, '\n'); if (tmp == NULL) tmp = strchr(ptr, EOF); } else tmp = strchr(ptr, '\t'); return(tmp); } /* intro_menu() presents the top-level menu, and returns the user's choice. To avoid accidental exits because of key bouncing, it does not have a defined default choice. */ int intro_menu() { char buf[10]; int i; printf("\n 1. Find Subject Record\n"); printf(" 2. Exit\n"); i = 0; /* default choice: do nothing */ do { printf("Please enter 1 or 2: "); fgets(buf, 10, stdin); sscanf(buf, "%d", &i); } while (i != 1 && i != 2); return (i); } /* menu() is called to present any of the other menus. The caller supplies a pointer to the menu structure (defined in menu.h), and the number of choices on the menu. menu() returns the user's choice (0 if the user chooses to return to the previous menu, or if the user simply presses Enter). */ int menu(menup, n) struct menu_entry *menup; int n; { char buf[10]; int i; printf(" 0. Previous Menu\n"); for (i = 0; i < n; i++) printf("%2d. %s\n", menup[i].index_no, menup[i].menu_string); i = 0; /* default choice: return to previous menu */ do { printf("Please enter a number between 0 and %d: ", n); fgets(buf, 10, stdin); sscanf(buf, "%d", &i); } while (i < 0 || i > n); return (i); } /* read_data_file() opens the files for the subject (patient) chosen by the user, loads them into buf as needed, and extracts the chosen data from them. */ void read_data_file(subject) char *subject; { FILE *ifile; char *buf, c; int index, main_choice, section_choice, time1, time2; static char answer[20], filename[128]; char *data, *data2, *data3, *heading, *heading2, *ptr, *tmp, *section; int nr, i; long filesize; /* Find the file to be read. */ while (1) { section_choice = menu(section_list, SECLISTLEN); if (section_choice == 0) break; for (i = 0, section = NULL; i < SECLISTLEN; i++) if (section_list[i].index_no == section_choice) { section = section_list[i].name; break; } if (section == NULL) continue; /* Generate the file name from the subject id and section name. */ sprintf(filename, "%s%s", subject, section); /* Open the file. */ if ((ifile = fopen(filename, "r")) == NULL) { fprintf(stderr, "Can't open %s\n", filename); continue; } /* Get the file's size and allocate a buffer in memory for it. */ (void)fseek(ifile, 0L, SEEK_END); filesize = ftell(ifile); (void)fseek(ifile, 0L, SEEK_SET); tmp = buf = (char *)malloc((filesize+1)*sizeof(char)); /* Read the file into the buffer. */ while ((c=(char)fgetc(ifile)) != EOF) { *tmp = c; tmp++; } *tmp = '\0'; fclose(ifile); /* Get the secondary menu choice. */ while (1){ if (section_choice == MHISTORY) { printf("History\n"); main_choice = menu(history_list, HISLISTLEN); if (main_choice == 0) break; for (i = 0, heading = NULL; i < HISLISTLEN; i++) if (history_list[i].index_no == main_choice) { heading = history_list[i].name; break; } if (heading == NULL) continue; } else if (section_choice == MSYSTEMR) { printf("System Review\n"); main_choice = menu(sys_list, SYSLISTLEN); if (main_choice == 0) break; for (i = 0, heading = NULL; i < SYSLISTLEN; i++) if (sys_list[i].index_no == main_choice) { heading = sys_list[i].name; break; } if (heading == NULL) continue; } else if (section_choice == MPHYSICA) { printf("Physical Examination\n"); main_choice = menu(phys_list, PHYLISTLEN); if (main_choice == 0) break; for (i = 0, heading = NULL; i < PHYLISTLEN; i++) if (phys_list[i].index_no == main_choice) { heading = phys_list[i].name; break; } if (heading == NULL) continue; } else if (section_choice == MPROBLEM) { nr = number_of_rows(buf); printf("\nProblem Number\tProblem\n"); for (i = 1; i <= nr; i++) { ptr = head(buf, "Problem number", i); tmp = tail(buf, "Problem\"", i); data = (char *) malloc((tmp-ptr+1)*sizeof(char)); strncpy(data, ptr, (size_t)(tmp-ptr)); *(data+(tmp-ptr)) = '\0'; printf("%s\n", data); prompt(); free(data); } break; } else if (section_choice == MPROGRES) { heading = "Progress Note"; printf("Please enter range of times (in hours from 0-24)\n"); printf("Initial time: "); fgets(answer, 20, stdin); sscanf(answer, "%d", &time1); printf("Final time: "); fgets(answer, 20, stdin); sscanf(answer, "%d", &time2); nr = number_of_rows(buf); for(i = 1; i <= nr; i++) { ptr = head(buf, "date", i); tmp = tail(buf, "date", i); data = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data, ptr, (size_t)(tmp-ptr)); *(data+(tmp-ptr))='\0'; if (atoi(data+11)>=time1 && atoi(data+11)<=time2) { ptr = head(buf, heading, i); tmp = tail(buf, heading, i); data2 = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data2, ptr, (size_t)(tmp-ptr)); *(data2+(tmp-ptr))='\0'; printf("\n------------------------------------\n"); printf("time\tProgress Note:\n"); printf("%s\n%s\n", data, data2); printf("\n------------------------------------\n"); prompt(); free(data2); } free(data); } break; } else if (section_choice == MSOAPNOT) { heading = "Subjective"; heading2 = "Action Taken"; printf("Please enter the problem number: "); fgets(answer, 20, stdin); sscanf(answer, "%d", &time1); nr = number_of_rows(buf); for (i = 1; i <= nr; i++) { ptr = head(buf, "Problem Number", i); tmp = tail(buf, "Problem Number", i); data = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data, ptr, (size_t)(tmp-ptr)); *(data+(tmp-ptr)) = '\0'; if (atoi(data) == time1) { ptr = head(buf, heading, i); tmp = tail(buf, heading2, i); data2 = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data2, ptr, (size_t)(tmp-ptr)); *(data2+(tmp-ptr)) = '\0'; printf("\n------------------------------------\n"); printf("Problem Number\tSOAP Note:\n"); printf("%s\n%s\n", data, data2); printf("\n------------------------------------\n"); prompt(); free(data2); } free(data); } break; } else if (section_choice == MCARDIOV) { printf("Cardiovascular\n"); main_choice = menu(cardio_list, CARLISTLEN); if (main_choice == 0) break; for (i = 0, heading = NULL; i < CARLISTLEN; i++) if (cardio_list[i].index_no == main_choice) { heading = cardio_list[i].name; break; } if (heading == NULL) continue; if (main_choice == CBLOODP) { heading = "Systolic"; heading2 = "Diastolic"; printf("Please enter range of times (in hours from 0-24)\n"); printf("Initial time: "); fgets(answer, 20, stdin); sscanf(answer, "%d", &time1); printf("Final time: "); fgets(answer, 20, stdin); sscanf(answer, "%d", &time2); nr = number_of_rows(buf); for (i = 1; i <= nr; i++) { ptr = head(buf, "time", i); tmp = tail(buf, "time", i); data =(char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data, ptr, (size_t)(tmp-ptr)); *(data+(tmp-ptr)) = '\0'; if (atoi(data+11) >= time1 && atoi(data+11) <= time2) { ptr = head(buf, heading, i); tmp = tail(buf, heading, i); data2 = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data2, ptr, (size_t)(tmp-ptr)); *(data2+(tmp-ptr)) = '\0'; ptr = head(buf, heading2, i); tmp = tail(buf, heading2, i); data3 = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data3, ptr, (size_t)(tmp-ptr)); *(data3+(tmp-ptr)) = '\0'; printf("\n------------------------------------\n"); printf("time\t\tsystolic\tdiastolic\n"); printf("%s\t%s\t%s\n", data, data2, data3); printf("\n------------------------------------\n"); prompt(); free(data2); free(data3); } free(data); } } if (main_choice == 2) break; else continue; } if (section_choice == MHISTORY || section_choice == MSYSTEMR || section_choice == MPHYSICA) { ptr = head(buf, heading, 1); tmp = tail(buf, heading, 1); data = (char *)malloc((tmp-ptr+1)*sizeof(char)); strncpy(data, ptr, (size_t)(tmp-ptr)); *(data+(tmp-ptr)) = '\0'; printf("\n-------------------------------------\n"); printf("%s-\n%s\n", heading, data); printf("\n-------------------------------------\n"); prompt(); free(data); } } } } /* main() presents the top-level menu, gets the user's choice of subject (patient), and invokes read_data_file() to retrieve and present the chosen data interactively. */ void main() { static char subject[20]; while (1) { /* Choose a subject. */ switch (intro_menu()) { case 1: printf("\nEnter Subject Number: "); fgets(subject, 20, stdin); subject[strlen(subject)-1] = '\0'; break; case 2: exit(EXIT_SUCCESS); break; } /* Read a file and report its contents. */ read_data_file(subject); } }