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.