// saw_io.h - Cross platform file system manipulation // - Io abstraction including file, memory, pipe, COM implementations // - Bit streaming // - Bit twiddling and byte swapping // // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // For more information, please refer to //----------------------------------------------------------------------------------------------------------- // History // - v1.17 - 04/02/18 - Added FsLoadTextFile common functionality // - Added FsLoadBinFile common functionality // - v1.16 - 03/07/18 - Fixed implicit cast warning // - Fixed serial port buffer incompletion (flush/purge) // - Fixed FsGetFullPath to include trailing directory slash in both Windows and Linux // - Fixed FsCopyFile in Linux (Error checking and return success/failure) // - Fixed FsGetWorkingDir memory leak in Linux // - Added serial port implementation in Linux // - Added utf8to16 and utf16to8 (only in Windows) // - v1.15 - 05/24/17 - Added serial port configuration support // - v1.14 - 05/15/17 - Fixed return logic in FsWalk (Linux) // - Fixed FsGetUserDir to work in Linux // - v1.13 - 04/24/17 - Added FsCopyFile // - v1.12 - 04/19/17 - Added FsGetUserDir // - v1.11 - 02/25/17 - Added IoOpenSerial // - Added FsEnumSerial // - Added IoOpenPipe // - Added FS_WALK_TOP for not traversing through children // - v1.10 - 01/30/17 - Fixed async Windows file system issues for folders and rename // - Fixed memory leak in IoOpenMem and IoOpenVec // - Added FsGetTempFile // - Added IoOpenFileInMem // - v1.09 - 12/07/16 - Fixed IoReadDbe and IoReadDle sign extended errors // - v1.08 - 11/16/16 - Fixed IoReadLine8 and IoReadText8 for 0 length strings and return value // - v1.07 - 06/20/16 - Fixed async delete-then-recreate problem in Windows // - v1.06 - 04/28/16 - Refactored and committed to C++ style strings and expectations // - Refactored bit/byte functions for consistency // - Refactored for reduced dependencies // - v1.05 - 04/20/16 - Fixed FsWatch on 32-bit platforms // - Fixed FsWalk in linux // - v1.04 - 04/16/16 - Added FsGetRelPath // - Added FsWatch // - Added FsWalk // - v1.03 - 03/30/16 - Fixed a slightly ambiguous placement of braces // - v1.02 - 03/17/16 - Merged with bit streaming, bit twiddling, byte swapping libraries // - Merged with file system manipulation library // - v1.01 - 03/17/16 - Fixed treat C-string like std::string in Linux IoOpenFile // - v1.00 - 03/12/16 - Initial release by Scott Williams //----------------------------------------------------------------------------------------------------------- // Notes // - Simple, tested C++ solution for most filesystem i/o needs //----------------------------------------------------------------------------------------------------------- // Usage // - Define SAW_IO_IMPLEMENTATION before inclusion in one file for library implementation // - Functionality is in saw:: namespace //----------------------------------------------------------------------------------------------------------- // Todo // - IoOpenPipe for linux #ifndef _SAW_IO_H_INCLUDED #define _SAW_IO_H_INCLUDED #include #include namespace saw { // Returning false stops the file tree walk typedef bool(*FsWalkFunc)(const std::string &fullPath, bool isFile, void *user); enum FsWatchType { FS_WATCH_NONE = 0, FS_WATCH_REMOVED, FS_WATCH_CHANGED, FS_WATCH_CREATED, }; enum FsWalkType { FS_WALK_NORMAL, // top-down traversal of file tree FS_WALK_DEPTH, // depth-first traversal of file tree FS_WALK_TOP, // only walk top level (no children) }; enum IoStopType { IO_STOP_1, IO_STOP_1_5, IO_STOP_2, }; enum IoParityType { IO_PARITY_NONE, IO_PARITY_ODD, IO_PARITY_EVEN, IO_PARITY_MARK, IO_PARITY_SPACE, }; enum IoSeekType { IO_SEEK_SET, IO_SEEK_REL, IO_SEEK_END }; enum IoAccessType { IO_ACCESS_RW_NEW, IO_ACCESS_RW, IO_ACCESS_R }; struct Io { void *handle; bool (*funcRead)(void *, size_t, void *); bool (*funcWrite)(void *, size_t, const void *); bool (*funcSeek)(void *, IoSeekType, long long); long long (*funcTell)(void *); void (*funcClose)(void *); }; struct BitStreamIn { const unsigned char *pBase; const unsigned char *pPos; unsigned int hasBits; unsigned int currBits; unsigned int bytesLeft; }; struct BitStreamOut { unsigned char *pBase; unsigned char *pPos; unsigned int hasBits; unsigned int currBits; unsigned int bytesLeft; }; // Cross platform file system manipulation bool FsCreateDir(const std::string &fullPathDir); bool FsDeleteDir(const std::string &fullPathDir); bool FsDeleteFile(const std::string &fullPathFile); bool FsRename(const std::string &fullPathOld, const std::string &fullPathNew); bool FsCopyFile(const std::string &fullPathSrc, const std::string &fullPathDest); bool FsSetWorkingDir(const std::string &fullPathDir); bool FsIsDir(const std::string &fullPath); bool FsIsFile(const std::string &fullPath); bool FsIsPipe(const std::string &pipeName); bool FsIsReadOnly(const std::string &fullPath); bool FsIsHidden(const std::string &fullPath); bool FsIsRelative(const std::string &path); time_t FsGetTimeAccess(const std::string &fullPath); time_t FsGetTimeMod(const std::string &fullPath); time_t FsGetTimeCreate(const std::string &fullPath); std::string FsGetTempFile(); std::string FsGetTempDir(bool includeSlash); std::string FsGetWorkingDir(bool includeSlash); std::string FsGetUserDir(const std::string &appName, bool includeSlash); std::string FsGetAppDir(bool includeSlash); std::string FsGetAppPath(); std::string FsGetModuleDir(bool includeSlash); std::string FsGetModulePath(); std::string FsGetRelPath(const std::string &fullPathFrom, const std::string &fullPathTo); std::string FsGetFullPath(const std::string &path); std::string FsGetPathDir(const std::string &fullPath, bool includeSlash); std::string FsGetPathFile(const std::string &fullPath, bool includeExt); std::string FsGetPathExt(const std::string &fullPath, bool includeDot); unsigned long long FsGetFileSize(const std::string &fullPathFile); FsWatchType FsWatch(const std::string &fullPath, int *state); // init state to 0 bool FsWalk(const std::string &fullPath, FsWalkType type, FsWalkFunc callback, void *user); bool FsEnumSerial(FsWalkFunc callback, void *user); bool FsLoadTextFile(const std::string &filename, std::string *pOut); bool FsLoadBinFile(const std::string &filename, std::vector *pVec); // Io abstraction - implementations bool IoOpenFile(Io *io, const std::string &filename, IoAccessType access); bool IoOpenMem(Io *io, const void *pMem, size_t size); bool IoOpenSerial(Io *io, const std::string &com, int baudRate = 38400, int byteSize = 8, IoStopType stopBits = IO_STOP_1, IoParityType parity = IO_PARITY_NONE); bool IoOpenPipe(Io *io, const std::string &pipeName, IoAccessType access); bool IoOpenVec(Io *io, std::vector *pVec); bool IoOpenFileInMem(Io *io, const std::string &filename, IoAccessType access); // Io abstraction - general void IoClose(Io *io); inline bool IoSeek(const Io *io, IoSeekType type, long long offset); inline bool IoSkip(const Io *io, long long offset); inline long long IoSize(const Io *io); inline long long IoTell(const Io *io); inline int IoRead8(const Io *io); inline int IoRead16le(const Io *io); inline int IoRead16be(const Io *io); inline int IoRead32le(const Io *io); inline int IoRead32be(const Io *io); inline float IoReadFle(const Io *io); inline float IoReadFbe(const Io *io); inline double IoReadDle(const Io *io); inline double IoReadDbe(const Io *io); inline bool IoReadRaw(const Io *io, size_t bytes, void *pOut); bool IoReadText8(const Io *io, char *buf, int bufSize); // Stops at null, eof, or bufSize; bufSize must consider null terminator; error if buf too small, null pointer, or eof at the beginning bool IoReadLine8(const Io *io, char *buf, int bufSize); // Stops at null, eof, eol, or bufSize; bufSize must consider null terminator; does NOT include eol character(s) in output; error if buf too small, null pointer, or eof at the beginning bool IoReadText8(const Io *io, std::string *pOut, int len); // Stops at null, eof, or len if >= 0 for more secure reads; error if null pointer or eof at the beginning bool IoReadLine8(const Io *io, std::string *pOut); // Stops at null, eof, or eol; error if null pointer or eof at the beginning inline void IoWrite8(const Io *io, int val); inline void IoWrite16le(const Io *io, int val); inline void IoWrite16be(const Io *io, int val); inline void IoWrite32le(const Io *io, int val); inline void IoWrite32be(const Io *io, int val); inline void IoWriteFle(const Io *io, float val); inline void IoWriteFbe(const Io *io, float val); inline void IoWriteDle(const Io *io, double val); inline void IoWriteDbe(const Io *io, double val); inline void IoWriteRaw(const Io *io, size_t bytes, const void *pIn); void IoWriteSle(const Io *io, const char *format, ...); void IoWriteSbe(const Io *io, const char *format, ...); void IoWriteText8(const Io *io, const char *in, bool includeNull); void IoWriteLine8(const Io *io, const char *in); // ensures newline // Bit streaming inline void BsInit(BitStreamIn *pBits, const void *pBase, unsigned int byteSize); inline void BsInit(BitStreamOut *pBits, void *pBase, unsigned int byteSize); inline bool BsReadLsb(BitStreamIn *pBits, unsigned int *pOut, unsigned char numBits); inline bool BsReadMsb(BitStreamIn *pBits, unsigned int *pOut, unsigned char numBits); inline void BsReadFlush(BitStreamIn *pBits); // Flush partial byte if there is one inline bool BsWriteLsb(BitStreamOut *pBits, unsigned int in, unsigned char numBits); inline bool BsWriteMsb(BitStreamOut *pBits, unsigned int in, unsigned char numBits); inline void BsWriteFlushLsb(BitStreamOut *pBits); // Flush partial byte if there is one inline void BsWriteFlushMsb(BitStreamOut *pBits); // Flush partial byte if there is one template inline void BsSetPosLsb(BitStream *pBits, unsigned int bitPos); template inline void BsSetPosMsb(BitStream *pBits, unsigned int bitPos); template inline void BsSkipLsb(BitStream *pBits, int numBits); template inline void BsSkipMsb(BitStream *pBits, int numBits); template inline void BsUndoLsb(BitStream *pBits, unsigned int in, unsigned char numBits); template inline void BsUndoMsb(BitStream *pBits, unsigned int in, unsigned char numBits); // Bit twiddling and byte swapping inline unsigned char BitReverse8(unsigned char value); inline unsigned short BitReverse16(unsigned short value); inline unsigned int BitReverse32(unsigned int value); inline int BitGetCount(unsigned int value); inline int BitGetMsb(unsigned int value); inline int BitGetLsb(unsigned int value); void BitInvertBuf(void *pData, size_t numBytes); void BitReverseBuf8(void *pData, size_t numBytes); inline unsigned short ByteSwap16(unsigned short value); inline unsigned int ByteSwap32(unsigned int value); inline unsigned long long ByteSwap64(unsigned long long value); void ByteSwapBuf16(void *pData, size_t count); // count in words, not bytes void ByteSwapBuf32(void *pData, size_t count); // count in double words, not bytes #ifdef _WIN32 std::wstring utf8to16(const std::string &utf8); std::string utf16to8(const std::wstring &utf16); #endif //----------------------------------------------------------------------------------------------------------- inline bool IoSeek(const Io *io, IoSeekType type, long long offset) { return io->funcSeek(io->handle, type, offset); } //----------------------------------------------------------------------------------------------------------- inline bool IoSkip(const Io *io, long long offset) { return io->funcSeek(io->handle, IO_SEEK_REL, offset); } //----------------------------------------------------------------------------------------------------------- inline long long IoSize(const Io *io) { long long pos = io->funcTell(io->handle); io->funcSeek(io->handle, IO_SEEK_END, 0); long long size = io->funcTell(io->handle); io->funcSeek(io->handle, IO_SEEK_SET, pos); return size; } //----------------------------------------------------------------------------------------------------------- inline long long IoTell(const Io *io) { return io->funcTell(io->handle); } //----------------------------------------------------------------------------------------------------------- inline int IoRead8(const Io *io) { unsigned char ret; io->funcRead(io->handle, 1, &ret); return static_cast(ret); } //----------------------------------------------------------------------------------------------------------- inline int IoRead16be(const Io *io) { int high = IoRead8(io) << 8; return high | IoRead8(io); } //----------------------------------------------------------------------------------------------------------- inline int IoRead16le(const Io *io) { int low = IoRead8(io); return low | (IoRead8(io) << 8); } //----------------------------------------------------------------------------------------------------------- inline int IoRead32be(const Io *io) { int high = IoRead16be(io) << 16; // Don't want 16 bit value to be sign-extended return high | IoRead16be(io); } //----------------------------------------------------------------------------------------------------------- inline int IoRead32le(const Io *io) { int low = IoRead16le(io); // Don't want 16 bit value to be sign-extended return low | (IoRead16le(io) << 16); } //----------------------------------------------------------------------------------------------------------- inline float IoReadFbe(const Io *io) { int low = IoRead16be(io) << 16; // Don't want 16 bit value to be sign-extended low |= IoRead16be(io); return *reinterpret_cast(&low); } //----------------------------------------------------------------------------------------------------------- inline float IoReadFle(const Io *io) { int low = IoRead16le(io); // Don't want 16 bit value to be sign-extended low |= IoRead16le(io) << 16; return *reinterpret_cast(&low); } //----------------------------------------------------------------------------------------------------------- inline double IoReadDbe(const Io *io) { unsigned long long high = static_cast(IoRead32be(io)); unsigned long long low = static_cast(IoRead32be(io)); low |= high << 32; return *reinterpret_cast(&low); } //----------------------------------------------------------------------------------------------------------- inline double IoReadDle(const Io *io) { unsigned long long low = static_cast(IoRead32le(io)); unsigned long long high = static_cast(IoRead32le(io)); low |= high << 32; return *reinterpret_cast(&low); } //----------------------------------------------------------------------------------------------------------- inline bool IoReadRaw(const Io *io, size_t bytes, void *pOut) { if (bytes == 0) return true; return io->funcRead(io->handle, bytes, pOut); } //----------------------------------------------------------------------------------------------------------- inline void IoWrite8(const Io *io, int val) { io->funcWrite(io->handle, 1, &val); } //----------------------------------------------------------------------------------------------------------- inline void IoWrite16be(const Io *io, int val) { IoWrite8(io, val >> 8); IoWrite8(io, val & 0xff); } //----------------------------------------------------------------------------------------------------------- inline void IoWrite16le(const Io *io, int val) { IoWrite8(io, val & 0xff); IoWrite8(io, val >> 8); } //----------------------------------------------------------------------------------------------------------- inline void IoWrite32be(const Io *io, int val) { IoWrite16be(io, val >> 16); IoWrite16be(io, val & 0xffff); } //----------------------------------------------------------------------------------------------------------- inline void IoWrite32le(const Io *io, int val) { IoWrite16le(io, val & 0xffff); IoWrite16le(io, val >> 16); } //----------------------------------------------------------------------------------------------------------- inline void IoWriteFbe(const Io *io, float val) { IoWrite32be(io, *reinterpret_cast(&val)); } //----------------------------------------------------------------------------------------------------------- inline void IoWriteFle(const Io *io, float val) { IoWrite32le(io, *reinterpret_cast(&val)); } //----------------------------------------------------------------------------------------------------------- inline void IoWriteDbe(const Io *io, double val) { unsigned long long v64 = *reinterpret_cast(&val); IoWrite32be(io, v64 >> 32); IoWrite32be(io, v64 & 0xffffffff); } //----------------------------------------------------------------------------------------------------------- inline void IoWriteDle(const Io *io, double val) { unsigned long long v64 = *reinterpret_cast(&val); IoWrite32le(io, v64 & 0xffffffff); IoWrite32le(io, v64 >> 32); } //----------------------------------------------------------------------------------------------------------- inline void IoWriteRaw(const Io *io, size_t bytes, const void *pIn) { if (bytes == 0) return; io->funcWrite(io->handle, bytes, pIn); } //------------------------------------------------------------------------------------------------------- inline void BsInit(BitStreamIn *pBits, const void *pBase, unsigned int byteSize) { pBits->pPos = pBits->pBase = reinterpret_cast(pBase); pBits->bytesLeft = byteSize; pBits->hasBits = pBits->currBits = 0; } //----------------------------------------------------------------------------------------------------------- inline void BsInit(BitStreamOut *pBits, void *pBase, unsigned int byteSize) { pBits->pPos = pBits->pBase = reinterpret_cast(pBase); pBits->bytesLeft = byteSize; pBits->hasBits = pBits->currBits = 0; } //----------------------------------------------------------------------------------------------------------- inline bool BsReadLsb(BitStreamIn *pBits, unsigned int *pOut, unsigned char numBits) { while (pBits->hasBits < numBits) { if (pBits->bytesLeft == 0) return false; pBits->currBits |= *pBits->pPos++ << pBits->hasBits; pBits->bytesLeft--; pBits->hasBits += 8; } *pOut = pBits->currBits & ((1 << numBits) - 1); pBits->currBits >>= numBits; pBits->hasBits -= numBits; return true; } //----------------------------------------------------------------------------------------------------------- inline bool BsReadMsb(BitStreamIn *pBits, unsigned int *pOut, unsigned char numBits) { while (pBits->hasBits < numBits) { if (pBits->bytesLeft == 0) return false; pBits->currBits |= *pBits->pPos++ << (24 - pBits->hasBits); pBits->bytesLeft--; pBits->hasBits += 8; } *pOut = pBits->currBits >> (32 - numBits); pBits->currBits <<= numBits; pBits->hasBits -= numBits; return true; } //----------------------------------------------------------------------------------------------------------- inline void BsReadFlush(BitStreamIn *pBits) { if (pBits->hasBits) { pBits->pPos++; pBits->bytesLeft--; pBits->currBits = 0; pBits->hasBits = 0; } } //----------------------------------------------------------------------------------------------------------- inline bool BsWriteLsb(BitStreamOut *pBits, unsigned int in, unsigned char numBits) { pBits->currBits |= in << pBits->hasBits; pBits->hasBits += numBits; while (pBits->hasBits >= 8) { *pBits->pPos++ = pBits->currBits & 0xff; pBits->bytesLeft--; pBits->currBits >>= 8; pBits->hasBits -= 8; } return true; } //----------------------------------------------------------------------------------------------------------- inline bool BsWriteMsb(BitStreamOut *pBits, unsigned int in, unsigned char numBits) { pBits->currBits |= in << (32 - numBits - pBits->hasBits); pBits->hasBits += numBits; while (pBits->hasBits >= 8) { *pBits->pPos++ = pBits->currBits >> 24; pBits->bytesLeft--; pBits->currBits <<= 8; pBits->hasBits -= 8; } return true; } //----------------------------------------------------------------------------------------------------------- inline void BsWriteFlushLsb(BitStreamOut *pBits) { BsWriteLsb(pBits, 0, 0); if (pBits->hasBits) { unsigned char mask = (1 << pBits->hasBits) - 1; *pBits->pPos = *pBits->pPos & ~mask; *pBits->pPos++ |= pBits->currBits & mask; pBits->bytesLeft--; pBits->hasBits = 0; pBits->currBits = 0; } } //----------------------------------------------------------------------------------------------------------- inline void BsWriteFlushMsb(BitStreamOut *pBits) { BsWriteMsb(pBits, 0, 0); if (pBits->hasBits) { unsigned char invmask = (1 << (8 - pBits->hasBits)) - 1; *pBits->pPos = *pBits->pPos & invmask; *pBits->pPos++ |= (pBits->currBits >> 24) & ~invmask; pBits->bytesLeft--; pBits->hasBits = 0; pBits->currBits = 0; } } //----------------------------------------------------------------------------------------------------------- template inline void BsSetPosLsb(BitStream *pBits, unsigned int bitPos) { pBits->bytesLeft = pBits->bytesLeft + (pBits->pPos - pBits->pBase) - ((bitPos + 7) >> 3); pBits->pPos = pBits->pBase + (bitPos >> 3); pBits->hasBits = (8 - (bitPos & 7)) & 7; pBits->currBits = 0; if (pBits->hasBits) pBits->currBits = *pBits->pPos++ >> (bitPos & 7); } //----------------------------------------------------------------------------------------------------------- template inline void BsSetPosMsb(BitStream *pBits, unsigned int bitPos) { pBits->bytesLeft = pBits->bytesLeft + (pBits->pPos - pBits->pBase) - ((bitPos + 7) >> 3); pBits->pPos = pBits->pBase + (bitPos >> 3); pBits->hasBits = (8 - (bitPos & 7)) & 7; pBits->currBits = 0; if (pBits->hasBits) pBits->currBits = *pBits->pPos++ << (24 + (bitPos & 7)); } //----------------------------------------------------------------------------------------------------------- template inline void BsSkipLsb(BitStream *pBits, int numBits) { int curr = static_cast(((pBits->pPos - pBits->pBase) << 3) + pBits->hasBits); BsSetPosLsb(pBits, static_cast(curr + numBits)); } //----------------------------------------------------------------------------------------------------------- template inline void BsSkipMsb(BitStream *pBits, int numBits) { int curr = static_cast(((pBits->pPos - pBits->pBase) << 3) + pBits->hasBits); BsSetPosMsb(pBits, static_cast(curr + numBits)); } //----------------------------------------------------------------------------------------------------------- template inline void BsUndoLsb(BitStream *pBits, unsigned int in, unsigned char numBits) { pBits->currBits <<= numBits; pBits->currBits |= in & ((1 << numBits) - 1); pBits->hasBits += numBits; } //----------------------------------------------------------------------------------------------------------- template inline void BsUndoMsb(BitStream *pBits, unsigned int in, unsigned char numBits) { pBits->currBits >>= numBits; pBits->currBits |= in << (32 - numBits); pBits->hasBits += numBits; } //------------------------------------------------------------------------------------------------------- inline unsigned char BitReverse8(unsigned char value) { return static_cast(((value * 0x0802u & 0x22110u) | (value * 0x8020u & 0x88440u)) * 0x10101u >> 16); } //------------------------------------------------------------------------------------------------------- inline unsigned short BitReverse16(unsigned short value) { value = ((value & 0xaaaa) >> 1) | ((value & 0x5555) << 1); value = ((value & 0xcccc) >> 2) | ((value & 0x3333) << 2); value = ((value & 0xf0f0) >> 4) | ((value & 0x0f0f) << 4); return (value >> 8) | (value << 8); } //------------------------------------------------------------------------------------------------------- inline unsigned int BitReverse32(unsigned int value) { value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1); value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2); value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4); value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8); return (value >> 16) | (value << 16); } //------------------------------------------------------------------------------------------------------- inline int BitGetCount(unsigned int value) { value = value - ((value >> 1) & 0x55555555); value = (value & 0x33333333) + ((value >> 2) & 0x33333333); return ((value + (value >> 4) & 0xf0f0f0f0) * 0x01010101) >> 24; } //------------------------------------------------------------------------------------------------------- inline int BitGetMsb(unsigned int value) { static const int MultiplyDeBruijnBitPosition[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; return MultiplyDeBruijnBitPosition[(value * 0x07c4acddu) >> 27]; } //------------------------------------------------------------------------------------------------------- inline int BitGetLsb(unsigned int value) { static const int MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return MultiplyDeBruijnBitPosition[((value & -static_cast(value)) * 0x077cb531u) >> 27]; } //------------------------------------------------------------------------------------------------------- inline unsigned short ByteSwap16(unsigned short value) { value = (value >> 8) | (value << 8); return value; } //------------------------------------------------------------------------------------------------------- inline unsigned int ByteSwap32(unsigned int value) { value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8); value = (value >> 16) | (value << 16); return value; } //------------------------------------------------------------------------------------------------------- inline unsigned long long ByteSwap64(unsigned long long value) { value = ((value & 0xff00ff00ff00ff00ull) >> 8) | ((value & 0x00ff00ff00ff00ffull) << 8); value = ((value & 0xffff0000ffff0000ull) >> 16) | ((value & 0x0000ffff0000ffffull) << 16); value = (value >> 32) | (value << 32); return value; } } // namespace #endif // _SAW_IO_H_INCLUDED #ifdef SAW_IO_IMPLEMENTATION #include // va_list #include // fopen and family #include // strncmp/_strnicmp #include // memcpy #ifdef _WIN32 # include // many windows-specific functions # include // get appdata folders #else # ifdef __APPLE__ # include // _NSGetExecutablePath # endif # include // readlink, getcwd, chdir # include // This has to precede sys/stat.h # include // stat # include // open, O_* macros # include // recursive folder walk # include // scandir, opendir, readdir # include // getenv, realpath # include // basename # include // ioctl for serial port stuff # include // serial_struct # include // termios and ioctl constants # include // current user/group #endif #define SAW_IO_SERIAL_FLUSH 1 namespace saw { static const int FS_PATH_MAX_LEN = 2048; #ifdef _WIN32 std::wstring utf8to16(const std::string &utf8) { std::wstring utf16; utf16.resize(FS_PATH_MAX_LEN); int size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), static_cast(utf8.size()), &utf16[0], FS_PATH_MAX_LEN); utf16.resize(size); return std::move(utf16); } std::string utf16to8(const std::wstring &utf16) { std::string utf8; utf8.resize(FS_PATH_MAX_LEN); int size = WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), static_cast(utf16.size()), &utf8[0], FS_PATH_MAX_LEN, 0, 0); utf8.resize(size); return std::move(utf8); } #endif //----------------------------------------------------------------------------------------------------------- bool IoOpenFile(Io *io, const std::string &filename, IoAccessType access) { if (!io) return false; #ifdef _WIN32 std::wstring utf16 = std::move(utf8to16(filename)); switch (access) { case IO_ACCESS_RW_NEW: _wfopen_s(reinterpret_cast(&io->handle), utf16.c_str(), L"w+b"); break; case IO_ACCESS_RW: _wfopen_s(reinterpret_cast(&io->handle), utf16.c_str(), L"r+b"); if (!io->handle) _wfopen_s(reinterpret_cast(&io->handle), utf16.c_str(), L"w+b"); break; case IO_ACCESS_R: _wfopen_s(reinterpret_cast(&io->handle), utf16.c_str(), L"rb"); break; default: return false; } #else switch (access) { case IO_ACCESS_RW_NEW: io->handle = fopen(filename.c_str(), "w+b"); break; case IO_ACCESS_RW: io->handle = fopen(filename.c_str(), "r+b"); if (!io->handle) io->handle = fopen(filename.c_str(), "w+b"); break; case IO_ACCESS_R: io->handle = fopen(filename.c_str(), "rb"); break; default: return false; } #endif if (!io->handle) return false; io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { return fread(pOut, 1, bytes, reinterpret_cast(handle)) == bytes; }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { return fwrite(pIn, 1, bytes, reinterpret_cast(handle)) == bytes; }; io->funcSeek = [](void *handle, IoSeekType type, long long offset) -> bool { int from = SEEK_SET; if (type == IO_SEEK_REL) from = SEEK_CUR; if (type == IO_SEEK_END) from = SEEK_END; #ifdef _WIN32 return _fseeki64(reinterpret_cast(handle), offset, from) == 0; #else return fseeko64(reinterpret_cast(handle), static_cast(offset), from) == 0; #endif }; io->funcTell = [](void *handle) -> long long { return ftell(reinterpret_cast(handle)); }; io->funcClose = [](void *handle) { fclose(reinterpret_cast(handle)); }; return true; } //----------------------------------------------------------------------------------------------------------- bool IoOpenMem(Io *io, const void *pMem, size_t size) { struct MemHandle { char *pBase; char *pCurr; char *pEnd; }; if (!io) return false; MemHandle *pH = new MemHandle; pH->pBase = reinterpret_cast(const_cast(pMem)); pH->pCurr = pH->pBase; pH->pEnd = pH->pBase + size; io->handle = pH; if (!io->handle) { delete pH; return false; } io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { MemHandle *pMem = reinterpret_cast(handle); if (pMem->pCurr + bytes > pMem->pEnd) return false; memcpy(pOut, pMem->pCurr, bytes); pMem->pCurr += bytes; return true; }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { MemHandle *pMem = reinterpret_cast(handle); if (pMem->pCurr + bytes > pMem->pEnd) return false; memcpy(pMem->pCurr, pIn, bytes); pMem->pCurr += bytes; return true; }; io->funcSeek = [](void *handle, IoSeekType type, long long offset) -> bool { MemHandle *pMem = reinterpret_cast(handle); switch (type) { case IO_SEEK_SET: if (pMem->pBase + offset > pMem->pEnd || offset < 0) return false; pMem->pCurr = pMem->pBase + offset; break; case IO_SEEK_REL: if (pMem->pCurr + offset > pMem->pEnd || pMem->pCurr + offset < pMem->pBase) return false; pMem->pCurr += offset; break; case IO_SEEK_END: if (pMem->pEnd - offset < pMem->pBase || offset < 0) return false; pMem->pCurr = pMem->pEnd - offset; break; } return true; }; io->funcTell = [](void *handle) -> long long { MemHandle *pMem = reinterpret_cast(handle); return pMem->pCurr - pMem->pBase; }; io->funcClose = [](void *handle) { delete reinterpret_cast(handle); }; return true; } //----------------------------------------------------------------------------------------------------------- bool IoOpenSerial(Io *io, const std::string &com, int baudRate, int byteSize, IoStopType stopBits, IoParityType parity) { if (!io) return false; const int READ_TIMEOUT_MS = 500; #ifdef _WIN32 HANDLE hComm = 0; hComm = CreateFileA(("\\\\.\\" + com).c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hComm == INVALID_HANDLE_VALUE) return false; io->handle = hComm; DCB dcb = { 0 }; dcb.DCBlength = sizeof(DCB); GetCommState(hComm, &dcb); dcb.BaudRate = baudRate; dcb.ByteSize = static_cast(byteSize); switch (stopBits) { case IO_STOP_1: dcb.StopBits = ONESTOPBIT; break; case IO_STOP_1_5: dcb.StopBits = ONE5STOPBITS; break; case IO_STOP_2: dcb.StopBits = TWOSTOPBITS; break; } switch (parity) { case IO_PARITY_NONE: dcb.Parity = NOPARITY; break; case IO_PARITY_EVEN: dcb.Parity = EVENPARITY; break; case IO_PARITY_ODD: dcb.Parity = ODDPARITY; break; case IO_PARITY_MARK: dcb.Parity = MARKPARITY; break; case IO_PARITY_SPACE: dcb.Parity = SPACEPARITY; break; } if (!SetCommState(hComm, &dcb)) { CloseHandle(hComm); return false; } COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = 0; timeouts.ReadTotalTimeoutConstant = READ_TIMEOUT_MS; timeouts.ReadTotalTimeoutMultiplier = 0; // + 0ms * bytes timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; //timeouts.ReadIntervalTimeout = 500; //timeouts.ReadTotalTimeoutConstant = 0; // wait 1000ms //timeouts.ReadTotalTimeoutMultiplier = 500; // + 0ms * bytes //timeouts.WriteTotalTimeoutConstant = 10; //timeouts.WriteTotalTimeoutMultiplier = 500; SetCommTimeouts(hComm, &timeouts); io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { // Return false if data not ready so we don't lock up /*SetCommMask(handle, EV_RXCHAR); DWORD dwEventMask = 0; while (dwEventMask != EV_RXCHAR) WaitCommEvent(handle, &dwEventMask, NULL);*/ /*DWORD read = 0; if (!ReadFile(handle, pOut, bytes, &read, NULL)) return false; if (read == 0) return false;*/ size_t total = 0; char *pOut8 = reinterpret_cast(pOut); while (total < bytes) { char c = 0; DWORD read = 0; // Returns after timeout even if no data, just says there was no data... if (!ReadFile(handle, &c, 1, &read, NULL)) return false; if (read == 0) return false; pOut8[total] = c; total += read; } return true; }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { PurgeComm(handle, 0xf); DWORD written = 0; WriteFile(handle, pIn, static_cast(bytes), &written, NULL); # ifdef SAW_IO_SERIAL_FLUSH FlushFileBuffers(handle); # endif return written == bytes; }; io->funcClose = [](void *handle) { CloseHandle(handle); }; #else int fd = open(("/dev/" + com).c_str(), O_RDWR | O_NOCTTY); if (fd == -1) return false; struct termios tty; struct termios tty_old; memset(&tty, 0, sizeof tty); if (tcgetattr(fd, &tty) != 0) { close(fd); return false; } tty_old = tty; int brc = -1; switch (baudRate) { case 50: brc = B50; break; case 75: brc = B75; break; case 110: brc = B110; break; case 134: brc = B134; break; case 150: brc = B150; break; case 200: brc = B200; break; case 300: brc = B300; break; case 600: brc = B600; break; case 1200: brc = B1200; break; case 1800: brc = B1800; break; case 2400: brc = B2400; break; case 4800: brc = B4800; break; case 9600: brc = B9600; break; case 19200: brc = B19200; break; case 38400: brc = B38400; break; case 57600: brc = B57600; break; case 115200: brc = B115200; break; case 230400: brc = B230400; break; case 460800: brc = B460800; break; case 500000: brc = B500000; break; case 576000: brc = B576000; break; case 921600: brc = B921600; break; } if (brc == -1) { close(fd); return false; } // Set speed parameters parameters cfsetospeed(&tty, (speed_t)brc); cfsetispeed(&tty, (speed_t)brc); //cfmakeraw(&tty); // Set all other parameters tty.c_cflag &= ~CSTOPB; switch (stopBits) { case IO_STOP_1: break; case IO_STOP_1_5: // 1.5 stop bits is standard for character size of 5 bits, otherwise 1 or 2 stop bits tty.c_cflag |= CSTOPB; if (byteSize != 5) { close(fd); return false; } break; case IO_STOP_2: tty.c_cflag |= CSTOPB; break; } tty.c_cflag &= ~(PARENB | PARODD | CMSPAR); switch (parity) { case IO_PARITY_NONE: break; case IO_PARITY_EVEN: tty.c_cflag |= PARENB; break; case IO_PARITY_ODD: tty.c_cflag |= PARENB | PARODD; break; case IO_PARITY_MARK: tty.c_cflag |= PARENB | PARODD | CMSPAR; break; case IO_PARITY_SPACE: tty.c_cflag |= PARENB | CMSPAR; break; } tty.c_cflag &= ~CSIZE; switch (byteSize) { case 5: tty.c_cflag |= CS5; break; case 6: tty.c_cflag |= CS6; break; case 7: tty.c_cflag |= CS7; break; case 8: tty.c_cflag |= CS8; break; default: close(fd); return false; } tty.c_cflag &= ~CRTSCTS; // no hardware flow control // Polling/non-blocking VMIN, VTIME = 0, 0 // Purely timed VMIN, VTIME = 0, >0 // Read up to 'VMIN' characters, or timeout (AFTER at least 1 character is read) // VMIN, VTIME = >0, >0 // Block until 'VMIN' characters // VMIN, VTIME = >0, 0 tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = READ_TIMEOUT_MS / 100; // 10ths of a second tty.c_cflag |= CREAD | CLOCAL; // turn on READ, ignore modem ctrl lines (local mode) // Set non-canonical mode tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); tty.c_lflag &= ~(ECHO | ECHONL | ECHOE | ICANON | ISIG | IEXTEN); tty.c_oflag &= ~OPOST; // @@ Good idea or no? tcflush(fd, TCIFLUSH); if (tcsetattr(fd, TCSANOW, &tty) != 0) { close(fd); return false; } io->handle = reinterpret_cast(fd); io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { size_t total = 0; char *pOut8 = reinterpret_cast(pOut); while (total < bytes) { char c = 0; int readBytes = read(static_cast(reinterpret_cast(handle)), &c, 1); if (readBytes <= 0) return false; pOut8[total] = c; total += readBytes; } return true; }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { // tcflush == PurgeComm, tcdrain == FlushFileBuffers int fd = static_cast(reinterpret_cast(handle)); // Clear anything pending, especially unread reads, so we can determine the exact next // response to this write. tcflush(fd, TCIOFLUSH); int written = write(fd, pIn, static_cast(bytes)); // @@ Good idea or no? // Finish output before continuing # ifdef SAW_IO_SERIAL_FLUSH tcdrain(fd); # endif return written == bytes; }; io->funcClose = [](void *handle) { int fd = static_cast(reinterpret_cast(handle)); tcflush(fd, TCIOFLUSH); close(fd); }; #endif io->funcSeek = [](void * /*handle*/, IoSeekType /*type*/, long long /*offset*/) -> bool { return false; }; io->funcTell = [](void * /*handle*/) -> long long { return -1; }; return true; } //----------------------------------------------------------------------------------------------------------- bool IoOpenPipe(Io *io, const std::string &pipeName, IoAccessType access) { if (!io) return false; #ifdef _WIN32 HANDLE hPipe = 0; std::wstring utf16 = L"\\\\.\\pipe\\" + utf8to16(pipeName); switch (access) { case IO_ACCESS_RW_NEW: hPipe = CreateNamedPipeW(utf16.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, 1, 1, 1, 0, NULL); if (!ConnectNamedPipe(hPipe, NULL)) { if (GetLastError() != ERROR_PIPE_CONNECTED) return false; } break; case IO_ACCESS_RW: if (WaitNamedPipeW(utf16.c_str(), 3000) == 0) return false; hPipe = CreateFileW(utf16.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hPipe == INVALID_HANDLE_VALUE) return false; break; case IO_ACCESS_R: hPipe = CreateFileW(utf16.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); break; default: return false; } if (hPipe == INVALID_HANDLE_VALUE) return false; io->handle = hPipe; io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { DWORD avail = 0; if (!PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) return false; if (avail == 0) return bytes == 0; DWORD read = 0; // Synchronized read doesn't return until it gets data ReadFile(handle, pOut, static_cast(bytes), &read, NULL); return read == bytes; }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { DWORD written = 0; WriteFile(handle, pIn, static_cast(bytes), &written, NULL); return written == bytes; }; io->funcClose = [](void *handle) { CloseHandle(handle); }; #endif io->funcSeek = [](void * /*handle*/, IoSeekType /*type*/, long long /*offset*/) -> bool { return false; }; io->funcTell = [](void * /*handle*/) -> long long { return -1; }; return true; } //----------------------------------------------------------------------------------------------------------- bool IoOpenVec(Io *io, std::vector *pVec) { struct VecHandle { std::vector *pVec; size_t pos; }; if (!io) return false; VecHandle *pH = new VecHandle; pH->pVec = pVec; pH->pos = 0; io->handle = pH; if (!io->handle) { delete pH; return false; } io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { VecHandle *pVec = reinterpret_cast(handle); if (pVec->pos + bytes > pVec->pVec->size()) return false; memcpy(pOut, &pVec->pVec->at(pVec->pos), bytes); pVec->pos += bytes; return true; }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { VecHandle *pVec = reinterpret_cast(handle); if (pVec->pos + bytes > pVec->pVec->size()) pVec->pVec->resize(pVec->pos + bytes); memcpy(&pVec->pVec->at(pVec->pos), pIn, bytes); pVec->pos += bytes; return true; }; io->funcSeek = [](void *handle, IoSeekType type, long long offset) -> bool { VecHandle *pVec = reinterpret_cast(handle); switch (type) { case IO_SEEK_SET: if (offset > static_cast(pVec->pVec->size()) || offset < 0) return false; pVec->pos = static_cast(offset); break; case IO_SEEK_REL: if (static_cast(pVec->pos) + offset > static_cast(pVec->pVec->size()) || pVec->pos + offset < 0) return false; pVec->pos = static_cast(static_cast(pVec->pos) + offset); break; case IO_SEEK_END: if (offset > static_cast(pVec->pVec->size()) || offset < 0) return false; pVec->pos = static_cast(static_cast(pVec->pVec->size()) - offset); break; } return true; }; io->funcTell = [](void *handle) -> long long { VecHandle *pVec = reinterpret_cast(handle); return static_cast(pVec->pos); }; io->funcClose = [](void *handle) { delete reinterpret_cast(handle); }; return true; } bool IoOpenFileInMem(Io *io, const std::string &filename, IoAccessType access) { struct FileMemHandle { Io ioMem; std::vector mem; bool closeWrite; }; if (!io) return false; FileMemHandle *pH = new FileMemHandle; if (!IoOpenVec(&pH->ioMem, &pH->mem)) { delete pH; return false; } pH->closeWrite = access == IoAccessType::IO_ACCESS_RW_NEW || access == IoAccessType::IO_ACCESS_RW; io->handle = pH; if (!io->handle) { delete pH; return false; } if (access == IoAccessType::IO_ACCESS_R || access == IoAccessType::IO_ACCESS_RW) { Io ioFile; if (!IoOpenFile(&ioFile, filename, access)) { delete pH; return false; } size_t size = static_cast(IoSize(&ioFile)); pH->mem.resize(size); IoReadRaw(&ioFile, size, &pH->mem[0]); IoClose(&ioFile); } io->funcRead = [](void *handle, size_t bytes, void *pOut) -> bool { FileMemHandle *pVec = reinterpret_cast(handle); return pVec->ioMem.funcRead(pVec->ioMem.handle, bytes, pOut); }; io->funcWrite = [](void *handle, size_t bytes, const void *pIn) -> bool { FileMemHandle *pVec = reinterpret_cast(handle); return pVec->ioMem.funcWrite(pVec->ioMem.handle, bytes, pIn); }; io->funcSeek = [](void *handle, IoSeekType type, long long offset) -> bool { FileMemHandle *pVec = reinterpret_cast(handle); return pVec->ioMem.funcSeek(pVec->ioMem.handle, type, offset); }; io->funcTell = [](void *handle) -> long long { FileMemHandle *pVec = reinterpret_cast(handle); return pVec->ioMem.funcTell(pVec->ioMem.handle); }; io->funcClose = [](void *handle) { FileMemHandle *pVec = reinterpret_cast(handle); pVec->ioMem.funcClose(pVec->ioMem.handle); delete reinterpret_cast(handle); }; return true; } //----------------------------------------------------------------------------------------------------------- void IoClose(Io *io) { if (io && io->handle) { io->funcClose(io->handle); io->handle = 0; io->funcRead = 0; io->funcWrite = 0; io->funcSeek = 0; io->funcTell = 0; io->funcClose = 0; } } //----------------------------------------------------------------------------------------------------------- // Reads until null character or eof // Error if buffer too small, null pointer, or eof at the beginning bool IoReadText8(const Io *io, char *buf, int bufSize) { if (bufSize <= 0 || !buf || !io) return false; bool canHold = false; bool eof = true; while (bufSize > 0) { bool canRead = io->funcRead(io->handle, 1, buf); if (canRead) eof = false; if (!canRead || !*buf) { *buf = 0; // in case it was end of stream canHold = true; break; } buf++; bufSize--; } return canHold && !eof; } //----------------------------------------------------------------------------------------------------------- // Reads until null character, eof, or eol // Error if buffer too small, null pointer, or eof at the beginning bool IoReadLine8(const Io *io, char *buf, int bufSize) { if (bufSize <= 0 || !buf || !io) return false; bool canHold = false; bool eof = true; while (bufSize > 0) { bool canRead = io->funcRead(io->handle, 1, buf); if (canRead) eof = false; if (!canRead || !*buf || *buf == '\r' || *buf == '\n') { if (*buf == '\r' || *buf == '\n') { // Read extra character if it's a two byte newline sequence char c2 = 0; if (io->funcRead(io->handle, 1, &c2)) { if ((*buf == '\r' && c2 != '\n') || (*buf == '\n' && c2 != '\r')) IoSeek(io, IO_SEEK_REL, -1); } } *buf = 0; canHold = true; break; } buf++; bufSize--; } return canHold && !eof; } //----------------------------------------------------------------------------------------------------------- // Reads until null character, eof, or len if >= 0 // len = -1 reads until null character or end of stream // Error if null pointer or eof at the beginning bool IoReadText8(const Io *io, std::string *pOut, int len) { if (!pOut || !io) return false; char buf[256]; // buffered concatenation to std::string int i = 0; *pOut = ""; bool eof = true; while (len != 0) { bool canRead = io->funcRead(io->handle, 1, &buf[i]); if (canRead) eof = false; if (!canRead || !buf[i]) break; i++; if (i == 255) { buf[i] = 0; i = 0; *pOut += buf; } len--; } buf[i] = 0; *pOut += buf; return !eof || len == 0; } //----------------------------------------------------------------------------------------------------------- // Reads until null character, eof, or eol // Error if null pointer or eof at the beginning bool IoReadLine8(const Io *io, std::string *pOut) { if (!pOut || !io) return false; // 256 would be fine, but we'll need 1 extra for if we hit EOL and need to read 1 extra character char buf[257]; int i = 0; *pOut = ""; bool eof = true; while (1) { bool canRead = io->funcRead(io->handle, 1, &buf[i]); if (canRead) eof = false; if (!canRead || !buf[i] || buf[i] == '\r' || buf[i] == '\n') { if (buf[i] == '\r' || buf[i] == '\n') { // Read extra character if it's a two byte newline sequence char c2 = 0; if (io->funcRead(io->handle, 1, &c2)) { if ((buf[i] == '\r' && c2 != '\n') || (buf[i] == '\n' && c2 != '\r')) IoSeek(io, IO_SEEK_REL, -1); } } break; } i++; if (i == 255) { buf[i] = 0; i = 0; *pOut += buf; } } buf[i] = 0; *pOut += buf; return !eof; } //----------------------------------------------------------------------------------------------------------- void IoWriteSVle(const Io *io, const char *format, va_list v) { while (*format) { switch (*format++) { case ' ': break; case '1': { int *x = va_arg(v, int *); IoWrite8(io, *x); break; } case '2': { int *x = va_arg(v, int *); IoWrite16le(io, *x); break; } case '4': { int *x = va_arg(v, int *); IoWrite32le(io, *x); break; } case 'f': case 'F': { float *x = va_arg(v, float *); IoWriteFle(io, *x); break; } case 'd': case 'D': { double *x = va_arg(v, double *); IoWriteDle(io, *x); break; } default: va_end(v); return; } } } //----------------------------------------------------------------------------------------------------------- void IoWriteSle(const Io *io, const char *format, ...) { va_list v; va_start(v, format); IoWriteSVle(io, format, v); va_end(v); } //----------------------------------------------------------------------------------------------------------- void IoWriteSVbe(const Io *io, const char *format, va_list v) { while (*format) { switch (*format++) { case ' ': break; case '1': { int *x = va_arg(v, int *); IoWrite8(io, *x); break; } case '2': { int *x = va_arg(v, int *); IoWrite16be(io, *x); break; } case '4': { int *x = va_arg(v, int *); IoWrite32be(io, *x); break; } case 'f': case 'F': { float *x = va_arg(v, float *); IoWriteFbe(io, *x); break; } case 'd': case 'D': { double *x = va_arg(v, double *); IoWriteDbe(io, *x); break; } default: return; } } } //----------------------------------------------------------------------------------------------------------- void IoWriteSbe(const Io *io, const char *format, ...) { va_list v; va_start(v, format); IoWriteSVbe(io, format, v); va_end(v); } //----------------------------------------------------------------------------------------------------------- void IoWriteText8(const Io *io, const char *in, bool includeNull) { IoWriteRaw(io, strlen(in) + (includeNull ? 1 : 0), in); } //----------------------------------------------------------------------------------------------------------- void IoWriteLine8(const Io *io, const char *in) { size_t bytes = strlen(in); IoWriteRaw(io, bytes, in); if (in[bytes - 1] != '\r' && in[bytes - 1] != '\n') { IoWrite8(io, '\r'); IoWrite8(io, '\n'); } } //----------------------------------------------------------------------------------------------------------- void BitInvertBuf(void *pData, size_t numBytes) { unsigned int *pData32 = reinterpret_cast(pData); for (size_t i = numBytes >> 2; i--; pData32++) *pData32 = ~(*pData32); unsigned char *pData8 = reinterpret_cast(pData32); for (size_t i = numBytes & 3; i--; pData8++) *pData8 = ~(*pData8); } //----------------------------------------------------------------------------------------------------------- void BitReverseBuf8(void *pData, size_t numBytes) { unsigned int *pData32 = reinterpret_cast(pData); for (size_t i = numBytes >> 2; i--; pData32++) { // Reverse 4 bytes at a time in parallel *pData32 = ((*pData32 & 0xaaaaaaaa) >> 1) | ((*pData32 & 0x55555555) << 1); *pData32 = ((*pData32 & 0xcccccccc) >> 2) | ((*pData32 & 0x33333333) << 2); *pData32 = ((*pData32 & 0xf0f0f0f0) >> 4) | ((*pData32 & 0x0f0f0f0f) << 4); } unsigned char *pData8 = reinterpret_cast(pData32); for (size_t i = numBytes & 3; i--; pData8++) *pData8 = BitReverse8(*pData8); } //----------------------------------------------------------------------------------------------------------- void ByteSwapBuf16(void *pData, size_t count) { unsigned short *pData16 = reinterpret_cast(pData); for (size_t i = 0; i < count; i++) ByteSwap16(pData16[i]); } //----------------------------------------------------------------------------------------------------------- void ByteSwapBuf32(void *pData, size_t count) { unsigned int *pData32 = reinterpret_cast(pData); for (size_t i = 0; i < count; i++) ByteSwap32(pData32[i]); } //----------------------------------------------------------------------------------------------------------- bool FsCreateDir(const std::string &fullPathDir) { #ifdef _WIN32 // http://mfctips.com/2012/03/26/best-way-to-check-if-file-or-directory-exists/ std::wstring utf16 = utf8to16(fullPathDir); DWORD attrib = GetFileAttributesW(utf16.c_str()); DWORD err = GetLastError(); if (attrib == INVALID_FILE_ATTRIBUTES && (err == ERROR_PATH_NOT_FOUND || err == ERROR_FILE_NOT_FOUND)) #else struct stat data; if (stat(fullPathDir.c_str(), &data) != 0) #endif { size_t index = fullPathDir.find_last_of("/\\"); if (index == fullPathDir.length() - 1) index = fullPathDir.find_last_of("/\\", index - 1); if (index != std::string::npos && !FsCreateDir(std::move(fullPathDir.substr(0, index)))) return false; #ifdef _WIN32 if (!CreateDirectoryW(utf16.c_str(), NULL)) return false; #else if (mkdir(fullPathDir.c_str(), S_IRWXU) != 0) return false; #endif } return true; } //----------------------------------------------------------------------------------------------------------- bool FsDeleteDir(const std::string &fullPathDir) { #ifdef _WIN32 auto funcDel = [](const std::string &fullPath, bool isFile, void * /*user*/) -> bool { if (isFile) return FsDeleteFile(fullPath); return RemoveDirectoryW(utf8to16(fullPath).c_str()) != 0; }; #else auto funcDel = [](const std::string &fullPath, bool isFile, void * /*user*/) -> bool { if (isFile) return unlink(fullPath.c_str()) == 0; return rmdir(fullPath.c_str()) == 0; }; #endif return FsWalk(fullPathDir, FS_WALK_DEPTH, funcDel, 0); } //----------------------------------------------------------------------------------------------------------- bool FsDeleteFile(const std::string &fullPathFile) { #ifdef _WIN32 // http://stackoverflow.com/questions/3764072/c-win32-how-to-wait-for-a-pending-delete-to-complete // Looks like a problem specific to Windows std::wstring tempFile16 = std::move(utf8to16(FsGetTempFile())); if (MoveFileExW(utf8to16(fullPathFile).c_str(), tempFile16.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED) == 0) return false; return DeleteFileW(tempFile16.c_str()) != 0; #else return unlink(fullPathFile.c_str()) == 0; #endif } //----------------------------------------------------------------------------------------------------------- bool FsRename(const std::string &fullPathOld, const std::string &fullPathNew) { #ifdef _WIN32 return MoveFileExW(utf8to16(fullPathOld).c_str(), utf8to16(fullPathNew).c_str(), MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED) != 0; #else return rename(fullPathOld.c_str(), fullPathNew.c_str()) != -1; #endif } //----------------------------------------------------------------------------------------------------------- bool FsCopyFile(const std::string &fullPathSrc, const std::string &fullPathDest) { #ifdef _WIN32 return CopyFileW(utf8to16(fullPathSrc).c_str(), utf8to16(fullPathDest).c_str(), FALSE) != 0; #else char buf[32768]; size_t size; int source = open(fullPathSrc.c_str(), O_RDONLY, 0); if (source == -1) return false; int dest = open(fullPathDest.c_str(), O_WRONLY | O_CREAT, 0644); if (dest == -1) { close(source); return false; } bool ret = true; while ((size = read(source, buf, 32768)) > 0) { if (write(dest, buf, size) != size) { ret = false; break; } } close(source); close(dest); return ret; #endif } //----------------------------------------------------------------------------------------------------------- bool FsSetWorkingDir(const std::string &fullPathDir) { #ifdef _WIN32 return SetCurrentDirectoryW(utf8to16(fullPathDir).c_str()) != 0; #else return chdir(fullPathDir.c_str()) != -1; #endif } //----------------------------------------------------------------------------------------------------------- bool FsIsDir(const std::string &fullPath) { #ifdef _WIN32 DWORD attrib = GetFileAttributesW(utf8to16(fullPath).c_str()); return attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY); #else struct stat data; return stat(fullPath.c_str(), &data) == 0 && (S_ISDIR(data.st_mode)); #endif } //----------------------------------------------------------------------------------------------------------- bool FsIsFile(const std::string &fullPath) { #ifdef _WIN32 DWORD attrib = GetFileAttributesW(utf8to16(fullPath).c_str()); return attrib != INVALID_FILE_ATTRIBUTES && !(attrib & FILE_ATTRIBUTE_DIRECTORY); #else struct stat data; return stat(fullPath.c_str(), &data) == 0 && (S_ISREG(data.st_mode)); #endif } //----------------------------------------------------------------------------------------------------------- bool FsIsPipe(const std::string &pipeName) { #ifdef _WIN32 return WaitNamedPipeW(utf8to16("\\\\.\\pipe\\" + pipeName).c_str(), 0) != 0; #else struct stat data; return stat(pipeName.c_str(), &data) == 0 && (S_ISFIFO(data.st_mode)); #endif } //----------------------------------------------------------------------------------------------------------- bool FsIsReadOnly(const std::string &fullPath) { #ifdef _WIN32 DWORD attrib = GetFileAttributesW(utf8to16(fullPath).c_str()); return attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY); #else struct stat data; return stat(fullPath.c_str(), &data) == 0 && access(fullPath.c_str(), W_OK) == -1; #endif } //----------------------------------------------------------------------------------------------------------- bool FsIsHidden(const std::string &fullPath) { #ifdef _WIN32 DWORD attrib = GetFileAttributesW(utf8to16(fullPath).c_str()); return attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_HIDDEN); #else // Linux hidden files simply begin with a . // Need extension detection in case the file name is something like .abcdefg std::string file = FsGetPathFile(fullPath, true); return !file.empty() && file.at(0) == '.'; #endif } //----------------------------------------------------------------------------------------------------------- bool FsIsRelative(const std::string &path) { const char *buf = path.c_str(); if (buf && buf[0] == '.') { int n = buf[1] == '.' ? 2 : 1; return buf[n] == '/' || buf[n] == '\\'; } return false; } //----------------------------------------------------------------------------------------------------------- time_t FsGetTimeAccess(const std::string &fullPath) { #ifdef _WIN32 WIN32_FILE_ATTRIBUTE_DATA data; if (!GetFileAttributesExW(utf8to16(fullPath).c_str(), GetFileExInfoStandard, &data)) return 0; unsigned long long hns1601 = (static_cast(data.ftLastAccessTime.dwHighDateTime) << 32) | data.ftLastAccessTime.dwLowDateTime; return static_cast(hns1601 / 10000000ULL - 11644473600ULL); #else struct stat data; stat(fullPath.c_str(), &data); return data.st_atime; #endif } //----------------------------------------------------------------------------------------------------------- time_t FsGetTimeMod(const std::string &fullPath) { #ifdef _WIN32 WIN32_FILE_ATTRIBUTE_DATA data; if (!GetFileAttributesExW(utf8to16(fullPath).c_str(), GetFileExInfoStandard, &data)) return 0; unsigned long long hns1601 = (static_cast(data.ftLastWriteTime.dwHighDateTime) << 32) | data.ftLastWriteTime.dwLowDateTime; return static_cast(hns1601 / 10000000ULL - 11644473600ULL); #else struct stat data; stat(fullPath.c_str(), &data); return data.st_mtime; #endif } //----------------------------------------------------------------------------------------------------------- time_t FsGetTimeCreate(const std::string &fullPath) { #ifdef _WIN32 WIN32_FILE_ATTRIBUTE_DATA data; if (!GetFileAttributesExW(utf8to16(fullPath).c_str(), GetFileExInfoStandard, &data)) return 0; unsigned long long hns1601 = (static_cast(data.ftCreationTime.dwHighDateTime) << 32) | data.ftCreationTime.dwLowDateTime; return static_cast(hns1601 / 10000000ULL - 11644473600ULL); #else struct stat data; stat(fullPath.c_str(), &data); return data.st_ctime; #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetTempFile() { #ifdef _WIN32 wchar_t path[FS_PATH_MAX_LEN]; wchar_t file[FS_PATH_MAX_LEN]; GetTempPathW(FS_PATH_MAX_LEN, path); GetTempFileNameW(path, L"saw", 0, file); return std::move(utf16to8(file)); #else char path[] = ".sawXXXXXX"; int fd = mkstemp(path); if (fd == -1) return ""; close(fd); return std::move(std::string(path)); #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetTempDir(bool includeSlash) { #ifdef _WIN32 wchar_t path[FS_PATH_MAX_LEN]; GetTempPathW(FS_PATH_MAX_LEN, path); return std::move(utf16to8(path) + (includeSlash ? "\\" : "")); #else const char *path = getenv("TMPDIR"); if (path == 0) path = "/tmp"; return std::move(std::string(path) + (includeSlash ? "/" : "")); #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetWorkingDir(bool includeSlash) { #ifdef _WIN32 wchar_t path[FS_PATH_MAX_LEN]; GetCurrentDirectoryW(FS_PATH_MAX_LEN, path); return std::move(utf16to8(path) + (includeSlash ? "\\" : "")); #else char *path = getcwd(nullptr, 0); if (!path) return std::string(); std::string spath(path); free(path); return std::move(spath + (includeSlash ? "/" : "")); #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetUserDir(const std::string &appName, bool includeSlash) { #ifdef _WIN32 wchar_t path[FS_PATH_MAX_LEN]; // CSIDL_APPDATA is roaming (user-specific) // CSIDL_LOCAL_APPDATA is local (user-specific machine-specific) // CSIDL_COMMON_APPDATA is local (machine-specific) SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, SHGFP_TYPE_CURRENT, path); return std::move(utf16to8(path) + "\\" + appName + (includeSlash ? "\\" : "")); #else const char *base = getenv("XDG_CONFIG_HOME"); if (!base) base = getenv("HOME"); if (!base) base = getpwuid(getuid())->pw_dir; return std::move(base + std::string("/.config/") + appName + (includeSlash ? "/" : "")); #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetAppDir(bool includeSlash) { return std::move(FsGetPathDir(FsGetAppPath(), includeSlash)); } //----------------------------------------------------------------------------------------------------------- std::string FsGetAppPath() { #ifdef _WIN32 wchar_t path[FS_PATH_MAX_LEN]; DWORD len = GetModuleFileNameW(0, path, FS_PATH_MAX_LEN); if (len <= 0 || len == FS_PATH_MAX_LEN) path[0] = 0; return std::move(utf16to8(path)); #elif defined(__linux) || defined(__NetBSD__) || defined(__OpenBSD__) char path[FS_PATH_MAX_LEN]; char link[32]; # ifdef __linux snprintf(link, sizeof(link), "/proc/%d/exe", getpid()); # else snprintf(link, sizeof(link), "/proc/%d/file", getpid()); # endif ssize_t len = readlink(link, path, FS_PATH_MAX_LEN); if (len <= 0 || len == FS_PATH_MAX_LEN) path[0] = 0; path[len] = 0; // readlink doesn't null terminate return path; #elif defined(__FreeBSD__) char path[FS_PATH_MAX_LEN]; int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; size_t len = FS_PATH_MAX_LEN - 1; if (sysctl(name, 4, path, &len, NULL, 0) != 0) path[0] = 0; return path; #elif defined(__APPLE__) char path[FS_PATH_MAX_LEN]; uint32_t bufSize = FS_PATH_MAX_LEN; if (_NSGetExecutablePath(path, &bufSize) != 0) path[0] = 0; return path; #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetModuleDir(bool includeSlash) { return std::move(FsGetPathDir(FsGetModulePath(), includeSlash)); } //----------------------------------------------------------------------------------------------------------- std::string FsGetModulePath() { #ifdef _WIN32 wchar_t path[FS_PATH_MAX_LEN]; HMODULE handle = 0; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)FsGetModulePath, &handle); DWORD len = GetModuleFileNameW(handle, path, FS_PATH_MAX_LEN); if (len <= 0 || len == FS_PATH_MAX_LEN) path[0] = 0; return std::move(utf16to8(path)); #else return FsGetAppPath(); #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetRelPath(const std::string &fullPathFrom, const std::string &fullPathTo) { const char *bufFrom = fullPathFrom.c_str(); const char *bufTo = fullPathTo.c_str(); size_t lenFrom = fullPathFrom.size(); size_t lenTo = fullPathTo.size(); if (!lenFrom || !lenTo) return std::string(); #ifdef _WIN32 else { // No relative path if the drives are different char lowFrom = bufFrom[0] >= 'A' && bufFrom[0] <= 'Z' ? bufFrom[0] | 0x60 : bufFrom[0]; char lowTo = bufTo[0] >= 'A' && bufTo[0] <= 'Z' ? bufTo[0] | 0x60 : bufTo[0]; if (lowFrom != lowTo) return std::string(); } const char *dot = ".\\"; const char *dotdot = "..\\"; const char *delim = "\\"; #else const char *dot = "./"; const char *dotdot = "../"; const char *delim = "/"; #endif size_t posMatch = 0; size_t posFrom = 0; while (posFrom != lenFrom) { posFrom = fullPathFrom.find_first_of("\\/", posMatch); if (posFrom == std::string::npos) posFrom = lenFrom; if (posFrom > lenTo) break; #ifdef _WIN32 // Windows FS is case insensitive if (_strnicmp(bufFrom + posMatch, bufTo + posMatch, posFrom - posMatch) != 0) break; #else // Non-windows FS is case sensitive if (strncmp(bufFrom + posMatch, bufTo + posMatch, posFrom - posMatch) != 0) break; #endif posMatch = fullPathFrom.find_first_not_of("\\/", posFrom); if (posMatch == std::string::npos) posMatch = lenFrom; } std::string output; output.reserve(FS_PATH_MAX_LEN); if (posFrom == lenFrom) { output.assign(dot); } else { output.append(dotdot); while (posFrom != std::string::npos) { posFrom = fullPathFrom.find_first_not_of("\\/", posFrom); if (posFrom != std::string::npos) { posFrom = fullPathFrom.find_first_of("\\/", posFrom); output.append(dotdot); } } } posMatch = fullPathTo.find_first_not_of("\\/", posMatch); while (posMatch != std::string::npos) { size_t posTo = fullPathTo.find_first_of("\\/", posMatch); if (posTo == std::string::npos) posTo = lenTo; output.append(bufTo + posMatch, bufTo + posTo); posMatch = fullPathTo.find_first_not_of("\\/", posTo); if (posMatch == std::string::npos) { if (posTo != lenTo) output.append(delim); } }; return std::move(output); } //----------------------------------------------------------------------------------------------------------- std::string FsGetFullPath(const std::string &path) { #ifdef _WIN32 wchar_t out16[FS_PATH_MAX_LEN]; GetFullPathNameW(utf8to16(path).c_str(), FS_PATH_MAX_LEN - 1, out16, 0); return std::move(utf16to8(out16)); #else char *out = realpath(path.c_str(), nullptr); std::string sout(out); free(out); if (FsIsDir(sout)) sout += '/'; return std::move(sout); #endif } //----------------------------------------------------------------------------------------------------------- std::string FsGetPathDir(const std::string &fullPath, bool includeSlash) { std::string dir; auto lastSlash = fullPath.find_last_of("/\\"); if (lastSlash != std::string::npos) dir = fullPath.substr(0, lastSlash + (includeSlash ? 1 : 0)); return std::move(dir); } //----------------------------------------------------------------------------------------------------------- std::string FsGetPathFile(const std::string &fullPath, bool includeExt) { std::string file; auto lastDot = fullPath.find_last_of('.'); auto fileStart = fullPath.find_last_of("/\\"); if (lastDot != std::string::npos && fileStart != std::string::npos && lastDot < fileStart) lastDot = std::string::npos; if (fileStart != std::string::npos) fileStart++; else fileStart = 0; file = fullPath.substr(fileStart, (includeExt ? fullPath.length() : lastDot) - fileStart); return std::move(file); } //----------------------------------------------------------------------------------------------------------- std::string FsGetPathExt(const std::string &fullPath, bool includeDot) { std::string ext; auto lastDot = fullPath.find_last_of('.'); auto lastSlash = fullPath.find_last_of("/\\"); if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastDot > lastSlash)) { if (!includeDot) lastDot++; ext = fullPath.substr(lastDot, fullPath.length() - lastDot); } return std::move(ext); } //----------------------------------------------------------------------------------------------------------- unsigned long long FsGetFileSize(const std::string &fullPathFile) { #ifdef _WIN32 WIN32_FILE_ATTRIBUTE_DATA data; if (!GetFileAttributesExW(utf8to16(fullPathFile).c_str(), GetFileExInfoStandard, &data)) return 0; return (static_cast(data.nFileSizeHigh) << 32) | data.nFileSizeLow; #else struct stat64 data; stat64(fullPathFile.c_str(), &data); return data.st_size; #endif } //----------------------------------------------------------------------------------------------------------- FsWatchType FsWatch(const std::string &fullPath, int *state) { time_t timeMod = FsGetTimeMod(fullPath); #ifdef _WIN32 std::wstring utf16 = utf8to16(fullPath); DWORD attrib = GetFileAttributesW(utf16.c_str()); bool exists = attrib != INVALID_FILE_ATTRIBUTES; #else struct stat data; bool exists = stat(fullPath.c_str(), &data) == 0; #endif int hash = static_cast(((timeMod >> 16) ^ timeMod) << 16); if (exists) hash |= 0x00008000; int oldState = *state; if (oldState != hash) { *state = hash; if (oldState) { bool oldExists = !!(oldState & 0x00008000); if (oldExists != exists) return exists ? FS_WATCH_CREATED : FS_WATCH_REMOVED; return FS_WATCH_CHANGED; } } return FS_WATCH_NONE; } //----------------------------------------------------------------------------------------------------------- #ifndef _WIN32 static FsWalkFunc gFsWalkCallback; static void *gFsWalkUser; #endif bool FsWalk(const std::string &fullPath, FsWalkType type, FsWalkFunc callback, void *user) { #ifdef _WIN32 if (type == FS_WALK_NORMAL) { if (!callback(fullPath, false, user)) return false; } WIN32_FIND_DATAW fd; std::wstring fullPath16 = std::move(utf8to16(fullPath)); HANDLE hEnum = FindFirstFileExW((fullPath16 + L"\\*").c_str(), FindExInfoBasic, &fd, FindExSearchNameMatch, 0, 0); if (hEnum == INVALID_HANDLE_VALUE) return false; while (1) { if (fd.cFileName[0] != L'.' || (fd.cFileName[1] != 0 && (fd.cFileName[1] != L'.' || fd.cFileName[2] != 0))) { bool isDir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); std::string nextPath = std::move(utf16to8(fullPath16 + L'\\' + fd.cFileName)); if (isDir && type != FS_WALK_TOP) { if (!FsWalk(nextPath, type, callback, user)) { FindClose(hEnum); return false; } } else if (!callback(nextPath, true, user)) { FindClose(hEnum); return false; } } if (FindNextFileW(hEnum, &fd) == FALSE) { if (GetLastError() != ERROR_NO_MORE_FILES) { FindClose(hEnum); return false; } break; } } FindClose(hEnum); if (type == FS_WALK_DEPTH) return callback(fullPath, false, user); return true; #else if (type == FS_WALK_TOP) { struct dirent *dir = nullptr; DIR *d = opendir(fullPath.c_str()); if (d) { while ((dir = readdir(d)) != NULL) callback(dir->d_name, dir->d_type != DT_DIR, user); closedir(d); return true; } return false; } gFsWalkCallback = callback; gFsWalkUser = user; auto funcFtw = [](const char *nextPath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -> int { if (!gFsWalkCallback(nextPath, typeflag == FTW_F, gFsWalkUser)) return -1; return 0; }; int flags = FTW_PHYS | (type == FS_WALK_DEPTH ? FTW_DEPTH : 0); return nftw(fullPath.c_str(), funcFtw, 64, flags) == 0; #endif } //----------------------------------------------------------------------------------------------------------- bool FsEnumSerial(FsWalkFunc callback, void *user) { #ifdef _WIN32 HKEY hKey = 0; int ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", NULL, KEY_READ, &hKey); // Might not exist if (ret != ERROR_SUCCESS) return true; DWORD numVal = 0; ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &numVal, NULL, NULL, NULL, NULL); if (ret != ERROR_SUCCESS) { RegCloseKey(hKey); return false; } for (DWORD i = 0; i < numVal; i++) { char name[512]; DWORD size = 512; DWORD type = 0; unsigned char data[512]; DWORD sizeData = 512; ret = RegEnumValueA(hKey, i, name, &size, NULL, &type, data, &sizeData); if (ret == ERROR_SUCCESS && type == REG_SZ) callback(reinterpret_cast(data), true, user); } RegCloseKey(hKey); #else struct dirent **names = nullptr; int n = scandir("/sys/class/tty/", &names, NULL, NULL); if (n < 0) return false; while (n--) { if (names[n]->d_name[0] != '.') { std::string testDev = std::string("/sys/class/tty/") + names[n]->d_name; std::string driver = testDev + "/device"; struct stat data; if (lstat(driver.c_str(), &data) == 0 && S_ISLNK(data.st_mode)) { char path[FS_PATH_MAX_LEN]; driver += "/driver"; ssize_t len = readlink(driver.c_str(), path, FS_PATH_MAX_LEN); if (len <= 0 || len == FS_PATH_MAX_LEN) path[0] = 0; else { path[len] = 0; // readlink doesn't null terminate driver = basename(path); // Has a driver, check device std::string dev = std::string("/dev/") + names[n]->d_name; if (driver == "serial8250") { int fd = open(dev.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY); if (fd >= 0) { struct serial_struct info; if (ioctl(fd, TIOCGSERIAL, &info) == 0) { if (info.type != PORT_UNKNOWN) callback(names[n]->d_name, true, user); } close(fd); } } else callback(names[n]->d_name, true, user); } } } free(names[n]); } free(names); #endif return true; } //----------------------------------------------------------------------------------------------------------- bool FsLoadTextFile(const std::string &filename, std::string *pOut) { Io io; if (!IoOpenFile(&io, filename, IO_ACCESS_R)) return false; bool ret = IoReadText8(&io, pOut, -1); IoClose(&io); return ret; } //----------------------------------------------------------------------------------------------------------- bool FsLoadBinFile(const std::string &filename, std::vector *pVec) { Io io; if (!IoOpenFile(&io, filename, IO_ACCESS_R)) return false; long long size = IoSize(&io); pVec->resize(static_cast(size)); bool ret = IoReadRaw(&io, size, &((*pVec)[0])); IoClose(&io); return ret; } } // namespace #endif // SAW_IO_IMPLEMENTATION