home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // FileTable
- // This software is without warranty of any kind. Use at your own risk.
- // -------------------------------------------------------------------------------------
-
- #import <objc/objc.h>
- #import <appkit/appkit.h>
- #import <mach/mach.h>
- #import <dbkit/dbkit.h>
- #import <libc.h>
- #import <stdio.h>
- #import <string.h>
- #import <ctype.h>
- #import "FileTable.h"
-
- // -------------------------------------------------------------------------------------
- @implementation FileTable
-
- // -------------------------------------------------------------------------------------
-
- /* count items in an unparsed table */
- static int dt_countList(char *list)
- {
- int c;
- char *r = list;
-
- /* make sure there is at least one entry */
- if (!r) return 0;
- while (isspace(*r)) r++;
- if (!*r) return 0;
-
- /* count remaining entries */
- for (c = 0, r = list; *r;) {
- c++;
- while (*r && !isspace(*r)) r++; // skip value
- while (isspace(*r)) r++; // skip white
- }
-
- return c;
- }
-
- /* count items in a parsed table */
- static int dt_countTable(char **tbl)
- {
- int c;
- if (!tbl) return 0; // no entries counted
- for (c = 0; tbl[c]; c++);
- return c;
- }
-
- /* parse a string table */
- static char **dt_parseTable(char *list)
- {
- int n, tblSize;
- char **theTbl, *r;
-
- /* skip whitespace in list */
- if (!list) return (char**)NULL;
- while (isspace(*list)) list++;
- if (!*list) return (char**)NULL;
-
- /* count items in table */
- tblSize = sizeof(char*) * (dt_countList(list) + 1); // include terminator
- theTbl = (char**)malloc(tblSize);
- memset(theTbl, 0, tblSize);
-
- /* fill table */
- for (n = 0, r = list; *r;) {
- theTbl[n++] = r;
- while (*r && !isspace(*r)) r++;
- while (isspace(*r)) *r++ = 0;
- }
-
- /* return the table */
- return theTbl;
-
- }
-
- // -------------------------------------------------------------------------------------
-
- /* return access timestamp */
- - (time_t)timestamp
- {
- struct stat st;
- if (stat((char*)tableHandle->access, &st) < 0) {
- NXLogError("Unable to stat file %s", tableHandle->access);
- return 0;
- }
- return st.st_mtime;
- }
-
- // -------------------------------------------------------------------------------------
-
- - _addSimpleColumn:(char*)title :(char)cType :(float)size
- {
- int t;
- char *types = CDT_TYPES;
- dataColumn_t *dc = (dataColumn_t*)malloc(sizeof(dataColumn_t));
- memset(dc, 0, sizeof(dataColumn_t));
- for (t = 0; types[t] && (types[t] != cType); t++);
- dc->index = [tableHandle->columnId count];
- dc->type = types[t]? t : CDT_UNKNOWN;
- dc->keyTag = NXCopyStringBuffer(title);
- dc->title = (char*)nil;
- dc->size = size;
- dc->minSize = 0.0;
- dc->displayOrder = dc->index;
- dc->alignment = NX_CENTERED;
- dc->isEditable = dc->index? YES : NO;
- dc->isHidden = (dc->displayOrder >= 0)? NO : YES;
- dc->nilValue = (char*)nil;
- [self addColumnInfo:dc];
- return self;
- }
-
- /* load table column info */
- - readTableColumns
- {
- char buf[2048];
- BOOL cLoaded = NO, vLoaded = NO;
- FILE *fh;
-
- /* open table */
- if (!(fh = fopen(tableHandle->access, "r"))) {
- NXLogError("Unable to open file %s", tableHandle->access);
- return (id)nil;
- }
-
- /* find/read column information */
- while (fgets(buf, sizeof(buf), fh) && (*buf == '#')) {
-
- /* view information */
- if (!vLoaded && !strncmp(buf, "#!t", 3)) {
- char viewName[128];
- NXSize s = { 0.0, 0.0 };
- sscanf(buf + 4, "%s %f %f", viewName, &s.width, &s.height);
- tableHandle->viewSize = s;
- vLoaded = YES;
- continue;
- }
-
- /* column information */
- if (!cLoaded && (!strncmp(buf, "#!c\t", 4) || !strncmp(buf, "#\t", 2))) {
- int key, numCols;
- char **tbl, *b = buf + 1;
- for (;*b && (*b != ' ') && (*b != '\t'); b++);
- for (;(*b == ' ') || (*b == '\t'); b++);
- if (!(tbl = dt_parseTable(b))) continue;
- numCols = dt_countTable(tbl);
- for (key = 0; key < numCols; key++) {
- float size = 0.0;
- char *title = tbl[key], *p = index(title, ':'), ctype[4];
- if (p) { *p++ = 0; sscanf(p, "%c:%f", ctype, &size); }
- [self _addSimpleColumn:title :*ctype :size];
- }
- free(tbl);
- cLoaded = YES;
- continue;
- }
-
- }
-
- /* close table */
- fclose(fh);
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
-
- /* load table */
- - readTableData
- {
- FILE *fh;
- u_int r;
- char buf[4096];
- int count = [tableHandle->columnId count];
-
- /* open table */
- if (!tableHandle->access) return (id)nil;
- if (!(fh = fopen((char*)tableHandle->access, "r"))) {
- NXLogError("Unable to open file %s", tableHandle->access);
- return (id)nil;
- }
-
- /* read table data */
- for (r = 0; fgets(buf, sizeof(buf), fh);) {
- int c, tlen;
- id rowId;
- char **tbl;
- if (!*buf || (*buf == '#') || (*buf == '\t') || (*buf == ' ')) continue;
- if (!(tbl = dt_parseTable(buf))) continue;
- rowId = [[List alloc] initCount:count];
- [tableHandle->dataId insertObject:rowId at:r];
- for (tlen = dt_countTable(tbl), c = 0; c < count; c++) {
- char *val = (c < tlen)? tbl[c] : (char*)nil;
- dataEntry_t *de = entryNEW;
- de->value = (char*)[self copyStringValue:val forColumn:c];
- de->isValid = -1;
- [rowId insertObject:(id)de at:c];
- }
- free(tbl);
- r++;
- }
-
- /* close table */
- fclose(fh);
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
-
- - writeTable
- {
- int r, c;
- FILE *fh;
- struct stat st;
-
- /* make backup copy */
- if (stat(tableHandle->access, &st) >= 0) {
- char bku[MAXPATHLEN + 1];
- sprintf(bku, "%s~", tableHandle->access);
- if (rename(tableHandle->access, bku)) {
- NXLogError("Unable to rename file %s to %s", tableHandle->access, bku);
- return (id)nil;
- }
- }
-
- /* open file for writing */
- if (!(fh = fopen(tableHandle->access, "w+"))) {
- NXLogError("Unable to open file %s", tableHandle->access);
- return (id)nil;
- }
-
- /* view header */
- fprintf(fh, "#!t %s %.0f %.0f\n", tableHandle->name,
- tableHandle->viewSize.width, tableHandle->viewSize.height);
-
- /* column headers */
- fprintf(fh, "#!c\t");
- for (c = 0; c < [tableHandle->columnId count]; c++) {
- dataColumn_t *ci = [self columnInfoAt:c];
- fprintf(fh, "%s:%c:%.0f\t", ci->keyTag, *(CDT_TYPES + ci->type), ci->size);
- }
- fprintf(fh, "\n");
-
- /* data */
- for (r = 0; r < [tableHandle->dataId count]; r++) {
- id rowId = [tableHandle->dataId objectAt:r];
- for (c = 0; c < [rowId count]; c++) {
- dataColumn_t *ci = [self columnInfoAt:c];
- dataEntry_t *de = entryPTR(rowId, c);
- if (c) fprintf(fh, "\t");
- fprintf(fh, "%s", (*de->value?de->value:(ci->nilValue?ci->nilValue:"*")));
- }
- fprintf(fh, "\n");
- }
-
- /* close file */
- fclose(fh);
-
- /* re-stat file to get modified date */
- if (stat((char*)tableHandle->access, &st) >= 0) tableHandle->date = st.st_mtime;
- else NXLogError("Unable to stat file %s", tableHandle->access);
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
-
- /* create value for column */
- - (const char*)copyStringValue:(const char*)value forColumn:(int)index
- {
- char *vcopy, buff[64];
- dataColumn_t *dc = [self columnInfoAt:index];
-
- /* check arguments */
- if (!dc) return (char*)nil;
-
- /* special return types */
- switch (dc->type) {
- case CDT_BOOLEAN:
- return NXCopyStringBuffer(((value && strcmp(value,"NO"))?"YES":"NO"));
- break;
- case CDT_INTEGER:
- sprintf(buff, "%d", (value?atoi(value):0));
- return NXCopyStringBuffer(buff);
- break;
- case CDT_STRING:
- default:
- if (!value) return NXCopyStringBuffer("");
- for (vcopy = (char*)value; isspace(*vcopy); vcopy++); // skip white space
- vcopy = strcpy((char*)malloc(strlen(vcopy) + 1), vcopy);
- { char *b,*v; for (b=v=vcopy;*v;v++) if(!isspace(*v)){if(b!=v)*b=*v;b++;} *b=0; }
- if (dc->nilValue && !strcmp(dc->nilValue, vcopy)) *vcopy = 0;
- return (char*)realloc(vcopy, strlen(vcopy) + 1);
- break;
- }
-
- /* control should not reach here */
- return NXCopyStringBuffer("");
- }
-
- // -------------------------------------------------------------------------------------
-
- /* validate specific value (intended for subclass implementation) */
- - (BOOL)verifyValue:(const char*)value dataType:(int)dataType
- {
- if (!value) return NO;
- switch (dataType) {
- case CDT_STRING:
- case CDT_INTEGER:
- default:
- return YES;
- }
- return YES;
- }
-
- // -------------------------------------------------------------------------------------
- @end