summaryrefslogtreecommitdiff
path: root/lib/dbdict.c
diff options
context:
space:
mode:
authorhorchi <vdr@jwendel.de>2017-03-05 16:39:28 +0100
committerhorchi <vdr@jwendel.de>2017-03-05 16:39:28 +0100
commite2a48d8701f91b8e24fbe9e99e91eb72a87bb749 (patch)
tree726f70554b4ca985a09ef6e30a7fdc8df089993c /lib/dbdict.c
downloadvdr-epg-daemon-e2a48d8701f91b8e24fbe9e99e91eb72a87bb749.tar.gz
vdr-epg-daemon-e2a48d8701f91b8e24fbe9e99e91eb72a87bb749.tar.bz2
git init1.1.103
Diffstat (limited to 'lib/dbdict.c')
-rw-r--r--lib/dbdict.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/lib/dbdict.c b/lib/dbdict.c
new file mode 100644
index 0000000..1052a13
--- /dev/null
+++ b/lib/dbdict.c
@@ -0,0 +1,527 @@
+/*
+ * dbdict.c
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "errno.h"
+
+#include "common.h"
+#include "dbdict.h"
+
+//***************************************************************************
+// Get Token
+//***************************************************************************
+
+int getToken(const char*& p, char* token, int size, char delimiter = ' ', char end = ',')
+{
+ char* dest = token;
+ int num = 0;
+ int insideStr = no;
+
+ while (*p && (*p == ' ' || *p == delimiter))
+ p++;
+
+ if (*p == end || isEmpty(p))
+ return fail;
+
+ while (*p && num < size && !((*p == delimiter || *p == end) && !insideStr))
+ {
+ if (*p == '"')
+ {
+ insideStr = !insideStr;
+ p++;
+ }
+ else
+ {
+ *dest++ = *p++;
+ num++;
+ }
+ }
+
+ *dest = 0;
+
+ return success;
+}
+
+//***************************************************************************
+// cDbService
+//***************************************************************************
+
+const char* cDbService::formats[] =
+{
+ "INT",
+ "INT",
+ "BIGINT",
+ "BIGINT",
+ "VARCHAR",
+ "TEXT",
+ "MEDIUMTEXT",
+ "MEDIUMBLOB",
+ "FLOAT",
+ "DATETIME",
+
+ 0
+};
+
+const char* cDbService::toString(FieldFormat t)
+{
+ return formats[t];
+}
+
+const char* cDbService::dictFormats[] =
+{
+ "int",
+ "uint",
+ "bigint",
+ "ubigint",
+ "ascii",
+ "text",
+ "mtext",
+ "mlob",
+ "float",
+ "datetime",
+
+ 0
+};
+
+cDbService::FieldFormat cDbService::toDictFormat(const char* format)
+{
+ for (int i = 0; i < ffCount; i++)
+ if (strcasecmp(dictFormats[i], format) == 0)
+ return (FieldFormat)i;
+
+ return ffUnknown;
+}
+
+cDbService::TypeDef cDbService::types[] =
+{
+ { ftData, "data" },
+ { ftPrimary, "primary" },
+ { ftMeta, "meta" },
+ { ftAutoinc, "autoinc" },
+
+ { ftUnknown, "" }
+};
+
+int cDbService::toType(const char* theTypes)
+{
+ const int sizeTokenMax = 100;
+ char token[sizeTokenMax+TB];
+ const char* p = theTypes;
+
+ int type = ftNo;
+
+ // field can of more than one type -> bitmask
+
+ while (getToken(p, token, sizeTokenMax, '|') == success)
+ {
+ for (int i = 0; types[i].type != ftUnknown; i++)
+ {
+ if (strcasecmp(types[i].name, token) == 0)
+ {
+ type |= types[i].type;
+ continue;
+ }
+ }
+ }
+
+ return type;
+}
+
+const char* cDbService::toName(FieldType type, char* buf)
+{
+ *buf = 0;
+
+ // field can of more than one type -> bitmask !!
+
+ for (int i = 0; types[i].type != ftUnknown; i++)
+ {
+ if (types[i].type & type)
+ {
+ if (!isEmpty(buf))
+ strcat(buf, "|");
+
+ strcat(buf, types[i].name);
+ continue;
+ }
+ }
+
+ return buf;
+}
+
+//***************************************************************************
+//***************************************************************************
+// Class cDbDict
+//***************************************************************************
+
+cDbDict dbDict;
+
+//***************************************************************************
+// Object
+//***************************************************************************
+
+cDbDict::cDbDict()
+{
+ curTable = 0;
+ inside = no;
+ path = 0;
+ fieldFilter = 0; // 0 -> filter off use all fields
+ fltFromNameFct = 0;
+}
+
+cDbDict::~cDbDict()
+{
+ std::map<std::string, cDbTableDef*>::iterator t;
+
+ while ((t = tables.begin()) != tables.end())
+ {
+ if (t->second)
+ delete t->second;
+
+ tables.erase(t);
+ }
+
+ free(path);
+}
+
+//***************************************************************************
+// Get Table
+//***************************************************************************
+
+cDbTableDef* cDbDict::getTable(const char* aName)
+{
+ std::map<std::string, cDbTableDef*>::iterator t;
+
+ if ((t = tables.find(aName)) != tables.end())
+ return t->second;
+
+ return 0;
+}
+
+//***************************************************************************
+// Init
+//***************************************************************************
+
+int cDbDict::init(cDbFieldDef*& field, const char* tname, const char* fname)
+{
+ cDbTableDef* table = getTable(tname);
+
+ if (table)
+ if ((field = table->getField(fname)))
+ return success;
+
+ tell(0, "Fatal: Can't init field %s.%s, not found in dictionary", tname, fname);
+
+ return fail;
+}
+
+//***************************************************************************
+// In
+//***************************************************************************
+
+int cDbDict::in(const char* file, int filter)
+{
+ FILE* f;
+ char* line = 0;
+ size_t size = 0;
+
+ if (isEmpty(file))
+ return fail;
+
+ fieldFilter = filter;
+ asprintf(&path, "%s", file);
+
+ f = fopen(path, "r");
+
+ if (!f)
+ {
+ tell(0, "Error: Can' open file '%s', error was '%s'", path, strerror(errno));
+ return fail;
+ }
+
+ while (getline(&line, &size, f) > 0)
+ {
+ char* p = strstr(line, "//");
+
+ if (p) *p = 0;
+
+ allTrim(line);
+
+ if (isEmpty(line))
+ continue;
+
+ if (atLine(line) != success)
+ {
+ tell(0, "Error: Found unexpected definition '%s', aborting", line);
+ free(path);
+ return fail;
+ }
+ }
+
+ fclose(f);
+ free(line);
+
+ return success;
+}
+
+//***************************************************************************
+// Forget
+//***************************************************************************
+
+void cDbDict::forget()
+{
+ std::map<std::string, cDbTableDef*>::iterator t;
+
+ while ((t = tables.begin()) != tables.end())
+ {
+ if (t->second)
+ delete t->second;
+
+ tables.erase(t);
+ }
+
+ free(path);
+
+ curTable = 0;
+ inside = no;
+ path = 0;
+ fieldFilter = 0; // 0 -> filter off use all fields
+ fltFromNameFct = 0;
+}
+
+//***************************************************************************
+// Show
+//***************************************************************************
+
+void cDbDict::show()
+{
+ std::map<std::string, cDbTableDef*>::iterator t;
+
+ for (t = tables.begin(); t != tables.end(); t++)
+ {
+ tell(0, "-------------------------------------------------------------------------------------------------");
+ tell(0, "Table '%s'", t->first.c_str());
+ tell(0, "-------------------------------------------------------------------------------------------------");
+ t->second->show();
+ }
+}
+
+//***************************************************************************
+// At Line
+//***************************************************************************
+
+int cDbDict::atLine(const char* line)
+{
+ int status = success;
+ static int prsTable = no;
+ static int prsIndex = no;
+
+ const char* p;
+
+ if (strncasecmp(line, "Table ", 6) == 0)
+ {
+ char tableName[100];
+
+ prsTable = yes;
+ curTable = 0;
+ p = line + strlen("Table ");
+ strcpy(tableName, p);
+ allTrim(tableName);
+
+ if (!getTable(tableName))
+ {
+ curTable = new cDbTableDef(tableName);
+ tables[tableName] = curTable;
+ }
+ else
+ tell(0, "Fatal: Table '%s' doubly defined", tableName);
+ }
+ else if (strncasecmp(line, "Index ", 6) == 0)
+ {
+ prsIndex = yes;
+ p = line + strlen("Table ");
+ }
+
+ else if (strchr(line, '{'))
+ inside = yes;
+
+ else if (strchr(line, '}'))
+ {
+ inside = no;
+ prsTable = no;
+ prsIndex = no;
+ }
+
+ else if (inside && prsTable)
+ status += parseField(line);
+
+ else if (inside && prsIndex)
+ status += parseIndex(line);
+
+ else
+ tell(0, "Info: Ignoring extra line [%s]", line);
+
+ return status;
+}
+
+//***************************************************************************
+// Parse Filter
+//***************************************************************************
+
+int cDbDict::parseFilter(cDbFieldDef* f, const char* value)
+{
+ const int sizeNameMax = 50;
+ char name[sizeNameMax+TB];
+ const char* v = value;
+
+ f->filter = 0;
+
+ while (getToken(v, name, sizeNameMax, '|') == success)
+ {
+ if (isalpha(*name) && fltFromNameFct)
+ f->filter |= fltFromNameFct(name);
+ else
+ f->filter |= atoi(name);
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Parse Field
+//***************************************************************************
+
+int cDbDict::parseField(const char* line)
+{
+ const int sizeTokenMax = 100;
+ char token[sizeTokenMax+TB];
+ const char* p = line;
+
+ cDbFieldDef* f = new cDbFieldDef;
+ f->filter = 0xFFFF;
+
+ if (!curTable)
+ return fail;
+
+ // first parse fixed part of field definition up to the 'type'
+
+ for (int i = 0; i < dtCount; i++)
+ {
+ if (getToken(p, token, sizeTokenMax) != success)
+ {
+ if (i >= dtType) // all after type is optional (filter, ...)
+ break;
+
+ delete f;
+ tell(0, "Error: Can't parse line [%s]", line);
+ return fail;
+ }
+
+ if (i == dtCount-1 && strchr(token, ','))
+ *(strchr(token, ',')) = 0;
+
+ switch (i)
+ {
+ case dtName: f->name = strdup(token); break;
+ case dtDescription: f->setDescription(token); break;
+ case dtDbName: f->dbname = strdup(token); break;
+ case dtFormat: f->format = toDictFormat(token); break;
+ case dtSize: f->size = atoi(token); break;
+ case dtType: f->type = toType(token); break;
+ }
+ }
+
+ // second ... parse dynamic part of the field definition like filter and default
+
+ while (getToken(p, token, sizeTokenMax) == success)
+ {
+ char content[sizeTokenMax+TB];
+
+ if (getToken(p, content, sizeTokenMax) != success || isEmpty(content))
+ {
+ tell(0, "Error: Skipping token '%s' missing content!", token);
+ break;
+ }
+
+ if (strcasecmp(token, "filter") == 0)
+ parseFilter(f, content);
+
+ else if (strcasecmp(token, "default") == 0)
+ f->def = strdup(content);
+
+ else
+ tell(0, "Warning: Skipping unexpected token '%s'", token);
+ }
+
+ if (!f->isValid())
+ {
+ tell(0, "Error: Can't parse line [%s], invalid field configuration", line);
+ delete f;
+ return fail;
+ }
+
+ if (f->filterMatch(fieldFilter))
+ {
+ f->index = curTable->fieldCount();
+ curTable->addField(f);
+ }
+ else
+ {
+ tell(2, "Info: Ignoring field '%s' due to filter configiuration",
+ f->getName());
+ delete f;
+ }
+
+ return success;
+}
+
+//***************************************************************************
+// Parse Index
+//***************************************************************************
+
+int cDbDict::parseIndex(const char* line)
+{
+ const int sizeTokenMax = 100;
+ char token[sizeTokenMax+TB];
+ const char* p = line;
+ int done = no;
+
+ if (!curTable)
+ return fail;
+
+ cDbIndexDef* index = new cDbIndexDef();
+
+ for (int i = 0; !done && i < 20; i++)
+ {
+ if (getToken(p, token, sizeTokenMax) != success)
+ {
+ if (i <= idtFields)
+ {
+ delete index;
+ tell(0, "Error: Can't parse line [%s]", line);
+ return fail;
+ }
+
+ break;
+ }
+
+ if (strchr(token, ','))
+ {
+ done = yes;
+ *(strchr(token, ',')) = 0;
+ }
+
+ if (i == idtName)
+ index->setName(token);
+ else if (i == idtDescription)
+ index->setDescription(token);
+ else if (i >= idtFields && i < idtFields+20)
+ index->addField(curTable->getField(token));
+ }
+
+ curTable->addIndex(index);
+
+ return success;
+}