28be0ea84d09b7a83a763086bf9e26c9d427a886 kent Tue Apr 2 11:29:29 2013 -0700 Moving fieldedTable to a new library module. diff --git src/lib/fieldedTable.c src/lib/fieldedTable.c new file mode 100644 index 0000000..d2aadf2 --- /dev/null +++ src/lib/fieldedTable.c @@ -0,0 +1,102 @@ +/* fieldedTable - a table composed of untyped strings in memory. Includes names for each + * field. This is a handy way of storing small-to-medium tab-separated files that begin + * with a "#list of fields" line among other things. */ + +#include "common.h" +#include "localmem.h" +#include "linefile.h" +#include "fieldedTable.h" + +struct fieldedTable *fieldedTableNew(char *name, char **fields, int fieldCount) +/* Create a new empty fieldedTable with given name, often a file name. */ +{ +struct fieldedTable *table; +AllocVar(table); +struct lm *lm = table->lm = lmInit(0); +table->name = lmCloneString(lm, name); +table->cursor = &table->rowList; +table->fieldCount = fieldCount; +int i; +char **row = lmAllocArray(lm, table->fields, fieldCount); +for (i=0; i<fieldCount; ++i) + { + row[i] = lmCloneString(lm, fields[i]); + } +return table; +} + +void fieldedTableFree(struct fieldedTable **pTable) +/* Free up memory resources associated with table. */ +{ +struct fieldedTable *table = *pTable; +if (table != NULL) + { + lmCleanup(&table->lm); + freez(pTable); + } +} + +struct fieldedRow *fieldedTableAdd(struct fieldedTable *table, char **row, int rowSize, int id) +/* Create a new row and add it to table. Return row. */ +{ +/* Make sure we got right number of fields. */ +if (table->fieldCount != rowSize) + errAbort("%s starts with %d fields, but at line %d has %d fields instead", + table->name, table->fieldCount, id, rowSize); + +/* Allocate field from local memory and start filling it in. */ +struct lm *lm = table->lm; +struct fieldedRow *fr; +lmAllocVar(lm, fr); +lmAllocArray(lm, fr->row, rowSize); +fr->id = id; +int i; +for (i=0; i<rowSize; ++i) + fr->row[i] = lmCloneString(lm, row[i]); + +/* Add it to end of list using cursor to avoid slReverse hassles. */ +*(table->cursor) = fr; +table->cursor = &fr->next; + +return fr; +} + +struct fieldedTable *fieldedTableFromTabFile(char *url, char *requiredFields[], int requiredCount) +/* Read table from tab-separated file with a #header line that defines the fields. Ensures + * all requiredFields (if any) are present. */ +{ +struct lineFile *lf = lineFileOpen(url, TRUE); +char *line; + +/* Get first line and turn it into field list. */ +if (!lineFileNext(lf, &line, NULL)) + errAbort("%s is empty", url); +if (line[0] != '#') + errAbort("%s must start with '#' and field names on first line", url); +line = skipLeadingSpaces(line+1); +int fieldCount = chopByChar(line, '\t', NULL, 0); +char *fields[fieldCount]; +chopTabs(line, fields); + +/* Make sure that all required fields are present. */ +int i; +for (i = 0; i < requiredCount; ++i) + { + char *required = requiredFields[i]; + int ix = stringArrayIx(required, fields, fieldCount); + if (ix < 0) + errAbort("%s is missing required field '%s'", url, required); + } + +/* Create fieldedTable . */ +struct fieldedTable *table = fieldedTableNew(url, fields, fieldCount); +while (lineFileRowTab(lf, fields)) + { + fieldedTableAdd(table, fields, fieldCount, lf->lineIx); + } + +/* Clean up and go home. */ +lineFileClose(&lf); +return table; +} +