This is BibliographicFile.m in view mode; [Download] [Up]
// Copyright H. Giesen, University of Koblenz-Landau 1996 #import "Controller.h" #import "BibliographicFile.h" #import "BrowserController.h" #import "BibTeXObject.h" #import "Preferences.h" #import "MyBrowserCell.h" static char *types[] = { "bib", "bib~", NULL }; struct rusage stime, etime; static int sMSec, eMSec; // temporary stream static NXStream *localStream = NULL; #define NEWLINE fwrite( "\n", sizeof(char), 1, fp ) #define OUTPUT(obj) \ { fwrite( "\n", sizeof(char), 1, fp );\ [obj copyToFile:fp];\ } #define GET_BUFFER NXGetMemoryBuffer(memStream, &theBuffer, &length, &maxLength) // special entryNames static int STRING = -1; static int PREAMBLE = -1; static Preferences *preferences = nil; @implementation BibliographicFile /************* class methods ***********/ + (BOOL) canLoadFromFile:(const char *)fName { int i; char *extension; // is fullName valid ?? extension = rindex( fName, '.' ); if( extension==0 ) return NO; extension++; for( i=0; types[i]; i++ ) if( strcmp( extension, types[i] )==0 ) return YES; return NO; } + (char **) fileTypes { return types; } /************* end of class methods ***********/ - (void)alert:(const char *)txt :(const char *)para { NXRunAlertPanel("Bibliography", txt, " OK ", // 1: default button NULL, // 0: alternate NULL, //-1: other para ); } - (BOOL)fileStatus { struct stat fBuf; int rtn = stat(myName, &fBuf); if( rtn<0 ){ [self alert:"cannot 'stat' the file\n%s" :strerror(errno)]; return NO; } lastModified = fBuf.st_mtime; return YES; } - (int) compare:(locType)a with:(locType)b { int len = (a.length < b.length) ? a.length : b.length; int rtn = strncasecmp( &theBuffer[a.start], &theBuffer[b.start], len); if( rtn!=0 ) return rtn; return (a.length - b.length); } // the following lists are supported // listOfObjects contains all bibObjects 'sorted' be creation time // sortedListOfObjects the bibObjects are sorted by key // listOfTypedObjects bibObjects sorted by key, they are the same entryType - (List *)listOfBibObjects { return listOfObjects; } - (List *)listOfSortedBibObjects { return sortedListOfObjects; } - (List *)listForType:(int)theType sorted:(BOOL)sort { int i; List *theList; if( listOfTypedObjects==nil ) listOfTypedObjects = [[List alloc] init]; [listOfTypedObjects empty]; if( entryCountList[theType]==0 ) return listOfTypedObjects; if( sort ) theList = sortedListOfObjects; else theList = listOfObjects; for(i=0; i<[theList count]; i++ ){ id obj = [theList objectAt:i]; if( [obj entryType]==theType ){ [listOfTypedObjects addObject:obj]; if( entryCountList[theType]==[listOfTypedObjects count] ) return listOfTypedObjects; // no further objects } } return listOfTypedObjects; } // inserts 'theObject' into the already sorted list 'sortedListOfObjects' // returns the index of the insertion point - (int)insertBibObject:(BibTeXObject*)theObject { int j = [sortedListOfObjects count] - 1; locType keyRange = [theObject key]; GET_BUFFER; while( j>=0 ){ // insertion sort locType keyRangeL = [[sortedListOfObjects objectAt:j] key]; if( [self compare:keyRangeL with:keyRange] > 0 ) j--; else break; } [sortedListOfObjects insertObject:theObject at:j+1]; [listOfObjects addObjectIfAbsent:theObject]; return (j+1); } // comb sort - (void) combSort:(List *)aList { int gap, switches, top, i, j; int numElements = [aList count]; gap = numElements; do{ gap = (int)( (float)gap/1.3 ); switch( gap ){ case 0 : gap = 1; break; case 9 : case 10: gap = 11; break; default: break; } switches = 0; // no swaps top = numElements - gap; for( i=0; i<top; i++ ){ id hi, hj; locType a, b; j = i+gap; hi = [aList objectAt:i]; a = [hi key]; hj = [aList objectAt:j]; b = [hj key]; if( [self compare:a with:b] > 0 ) { switches++; // swap with neighbor [aList replaceObjectAt:i with:hj]; [aList replaceObjectAt:j with:hi]; } } } while( switches || gap>1 ); } // creates and parses a new bibObject // returns this object if successful else nil - (BibTeXObject*)parseNewObject { int count; id newObject = [[BibTeXObject alloc] initFromStream:memStream owner:self]; if( report ){ if( report==-2 ) return nil; // abort } return newObject; if( newObject ){ count = entryCountList[ [newObject entryType] ]; entryCountList[ [newObject entryType] ]++; if( count==0 ){ // was empty [[[NXApp delegate] browserController] entryListDidChange:[newObject entryType] to:1]; } } return newObject; } - (int)countForType:(int)theType { return entryCountList[ theType ]; } - (void) initObjectList { id obj; getrusage( RUSAGE_SELF, &stime ); listOfObjects = [[List alloc] initCount:50]; [BibTexParser initParsing]; GET_BUFFER; report = 0; // get the first entryObject obj = [self parseNewObject]; if( obj ){ headerLength = [obj range].start; } // the following builds an entryObject-list without sorting while( obj ){ [self addBibObject:obj]; obj = [self parseNewObject]; } sortedListOfObjects = [listOfObjects copyFromZone:[listOfObjects zone]]; // now sort with comb sort [self combSort:sortedListOfObjects]; getrusage( RUSAGE_SELF, &etime ); sMSec = stime.ru_utime.tv_sec*1000 + stime.ru_utime.tv_usec/1000; eMSec = etime.ru_utime.tv_sec*1000 + etime.ru_utime.tv_usec/1000; uTime = eMSec - sMSec; sMSec = stime.ru_stime.tv_sec*1000 + stime.ru_stime.tv_usec/1000; eMSec = etime.ru_stime.tv_sec*1000 + etime.ru_stime.tv_usec/1000; sTime = eMSec - sMSec; } - (int)uTime { return uTime; } - (int)sTime { return sTime; } - parsingReport:(int)msg { report = msg; return self; } - (char *)fullPath { return myName; } - (char *)fileName { return rindex( myName, '/' )+1; } - (int) fileSize { GET_BUFFER; return length; } - (int) entries { return [listOfObjects count]; } - (NXStream *)stream { return memStream; } - (int)lastModified { return (int)lastModified; } - setDirty { isDirty = YES; return self; } - (BOOL)isDirty { return isDirty; } - (BOOL)isNewFile { return isNewFile; } - (void) getSpecials { if( preferences==nil ){ preferences = [Preferences new]; STRING = [preferences indexOfEntryname:"STRING"]; PREAMBLE = [preferences indexOfEntryname:"PREAMBLE"]; } } - initNewFile:(const char *)fName { [super init]; [self getSpecials]; myName = NXCopyStringBuffer(fName); memStream = NXOpenMemory( NULL, 0, NX_READWRITE ); listOfObjects = [[List alloc] initCount:50]; sortedListOfObjects = [[List alloc] initCount:50]; isNewFile = YES; return self; } - initFromFile:(const char *)fName { if( fName==NULL) return [super free]; // should never happen [super init]; [self getSpecials]; myName = NXCopyStringBuffer(fName); // read the file memStream = NXMapFile(myName, NX_READWRITE); if( memStream==NULL ){ free( myName ); return [super free]; } isNewFile = NO; if( [self fileStatus]==NO ) return [super free]; [self initObjectList]; fLength = NXTell( memStream ); // here start new/updated entries //fprintf( stderr, "size is %d\n", fLength ); lastSearchPos = -1; return self; } - changeFilenameTo:(const char *)newName { if( myName ) free( myName ); myName = NXCopyStringBuffer(newName); isDirty = YES; return self; } - saveList:(List *)aList to:(FILE *)fp { int i; if( [aList count] ){ for(i=0; i< [aList count]; i++ ) OUTPUT( [aList objectAt:i] ); } return self; } // save returns -1 on error, NX_OKTAG otherwise - (int) save { int i; List *typedList; FILE *fp; fp = fopen( myName, "w+" ); if( fp==NULL ) return -1; if( [self fileStatus]==NO ) return -1; GET_BUFFER; // first write out the file header if( headerLength>2 ) fwrite( &theBuffer[ 0 ], sizeof(char), headerLength-1, fp ); // first write out all PREAMBLEs typedList = [self listForType:PREAMBLE sorted:YES]; [self saveList:typedList to:fp]; // then write out all STRINGs typedList = [self listForType:STRING sorted:YES]; [self saveList:typedList to:fp]; for(i=0; i< [sortedListOfObjects count]; i++ ){ id obj = [sortedListOfObjects objectAt:i]; if( [obj entryType]==STRING ) continue; if( [obj entryType]==PREAMBLE ) continue; // crossref should be the last to print NEWLINE; OUTPUT(obj); } NEWLINE; NEWLINE; fclose( fp ); isDirty = NO; isNewFile = NO; [self fileStatus]; // ????? return NX_OKTAG; } - objectAt:(unsigned int)index { return [sortedListOfObjects objectAt:index]; } - objectAtCharpos:(int)pos andSelectInMatrix:matrix { int i; locType range; for( i=0; i<[sortedListOfObjects count]; i++ ){ range = [[sortedListOfObjects objectAt:i] range]; if( (range.start<=pos) && (pos<(range.start + range.length)) ){ if( matrix ){ [matrix selectCellAt:i :0]; [matrix scrollCellToVisible:i :0]; } return [sortedListOfObjects objectAt:i]; } } return NULL; } - showFile { // only for testing : if( theText==NULL ) [NXApp loadNibSection:"BibText.nib" owner:self]; NXFlush(memStream); NXSeek(memStream, 0, NX_FROMSTART); [theText readText:memStream]; [[theText window] setTitleAsFilename:myName]; [[theText window] orderFront:self]; return self; } - findKey:(const char *)theKey { int left=0; int right=[sortedListOfObjects count]-1; char keyBuffer[64]; GET_BUFFER; // invariant : theKey must be between left and right while( left <= right ){ int cmp; int i=(left + right)/2; id theObj = [sortedListOfObjects objectAt:i]; locType keyRange = [theObj key]; [theObj copy:keyRange.start :keyRange.length toBuffer:keyBuffer]; cmp = strcasecmp( keyBuffer, theKey ); if( cmp == 0 ) return theObj; if( cmp > 0 ) right = i-1; else left = i+1; } // invariant : theKey must be between 'left' and 'right' return nil; // not found } - free { [sortedListOfObjects free]; [listOfTypedObjects free]; [[listOfObjects freeObjects] free]; free( myName ); NXCloseMemory( memStream, NX_FREEBUFFER ); if( theText )[[theText window] free]; return [super free]; } // adds a new bibObject to the list of bibObjects and // updates the type counter // if a sorted bibObject list exists this object is inserted // with insertion sort. This list does not exist during initialisation - (void) addBibObject:(BibTeXObject*)obj { int j; locType keyRange; [listOfObjects addObjectIfAbsent:obj]; entryCountList[ [obj entryType] ]++; if( entryCountList[ [obj entryType] ]==1 ){ [[[NXApp delegate] browserController] entryListDidChange:[obj entryType] to:1]; } if( sortedListOfObjects==nil ) return; j = [sortedListOfObjects count] - 1; keyRange = [obj key]; GET_BUFFER; while( j>=0 ){ // insertion sort locType keyRangeL = [[sortedListOfObjects objectAt:j] key]; if( [self compare:keyRangeL with:keyRange] > 0 ) j--; else break; } [sortedListOfObjects insertObject:obj at:j+1]; } - (void) deleteBibObject:(BibTeXObject*)obj { [sortedListOfObjects removeObject:obj]; [listOfObjects removeObject:obj]; entryCountList[ [obj entryType] ]--; if( entryCountList[ [obj entryType] ]==0 ){ [[[NXApp delegate] browserController] entryListDidChange:[obj entryType] to:0]; } [listOfTypedObjects removeObject:obj]; [obj free]; } - (void) changeKeyOfBibObject:(BibTeXObject*)obj { [sortedListOfObjects removeObject:obj]; [self insertBibObject:obj]; } - (void) changeTypeOfBibObject:(BibTeXObject*)obj from:(int)old to:(int)new { entryCountList[ old ]--; if( entryCountList[ old ]==0 ){ [[[NXApp delegate] browserController] entryListDidChange:old to:0]; } entryCountList[ new ]++; if( entryCountList[ new ]==1 ){ [[[NXApp delegate] browserController] entryListDidChange:new to:1]; } } - addObjects:(List *)objList // list of matrixCells { int i; id obj; int lastPos; BOOL sl; char *localBuffer; int locBufferLength, locBufferMaxLength; if ( localStream==NULL ){ localStream = NXOpenMemory( NULL, 0, NX_READWRITE ); } NXSeek(localStream, 0, NX_FROMSTART); for( i=0; i<[objList count]; i++ ){ NXPutc( localStream, '\n' ); [[[objList objectAt:i] object] copyToStream:localStream]; NXPutc( localStream, '\n' ); } NXFlush( localStream ); lastPos = NXTell( localStream ); NXGetMemoryBuffer( localStream, &localBuffer, &locBufferLength, &locBufferMaxLength); NXSeek( memStream, 0, NX_FROMEND); // append here NXWrite( memStream, localBuffer, lastPos ); NXFlush( memStream ); // parse the new string NXSeek( memStream, -lastPos, NX_FROMEND); // go back obj = [self parseNewObject]; sl = NO; while( obj ){ [self addBibObject:obj]; [[NXApp delegate] update:obj for:self isNew:YES shiftLit:sl]; sl = YES; obj = [self parseNewObject]; } isDirty = YES; return self; } - saveChanges:(int)len :(char *)buffer { char backUpname[512]; struct stat fBuf; int rtn; // check if we have already a backupfile if( changeFilePointer==NULL ){ // create a new backupfile if( preferences==nil ){ preferences = [Preferences new]; } sprintf( backUpname, "%s/_%s", [preferences tempDirectory], [self fileName] ); rtn = stat(backUpname, &fBuf); // if rtn==0 a file with this name already exists changeFilePointer = fopen( backUpname, "w+" ); if(0)fprintf( stderr, "new file %s (%d) rtn=%d\n", backUpname, (unsigned)changeFilePointer, rtn ); if( changeFilePointer==NULL ){ fprintf( stderr, "cannot open %s\n", backUpname ); return self; } } //fprintf( stderr, "save %d byte\n", len ); fwrite( "\n", sizeof(char), 1, changeFilePointer ); fwrite( &buffer[ 0 ], sizeof(char), len, changeFilePointer ); fwrite( "\n", sizeof(char), 1, changeFilePointer ); fflush( changeFilePointer ); return self; } - appendNewText:(int)len :(char *)buffer { id newObject, rtnObject; NXSeek( memStream, 0, NX_FROMEND); // append here NXPrintf( memStream, "\n\n" ); NXWrite( memStream, buffer, len ); NXFlush( memStream ); [self saveChanges:len :buffer]; // parse the new text NXSeek( memStream, -len-1, NX_FROMEND); // go back rtnObject = newObject = [self parseNewObject]; while( newObject ){ [self addBibObject:newObject]; [[NXApp delegate] update:newObject for:self isNew:YES shiftLit:NO]; newObject = [self parseNewObject]; if( newObject ) rtnObject = newObject; } isDirty = YES; return rtnObject; } // this method is called if the object's text did change - replaceTextOf:(BibTeXObject*)obj with:(char *)buffer length:(int)len { locType newRange; int oldEntryType = [obj entryType]; locType keyRangeOld = [obj key]; NXSeek( memStream, 0, NX_FROMEND); // append here newRange.start = NXTell( memStream ); // new position NXWrite( memStream, buffer, len ); NXFlush( memStream ); [self saveChanges:len :buffer]; newRange.length = len; [obj setRange:newRange]; [obj parseSelf]; // parse again isDirty = YES; GET_BUFFER; if( [self compare:keyRangeOld with:[obj key]] != 0 ){ [self changeKeyOfBibObject:obj]; // key did change } if( oldEntryType==[obj entryType] ) return self; [self changeTypeOfBibObject:obj from:oldEntryType to:[obj entryType]]; return self; } @end /*************** category for the List class ***********/ @implementation List(exch) - exchange:(int)i :(int)j; { id h = [self objectAt:i]; [self replaceObjectAt:i with:[self objectAt:j]]; [self replaceObjectAt:j with:h]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.