/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) loincmgr.cc (Gunther Schadow) 10/17/96"); #include "LOINC.h" #include #include #include #include #include #include #include #include #include bool lock = false; class LoincMgr : public Loinc { char **item; const int noitems = 25; static const char *name[noitems]; static const bool indexed[noitems]; private: /* * check first item for correct check digit */ void chkchkdg() { char *num = item[0]; while(! isdigit(*num)) num++; char *p = strchr(num, '-'); if(p != NULL) // there is a check digit { *p++ = '\0'; int x = atoi(num); // loinc number int gckd = *p - '0'; // given check digit int ackd = chkdg(x); // actual check digit sprintf(item[0], "%s-%c", item[0], ackd + '0'); // FIXME! if(gckd != ackd) { cerr << "check digit error corrected in " << item[0] << ", was -" << gckd << endl; } } else // there is no check digit { int x = atoi(num); // loinc number int ackd = chkdg(x); // actual check digit char *p = new char[strlen(item[0]) + 3]; strncpy(p, item[0], num - item[0]); num = p + ( num - item[0] ); delete [] item[0]; item[0] = p; sprintf(num, "%d-%c", x, ackd + '0'); cerr << "check digit added in " << item[0] << endl; } } public: LoincMgr(int mode = odbm::write) : Loinc(mode | ( mode & odbm::write ? odbm::fast : 0 )) { item = &loinc_num; } /****************************************************************** * READ RECORD */ result readrecord(istream &is) { int i = 0; while(is.ipfx(1) && i < noitems) { char del; if(is.peek() == '"') { del = '"'; is.get(); } else del = '\t'; is.gets(&item[i], del); if(item[i++] == NULL) break; if(del == '"' && is.peek() == '\t') is.get(); if(is.peek() == '\n') { is.get(); break; } } if(i == noitems) // all fields must have been read { // correct check digit chkchkdg(); // insert item odbm::insert(); // RELAT_NMS must be manually indexed char *p = relat_nms, *comp; while((comp = strsep(&p, ";")) != NULL) { if(comp[0] != '\0') // don't store empty terms odbm::index(&relat_nms, comp); } return SUCCESS; } else return FAIL - i; } /****************************************************************** * READ RECORD TO DELETE */ result excluderecord(istream &is) { if(is.ipfx(1)) { char del; if(is.peek() == '"') { del = '"'; is.get(); } else del = '\t'; is.gets(&item[0], del); if(item[0] == NULL) return SUCCESS; is.ignore(9999999, '\n'); if(is.peek() == '\n') is.get(); } // correct check digit chkchkdg(); // delete item odbm::remove(); return SUCCESS; } /* * fieldno( NAME ) returns the field number starting from 0 for the key. */ static int fieldno(const char *s) { for(int i = 0; i < noitems; i++) if(strcasecmp(s, name[i]) == 0) return i; int n = atoi(s) - 1; if(n < 0) { cerr << "illegal field id: `" << s << "'" << endl; exit(0); } else return n; } /****************************************************************** * QUERY */ enum style_t { s_ascii, s_dump, s_html, }; void query(const char *fld, const char *value, int argc, char *argv[], style_t style = s_ascii) { int field = fieldno(fld); if(field < 0 || field >= noitems) { cerr << "field number " << field + 1 << " out of range [" << 1 << ".." << noitems << "]" << endl; exit(1); } if(style == s_ascii) cout << "QUERY FOR " << name[field] << " == \"" << value << "\""; else if(style == s_html) { cout << "" << endl << "" << endl; for(int i = 0; i < noitems; i++) { cout << "" << endl; } } u_int hits = 0; if(field == 0) // query is for key { if(style != s_ascii) cout << endl << endl; if(fetch(value) == SUCCESS) { hits++; if(style == s_ascii) cout << "HIT " << hits << ":" << endl; if(style == s_ascii) { for(int i = 0; i < noitems; i++) cout.form(" %2d %12s = \"%s\"\n", i+1, name[i], item[i]); cout << endl; } else if(style == s_dump) { cout.form("\"%s\"", item[0]); for(int i = 1; i < noitems; i++) cout.form("\t\"%s\"", item[i]); cout << endl; } else if(style == s_html) { cout << "" << endl; for(int i = 0; i < noitems; i++) cout << "" << endl; cout << "" << endl; } } } else // query is for an index { // setup filter for(int i = 0; i < argc - 2; i+=3) { if(style == s_ascii) if(i == 0) cout << " WHERE "; else cout << " AND "; int field = fieldno(argv[i]); if(style == s_ascii) cout << name[field]; operator_t op; if(strcmp("==",argv[i+1]) == 0) { op = eq; if(style == s_ascii) cout << " == "; } else if(strcmp("!=",argv[i+1]) == 0) { op = ne; if(style == s_ascii) cout << " != "; } else { cerr << "operator must be one of `==' and `!='" << endl; exit(1); } char *value = argv[i+2]; if(style == s_ascii) cout << "\"" << value << "\""; if(field < 0 || field >= noitems) { cerr << "field number " << field + 1 << " out of range [" << 1 << ".." << noitems << "]" << endl; exit(1); } addfilt(&item[field], value, op); } if(style == s_ascii) cout << endl << endl; for(u_int cursor = 0; fetch(&item[field], value, &cursor) == SUCCESS; cursor++) { hits++; if(style == s_ascii) { cout << "HIT " << hits << " [" << cursor << "]:" << endl; for(int i = 0; i < noitems; i++) cout.form(" %2d %12s = \"%s\"\n", i+1, name[i], item[i]); cout << endl; } else if(style == s_dump) { cout.form("\"%s\"", item[0]); for(int i = 1; i < noitems; i++) cout.form("\t\"%s\"", item[i]); cout << endl; } else if(style == s_html) { cout << "" << endl; for(int i = 0; i < noitems; i++) cout << "" << endl; cout << "" << endl; } } } if(style == s_ascii) cout << "TOTAL: " << hits << " HITS" << endl; else if(style == s_html) { cout << "" << endl; for(int i = 0; i < noitems; i++) { const char *val = NULL; if(i == field) val = value; else if(thefilter() != NULL) { val = (*thefilter())[i]; } if(lock && ( i == 0)) { item[0] = (char *)val; chkchkdg(); val = item[0]; } cout << "" << endl; } cout << "
" << endl; if(indexed[i]) { cout << ""; } cout << name[i] << "
" << item[i] << "
" << item[i] << "
" << endl; } } /****************************************************************** * REMOVE */ void remove(const char *fld, const char *value, int argc, char *argv[]) { int field = fieldno(fld); if(field < 0 || field >= noitems) { cerr << "field number " << field + 1 << " out of range [" << 1 << ".." << noitems << "]" << endl; exit(1); } cout << "DELETE FOR " << name[field] << " == \"" << value << "\""; u_int hits = 0; if(field == 0) { cout << endl << endl; if(fetch(value) == SUCCESS) { hits++; cout << "HIT " << hits << ":" << endl; for(int i = 0; i < noitems; i++) cout.form(" %2d %12s = \"%s\"\n", i+1, name[i], item[i]); cout << endl; odbm::remove(); } } else { // setup filter for(int i = 0; i < argc - 2; i+=3) { if(i == 0) cout << " WHERE "; else cout << " AND "; int field = fieldno(argv[i]); cout << name[field]; operator_t op; if(strcmp("==",argv[i+1]) == 0) { op = eq; cout << " == "; } else if(strcmp("!=",argv[i+1]) == 0) { op = ne; cout << " != "; } else { cerr << "operator must be one of `==' and `!='" << endl; exit(1); } char *value = argv[i+2]; cout << "\"" << value << "\""; if(field < 0 || field >= noitems) { cerr << "field number " << field + 1 << " out of range [" << 1 << ".." << noitems << "]" << endl; exit(1); } addfilt(&item[field], value, op); } cout << endl << endl; for(u_int cursor = 0; fetch(&item[field], value, &cursor) == SUCCESS; cursor++) { hits++; cout << "DELETING " << hits << " [" << cursor << "]:" << endl; for(int i = 0; i < noitems; i++) cout.form(" %2d %12s = \"%s\"\n", i+1, name[i], item[i]); cout << endl; odbm::remove(); } } cout << "TOTAL: " << hits << " RECORDS DELETED" << endl; } /****************************************************************** * DUMP DATABASE */ void dump() { for(const char *k = firstkey(); k != NULL; k = nextkey()) { fetch(k); cout.form("\"%s\"", item[0]); for(int i = 1; i < noitems; i++) cout.form("\t\"%s\"", item[i]); cout << endl; } } }; const char *LoincMgr::name[noitems] = { "LOINC_NUM", "MEASURE", "PROPERTY", "TIMING", "SPECIMEN", "PRECISION", "METHOD", "RELAT_NMS", "CLASS", "SOURCE", "EUCLIDE_CD", "ASTM_CD", "IUPAC_CD", "DT_LAST_CH", "COMMENTS", "ANSWERLIST", "STATUS", "SCOPE", "SNOMED_CD", "VA_CD", "METPATH_CODE", "HCFA_CODE", "NORM_RANGE", "UNITS", "MAP_TO" }; const bool LoincMgr::indexed[noitems] = { TRUE, // "LOINC_NUM", TRUE, // "MEASURE", FALSE, // "PROPERTY", FALSE, // "TIMING", FALSE, // "SPECIMEN", FALSE, // "PRECISION", FALSE, // "METHOD", TRUE, // "RELAT_NMS", TRUE, // "CLASS", TRUE, // "SOURCE", TRUE, // "EUCLIDE_CD", TRUE, // "ASTM_CD", TRUE, // "IUPAC_CD", FALSE, // "DT_LAST_CH", FALSE, // "COMMENTS", FALSE, // "ANSWERLIST", FALSE, // "STATUS", FALSE, // "SCOPE", FALSE, // "SNOMED_CD", FALSE, // "VA_CD", TRUE, // "METPATH_CODE", FALSE, // "HCFA_CODE", FALSE, // "NORM_RANGE", FALSE, // "UNITS", FALSE, // "MAP_TO" }; static void usage(char *prog) { cerr << "usage: " << prog << " -[crux] files ..." << endl << " " << prog << " -[dq] field == value {field op value}" << endl << " " << prog << " -D [field == value {field op value}]" << endl << " " << prog << " -R" << endl; exit(1); } /* Options: * -c files ... create database * -r files ... update database (update) * -u files ... update database (insert) * -x files ... exclude from database * -q field == value {field op value} query * -d field == value {field op value} delete * -D [field == value {field op value}] dump database * -R reorganize database * -h produce HTML output with -q */ int main(int argc, char *argv[]) { log_init(argv[0], "./loinc.log", "test"); log_level(L_JUNK); char *prog = argv[0]; enum action_t { a_null, a_create, a_update, a_insert, a_query, a_exclude, a_delete, a_dump, a_reorg, } action = a_null; LoincMgr::style_t style = LoincMgr::s_ascii; GetOpt getopt(argc, argv, "cruxdqDRhl"); int option_char; while((option_char = getopt()) != EOF) switch(option_char) { case 'c': action = a_create; break; case 'h': style = LoincMgr::s_html; break; case 'l': lock = true; break; case 'r': action = a_update; break; case 'u': action = a_insert; break; case 'x': action = a_exclude; break; case 'q': action = a_query; break; case 'd': action = a_delete; break; case 'D': action = a_dump; break; case 'R': action = a_reorg; break; case '?': usage(prog); } argv += getopt.optind - 1; argc -= getopt.optind - 1; if(action == a_null) usage(prog); if(action == a_query) { if(argc < 4 || strcmp("==",argv[2]) != 0) usage(prog); LoincMgr loinc(odbm::read); loinc.query(argv[1], argv[3], argc - 4, &argv[4], style); return SUCCESS; } else if(action == a_delete) { if(argc < 4 || strcmp("==",argv[2]) != 0) usage(prog); LoincMgr loinc(odbm::write); loinc.remove(argv[1], argv[3], argc - 4, &argv[4]); return SUCCESS; } else if(action == a_dump) { LoincMgr loinc(odbm::read); if(argc > 1) if(argc < 4 || strcmp("==",argv[2]) != 0) usage(prog); else loinc.query(argv[1], argv[3], argc - 4, &argv[4], LoincMgr::s_dump); else loinc.dump(); return SUCCESS; } else if(action == a_update) { cerr << "rewrite is not yet implemented, sorry" << endl; exit(1); } else if(action == a_reorg) { cerr << "reorganize is not yet implemented, sorry" << endl; exit(1); } else // a_create or a_insert or a_exclude { int mode = odbm::write; if(action == a_create) mode |= odbm::newdb; LoincMgr loinc(mode); int lines_total = 0; while(--argc > 0) { char *file = *++argv; cout << file << endl; fstream fin(file, ios::in); int lineno = 1; while(fin.ipfx(1)) { result res; if(action == a_exclude) res = loinc.excluderecord(fin); else res = loinc.readrecord(fin); if(res == SUCCESS) ; else if(res == FAIL) break; else if(fin.eof()) { cerr << file << ":" << lineno << ": premature end of file at field " << FAIL - res << endl; ERROR("%s:%d: premature end of file at field %d", file, lineno, FAIL - res); } else if(! fin) { cerr << file << ":" << lineno << ": error in file at field " << FAIL - res << ": " << sys_errlist[errno] << endl; ERROS("%s:%d: error in file at field %d", file, lineno, FAIL - res); } else { cerr << file << ":" << lineno << ": premature end of record at field " << FAIL - res << endl; } if(lineno % 100 == 0) cout << lineno << " lines read" << endl; lines_total++; lineno++; } } cout << "total " << lines_total << " lines "; if(action == a_exclude) cout << "excluded" << endl; else cout << "read" << endl; return SUCCESS; } abort(); }