diff --git a/include/lgi/common/Error.h b/include/lgi/common/Error.h --- a/include/lgi/common/Error.h +++ b/include/lgi/common/Error.h @@ -1,96 +1,97 @@ // // LCancel.h // Lgi // // Created by Matthew Allen on 19/10/2017. // // #ifndef _LERROR_H_ #define _LERROR_H_ #include #ifdef POSIX #include "errno.h" #endif -LgiFunc const char *GetErrorName(int e); - // Some cross platform error symbols (by no means a complete list) enum LErrorCodes { #if defined(WINDOWS) LErrorNone = ERROR_SUCCESS, LErrorAccessDenied = ERROR_ACCESS_DENIED, LErrorNoMem = ERROR_NOT_ENOUGH_MEMORY, LErrorInvalidParam = ERROR_INVALID_PARAMETER, LErrorTooManyFiles = ERROR_NO_MORE_FILES, LErrorFileExists = ERROR_FILE_EXISTS, LErrorPathNotFound = ERROR_PATH_NOT_FOUND, #elif defined(POSIX) LErrorNone = 0, LErrorAccessDenied = EACCES, LErrorNoMem = ENOMEM, LErrorInvalidParam = EINVAL, LErrorTooManyFiles = EMFILE, LErrorFileExists = EEXIST, LErrorPathNotFound = ENOTDIR, #else #warning "Impl me." #endif }; +/// Converts an OS error code into a text string +LgiExtern LString LErrorCodeToString(uint32_t ErrorCode); + class LgiClass LError { // This error code is defined by the operating system int Code; LString Msg; public: LString::Array DevNotes; LError(int code = 0, const char *msg = NULL) { Set(code, msg); } LError &operator =(int code) { Code = code; return *this; } void Set(int code, const char *msg = NULL) { Code = code; Msg = msg; } int GetCode() { return Code; } LString GetMsg() { if (!Msg) - Msg = GetErrorName(Code); + Msg = LErrorCodeToString(Code); return Msg; } void AddNote(const char *File, int Line, const char *Fmt, ...) { char buffer[512] = {0}; va_list arg; va_start(arg, Fmt); vsprintf_s(buffer, sizeof(buffer), Fmt, arg); va_end(arg); DevNotes.New().Printf("%s:%i - %s", File, Line, buffer); } LString GetNotes() { return LString("\n").Join(DevNotes); } }; #endif /* _LERROR_H_ */ diff --git a/include/lgi/common/LgiCommon.h b/include/lgi/common/LgiCommon.h --- a/include/lgi/common/LgiCommon.h +++ b/include/lgi/common/LgiCommon.h @@ -1,420 +1,417 @@ /** \file \author Matthew Allen \date 27/3/2000 \brief Common LGI include Copyright (C) 2000-2004, Matthew Allen */ /** * \defgroup Base Foundation tools * \ingroup Lgi */ /** * \defgroup Text Text handling * \ingroup Lgi */ #ifndef _LGI_COMMON_H #define _LGI_COMMON_H #if defined LINUX #include #endif #include "lgi/common/Mem.h" #include "lgi/common/Array.h" #include "lgi/common/LgiString.h" #include "lgi/common/StringClass.h" #include "lgi/common/CurrentTime.h" #include "lgi/common/LgiUiBase.h" /// Returns the system path specified /// \ingroup Base LgiExtern LString LGetSystemPath( /// Which path to retreive LSystemPath Which, int WordSize = 0 ); /// Gets the path of the currently running executable /// \ingroup Base LgiExtern LString LGetExePath(); /// Gets the file of the currently running executable /// \ingroup Base LgiExtern LString LGetExeFile(); /// Returns the mime type of the file /// \ingroup Mime LgiExtern LString LGetFileMimeType ( /// File to find mime type for const char *File ); /// Finds a file in the applications directory or nearby /// \ingroup Base LgiExtern LString LFindFile(const char *Name); /// Returns the application associated with the mime type /// \ingroup Mime LgiExtern LString LGetAppForMimeType ( /// Type of the file to find and app for const char *Mime ); /// \return a formatted file size LgiExtern LString LFormatSize(int64_t Size); /// URL encode a string LgiExtern LString LUrlEncode(const char *s, const char *delim); /// URL decode a string LgiExtern LString LUrlDecode(const char *s); /// Gets the current user LgiExtern LString LCurrentUserName(); /// Returns an environment variable. LgiExtern LString LGetEnv(const char *Var); /// Gets the system path.. LgiExtern LString::Array LGetPath(); /// Check for a valid email string LgiExtern bool LIsValidEmail(LString Email); /// Finds an application to handle a protocol request (e.g. 'mailto', 'http' etc) LgiExtern LString LGetAppForProtocol(const char *Protocol); /// Converts a string to the native 8bit charset of the OS from utf-8 /// \ingroup Text LgiExtern LString LToNativeCp(const char *In, ssize_t InLen = -1); /// Converts a string from the native 8bit charset of the OS to utf-8 /// \ingroup Text LgiExtern LString LFromNativeCp(const char *In, ssize_t InLen = -1); LgiExtern LString LStrConvertCp ( /// Output charset const char *OutCp, /// Input buffer const void *In, /// The input data's charset const char *InCp, /// Bytes of valid data in the input ssize_t InLen = -1 ); -/// Converts an OS error code into a text string -LgiExtern LString LErrorCodeToString(uint32_t ErrorCode); - #ifdef __cplusplus extern "C" { #endif ///////////////////////////////////////////////////////////// // Externs // Codepages /// Converts a buffer of text to a different charset /// \ingroup Text /// \returns the bytes written to the location pointed to by 'Out' LgiFunc ssize_t LBufConvertCp(void *Out, const char *OutCp, ssize_t OutLen, const void *&In, const char *InCp, ssize_t &InLen); /// \brief Converts a string to a new charset /// \return A dynamically allocate, null terminated string in the new charset /// \ingroup Text LgiFunc void *LNewConvertCp ( /// Output charset const char *OutCp, /// Input buffer const void *In, /// The input data's charset const char *InCp, /// Bytes of valid data in the input ssize_t InLen = -1 ); /// Return true if Lgi support the charset /// \ingroup Text LgiFunc bool LIsCpImplemented(const char *Cp); /// Converts the ANSI code page to a charset name /// \ingroup Text LgiFunc const char *LAnsiToLgiCp(int AnsiCodePage = -1); /// Calculate the number of characters in a string /// \ingroup Text LgiFunc int LCharLen(const void *Str, const char *Cp, int Bytes = -1); /// Move a pointer along a utf-8 string by characters /// \ingroup Text LgiFunc char *LSeekUtf8 ( /// Pointer to the current character const char *Ptr, /// The number of characters to move forward or back ssize_t D, /// The start of the memory buffer if you known char *Start = 0 ); /// Return true if the string is valid utf-8 /// \ingroup Text LgiFunc bool LIsUtf8(const char *s, ssize_t len = -1); /// Returns the next token in a string, leaving the argument pointing to the end of the token /// \ingroup Text LgiFunc char *LTokStr(const char *&s); /// Formats a data size into appropriate units /// \ingroup Base LgiFunc void LFormatSize ( /// Output string char *Str, /// Output string buffer length int SLen, /// Input size in bytes int64_t Size ); /// \returns true if the path is a volume root. LgiFunc bool LIsVolumeRoot(const char *Path); /// Converts a string from URI encoding (ala %20 -> ' ') /// \returns a dynamically allocated string or NULL on error /// \ingroup Text LgiFunc char *LDecodeUri ( /// The URI const char *uri, /// The length or -1 if NULL terminated int len = -1 ); /// Converts a string to URI encoding (ala %20 -> ' ') /// \returns a dynamically allocated string or NULL on error /// \ingroup Text LgiFunc char *LEncodeUri ( /// The URI const char *uri, /// The length or -1 if NULL terminated int len = -1 ); // Path #if LGI_COCOA || defined(__GTK_H__) || defined(HAIKU) LgiExtern LString LgiArgsAppPath; #endif /// Returns the system path specified /// \ingroup Base LgiFunc bool LGetSystemPath ( /// Which path to retreive LSystemPath Which, /// The buffer to receive the path into char *Dst, /// The size of the receive buffer in bytes ssize_t DstSize ); /// \brief Recursively search for files /// \return Non zero if something was found /// \ingroup Base LgiFunc bool LRecursiveFileSearch ( /// Start search in this dir const char *Root, /// Extensions to match LArray *Ext = NULL, /// [optional] Output filenames LArray *Files = NULL, /// [optional] Output total size uint64 *Size = NULL, /// [optional] File count uint64 *Count = NULL, /// [optional] Callback for match std::function Callback = NULL, /// [optional] Cancel object LCancel *Cancel = NULL ); // Resources /// Gets the currently selected language /// \ingroup Resources LgiFunc struct LLanguage *LGetLanguageId(); // Os version functions /// Gets the current operating system and optionally it's version. /// \returns One of the defines starting with #LGI_OS_UNKNOWN in LgiDefs.h /// \ingroup Base LgiFunc int LGetOs(LArray *Ver = 0); /// Gets the current operation systems name. /// \ingroup Base LgiFunc const char *LGetOsName(); // System /// \brief Opens a file or directory. /// /// If the input is an executable then it is run. If the input file /// is a document then an appropriate application is found to open the /// file and the file is passed to that application. If the input is /// a directory then the OS's file manager is openned to browse the /// directory. /// /// \ingroup Base LgiFunc bool LExecute ( /// The file to open const char *File, /// The arguments to pass to the program const char *Arguments="", /// The directory to run in const char *Dir = 0, /// An error message LString *ErrorMsg = NULL ); /// Initializes the random number generator /// \ingroup Base LgiFunc void LRandomize(uint Seed); /// Returns a random number between 0 and Max-1 /// \ingroup Base LgiFunc uint LRand(uint Max = 0); LgiFunc bool _lgi_read_colour_config(const char *Tag, uint32_t *c); #ifndef SND_ASYNC #define SND_ASYNC 0x0001 #endif /// Plays a sound /// \ingroup Base LgiFunc bool LPlaySound ( /// File name of the sound to play const char *FileName, /// 0 or SND_ASYNC. If 0 the function blocks till the sound finishes. int Flags ); /** * \defgroup Mime Mime handling support. * \ingroup Lgi */ /// Returns the file extensions associated with the mimetype /// \ingroup Mime LgiExtern bool LGetMimeTypeExtensions ( /// The returned mime type const char *Mime, /// The extensions LArray &Ext ); inline bool LGetAppForMimeType(const char *Mime, char *AppPath, int BufSize) { LString p = LGetAppForMimeType(Mime); if (AppPath && p) strcpy_s(AppPath, BufSize, p); return p.Length() > 0; } /// Returns the all applications that can open a given mime type. /// \ingroup Mime LgiFunc bool LGetAppsForMimeType ( /// The type of files to match apps to. /// /// Two special cases exist: /// - application/email gets the default email client /// - application/browser get the default web browser const char *Mime, /// The applications that can handle the LArray &Apps, /// Limit the length of the results, i.e. stop looking after 'Limit' matches. /// -1 means return all matches. int Limit = -1 ); // Debug /// Returns true if the build is for release. /// \ingroup Base LgiFunc int LIsReleaseBuild(); #if defined WIN32 /// Registers an active x control LgiFunc bool RegisterActiveXControl(const char *Dll); enum HWBRK_TYPE { HWBRK_TYPE_CODE, HWBRK_TYPE_READWRITE, HWBRK_TYPE_WRITE, }; enum HWBRK_SIZE { HWBRK_SIZE_1, HWBRK_SIZE_2, HWBRK_SIZE_4, HWBRK_SIZE_8, }; /// Set a hardware breakpoint. LgiFunc HANDLE SetHardwareBreakpoint ( /// Use GetCurrentThread() HANDLE hThread, /// Type of breakpoint HWBRK_TYPE Type, /// Size of breakpoint HWBRK_SIZE Size, /// The pointer to the data to break on void *s ); /// Deletes a hardware breakpoint LgiFunc bool RemoveHardwareBreakpoint(HANDLE hBrk); #elif defined LINUX /// Window managers enum WindowManager { WM_Unknown, WM_Kde, WM_Gnome }; /// Returns the currently running window manager WindowManager LGetWindowManager(); #elif defined(__OBJC__) LgiFunc NSCursor *LCocoaCursor(LCursor lc); #endif #ifdef __cplusplus } #endif #endif diff --git a/include/lgi/common/Net.h b/include/lgi/common/Net.h --- a/include/lgi/common/Net.h +++ b/include/lgi/common/Net.h @@ -1,554 +1,554 @@ /** \file \author Matthew Allen \date 28/5/1998 \brief Network sockets classes Copyright (C) 1998, Matthew Allen */ #ifndef __INET_H #define __INET_H #include "lgi/common/LgiNetInc.h" #include "lgi/common/LgiInterfaces.h" #include "lgi/common/Mem.h" #include "lgi/common/Containers.h" #include "lgi/common/Stream.h" #include "lgi/common/LgiString.h" #include "lgi/common/Cancel.h" #include "lgi/common/HashTable.h" #if defined WIN32 #include "ws2ipdef.h" #elif defined POSIX #include #elif defined BEOS #include #include #else typedef int SOCKET; #endif #define DEFAULT_TIMEOUT 30 // Standard ports #define FTP_PORT 21 #define SSH_PORT 22 #define SMTP_PORT 25 #define HTTP_PORT 80 #define HTTPS_PORT 443 #define SMTP_SSL_PORT 465 #define POP3_PORT 110 #define POP3_SSL_PORT 995 #define IMAP_PORT 143 #define IMAP_SSL_PORT 993 // Parameters for passing to LSocket::SetVariant /// Turn on/off logging. Used with LSocket::SetParameter. #define LSocket_Log "Log" /// Set the progress object. Used with LSocket::SetParameter. Value = (LProgressView*)Prog #define LSocket_Progress "Progress" /// Set the size of the transfer. Used with LSocket::SetParameter. Value = (int)Size #define LSocket_TransferSize "TransferSize" /// Set the type of protocol. Used with LSocket::SetParameter. Value = (char*)Protocol #define LSocket_Protocol "Protocol" #define LSocket_SetDelete "SetDelete" // Functions LgiNetFunc bool HaveNetConnection(); LgiNetFunc bool WhatsMyIp(LAutoString &Ip); LgiExtern LString LIpToStr(uint32_t ip); LgiExtern uint32_t LIpToInt(LString str); // Convert IP as string to host order int LgiExtern uint32_t LHostnameToIp(const char *HostName); // Hostname lookup (DNS), returns IP in host order or 0 on error /// Make md5 hash LgiNetFunc void MDStringToDigest ( /// Buffer to receive md5 hash unsigned char digest[16], /// Input string char *Str, /// Length of input string or -1 for null terminated int Len = -1 ); /// Implementation of a network socket class LgiNetClass LSocket : public LSocketI, public LStream { protected: class LSocketImplPrivate *d; // Methods void Log(const char *Msg, ssize_t Ret, const char *Buf, ssize_t Len); bool CreateUdpSocket(); public: ssize_t BytesRead, BytesWritten; /// Creates the class LSocket(LStreamI *logger = 0, void *unused_param = 0); /// Destroys the class ~LSocket(); /// Gets the active cancellation object LCancel *GetCancel() override; /// Sets the active cancellation object void SetCancel(LCancel *c) override; /// Returns the operating system handle to the socket. OsSocket Handle(OsSocket Set = INVALID_SOCKET) override; OsSocket ReleaseHandle(); /// Returns true if the internal state of the class is ok bool IsOK(); /// Returns the IP address at this end of the socket. bool GetLocalIp(char *IpAddr) override; /// Returns the port at this end of the socket. int GetLocalPort() override; /// Gets the IP address at the remote end of the socket. bool GetRemoteIp(uint32_t *IpAddr); bool GetRemoteIp(char *IpAddr) override; /// Gets the IP address at the remote end of the socket. int GetRemotePort() override; /// Gets the current timeout for operations in ms int GetTimeout() override; /// Sets the current timeout for operations in ms void SetTimeout(int ms) override; /// Returns whether there is data available for reading. bool IsReadable(int TimeoutMs = 0) override; /// Returns whether there is data available for reading. bool IsWritable(int TimeoutMs = 0) override; /// Returns if the socket is ready to accept a connection bool CanAccept(int TimeoutMs = 0) override; /// Returns if the socket is set to block bool IsBlocking() override; /// Set the socket to block void IsBlocking(bool block) override; /// Get the send delay setting bool IsDelayed() override; /// Set the send delay setting void IsDelayed(bool Delay) override; /// Opens a connection. int Open ( /// The name of the remote host. const char *HostAddr, /// The port on the remote host. int Port ) override; /// Returns true if the socket is connected. bool IsOpen() override; /// Closes the connection to the remote host. int Close() override; /// Binds on a given port. bool Bind(int Port, bool reuseAddr = true); /// Binds and listens on a given port for an incomming connection. bool Listen(int Port = 0) override; /// Accepts an incomming connection and connects the socket you pass in to the remote host. bool Accept ( /// The socket to handle the connection. LSocketI *c ) override; /// \brief Sends data to the remote host. /// \return the number of bytes written or <= 0 on error. ssize_t Write ( /// Pointer to the data to write const void *Data, /// Numbers of bytes to write ssize_t Len, /// Flags to pass to send int Flags = 0 ) override; inline ssize_t Write(const LString s) { return LStreamI::Write(s); } /// \brief Reads data from the remote host. /// \return the number of bytes read or <= 0 on error. /// /// Generally the number of bytes returned is less than the buffer size. Depending on how much data /// you are expecting you will need to keep reading until you get and end of field marker or the number /// of bytes your looking for. ssize_t Read ( /// Pointer to the buffer to write output to void *Data, /// The length of the receive buffer. ssize_t Len, /// The flags to pass to recv int Flags = 0 ) override; /// Returns the last error or 0. int Error(void *Param = 0) override; const char *GetErrorString() override; /// Not supported int64 GetSize() override { return -1; } /// Not supported int64 SetSize(int64 Size) override { return -1; } /// Not supported int64 GetPos() override { return -1; } /// Not supported int64 SetPos(int64 Pos) override { return -1; } /// Gets called when the connection is disconnected void OnDisconnect() override; /// Gets called when data is received. void OnRead(char *Data, ssize_t Len) override {} /// Gets called when data is sent. void OnWrite(const char *Data, ssize_t Len) override {} /// Gets called when an error occurs. void OnError(int ErrorCode, const char *ErrorDescription) override; /// Gets called when some information is available. void OnInformation(const char *Str) override {} /// Parameter change handler. int SetParameter ( /// e.g. #LSocket_Log int Param, int Value ) { return false; } /// Get UPD mode bool GetUdp() override; /// Set UPD mode void SetUdp(bool isUdp = true) override; /// Makes the socket able to broadcast void SetBroadcast(bool isBroadcast = true); /// Read UPD packet int ReadUdp(void *Buffer, int Size, int Flags, uint32_t *Ip = 0, uint16_t *Port = 0) override; /// Write UPD packet int WriteUdp(void *Buffer, int Size, int Flags, uint32_t Ip, uint16_t Port) override; bool AddMulticastMember(uint32_t MulticastIp, uint32_t LocalInterface); bool SetMulticastInterface(uint32_t Interface); // Impl LStreamI *Clone() override { LSocket *s = new LSocket; if (s) s->SetCancel(GetCancel()); return s; } // Statics /// Enumerates the current interfaces struct Interface { LString Name; uint32_t Ip4; // Host order... uint32_t Netmask4; bool IsLoopBack() { return Ip4 == 0x7f000001; } bool IsPrivate() { uint8_t h1 = (Ip4 >> 24) & 0xff; if (h1 == 192) { uint8_t h2 = ((Ip4 >> 16) & 0xff); return h2 == 168; } else if (h1 == 10) { return true; } else if (h1 == 172) { uint8_t h2 = ((Ip4 >> 16) & 0xff); return h2 >= 16 && h2 <= 31; } return false; } bool IsLinkLocal() { uint8_t h1 = (Ip4 >> 24) & 0xff; if (h1 == 169) { uint8_t h2 = ((Ip4 >> 16) & 0xff); return h2 == 254; } return false; } }; static bool EnumInterfaces(LArray &Out); }; class LgiNetClass LSocks5Socket : public LSocket { LAutoString Proxy; int Port; LAutoString UserName; LAutoString Password; protected: bool Socks5Connected; public: LSocks5Socket(); LSocks5Socket &operator=(const LSocks5Socket &s) { Proxy.Reset(NewStr(s.Proxy)); UserName.Reset(NewStr(s.UserName)); Password.Reset(NewStr(s.Password)); Port = s.Port; return *this; } // Connection void SetProxy(char *proxy, int port, char *username, char *password); void SetProxy(const LSocks5Socket *s); int Open(const char *HostAddr, int port); // Server bool Listen(int Port) { return false; } }; /// Uri parser class LgiNetClass LUri { public: LString sProtocol; LString sUser; LString sPass; LString sHost; int Port; LString sPath; LString sAnchor; /// Parser for URI's. LUri ( /// Optional URI to start parsing const char *uri = 0 ); ~LUri(); bool IsProtocol(const char *p) { return sProtocol.Equals(p); } bool IsHttp() { return sProtocol.Equals("http") || sProtocol.Equals("https"); } bool IsFile() { return sProtocol.Equals("file"); } void SetFile(LString Path) { Empty(); sProtocol = "file"; sPath = Path; } const char *LocalPath(); operator bool(); /// Parse a URI into it's sub fields... bool Set(const char *uri); /// Re-constructs the URI LString ToString(); /// Empty this object... void Empty(); /// URL encode LString EncodeStr ( /// The string to encode const char *s, /// [Optional] Any extra characters you want encoded const char *ExtraCharsToEncode = 0 ); /// URL decode LString DecodeStr(const char *s); /// Separate args into map typedef LHashTbl,LString> StrMap; StrMap Params(); LUri &operator =(const LUri &u); LUri &operator =(const char *s) { Set(s); return *this; } LUri &operator +=(const char *s); }; /// Proxy settings lookup class LgiNetClass LProxyUri : public LUri { public: LProxyUri(); }; #define MAX_UDP_SIZE 512 class LUdpListener : public LSocket { LStream *Log; LString Context; public: bool Status = false; /* If this isn't working on Linux, most likely it's a firewall issue. For instance if you are running 'ufw' as your firewall you could allow packets through with: sudo ufw allow ${port}/udp */ LUdpListener(LArray interface_ips, uint32_t mc_ip, uint16_t port, LStream *log = NULL) : Log(log) { SetUdp(true); struct sockaddr_in addr; ZeroObj(addr); addr.sin_family = AF_INET; addr.sin_port = htons(port); #ifdef WINDOWS addr.sin_addr.S_un.S_addr = INADDR_ANY; #elif defined(MAC) addr.sin_addr.s_addr = htonl(mc_ip); #else addr.sin_addr.s_addr = INADDR_ANY; #endif Context = "LUdpListener.bind"; Status = bind(Handle(), (struct sockaddr*)&addr, sizeof(addr)) == 0; if (!Status) { #ifdef WIN32 int err = WSAGetLastError(); #else int err = errno; #endif - OnError(err, GetErrorName(err)); + OnError(err, LErrorCodeToString(err)); LgiTrace("Error: Bind on %s:%i\n", LIpToStr(ntohl(addr.sin_addr.s_addr)).Get(), port); } if (mc_ip) { Context = "LUdpListener.AddMulticastMember"; for (auto ip: interface_ips) AddMulticastMember(mc_ip, ip); } } bool ReadPacket(LString &d, uint32_t &Ip, uint16_t &Port) { if (!IsReadable(10)) return false; char Data[MAX_UDP_SIZE]; int Rd = ReadUdp(Data, sizeof(Data), 0, &Ip, &Port); if (Rd <= 0) return false; d.Set(Data, Rd); return true; } void OnError(int ErrorCode, const char *ErrorDescription) { LString s; if (Context) s.Printf("Error: %s - %i, %s\n", Context.Get(), ErrorCode, ErrorDescription); else s.Printf("Error: %i, %s\n", ErrorCode, ErrorDescription); if (Log) Log->Print("%s", s.Get()); else LgiTrace("%s", s.Get()); } }; class LUdpBroadcast : public LSocket { // LArray Intf; uint32_t SelectIf; public: LUdpBroadcast(uint32_t selectIf) : SelectIf(selectIf) { SetBroadcast(); SetUdp(true); // EnumInterfaces(Intf); } bool BroadcastPacket(LString Data, uint32_t Ip, uint16_t Port) { return BroadcastPacket(Data.Get(), Data.Length(), Ip, Port); } bool BroadcastPacket(void *Ptr, size_t Size, uint32_t Ip, uint16_t Port) { if (Size > MAX_UDP_SIZE) return false; if (SelectIf) { struct in_addr addr; addr.s_addr = htonl(SelectIf); auto r = setsockopt(Handle(), IPPROTO_IP, IP_MULTICAST_IF, (char*)&addr, sizeof(addr)); if (r) LgiTrace("%s:%i - set IP_MULTICAST_IF for '%s' failed: %i\n", _FL, LIpToStr(SelectIf).Get(), r); SelectIf = 0; } uint32_t BroadcastIp = Ip; #if 0 LgiTrace("Broadcast %i.%i.%i.%i\n", (BroadcastIp >> 24) & 0xff, (BroadcastIp >> 16) & 0xff, (BroadcastIp >> 8) & 0xff, (BroadcastIp) & 0xff); #endif int wr = WriteUdp(Ptr, (int)Size, 0, BroadcastIp, Port); return wr == Size; } }; #endif diff --git a/src/haiku/File.cpp b/src/haiku/File.cpp --- a/src/haiku/File.cpp +++ b/src/haiku/File.cpp @@ -1,1770 +1,1513 @@ /*hdr ** FILE: File.cpp ** AUTHOR: Matthew Allen ** DATE: 8/10/2000 ** DESCRIPTION: The new file subsystem ** ** Copyright (C) 2000, Matthew Allen ** fret@memecode.com */ /****************************** Includes **********************************/ #include #include #include #include #include #include #include #include #include #include "Path.h" #include "lgi/common/LgiDefs.h" #include "lgi/common/File.h" #include "lgi/common/Containers.h" #include "lgi/common/Token.h" #include "lgi/common/Gdc2.h" #include "lgi/common/LgiCommon.h" #include "lgi/common/LgiString.h" #include "lgi/common/DateTime.h" -/****************************** Defines ***********************************/ - -// #define FILEDEBUG - -#define FLOPPY_360K 0x0001 -#define FLOPPY_720K 0x0002 -#define FLOPPY_1_2M 0x0004 -#define FLOPPY_1_4M 0x0008 -#define FLOPPY_5_25 (FLOPPY_360K | FLOPPY_1_2M) -#define FLOPPY_3_5 (FLOPPY_720K | FLOPPY_1_4M) - /****************************** Globals ***********************************/ - LString LFile::Path::Sep(DIR_STR); -struct ErrorCodeType -{ - const char *Name; - int Code; - const char *Desc; -} -ErrorCodes[] = -{ - #if defined(WIN32) - - {"EPERM", 1, "Not owner"}, - {"ENOENT", 2, "No such file"}, - {"ESRCH", 3, "No such process"}, - {"EINTR", 4, "Interrupted system"}, - {"EIO", 5, "I/O error"}, - {"ENXIO", 6, "No such device"}, - {"E2BIG", 7, "Argument list too long"}, - {"ENOEXEC", 8, "Exec format error"}, - {"EBADF", 9, "Bad file number"}, - {"ECHILD", 10, "No children"}, - {"EAGAIN", 11, "No more processes"}, - {"ENOMEM", 12, "Not enough core"}, - {"EACCES", 13, "Permission denied"}, - {"EFAULT", 14, "Bad address"}, - {"ENOTBLK", 15, "Block device required"}, - {"EBUSY", 16, "Mount device busy"}, - {"EEXIST", 17, "File exists"}, - {"EXDEV", 18, "Cross-device link"}, - {"ENODEV", 19, "No such device"}, - {"ENOTDIR", 20, "Not a directory"}, - {"EISDIR", 21, "Is a directory"}, - {"EINVAL", 22, "Invalid argument"}, - {"ENFILE", 23, "File table overflow"}, - {"EMFILE", 24, "Too many open file"}, - {"ENOTTY", 25, "Not a typewriter"}, - {"ETXTBSY", 26, "Text file busy"}, - {"EFBIG", 27, "File too large"}, - {"ENOSPC", 28, "No space left on"}, - {"ESPIPE", 29, "Illegal seek"}, - {"EROFS", 30, "Read-only file system"}, - {"EMLINK", 31, "Too many links"}, - {"EPIPE", 32, "Broken pipe"}, - {"EWOULDBLOCK", 35, "Operation would block"}, - {"EINPROGRESS", 36, "Operation now in progress"}, - {"EALREADY", 37, "Operation already in progress"}, - {"ENOTSOCK", 38, "Socket operation on"}, - {"EDESTADDRREQ", 39, "Destination address required"}, - {"EMSGSIZE", 40, "Message too long"}, - {"EPROTOTYPE", 41, "Protocol wrong type"}, - {"ENOPROTOOPT", 42, "Protocol not available"}, - {"EPROTONOSUPPORT", 43, "Protocol not supported"}, - {"ESOCKTNOSUPPORT", 44, "Socket type not supported"}, - {"EOPNOTSUPP", 45, "Operation not supported"}, - {"EPFNOSUPPORT", 46, "Protocol family not supported"}, - {"EAFNOSUPPORT", 47, "Address family not supported"}, - {"EADDRINUSE", 48, "Address already in use"}, - {"EADDRNOTAVAIL", 49, "Can't assign requested address"}, - {"ENETDOWN", 50, "Network is down"}, - {"ENETUNREACH", 51, "Network is unreachable"}, - {"ENETRESET", 52, "Network dropped connection"}, - {"ECONNABORTED", 53, "Software caused connection"}, - {"ECONNRESET", 54, "Connection reset by peer"}, - {"ENOBUFS", 55, "No buffer space available"}, - {"EISCONN", 56, "Socket is already connected"}, - {"ENOTCONN", 57, "Socket is not connected" }, - {"ESHUTDOWN", 58, "Can't send after shutdown"}, - {"ETOOMANYREFS", 59, "Too many references"}, - {"ETIMEDOUT", 60, "Connection timed out"}, - {"ECONNREFUSED", 61, "Connection refused"}, - {"ELOOP", 62, "Too many levels of nesting"}, - {"ENAMETOOLONG", 63, "File name too long" }, - {"EHOSTDOWN", 64, "Host is down"}, - {"EHOSTUNREACH", 65, "No route to host"}, - {"ENOTEMPTY", 66, "Directory not empty"}, - {"EPROCLIM", 67, "Too many processes"}, - {"EUSERS", 68, "Too many users"}, - {"EDQUOT", 69, "Disc quota exceeded"}, - {"ESTALE", 70, "Stale NFS file handle"}, - {"EREMOTE", 71, "Too many levels of remote in the path"}, - {"ENOSTR", 72, "Device is not a stream"}, - {"ETIME", 73, "Timer expired"}, - {"ENOSR", 74, "Out of streams resources"}, - {"ENOMSG", 75, "No message"}, - {"EBADMSG", 76, "Trying to read unreadable message"}, - {"EIDRM", 77, "Identifier removed"}, - {"EDEADLK", 78, "Deadlock condition"}, - {"ENOLCK", 79, "No record locks available"}, - {"ENONET", 80, "Machine is not on network"}, - {"ERREMOTE", 81, "Object is remote"}, - {"ENOLINK", 82, "The link has been severed"}, - {"EADV", 83, "ADVERTISE error"}, - {"ESRMNT", 84, "SRMOUNT error"}, - {"ECOMM", 85, "Communication error"}, - {"EPROTO", 86, "Protocol error"}, - {"EMULTIHOP", 87, "Multihop attempted"}, - {"EDOTDOT", 88, "Cross mount point"}, - {"EREMCHG", 89, "Remote address change"}, - - #elif defined(LINUX) || defined(__GTK_H__) - - {"EPERM", EPERM, "Operation not permitted"}, - {"ENOENT", ENOENT, "No such file or directory"}, - {"ESRCH", ESRCH, "No such process"}, - {"EINTR", EINTR, "Interrupted system call"}, - {"EIO", EIO, "I/O error"}, - {"ENXIO", ENXIO, "No such device or address"}, - {"E2BIG", E2BIG, "Argument list too long"}, - {"ENOEXEC", ENOEXEC, "Exec format error"}, - {"EBADF", EBADF, "Bad file number"}, - {"ECHILD", ECHILD, "No child processes"}, - {"EAGAIN", EAGAIN, "Try again"}, - {"ENOMEM", ENOMEM, "Out of memory"}, - {"EACCES", EACCES, "Permission denied"}, - {"EFAULT", EFAULT, "Bad address"}, - {"ENOTBLK", ENOTBLK, "Block device required"}, - {"EBUSY", EBUSY, "Device or resource busy"}, - {"EEXIST", EEXIST, "File exists"}, - {"EXDEV", EXDEV, "Cross-device link"}, - {"ENODEV", ENODEV, "No such device"}, - {"ENOTDIR", ENOTDIR, "Not a directory"}, - {"EISDIR", EISDIR, "Is a directory"}, - {"EINVAL", EINVAL, "Invalid argument"}, - {"ENFILE", ENFILE, "File table overflow"}, - {"EMFILE", EMFILE, "Too many open files"}, - {"ENOTTY", ENOTTY, "Not a typewriter"}, - {"ETXTBSY", ETXTBSY, "Text file busy"}, - {"EFBIG", EFBIG, "File too large"}, - {"ENOSPC", ENOSPC, "No space left on device"}, - {"ESPIPE", ESPIPE, "Illegal seek"}, - {"EROFS", EROFS, "Read-only file system"}, - {"EMLINK", EMLINK, "Too many links"}, - {"EPIPE", EPIPE, "Broken pipe"}, - {"EDOM", EDOM, "Math argument out of domain of func"}, - {"ERANGE", ERANGE, "Math result not representable"}, - {"EDEADLK", EDEADLK, "Resource deadlock would occur"}, - {"ENAMETOOLONG", ENAMETOOLONG, "File name too long"}, - {"ENOLCK", ENOLCK, "No record locks available"}, - {"ENOSYS", ENOSYS, "Function not implemented"}, - {"ENOTEMPTY", ENOTEMPTY, "Directory not empty"}, - {"ELOOP", ELOOP, "Too many symbolic links encountered"}, - {"EWOULDBLOCK", EWOULDBLOCK, "Operation would block"}, - {"ENOMSG", ENOMSG, "No message of desired type"}, - {"EIDRM", EIDRM, "Identifier removed"}, - {"EREMOTE", EREMOTE, "Object is remote"}, - {"ENOLINK", ENOLINK, "Link has been severed"}, - {"ENOSTR", ENOSTR, "Device not a stream"}, - {"ENODATA", ENODATA, "No data available"}, - {"ETIME", ETIME, "Timer expired"}, - {"ENOSR", ENOSR, "Out of streams resources"}, - {"EPROTO", EPROTO, "Protocol error"}, - {"EMULTIHOP", EMULTIHOP, "Multihop attempted"}, - {"EBADMSG", EBADMSG, "Not a data message"}, - {"EOVERFLOW", EOVERFLOW, "Value too large for defined data type"}, - {"EILSEQ", EILSEQ, "Illegal byte sequence"}, - {"EUSERS", EUSERS, "Too many users"}, - {"ENOTSOCK", ENOTSOCK, "Socket operation on non-socket"}, - {"EDESTADDRREQ", EDESTADDRREQ, "Destination address required"}, - {"EMSGSIZE", EMSGSIZE, "Message too long"}, - {"EPROTOTYPE", EPROTOTYPE, "Protocol wrong type for socket"}, - {"ENOPROTOOPT", ENOPROTOOPT, "Protocol not available"}, - {"EPROTONOSUPPORT", EPROTONOSUPPORT, "Protocol not supported"}, - {"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, "Socket type not supported"}, - {"EOPNOTSUPP", EOPNOTSUPP, "Operation not supported on transport endpoint"}, - {"EPFNOSUPPORT", EPFNOSUPPORT, "Protocol family not supported"}, - {"EAFNOSUPPORT", EAFNOSUPPORT, "Address family not supported by protocol"}, - {"EADDRINUSE", EADDRINUSE, "Address already in use"}, - {"EADDRNOTAVAIL", EADDRNOTAVAIL, "Cannot assign requested address"}, - {"ENETDOWN", ENETDOWN, "Network is down"}, - {"ENETUNREACH", ENETUNREACH, "Network is unreachable"}, - {"ENETRESET", ENETRESET, "Network dropped connection because of reset"}, - {"ECONNABORTED", ECONNABORTED, "Software caused connection abort"}, - {"ECONNRESET", ECONNRESET, "Connection reset by peer"}, - {"ENOBUFS", ENOBUFS, "No buffer space available"}, - {"EISCONN", EISCONN, "Transport endpoint is already connected"}, - {"ENOTCONN", ENOTCONN, "Transport endpoint is not connected"}, - {"ESHUTDOWN", ESHUTDOWN, "Cannot send after transport endpoint shutdown"}, - {"ETOOMANYREFS", ETOOMANYREFS, "Too many references: cannot splice"}, - {"ETIMEDOUT", ETIMEDOUT, "Connection timed out"}, - {"ECONNREFUSED", ECONNREFUSED, "Connection refused"}, - {"EHOSTDOWN", EHOSTDOWN, "Host is down"}, - {"EHOSTUNREACH", EHOSTUNREACH, "No route to host"}, - {"EALREADY", EALREADY, "Operation already in progress"}, - {"EINPROGRESS", EINPROGRESS, "Operation now in progress"}, - {"ESTALE", ESTALE, "Stale NFS file handle"}, - - #ifndef __GTK_H__ - {"EDQUOT", EDQUOT, "Quota exceeded"}, - {"ENOMEDIUM", ENOMEDIUM, "No medium found"}, - {"EMEDIUMTYPE", EMEDIUMTYPE, "Wrong medium type"}, - {"EUCLEAN", EUCLEAN, "Structure needs cleaning"}, - {"ENOTNAM", ENOTNAM, "Not a XENIX named type file"}, - {"ENAVAIL", ENAVAIL, "No XENIX semaphores available"}, - {"EISNAM", EISNAM, "Is a named type file"}, - {"EREMOTEIO", EREMOTEIO, "Remote I/O error"}, - {"ERESTART", ERESTART, "Interrupted system call should be restarted"}, - {"ESTRPIPE", ESTRPIPE, "Streams pipe error"}, - {"ECOMM", ECOMM, "Communication error on send"}, - {"EDOTDOT", EDOTDOT, "RFS specific error"}, - {"ENOTUNIQ", ENOTUNIQ, "Name not unique on network"}, - {"EBADFD", EBADFD, "File descriptor in bad state"}, - {"EREMCHG", EREMCHG, "Remote address changed"}, - {"ELIBACC", ELIBACC, "Can not access a needed shared library"}, - {"ELIBBAD", ELIBBAD, "Accessing a corrupted shared library"}, - {"ELIBSCN", ELIBSCN, ".lib section in a.out corrupted"}, - {"ELIBMAX", ELIBMAX, "Attempting to link in too many shared libraries"}, - {"ELIBEXEC", ELIBEXEC, "Cannot exec a shared library directly"}, - {"ECHRNG", ECHRNG, "Channel number out of range"}, - {"EL2NSYNC", EL2NSYNC, "Level 2 not synchronized"}, - {"EL3HLT", EL3HLT, "Level 3 halted"}, - {"EL3RST", EL3RST, "Level 3 reset"}, - {"ELNRNG", ELNRNG, "Link number out of range"}, - {"EUNATCH", EUNATCH, "Protocol driver not attached"}, - {"ENOCSI", ENOCSI, "No CSI structure available"}, - {"EL2HLT", EL2HLT, "Level 2 halted"}, - {"EBADE", EBADE, "Invalid exchange"}, - {"EBADR", EBADR, "Invalid request descriptor"}, - {"EXFULL", EXFULL, "Exchange full"}, - {"ENOANO", ENOANO, "No anode"}, - {"EBADRQC", EBADRQC, "Invalid request code"}, - {"EBADSLT", EBADSLT, "Invalid slot"}, - {"EBFONT", EBFONT, "Bad font file format"}, - {"EADV", EADV, "Advertise error"}, - {"ESRMNT", ESRMNT, "Srmount error"}, - {"ENONET", ENONET, "Machine is not on the network"}, - {"ENOPKG", ENOPKG, "Package not installed"}, - #endif - - #endif - - {"NONE", 0, "No error"}, -}; - -const char *GetErrorName(int e) -{ - for (ErrorCodeType *c=ErrorCodes; c->Code; c++) - { - if (e == c->Code) - { - return c->Name; - } - } - - static char s[32]; - sprintf(s, "Unknown(%i)", e); - return s; -} - -const char *GetErrorDesc(int e) -{ - for (ErrorCodeType *c=ErrorCodes; c->Code; c++) - { - if (e == c->Code) - return c->Desc; - } - - return 0; -} - /****************************** Helper Functions **************************/ char *LReadTextFile(const char *File) { char *s = 0; LFile f; if (File && f.Open(File, O_READ)) { int Len = f.GetSize(); s = new char[Len+1]; if (s) { int Read = f.Read(s, Len); s[Read] = 0; } } return s; } int64 LFileSize(const char *FileName) { struct stat s; if (FileName && stat(FileName, &s) == 0) { return s.st_size; } return 0; } bool LDirExists(const char *FileName, char *CorrectCase) { bool Status = false; if (FileName) { struct stat s; // Check for exact match... int r = lstat(FileName, &s); if (r == 0) { Status = S_ISDIR(s.st_mode) || S_ISLNK(s.st_mode); // printf("DirStatus(%s) lstat = %i, %i\n", FileName, Status, s.st_mode); } else { r = stat(FileName, &s); if (r == 0) { Status = S_ISDIR(s.st_mode) || S_ISLNK(s.st_mode); // printf("DirStatus(%s) stat ok = %i, %i\n", FileName, Status, s.st_mode); } else { // printf("DirStatus(%s) lstat and stat failed, r=%i, errno=%i\n", FileName, r, errno); } } } return Status; } bool LFileExists(const char *FileName, char *CorrectCase) { bool Status = false; if (FileName) { struct stat s; // Check for exact match... if (stat(FileName, &s) == 0) { Status = !S_ISDIR(s.st_mode); } else if (CorrectCase) { // Look for altenate case by enumerating the directory char d[256]; strcpy(d, FileName); char *e = strrchr(d, DIR_CHAR); if (e) { *e++ = 0; DIR *Dir = opendir(d); if (Dir) { dirent *De; while ((De = readdir(Dir))) { if (stricmp(De->d_name, e) == 0) { try { // Tell the calling program the actual case of the file... e = (char*) strrchr(FileName, DIR_CHAR); // If this crashes because the argument is read only then we get caught by the try catch strcpy(e+1, De->d_name); // It worked! Status = true; } catch (...) { // It didn't work :( #ifdef _DEBUG printf("%s,%i - LFileExists(%s) found an alternate case version but couldn't return it to the caller.\n", __FILE__, __LINE__, FileName); #endif } break; } } closedir(Dir); } } } } return Status; } bool LResolveShortcut(const char *LinkFile, char *Path, ssize_t Len) { if (!LinkFile || !Path || Len < 1) return false; BEntry e(LinkFile); auto r = e.InitCheck(); if (r != B_OK) { printf("%s:%i - LResolveShortcut: %i\n", _FL, r); return false; } if (!e.IsSymLink()) { return false; } r = e.SetTo(LinkFile, true); if (r != B_OK) { printf("%s:%i - LResolveShortcut: %i\n", _FL, r); return false; } BPath p; r = e.GetPath(&p); if (r != B_OK) { printf("%s:%i - LResolveShortcut: %i\n", _FL, r); return false; } strcpy_s(Path, Len, p.Path()); return true; } void WriteStr(LFile &f, const char *s) { uint32_t Len = (s) ? strlen(s) : 0; f << Len; if (Len > 0) { f.Write(s, Len); } } char *ReadStr(LFile &f DeclDebugArgs) { char *s = 0; // read the strings length... uint32_t Len; f >> Len; if (Len > 0) { // 16mb sanity check.... anything over this // is _probably_ an error if (Len >= (16 << 20)) { // LAssert(0); return 0; } // allocate the memory buffer #if defined(_DEBUG) && defined MEMORY_DEBUG s = new(_file, _line) char[Len+1]; #else s = new char[Len+1]; #endif if (s) { // read the bytes from disk f.Read(s, Len); s[Len] = 0; } else { // memory allocation error, skip the data // on disk so the caller is where they think // they are in the file. f.Seek(Len, SEEK_CUR); } } return s; } ssize_t SizeofStr(const char *s) { return sizeof(ulong) + ((s) ? strlen(s) : 0); } bool LGetDriveInfo ( char *Path, uint64 *Free, uint64 *Size, uint64 *Available ) { bool Status = false; if (Path) { struct stat s; if (lstat(Path, &s) == 0) { // printf("LGetDriveInfo dev=%i\n", s.st_dev); } } return Status; } ///////////////////////////////////////////////////////////////////////// #include #include struct LVolumePriv { int64 _Size, _Free; int _Type, _Flags; LSystemPath SysPath; LString _Name, _Path; List _Sub; List::I _It; void Init() { SysPath = LSP_ROOT; _Type = VT_NONE; _Flags = 0; _Size = 0; _Free = 0; } LVolumePriv(const char *path) : _It(_Sub.end()) { Init(); _Path = path; _Name = LGetLeaf(path); _Type = VT_FOLDER; } LVolumePriv(LSystemPath sys, const char *name) : _It(_Sub.end()) { Init(); SysPath = sys; if (SysPath == LSP_ROOT) _Path = "/"; else _Path = LGetSystemPath(SysPath); if (_Path) { _Name = name; _Type = sys == LSP_DESKTOP ? VT_DESKTOP : VT_FOLDER; } } ~LVolumePriv() { _Sub.DeleteObjects(); } LVolume *First() { if (SysPath == LSP_DESKTOP && !_Sub.Length()) { // Get various shortcuts to points of interest LVolume *v = new LVolume(LSP_ROOT, "Root"); if (v) _Sub.Insert(v); struct passwd *pw = getpwuid(getuid()); if (pw) { v = new LVolume(LSP_HOME, "Home"); if (v) _Sub.Insert(v); } // Get mount list // this is just a hack at this stage to establish some base // functionality. I would appreciate someone telling me how // to do this properly. Till then... LFile f; auto fstab = "/etc/fstab"; if (LFileExists(fstab) && f.Open(fstab, O_READ)) { auto Buf= f.Read(); f.Close(); auto Lines = Buf.SplitDelimit("\r\n"); for (auto ln : Lines) { auto M = ln.Strip().SplitDelimit(" \t"); if (M[0](0) != '#' && M.Length() > 2) { auto &Device = M[0]; auto &Mount = M[1]; auto &FileSys = M[2]; if ( (Device.Find("/dev/") == 0 || Mount.Find("/mnt/") == 0) && Device.Lower().Find("/by-uuid/") < 0 && Mount.Length() > 1 && !FileSys.Equals("swap") ) { v = new LVolume(0); if (v) { char *MountName = strrchr(Mount, '/'); v->d->_Name = (MountName ? MountName + 1 : Mount.Get()); v->d->_Path = Mount; v->d->_Type = VT_HARDDISK; char *Device = M[0]; // char *FileSys = M[2]; if (stristr(Device, "fd")) { v->d->_Type = VT_FLOPPY; } else if (stristr(Device, "cdrom")) { v->d->_Type = VT_CDROM; } _Sub.Insert(v); } } } } } LSystemPath p[] = {LSP_USER_DOCUMENTS, LSP_USER_MUSIC, LSP_USER_VIDEO, LSP_USER_DOWNLOADS, LSP_USER_PICTURES}; for (int i=0; id->_Path = Path; v->d->_Name = *Parts.rbegin(); v->d->_Type = VT_FOLDER; _Sub.Insert(v); } } } _It = _Sub.begin(); return *_It; } LVolume *Next() { return *(++_It); } }; LVolume::LVolume(const char *Path) { d = new LVolumePriv(Path); } LVolume::LVolume(LSystemPath SysPath, const char *Name) { d = new LVolumePriv(SysPath, Name); } LVolume::~LVolume() { DeleteObj(d); } const char *LVolume::Name() const { return d->_Name; } const char *LVolume::Path() const { return d->_Path; } int LVolume::Type() const { return d->_Type; } int LVolume::Flags() const { return d->_Flags; } uint64 LVolume::Size() const { return d->_Size; } uint64 LVolume::Free() const { return d->_Free; } LSurface *LVolume::Icon() const { return NULL; } bool LVolume::IsMounted() const { return true; } bool LVolume::SetMounted(bool Mount) { return Mount; } LVolume *LVolume::First() { return d->First(); } LVolume *LVolume::Next() { return d->Next(); } void LVolume::Insert(LAutoPtr v) { d->_Sub.Insert(v.Release()); } LDirectory *LVolume::GetContents() { LDirectory *Dir = 0; if (d->_Path) { Dir = new LDirectory; if (Dir && !Dir->First(d->_Path)) DeleteObj(Dir); } return Dir; } /////////////////////////////////////////////////////////////////////////////// LFileSystem *LFileSystem::Instance = 0; LFileSystem::LFileSystem() { Instance = this; Root = 0; } LFileSystem::~LFileSystem() { DeleteObj(Root); } void LFileSystem::OnDeviceChange(char *Reserved) { } LVolume *LFileSystem::GetRootVolume() { if (!Root) Root = new LVolume(LSP_DESKTOP, "Desktop"); return Root; } bool LFileSystem::Copy(const char *From, const char *To, LError *Status, CopyFileCallback Callback, void *Token) { LArray Buf; if (Status) *Status = 0; if (Buf.Length(2 << 20)) { LFile In, Out; if (!In.Open(From, O_READ)) { if (Status) *Status = In.GetError(); return false; } if (!Out.Open(To, O_WRITE)) { if (Status) *Status = Out.GetError(); return false; } int64 Size = In.GetSize(), Done = 0; for (int64 i=0; i 0) { int w = Out.Write(&Buf[0], r); if (w <= 0) break; r -= w; Done += w; if (Callback) Callback(Token, Done, Size); } if (r > 0) break; } return Done == Size; } return false; } bool LFileSystem::Delete(LArray &Files, LArray *Status, bool ToTrash) { bool Error = false; if (ToTrash) { char p[MAX_PATH_LEN]; if (LGetSystemPath(LSP_TRASH, p, sizeof(p))) { for (int i=0; i f; f.Add(FileName); return Delete(f, 0, ToTrash); } return false; } bool LFileSystem::CreateFolder(const char *PathName, bool CreateParentTree, LError *ErrorCode) { int r = mkdir(PathName, S_IRWXU | S_IXGRP | S_IXOTH); if (r) { if (ErrorCode) *ErrorCode = errno; if (CreateParentTree) { LFile::Path p(PathName); LString dir = DIR_STR; for (size_t i=1; i<=p.Length(); i++) { LFile::Path Par(dir + dir.Join(p.Slice(0, i))); if (!Par.Exists()) { const char *ParDir = Par; r = mkdir(ParDir, S_IRWXU | S_IXGRP | S_IXOTH); if (r) { if (ErrorCode) *ErrorCode = errno; break; } LgiTrace("%s:%i - Created '%s'\n", _FL, Par.GetFull().Get()); } } if (p.Exists()) return true; } LgiTrace("%s:%i - mkdir('%s') failed with %i, errno=%i\n", _FL, PathName, r, errno); } return r == 0; } bool LFileSystem::RemoveFolder(const char *PathName, bool Recurse) { if (Recurse) { LDirectory *Dir = new LDirectory; if (Dir && Dir->First(PathName)) { do { char Str[256]; Dir->Path(Str, sizeof(Str)); if (Dir->IsDir()) { RemoveFolder(Str, Recurse); } else { Delete(Str, false); } } while (Dir->Next()); } DeleteObj(Dir); } return rmdir(PathName) == 0; } bool LFileSystem::Move(const char *OldName, const char *NewName, LError *Err) { if (rename(OldName, NewName)) { printf("%s:%i - rename failed, error: %s(%i)\n", - _FL, GetErrorName(errno), errno); + _FL, LErrorCodeToString(errno), errno); return false; } return true; } /* bool Match(char *Name, char *Mask) { strupr(Name); strupr(Mask); while (*Name && *Mask) { if (*Mask == '*') { if (*Name == *(Mask+1)) { Mask++; } else { Name++; } } else if (*Mask == '?' || *Mask == *Name) { Mask++; Name++; } else { return false; } } while (*Mask && ((*Mask == '*') || (*Mask == '.'))) Mask++; return (*Name == 0 && *Mask == 0); } */ short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int LeapYear(int year) { if (year & 3) { return 0; } if ((year % 100 == 0) && !(year % 400 == 0)) { return 0; } return 1; } ///////////////////////////////////////////////////////////////////////////////// bool LDirectory::ConvertToTime(char *Str, int SLen, uint64 Time) const { time_t k = Time; struct tm *t = localtime(&k); if (t) { strftime(Str, SLen, "%I:%M:%S", t); return true; } return false; } bool LDirectory::ConvertToDate(char *Str, int SLen, uint64 Time) const { time_t k = Time; struct tm *t = localtime(&k); if (t) { strftime(Str, SLen, "%d/%m/%y", t); return true; } return false; } ///////////////////////////////////////////////////////////////////////////////// //////////////////////////// Directory ////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// struct LDirectoryPriv { char BasePath[MAX_PATH_LEN]; - DIR *Dir; - struct dirent *De; + char *BaseEnd = NULL; + DIR *Dir = NULL; + struct dirent *De = NULL; struct stat Stat; LString Pattern; LDirectoryPriv() { - Dir = 0; - De = 0; BasePath[0] = 0; } bool Ignore() { return De && ( strcmp(De->d_name, ".") == 0 || strcmp(De->d_name, "..") == 0 || ( Pattern && !MatchStr(Pattern, De->d_name) ) ); } }; LDirectory::LDirectory() { d = new LDirectoryPriv; } LDirectory::~LDirectory() { Close(); DeleteObj(d); } LDirectory *LDirectory::Clone() { return new LDirectory; } int LDirectory::First(const char *Name, const char *Pattern) { Close(); if (!Name) return 0; - strcpy(d->BasePath, Name); + strcpy_s(d->BasePath, sizeof(d->BasePath), Name); if (!Pattern || stricmp(Pattern, LGI_ALL_FILES) == 0) { struct stat S; if (lstat(Name, &S) == 0) { if (S_ISREG(S.st_mode)) { char *Dir = strrchr(d->BasePath, DIR_CHAR); if (Dir) { *Dir++ = 0; d->Pattern = Dir; } } } } else { d->Pattern = Pattern; } d->Dir = opendir(d->BasePath); if (d->Dir) { d->De = readdir(d->Dir); if (d->De) { char s[MaxPathLen]; LMakePath(s, sizeof(s), d->BasePath, GetName()); lstat(s, &d->Stat); if (d->Ignore() && !Next()) return false; } } return d->Dir != NULL && d->De != NULL; } int LDirectory::Next() { int Status = false; while (d->Dir && d->De) { if ((d->De = readdir(d->Dir))) { char s[MaxPathLen]; LMakePath(s, sizeof(s), d->BasePath, GetName()); lstat(s, &d->Stat); if (!d->Ignore()) { Status = true; break; } } } return Status; } int LDirectory::Close() { if (d->Dir) { closedir(d->Dir); - d->Dir = 0; + d->Dir = NULL; } - d->De = 0; + d->De = NULL; + d->BaseEnd = NULL; + d->BasePath[0] = 0; return true; } const char *LDirectory::FullPath() { - static char s[MAX_PATH_LEN]; - #warning this should really be optimized, and thread safe... - Path(s, sizeof(s)); - return s; + auto nm = GetName(); + if (!nm) + return NULL; + + if (!d->BaseEnd) + { + d->BaseEnd = d->BasePath + strlen(d->BasePath); + if (d->BaseEnd > d->BasePath && + d->BaseEnd[-1] != DIR_CHAR) + { + *d->BaseEnd++ = DIR_CHAR; + } + } + + auto used = d->BaseEnd - d->BasePath; + auto remaining = sizeof(d->BasePath) - used; + + strcpy_s(d->BaseEnd, remaining, nm); + return d->BasePath; } LString LDirectory::FileName() const { return GetName(); } bool LDirectory::Path(char *s, int BufLen) const { if (!s) { return false; } return LMakePath(s, BufLen, d->BasePath, GetName()); } int LDirectory::GetType() const { return IsDir() ? VT_FOLDER : VT_FILE; } int LDirectory::GetUser(bool Group) const { if (Group) { return d->Stat.st_gid; } else { return d->Stat.st_uid; } } bool LDirectory::IsReadOnly() const { if (getuid() == d->Stat.st_uid) { // Check user perms return !TestFlag(GetAttributes(), S_IWUSR); } else if (getgid() == d->Stat.st_gid) { // Check group perms return !TestFlag(GetAttributes(), S_IWGRP); } // Check global perms return !TestFlag(GetAttributes(), S_IWOTH); } bool LDirectory::IsHidden() const { return GetName() && GetName()[0] == '.'; } bool LDirectory::IsDir() const { int a = GetAttributes(); return !S_ISLNK(a) && S_ISDIR(a); } bool LDirectory::IsSymLink() const { int a = GetAttributes(); return S_ISLNK(a); } long LDirectory::GetAttributes() const { return d->Stat.st_mode; } char *LDirectory::GetName() const { return (d->De) ? d->De->d_name : NULL; } uint64 LDirectory::GetCreationTime() const { return (uint64) d->Stat.st_ctime * LDateTime::Second64Bit; } uint64 LDirectory::GetLastAccessTime() const { return (uint64) d->Stat.st_atime * LDateTime::Second64Bit; } uint64 LDirectory::GetLastWriteTime() const { return (uint64) d->Stat.st_mtime * LDateTime::Second64Bit; } uint64 LDirectory::GetSize() const { - return (uint32_t)d->Stat.st_size; + return d->Stat.st_size; } int64 LDirectory::GetSizeOnDisk() { - return (uint32_t)d->Stat.st_size; + return d->Stat.st_size; } ///////////////////////////////////////////////////////////////////////////////// -//////////////////////////// File /////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// class LFilePrivate { public: int hFile; char *Name; bool Swap; int Status; int Attributes; int ErrorCode; LFilePrivate() { hFile = INVALID_HANDLE; Name = 0; Swap = false; Status = true; Attributes = 0; ErrorCode = 0; } ~LFilePrivate() { DeleteArray(Name); } }; LFile::LFile(const char *Path, int Mode) { d = new LFilePrivate; if (Path) Open(Path, Mode); } LFile::~LFile() { if (d && ValidHandle(d->hFile)) { Close(); } DeleteObj(d); } OsFile LFile::Handle() { return d->hFile; } int LFile::GetError() { return d->ErrorCode; } bool LFile::IsOpen() { return ValidHandle(d->hFile); } #define DEBUG_OPEN_FILES 0 #if DEBUG_OPEN_FILES LMutex Lck; LArray OpenFiles; #endif int LFile::Open(const char *File, int Mode) { int Status = false; if (File) { if (TestFlag(Mode, O_WRITE) || TestFlag(Mode, O_READWRITE)) { Mode |= O_CREAT; } Close(); d->hFile = open(File, Mode #ifdef O_LARGEFILE | O_LARGEFILE #endif , S_IRUSR | S_IWUSR); if (ValidHandle(d->hFile)) { d->Attributes = Mode; d->Name = new char[strlen(File)+1]; if (d->Name) { strcpy(d->Name, File); } Status = true; d->Status = true; #if DEBUG_OPEN_FILES if (Lck.Lock(_FL)) { if (!OpenFiles.HasItem(this)) OpenFiles.Add(this); Lck.Unlock(); } #endif } else { d->ErrorCode = errno; #if DEBUG_OPEN_FILES if (Lck.Lock(_FL)) { for (unsigned i=0; iGetName()); Lck.Unlock(); } #endif - printf("LFile::Open failed\n\topen(%s,%08.8x) = %i\n\terrno=%s (%s)\n", + printf("LFile::Open failed\n\topen(%s,%08.8x) = %i\n\terrno=%s\n", File, Mode, d->hFile, - GetErrorName(d->ErrorCode), - GetErrorDesc(d->ErrorCode)); + LErrorCodeToString(d->ErrorCode)); } } return Status; } int LFile::Close() { if (ValidHandle(d->hFile)) { close(d->hFile); d->hFile = INVALID_HANDLE; DeleteArray(d->Name); #if DEBUG_OPEN_FILES if (Lck.Lock(_FL)) { OpenFiles.Delete(this); Lck.Unlock(); } #endif } return true; } uint64_t LFile::GetModifiedTime() { struct stat s; stat(d->Name, &s); return s.st_mtime; } bool LFile::SetModifiedTime(uint64_t dt) { struct stat s; stat(d->Name, &s); struct timeval times[2] = {}; times[0].tv_sec = s.st_atime; times[1].tv_sec = dt; int r = utimes(d->Name, times); return r == 0; } void LFile::ChangeThread() { } #define CHUNK 0xFFF0 ssize_t LFile::Read(void *Buffer, ssize_t Size, int Flags) { int Red = 0; if (Buffer && Size > 0) { Red = read(d->hFile, Buffer, Size); } d->Status = Red == Size; return MAX(Red, 0); } ssize_t LFile::Write(const void *Buffer, ssize_t Size, int Flags) { int Written = 0; if (Buffer && Size > 0) { Written = write(d->hFile, Buffer, Size); } d->Status = Written == Size; return MAX(Written, 0); } #ifdef LINUX #define LINUX64 1 #endif int64 LFile::Seek(int64 To, int Whence) { #if LINUX64 return lseek64(d->hFile, To, Whence); // If this doesn't compile, switch off LINUX64 #else return lseek(d->hFile, To, Whence); #endif } int64 LFile::SetPos(int64 Pos) { #if LINUX64 int64 p = lseek64(d->hFile, Pos, SEEK_SET); if (p < 0) { int e = errno; printf("%s:%i - lseek64(%Lx) failed (error %i: %s).\n", __FILE__, __LINE__, Pos, e, GetErrorName(e)); } #else return lseek(d->hFile, Pos, SEEK_SET); #endif } int64 LFile::GetPos() { #if LINUX64 int64 p = lseek64(d->hFile, 0, SEEK_CUR); if (p < 0) { int e = errno; printf("%s:%i - lseek64 failed (error %i: %s).\n", __FILE__, __LINE__, e, GetErrorName(e)); } return p; #else return lseek(d->hFile, 0, SEEK_CUR); #endif } int64 LFile::GetSize() { int64 Here = GetPos(); #if LINUX64 int64 Ret = lseek64(d->hFile, 0, SEEK_END); #else int64 Ret = lseek(d->hFile, 0, SEEK_END); #endif SetPos(Here); return Ret; } int64 LFile::SetSize(int64 Size) { if (ValidHandle(d->hFile)) { int64 Pos = GetPos(); #if LINUX64 ftruncate64(d->hFile, Size); #else ftruncate(d->hFile, Size); #endif if (d->hFile) { SetPos(Pos); } } return GetSize(); } bool LFile::Eof() { return GetPos() >= GetSize(); } ssize_t LFile::SwapRead(uchar *Buf, ssize_t Size) { ssize_t i = 0; ssize_t r = 0; Buf = &Buf[Size-1]; while (Size--) { r = read(d->hFile, Buf--, 1); i += r; } return i; } ssize_t LFile::SwapWrite(uchar *Buf, ssize_t Size) { ssize_t i = 0; ssize_t w = 0; Buf = &Buf[Size-1]; while (Size--) { w = write(d->hFile, Buf--, 1); i += w; } return i; } ssize_t LFile::ReadStr(char *Buf, ssize_t Size) { ssize_t i = 0; ssize_t r = 0; if (Buf && Size > 0) { char c; Size--; do { r = read(d->hFile, &c, 1); if (Eof()) { break; } *Buf++ = c; i++; } while (i < Size - 1 && c != '\n'); *Buf = 0; } return i; } ssize_t LFile::WriteStr(char *Buf, ssize_t Size) { ssize_t i = 0; ssize_t w; while (i <= Size) { w = write(d->hFile, Buf, 1); Buf++; i++; if (*Buf == '\n') break; } return i; } void LFile::SetStatus(bool s) { d->Status = s; } bool LFile::GetStatus() { return d->Status; } void LFile::SetSwap(bool s) { d->Swap = s; } bool LFile::GetSwap() { return d->Swap; } int LFile::GetOpenMode() { return d->Attributes; } const char *LFile::GetName() { return d->Name; } #define GFileOp(type) LFile &LFile::operator >> (type &i) { d->Status |= ((d->Swap) ? SwapRead((uchar*) &i, sizeof(i)) : Read(&i, sizeof(i))) != sizeof(i); return *this; } GFileOps(); #undef GFileOp #define GFileOp(type) LFile &LFile::operator << (type i) { d->Status |= ((d->Swap) ? SwapWrite((uchar*) &i, sizeof(i)) : Write(&i, sizeof(i))) != sizeof(i); return *this; } GFileOps(); #undef GFileOp ////////////////////////////////////////////////////////////////////////////// bool LFile::Path::FixCase() { LString Prev; bool Changed = false; // printf("FixCase '%s'\n", GetFull().Get()); for (size_t i=0; i %s\n", part.Get(), name); Part = Name; Next = (Prev ? Prev : "") + Sep + Part; Changed = true; } } } Prev = Next; } return Changed; }