home *** CD-ROM | disk | FTP | other *** search
- #include "datapriv.hpp"
-
- /*
- This file contains the routines concerned with the generation and use
- of index files, and contains the routines for writing a new or changed
- record, and finding a record in the index file
- */
-
- /****************************************************************************
- * *
- * The following routines are all coupled to records *
- * *
- ****************************************************************************/
-
- /********************************************************************
-
- Index Creation Stuff
-
- *********************************************************************/
-
- // Check an index expression and return its length
-
- int record::indchk(char *ex,int &rtype)
- {
- char *wsp;
- op *rv;
-
- if (!db->ex)
- {
- db->ex=new expval(db);
- db->exwork=new char[512];
- }
- if (!db->ex || !db->exwork || db->ex->erflag==2) return 0;
- db->ex->erflag=0;
-
- wsp=db->exwork;
- db->ex->exetoken(ex,&wsp);
- if (db->ex->erflag==1) {db->ex->erflag=3; return 0;} // show tokeniser error
-
- wsp=db->exwork;
- indflg=1;
- rv=db->ex->exeval(&wsp,this);
- if (db->ex->erflag) return 0;
-
- rtype=rv->optype;
- if (rtype==OPINT) return 8;
- if (rtype==OPSTR)
- {
- if (rv->oplen<1) strcpy(db->ers,"Index length must be greater than 0");
- if (rv->oplen>100) strcpy(db->ers,"Index length must be 100 or less");
- return (rv->oplen>0 && rv->oplen<101) ? rv->oplen : 0;
- }
- if (rtype==OPDATE) return 8;
- strcpy(db->ers,"No Logical expr. in index"); return 0; // Must be logical
- }
-
- /********************* RECORD::GETIND ************************/
-
- // Get the specified index record, we know already that there is one
- // 'cos the calling function has checked the bounds.
-
- long record::getind(long n)
- {
- long ln=n; // Local copy of n
- struct indpt *npt; // Useful pointer
- int dirn=1; // + or - 1 depending on next/previous
-
- if (n==FIRST || n==LAST) // Return to top level indclus
- {
- while(curind->icp->parent)
- {
- npt=curind;
- curind=curind->parent;
- delete npt;
- }
- if (n==FIRST)
- {ln=NEXT; curind->currec=-1;} // FIRST
- else
- {curind->currec=curind->icp->nclusrec; ln=PREVIOUS;} // LAST
- }
-
- if (ln==PREVIOUS) dirn=-1;
-
- while (1) // Select next record
- {
- if (!curind->icp->clustyp) // Is this a record cluster ?
- {
- if ((ln==NEXT && curind->currec<curind->icp->nclusrec-1) ||
- (ln==PREVIOUS && curind->currec>0)) // Get next record
- {
- curind->currec+=dirn;
- long rv=*(long *)(curind->icp->clusdat+8+curind->currec*curind->icp->reclen);
- return(rv);
- }
- else // Run out of records in this cluster
- {
- if (!curind->parent) return 0;
- npt=curind->parent; delete curind; // Get parent record
- curind=npt;
- }
- }
- else // We have a pointer cluster
- {
- long nclus;
-
- if ((ln==NEXT && curind->currec<curind->icp->nclusrec-1) ||
- (ln==PREVIOUS && curind->currec>0)) // Get next record
- {
- curind->currec+=dirn;
- nclus=*(long *)(curind->icp->clusdat+4+curind->currec*curind->icp->reclen);
- curind=new indpt(nclus,curind,curind->icp->oi);
- if (ln==PREVIOUS) curind->currec=curind->icp->nclusrec;
- }
- else // Go back to parent
- {
- if (!curind->parent)
- {
- if (ln==NEXT) getind(LAST); else getind(FIRST);
- return 0;
- }
- npt=curind->parent; delete curind;
- curind=npt;
- }
- }
- }
- }
-
-
- /********************** SELKEY ********************************/
-
- // Select record by key
-
- int record::selkey(double value,int type)
- {
- char ws[10];
-
- *(double *)ws=value;
-
- return selkey(ws,type,OPINT);
- }
-
- // Character or Double, flag shows which
- // Note in this function if type is READIND then the record is not read
- // from disk,l this is used purely to update the index pointers for a
- // newly written record
-
- int record::selkey(char *value,int type,int num)
- {
- int i;
- int ret=-1;
-
- if (!curind) return CANTSEL;
- memcpy(lkey,value,curind->icp->explen+1);
- ltype=type;
- lseltype=num;
-
- indpt *npt; // Now go back up to top level index
-
- while(curind->parent) {npt=curind; curind=curind->parent; delete npt;}
- delete curind;
- curind=new indpt(oi->topind);
-
- curind=curind->selkey(value,num,ret,i);
- rn=*(long *)(curind->icp->clusdat+8+i*curind->icp->reclen);
- if (type!=READIND) select(rn,ALL,1);
-
- while((type==DEL && *recbuf==' ') || (type==NOTDEL && *recbuf=='*'))
- {
- if (ret=selkey()) return ret;
- }
- return ret;
- }
-
- // Get the next record which matches the current record key
-
- int record::selkey(void)
- {
- char *keyp;
-
- if (ltype==-1) return CANTSEL;
-
- do
- {
- long tn;
-
- if (!(tn=getind(NEXT))) return CANTSEL;
-
- keyp=curind->icp->clusdat+(curind->currec)*curind->icp->reclen+12;
- if ((lseltype!=OPINT && strcmpdb(keyp,lkey,curind->icp->explen)) ||
- (lseltype==OPINT && *(double *)lkey-*(double *)keyp)) return NOKEY;
-
- rn=tn;
- select(rn,ALL,1);
- }
- while((ltype==DEL && *recbuf==' ') || (ltype==NOTDEL && *recbuf=='*'));
-
- return 0;
- }
-
- // Delete a key from the record
- // Use a new record to find this records key in the index file, when
- // we find it delete the key
-
- void record::delkey(char *oldkey,index *ip)
- {
- record rec(*db,ip->name); // Create record used to search index file
- rec.selkey(oldkey,ALL,ip->indtype); // Now find this record in the index file
- while(rec.rn!=rn)
- {
- if (rec.selkey())
- {
- dbfer(NODELKEY);
- return;
- }
- }
- rec.curind->icp->subtract(rec.curind->currec); // Delete key from tree
- }
-
- // Insert a new key from the current record
-
- void record::inskey(char *newkey,index *ip)
- {
- int rsk; // Return value from selkey
- int irn; // Record value found by selkey
-
- indpt *clin=new indpt(ip->tblk,0,ip); // Cluster to insert key
- clin=clin->selkey(newkey,ip->indtype,rsk,irn); // Find nearest key in index
-
- clin->icp->add(newkey,rn,irn,rsk); // Add the new key
-
- indpt *nin;
- while(clin->parent) {nin=clin->parent; delete clin; clin=nin;} delete clin;
- }
-
- /**************************************************************************
-
- Routines concerned with the index structure
-
- **************************************************************************/
-
- index::~index()
- {
- if (chng)
- {
- *(long *)fclus=tblk;
- *(long *)(fclus+4)=lblk;
- Fseek(fp,0,SEEK_SET);
- Fwrite(fclus,512,1,fp);
- }
-
- indclus *ip,*nip;
-
- ip=topind; while(ip) {nip=ip->next; delete ip; ip=nip;}
- fclose(fp);
- }
-
- /***************** Add an index to the database file ********************/
-
- int database::addindex(char *iname)
- {
- index *ip,*nip,*lip;
- char ws[128],*fcp,*wsp;
- FILE *fp;
-
- strcpy(ws,iname);
- if (wsp=strchr(ws,'.')) *wsp=0; strcat(ws,".NDX");
- if (!(fp=fopen(ws,"r+b"))) return NOINDEX;
-
- *strchr(ws,'.')=0;
- if (!(wsp=strrchr(ws,'\\'))) wsp=ws; else wsp++;
- strupr(wsp);
- if (getindex(wsp)) {delete ip; return INVIND;}
-
- ip=new index;
- ip->next=0;
- ip->fp=fp;
- strcpy(ip->fname,iname); // Open files & enter names
- strcpy(ip->name,wsp);
-
- fcp=ip->fclus;
- Fread(fcp,512,1,fp); /// Read the header cluster
- ip->nclus=0;
- ip->topind=0;
- ip->topind=new indclus(*(long *)fcp,0,ip);
-
- ip->topind->reclen=*(int *)(fcp+INDLEN); // Length of index
- ip->topind->explen=*(int *)(fcp+INDELEN); // Expression length
- ip->tblk=*(long *)(fcp); // Top Block
- ip->lblk=*(long *)(fcp+4); // Last Block
- ip->maxclusrec=*(long *)(fcp+14); // Max records/cluster
- ip->firstrec=0; // First record using cluster
- ip->chng=0; // Change flag
- strcpy(ip->indexp,fcp+INDEXP); // Index expression
- if (!ex)
- {
- ex=new expval(this);
- exwork=new char[512];
- }
- if (!ex || !exwork || ex->erflag==2) return NOINDEX;
- wsp=ip->tindexp;
- ex->erflag=0;
- ex->exetoken(fcp+INDEXP,&wsp); // Tokenise it
- if (ex->erflag)
- {
- if (ex->erflag==1) ex->erflag=3; // Invalid index expression
- delete ip;
- return INVIND;
- }
- ip->indtype=(*(int *)(fcp+INDTYPE)) ? OPINT : OPSTR; // Index type
- switch(*(fcp+9))
- {
- case 'N' : ip->restype=OPINT; break;
- case 'D' : ip->restype=OPDATE; break;
- case 'C' : ip->restype=OPSTR; break;
- default : dbfer(INVIND); break;
- }
-
- // Finally we got here so link the index into the tree
-
- if (!findex) findex=ip; // First index
- else
- {
- lip=findex; nip=findex->next;
- while(nip) {lip=nip; nip=lip->next;} // Add to end of tree
- lip->next=ip;
- }
-
- return 0;
- }
-
- // Remove an index from this database
-
- int database::subindex(char *name)
- {
- index *nip,*lip,*ip;
-
- if (!(ip=getindex(name))) return NOINDEX;
-
- if (ip==findex) findex=ip->next;
- else
- {
- lip=findex;
- nip=findex->next;
-
- while(nip!=ip) {lip=nip; nip=lip->next;}
- lip->next=nip->next;
- }
-
- delete ip;
- return 0;
- }
-
-