/* File: lexsubs4.c * Author: Jean Thierry-Mieg (mieg@mrc-lmb.cam.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1991 * ------------------------------------------------------------------- * 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-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (Sanger Centre, UK) rd@sanger.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: * Exported functions: * HISTORY: * Last edited: Aug 6 09:35 2003 (rnc) * * Mar 11 00:26 2002 (rd): changed sessionUserKey() in readTimeStamps() to thisSession.userKey because a kernel operation. * * Mar 10 23:15 2002 (rd): added #include . * * Sep 2 14:47 1998 (edgrif): Removed faulty declaration of lexAlphaMark, * and lexAlphaSaveAll, which are defined in lex.h anyway. * * Aug 29 20:07 1994 (mieg): _Tags are now defined as KEY variables * their value is computed dynamically, ensuring consistency * accross databases * * Jan 10 00:54 1992 (mieg): added lexAlias, eliminated lastmodif * * Dec 11 12:22 1991 (mieg): lexcleanup to remove spaces in addkey * * Nov 5 21:28 1991 (mieg): introduced the hash package * Created: Tue Nov 5 21:28:40 1991 (mieg) * CVS info: $Id: lexsubs4.c,v 1.96 2003/08/06 08:45:09 rnc Exp $ *------------------------------------------------------------------- */ /***************************************************************/ /***************************************************************/ /** File lexsubs4.c : **/ /** Handles as Arrays the lexique of the ACeDB program. **/ /***************************************************************/ /***************************************************************/ /* */ /* Many routines are public : */ /* avail,show, make/dump, 2define, */ /* iskey,lock/unlock, iskeylocked, */ /* KEY2LEX,key2word/word2key, addkey, kill, */ /* randomkey,lexNext and lexstrcmp. */ /* */ /* NB lextstrcmp & lexstrCasecmp now moved to w1/utils.c */ /* */ /* There are 256 vocabularies, the first 128 are generic */ /* and common to all implementations of the base, the others */ /* can be handled freely by every user. */ /* */ /* Voc[0] contains the flow control dialog, the others the */ /* names of the genes, alleles, bits of the map and other */ /* objects, and the keywords describing the phenotypes. */ /* */ /* Each word is coded by a key. A key is 4 bytes long, the */ /* first byte is the vocabulary number, thus, the maximum */ /* number of entry per voc. is 16M. Each key may then refer */ /* to a disk address (or 0 if the word is not the name of an */ /* object or a list), and to a cache control area if the */ /* object is loaded. */ /* */ /* Lexshow is for debugging. */ /* Lexmake is called on entering the session, it reads in the */ /* lexiques from disk and return 0 if ok. LexSave writes */ /* them back to disk. Note that the lexique is not saved on */ /* every modification, therefore, if a crash occurs during */ /* a session, the lexiques must be reconstructed from the */ /* disk. */ /* */ /* iskey returns 0 on unknown keys, 1 if the key is empty */ /* or 2 if an object is associated to this key. */ /* lock prevents the simultaneous updating by 2 processes */ /* it is invoked by BSlock_make */ /* */ /* Name make a key into a word, and word2key a word */ /* into a key if the word is known. Lexaddkey(w,k,t) adds */ /* a new word w and sets its key k in vocabulary t. */ /* */ /* Lexstrcmp is a case insensitive strcmp. */ /* */ /***************************************************************/ #if defined(ACEDB4) || defined(ACEDB5) #include #include #include #include #include #include #include #include /* for tagInit, classInit */ #include /*call to diskprepare in lexinit*/ #include /* for aliassubclass */ #include #include #include #include #include #include #include /* for isspace() and isdigit() */ #include /* for lexstrcmp */ #define MAXLEX ((1<<24) - 1) /*number of entries per vocabulary <=FFFFFF*/ /*number of char in a vocabulary 1073741824 wheras it looks like this number is supposed to be 2147483647, but the alpha-compiler complains about 1<<31 to be too large, so we make that number directly by its HEX representation */ #define MAXTABLE 256 /*number of vocabulary tables <= 256*/ /* watchout 256 meaning MAXTABLE used in coresubs */ /* and also in parse.c , maybe elsewhere */ /* and corresponds to the first byte inside KEY */ /***********************/ static Array Lexi1 [MAXTABLE]; static Array Lexi2 = 0 ; static int minFreeLexi2 = 0 ; static KEYSET LexHashTable[MAXTABLE] ; static int nClass[MAXTABLE] ; /* number of bits for hasher */ static Stack Voc [MAXTABLE]; static BOOL lexIsRead[MAXTABLE] ; static char* nakedName(KEY key) ; /* Does not follow aliases */ static int vocmodif[MAXTABLE]; static int lexhmodif[MAXTABLE]; static int leximodif[MAXTABLE]; static int lexSessionStartSize[MAXTABLE] ; KEYSET touchedByClient = 0 ; static Array timeStamps[MAXTABLE]; static BOOL timeModif[MAXTABLE]; static void lexKeyUpdated(KEY key); #define si1 ((U_Int)sizeof(LEXI1)) #define si2 ((U_Int)sizeof(LEXI2)) static void lexvocread(int table) ; static void lexvocwrite(int table); static void lexiread(int table) ; static void lexiwrite(int table); static void lexhread(int table) ; static void lexhwrite(int table); static void lexReadTimeStamps(int t); static void lexWriteTimeStamps(int t); extern void lexDefineSystemTags(void) ; /* in whooks/sysclass.c */ extern void sysClassInit (void) ; static void lexHashInsert(int classe, KEY key) ; void lexReHashClass(int classe) ; static BOOL lexRealAlias(KEY key, char* newName, BOOL doAsk, BOOL keepOldName, char **err_msg_out, STORE_HANDLE handle) ; static LEXP1 KEY2LEX1(KEY kk) ; static LEXP KEY2LEX2(KEY kk) ; #define PRINT FALSE /* set TRUE to show save progress */ /**************************************************************/ void lexavail(unsigned long *vocnum, unsigned long *lex1num, unsigned long *lex2num, unsigned long *vocspace, unsigned long *hspace, unsigned long *totalspace) { register int t = 256; unsigned long n = 0, v = 0, k1 = 0, k2 = 0, h = 0, tt = 0; while(t--) if(Lexi1[t]) { n++; v += (unsigned long) stackMark(Voc[t]) ; k1 += (unsigned long) arrayMax(Lexi1[t]) ; h += arrayMax(LexHashTable[t]) ; tt += Voc[t]->a->dim +( Lexi1[t]->dim)*(Lexi1[t]->size)+(LexHashTable[t]->dim)*(LexHashTable[t]->size); } k2 = (unsigned long) arrayMax(Lexi2) ; tt += Lexi2->dim * Lexi2->size ; *vocspace=v; *vocnum=n; *lex1num=k1; *lex2num=k2; *hspace = h ; *totalspace = tt/1024; } /*************************************************/ void lexInit(void) { int n, i ; LEXP lxp ; register int t = MAXTABLE ; minFreeLexi2 = 1 ; n = 2000 ; Lexi2 = arrayCreate (n, LEXI2) ; array (Lexi2, n - 1, LEXI2).dlx.lx = 0 ; /* make room */ for (i = 1 , lxp = arrp (Lexi2, i, LEXI2) ; i < n - 1 ; i++, lxp++) lxp->dlx.lx = i + 1 ; while(t--) { vocmodif[t] = lexhmodif[t] = leximodif[t] = timeModif[t] = FALSE ; Voc[t] = 0 ; Lexi1[t] = LexHashTable[t] = timeStamps[t] = 0 ; nClass[t] = 0 ; lexIsRead[t] = FALSE ; } /* diskprepare() will be called implicitly on first disk access */ /* Hard define the tags needed for sessionInit */ lexDefineSystemTags() ; /* read the tag files, if they exist, for compatibility in case lex0 has never been saved */ lexIsRead[0] = lexIsRead[1] = TRUE ; lexReHashClass(0) ; lexReHashClass(1) ; } /********************************************/ static void lexi2clear(LEXP lxp) { int i ; if (lxp) { lxp->dlx.dk = 0 ; lxp->addr = 0 ; lxp->cache = 0 ; i = lxp - arrp(Lexi2, 0, LEXI2) ; lxp->key = 0 ; lxp->dlx.lx = minFreeLexi2 ; /* overload addr */ minFreeLexi2 = i ; } } /************************************************/ static void lexi1Clear (int t) { int i ; LEXI1* lx ; if (Lexi1[t]) { i = arrayMax(Lexi1[t]) ; lx = arrp(Lexi1[t], 0, LEXI1) - 1 ; while (lx++, i--) if (lx->is2) lexi2clear (arrp (Lexi2, lx->dlx.lx, LEXI2)) ; } arrayDestroy(Lexi1[t]) ; keySetDestroy(LexHashTable[t]) ; arrayDestroy(timeStamps[t]); } /*************************************************/ /* To be used only from the session manager */ void lexClear(void) { register int t ; for(t=3;t< MAXTABLE; t++) { vocmodif[t] = lexhmodif[t] = leximodif[t] = timeModif[t] = FALSE ; stackDestroy(Voc[t]) ; if (arrayExists(Lexi1[t])) lexSessionStartSize[t] = arrayMax(Lexi1[t]) ; lexi1Clear (t) ; keySetDestroy(LexHashTable[t]) ; nClass[t] = 0 ; lexIsRead[t] = FALSE ; /* lexClearClassStatus (t, TOUCHSTATUS) ; NON: this reads the lex back ! */ } } BOOL lexTableRead(KEY key) { return lexIsRead[class(key)]; } /*************************************************/ static void lexReadTable(int t) { STORE_HANDLE h; if (lexIsRead[t]) return ; lexIsRead[t] = TRUE ; h = handleCreate(); pickList[3].type = 'A' ; /* needed during bootstrap */ if (!t || pickList[t].name) { lexvocread(t) ; if(Voc[t]) /* could be 0 if class is new */ { messStatus (hprintf(h, "Reading in class %s", pickClass2Word(t))) ; lexiread(t) ; lexhread(t) ; lexSessionStartSize[t] = arrayMax(Lexi1[t]) ; lexClearClassStatus (t, TOUCHSTATUS) ; lexReadTimeStamps(t); } else /* Reinitialise this lexique */ { KEY dummy ; lexi1Clear (t) ; nClass[t] = 0 ; lexSessionStartSize[t] = 0 ; lexaddkey(0,&dummy,t) ; } } handleDestroy(h); } /*************************************************/ void lexRead (void) { KEY key ; /* table 0 is the tags, created in lexDefineTags() and dynamically table 1 is created by hand table 2 is the session chooser table 3 is the Voc lexique, it MUST be reinitialised table 4 is the BAT chooser */ STORE_HANDLE h = handleCreate(); if (lexword2key ("_voc3", &key, _VGlobal) && iskeyold(key)) { lexIsRead[_VVoc] = FALSE ; lexReadTable(_VVoc) ; } lexReadTable(_VBat) ; lexIsRead[_VDisplay] = FALSE ; lexIsRead[_VClass] = FALSE ; /* We now recover the correct values of the tags */ if (lexword2key ("_voc0", &key, _VVoc) && iskeyold(key)) { lexIsRead[0] = FALSE ; /* we recover the tags of the previous session */ lexReadTable(0) ; } lexDefineSystemTags () ; /* Redefine the system tags, which have been overwritten */ tagInit () ; /* finally, initialises correctly the _tag variables */ /* We now recover the correct values of the classes */ if (lexword2key (hprintf (h, "_voc%d", _VMainClasses), &key, _VVoc) && iskeyold(key)) { lexIsRead[_VMainClasses] = FALSE ; /* we recover the tags of the previous session */ lexReadTable(_VMainClasses) ; } sysClassInit () ; /* We redefine the system classes which may have been lost */ classInit () ; /* finally, initialises correctly the _VClass variables */ if (lexword2key (hprintf (h, "_voc%d", _VModel), &key, _VVoc) && iskeyold(key)) { lexIsRead[_VModel] = FALSE ; /* we recover the tags of the previous session */ lexReadTable (_VModel) ; } /* pickGetClassNames() ; */ sysClassOptions() ; handleDestroy(h); } /*************************************************/ void lexReadGlobalTables(void) { lexIsRead[1] = lexIsRead[0] = TRUE ; lexiread(1) ; lexReHashClass(1) ; diskPrepare() ; /* Reads the Global Bat */ lexReadTable(2) ; } /**************************************************************/ void lexmark(int tt) /*enables blocksubs to modify leximodif * when changing a disk address * note that the memory address is never * saved to disk, nor the locks * and that only lexaddkey and lexRename * touch lexh and voc */ { leximodif[tt] = TRUE ; } /******************************************************************/ /*writes the modified lexiques to disk*/ /*public ; called by saveAll() and dosomethingelse()*/ void lexSave(void) { int t = MAXTABLE ; lexAlphaSaveAll () ; while(t--) /* DO save lex[0]. tags.wrm only read for compatibility if lex0 is missing */ { if(t==1) lexiwrite(t); /*t = 1 Global voc */ else if(Lexi1[t]) { lexvocwrite(t); lexiwrite(t); lexhwrite(t); lexWriteTimeStamps(t); } } } /********************************************/ static void lexvocread(int t) { KEY key ; char buffer[24] ; sprintf(buffer, "_voc%d", t) ; lexaddkey (buffer,&key, !t || t>_VVoc ? _VVoc : _VGlobal ) ; lexaddkey (buffer,&key, !t || t>_VVoc ? _VVoc : _VGlobal ) ; stackDestroy(Voc[t]) ; Voc[t] = stackGet(key) ; vocmodif[t] = FALSE ; } /**************************************************************/ static void lexvocwrite(int t) { KEY key ; STORE_HANDLE h = handleCreate(); if (!isWriteAccess() || !vocmodif[t]) return ; if(PRINT) printf("\n writingVoc %d",t); lexaddkey(hprintf(h, "_voc%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; lexaddkey(hprintf(h, "_voc%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; stackStore(key,Voc[t]) ; vocmodif [t] = FALSE ; handleDestroy(h); } /**************************************************************/ static void lexhread(int t) { KEY key ; STORE_HANDLE h = handleCreate(); lexaddkey(hprintf(h, "_lexh%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; lexaddkey(hprintf(h, "_lexh%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; keySetDestroy(LexHashTable[t]) ; LexHashTable[t] = arrayGet(key, KEY,"k") ; if (!LexHashTable[t]) lexReHashClass(t) ; else { int n = arrayMax(LexHashTable[t]) , nBits = 0 ; while ((1 << ++nBits) < n) ; if(n != (1<_VVoc ? _VVoc : _VGlobal ) ; lexaddkey(hprintf(h"_lexh%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; arrayStore(key,LexHashTable[t]) ; handleDestroy(h); */ lexhmodif[t] = FALSE ; return ; } /**************************************************************/ static void lexiread(int t) { KEY key ; register LEXI1* lxi1 ; register int j ; Array dummy ; STORE_HANDLE h = handleCreate(); lexaddkey(hprintf(h, "_lexi%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; dummy = arrayGet(key, LEXI1,lexiFormat) ; lexi1Clear (t) ; Lexi1[t] = dummy ; if (!Lexi1[t]) messcrash("Lexi[%d] not found",t); lxi1 = arrp(Lexi1[t], 0, LEXI1) - 1; j = arrayMax(Lexi1[t]); while(lxi1++, j--) { lxi1->is2 = 0 ; lxi1->lock &= ~LOCKSTATUS ; /* NOT 3, to zero the last 2 bits */ } leximodif[t] = FALSE ; handleDestroy(h); } /**************************************************************/ /* i must restore the disk address in the union */ static void lexiwrite(int t) { Array lx ; KEY key ; DISK dsk ; LEXP1 lxp ; int i, j ; BOOL needCopy = FALSE ; STORE_HANDLE h = handleCreate(); if (!isWriteAccess() || !leximodif[t]) return ; lexaddkey(hprintf(h, "_lexi%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; if(PRINT) printf("\n writinglexi %3d, %8d entries.", t, arrayMax(Lexi1[t])); leximodif[t] = FALSE ; /* must come before the write */ lx = Lexi1[t] ; i = arrayMax(lx) ; lxp = arrp(lx, 0, LEXI1) - 1 ; while (lxp++, i--) if (lxp->is2) { needCopy = TRUE ; break ; } if (needCopy) /* restore the disk address in dlx union */ { lx = arrayCopy (Lexi1[t]) ; i = arrayMax(lx) ; lxp = arrp(lx, 0, LEXI1) - 1 ; while (lxp++, i--) if (lxp->is2) { j = lxp->dlx.lx ; if (j > arrayMax(Lexi2) || j <= 0) messcrash("lexi1/2 inconsistency") ; dsk = arrp(Lexi2, j, LEXI2)->dlx.dk ; lxp->is2 = 0 ; lxp->dlx.dk = dsk ; } } arrayStore(key,lx,lexiFormat) ; if (lx != Lexi1[t]) arrayDestroy(lx) ; handleDestroy(h); } /*************************************************************************/ static void lexReadTimeStamps(int t) { KEY key; STORE_HANDLE h = handleCreate(); lexaddkey(hprintf(h, "_lext%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; timeStamps[t] = arrayGet(key, TIMESTAMP, timeStampFormat); /* If this class has never had timestamps, make some now. */ if (!timeStamps[t]) timeStamps[t] = arrayCreate(arrayMax(Lexi1[t]), TIMESTAMP); /* below we deal with two cases, 1) adding timestamps to a class for the first time. 2) objects having been added by non timestamp aware binaries. In both cases we fill in the missing creation and update timestamps with those of the current kernel session. */ if (arrayMax(Lexi1[t]) > arrayMax(timeStamps[t])) { int i; if (t>3) messdump("Adding missing object-timestamps for class %s", pickClass2Word(t)); for (i = arrayMax(timeStamps[t]); iarrayMax(timeStamps[t])) return; array(timeStamps[t], i, TIMESTAMP).updated = sessionUserKey(); } static void lexWriteTimeStamps(int t) { KEY key ; STORE_HANDLE h = handleCreate(); if (!isWriteAccess()) return ; if(!timeModif[t]) return ; lexaddkey(hprintf(h, "_lext%d",t),&key,!t || t>_VVoc ? _VVoc : _VGlobal ) ; arrayStore(key, timeStamps[t], timeStampFormat); timeModif[t] = FALSE; handleDestroy(h); } void lexSetCreationUserSession(KEY key, KEY timeStamp) { int t = class(key); int i = KEYKEY(key); if (t<=1) return; if (!timeStamps[t]) lexReadTimeStamps(t); if (i>arrayMax(timeStamps[t])) return; arr(timeStamps[t], i, TIMESTAMP).created = timeStamp; timeModif[t] = TRUE; } KEY lexCreationUserSession(KEY key) { int t = class(key); int i = KEYKEY(key); if (t<=1) return 0; if (!timeStamps[t]) lexReadTimeStamps(t); if (i>arrayMax(timeStamps[t])) return 0; return arr(timeStamps[t], i, TIMESTAMP).created; } mytime_t lexCreationStamp(KEY key) { return sessionUserSession2Time(lexCreationUserSession(key)); } KEY lexUpdateUserSession(KEY key) { int t = class(key); int i = KEYKEY(key); if (t<=1) return 0; if (!timeStamps[t]) lexReadTimeStamps(t); if (i>arrayMax(timeStamps[t])) return 0; return arr(timeStamps[t], i, TIMESTAMP).updated; } mytime_t lexUpdateStamp(KEY key) { return sessionUserSession2Time(lexUpdateUserSession(key)); } /******************************************************************/ void lexOverLoad(KEY key,DISK disk) /* used in session.c only, for bootstrap */ { LEXP lx2 = KEY2LEX2(key) ; LEXP1 lx1 = KEY2LEX1(key) ; if (lx2) { lx2->dlx.dk = disk ; lx2->cache = 0 ; lx2->addr = 0 ; lx2->key = key ; } else lx1->dlx.dk = disk ; } /******************************************************************/ DISK lexDisk(KEY key) { LEXP lx2 = KEY2LEX2(key) ; LEXP1 lx1 = KEY2LEX1(key) ; return lx2 ? lx2->dlx.dk : lx1->dlx.dk ; } /******************************************************************/ static LEXP1 myKey2Lex1 = 0 ; static LEXP myKey2Lex2 = 0 ; void lexSetDisk(KEY key, DISK dk) { DISK old = 0 ; iskey(key) ; if (myKey2Lex2) { old = myKey2Lex2->dlx.dk ; myKey2Lex2->dlx.dk = dk ; } else if (myKey2Lex1) { old = myKey2Lex1->dlx.dk ; myKey2Lex1->dlx.dk = dk ; } else messcrash ("bad call to lexSetDisk(%s)", name(key)) ; if (dk != old) { leximodif[class(key)] = TRUE ; if (class(key)>1) { timeModif[class(key)] = TRUE; array(timeStamps[class(key)], KEYKEY(key), TIMESTAMP).updated = sessionUserKey(); } } } /******************************************************************/ /* Remove leading and trailing spaces and non ascii */ /* rd 971009: remove leading/trailing \t, change other \t to ' ' return result in private buffer (i.e. do not change orginal) */ /* check that name has some non-blank chars */ BOOL lexIsGoodName(char *name) { if (!name) return FALSE; while (*name) if (!isspace(*name++)) return TRUE; return FALSE; } char *lexcleanup(char *cp, STORE_HANDLE handle ) { char *cq; while (*cp == ' ' || *cp == '\t') cp++ ; cp = strnew(cp, handle); for (cq = cp + strlen(cp) ; cq-- > cp ; ) if (*cq == ' ' || *cq == '\t') *cq = 0 ; else break ; return cp ; } /******************************************************************/ void lexHardDefineKey (int table, KEY key, char *cp) { int i ; KEY key2 ; char *cq ; STORE_HANDLE h = handleCreate(); if(lexword2key(cp,&key2,table)) { if (key == KEYKEY(key2)) { handleDestroy(h); return ; } else messcrash ("The old %d:%d and new %d:%d values of key %s differ.\n%s\n%s", table,KEYKEY(key2), table, key, cp, "As of ace.3.4 files wspec/sys* and tags can be removed", "before creating the database, but not later on") ; } i = lexMax(table) ; if(!i) i = 1 ; /* Because a zeroth key is created by lexaddkey */ if (!key) key = i ; if(i < key) { for(;i < key; i++) lexaddkey(hprintf(h, "__sys%d",i),&key2,table) ; lexaddkey(cp,&key,table); } else if(i == key) lexaddkey(cp,&key,table); else { cq = name(KEYMAKE(table,key)) ; if(strcmp(cq,hprintf(h, "__sys%d",key))) messcrash("Tag %s = %d tries to overwrite tag %s", cp, key, cq) ; /* Else I am overwriting a dummy tag */ array(Lexi1[table], (int) key, LEXI1).nameoffset = stackMark(Voc[table]) ; pushText(Voc[table], lexcleanup(cp, h) ) ; leximodif[table] = lexhmodif[table] = vocmodif[table] = TRUE ; lexAlphaMark (table) ; } handleDestroy(h); } /********************************************/ /* Returns 0 if kk is unknown, * 1 if kk is just a vocabulary entry * 2 if kk corresponds to a full object * * sets myKey2Lex */ int isNakedKey(KEY kk) { KEY k = KEYKEY(kk); int t = class(kk); if (!kk || (k >= lexMax(t)) ) { myKey2Lex1 = 0 ; myKey2Lex2 = 0 ; return 0 ; } myKey2Lex1 = arrp(Lexi1[t], k, LEXI1); myKey2Lex2 = myKey2Lex1->is2 ? arrp(Lexi2, myKey2Lex1->dlx.lx, LEXI2) : 0 ; if (myKey2Lex2 && myKey2Lex2->key != kk) messcrash ("inconsistency in isNakedkey") ; return myKey2Lex1->dlx.dk || myKey2Lex2 ? 2 : 1 ; /* disk or lex2 both mean full object */ } /********************************************/ /* prevent the formation of alias loops */ BOOL lexAliasLoop(KEY old, KEY new) { KEY kk = new ; int result ; while (TRUE) { result = isNakedKey(kk) ; if (!result || ! (myKey2Lex1->lock & ALIASSTATUS)) break ; kk = myKey2Lex1->dlx.alias ; if (kk == old) /* to alias old to new would create a loop */ return TRUE ; } return FALSE ; } #ifdef NOT_NEED_BACUSE_APPERENTLY_NOT_USED /********************************************/ /* Scans the alias list for occurence of newName */ static BOOL lexAliasExists(KEY key, char* newName) { KEY kk = key ; char *name ; BOOL isCaseSensitive = pickList[class(key) & 255 ].isCaseSensitive ; /* int (*lexstrIsCasecmp)(const char *a,const char *b) = isCaseSensitive ? */ /* lexstrCasecmp : lexstrcmp ; */ int (*lexstrIsCasecmp)() = isCaseSensitive ? strcmp : strcasecmp ; while (TRUE) { name = nakedName(kk) ; /* sets myKey2lex */ if (!lexstrIsCasecmp(name, newName)) /* Equivalence */ return TRUE ; if (!(myKey2Lex1->lock & ALIASSTATUS)) return FALSE ; kk = myKey2Lex1->dlx.alias ; } } #endif /* NOT_NEED_BACUSE_APPERENTLY_NOT_USED */ /********************************************/ /* Called from parse.c, removes an aliased key and relinks the alias list */ BOOL lexDestroyAlias (KEY key) { KEY under = 0 ; LEXI1* lx ; int t, i ; if (!isNakedKey(key) || /* sets myKey2lex */ ! (myKey2Lex1->lock & ALIASSTATUS)) return FALSE ; under = myKey2Lex1->dlx.alias ; myKey2Lex1->lock &= ~ALIASSTATUS ; myKey2Lex1->lock |= EMPTYSTATUS ; myKey2Lex1->dlx.alias = 0 ; t = class(key) ; leximodif[t] = TRUE ; lexAlphaMark (t) ; lx = arrp(Lexi1[t], 0, LEXI1) - 1 ; i = arrayMax(Lexi1[t]) ; while(lx++, i--) if ((lx->lock & ALIASSTATUS) && lx->dlx.alias == key) lx->dlx.alias = under ; return TRUE ; } /********************************************/ /* Scans the alias list for valid object, used by bsCreate etc */ KEY lexAliasOf (KEY key) { while (TRUE) { if (!isNakedKey(key) || /* sets myKey2lex */ ! (myKey2Lex1->lock & ALIASSTATUS)) return key ; key = myKey2Lex1->dlx.alias ; } } /***************************************************/ /* returns false if key does not exist, has aliasstatus or emptystatus, is in class 0 (a tag), a private kernel class or is a KEYKEY 0 model */ /* cannot use lexGetStatus for this as it follows the alias list */ BOOL lexIsKeyVisible (KEY key) { if (!isNakedKey(key) || (myKey2Lex1->lock & (ALIASSTATUS | EMPTYSTATUS))) return FALSE ; if (pickList[class(key)].private) /* i.e. a tag */ return FALSE ; #ifdef ACEDB4 if (KEYKEY(key) == 0 && pickList[class(key)].type == 'B') /* i.e. a model */ return FALSE ; #endif return TRUE ; } /********************************************/ /* Iterates through the alias list */ LexKeyType iskey(KEY kk) { KEY start = kk ; int result ; while (TRUE) { result = isNakedKey(kk) ; if (!result || ! (myKey2Lex1->lock & ALIASSTATUS)) break ; kk = myKey2Lex1->dlx.alias ; if (kk == start) messcrash ("Loop in alias list of Key %d : %s", kk, nakedName(kk) ) ; } return result ; } /********************************************/ /* Returns TRUE * if kk has a primary cache or a disk address */ BOOL iskeyold(KEY kk) { if (!iskey(kk)) return FALSE ; if (!myKey2Lex2) return myKey2Lex1->dlx.dk ? TRUE : FALSE ; return myKey2Lex2->addr || myKey2Lex2->dlx.dk ? TRUE : FALSE ; } /********************************************/ static LEXP1 KEY2LEX1(KEY kk) { return iskey(kk) ? myKey2Lex1 : 0 ; } /********************************************/ static LEXP KEY2LEX2(KEY kk) { return iskey(kk) ? myKey2Lex2 : 0 ; } /********************************************/ /* for external code ; creates the lex2 entry */ static int newLexi2(void) { LEXP lx2 ; int i , j ; i = minFreeLexi2 ; if (!i) { i = arrayMax (Lexi2) ; array (Lexi2, i + 1000, LEXI2).dlx.lx = 0 ; for (j = i , lx2 = arrp (Lexi2, j, LEXI2) ; j < i + 1000 ; j++, lx2++) lx2->dlx.lx = j + 1 ; } lx2 = arrayp(Lexi2, i, LEXI2) ; minFreeLexi2 = lx2->dlx.lx ; lx2->dlx.dk = 0 ; /* un-overload dlx */ return i ; } LEXP KEY2LEX(KEY kk) { DISK disk ; int j2 ; int result; result = iskey(kk); /* sets myKey2Lex1,2 */ if (!result) messcrash("KEY2LEX called with invalid KEY %s", name(kk)); if (myKey2Lex2) return myKey2Lex2 ; if (!myKey2Lex1) return 0 ; disk = myKey2Lex1->dlx.dk ; myKey2Lex1->dlx.lx = j2 = newLexi2() ; myKey2Lex1->is2 = 1 ; myKey2Lex2 = arrayp(Lexi2, j2, LEXI2) ; myKey2Lex2->dlx.dk = disk ; myKey2Lex2->cache = 0 ; myKey2Lex2->addr = 0 ; { /* RD 980807 : important bug fix must follow aliases!! NB can't use lexAliasOf() because crash during bootstrap but LEXI2->key only needed for checking so could remove it completely - it was bugged! */ while (arrp(Lexi1[class(kk)], KEYKEY(kk), LEXI1)->lock & ALIASSTATUS) kk = arrp(Lexi1[class(kk)], KEYKEY(kk), LEXI1)->dlx.alias ; } myKey2Lex2->key = kk ; return myKey2Lex2 ; } /********************************************/ BOOL lexlock(KEY key) { /* code partially duplicated in cacheLock*/ LEXP1 q=KEY2LEX1(key); if(q) { if(q->lock & LOCKSTATUS) messerror("Double locking of %s", name(key)); else { q->lock |= LOCKSTATUS; return TRUE; } } return FALSE; } /********************************************/ void lexunlock(KEY key) { LEXP1 q=KEY2LEX1(key); if( q && (q->lock & LOCKSTATUS) ) q->lock &= ~LOCKSTATUS; else messcrash("Unbalanced unlock of %s", name(key)); } /********************************************/ /* subClass mechanism */ void lexSetIsA (KEY key, unsigned char c) { LEXP1 q=KEY2LEX1(key); if(q) q->isMask = c ; } /********************************************/ /* subClass mechanism: test appartenance */ BOOL lexIsInClass(KEY key, KEY classe) { unsigned char mask ; LEXP1 q=KEY2LEX1(key); if (classe < 256) return classe == class(key) ; if (!q) return FALSE ; pickIsA(&classe, &mask) ; return classe == class(key) && (mask & q->isMask) == mask ; } /********************************************/ /* alteration of LEXPRIVATESTATUS flags would foul the lex system */ /* code duplicated in cacheUpdate jan.97: i think this is no longer true */ /* do not mark as touched an object obtained from the server */ BOOL X_CLIENT_PARSING = FALSE ; void lexSetStatus(KEY key, unsigned char c) { LEXP1 q=KEY2LEX1(key); if (c == TOUCHSTATUS) { if (X_CLIENT_PARSING) return ; if (touchedByClient) keySet(touchedByClient, keySetMax(touchedByClient)) = key; } if (q) q->lock |= (c & ~LEXPRIVATESTATUS) ; else messcrash("lexSetStatus called with bad key %s", name(key)); } /********************************************/ /* code duplicated in cacheUpdate jan.97: i think this is no longer true */ void lexUnsetStatus(KEY key, unsigned char c) { LEXP1 q=KEY2LEX1(key); if (q) q->lock &= (~c) | LEXPRIVATESTATUS ; else messcrash("lexUnsetStatus called with bad key %s", name(key)); } /********************************************/ void lexClearClassStatus(KEY classe, unsigned char c) { int i ; LEXP1 q ; c = (~c) | LEXPRIVATESTATUS ; if ((i = lexMax(classe))) { q = arrp(Lexi1[classe], 0, LEXI1) - 1 ; while (q++, i--) q->lock &= c ; } } /********************************************/ unsigned char lexGetStatus(KEY key) { LEXP1 q=KEY2LEX1(key); if(q) return q->lock ; else messcrash("lexGetStatus called with bad key %s", name(key)); return 0 ; /* for compiler happiness */ } /********************************************/ /* Avoid the alias system, used by word2key and hasher */ static char * nakedName(KEY kk) { return isNakedKey(kk) ? stackText(Voc[class(kk)], myKey2Lex1->nameoffset) : "\177\177(NULL KEY)" ; } /********************************************/ char * name(KEY kk) { return iskey(kk) ? stackText(Voc[class(kk)], myKey2Lex1->nameoffset) : "\177\177(NULL KEY)" ; } char* nameWithClass (KEY k) { static char *buf = 0 ; static int buflen = 0 ; char *nam = name (k) ; char *classNam = className (k) ; if (strlen(nam) + strlen(classNam) + 2 > buflen) { if (buf) messfree (buf) ; buflen = strlen(nam) + strlen(classNam) + 2 ; buf = (char*) messalloc (buflen) ; } sprintf (buf, "%s:%s", classNam, nam) ; return buf ; } /********************************************/ /* Iterates through the alias list */ BOOL nextName(KEY key, char **cpp) { static LEXP1 lxp = 0 ; static KEY lastKey = 0 , kk ; if ( *cpp && key != lastKey) messcrash ("nextName %s called out of context", nakedName(key)) ; if (!*cpp) /* initialise */ kk = lastKey = key ; else /* loop */ { if (!lxp || !(lxp->lock & ALIASSTATUS)) /* shift once */ return FALSE ; kk = lxp->dlx.alias ; } while (TRUE) { if (!isNakedKey(kk)) return FALSE ; if (!(myKey2Lex1->lock & EMPTYSTATUS)) /* if empty */ break ; if (!(myKey2Lex1->lock & ALIASSTATUS)) /* loop again */ return FALSE ; kk = myKey2Lex1->dlx.alias ; } lxp = myKey2Lex1 ; /* used by shift once */ *cpp = nakedName(kk) ; return TRUE ; } /********************************************/ BOOL lexReClass(KEY key,KEY *kp, int t) { return lexword2key(name(key),kp,t) ; } /**********************************************/ static BOOL colonParse (char* note, char **classe, char **item) { char *cp ; for (cp = note ; *cp != ':' ; ++cp) if (!*cp) return FALSE ; *cp = 0 ; *classe = note ; *item = ++cp ; return TRUE ; } BOOL lexClassKey(char *text, KEY *kp) { int classe ; char *cName, *kName ; *kp = 0 ; return colonParse (text, &cName, &kName) && (classe = pickWord2Class (cName)) && (*(kName - 1) = ':') && /* Restore text */ lexword2key (kName, kp, classe) ; } /********************************************/ /********************************************/ /************ Hash Package ******************/ /* Hashing system */ #define HASH_RATE 1.6 #define SIZEOFINT (8 * sizeof (int)) unsigned int hashText(char *cp, int n, BOOL isOdd) { unsigned int i, h = 0 , j = 0, rotate = (isOdd ? 21 : 13) , leftover = SIZEOFINT - rotate ; chrono("hashText") ; while(*cp) { h = freeupper(*cp++) ^ /* XOR*/ ( ( h >> leftover ) | (h << rotate)) ; } /* compress down to n bits */ for(j = h, i = n ; i< SIZEOFINT; i+=n) j ^= (h>>i) ; chronoReturn() ; if (isOdd) /* return odd number */ j |= 0x1 ; return j & ( (1< keySetMax(hashTable))) lexReHashClass(classe) ; cp = nakedName(KEYMAKE(classe,key)) ; if (!*cp) return ; /* may occur after a rename */ h = hashText(cp, nClass [classe], FALSE) ; nn = nClass[classe] ; n = max = 1 << nn ; kp = arrp(hashTable, h , KEY) ; kpMax = arrp(hashTable, n -1, KEY) ; while ( n--) /* circulate thru table, starting at h */ { if(!*kp) /* found empty slot, do insert */ { *kp = key ; NII++ ; chronoReturn() ; return ; } /* circulate */ if (!dh) dh = hashText(cp, nClass [classe], TRUE) ; kp += dh ; if (kp > kpMax) kp -= max ; NIC++ ; } if (failedFirstPass) messcrash("Hash table full, NIC = %d, lexhashInsert is bugged", NIC) ; else { /* we may fail to find a slot because of the presence of stale keys */ /* after lexHardRename has run, try one more time, having purged */ /* the stale keys by re-hashing - srk */ failedFirstPass = TRUE; goto retry; } } /* I comment out this function it needs plot so prevents acequery froom linking Note that i am not sure that it coincides with the current strategy in hashInsert and word2key #include "plot.h" void lexHashTest(int classe) { static KEYSET hashTable ; unsigned int h ; int n, nn , nn1 ; KEY *kp, *kpMin, *kpMax ; int empty = 0 , full = 0 , j ; Array histo = arrayCreate(200,int) ; char *cp ; hashTable = LexHashTable[classe] ; if(!hashTable || !arrayMax(hashTable)) { messerror("empty table\n") ; return ; } kpMin = arrp(hashTable, 0 , KEY) ; kpMax = arrp(hashTable, n -1, KEY) ; nn = nClass[classe] ; nn1 = n = arrayMax(hashTable) ; for(h=0; h kpMax) kp -= max ; j++ ; } } } plotHisto (messprintf("Hash correl %s", pickClass2Word(classe)), histo) ; } */ /********************************************/ static void lexHardRename(KEY key, char *newName) { char *cp, *oldName = nakedName(key) ; /* sets myKey2Lex */ int classe = class(key) , n = strlen(oldName) ; for (cp = oldName ; *cp ;) *cp++ = 0 ; /* will be left out of hash on next rehashing */ if (strlen(newName) > n ) { myKey2Lex1->nameoffset = stackMark(Voc[classe]) ; leximodif[classe] = TRUE ; /* since nameoffset is changed */ pushText (Voc[classe], newName) ; } else strcpy (oldName, newName) ; if (myKey2Lex1->lock & EMPTYSTATUS) /* because the new name should appear in lists */ { myKey2Lex1->lock &= ~EMPTYSTATUS ; leximodif[class(key)] = TRUE ; } lexHashInsert(classe, key) ; /* re-insert in hash table */ vocmodif[classe] = TRUE ; } /********************************************/ void lex2Check (void) {return ; /* LEXP lx = arrp(Lexi2, 0, LEXI2) - 1 ; int i = arrayMax(Lexi2) ; while (lx++, i--) if (lx->cache && (*(KEY*)lx->cache) != lx->key) messcrash("lex2Check error") ; */ } void lex2clear (KEY key) { iskey (key) ; if (!myKey2Lex2 || myKey2Lex2->cache || myKey2Lex2->addr) return ; /* lex2Check() ; */ myKey2Lex1->dlx.dk = myKey2Lex2->dlx.dk ; myKey2Lex1->is2 = 0 ; lexi2clear(myKey2Lex2) ; } /********************************************/ static void lexMakeAlias(KEY key, KEY alias, BOOL keepOldName) { isNakedKey(key) ; myKey2Lex1->dlx.alias = alias ; /* overloading dlx union with alias key */ myKey2Lex1->lock = ALIASSTATUS ; if (!keepOldName) myKey2Lex1->lock |= EMPTYSTATUS ; /* destroy the lex2 entry */ myKey2Lex1->is2 = 0 ; if (myKey2Lex2) lexi2clear(myKey2Lex2) ; if (!keepOldName) /* Clean up */ { char *cp = nakedName(key) ; while (*cp) *cp++ = 0 ; myKey2Lex1->nameoffset = 0 ; } isNakedKey(alias) ; myKey2Lex1->lock &= ~EMPTYSTATUS ; leximodif[class(key)] = TRUE ; } /********************************************/ /* Returns TRUE if newName is accepted */ /* if keepOldName, the 2 names will be used in the future, * so we need 2 keys anyway, * If not, we would rather keep a single key except if the old * and new one allready exist because they may be referenced from * elsewhere. */ static BOOL lexRealAlias(KEY key, char* newName, BOOL doAsk, BOOL keepOldName, char **err_msg_out, STORE_HANDLE handle) { char *oldName, *oldnamepreserve = 0 ; int classe = class(key) ; KEY newKey ; BOOL isCaseSensitive = pickList[classe & 255 ].isCaseSensitive ; int (*lexstrIsCasecmp)() = isCaseSensitive ? strcmp : strcasecmp ; /********** Protections *************/ if (!isNakedKey(key) || /* key is not a vocabulary entry */ (myKey2Lex1->lock & (ALIASSTATUS | EMPTYSTATUS))) /* already aliased or EMPTY, to forbid complex alias graphs */ return TRUE ; /* no error when parsing same ace file twice */ if (lexiskeylocked(key)) { if (doAsk) messout("Sorry, alias fails because %s is locked elsewhere", name(key)) ; else if (err_msg_out) *err_msg_out = hprintf(0, "Sorry, alias fails because %s is locked elsewhere", name(key)) ; return FALSE ; } /************* Trivial cases *************/ newName = lexcleanup(newName, handle) ; oldName = name(key) ; if (!strcmp(oldName, newName)) /* Identity */ return TRUE ; if (externalAlias) oldnamepreserve = strnew(oldName, handle) ; if (!lexstrIsCasecmp(oldName, newName)) /* Equivalence and certainly same length */ { lexHardRename(key, newName) ; /* but not exact (dealt with earlier */ lexKeyUpdated(key); goto ok ; } /************* Hard Rename to unknown name case *************/ if (!keepOldName && !lexword2key (newName, &newKey, classe)) { lexHardRename(key, newName) ; lexKeyUpdated(key); goto ok ; } /************ Else I want the 2 keys defined ************/ lexaddkey(newName, &newKey, classe) ; /* attention, this may invalidate oldName pointer */ if (lexiskeylocked(newKey)) { if (doAsk) messout("Sorry, alias fails because %s is locked elsewhere", name(newKey)) ; else if (err_msg_out) *err_msg_out = hprintf(0, "Sorry, alias fails because %s is locked elsewhere", name(newKey)) ; return FALSE ; } if(lexAliasLoop(key, newKey)) /* No loops */ { if (doAsk) messout("Sorry, aliasing %s to %s would create a loop.", oldName, newName) ; else if (err_msg_out) *err_msg_out = hprintf(0, "Sorry, aliasing %s to %s would create a loop.", oldName, newName) ; return FALSE ; } /* should I fuse ? */ if (isNakedKey(key) == 1 ) /* key is actually empty */ lexMakeAlias(key, newKey, keepOldName) ; else /* isNakedkey(key) == 2 */ { if (isNakedKey(newKey) == 1 ) /* key is full but newkey is empty */ { /* Just shift things over */ LEXP1 newLex1 = KEY2LEX1(newKey) , lex1 = KEY2LEX1(key) ; LEXOFFST offset = lex1->nameoffset ; KEY kk ; lex1->nameoffset = newLex1->nameoffset ; newLex1->nameoffset = offset ; kk = key ; key = newKey ; newKey = kk ; /* swap before clean up */ lexMakeAlias(key, newKey, keepOldName) ; lexKeyUpdated(newKey); } else /* also isNakedKey(newKey) == 2 */ { if (pickType(key) != 'B') { char *msg = "merging of non-Tree objects cannot be done." ; if (doAsk) messout(msg) ; else if (err_msg_out) *err_msg_out = hprintf(0, msg) ; return FALSE ; } /* NOTE WELL, if interactive we ask the user if they really want */ /* to merge one object with another. But even if err_msg_out is */ /* non-NULL we don't return anything here, the fusing is just done.*/ if (doAsk && !messQuery (hprintf(handle, "%s and %s both exist.\n" "In case of conflict i will rather keep the data of %s.\n\n" "Do you want to proceed", name(key), name(newKey), name(newKey)))) return FALSE ; bsFuseObjects (key, newKey) ; /* to late to return FALSE, old is already destroyed */ lexMakeAlias(key, newKey, keepOldName) ; lexKeyUpdated(newKey); lexKeyUpdated(key); } } ok: lexHashInsert(classe, key) ; /* re-insert in hash table */ if (pickType (key) == 'B') /* set subclass lex bits */ { OBJ obj = bsCreate (key) ; bIndexObject(key, obj) ; /* needed always */ bsDestroy (obj) ; } lexAlphaMark (class(key)) ; if (externalAlias) externalAlias (key, oldnamepreserve, keepOldName) ; return TRUE ; } int lexNakedAlphaOrder(void *a, void *b) /* called in lexalpha.c */ { KEY k1 = *(KEY*)a, k2 = *(KEY*)b ; int i = class(k1) - class(k2) ; if (i) return i ; return lexstrcmp (nakedName(k1), nakedName(k2)) ; } /********************************************/ /* Alias functions, these will set up an alias for an object, or if the new */ /* name is an existing object, the two objects will be merged. */ /* */ /* I would like to see lexAlias gradually replaced by lexDoAlias. */ /* */ /* The original lexAlias() function either does not output error messaages */ /* or outputs them to the screen. For the parse code to work with the */ /* server, I need to returned a pointer to the message. This is done via */ /* err_msg_out, if its NULL no error message is returned (as in lexAlias), */ /* otherwise an error message is returned. N.B. caller is responsible for */ /* messfree() of err_msg_out. */ BOOL lexAlias(KEY key, char* newName, BOOL doAsk, BOOL keepOldName) { STORE_HANDLE h ; BOOL ret ; h = handleCreate(); ret = lexRealAlias(key, newName, doAsk, keepOldName, NULL, h); handleDestroy(h); return ret; } BOOL lexDoAlias(KEY key, char* newName, BOOL doAsk, BOOL keepOldName, char **err_msg_out) { STORE_HANDLE h ; BOOL ret ; h = handleCreate(); ret = lexRealAlias(key, newName, doAsk, keepOldName, err_msg_out, h); handleDestroy(h); return ret; } /********************************************/ /********************************************/ /* following are private communication lexword2key -> lexaddkey */ static KEY *KP ; static BOOL isSubclassFailInWord2key ; BOOL lexword2key(char *cp, KEY *key, KEY classe) /* given a word *cp, sets its key in *key */ /* returns TRUE if found, FALSE if not */ /* No completion performed */ /* classe can be a table or key in class Class */ { unsigned int h, dh = 0 ; int max, n , nn ; KEY *kp, *kpMax ; KEYSET hashTable ; int t ; unsigned char mask ; LEXI1 *lexi ; char *voc ; BOOL isCaseSensitive ; int (*lexstrIsCasecmp)() ; pickIsA(&classe, &mask) ; t = classe ; isCaseSensitive = pickList[t & 255 ].isCaseSensitive ; lexstrIsCasecmp = isCaseSensitive ? strcmp : strcasecmp ; if (t < 0 || t >= MAXTABLE) messcrash("lexword2key called on impossible class %d", classe) ; if(!lexIsRead[classe]) lexReadTable(classe) ; hashTable = LexHashTable [classe] ; if ( !cp || !*cp || !nClass[classe] || !arrayMax(Lexi1[classe]) ) { *key = 0; chronoReturn() ; return FALSE ; } chrono("lexword2key") ; cp = lexcleanup(cp, 0) ; /* remove spaces etc */ voc = stackText(Voc[classe],0) ; lexi = arrp(Lexi1[classe], 0 ,LEXI1) ; h = hashText(cp, nn = nClass [classe], 0) ; n = max = arrayMax(hashTable) ; kp = arrp(hashTable, h , KEY) ; kpMax = arrp(hashTable, n -1, KEY) ; while ( n--) /* circulate thru table, starting at h */ { if(!*kp) /* found empty slot, cp is unknown */ { *key = 0 ; KP = kp ; /* private to be used by lexaddkey */ chronoReturn() ; NWG++ ; messfree(cp); return FALSE ; } if (!lexstrIsCasecmp(cp, voc + (lexi + *kp)->nameoffset)) { *key = KEYMAKE(classe,*kp) ; chronoReturn() ; if (mask != (mask & lexi[*kp].isMask)) /* subset criteria fail */ { isSubclassFailInWord2key = TRUE ; NWG++ ; messfree(cp); return FALSE ; } NWF++ ; messfree(cp); return TRUE ; /* found */ } /* circulate */ if (!dh) dh = hashText(cp, nn, TRUE) ; kp += dh ; if (kp > kpMax) kp -= max ; NWC++ ; } chronoReturn() ; KP = 0 ; messfree(cp); return FALSE ; /* Whole table scanned without success */ } /**************************************************************************/ /* To obtain the next key in KEY order */ /* or the first entry if *key=0. */ /* Updtates the value of *key, */ /* Returns 0 if the vocabulary is empty */ /* or if *key is its last entry, 1 otherwise. */ KEY lexLastKey(int classe) { KEY key = 0, k = 0 ; while(lexNext(classe,&k)) key = k ; return key ; } /********/ /* RD 980713: removed static i to check sequential use on same class. Necessary for AQL, but discussion with jean concluded this is also correct, not changing the semantics described above! */ BOOL lexNext (KEY classe, KEY *kp) { LEXP1 lxp1 ; int i ; unsigned char mask ; if (!pickIsA(&classe, &mask)) { *kp = 0 ; return FALSE ; } if (!lexIsRead[classe]) lexReadTable(classe) ; if (!*kp) /* Find beginning of lexique */ { if (!classe) { *kp = 1 ; return TRUE ; } /* prevents looping on class 0 */ else { #ifdef ACEDB4 i = pickList[classe].type == 'B' ? 0 : -1 ; #else i = -1 ; #endif } } else /* *kp is already in the classe (we hope!) */ { if (class(*kp) != classe) { messerror ("class mixup in lexNext: class(%s) is %s not %s", name(*kp), className(*kp), className(KEYMAKE(classe,0))) ; return FALSE ; } i = KEYKEY(*kp) ; } if (Lexi1[classe] /* lexique non-empty */ && i+1 < arrayMax(Lexi1[classe])) { lxp1 = arrp(Lexi1[classe], i, LEXI1) ; while (++lxp1, ++i < arrayMax(Lexi1[classe])) /* not at end of lexique */ if (!(lxp1-> lock & EMPTYSTATUS)) { if (!(lxp1->lock & ALIASSTATUS)) { if ( mask == (mask & lxp1-> isMask)) { *kp = KEYMAKE(classe, i) ; return TRUE; } } else { /* it an alias so test the mask on the canonical key */ lexAliasOf(KEYMAKE(classe, i)); if ( mask == (mask & myKey2Lex1-> isMask)) { *kp = KEYMAKE(classe, i) ; return TRUE; } } } } *kp = 0 ; return FALSE ; } /********************************************/ void lexkill(KEY kk) /*deallocates a key*/ { iskey(kk) ; if (!myKey2Lex1) return ; chrono("lexkill") ; /* saveAll() ; must be done before to empty the cache but not here because lexkill is used by sessionControl */ if (lexDestroyAlias(kk)) return ; if(myKey2Lex1->dlx.dk) { myKey2Lex1->dlx.dk = 0 ; leximodif[class(kk)] = TRUE ; lexAlphaMark (class(kk)) ; } if ( ! (myKey2Lex1->lock & EMPTYSTATUS )) { myKey2Lex1->lock |= EMPTYSTATUS; leximodif[class(kk)] = TRUE ; lexAlphaMark (class(kk)) ; } myKey2Lex1->is2 = 0 ; if (myKey2Lex2) lexi2clear(myKey2Lex2) ; chronoReturn() ; return; } /**************************************************************/ KEY str2tag(char *tagname) { KEY k; lexaddkey(tagname, &k, 0); return k; } /**************************************************************/ BOOL lexaddkey(char *cp,KEY *kptr,KEY t) /*add to the t lexique the word *cp */ /*returns TRUE if added, FALSE if known */ { KEY k; LEXI1 ai; unsigned char mask ; #ifndef ACEDB4 extern BOOL READING_MODELS ; #endif STORE_HANDLE h = handleCreate(); chrono("lexaddkey") ; pickIsA(&t, &mask) ; #ifndef ACEDB4 if (t == _VMainClasses && !READING_MODELS ) { handleDestroy(h); return lexword2key (cp, kptr, t) ; } #endif KP = 0 ; isSubclassFailInWord2key = FALSE ; if (cp && (lexword2key (cp,kptr,t) || isSubclassFailInWord2key)) { chronoReturn() ; handleDestroy(h); return FALSE ; } /* word already known */ if (!lexIsRead[t]) { lexReadTable(t) ; KP = 0 ; } /* initialise the lexique */ if (!Lexi1[t]) { int nBits = 8 ; Voc[t] = stackCreate(1 << (nBits + 2)) ; stackTextOnly(Voc[t]); Lexi1[t] = arrayCreate(1 << (nBits - 1), LEXI1); timeStamps[t] = arrayCreate(1 << (nBits - 1), TIMESTAMP); timeModif[t] = TRUE; LexHashTable[t] = arrayCreate(1< lock = EMPTYSTATUS ; /* i.e. never display it */ KP = 0 ; } k = (KEY) arrayMax(Lexi1[t]) ; if (!cp && k <= 2) /* call with cp=0 used for initialisation */ { *kptr = KEYMAKE(t,0); chronoReturn() ; handleDestroy(h) ; return TRUE ; } cp = lexcleanup(cp, h) ; if (!cp || !*cp) messcrash("Attempt to create a key with emtpy name in class%s", className (KEYMAKE(t,0))) ; if ( (k>=MAXLEX) || ( (stackMark(Voc[t]) + strlen(cp)) > MAXVOCAB )) messcrash("Lexique %d full: now %d keys, %d size stack", t, k, stackMark(Voc[t])) ; /*create a lex record*/ ai.nameoffset = stackMark(Voc[t]) ; ai.dlx.dk = 0 ; ai.lock = 0; ai.isMask = 0; ai.is2 = 0 ; /* add the word to the end of vocabulary */ pushText(Voc[t], cp) ; /*add an entry to the end of lex */ array(Lexi1[t], (int) k, LEXI1) = ai ; /* note that Voc and Lexi must be updated first for lexHashInsert to work */ if (KP) { chrono("Found KP") ; chronoReturn() ; *KP = k ; /* direct insertion , be careful if you touch the code */ /* KP is inherited from the last call to lexword2key */ /* SRK - restored this to original in lexsubs.c, we were re-hashing on every call to lexaddkey if(arrayMax(Lexi1[t]) > .34 * keySetMax(LexHashTable[t])) */ if(arrayMax(Lexi1[t]) * HASH_RATE > keySetMax(LexHashTable[t])) lexReHashClass(t) ; } else lexHashInsert(t, k) ; /* NOTE: sessionUserKey() can call lexaddkey which corrupts KP, so the following code MUST follow the block above, and not vice-versa, alternatively, rewrite this stuff to eliminate the static variables and do us all a favour. - srk */ if ((t>1) && timeStamps[t]) { array(timeStamps[t], (int) k, TIMESTAMP).created = array(timeStamps[t], (int) k, TIMESTAMP).updated = sessionUserKey(); timeModif[t] = TRUE; } leximodif[t] = lexhmodif[t] = vocmodif[t] = TRUE ; lexAlphaMark (t) ; *kptr = KEYMAKE(t,k) ; chronoReturn() ; handleDestroy(h); return TRUE; } /*************************************************/ int lexMax(int t) { int nn = 0 ; if (t<0 || t>MAXTABLE) return 0 ; if (!lexIsRead[t]) lexReadTable(t) ; if (Lexi1[t]) nn = arrayMax(Lexi1[t]) ; /* nn introduced to please solaris */ return nn ; } /*************************************************/ int lexHashMax(int t) { return LexHashTable[t] ? arrayMax (LexHashTable[t]) : 0 ; } /*************************************************/ int vocMax(int t) { return Voc[t] ? stackMark(Voc[t]) : 0 ; } /*************************************************/ /*************************************************/ void lexSessionStart (void) { int t ; for (t = 0 ; t < MAXTABLE ; ++t) if (lexIsRead[t] && Lexi1[t]) { lexSessionStartSize[t] = arrayMax(Lexi1[t]) ; lexClearClassStatus (t, TOUCHSTATUS) ; } else lexSessionStartSize[t] = 0 ; keySetDestroy (touchedByClient) ; if (externalSaver) touchedByClient = keySetCreate () ; } void lexSessionEnd (void) { static int lastSession = 0 ; KEYSET newKeys = keySetCreate () ; KEYSET touchedKeys = keySetCreate () ; int t, i ; LEXI1* q ; char *cp = 0 ; STORE_HANDLE h = handleCreate(); ACEOUT new_out, touched_out; #ifndef ACEDB4 #define KEYKEY0 1 #else #define KEYKEY0 2 #endif if (lastSession == thisSession.session) { handleDestroy(h); return ; /* prevents loops */ } for (t = 0 ; t < MAXTABLE ; t++) if (!pickList[t].protected && lexIsRead[t] && Lexi1[t]) { q = 0 ; i = lexSessionStartSize[t]>KEYKEY0 ? lexSessionStartSize[t] : KEYKEY0 ; if (i < arrayMax(Lexi1[t])) { q = arrp(Lexi1[t], i, LEXI1) ; for(; i < arrayMax(Lexi1[t]) ; ++i, ++q) if (!(q->lock & ALIASSTATUS)) keySet(newKeys, arrayMax(newKeys)) = KEYMAKE(t,i) ; } if (KEYKEY0 < arrayMax(Lexi1[t])) for (i = KEYKEY0, q = arrp(Lexi1[t], i, LEXI1) ; i < arrayMax(Lexi1[t]) ; ++i, ++q) if ((q->lock & TOUCHSTATUS) && !(q->lock & ALIASSTATUS)) keySet(touchedKeys, arrayMax(touchedKeys)) = KEYMAKE(t,i) ; } if (keySetMax (newKeys)) { keySetSort (newKeys) ; #ifdef NEW_INSIDE lexaddkey (hprintf (h, "new-%s", name(thisSession.userKey)), &key, _VKeySet) ; arrayStore (key, newKeys, "k") ; #else /* !NEW_INSIDE */ #if !( defined(MACINTOSH) || defined(WIN32) ) seteuid(euid); #endif /* !(MAC || WIN) */ if (!dbPathStrictFilName("database", "new", "", "wd", h)) { char *path = dbPathMakeFilName("database", "new", "", h); int result = mkdir (path, 0755); if (result == -1) messerror ("Cannot create directory %s (%s)", path, messSysErrorText()); } cp = dbPathMakeFilName("database/new", hprintf (h, "new-%s", name(thisSession.userKey)), 0, h) ; new_out = aceOutCreateToFile (cp, "w", h); if (!new_out) { if (errno == EACCES) /* permission denied */ messout ("Please make sure the directory 'database/new' is writable !") ; else messout ("Please create a writable directory called 'database/new' !") ; cp = dbPathMakeFilName("database", hprintf (h, "new-%s", name(thisSession.userKey)), 0, h) ; new_out = aceOutCreateToFile (cp, "w", h); } if (new_out) keySetDump(new_out, newKeys) ; #if !( defined(MACINTOSH) || defined(WIN32) ) seteuid(ruid); #endif /* !(MAC || WIN) */ #endif /* !NEW_INSIDE */ } keySetDestroy (newKeys) ; if (keySetMax (touchedKeys)) { keySetSort (touchedKeys) ; #ifdef NEW_INSIDE lexaddkey (hprintf (h, "touched-%s", name(thisSession.userKey)), &key, _VKeySet) ; arrayStore (key, touchedKeys, "k") ; #else /* !NEW_INSIDE */ #if !( defined(MACINTOSH) || defined(WIN32) ) seteuid(euid); #endif /* !(MAC || WIN) */ if (!dbPathStrictFilName("database", "touched", "", "wd", h)) { char *path = dbPathMakeFilName("database", "touched", "", h); int result = mkdir (path, 0755); if (result == -1) messerror ("Cannot create directory %s (%s)", path, messSysErrorText()); } cp = dbPathMakeFilName("database/touched", hprintf (h, "touched-%s", name(thisSession.userKey)), 0, h) ; touched_out = aceOutCreateToFile (cp, "w", h); if (!touched_out) { if (errno == EACCES) /* permission denied */ messout ("Please make sure the directory 'database/touched' is writable !") ; else messout ("Please create a writable directory called 'database/touched' !") ; cp = dbPathMakeFilName("database", hprintf (h, "touched-%s", name(thisSession.userKey)), 0, h) ; touched_out = aceOutCreateToFile (cp, "w", h); } if (touched_out) keySetDump(touched_out, touchedKeys); #if !( defined(MACINTOSH) || defined(WIN32) ) seteuid(ruid); #endif /* !(MAC || WIN) */ #endif /* !NEW_INSIDE */ } keySetDestroy (touchedKeys) ; keySetDestroy (touchedByClient) ; handleDestroy(h); lastSession = thisSession.session ; return; } /* lexSessionEnd */ /************** end of file **********************/ #endif /* ACEDB4 */