mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
VDR developer version 2.3.1 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.3.1.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.2.0-2.3.1.diff MD5 checksums: 391c2ed60e2f7d24563fe3ed5854bc4f vdr-2.3.1.tar.bz2 983fd4bad7d19cd98301d54173107129 vdr-2.2.0-2.3.1.diff WARNING: ======== This is a *developer* version. Even though *I* use it in my productive environment, I strongly recommend that you only use it under controlled conditions and for testing and debugging. *** PLEASE BE VERY CAREFUL WHEN USING THIS DEVELOPER VERSION, ESPECIALLY *** IF YOU ENABLE THE NEW SVDRP PEERING! KEEP BACKUPS OF ALL YOUR TIMERS *** AND OBSERVE VERY CLOSELY WHETHER EVERYTHING WORKS AS EXPECTED. THIS *** VERSION INTRODUCES SOME MAJOR CHANGES IN HANDLING GLOBAL LISTS AND *** LOCKING, SO ANYTHING CAN HAPPEN! YOU HAVE BEEN WARNED! The main focus of this developer version is on the new locking mechanism for global lists, and the ability to handle remote timers. Any plugins that access the global lists of timers, channels, schedules or recordings, will need to be adjusted (see below for details). Please do initial tests with plain vanilla VDR and just the output plugin you need. Known bugs/problems: - After deleting the last recording in a sub folder, the cursor may not be positioned correctly. - Instant recordings and pausing live video don't (yet) use the default SVDRP host for recording. From the HISTORY file: - The new function cOsd::MaxPixmapSize() can be called to determine the maximum size a cPixmap may have on the current OSD. The 'osddemo' example has been modified accordingly. Plugin authors may want to use this function in case they use pixmaps that are larger than the full OSD size. The default implementation sets this limit to 2048x2048 pixel. - The Setup/CAM menu now displays which device an individual CAM is currently assigned to (suggested by Frank Neumann). - Added detection of 24fps (thanks to Thomas Reufer). - Added a note about the VDR User Counter and VDR's facebook page to the README file. - The dvbhddevice plugin is no longer part of the VDR source archive. You can get the latest version of this plugin from the author's repository at https://bitbucket.org/powARman/dvbhddevice. - The dvbsddevice and rcu plugins are no longer part of the VDR source archive. You can get the latest versions of these plugins from ftp://ftp.tvdr.de/vdr/Plugins. - Added a section about Output Devices to the INSTALL file. - Fixed setting the source value of newly created channels, in case the NIT is received from a different, but very close satellite position (reported by Daniel Ribeiro). The code for handling different NITs has been removed from nit.c, because according to the DVB standard table id 0x40 carries only the NIT of the actual network. - Added some comment to cPixmap about the relation between OSD, ViewPort and DrawPort (suggested by Thomas Reufer). - Improved syncing on sections when parsing the NIT and SDT. - Fixed scaling subtitles (their areas could sometimes extend outside the actual OSD). - Reduced the priority of the "video directory scanner" thread (suggested by Thomas Reufer) and checking cIoThrottle::Engaged() when it is running. - The script that gets called for recordings is now also called right before a recording is edited, with the first parameter being "editing" (suggested by Dieter Ferdinand). - The new setup option "OSD/Default sort mode for recordings" can be used to define how recordings shall be sorted by default (either by time or by name, with "by time" being the default). If a particular sort mode has been selected for a folder by pressing '0', the default no longer applies to that folder. Repeating timers no longer write a ".sort" file into a recordings folder to have the recordings sorted by time. - The command line option -D now accepts the value '-' (as in -D-), which prevents VDR from using any DVB devices (suggested by Dietmar Spingler). - The -V and -h options now list the plugins in alphabetical order (suggested by Dietmar Spingler). - Fixed a compiler warning in font.c. - Commented out the line #define DEPRECATED_VIDEOSYSTEM in device.h. If a plugin doesn't compile with this version of VDR, you can uncomment this line as a quick workaround. In the long run the plugin will need to be adapted. - The function cOsd::GetBitmap() is now 'protected'. If a plugin doesn't compile with this version of VDR, you can uncomment the line //#define DEPRECATED_GETBITMAP in osd.h as a quick workaround. In the long run the plugin will need to be adapted. - The -u option now also accepts a numerical user id (suggested by Derek Kelly). - The SVDRP port now accepts multiple concurrent connections. You can now keep an SVDRP connection open as long as you wish, without preventing others from connecting. Note, though, that SVDRP connections still get closed automatically if there has been no activity for 300 seconds (configurable via "Setup/Miscellaneous/SVDRP timeout (s)"). - The SVDRP log messages have been unified and now always contain the IP and port number of the remote host. - SVDRP connections are now handled in a separate "SVDRP server handler" thread, which makes them more responsive. Note that there is only one thread that handles all concurrent SVDRP connections. That way each SVDRP command is guaranteed to be processed separately, without interfering with any other SVDRP commands that might be issued at the same time. Plugins that implement SVDRP commands may need to take care of proper locking if the commands access global data. - VDR now sends out a broadcast to port 6419/udp, which was assigned to 'svdrp-disc' by the IANA. VDRs listening on that port will automatically initiate an SVDRP connection to the broadcasting VDR, and in turn send out a broadcast to make other VDRs connect to them. That way all VDRs within the local network will have permanent "peer-to-peer" SVDRP connections between each other. The configuration in the svdrphosts.conf file is taken into account when considering whether or not to respond to an SVDRP discover broadcast. - The new SVDRP command PING is used by automatically established peer-to-peer connections to keep them alive. - The new function GetSVDRPServerNames() can be used to get a list of all VDRs this VDR is connected to via SVDRP. - The new function ExecSVDRPCommand() can be used to execute an SVDRP command on one of the servers this VDR is connected to, and retrieve the result. The helper functions SVDRPCode() and SVDRPValue() can be used to easily access the codes and values returned by ExecSVDRPCommand(). - The cTimer class now has a new member named 'remote', which holds the name of the remote server this timer will record on. If this is NULL, it is a local timer. - Timers from other VDRs that are connected to this VDR via SVDRP are now automatically fetched and stored in the global Timers list. In order for this to work, all of the channels used by timers on the remote VDR must also be defined on the local VDR (however, not necessarily in the same sequence). Automatic channel syncing will be implemented later. - The main menu of the LCARS skin now displays a small rectangle on the left side of a timer if this is a remote timer. The color of that rectangle changes if the timer is currently recording on the remote VDR. - Accessing the global Timers list now has to be protected by proper locking, because SVDRP commands are now executed in a separate thread. The introduction of this locking mechanism required the following changes: + The new classes cStateLock and cStateKey are used to implement locking with quick detection of state changes. + cConfig::cConfig() now has a parameter that indicates whether this list requires locking. + The global lists of Timers, Channels, Schedules and Recordings are no longer static variables. They are now pointers that need to be retrieved through a call to cTimers::GetTimersRead/Write(), cChannels::GetChannelsRead/Write(), cSchedules::GetSchedulesRead/Write() and cRecordings::GetRecordingsRead/Write(), respectively. + References from/to link channels are now removed in cChannels::Del() rather than cChannel::~cChannel(), to make sure the caller holds a proper lock. + cChannel::HasTimer() has been removed. This information is now retrieved via cSchedule::HasTimer(). + Several member functions of cChannel, cTimer, cMarks and cRecording have been made 'const', and some of them are now available as both 'const' and 'non-const' versions. + The cChannel::Set...() functions are now 'bool' and return true if they have actually changed any of the channels's members. + cChannels::SetModified() has been renamed to cChannels::SetModifiedByUser(). + cChannels::Modified() has been renamed to cChannels::ModifiedByUser(), and now has a 'State' parameter that allows the caller to see whether a channel has been modified since the last call to this function with the same State variable. + The macros CHANNELSMOD_NONE/_AUTO/_USER have been removed. + cMarks now requires locking via cStateKey. + cSortedTimers now requires a pointer to the list of timers. + cEvent::HasTimer() no longer scans the list of timers to check whether an event is referenced by a timer, but rather keeps score of how many timers reference it. This was necessary in order to avoid having to lock the list of timers from within a cEvent. + The new class cListGarbageCollector is used to temporary store any objects deleted from cLists that require locking. This allows pointers to such objects to be dereferenced even if the objects are no longer part of the list. + cListBase::Contains() can be used to check whether a particular object is still contained in that list. + Outdated events are no longer "phased out", but rather deleted right away and thus taken care of by the new "garbage collector" of the list. + Deleted cRecording objects are no longer kept in a list of "vanished" recordings, but are rather taken care of by the new "garbage collector" of the list. + cSchedules::ClearAll() has been removed. The functionality is now implemented directly in cSVDRPServer::CmdCLRE(). + tEventID has been changed to u_int16_t in order to make room for the new member numTimers in cEvent. + cSchedule now has a member Modified(), which can be used with a State variable to quickly determine whether this schedule has been modified since the last call to this function with the same State variable. + cSchedulesLock has been removed. Locking the list of schedules is now done via the cList's new locking mechanism. + The 'OnlyRunningStatus' parameters in cEpgHandler::BeginSegmentTransfer() and cEpgHandler::EndSegmentTransfer() are now obsolete. They are still present in the interface for backward compatibility, but may be removed in a future version. Their value is always 'false'. + The constant tcMod is no longer used in cStatus::TimerChange(). The definition is still there for backward compatibility. Plugins that access the global lists of Timers, Channels, Recordings or Schedules will need to be adapted as follows: + Instead of directly accessing the global variables Timers, Channels or Recordings, they need to set up a cStateKey variable and call the proper getter function, as in cStateKey StateKey; if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) { // access the timers StateKey.Remove(); } and cStateKey StateKey; if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) { // access the timers StateKey.Remove(); } See timers.h, thread.h and tools.h for details on this new locking mechanism. + There are convenience macros for easily accessing these lists without having to explicitly set up a cStateKey and calling its Remove() function. These macros have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or RECORDINGS). Simply put such a macro before the point where you need to access the respective list, and there will be a pointer named Timers, Channels, Schedules or Recordings, respectively, which is valid until the end of the current block. + If a plugin needs to access several of the global lists in parallel, locking must always be done in the sequence Timers, Channels, Recordings, Schedules. This is necessary to make sure that different threads that need to lock several lists at the same time don't end up in a deadlock. + Some pointer variables may need to be made 'const'. The compiler will tell you about these. - cSectionSyncer has been improved to better handle missed sections. - Added a missing initialization of 'seen' in cChannel's copy constructor. - Background modifications of channels, timers and events are now displayed immediately in the corresponding menus. - cEIT now checks the version of the tables before doing any processing, which saves a lot of locking and processing. - If a timer is newly created with the Red button in the Schedule menu, and the timer is presented to the user in the "Edit timer" menu because it will start immediately, it now *must* be confirmed with "Ok" to set the timer. Otherwise the timer will not be created. - Recordings and deleted recordings are now scanned in a single thread. - The new SVDRP command POLL is used by automatically established peer-to-peer connections to trigger fetching remote timers. - You can now set DumpSVDRPDataTransfer in svdrp.c to true to have all SVDRP communication printed to the console for debugging. - Added a missing 'const' to cReceiver::Receive(), to protect the given Data from being modified. - The SVDRP commands that deal with timers (DELT, LSTT, MODT, NEWT, NEXT and UPDT) as well as any log messages that refer to timers, now use a unique id for each timer, which remains valid as long as this instance of VDR is running. This means that timers are no longer continuously numbered from 1 to N in LSTT. There may be gaps in the sequence, in case timers have been deleted. - The Timers menu now displays the name of the remote VDR in front of the timer's file name, if this is a remote timer. - The new options "Setup/Miscellaneous/SVDRP peering", ".../SVDRP host name" and ".../SVDRP default host" can be used to configure automatic peering between VDRs in the same network. Peering is disabled by default and can be enabled by setting "SVDRP peering" to "yes". - The function cTimer::ToText() no longer returns a newline character at the end of the string. The newline is now added by the caller as necessary. This was changed because cTimer::ToText() is now also needed in a context where the terminating newline can't be used. Consequently, cChannel::ToText() and cMark::ToText() have been modified accordingly. - All timer related response strings from SVDRP commands now use the channel ID instead of channel numbers. - The "Edit timer" menu now has a new parameter "Record on", which can be used to select the VDR on which this timer shall record. Timers can be freely moved between connected VDRs by simply selecting the desired machine in this field. - The SVDRP command DELT no longer checks whether the timer that shall be deleted is currently recording. - The character 0x0D is now stripped from EPG texts (reported by Janne Pänkälä). - The EPG scanner no longer moves the dish if there is a positioner. - The 'newplugin' script now creates the 'po' subdirectory for translations (thanks to Thomas Reufer). - Skins can now implement cSkinDisplayMenu::MenuOrientation() to display horizontal menus (thanks to Stefan Braun). - Fixed a possible stack overflow in cListBase::Sort() (thanks to Oliver Endriss). - Changed the description of the --chartab option in the INSTALL file to refer to "DVB SI table strings" instead of "EPG data". - The width and height of the OSD are now limited to the actual maximum dimensions of the output device, taking into account the top and left offset. - The new setup option "Recording/Record key handling" can be used to define what happens if the Record key on the remote control is pressed during live tv (suggested by Dietmar Spingler). - Empty adaptation field TS packets are now skipped when recording (thanks to Christopher Reimer, based on the "AFFcleaner" by Stefan Pöschel).
813 lines
32 KiB
C++
813 lines
32 KiB
C++
/*
|
|
* tools.h: Various tools
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: tools.h 4.3 2015/09/06 10:45:54 kls Exp $
|
|
*/
|
|
|
|
#ifndef __TOOLS_H
|
|
#define __TOOLS_H
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <float.h>
|
|
#include <iconv.h>
|
|
#include <math.h>
|
|
#include <poll.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include "thread.h"
|
|
|
|
typedef unsigned char uchar;
|
|
|
|
extern int SysLogLevel;
|
|
|
|
#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR, a) : void() )
|
|
#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_INFO, a) : void() )
|
|
#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_DEBUG, a) : void() )
|
|
|
|
#define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__)
|
|
#define LOG_ERROR_STR(s) esyslog("ERROR (%s,%d): %s: %m", __FILE__, __LINE__, s)
|
|
|
|
#define SECSINDAY 86400
|
|
|
|
#define KILOBYTE(n) ((n) * 1024)
|
|
#define MEGABYTE(n) ((n) * 1024LL * 1024LL)
|
|
|
|
#define MALLOC(type, size) (type *)malloc(sizeof(type) * (size))
|
|
|
|
template<class T> inline void DELETENULL(T *&p) { T *q = p; p = NULL; delete q; }
|
|
|
|
#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
|
|
#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR)
|
|
|
|
#ifndef __STL_CONFIG_H // in case some plugin needs to use the STL
|
|
template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
|
|
template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
|
|
template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
|
|
template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
|
|
#endif
|
|
|
|
template<class T> inline T constrain(T v, T l, T h) { return v < l ? l : v > h ? h : v; }
|
|
|
|
void syslog_with_tid(int priority, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
|
|
|
#define BCDCHARTOINT(x) (10 * ((x & 0xF0) >> 4) + (x & 0xF))
|
|
int BCD2INT(int x);
|
|
|
|
#define IsBitSet(v, b) ((v) & (1 << (b))) // checks if the bit at index b is set in v, where the least significant bit has index 0
|
|
|
|
// Unfortunately there are no platform independent macros for unaligned
|
|
// access, so we do it this way:
|
|
|
|
template<class T> inline T get_unaligned(T *p)
|
|
{
|
|
struct s { T v; } __attribute__((packed));
|
|
return ((s *)p)->v;
|
|
}
|
|
|
|
template<class T> inline void put_unaligned(unsigned int v, T* p)
|
|
{
|
|
struct s { T v; } __attribute__((packed));
|
|
((s *)p)->v = v;
|
|
}
|
|
|
|
// Comparing doubles for equality is unsafe, but unfortunately we can't
|
|
// overwrite operator==(double, double), so this will have to do:
|
|
|
|
inline bool DoubleEqual(double a, double b)
|
|
{
|
|
return fabs(a - b) <= DBL_EPSILON;
|
|
}
|
|
|
|
// When handling strings that might contain UTF-8 characters, it may be necessary
|
|
// to process a "symbol" that consists of several actual character bytes. The
|
|
// following functions allow transparently accessing a "char *" string without
|
|
// having to worry about what character set is actually used.
|
|
|
|
int Utf8CharLen(const char *s);
|
|
///< Returns the number of character bytes at the beginning of the given
|
|
///< string that form a UTF-8 symbol.
|
|
uint Utf8CharGet(const char *s, int Length = 0);
|
|
///< Returns the UTF-8 symbol at the beginning of the given string.
|
|
///< Length can be given from a previous call to Utf8CharLen() to avoid calculating
|
|
///< it again. If no Length is given, Utf8CharLen() will be called.
|
|
int Utf8CharSet(uint c, char *s = NULL);
|
|
///< Converts the given UTF-8 symbol to a sequence of character bytes and copies
|
|
///< them to the given string. Returns the number of bytes written. If no string
|
|
///< is given, only the number of bytes is returned and nothing is copied.
|
|
int Utf8SymChars(const char *s, int Symbols);
|
|
///< Returns the number of character bytes at the beginning of the given
|
|
///< string that form at most the given number of UTF-8 symbols.
|
|
int Utf8StrLen(const char *s);
|
|
///< Returns the number of UTF-8 symbols formed by the given string of
|
|
///< character bytes.
|
|
char *Utf8Strn0Cpy(char *Dest, const char *Src, int n);
|
|
///< Copies at most n character bytes from Src to Dest, making sure that the
|
|
///< resulting copy ends with a complete UTF-8 symbol. The copy is guaranteed
|
|
///< to be zero terminated.
|
|
///< Returns a pointer to Dest.
|
|
int Utf8ToArray(const char *s, uint *a, int Size);
|
|
///< Converts the given character bytes (including the terminating 0) into an
|
|
///< array of UTF-8 symbols of the given Size. Returns the number of symbols
|
|
///< in the array (without the terminating 0).
|
|
int Utf8FromArray(const uint *a, char *s, int Size, int Max = -1);
|
|
///< Converts the given array of UTF-8 symbols (including the terminating 0)
|
|
///< into a sequence of character bytes of at most Size length. Returns the
|
|
///< number of character bytes written (without the terminating 0).
|
|
///< If Max is given, only that many symbols will be converted.
|
|
///< The resulting string is always zero-terminated if Size is big enough.
|
|
|
|
// When allocating buffer space, make sure we reserve enough space to hold
|
|
// a string in UTF-8 representation:
|
|
|
|
#define Utf8BufSize(s) ((s) * 4)
|
|
|
|
// The following macros automatically use the correct versions of the character
|
|
// class functions:
|
|
|
|
#define Utf8to(conv, c) (cCharSetConv::SystemCharacterTable() ? to##conv(c) : tow##conv(c))
|
|
#define Utf8is(ccls, c) (cCharSetConv::SystemCharacterTable() ? is##ccls(c) : isw##ccls(c))
|
|
|
|
class cCharSetConv {
|
|
private:
|
|
iconv_t cd;
|
|
char *result;
|
|
size_t length;
|
|
static char *systemCharacterTable;
|
|
public:
|
|
cCharSetConv(const char *FromCode = NULL, const char *ToCode = NULL);
|
|
///< Sets up a character set converter to convert from FromCode to ToCode.
|
|
///< If FromCode is NULL, the previously set systemCharacterTable is used
|
|
///< (or "UTF-8" if no systemCharacterTable has been set).
|
|
///< If ToCode is NULL, "UTF-8" is used.
|
|
~cCharSetConv();
|
|
const char *Convert(const char *From, char *To = NULL, size_t ToLength = 0);
|
|
///< Converts the given Text from FromCode to ToCode (as set in the constructor).
|
|
///< If To is given, it is used to copy at most ToLength bytes of the result
|
|
///< (including the terminating 0) into that buffer. If To is not given,
|
|
///< the result is copied into a dynamically allocated buffer and is valid as
|
|
///< long as this object lives, or until the next call to Convert(). The
|
|
///< return value always points to the result if the conversion was successful
|
|
///< (even if a fixed size To buffer was given and the result didn't fit into
|
|
///< it). If the string could not be converted, the result points to the
|
|
///< original From string.
|
|
static const char *SystemCharacterTable(void) { return systemCharacterTable; }
|
|
static void SetSystemCharacterTable(const char *CharacterTable);
|
|
};
|
|
|
|
class cString {
|
|
private:
|
|
char *s;
|
|
public:
|
|
cString(const char *S = NULL, bool TakePointer = false);
|
|
cString(const char *S, const char *To); ///< Copies S up to To (exclusive). To must be a valid pointer into S. If To is NULL, everything is copied.
|
|
cString(const cString &String);
|
|
virtual ~cString();
|
|
operator const void * () const { return s; } // to catch cases where operator*() should be used
|
|
operator const char * () const { return s; } // for use in (const char *) context
|
|
const char * operator*() const { return s; } // for use in (const void *) context (printf() etc.)
|
|
cString &operator=(const cString &String);
|
|
cString &operator=(const char *String);
|
|
cString &Append(const char *String);
|
|
cString &Truncate(int Index); ///< Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
|
|
cString &CompactChars(char c); ///< Compact any sequence of characters 'c' to a single character, and strip all of them from the beginning and end of this string.
|
|
static cString sprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
|
static cString vsprintf(const char *fmt, va_list &ap);
|
|
};
|
|
|
|
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);
|
|
int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs = 0, int RetryMs = 0);
|
|
///< Writes either all Data to the given file descriptor, or nothing at all.
|
|
///< If TimeoutMs is greater than 0, it will only retry for that long, otherwise
|
|
///< it will retry forever. RetryMs defines the time between two retries.
|
|
char *strcpyrealloc(char *dest, const char *src);
|
|
char *strn0cpy(char *dest, const char *src, size_t n);
|
|
char *strreplace(char *s, char c1, char c2);
|
|
char *strreplace(char *s, const char *s1, const char *s2); ///< re-allocates 's' and deletes the original string if necessary!
|
|
const char *strchrn(const char *s, char c, size_t n); ///< returns a pointer to the n'th occurrence (counting from 1) of c in s, or NULL if no such character was found. If n is 0, s is returned.
|
|
int strcountchr(const char *s, char c); ///< returns the number of occurrences of 'c' in 's'.
|
|
inline char *skipspace(const char *s)
|
|
{
|
|
if ((uchar)*s > ' ') // most strings don't have any leading space, so handle this case as fast as possible
|
|
return (char *)s;
|
|
while (*s && (uchar)*s <= ' ') // avoiding isspace() here, because it is much slower
|
|
s++;
|
|
return (char *)s;
|
|
}
|
|
char *stripspace(char *s);
|
|
char *compactspace(char *s);
|
|
char *compactchars(char *s, char c); ///< removes all occurrences of 'c' from the beginning an end of 's' and replaces sequences of multiple 'c's with a single 'c'.
|
|
cString strescape(const char *s, const char *chars);
|
|
cString strgetval(const char *s, const char *name, char d = '=');
|
|
///< Returns the value part of a 'name=value' pair in s.
|
|
///< name must either be at the beginning of s, or has to be preceded by white space.
|
|
///< There may be any number of white space around the '=' sign. The value is
|
|
///< everyting up to (and excluding) the next white space, or the end of s.
|
|
///< If an other delimiter shall be used (like, e.g., ':'), it can be given
|
|
///< as the third parameter.
|
|
///< If name occurs more than once in s, only the first occurrence is taken.
|
|
bool startswith(const char *s, const char *p);
|
|
bool endswith(const char *s, const char *p);
|
|
bool isempty(const char *s);
|
|
int numdigits(int n);
|
|
bool isnumber(const char *s);
|
|
int64_t StrToNum(const char *s);
|
|
///< Converts the given string to a number.
|
|
///< The numerical part of the string may be followed by one of the letters
|
|
///< K, M, G or T to abbreviate Kilo-, Mega-, Giga- or Terabyte, respectively
|
|
///< (based on 1024). Everything after the first non-numeric character is
|
|
///< silently ignored, as are any characters other than the ones mentioned here.
|
|
bool StrInArray(const char *a[], const char *s);
|
|
///< Returns true if the string s is equal to one of the strings pointed
|
|
///< to by the (NULL terminated) array a.
|
|
double atod(const char *s);
|
|
///< Converts the given string, which is a floating point number using a '.' as
|
|
///< the decimal point, to a double value, independent of the currently selected
|
|
///< locale.
|
|
cString dtoa(double d, const char *Format = "%f");
|
|
///< Converts the given double value to a string, making sure it uses a '.' as
|
|
///< the decimal point, independent of the currently selected locale.
|
|
///< If Format is given, it will be used instead of the default.
|
|
cString itoa(int n);
|
|
cString AddDirectory(const char *DirName, const char *FileName);
|
|
bool EntriesOnSameFileSystem(const char *File1, const char *File2);
|
|
///< Checks whether the given files are on the same file system. If either of the
|
|
///< files doesn't exist, this function returns *true* to avoid any actions that might be
|
|
///< triggered if files are on different file system.
|
|
int FreeDiskSpaceMB(const char *Directory, int *UsedMB = NULL);
|
|
bool DirectoryOk(const char *DirName, bool LogErrors = false);
|
|
bool MakeDirs(const char *FileName, bool IsDirectory = false);
|
|
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
|
|
bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false, const char *IgnoreFiles[] = NULL);
|
|
///< Removes all empty directories under the given directory DirName.
|
|
///< If RemoveThis is true, DirName will also be removed if it is empty.
|
|
///< IgnoreFiles can be set to an array of file names that will be ignored when
|
|
///< considering whether a directory is empty. If IgnoreFiles is given, the array
|
|
///< must end with a NULL pointer.
|
|
int DirSizeMB(const char *DirName); ///< returns the total size of the files in the given directory, or -1 in case of an error
|
|
char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error)
|
|
bool SpinUpDisk(const char *FileName);
|
|
void TouchFile(const char *FileName);
|
|
time_t LastModifiedTime(const char *FileName);
|
|
off_t FileSize(const char *FileName); ///< returns the size of the given file, or -1 in case of an error (e.g. if the file doesn't exist)
|
|
cString WeekDayName(int WeekDay);
|
|
///< Converts the given WeekDay (0=Sunday, 1=Monday, ...) to a three letter
|
|
///< day name.
|
|
cString WeekDayName(time_t t);
|
|
///< Converts the week day of the given time to a three letter day name.
|
|
cString WeekDayNameFull(int WeekDay);
|
|
///< Converts the given WeekDay (0=Sunday, 1=Monday, ...) to a full
|
|
///< day name.
|
|
cString WeekDayNameFull(time_t t);
|
|
///< Converts the week day of the given time to a full day name.
|
|
cString DayDateTime(time_t t = 0);
|
|
///< Converts the given time to a string of the form "www dd.mm. hh:mm".
|
|
///< If no time is given, the current time is taken.
|
|
cString TimeToString(time_t t);
|
|
///< Converts the given time to a string of the form "www mmm dd hh:mm:ss yyyy".
|
|
cString DateString(time_t t);
|
|
///< Converts the given time to a string of the form "www dd.mm.yyyy".
|
|
cString ShortDateString(time_t t);
|
|
///< Converts the given time to a string of the form "dd.mm.yy".
|
|
cString TimeString(time_t t);
|
|
///< Converts the given time to a string of the form "hh:mm".
|
|
uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality = 100);
|
|
///< Converts the given Memory to a JPEG image and returns a pointer
|
|
///< to the resulting image. Mem must point to a data block of exactly
|
|
///< (Width * Height) triplets of RGB image data bytes. Upon return, Size
|
|
///< will hold the number of bytes of the resulting JPEG data.
|
|
///< Quality can be in the range 0..100 and controls the quality of the
|
|
///< resulting image, where 100 is "best". The caller takes ownership of
|
|
///< the result and has to delete it once it is no longer needed.
|
|
///< The result may be NULL in case of an error.
|
|
const char *GetHostName(void);
|
|
///< Gets the host name of this machine.
|
|
|
|
class cBase64Encoder {
|
|
private:
|
|
const uchar *data;
|
|
int length;
|
|
int maxResult;
|
|
int i;
|
|
char *result;
|
|
static const char *b64;
|
|
public:
|
|
cBase64Encoder(const uchar *Data, int Length, int MaxResult = 64);
|
|
///< Sets up a new base 64 encoder for the given Data, with the given Length.
|
|
///< Data will not be copied and must be valid as long as NextLine() will be
|
|
///< called. MaxResult defines the maximum number of characters in any
|
|
///< result line. The resulting lines may be shorter than MaxResult in case
|
|
///< its value is not a multiple of 4.
|
|
~cBase64Encoder();
|
|
const char *NextLine(void);
|
|
///< Returns the next line of encoded data (terminated by '\0'), or NULL if
|
|
///< there is no more encoded data. The caller must call NextLine() and process
|
|
///< each returned line until NULL is returned, in order to get the entire
|
|
///< data encoded. The returned data is only valid until the next time NextLine()
|
|
///< is called, or until the object is destroyed.
|
|
};
|
|
|
|
class cBitStream {
|
|
private:
|
|
const uint8_t *data;
|
|
int length; // in bits
|
|
int index; // in bits
|
|
public:
|
|
cBitStream(const uint8_t *Data, int Length) : data(Data), length(Length), index(0) {}
|
|
~cBitStream() {}
|
|
int GetBit(void);
|
|
uint32_t GetBits(int n);
|
|
void ByteAlign(void);
|
|
void WordAlign(void);
|
|
bool SetLength(int Length);
|
|
void SkipBits(int n) { index += n; }
|
|
void SkipBit(void) { SkipBits(1); }
|
|
bool IsEOF(void) const { return index >= length; }
|
|
void Reset(void) { index = 0; }
|
|
int Length(void) const { return length; }
|
|
int Index(void) const { return (IsEOF() ? length : index); }
|
|
const uint8_t *GetData(void) const { return (IsEOF() ? NULL : data + (index / 8)); }
|
|
};
|
|
|
|
class cTimeMs {
|
|
private:
|
|
uint64_t begin;
|
|
public:
|
|
cTimeMs(int Ms = 0);
|
|
///< Creates a timer with ms resolution and an initial timeout of Ms.
|
|
///< If Ms is negative the timer is not initialized with the current
|
|
///< time.
|
|
static uint64_t Now(void);
|
|
void Set(int Ms = 0);
|
|
bool TimedOut(void) const;
|
|
uint64_t Elapsed(void) const;
|
|
};
|
|
|
|
class cReadLine {
|
|
private:
|
|
size_t size;
|
|
char *buffer;
|
|
public:
|
|
cReadLine(void);
|
|
~cReadLine();
|
|
char *Read(FILE *f);
|
|
};
|
|
|
|
class cPoller {
|
|
private:
|
|
enum { MaxPollFiles = 64 };
|
|
pollfd pfd[MaxPollFiles];
|
|
int numFileHandles;
|
|
public:
|
|
cPoller(int FileHandle = -1, bool Out = false);
|
|
bool Add(int FileHandle, bool Out);
|
|
void Del(int FileHandle, bool Out);
|
|
bool Poll(int TimeoutMs = 0);
|
|
};
|
|
|
|
class cReadDir {
|
|
private:
|
|
DIR *directory;
|
|
struct dirent *result;
|
|
union { // according to "The GNU C Library Reference Manual"
|
|
struct dirent d;
|
|
char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
|
|
} u;
|
|
public:
|
|
cReadDir(const char *Directory);
|
|
~cReadDir();
|
|
bool Ok(void) { return directory != NULL; }
|
|
struct dirent *Next(void);
|
|
};
|
|
|
|
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 = DEFFILEMODE);
|
|
bool Open(int FileDes);
|
|
void Close(void);
|
|
bool IsOpen(void) { return f >= 0; }
|
|
bool Ready(bool Wait = true);
|
|
static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
|
|
static bool FileReady(int FileDes, int TimeoutMs = 1000);
|
|
static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
|
|
};
|
|
|
|
class cSafeFile {
|
|
private:
|
|
FILE *f;
|
|
char *fileName;
|
|
char *tempName;
|
|
public:
|
|
cSafeFile(const char *FileName);
|
|
~cSafeFile();
|
|
operator FILE* () { return f; }
|
|
bool Open(void);
|
|
bool Close(void);
|
|
};
|
|
|
|
/// cUnbufferedFile is used for large files that are mainly written or read
|
|
/// in a streaming manner, and thus should not be cached.
|
|
|
|
class cUnbufferedFile {
|
|
private:
|
|
int fd;
|
|
off_t curpos;
|
|
off_t cachedstart;
|
|
off_t cachedend;
|
|
off_t begin;
|
|
off_t lastpos;
|
|
off_t ahead;
|
|
size_t readahead;
|
|
size_t written;
|
|
size_t totwritten;
|
|
int FadviseDrop(off_t Offset, off_t Len);
|
|
public:
|
|
cUnbufferedFile(void);
|
|
~cUnbufferedFile();
|
|
int Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
|
|
int Close(void);
|
|
void SetReadAhead(size_t ra);
|
|
off_t Seek(off_t Offset, int Whence);
|
|
ssize_t Read(void *Data, size_t Size);
|
|
ssize_t Write(const void *Data, size_t Size);
|
|
static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
|
|
};
|
|
|
|
class cLockFile {
|
|
private:
|
|
char *fileName;
|
|
int f;
|
|
public:
|
|
cLockFile(const char *Directory);
|
|
~cLockFile();
|
|
bool Lock(int WaitSeconds = 0);
|
|
void Unlock(void);
|
|
};
|
|
|
|
class cListObject {
|
|
friend class cListGarbageCollector;
|
|
private:
|
|
cListObject *prev, *next;
|
|
public:
|
|
cListObject(void);
|
|
virtual ~cListObject();
|
|
virtual int Compare(const cListObject &ListObject) const { return 0; }
|
|
///< Must return 0 if this object is equal to ListObject, a positive value
|
|
///< if it is "greater", and a negative value if it is "smaller".
|
|
void Append(cListObject *Object);
|
|
void Insert(cListObject *Object);
|
|
void Unlink(void);
|
|
int Index(void) const;
|
|
cListObject *Prev(void) const { return prev; }
|
|
cListObject *Next(void) const { return next; }
|
|
};
|
|
|
|
class cListGarbageCollector {
|
|
private:
|
|
cMutex mutex;
|
|
cListObject *objects;
|
|
time_t lastPut;
|
|
public:
|
|
cListGarbageCollector(void);
|
|
~cListGarbageCollector();
|
|
void Put(cListObject *Object);
|
|
void Purge(bool Force = false);
|
|
};
|
|
|
|
extern cListGarbageCollector ListGarbageCollector;
|
|
|
|
class cListBase {
|
|
protected:
|
|
cListObject *objects, *lastObject;
|
|
int count;
|
|
mutable cStateLock stateLock;
|
|
const char *needsLocking;
|
|
bool useGarbageCollector;
|
|
cListBase(const char *NeedsLocking = NULL);
|
|
public:
|
|
virtual ~cListBase();
|
|
bool Lock(cStateKey &StateKey, bool Write = false, int TimeoutMs = 0) const;
|
|
///< Tries to get a lock on this list and returns true if successful.
|
|
///< By default a read lock is requested. Set Write to true to obtain
|
|
///< a write lock. If TimeoutMs is not zero, it waits for the given
|
|
///< number of milliseconds before giving up.
|
|
///< If you need to lock more than one list at the same time, make sure
|
|
///< you set TimeoutMs to a suitable value in all of the calls to
|
|
///< Lock(), and be prepared to handle situations where you do not get all
|
|
///< of the requested locks. In such cases you should release all the locks
|
|
///< you have obtained so far and try again. StateKey.TimedOut() tells you
|
|
///< whether the lock attempt failed due to a timeout or because the state
|
|
///< of the lock hasn't changed since the previous locking attempt.
|
|
///< To implicitly avoid deadlocks when locking more than one of the global
|
|
///< lists of VDR at the same time, make sure you always lock Timers, Channels,
|
|
///< Recordings and Schedules in this sequence.
|
|
///< You may keep pointers to objects in this list, even after releasing
|
|
///< the lock. However, you may only access such objects if you are
|
|
///< holding a proper lock again. If an object has been deleted from the list
|
|
///< while you did not hold a lock (for instance by an other thread), the
|
|
///< object will still be there, but no longer within this list (it is then
|
|
///< stored in the ListGarbageCollector). That way even if you access the object
|
|
///< after it has been deleted, you won't cause a segfault. You can call the
|
|
///< Contains() function to check whether an object you are holding a pointer
|
|
///< to is still in the list. Note that the garbage collector is purged when
|
|
///< the usual housekeeping is done.
|
|
void SetUseGarbageCollector(void) { useGarbageCollector = true; }
|
|
void SetExplicitModify(void);
|
|
///< If you have obtained a write lock on this list, and you don't want it to
|
|
///< be automatically marked as modified when the lock is released, a call to
|
|
///< this function will disable this, and you can explicitly call SetModified()
|
|
///< to have the list marked as modified.
|
|
void SetModified(void);
|
|
///< Unconditionally marks this list as modified.
|
|
void Add(cListObject *Object, cListObject *After = NULL);
|
|
void Ins(cListObject *Object, cListObject *Before = NULL);
|
|
void Del(cListObject *Object, bool DeleteObject = true);
|
|
virtual void Move(int From, int To);
|
|
void Move(cListObject *From, cListObject *To);
|
|
virtual void Clear(void);
|
|
bool Contains(const cListObject *Object) const;
|
|
///< If a pointer to an object contained in this list has been obtained while
|
|
///< holding a lock, and that lock has been released, but the pointer is kept for
|
|
///< later use (after obtaining a new lock), Contains() can be called with that
|
|
///< pointer to make sure the object it points to is still part of this list
|
|
///< (it may have been deleted or otherwise removed from the list after the lock
|
|
///< during which the pointer was initially retrieved has been released).
|
|
const cListObject *Get(int Index) const;
|
|
cListObject *Get(int Index) { return const_cast<cListObject *>(static_cast<const cListBase *>(this)->Get(Index)); }
|
|
int Count(void) const { return count; }
|
|
void Sort(void);
|
|
};
|
|
|
|
template<class T> class cList : public cListBase {
|
|
public:
|
|
cList(const char *NeedsLocking = NULL): cListBase(NeedsLocking) {}
|
|
///< Sets up a new cList of the given type T. If NeedsLocking is given, the list
|
|
///< and any of its elements may only be accessed if the caller holds a lock
|
|
///< obtained by a call to Lock() (see cListBase::Lock() for details).
|
|
///< NeedsLocking is used as both a boolean flag to enable locking, and as
|
|
///< a name to identify this list in debug output. It must be a static string
|
|
///< and should be no longer than 10 characters. The string will not be copied!
|
|
const T *Get(int Index) const { return (T *)cListBase::Get(Index); }
|
|
///< Returns the list element at the given Index, or NULL if no such element
|
|
///< exists.
|
|
const T *First(void) const { return (T *)objects; }
|
|
///< Returns the first element in this list, or NULL if the list is empty.
|
|
const T *Last(void) const { return (T *)lastObject; }
|
|
///< Returns the last element in this list, or NULL if the list is empty.
|
|
const T *Prev(const T *Object) const { return (T *)Object->cListObject::Prev(); } // need to call cListObject's members to
|
|
///< Returns the element immediately before Object in this list, or NULL
|
|
///< if Object is the first element in the list. Object must not be NULL!
|
|
const T *Next(const T *Object) const { return (T *)Object->cListObject::Next(); } // avoid ambiguities in case of a "list of lists"
|
|
///< Returns the element immediately following Object in this list, or NULL
|
|
///< if Object is the last element in the list. Object must not be NULL!
|
|
T *Get(int Index) { return const_cast<T *>(static_cast<const cList<T> *>(this)->Get(Index)); }
|
|
///< Non-const version of Get().
|
|
T *First(void) { return const_cast<T *>(static_cast<const cList<T> *>(this)->First()); }
|
|
///< Non-const version of First().
|
|
T *Last(void) { return const_cast<T *>(static_cast<const cList<T> *>(this)->Last()); }
|
|
///< Non-const version of Last().
|
|
T *Prev(const T *Object) { return const_cast<T *>(static_cast<const cList<T> *>(this)->Prev(Object)); }
|
|
///< Non-const version of Prev().
|
|
T *Next(const T *Object) { return const_cast<T *>(static_cast<const cList<T> *>(this)->Next(Object)); }
|
|
///< Non-const version of Next().
|
|
};
|
|
|
|
// The DEF_LIST_LOCK macro defines a convenience class that can be used to obtain
|
|
// a lock on a cList and make sure the lock is released when the current scope
|
|
// is left:
|
|
|
|
#define DEF_LIST_LOCK2(Class, Name) \
|
|
class c##Name##Lock { \
|
|
private: \
|
|
cStateKey stateKey; \
|
|
const c##Class *list; \
|
|
public: \
|
|
c##Name##Lock(bool Write = false) \
|
|
{ \
|
|
if (Write) \
|
|
list = c##Class::Get##Name##Write(stateKey); \
|
|
else \
|
|
list = c##Class::Get##Name##Read(stateKey); \
|
|
} \
|
|
~c##Name##Lock() { stateKey.Remove(); } \
|
|
const c##Class *Name(void) const { return list; } \
|
|
c##Class *Name(void) { return const_cast<c##Class *>(list); } \
|
|
}
|
|
#define DEF_LIST_LOCK(Class) DEF_LIST_LOCK2(Class, Class)
|
|
|
|
// The USE_LIST_LOCK macro sets up a local variable of a class defined by
|
|
// a suitable DEF_LIST_LOCK, and also a pointer to the provided list:
|
|
|
|
#define USE_LIST_LOCK_READ2(Class, Name) \
|
|
c##Name##Lock Name##Lock(false); \
|
|
const c##Class *Name __attribute__((unused)) = Name##Lock.Name();
|
|
#define USE_LIST_LOCK_READ(Class) USE_LIST_LOCK_READ2(Class, Class)
|
|
|
|
#define USE_LIST_LOCK_WRITE2(Class, Name) \
|
|
c##Name##Lock Name##Lock(true); \
|
|
c##Class *Name __attribute__((unused)) = Name##Lock.Name();
|
|
#define USE_LIST_LOCK_WRITE(Class) USE_LIST_LOCK_WRITE2(Class, Class)
|
|
|
|
template<class T> class cVector {
|
|
///< cVector may only be used for *simple* types, like int or pointers - not for class objects that allocate additional memory!
|
|
private:
|
|
mutable int allocated;
|
|
mutable int size;
|
|
mutable T *data;
|
|
cVector(const cVector &Vector) {} // don't copy...
|
|
cVector &operator=(const cVector &Vector) { return *this; } // ...or assign this!
|
|
void Realloc(int Index) const
|
|
{
|
|
if (++Index > allocated) {
|
|
data = (T *)realloc(data, Index * sizeof(T));
|
|
if (!data) {
|
|
esyslog("ERROR: out of memory - abort!");
|
|
abort();
|
|
}
|
|
for (int i = allocated; i < Index; i++)
|
|
data[i] = T(0);
|
|
allocated = Index;
|
|
}
|
|
}
|
|
public:
|
|
cVector(int Allocated = 10)
|
|
{
|
|
allocated = 0;
|
|
size = 0;
|
|
data = NULL;
|
|
Realloc(Allocated);
|
|
}
|
|
virtual ~cVector() { free(data); }
|
|
T& At(int Index) const
|
|
{
|
|
Realloc(Index);
|
|
if (Index >= size)
|
|
size = Index + 1;
|
|
return data[Index];
|
|
}
|
|
const T& operator[](int Index) const
|
|
{
|
|
return At(Index);
|
|
}
|
|
T& operator[](int Index)
|
|
{
|
|
return At(Index);
|
|
}
|
|
int IndexOf(const T &Data) // returns the index of Data, or -1 if not found
|
|
{
|
|
for (int i = 0; i < size; i++) {
|
|
if (data[i] == Data)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
int Size(void) const { return size; }
|
|
virtual void Insert(T Data, int Before = 0)
|
|
{
|
|
if (Before < size) {
|
|
Realloc(size);
|
|
memmove(&data[Before + 1], &data[Before], (size - Before) * sizeof(T));
|
|
size++;
|
|
data[Before] = Data;
|
|
}
|
|
else
|
|
Append(Data);
|
|
}
|
|
bool InsertUnique(T Data, int Before = 0)
|
|
{
|
|
if (IndexOf(Data) < 0) {
|
|
Insert(Data, Before);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
virtual void Append(T Data)
|
|
{
|
|
if (size >= allocated)
|
|
Realloc(allocated * 3 / 2); // increase size by 50%
|
|
data[size++] = Data;
|
|
}
|
|
bool AppendUnique(T Data)
|
|
{
|
|
if (IndexOf(Data) < 0) {
|
|
Append(Data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
virtual void Remove(int Index)
|
|
{
|
|
if (Index < 0)
|
|
return; // prevents out-of-bounds access
|
|
if (Index < size - 1)
|
|
memmove(&data[Index], &data[Index + 1], (size - Index) * sizeof(T));
|
|
size--;
|
|
}
|
|
bool RemoveElement(const T &Data)
|
|
{
|
|
int i = IndexOf(Data);
|
|
if (i >= 0) {
|
|
Remove(i);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
virtual void Clear(void)
|
|
{
|
|
for (int i = 0; i < size; i++)
|
|
data[i] = T(0);
|
|
size = 0;
|
|
}
|
|
void Sort(__compar_fn_t Compare)
|
|
{
|
|
qsort(data, size, sizeof(T), Compare);
|
|
}
|
|
};
|
|
|
|
inline int CompareStrings(const void *a, const void *b)
|
|
{
|
|
return strcmp(*(const char **)a, *(const char **)b);
|
|
}
|
|
|
|
inline int CompareStringsIgnoreCase(const void *a, const void *b)
|
|
{
|
|
return strcasecmp(*(const char **)a, *(const char **)b);
|
|
}
|
|
|
|
class cStringList : public cVector<char *> {
|
|
public:
|
|
cStringList(int Allocated = 10): cVector<char *>(Allocated) {}
|
|
virtual ~cStringList();
|
|
int Find(const char *s) const;
|
|
void Sort(bool IgnoreCase = false)
|
|
{
|
|
if (IgnoreCase)
|
|
cVector<char *>::Sort(CompareStringsIgnoreCase);
|
|
else
|
|
cVector<char *>::Sort(CompareStrings);
|
|
}
|
|
virtual void Clear(void);
|
|
};
|
|
|
|
class cFileNameList : public cStringList {
|
|
public:
|
|
cFileNameList(const char *Directory = NULL, bool DirsOnly = false);
|
|
bool Load(const char *Directory, bool DirsOnly = false);
|
|
};
|
|
|
|
class cHashObject : public cListObject {
|
|
friend class cHashBase;
|
|
private:
|
|
unsigned int id;
|
|
cListObject *object;
|
|
public:
|
|
cHashObject(cListObject *Object, unsigned int Id) { object = Object; id = Id; }
|
|
cListObject *Object(void) { return object; }
|
|
};
|
|
|
|
class cHashBase {
|
|
private:
|
|
cList<cHashObject> **hashTable;
|
|
int size;
|
|
unsigned int hashfn(unsigned int Id) const { return Id % size; }
|
|
protected:
|
|
cHashBase(int Size);
|
|
public:
|
|
virtual ~cHashBase();
|
|
void Add(cListObject *Object, unsigned int Id);
|
|
void Del(cListObject *Object, unsigned int Id);
|
|
void Clear(void);
|
|
cListObject *Get(unsigned int Id) const;
|
|
cList<cHashObject> *GetList(unsigned int Id) const;
|
|
};
|
|
|
|
#define HASHSIZE 512
|
|
|
|
template<class T> class cHash : public cHashBase {
|
|
public:
|
|
cHash(int Size = HASHSIZE) : cHashBase(Size) {}
|
|
T *Get(unsigned int Id) const { return (T *)cHashBase::Get(Id); }
|
|
};
|
|
|
|
#endif //__TOOLS_H
|