1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

New command line option --dirnames

This commit is contained in:
Klaus Schmidinger 2013-02-08 09:24:55 +01:00
parent b16437e784
commit 7f66e1573e
11 changed files with 219 additions and 61 deletions

10
HISTORY
View File

@ -7534,7 +7534,7 @@ Video Disk Recorder Revision History
- Reduced the number of retries in cTransfer::Receive() to avoid blocking recordings
in case the primary device can't handle the current live signal.
2013-02-03: Version 1.7.37
2013-02-08: Version 1.7.37
- Now also using FindHeader() in cMpeg2Fixer::AdjTref() (pointed out by Sören Moch).
- Added missing template for DVBDIR to Make.config.template (reported by Derek Kelly).
@ -7570,3 +7570,11 @@ Video Disk Recorder Revision History
recommended that plugins that implement an interface to any kind of remote controls
also use the parameters Setup.RcRepeatDelay and Setup.RcRepeatDelta for the desired
purpose, and remove any setup options they might have that serve the same purpose.
- cTimer no longer does any special "VFAT" handling to shorten directory names to 40
characters. When a string is used as a directory name for a recording, the maximum
length of the directory path, as well as the individual directory names, is now
limited to the values specified by the new command line option --dirnames (see
man vdr(1) for details). For backwards compatibility the option --vfat is still
available and has the same effect as --dirnames=250,40,1.
- The macro MaxFileName is now obsolete and may be removed in future versions. Use
NAME_MAX directly instead.

View File

@ -58,8 +58,10 @@ Alternatively you can use the '--lirc' option at runtime.
This option accepts an optional path to the remote control device,
the default of which can be set via the LIRC_DEVICE macro.
If your video directory will be on a VFAT partition, you can call VDR with
the command line option '--vfat'.
If you want to make your video directory available to other machines that
have limitations on directory name lengths and/or allowed characters in
directory names, you can call VDR with the command line option '--dirnames'
(see man vdr(1) for details).
When running, the 'vdr' program writes status information into the
system log file, which is usually /var/log/messages (or /var/log/user.log,

View File

@ -369,6 +369,12 @@ Recordings:
recording, even if there is no mark set at that point.
- The new option "Setup/Replay/Pause on mark set" can be used to activate automatically
going into Pause mode if an editing mark is set during replay.
- Timers no longer do any special "VFAT" handling to shorten directory names to 40
characters. When a string is used as a directory name for a recording, the maximum
length of the directory path, as well as the individual directory names, is now
limited to the values specified by the new command line option --dirnames (see
man vdr(1) for details). For backwards compatibility the option --vfat is still
available and has the same effect as --dirnames=250,40,1.
SVDRP:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 2.33 2013/02/02 13:44:33 kls Exp $
* $Id: config.c 2.34 2013/02/05 11:16:08 kls Exp $
*/
#include "config.h"
@ -587,7 +587,7 @@ bool cSetup::Parse(const char *Name, const char *Value)
else if (!strcasecmp(Name, "MenuScrollWrap")) MenuScrollWrap = atoi(Value);
else if (!strcasecmp(Name, "MenuKeyCloses")) MenuKeyCloses = atoi(Value);
else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value);
else if (!strcasecmp(Name, "NameInstantRecord")) Utf8Strn0Cpy(NameInstantRecord, Value, MaxFileName);
else if (!strcasecmp(Name, "NameInstantRecord")) Utf8Strn0Cpy(NameInstantRecord, Value, sizeof(NameInstantRecord));
else if (!strcasecmp(Name, "InstantRecordTime")) InstantRecordTime = atoi(Value);
else if (!strcasecmp(Name, "LnbSLOF")) LnbSLOF = atoi(Value);
else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 2.63 2013/02/02 13:45:19 kls Exp $
* $Id: config.h 2.64 2013/02/05 11:19:20 kls Exp $
*/
#ifndef __CONFIG_H
@ -49,7 +49,7 @@
#define MINOSDHEIGHT 324
#define MAXOSDHEIGHT 1200
#define MaxFileName 256
#define MaxFileName NAME_MAX // obsolete - use NAME_MAX directly instead!
#define MaxSkinName 16
#define MaxThemeName 16
@ -257,7 +257,7 @@ public:
int MenuScrollWrap;
int MenuKeyCloses;
int MarkInstantRecord;
char NameInstantRecord[MaxFileName];
char NameInstantRecord[NAME_MAX];
int InstantRecordTime;
int LnbSLOF;
int LnbFrequLo;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.c 2.85 2013/01/25 14:33:16 kls Exp $
* $Id: recording.c 2.86 2013/02/08 09:02:07 kls Exp $
*/
#include "recording.h"
@ -64,11 +64,11 @@
#define MARKSUPDATEDELTA 10 // seconds between checks for updating editing marks
#define MININDEXAGE 3600 // seconds before an index file is considered no longer to be written
#define MAX_SUBTITLE_LENGTH 40
#define MAX_LINK_LEVEL 6
bool VfatFileSystem = false;
int DirectoryPathMax = PATH_MAX;
int DirectoryNameMax = NAME_MAX;
bool DirectoryEncoding = false;
int InstanceId = 0;
cRecordings DeletedRecordings(true);
@ -540,22 +540,30 @@ tCharExchange CharExchange[] = {
{ 0, 0 }
};
const char *InvalidChars = "\"\\/:*?|<>#";
bool NeedsConversion(const char *p)
{
return DirectoryEncoding &&
(strchr(InvalidChars, *p) // characters that can't be part of a Windows file/directory name
|| *p == '.' && (!*(p + 1) || *(p + 1) == FOLDERDELIMCHAR)); // Windows can't handle '.' at the end of file/directory names
}
char *ExchangeChars(char *s, bool ToFileSystem)
{
char *p = s;
while (*p) {
if (VfatFileSystem) {
// The VFAT file system can't handle all characters, so we
if (DirectoryEncoding) {
// Some file systems can't handle all characters, so we
// have to take extra efforts to encode/decode them:
if (ToFileSystem) {
const char *InvalidChars = "\"\\/:*?|<>#";
switch (*p) {
// characters that can be mapped to other characters:
case ' ': *p = '_'; break;
case FOLDERDELIMCHAR: *p = '/'; break;
// characters that have to be encoded:
default:
if (strchr(InvalidChars, *p) || *p == '.' && (!*(p + 1) || *(p + 1) == FOLDERDELIMCHAR)) { // Windows can't handle '.' at the end of file/directory names
if (NeedsConversion(p)) {
int l = p - s;
if (char *NewBuffer = (char *)realloc(s, strlen(s) + 10)) {
s = NewBuffer;
@ -610,6 +618,107 @@ char *ExchangeChars(char *s, bool ToFileSystem)
return s;
}
char *LimitNameLengths(char *s, int PathMax, int NameMax)
{
// Limits the total length of the directory path in 's' to PathMax, and each
// individual directory name to NameMax. The lengths of characters that need
// conversion when using 's' as a file name are taken into account accordingly.
// If a directory name exceeds NameMax, it will be truncated. If the whole
// directory path exceeds PathMax, individual directory names will be shortened
// (from right to left) until the limit is met, or until the currently handled
// directory name consists of only a single character. All operations are performed
// directly on the given 's', which may become shorter (but never longer) than
// the original value.
// Returns a pointer to 's'.
int Length = strlen(s);
int PathLength = 0;
// Collect the resulting lengths of each character:
bool NameTooLong = false;
int8_t a[Length];
int n = 0;
int NameLength = 0;
for (char *p = s; *p; p++) {
if (*p == FOLDERDELIMCHAR) {
a[n] = -1; // FOLDERDELIMCHAR is a single character, neg. sign marks it
NameTooLong |= NameLength > NameMax;
NameLength = 0;
PathLength += 1;
}
else if (NeedsConversion(p)) {
a[n] = 3; // "#xx"
NameLength += 3;
PathLength += 3;
}
else {
int8_t l = Utf8CharLen(p);
a[n] = l;
NameLength += l;
PathLength += l;
while (l-- > 1) {
a[++n] = 0;
p++;
}
}
n++;
}
NameTooLong |= NameLength > NameMax;
// Limit names to NameMax:
if (NameTooLong) {
while (n > 0) {
// Calculate the length of the current name:
int NameLength = 0;
int i = n;
int b = i;
while (i-- > 0 && a[i] >= 0) {
NameLength += a[i];
b = i;
}
// Shorten the name if necessary:
if (NameLength > NameMax) {
int l = 0;
i = n;
while (i-- > 0 && a[i] >= 0) {
l += a[i];
if (NameLength - l <= NameMax) {
memmove(s + i, s + n, Length - n + 1);
memmove(a + i, a + n, Length - n + 1);
Length -= n - i;
PathLength -= l;
break;
}
}
}
// Switch to the next name:
n = b - 1;
}
}
// Limit path to PathMax:
n = Length;
while (PathLength > PathMax && n > 0) {
// Calculate how much to cut off the current name:
int i = n;
int b = i;
int l = 0;
while (--i > 0 && a[i - 1] >= 0) {
if (a[i] > 0) {
l += a[i];
b = i;
if (PathLength - l <= PathMax)
break;
}
}
// Shorten the name if necessary:
if (l > 0) {
memmove(s + b, s + n, Length - n + 1);
Length -= n - b;
PathLength -= l;
}
// Switch to the next name:
n = i - 1;
}
return s;
}
cRecording::cRecording(cTimer *Timer, const cEvent *Event)
{
resume = RESUME_NOT_INITIALIZED;
@ -628,16 +737,10 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event)
// 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:
Utf8Strn0Cpy(SubtitleBuffer, Subtitle, MAX_SUBTITLE_LENGTH);
Subtitle = SubtitleBuffer;
}
const char *macroTITLE = strstr(Timer->File(), TIMERMACRO_TITLE);
const char *macroEPISODE = strstr(Timer->File(), TIMERMACRO_EPISODE);
if (macroTITLE || macroEPISODE) {
@ -872,9 +975,12 @@ const char *cRecording::FileName(void) const
const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS;
int ch = isPesRecording ? priority : channel;
int ri = isPesRecording ? lifetime : instanceId;
name = ExchangeChars(name, true);
fileName = strdup(cString::sprintf(fmt, VideoDirectory, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
name = ExchangeChars(name, false);
char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(VideoDirectory) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve
if (strcmp(Name, name) != 0)
dsyslog("recording file name '%s' truncated to '%s'", name, Name);
Name = ExchangeChars(Name, true);
fileName = strdup(cString::sprintf(fmt, VideoDirectory, Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
free(Name);
}
return fileName;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.h 2.41 2012/12/23 15:11:53 kls Exp $
* $Id: recording.h 2.42 2013/02/07 13:42:17 kls Exp $
*/
#ifndef __RECORDING_H
@ -22,7 +22,9 @@
#define TIMERMACRO_TITLE "TITLE"
#define TIMERMACRO_EPISODE "EPISODE"
extern bool VfatFileSystem;
extern int DirectoryPathMax;
extern int DirectoryNameMax;
extern bool DirectoryEncoding;
extern int InstanceId;
void RemoveDeletedRecordings(void);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: timers.c 2.15 2012/12/07 13:14:00 kls Exp $
* $Id: timers.c 2.16 2013/02/05 11:13:20 kls Exp $
*/
#include "timers.h"
@ -17,8 +17,6 @@
#include "remote.h"
#include "status.h"
#define VFAT_MAX_FILENAME 40 // same as MAX_SUBTITLE_LENGTH in recording.c
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
// format characters in order to allow any number of blanks after a numeric
// value!
@ -80,11 +78,6 @@ cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
if (Instant && channel)
snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
if (VfatFileSystem && (Utf8StrLen(file) > VFAT_MAX_FILENAME)) {
dsyslog("timer file name too long for VFAT file system: '%s'", file);
file[Utf8SymChars(file, VFAT_MAX_FILENAME)] = 0;
dsyslog("timer file name truncated to '%s'", file);
}
}
cTimer::cTimer(const cEvent *Event)
@ -120,11 +113,6 @@ cTimer::cTimer(const cEvent *Event)
const char *Title = Event->Title();
if (!isempty(Title))
Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
if (VfatFileSystem && (Utf8StrLen(file) > VFAT_MAX_FILENAME)) {
dsyslog("timer file name too long for VFAT file system: '%s'", file);
file[Utf8SymChars(file, VFAT_MAX_FILENAME)] = 0;
dsyslog("timer file name truncated to '%s'", file);
}
SetEvent(Event);
}
@ -332,18 +320,6 @@ bool cTimer::Parse(const char *s)
}
//TODO add more plausibility checks
result = ParseDay(daybuffer, day, weekdays);
if (VfatFileSystem) {
char *p = strrchr(filebuffer, FOLDERDELIMCHAR);
if (p)
p++;
else
p = filebuffer;
if (Utf8StrLen(p) > VFAT_MAX_FILENAME) {
dsyslog("timer file name too long for VFAT file system: '%s'", p);
p[Utf8SymChars(p, VFAT_MAX_FILENAME)] = 0;
dsyslog("timer file name truncated to '%s'", p);
}
}
Utf8Strn0Cpy(file, filebuffer, sizeof(file));
strreplace(file, '|', ':');
if (isnumber(channelbuffer))

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: timers.h 2.5 2012/12/07 13:13:40 kls Exp $
* $Id: timers.h 2.6 2013/02/05 11:23:24 kls Exp $
*/
#ifndef __TIMERS_H
@ -39,7 +39,7 @@ private:
int stop;
int priority;
int lifetime;
mutable char file[MaxFileName];
mutable char file[NAME_MAX * 2]; // *2 to be able to hold 'title' and 'episode', which can each be up to 255 characters long
char *aux;
const cEvent *event;
public:

14
vdr.1
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.1 2.9 2012/09/01 13:40:49 kls Exp $
.\" $Id: vdr.1 2.10 2013/02/07 14:40:46 kls Exp $
.\"
.TH vdr 1 "10 Feb 2008" "1.6" "Video Disk Recorder"
.SH NAME
@ -57,6 +57,15 @@ Run in daemon mode (implies \-\-no\-kbd).
Use only the given DVB device (\fInum\fR = 0, 1, 2...).
There may be several \fB\-D\fR options (by default all DVB devices will be used).
.TP
.BI \-\-dirnames= path[,name[,enc]]
Set the maximum directory path length to \fIpath\fR (default is the maximum value
allowed on the system). If \fIname\fR is also given, it defines the maximum directory
name length (default is the maximum value allowed on the system). The optional
\fIenc\fR can be 0 or 1, and controls whether special characters in directory names
are encoded as hex values (default: 0).
The length of the video directory name and that of the actual recording directory is
subtracted from \fIpath\fR, to make sure the directory path will never become too long.
.TP
.BI \-\-edit= rec
Edit the given recording.
\fIrec\fR must be the full path name of an existing recording.
@ -188,8 +197,7 @@ operation.
allow coredumps if -u is given (only for debugging).
.TP
.BI \-\-vfat
Encode special characters in recording names to avoid problems
with VFAT file systems.
For backwards compatibility (same as \-\-dirnames= 250,40,1.
.TP
.BI \-v\ dir ,\ \-\-video= dir
Use \fIdir\fR as video directory.

58
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.tvdr.de
*
* $Id: vdr.c 2.46 2013/01/29 11:17:45 kls Exp $
* $Id: vdr.c 2.47 2013/02/07 13:53:01 kls Exp $
*/
#include <getopt.h>
@ -227,6 +227,7 @@ int main(int argc, char *argv[])
{ "config", required_argument, NULL, 'c' },
{ "daemon", no_argument, NULL, 'd' },
{ "device", required_argument, NULL, 'D' },
{ "dirnames", required_argument, NULL, 'd' | 0x100 },
{ "edit", required_argument, NULL, 'e' | 0x100 },
{ "epgfile", required_argument, NULL, 'E' },
{ "filesize", required_argument, NULL, 'f' | 0x100 },
@ -277,6 +278,44 @@ int main(int argc, char *argv[])
fprintf(stderr, "vdr: invalid DVB device number: %s\n", optarg);
return 2;
break;
case 'd' | 0x100: {
char *s = optarg;
int n = strtol(s, &s, 10);
if (n <= 0 || n >= PATH_MAX) {
fprintf(stderr, "vdr: invalid directory path length: %s\n", optarg);
return 2;
}
DirectoryPathMax = n;
if (!*s)
break;
if (*s++ != ',') {
fprintf(stderr, "vdr: invalid delimiter: %s\n", optarg);
return 2;
}
n = strtol(s, &s, 10);
if (n <= 0 || n >= NAME_MAX) {
fprintf(stderr, "vdr: invalid directory name length: %s\n", optarg);
return 2;
}
DirectoryNameMax = n;
if (!*s)
break;
if (*s++ != ',') {
fprintf(stderr, "vdr: invalid delimiter: %s\n", optarg);
return 2;
}
n = strtol(s, &s, 10);
if (n != 0 && n != 1) {
fprintf(stderr, "vdr: invalid directory encoding: %s\n", optarg);
return 2;
}
DirectoryEncoding = n;
if (*s) {
fprintf(stderr, "vdr: unexpected data: %s\n", optarg);
return 2;
}
}
break;
case 'e' | 0x100:
return CutRecording(optarg) ? 0 : 2;
case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
@ -384,7 +423,9 @@ int main(int argc, char *argv[])
case 'V': DisplayVersion = true;
break;
case 'v' | 0x100:
VfatFileSystem = true;
DirectoryPathMax = 250;
DirectoryNameMax = 40;
DirectoryEncoding = true;
break;
case 'v': VideoDirectory = optarg;
while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
@ -435,6 +476,13 @@ int main(int argc, char *argv[])
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
" there may be several -D options (default: all DVB\n"
" devices will be used)\n"
" --dirnames=PATH[,NAME[,ENC]]\n"
" set the maximum directory path length to PATH\n"
" (default: %d); if NAME is also given, it defines\n"
" the maximum directory name length (default: %d);\n"
" the optional ENC can be 0 or 1, and controls whether\n"
" special characters in directory names are encoded as\n"
" hex values (default: 0)\n"
" --edit=REC cut recording REC and exit\n"
" -E FILE, --epgfile=FILE write the EPG data into the given FILE (default is\n"
" '%s' in the video directory)\n"
@ -477,13 +525,15 @@ int main(int argc, char *argv[])
" --userdump allow coredumps if -u is given (debugging)\n"
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
" -V, --version print version information and exit\n"
" --vfat encode special characters in recording names to\n"
" avoid problems with VFAT file systems\n"
" --vfat for backwards compatibility (same as\n"
" --dirnames=250,40,1\n"
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
" seconds (default: %d); '0' disables the watchdog\n"
"\n",
DEFAULTCACHEDIR,
DEFAULTCONFDIR,
PATH_MAX,
NAME_MAX,
DEFAULTEPGDATAFILENAME,
MAXVIDEOFILESIZEDEFAULT,
DEFAULTPLUGINDIR,