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.