/* File: idacedb.c * Author: Richard Durbin (rd@sanger.ac.uk) * Copyright (C) Richard Durbin, Genome Research Limited, 2002 *------------------------------------------------------------------- * This file is part of the ACEDB genome database package * * Acedb 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 * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------ * Description: Acedb database implementation of the idcurate.h interface. Also provides tace/aceserver interface. Administrator interface is not supported, since the models need to be touched. * Exported functions: * Outstanding: idaFind() should only return Live things, but then need to write an internal name for duplicates that finds dead things. Or should we have separate functions for finding live only, or live+dead? * HISTORY: * Last edited: Apr 19 18:33 2002 (edgrif) * Created: Tue Jan 22 23:30:14 2002 (rd) * CVS info: $Id: idacedb.c,v 1.4 2002/04/19 17:52:11 edgrif Exp $ *------------------------------------------------------------------- */ /*************** models required by this file // in models.wrm for each supported domain ? Name_version UNIQUE Int Name Public_name UNIQUE ?_name XREF Public_name // not directly editable _name [UNIQUE] ?_name XREF _name ... History Version_change Int UNIQUE DateType UNIQUE Text #ID_history_action // Text above is the curator user name Merged_into UNIQUE ?Gene XREF Acquires_merge Acquires_merge ?Gene XREF Merged_into Split_from UNIQUE ?Gene XREF Split_into Split_into ?Gene XREF Split_from_parent Live // remove live to make gene "dead" ?_name Public_name ? XREF Public_name _name ? XREF _name ... // and in whooks/sysclass.c #ID_history_action Event Created Killed Resurrected Merged_into UNIQUE Text Acquires_merge UNIQUE Text Split_from UNIQUE Text Split_into UNIQUE Text Add_name UNIQUE Text UNIQUE Text UNIQUE Text Remove_name UNIQUE Text UNIQUE Text UNIQUE Text Imported Text // the three Texts are // Merged_into, Acquires_merge, Split_into, Split_from use // Created, Split_from, Add_name, Remove_name use // if is unique then Add_name replaces, as normal in acedb ?Class ... ... ID_local ID_template UNIQUE Text ID_nametype Text #ID_nametype_qualifiers #ID_nametype_qualifiers Is_unique Is_primary Users_with_write_access Text ****************************/ #include #include #include #include #include #include #include #include #include #include #include /* for sessionUserSession2User() */ #include typedef struct { int objectClass ; int nameClass ; char *template ; Array types ; /* of NameType */ DICT *typeDict ; } DomainType ; struct IdConnectionStruct { STORE_HANDLE handle ; /* internal allocations on this */ char *user ; IdError error ; DomainType *domain ; } ; typedef struct { char* text ; BOOL isUnique ; BOOL isPrimary ; KEY tag ; } NameType ; static int classClass = 0 ; static void idaCurateInitialise (void) ; static void idaFinalise (void* ix) ; static KEY newId (IdCnxn ix) ; static BOOL setPublicName (DomainType *domain, KEY id) ; static void updateHistory (KEY id, char* user, char* event, KEY target, NameType *type, char* name) ; static DomainType *getDomain (char *domain) ; /*************************************************************/ /****** initialise a connection ******************************/ /*************************************************************/ IdCnxn idaConnect (char *host, int port, char *user, char *password, IdError *err) /* ignore host, port and password in this implementation */ { IdCnxn ix ; idaCurateInitialise () ; ix = (IdCnxn) messalloc (sizeof(struct IdConnectionStruct)) ; ix->handle = handleCreate () ; blockSetFinalise (ix, idaFinalise) ; if (!user || !*user) { if (err) *err = BAD_PASSWORD ; messfree (ix) ; return 0 ; } else ix->user = strnew (user, ix->handle) ; if (err) *err = SUCCESS ; return ix ; } static void idaFinalise (void* ix) { handleDestroy (((IdCnxn)ix)->handle) ; } IdError idaError (IdCnxn ix) { if (!ix) return BAD_CONNECTION ; return ix->error ; } /*************************************************************/ /****** check utilities for tops of functions ****************/ /*************************************************************/ static BOOL ixCheck (IdCnxn ix, char *domain, BOOL isEdit) { if (!ix) return FALSE ; ix->error = SUCCESS ; if (isEdit && !(isWriteAccess () || sessionGainWriteAccess ())) { ix->error = NO_PERMISSION ; return FALSE ; } if (!(ix->domain = getDomain (domain))) { ix->error = BAD_DOMAIN ; return FALSE ;} return TRUE ; } static BOOL idCheck (IdCnxn ix, ID id, KEY *key, OBJ *obj, BOOL live, BOOL isEdit) { if (!lexword2key (id, key, ix->domain->objectClass) || (live && !bIndexTag (*key, str2tag("Live"))) || (!live && bIndexTag (*key, str2tag("Live")))) { ix->error = BAD_ID ; return FALSE ; } if (isEdit && !(*obj = bsUpdate (*key))) { ix->error = NO_PERMISSION ; return FALSE ; } if (!isEdit && !(*obj = bsCreate (*key))) { ix->error = BAD_ID ; return FALSE ; } return TRUE ; } static BOOL typeCheck (IdCnxn ix, char *nameType, NameType **type) { int t ; if (!nameType || !dictFind (ix->domain->typeDict, nameType, &t)) { ix->error = BAD_NAME_TYPE ; return FALSE ; } *type = arrayp(ix->domain->types, t, NameType) ; return TRUE ; } /*************************************************************/ /****** primary actions to create, merge, split, kill etc. ***/ /*************************************************************/ ID idaCreate (IdCnxn ix, char* domain, mytime_t date) { KEY idKey ; if (!ixCheck (ix, domain, TRUE)) return 0 ; idKey = newId (ix) ; updateHistory (idKey, ix->user, "Created", 0, 0, 0) ; return (ID) name(idKey) ; } /**************************************************************/ BOOL idaKill (IdCnxn ix, char* domain, ID id, mytime_t date) { OBJ obj ; KEY idKey ; if (!ixCheck (ix, domain, TRUE)) return 0 ; if (!idCheck (ix, id, &idKey, &obj, TRUE, TRUE)) return 0 ; if (!bsFindTag (obj, str2tag("Live"))) messcrash ("screwup in idKill for %s", id) ; bsPrune (obj) ; bsSave (obj) ; updateHistory (idKey, ix->user, "Killed", 0, 0, 0) ; return TRUE ; } /**************************************************************/ BOOL idaResurrect (IdCnxn ix, char *domain, ID id, mytime_t date) { OBJ obj ; KEY idKey ; if (!ixCheck (ix, domain, TRUE)) return 0 ; if (!idCheck (ix, id, &idKey, &obj, FALSE, TRUE)) return 0 ; if (bIndexTag (idKey, str2tag("Merged_into"))) { ix->error = BAD_ID ; return 0 ; } if (!bsAddTag (obj, str2tag("Live"))) messcrash ("screwup in idResurrect for %s", id) ; bsSave (obj) ; updateHistory (idKey, ix->user, "Resurrected", 0, 0, 0) ; return TRUE ; } /**************************************************************/ BOOL idaMerge (IdCnxn ix, char *domain, ID id, ID target, mytime_t date) { OBJ obj ; KEY idKey, targetKey ; if (!ixCheck (ix, domain, TRUE)) return 0 ; if (!lexword2key (target, &targetKey, ix->domain->objectClass) || !bIndexTag (targetKey, str2tag("Live"))) { ix->error = BAD_TARGET ; return FALSE ; } if (!idCheck (ix, id, &idKey, &obj, TRUE, TRUE)) return 0 ; if (!bsFindTag (obj, str2tag("Live"))) messcrash ("screwup in idMerge for %s", id) ; bsPrune (obj) ; bsSave (obj) ; updateHistory (idKey, ix->user, "Merged_into", targetKey, 0, 0) ; updateHistory (targetKey, ix->user, "Acquires_merge", idKey, 0,0) ; return TRUE ; } /**************************************************************/ ID idaSplit (IdCnxn ix, char *domain, ID id, mytime_t date) { KEY idKey, child ; OBJ obj ; if (!ixCheck (ix, domain, TRUE)) return 0 ; if (!idCheck (ix, id, &idKey, &obj, TRUE, TRUE)) return 0 ; bsDestroy (obj) ; /* don't update here */ child = newId (ix) ; updateHistory (child, ix->user, "Split_from", idKey, 0, 0) ; updateHistory (idKey, ix->user, "Split_into", child, 0, 0) ; return (ID) name(child) ; } /**************************************************************/ BOOL idaAddName (IdCnxn ix, char *domain, ID id, char*nameType, char *newName, mytime_t date) { KEY idKey, nameKey ; OBJ obj ; NameType *type ; if (!ixCheck (ix, domain, TRUE)) return FALSE ; if (!typeCheck (ix, nameType, &type)) return FALSE ; if (!name || !*name || (type->isPrimary && idaFind (ix, domain, nameType, newName))) { ix->error = DUPLICATE_NAME ; return 0 ; } if (!idCheck (ix, id, &idKey, &obj, TRUE, TRUE)) return FALSE ; lexaddkey (newName, &nameKey, ix->domain->nameClass) ; bsAddKey (obj, type->tag, nameKey) ; bsSave (obj) ; setPublicName (ix->domain, idKey) ; updateHistory (idKey, ix->user, "Add_name", 0, type, newName) ; return TRUE ; } /*************************************************************/ BOOL idaRemoveName (IdCnxn ix, char *domain, ID id, char*nameType, char *name, mytime_t date) { KEY idKey, nameKey ; OBJ obj ; NameType *type ; if (!ixCheck (ix, domain, TRUE)) return FALSE ; if (!typeCheck (ix, nameType, &type)) return FALSE ; if (!idCheck (ix, id, &idKey, &obj, TRUE, TRUE)) return FALSE ; if (!name || !*name || !lexword2key (name, &nameKey, ix->domain->nameClass) || !bsFindKey (obj, type->tag, nameKey)) { ix->error = BAD_NAME ; bsDestroy (obj) ; return FALSE ; } bsPrune (obj) ; bsSave (obj) ; setPublicName (ix->domain, idKey) ; updateHistory (idKey, ix->user, "Remove_name", 0, type, name) ; return TRUE ; } /*************************************************************/ /****** access functions *************************************/ /*************************************************************/ ID* idaGetByName (IdCnxn ix, char *domain, char* nameType, char* nam) { KEY nameKey, key ; OBJ obj ; ID *idList ; NameType *type ; int i ; static Array a = 0 ; a = arrayReCreate (a, 16, KEY) ; if (!ixCheck (ix, domain, FALSE)) return 0 ; if (!nam || !*nam) { ix->error = BAD_NAME ; return 0 ; } if (lexword2key (nam, &nameKey, ix->domain->nameClass) && (obj = bsCreate (nameKey))) { if (!*nameType) /* legal - use all types */ { for (i = 1 ; i < arrayMax(ix->domain->types) ; ++i) if (bsGetKey (obj, arrp(ix->domain->types, i, NameType)->tag, &key)) do if (bIndexTag(key, str2tag("Live"))) array (a, arrayMax(a), KEY) = key ; while (bsGetKey (obj, _bsDown, &key)) ; } else if (!strcmp (nameType, "Public")) { if (bsGetKey (obj, str2tag("Public_name"), &key)) do if (bIndexTag(key, str2tag("Live"))) array (a, arrayMax(a), KEY) = key ; while (bsGetKey (obj, _bsDown, &key)) ; } else { if (!typeCheck (ix, nameType, &type)) { bsDestroy (obj) ; return FALSE ; } if (bsGetKey (obj, type->tag, &key)) do if (bIndexTag(key, str2tag("Live"))) array (a, arrayMax(a), KEY) = key ; while (bsGetKey (obj, _bsDown, &key)) ; } bsDestroy (obj) ; } idList = (ID*) messalloc ((arrayMax(a)+1)*sizeof(ID)) ; for (i = 0 ; i < arrayMax(a) ; ++i) idList[i] = name(arr(a,i,KEY)) ; return idList ; } /****************************************/ ID idaFind (IdCnxn ix, char *domain, char *nameType, char *nam) { KEY nameKey ; NameType *type ; KEY key = 0 ; OBJ obj ; if (!ixCheck (ix, domain, FALSE)) return 0 ; if (!nam || !*nam) { ix->error = BAD_NAME ; return 0 ; } if (!typeCheck (ix, nameType, &type)) return 0 ; if (!type->isPrimary) { ix->error = BAD_NAME_TYPE ; return 0 ; } if (lexword2key (nam, &nameKey, ix->domain->nameClass) && (obj = bsCreate (nameKey))) { bsGetKey (obj, type->tag, &key) ; bsDestroy (obj) ; } if (key) return (ID) name(key) ; else return 0 ; } /*************************************************************/ /****** properties of ID objects *****************************/ /*************************************************************/ char* idaPublicName (IdCnxn ix, char *domain, ID id) { KEY idKey ; OBJ obj ; KEY key ; if (!ixCheck (ix, domain, FALSE)) return 0 ; if (!idCheck (ix, id, &idKey, &obj, TRUE, FALSE)) return 0 ; bsGetKey (obj, str2tag("Public_name"), &key) ; bsDestroy (obj) ; if (key) return name(key) ; else return 0 ; } /****************************************/ char** idaTypedNames (IdCnxn ix, char *domain, ID id, char *nameType) { KEY idKey, key ; OBJ obj ; NameType *type ; char **nameList ; static Array a = 0 ; int i ; a = arrayReCreate (a, 16, KEY) ; if (!ixCheck (ix, domain, FALSE)) return 0 ; if (!typeCheck (ix, nameType, &type)) return 0 ; if (!idCheck (ix, id, &idKey, &obj, TRUE, FALSE)) return 0 ; if (bsGetKey (obj, type->tag, &key)) do array (a, arrayMax(a), KEY) = key ; while (bsGetKey (obj, _bsDown, &key)) ; bsDestroy (obj) ; nameList = (char**) messalloc ((arrayMax(a)+1)*sizeof(char*)) ; for (i = 0 ; i < arrayMax(a) ; ++i) nameList[i] = name(arr(a,i,KEY)) ; return nameList ; } /****************************************/ char* idaTypedName (IdCnxn ix, char *domain, ID id, char *nameType) { KEY idKey ; OBJ obj ; NameType *type ; KEY key = 0 ; if (!ixCheck (ix, domain, FALSE)) return 0 ; if (!typeCheck (ix, nameType, &type)) return 0 ; if (!type->isUnique) { ix->error = BAD_DOMAIN ; return 0 ; } if (!idCheck (ix, id, &idKey, &obj, TRUE, FALSE)) return 0 ; bsGetKey (obj, type->tag, &key) ; bsDestroy (obj) ; if (key) return name(key) ; else return 0 ; } /****************************************/ int idaVersion (IdCnxn ix, char *domain, ID id) { KEY idKey ; OBJ obj ; int result = 0 ; if (!ixCheck (ix, domain, FALSE)) return 0 ; if (!idCheck (ix, id, &idKey, &obj, TRUE, FALSE)) return 0 ; if (!bsGetData (obj, str2tag("Name_version"), _Int, &result)) { ix->error = BAD_ID ; return 0 ; } return result ; } /****************************************/ BOOL idaLive (IdCnxn ix, char *domain, ID id) { KEY idKey ; OBJ obj ; BOOL result ; if (!ixCheck (ix, domain, FALSE)) return 0 ; /* can't use idCheck() because it requires to know whether live or not before calling */ if (!lexword2key (id, &idKey, ix->domain->objectClass)) { ix->error = BAD_ID ; return FALSE ; } if (!(obj = bsCreate (idKey))) { ix->error = NO_PERMISSION ; return FALSE ; } result = bsFindTag (obj, str2tag("Live")) ; bsDestroy (obj) ; return result ; } /****************************************/ ID idaUltimate (IdCnxn ix, char *domain, ID id) { KEY idKey ; OBJ obj = 0 ; if (!ixCheck (ix, domain, FALSE)) return 0 ; /* can't use idCheck() because it requires to know whether live or not before calling */ if (!lexword2key (id, &idKey, ix->domain->objectClass)) { ix->error = BAD_ID ; return 0 ; } while ((obj = bsCreate (idKey))) { if (!bsGetKey (obj, str2tag ("Merged_into"), &idKey)) { bsDestroy (obj) ; break ; } bsDestroy (obj) ; } return (ID) name(idKey) ; } /****************************************/ static void addChildren (Array a, KEY key) { OBJ obj = 0 ; array (a, arrayMax(a), KEY) = key ; if ((obj = bsCreate (key))) { if (bsGetKey (obj, str2tag ("Merged_into"), &key)) addChildren (a, key) ; if (bsGetKey (obj, str2tag ("Split_into"), &key)) do addChildren (a, key) ; while (bsGetKey (obj, _bsDown, &key)) ; bsDestroy (obj) ; } } ID* idaChildren (IdCnxn ix, char *domain, ID id) { KEY idKey ; ID *idList ; int i ; static Array a = 0 ; a = arrayReCreate (a, 16, KEY) ; if (!ixCheck (ix, domain, FALSE)) return 0 ; /* can't use idCheck() because it requires to know whether live or not before calling */ if (!lexword2key (id, &idKey, ix->domain->objectClass)) { ix->error = BAD_ID ; return 0 ; } addChildren (a, idKey) ; idList = (ID*) messalloc ((arrayMax(a)+1)*sizeof(ID)) ; for (i = 0 ; i < arrayMax(a) ; ++i) idList[i] = name(arr(a,i,KEY)) ; return idList ; } /****************************************/ BOOL idaExists (IdCnxn ix, char *domain, ID id) { KEY idKey ; if (!ixCheck (ix, domain, FALSE)) return 0 ; /* can't use idCheck() because it requires to know whether live or not before calling and sets ix->error */ if (lexword2key (id, &idKey, ix->domain->objectClass)) return TRUE ; else return FALSE ; } /****************************************/ IdHistory* idaHistory (IdCnxn ix, char *domain, ID id, int version) { ix->error = UNIMPLEMENTED ; return 0 ; } ID* idaChanged (IdCnxn ix, char *domain, char* timeStamp) { ix->error = UNIMPLEMENTED ; return 0 ; } /*************************************************************/ /****** administrator functions ******************************/ /*************************************************************/ IdError idaCreateDomain (IdCnxn ix, char *domain) { return (ix->error = UNIMPLEMENTED) ; } IdError idaSetTemplate (IdCnxn ix, char *domain, char *template) { return (ix->error = UNIMPLEMENTED) ; } IdError idaAddNameType (IdCnxn ix, char *domain, BOOL isUnique, BOOL isPrimary) { return (ix->error = UNIMPLEMENTED) ; } /*************************************************************/ /****** local functions **************************************/ /*************************************************************/ static void idaCurateInitialise (void) { int isDone = FALSE ; if (isDone) return ; if (!(classClass = pickWord2Class ("Class"))) messcrash ("No Class class") ; isDone = TRUE ; } /*************************************************************/ static KEY newId (IdCnxn ix) { KEY idKey ; static int n = 0 ; /* NB static to save position */ OBJ obj ; while (!lexaddkey (messprintf (ix->domain->template, ++n), &idKey, ix->domain->objectClass)) ; if ((obj = bsUpdate (idKey))) { bsAddTag (obj, str2tag("Live")) ; bsSave (obj) ; setPublicName (ix->domain, idKey) ; return idKey ; } else { ix->error = BAD_ID ; return 0 ; } } /*************************************************************/ static BOOL setPublicName (DomainType *domain, KEY idKey) { OBJ obj = bsUpdate (idKey) ; KEY nameKey ; BOOL result = FALSE ; BOOL gotit = FALSE ; int i ; if (!obj) return FALSE ; for (i = 0 ; i < arrayMax(domain->types) ; ++i) if (bsGetKey (obj, arrp(domain->types, i, NameType)->tag, &nameKey)) { result = bsAddKey (obj, str2tag("Public_name"), nameKey) ; gotit = TRUE ; } if (!gotit) { lexaddkey (name(idKey), &nameKey, domain->nameClass) ; bsAddKey (obj, str2tag("Public_name"), nameKey) ; } bsSave (obj) ; return result ; } /*************************************************************/ static void updateHistory (KEY id, char* user, char* event, KEY target, NameType *type, char* newName) { OBJ obj = bsUpdate (id) ; int version = 0 ; mytime_t time = timeParse ("now") ; if (!obj) { messerror ("empty obj in updateHistory") ; return ; } bsGetData (obj, str2tag("Name_version"), _Int, &version) ; ++version ; if (!bsAddData (obj, str2tag("Name_version"), _Int, &version)) messerror ("updateHistory could not update Name_version") ; if (bsAddData (obj, str2tag("Version_change"), _Int, &version) && bsAddData (obj, _bsRight, _DateType, &time) && bsAddData (obj, _bsRight, _Text, user) && bsPushObj (obj)) { if (!bsAddTag (obj, str2tag(event))) messerror ("Couldn't add event %s in updateHistory()", event) ; if (target && !bsAddData (obj, _bsRight, _Text, name(target))) messerror ("Couldn't add target %s in updateHistory()", target) ; if (type && !target && !bsAddData (obj, _bsRight, _Text, "")) messerror ("Couldn't add dummy target in updateHistory()") ; if (type && !bsAddData (obj, _bsRight, _Text, name(type->tag))) messerror ("Couldn't add nameType %s in updateHistory()", name(type->tag)) ; if (newName && !bsAddData (obj, _bsRight, _Text, newName)) messerror ("Couldn't add name %s in updateHistory()", newName) ; } else messerror ("updateHistory could not add Version_change") ; bsGoto (obj, 0) ; /* drop out of subtype */ if (!strcmp (event, "Merged_into")) bsAddKey (obj, str2tag(event), target) ; else if (!strcmp (event, "Split_from")) bsAddKey (obj, str2tag(event), target) ; /* don't need reciprocals since done by XREF */ bsSave (obj) ; return ; } /*************************************************************/ /****** command.c interface **********************************/ /*************************************************************/ /** Basic structure is acquired from gifcontrol, but this ** is considerably simpler. ** ** I decided to make it stateless, passing the class every time. ** This makes server/client interaction MUCH simpler. ** ** All commands (except ? and quit) return a status line first, ** then other return values only if status was SUCCESS. **/ static FREEOPT options[] = { { 16, "acedb-id" }, { '?', "? : this list of commands. Multiple commands ';' separated accepted" }, { 'C', "create : returns id" }, { 'K', "kill " }, { 'R', "resurrect : only if killed, not merged" }, { 'M', "merge " }, { 'S', "split : returns id" }, { '+', "add_name " }, { '-', "remove_name " }, { 'g', "get_by_name : returns id list" }, { 'p', "public_name : returns name" }, { 't', "typed_name : returns name list" }, { 'v', "version : returns version" }, { 'l', "live : returnus TRUE of FALSE" }, { 'u', "ultimate : follows Merged_into recursively" }, { 'c', "children : self and all recursive Merged_into and Split_into children" }, { 'e', "exists : TRUE or FALSE" } } ; extern char* idErrorString[] ; /* indexed on IdError */ /* two utilities */ static BOOL statusOK (ACEOUT fo, IdCnxn ix) ; static char* nextWord (ACEIN fi, STORE_HANDLE h) ; /******** temporary hack for server until userSessions fixed ********/ #ifndef NEW_SESSION_CODE char* idUser (char* user) { static char* userID = 0 ; if (user) { if (userID) messfree (userID) ; userID = strnew (user, 0) ; } else userID = getLogin (TRUE) ; return userID ; } #endif void idControl (KEYSET ks, ACEIN fi, ACEOUT fo) { KEY option ; int i ; if (aceInKey (fi, &option, options)) /* something on line */ { if (option == '?') /* help */ for (i = 1 ; i <= options[0].key ; i++) aceOutPrint (fo, "// %s\n", options[i].text) ; else /* real commands */ { STORE_HANDLE h = handleCreate () ; IdCnxn ix ; IdError err ; char *domain = nextWord (fi, h) ; #ifdef NEW_SESSION_CODE ix = idaConnect (0, 0, sessionUserSession2User(0), 0, &err) ; #else ix = idaConnect (0, 0, idUser(0), 0, &err) ; #endif if (err) { aceOutPrint (fo, "STATUS %s\n", idErrorString[err]) ; return ; } switch (option) { case 'C': /* create */ { ID id = idaCreate (ix, domain, 0) ; if (statusOK (fo, ix)) aceOutPrint (fo, "%s\n", (char*)id) ; } break ; case 'K': /* kill */ idaKill (ix, domain, nextWord(fi,h), 0) ; statusOK (fo, ix) ; break ; case 'R': /* resurrect */ idaResurrect (ix, domain, nextWord(fi,h), 0) ; statusOK (fo, ix) ; break ; case 'M': /* merge */ { char *id1 = nextWord(fi,h) ; char *id2 = nextWord(fi,h) ; idaMerge (ix, domain, id1, id2, 0) ; statusOK (fo, ix) ; } break ; case 'S': /* split */ { char *id1 = nextWord(fi,h) ; ID id = idaSplit (ix, domain, id1, 0) ; if (statusOK (fo, ix)) aceOutPrint (fo, "%s\n", (char*)id) ; } break ; case '+': /* add_name */ { char *id = nextWord(fi,h) ; char *type = nextWord(fi,h) ; char *nam = nextWord(fi,h) ; idaAddName (ix, domain, id, type, nam, 0) ; statusOK (fo, ix) ; } break ; case '-': /* remove_name */ { char *id = nextWord(fi,h) ; char *type = nextWord(fi,h) ; char *nam = nextWord(fi,h) ; idaRemoveName (ix, domain, id, type, nam, 0) ; statusOK (fo, ix) ; } break ; case 'g': /* get_by_name */ { char *type = nextWord(fi,h) ; char *nam = nextWord(fi,h) ; ID* idList = idaGetByName (ix, domain, type, nam) ; ID* ip = idList ; if (statusOK (fo, ix)) { while (*ip) aceOutPrint (fo, "%s\n", (char*)*ip++) ; messfree (idList) ; } } break ; case 'p': /* public_name */ { char* cp = idaPublicName (ix, domain, nextWord(fi,h)) ; if (statusOK (fo, ix)) aceOutPrint (fo, "%s\n", cp) ; } break ; case 't': /* typed_name */ { char *id = nextWord(fi,h) ; char *type = nextWord(fi,h) ; char** namList = idaTypedNames (ix, domain, id, type) ; char** nam = namList ; if (statusOK (fo, ix)) { while (*nam) aceOutPrint (fo, "%s\n", *nam++) ; messfree (namList) ; } } break ; case 'v': /* version */ { int v = idaVersion (ix, domain, nextWord(fi,h)) ; if (statusOK (fo, ix)) aceOutPrint (fo, "%d\n", v) ; } break ; case 'l': /* live */ { BOOL x = idaLive (ix, domain, nextWord(fi,h)) ; if (statusOK (fo, ix)) aceOutPrint (fo, "%s\n", x ? "TRUE" : "FALSE") ; } break ; case 'u': /* ultimate */ { ID id = idaUltimate (ix, domain, nextWord(fi,h)) ; if (statusOK (fo, ix)) aceOutPrint (fo, "%s\n", (char*)id) ; } break ; case 'c': /* children */ { ID* idList = idaChildren (ix, domain,nextWord(fi,h)); ID* ip = idList ; if (statusOK (fo, ix)) { while (*ip) aceOutPrint (fo, "%s\n", (char*)*ip++) ; messfree (idList) ; } } break ; case 'e': /* exists */ { BOOL res = idaExists (ix, domain, nextWord(fi,h)) ; if (statusOK (fo, ix)) if (res) aceOutPrint (fo, "TRUE\n") ; else aceOutPrint (fo, "FALSE\n") ; } break ; } /* end of switch */ handleDestroy (h) ; /* frees memory */ idConnectionDestroy (ix) ; } /* end of else (a real command) */ } else /* nothing typed on line */ aceOutPrint (fo, "// ID command expects a subcommand - use ID ? for more help\n") ; } static BOOL statusOK (ACEOUT fo, IdCnxn ix) { if (ix) { aceOutPrint (fo, "STATUS %s\n", idErrorString[ix->error]) ; return ix->error == SUCCESS ? TRUE : FALSE ; } else { aceOutPrint (fo, "STATUS BAD_CONNECTION\n") ; return FALSE ; } } static char* nextWord (ACEIN fi, STORE_HANDLE h) { char *word = aceInWord (fi) ; if (word) return strnew (word, h) ; else return 0 ; } /***************************************************************/ /********* domain cache ****************************************/ static DICT *domainDict = 0 ; static Array domains ; static DomainType *getDomain (char *domain) { int n ; KEY key ; OBJ obj = 0 ; /* suppresses compiler warning */ char *text, *temp ; BSMARK mark = 0 ; NameType *type ; DomainType *dom ; if (!domainDict) { domainDict = dictCreate (16) ; domains = arrayCreate (16, DomainType) ; } if (!domain || !*domain) return 0 ; if (dictFind (domainDict, domain, &n)) return arrp(domains, n, DomainType) ; /* domain is the name of a class, which must have a template and at least one name type */ if (!pickWord2Class (domain) || !pickWord2Class (messprintf ("%s_name", domain)) || !lexword2key (domain, &key, classClass) || !(obj = bsCreate (key)) || !bsGetData (obj, str2tag("Id_template"), _Text, &temp) || !bsGetData (obj, str2tag("Id_nametype"), _Text, &text)) { if (obj) bsDestroy (obj) ; return 0 ; } dictAdd (domainDict, domain, &n) ; dom = arrayp (domains, n, DomainType) ; dom->objectClass = pickWord2Class (domain) ; dom->nameClass = pickWord2Class (messprintf ("%s_name", domain)) ; dom->template = strnew (temp, 0) ; dom->typeDict = dictCreate (16) ; dom->types = arrayCreate (16, NameType) ; do /* get information about name types */ { dictAdd (dom->typeDict, text, &n) ; type = arrayp (dom->types, n, NameType) ; type->text = strnew (text, 0) ; type->tag = str2tag (messprintf ("%s_name", text)) ; mark = bsMark (obj, mark) ; bsPushObj (obj) ; type->isUnique = bsFindTag (obj, str2tag ("Is_unique")) ; type->isPrimary = bsFindTag (obj, str2tag ("Is_primary")) ; bsGoto (obj, mark) ; } while (bsGetData (obj, _bsDown, _Text, &text)) ; bsDestroy (obj) ; /* the classClass object */ return dom ; } /********************* end of file ******************/