X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/2d40d85af18c3033e0248e67c8b997590dd07c4f..3220cd98443043566e3617c564370683d011878e:/lib/dvb/db.cpp diff --git a/lib/dvb/db.cpp b/lib/dvb/db.cpp index a380ad0c..1bfc06ee 100644 --- a/lib/dvb/db.cpp +++ b/lib/dvb/db.cpp @@ -3,13 +3,108 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include DEFINE_REF(eDVBService); +RESULT eBouquet::addService(const eServiceReference &ref) +{ + list::iterator it = + std::find(m_services.begin(), m_services.end(), ref); + if ( it != m_services.end() ) + return -1; + m_services.push_back(ref); + return 0; +} + +RESULT eBouquet::removeService(const eServiceReference &ref) +{ + list::iterator it = + std::find(m_services.begin(), m_services.end(), ref); + if ( it == m_services.end() ) + return -1; + m_services.erase(it); + return 0; +} + +RESULT eBouquet::moveService(const eServiceReference &ref, unsigned int pos) +{ + if ( pos < 0 || pos >= m_services.size() ) + return -1; + ++pos; + list::iterator source=m_services.end(); + list::iterator dest=m_services.end(); + bool forward = false; + for (list::iterator it(m_services.begin()); it != m_services.end(); ++it) + { + if (dest == m_services.end() && !--pos) + dest = it; + if (*it == ref) + { + source = it; + forward = pos>0; + } + if (dest != m_services.end() && source != m_services.end()) + break; + } + if (dest == m_services.end() || source == m_services.end() || source == dest) + return -1; + while (source != dest) + { + if (forward) + std::iter_swap(source++, source); + else + std::iter_swap(source--, source); + } + return 0; +} + +RESULT eBouquet::flushChanges() +{ + FILE *f=fopen(m_path.c_str(), "wt"); + if (!f) + return -1; + if ( fprintf(f, "#NAME %s\r\n", m_bouquet_name.c_str()) < 0 ) + goto err; + for (list::iterator i(m_services.begin()); i != m_services.end(); ++i) + { + eServiceReference tmp = *i; + std::string str = tmp.path; + if ( (i->flags&eServiceReference::flagDirectory) == eServiceReference::flagDirectory ) + { + unsigned int p1 = str.find("FROM BOUQUET \""); + if (p1 == std::string::npos) + { + eDebug("doof... kaputt"); + continue; + } + str.erase(0, p1+14); + p1 = str.find("\""); + if (p1 == std::string::npos) + { + eDebug("doof2... kaputt"); + continue; + } + str.erase(p1); + tmp.path=str; + } + if ( fprintf(f, "#SERVICE %s\r\n", tmp.toString().c_str()) < 0 ) + goto err; + if ( i->name.length() ) + if ( fprintf(f, "#DESCRIPTION %s\r\n", i->name.c_str()) < 0 ) + goto err; + } + fclose(f); + return 0; +err: + fclose(f); + eDebug("couldn't write file %s", m_path.c_str()); + return -1; +} + eDVBService::eDVBService() { } @@ -21,6 +116,7 @@ eDVBService::~eDVBService() eDVBService &eDVBService::operator=(const eDVBService &s) { m_service_name = s.m_service_name; + m_service_name_sort = s.m_service_name_sort; m_provider_name = s.m_provider_name; m_flags = s.m_flags; m_ca = s.m_ca; @@ -28,9 +124,26 @@ eDVBService &eDVBService::operator=(const eDVBService &s) return *this; } +void eDVBService::genSortName() +{ + m_service_name_sort = removeDVBChars(m_service_name); + makeUpper(m_service_name_sort); + while ((!m_service_name_sort.empty()) && m_service_name_sort[0] == ' ') + m_service_name_sort.erase(0, 1); + + /* put unnamed services at the end, not at the beginning. */ + if (m_service_name_sort.empty()) + m_service_name_sort = "\xFF"; +} + RESULT eDVBService::getName(const eServiceReference &ref, std::string &name) { - name = convertDVBUTF8(m_service_name); + if (!ref.name.empty()) + name = ref.name; + else if (!m_service_name.empty()) + name = m_service_name; + else + name = "(...)"; return 0; } @@ -45,7 +158,7 @@ int eDVBService::checkFilter(const eServiceReferenceDVB &ref, const eDVBChannelQ switch (query.m_type) { case eDVBChannelQuery::tName: - res = m_service_name.find(query.m_string) != std::string::npos; + res = m_service_name_sort.find(query.m_string) != std::string::npos; break; case eDVBChannelQuery::tProvider: res = m_provider_name.find(query.m_string) != std::string::npos; @@ -95,6 +208,7 @@ void eDVBService::setCachePID(cacheID id, int pid) DEFINE_REF(eDVBDB); + /* THIS CODE IS BAD. it should be replaced by somethine better. */ void eDVBDB::load() { eDebug("---- opening lame channel db"); @@ -143,18 +257,31 @@ void eDVBDB::load() { eDVBFrontendParametersSatellite sat; int frequency, symbol_rate, polarisation, fec, orbital_position, inversion; - sscanf(line+2, "%d:%d:%d:%d:%d:%d", &frequency, &symbol_rate, &polarisation, &fec, &inversion, &orbital_position); + sscanf(line+2, "%d:%d:%d:%d:%d:%d", &frequency, &symbol_rate, &polarisation, &fec, &orbital_position, &inversion); sat.frequency = frequency; sat.symbol_rate = symbol_rate; sat.polarisation = polarisation; sat.fec = fec; sat.orbital_position = orbital_position; sat.inversion = inversion; - // ... -// t.setSatellite(frequency, symbol_rate, polarisation, fec, sat, inversion); feparm->setDVBS(sat); - } - if (line[1]=='c') + } else if (line[1]=='t') + { + eDVBFrontendParametersTerrestrial ter; + int frequency, bandwidth, code_rate_HP, code_rate_LP, modulation, transmission_mode, guard_interval, hierarchy, inversion; + sscanf(line+2, "%d:%d:%d:%d:%d:%d:%d:%d:%d", &frequency, &bandwidth, &code_rate_HP, &code_rate_LP, &modulation, &transmission_mode, &guard_interval, &hierarchy, &inversion); + ter.frequency = frequency; + ter.bandwidth = bandwidth; + ter.code_rate_HP = code_rate_HP; + ter.code_rate_LP = code_rate_LP; + ter.modulation = modulation; + ter.transmission_mode = transmission_mode; + ter.guard_interval = guard_interval; + ter.hierarchy = hierarchy; + ter.inversion = inversion; + + feparm->setDVBT(ter); + } else if (line[1]=='c') { int frequency, symbol_rate, inversion=0, modulation=3; sscanf(line+2, "%d:%d:%d:%d", &frequency, &symbol_rate, &inversion, &modulation); @@ -197,7 +324,10 @@ void eDVBDB::load() fgets(line, 256, f); if (strlen(line)) line[strlen(line)-1]=0; - s->m_service_name=line; + + s->m_service_name = line; + s->genSortName(); + fgets(line, 256, f); if (strlen(line)) line[strlen(line)-1]=0; @@ -266,12 +396,20 @@ void eDVBDB::save() fprintf(f, "%08x:%04x:%04x\n", chid.dvbnamespace.get(), chid.transport_stream_id.get(), chid.original_network_id.get()); eDVBFrontendParametersSatellite sat; + eDVBFrontendParametersTerrestrial ter; if (!ch.m_frontendParameters->getDVBS(sat)) { fprintf(f, "\ts %d:%d:%d:%d:%d:%d\n", sat.frequency, sat.symbol_rate, - sat.polarisation, sat.fec, sat.inversion, - sat.orbital_position); + sat.polarisation, sat.fec, sat.orbital_position, + sat.inversion); + } + if (!ch.m_frontendParameters->getDVBT(ter)) + { + fprintf(f, "\tt %d:%d:%d:%d:%d:%d:%d:%d:%d\n", + ter.frequency, ter.bandwidth, ter.code_rate_HP, + ter.code_rate_LP, ter.modulation, ter.transmission_mode, + ter.guard_interval, ter.hierarchy, ter.inversion); } fprintf(f, "/\n"); channels++; @@ -309,9 +447,148 @@ void eDVBDB::save() fclose(f); } +void eDVBDB::loadBouquet(const char *path) +{ + std::string bouquet_name = path; + if (!bouquet_name.length()) + { + eDebug("Bouquet load failed.. no path given.."); + return; + } + unsigned int pos = bouquet_name.rfind('/'); + if ( pos != std::string::npos ) + bouquet_name.erase(0, pos+1); + if (bouquet_name.empty()) + { + eDebug("Bouquet load failed.. no filename given.."); + return; + } + eBouquet &bouquet = m_bouquets[bouquet_name]; + bouquet.m_path = path; + std::list &list = bouquet.m_services; + list.clear(); + + eDebug("loading bouquet... %s", path); + FILE *fp=fopen(path, "rt"); + int entries=0; + if (!fp) + { + eDebug("failed to open."); + if ( strstr(path, "bouquets.tv") ) + { + eDebug("recreate bouquets.tv"); + bouquet.m_bouquet_name="Bouquets (TV)"; + bouquet.flushChanges(); + } + else if ( strstr(path, "bouquets.radio") ) + { + eDebug("recreate bouquets.radio"); + bouquet.m_bouquet_name="Bouquets (Radio)"; + bouquet.flushChanges(); + } + return; + } + char line[256]; + bool read_descr=false; + eServiceReference *e = NULL; + while (1) + { + if (!fgets(line, 256, fp)) + break; + line[strlen(line)-1]=0; + if (strlen(line) && line[strlen(line)-1]=='\r') + line[strlen(line)-1]=0; + if (!line[0]) + break; + if (line[0]=='#') + { + if (!strncmp(line, "#SERVICE ", 9) || !strncmp(line, "#SERVICE: ", 10)) + { + int offs = line[8] == ':' ? 10 : 9; + eServiceReference tmp(line+offs); + if (tmp.type != eServiceReference::idDVB) + { + eDebug("only DVB Bouquets supported"); + continue; + } + if ( (tmp.flags&eServiceReference::flagDirectory) == eServiceReference::flagDirectory ) + { + std::string str = tmp.path; + unsigned int pos = str.rfind('/'); + if ( pos != std::string::npos ) + str.erase(0, pos+1); + if (str.empty()) + { + eDebug("Bouquet load failed.. no filename given.."); + continue; + } + loadBouquet(tmp.path.c_str()); + char buf[256]; + snprintf(buf, 256, "(type == %d) FROM BOUQUET \"%s\" ORDER BY bouquet", tmp.data[0], str.c_str()); + tmp.path = buf; + } + list.push_back(tmp); + e = &list.back(); + read_descr=true; + ++entries; + } + else if (read_descr && !strncmp(line, "#DESCRIPTION ", 13)) + { + e->name = line+13; + read_descr=false; + } + else if (!strncmp(line, "#NAME ", 6)) + bouquet.m_bouquet_name=line+6; + continue; + } + } + fclose(fp); + eDebug("%d entries in Bouquet %s", entries, bouquet_name.c_str()); +} + +void eDVBDB::loadBouquets() +{ + loadBouquet("bouquets.tv"); + loadBouquet("bouquets.radio"); +// create default bouquets when missing + if ( m_bouquets.find("userbouquet.favourites.tv") == m_bouquets.end() ) + { + eBouquet &b = m_bouquets["userbouquet.favourites.tv"]; + b.m_path = "userbouquet.favourites.tv"; + b.m_bouquet_name = "Favourites (TV)"; + b.flushChanges(); + eServiceReference ref; + memset(ref.data, 0, sizeof(ref.data)); + ref.type=1; + ref.flags=7; + ref.data[0]=1; + ref.path="(type == 1) FROM BOUQUET \"userbouquet.favourites.tv\" ORDER BY bouquet"; + eBouquet &parent = m_bouquets["bouquets.tv"]; + parent.m_services.push_back(ref); + parent.flushChanges(); + } + if ( m_bouquets.find("userbouquet.favourites.radio") == m_bouquets.end() ) + { + eBouquet &b = m_bouquets["userbouquet.favourites.radio"]; + b.m_path = "userbouquet.favourites.radio"; + b.m_bouquet_name = "Favourites (Radio)"; + b.flushChanges(); + eServiceReference ref; + memset(ref.data, 0, sizeof(ref.data)); + ref.type=1; + ref.flags=7; + ref.data[0]=1; + ref.path="(type == 2) FROM BOUQUET \"userbouquet.favourites.radio\" ORDER BY bouquet"; + eBouquet &parent = m_bouquets["bouquets.radio"]; + parent.m_services.push_back(ref); + parent.flushChanges(); + } +} + eDVBDB::eDVBDB() { load(); + loadBouquets(); } eDVBDB::~eDVBDB() @@ -365,17 +642,97 @@ RESULT eDVBDB::getService(const eServiceReferenceDVB &reference, ePtr &query, eDVBChannelQuery *q) +RESULT eDVBDB::flush() +{ + save(); + return 0; +} + +RESULT eDVBDB::getBouquet(const eServiceReference &ref, eBouquet* &bouquet) +{ + std::string str = ref.path; + if (str.empty()) + { + eDebug("getBouquet failed.. no path given!"); + return -1; + } + unsigned int pos = str.find("FROM BOUQUET \""); + if ( pos != std::string::npos ) + { + str.erase(0, pos+14); + pos = str.find('"'); + if ( pos != std::string::npos ) + str.erase(pos); + else + str.clear(); + } + if (str.empty()) + { + eDebug("getBouquet failed.. couldn't parse bouquet name"); + return -1; + } + std::map::iterator i = + m_bouquets.find(str); + if (i == m_bouquets.end()) + { + bouquet = 0; + return -ENOENT; + } + bouquet = &i->second; + return 0; +} + +RESULT eDVBDB::startQuery(ePtr &query, eDVBChannelQuery *q, const eServiceReference &source) { - query = new eDVBDBQuery(this, eServiceReference(), q); + if ( q && q->m_bouquet_name.length() ) + query = new eDVBDBBouquetQuery(this, source, q); + else + query = new eDVBDBQuery(this, source, q); return 0; } -DEFINE_REF(eDVBDBQuery); +DEFINE_REF(eDVBDBQueryBase); + +eDVBDBQueryBase::eDVBDBQueryBase(eDVBDB *db, const eServiceReference &source, eDVBChannelQuery *query) + :m_db(db), m_query(query), m_source(source) +{ +} + +int eDVBDBQueryBase::compareLessEqual(const eServiceReferenceDVB &a, const eServiceReferenceDVB &b) +{ + ePtr a_service, b_service; + + int sortmode = m_query ? m_query->m_sort : eDVBChannelQuery::tName; + + if ((sortmode == eDVBChannelQuery::tName) || (sortmode == eDVBChannelQuery::tProvider)) + { + if (m_db->getService(a, a_service)) + return 1; + if (m_db->getService(b, b_service)) + return 1; + } + + switch (sortmode) + { + case eDVBChannelQuery::tName: + return a_service->m_service_name_sort < b_service->m_service_name_sort; + case eDVBChannelQuery::tProvider: + return a_service->m_provider_name < b_service->m_provider_name; + case eDVBChannelQuery::tType: + return a.getServiceType() < b.getServiceType(); + case eDVBChannelQuery::tBouquet: + return 0; + case eDVBChannelQuery::tSatellitePosition: + return (a.getDVBNamespace().get() >> 16) < (b.getDVBNamespace().get() >> 16); + default: + return 1; + } + return 0; +} -eDVBDBQuery::eDVBDBQuery(eDVBDB *db, const eServiceReference &source, eDVBChannelQuery *query): m_db(db), m_query(query) +eDVBDBQuery::eDVBDBQuery(eDVBDB *db, const eServiceReference &source, eDVBChannelQuery *query) + :eDVBDBQueryBase(db, source, query) { - // TODO: use SOURCE ... m_cursor = m_db->m_services.begin(); } @@ -384,18 +741,68 @@ RESULT eDVBDBQuery::getNextResult(eServiceReferenceDVB &ref) while (m_cursor != m_db->m_services.end()) { ref = m_cursor->first; - + int res = (!m_query) || m_cursor->second->checkFilter(ref, *m_query); ++m_cursor; - + + if (res) + return 0; + } + + ref.type = eServiceReference::idInvalid; + + return 1; +} + +eDVBDBBouquetQuery::eDVBDBBouquetQuery(eDVBDB *db, const eServiceReference &source, eDVBChannelQuery *query) + :eDVBDBQueryBase(db, source, query), m_cursor(db->m_bouquets[query->m_bouquet_name].m_services.begin()) +{ +} + +RESULT eDVBDBBouquetQuery::getNextResult(eServiceReferenceDVB &ref) +{ + eBouquet &bouquet = m_db->m_bouquets[m_query->m_bouquet_name]; + std::list &list = bouquet.m_services; + while (m_cursor != list.end()) + { + ref = *((eServiceReferenceDVB*)&(*m_cursor)); + + std::map >::iterator it = + m_db->m_services.find(ref); + + int res = (!m_query) || it == m_db->m_services.end() || it->second->checkFilter(ref, *m_query); + + ++m_cursor; + if (res) return 0; } + + ref.type = eServiceReference::idInvalid; + return 1; } -/* ( <==|...> <"string"|int>)[AND (..)] */ +/* ( <==|...> <"string"|int>)[||,&& (..)] */ + +static int decodeType(const std::string &type) +{ + if (type == "name") + return eDVBChannelQuery::tName; + else if (type == "provider") + return eDVBChannelQuery::tProvider; + else if (type == "type") + return eDVBChannelQuery::tType; + else if (type == "bouquet") + return eDVBChannelQuery::tBouquet; + else if (type == "satellitePosition") + return eDVBChannelQuery::tSatellitePosition; + else if (type == "channelID") + return eDVBChannelQuery::tChannelID; + else + return -1; +} /* never, NEVER write a parser in C++! */ RESULT parseExpression(ePtr &res, std::list::const_iterator begin, std::list::const_iterator end) @@ -425,12 +832,16 @@ RESULT parseExpression(ePtr &res, std::list::cons /* we had only one sub expression */ if (end_of_exp == end) - return 1; + { +// eDebug("only one sub expression"); + return 0; + } /* otherwise we have an operator here.. */ ePtr r2 = res; res = new eDVBChannelQuery(); + res->m_sort = 0; res->m_p1 = r2; res->m_inverse = 0; r2 = 0; @@ -455,6 +866,7 @@ RESULT parseExpression(ePtr &res, std::list::cons std::string type, op, val; res = new eDVBChannelQuery(); + res->m_sort = 0; int cnt = 0; while (begin != end) @@ -485,27 +897,15 @@ RESULT parseExpression(ePtr &res, std::list::cons return 1; } - if (type == "name") - res->m_type = eDVBChannelQuery::tName; - else if (type == "provider") - res->m_type = eDVBChannelQuery::tProvider; - else if (type == "type") - res->m_type = eDVBChannelQuery::tType; - else if (type == "bouquet") - res->m_type = eDVBChannelQuery::tBouquet; - else if (type == "satellitePosition") - res->m_type = eDVBChannelQuery::tSatellitePosition; - else if (type == "channelID") - res->m_type = eDVBChannelQuery::tChannelID; - else + res->m_type = decodeType(type); + + if (res->m_type == -1) { eDebug("malformed query: invalid type %s", type.c_str()); res = 0; return 1; } - eDebug("type is %d, nice!", res->m_type); - if (op == "==") res->m_inverse = 0; else if (op == "!=") @@ -529,7 +929,8 @@ RESULT eDVBChannelQuery::compile(ePtr &res, std::string query) std::list tokens; std::string current_token; - + std::string bouquet_name; + // eDebug("splitting %s....", query.c_str()); unsigned int i = 0; const char *splitchars="()"; @@ -570,15 +971,63 @@ RESULT eDVBChannelQuery::compile(ePtr &res, std::string query) // { // printf("%s\n", a->c_str()); // } + + int sort = eDVBChannelQuery::tName; + /* check for "ORDER BY ..." */ + + while (tokens.size() > 2) + { + std::list::iterator it = tokens.end(); + --it; --it; --it; + if (*it == "ORDER") + { + ++it; + if (*it == "BY") + { + ++it; + sort = decodeType(*it); + tokens.pop_back(); // ... + tokens.pop_back(); // BY + tokens.pop_back(); // ORDER + } else + sort = -1; + } + else if (*it == "FROM") + { + ++it; + if (*it == "BOUQUET") + { + ++it; + bouquet_name = *it; + tokens.pop_back(); // ... + tokens.pop_back(); // FROM + tokens.pop_back(); // BOUQUET + } + } + else + break; + } + + if (sort == -1) + { + eWarning("ORDER BY .. string invalid."); + res = 0; + return -1; + } + +// eDebug("sort by %d", sort); /* now we recursivly parse that. */ - return parseExpression(res, tokens.begin(), tokens.end()); -/* - res = new eDVBChannelQuery(); - res->m_type = eDVBChannelQuery::tName; - res->m_inverse = 0; - res->m_string = query; - return 0; */ + int r = parseExpression(res, tokens.begin(), tokens.end()); + + if (res) + { + res->m_sort = sort; + res->m_bouquet_name = bouquet_name; + } + +// eDebug("return: %d", r); + return r; } DEFINE_REF(eDVBChannelQuery);