summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoretobi <git@e-tobi.net>2013-03-10 02:19:57 +0100
committeretobi <git@e-tobi.net>2013-03-10 17:15:21 +0100
commit77f4101fcb296de31f4856376d2f93b2794fcc02 (patch)
treea1f0947c7789f408ef73f016da1487e32424435d
parent1dbdedfbbbd4fde5da95fdc07b0547517665c09e (diff)
downloadvdr-plugin-osdteletext-77f4101fcb296de31f4856376d2f93b2794fcc02.tar.gz
vdr-plugin-osdteletext-77f4101fcb296de31f4856376d2f93b2794fcc02.tar.bz2
Refactoring: Moved storage related classes into their own files
-rw-r--r--Makefile2
-rw-r--r--legacystorage.c144
-rw-r--r--legacystorage.h40
-rw-r--r--osdteletext.c14
-rw-r--r--packedstorage.c131
-rw-r--r--packedstorage.h36
-rw-r--r--pageid.h31
-rw-r--r--rootdir.c21
-rw-r--r--rootdir.h22
-rw-r--r--storage.c142
-rw-r--r--storage.h65
-rw-r--r--txtrecv.c409
-rw-r--r--txtrecv.h110
13 files changed, 646 insertions, 521 deletions
diff --git a/Makefile b/Makefile
index 9ef006c..dea6845 100644
--- a/Makefile
+++ b/Makefile
@@ -53,7 +53,7 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
-OBJS = $(PLUGIN).o menu.o txtfont.o txtrecv.o txtrender.o displaybase.o display.o
+OBJS = $(PLUGIN).o menu.o txtfont.o txtrecv.o txtrender.o displaybase.o display.o storage.o legacystorage.o packedstorage.o rootdir.o
### The main target:
diff --git a/legacystorage.c b/legacystorage.c
new file mode 100644
index 0000000..3bed512
--- /dev/null
+++ b/legacystorage.c
@@ -0,0 +1,144 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include <sys/vfs.h>
+
+#include "legacystorage.h"
+
+LegacyStorage::LegacyStorage(int maxMB)
+ : fsBlockSize(1), pageBytes(TELETEXT_PAGESIZE)
+{
+ initMaxStorage(maxMB);
+}
+
+LegacyStorage::~LegacyStorage() {
+}
+
+/*
+static inline int FilesForMegabytes(double MB, int blocksize) {
+ double pageBytes;
+ if (TELETEXT_PAGESIZE<=blocksize)
+ pageBytes=blocksize;
+ else
+ pageBytes=((TELETEXT_PAGESIZE/blocksize)+1)*blocksize;
+ //reserve 10% for directories
+ return (int)( (1024.0 * 1024.0 * (MB-MB*0.1)) / pageBytes );
+}*/
+
+int LegacyStorage::actualFileSize(int netFileSize) {
+ if (netFileSize<=0)
+ return 0;
+ if (netFileSize<=fsBlockSize)
+ return fsBlockSize;
+ else
+ return ((netFileSize/fsBlockSize)+1)*fsBlockSize;
+}
+
+//max==0 means unlimited, max==-1 means a reasonable default value shall be calculated
+void LegacyStorage::initMaxStorage(int maxMB) {
+ struct statfs fs;
+ if (statfs(getRootDir(), &fs)!=0) {
+ esyslog("OSD-Teletext: Error statfs'ing root directory \"%s\": %s, cache size uncontrolled", getRootDir(), strerror(errno));
+ return;
+ }
+ fsBlockSize=fs.f_bsize;
+
+ pageBytes=actualFileSize(TELETEXT_PAGESIZE);
+
+ if (maxMB>=0) {
+ if (maxMB<3) {
+ esyslog("OSD-Teletext: Request to use at most %d MB for caching. This is not enough, using 3 MB", maxMB);
+ maxMB=3;
+ }
+ maxBytes=MEGABYTE(maxMB);
+ } else if (maxMB==-1) {
+ //calculate a default value
+ double blocksPerMeg = 1024.0 * 1024.0 / fs.f_bsize;
+ double capacityMB=fs.f_blocks / blocksPerMeg;
+ double freeMB=(fs.f_bavail / blocksPerMeg);
+ if (capacityMB<=50 || freeMB<50) {
+ //small (<=50MB) filesystems as root dir are assumed to be dedicated for use as a teletext cache
+ //for others, the maximum default size is set to 50 MB
+ maxBytes=MEGABYTE((int)freeMB);
+ //maxPages= FilesForMegabytes(freeMB, fs.f_bsize);
+ if (freeMB<3.0) {
+ esyslog("OSD-Teletext: Less than %.1f MB free on filesystem of root directory \"%s\"!", freeMB, getRootDir());
+ maxBytes=MEGABYTE(3);
+ }
+ } else {
+ //the maximum default size is set to 50 MB
+ maxBytes=MEGABYTE(50);
+ }
+ //printf("Set maxBytes to %ld, %.2f %.2f\n", maxBytes, capacityMB, freeMB);
+ }
+}
+
+void LegacyStorage::cleanUp() {
+ byteCount -= Storage::doCleanUp();
+}
+
+void LegacyStorage::registerFile(PageID page) {
+ //pageBytes is already effective size
+ if ( maxBytes && (byteCount+=pageBytes)>maxBytes )
+ freeSpace();
+}
+
+StorageHandle LegacyStorage::openForReading(PageID page, bool countAsAccess) {
+ //the countAsAccess argument was intended for use in a LRU cache, currently unused
+ char filename[PATH_MAX];
+ getFilename(filename, sizeof(filename), page);
+ StorageHandle ret=(StorageHandle)open(filename, O_RDONLY);
+ return ret;
+}
+
+StorageHandle LegacyStorage::openForWriting(PageID page) {
+ static bool wroteError=false;
+ char filename[PATH_MAX];
+ getFilename(filename, sizeof(filename), page);
+ bool existed=exists(filename);
+ //first try
+ StorageHandle fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd) {
+ if (!existed)
+ registerFile(page);
+ return fd;
+ }
+ //no space on disk? make some space available
+ if (errno == ENOSPC)
+ freeSpace();
+ //second try
+ fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (!fd && !wroteError) {
+ //report error to syslog - once!
+ wroteError=true;
+ esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno));
+ }
+ //make sure newly created files are counted
+ if (fd && !existed)
+ registerFile(page);
+ return fd;
+}
+
+ssize_t LegacyStorage::write(const void *ptr, size_t size, StorageHandle stream) {
+ ssize_t written;
+ if (!(written=::write((int)stream, ptr, size)) ) {
+ switch (errno) {
+ case ENOSPC:
+ freeSpace();
+ return ::write((int)stream, ptr, size);
+ case EINTR:
+ esyslog("OSD-Teletext: EINTR while writing. Please contact the author and tell him this happened.");
+ break;
+ default:
+ break;
+ }
+ }
+ return written;
+}
diff --git a/legacystorage.h b/legacystorage.h
new file mode 100644
index 0000000..266a6fd
--- /dev/null
+++ b/legacystorage.h
@@ -0,0 +1,40 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __LEGACYSTORAGE_H
+#define __LEGACYSTORAGE_H
+
+#include "storage.h"
+
+class LegacyStorage : public Storage {
+private:
+ void initMaxStorage(int maxMB=-1);
+public:
+ LegacyStorage(int maxMB);
+ virtual ~LegacyStorage();
+ virtual void cleanUp();
+
+ virtual StorageHandle openForWriting(PageID page);
+ virtual StorageHandle openForReading(PageID page, bool countAsAccess);
+ virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream);
+ virtual ssize_t read(void *ptr, size_t size, StorageHandle stream)
+ { return ::read((int)stream, ptr, size); }
+ virtual void close(StorageHandle stream)
+ { ::close((int)stream); }
+protected:
+ void registerFile(PageID page);
+ virtual int actualFileSize(int netFileSize);
+ //int maxPages;
+ long maxBytes;
+ int fsBlockSize;
+ int pageBytes;
+};
+
+#endif
diff --git a/osdteletext.c b/osdteletext.c
index 04e0b49..f03ef54 100644
--- a/osdteletext.c
+++ b/osdteletext.c
@@ -20,6 +20,8 @@ using namespace std;
#include "menu.h"
#include "txtrecv.h"
#include "setup.h"
+#include "legacystorage.h"
+#include "packedstorage.h"
#if defined(APIVERSNUM) && APIVERSNUM < 10739
#error "VDR-1.7.39 API version or greater is required!"
@@ -175,10 +177,16 @@ bool cPluginTeletextosd::ProcessArgs(int argc, char *argv[])
bool cPluginTeletextosd::Start(void)
{
- // Start any background activities the plugin shall perform.
- //Clean any files which might be remaining from the last session,
+ // Start any background activities the plugin shall perform.
+ //Clean any files which might be remaining from the last session,
//perhaps due to a crash they have not been deleted.
- storage = Storage::CreateInstance(storageSystem, maxStorage);
+ switch (storageSystem) {
+ case Storage::StorageSystemLegacy:
+ storage = new LegacyStorage(maxStorage);
+ case Storage::StorageSystemPacked:
+ default:
+ storage = new PackedStorage(maxStorage);
+ }
initTexts();
if (startReceiver)
diff --git a/packedstorage.c b/packedstorage.c
new file mode 100644
index 0000000..f23866f
--- /dev/null
+++ b/packedstorage.c
@@ -0,0 +1,131 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "packedstorage.h"
+
+PackedStorage::PackedStorage(int maxMB)
+ : LegacyStorage(maxMB) {
+}
+#define TOC_SIZE 8
+//The file structure is simple:
+// TOC_SIZE*PageAddress contains the numbers of the following pages
+// TOC_SIZE*TELETEXT_PAGESIZE contains the page data
+//and the same again.
+bool PackedStorage::seekTo(PageID page, int desc, bool create) {
+ lseek(desc, 0, SEEK_SET);
+ PageAddress addr[TOC_SIZE];
+
+ while (::read(desc, addr, sizeof(addr)) == sizeof(addr)) {
+ lseek(desc, 0, SEEK_CUR);
+ for (int index=0; index<TOC_SIZE; index++) {
+ if (addr[index]==page) {
+ lseek(desc, index*TELETEXT_PAGESIZE, SEEK_CUR);
+ return true;
+ } else if (addr[index].page==0) {
+ //0 means: no more pages follow
+ if (create) {
+ //rewind what was read
+ lseek(desc, -(off_t)sizeof(addr), SEEK_CUR);
+ //update index
+ addr[index]=page;
+ if (::write(desc, addr, sizeof(addr)) != sizeof(addr))
+ return false;
+ //seek to data position
+ lseek(desc, TELETEXT_PAGESIZE*index, SEEK_CUR);
+ return true;
+ } else
+ return false;
+ }
+ }
+
+ //seek over data area
+ lseek(desc, TELETEXT_PAGESIZE*TOC_SIZE, SEEK_CUR);
+ }
+
+ int oldSize=actualFileSize(lseek(desc, 0, SEEK_CUR));
+ if (create) {
+ //create a new set of a TOC and a TOC_SIZE*TELETEXT_PAGESIZE data area
+ memset(addr, 0, sizeof(addr));
+ //first entry is our page
+ addr[0]=page;
+ if (::write(desc, addr, sizeof(addr)) != sizeof(addr))
+ return false;
+ //seek beyond end of file
+ lseek(desc, (TELETEXT_PAGESIZE*TOC_SIZE)-1, SEEK_CUR);
+ //write one byte to enlarge the file to the sought position
+ char c=1;
+ if (::write(desc, &c, 1) != 1)
+ return false;
+ //Now, calculate new file size
+ byteCount += ( actualFileSize(lseek(desc, 0, SEEK_CUR)) - oldSize );
+ //seek to beginning of data, which is requested
+ lseek(desc, -(off_t)(TELETEXT_PAGESIZE*TOC_SIZE), SEEK_CUR);
+ return true;
+ } else
+ return false;
+}
+
+void PackedStorage::getFilename(char *buffer, int bufLength, PageID page) {
+ //This is a different scheme: page 576_07 will have the name 570s.vtx, the same as e.g. 571_01 or 575_00
+ //Think of "the five hundred seventies"
+ snprintf(buffer, bufLength, "%s/%s/%03xs.vtx", getRootDir(),
+ *page.channel.ToString(), (page.page & 0xFF0));
+}
+
+StorageHandle PackedStorage::openForWriting(PageID page) {
+ static bool wroteError=false;
+ char filename[PATH_MAX];
+ getFilename(filename, sizeof(filename), page);
+ //first try
+ int desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (desc != -1) {
+ if (!seekTo(page, desc, true)) {
+ ::close(desc);
+ return StorageHandle();
+ }
+ if ( maxBytes && byteCount>maxBytes )
+ freeSpace();
+ return (StorageHandle)desc;
+ }
+ //no space on disk? make some space available
+ if (errno == ENOSPC)
+ freeSpace();
+ //second try
+ desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (desc==-1 && !wroteError) {
+ //report error to syslog - once!
+ wroteError=true;
+ esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno));
+ }
+
+ if (desc==-1)
+ return StorageHandle();
+ else if (!seekTo(page, desc, true)) {
+ ::close(desc);
+ return StorageHandle();
+ }
+
+ if ( maxBytes && byteCount>maxBytes )
+ freeSpace();
+ return (StorageHandle)desc;
+}
+
+StorageHandle PackedStorage::openForReading(PageID page, bool countAsAccess) {
+ int desc;
+ if ( (desc=(int)LegacyStorage::openForReading(page, false))!= -1 ) {
+ if (!seekTo(page, desc, false)) {
+ //this is not an error condition here, may and shall happen!
+ ::close(desc);
+ } else {
+ return (StorageHandle)desc;
+ }
+ }
+ return StorageHandle();
+}
diff --git a/packedstorage.h b/packedstorage.h
new file mode 100644
index 0000000..5df219b
--- /dev/null
+++ b/packedstorage.h
@@ -0,0 +1,36 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __PACKEDSTORAGE_H
+#define __PACKEDSTORAGE_H
+
+#include "legacystorage.h"
+
+class PackedStorage : public LegacyStorage {
+public:
+ PackedStorage(int maxMB);
+
+ virtual void getFilename(char *buffer, int bufLength, PageID page);
+ virtual StorageHandle openForWriting(PageID page);
+ virtual StorageHandle openForReading(PageID page, bool countAsAccess);
+protected:
+ struct PageAddress {
+ bool operator==(const PageID &id) const
+ { return page==id.page && subPage==id.subPage; }
+ void operator=(const PageID &id)
+ { page=id.page; subPage=id.subPage; }
+ int page;
+ int subPage;
+ };
+ bool seekTo(PageID page, int fd, bool create);
+ void registerFile(PageID page);
+};
+
+#endif
diff --git a/pageid.h b/pageid.h
new file mode 100644
index 0000000..cb2709d
--- /dev/null
+++ b/pageid.h
@@ -0,0 +1,31 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __PAGEID_H
+#define __PAGEID_H
+
+#include <vdr/status.h>
+#include <vdr/receiver.h>
+#include <vdr/thread.h>
+#include <vdr/ringbuffer.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+struct PageID {
+ PageID() { page=subPage=0; }
+ PageID(tChannelID id, int p, int s) { set(id, p, s); }
+ void set(tChannelID id, int p, int s) { channel=id; page=p; subPage=s; }
+ tChannelID channel;
+ int page;
+ int subPage;
+};
+
+#endif
diff --git a/rootdir.c b/rootdir.c
new file mode 100644
index 0000000..7076e0d
--- /dev/null
+++ b/rootdir.c
@@ -0,0 +1,21 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "rootdir.h"
+
+const char *RootDir::root = "/var/cache/vdr/vtx";
+
+void RootDir::setRootDir(const char *newRoot) {
+ root=newRoot;
+}
+
+const char *RootDir::getRootDir() {
+ return root;
+}
diff --git a/rootdir.h b/rootdir.h
new file mode 100644
index 0000000..90ce49c
--- /dev/null
+++ b/rootdir.h
@@ -0,0 +1,22 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __ROOTDIR_H
+#define __ROOTDIR_H
+
+class RootDir {
+public:
+ static void setRootDir(const char *);
+ static const char *getRootDir();
+protected:
+ static const char *root;
+};
+
+#endif
diff --git a/storage.c b/storage.c
new file mode 100644
index 0000000..10bd74a
--- /dev/null
+++ b/storage.c
@@ -0,0 +1,142 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "storage.h"
+
+int Storage::doCleanUp() {
+ DIR *top=opendir(root);
+ int pagesDeleted=0;
+ if (top) {
+ struct dirent *chandir, path;
+ struct stat chandirstat;
+ char fullPath[PATH_MAX];
+ while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) {
+ if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0)
+ continue;
+ snprintf(fullPath, PATH_MAX, "%s/%s", root, chandir->d_name);
+ if (stat(fullPath, &chandirstat)==0) {
+ if (S_ISDIR(chandirstat.st_mode)) {
+ pagesDeleted+=cleanSubDir(fullPath);
+ }
+ }
+ }
+ closedir(top);
+ } else {
+ esyslog("OSD-Teletext: Error opening teletext storage directory \"%s\": %s", root, strerror(errno));
+ }
+ return pagesDeleted;
+}
+
+int Storage::cleanSubDir(const char *dir) {
+ static bool reportedError=false; //avoid filling up syslog
+ DIR *d=opendir(dir);
+ bool hadError=false;
+ int bytesDeleted=0;
+ if (d) {
+ struct dirent *txtfile, path;
+ struct stat txtfilestat;
+ char fullPath[PATH_MAX];
+ int filesize;
+ while ( (!readdir_r(d, &path, &txtfile) && txtfile != NULL) ) {
+ int len=strlen(txtfile->d_name);
+ //check that the file end with .vtx to avoid accidents and disasters
+ if (strcmp(txtfile->d_name+len-4, ".vtx")==0) {
+ snprintf(fullPath, PATH_MAX, "%s/%s", dir, txtfile->d_name);
+ stat(fullPath, &txtfilestat);
+ filesize=actualFileSize(txtfilestat.st_size);
+ int ret=unlink(fullPath);
+ if (ret==0)
+ bytesDeleted+=filesize;
+ else
+ hadError=ret;
+ }
+ }
+ closedir(d);
+ rmdir(dir);
+ } else {
+ if (!reportedError) {
+ esyslog("OSD-Teletext: Error opening teletext storage subdirectory \"%s\": %s", dir, strerror(errno));
+ reportedError=true;
+ }
+ }
+
+ if (hadError && !reportedError) {
+ esyslog("OSD-Teletext: Error removing teletext storage subdirectory \"%s\": %s", dir, strerror(hadError));
+ reportedError=true;
+ }
+ return bytesDeleted;
+}
+
+Storage::Storage()
+ : byteCount(0), failedFreeSpace(false)
+{
+}
+
+Storage::~Storage() {
+}
+
+void Storage::freeSpace() {
+ //there might be a situation where only the current directory is left and
+ //occupies the whole space. We cannot delete anything. Don't waste time scanning.
+ if (failedFreeSpace)
+ return;
+
+ //printf("freeSpace()\n");
+ time_t min=time(0);
+ char minDir[PATH_MAX];
+ char fullPath[PATH_MAX];
+ DIR *top=opendir(getRootDir());
+ if (top) {
+ int haveDir=0;
+ struct dirent *chandir, path;
+ struct stat chandirstat;
+ while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) {
+ if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0)
+ continue;
+ snprintf(fullPath, PATH_MAX, "%s/%s", getRootDir(), chandir->d_name);
+ if (stat(fullPath, &chandirstat)==0) {
+ if (S_ISDIR(chandirstat.st_mode)) {
+ if (chandirstat.st_ctime < min && strcmp(fullPath, currentDir)) {
+ min=chandirstat.st_ctime;
+ strcpy(minDir, fullPath);
+ haveDir++;
+ }
+ }
+ }
+ }
+ closedir(top);
+
+ //if haveDir, only current directory present, which must not be deleted
+ if (haveDir>=2)
+ byteCount-=cleanSubDir(minDir);
+ else
+ failedFreeSpace=true;
+ }
+}
+
+bool Storage::exists(const char* file) {
+ struct stat s;
+ return (stat(file, &s)==0);
+}
+
+void Storage::getFilename(char *buffer, int bufLength, PageID page) {
+ snprintf(buffer, bufLength, "%s/%s/%03x_%02x.vtx", getRootDir(),
+ *page.channel.ToString(), page.page, page.subPage);
+}
+
+void Storage::prepareDirectory(tChannelID chan) {
+ currentDir = cString::sprintf("%s/%s", root, *chan.ToString());
+ if (!MakeDirs(currentDir, 1)) {
+ esyslog("OSD-Teletext: Error preparing directory for channel \"%s\"",
+ *chan.ToString());
+ return;
+ }
+ failedFreeSpace=false;
+}
diff --git a/storage.h b/storage.h
new file mode 100644
index 0000000..29ae022
--- /dev/null
+++ b/storage.h
@@ -0,0 +1,65 @@
+/*************************************************************** -*- c++ -*-
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __STORAGE_H
+#define __STORAGE_H
+
+#include "rootdir.h"
+#include "pageid.h"
+
+#define TELETEXT_PAGESIZE 972
+
+struct StorageHandle {
+public:
+ StorageHandle() { handle=-1; }
+ StorageHandle(const StorageHandle &s) { handle=s.handle; }
+ StorageHandle(int h) { handle=h; }
+ StorageHandle &operator=(int h) { handle=h; return *this; }
+ StorageHandle &operator=(const StorageHandle &s) { handle=s.handle; return *this; }
+ operator bool() const { return handle!=-1; }
+ operator int() const { return handle; }
+private:
+ int handle;
+};
+
+enum StorageSystem { StorageSystemLegacy, StorageSystemPacked };
+
+class Storage : public RootDir {
+public:
+ virtual ~Storage();
+ enum StorageSystem { StorageSystemLegacy, StorageSystemPacked };
+ //must be called before the first call to instance()
+
+ //must be called before operation starts. Set all options (RootDir, maxStorage) before.
+ virtual void cleanUp() = 0;
+
+ virtual void getFilename(char *buffer, int bufLength, PageID page);
+ virtual void prepareDirectory(tChannelID chan);
+
+ virtual StorageHandle openForWriting(PageID page) = 0;
+ virtual StorageHandle openForReading(PageID page, bool countAsAccess) = 0;
+ virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream) = 0;
+ virtual ssize_t read(void *ptr, size_t size, StorageHandle stream) = 0;
+ virtual void close(StorageHandle stream) = 0;
+protected:
+ Storage();
+ int cleanSubDir(const char *dir);
+ int doCleanUp();
+ virtual int actualFileSize(int netFileSize) { return netFileSize; }
+ void freeSpace();
+ bool exists(const char* file);
+
+ long byteCount;
+ cString currentDir;
+private:
+ bool failedFreeSpace;
+};
+
+#endif
diff --git a/txtrecv.c b/txtrecv.c
index 9aa8984..f59c804 100644
--- a/txtrecv.c
+++ b/txtrecv.c
@@ -27,415 +27,6 @@
#include <sys/stat.h>
#include <fcntl.h>
-const char *RootDir::root = "/var/cache/vdr/vtx";
-
-void RootDir::setRootDir(const char *newRoot) {
- root=newRoot;
-}
-
-const char *RootDir::getRootDir() {
- return root;
-}
-
-int Storage::doCleanUp() {
- DIR *top=opendir(root);
- int pagesDeleted=0;
- if (top) {
- struct dirent *chandir, path;
- struct stat chandirstat;
- char fullPath[PATH_MAX];
- while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) {
- if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0)
- continue;
- snprintf(fullPath, PATH_MAX, "%s/%s", root, chandir->d_name);
- if (stat(fullPath, &chandirstat)==0) {
- if (S_ISDIR(chandirstat.st_mode)) {
- pagesDeleted+=cleanSubDir(fullPath);
- }
- }
- }
- closedir(top);
- } else {
- esyslog("OSD-Teletext: Error opening teletext storage directory \"%s\": %s", root, strerror(errno));
- }
- return pagesDeleted;
-}
-
-int Storage::cleanSubDir(const char *dir) {
- static bool reportedError=false; //avoid filling up syslog
- DIR *d=opendir(dir);
- bool hadError=false;
- int bytesDeleted=0;
- if (d) {
- struct dirent *txtfile, path;
- struct stat txtfilestat;
- char fullPath[PATH_MAX];
- int filesize;
- while ( (!readdir_r(d, &path, &txtfile) && txtfile != NULL) ) {
- int len=strlen(txtfile->d_name);
- //check that the file end with .vtx to avoid accidents and disasters
- if (strcmp(txtfile->d_name+len-4, ".vtx")==0) {
- snprintf(fullPath, PATH_MAX, "%s/%s", dir, txtfile->d_name);
- stat(fullPath, &txtfilestat);
- filesize=actualFileSize(txtfilestat.st_size);
- int ret=unlink(fullPath);
- if (ret==0)
- bytesDeleted+=filesize;
- else
- hadError=ret;
- }
- }
- closedir(d);
- rmdir(dir);
- } else {
- if (!reportedError) {
- esyslog("OSD-Teletext: Error opening teletext storage subdirectory \"%s\": %s", dir, strerror(errno));
- reportedError=true;
- }
- }
-
- if (hadError && !reportedError) {
- esyslog("OSD-Teletext: Error removing teletext storage subdirectory \"%s\": %s", dir, strerror(hadError));
- reportedError=true;
- }
- return bytesDeleted;
-}
-
-Storage::Storage()
- : byteCount(0), failedFreeSpace(false)
-{
-}
-
-Storage::~Storage() {
-}
-
-Storage *Storage::CreateInstance(StorageSystem system, int storageLimit) {
- switch (system) {
- case StorageSystemLegacy:
- return new LegacyStorage(storageLimit);
- case StorageSystemPacked:
- default:
- return new PackedStorage(storageLimit);
- }
-}
-
-void Storage::freeSpace() {
- //there might be a situation where only the current directory is left and
- //occupies the whole space. We cannot delete anything. Don't waste time scanning.
- if (failedFreeSpace)
- return;
-
- //printf("freeSpace()\n");
- time_t min=time(0);
- char minDir[PATH_MAX];
- char fullPath[PATH_MAX];
- DIR *top=opendir(getRootDir());
- if (top) {
- int haveDir=0;
- struct dirent *chandir, path;
- struct stat chandirstat;
- while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) {
- if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0)
- continue;
- snprintf(fullPath, PATH_MAX, "%s/%s", getRootDir(), chandir->d_name);
- if (stat(fullPath, &chandirstat)==0) {
- if (S_ISDIR(chandirstat.st_mode)) {
- if (chandirstat.st_ctime < min && strcmp(fullPath, currentDir)) {
- min=chandirstat.st_ctime;
- strcpy(minDir, fullPath);
- haveDir++;
- }
- }
- }
- }
- closedir(top);
-
- //if haveDir, only current directory present, which must not be deleted
- if (haveDir>=2)
- byteCount-=cleanSubDir(minDir);
- else
- failedFreeSpace=true;
- }
-}
-
-bool Storage::exists(const char* file) {
- struct stat s;
- return (stat(file, &s)==0);
-}
-
-void Storage::getFilename(char *buffer, int bufLength, PageID page) {
- snprintf(buffer, bufLength, "%s/%s/%03x_%02x.vtx", getRootDir(),
- *page.channel.ToString(), page.page, page.subPage);
-}
-
-void Storage::prepareDirectory(tChannelID chan) {
- currentDir = cString::sprintf("%s/%s", root, *chan.ToString());
- if (!MakeDirs(currentDir, 1)) {
- esyslog("OSD-Teletext: Error preparing directory for channel \"%s\"",
- *chan.ToString());
- return;
- }
- failedFreeSpace=false;
-}
-
-#define TELETEXT_PAGESIZE 972
-
-LegacyStorage::LegacyStorage(int maxMB)
- : fsBlockSize(1), pageBytes(TELETEXT_PAGESIZE)
-{
- initMaxStorage(maxMB);
-}
-
-LegacyStorage::~LegacyStorage() {
-}
-
-/*
-static inline int FilesForMegabytes(double MB, int blocksize) {
- double pageBytes;
- if (TELETEXT_PAGESIZE<=blocksize)
- pageBytes=blocksize;
- else
- pageBytes=((TELETEXT_PAGESIZE/blocksize)+1)*blocksize;
- //reserve 10% for directories
- return (int)( (1024.0 * 1024.0 * (MB-MB*0.1)) / pageBytes );
-}*/
-
-int LegacyStorage::actualFileSize(int netFileSize) {
- if (netFileSize<=0)
- return 0;
- if (netFileSize<=fsBlockSize)
- return fsBlockSize;
- else
- return ((netFileSize/fsBlockSize)+1)*fsBlockSize;
-}
-
-//max==0 means unlimited, max==-1 means a reasonable default value shall be calculated
-void LegacyStorage::initMaxStorage(int maxMB) {
- struct statfs fs;
- if (statfs(getRootDir(), &fs)!=0) {
- esyslog("OSD-Teletext: Error statfs'ing root directory \"%s\": %s, cache size uncontrolled", getRootDir(), strerror(errno));
- return;
- }
- fsBlockSize=fs.f_bsize;
-
- pageBytes=actualFileSize(TELETEXT_PAGESIZE);
-
- if (maxMB>=0) {
- if (maxMB<3) {
- esyslog("OSD-Teletext: Request to use at most %d MB for caching. This is not enough, using 3 MB", maxMB);
- maxMB=3;
- }
- maxBytes=MEGABYTE(maxMB);
- } else if (maxMB==-1) {
- //calculate a default value
- double blocksPerMeg = 1024.0 * 1024.0 / fs.f_bsize;
- double capacityMB=fs.f_blocks / blocksPerMeg;
- double freeMB=(fs.f_bavail / blocksPerMeg);
- if (capacityMB<=50 || freeMB<50) {
- //small (<=50MB) filesystems as root dir are assumed to be dedicated for use as a teletext cache
- //for others, the maximum default size is set to 50 MB
- maxBytes=MEGABYTE((int)freeMB);
- //maxPages= FilesForMegabytes(freeMB, fs.f_bsize);
- if (freeMB<3.0) {
- esyslog("OSD-Teletext: Less than %.1f MB free on filesystem of root directory \"%s\"!", freeMB, getRootDir());
- maxBytes=MEGABYTE(3);
- }
- } else {
- //the maximum default size is set to 50 MB
- maxBytes=MEGABYTE(50);
- }
- //printf("Set maxBytes to %ld, %.2f %.2f\n", maxBytes, capacityMB, freeMB);
- }
-}
-
-void LegacyStorage::cleanUp() {
- byteCount -= Storage::doCleanUp();
-}
-
-void LegacyStorage::registerFile(PageID page) {
- //pageBytes is already effective size
- if ( maxBytes && (byteCount+=pageBytes)>maxBytes )
- freeSpace();
-}
-
-StorageHandle LegacyStorage::openForReading(PageID page, bool countAsAccess) {
- //the countAsAccess argument was intended for use in a LRU cache, currently unused
- char filename[PATH_MAX];
- getFilename(filename, sizeof(filename), page);
- StorageHandle ret=(StorageHandle)open(filename, O_RDONLY);
- return ret;
-}
-
-StorageHandle LegacyStorage::openForWriting(PageID page) {
- static bool wroteError=false;
- char filename[PATH_MAX];
- getFilename(filename, sizeof(filename), page);
- bool existed=exists(filename);
- //first try
- StorageHandle fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (fd) {
- if (!existed)
- registerFile(page);
- return fd;
- }
- //no space on disk? make some space available
- if (errno == ENOSPC)
- freeSpace();
- //second try
- fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (!fd && !wroteError) {
- //report error to syslog - once!
- wroteError=true;
- esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno));
- }
- //make sure newly created files are counted
- if (fd && !existed)
- registerFile(page);
- return fd;
-}
-
-ssize_t LegacyStorage::write(const void *ptr, size_t size, StorageHandle stream) {
- ssize_t written;
- if (!(written=::write((int)stream, ptr, size)) ) {
- switch (errno) {
- case ENOSPC:
- freeSpace();
- return ::write((int)stream, ptr, size);
- case EINTR:
- esyslog("OSD-Teletext: EINTR while writing. Please contact the author and tell him this happened.");
- break;
- default:
- break;
- }
- }
- return written;
-}
-
-
-
-PackedStorage::PackedStorage(int maxMB)
- : LegacyStorage(maxMB) {
-}
-#define TOC_SIZE 8
-//The file structure is simple:
-// TOC_SIZE*PageAddress contains the numbers of the following pages
-// TOC_SIZE*TELETEXT_PAGESIZE contains the page data
-//and the same again.
-bool PackedStorage::seekTo(PageID page, int desc, bool create) {
- lseek(desc, 0, SEEK_SET);
- PageAddress addr[TOC_SIZE];
-
- while (::read(desc, addr, sizeof(addr)) == sizeof(addr)) {
- lseek(desc, 0, SEEK_CUR);
- for (int index=0; index<TOC_SIZE; index++) {
- if (addr[index]==page) {
- lseek(desc, index*TELETEXT_PAGESIZE, SEEK_CUR);
- return true;
- } else if (addr[index].page==0) {
- //0 means: no more pages follow
- if (create) {
- //rewind what was read
- lseek(desc, -(off_t)sizeof(addr), SEEK_CUR);
- //update index
- addr[index]=page;
- if (::write(desc, addr, sizeof(addr)) != sizeof(addr))
- return false;
- //seek to data position
- lseek(desc, TELETEXT_PAGESIZE*index, SEEK_CUR);
- return true;
- } else
- return false;
- }
- }
-
- //seek over data area
- lseek(desc, TELETEXT_PAGESIZE*TOC_SIZE, SEEK_CUR);
- }
-
- int oldSize=actualFileSize(lseek(desc, 0, SEEK_CUR));
- if (create) {
- //create a new set of a TOC and a TOC_SIZE*TELETEXT_PAGESIZE data area
- memset(addr, 0, sizeof(addr));
- //first entry is our page
- addr[0]=page;
- if (::write(desc, addr, sizeof(addr)) != sizeof(addr))
- return false;
- //seek beyond end of file
- lseek(desc, (TELETEXT_PAGESIZE*TOC_SIZE)-1, SEEK_CUR);
- //write one byte to enlarge the file to the sought position
- char c=1;
- if (::write(desc, &c, 1) != 1)
- return false;
- //Now, calculate new file size
- byteCount += ( actualFileSize(lseek(desc, 0, SEEK_CUR)) - oldSize );
- //seek to beginning of data, which is requested
- lseek(desc, -(off_t)(TELETEXT_PAGESIZE*TOC_SIZE), SEEK_CUR);
- return true;
- } else
- return false;
-}
-
-void PackedStorage::getFilename(char *buffer, int bufLength, PageID page) {
- //This is a different scheme: page 576_07 will have the name 570s.vtx, the same as e.g. 571_01 or 575_00
- //Think of "the five hundred seventies"
- snprintf(buffer, bufLength, "%s/%s/%03xs.vtx", getRootDir(),
- *page.channel.ToString(), (page.page & 0xFF0));
-}
-
-StorageHandle PackedStorage::openForWriting(PageID page) {
- static bool wroteError=false;
- char filename[PATH_MAX];
- getFilename(filename, sizeof(filename), page);
- //first try
- int desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (desc != -1) {
- if (!seekTo(page, desc, true)) {
- ::close(desc);
- return StorageHandle();
- }
- if ( maxBytes && byteCount>maxBytes )
- freeSpace();
- return (StorageHandle)desc;
- }
- //no space on disk? make some space available
- if (errno == ENOSPC)
- freeSpace();
- //second try
- desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (desc==-1 && !wroteError) {
- //report error to syslog - once!
- wroteError=true;
- esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno));
- }
-
- if (desc==-1)
- return StorageHandle();
- else if (!seekTo(page, desc, true)) {
- ::close(desc);
- return StorageHandle();
- }
-
- if ( maxBytes && byteCount>maxBytes )
- freeSpace();
- return (StorageHandle)desc;
-}
-
-StorageHandle PackedStorage::openForReading(PageID page, bool countAsAccess) {
- int desc;
- if ( (desc=(int)LegacyStorage::openForReading(page, false))!= -1 ) {
- if (!seekTo(page, desc, false)) {
- //this is not an error condition here, may and shall happen!
- ::close(desc);
- } else {
- return (StorageHandle)desc;
- }
- }
- return StorageHandle();
-}
-
-
-
-
cTelePage::cTelePage(PageID t_page, uchar t_flags, uchar t_lang,int t_mag, Storage *s)
: mag(t_mag), flags(t_flags), lang(t_lang), page(t_page), storage(s)
{
diff --git a/txtrecv.h b/txtrecv.h
index 2558bb5..a1be1f4 100644
--- a/txtrecv.h
+++ b/txtrecv.h
@@ -8,7 +8,7 @@
* *
***************************************************************************/
-#ifndef __TXTRECV_H
+#ifndef __TXTRECV_H
#define __TXTRECV_H
#include <vdr/status.h>
@@ -19,113 +19,7 @@
#include <stdio.h>
#include <unistd.h>
-struct PageID {
- PageID() { page=subPage=0; }
- PageID(tChannelID id, int p, int s) { set(id, p, s); }
- void set(tChannelID id, int p, int s)
- { channel=id; page=p; subPage=s; }
- tChannelID channel;
- int page;
- int subPage;
-};
-
-struct StorageHandle {
-public:
- StorageHandle() { handle=-1; }
- StorageHandle(const StorageHandle &s) { handle=s.handle; }
- StorageHandle(int h) { handle=h; }
- StorageHandle &operator=(int h) { handle=h; return *this; }
- StorageHandle &operator=(const StorageHandle &s) { handle=s.handle; return *this; }
- operator bool() const { return handle!=-1; }
- operator int() const { return handle; }
-private:
- int handle;
-};
-
-class RootDir {
-public:
- static void setRootDir(const char *);
- static const char *getRootDir();
-protected:
- static const char *root;
-};
-
-class Storage : public RootDir {
-public:
- virtual ~Storage();
- enum StorageSystem { StorageSystemLegacy, StorageSystemPacked };
- //must be called before the first call to instance()
-
- static Storage *CreateInstance(StorageSystem system, int storageLimit);
-
- //must be called before operation starts. Set all options (RootDir, maxStorage) before.
- virtual void cleanUp() = 0;
-
- virtual void getFilename(char *buffer, int bufLength, PageID page);
- virtual void prepareDirectory(tChannelID chan);
-
- virtual StorageHandle openForWriting(PageID page) = 0;
- virtual StorageHandle openForReading(PageID page, bool countAsAccess) = 0;
- virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream) = 0;
- virtual ssize_t read(void *ptr, size_t size, StorageHandle stream) = 0;
- virtual void close(StorageHandle stream) = 0;
-protected:
- Storage();
- int cleanSubDir(const char *dir);
- int doCleanUp();
- virtual int actualFileSize(int netFileSize) { return netFileSize; }
- void freeSpace();
- bool exists(const char* file);
-
- long byteCount;
- cString currentDir;
-private:
- bool failedFreeSpace;
-};
-
-class LegacyStorage : public Storage {
-private:
- void initMaxStorage(int maxMB=-1);
-public:
- LegacyStorage(int maxMB);
- virtual ~LegacyStorage();
- virtual void cleanUp();
-
- virtual StorageHandle openForWriting(PageID page);
- virtual StorageHandle openForReading(PageID page, bool countAsAccess);
- virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream);
- virtual ssize_t read(void *ptr, size_t size, StorageHandle stream)
- { return ::read((int)stream, ptr, size); }
- virtual void close(StorageHandle stream)
- { ::close((int)stream); }
-protected:
- void registerFile(PageID page);
- virtual int actualFileSize(int netFileSize);
- //int maxPages;
- long maxBytes;
- int fsBlockSize;
- int pageBytes;
-};
-
-class PackedStorage : public LegacyStorage {
-public:
- PackedStorage(int maxMB);
-
- virtual void getFilename(char *buffer, int bufLength, PageID page);
- virtual StorageHandle openForWriting(PageID page);
- virtual StorageHandle openForReading(PageID page, bool countAsAccess);
-protected:
- struct PageAddress {
- bool operator==(const PageID &id) const
- { return page==id.page && subPage==id.subPage; }
- void operator=(const PageID &id)
- { page=id.page; subPage=id.subPage; }
- int page;
- int subPage;
- };
- bool seekTo(PageID page, int fd, bool create);
- void registerFile(PageID page);
-};
+#include "storage.h"
class cTelePage {
private: