This is dbmAdaptor.m in view mode; [Download] [Up]
/*
* File: dbmAdaptor.m
*
* Author: Mike Henry
* Created: 24 Oct 1993
*
* Description: A simple adaptor using DBM
*
* Copyright (c) Mike Henry, 1993. All Rights Reserved.
*/
#import "dbmAdaptor.h"
#import <appkit/Application.h>
#import <appkit/NXHelpPanel.h>
#import <appkit/Panel.h>
#import <appkit/nextstd.h>
#import <architecture/byte_order.h>
#import <dbkit/DBAttribute.h>
#import <dbkit/DBBinder.h>
#import <dbkit/DBDatabase.h>
#import <dbkit/DBEntity.h>
#import <dbkit/expressionValues.h>
#import <dbkit/DBRelationship.h>
#import <dbkit/DBQualifier.h>
#import <objc/NXBundle.h>
#import <objc/hashtable.h>
BOOL yyError;
NXZone* yyZone;
DBBinder* yyBinder;
dbmAdaptor* yyAdaptor;
const char* yyInput;
extern yyparse();
static const char* const dbmAdaptorVersion[] =
{
"@(#) gdbmAdaptor",
"@(#) Release 3.3 (v0.5)",
"@(#) Copyright (c) Mike Henry 1995. All Rights Reserved.",
"@(#) Send suggestions and bug reports to mike@enea.se"
};
#define NAME (dbmAdaptorVersion[0]+5)
#define VERSION (dbmAdaptorVersion[1]+5)
#define COPYRIGHT (dbmAdaptorVersion[2]+5)
#define COMMENTS (dbmAdaptorVersion[3]+5)
@implementation dbmAdaptor
- initForDatabase:(DBDatabase*)aDatabase
{
#ifdef GDBM
fprintf(stderr, "*** This is %s, %s\n*** %s\n*** %s\n*** %s\n",
NAME, VERSION, COPYRIGHT, COMMENTS, gdbm_version);
#else
fprintf(stderr, "*** This is %s, %s\n*** %s\n*** %s\n",
NAME, VERSION, COPYRIGHT, COMMENTS);
#endif
bundle = [NXBundle bundleForClass:[self class]];
if(helpPanel = [NXHelpPanel new])
[helpPanel addSupplement:"Help" inPath:[bundle directory]];
database = aDatabase;
[database setName:NAME];
return self;
}
/* This method is usually called by the DBDatabase object */
- (BOOL)connectUsingString:(const char*)aString
{
int i, count;
List* entities;
char dbmInfoFile[MAXPATHLEN+1];
if(isOpen)
return NO;
isOpen = YES;
if(aString == 0)
aString = NXCopyStringBufferFromZone("NoName.dbm", [self zone]);
wrapper = (char*)NXCopyStringBufferFromZone(aString, [self zone]);
if(mkdir(wrapper, 00755) == -1 && errno != EEXIST)
{
fprintf(stderr, "%s: Couldn't create database wrapper %s: %s.\n",
NAME, wrapper, sys_errlist[errno]);
return NO;
}
strcpy(dbmInfoFile, wrapper);
strcat(dbmInfoFile, "/dbmInfo");
#ifdef GDBM
if((dbmInfo = gdbm_open(dbmInfoFile, 512, GDBM_WRCREAT, 00644, NULL)) == 0)
#else
if((dbmInfo = dbm_open(dbmInfoFile, O_CREAT|O_RDWR, 00644)) == 0)
#endif
{
fprintf(stderr, "%s: Couldn't open dbmInfo file: %s.\n",
NAME, dbmInfoFile);
return NO;
}
entities = [[List allocFromZone:[self zone]] init];
[database getEntities:entities];
count = [entities count];
for(i = 0; i < count; i++)
{
char entityDir[MAXPATHLEN+1];
dbm_t start = NXSwapHostLongLongToBig(0);
datum key, value;
const char* entityName = [[entities objectAt:i] expressionValue];
value.dptr = (char*)&start;
value.dsize = sizeof(dbm_t);
key.dsize = strlen(entityName) + 5;
NX_MALLOC(key.dptr, char, key.dsize);
strcat(strcpy(key.dptr, entityName), SIZE_INFO);
#ifdef GDBM
gdbm_store(dbmInfo, key, value, GDBM_INSERT);
strcat(strcpy(key.dptr, entityName), ROW_INFO);
gdbm_store(dbmInfo, key, value, GDBM_INSERT);
strcat(strcpy(key.dptr, entityName), HOLE_INFO);
gdbm_store(dbmInfo, key, value, GDBM_INSERT);
strcat(strcpy(key.dptr, entityName), COUNT_INFO);
gdbm_store(dbmInfo, key, value, GDBM_INSERT);
#else
dbm_store(dbmInfo, key, value, DBM_INSERT);
strcat(strcpy(key.dptr, entityName), ROW_INFO);
dbm_store(dbmInfo, key, value, DBM_INSERT);
strcat(strcpy(key.dptr, entityName), HOLE_INFO);
dbm_store(dbmInfo, key, value, DBM_INSERT);
strcat(strcpy(key.dptr, entityName), COUNT_INFO);
dbm_store(dbmInfo, key, value, DBM_INSERT);
#endif
NX_FREE(key.dptr);
strcat(strcat(strcpy(entityDir, wrapper), "/"), entityName);
if(mkdir(entityDir, 00755) == -1 && errno != EEXIST)
{
fprintf(stderr, "%s: Couldn't create wrapper for entity %s: %s.\n",
NAME, entityDir, sys_errlist[errno]);
#ifdef GDBM
gdbm_close(dbmInfo);
#else
dbm_close(dbmInfo);
#endif
[entities free];
return NO;
}
}
[entities free];
return YES;
}
- (BOOL)disconnectUsingString:(const char*)aString
{
if(!isOpen)
return NO;
#ifdef GDBM
gdbm_close(dbmInfo);
#else
dbm_close(dbmInfo);
#endif
NXZoneFree([self zone], wrapper);
wrapper = 0;
isOpen = NO;
return YES;
}
- (BOOL)isConnected
{
return isOpen;
}
- (BOOL)deleteData:(DBBinder*)binder
{
id entity;
int i, count;
BOOL gotOne = NO;
List* list;
datum key;
if(!isOpen)
return NO;
key = [self getKeyForBinder:binder];
if(key.dptr == NULL)
return NO;
list = [[List allocFromZone:[self zone]] init];
count = [[binder getProperties:list] count];
entity = [[list lastObject] entity];
for(i = 0; i < count; i++)
{
id property = [list objectAt:i];
DBM_FILE db = [self openDBMFileForProperty:property];
if(db == 0)
continue;
if(!gotOne && ![property isEntity]) {
DBM_FILE dbi;
char file[MAXPATHLEN+1];
datum index;
dbm_t arch_dep, nholes, currentSize;
strcat(strcat(strcpy(file, wrapper), "/"), [entity expressionValue]);
#ifdef GDBM
if((dbi = gdbm_open(file, 0, GDBM_WRCREAT, 00644, NULL)) == 0)
#else
if((dbi = dbm_open(file, O_CREAT|O_RDWR, 00644)) == 0)
#endif
{
NX_FREE(key.dptr);
[list free];
return NO;
}
currentSize = [self get:SIZE_INFO for:entity];
[self set:SIZE_INFO for:entity with:(currentSize-1)];
nholes = [self get:HOLE_INFO for:entity];
[self set:HOLE_INFO for:entity with:(nholes+1)];
arch_dep = NXSwapHostLongLongToBig(nholes);
index.dptr = (char*)&arch_dep;
index.dsize = sizeof(dbm_t);
#ifdef GDBM
gdbm_store(dbi, index, key, GDBM_INSERT);
gdbm_close(dbi);
#else
dbm_store(dbi, index, key, DBM_INSERT);
dbm_close(dbi);
#endif
gotOne = YES;
}
#ifdef GDBM
if(gdbm_delete(db, key))
#else
if(dbm_delete(db, key))
#endif
fprintf(stderr,
"%s: Cannot delete data for key: %x.\n",
NAME,
(int)NXSwapBigLongLongToHost(*(dbm_t*)key.dptr));
#ifdef GDBM
gdbm_close(db);
#else
dbm_close(db);
#endif
}
NX_FREE(key.dptr);
[list free];
return YES;
}
- (BOOL)updateData:(DBBinder*)binder
{
BOOL bool;
datum key;
if(!isOpen)
return NO;
key = [self getKeyForBinder:binder];
bool = [self storeForKey:key withBinder:binder];
NX_FREE(key.dptr);
return bool;
}
- (BOOL)insertData:(DBBinder*)binder
{
id entity;
List* list;
datum key;
dbm_t size, nholes;
if(!isOpen)
return NO;
list = [binder getProperties:[[List allocFromZone:[self zone]] init]];
entity = [[list lastObject] entity];
size = [self get:SIZE_INFO for:entity];
nholes = [self get:HOLE_INFO for:entity];
if(nholes > 0)
{
datum index;
DBM_FILE dbh;
char holeFile[MAXPATHLEN+1];
strcat(strcat(strcpy(holeFile, wrapper), "/"), [entity expressionValue]);
#ifdef GDBM
if((dbh = gdbm_open(holeFile, 0, GDBM_WRCREAT, 00644, NULL)) == 0)
#else
if((dbh = dbm_open(holeFile, O_CREAT|O_RDWR, 00644)) == 0)
#endif
{
fprintf(stderr, "%s: Couldn't open hole database %s.\n", NAME, holeFile);
[list free];
return NO;
}
#ifdef GDBM
index = gdbm_firstkey(dbh);
#else
index = dbm_firstkey(dbh);
#endif
if(index.dptr == 0)
{
fprintf(stderr, "%s: Couldn't find any holes.\n", NAME);
#ifdef GDBM
gdbm_close(dbh);
#else
dbm_close(dbh);
#endif
[list free];
return NO;
}
#ifdef GDBM
key = gdbm_fetch(dbh, index);
#else
key = dbm_fetch(dbh, index);
#endif
if(key.dptr == 0)
{
fprintf(stderr, "%s: No rows for hole.\n", NAME);
#ifdef GDBM
free(index.dptr);
gdbm_close(dbh);
#else
dbm_close(dbh);
#endif
[list free];
return NO;
}
if([self storeForKey:key withBinder:binder] == NO)
{
fprintf(stderr, "%s: Couldn't store new row for hole.\n", NAME);
#ifdef GDBM
free(index.dptr);
gdbm_close(dbh);
#else
dbm_close(dbh);
#endif
[list free];
return NO;
}
[self set:HOLE_INFO for:entity with:(nholes-1)];
#ifdef GDBM
if(gdbm_delete(dbh, index) != 0)
#else
if(dbm_delete(dbh, index) != 0)
#endif
{
fprintf(stderr, "%s: Couldn't delete used hole.\n", NAME);
#ifdef GDBM
free(key.dptr);
free(index.dptr);
gdbm_close(dbh);
#else
dbm_close(dbh);
#endif
[list free];
return NO;
}
#ifdef GDBM
free(key.dptr);
free(index.dptr);
gdbm_close(dbh);
#else
dbm_close(dbh);
#endif
}
else
{
dbm_t arch_dep = NXSwapHostLongLongToBig(size);
key.dptr = (char*)&arch_dep;
key.dsize = sizeof(dbm_t);
if([self storeForKey:key withBinder:binder] == NO)
{
fprintf(stderr, "%s: Couldn't store new row for key.\n", NAME);
[list free];
return NO;
}
[self set:SIZE_INFO for:entity with:(size+1)];
}
[list free];
return YES;
}
/* This method gets called when doing a "fetchAllRecords" at the IB level */
- (BOOL)selectData:(DBBinder*)binder
{
id entity;
List* list;
if(!isOpen)
return NO;
list = [[List allocFromZone:[self zone]] init];
entity = [[[binder getProperties:list] lastObject] entity];
[self set:ROW_INFO for:entity with:0];
[self set:COUNT_INFO for:entity with:0];
[list free];
return YES;
}
- (BOOL)fetchData:(DBBinder*)binder
{
id entity;
int i, count;
List* list;
dbm_t found = 0, currentRow = 0;
if(!isOpen)
{
fprintf(stderr, "%s: fetch: Database not open.\n", NAME);
return NO;
}
list = [[List allocFromZone:[self zone]] init];
count = [[binder getProperties:list] count];
entity = [[list lastObject] entity];
if([binder qualifier] && ![[binder qualifier] isEmpty])
{
dbm_t row = [self get:ROW_INFO for:entity];
dbm_t size = [self get:SIZE_INFO for:entity];
const char* qual = [[binder qualifier] expressionValue];
while(![self evaluateString:qual using:binder])
if(row < size)
[self set:ROW_INFO for:entity with:++row];
else
{
[list free];
return NO;
}
}
for(i = 0; i < count; i++)
{
id property = [list objectAt:i];
id value = [binder valueForProperty:property];
DBM_FILE db = [self openDBMFileForProperty:property];
datum key, data = {0, 0};
dbm_t arch_dep;
if(db == 0)
{
fprintf(stderr, "%s: Couldn't open entity: %s.\n",
NAME, [entity expressionValue]);
[list free];
return NO;
}
found = [self get:COUNT_INFO for:entity];
currentRow = [self get:ROW_INFO for:entity];
arch_dep = NXSwapHostLongLongToBig(currentRow);
key.dptr = (char*)&arch_dep;
key.dsize = sizeof(dbm_t);
#ifdef GDBM
data = gdbm_fetch(db, key);
#else
data = dbm_fetch(db, key);
#endif
if(data.dptr == 0)
{
#ifdef GDBM
gdbm_close(db);
#else
dbm_close(db);
#endif
if((([self get:HOLE_INFO for:entity] - found) > 0) &&
([self get:SIZE_INFO for:entity] > currentRow))
{
[self set:COUNT_INFO for:entity with:(found+1)];
[self set:ROW_INFO for:entity with:(currentRow+1)];
[list free];
return [self fetchData:binder];
}
[list free];
return NO;
}
// Eeek!! This switch should be replaced by a dynamically bound method;
if(data.dsize == 0)
[value setNull];
else
switch([[property propertyType] objcType][0])
{
case '*':
[value setStringValue:data.dptr];
break;
case 'i':
[value setIntValue:NXSwapBigIntToHost(*(int*)data.dptr)];
break;
case 'f':
[value setFloatValue:NXSwapBigFloatToHost(*(float*)data.dptr)];
break;
case 'd':
[value setDoubleValue:NXSwapBigDoubleToHost(*(double*)data.dptr)];
break;
case '@':
{
NXStream* stream = NXOpenMemory(data.dptr, data.dsize, NX_READONLY);
[value setObjectValue:NXReadObject(stream)];
NXCloseMemory(stream, NX_FREEBUFFER);
}
break;
default:
[value setNull];
break;
}
#ifdef GDBM
free(data.dptr);
gdbm_close(db);
#else
dbm_close(db);
#endif
}
[self set:ROW_INFO for:entity with:(currentRow+1)];
[list free];
return YES;
}
- (void)fetchDone:(DBBinder*)binder
{
id entity;
List* list;
dbm_t row, size;
list = [binder getProperties:[[List allocFromZone:[self zone]] init]];
entity = [[list lastObject] entity];
row = [self get:ROW_INFO for:entity];
size = [self get:SIZE_INFO for:entity];
[self set:SIZE_INFO for:entity with:(size>row?size:row)];
[list free];
}
- (BOOL)evaluateString:(const char*)string using:(DBBinder*)binder
{
yyAdaptor = self;
yyBinder = binder;
yyError = NO;
yyInput = string;
yyZone = [self zone];
return !(yyparse() == 1 || yyError);
}
- (const char*)connectionName
{
return NAME;
}
- (const char*)currentLoginString
{
return wrapper;
}
- enumerateEntities:(List*)aList
{
[aList empty];
return nil;
}
- enumerateProperties:(List*)aList forEntity:(id<DBEntities>)anEntity
{
[aList empty];
return nil;
}
@end
@implementation dbmAdaptor(Private)
- (DBM_FILE)openDBMFileForProperty:(id<DBProperties, DBExpressionValues>)property
{
char file[MAXPATHLEN+1];
if(property == nil)
return 0;
strcpy(file, wrapper);
strcat(file, "/");
strcat(file, [(id<DBEntities>)[property entity] expressionValue]);
strcat(file, "/");
strcat(file, [property expressionValue]);
#ifdef GDBM
return gdbm_open(file, 0, GDBM_WRCREAT, 00644, NULL);
#else
return dbm_open(file, O_CREAT|O_RDWR, 00644);
#endif
}
- (datum)getKeyForBinder:(DBBinder*)binder
{
datum data, key, val;
List* list = [[List allocFromZone:[self zone]] init];
int i, count = [[binder getProperties:list] count];
for(i = 0; i < count; i++)
{
id prop = [list objectAt:i];
if([prop isKey])
{
DBM_FILE db = [self openDBMFileForProperty:prop];
if(db == 0)
continue;
[self convertDBValue:[binder valueForProperty:prop] toDatum:&val];
#ifdef GDBM
for(key = gdbm_firstkey(db); key.dptr; key = gdbm_nextkey(db, key))
{
data = gdbm_fetch(db, key);
#else
for(key = dbm_firstkey(db); key.dptr; key = dbm_nextkey(db))
{
data = dbm_fetch(db, key);
#endif
if(val.dsize == data.dsize &&
bcmp(val.dptr, data.dptr, val.dsize) == 0)
break;
NX_FREE(val.dptr);
val.dptr = 0;
val.dsize = 0;
}
#ifdef GDBM
gdbm_close(db);
#else
dbm_close(db);
#endif
if(key.dptr)
break;
}
}
NX_FREE(val.dptr);
[list free];
if(key.dptr)
{
char* tmp;
NX_MALLOC(tmp, char, key.dsize);
bcopy(key.dptr, tmp, key.dsize);
#ifdef GDBM
free(key.dptr);
#endif
key.dptr = tmp;
}
return key;
}
- (BOOL)storeForKey:(datum)key withBinder:(DBBinder*)binder
{
int i;
int count;
List* list;
if(key.dptr == 0)
{
fprintf(stderr, "%s: Invalid key.\n", NAME);
return NO;
}
list = [[List allocFromZone:[self zone]] init];
count = [[binder getProperties:list] count];
for(i = 0; i < count; i++)
{
datum data;
id prop = [list objectAt:i];
id value = [binder valueForProperty:prop];
DBM_FILE db = [self openDBMFileForProperty:prop];
if(db == 0)
{
fprintf(stderr, "%s: Couldn't open entity: %s.\n",
NAME, [(id<DBEntities>)[prop entity] expressionValue]);
[list free];
return NO;
}
[self convertDBValue:value toDatum:&data];
#ifdef GDBM
if(gdbm_store(db, key, data, GDBM_REPLACE) != 0)
#else
if(dbm_store(db, key, data, DBM_REPLACE) != 0)
#endif
fprintf(stderr,
"%s: Cannot store data for key: %x.\n",
NAME,
(int)NXSwapBigLongLongToHost(*(dbm_t*)key.dptr));
NX_FREE(data.dptr);
#ifdef GDBM
gdbm_close(db);
#else
dbm_close(db);
#endif
}
[list free];
return YES;
}
- (void)convertDBValue:(DBValue*)value toDatum:(datum*)data
{
datum nullDatum = {0, 0};
*data = nullDatum;
if([value isNull])
return;
// Eeek!! This switch should be replaced by a dynamically bound method;
switch([[value valueType] objcType][0])
{
case 'i':
data->dptr = (char*)malloc(sizeof(int));
*(int*)data->dptr = NXSwapHostIntToBig([value intValue]);
data->dsize = sizeof(int);
break;
case 'f':
data->dptr = (char*)malloc(sizeof(float));
*(float*)data->dptr = NXSwapHostFloatToBig([value floatValue]);
data->dsize = sizeof(float);
break;
case 'd':
data->dptr = (char*)malloc(sizeof(double));
*(double*)data->dptr = NXSwapHostDoubleToBig([value doubleValue]);
data->dsize = sizeof(double);
break;
case '*':
{
const char* str = [value stringValue];
data->dptr = (char*)malloc((str ? strlen(str) + 1 : 1)*sizeof(char));
strcpy(data->dptr, str ? str : "");
data->dsize = (strlen(data->dptr) + 1) * sizeof(char);
}
break;
case '@':
{
int maxLength;
NXStream* stream = NXOpenMemory(NULL, 0, NX_READWRITE);
NXWriteObject(stream, [value objectValue]);
fprintf(stderr, "Object: \"%s\"\n", [[value objectValue] name]);
NXGetMemoryBuffer(stream, &data->dptr, &data->dsize, &maxLength);
NXCloseMemory(stream, NX_FREEBUFFER);
}
break;
default:
break;
}
}
- (dbm_t)get:(const char*)info for:entity
{
dbm_t arch_dep;
datum key, data;
key.dsize = strlen([entity expressionValue]) + strlen(info) + sizeof(char);
NX_MALLOC(key.dptr, char, key.dsize);
strcat(strcpy(key.dptr, [entity expressionValue]), info);
#ifdef GDBM
data = gdbm_fetch(dbmInfo, key);
#else
data = dbm_fetch(dbmInfo, key);
#endif
NX_FREE(key.dptr);
if(data.dptr == 0)
return 0;
arch_dep = NXSwapBigLongLongToHost(*(dbm_t*)data.dptr);
#ifdef GDBM
free(data.dptr);
#endif;
return arch_dep;
}
- set:(const char*)info for:entity with:(dbm_t)value
{
datum key, data;
dbm_t arch_dep = NXSwapHostLongLongToBig(value);
key.dsize = strlen([entity expressionValue]) + strlen(info) + sizeof(char);
NX_MALLOC(key.dptr, char, key.dsize);
strcat(strcpy(key.dptr, [entity expressionValue]), info);
data.dptr = (char*)&arch_dep;
data.dsize = sizeof(dbm_t);
#ifdef GDBM
gdbm_store(dbmInfo, key, data, GDBM_REPLACE);
#else
dbm_store(dbmInfo, key, data, DBM_REPLACE);
#endif
NX_FREE(key.dptr);
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.