Implemented centralized 'wait for input'

This commit is contained in:
Klaus Schmidinger 2000-09-17 08:23:46 +02:00
parent 7fbf9e6c49
commit 4716cfb5a1
9 changed files with 172 additions and 97 deletions

View File

@ -168,7 +168,7 @@ Video Disk Recorder Revision History
entered so far together with the name of that channel are displayed on the entered so far together with the name of that channel are displayed on the
OSD (suggested by Martin Hammerschmid). OSD (suggested by Martin Hammerschmid).
2000-09-15: Version 0.64 2000-09-16: Version 0.64
- Video files now have the 'group read' bit set. - Video files now have the 'group read' bit set.
- Fixed handling errors in 'readstring()'. - Fixed handling errors in 'readstring()'.
@ -178,4 +178,7 @@ Video Disk Recorder Revision History
current '*.conf' files to your video directory ('/video' by default), or current '*.conf' files to your video directory ('/video' by default), or
use "-c ." to get the old behaviour of loading the configuration files use "-c ." to get the old behaviour of loading the configuration files
from the current directory. from the current directory.
- Waiting for input is now handled by a common function, which improves
response time on user actions. As a consequence the EIT data may sometimes
not be displayed, but this will change later when cEIT runs as a separate
thread.

13
eit.c
View File

@ -13,7 +13,7 @@
* the Free Software Foundation; either version 2 of the License, or * * the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. * * (at your option) any later version. *
* * * *
* $Id: eit.c 1.1 2000/09/03 10:22:25 kls Exp $ * $Id: eit.c 1.2 2000/09/17 08:02:30 kls Exp $
***************************************************************************/ ***************************************************************************/
#include "eit.h" #include "eit.h"
@ -22,7 +22,6 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <time.h> #include <time.h>
#include <sys/poll.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <dvb_comcode.h> #include <dvb_comcode.h>
#include "tools.h" #include "tools.h"
@ -253,15 +252,12 @@ int cEIT::GetSection(unsigned char *buf, ushort PID, unsigned char sec)
int seclen=0; int seclen=0;
unsigned short handle, pid; unsigned short handle, pid;
unsigned char section, sectionnum=0xff, maxsec=0; unsigned char section, sectionnum=0xff, maxsec=0;
struct pollfd pfd;
if ((handle = SetBitFilter(PID, (sec<<8)|0x00ff, SECTION_CONTINUOS))==0xffff) if ((handle = SetBitFilter(PID, (sec<<8)|0x00ff, SECTION_CONTINUOS))==0xffff)
return -1; return -1;
seclen=0; seclen=0;
pfd.fd=fsvbi; if (!cFile::AnyFileReady(fsvbi, 20000))
pfd.events=POLLIN;
if (poll(&pfd, 1, 20000)==0)
{ {
//cerr << "Timeout\n"; //cerr << "Timeout\n";
return -1; return -1;
@ -318,7 +314,6 @@ int cEIT::GetEIT()
struct eit_short_event_descriptor_struct *eitevt; struct eit_short_event_descriptor_struct *eitevt;
int seclen; int seclen;
unsigned short handle, pid; unsigned short handle, pid;
struct pollfd pfd;
eit_event * pevt = (eit_event *)0; eit_event * pevt = (eit_event *)0;
time_t tstart; time_t tstart;
@ -344,9 +339,7 @@ int cEIT::GetEIT()
tstart = time(NULL); tstart = time(NULL);
while ((!evtRunning.bIsValid || !evtNext.bIsValid) && nReceivedEITs < 20 && difftime(time(NULL), tstart) < 4) while ((!evtRunning.bIsValid || !evtNext.bIsValid) && nReceivedEITs < 20 && difftime(time(NULL), tstart) < 4)
{ {
pfd.fd=fsvbi; if (!cFile::AnyFileReady(fsvbi, 5000))
pfd.events=POLLIN;
if (poll(&pfd, 1, 5000)==0)
{ {
//cerr << "Timeout\n"; //cerr << "Timeout\n";
CloseFilter(handle); CloseFilter(handle);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: interface.c 1.15 2000/09/10 15:00:00 kls Exp $ * $Id: interface.c 1.16 2000/09/17 08:21:45 kls Exp $
*/ */
#include "interface.h" #include "interface.h"
@ -52,10 +52,6 @@ void cInterface::Close(void)
unsigned int cInterface::GetCh(bool Wait) unsigned int cInterface::GetCh(bool Wait)
{ {
#ifdef DEBUG_OSD
timeout(0);
getch(); // just to make 'ncurses' display the window:
#endif
if (RcIo.InputAvailable(Wait)) { if (RcIo.InputAvailable(Wait)) {
unsigned int Command; unsigned int Command;
return RcIo.GetCommand(&Command, NULL) ? Command : 0; return RcIo.GetCommand(&Command, NULL) ? Command : 0;
@ -77,7 +73,7 @@ eKeys cInterface::Wait(int Seconds, bool KeepChar)
while (time_ms() < t0) { while (time_ms() < t0) {
Key = GetKey(); Key = GetKey();
if (Key != kNone) if (Key != kNone || cFile::AnyFileReady())
break; break;
} }
if (KeepChar) if (KeepChar)

View File

@ -6,7 +6,7 @@
* *
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16. * Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
* *
* $Id: remote.c 1.11 2000/07/29 16:23:47 kls Exp $ * $Id: remote.c 1.12 2000/09/16 16:42:30 kls Exp $
*/ */
#include "remote.h" #include "remote.h"
@ -49,6 +49,7 @@ cRcIoBase::~cRcIoBase()
cRcIoKBD::cRcIoKBD(void) cRcIoKBD::cRcIoKBD(void)
{ {
f.Open(0); // stdin
} }
cRcIoKBD::~cRcIoKBD() cRcIoKBD::~cRcIoKBD()
@ -70,12 +71,7 @@ void cRcIoKBD::Flush(int WaitSeconds)
bool cRcIoKBD::InputAvailable(bool Wait) bool cRcIoKBD::InputAvailable(bool Wait)
{ {
timeout(Wait ? 1000 : 10); return f.Ready(Wait);
int ch = getch();
if (ch == ERR)
return false;
ungetch(ch);
return true;
} }
bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *) bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *)
@ -98,7 +94,7 @@ cRcIoRCU::cRcIoRCU(char *DeviceName)
code = 0; code = 0;
address = 0xFFFF; address = 0xFFFF;
lastNumber = 0; lastNumber = 0;
if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) { if (f.Open(DeviceName, O_RDWR | O_NONBLOCK)) {
struct termios t; struct termios t;
if (tcgetattr(f, &t) == 0) { if (tcgetattr(f, &t) == 0) {
cfsetspeed(&t, B9600); cfsetspeed(&t, B9600);
@ -107,17 +103,14 @@ cRcIoRCU::cRcIoRCU(char *DeviceName)
return; return;
} }
LOG_ERROR_STR(DeviceName); LOG_ERROR_STR(DeviceName);
close(f); f.Close();
} }
else else
LOG_ERROR_STR(DeviceName); LOG_ERROR_STR(DeviceName);
f = -1;
} }
cRcIoRCU::~cRcIoRCU() cRcIoRCU::~cRcIoRCU()
{ {
if (f >= 0)
close(f);
} }
int cRcIoRCU::ReceiveByte(bool Wait) int cRcIoRCU::ReceiveByte(bool Wait)
@ -135,7 +128,7 @@ int cRcIoRCU::ReceiveByte(bool Wait)
bool cRcIoRCU::SendByteHandshake(unsigned char c) bool cRcIoRCU::SendByteHandshake(unsigned char c)
{ {
if (f >= 0) { if (f.IsOpen()) {
int w = write(f, &c, 1); int w = write(f, &c, 1);
if (w == 1) { if (w == 1) {
for (int reply = ReceiveByte(); reply >= 0;) { for (int reply = ReceiveByte(); reply >= 0;) {
@ -193,7 +186,7 @@ void cRcIoRCU::Flush(int WaitSeconds)
bool cRcIoRCU::InputAvailable(bool Wait) bool cRcIoRCU::InputAvailable(bool Wait)
{ {
return DataAvailable(f, Wait); return f.Ready(Wait);
} }
bool cRcIoRCU::GetCommand(unsigned int *Command, unsigned short *Address) bool cRcIoRCU::GetCommand(unsigned int *Command, unsigned short *Address)
@ -349,22 +342,21 @@ cRcIoLIRC::cRcIoLIRC(char *DeviceName)
struct sockaddr_un addr; struct sockaddr_un addr;
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, DeviceName); strcpy(addr.sun_path, DeviceName);
f = socket(AF_UNIX, SOCK_STREAM, 0); int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (f >= 0) { if (sock >= 0) {
if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0) if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) >= 0) {
f.Open(sock);
return; return;
}
LOG_ERROR_STR(DeviceName); LOG_ERROR_STR(DeviceName);
close(f); close(sock);
} }
else else
LOG_ERROR_STR(DeviceName); LOG_ERROR_STR(DeviceName);
f = -1;
} }
cRcIoLIRC::~cRcIoLIRC() cRcIoLIRC::~cRcIoLIRC()
{ {
if (f >= 0)
close(f);
} }
const char *cRcIoLIRC::ReceiveString(void) const char *cRcIoLIRC::ReceiveString(void)
@ -406,7 +398,7 @@ void cRcIoLIRC::Flush(int WaitSeconds)
bool cRcIoLIRC::InputAvailable(bool Wait) bool cRcIoLIRC::InputAvailable(bool Wait)
{ {
return DataAvailable(f, Wait); return f.Ready(Wait);
} }
bool cRcIoLIRC::GetCommand(unsigned int *Command, unsigned short *) bool cRcIoLIRC::GetCommand(unsigned int *Command, unsigned short *)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remote.h 1.7 2000/07/15 16:32:43 kls Exp $ * $Id: remote.h 1.8 2000/09/16 14:01:14 kls Exp $
*/ */
#ifndef __REMOTE_H #ifndef __REMOTE_H
@ -12,6 +12,7 @@
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include "tools.h"
class cRcIoBase { class cRcIoBase {
protected: protected:
@ -36,6 +37,8 @@ public:
#if defined REMOTE_KBD #if defined REMOTE_KBD
class cRcIoKBD : public cRcIoBase { class cRcIoKBD : public cRcIoBase {
private:
cFile f;
public: public:
cRcIoKBD(void); cRcIoKBD(void);
virtual ~cRcIoKBD(); virtual ~cRcIoKBD();
@ -48,7 +51,7 @@ public:
class cRcIoRCU : public cRcIoBase { class cRcIoRCU : public cRcIoBase {
private: private:
int f; cFile f;
unsigned char dp, code, mode; unsigned char dp, code, mode;
unsigned short address; unsigned short address;
int lastNumber; int lastNumber;
@ -76,7 +79,7 @@ public:
class cRcIoLIRC : public cRcIoBase { class cRcIoLIRC : public cRcIoBase {
private: private:
enum { LIRC_KEY_BUF = 8, LIRC_BUFFER_SIZE = 128 }; enum { LIRC_KEY_BUF = 8, LIRC_BUFFER_SIZE = 128 };
int f; cFile f;
char keyName[LIRC_KEY_BUF]; char keyName[LIRC_KEY_BUF];
const char *ReceiveString(void); const char *ReceiveString(void);
public: public:

18
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * graphical interface that sits on top of an SVDRP connection.
* *
* $Id: svdrp.c 1.6 2000/09/09 10:51:21 kls Exp $ * $Id: svdrp.c 1.7 2000/09/16 13:34:28 kls Exp $
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
@ -206,7 +206,6 @@ const char *GetHelpPage(const char *Cmd)
cSVDRP::cSVDRP(int Port) cSVDRP::cSVDRP(int Port)
:socket(Port) :socket(Port)
{ {
filedes = -1;
isyslog(LOG_INFO, "SVDRP listening on port %d", Port); isyslog(LOG_INFO, "SVDRP listening on port %d", Port);
} }
@ -217,14 +216,13 @@ cSVDRP::~cSVDRP()
void cSVDRP::Close(void) void cSVDRP::Close(void)
{ {
if (filedes >= 0) { if (file.IsOpen()) {
//TODO how can we get the *full* hostname? //TODO how can we get the *full* hostname?
char buffer[MAXCMDBUFFER]; char buffer[MAXCMDBUFFER];
gethostname(buffer, sizeof(buffer)); gethostname(buffer, sizeof(buffer));
Reply(221, "%s closing connection", buffer); Reply(221, "%s closing connection", buffer);
isyslog(LOG_INFO, "closing connection"); //TODO store IP#??? isyslog(LOG_INFO, "closing connection"); //TODO store IP#???
close(filedes); file.Close();
filedes = -1;
} }
} }
@ -232,7 +230,7 @@ bool cSVDRP::Send(const char *s, int length)
{ {
if (length < 0) if (length < 0)
length = strlen(s); length = strlen(s);
int wbytes = write(filedes, s, length); int wbytes = write(file, s, length);
if (wbytes == length) if (wbytes == length)
return true; return true;
if (wbytes < 0) if (wbytes < 0)
@ -244,7 +242,7 @@ bool cSVDRP::Send(const char *s, int length)
void cSVDRP::Reply(int Code, const char *fmt, ...) void cSVDRP::Reply(int Code, const char *fmt, ...)
{ {
if (filedes >= 0) { if (file.IsOpen()) {
if (Code != 0) { if (Code != 0) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
@ -632,9 +630,9 @@ void cSVDRP::Execute(char *Cmd)
void cSVDRP::Process(void) void cSVDRP::Process(void)
{ {
bool SendGreeting = filedes < 0; bool SendGreeting = !file.IsOpen();
if (filedes >= 0 || (filedes = socket.Accept()) >= 0) { if (file.IsOpen() || file.Open(socket.Accept())) {
char buffer[MAXCMDBUFFER]; char buffer[MAXCMDBUFFER];
if (SendGreeting) { if (SendGreeting) {
//TODO how can we get the *full* hostname? //TODO how can we get the *full* hostname?
@ -642,7 +640,7 @@ void cSVDRP::Process(void)
time_t now = time(NULL); time_t now = time(NULL);
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", buffer, VDRVERSION, ctime(&now)); Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", buffer, VDRVERSION, ctime(&now));
} }
int rbytes = readstring(filedes, buffer, sizeof(buffer) - 1); int rbytes = file.ReadString(buffer, sizeof(buffer) - 1);
if (rbytes > 0) { if (rbytes > 0) {
//XXX overflow check??? //XXX overflow check???
// strip trailing whitespace: // strip trailing whitespace:

View File

@ -4,12 +4,14 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: svdrp.h 1.2 2000/08/06 12:45:28 kls Exp $ * $Id: svdrp.h 1.3 2000/09/16 11:48:36 kls Exp $
*/ */
#ifndef __SVDRP_H #ifndef __SVDRP_H
#define __SVDRP_H #define __SVDRP_H
#include "tools.h"
class cSocket { class cSocket {
private: private:
int port; int port;
@ -26,7 +28,7 @@ public:
class cSVDRP { class cSVDRP {
private: private:
cSocket socket; cSocket socket;
int filedes; cFile file;
void Close(void); void Close(void);
bool Send(const char *s, int length = -1); bool Send(const char *s, int length = -1);
void Reply(int Code, const char *fmt, ...); void Reply(int Code, const char *fmt, ...);

147
tools.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.c 1.16 2000/09/15 14:45:31 kls Exp $ * $Id: tools.c 1.17 2000/09/17 08:23:46 kls Exp $
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
@ -12,31 +12,20 @@
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#if defined(DEBUG_OSD)
#include <ncurses.h>
#endif
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#define MaxBuffer 1000 #define MaxBuffer 1000
int SysLogLevel = 3; int SysLogLevel = 3;
bool DataAvailable(int filedes, bool wait)
{
if (filedes >= 0) {
fd_set set;
FD_ZERO(&set);
FD_SET(filedes, &set);
struct timeval timeout;
timeout.tv_sec = wait ? 1 : 0;
timeout.tv_usec = wait ? 0 : 10000;
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(filedes, &set);
}
return false;
}
void writechar(int filedes, char c) void writechar(int filedes, char c)
{ {
write(filedes, &c, sizeof(c)); write(filedes, &c, sizeof(c));
@ -56,32 +45,12 @@ char readchar(int filedes)
bool readint(int filedes, int &n) bool readint(int filedes, int &n)
{ {
return DataAvailable(filedes) && read(filedes, &n, sizeof(n)) == sizeof(n); return cFile::AnyFileReady(filedes, 0) && read(filedes, &n, sizeof(n)) == sizeof(n);
}
int readstring(int filedes, char *buffer, int size, bool wait = false)
{
int rbytes = 0;
while (DataAvailable(filedes, wait)) {
int n = read(filedes, buffer + rbytes, size - rbytes);
if (n == 0)
break; // EOF
if (n < 0) {
LOG_ERROR;
return -1;
}
rbytes += n;
if (rbytes == size)
break;
wait = false;
}
return rbytes;
} }
void purge(int filedes) void purge(int filedes)
{ {
while (DataAvailable(filedes)) while (cFile::AnyFileReady(filedes, 0))
readchar(filedes); readchar(filedes);
} }
@ -321,6 +290,108 @@ void KillProcess(pid_t pid, int Timeout)
} }
} }
// --- cFile -----------------------------------------------------------------
bool cFile::files[FD_SETSIZE] = { false };
int cFile::maxFiles = 0;
cFile::cFile(void)
{
f = -1;
}
cFile::~cFile()
{
Close();
}
bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
{
if (!IsOpen())
return Open(open(FileName, Flags, Mode));
esyslog(LOG_ERR, "ERROR: attempt to re-open %s", FileName);
return false;
}
bool cFile::Open(int FileDes)
{
if (FileDes >= 0) {
if (!IsOpen()) {
f = FileDes;
if (f >= 0) {
if (f < FD_SETSIZE) {
if (f >= maxFiles)
maxFiles = f + 1;
if (!files[f])
files[f] = true;
else
esyslog(LOG_ERR, "ERROR: file descriptor %d already in files[]", f);
return true;
}
else
esyslog(LOG_ERR, "ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
}
}
else
esyslog(LOG_ERR, "ERROR: attempt to re-open file descriptor %d", FileDes);
}
return false;
}
void cFile::Close(void)
{
if (f >= 0) {
close(f);
files[f] = false;
f = -1;
}
}
int cFile::ReadString(char *Buffer, int Size)
{
int rbytes = 0;
bool wait = true;
while (Ready(wait)) {
int n = read(f, Buffer + rbytes, Size - rbytes);
if (n == 0)
break; // EOF
if (n < 0) {
LOG_ERROR;
return -1;
}
rbytes += n;
if (rbytes == Size)
break;
wait = false;
}
return rbytes;
}
bool cFile::Ready(bool Wait)
{
return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
}
bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
{
#ifdef DEBUG_OSD
refresh();
#endif
fd_set set;
FD_ZERO(&set);
for (int i = 0; i < maxFiles; i++) {
if (files[i])
FD_SET(i, &set);
}
if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
struct timeval timeout;
timeout.tv_sec = TimeoutMs / 1000;
timeout.tv_usec = (TimeoutMs % 1000) * 1000;
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
}
// --- cListObject ----------------------------------------------------------- // --- cListObject -----------------------------------------------------------
cListObject::cListObject(void) cListObject::cListObject(void)

25
tools.h
View File

@ -4,16 +4,17 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.h 1.14 2000/09/15 14:23:29 kls Exp $ * $Id: tools.h 1.15 2000/09/17 07:58:19 kls Exp $
*/ */
#ifndef __TOOLS_H #ifndef __TOOLS_H
#define __TOOLS_H #define __TOOLS_H
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <syslog.h> #include <syslog.h>
#include <sys/wait.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
extern int SysLogLevel; extern int SysLogLevel;
@ -30,12 +31,10 @@ extern int SysLogLevel;
#define DELETENULL(p) (delete (p), p = NULL) #define DELETENULL(p) (delete (p), p = NULL)
bool DataAvailable(int filedes, bool wait = false);
void writechar(int filedes, char c); void writechar(int filedes, char c);
void writeint(int filedes, int n); void writeint(int filedes, int n);
char readchar(int filedes); char readchar(int filedes);
bool readint(int filedes, int &n); bool readint(int filedes, int &n);
int readstring(int filedes, char *buffer, int size, bool wait = false);
void purge(int filedes); void purge(int filedes);
char *readline(FILE *f); char *readline(FILE *f);
char *strn0cpy(char *dest, const char *src, size_t n); char *strn0cpy(char *dest, const char *src, size_t n);
@ -52,6 +51,24 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
bool CheckProcess(pid_t pid); bool CheckProcess(pid_t pid);
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT); void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
class cFile {
private:
static bool files[];
static int maxFiles;
int f;
public:
cFile(void);
~cFile();
operator int () { return f; }
bool Open(const char *FileName, int Flags, mode_t Mode = S_IRUSR | S_IWUSR | S_IRGRP);
bool Open(int FileDes);
void Close(void);
bool IsOpen(void) { return f >= 0; }
int ReadString(char *Buffer, int Size);
bool Ready(bool Wait = true);
static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
};
class cListObject { class cListObject {
private: private:
cListObject *prev, *next; cListObject *prev, *next;