r1107 - in vdr/vdr/trunk/debian: . patches

Thomas Schmidt tschmidt at costa.debian.org
Wed Aug 31 19:21:35 UTC 2005


Author: tschmidt
Date: 2005-08-31 19:21:34 +0000 (Wed, 31 Aug 2005)
New Revision: 1107

Added:
   vdr/vdr/trunk/debian/patches/16_avoidTrashing.dpatch
Modified:
   vdr/vdr/trunk/debian/changelog
   vdr/vdr/trunk/debian/patches/00list
   vdr/vdr/trunk/debian/patches/01_Makefile-fPIC-fix.dpatch
   vdr/vdr/trunk/debian/patches/09_sort_options.dpatch
   vdr/vdr/trunk/debian/patches/15_dvbplayer.dpatch
   vdr/vdr/trunk/debian/patches/17_replay.dpatch
   vdr/vdr/trunk/debian/patches/opt-21_subtitles_and_ttxtsubs.dpatch
Log:
vdr: Added avoidTrashing patch, removed svn:executable property on a few dpatches

Modified: vdr/vdr/trunk/debian/changelog
===================================================================
--- vdr/vdr/trunk/debian/changelog	2005-08-29 20:57:02 UTC (rev 1106)
+++ vdr/vdr/trunk/debian/changelog	2005-08-31 19:21:34 UTC (rev 1107)
@@ -5,6 +5,8 @@
   * Thomas Schmidt <tschmidt at debian.org>
     - New upstream release
     - Use --background when calling start-stop-daemon in the init-script
+    - Added 16_avoidTrashing.dpatch, which prevent vdr from trashing the 
+      file system buffers when reading/writing recordings
   * Tobias Grimm <tg at e-tobi.net>
     - Made debian/patchlevel.sh acccept opt-entries in 00list with leading
       spaces and removed leading space from vdr-patchlevel output

Modified: vdr/vdr/trunk/debian/patches/00list
===================================================================
--- vdr/vdr/trunk/debian/patches/00list	2005-08-29 20:57:02 UTC (rev 1106)
+++ vdr/vdr/trunk/debian/patches/00list	2005-08-31 19:21:34 UTC (rev 1107)
@@ -13,6 +13,7 @@
 13_epgfix
 14_update-resume
 15_dvbplayer
+16_avoidTrashing
 17_replay
 
 # Patch needed for DVB subtitles or ttxtsubs (does not work with AC3-patch)


Property changes on: vdr/vdr/trunk/debian/patches/01_Makefile-fPIC-fix.dpatch
___________________________________________________________________
Name: svn:executable
   - *


Property changes on: vdr/vdr/trunk/debian/patches/09_sort_options.dpatch
___________________________________________________________________
Name: svn:executable
   - *


Property changes on: vdr/vdr/trunk/debian/patches/15_dvbplayer.dpatch
___________________________________________________________________
Name: svn:executable
   - *

Added: vdr/vdr/trunk/debian/patches/16_avoidTrashing.dpatch
===================================================================
--- vdr/vdr/trunk/debian/patches/16_avoidTrashing.dpatch	2005-08-29 20:57:02 UTC (rev 1106)
+++ vdr/vdr/trunk/debian/patches/16_avoidTrashing.dpatch	2005-08-31 19:21:34 UTC (rev 1107)
@@ -0,0 +1,1946 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+
+## 16_avoidTrashing.dpatch by Ralf Müller <rmvdr at bj-ig.de>
+## http://vdr.unetz.com/download/patches/vdr-avoidTrashing-0.2.2-plain-1.3.31.diff.gz
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: prevents vdr to trash the file system buffers
+
+ at DPATCH@
+diff -urNad vdr-1.3.31/cutter.c /tmp/dpep.cLVvp8/vdr-1.3.31/cutter.c
+--- vdr-1.3.31/cutter.c	2005-08-14 12:51:54.000000000 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/cutter.c	2005-08-31 21:04:17.153931440 +0200
+@@ -131,7 +131,11 @@
+                  cutIn = false;
+                  }
+               }
++#ifdef AVOID_TRASHING
++           if (WriteStream(toFile, buffer, Length) < 0) {
++#else
+            if (safe_write(toFile, buffer, Length) < 0) {
++#endif // AVOID_TRASHING
+               error = "safe_write";
+               break;
+               }
+diff -urNad vdr-1.3.31/dvbplayer.c /tmp/dpep.cLVvp8/vdr-1.3.31/dvbplayer.c
+--- vdr-1.3.31/dvbplayer.c	2005-08-31 21:03:49.573124360 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/dvbplayer.c	2005-08-31 21:04:17.153931440 +0200
+@@ -147,7 +147,11 @@
+   while (Running()) {
+         Lock();
+         if (!hasData && f >= 0 && buffer) {
++#ifdef AVOID_TRASHING
++           int r = ReadStream(f, buffer + length, wanted - length);
++#else
+            int r = safe_read(f, buffer + length, wanted - length);
++#endif // AVOID_TRASHING
+            if (r >= 0) {
+               length += r;
+               if (!r || length == wanted) // r == 0 means EOF
+diff -urNad vdr-1.3.31/Makefile /tmp/dpep.cLVvp8/vdr-1.3.31/Makefile
+--- vdr-1.3.31/Makefile	2005-08-31 21:03:49.100196256 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/Makefile	2005-08-31 21:04:17.154931288 +0200
+@@ -30,6 +30,8 @@
+ DOXYGEN  = /usr/bin/doxygen
+ DOXYFILE = Doxyfile
+ 
++AVOID_TRASHING=1
++
+ -include Make.config
+ 
+ INCLUDES += -I$(DVBDIR)/include
+@@ -91,6 +93,10 @@
+ DEFINES += -DVFAT
+ endif
+ 
++ifdef AVOID_TRASHING
++DEFINES += -DAVOID_TRASHING
++endif
++
+ all: vdr
+ font: genfontfile\
+       fontfix-iso8859-1.c fontosd-iso8859-1.c fontsml-iso8859-1.c\
+diff -urNad vdr-1.3.31/Makefile.orig /tmp/dpep.cLVvp8/vdr-1.3.31/Makefile.orig
+--- vdr-1.3.31/Makefile.orig	1970-01-01 01:00:00.000000000 +0100
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/Makefile.orig	2005-08-31 21:03:49.100196000 +0200
+@@ -0,0 +1,220 @@
++#
++# Makefile for the Video Disk Recorder
++#
++# See the main source file 'vdr.c' for copyright information and
++# how to reach the author.
++#
++# $Id: Makefile 1.77 2005/08/14 11:42:20 kls Exp $
++
++.DELETE_ON_ERROR:
++
++CC       ?= gcc
++CFLAGS   ?= -O2
++
++CXX      ?= g++
++CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual
++
++DVBDIR   = ../DVB
++LSIDIR   = ./libsi
++MANDIR   = /usr/local/man
++BINDIR   = /usr/local/bin
++LIBS     = -ljpeg -lpthread -ldl -lcap
++INCLUDES =
++
++PLUGINDIR= ./PLUGINS
++PLUGINLIBDIR= $(PLUGINDIR)/lib
++
++VIDEODIR = /video
++CFGDIR ?= $(VIDEODIR)
++
++DOXYGEN  = /usr/bin/doxygen
++DOXYFILE = Doxyfile
++
++-include Make.config
++
++INCLUDES += -I$(DVBDIR)/include
++
++SILIB    = $(LSIDIR)/libsi.a
++
++OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\
++       dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
++       lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
++       receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\
++       skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
++       timers.o tools.o transfer.o vdr.o videodir.o
++
++FIXFONT_ISO8859_1 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
++OSDFONT_ISO8859_1 = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
++SMLFONT_ISO8859_1 = -adobe-helvetica-medium-r-normal--18-*-100-100-p-*-iso8859-1
++
++FIXFONT_ISO8859_2 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-2
++OSDFONT_ISO8859_2 = -adobe-helvetica-medium-r-normal--24-*-75-75-p-*-iso8859-2
++SMLFONT_ISO8859_2 = -adobe-helvetica-medium-r-normal--18-*-75-75-p-*-iso8859-2
++
++FIXFONT_ISO8859_5 = -rfx-courier-bold-r-normal--24-*-75-75-m-*-iso8859-5
++OSDFONT_ISO8859_5 = -rfx-helvetica-medium-r-normal--24-*-75-75-p-*-iso8859-5
++SMLFONT_ISO8859_5 = -rfx-helvetica-medium-r-normal--18-*-75-75-p-*-iso8859-5
++
++FIXFONT_ISO8859_7 = --user-medium-r-normal--26-171-110-110-m-140-iso8859-7
++OSDFONT_ISO8859_7 = --user-medium-r-normal--23-179-85-85-m-120-iso8859-7
++SMLFONT_ISO8859_7 = --user-medium-r-normal--19-160-72-72-m-110-iso8859-7
++
++FIXFONT_ISO8859_15 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-15
++OSDFONT_ISO8859_15 = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-15
++SMLFONT_ISO8859_15 = -adobe-helvetica-medium-r-normal--18-*-100-100-p-*-iso8859-15
++
++ifndef NO_KBD
++DEFINES += -DREMOTE_KBD
++endif
++ifdef REMOTE
++DEFINES += -DREMOTE_$(REMOTE)
++endif
++
++LIRC_DEVICE ?= /dev/lircd
++RCU_DEVICE  ?= /dev/ttyS1
++
++DEFINES += -DLIRC_DEVICE=\"$(LIRC_DEVICE)\" -DRCU_DEVICE=\"$(RCU_DEVICE)\"
++
++DEFINES += -DCMD_SUBMENUS
++DEFINES += -D_GNU_SOURCE
++
++DEFINES += -DVIDEODIR=\"$(VIDEODIR)\"
++DEFINES += -DCFGDIR=\"$(CFGDIR)\"
++DEFINES += -DPLUGINDIR=\"$(PLUGINLIBDIR)\"
++
++# The version number of VDR (taken from VDR's "config.h"):
++
++VDRVERSION = $(shell grep 'define VDRVERSION ' config.h | awk '{ print $$3 }' | sed -e 's/"//g')
++
++ifdef VFAT
++# for people who want their video directory on a VFAT partition
++DEFINES += -DVFAT
++endif
++
++all: vdr
++font: genfontfile\
++      fontfix-iso8859-1.c fontosd-iso8859-1.c fontsml-iso8859-1.c\
++      fontfix-iso8859-2.c fontosd-iso8859-2.c fontsml-iso8859-2.c\
++      fontfix-iso8859-5.c fontosd-iso8859-5.c fontsml-iso8859-5.c\
++      fontfix-iso8859-7.c fontosd-iso8859-7.c fontsml-iso8859-7.c\
++      fontfix-iso8859-15.c fontosd-iso8859-15.c fontsml-iso8859-15.c
++	@echo "font files created."
++
++# Implicit rules:
++
++%.o: %.c
++	$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
++
++# Dependencies:
++
++MAKEDEP = $(CXX) -MM -MG
++DEPFILE = .dependencies
++$(DEPFILE): Makefile
++	@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
++
++-include $(DEPFILE)
++
++# The main program:
++
++vdr: $(OBJS) $(SILIB)
++	$(CXX) $(CXXFLAGS) -rdynamic $(OBJS) $(NCURSESLIB) $(LIBS) $(LIBDIRS) $(SILIB) -o vdr
++
++# The font files:
++
++fontfix-iso8859-1.c:
++	./genfontfile "cFont::tPixelData FontFix_iso8859_1" "$(FIXFONT_ISO8859_1)" > $@
++fontosd-iso8859-1.c:
++	./genfontfile "cFont::tPixelData FontOsd_iso8859_1" "$(OSDFONT_ISO8859_1)" > $@
++fontsml-iso8859-1.c:
++	./genfontfile "cFont::tPixelData FontSml_iso8859_1" "$(SMLFONT_ISO8859_1)" > $@
++
++fontfix-iso8859-2.c:
++	./genfontfile "cFont::tPixelData FontFix_iso8859_2" "$(FIXFONT_ISO8859_2)" > $@
++fontosd-iso8859-2.c:
++	./genfontfile "cFont::tPixelData FontOsd_iso8859_2" "$(OSDFONT_ISO8859_2)" > $@
++fontsml-iso8859-2.c:
++	./genfontfile "cFont::tPixelData FontSml_iso8859_2" "$(SMLFONT_ISO8859_2)" > $@
++
++fontfix-iso8859-5.c:
++	./genfontfile "cFont::tPixelData FontFix_iso8859_5" "$(FIXFONT_ISO8859_5)" > $@
++fontosd-iso8859-5.c:
++	./genfontfile "cFont::tPixelData FontOsd_iso8859_5" "$(OSDFONT_ISO8859_5)" > $@
++fontsml-iso8859-5.c:
++	./genfontfile "cFont::tPixelData FontSml_iso8859_5" "$(SMLFONT_ISO8859_5)" > $@
++
++fontfix-iso8859-7.c:
++	./genfontfile "cFont::tPixelData FontFix_iso8859_7" "$(FIXFONT_ISO8859_7)" > $@
++fontosd-iso8859-7.c:
++	./genfontfile "cFont::tPixelData FontOsd_iso8859_7" "$(OSDFONT_ISO8859_7)" > $@
++fontsml-iso8859-7.c:
++	./genfontfile "cFont::tPixelData FontSml_iso8859_7" "$(SMLFONT_ISO8859_7)" > $@
++
++fontfix-iso8859-15.c:
++	./genfontfile "cFont::tPixelData FontFix_iso8859_15" "$(FIXFONT_ISO8859_15)" > $@
++fontosd-iso8859-15.c:
++	./genfontfile "cFont::tPixelData FontOsd_iso8859_15" "$(OSDFONT_ISO8859_15)" > $@
++fontsml-iso8859-15.c:
++	./genfontfile "cFont::tPixelData FontSml_iso8859_15" "$(SMLFONT_ISO8859_15)" > $@
++
++# The font file generator:
++
++genfontfile: genfontfile.c
++	$(CC) $(CFLAGS) -o $@ -L/usr/X11R6/lib $< -lX11
++
++# The libsi library:
++
++$(SILIB):
++	$(MAKE) -C $(LSIDIR) all
++
++# The 'include' directory (for plugins):
++
++include-dir:
++	@mkdir -p include/vdr
++	@(cd include/vdr; for i in ../../*.h; do ln -fs $$i .; done)
++	@mkdir -p include/libsi
++	@(cd include/libsi; for i in ../../libsi/*.h; do ln -fs $$i .; done)
++
++# Plugins:
++
++plugins: include-dir
++	@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" all; done
++
++plugins-clean:
++	@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" clean; done
++	@-rm -f $(PLUGINLIBDIR)/libvdr-*.so.$(VDRVERSION)
++
++# Install the files:
++
++install:
++	@mkdir -p $(BINDIR)
++	@cp vdr runvdr $(BINDIR)
++	@mkdir -p $(BINDIR)/$(PLUGINLIBDIR)
++	@cp $(PLUGINLIBDIR)/* $(BINDIR)/$(PLUGINLIBDIR)
++	@mkdir -p $(MANDIR)/man1
++	@mkdir -p $(MANDIR)/man5
++	@gzip -c vdr.1 > $(MANDIR)/man1/vdr.1.gz
++	@gzip -c vdr.5 > $(MANDIR)/man5/vdr.5.gz
++	@if [ ! -d $(VIDEODIR) ]; then\
++            mkdir -p $(VIDEODIR);\
++            cp *.conf $(VIDEODIR);\
++            fi
++
++# Source documentation:
++
++srcdoc:
++	@cp $(DOXYFILE) $(DOXYFILE).tmp
++	@echo PROJECT_NUMBER = $(VDRVERSION) >> $(DOXYFILE).tmp
++	$(DOXYGEN) $(DOXYFILE).tmp
++	@rm $(DOXYFILE).tmp
++
++# Housekeeping:
++
++clean:
++	$(MAKE) -C $(LSIDIR) clean
++	-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
++	-rm -rf include
++	-rm -rf srcdoc
++fontclean:
++	-rm -f fontfix*.c fontosd*.c fontsml*.c
++CLEAN: clean fontclean
++
+diff -urNad vdr-1.3.31/recorder.c /tmp/dpep.cLVvp8/vdr-1.3.31/recorder.c
+--- vdr-1.3.31/recorder.c	2005-08-31 21:03:49.369155368 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/recorder.c	2005-08-31 21:04:17.154931288 +0200
+@@ -102,7 +102,11 @@
+            if (NextFile()) {
+               if (index && pictureType != NO_PICTURE)
+                  index->Write(pictureType, fileName->Number(), fileSize);
++#ifdef AVOID_TRASHING
++              if (WriteStream(recordFile, p, Count) < 0) {
++#else
+               if (safe_write(recordFile, p, Count) < 0) {
++#endif // AVOID_TRASHING
+                  LOG_ERROR_STR(fileName->Name());
+                  break;
+                  }
+diff -urNad vdr-1.3.31/recording.c /tmp/dpep.cLVvp8/vdr-1.3.31/recording.c
+--- vdr-1.3.31/recording.c	2005-08-31 21:03:49.532130592 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/recording.c	2005-08-31 21:04:17.162930072 +0200
+@@ -964,6 +964,15 @@
+                  size = last + 1;
+                  index = MALLOC(tIndex, size);
+                  if (index) {
++#ifdef AVOID_TRASHING
++                    f = OpenStream(fileName, O_RDONLY);
++                    if (f >= 0) {
++                       if ((int)ReadStream(f, index, buf.st_size) != buf.st_size) {
++                          esyslog("ERROR: can't read from file '%s'", fileName);
++                          free(index);
++                          index = NULL;
++                          CloseStream(f);
++#else
+                     f = open(fileName, O_RDONLY);
+                     if (f >= 0) {
+                        if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
+@@ -971,6 +980,7 @@
+                           free(index);
+                           index = NULL;
+                           close(f);
++#endif // AVOID_TRASHING
+                           f = -1;
+                           }
+                        // we don't close f here, see CatchUp()!
+@@ -988,7 +998,11 @@
+         else if (!Record)
+            isyslog("missing index file %s", fileName);
+         if (Record) {
++#ifdef AVOID_TRASHING
++           if ((f = OpenStream(fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
++#else
+            if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
++#endif // AVOID_TRASHING
+               if (delta) {
+                  esyslog("ERROR: padding index file with %d '0' bytes", delta);
+                  while (delta--)
+@@ -1007,7 +1021,11 @@
+ cIndexFile::~cIndexFile()
+ {
+   if (f >= 0)
++#ifdef AVOID_TRASHING
++     CloseStream(f);
++#else
+      close(f);
++#endif // AVOID_TRASHING
+   free(fileName);
+   free(index);
+ }
+@@ -1022,7 +1040,11 @@
+          if (fstat(f, &buf) == 0) {
+             if (time(NULL) - buf.st_mtime > MININDEXAGE) {
+                // apparently the index file is not being written any more
++#ifdef AVOID_TRASHING
++               CloseStream(f);
++#else
+                close(f);
++#endif // AVOID_TRASHING
+                f = -1;
+                break;
+                }
+@@ -1038,11 +1060,19 @@
+                   int offset = (last + 1) * sizeof(tIndex);
+                   int delta = (newLast - last) * sizeof(tIndex);
+                   if (lseek(f, offset, SEEK_SET) == offset) {
++#ifdef AVOID_TRASHING
++                     if (ReadStream(f, &index[last + 1], delta) != delta) {
++                        esyslog("ERROR: can't read from index");
++                        free(index);
++                        index = NULL;
++                        CloseStream(f);
++#else
+                      if (safe_read(f, &index[last + 1], delta) != delta) {
+                         esyslog("ERROR: can't read from index");
+                         free(index);
+                         index = NULL;
+                         close(f);
++#endif // AVOID_TRASHING
+                         f = -1;
+                         break;
+                         }
+@@ -1069,9 +1099,17 @@
+ {
+   if (f >= 0) {
+      tIndex i = { FileOffset, PictureType, FileNumber, 0 };
++#ifdef AVOID_TRASHING
++     if (WriteStream(f, &i, sizeof(i)) < 0) {
++#else
+      if (safe_write(f, &i, sizeof(i)) < 0) {
++#endif // AVOID_TRASHING
+         LOG_ERROR_STR(fileName);
++#ifdef AVOID_TRASHING
++        CloseStream(f);
++#else
+         close(f);
++#endif // AVOID_TRASHING
+         f = -1;
+         return false;
+         }
+@@ -1199,7 +1237,11 @@
+      else {
+         if (access(fileName, R_OK) == 0) {
+            dsyslog("playing '%s'", fileName);
++#ifdef AVOID_TRASHING
++           file = OpenStream(fileName, O_RDONLY | BlockingFlag);
++#else
+            file = open(fileName, O_RDONLY | BlockingFlag);
++#endif // AVOID_TRASHING
+            if (file < 0)
+               LOG_ERROR_STR(fileName);
+            }
+@@ -1302,7 +1344,11 @@
+      esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
+      Length = Max;
+      }
++#ifdef AVOID_TRASHING
++  int r = ReadStream(f, b, Length);
++#else
+   int r = safe_read(f, b, Length);
++#endif
+   if (r < 0)
+      LOG_ERROR;
+   return r;
+diff -urNad vdr-1.3.31/recording.c.orig /tmp/dpep.cLVvp8/vdr-1.3.31/recording.c.orig
+--- vdr-1.3.31/recording.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/recording.c.orig	2005-08-31 21:03:49.532130000 +0200
+@@ -0,0 +1,1311 @@
++/*
++ * recording.c: Recording file handling
++ *
++ * See the main source file 'vdr.c' for copyright information and
++ * how to reach the author.
++ *
++ * $Id: recording.c 1.111 2005/08/13 14:00:48 kls Exp $
++ */
++
++#include "recording.h"
++#include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include "channels.h"
++#include "i18n.h"
++#include "interface.h"
++#include "remux.h" //XXX+ I_FRAME
++#include "skins.h"
++#include "tools.h"
++#include "videodir.h"
++
++#define SUMMARYFALLBACK
++
++#define RECEXT       ".rec"
++#define DELEXT       ".del"
++/* This was the original code, which works fine in a Linux only environment.
++   Unfortunately, because of Windows and its brain dead file system, we have
++   to use a more complicated approach, in order to allow users who have enabled
++   the VFAT compile time option to see their recordings even if they forget to
++   enable VFAT when compiling a new version of VDR... Gee, do I hate Windows.
++   (kls 2002-07-27)
++#define DATAFORMAT   "%4d-%02d-%02d.%02d:%02d.%02d.%02d" RECEXT
++#define NAMEFORMAT   "%s/%s/" DATAFORMAT
++*/
++// start of implementation for brain dead systems
++#define DATAFORMAT   "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
++#ifdef VFAT
++#define nameFORMAT   "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
++#else
++#define nameFORMAT   "%4d-%02d-%02d.%02d:%02d.%02d.%02d" RECEXT
++#endif
++#define NAMEFORMAT   "%s/%s/" nameFORMAT
++// end of implementation for brain dead systems
++
++#define RESUMEFILESUFFIX  "/resume%s%s.vdr"
++#ifdef SUMMARYFALLBACK
++#define SUMMARYFILESUFFIX "/summary.vdr"
++#endif
++#define INFOFILESUFFIX    "/info.vdr"
++#define MARKSFILESUFFIX   "/marks.vdr"
++
++#define MINDISKSPACE 1024 // MB
++
++#define DELETEDLIFETIME     1 // hours after which a deleted recording will be actually removed
++#define REMOVECHECKDELTA 3600 // seconds between checks for removing deleted files
++#define DISKCHECKDELTA    100 // seconds between checks for free disk space
++#define REMOVELATENCY      10 // seconds to wait until next check after removing a file
++
++#define TIMERMACRO_TITLE    "TITLE"
++#define TIMERMACRO_EPISODE  "EPISODE"
++
++#define MAX_SUBTITLE_LENGTH  40
++
++void RemoveDeletedRecordings(void)
++{
++  static time_t LastRemoveCheck = 0;
++  if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) {
++     // Make sure only one instance of VDR does this:
++     cLockFile LockFile(VideoDirectory);
++     if (!LockFile.Lock())
++        return;
++     // Remove the oldest file that has been "deleted":
++     cRecordings DeletedRecordings(true);
++     if (DeletedRecordings.Load()) {
++        cRecording *r = DeletedRecordings.First();
++        cRecording *r0 = r;
++        while (r) {
++              if (r->start < r0->start)
++                 r0 = r;
++              r = DeletedRecordings.Next(r);
++              }
++        if (r0 && time(NULL) - r0->start > DELETEDLIFETIME * 3600) {
++           r0->Remove();
++           RemoveEmptyVideoDirectories();
++           LastRemoveCheck += REMOVELATENCY;
++           return;
++           }
++        }
++     LastRemoveCheck = time(NULL);
++     }
++}
++
++void AssertFreeDiskSpace(int Priority)
++{
++  // With every call to this function we try to actually remove
++  // a file, or mark a file for removal ("delete" it), so that
++  // it will get removed during the next call.
++  static time_t LastFreeDiskCheck = 0;
++  int Factor = (Priority == -1) ? 10 : 1;
++  if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA / Factor) {
++     if (!VideoFileSpaceAvailable(MINDISKSPACE)) {
++        // Make sure only one instance of VDR does this:
++        cLockFile LockFile(VideoDirectory);
++        if (!LockFile.Lock())
++           return;
++        // Remove the oldest file that has been "deleted":
++        isyslog("low disk space while recording, trying to remove a deleted recording...");
++        cRecordings DeletedRecordings(true);
++        if (DeletedRecordings.Load()) {
++           cRecording *r = DeletedRecordings.First();
++           cRecording *r0 = r;
++           while (r) {
++                 if (r->start < r0->start)
++                    r0 = r;
++                 r = DeletedRecordings.Next(r);
++                 }
++           if (r0 && r0->Remove()) {
++              LastFreeDiskCheck += REMOVELATENCY / Factor;
++              return;
++              }
++           }
++        // No "deleted" files to remove, so let's see if we can delete a recording:
++        isyslog("...no deleted recording found, trying to delete an old recording...");
++        if (Recordings.Load()) {
++           cRecording *r = Recordings.First();
++           cRecording *r0 = NULL;
++           while (r) {
++                 if (!r->IsEdited() && r->lifetime < MAXLIFETIME) { // edited recordings and recordings with MAXLIFETIME live forever
++                    if ((r->lifetime == 0 && Priority > r->priority) || // the recording has no guaranteed lifetime and the new recording has higher priority
++                        (r->lifetime > 0 && (time(NULL) - r->start) / SECSINDAY >= r->lifetime)) { // the recording's guaranteed lifetime has expired
++                       if (r0) {
++                          if (r->priority < r0->priority || (r->priority == r0->priority && r->start < r0->start))
++                             r0 = r; // in any case we delete the one with the lowest priority (or the older one in case of equal priorities)
++                          }
++                       else
++                          r0 = r;
++                       }
++                    }
++                 r = Recordings.Next(r);
++                 }
++           if (r0 && r0->Delete()) {
++              Recordings.Del(r0);
++              return;
++              }
++           }
++        // Unable to free disk space, but there's nothing we can do about that...
++        isyslog("...no old recording found, giving up");
++        Interface->Confirm(tr("Low disk space!"), 30);
++        }
++     LastFreeDiskCheck = time(NULL);
++     }
++}
++
++// --- cResumeFile ------------------------------------------------------------
++
++cResumeFile::cResumeFile(const char *FileName)
++{
++  fileName = MALLOC(char, strlen(FileName) + strlen(RESUMEFILESUFFIX) + 1);
++  if (fileName) {
++     strcpy(fileName, FileName);
++     sprintf(fileName + strlen(fileName), RESUMEFILESUFFIX, Setup.ResumeID ? "." : "", Setup.ResumeID ? *itoa(Setup.ResumeID) : "");
++     }
++  else
++     esyslog("ERROR: can't allocate memory for resume file name");
++}
++
++cResumeFile::~cResumeFile()
++{
++  free(fileName);
++}
++
++int cResumeFile::Read(void)
++{
++  int resume = -1;
++  if (fileName) {
++     struct stat st;
++     if (stat(fileName, &st) == 0) {
++        if ((st.st_mode & S_IWUSR) == 0) // no write access, assume no resume
++           return -1;
++        }
++     int f = open(fileName, O_RDONLY);
++     if (f >= 0) {
++        if (safe_read(f, &resume, sizeof(resume)) != sizeof(resume)) {
++           resume = -1;
++           LOG_ERROR_STR(fileName);
++           }
++        close(f);
++        }
++     else if (errno != ENOENT)
++        LOG_ERROR_STR(fileName);
++     }
++  return resume;
++}
++
++bool cResumeFile::Save(int Index)
++{
++  if (fileName) {
++     int f = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
++     if (f >= 0) {
++        if (safe_write(f, &Index, sizeof(Index)) < 0)
++           LOG_ERROR_STR(fileName);
++        close(f);
++        Recordings.ResetResume(fileName);
++        return true;
++        }
++     }
++  return false;
++}
++
++void cResumeFile::Delete(void)
++{
++  if (fileName) {
++     if (remove(fileName) < 0 && errno != ENOENT)
++        LOG_ERROR_STR(fileName);
++     Recordings.ResetResume(fileName);
++     }
++}
++
++// --- cRecordingInfo --------------------------------------------------------
++
++cRecordingInfo::cRecordingInfo(tChannelID ChannelID, const cEvent *Event)
++{
++  channelID = ChannelID;
++  if (Event) {
++     event = Event;
++     ownEvent = NULL;
++     }
++  else
++     event = ownEvent = new cEvent(0);
++}
++
++cRecordingInfo::~cRecordingInfo()
++{
++  delete ownEvent;
++}
++
++void cRecordingInfo::SetData(const char *Title, const char *ShortText, const char *Description)
++{
++  if (!isempty(Title))
++     ((cEvent *)event)->SetTitle(Title);
++  if (!isempty(ShortText))
++     ((cEvent *)event)->SetShortText(ShortText);
++  if (!isempty(Description))
++     ((cEvent *)event)->SetDescription(Description);
++}
++
++bool cRecordingInfo::Read(FILE *f)
++{
++  if (ownEvent) {
++     cReadLine ReadLine;
++     char *s;
++     while ((s = ReadLine.Read(f)) != NULL) {
++           char *t = skipspace(s + 1);
++           switch (*s) {
++             case 'C': {
++                         char *p = strchr(t, ' ');
++                         if (p)
++                            *p = 0; // strips optional channel name
++                         if (*t)
++                            channelID = tChannelID::FromString(t);
++                       }
++                       break;
++             default: if (!ownEvent->Parse(s))
++                         return false;
++                      break;
++             }
++           }
++     return true;
++     }
++  return false;
++}
++
++bool cRecordingInfo::Write(FILE *f, const char *Prefix) const
++{
++  if (channelID.Valid())
++     fprintf(f, "%sC %s\n", Prefix, *channelID.ToString());
++  event->Dump(f, Prefix, true);
++  return true;
++}
++
++// --- cRecording ------------------------------------------------------------
++
++#define RESUME_NOT_INITIALIZED (-2)
++
++struct tCharExchange { char a; char b; };
++tCharExchange CharExchange[] = {
++  { '~',  '/'    },
++  { ' ',  '_'    },
++  { '\'', '\x01' },
++  { '/',  '\x02' },
++  { 0, 0 }
++  };
++
++static char *ExchangeChars(char *s, bool ToFileSystem)
++{
++  char *p = s;
++  while (*p) {
++#ifdef VFAT
++        // The VFAT file system can't handle all characters, so we
++        // have to take extra efforts to encode/decode them:
++        if (ToFileSystem) {
++           switch (*p) {
++                  // characters that can be used "as is":
++                  case '!':
++                  case '@':
++                  case '$':
++                  case '%':
++                  case '&':
++                  case '(':
++                  case ')':
++                  case '+':
++                  case ',':
++                  case '-':
++                  case ';':
++                  case '=':
++                  case '0' ... '9':
++                  case 'a' ... 'z':
++                  case 'A' ... 'Z':
++                  case 'ä': case 'Ä':
++                  case 'ö': case 'Ö':
++                  case 'ü': case 'Ü':
++                  case 'ß':
++                       break;
++                  // characters that can be mapped to other characters:
++                  case ' ': *p = '_'; break;
++                  case '~': *p = '/'; break;
++                  // characters that have to be encoded:
++                  default:
++                    if (*p != '.' || !*(p + 1) || *(p + 1) == '~') { // Windows can't handle '.' at the end of directory names
++                       int l = p - s;
++                       s = (char *)realloc(s, strlen(s) + 10);
++                       p = s + l;
++                       char buf[4];
++                       sprintf(buf, "#%02X", (unsigned char)*p);
++                       memmove(p + 2, p, strlen(p) + 1);
++                       strncpy(p, buf, 3);
++                       p += 2;
++                       }
++                  }
++           }
++        else {
++           switch (*p) {
++             // mapped characters:
++             case '_': *p = ' '; break;
++             case '/': *p = '~'; break;
++             // encodes characters:
++             case '#': {
++                  if (strlen(p) > 2) {
++                     char buf[3];
++                     sprintf(buf, "%c%c", *(p + 1), *(p + 2));
++                     unsigned char c = strtol(buf, NULL, 16);
++                     *p = c;
++                     memmove(p + 1, p + 3, strlen(p) - 2);
++                     }
++                  }
++                  break;
++             // backwards compatibility:
++             case '\x01': *p = '\''; break;
++             case '\x02': *p = '/';  break;
++             case '\x03': *p = ':';  break;
++             }
++           }
++#else
++        for (struct tCharExchange *ce = CharExchange; ce->a && ce->b; ce++) {
++            if (*p == (ToFileSystem ? ce->a : ce->b)) {
++               *p = ToFileSystem ? ce->b : ce->a;
++               break;
++               }
++            }
++#endif
++        p++;
++        }
++  return s;
++}
++
++cRecording::cRecording(cTimer *Timer, const cEvent *Event)
++{
++  resume = RESUME_NOT_INITIALIZED;
++  titleBuffer = NULL;
++  sortBuffer = NULL;
++  fileName = NULL;
++  name = NULL;
++  // set up the actual name:
++  const char *Title = Event ? Event->Title() : NULL;
++  const char *Subtitle = Event ? Event->ShortText() : NULL;
++  char SubtitleBuffer[MAX_SUBTITLE_LENGTH];
++  if (isempty(Title))
++     Title = Timer->Channel()->Name();
++  if (isempty(Subtitle))
++     Subtitle = " ";
++  else if (strlen(Subtitle) > MAX_SUBTITLE_LENGTH) {
++     // let's make sure the Subtitle doesn't produce too long a file name:
++     strn0cpy(SubtitleBuffer, Subtitle, MAX_SUBTITLE_LENGTH);
++     Subtitle = SubtitleBuffer;
++     }
++  char *macroTITLE   = strstr(Timer->File(), TIMERMACRO_TITLE);
++  char *macroEPISODE = strstr(Timer->File(), TIMERMACRO_EPISODE);
++  if (macroTITLE || macroEPISODE) {
++     name = strdup(Timer->File());
++     name = strreplace(name, TIMERMACRO_TITLE, Title);
++     name = strreplace(name, TIMERMACRO_EPISODE, Subtitle);
++     // avoid blanks at the end:
++     int l = strlen(name);
++     while (l-- > 2) {
++           if (name[l] == ' ' && name[l - 1] != '~')
++              name[l] = 0;
++           else
++              break;
++           }
++     if (Timer->IsSingleEvent()) {
++        Timer->SetFile(name); // this was an instant recording, so let's set the actual data
++        Timers.SetModified();
++        }
++     }
++  else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
++     name = strdup(Timer->File());
++  else
++     asprintf(&name, "%s~%s", Timer->File(), Subtitle);
++  // substitute characters that would cause problems in file names:
++  strreplace(name, '\n', ' ');
++  start = Timer->StartTime();
++  priority = Timer->Priority();
++  lifetime = Timer->Lifetime();
++  // handle info:
++  info = new cRecordingInfo(Timer->Channel()->GetChannelID(), Event);
++  // this is a somewhat ugly hack to get the 'summary' information from the
++  // timer into the recording info, but it saves us from having to actually
++  // copy the entire event data:
++  if (!isempty(Timer->Summary()))
++     info->SetData(isempty(info->Title()) ? Timer->File() : NULL, NULL, Timer->Summary());
++}
++
++cRecording::cRecording(const char *FileName)
++{
++  resume = RESUME_NOT_INITIALIZED;
++  titleBuffer = NULL;
++  sortBuffer = NULL;
++  fileName = strdup(FileName);
++  FileName += strlen(VideoDirectory) + 1;
++  char *p = strrchr(FileName, '/');
++
++  name = NULL;
++  info = new cRecordingInfo;
++  if (p) {
++     time_t now = time(NULL);
++     struct tm tm_r;
++     struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't'
++     t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
++     if (7 == sscanf(p + 1, DATAFORMAT, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &priority, &lifetime)) {
++        t.tm_year -= 1900;
++        t.tm_mon--;
++        t.tm_sec = 0;
++        start = mktime(&t);
++        name = MALLOC(char, p - FileName + 1);
++        strncpy(name, FileName, p - FileName);
++        name[p - FileName] = 0;
++        name = ExchangeChars(name, false);
++        }
++     // read an optional info file:
++     char *InfoFileName = NULL;
++     asprintf(&InfoFileName, "%s%s", fileName, INFOFILESUFFIX);
++     FILE *f = fopen(InfoFileName, "r");
++     if (f) {
++        info->Read(f);
++        fclose(f);
++        }
++     else if (errno != ENOENT)
++        LOG_ERROR_STR(InfoFileName);
++     free(InfoFileName);
++#ifdef SUMMARYFALLBACK
++     // fall back to the old 'summary.vdr' if there was no 'info.vdr':
++     if (isempty(info->Title())) {
++        char *SummaryFileName = NULL;
++        asprintf(&SummaryFileName, "%s%s", fileName, SUMMARYFILESUFFIX);
++        FILE *f = fopen(SummaryFileName, "r");
++        if (f) {
++           int line = 0;
++           char *data[3] = { NULL };
++           cReadLine ReadLine;
++           char *s;
++           while ((s = ReadLine.Read(f)) != NULL) {
++                 if (*s || line > 1) {
++                    if (data[line]) {
++                       int len = strlen(s);
++                       len += strlen(data[line]) + 1;
++                       data[line] = (char *)realloc(data[line], len + 1);
++                       strcat(data[line], "\n");
++                       strcat(data[line], s);
++                       }
++                    else
++                       data[line] = strdup(s);
++                    }
++                 else
++                    line++;
++                 }
++           fclose(f);
++           if (line == 1) {
++              data[2] = data[1];
++              data[1] = NULL;
++              }
++           info->SetData(data[0], data[1], data[2]);
++           for (int i = 0; i < 3; i ++)
++               free(data[i]);
++           }
++        else if (errno != ENOENT)
++           LOG_ERROR_STR(SummaryFileName);
++        free(SummaryFileName);
++        }
++#endif
++     }
++}
++
++cRecording::~cRecording()
++{
++  free(titleBuffer);
++  free(sortBuffer);
++  free(fileName);
++  free(name);
++  delete info;
++}
++
++char *cRecording::StripEpisodeName(char *s)
++{
++  char *t = s, *s1 = NULL, *s2 = NULL;
++  while (*t) {
++        if (*t == '/') {
++           if (s1) {
++              if (s2)
++                 s1 = s2;
++              s2 = t;
++              }
++           else
++              s1 = t;
++           }
++        t++;
++        } *s1 = 255;
++  if (s1 && s2 && s1 != s && !strchr(".-$ª", *(s1 - 1)))
++     memmove(s1 + 1, s2, t - s2 + 1);
++  return s;
++}
++
++char *cRecording::SortName(void) const
++{
++  if (!sortBuffer) {
++     char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory) ));
++     int l = strxfrm(NULL, s, 0) + 1;
++     sortBuffer = MALLOC(char, l);
++     strxfrm(sortBuffer, s, l);
++     free(s);
++     }
++  return sortBuffer;
++}
++
++int cRecording::GetResume(void) const
++{
++  if (resume == RESUME_NOT_INITIALIZED) {
++     cResumeFile ResumeFile(FileName());
++     resume = ResumeFile.Read();
++     }
++  return resume;
++}
++
++int cRecording::Compare(const cListObject &ListObject) const
++{
++  cRecording *r = (cRecording *)&ListObject;
++  return strcasecmp(SortName(), r->SortName());
++}
++
++const char *cRecording::FileName(void) const
++{
++  if (!fileName) {
++     struct tm tm_r;
++     struct tm *t = localtime_r(&start, &tm_r);
++     name = ExchangeChars(name, true);
++     asprintf(&fileName, NAMEFORMAT, VideoDirectory, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, priority, lifetime);
++     name = ExchangeChars(name, false);
++     }
++  return fileName;
++}
++
++const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) const
++{
++  char New = NewIndicator && IsNew() ? '*' : ' ';
++  free(titleBuffer);
++  titleBuffer = NULL;
++  if (Level < 0 || Level == HierarchyLevels()) {
++     struct tm tm_r;
++     struct tm *t = localtime_r(&start, &tm_r);
++     char *s;
++     if (Level > 0 && (s = strrchr(name, '~')) != NULL)
++        s++;
++     else
++        s = name;
++     asprintf(&titleBuffer, "%02d.%02d.%02d%c%02d:%02d%c%c%s",
++                            t->tm_mday,
++                            t->tm_mon + 1,
++                            t->tm_year % 100,
++                            Delimiter,
++                            t->tm_hour,
++                            t->tm_min,
++                            New,
++                            Delimiter,
++                            s);
++     // let's not display a trailing '~':
++     if (!NewIndicator)
++        stripspace(titleBuffer);
++     s = &titleBuffer[strlen(titleBuffer) - 1];
++     if (*s == '~')
++        *s = 0;
++     }
++  else if (Level < HierarchyLevels()) {
++     const char *s = name;
++     const char *p = s;
++     while (*++s) {
++           if (*s == '~') {
++              if (Level--)
++                 p = s + 1;
++              else
++                 break;
++              }
++           }
++     titleBuffer = MALLOC(char, s - p + 3);
++     *titleBuffer = Delimiter;
++     *(titleBuffer + 1) = Delimiter;
++     strn0cpy(titleBuffer + 2, p, s - p + 1);
++     }
++  else
++     return "";
++  return titleBuffer;
++}
++
++const char *cRecording::PrefixFileName(char Prefix)
++{
++  cString p = PrefixVideoFileName(FileName(), Prefix);
++  if (*p) {
++     free(fileName);
++     fileName = strdup(p);
++     return fileName;
++     }
++  return NULL;
++}
++
++int cRecording::HierarchyLevels(void) const
++{
++  const char *s = name;
++  int level = 0;
++  while (*++s) {
++        if (*s == '~')
++           level++;
++        }
++  return level;
++}
++
++bool cRecording::IsEdited(void) const
++{
++  const char *s = strrchr(name, '~');
++  s = !s ? name : s + 1;
++  return *s == '%';
++}
++
++bool cRecording::WriteInfo(void)
++{
++  char *InfoFileName = NULL;
++  asprintf(&InfoFileName, "%s%s", fileName, INFOFILESUFFIX);
++  FILE *f = fopen(InfoFileName, "w");
++  if (f) {
++     info->Write(f);
++     fclose(f);
++     }
++  else
++     LOG_ERROR_STR(InfoFileName);
++  free(InfoFileName);
++  return true;
++}
++
++bool cRecording::Delete(void)
++{
++  bool result = true;
++  char *NewName = strdup(FileName());
++  char *ext = strrchr(NewName, '.');
++  if (strcmp(ext, RECEXT) == 0) {
++     strncpy(ext, DELEXT, strlen(ext));
++     if (access(NewName, F_OK) == 0) {
++        // the new name already exists, so let's remove that one first:
++        isyslog("removing recording %s", NewName);
++        RemoveVideoFile(NewName);
++        }
++     isyslog("deleting recording %s", FileName());
++     result = RenameVideoFile(FileName(), NewName);
++     }
++  free(NewName);
++  return result;
++}
++
++bool cRecording::Remove(void)
++{
++  // let's do a final safety check here:
++  if (!endswith(FileName(), DELEXT)) {
++     esyslog("attempt to remove recording %s", FileName());
++     return false;
++     }
++  isyslog("removing recording %s", FileName());
++  return RemoveVideoFile(FileName());
++}
++
++void cRecording::ResetResume(void) const
++{
++  resume = RESUME_NOT_INITIALIZED;
++}
++
++// --- cRecordings -----------------------------------------------------------
++
++cRecordings Recordings;
++
++cRecordings::cRecordings(bool Deleted)
++{
++  deleted = Deleted;
++  lastUpdate = 0;
++}
++
++void cRecordings::ScanVideoDir(const char *DirName)
++{
++  cReadDir d(DirName);
++  struct dirent *e;
++  while ((e = d.Next()) != NULL) {
++        if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
++           char *buffer;
++           asprintf(&buffer, "%s/%s", DirName, e->d_name);
++           struct stat st;
++           if (stat(buffer, &st) == 0) {
++              if (S_ISLNK(st.st_mode)) {
++                 char *old = buffer;
++                 buffer = ReadLink(old);
++                 free(old);
++                 if (!buffer)
++                    continue;
++                 if (stat(buffer, &st) != 0) {
++                    free(buffer);
++                    continue;
++                    }
++                 }
++              if (S_ISDIR(st.st_mode)) {
++                 if (endswith(buffer, deleted ? DELEXT : RECEXT)) {
++                    cRecording *r = new cRecording(buffer);
++                    if (r->Name())
++                       Add(r);
++                    else
++                       delete r;
++                    }
++                 else
++                    ScanVideoDir(buffer);
++                 }
++              }
++           free(buffer);
++           }
++        }
++}
++
++bool cRecordings::NeedsUpdate(void)
++{
++  return lastUpdate <= LastModifiedTime(AddDirectory(VideoDirectory, ".update"));
++}
++
++bool cRecordings::Load(void)
++{
++  lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
++  Clear();
++  ScanVideoDir(VideoDirectory);
++  Sort();
++  return Count() > 0;
++}
++
++cRecording *cRecordings::GetByName(const char *FileName)
++{
++  for (cRecording *recording = First(); recording; recording = Next(recording)) {
++      if (strcmp(recording->FileName(), FileName) == 0)
++         return recording;
++      }
++  return NULL;
++}
++
++void cRecordings::AddByName(const char *FileName)
++{
++  cRecording *recording = GetByName(FileName);
++  if (!recording) {
++     recording = new cRecording(FileName);
++     Add(recording);
++     }
++}
++
++void cRecordings::DelByName(const char *FileName)
++{
++  cRecording *recording = GetByName(FileName);
++  if (recording)
++     Del(recording);
++}
++
++void cRecordings::ResetResume(const char *ResumeFileName)
++{
++  for (cRecording *recording = First(); recording; recording = Next(recording))
++     if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
++        recording->ResetResume();
++}
++
++// --- cMark -----------------------------------------------------------------
++
++cMark::cMark(int Position, const char *Comment)
++{
++  position = Position;
++  comment = Comment ? strdup(Comment) : NULL;
++}
++
++cMark::~cMark()
++{
++  free(comment);
++}
++
++cString cMark::ToText(void)
++{
++  char *buffer;
++  asprintf(&buffer, "%s%s%s\n", *IndexToHMSF(position, true), comment ? " " : "", comment ? comment : "");
++  return cString(buffer, true);
++}
++
++bool cMark::Parse(const char *s)
++{
++  free(comment);
++  comment = NULL;
++  position = HMSFToIndex(s);
++  const char *p = strchr(s, ' ');
++  if (p) {
++     p = skipspace(p);
++     if (*p)
++        comment = strdup(p);
++     }
++  return true;
++}
++
++bool cMark::Save(FILE *f)
++{
++  return fprintf(f, ToText()) > 0;
++}
++
++// --- cMarks ----------------------------------------------------------------
++
++bool cMarks::Load(const char *RecordingFileName)
++{
++  if (cConfig<cMark>::Load(AddDirectory(RecordingFileName, MARKSFILESUFFIX))) {
++     Sort();
++     return true;
++     }
++  return false;
++}
++
++void cMarks::Sort(void)
++{
++  for (cMark *m1 = First(); m1; m1 = Next(m1)) {
++      for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) {
++          if (m2->position < m1->position) {
++             swap(m1->position, m2->position);
++             swap(m1->comment, m2->comment);
++             }
++          }
++      }
++}
++
++cMark *cMarks::Add(int Position)
++{
++  cMark *m = Get(Position);
++  if (!m) {
++     cConfig<cMark>::Add(m = new cMark(Position));
++     Sort();
++     }
++  return m;
++}
++
++cMark *cMarks::Get(int Position)
++{
++  for (cMark *mi = First(); mi; mi = Next(mi)) {
++      if (mi->position == Position)
++         return mi;
++      }
++  return NULL;
++}
++
++cMark *cMarks::GetPrev(int Position)
++{
++  for (cMark *mi = Last(); mi; mi = Prev(mi)) {
++      if (mi->position < Position)
++         return mi;
++      }
++  return NULL;
++}
++
++cMark *cMarks::GetNext(int Position)
++{
++  for (cMark *mi = First(); mi; mi = Next(mi)) {
++      if (mi->position > Position)
++         return mi;
++      }
++  return NULL;
++}
++
++// --- cRecordingUserCommand -------------------------------------------------
++
++const char *cRecordingUserCommand::command = NULL;
++
++void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName)
++{
++  if (command) {
++     char *cmd;
++     asprintf(&cmd, "%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\"$"));
++     isyslog("executing '%s'", cmd);
++     SystemExec(cmd);
++     free(cmd);
++     }
++}
++
++// --- XXX+
++
++//XXX+ somewhere else???
++// --- cIndexFile ------------------------------------------------------------
++
++#define INDEXFILESUFFIX     "/index.vdr"
++
++// The number of frames to stay off the end in case of time shift:
++#define INDEXSAFETYLIMIT 150 // frames
++
++// The maximum time to wait before giving up while catching up on an index file:
++#define MAXINDEXCATCHUP   8 // seconds
++
++// The minimum age of an index file for considering it no longer to be written:
++#define MININDEXAGE    3600 // seconds
++
++cIndexFile::cIndexFile(const char *FileName, bool Record)
++:resumeFile(FileName)
++{
++  f = -1;
++  fileName = NULL;
++  size = 0;
++  last = -1;
++  index = NULL;
++  if (FileName) {
++     fileName = MALLOC(char, strlen(FileName) + strlen(INDEXFILESUFFIX) + 1);
++     if (fileName) {
++        strcpy(fileName, FileName);
++        char *pFileExt = fileName + strlen(fileName);
++        strcpy(pFileExt, INDEXFILESUFFIX);
++        int delta = 0;
++        if (access(fileName, R_OK) == 0) {
++           struct stat buf;
++           if (stat(fileName, &buf) == 0) {
++              delta = buf.st_size % sizeof(tIndex);
++              if (delta) {
++                 delta = sizeof(tIndex) - delta;
++                 esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName);
++                 }
++              last = (buf.st_size + delta) / sizeof(tIndex) - 1;
++              if (!Record && last >= 0) {
++                 size = last + 1;
++                 index = MALLOC(tIndex, size);
++                 if (index) {
++                    f = open(fileName, O_RDONLY);
++                    if (f >= 0) {
++                       if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
++                          esyslog("ERROR: can't read from file '%s'", fileName);
++                          free(index);
++                          index = NULL;
++                          close(f);
++                          f = -1;
++                          }
++                       // we don't close f here, see CatchUp()!
++                       }
++                    else
++                       LOG_ERROR_STR(fileName);
++                    }
++                 else
++                    esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName);
++                 }
++              }
++           else
++              LOG_ERROR;
++           }
++        else if (!Record)
++           isyslog("missing index file %s", fileName);
++        if (Record) {
++           if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE)) >= 0) {
++              if (delta) {
++                 esyslog("ERROR: padding index file with %d '0' bytes", delta);
++                 while (delta--)
++                       writechar(f, 0);
++                 }
++              }
++           else
++              LOG_ERROR_STR(fileName);
++           }
++        }
++     else
++        esyslog("ERROR: can't copy file name '%s'", FileName);
++     }
++}
++
++cIndexFile::~cIndexFile()
++{
++  if (f >= 0)
++     close(f);
++  free(fileName);
++  free(index);
++}
++
++bool cIndexFile::CatchUp(int Index)
++{
++  // returns true unless something really goes wrong, so that 'index' becomes NULL
++  if (index && f >= 0) {
++     cMutexLock MutexLock(&mutex);
++     for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) {
++         struct stat buf;
++         if (fstat(f, &buf) == 0) {
++            if (time(NULL) - buf.st_mtime > MININDEXAGE) {
++               // apparently the index file is not being written any more
++               close(f);
++               f = -1;
++               break;
++               }
++            int newLast = buf.st_size / sizeof(tIndex) - 1;
++            if (newLast > last) {
++               if (size <= newLast) {
++                  size *= 2;
++                  if (size <= newLast)
++                     size = newLast + 1;
++                  }
++               index = (tIndex *)realloc(index, size * sizeof(tIndex));
++               if (index) {
++                  int offset = (last + 1) * sizeof(tIndex);
++                  int delta = (newLast - last) * sizeof(tIndex);
++                  if (lseek(f, offset, SEEK_SET) == offset) {
++                     if (safe_read(f, &index[last + 1], delta) != delta) {
++                        esyslog("ERROR: can't read from index");
++                        free(index);
++                        index = NULL;
++                        close(f);
++                        f = -1;
++                        break;
++                        }
++                     last = newLast;
++                     }
++                  else
++                     LOG_ERROR_STR(fileName);
++                  }
++               else
++                  esyslog("ERROR: can't realloc() index");
++               }
++            }
++         else
++            LOG_ERROR_STR(fileName);
++         if (Index < last - (i ? 2 * INDEXSAFETYLIMIT : 0) || Index > 10 * INDEXSAFETYLIMIT) // keep off the end in case of "Pause live video"
++            break;
++         cCondWait::SleepMs(1000);
++         }
++     }
++  return index != NULL;
++}
++
++bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
++{
++  if (f >= 0) {
++     tIndex i = { FileOffset, PictureType, FileNumber, 0 };
++     if (safe_write(f, &i, sizeof(i)) < 0) {
++        LOG_ERROR_STR(fileName);
++        close(f);
++        f = -1;
++        return false;
++        }
++     last++;
++     }
++  return f >= 0;
++}
++
++bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
++{
++  if (CatchUp(Index)) {
++     if (Index >= 0 && Index < last) {
++        *FileNumber = index[Index].number;
++        *FileOffset = index[Index].offset;
++        if (PictureType)
++           *PictureType = index[Index].type;
++        if (Length) {
++           int fn = index[Index + 1].number;
++           int fo = index[Index + 1].offset;
++           if (fn == *FileNumber)
++              *Length = fo - *FileOffset;
++           else
++              *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
++           }
++        return true;
++        }
++     }
++  return false;
++}
++
++int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd)
++{
++  if (CatchUp()) {
++     int d = Forward ? 1 : -1;
++     for (;;) {
++         Index += d;
++         if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? INDEXSAFETYLIMIT : 0)) {
++            if (index[Index].type == I_FRAME) {
++               if (FileNumber)
++                  *FileNumber = index[Index].number;
++               else
++                  FileNumber = &index[Index].number;
++               if (FileOffset)
++                  *FileOffset = index[Index].offset;
++               else
++                  FileOffset = &index[Index].offset;
++               if (Length) {
++                  // all recordings end with a non-I_FRAME, so the following should be safe:
++                  int fn = index[Index + 1].number;
++                  int fo = index[Index + 1].offset;
++                  if (fn == *FileNumber)
++                     *Length = fo - *FileOffset;
++                  else {
++                     esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
++                     *Length = -1;
++                     }
++                  }
++               return Index;
++               }
++            }
++         else
++            break;
++         }
++     }
++  return -1;
++}
++
++int cIndexFile::Get(uchar FileNumber, int FileOffset)
++{
++  if (CatchUp()) {
++     //TODO implement binary search!
++     int i;
++     for (i = 0; i < last; i++) {
++         if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset)
++            break;
++         }
++     return i;
++     }
++  return -1;
++}
++
++// --- cFileName -------------------------------------------------------------
++
++#include <errno.h>
++#include <unistd.h>
++#include "videodir.h"
++
++#define MAXFILESPERRECORDING 255
++#define RECORDFILESUFFIX    "/%03d.vdr"
++#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
++
++cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
++{
++  file = -1;
++  fileNumber = 0;
++  record = Record;
++  blocking = Blocking;
++  // Prepare the file name:
++  fileName = MALLOC(char, strlen(FileName) + RECORDFILESUFFIXLEN);
++  if (!fileName) {
++     esyslog("ERROR: can't copy file name '%s'", fileName);
++     return;
++     }
++  strcpy(fileName, FileName);
++  pFileNumber = fileName + strlen(fileName);
++  SetOffset(1);
++}
++
++cFileName::~cFileName()
++{
++  Close();
++  free(fileName);
++}
++
++int cFileName::Open(void)
++{
++  if (file < 0) {
++     int BlockingFlag = blocking ? 0 : O_NONBLOCK;
++     if (record) {
++        dsyslog("recording to '%s'", fileName);
++        file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
++        if (file < 0)
++           LOG_ERROR_STR(fileName);
++        }
++     else {
++        if (access(fileName, R_OK) == 0) {
++           dsyslog("playing '%s'", fileName);
++           file = open(fileName, O_RDONLY | BlockingFlag);
++           if (file < 0)
++              LOG_ERROR_STR(fileName);
++           }
++        else if (errno != ENOENT)
++           LOG_ERROR_STR(fileName);
++        }
++     }
++  return file;
++}
++
++void cFileName::Close(void)
++{
++  if (file >= 0) {
++     if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
++        LOG_ERROR_STR(fileName);
++     file = -1;
++     }
++}
++
++int cFileName::SetOffset(int Number, int Offset)
++{
++  if (fileNumber != Number)
++     Close();
++  if (0 < Number && Number <= MAXFILESPERRECORDING) {
++     fileNumber = Number;
++     sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
++     if (record) {
++        if (access(fileName, F_OK) == 0) {
++           // files exists, check if it has non-zero size
++           struct stat buf;
++           if (stat(fileName, &buf) == 0) {
++              if (buf.st_size != 0)
++                 return SetOffset(Number + 1); // file exists and has non zero size, let's try next suffix
++              else {
++                 // zero size file, remove it
++                 dsyslog ("cFileName::SetOffset: removing zero-sized file %s\n", fileName);
++                 unlink (fileName);
++                 }
++              }
++           else
++              return SetOffset(Number + 1); // error with fstat - should not happen, just to be on the safe side
++           }
++        else if (errno != ENOENT) { // something serious has happened
++           LOG_ERROR_STR(fileName);
++           return -1;
++           }
++        // found a non existing file suffix
++        }
++     if (Open() >= 0) {
++        if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
++           LOG_ERROR_STR(fileName);
++           return -1;
++           }
++        }
++     return file;
++     }
++  esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
++  return -1;
++}
++
++int cFileName::NextFile(void)
++{
++  return SetOffset(fileNumber + 1);
++}
++
++// --- Index stuff -----------------------------------------------------------
++
++cString IndexToHMSF(int Index, bool WithFrame)
++{
++  char buffer[16];
++  int f = (Index % FRAMESPERSEC) + 1;
++  int s = (Index / FRAMESPERSEC);
++  int m = s / 60 % 60;
++  int h = s / 3600;
++  s %= 60;
++  snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
++  return buffer;
++}
++
++int HMSFToIndex(const char *HMSF)
++{
++  int h, m, s, f = 0;
++  if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
++     return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
++  return 0;
++}
++
++int SecondsToFrames(int Seconds)
++{
++  return Seconds * FRAMESPERSEC;
++}
++
++// --- ReadFrame -------------------------------------------------------------
++
++int ReadFrame(int f, uchar *b, int Length, int Max)
++{
++  if (Length == -1)
++     Length = Max; // this means we read up to EOF (see cIndex)
++  else if (Length > Max) {
++     esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
++     Length = Max;
++     }
++  int r = safe_read(f, b, Length);
++  if (r < 0)
++     LOG_ERROR;
++  return r;
++}
++
++
+diff -urNad vdr-1.3.31/tools.c /tmp/dpep.cLVvp8/vdr-1.3.31/tools.c
+--- vdr-1.3.31/tools.c	2005-08-27 16:43:55.000000000 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/tools.c	2005-08-31 21:04:17.163929920 +0200
+@@ -29,6 +29,156 @@
+                      BCDCHARTOINT( x        & 0xFF));
+ }
+ 
++#ifdef AVOID_TRASHING
++
++struct tUsageRange {
++  off_t begin;
++  off_t end;
++  off_t ahead;
++  ssize_t written;
++};
++
++#define MAX_STREAM_FD (256)
++struct tUsageRange StreamRanges[MAX_STREAM_FD];
++
++#define READ_AHEAD MEGABYTE(2)
++#define WRITE_BUFFER MEGABYTE(10)
++
++void RegisterStream(int fd)
++{
++  if (fd < 0 || fd >= MAX_STREAM_FD) {
++     return;
++     }
++  dsyslog("Open stream: %d", fd);
++  StreamRanges[fd].begin = -1;
++  StreamRanges[fd].end = -1;
++  StreamRanges[fd].ahead = -1;
++  StreamRanges[fd].written = 0;
++}
++
++int OpenStream(const char* PathName, int Flags, mode_t Mode)
++{
++  int retVal=open(PathName, Flags, Mode);
++  RegisterStream(retVal);
++  return retVal;
++}
++
++int OpenStream(const char* PathName, int Flags)
++{
++  int retVal=open(PathName, Flags);
++  RegisterStream(retVal);
++  return retVal;
++}
++
++ssize_t ReadStream(int fd, void* Buffer, size_t Size)
++{
++  if (fd >=0 && fd < MAX_STREAM_FD) {
++     off_t pos=lseek(fd, 0, SEEK_CUR);
++     off_t begin=StreamRanges[fd].begin;
++     off_t end=StreamRanges[fd].end;
++     off_t ahead=StreamRanges[fd].ahead;
++     // jump forward - adjust end position
++     if (pos > end) {
++        end = pos;
++        }
++     // after adjusting end - don't clear more then previously requested
++     if (end > ahead) {
++        end = ahead;
++        }
++     // jump backward - drop read ahead of previous run
++     if (pos < begin) {
++        end = ahead;
++        }
++     if (begin >= 0 && end > begin) {
++        posix_fadvise(fd, begin-KILOBYTE(200), end-begin+KILOBYTE(200), POSIX_FADV_DONTNEED);
++        }
++     StreamRanges[fd].begin = pos;
++     ssize_t bytesRead = safe_read(fd, Buffer, Size);
++     if (bytesRead > 0) {
++        pos += bytesRead;
++        StreamRanges[fd].end = pos;
++        // this seems to trigger a non blocking read - this
++        // may or may not have been finished when we will be called next time.
++        // If it is not finished we can't release the not yet filled buffers.
++        // So this is commented out till we find a better solution.
++//        posix_fadvise(fd, pos, READ_AHEAD, POSIX_FADV_WILLNEED);
++        StreamRanges[fd].ahead = pos + READ_AHEAD;
++        }
++     else {
++        StreamRanges[fd].end = pos;
++        }
++     return bytesRead;
++     }
++  return safe_read(fd, Buffer, Size);
++}
++
++ssize_t WriteStream(int fd, const void* Buffer, size_t Size)
++{
++  if (fd >=0 && fd < MAX_STREAM_FD) {
++     off_t pos=lseek(fd, 0, SEEK_CUR);
++     ssize_t bytesWritten = safe_write(fd, Buffer, Size);
++     if (bytesWritten >= 0) {
++        StreamRanges[fd].written += bytesWritten;
++        off_t begin=StreamRanges[fd].begin;
++        off_t end=StreamRanges[fd].end;
++        ssize_t written=StreamRanges[fd].written;
++        if (begin >= 0) {
++           if (pos < begin) {
++              begin = pos;
++              }
++           }
++        else {
++           begin = pos;
++           }
++        if (pos + bytesWritten > end) {
++           end = pos + bytesWritten;
++           }
++        if (written > WRITE_BUFFER) {
++//           dsyslog("flush buffer: %d (%d bytes, %ld-%ld)", fd, written, begin, end);
++           fdatasync(fd);
++           if (begin >= 0 && end > begin) {
++              posix_fadvise(fd, begin, end-begin, POSIX_FADV_DONTNEED);
++              }
++           begin = -1;
++           end = -1;
++           written = 0;
++           }
++        StreamRanges[fd].begin = begin;
++        StreamRanges[fd].end = end;
++        StreamRanges[fd].written = written;
++        }
++     return bytesWritten;
++     }
++  return safe_write(fd, Buffer, Size);
++}
++
++int CloseStream(int fd)
++{
++  if (fd >=0 && fd < MAX_STREAM_FD) {
++     off_t begin=StreamRanges[fd].begin;
++     off_t end=StreamRanges[fd].end;
++     off_t ahead=StreamRanges[fd].ahead;
++     if (ahead > end) {
++        end = ahead;
++        }
++     if (begin >= 0 && end > begin) {
++        ssize_t written=StreamRanges[fd].written;
++        dsyslog("close buffer: %d (flush: %d bytes, %ld-%ld)", fd, written, begin, end);
++        if (written) {
++           fdatasync(fd);
++           }
++        posix_fadvise(fd, begin, end-begin, POSIX_FADV_DONTNEED);
++        }
++     StreamRanges[fd].begin = -1;
++     StreamRanges[fd].end = -1;
++     StreamRanges[fd].ahead = -1;
++     StreamRanges[fd].written = 0;
++  }
++  return close(fd);
++}
++
++#endif // AVOID_TRASHING
++
+ ssize_t safe_read(int filedes, void *buffer, size_t size)
+ {
+   for (;;) {
+diff -urNad vdr-1.3.31/tools.h /tmp/dpep.cLVvp8/vdr-1.3.31/tools.h
+--- vdr-1.3.31/tools.h	2005-08-27 16:40:08.000000000 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/tools.h	2005-08-31 21:04:17.164929768 +0200
+@@ -84,6 +84,14 @@
+   static cString sprintf(const char *fmt, ...);
+   };
+ 
++#ifdef AVOID_TRASHING
++int OpenStream(const char* PathName, int Flags, mode_t Mode);
++int OpenStream(const char* PathName, int Flags);
++ssize_t ReadStream(int fd, void* Buffer, size_t Size);
++ssize_t WriteStream(int fd, const void* Buffer, size_t Size);
++int CloseStream(int fd);
++#endif // AVOID_TRASHING
++
+ ssize_t safe_read(int filedes, void *buffer, size_t size);
+ ssize_t safe_write(int filedes, const void *buffer, size_t size);
+ void writechar(int filedes, char c);
+diff -urNad vdr-1.3.31/videodir.c /tmp/dpep.cLVvp8/vdr-1.3.31/videodir.c
+--- vdr-1.3.31/videodir.c	2005-08-06 11:53:21.000000000 +0200
++++ /tmp/dpep.cLVvp8/vdr-1.3.31/videodir.c	2005-08-31 21:04:17.164929768 +0200
+@@ -137,7 +137,11 @@
+            }
+         }
+      }
++#ifdef AVOID_TRASHING
++  int Result = OpenStream(ActualFileName, Flags, DEFFILEMODE);
++#else
+   int Result = open(ActualFileName, Flags, DEFFILEMODE);
++#endif // AVOID_TRASHING
+   if (ActualFileName != FileName)
+      free((char *)ActualFileName);
+   return Result;
+@@ -146,7 +150,11 @@
+ int CloseVideoFile(int FileHandle)
+ {
+   // just in case we ever decide to do something special when closing the file!
++#ifdef AVOID_TRASHING
++  return CloseStream(FileHandle);
++#else
+   return close(FileHandle);
++#endif // AVOID_TRASHING
+ }
+ 
+ bool RenameVideoFile(const char *OldName, const char *NewName)


Property changes on: vdr/vdr/trunk/debian/patches/17_replay.dpatch
___________________________________________________________________
Name: svn:executable
   - *


Property changes on: vdr/vdr/trunk/debian/patches/opt-21_subtitles_and_ttxtsubs.dpatch
___________________________________________________________________
Name: svn:executable
   - *




More information about the pkg-vdr-dvb-changes mailing list