mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-neutrino.git
synced 2025-08-31 17:31:11 +02:00
git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@245 e54a6e83-5905-42d5-8d5c-058d10e6a962
Origin commit data
------------------
Branch: ni/coolstream
Commit: 56536f2709
Author: Jacek Jendrzej <overx300@gmail.com>
Date: 2010-02-01 (Mon, 01 Feb 2010)
Origin message was:
------------------
add digi icons to menus
git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@245 e54a6e83-5905-42d5-8d5c-058d10e6a962
------------------
This commit was generated by Migit
582 lines
17 KiB
C++
582 lines
17 KiB
C++
//
|
||
// $Id: SIevents.cpp,v 1.35 2008/08/16 19:23:18 seife Exp $
|
||
//
|
||
// classes SIevent and SIevents (dbox-II-project)
|
||
//
|
||
// Homepage: http://dbox2.elxsi.de
|
||
//
|
||
// Copyright (C) 2001 fnbrd (fnbrd@gmx.de)
|
||
//
|
||
// This program is free software; you can redistribute it and/or modify
|
||
// it under the terms of the GNU General Public License as published by
|
||
// the Free Software Foundation; either version 2 of the License, or
|
||
// (at your option) any later version.
|
||
//
|
||
// This program is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
// GNU General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU General Public License
|
||
// along with this program; if not, write to the Free Software
|
||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
//
|
||
//
|
||
|
||
#include <assert.h>
|
||
#include <stdio.h>
|
||
#include <time.h>
|
||
|
||
#include <unistd.h>
|
||
#include <sys/ioctl.h>
|
||
#include <fcntl.h>
|
||
#include <sys/poll.h> // fuer poll()
|
||
|
||
#include <set>
|
||
#include <algorithm>
|
||
#include <string>
|
||
|
||
#include "SIlanguage.hpp"
|
||
#include "SIutils.hpp"
|
||
#include "SIservices.hpp"
|
||
#include "SIevents.hpp"
|
||
#ifdef UPDATE_NETWORKS
|
||
#include "SIbouquets.hpp"
|
||
#include "SInetworks.hpp"
|
||
#endif
|
||
#include "SIsections.hpp"
|
||
#include <dmxapi.h>
|
||
|
||
const std::string languangeOFF = "OFF";
|
||
|
||
SIevent::SIevent(const struct eit_event *e)
|
||
{
|
||
eventID = (e->event_id_hi << 8) | e->event_id_lo;
|
||
time_t start_time = changeUTCtoCtime(((const unsigned char *)e) + 2);
|
||
unsigned long duration = 0;
|
||
|
||
if (!((e->duration_hi == 0xff) && (e->duration_mid == 0xff) && (e->duration_lo == 0xff)))
|
||
duration = ((e->duration_hi)>>4)*10*3600L + ((e->duration_hi)&0x0f)*3600L +
|
||
((e->duration_mid)>>4)*10*60L + ((e->duration_mid)&0x0f)*60L +
|
||
((e->duration_lo)>>4)*10 + ((e->duration_lo)&0x0f);
|
||
|
||
if (start_time && duration)
|
||
times.insert(SItime(start_time, duration));
|
||
|
||
running = (int)e->running_status;
|
||
|
||
service_id = 0;
|
||
original_network_id = 0;
|
||
transport_stream_id = 0;
|
||
}
|
||
|
||
SIevent::SIevent(const t_original_network_id _original_network_id, const t_transport_stream_id _transport_stream_id, const t_service_id _service_id,
|
||
const unsigned short _event_id)
|
||
{
|
||
original_network_id = _original_network_id;
|
||
transport_stream_id = _transport_stream_id;
|
||
service_id = _service_id;
|
||
eventID = _event_id;
|
||
/* contentClassification = "";
|
||
userClassification = "";
|
||
itemDescription = "";
|
||
item = "";
|
||
extendedText = "";*/
|
||
}
|
||
|
||
// Std-Copy
|
||
SIevent::SIevent(const SIevent &e)
|
||
{
|
||
eventID=e.eventID;
|
||
langName=e.langName;
|
||
langText=e.langText;
|
||
// startzeit=e.startzeit;
|
||
// dauer=e.dauer;
|
||
times=e.times;
|
||
service_id = e.service_id;
|
||
original_network_id = e.original_network_id;
|
||
transport_stream_id = e.transport_stream_id;
|
||
itemDescription=e.itemDescription;
|
||
item=e.item;
|
||
langExtendedText=e.langExtendedText;
|
||
contentClassification=e.contentClassification;
|
||
userClassification=e.userClassification;
|
||
components=e.components;
|
||
ratings=e.ratings;
|
||
linkage_descs=e.linkage_descs;
|
||
running=e.running;
|
||
}
|
||
|
||
int SIevent::saveXML(FILE *file, const char *serviceName) const
|
||
{
|
||
if(saveXML0(file))
|
||
return 1;
|
||
if(serviceName) {
|
||
if(fprintf(file, " <service_name>")<0)
|
||
return 2;
|
||
saveStringToXMLfile(file, serviceName);
|
||
if(fprintf(file, "</service_name>\n")<0)
|
||
return 3;
|
||
}
|
||
return saveXML2(file);
|
||
}
|
||
|
||
char SIevent::getFSK() const
|
||
{
|
||
for (SIparentalRatings::iterator it = ratings.begin(); it != ratings.end(); ++it)
|
||
{
|
||
if (it->countryCode == "DEU")
|
||
{
|
||
if ((it->rating >= 0x01) && (it->rating <= 0x0F))
|
||
return (it->rating + 3); // 0x01 to 0x0F minimum age = rating + 3 years
|
||
else
|
||
return (it->rating == 0 ? 0 : 18); // return FSK 18 for : 0x10 to 0xFF defined by the broadcaster
|
||
}
|
||
}
|
||
if (!ratings.empty())
|
||
{
|
||
if ((ratings.begin()->rating >= 0x01) && (ratings.begin()->rating <= 0x0F))
|
||
return (ratings.begin()->rating + 3);
|
||
else
|
||
return (ratings.begin()->rating == 0 ? 0 : 18);
|
||
}
|
||
|
||
return 0x00; // 0x00 undefined
|
||
}
|
||
|
||
int SIevent::saveXML0(FILE *file) const
|
||
{
|
||
if(fprintf(file, "\t\t<event id=\"%04x\">\n", eventID)<0)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
int SIevent::saveXML2(FILE *file) const
|
||
{
|
||
for (std::map<std::string, std::string>::const_iterator
|
||
i = langName.begin() ;
|
||
i != langName.end() ;
|
||
i++) {
|
||
if (i->second.length()) {
|
||
fprintf(file, "\t\t\t<name lang=\"%s\" string=\"", i->first.c_str());
|
||
saveStringToXMLfile(file, i->second.c_str());
|
||
fprintf(file, "\"/>\n");
|
||
}
|
||
}
|
||
for (std::map<std::string, std::string>::const_iterator
|
||
i = langText.begin() ;
|
||
i != langText.end() ;
|
||
i++) {
|
||
if (i->second.length()) {
|
||
fprintf(file, "\t\t\t<text lang=\"%s\" string=\"", i->first.c_str());
|
||
saveStringToXMLfile(file, i->second.c_str());
|
||
fprintf(file, "\"/>\n");
|
||
}
|
||
}
|
||
if(item.length()) {
|
||
fprintf(file, "\t\t\t<item string=\"");
|
||
saveStringToXMLfile(file, item.c_str());
|
||
fprintf(file, "\"/>\n");
|
||
}
|
||
if(itemDescription.length()) {
|
||
fprintf(file, "\t\t\t<item_description string=\"");
|
||
saveStringToXMLfile(file, itemDescription.c_str());
|
||
fprintf(file, "\"/>\n");
|
||
}
|
||
for (std::map<std::string, std::string>::const_iterator
|
||
i = langExtendedText.begin() ;
|
||
i != langExtendedText.end() ;
|
||
i++) {
|
||
if (i->second.length()) {
|
||
fprintf(file, "\t\t\t<extended_text lang=\"%s\" string=\"", i->first.c_str());
|
||
saveStringToXMLfile(file, i->second.c_str());
|
||
fprintf(file, "\"/>\n");
|
||
}
|
||
}
|
||
for_each(times.begin(), times.end(), saveSItimeXML(file));
|
||
for(unsigned i=0; i<contentClassification.length(); i++)
|
||
fprintf(file, "\t\t\t<content class=\"%02x\" user=\"%02x\"/>\n", contentClassification[i], userClassification[i]);
|
||
for_each(components.begin(), components.end(), saveSIcomponentXML(file));
|
||
for_each(ratings.begin(), ratings.end(), saveSIparentalRatingXML(file));
|
||
for_each(linkage_descs.begin(), linkage_descs.end(), saveSIlinkageXML(file));
|
||
fprintf(file, "\t\t</event>\n");
|
||
return 0;
|
||
}
|
||
|
||
std::string SIevent::getName() const
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
std::map<std::string, std::string>::const_iterator it = langName.begin() ;
|
||
if (it != langName.end()) return it->second;
|
||
else return("");
|
||
} else {
|
||
std::string retval;
|
||
SIlanguage::filter(langName, 1, retval);
|
||
return retval;
|
||
}
|
||
}
|
||
|
||
void SIevent::setName(const std::string &lang, const std::string &name)
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
langName[languangeOFF] = name;
|
||
} else {
|
||
langName[lang] = name;
|
||
}
|
||
}
|
||
|
||
std::string SIevent::getText() const
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
std::map<std::string, std::string>::const_iterator it = langText.begin() ;
|
||
if (it != langText.end()) return it->second;
|
||
else return("");
|
||
} else {
|
||
std::string retval;
|
||
SIlanguage::filter(langText, 0, retval);
|
||
return retval;
|
||
}
|
||
}
|
||
|
||
void SIevent::setText(const std::string &lang, const std::string &text)
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
langText[languangeOFF] = text;
|
||
} else {
|
||
langText[lang] = text;
|
||
}
|
||
}
|
||
|
||
std::string SIevent::getExtendedText() const
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
std::map<std::string, std::string>::const_iterator it = langExtendedText.begin() ;
|
||
if (it != langExtendedText.end()) return it->second;
|
||
else return("");
|
||
} else {
|
||
std::string retval;
|
||
SIlanguage::filter(langExtendedText, 0, retval);
|
||
return retval;
|
||
}
|
||
}
|
||
|
||
void SIevent::appendExtendedText(const std::string &lang, const std::string &text)
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
langExtendedText[languangeOFF] += text;
|
||
} else {
|
||
langExtendedText[lang] += text;
|
||
}
|
||
}
|
||
|
||
void SIevent::setExtendedText(const std::string &lang, const std::string &text)
|
||
{
|
||
if (CSectionsdClient::LANGUAGE_MODE_OFF == SIlanguage::getMode()){
|
||
langExtendedText[languangeOFF] = text;
|
||
} else {
|
||
langExtendedText[lang] = text;
|
||
}
|
||
}
|
||
|
||
void SIevent::dump(void) const
|
||
{
|
||
printf("Unique key: %llx\n", uniqueKey());
|
||
if(original_network_id)
|
||
printf("Original-Network-ID: %hu\n", original_network_id);
|
||
if (service_id)
|
||
printf("Service-ID: %hu\n", service_id);
|
||
printf("Event-ID: %hu\n", eventID);
|
||
if(item.length())
|
||
printf("Item: %s\n", item.c_str());
|
||
if(itemDescription.length())
|
||
printf("Item-Description: %s\n", itemDescription.c_str());
|
||
|
||
for (std::map<std::string, std::string>::const_iterator it = langName.begin() ;
|
||
it != langName.end() ; ++it)
|
||
printf("Name (%s): %s\n", it->first.c_str(), it->second.c_str());
|
||
for (std::map<std::string, std::string>::const_iterator it = langText.begin() ;
|
||
it != langText.end() ; ++it)
|
||
printf("Text (%s): %s\n", it->first.c_str(), it->second.c_str());
|
||
for (std::map<std::string, std::string>::const_iterator it = langExtendedText.begin() ;
|
||
it != langExtendedText.end() ; ++it)
|
||
printf("Extended-Text (%s): %s\n", it->first.c_str(), it->second.c_str());
|
||
|
||
if(contentClassification.length()) {
|
||
printf("Content classification:");
|
||
for(unsigned i=0; i<contentClassification.length(); i++)
|
||
printf(" 0x%02hhx", contentClassification[i]);
|
||
printf("\n");
|
||
}
|
||
if(userClassification.length()) {
|
||
printf("User classification:");
|
||
for(unsigned i=0; i<userClassification.length(); i++)
|
||
printf(" 0x%02hhx", userClassification[i]);
|
||
printf("\n");
|
||
}
|
||
/*
|
||
if(startzeit)
|
||
printf("Startzeit: %s", ctime(&startzeit));
|
||
if(dauer)
|
||
printf("Dauer: %02u:%02u:%02u (%umin, %us)\n", dauer/3600, (dauer%3600)/60, dauer%60, dauer/60, dauer);
|
||
*/
|
||
for_each(times.begin(), times.end(), printSItime());
|
||
for_each(components.begin(), components.end(), printSIcomponent());
|
||
for_each(ratings.begin(), ratings.end(), printSIparentalRating());
|
||
for_each(linkage_descs.begin(), linkage_descs.end(), printSIlinkage());
|
||
}
|
||
|
||
void SIevent::dumpSmall(void) const
|
||
{
|
||
for (std::map<std::string, std::string>::const_iterator it = langName.begin() ;
|
||
it != langName.end() ; ++it)
|
||
printf("Name (%s): %s\n", it->first.c_str(), it->second.c_str());
|
||
for (std::map<std::string, std::string>::const_iterator it = langText.begin() ;
|
||
it != langText.end() ; ++it)
|
||
printf("Text (%s): %s\n", it->first.c_str(), it->second.c_str());
|
||
for (std::map<std::string, std::string>::const_iterator it = langExtendedText.begin() ;
|
||
it != langExtendedText.end() ; ++it)
|
||
printf("Extended-Text (%s): %s\n", it->first.c_str(), it->second.c_str());
|
||
/*
|
||
if(startzeit)
|
||
printf("Startzeit: %s", ctime(&startzeit));
|
||
if(dauer)
|
||
printf("Dauer: %02u:%02u:%02u (%umin, %us)\n", dauer/3600, (dauer%3600)/60, dauer%60, dauer/60, dauer);
|
||
*/
|
||
for_each(times.begin(), times.end(), printSItime());
|
||
for_each(ratings.begin(), ratings.end(), printSIparentalRating());
|
||
for_each(linkage_descs.begin(), linkage_descs.end(), printSIlinkage());
|
||
}
|
||
/*
|
||
// Liest n Bytes aus einem Socket per read
|
||
inline int readNbytes(int fd, char *buf, int n)
|
||
{
|
||
int j;
|
||
for(j=0; j<n;) {
|
||
int r=read (fd, buf, n-j);
|
||
if(r<=0) {
|
||
perror ("read");
|
||
return -1;
|
||
}
|
||
j+=r;
|
||
buf+=r;
|
||
}
|
||
return j;
|
||
}
|
||
*/
|
||
|
||
#ifndef DO_NOT_INCLUDE_STUFF_NOT_NEEDED_FOR_SECTIONSD
|
||
// Liest n Bytes aus einem Socket per read
|
||
// Liefert 0 bei timeout
|
||
// und -1 bei Fehler
|
||
// ansonsten die Anzahl gelesener Bytes
|
||
inline int readNbytes(int fd, char *buf, int n, unsigned timeoutInSeconds)
|
||
{
|
||
int j;
|
||
for(j=0; j<n;) {
|
||
struct pollfd ufds;
|
||
// memset(&ufds, 0, sizeof(ufds));
|
||
ufds.fd=fd;
|
||
ufds.events=POLLIN|POLLPRI;
|
||
ufds.revents=0;
|
||
int rc=poll(&ufds, 1, timeoutInSeconds*1000);
|
||
if(!rc)
|
||
return 0; // timeout
|
||
else if(rc<0) {
|
||
perror ("poll");
|
||
return -1;
|
||
}
|
||
int r=read (fd, buf, n-j);
|
||
if(r<=0) {
|
||
perror ("read");
|
||
return -1;
|
||
}
|
||
j+=r;
|
||
buf+=r;
|
||
}
|
||
return j;
|
||
}
|
||
|
||
SIevent SIevent::readActualEvent(t_service_id serviceID, unsigned timeoutInSeconds)
|
||
{
|
||
int fd;
|
||
SIevent evt; // Std-Event das bei Fehler zurueckgeliefert wird
|
||
struct SI_section_header header;
|
||
char *buf;
|
||
unsigned short section_length;
|
||
|
||
if ((fd = open(DEMUX_DEVICE, O_RDWR)) == -1) {
|
||
perror(DEMUX_DEVICE);
|
||
return evt;
|
||
}
|
||
|
||
if (!setfilter(fd, 0x12, 0x4e, 0xff, DMX_IMMEDIATE_START | DMX_CHECK_CRC)) {
|
||
close(fd);
|
||
return evt;
|
||
}
|
||
|
||
time_t szeit = time(NULL);
|
||
|
||
// Segment mit Event fuer sid suchen
|
||
do {
|
||
int rc = readNbytes(fd, (char *)&header, sizeof(header), timeoutInSeconds);
|
||
|
||
if(!rc)
|
||
break; // timeout
|
||
|
||
else if(rc<0) {
|
||
close(fd);
|
||
perror ("read header");
|
||
return evt;
|
||
}
|
||
|
||
section_length = (header.section_length_hi << 8) | header.section_length_lo;
|
||
|
||
buf = new char[sizeof(header) + section_length - 5];
|
||
|
||
if (!buf) {
|
||
close(fd);
|
||
printf("Not enough memory!\n");
|
||
return evt;
|
||
}
|
||
|
||
// Den Header kopieren
|
||
memcpy(buf, &header, sizeof(header));
|
||
rc = readNbytes(fd, &buf[sizeof(header)], section_length - 5, timeoutInSeconds);
|
||
|
||
if(!rc) {
|
||
delete[] buf;
|
||
break; // timeout
|
||
}
|
||
|
||
if (rc < 0) {
|
||
close(fd);
|
||
delete[] buf;
|
||
perror ("read section");
|
||
return evt;
|
||
}
|
||
|
||
if (header.current_next_indicator) {
|
||
// Wir wollen nur aktuelle sections
|
||
SIsectionEIT e(SIsection(sizeof(header) + section_length - 5, buf));
|
||
time_t zeit = time(NULL);
|
||
for (SIevents::iterator k = e.events().begin(); k != e.events().end(); ++k)
|
||
if (k->service_id == serviceID)
|
||
for (SItimes::iterator t = k->times.begin(); t != k->times.end(); ++t)
|
||
if ((t->startzeit <= zeit) && (zeit <= (long)(t->startzeit+t->dauer))) {
|
||
close(fd);
|
||
return SIevent(*k);
|
||
}
|
||
}
|
||
|
||
else {
|
||
delete[] buf;
|
||
}
|
||
|
||
} while (time(NULL) < szeit + (long)timeoutInSeconds);
|
||
|
||
close(fd);
|
||
return evt;
|
||
}
|
||
|
||
void SIevents::removeOldEvents(long seconds)
|
||
{
|
||
time_t current_time = time(NULL);
|
||
|
||
for(SIevents::iterator it = begin(); it != end(); )
|
||
{
|
||
// "it->times.erase(kt);":
|
||
// passing `const SItimes' as `this' argument of `void set<SItime,less<SItime>,allocator<SItime> >::erase(_Rb_tree_iterator<SItime,const SItime &,const SItime *>)' discards qualifiers
|
||
|
||
// hence we have to modify a copy
|
||
|
||
SIevent copy_of_event(*it);
|
||
bool copy_has_changed = false;
|
||
|
||
for (SItimes::iterator jt = copy_of_event.times.begin(); jt != copy_of_event.times.end(); )
|
||
{
|
||
if ((jt->startzeit) + (int)(jt->dauer) < current_time - seconds)
|
||
{
|
||
copy_of_event.times.erase(jt++);
|
||
copy_has_changed = true;
|
||
}
|
||
else
|
||
++jt;
|
||
}
|
||
if (copy_has_changed)
|
||
{
|
||
erase(it++);
|
||
|
||
// Set has the important property that inserting a new element into a set does not
|
||
// invalidate iterators that point to existing elements.
|
||
if (copy_of_event.times.size() != 0)
|
||
#ifdef DEBUG
|
||
assert((++insert(it, copy_of_event)) == it);
|
||
#else
|
||
insert(it, copy_of_event); // it is the hint where to insert (I hope that doesn't invalidate it)
|
||
// insert(copy_of_event); // alternative method without hint
|
||
#endif
|
||
|
||
}
|
||
else
|
||
++it;
|
||
}
|
||
}
|
||
|
||
// Entfernt anhand der Services alle time shifted events (ohne Text,
|
||
// mit service-id welcher im nvod steht)
|
||
// und sortiert deren Zeiten in die Events mit dem Text ein.
|
||
void SIevents::mergeAndRemoveTimeShiftedEvents(const SIservices &services)
|
||
{
|
||
// Wir gehen alle services durch, suchen uns die services mit nvods raus
|
||
// und fuegen dann die Zeiten der Events mit der service-id eines nvods
|
||
// in das entsprechende Event mit der service-id das die nvods hat ein.
|
||
// die 'nvod-events' werden auch geloescht
|
||
|
||
// SIevents eventsToDelete; // Hier rein kommen Events die geloescht werden sollen
|
||
for(SIservices::iterator k=services.begin(); k!=services.end(); ++k)
|
||
if(k->nvods.size()) {
|
||
// NVOD-Referenzen gefunden
|
||
// Zuerst mal das Event mit dem Text holen
|
||
iterator e;
|
||
for(e=begin(); e!=end(); ++e)
|
||
if(e->service_id == k->service_id)
|
||
break;
|
||
if(e!=end()) {
|
||
// *e == event mit dem Text
|
||
SIevent newEvent(*e); // Kopie des Events
|
||
// Jetzt die nvods druchgehen und deren Uhrzeiten in obiges Event einfuegen
|
||
for(SInvodReferences::iterator n=k->nvods.begin(); n!=k->nvods.end(); ++n) {
|
||
// Alle druchgehen und deren Events suchen
|
||
for(iterator en=begin(); en!=end(); en++) {
|
||
if(en->service_id==n->getServiceID()) {
|
||
newEvent.times.insert(en->times.begin(), en->times.end());
|
||
// newEvent.times.insert(SItime(en->startzeit, en->dauer));
|
||
// eventsToDelete.insert(SIevent(*en));
|
||
}
|
||
}
|
||
}
|
||
erase(e); // Altes Event loeschen -> iterator (e) ung<6E>ltig
|
||
insert(newEvent); // und das erweiterte Event wieder einfuegen
|
||
}
|
||
}
|
||
|
||
//
|
||
// delete all events with serviceID that have a service type 0
|
||
//
|
||
for (iterator it = begin(); it != end(); )
|
||
{
|
||
SIservices::iterator s = services.find(SIservice(it->service_id, it->original_network_id, it->transport_stream_id));
|
||
if ((s != services.end()) && (s->serviceTyp == 0))
|
||
{
|
||
// Set is a Sorted Associative Container
|
||
// Erasing an element from a set also does not invalidate any iterators,
|
||
// except, of course, for iterators that actually point to the element
|
||
// that is being erased.
|
||
erase(it++);
|
||
}
|
||
else
|
||
++it;
|
||
}
|
||
}
|
||
#endif
|