diff options
author | elfmz <fenix1905@tut.by> | 2021-12-31 12:15:36 +0300 |
---|---|---|
committer | elfmz <fenix1905@tut.by> | 2021-12-31 12:21:48 +0300 |
commit | 6b7b5f7fe480107373a6ef3cbd6ee41378c93642 (patch) | |
tree | 1af3f15ecc76c47749f74f38302cb33e0a3796e1 /far2l/src/mix | |
parent | a14dc1a81c797928d4f1b7d6a6b46ecc63f98308 (diff) |
split /aux to /mix and /base
Diffstat (limited to 'far2l/src/mix')
29 files changed, 9962 insertions, 0 deletions
diff --git a/far2l/src/mix/MountInfo.cpp b/far2l/src/mix/MountInfo.cpp new file mode 100644 index 00000000..96368b45 --- /dev/null +++ b/far2l/src/mix/MountInfo.cpp @@ -0,0 +1,209 @@ +#include "headers.hpp" + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <fcntl.h> +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__CYGWIN__) +# include <errno.h> +# include <sys/mount.h> +#else +# include <sys/statfs.h> +# include <linux/fs.h> +#endif +#include <fstream> +#include "MountInfo.h" +#include <ScopeHelpers.h> +#include <os_call.hpp> + +static struct FSMagic { + const char *name; + unsigned int magic; +} s_fs_magics[] = { +{"ADFS", 0xadf5}, +{"AFFS", 0xadff}, +{"AFS", 0x5346414F}, +{"AUTOFS", 0x0187}, +{"CODA", 0x73757245}, +{"CRAMFS", 0x28cd3d45}, /* some random number */ +{"CRAMFS", 0x453dcd28}, /* magic number with the wrong endianess */ +{"DEBUGFS", 0x64626720}, +{"SECURITYFS", 0x73636673}, +{"SELINUX", 0xf97cff8c}, +{"SMACK", 0x43415d53}, /* "SMAC" */ +{"RAMFS", 0x858458f6}, /* some random number */ +{"TMPFS", 0x01021994}, +{"HUGETLBFS", 0x958458f6}, /* some random number */ +{"SQUASHFS", 0x73717368}, +{"ECRYPTFS", 0xf15f}, +{"EFS", 0x414A53}, +{"EXT2", 0xEF53}, +{"EXT3", 0xEF53}, +{"XENFS", 0xabba1974}, +{"EXT4", 0xEF53}, +{"BTRFS", 0x9123683E}, +{"NILFS", 0x3434}, +{"F2FS", 0xF2F52010}, +{"HPFS", 0xf995e849}, +{"ISOFS", 0x9660}, +{"JFFS2", 0x72b6}, +{"PSTOREFS", 0x6165676C}, +{"EFIVARFS", 0xde5e81e4}, +{"HOSTFS", 0x00c0ffee}, + +{"MINIX", 0x137F}, /* minix v1 fs, 14 char names */ +{"MINIX", 0x138F}, /* minix v1 fs, 30 char names */ +{"MINIX2", 0x2468}, /* minix v2 fs, 14 char names */ +{"MINIX2", 0x2478}, /* minix v2 fs, 30 char names */ +{"MINIX3", 0x4d5a}, /* minix v3 fs, 60 char names */ + +{"MSDOS", 0x4d44}, /* MD */ +{"NCP", 0x564c}, /* Guess, what 0x564c is :-) */ +{"NFS", 0x6969}, +{"OPENPROM", 0x9fa1}, +{"QNX4", 0x002f}, /* qnx4 fs detection */ +{"QNX6", 0x68191122}, /* qnx6 fs detection */ + +{"REISERFS", 0x52654973}, /* used by gcc */ + /* used by file system utilities that + look at the superblock, etc. */ +{"SMB", 0x517B}, +{"CGROUP", 0x27e0eb}, + + +{"STACK_END", 0x57AC6E9D}, + +{"TRACEFS", 0x74726163}, + +{"V9FS", 0x01021997}, + +{"BDEVFS", 0x62646576}, +{"BINFMTFS", 0x42494e4d}, +{"DEVPTS", 0x1cd1}, +{"FUTEXFS", 0xBAD1DEA}, +{"PIPEFS", 0x50495045}, +{"PROC", 0x9fa0}, +{"SOCKFS", 0x534F434B}, +{"SYSFS", 0x62656572}, +{"USBDEVICE", 0x9fa2}, +{"MTD_INODE_FS", 0x11307854}, +{"ANON_INODE_FS", 0x09041934}, +{"BTRFS_TEST", 0x73727279}, +{"NSFS", 0x6e736673}, +{"BPF_FS", 0xcafe4a11}}; + + +///////////////////////////////////////////////////////////////////////////////////////////// + +MountInfo::MountInfo() +{ +#ifdef __linux__ + // force-enable multi-threaded disk access: echo e > ~/.config/far2l/mtfs + // force-disable multi-threaded disk access: echo d > ~/.config/far2l/mtfs + FDScope fd(open(InMyConfig("mtfs").c_str(), O_RDONLY)); + if (fd.Valid()) { + if (os_call_ssize(read, (int)fd, (void *)&_mtfs, sizeof(_mtfs)) == 0) { + _mtfs = 'e'; + } + fprintf(stderr, "%s: _mtfs='%c'\n", __FUNCTION__, _mtfs); + } + + std::ifstream is("/proc/mounts"); + if (is.is_open()) { + std::string line, sys_path; + std::vector<std::string> parts; + while (std::getline(is, line)) { + parts.clear(); + StrExplode(parts, line, " \t"); + if (parts.size() > 1 && StrStartsFrom(parts[1], "/")) { + bool multi_thread_friendly; + if (StrStartsFrom(parts[0], "/dev/")) { + sys_path = "/sys/block/"; + sys_path+= parts[0].substr(5); + // strip device suffix, sda1 -> sda, mmcblk0p1 -> mmcblk0 + struct stat s; + while (sys_path.size() > 12 && stat(sys_path.c_str(), &s) == -1) { + sys_path.resize(sys_path.size() - 1); + } + sys_path+= "/queue/rotational"; + char c = '?'; + FDScope fd(open(sys_path.c_str(), O_RDONLY)); + if (fd.Valid()) { + os_call_ssize(read, (int)fd, (void *)&c, sizeof(c)); + } else { + fprintf(stderr, "%s: can't read '%s'\n", __FUNCTION__, sys_path.c_str()); + } + multi_thread_friendly = (c == '0'); + } else { + multi_thread_friendly = (parts[0] == "tmpfs" || parts[0] == "proc"); + } + if (parts.size() == 1) { + parts.emplace_back(); + } + _mountpoints.emplace_back(Mountpoint{parts[1], parts[2], multi_thread_friendly}); + fprintf(stderr, "%s: mtf=%d fs='%s' for '%s' at '%s'\n", __FUNCTION__, + multi_thread_friendly, parts[2].c_str(), parts[0].c_str(), parts[1].c_str()); + } + } + } + if (_mountpoints.empty()) { + fprintf(stderr, "%s: failed to parse /proc/mounts\n", __FUNCTION__); + } +#endif + + // TODO: BSD, MacOS +} + + +std::string MountInfo::GetFileSystem(const std::string &path) const +{ + std::string out; + size_t longest_match = 0; + for (const auto &it : _mountpoints) { + if (it.path.size() > longest_match && StrStartsFrom(path, it.path.c_str())) { + longest_match = it.path.size(); + out = it.filesystem; + } + } + + if (out.empty()) { + struct statfs sfs{}; +#if !defined(__FreeBSD__) && !defined(__CYGWIN__) + if (sdc_statfs(path.c_str(), &sfs) == 0) { +#else + if (statfs(path.c_str(), &sfs) == 0) { +#endif +#ifdef __APPLE__ + out = sfs.f_fstypename; + if (out.empty()) +#endif + for (size_t i = 0; i < ARRAYSIZE(s_fs_magics); ++i) { + if (sfs.f_type == s_fs_magics[i].magic) { + out = s_fs_magics[i].name; + break; + } + } + } + } + + return out; +} + +bool MountInfo::IsMultiThreadFriendly(const std::string &path) const +{ + if (_mtfs != 0) { + return (_mtfs == 'e' || _mtfs == 'E'); + } + + bool out = true; + size_t longest_match = 0; + for (const auto &it : _mountpoints) { + if (it.path.size() > longest_match && StrStartsFrom(path, it.path.c_str())) { + longest_match = it.path.size(); + out = it.multi_thread_friendly; + } + } + return out; +} diff --git a/far2l/src/mix/MountInfo.h b/far2l/src/mix/MountInfo.h new file mode 100644 index 00000000..4c630697 --- /dev/null +++ b/far2l/src/mix/MountInfo.h @@ -0,0 +1,26 @@ +#pragma once +#include <string> +#include <vector> + +/* This class detects if path points to device that is best to be + * accessed in multi-thread parallel manner, like SSD drives. + * Currently works only under Linux, others defaulted to <true>. + */ +class MountInfo +{ + struct Mountpoint { + std::string path; + std::string filesystem; + bool multi_thread_friendly; + }; + struct Mountpoints : std::vector<Mountpoint> {} _mountpoints; + char _mtfs = 0; + +public: + MountInfo(); + + std::string GetFileSystem(const std::string &path) const; + + /// Returns true if path fine to be used multi-threaded-ly + bool IsMultiThreadFriendly(const std::string &path) const; +}; diff --git a/far2l/src/mix/RegExp.cpp b/far2l/src/mix/RegExp.cpp new file mode 100644 index 00000000..eb7f2b6d --- /dev/null +++ b/far2l/src/mix/RegExp.cpp @@ -0,0 +1,3766 @@ +/* +RegExp.cpp + +Regular expressions +Syntax and semantics are very close to perl +*/ +/* +Copyright © 2000 Konstantin Stupnik +Copyright © 2008 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" +#include <vector> +#include <bitset> +#include <set> +#include <cstring> +#include <algorithm> +#include <functional> +#include <unordered_map> +#include <memory> +#include <wctype.h> +#include <cwctype> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include "RegExp.hpp" + +//---------------------------------------------------------------------------- + +#ifdef RE_DEBUG + +#ifdef dpf +#undef dpf +#endif +#define dpf(x) + +static const wchar_t* ops[]= +{ + "opNone", + "opLineStart", + "opLineEnd", + "opDataStart", + "opDataEnd", + "opWordBound", + "opNotWordBound", + "opType", + "opNotType", + "opCharAny", + "opCharAnyAll", + "opSymbol", + "opNotSymbol", + "opSymbolIgnoreCase", + "opNotSymbolIgnoreCase", + "opSymbolClass", + "opOpenBracket", + "opClosingBracket", + "opAlternative", + "opBackRef", + "opNamedBracket", + "opNamedBackRef", + "opRangesBegin", + "opRange", + "opMinRange", + "opSymbolRange", + "opSymbolMinRange", + "opNotSymbolRange", + "opNotSymbolMinRange", + "opAnyRange", + "opAnyMinRange", + "opTypeRange", + "opTypeMinRange", + "opNotTypeRange", + "opNotTypeMinRange", + "opClassRange", + "opClassMinRange", + "opBracketRange", + "opBracketMinRange", + "opBackRefRange", + "opBackRefMinRange", + "opNamedRefRange", + "opNamedRefMinRange", + "opRangesEnd", + "opAssertionsBegin", + "opLookAhead", + "opNotLookAhead", + "opLookBehind", + "opNotLookBehind", + "opAsserionsEnd", + "opNoReturn", + "opRegExpEnd", +}; + +#else +#define dpf(x) +#endif + + +#define ISDIGIT(c) std::iswdigit(c) +#define ISSPACE(c) std::iswspace(c) +#define ISWORD(c) (std::iswalnum(c) || c == '_') +#define ISLOWER(c) std::iswlower(c) +#define ISUPPER(c) std::iswupper(c) +#define ISALPHA(c) std::iswalpha(c) +#define TOUPPER(c) wchar_t(std::towupper(c)) +#define TOLOWER(c) wchar_t(std::towlower(c)) + +//! Max brackets depth +enum +{ + MAXDEPTH = 256, +}; + +typedef uint8_t CharTypeMask; +typedef uint16_t CharTypeDoubleMask; +#define CharTypeMaskBits (sizeof(CharTypeMask) * 8) + +// Locale Info bits +enum CharType : CharTypeMask +{ + //! Digits + TYPE_DIGITCHAR = 0x01, + //! space, newlines tab etc + TYPE_SPACECHAR = 0x02, + //! alphanumeric and _ + TYPE_WORDCHAR = 0x04, + //! lo-case symbol + TYPE_LOWCASE = 0x08, + //! up-case symbol + TYPE_UPCASE = 0x10, + //! letter + TYPE_ALPHACHAR = 0x20, + + TYPE_HIGHEST = TYPE_ALPHACHAR +}; + +// Bracket handler actions +enum +{ + //! Matched Closing bracket + bhMatch = 1, + //! Bracket rollback + bhRollBack = 2, +}; + +enum +{ + RE_FAST_CHAR_COUNT = 0x10000 +}; + +static bool isTypeUncached(wchar_t chr, CharType type) +{ + switch (type) + { + case TYPE_DIGITCHAR: return ISDIGIT(chr); + case TYPE_SPACECHAR: return ISSPACE(chr); + case TYPE_WORDCHAR: return ISWORD(chr); + case TYPE_LOWCASE: return ISLOWER(chr); + case TYPE_UPCASE: return ISUPPER(chr); + case TYPE_ALPHACHAR: return ISALPHA(chr); + + default: + return false; + } +} + +// use volatile instead of full synchronization cuz later doesn't worth that +static volatile CharTypeDoubleMask s_AsciiTypesCache[0x80] = {0}; + +static inline bool isType(wchar_t chr, CharType type) +{ + if (LIKELY((size_t)chr < ARRAYSIZE(s_AsciiTypesCache))) + { + const CharTypeDoubleMask ctdm = (CharTypeDoubleMask)type; + CharTypeDoubleMask cached = s_AsciiTypesCache[chr]; + + if (UNLIKELY(!(cached & (ctdm << CharTypeMaskBits)))) + { + cached|= (ctdm << CharTypeMaskBits); + + if (isTypeUncached(chr, type)) + cached|= ctdm; + + s_AsciiTypesCache[chr] = cached; + } + + return (cached & ctdm) != 0; + } + + return isTypeUncached(chr, type); +} + +class RegExp::UniSet +{ + std::bitset<RE_FAST_CHAR_COUNT> Bits; // for below RE_FAST_CHAR_COUNT +#if (__WCHAR_MAX__ > 0xffff) + std::set<wchar_t> HighBits; // for above RE_FAST_CHAR_COUNT +#endif +public: + CharTypeMask types = 0; + CharTypeMask nottypes = 0; + bool negative = false; + + void Reset() + { + Bits.reset(); + HighBits.clear(); + types = 0; + nottypes = 0; + negative = false; + } + + inline bool GetBit(wchar_t chr) const + { + if (types) + { + CharTypeMask t = TYPE_HIGHEST; + + while (t) + { + if (types&t) + { + if (isType(chr, (CharType)t)) + return !negative; + } + + t>>= 1; + } + } + + if (nottypes) + { + CharTypeMask t = TYPE_HIGHEST; + + while (t) + { + if (nottypes&t) + { + if (!isType(chr, (CharType)t)) + return !negative; + } + + t>>=1; + } + } + + bool Set; +#if (__WCHAR_MAX__ > 0xffff) + if (UNLIKELY(chr >= RE_FAST_CHAR_COUNT)) + { + Set = (HighBits.find(chr) != HighBits.end()); + + } + else +#endif + { + Set = Bits[chr]; + } + + return negative ^ Set; + } + + inline void SetBit(wchar_t chr) + { +#if (__WCHAR_MAX__ > 0xffff) + if (UNLIKELY(chr >= RE_FAST_CHAR_COUNT)) + { + HighBits.insert(chr); + } + else +#endif + { + Bits.set(chr, true); + } + } + + inline void ClearBit(wchar_t chr) + { +#if (__WCHAR_MAX__ > 0xffff) + if (UNLIKELY(chr >= RE_FAST_CHAR_COUNT)) + { + HighBits.erase(chr); + } + else +#endif + { + Bits.set(chr, false); + } + } +}; + +enum REOp +{ + opNone, + opLineStart, // ^ + opLineEnd, // $ + opDataStart, // \A and ^ in single line mode + opDataEnd, // \Z and $ in signle line mode + + opWordBound, // \b + opNotWordBound, // \B + + opType, // \d\s\w\l\u\e + opNotType, // \D\S\W\L\U\E + + opCharAny, // . + opCharAnyAll, // . in single line mode + + opSymbol, // single char + opNotSymbol, // [^c] negative charclass with one char + opSymbolIgnoreCase, // symbol with IGNORE_CASE turned on + opNotSymbolIgnoreCase, // [^c] with ignore case set. + + opSymbolClass, // [chars] + + opOpenBracket, // ( + + opClosingBracket, // ) + + opAlternative, // | + + opBackRef, // \1 + + opNamedBracket, // (?{name} + opNamedBackRef, // \p{name} + + opRangesBegin, // for op type check + + opRange, // generic range + opMinRange, // generic minimizing range + + opSymbolRange, // quantifier applied to single char + opSymbolMinRange, // minimizing quantifier + + opNotSymbolRange, // [^x] + opNotSymbolMinRange, + + opAnyRange, // . + opAnyMinRange, + + opTypeRange, // \w, \d, \s + opTypeMinRange, + + opNotTypeRange, // \W, \D, \S + opNotTypeMinRange, + + opClassRange, // for char classes + opClassMinRange, + + opBracketRange, // for brackets + opBracketMinRange, + + opBackRefRange, // for backrefs + opBackRefMinRange, + + opNamedRefRange, + opNamedRefMinRange, + + opRangesEnd, // end of ranges + + opAssertionsBegin, + + opLookAhead, + opNotLookAhead, + + opLookBehind, + opNotLookBehind, + + opAsserionsEnd, + + opNoReturn, + + opRegExpEnd +}; + +struct REOpCode_data +{ + int op; //movable<int> +#ifdef RE_DEBUG + int srcpos; +#endif + + struct SBracket + { + RegExp::REOpCode* nextalt; + int index; + RegExp::REOpCode* pairindex; + }; + + struct SRange + { + union + { + SBracket bracket; + int op; + wchar_t symbol; + RegExp::UniSet *symbolclass; + RegExp::REOpCode* nextalt; + int refindex; + const wchar_t* refname; + CharType type; + }; + int min,max; + }; + + struct SNamedBracket + { + RegExp::REOpCode* nextalt; + const wchar_t* name; + RegExp::REOpCode* pairindex; + }; + + struct SAssert + { + RegExp::REOpCode* nextalt; + int length; + RegExp::REOpCode* pairindex; + }; + + struct SAlternative + { + RegExp::REOpCode* nextalt; + RegExp::REOpCode* endindex; + }; + + union + { + SRange range; + SBracket bracket; + SNamedBracket nbracket; + SAssert assert; + SAlternative alternative; + wchar_t symbol; + RegExp::UniSet *symbolclass; + int refindex; + const wchar_t* refname; + CharType type; + }; +}; + +struct RegExp::REOpCode: public REOpCode_data +{ +// NONCOPYABLE(REOpCode); +// MOVE_CONSTRUCTIBLE(REOpCode); + + REOpCode(): + REOpCode_data{} + { + } + + ~REOpCode() + { + switch (op) + { + case opSymbolClass:delete symbolclass; break; + case opClassRange: + case opClassMinRange:delete range.symbolclass; break; + case opNamedBracket:delete[] nbracket.name; break; + case opNamedBackRef:delete[] refname; break; + } + } +}; + +RegExp::RegExp(): + slashChar('/'), + backslashChar('\\'), + firstptr(new UniSet()), + first(*firstptr), + errorcode(errNotCompiled) +{ +} + +RegExp::~RegExp() = default; +RegExp::RegExp(RegExp&&) noexcept = default; + +int RegExp::CalcLength(ReStringView src) +{ + const auto srclength = static_cast<int>(src.size()); + int length=3;//global brackets + int brackets[MAXDEPTH]; + int count=0; + int save; + bracketscount=1; + int inquote=0; + + for (int i=0; i<srclength; i++,length++) + { + if (inquote && src[i]!=backslashChar && (i + 1 == srclength || src[i+1] != L'E')) + { + continue; + } + + if (src[i]==backslashChar) + { + i++; + if (i == srclength) + continue; + + if (src[i] == L'Q')inquote=1; + + if (src[i] == L'E')inquote=0; + + if (src[i] == L'x') + { + i++; + if(i != srclength && isxdigit(src[i])) + { + for(int j=1,k=i;j<4;j++) + { + if(k + j != srclength && isxdigit(src[k+j])) + { + i++; + } + else + { + break; + } + } + } + else + return SetError(errSyntax,i); + } + + if (src[i] == L'p') + { + i++; + + if (i == srclength || src[i] != L'{') + return SetError(errSyntax, i); + + i++; + const auto save2 = i; + + while (i < srclength && (ISWORD(src[i]) || ISSPACE(src[i])) && src[i] != L'}') + i++; + + if (i >= srclength) + return SetError(errBrackets, save2); + + if (src[i] != L'}' && !(ISWORD(src[i]) || ISSPACE(src[i]))) + return SetError(errSyntax, i); + } + + continue; + } + + switch (src[i]) + { + case L'(': + { + brackets[count++]=i; + if (count >= MAXDEPTH) + return SetError(errMaxDepth, i); + + if (i + 1 != srclength && src[i + 1]==L'?') + { + i+=2; + + if (i != srclength && src[i] == L'{') + { + save = i; + i++; + + while (i < srclength && (ISWORD(src[i]) || ISSPACE(src[i])) && src[i] != L'}') + i++; + + if (i >= srclength) + return SetError(errBrackets, save); + + if (src[i] != L'}' && !(ISWORD(src[i]) || ISSPACE(src[i]))) + return SetError(errSyntax, i); + } + } + else + { + bracketscount++; + } + + break; + } + case L')': + { + count--; + + if (count < 0) + return SetError(errBrackets,i); + + break; + } + case L'{': + case L'*': + case L'+': + case L'?': + { + length--; + + if (src[i] == L'{') + { + save=i; + + while (i < srclength && src[i] != L'}') + ++i; + + if (i >= srclength) + return SetError(errBrackets,save); + } + + if (i + 1 != srclength && src[i + 1] == '?') + ++i; + + break; + } + case L'[': + { + save=i; + + while (i < srclength && src[i] != L']') + i += (backslashChar == src[i] && src[i+1] ? 2 : 1); + + if (i >= srclength) + return SetError(errBrackets,save); + + break; + } + } + } + + if (count) + return SetError(errBrackets, brackets[0]); + + return length; +} + +bool RegExp::Compile(ReStringView const src, int options) +{ + if (options&OP_CPPMODE) + { + slashChar='\\'; + backslashChar='/'; + } + else + { + slashChar='/'; + backslashChar='\\'; + } + + havefirst=0; + + code.clear(); + + ReStringView Regex; + + if (options&OP_PERLSTYLE) + { + if (!StrStartsFrom(src, slashChar)) + return SetError(errSyntax, 0); + + const auto End = src.rfind(slashChar); + if (End == 0) + return SetError(errSyntax, static_cast<int>(src.size())); + + Regex = src.substr(1, End - 1); + const auto Options = src.substr(End + 1); + for (auto i = Options.cbegin(); i != Options.cend(); ++i) + { + switch (*i) + { + case 'i':options|=OP_IGNORECASE; break; + case 's':options|=OP_SINGLELINE; break; + case 'm':options|=OP_MULTILINE; break; + case 'x':options|=OP_XTENDEDSYNTAX; break; + case 'o':options|=OP_OPTIMIZE; break; + default:return SetError(errOptions,i - Options.cbegin()); + } + } + } + else + { + Regex = src; + } + + ignorecase = (options&OP_IGNORECASE) != 0; + + const auto relength = CalcLength(Regex); + if (!relength) + return false; + + code.resize(relength); + + if (!InnerCompile(src.data(), Regex.data(), static_cast<int>(Regex.size()), options)) + { + code.clear(); + return false; + } + + errorcode = errNone; + minlength = 0; + + if (options&OP_OPTIMIZE) + Optimize(); + + return true; +} + +static int GetNum(const wchar_t* src,int& i) +{ + int res=0; + + while (ISDIGIT(src[i])) + { + res*=10; + res+=src[i]-'0'; + i++; + } + + return res; +} + +static int CalcPatternLength(const RegExp::REOpCode* from, const RegExp::REOpCode* to) +{ + int len=0; + int altcnt=0; + int altlen=-1; + + for (; from <= to; ++from) + { + switch (from->op) + { + //zero width + case opLineStart: + case opLineEnd: + case opDataStart: + case opDataEnd: + case opWordBound: + case opNotWordBound: + continue; + + case opType: + case opNotType: + case opCharAny: + case opCharAnyAll: + case opSymbol: + case opNotSymbol: + case opSymbolIgnoreCase: + case opNotSymbolIgnoreCase: + case opSymbolClass: + len++; + altcnt++; + continue; + + case opNamedBracket: + case opOpenBracket: + { + const auto l = CalcPatternLength(from + 1, from->bracket.pairindex - 1); + + if (l==-1)return -1; + + len+=l; + altcnt+=l; + from=from->bracket.pairindex; + continue; + } + case opClosingBracket: + break; + case opAlternative: + + if (altlen!=-1 && altcnt!=altlen)return -1; + + altlen=altcnt; + altcnt=0; + continue; + case opBackRef: + case opNamedBackRef: + return -1; + case opRangesBegin: + case opRange: + case opMinRange: + case opSymbolRange: + case opSymbolMinRange: + case opNotSymbolRange: + case opNotSymbolMinRange: + case opAnyRange: + case opAnyMinRange: + case opTypeRange: + case opTypeMinRange: + case opNotTypeRange: + case opNotTypeMinRange: + case opClassRange: + case opClassMinRange: + + if (from->range.min!=from->range.max)return -1; + + len+=from->range.min; + altcnt+=from->range.min; + continue; + case opBracketRange: + case opBracketMinRange: + { + if (from->range.min!=from->range.max)return -1; + + const auto l = CalcPatternLength(from + 1,from->bracket.pairindex - 1); + + if (l==-1)return -1; + + len+=from->range.min*l; + altcnt+=from->range.min*l; + from=from->bracket.pairindex; + continue; + } + case opBackRefRange: + case opBackRefMinRange: + case opNamedRefRange: + case opNamedRefMinRange: + return -1; + case opRangesEnd: + case opAssertionsBegin: + case opLookAhead: + case opNotLookAhead: + case opLookBehind: + case opNotLookBehind: + from=from->assert.pairindex; + continue; + case opAsserionsEnd: + case opNoReturn: + continue; + } + } + + if (altlen!=-1 && altlen!=altcnt)return -1; + + return altlen==-1?len:altlen; +} + +bool RegExp::InnerCompile(const wchar_t* const start, const wchar_t* src, int srclength, int options) +{ + REOpCode* brackets[MAXDEPTH]; + // current brackets depth + // one place reserved for surrounding 'main' brackets + int brdepth=1; + // compiling interior of lookbehind + // used to apply restrictions of lookbehind + int lookbehind=0; + // counter of normal brackets + int brcount=0; + // counter of closed brackets + // used to check correctness of back-references + std::vector<bool> closedbrackets(1); + // quoting is active + int inquote=0; + maxbackref=0; + UniSet *tmpclass; + code[0].op=opOpenBracket; + code[0].bracket.index = 0; + MatchHash h; + RegExpMatch m = {}; + int pos=1; + brackets[0]=code.data(); +#ifdef RE_DEBUG + resrc = L'('; + resrc.append(src, srclength).append(L")\x2190"); +#endif + havelookahead=0; + + for (int i=0; i<srclength; i++) + { + auto op = &code[pos]; + pos++; +#ifdef RE_DEBUG + op->srcpos=i+1; +#endif + + if (inquote && src[i]!=backslashChar) + { + op->op=ignorecase?opSymbolIgnoreCase:opSymbol; + op->symbol=ignorecase?TOLOWER(src[i]):src[i]; + + if (ignorecase && TOUPPER(op->symbol)==op->symbol)op->op=opSymbol; + + continue; + } + + if (src[i]==backslashChar) + { + i++; + + if (inquote && src[i]!='E') + { + op->op=opSymbol; + op->symbol=backslashChar; + op = &code[pos]; + pos++; + op->op=ignorecase?opSymbolIgnoreCase:opSymbol; + op->symbol=ignorecase?TOLOWER(src[i]):src[i]; + + if (ignorecase && TOUPPER(op->symbol)==op->symbol)op->op=opSymbol; + + continue; + } + + op->op=opType; + + switch (src[i]) + { + case 'Q':inquote=1; pos--; continue; + case 'E':inquote=0; pos--; continue; + case 'b':op->op=opWordBound; continue; + case 'B':op->op=opNotWordBound; continue; + case 'D':op->op=opNotType; [[fallthrough]]; + case 'd':op->type=TYPE_DIGITCHAR; continue; + case 'S':op->op=opNotType; [[fallthrough]]; + case 's':op->type=TYPE_SPACECHAR; continue; + case 'W':op->op=opNotType; [[fallthrough]]; + case 'w':op->type=TYPE_WORDCHAR; continue; + case 'U':op->op=opNotType; [[fallthrough]]; + case 'u':op->type=TYPE_UPCASE; continue; + case 'L':op->op=opNotType; [[fallthrough]]; + case 'l':op->type=TYPE_LOWCASE; continue; + case 'I':op->op=opNotType; [[fallthrough]]; + case 'i':op->type=TYPE_ALPHACHAR; continue; + case 'A':op->op=opDataStart; continue; + case 'Z':op->op=opDataEnd; continue; + case 'n':op->op=opSymbol; op->symbol='\n'; continue; + case 'r':op->op=opSymbol; op->symbol='\r'; continue; + case 't':op->op=opSymbol; op->symbol='\t'; continue; + case 'f':op->op=opSymbol; op->symbol='\f'; continue; + case 'e':op->op=opSymbol; op->symbol=27; continue; + case 'O':op->op=opNoReturn; continue; + case 'p': + { + op->op = opNamedBackRef; + i++; + + if (src[i] != L'{')return SetError(errSyntax, i + (src - start)); + + int len = 0; i++; + + while (src[i + len] != L'}')len++; + + if (len > 0) + { + const auto Name = new wchar_t[len + 1]; + std::memcpy(Name, src + i, len*sizeof(wchar_t)); + Name[len] = 0; + if (!h.Matches.count(Name)) + { + delete[] Name; + return SetError(errReferenceToUndefinedNamedBracket, i + (src - start)); + } + op->refname = Name; + + i += len; + } + else + { + return SetError(errSyntax, i + (src - start)); + } + } continue; + + case 'x': + { + i++; + + if (i >= srclength)return SetError(errSyntax, i + (src - start) - 1); + + if(isxdigit(src[i])) + { + int c=TOLOWER(src[i])-'0'; + + if (c>9)c-='a'-'0'-10; + + op->op=ignorecase?opSymbolIgnoreCase:opSymbol; + op->symbol=c; + for(int j=1,k=i;j<4 && k+j<srclength;j++) + { + if(isxdigit(src[k+j])) + { + i++; + c=TOLOWER(src[k+j])-'0'; + if (c>9)c-='a'-'0'-10; + op->symbol<<=4; + op->symbol|=c; + } + else + { + break; + } + } + if (ignorecase) + { + op->symbol=TOLOWER(op->symbol); + if (TOUPPER(op->symbol)==TOLOWER(op->symbol)) + { + op->op=opSymbol; + } + } + } + else return SetError(errSyntax, i + (src - start)); + + continue; + } + default: + { + if (ISDIGIT(src[i])) + { + int save=i; + op->op=opBackRef; + op->refindex=GetNum(src,i); i--; + + if (op->refindex<=0 || op->refindex>brcount || !closedbrackets[op->refindex]) + { + return SetError(errInvalidBackRef, save + (src - start) - 1); + } + + if (op->refindex>maxbackref)maxbackref=op->refindex; + } + else + { + if (options&OP_STRICT && ISALPHA(src[i])) + { + return SetError(errInvalidEscape, i + (src - start) - 1); + } + + op->op=ignorecase?opSymbolIgnoreCase:opSymbol; + op->symbol=ignorecase?TOLOWER(src[i]):src[i]; + + if (TOLOWER(op->symbol)==TOUPPER(op->symbol)) + { + op->op=opSymbol; + } + } + } + } + + continue; + } + + switch (src[i]) + { + case '.': + { + if (options&OP_SINGLELINE) + { + op->op=opCharAnyAll; + } + else + { + op->op=opCharAny; + } + + continue; + } + case '^': + { + if (options&OP_MULTILINE) + { + op->op=opLineStart; + } + else + { + op->op=opDataStart; + } + + continue; + } + case '$': + { + if (options&OP_MULTILINE) + { + op->op=opLineEnd; + } + else + { + op->op=opDataEnd; + } + + continue; + } + case '|': + { + if (brackets[brdepth-1]->op==opAlternative) + { + brackets[brdepth-1]->alternative.nextalt=op; + } + else + { + if (brackets[brdepth-1]->op==opOpenBracket) + { + brackets[brdepth-1]->bracket.nextalt=op; + } + else + { + brackets[brdepth-1]->assert.nextalt=op; + } + } + + if ((brdepth + 1) >= MAXDEPTH) + return SetError(errMaxDepth, i + (src - start)); + + brackets[brdepth++]=op; + op->op=opAlternative; + continue; + } + case '(': + { + op->op=opOpenBracket; + + if (src[i+1]=='?') + { + i+=2; + + switch (src[i]) + { + case ':':op->bracket.index=-1; break; + case '=':op->op=opLookAhead; havelookahead=1; break; + case '!':op->op=opNotLookAhead; havelookahead=1; break; + case '<': + { + i++; + + if (src[i]=='=') + { + op->op=opLookBehind; + } + else if (src[i]=='!') + { + op->op=opNotLookBehind; + } + else + return SetError(errSyntax, i + (src - start)); + + lookbehind++; + } break; + case L'{': + { + op->op = opNamedBracket; + havenamedbrackets = 1; + int len = 0; + i++; + + while (src[i + len] != L'}')len++; + + if (len > 0) + { + const auto Name = new wchar_t[len + 1]; + std::memcpy(Name, src + i, len*sizeof(wchar_t)); + Name[len] = 0; + op->nbracket.name = Name; + } + else + { + op->op = opOpenBracket; + op->bracket.index = -1; + } + + i += len; + } break; + default: + { + return SetError(errSyntax, i + (src - start)); + } + } + } + else + { + ++brcount; + closedbrackets.push_back(false); + op->bracket.index=brcount; + } + + brackets[brdepth]=op; + brdepth++; + continue; + } + case ')': + { + op->op=opClosingBracket; + brdepth--; + + while (brackets[brdepth]->op==opAlternative) + { + brackets[brdepth]->alternative.endindex=op; + brdepth--; + } + + switch (brackets[brdepth]->op) + { + case opOpenBracket: + { + op->bracket.pairindex=brackets[brdepth]; + brackets[brdepth]->bracket.pairindex=op; + op->bracket.index=brackets[brdepth]->bracket.index; + + if (op->bracket.index!=-1) + { + closedbrackets[op->bracket.index]=true; + } + + break; + } + case opNamedBracket: + { + op->nbracket.pairindex = brackets[brdepth]; + brackets[brdepth]->nbracket.pairindex = op; + op->nbracket.name = brackets[brdepth]->nbracket.name; + h.Matches[op->nbracket.name] = m; + break; + } + case opLookBehind: + case opNotLookBehind: + { + lookbehind--; + int l=CalcPatternLength(brackets[brdepth] + 1, op - 1); + + if (l == -1)return SetError(errVariableLengthLookBehind, i + (src - start)); + + brackets[brdepth]->assert.length=l; + } + [[fallthrough]]; + + case opLookAhead: + case opNotLookAhead: + { + op->assert.pairindex=brackets[brdepth]; + brackets[brdepth]->assert.pairindex=op; + break; + } + } + + continue; + } + case '[': + { + i++; + bool negative = false; + + if (src[i]=='^') + { + negative = true; + i++; + } + + int lastchar=-1; + int classsize=0; + op->op=opSymbolClass; + //op->symbolclass=new wchar_t[32](); + op->symbolclass=new UniSet(); + tmpclass=op->symbolclass; + + for (; src[i]!=']'; i++) + { + if (src[i]==backslashChar) + { + i++; + int isnottype=0; + int type=0; + lastchar=-1; + + switch (src[i]) + { + case 'D':isnottype=1; [[fallthrough]]; + case 'd':type=TYPE_DIGITCHAR; break; + case 'W':isnottype=1; [[fallthrough]]; + case 'w':type=TYPE_WORDCHAR; break; + case 'S':isnottype=1; [[fallthrough]]; + case 's':type=TYPE_SPACECHAR; break; + case 'L':isnottype=1; [[fallthrough]]; + case 'l':type=TYPE_LOWCASE; break; + case 'U':isnottype=1; [[fallthrough]]; + case 'u':type=TYPE_UPCASE; break; + case 'I':isnottype=1; [[fallthrough]]; + case 'i':type=TYPE_ALPHACHAR; break; + case 'n':lastchar='\n'; break; + case 'r':lastchar='\r'; break; + case 't':lastchar='\t'; break; + case 'f':lastchar='\f'; break; + case 'e':lastchar=27; break; + case 'x': + { + i++; + + if (i >= srclength)return SetError(errSyntax, i + (src - start) - 1); + + if (isxdigit(src[i])) + { + int c=TOLOWER(src[i])-'0'; + + if (c>9)c-='a'-'0'-10; + + lastchar=c; + + for(int j=1,k=i;j<4 && k+j<srclength;j++) + { + if (isxdigit(src[k+j])) + { + i++; + c=TOLOWER(src[k+j])-'0'; + + if (c>9)c-='a'-'0'-10; + + lastchar<<=4; + lastchar|=c; + } + else + { + break; + } + } + dpf((L"Last char=%c(%02x)\n",lastchar,lastchar)); + } + else return SetError(errSyntax, i + (src - start)); + + break; + } + default: + { + if (options&OP_STRICT && ISALPHA(src[i])) + { + return SetError(errInvalidEscape, i + (src - start) - 1); + } + + lastchar=src[i]; + } + } + + if (type) + { + if (isnottype) + { + tmpclass->nottypes|=type; + } + else + { + tmpclass->types|=type; + } + classsize=257; + //for(int j=0;j<32;j++)op->symbolclass[j]|=charbits[classindex+j]^isnottype; + //classsize+=charsizes[classindex>>5]; + //int setbit; + /*for(int j=0;j<256;j++) + { + setbit=(chartypes[j]^isnottype)&type; + if(setbit) + { + if(ignorecase) + { + SetBit(op->symbolclass,lc[j]); + SetBit(op->symbolclass,uc[j]); + } + else + { + SetBit(op->symbolclass,j); + } + classsize++; + } + }*/ + } + else + { + if (options&OP_IGNORECASE) + { + tmpclass->SetBit(TOLOWER(lastchar)); + tmpclass->SetBit(TOUPPER(lastchar)); + } + else + { + tmpclass->SetBit(lastchar); + } + + classsize++; + } + + continue; + } + + if (src[i]=='-') + { + if (lastchar!=-1 && src[i+1]!=']') + { + int to=src[i+1]; + + if (to==backslashChar) + { + to=src[i+2]; + + if (to=='x') + { + i+=2; + to=TOLOWER(src[i+1]); + + if(isxdigit(to)) + { + to-='0'; + + if (to>9)to-='a'-'0'-10; + + for(int j=1,k=(i+1);j<4 && k+j<srclength;j++) + { + int c=TOLOWER(src[k+j]); + if(isxdigit(c)) + { + i++; + c-='0'; + + if (c>9)c-='a'-'0'-10; + + to<<=4; + to|=c; + } + else + { + break; + } + } + } + else return SetError(errSyntax, i + (src - start)); + } + else + { + tmpclass->SetBit('-'); + classsize++; + continue; + } + } + + i++; + dpf((L"from %d to %d\n",lastchar,to)); + + for (int j=lastchar; j<=to; j++) + { + if (ignorecase) + { + tmpclass->SetBit(TOLOWER(j)); + tmpclass->SetBit(TOUPPER(j)); + } + else + { + tmpclass->SetBit(j); + } + + classsize++; + } + + continue; + } + } + + lastchar=src[i]; + + if (ignorecase) + { + tmpclass->SetBit(TOLOWER(lastchar)); + tmpclass->SetBit(TOUPPER(lastchar)); + } + else + { + tmpclass->SetBit(lastchar); + } + + classsize++; + } + + if (negative && classsize>1) + { + tmpclass->negative = negative; + //for(int j=0;j<32;j++)op->symbolclass[j]^=0xff; + } + + if (classsize==1) + { + delete op->symbolclass; + op->symbolclass = nullptr; + tmpclass = nullptr; + op->op=negative?opNotSymbol:opSymbol; + + if (ignorecase) + { + op->op+=2; + op->symbol=TOLOWER(lastchar); + } + else + { + op->symbol=lastchar; + } + } + + if (tmpclass) + tmpclass->negative = negative; + continue; + } + case '+': + case '*': + case '?': + case '{': + { + int min=0,max=0; + + switch (src[i]) + { + case '+':min=1; max=-2; break; + case '*':min=0; max=-2; break; + case '?': + { + //if(src[i+1]=='?') return SetError(errInvalidQuantifiersCombination,i); + min=0; max=1; + break; + } + case '{': + { + i++; + int save=i; + min=GetNum(src,i); + max=min; + + if (min<0)return SetError(errInvalidRange, save + (src - start)); + +// i++; + if (src[i]==',') + { + if (src[i+1]=='}') + { + i++; + max=-2; + } + else + { + i++; + max=GetNum(src,i); + +// i++; + if (max<min)return SetError(errInvalidRange, save + (src - start)); + } + } + + if (src[i] != '}')return SetError(errInvalidRange, save + (src - start)); + } + } + + pos--; + op = code.data() + pos - 1; + + if (min==1 && max==1)continue; + + op->range.min=min; + op->range.max=max; + + switch (op->op) + { + case opLineStart: + case opLineEnd: + case opDataStart: + case opDataEnd: + case opWordBound: + case opNotWordBound: + { + return SetError(errInvalidQuantifiersCombination, i + (src - start)); +// op->range.op=op->op; +// op->op=opRange; +// continue; + } + case opCharAny: + case opCharAnyAll: + { + op->range.op=op->op; + op->op=opAnyRange; + break; + } + case opType: + { + op->op=opTypeRange; + break; + } + case opNotType: + { + op->op=opNotTypeRange; + break; + } + case opSymbolIgnoreCase: + case opSymbol: + { + op->op=opSymbolRange; + break; + } + case opNotSymbol: + case opNotSymbolIgnoreCase: + { + op->op=opNotSymbolRange; + break; + } + case opSymbolClass: + { + op->op=opClassRange; + break; + } + case opBackRef: + { + op->op=opBackRefRange; + break; + } + case opNamedBackRef: + { + op->op = opNamedRefRange; + break; + } + case opClosingBracket: + { + op=op->bracket.pairindex; + + if (op->op != opOpenBracket)return SetError(errInvalidQuantifiersCombination, i + (src - start)); + + op->range.min=min; + op->range.max=max; + op->op=opBracketRange; + break; + } + default: + { + dpf((L"op->=%d\n",op->op)); + return SetError(errInvalidQuantifiersCombination, i + (src - start)); + } + }//switch(code.op) + + if (src[i+1]=='?') + { + ++op->op; + ++i; + } + + continue; + }// case +*?{ + case ' ': + case '\t': + case '\n': + case '\r': + { + if (options&OP_XTENDEDSYNTAX) + { + pos--; + continue; + } + } + [[fallthrough]]; + default: + { + op->op=options&OP_IGNORECASE?opSymbolIgnoreCase:opSymbol; + + if (ignorecase) + { + op->symbol=TOLOWER(src[i]); + } + else + { + op->symbol=src[i]; + } + } + }//switch(src[i]) + }//for() + + auto op = &code[pos]; + pos++; + brdepth--; + + while (brdepth>=0 && brackets[brdepth]->op==opAlternative) + { + brackets[brdepth]->alternative.endindex=op; + brdepth--; + } + + op->op=opClosingBracket; + op->bracket.pairindex = code.data(); + code[0].bracket.pairindex=op; +#ifdef RE_DEBUG + op->srcpos=i; +#endif + op = &code[pos]; + //pos++; + op->op=opRegExpEnd; +#ifdef RE_DEBUG + op->srcpos=i+1; +#endif + return true; +} + +struct RegExp::StateStackItem +{ + const REOpCode* pos{}; + const wchar_t* savestr{}; + const wchar_t* startstr{}; + int op{}; + int min{}; + int cnt{}; + int max{}; + int forward{}; +}; + +static const RegExp::StateStackItem& FindStateByPos(const std::vector<RegExp::StateStackItem>& stack, RegExp::REOpCode* pos, int op) +{ + return *std::find_if(stack.rbegin(), stack.rend(), + [&](const RegExp::StateStackItem& i){ return i.pos == pos && i.op == op; }); +} + +int RegExp::StrCmp(const wchar_t*& str, const wchar_t* start, const wchar_t* end) const +{ + const wchar_t* save=str; + + if (ignorecase) + { + while (start<end) + { + if (TOLOWER(*str)!=TOLOWER(*start)) {str=save; return 0;} + + str++; + start++; + } + } + else + { + while (start<end) + { + if (*str!=*start) {str=save; return 0;} + + str++; + start++; + } + } + + return 1; +} +static const RegExpMatch def_match = { -1, -1 }; + +bool RegExp::InnerMatch(const wchar_t* const start, const wchar_t* str, const wchar_t* strend, RegExpMatch* match, int& matchcount, MatchHash* hmatch, std::vector<StateStackItem>& stack) const +{ + int i,j; + int minimizing; + const REOpCode* tmp=nullptr; + RegExpMatch* m; + int inrangebracket=0; + + if (errorcode==errNotCompiled) + return false; + + if (matchcount<maxbackref)return SetError(errNotEnoughMatches,maxbackref); + + if (havenamedbrackets && !hmatch)return SetError(errNoStorageForNB, 0); + + stack.clear(); + + errorcode=errNone; + + if (bracketscount<matchcount)matchcount=bracketscount; + + std::fill_n(match, matchcount, def_match); + + for(const auto* op = code.data(), *end = op + code.size(); op != end; ++op) + { + //dpf(("op:%s,\tpos:%d,\tstr:%d\n",ops[op->op],pos,str-start)); + //dpf((L"=================\n")); + //BADFORMAT: dpf((L"S:%ls\n%*ls\n", start, str - start + 3, "^")); + //BADFORMAT: dpf((L"R:%ls\n%*ls\n", resrc.data(), op->srcpos + 3, "^")); + + if (str<=strend) + { + const auto MinSkip = [&](StateStackItem& st, std::function<bool(const wchar_t *)> const &cmp) + { + switch (std::next(op)->op) + { + case opSymbol: { + auto jj = std::next(op)->symbol; + if(*str!=jj) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(str[0]==jj) + break; + } + } + } break; + + case opNotSymbol: { + auto jj = std::next(op)->symbol; + if(*str==jj) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(str[0]!=jj) + break; + } + } + } break; + + case opSymbolIgnoreCase: { + auto jj = std::next(op)->symbol; + if(TOLOWER(*str)!=jj) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(TOLOWER(str[0])==jj) + break; + } + } + } break; + + case opNotSymbolIgnoreCase: { + auto jj = std::next(op)->symbol; + if(TOLOWER(*str)==jj) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(TOLOWER(str[0])!=jj) + break; + } + } + } break; + + case opType: { + auto jj = std::next(op)->type; + if(!isType(*str, jj)) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(isType(str[0], jj)) + break; + } + } + } break; + + case opNotType: { + auto jj = std::next(op)->type; + if(isType(*str, (CharType)jj)) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(!isType(str[0],jj)) + break; + } + } + } break; + + case opSymbolClass: { + auto cl = std::next(op)->symbolclass; + if(!cl->GetBit(*str)) + { + while(str<strend && cmp(str) && st.max--) + { + str++; + if(cl->GetBit(str[0])) + break; + } + } + } break; + } + }; + + switch (op->op) + { + case opLineStart: + { + if (str == start || IsEol(str[-1])) + continue; + + break; + } + case opLineEnd: + { + if (str == strend || IsEol(str[0])) + continue; + + break; + } + case opDataStart: + { + if (str==start)continue; + + break; + } + case opDataEnd: + { + if (str==strend)continue; + + break; + } + case opWordBound: + case opNotWordBound: + { + const auto IsWordBoundary = [&] + { + if (start == strend) + return false; + + if (str == start) + return ISWORD(*str); + + if (str == strend) + return ISWORD(str[-1]); + + return ISWORD(str[-1]) != ISWORD(*str); + }(); + + if (op->op == opWordBound? IsWordBoundary : !IsWordBoundary) + continue; + + break; + } + case opType: + { + if (isType(*str,op->type)) + { + str++; + continue; + } + + break; + } + case opNotType: + { + if (!isType(*str,op->type)) + { + str++; + continue; + } + + break; + } + case opCharAny: + { + if (!IsEol(*str)) + { + str++; + continue; + } + + break; + } + case opCharAnyAll: + { + str++; + continue; + } + case opSymbol: + { + if (*str==op->symbol) + { + str++; + continue; + } + + break; + } + case opNotSymbol: + { + if (*str!=op->symbol) + { + str++; + continue; + } + + break; + } + case opSymbolIgnoreCase: + { + if (TOLOWER(*str)==op->symbol) + { + str++; + continue; + } + + break; + } + case opNotSymbolIgnoreCase: + { + if (TOLOWER(*str)!=op->symbol) + { + str++; + continue; + } + + break; + } + case opSymbolClass: + { + if (op->symbolclass->GetBit(*str)) + { + str++; + continue; + } + + break; + } + case opOpenBracket: + { + if (op->bracket.index>=0 && op->bracket.index<matchcount) + { + //if (inrangebracket) Mantis#1388 + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opOpenBracket; + st.pos=op; + st.min=match[op->bracket.index].start; + st.max=match[op->bracket.index].end; + } + + match[op->bracket.index].start = str - start; + } + + if (op->bracket.nextalt) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op = opAlternative; + st.pos=op->bracket.nextalt; + st.savestr=str; + } + + continue; + } + case opNamedBracket: + { + if (hmatch) + { + if (!hmatch->Matches.count(op->nbracket.name)) + { + RegExpMatch sm; + sm.start = -1; + sm.end = -1; + hmatch->Matches[op->nbracket.name] = sm; + } + const auto m2 = &hmatch->Matches[op->nbracket.name]; + + //if (inrangebracket) Mantis#1388 + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op = opNamedBracket; + st.pos = op; + st.min = m2->start; + st.max = m2->end; + } + + m2->start = str - start; + } + + if (op->bracket.nextalt) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op = opAlternative; + st.pos = op->bracket.nextalt; + st.savestr = str; + } + + continue; + } + case opClosingBracket: + { + switch (op->bracket.pairindex->op) + { + case opOpenBracket: + { + if (op->bracket.index>=0 && op->bracket.index<matchcount) + { + match[op->bracket.index].end = str - start; + } + + continue; + } + case opNamedBracket: + { + if (hmatch) + { + hmatch->Matches[op->nbracket.name].end = str - start; + } + + continue; + } + case opBracketRange: + { + auto st = FindStateByPos(stack, op->bracket.pairindex,opBracketRange); + + if (str==st.startstr) + { + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].end = str - start; + } + + inrangebracket--; + continue; + } + + if (st.min>0)st.min--; + + if (st.min) + { + st.max--; + st.startstr=str; + st.savestr=str; + op=st.pos; + stack.emplace_back(st); + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + StateStackItem Item; + match[op->range.bracket.index].start = str - start; + Item.op=opOpenBracket; + Item.pos=op; + Item.min=match[op->range.bracket.index].start; + Item.max=match[op->range.bracket.index].end; + stack.emplace_back(Item); + } + + if (op->range.bracket.nextalt) + { + stack.emplace_back(); + StateStackItem &Item = stack.back(); + Item.op=opAlternative; + Item.pos=op->range.bracket.nextalt; + Item.savestr=str; + } + + continue; + } + + st.max--; + + if (!st.max) + { + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].end = str - start; + } + + inrangebracket--; + continue; + } + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].end = str - start; + tmp=op; + } + + st.startstr=str; + st.savestr=str; + op=st.pos; + stack.emplace_back(st); + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + stack.emplace_back(); + StateStackItem &Item = stack.back(); + Item.op=opOpenBracket; + Item.pos=tmp; + Item.min=match[op->range.bracket.index].start; + Item.max=match[op->range.bracket.index].end; + match[op->range.bracket.index].start = str-start; + } + + if (op->range.bracket.nextalt) + { + stack.emplace_back(); + StateStackItem &Item = stack.back(); + Item.op=opAlternative; + Item.pos=op->range.bracket.nextalt; + Item.savestr=str; + } + + continue; + } + case opBracketMinRange: + { + auto& ps = FindStateByPos(stack, op->bracket.pairindex, opBracketMinRange); + auto st = ps; + + if (st.min>0)st.min--; + + if (st.min) + { + //st.min--; + st.max--; + st.startstr=str; + st.savestr=str; + op=st.pos; + stack.emplace_back(st); + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].start = str - start; + st.op=opOpenBracket; + st.pos=op; + st.min=match[op->range.bracket.index].start; + st.max=match[op->range.bracket.index].end; + stack.emplace_back(st); + } + + if (op->range.bracket.nextalt) + { + st.op=opAlternative; + st.pos=op->range.bracket.nextalt; + st.savestr=str; + stack.emplace_back(st); + } + + continue; + } + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].end = str - start; + } + + st.max--; + inrangebracket--; + + if (!st.max)continue; + + st.forward=str>ps.startstr?1:0; + st.startstr=str; + st.savestr=str; + stack.emplace_back(st); + + if (op->range.bracket.nextalt) + { + st.op=opAlternative; + st.pos=op->range.bracket.nextalt; + st.savestr=str; + stack.emplace_back(st); + } + + continue; + } + case opLookAhead: + { + tmp=op->bracket.pairindex; + + while (stack.back().pos != tmp || stack.back().op != opLookAhead) + { + stack.pop_back(); + } + str = stack.back().savestr; + stack.pop_back(); + continue; + } + case opNotLookAhead: + { + while (stack.back().op!=opNotLookAhead) + { + stack.pop_back(); + } + str = stack.back().savestr; + stack.pop_back(); + break; + } + case opLookBehind: + { + continue; + } + case opNotLookBehind: + { + stack.back().forward=0; + break; + } + }//switch(code[pairindex].op) + + break; + }//case opClosingBracket + case opAlternative: + { + op = std::prev(op->alternative.endindex); + continue; + } + case opNamedBackRef: + case opBackRef: + { + if (op->op == opBackRef) + { + m = &match[op->refindex]; + } + else + { + if (!hmatch || !hmatch->Matches.count(op->refname))break; + m = &hmatch->Matches[op->refname]; + } + + if (m->start==-1 || m->end==-1)break; + + if (ignorecase) + { + j=m->end; + + for (i=m->start; i<j; i++,str++) + { + if (TOLOWER(start[i])!=TOLOWER(*str))break; + + if (str>strend)break; + } + + if (i<j)break; + } + else + { + j=m->end; + + for (i=m->start; i<j; i++,str++) + { + if (start[i]!=*str)break; + + if (str>strend)break; + } + + if (i<j)break; + } + + continue; + } + case opAnyRange: + case opAnyMinRange: + { + StateStackItem st; + st.op=op->op; + minimizing=op->op==opAnyMinRange; + j=op->range.min; + st.max=op->range.max-j; + + if (op->range.op==opCharAny) + { + for (i=0; i<j; i++,str++) + { + if (str>strend || IsEol(*str))break; + } + + if (i<j) + { + break; + } + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && !IsEol(*str) && st.max--)str++; + } + else + { + MinSkip(st, [](const wchar_t* Str) { return !IsEol(*Str); }); + + if (st.max==-1)break; + } + } + else + { + //opCharAnyAll: + str+=j; + + if (str>strend)break; + + st.startstr=str; + + if (!minimizing) + { + if (st.max>=0) + { + if (str+st.max<strend) + { + str+=st.max; + st.max=0; + } + else + { + st.max -= static_cast<int>(strend - str); + str=strend; + } + } + else + { + str=strend; + } + } + else + { + MinSkip(st, [](const wchar_t*) { return true; }); + + if (st.max==-1)break; + } + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opSymbolRange: + case opSymbolMinRange: + { + StateStackItem st; + st.op=op->op; + minimizing=op->op==opSymbolMinRange; + j=op->range.min; + st.max=op->range.max-j; + + if (ignorecase) + { + for (i=0; i<j; i++,str++) + { + if (str>strend || TOLOWER(*str)!=op->range.symbol)break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && TOLOWER(*str)==op->range.symbol && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return TOLOWER(*Str) == op->range.symbol; }); + + if (st.max==-1)break; + } + } + else + { + for (i=0; i<j; i++,str++) + { + if (str>strend || *str!=op->range.symbol)break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && *str==op->range.symbol && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return *Str == op->range.symbol; }); + + if (st.max==-1)break; + } + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opNotSymbolRange: + case opNotSymbolMinRange: + { + StateStackItem st; + st.op=op->op; + minimizing=op->op==opNotSymbolMinRange; + j=op->range.min; + st.max=op->range.max-j; + + if (ignorecase) + { + for (i=0; i<j; i++,str++) + { + if (str>strend || TOLOWER(*str)==op->range.symbol)break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && TOLOWER(*str)!=op->range.symbol && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return TOLOWER(*Str) != op->range.symbol; }); + + if (st.max==-1)break; + } + } + else + { + for (i=0; i<j; i++,str++) + { + if (str>strend || *str==op->range.symbol)break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && *str!=op->range.symbol && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return *Str != op->range.symbol; }); + + if (st.max==-1)break; + } + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opClassRange: + case opClassMinRange: + { + StateStackItem st; + st.op=op->op; + minimizing=op->op==opClassMinRange; + j=op->range.min; + st.max=op->range.max-j; + + for (i=0; i<j; i++,str++) + { + if (str>strend || !op->range.symbolclass->GetBit(*str))break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && op->range.symbolclass->GetBit(*str) && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return op->range.symbolclass->GetBit(*Str); }); + + if (st.max==-1)break; + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opTypeRange: + case opTypeMinRange: + { + StateStackItem st; + st.op=op->op; + minimizing=op->op==opTypeMinRange; + j=op->range.min; + st.max=op->range.max-j; + + for (i=0; i<j; i++,str++) + { + if (str>strend || !isType(*str,op->range.type))break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && isType(*str,op->range.type) && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return isType(*Str, op->range.type); }); + + if (st.max==-1)break; + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opNotTypeRange: + case opNotTypeMinRange: + { + StateStackItem st; + st.op = op->op; + minimizing=op->op==opNotTypeMinRange; + j=op->range.min; + st.max=op->range.max-j; + + for (i=0; i<j; i++,str++) + { + if (str>strend || isType(*str,op->range.type))break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && !isType(*str,op->range.type) && st.max--)str++; + } + else + { + MinSkip(st, [op](const wchar_t* Str) { return !isType(*Str, op->range.type); }); + + if (st.max==-1)break; + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opNamedRefRange: + case opNamedRefMinRange: + case opBackRefRange: + case opBackRefMinRange: + { + StateStackItem st; + st.op = op->op; + minimizing = op->op == opBackRefMinRange || op->op == opNamedRefMinRange; + j=op->range.min; + st.max=op->range.max-j; + if (op->op == opBackRefRange || op->op == opBackRefMinRange) + { + m = &match[op->range.refindex]; + } + else + { + m = &hmatch->Matches[op->range.refname]; + } + + if (!m)break; + + if (m->start==-1 || m->end==-1) + { + if (!j)continue; + else break; + } + + for (i=0; i<j; i++) + { + if (str>strend || !StrCmp(str,start+m->start,start+m->end))break; + } + + if (i<j)break; + + st.startstr=str; + + if (!minimizing) + { + while (str<strend && StrCmp(str,start+m->start,start+m->end) && st.max--); + } + else + { + MinSkip(st, [&](const wchar_t* Str) { return this->StrCmp(Str, start + m->start, start + m->end) != 0; }); + + if (st.max==-1)break; + } + + if (op->range.max==j)continue; + + st.savestr=str; + st.pos=op; + stack.emplace_back(st); + continue; + } + case opBracketRange: + case opBracketMinRange: + { + if (inrangebracket && op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opOpenBracket; + st.pos=op->range.bracket.pairindex; + st.min=match[op->range.bracket.index].start; + st.max=match[op->range.bracket.index].end; + } + + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op = op->op; + st.pos = op; + st.min = op->range.min; + st.max = op->range.max; + st.startstr = str; + st.savestr = str; + st.forward = 1; + } + + if (op->range.nextalt) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opAlternative; + st.pos=op->range.bracket.nextalt; + st.savestr=str; + } + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].start= + /*match[op->range.bracket.index].end=*/ str - start; + } + + if (op->op==opBracketMinRange && !op->range.min) + { + op=op->range.bracket.pairindex; + } + else + { + inrangebracket++; + } + + continue; + } + case opLookAhead: + case opNotLookAhead: + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=op->op; + st.savestr=str; + st.pos=op; + st.forward=1; + + if (op->assert.nextalt) + { + stack.emplace_back(); + StateStackItem &Item = stack.back(); + Item.op=opAlternative; + Item.pos=op->assert.nextalt; + Item.savestr=str; + } + + continue; + } + case opLookBehind: + case opNotLookBehind: + { + if (str-op->assert.length<start) + { + if (op->op==opLookBehind)break; + + op=op->assert.pairindex; + continue; + } + + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op = op->op; + st.savestr = str; + st.pos = op; + st.forward = 1; + str -= op->assert.length; + } + + if (op->assert.nextalt) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opAlternative; + st.pos=op->assert.nextalt; + st.savestr=str; + } + + continue; + } + case opNoReturn: + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opNoReturn; + continue; + } + case opRegExpEnd: + return true; + }//switch(op) + } + + for (;; stack.pop_back()) + { + if (stack.empty()) + return false; + + const auto ps = std::prev(stack.end()); + + //dpf(("ps->op:%s\n",ops[ps->op])); + switch (ps->op) + { + case opAlternative: + { + str=ps->savestr; + op=ps->pos; + + if (op->alternative.nextalt) + { + ps->pos=op->alternative.nextalt; + } + else + { + stack.pop_back(); + } + + break; + } + case opAnyRange: + case opSymbolRange: + case opNotSymbolRange: + case opClassRange: + case opTypeRange: + case opNotTypeRange: + { + str=ps->savestr-1; + op=ps->pos; + + if (str<ps->startstr) + { + continue; + } + + ps->savestr=str; + break; + } + case opNamedRefRange: + case opBackRefRange: + { + if (ps->op == opBackRefRange) + { + m = &match[ps->pos->range.refindex]; + } + else + { + m = &hmatch->Matches[ps->pos->range.refname]; + } + str=ps->savestr-(m->end-m->start); + op=ps->pos; + + if (str<ps->startstr) + { + continue; + } + + ps->savestr=str; + break; + } + case opAnyMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + + if (ps->pos->range.op==opCharAny) + { + if (str<strend && !IsEol(*str)) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + } + else + { + if (str<strend) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + } + + break; + } + case opSymbolMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + + if (ignorecase) + { + if (str<strend && TOLOWER(*str)==op->symbol) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + } + else + { + if (str<strend && *str==op->symbol) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + } + + break; + } + case opNotSymbolMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + + if (ignorecase) + { + if (str<strend && TOLOWER(*str)!=op->symbol) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + } + else + { + if (str<strend && *str!=op->symbol) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + } + + break; + } + case opClassMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + + if (str<strend && op->range.symbolclass->GetBit(*str)) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + + break; + } + case opTypeMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + + if (str<strend && isType(*str,op->range.type)) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + + break; + } + case opNotTypeMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + + if (str<strend && !isType(*str,op->range.type)) + { + str++; + ps->savestr=str; + } + else + { + continue; + } + + break; + } + case opNamedRefMinRange: + case opBackRefMinRange: + { + if (!(ps->max--))continue; + + str=ps->savestr; + op=ps->pos; + if (ps->op == opBackRefMinRange) + { + m = &match[op->range.refindex]; + } + else + { + m = &hmatch->Matches[op->range.refname]; + } + + if (str+m->end-m->start<strend && StrCmp(str,start+m->start,start+m->end)) + { + ps->savestr=str; + } + else + { + continue; + } + + break; + } + case opBracketRange: + { + if (ps->min) + { + inrangebracket--; + continue; + } + + if (ps->forward) + { + ps->forward=0; + op=ps->pos->range.bracket.pairindex; + inrangebracket--; + str=ps->savestr; + + if (op->range.nextalt) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opAlternative; + st.pos=op->range.bracket.nextalt; + st.savestr=str; + } + /* + if(op->bracket.index>=0 && op->bracket.index<matchcount) + { + match[op->bracket.index].end=str-start; + } + */ + break; + } + + continue; + } + case opBracketMinRange: + { + if (!(ps->max--)) + { + inrangebracket--; + continue; + } + + if (ps->forward) + { + ps->forward=0; + op=ps->pos; + str=ps->savestr; + + if (op->range.bracket.index>=0 && op->range.bracket.index<matchcount) + { + match[op->range.bracket.index].start = str - start; + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opOpenBracket; + st.pos=op; + st.min=match[op->range.bracket.index].start; + st.max=match[op->range.bracket.index].end; + } + + if (op->range.nextalt) + { + stack.emplace_back(); + StateStackItem &st = stack.back(); + st.op=opAlternative; + st.pos=op->range.bracket.nextalt; + st.savestr=str; + } + + inrangebracket++; + break; + } + + inrangebracket--; + continue; + } + case opOpenBracket: + { + j=ps->pos->bracket.index; + + if (j>=0 && j<matchcount) + { + match[j].start=ps->min; + match[j].end=ps->max; + } + + continue; + } + case opNamedBracket: + { + const auto n = ps->pos->nbracket.name; + + if (n && hmatch) + { + RegExpMatch sm; + sm.start = ps->min; + sm.end = ps->max; + hmatch->Matches[n] = sm; + } + + continue; + } + case opLookAhead: + case opLookBehind: + { + continue; + } + case opNotLookBehind: + case opNotLookAhead: + { + op=ps->pos->assert.pairindex; + str=ps->savestr; + + if (ps->forward) + { + stack.pop_back(); + break; + } + else + { + continue; + } + } + case opNoReturn: + { + return false; + } + }//switch(op) + + break; + } + } + + return true; +} + +bool RegExp::Match(ReStringView const text, RegExpMatch* match, int& matchcount, MatchHash* hmatch) const +{ + return MatchEx(text, 0, match, matchcount, hmatch); +} + +bool RegExp::MatchEx(ReStringView const text, size_t const From, RegExpMatch* match, int& matchcount, MatchHash* hmatch) const +{ + const auto start = text.data(); + const auto textstart = text.data() + From; + const auto textend = text.data() + text.size(); + + if (havefirst && !first.GetBit(*textstart)) + return false; + + auto tempend = textend; + TrimTail(start, tempend); + + if (tempend<textstart) + return false; + + if (minlength && tempend-start<minlength) + return false; + + std::vector<StateStackItem> stack; + stack.reserve(8); + + if (!InnerMatch(start, textstart, tempend, match, matchcount, hmatch, stack)) + return false; + + for (int i=0; i<matchcount; i++) + { + if (match[i].start==-1 || match[i].end==-1 || match[i].start>match[i].end) + { + match[i].start=match[i].end=-1; + } + } + + return true; +} + +bool RegExp::Optimize() +{ + REOpCode* jumps[MAXDEPTH]; + int jumpcount=0; + + if (havefirst) + return true; + + first.Reset(); + + minlength=0; + int mlstackmin[MAXDEPTH]{}; + int mlstacksave[MAXDEPTH]{}; + int mlscnt=0; + + for (const auto* it = code.data(), *end = it + code.size(); it != end; ++it) + { + switch (it->op) + { + case opType: + case opNotType: + case opCharAny: + case opCharAnyAll: + case opSymbol: + case opNotSymbol: + case opSymbolIgnoreCase: + case opNotSymbolIgnoreCase: + case opSymbolClass: + minlength++; + continue; + case opSymbolRange: + case opSymbolMinRange: + case opNotSymbolRange: + case opNotSymbolMinRange: + case opAnyRange: + case opAnyMinRange: + case opTypeRange: + case opTypeMinRange: + case opNotTypeRange: + case opNotTypeMinRange: + case opClassRange: + case opClassMinRange: + minlength+=it->range.min; + break; + case opNamedBracket: + case opOpenBracket: + case opBracketRange: + case opBracketMinRange: + mlstacksave[mlscnt]=minlength; + mlstackmin[mlscnt++]=-1; + minlength=0; + continue; + case opClosingBracket: + { + if (it->bracket.pairindex->op>opAssertionsBegin && + it->bracket.pairindex->op<opAsserionsEnd) + { + continue; + } + + if (mlstackmin[mlscnt-1]!=-1 && mlstackmin[mlscnt-1]<minlength) + { + minlength=mlstackmin[mlscnt-1]; + } + + switch (it->bracket.pairindex->op) + { + case opBracketRange: + case opBracketMinRange: + minlength *= it->range.min; + break; + } + + minlength+=mlstacksave[--mlscnt]; + } continue; + case opAlternative: + { + if (mlstackmin[mlscnt-1]==-1) + { + mlstackmin[mlscnt-1]=minlength; + } + else + { + if (minlength<mlstackmin[mlscnt-1]) + { + mlstackmin[mlscnt-1]=minlength; + } + } + + minlength=0; + break; + } + case opLookAhead: + case opNotLookAhead: + case opLookBehind: + case opNotLookBehind: + { + it = it->assert.pairindex; + continue; + } + case opRegExpEnd: + it = nullptr; + break; + } + + if (!it)break; + } + + dpf((L"minlength=%d\n",minlength)); + + for (const auto* op = code.data(), *end = op + code.size(); op != end; ++op) + { + switch (op->op) + { + default: + { + return false; + } + case opType: + { + for (int i = 0; i < RE_FAST_CHAR_COUNT; i++) + if (isType(i, op->type)) + first.SetBit(i); + + break; + } + case opNotType: + { + for (int i = 0; i < RE_FAST_CHAR_COUNT; i++) + if (!isType(i, op->type)) + first.SetBit(i); + + break; + } + case opSymbol: + { + first.SetBit(op->symbol); + break; + } + case opSymbolIgnoreCase: + { + first.SetBit(op->symbol); + first.SetBit(TOUPPER(op->symbol)); + break; + } + case opSymbolClass: + { + for (int i = 0; i < RE_FAST_CHAR_COUNT; i++) + if (op->symbolclass->GetBit(i)) + first.SetBit(i); + + break; + } + case opNamedBracket: + case opOpenBracket: + { + if (op->bracket.nextalt) + { + jumps[jumpcount++]=op->bracket.nextalt; + } + + continue; + } + case opClosingBracket: + { + continue; + } + case opAlternative: + { + return false; + } + case opSymbolRange: + case opSymbolMinRange: + { + if (ignorecase) + { + first.SetBit(TOLOWER(op->range.symbol)); + first.SetBit(TOUPPER(op->range.symbol)); + } + else + { + first.SetBit(op->range.symbol); + } + + if (!op->range.min)continue; + + break; + } + case opTypeRange: + case opTypeMinRange: + { + for (int i=0; i<RE_FAST_CHAR_COUNT; i++) + if (isType(i,op->range.type)) + first.SetBit(i); + + if (!op->range.min)continue; + + break; + } + case opNotTypeRange: + case opNotTypeMinRange: + { + for (int i=0; i<RE_FAST_CHAR_COUNT; i++) + if (!isType(i, op->range.type)) + first.SetBit(i); + + if (!op->range.min)continue; + + break; + } + case opClassRange: + case opClassMinRange: + { + for (int i=0; i<RE_FAST_CHAR_COUNT; i++) + if (op->range.symbolclass->GetBit(i)) + first.SetBit(i); + + if (!op->range.min)continue; + + break; + } + case opBracketRange: + case opBracketMinRange: + { + if (!op->range.min) + return false; + + if (op->range.bracket.nextalt) + { + jumps[jumpcount++]=op->range.bracket.nextalt; + } + + continue; + } + //case opLookAhead: + //case opNotLookAhead: + //case opLookBehind: + //case opNotLookBehind: + case opRegExpEnd: + return false; + } + + if (jumpcount>0) + { + op=jumps[--jumpcount]; + + if (op->op==opAlternative && op->alternative.nextalt) + { + jumps[jumpcount++]=op->alternative.nextalt; + } + + continue; + } + + break; + } + + havefirst=1; + return true; +} + +bool RegExp::Search(ReStringView const text, RegExpMatch* match, int& matchcount, MatchHash* hmatch) const +{ + return SearchEx(text, 0, match, matchcount, hmatch); +} + +bool RegExp::SearchEx(ReStringView const text, size_t const From, RegExpMatch* match, int& matchcount, MatchHash* hmatch) const +{ + const auto start = text.data(); + const auto textstart = text.data() + From; + const auto textend = text.data() + text.size(); + + auto tempend = textend; + TrimTail(start, tempend); + + if (tempend<textstart) + return false; + + if (minlength && tempend-start<minlength) + return false; + + std::vector<StateStackItem> stack; + stack.reserve(8); + + auto str = textstart; + + if (!code[0].bracket.nextalt && code[1].op == opDataStart) + { + if (!InnerMatch(start, str, tempend, match, matchcount, hmatch, stack)) + return false; + } + else + { + if (!code[0].bracket.nextalt && code[1].op == opDataEnd && code[2].op == opClosingBracket) + { + matchcount=1; + match[0].start = textend - start; + match[0].end=match[0].start; + return true; + } + + if (havefirst) + { + bool res = false; + do + { + while (!first.GetBit(*str) && str<tempend)str++; + + if (InnerMatch(start, str, tempend, match, matchcount, hmatch, stack)) + { + res = true; + break; + } + + str++; + } + while (str<tempend); + + if (!res && !InnerMatch(start, str, tempend, match, matchcount, hmatch, stack)) + return false; + } + else + { + bool res = false; + do + { + if (InnerMatch(start, str, tempend, match, matchcount, hmatch, stack)) + { + res = true; + break; + } + + str++; + } + while (str<=tempend); + if (!res) + return false; + } + } + + for (int i=0; i<matchcount; i++) + { + if (match[i].start==-1 || match[i].end==-1 || match[i].start>match[i].end) + { + match[i].start=match[i].end=-1; + } + } + + return true; +} + +bool RegExp::Search(ReStringView const Str) const +{ + RegExpMatch Match; + int Count = 1; + return Search(Str, &Match, Count) != 0; +} + +void RegExp::TrimTail(const wchar_t* const start, const wchar_t*& strend) const +{ + if (havelookahead)return; + + if (code.empty() || code[0].bracket.nextalt)return; + + REOpCode* op = code[0].bracket.pairindex - 1; + + while (op->op==opClosingBracket) + { + if (op->bracket.pairindex->op!=opOpenBracket)return; + + if (op->bracket.pairindex->bracket.nextalt)return; + + --op; + } + + strend--; + + switch (op->op) + { + case opSymbol: + { + while (strend>=start && *strend!=op->symbol)strend--; + + break; + } + case opNotSymbol: + { + while (strend>=start && *strend==op->symbol)strend--; + + break; + } + case opSymbolIgnoreCase: + { + while (strend>=start && TOLOWER(*strend)!=op->symbol)strend--; + + break; + } + case opNotSymbolIgnoreCase: + { + while (strend>=start && TOLOWER(*strend)==op->symbol)strend--; + + break; + } + case opType: + { + while (strend>=start && !isType(*strend,op->type))strend--; + + break; + } + case opNotType: + { + while (strend>=start && isType(*strend,op->type))strend--; + + break; + } + case opSymbolClass: + { + while (strend>=start && !op->symbolclass->GetBit(*strend))strend--; + + break; + } + case opSymbolRange: + case opSymbolMinRange: + { + if (!op->range.min)break; + + if (ignorecase) + { + while (strend>=start && TOLOWER(*strend)!=op->range.symbol)strend--; + } + else + { + while (strend>=start && *strend!=op->range.symbol)strend--; + } + + break; + } + case opNotSymbolRange: + case opNotSymbolMinRange: + { + if (!op->range.min)break; + + if (ignorecase) + { + while (strend>=start && TOLOWER(*strend)==op->range.symbol)strend--; + } + else + { + while (strend>=start && *strend==op->range.symbol)strend--; + } + + break; + } + case opTypeRange: + case opTypeMinRange: + { + if (!op->range.min)break; + + while (strend>=start && !isType(*strend,op->range.type))strend--; + + break; + } + case opNotTypeRange: + case opNotTypeMinRange: + { + if (!op->range.min)break; + + while (strend>=start && isType(*strend,op->range.type))strend--; + + break; + } + case opClassRange: + case opClassMinRange: + { + if (!op->range.min)break; + + while (strend>=start && !op->range.symbolclass->GetBit(*strend))strend--; + + break; + } + default:break; + } + + strend++; +} + +#ifdef ENABLE_TESTS + +#include "testing.hpp" + +TEST_CASE("regex") +{ + { + RegExp re; + REQUIRE(re.Compile(L"/a*?ca/"sv)); + + RegExpMatch m = { -1, -1 }; + int n = 1; + REQUIRE(re.Search(L"abca", &m, n)); + REQUIRE(n == 1); + REQUIRE(m.start == 2); + REQUIRE(m.end == 4); + } + + { + RegExp re; + REQUIRE(re.Compile(L"/^\\[([\\w.]+)\\]:\\s*\\[(.*)\\]$/"sv, OP_PERLSTYLE)); + + RegExpMatch m = { -1, -1 }; + int n = 1; + REQUIRE(re.Search(L"[init.svc.imsdatadaemon]: [running]", &m, n)); + REQUIRE(n == 1); + REQUIRE(m.start == 0); + REQUIRE(m.end == 35); + } + +} +#endif diff --git a/far2l/src/mix/RegExp.hpp b/far2l/src/mix/RegExp.hpp new file mode 100644 index 00000000..bbab8557 --- /dev/null +++ b/far2l/src/mix/RegExp.hpp @@ -0,0 +1,308 @@ +#ifndef REGEXP_HPP_18B41BD7_69F8_461A_8A81_069B447D5554 +#define REGEXP_HPP_18B41BD7_69F8_461A_8A81_069B447D5554 +#pragma once + +/* +RegExp.hpp + +Regular expressions +Syntax and semantics are very close to perl +*/ +/* +Copyright © 2000 Konstantin Stupnik +Copyright © 2008 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "fpsdk/plugin.hpp" +#include <unordered_map> +#include <string> +#include <string.h> +#include "FARString.hpp" + +//---------------------------------------------------------------------------- + +//#define RE_DEBUG + +struct ReStringView // TODO: replace with std::wstring_view once will adopt C++17 +{ + typedef wchar_t value_type; + + ReStringView(const ReStringView&) = default; + ReStringView(ReStringView&&) = default; + + ReStringView& operator=(const ReStringView&) = default; + + inline ReStringView(const FARString &str) + : _pw(str.CPtr()), _sz(str.GetLength()) + { + } + + inline ReStringView() + : _pw(nullptr), _sz(0) + { + } + + inline ReStringView(const wchar_t *pw, size_t sz = std::string::npos) + : _pw(pw), _sz((sz == std::string::npos) ? wcslen(pw) : sz) + { + } + + inline ReStringView substr(size_t pos, size_t sz = std::string::npos) const + { + return ReStringView(_pw + pos, std::min(_sz - pos, sz)); + } + + inline bool operator ==(const ReStringView &other) + { + return _sz == other._sz && wmemcmp(_pw, other._pw, _sz) == 0; + } + + inline bool operator !=(const ReStringView &other) + { + return !operator ==(other); + } + + inline wchar_t front() const { return _sz ? _pw[0] : 0; } + inline wchar_t back() const { return _sz ? _pw[_sz - 1] : 0; } + inline const wchar_t *data() const { return _pw; } + inline size_t size() const { return _sz; } + inline bool empty() const { return _sz == 0; } + + inline const wchar_t operator[](size_t i) const { return _pw[i]; } + + size_t rfind(wchar_t c) const + { + for (size_t i = _sz; i;) { + --i; + if (_pw[i] == c) { + return i; + } + } + + return std::string::npos; + } + + inline const wchar_t *cbegin() const { return _pw; } + + inline const wchar_t *cend() const { return _pw + _sz; } + +private: + const wchar_t *_pw; + size_t _sz; +}; + + +//! Possible compile and runtime errors returned by LastError. +enum REError +{ + //! No errors + errNone=0, + //! RegExp wasn't even tried to compile + errNotCompiled, + //! expression contain syntax error + errSyntax, + //! Unbalanced brackets + errBrackets, + //! Max recursive brackets level reached. Controlled in compile time + errMaxDepth, + //! Invalid options combination + errOptions, + //! Reference to nonexistent bracket + errInvalidBackRef, + //! Invalid escape char + errInvalidEscape, + //! Invalid range value + errInvalidRange, + //! Quantifier applied to invalid object. f.e. lookahead assertion + errInvalidQuantifiersCombination, + //! Size of match array isn't large enough. + errNotEnoughMatches, + //! Attempt to match RegExp with Named Brackets, and no storage class provided. + errNoStorageForNB, + //! Reference to undefined named bracket + errReferenceToUndefinedNamedBracket, + //! Only fixed length look behind assertions are supported + errVariableLengthLookBehind, + + errCancelled +}; + +enum +{ + //! Match in a case insensitive manner + OP_IGNORECASE =0x0001, + //! Single line mode, dot meta-character will match newline symbol + OP_SINGLELINE =0x0002, + //! MultiLine mode, ^ and $ can match line start and line end + OP_MULTILINE =0x0004, + //! Extended syntax, spaces symbols are ignored unless escaped + OP_XTENDEDSYNTAX=0x0008, + //! Perl style RegExp provided. i.e. /expression/imsx + OP_PERLSTYLE =0x0010, + //! Optimize after compile + OP_OPTIMIZE =0x0020, + //! Strict escapes - only unrecognized escape will produce errInvalidEscape error + OP_STRICT =0x0040, + //! Replace backslash with slash, used + //! when RegExp source embedded in c++ sources + OP_CPPMODE =0x0080, +}; + +//! Hash table with match info +struct MatchHash +{ + std::unordered_map<std::wstring, RegExpMatch> Matches; +}; + +/*! Regular expressions support class. + +Expressions must be Compile'ed first, +and than Match string or Search for matching fragment. +*/ +class RegExp +{ +public: + struct REOpCode; + class UniSet; + struct StateStackItem; + +private: + // code + std::vector<REOpCode> code; + char slashChar; + char backslashChar; + + std::unique_ptr<UniSet> firstptr; + UniSet& first; + + int havefirst{}; + int havelookahead{}; + + int minlength{}; + + // error info + mutable int errorcode; + mutable int errorpos{}; + int srcstart{}; + + // options + int bracketscount{}; + int maxbackref{}; + int havenamedbrackets{}; + + bool ignorecase{}; + +#ifdef RE_DEBUG + std::wstring resrc; +#endif + + int CalcLength(ReStringView src); + bool InnerCompile(const wchar_t* start, const wchar_t* src, int srclength, int options); + + bool InnerMatch(const wchar_t* start, const wchar_t* str, const wchar_t* strend, RegExpMatch* match, int& matchcount, MatchHash* hmatch, std::vector<StateStackItem>& stack) const; + + void TrimTail(const wchar_t* start, const wchar_t*& strend) const; + + // BUGBUG not thread safe! + // TODO: split to compile errors (stateful) and match errors (stateless) + bool SetError(int _code, int pos) const { errorcode = _code; errorpos = pos; return false; } + + int StrCmp(const wchar_t*& str,const wchar_t* start,const wchar_t* end) const; + + public: + //! Default constructor. + RegExp(); + ~RegExp(); + + RegExp(RegExp&&) noexcept; + RegExp& operator=(RegExp&&) = delete; + + /*! Compile regular expression + Generate internal op-codes of expression. + + \param src - source of expression + \param options - compile options + + If compilation fails error code can be obtained with LastError function, + position of error in a expression can be obtained with ErrorPosition function. + See error codes in REError enumeration. + \sa LastError + \sa REError + \sa ErrorPosition + \sa options + */ + bool Compile(ReStringView src, int options=OP_PERLSTYLE|OP_OPTIMIZE); + + /*! Try to optimize regular expression + Significally speedup Search mode in some cases. + */ + bool Optimize(); + + /*! Try to match string with regular expression + \param text - string to match + \param match - array of SMatch structures that receive brackets positions. + \param matchcount - in/out parameter that indicate number of items in + match array on input, and number of brackets on output. + \param hmatch - storage of named brackets. + \sa SMatch + */ + bool Match(ReStringView text, RegExpMatch* match, int& matchcount, MatchHash* hmatch = nullptr) const; + /*! Advanced version of match. Can be used for multiple matches + on one string (to imitate /g modifier of perl regexp + */ + bool MatchEx(ReStringView text, size_t From, RegExpMatch* match, int& matchcount, MatchHash* hmatch = nullptr) const; + /*! Try to find substring that will match regexp. + Parameters and return value are the same as for Match. + It is highly recommended to call Optimize before Search. + */ + bool Search(ReStringView text, RegExpMatch* match, int& matchcount, MatchHash* hmatch = nullptr) const; + /*! Advanced version of search. Can be used for multiple searches + on one string (to imitate /g modifier of perl regexp + */ + bool SearchEx(ReStringView text, size_t From, RegExpMatch* match, int& matchcount, MatchHash* hmatch = nullptr) const; + + bool Search(ReStringView Str) const; + + /*! Get last error + \return code of the last error + Check REError for explanation + \sa REError + \sa ErrorPosition + */ + int LastError() const {return errorcode;} + /*! Get last error position. + \return position of the last error in the regexp source. + \sa LastError + */ + int ErrorPosition() const { return srcstart + errorpos; } + /*! Get number of brackets in expression + \return number of brackets, excluding brackets of type (:expr) + and named brackets. + */ + int GetBracketsCount() const {return bracketscount;} +}; + +#endif // REGEXP_HPP_18B41BD7_69F8_461A_8A81_069B447D5554 diff --git a/far2l/src/mix/cddrv.cpp b/far2l/src/mix/cddrv.cpp new file mode 100644 index 00000000..9ea43580 --- /dev/null +++ b/far2l/src/mix/cddrv.cpp @@ -0,0 +1,52 @@ +/* +cddrv.cpp + +про сидюк +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + +#include "cddrv.hpp" +#include "drivemix.hpp" +#include "pathmix.hpp" + + +bool IsDriveTypeCDROM(UINT DriveType) +{ + return DriveType == DRIVE_CDROM || (DriveType >= DRIVE_CD_RW && DriveType <= DRIVE_HDDVD_RW); +} + +UINT FAR_GetDriveType(const wchar_t *RootDir, CDROM_DeviceCapabilities *Caps, DWORD Detect) +{ + if (Caps) + *Caps = CAPABILITIES_NONE; + + return DRIVE_FIXED; +} diff --git a/far2l/src/mix/cddrv.hpp b/far2l/src/mix/cddrv.hpp new file mode 100644 index 00000000..fe69a860 --- /dev/null +++ b/far2l/src/mix/cddrv.hpp @@ -0,0 +1,142 @@ +#pragma once + +/* +cddrv.hpp + +про сидюк +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <WinCompat.h> + +enum CDROM_DeviceCapabilities +{ + CAPABILITIES_NONE = 0x00000000, + + //CD + CAPABILITIES_READ_CDROM = 0x00000001, + CAPABILITIES_READ_CDR = 0x00000002, + CAPABILITIES_READ_CDRW = 0x00000004, + + CAPABILITIES_WRITE_CDR = 0x00000008, + CAPABILITIES_WRITE_CDRW = 0x00000010, + + //DVD + CAPABILITIES_READ_DVDROM = 0x00000020, + CAPABILITIES_READ_DVDR = 0x00000040, + CAPABILITIES_READ_DVDRW = 0x00000080, + CAPABILITIES_READ_DVDRAM = 0x00000100, + + CAPABILITIES_WRITE_DVDR = 0x00000200, + CAPABILITIES_WRITE_DVDRW = 0x00000400, + CAPABILITIES_WRITE_DVDRAM = 0x00000800, + + //BlueRay + CAPABILITIES_READ_BDROM = 0x00001000, + CAPABILITIES_WRITE_BDROM = 0x00002000, + + //HD-DVD + CAPABILITIES_READ_HDDVD = 0x00004000, + CAPABILITIES_WRITE_HDDVD = 0x00008000, + + //GENERIC + + CAPABILITIES_GENERIC_CDROM = CAPABILITIES_READ_CDROM | CAPABILITIES_READ_CDR | CAPABILITIES_READ_CDRW, + CAPABILITIES_GENERIC_CDRW = CAPABILITIES_WRITE_CDR | CAPABILITIES_WRITE_CDRW, + CAPABILITIES_GENERIC_DVDROM = CAPABILITIES_READ_DVDROM | CAPABILITIES_READ_DVDR | CAPABILITIES_READ_DVDRW | CAPABILITIES_READ_DVDRAM, + CAPABILITIES_GENERIC_DVDRW = CAPABILITIES_WRITE_DVDR | CAPABILITIES_WRITE_DVDRW, + CAPABILITIES_GENERIC_DVDRAM = CAPABILITIES_WRITE_DVDRAM, + + CAPABILITIES_GENERIC_BDROM = CAPABILITIES_READ_BDROM, + CAPABILITIES_GENERIC_BDRW = CAPABILITIES_WRITE_BDROM, + + CAPABILITIES_GENERIC_HDDVD = CAPABILITIES_READ_HDDVD, + CAPABILITIES_GENERIC_HDDVDRW = CAPABILITIES_WRITE_HDDVD +}; + +enum MMC_Features { + MMC_FEATUREPROFILE_LIST = 0x0000, + MMC_FEATURECORE = 0x0001, + MMC_FEATURE_MORPHING = 0x0002, + MMC_FEATURE_REMOVABLE = 0x0003, + MMC_FEATURE_WRITE_PROTECT = 0x0004, + MMC_FEATURE_RANDOM_READ = 0x0010, + MMC_FEATURE_MULTIREAD = 0x001D, + MMC_FEATURE_CD_READ = 0x001E, + MMC_FEATURE_DVD_READ = 0x001F, + MMC_FEATURE_RANDOM_WRITE = 0x0020, + MMC_FEATURE_INC_STREAM_WRITE = 0x0021, + MMC_FEATURE_SECTOR_ERASE = 0x0022, + MMC_FEATURE_FORMAT = 0x0023, + MMC_FEATURE_HW_DEFECT_MANAGEMENT = 0x0024, + MMC_FEATURE_WRITE_ONCE = 0x0025, + MMC_FEATURE_RESTRICTED_OW = 0x0026, + MMC_FEATURE_CWRW_CAV_WRITE = 0x0027, + MMC_FEATURE_MRW = 0x0028, + MMC_FEATURE_ENH_DEFECT_REPORT = 0x0029, + MMC_FEATURE_DVDPLUSRW = 0x002A, + MMC_FEATURE_DVDPLUSR = 0x002B, + MMC_FEATURE_RIGID_RESTRICTED_OW = 0x002C, + MMC_FEATURE_CD_TAO = 0x002D, + MMC_FEATURE_CD_MASTERING = 0x002E, + MMC_FEATURE_DVDMINUSR_RW_WRITE = 0x002F, + MMC_FEATURE_DDCD_READ = 0x0030, + MMC_FEATURE_DDCDR_WRITE = 0x0031, + MMC_FEATURE_DDCDRW_WRITE = 0x0032, + MMC_FEATURE_CDRW_WRITE = 0x0037, + MMC_FEATURE_POWER_MANAGEMENT = 0x0100, + MMC_FEATURE_SMART = 0x0101, + MMC_FEATURE_EMBEDDED_CHARGER = 0x0102, + MMC_FEATURE_CD_AUDIO_ANALOG = 0x0103, + MMC_FEATURE_MICROCODE_UPGRADE = 0x0104, + MMC_FEATURE_TIMEOUT = 0x0105, + MMC_FEATURE_DVD_CSS = 0x0106, + MMC_FEATURE_REALTIME_STREAM = 0x0107, + MMC_FEATURE_DRIVE_SN = 0x0108, + MMC_FEATURE_DISC_CTRL_BLOCKS = 0x010A, + MMC_FEATURE_DVD_CPRM = 0x010B, + MMC_FEATURE_FIRMWARE_INFO = 0x010C, + // MMC-5/MMC-6. + MMC_FEATURE_LAYER_JUMP_REC = 0x0033, + MMC_FEATURE_BDR_POW = 0x0038, + MMC_FEATURE_DVDPLUSRW_DL = 0x003A, + MMC_FEATURE_DVDPLUSR_DL = 0x003B, + MMC_FEATURE_BD_READ = 0x0040, + MMC_FEATURE_BD_WRITE = 0x0041, + MMC_FEATURE_TSR = 0x0042, + MMC_FEATURE_HDDVD_READ = 0x0050, + MMC_FEATURE_HDDVD_WRITE = 0x0051, + MMC_FEATURE_HYBRID_DISC = 0x0080, + MMC_FEATURE_AACS = 0x010D, + MMC_FEATURE_VCPS = 0x0110 +}; + + +bool IsDriveTypeCDROM(UINT DriveType); +UINT FAR_GetDriveType(const wchar_t *RootDir, CDROM_DeviceCapabilities *caps=nullptr, DWORD Detect=0); diff --git a/far2l/src/mix/chgprior.cpp b/far2l/src/mix/chgprior.cpp new file mode 100644 index 00000000..91fb54fd --- /dev/null +++ b/far2l/src/mix/chgprior.cpp @@ -0,0 +1,45 @@ +/* +chgprior.cpp + +class ChangePriority +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + +#include "chgprior.hpp" + +ChangePriority::ChangePriority(Priority NewPriority) +{ +} + + +ChangePriority::~ChangePriority() +{ +} diff --git a/far2l/src/mix/chgprior.hpp b/far2l/src/mix/chgprior.hpp new file mode 100644 index 00000000..0add26b0 --- /dev/null +++ b/far2l/src/mix/chgprior.hpp @@ -0,0 +1,45 @@ +#pragma once + +/* +chgprior.hpp + +class ChangePriority +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class ChangePriority +{ + public: + enum Priority { + IDLE, + NORMAL + }; + ChangePriority(Priority NewPriority); + ~ChangePriority(); +}; diff --git a/far2l/src/mix/cvtname.cpp b/far2l/src/mix/cvtname.cpp new file mode 100644 index 00000000..e82b071e --- /dev/null +++ b/far2l/src/mix/cvtname.cpp @@ -0,0 +1,457 @@ +/* +cvtname.cpp + +Функций для преобразования имен файлов/путей. +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "cvtname.hpp" +#include "cddrv.hpp" +#include "syslog.hpp" +#include "pathmix.hpp" +#include "drivemix.hpp" +#include "strmix.hpp" +#include <set> + +#define IsDot(str) (str == L'.') + +void MixToFullPath(FARString& strPath) +{ + //Skip all path to root (with slash if exists) + LPWSTR pstPath=strPath.GetBuffer(); + //size_t PathOffset=0; +// Point2Root(pstPath,PathOffset); +// pstPath+=PathOffset; + + //Process "." and ".." if exists + for (int m = 0; pstPath[m];) + { + //fragment "." + if (IsDot(pstPath[m]) && (!m || IsSlash(pstPath[m - 1]))) + { + LPCWSTR pstSrc; + LPWSTR pstDst; + + switch (pstPath[m + 1]) + { + //fragment ".\" + //case L'\\': + //fragment "./" + case L'/': + { + for (pstSrc = pstPath + m + 2, pstDst = pstPath + m; *pstSrc; pstSrc++, pstDst++) + { + *pstDst = *pstSrc; + } + + *pstDst = 0; + continue; + } + break; + //fragment "." at the end + case 0: + { + pstPath[m] = 0; + continue; + } + break; + //fragment "..\" or "../" or ".." at the end + case L'.': + { + if (IsSlash(pstPath[m + 2]) || !pstPath[m + 2]) + { + int n; + + //Calculate subdir name offset + for (n = m - 2; (n >= 0) && (!IsSlash(pstPath[n])); n--); + + n = (n < 0) ? 0 : n + 1; + + //fragment "..\" or "../" + if (pstPath[m + 2]) + { + for (pstSrc = pstPath + m + 3, pstDst = pstPath + n; *pstSrc; pstSrc++, pstDst++) + { + *pstDst = *pstSrc; + } + + *pstDst = 0; + } + //fragment ".." at the end + else if (n > 0) + { + pstPath[n] = 0; + } + else + {//dont go to nowhere + pstPath[0] = GOOD_SLASH; + pstPath[1] = 0; + } + + m = n; + continue; + } + } + break; + } + } + + m++; + } + strPath.ReleaseBuffer(); + if (strPath.GetLength() > 1 && strPath[strPath.GetLength() - 1] == GOOD_SLASH) + strPath.Truncate(strPath.GetLength() - 1);// #249 +} + +bool MixToFullPath(LPCWSTR stPath, FARString& strDest, LPCWSTR stCurrentDir) +{ + if (stPath && *stPath == GOOD_SLASH) { + strDest = stPath; + MixToFullPath(strDest); + return true; + } + + strDest.Clear(); + + if (stCurrentDir && *stCurrentDir) { + strDest = stCurrentDir; + } + + if (strDest.IsEmpty()) { + apiGetCurrentDirectory(strDest); + if (strDest.IsEmpty()) { //wtf + strDest = L"." WGOOD_SLASH; + } + } + + if (strDest.At(strDest.GetLength() - 1) != GOOD_SLASH) + strDest+= GOOD_SLASH; + + if (stPath) { + while (stPath[0]=='.' && (!stPath[1] || stPath[1]==GOOD_SLASH)) { + ++stPath; + if (*stPath==GOOD_SLASH) + ++stPath; + } + strDest+= stPath; + } + + MixToFullPath(strDest); + return true; + + + + + + /* + size_t lPath=wcslen(NullToEmpty(stPath)), + lCurrentDir=wcslen(NullToEmpty(stCurrentDir)), + lFullPath=lPath+lCurrentDir; + + if (lFullPath > 0) + { + strDest.Clear(); + LPCWSTR pstPath = nullptr, pstCurrentDir = nullptr; + bool blIgnore = false; + size_t PathOffset=0; + PATH_PFX_TYPE PathType=Point2Root(stPath,PathOffset); + pstPath=stPath+PathOffset; + + switch (PathType) + { + case PPT_NONE: //"abc" + { + pstCurrentDir=stCurrentDir; + } + break; + case PPT_DRIVE: //"C:" or "C:abc" + { + WCHAR DriveVar[]={L'=',*stPath,L':',L'\0'}; + FARString strValue; + + if (apiGetEnvironmentVariable(DriveVar,strValue)) + { + strDest=strValue; + } + else + { + if (Upper(*stPath)==Upper(*stCurrentDir)) + { + strDest=stCurrentDir; + } + else + { + strDest=DriveVar+1; + } + } + + AddEndSlash(strDest); + } + break; + case PPT_ROOT: //"\" or "\abc" + { + if (stCurrentDir) + { + size_t PathOffset=0; + + if (Point2Root(stCurrentDir,PathOffset)!=PPT_NONE) + { + strDest=FARString(stCurrentDir,PathOffset); + } + } + } + break; + case PPT_PREFIX: //"C:\abc" + { + pstPath=stPath; + } + break; + case PPT_NT: //"\\?\abc" + { + blIgnore=true; + pstPath=stPath; + } + break; + } + + if (pstCurrentDir) + { + strDest+=pstCurrentDir; + AddEndSlash(strDest); + } + + if (pstPath) + { + strDest+=pstPath; + } + + if (!blIgnore) + MixToFullPath(strDest); + + return true; + } + + return false;*/ +} + + +/* + Преобразует Src в полный РЕАЛЬНЫЙ путь с учетом reparse point. + Note that Src can be partially non-existent. +*/ +void ConvertNameToReal(const wchar_t *Src, FARString &strDest) +{ + char buf[PATH_MAX + 1]; + std::string s = Wide2MB(Src); + if (*Src==GOOD_SLASH) { + std::string cutoff; + for (;;) { + if (sdc_realpath(s.c_str(), buf)) { + buf[sizeof(buf)-1] = 0; + if (strcmp(buf, s.c_str())!=0) { + strDest = buf; + if (!cutoff.empty()) + strDest.Append(cutoff.c_str()); + return; + } + break; + } + + size_t p = s.rfind(GOOD_SLASH); + if (p==std::string::npos || p==0) break; + cutoff.insert(0, s.c_str() + p); + s.resize(p); + } + } else { + if (sdc_realpath(s.c_str(), buf)) { + if (strcmp(buf, s.c_str()) != 0) { + strDest = buf; + return; + } + } else { + ssize_t r = sdc_readlink(s.c_str(), buf, sizeof(buf) - 1); + if (r > 0 && r < (ssize_t)sizeof(buf) && buf[0]) { + buf[r] = 0; + if (buf[0] != GOOD_SLASH) { + strDest = s; + CutToSlash(strDest); + strDest+= buf; + } else + strDest = buf; + ConvertNameToFull(strDest); + return; + } + } + } + strDest = Src; +} + +// Косметические преобразования строки пути. +// CheckFullPath используется в FCTL_SET[ANOTHER]PANELDIR +FARString& PrepareDiskPath(FARString &strPath, bool CheckFullPath) +{ + // elevation not required during cosmetic operation + + if (!strPath.IsEmpty()) + { +/* if (strPath.At(1)==L':' || (strPath.At(0)==L'/' && strPath.At(1)==L'/')) + { + bool DoubleSlash = strPath.At(1)==L'/'; + while(ReplaceStrings(strPath,L"//",L"/")); + if(DoubleSlash) + { + strPath = "/"+strPath; + } + + if (CheckFullPath) + { + ConvertNameToFull(strPath,strPath); + size_t FullLen=strPath.GetLength(); + wchar_t *lpwszPath=strPath.GetBuffer(),*Src=lpwszPath; + + if (IsLocalPath(lpwszPath)) + { + Src+=2; + + if (IsSlash(*Src)) + Src++; + } + + if (*Src) + { + wchar_t *Dst=Src; + + for (wchar_t c=*Src; ; Src++,c=*Src) + { + if (IsSlash(c) || (!c && !IsSlash(*(Src-1)))) + { + *Src=0; + FAR_FIND_DATA_EX fd; + BOOL find=apiGetFindDataEx(lpwszPath,fd); + *Src=c; + + if (find) + { + size_t n=fd.strFileName.GetLength(); + int n1=(int)(n-(Src-Dst)); + + if (n1>0) + { + size_t dSrc=Src-lpwszPath,dDst=Dst-lpwszPath; + strPath.ReleaseBuffer(FullLen); + lpwszPath=strPath.GetBuffer(FullLen+n1+1); + Src=lpwszPath+dSrc; + Dst=lpwszPath+dDst; + } + + if (n1) + { + wmemmove(Src+n1,Src,FullLen-(Src-lpwszPath)+1); + Src+=n1; + FullLen+=n1; + } + + wmemcpy(Dst,fd.strFileName,n); + } + + if (c) + { + Dst=Src+1; + } + } + + if (!*Src) + break; + } + } + + strPath.ReleaseBuffer(FullLen); + } + + wchar_t *lpwszPath = strPath.GetBuffer(); + + if (lpwszPath[0]==L'/' && lpwszPath[1]==L'/') + { + if (IsLocalPrefixPath(lpwszPath)) + { + lpwszPath[4] = Upper(lpwszPath[4]); + } + else + { + wchar_t *ptr=&lpwszPath[2]; + + while (*ptr && !IsSlash(*ptr)) + { + *ptr=Upper(*ptr); + ptr++; + } + } + } + else + { + lpwszPath[0]=Upper(lpwszPath[0]); + } + + strPath.ReleaseBuffer(strPath.GetLength()); + }*/ + } + + return strPath; +} + +void ConvertNameToFull(const wchar_t *lpwszSrc, FARString &strDest) +{ + if (*lpwszSrc != GOOD_SLASH) { + FARString strCurDir; + apiGetCurrentDirectory(strCurDir); + FARString strSrc = lpwszSrc; + MixToFullPath(strSrc,strDest,strCurDir); + } else { + strDest = lpwszSrc; + MixToFullPath(strDest); + } +} + + +void ConvertNameToFull(FARString &strSrcDest) +{ + ConvertNameToFull(strSrcDest, strSrcDest); +} + + +void ConvertHomePrefixInPath(FARString &strFileName) +{ + if (strFileName.GetLength() > 1 && strFileName[0] == L'~' && strFileName[1] == GOOD_SLASH) { + strFileName.Replace(0, 1, FARString(GetMyHome())); + } +} + diff --git a/far2l/src/mix/cvtname.hpp b/far2l/src/mix/cvtname.hpp new file mode 100644 index 00000000..12496e74 --- /dev/null +++ b/far2l/src/mix/cvtname.hpp @@ -0,0 +1,45 @@ +#pragma once + +/* +cvtname.hpp + +Функций для преобразования имен файлов/путей. +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "FARString.hpp" + +FARString& PrepareDiskPath(FARString &strPath, bool CheckFullPath=true); + +bool MixToFullPath(LPCWSTR stPath, FARString& strDest, LPCWSTR stCurrentDir); +void ConvertNameToReal(const wchar_t *Src, FARString &strDest); +void ConvertNameToFull(const wchar_t *Src, FARString &strDest); +void ConvertNameToFull(FARString &strSrcDest); + +void ConvertHomePrefixInPath(FARString &strFileName); diff --git a/far2l/src/mix/dirmix.cpp b/far2l/src/mix/dirmix.cpp new file mode 100644 index 00000000..e147d71a --- /dev/null +++ b/far2l/src/mix/dirmix.cpp @@ -0,0 +1,327 @@ +/* +dirmix.cpp + +Misc functions for working with directories +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "dirmix.hpp" +#include "cvtname.hpp" +#include "message.hpp" +#include "lang.hpp" +#include "language.hpp" +#include "ctrlobj.hpp" +#include "filepanels.hpp" +#include "treelist.hpp" +#include "config.hpp" +#include "pathmix.hpp" +#include "strmix.hpp" +#include "interf.hpp" +#include "scantree.hpp" +#include "delete.hpp" +#include <atomic> + + +BOOL FarChDir(const wchar_t *NewDir, BOOL ChangeDir) +{ + if (!NewDir || !*NewDir) + return FALSE; + + BOOL rc=FALSE; + FARString strCurDir; + + { + if (ChangeDir) + { + if (*NewDir=='/') { + strCurDir = NewDir; + rc = apiSetCurrentDirectory(strCurDir); // здесь берем корень + } else { + apiGetCurrentDirectory(strCurDir); + ConvertNameToFull(NewDir,strCurDir); + PrepareDiskPath(strCurDir,false); // TRUE ??? + rc = apiSetCurrentDirectory(strCurDir); + } + if (!rc) + { + fprintf(stderr, "FarChDir: FAILED - '%ls'\n", NewDir); + } + } + } + + return rc; +} + +/* + Функция TestFolder возвращает одно состояний тестируемого каталога: + + TSTFLD_NOTFOUND (2) - нет такого + TSTFLD_NOTEMPTY (1) - не пусто + TSTFLD_EMPTY (0) - пусто + TSTFLD_NOTACCESS (-1) - нет доступа + TSTFLD_ERROR (-2) - ошибка (кривые параметры или нехватило памяти для выделения промежуточных буферов) +*/ +TESTFOLDERCONST TestFolder(const wchar_t *Path) +{ + if (!(Path && *Path)) // проверка на вшивость + return TSTFLD_ERROR; + + std::string mbPath; + Wide2MB(Path, mbPath); + while (mbPath.size() > 1 && mbPath.back() == '/') + mbPath.resize(mbPath.size() - 1); + + struct stat s{}; + int r = sdc_stat(mbPath.c_str(), &s); + if (r == -1) + { + if (errno == EPERM || errno == EACCES) + return TSTFLD_NOTACCESS; + + return TSTFLD_NOTFOUND; + } + + if (!S_ISDIR(s.st_mode)) // not directories are always empty + return TSTFLD_EMPTY; + + DIR *d = sdc_opendir(mbPath.c_str()); + if (!d) switch (errno) + { + case EPERM: case EACCES: + return TSTFLD_NOTACCESS; + + case ENOMEM: case EMFILE: case ENFILE: case EBADF: + return TSTFLD_ERROR; + + default: + return TSTFLD_EMPTY; + } + + TESTFOLDERCONST out = TSTFLD_EMPTY; + for (;;) + { + struct dirent *de = sdc_readdir(d); + if (!de) break; + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) + { + out = TSTFLD_NOTEMPTY; + break; + } + } + sdc_closedir(d); + + return out; +} + +/* + Проверка пути или хост-файла на существование + Если идет проверка пути (IsHostFile=FALSE), то будет + предпринята попытка найти ближайший путь. Результат попытки + возвращается в переданном TestPath. + + Return: 0 - бЯда. + 1 - ОБИ!, + -1 - Почти что ОБИ, но ProcessPluginEvent вернул TRUE + TestPath может быть пустым, тогда просто исполним ProcessPluginEvent() + +*/ +int CheckShortcutFolder(FARString *pTestPath,int IsHostFile, BOOL Silent) +{ + if (pTestPath && !pTestPath->IsEmpty() && apiGetFileAttributes(*pTestPath) == INVALID_FILE_ATTRIBUTES) + { + int FoundPath=0; + FARString strTarget = *pTestPath; + TruncPathStr(strTarget, ScrX-16); + + if (IsHostFile) + { + WINPORT(SetLastError)(ERROR_FILE_NOT_FOUND); + + if (!Silent) + Message(MSG_WARNING | MSG_ERRORTYPE, 1, MSG(MError), strTarget, MSG(MOk)); + } + else // попытка найти! + { + WINPORT(SetLastError)(ERROR_PATH_NOT_FOUND); + + if (Silent || !Message(MSG_WARNING | MSG_ERRORTYPE, 2, MSG(MError), strTarget, MSG(MNeedNearPath), MSG(MHYes),MSG(MHNo))) + { + FARString strTestPathTemp = *pTestPath; + + for (;;) + { + if (!CutToSlash(strTestPathTemp,true)) + break; + + if (apiGetFileAttributes(strTestPathTemp) != INVALID_FILE_ATTRIBUTES) + { + int ChkFld=TestFolder(strTestPathTemp); + + if (ChkFld > TSTFLD_ERROR && ChkFld < TSTFLD_NOTFOUND) + { + if (!(pTestPath->At(0) == GOOD_SLASH && pTestPath->At(1) == GOOD_SLASH && !strTestPathTemp.At(1))) + { + *pTestPath = strTestPathTemp; + + if (pTestPath->GetLength() == 2) // для случая "C:", иначе попадем в текущий каталог диска C: + AddEndSlash(*pTestPath); + + FoundPath=1; + } + + break; + } + } + } + } + } + + if (!FoundPath) + return 0; + } + + if (CtrlObject->Cp()->ActivePanel->ProcessPluginEvent(FE_CLOSE,nullptr)) + return -1; + + return 1; +} + +void CreatePath(FARString &strPath) +{ + wchar_t *ChPtr = strPath.GetBuffer(); +// wchar_t *DirPart = ChPtr; + BOOL bEnd = FALSE; + + for (;;) + { + if (!*ChPtr || IsSlash(*ChPtr)) + { + if (!*ChPtr) + bEnd = TRUE; + + *ChPtr = 0; + + if (apiCreateDirectory(strPath, nullptr)) + TreeList::AddTreeName(strPath); + + if (bEnd) + break; + + *ChPtr = GOOD_SLASH; +// DirPart = ChPtr+1; + } + + ChPtr++; + } + + strPath.ReleaseBuffer(); +} + +std::string GetHelperPathName(const char *name) +{ + std::string out = g_strFarPath.GetMB(); + out+= GOOD_SLASH; + out+= name; + + struct stat s; + if (stat(out.c_str(), &s) == 0) + return out; + + if (TranslateInstallPath_Share2Lib(out)) { + if (stat(out.c_str(), &s) == 0) + return out; + } + + fprintf(stderr, "GetHelperPathName('%s') - not found\n", name); + return out; +} + +std::string GetMyScriptQuoted(const char *name) +{ + std::string out = "\""; + out+= EscapeCmdStr(GetHelperPathName(name)); + out+= "\""; + return out; +} + + +void PrepareTemporaryOpenPath(FARString &Path) +{ + Path = InMyTemp("open"); + + std::vector<FARString> outdated; + + ScanTree scan_tree(0, 0); + scan_tree.SetFindPath(Path.CPtr(), L"*", 0); + FAR_FIND_DATA_EX found_data; + FARString found_name; + time_t now = time(nullptr); + while (scan_tree.GetNextName(&found_data, found_name)) { + struct timespec ts_mod = {}, ts_change = {}; + WINPORT(FileTimeToLocalFileTime)(&found_data.ftUnixModificationTime, &found_data.ftUnixModificationTime); + WINPORT(FileTimeToLocalFileTime)(&found_data.ftUnixStatusChangeTime, &found_data.ftUnixStatusChangeTime); + WINPORT(FileTime_Win32ToUnix)(&found_data.ftUnixModificationTime, &ts_mod); + WINPORT(FileTime_Win32ToUnix)(&found_data.ftUnixStatusChangeTime, &ts_change); + time_t delta = std::min(now - ts_mod.tv_sec, now - ts_change.tv_sec); + if (delta > 60) {//one minute ought be enouht to open anything (c) + outdated.push_back(found_name); + fprintf(stderr, "PrepareTemporaryOpenPath: delta=%u for '%ls'\n", + (unsigned int)delta, found_name.CPtr()); + } + }; + + for (const auto &p : outdated) { + DeleteDirTree(p.CPtr()); + } + apiCreateDirectory(Path, nullptr); + + static std::atomic<unsigned short> s_counter{0}; + char tmp[64]; sprintf(tmp, "%c%u_%u", GOOD_SLASH, (unsigned int)getpid(), (unsigned int)++s_counter); + + Path+= tmp; + apiCreateDirectory(Path, nullptr); +} + + +FARString DefaultPanelInitialDirectory() +{ + FARString out; + const std::string &home = GetMyHome(); + if (!home.empty()) { + out = home; + } else { + out = g_strFarPath; + } + + DeleteEndSlash(out); + return out; +} diff --git a/far2l/src/mix/dirmix.hpp b/far2l/src/mix/dirmix.hpp new file mode 100644 index 00000000..4137d937 --- /dev/null +++ b/far2l/src/mix/dirmix.hpp @@ -0,0 +1,65 @@ +#pragma once + +/* +dirmix.hpp + +Misc functions for working with directories +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <WinCompat.h> +#include "FARString.hpp" + +enum TESTFOLDERCONST // for TestFolder() +{ + TSTFLD_ERROR = -2, + TSTFLD_NOTACCESS = -1, + TSTFLD_EMPTY = 0, + TSTFLD_NOTEMPTY = 1, + TSTFLD_NOTFOUND = 2, +}; + +/* $ 15.02.2002 IS + Установка нужного диска и каталога и установление соответствующей переменной + окружения. В случае успеха возвращается не ноль. + Если ChangeDir==FALSE, то не меняем текущий диск, а только устанавливаем + переменные окружения. +*/ +BOOL FarChDir(const wchar_t *NewDir,BOOL ChangeDir=TRUE); + +TESTFOLDERCONST TestFolder(const wchar_t *Name); +int CheckShortcutFolder(FARString *pTestPath,int IsHostFile, BOOL Silent=FALSE); + +void CreatePath(FARString &strPath); + +std::string GetHelperPathName(const char *name); +std::string GetMyScriptQuoted(const char *name); + +void PrepareTemporaryOpenPath(FARString &Path); +FARString DefaultPanelInitialDirectory(); diff --git a/far2l/src/mix/drivemix.cpp b/far2l/src/mix/drivemix.cpp new file mode 100644 index 00000000..b5449e24 --- /dev/null +++ b/far2l/src/mix/drivemix.cpp @@ -0,0 +1,95 @@ +/* +drivemix.cpp + +Misc functions for drive/disk info +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "drivemix.hpp" +#include "config.hpp" +#include "cddrv.hpp" +#include "pathmix.hpp" +#include <errno.h> + +static int StatForPathOrItsParent(const wchar_t *path, struct stat &s) +{ + std::string mb; + if (*path != GOOD_SLASH) { + FARString strSrcFullName; + ConvertNameToFull(path, strSrcFullName); + if (!strSrcFullName.IsEmpty()) + mb = strSrcFullName.GetMB(); + } + if (mb.empty()) + mb = Wide2MB(path); + + for (;;) { + int r = sdc_stat(mb.c_str(), &s); + if (r == 0 || mb.size() < 2) + return r; + + size_t slash = mb.rfind(GOOD_SLASH); + if (slash==std::string::npos || slash==0) + return -1; + + mb.resize(slash); + } +} + +int CheckDisksProps(const wchar_t *SrcPath,const wchar_t *DestPath,int CheckedType) +{ + if (CheckedType == CHECKEDPROPS_ISSAMEDISK) + { + struct stat s_src = {}, s_dest = {}; + int r = StatForPathOrItsParent(SrcPath, s_src); + if (r==-1) { + fprintf(stderr, "CheckDisksProps: stat errno=%u for src='%ls'\n", errno, SrcPath); + return false; + } + + r = StatForPathOrItsParent(DestPath, s_dest); + if (r==-1) { + fprintf(stderr, "CheckDisksProps: stat errno=%u for dest='%ls'\n", errno, DestPath); + return false; + } + + return (s_src.st_dev==s_dest.st_dev) ? TRUE : FALSE; + } + + if (CheckedType == CHECKEDPROPS_ISDST_ENCRYPTION) + { + return FALSE; + } + + return TRUE; +} + diff --git a/far2l/src/mix/drivemix.hpp b/far2l/src/mix/drivemix.hpp new file mode 100644 index 00000000..f2e4a547 --- /dev/null +++ b/far2l/src/mix/drivemix.hpp @@ -0,0 +1,68 @@ +#pragma once + +/* +drivemix.hpp + +Misc functions for drive/disk info +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +enum +{ + // DRIVE_UNKNOWN = 0, + // DRIVE_NO_ROOT_DIR = 1, + // DRIVE_REMOVABLE = 2, + // DRIVE_FIXED = 3, + // DRIVE_REMOTE = 4, + // DRIVE_CDROM = 5, + // DRIVE_RAMDISK = 6, + + DRIVE_SUBSTITUTE =15, + DRIVE_REMOTE_NOT_CONNECTED =16, + DRIVE_CD_RW =18, + DRIVE_CD_RWDVD =19, + DRIVE_DVD_ROM =20, + DRIVE_DVD_RW =21, + DRIVE_DVD_RAM =22, + DRIVE_BD_ROM =23, + DRIVE_BD_RW =24, + DRIVE_HDDVD_ROM =25, + DRIVE_HDDVD_RW =26, + DRIVE_USBDRIVE =40, + DRIVE_VIRTUAL =41, + DRIVE_NOT_INIT =255, +}; + +enum CHECKEDPROPS_TYPE +{ + CHECKEDPROPS_ISSAMEDISK, + CHECKEDPROPS_ISDST_ENCRYPTION, +}; + +int CheckDisksProps(const wchar_t *SrcPath,const wchar_t *DestPath,int CheckedType); diff --git a/far2l/src/mix/format.cpp b/far2l/src/mix/format.cpp new file mode 100644 index 00000000..8ecf8132 --- /dev/null +++ b/far2l/src/mix/format.cpp @@ -0,0 +1,147 @@ +/* +format.cpp + +Форматирование строк +*/ +/* +Copyright (c) 2009 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "format.hpp" +#include "interf.hpp" + +void BaseFormat::Reset() +{ + _Width=0; + _Precision=static_cast<size_t>(-1); + _FillChar=L' '; + _Align=fmt::A_RIGHT; +} + +void BaseFormat::Put(LPCWSTR Data,size_t Length) +{ + if (_Precision==static_cast<size_t>(-1)) + { + _Precision=Length; + } + + FARString OutStr(Data,Min(_Precision,Length)); + + if (_Align==fmt::A_RIGHT) + { + while (OutStr.GetLength()<_Width) + { + OutStr.Insert(0,_FillChar); + } + } + else + { + while (OutStr.GetLength()<_Width) + { + OutStr.Append(_FillChar); + } + } + + Commit(OutStr); + Reset(); +} + +BaseFormat& BaseFormat::operator<<(WCHAR Value) +{ + Put(&Value,1); + return *this; +} + +BaseFormat& BaseFormat::operator<<(INT64 Value) +{ + WCHAR Buffer[32]; + //_i64tow(Value,Buffer,10); + swprintf(Buffer, 32, L"%lli", Value); + Put(Buffer,StrLength(Buffer)); + return *this; +} + +BaseFormat& BaseFormat::operator<<(UINT64 Value) +{ + WCHAR Buffer[32]; + //_ui64tow(Value,Buffer,10); + swprintf(Buffer, 32, L"%llu", Value); + Put(Buffer,StrLength(Buffer)); + return *this; +} + +BaseFormat& BaseFormat::operator<<(LPCWSTR Data) +{ + Data=NullToEmpty(Data); + Put(Data,StrLength(Data)); + return *this; +} + +BaseFormat& BaseFormat::operator<<(FARString& String) +{ + Put(String,String.GetLength()); + return *this; +} + +BaseFormat& BaseFormat::operator<<(const fmt::Width& Manipulator) +{ + SetWidth(Manipulator.GetValue()); + return *this; +} + +BaseFormat& BaseFormat::operator<<(const fmt::Precision& Manipulator) +{ + SetPrecision(Manipulator.GetValue()); + return *this; +} + + +BaseFormat& BaseFormat::operator<<(const fmt::FillChar& Manipulator) +{ + SetFillChar(Manipulator.GetValue()); + return *this; +} + +BaseFormat& BaseFormat::operator<<(const fmt::LeftAlign& Manipulator) +{ + SetAlign(fmt::A_LEFT); + return *this; +} + +BaseFormat& BaseFormat::operator<<(const fmt::RightAlign& Manipulator) +{ + SetAlign(fmt::A_RIGHT); + return *this; +} + +void FormatScreen::Commit(const FARString& Data) +{ + Text(Data); +} + diff --git a/far2l/src/mix/format.hpp b/far2l/src/mix/format.hpp new file mode 100644 index 00000000..e1130e19 --- /dev/null +++ b/far2l/src/mix/format.hpp @@ -0,0 +1,138 @@ +#pragma once + +/* +format.hpp + +Форматирование строк +*/ +/* +Copyright (c) 2009 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <WinCompat.h> +#include "FARString.hpp" + +namespace fmt +{ + class Width + { + size_t Value; + public: + Width(size_t Value=0) {this->Value=Value;} + size_t GetValue()const {return Value;} + }; + + class Precision + { + size_t Value; + public: + Precision(size_t Value=static_cast<size_t>(-1)) {this->Value=Value;} + size_t GetValue()const {return Value;} + }; + + class FillChar + { + WCHAR Value; + public: + FillChar(WCHAR Value=L' ') {this->Value=Value;} + WCHAR GetValue()const {return Value;} + }; + + class LeftAlign {}; + + class RightAlign {}; + + enum AlignType + { + A_LEFT, + A_RIGHT, + }; + +}; + +class BaseFormat +{ + size_t _Width; + size_t _Precision; + WCHAR _FillChar; + fmt::AlignType _Align; + + void Reset(); + void Put(LPCWSTR Data,size_t Length); + + protected: + virtual void Commit(const FARString& Data)=0; + + public: + BaseFormat() {Reset();} + virtual ~BaseFormat() {} + + // attributes + void SetPrecision(size_t Precision=static_cast<size_t>(-1)) {_Precision=Precision;} + void SetWidth(size_t Width=0) {_Width=Width;} + void SetAlign(fmt::AlignType Align=fmt::A_RIGHT) {_Align=Align;} + void SetFillChar(WCHAR Char=L' ') {_FillChar=Char;} + + // data + BaseFormat& operator<<(INT64 Value); + BaseFormat& operator<<(UINT64 Value); + + BaseFormat& operator<<(short Value) {return operator<<(static_cast<INT64>(Value));} + BaseFormat& operator<<(USHORT Value) {return operator<<(static_cast<UINT64>(Value));} + + BaseFormat& operator<<(int Value) {return operator<<(static_cast<INT64>(Value));} + BaseFormat& operator<<(UINT Value) {return operator<<(static_cast<UINT64>(Value));} +#ifdef _WIN32 + BaseFormat& operator<<(long Value) {return operator<<(static_cast<INT64>(Value));} + BaseFormat& operator<<(ULONG Value) {return operator<<(static_cast<UINT64>(Value));} +#endif + BaseFormat& operator<<(WCHAR Value); + BaseFormat& operator<<(LPCWSTR Data); + BaseFormat& operator<<(FARString& String); + + // manipulators + BaseFormat& operator<<(const fmt::Width& Manipulator); + BaseFormat& operator<<(const fmt::Precision& Manipulator); + BaseFormat& operator<<(const fmt::LeftAlign& Manipulator); + BaseFormat& operator<<(const fmt::RightAlign& Manipulator); + BaseFormat& operator<<(const fmt::FillChar& Manipulator); +}; + +class FormatString:public BaseFormat +{ + FARString Value; + void Commit(const FARString& Data) {Value+=Data;} + public: + operator const wchar_t*()const {return Value;} + FARString& strValue() {return Value;} + const FARString& strValue() const {return Value;} + void Clear() {Value.Clear();} +}; + +class FormatScreen:public BaseFormat +{ + void Commit(const FARString& Data); +}; diff --git a/far2l/src/mix/mix.cpp b/far2l/src/mix/mix.cpp new file mode 100644 index 00000000..68dc8140 --- /dev/null +++ b/far2l/src/mix/mix.cpp @@ -0,0 +1,201 @@ +/* +mix.cpp + +Куча разных вспомогательных функций +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "mix.hpp" +#include "CFileMask.hpp" +#include "scantree.hpp" +#include "config.hpp" +#include "pathmix.hpp" +#include "dirmix.hpp" +#include "InterThreadCall.hpp" + +int ToPercent(uint32_t N1,uint32_t N2) +{ + if (N1 > 10000) + { + N1/=100; + N2/=100; + } + + if (!N2) + return 0; + + if (N2<N1) + return(100); + + return((int)(N1*100/N2)); +} + +int ToPercent64(uint64_t N1, uint64_t N2) +{ + if (N1 > 10000) + { + N1/=100; + N2/=100; + } + + if (!N2) + return 0; + + if (N2<N1) + return 100; + + return static_cast<int>(N1*100/N2); +} + +/* $ 30.07.2001 IS + 1. Проверяем правильность параметров. + 2. Теперь обработка каталогов не зависит от маски файлов + 3. Маска может быть стандартного фаровского вида (со скобками, + перечислением и пр.). Может быть несколько масок файлов, разделенных + запятыми или точкой с запятой, можно указывать маски исключения, + можно заключать маски в кавычки. Короче, все как и должно быть :-) +*/ +void WINAPI FarRecursiveSearch(const wchar_t *InitDir,const wchar_t *Mask,FRSUSERFUNC Func,DWORD Flags,void *Param) +{ + if (Func && InitDir && *InitDir && Mask && *Mask) + { + CFileMask FMask; + + if (!FMask.Set(Mask, FMF_SILENT)) return; + + Flags=Flags&0x000000FF; // только младший байт! + ScanTree ScTree(Flags & FRS_RETUPDIR,Flags & FRS_RECUR, Flags & FRS_SCANSYMLINK); + FAR_FIND_DATA_EX FindData; + FARString strFullName; + ScTree.SetFindPath(InitDir,L"*"); + + while (ScTree.GetNextName(&FindData,strFullName)) + { + if (FMask.Compare(FindData.strFileName)) + { + FAR_FIND_DATA fdata; + apiFindDataExToData(&FindData, &fdata); + + if (!Func(&fdata,strFullName,Param)) + { + apiFreeFindData(&fdata); + break; + } + + apiFreeFindData(&fdata); + } + } + } +} + +/* $ 14.09.2000 SVS + + Функция FarMkTemp - получение имени временного файла с полным путем. + Dest - приемник результата + Template - шаблон по правилам функции mktemp, например "FarTmpXXXXXX" + Вернет требуемый размер приемника. +*/ +int WINAPI FarMkTemp(wchar_t *Dest, DWORD size, const wchar_t *Prefix) +{ + FARString strDest; + if (FarMkTempEx(strDest, Prefix, TRUE) && Dest && size) + { + far_wcsncpy(Dest, strDest, size); + } + return static_cast<int>(strDest.GetLength()+1); +} + +/* + v - точка + prefXXX X X XXX + \ / ^ ^^^\ PID + TID + | \------/ + | + +---------- [0A-Z] +*/ +FARString& FarMkTempEx(FARString &strDest, const wchar_t *Prefix, BOOL WithTempPath, const wchar_t *UserTempPath) +{ + if (!(Prefix && *Prefix)) + Prefix=L"FTMP"; + FARString strPath = L"."; + + if (WithTempPath) + { + apiGetTempPath(strPath); + } + else if(UserTempPath) + { + strPath=UserTempPath; + } + + AddEndSlash(strPath); + + wchar_t *lpwszDest = strDest.GetBuffer(StrLength(Prefix)+strPath.GetLength()+13); + UINT uniq = WINPORT(GetCurrentProcessId)(), savePid = uniq; + + for (;;) + { + if (!uniq) ++uniq; + + if (WINPORT(GetTempFileName)(strPath, Prefix, uniq, lpwszDest) + && apiGetFileAttributes(lpwszDest) == INVALID_FILE_ATTRIBUTES) break; + + if (++uniq == savePid) + { + *lpwszDest = 0; + break; + } + } + + strDest.ReleaseBuffer(); + return strDest; +} + +void DisplayNotification(const wchar_t *action, const char *object) +{ + if (!Opt.NotifOpt.OnlyIfBackground || !WINPORT(IsConsoleActive)()) { + WINPORT(ConsoleDisplayNotification)(action, MB2Wide(object).c_str()); + } +} + +void DisplayNotification(const char *action, const char *object) +{ + if (!Opt.NotifOpt.OnlyIfBackground || !WINPORT(IsConsoleActive)()) { + DisplayNotification(MB2Wide(action).c_str(), object); + } +} + +void DisplayNotification(const wchar_t *action, const wchar_t *object) +{ + if (!Opt.NotifOpt.OnlyIfBackground || !WINPORT(IsConsoleActive)()) { + DisplayNotification(action, Wide2MB(object).c_str()); + } +} diff --git a/far2l/src/mix/mix.hpp b/far2l/src/mix/mix.hpp new file mode 100644 index 00000000..c738d9e3 --- /dev/null +++ b/far2l/src/mix/mix.hpp @@ -0,0 +1,59 @@ +#pragma once + +/* +mix.hpp + +Mix +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <WinCompat.h> +#include "FARString.hpp" + +int ToPercent(uint32_t N1,uint32_t N2); +int ToPercent64(uint64_t N1,uint64_t N2); + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef int (WINAPI *FRSUSERFUNC)(const FAR_FIND_DATA *FData,const wchar_t *FullName,void *param); + void WINAPI FarRecursiveSearch(const wchar_t *initdir,const wchar_t *mask,FRSUSERFUNC func,DWORD flags,void *param); + + int WINAPI FarMkTemp(wchar_t *Dest, DWORD size, const wchar_t *Prefix); + +#ifdef __cplusplus +}; +#endif + +FARString& FarMkTempEx(FARString &strDest, const wchar_t *Prefix=nullptr, BOOL WithTempPath=TRUE, const wchar_t *UserTempPath=nullptr); +void DisplayNotification(const wchar_t *action, const char *object); +void DisplayNotification(const char *action, const char *object); +void DisplayNotification(const wchar_t *action, const wchar_t *object); diff --git a/far2l/src/mix/panelmix.cpp b/far2l/src/mix/panelmix.cpp new file mode 100644 index 00000000..57fb8499 --- /dev/null +++ b/far2l/src/mix/panelmix.cpp @@ -0,0 +1,611 @@ +/* +panelmix.cpp + +Commonly used panel related functions +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "panelmix.hpp" +#include "strmix.hpp" +#include "filepanels.hpp" +#include "config.hpp" +#include "panel.hpp" +#include "ctrlobj.hpp" +#include "keys.hpp" +#include "treelist.hpp" +#include "filelist.hpp" +#include "pathmix.hpp" +#include "panelctype.hpp" +#include "lang.hpp" +#include "datetime.hpp" + +int ColumnTypeWidth[]={0, 6, 6, 8, 5, 14, 14, 14, 14, 10, 0, 0, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const wchar_t *ColumnSymbol[]={L"N",L"S",L"P",L"D",L"T",L"DM",L"DC",L"DA",L"DE",L"A",L"Z",L"O",L"U",L"LN",L"F",L"G",L"C0",L"C1",L"C2",L"C3",L"C4",L"C5",L"C6",L"C7",L"C8",L"C9"}; + + +void ShellUpdatePanels(Panel *SrcPanel,BOOL NeedSetUpADir) +{ + if (!SrcPanel) + SrcPanel=CtrlObject->Cp()->ActivePanel; + + Panel *AnotherPanel=CtrlObject->Cp()->GetAnotherPanel(SrcPanel); + + switch (SrcPanel->GetType()) + { + case QVIEW_PANEL: + case INFO_PANEL: + SrcPanel=CtrlObject->Cp()->GetAnotherPanel(AnotherPanel=SrcPanel); + } + + int AnotherType=AnotherPanel->GetType(); + + if (AnotherType!=QVIEW_PANEL && AnotherType!=INFO_PANEL) + { + if (NeedSetUpADir) + { + FARString strCurDir; + SrcPanel->GetCurDir(strCurDir); + AnotherPanel->SetCurDir(strCurDir,TRUE); + AnotherPanel->Update(UPDATE_KEEP_SELECTION|UPDATE_SECONDARY); + } + else + { + // TODO: ??? + //if(AnotherPanel->NeedUpdatePanel(SrcPanel)) + // AnotherPanel->Update(UPDATE_KEEP_SELECTION|UPDATE_SECONDARY); + //else + { + // Сбросим время обновления панели. Если там есть нотификация - обновится сама. + if (AnotherType==FILE_PANEL) + ((FileList *)AnotherPanel)->ResetLastUpdateTime(); + + AnotherPanel->UpdateIfChanged(UIC_UPDATE_NORMAL); + } + } + } + + SrcPanel->Update(UPDATE_KEEP_SELECTION); + + if (AnotherType==QVIEW_PANEL) + AnotherPanel->Update(UPDATE_KEEP_SELECTION|UPDATE_SECONDARY); + + CtrlObject->Cp()->Redraw(); +} + +int CheckUpdateAnotherPanel(Panel *SrcPanel,const wchar_t *SelName) +{ + if (!SrcPanel) + SrcPanel=CtrlObject->Cp()->ActivePanel; + + Panel *AnotherPanel=CtrlObject->Cp()->GetAnotherPanel(SrcPanel); + AnotherPanel->CloseFile(); + + if (AnotherPanel->GetMode() == NORMAL_PANEL) + { + FARString strAnotherCurDir; + FARString strFullName; + AnotherPanel->GetCurDir(strAnotherCurDir); + AddEndSlash(strAnotherCurDir); + ConvertNameToFull(SelName, strFullName); + AddEndSlash(strFullName); + + if (wcsstr(strAnotherCurDir,strFullName)) + { + ((FileList*)AnotherPanel)->CloseChangeNotification(); + return TRUE; + } + } + + return FALSE; +} + +int _MakePath1(DWORD Key, FARString &strPathName, const wchar_t *Param2) +{ + int RetCode=FALSE; + int NeedRealName=FALSE; + strPathName.Clear(); + + switch (Key) + { + case KEY_CTRLALTBRACKET: // Вставить сетевое (UNC) путь из левой панели + case KEY_CTRLALTBACKBRACKET: // Вставить сетевое (UNC) путь из правой панели + case KEY_ALTSHIFTBRACKET: // Вставить сетевое (UNC) путь из активной панели + case KEY_ALTSHIFTBACKBRACKET: // Вставить сетевое (UNC) путь из пассивной панели + NeedRealName=TRUE; + case KEY_CTRLBRACKET: // Вставить путь из левой панели + case KEY_CTRLBACKBRACKET: // Вставить путь из правой панели + case KEY_CTRLSHIFTBRACKET: // Вставить путь из активной панели + case KEY_CTRLSHIFTBACKBRACKET: // Вставить путь из пассивной панели + case KEY_CTRLSHIFTNUMENTER: // Текущий файл с пасс.панели + case KEY_SHIFTNUMENTER: // Текущий файл с актив.панели + case KEY_CTRLSHIFTENTER: // Текущий файл с пасс.панели + case KEY_SHIFTENTER: // Текущий файл с актив.панели + { + Panel *SrcPanel=nullptr; + FilePanels *Cp=CtrlObject->Cp(); + + switch (Key) + { + case KEY_CTRLALTBRACKET: + case KEY_CTRLBRACKET: + SrcPanel=Cp->LeftPanel; + break; + case KEY_CTRLALTBACKBRACKET: + case KEY_CTRLBACKBRACKET: + SrcPanel=Cp->RightPanel; + break; + case KEY_SHIFTNUMENTER: + case KEY_SHIFTENTER: + case KEY_ALTSHIFTBRACKET: + case KEY_CTRLSHIFTBRACKET: + SrcPanel=Cp->ActivePanel; + break; + case KEY_CTRLSHIFTNUMENTER: + case KEY_CTRLSHIFTENTER: + case KEY_ALTSHIFTBACKBRACKET: + case KEY_CTRLSHIFTBACKBRACKET: + SrcPanel=Cp->GetAnotherPanel(Cp->ActivePanel); + break; + } + + if (SrcPanel) + { + if (Key == KEY_SHIFTENTER || Key == KEY_CTRLSHIFTENTER || Key == KEY_SHIFTNUMENTER || Key == KEY_CTRLSHIFTNUMENTER) + { + SrcPanel->GetCurName(strPathName); + } + else + { + /* TODO: Здесь нужно учесть, что у TreeList тоже есть путь :-) */ + if (!(SrcPanel->GetType()==FILE_PANEL || SrcPanel->GetType()==TREE_PANEL)) + return FALSE; + + SrcPanel->GetCurDirPluginAware(strPathName); + if (NeedRealName && SrcPanel->GetType()==FILE_PANEL && SrcPanel->GetMode() != PLUGIN_PANEL) + { + FileList *SrcFilePanel = (FileList *)SrcPanel; + SrcFilePanel->CreateFullPathName(strPathName, FILE_ATTRIBUTE_DIRECTORY, strPathName,TRUE); + } + + AddEndSlash(strPathName); + } + + if (Opt.QuotedName"EDNAME_INSERT) + EscapeSpace(strPathName); + + if (Param2) + strPathName += Param2; + + RetCode=TRUE; + } + } + break; + } + + return RetCode; +} + + +void TextToViewSettings(const wchar_t *ColumnTitles,const wchar_t *ColumnWidths, + unsigned int *ViewColumnTypes,int *ViewColumnWidths,int *ViewColumnWidthsTypes,int &ColumnCount) +{ + const wchar_t *TextPtr=ColumnTitles; + + for (ColumnCount=0; ColumnCount < PANEL_COLUMNCOUNT; ColumnCount++) + { + FARString strArgName; + + if (!(TextPtr=GetCommaWord(TextPtr,strArgName))) + break; + + strArgName.Upper(); + + if (strArgName.At(0)==L'N') + { + unsigned int &ColumnType=ViewColumnTypes[ColumnCount]; + ColumnType=NAME_COLUMN; + const wchar_t *Ptr = strArgName.CPtr()+1; + + while (*Ptr) + { + switch (*Ptr) + { + case L'M': + ColumnType|=COLUMN_MARK; + break; + case L'O': + ColumnType|=COLUMN_NAMEONLY; + break; + case L'R': + ColumnType|=COLUMN_RIGHTALIGN; + break; + } + + Ptr++; + } + } + else + { + if (strArgName.At(0)==L'S' || strArgName.At(0)==L'P' || strArgName.At(0)==L'G') + { + unsigned int &ColumnType=ViewColumnTypes[ColumnCount]; + ColumnType=(strArgName.At(0)==L'S') ? SIZE_COLUMN:PHYSICAL_COLUMN; + const wchar_t *Ptr = strArgName.CPtr()+1; + + while (*Ptr) + { + switch (*Ptr) + { + case L'C': + ColumnType|=COLUMN_COMMAS; + break; + case L'E': + ColumnType|=COLUMN_ECONOMIC; + break; + case L'F': + ColumnType|=COLUMN_FLOATSIZE; + break; + case L'T': + ColumnType|=COLUMN_THOUSAND; + break; + } + + Ptr++; + } + } + else + { + if (!StrCmpN(strArgName,L"DM",2) || !StrCmpN(strArgName,L"DC",2) || !StrCmpN(strArgName,L"DA",2) || !StrCmpN(strArgName,L"DE",2)) + { + unsigned int &ColumnType=ViewColumnTypes[ColumnCount]; + + switch (strArgName.At(1)) + { + case L'M': + ColumnType=WDATE_COLUMN; + break; + case L'C': + ColumnType=CDATE_COLUMN; + break; + case L'A': + ColumnType=ADATE_COLUMN; + break; + case L'E': + ColumnType=CHDATE_COLUMN; + break; + } + + const wchar_t *Ptr = strArgName.CPtr()+2; + + while (*Ptr) + { + switch (*Ptr) + { + case L'B': + ColumnType|=COLUMN_BRIEF; + break; + case L'M': + ColumnType|=COLUMN_MONTH; + break; + } + + Ptr++; + } + } + else + { + if (strArgName.At(0)==L'U') + { + unsigned int &ColumnType=ViewColumnTypes[ColumnCount]; + ColumnType=GROUP_COLUMN; + } + else if (strArgName.At(0)==L'O') + { + unsigned int &ColumnType=ViewColumnTypes[ColumnCount]; + ColumnType=OWNER_COLUMN; + + if (strArgName.At(1)==L'L') + ColumnType|=COLUMN_FULLOWNER; + } + else + { + for (unsigned I=0; I<ARRAYSIZE(ColumnSymbol); I++) + { + if (!StrCmp(strArgName,ColumnSymbol[I])) + { + ViewColumnTypes[ColumnCount]=I; + break; + } + } + } + } + } + } + } + + TextPtr=ColumnWidths; + + for (int I=0; I<ColumnCount; I++) + { + FARString strArgName; + + if (!(TextPtr=GetCommaWord(TextPtr,strArgName))) + break; + + ViewColumnWidths[I]=_wtoi(strArgName); + ViewColumnWidthsTypes[I]=COUNT_WIDTH; + + if (strArgName.GetLength()>1) + { + switch (strArgName.At(strArgName.GetLength()-1)) + { + case L'%': + ViewColumnWidthsTypes[I]=PERCENT_WIDTH; + break; + } + } + } +} + + +void ViewSettingsToText(unsigned int *ViewColumnTypes,int *ViewColumnWidths, + int *ViewColumnWidthsTypes,int ColumnCount,FARString &strColumnTitles, + FARString &strColumnWidths) +{ + strColumnTitles.Clear(); + strColumnWidths.Clear(); + + for (int I=0; I<ColumnCount; I++) + { + FARString strType; + int ColumnType=ViewColumnTypes[I] & 0xff; + strType = ColumnSymbol[ColumnType]; + + if (ColumnType==NAME_COLUMN) + { + if (ViewColumnTypes[I] & COLUMN_MARK) + strType += L"M"; + + if (ViewColumnTypes[I] & COLUMN_NAMEONLY) + strType += L"O"; + + if (ViewColumnTypes[I] & COLUMN_RIGHTALIGN) + strType += L"R"; + } + + if (ColumnType==SIZE_COLUMN || ColumnType==PHYSICAL_COLUMN) + { + if (ViewColumnTypes[I] & COLUMN_COMMAS) + strType += L"C"; + + if (ViewColumnTypes[I] & COLUMN_ECONOMIC) + strType += L"E"; + + if (ViewColumnTypes[I] & COLUMN_FLOATSIZE) + strType += L"F"; + + if (ViewColumnTypes[I] & COLUMN_THOUSAND) + strType += L"T"; + } + + if (ColumnType==WDATE_COLUMN || ColumnType==ADATE_COLUMN || ColumnType==CDATE_COLUMN || ColumnType==CHDATE_COLUMN) + { + if (ViewColumnTypes[I] & COLUMN_BRIEF) + strType += L"B"; + + if (ViewColumnTypes[I] & COLUMN_MONTH) + strType += L"M"; + } + + if (ColumnType==OWNER_COLUMN) + { + if (ViewColumnTypes[I] & COLUMN_FULLOWNER) + strType += L"L"; + } + + strColumnTitles += strType; + wchar_t *lpwszWidth = strType.GetBuffer(20); + _itow(ViewColumnWidths[I],lpwszWidth,10); + strType.ReleaseBuffer(); + strColumnWidths += strType; + + switch (ViewColumnWidthsTypes[I]) + { + case PERCENT_WIDTH: + strColumnWidths += L"%"; + break; + } + + if (I<ColumnCount-1) + { + strColumnTitles += L","; + strColumnWidths += L","; + } + } +} + +const FARString FormatStr_Attribute( DWORD FileAttributes, DWORD UnixMode, int Width) +{ + FormatString strResult; + wchar_t OutStr[16] = { }; + if (UnixMode!=0) { + if (FileAttributes&FILE_ATTRIBUTE_BROKEN) + OutStr[0] = L'B'; + else if (FileAttributes&FILE_ATTRIBUTE_DEVICE) + OutStr[0] = L'V'; + else if (FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) + OutStr[0] = L'L'; + else if (FileAttributes&FILE_ATTRIBUTE_DIRECTORY) + OutStr[0] = L'D'; + else + OutStr[0] = L'F'; + + OutStr[1] = UnixMode&S_IRUSR? L'r' : L'-'; + OutStr[2] = UnixMode&S_IWUSR? L'w' : L'-'; + OutStr[3] = UnixMode&S_IXUSR? L'x' : L'-'; + OutStr[4] = UnixMode&S_IRGRP? L'r' : L'-'; + OutStr[5] = UnixMode&S_IWGRP? L'w' : L'-'; + OutStr[6] = UnixMode&S_IXGRP? L'x' : L'-'; + OutStr[7] = UnixMode&S_IROTH? L'r' : L'-'; + OutStr[8] = UnixMode&S_IWOTH? L'w' : L'-'; + OutStr[9] = UnixMode&S_IXOTH? L'x' : L'-'; + } else { + OutStr[0] = FileAttributes&FILE_ATTRIBUTE_EXECUTABLE ? L'X' : L' '; + OutStr[1] = FileAttributes&FILE_ATTRIBUTE_READONLY ? L'R' : L' '; + OutStr[2] = FileAttributes&FILE_ATTRIBUTE_SYSTEM ? L'S' : L' '; + OutStr[3] = FileAttributes&FILE_ATTRIBUTE_HIDDEN ? L'H' : L' '; + OutStr[4] = FileAttributes&FILE_ATTRIBUTE_ARCHIVE ? L'A' : L' '; + OutStr[5] = FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT ? L'L' : FileAttributes&FILE_ATTRIBUTE_SPARSE_FILE ? L'$' : L' '; + OutStr[6] = FileAttributes&FILE_ATTRIBUTE_COMPRESSED ? L'C' : FileAttributes&FILE_ATTRIBUTE_ENCRYPTED ? L'E' : L' '; + OutStr[7] = FileAttributes&FILE_ATTRIBUTE_TEMPORARY ? L'T' : L' '; + OutStr[8] = FileAttributes&FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ? L'I' : L' '; + OutStr[9] = FileAttributes&FILE_ATTRIBUTE_OFFLINE ? L'O' : L' '; + OutStr[10] = FileAttributes&FILE_ATTRIBUTE_VIRTUAL ? L'V' : L' '; + } + + if (Width > 0) + strResult<<fmt::Width(Width)<<fmt::Precision(Width); + + strResult<<OutStr; + + return std::move(strResult.strValue()); +} + +const FARString FormatStr_DateTime(const FILETIME *FileTime,int ColumnType,DWORD Flags,int Width) +{ + FormatString strResult; + + if (Width < 0) + { + if (ColumnType == DATE_COLUMN) + Width=0; + else + return std::move(strResult.strValue()); + } + + int ColumnWidth=Width; + int Brief=Flags & COLUMN_BRIEF; + int TextMonth=Flags & COLUMN_MONTH; + int FullYear=FALSE; + + switch(ColumnType) + { + case DATE_COLUMN: + case TIME_COLUMN: + { + Brief=FALSE; + TextMonth=FALSE; + if (ColumnType == DATE_COLUMN) + FullYear=ColumnWidth>9; + break; + } + case WDATE_COLUMN: + case CDATE_COLUMN: + case ADATE_COLUMN: + case CHDATE_COLUMN: + { + if (!Brief) + { + int CmpWidth=ColumnWidth-TextMonth; + + if (CmpWidth==15 || CmpWidth==16 || CmpWidth==18 || CmpWidth==19 || CmpWidth>21) + FullYear=TRUE; + } + ColumnWidth-=9; + break; + } + } + + FARString strDateStr,strTimeStr; + + ConvertDate(*FileTime,strDateStr,strTimeStr,ColumnWidth,Brief,TextMonth,FullYear); + + strResult<<fmt::Width(Width)<<fmt::Precision(Width); + switch(ColumnType) + { + case DATE_COLUMN: + strResult<<strDateStr; + break; + case TIME_COLUMN: + strResult<<strTimeStr; + break; + default: + strResult<<strDateStr<<L" "<<strTimeStr; + break; + } + + return std::move(strResult.strValue()); +} + +const FARString FormatStr_Size(int64_t FileSize, int64_t PhysicalSize, const FARString &strName,DWORD FileAttributes,uint8_t ShowFolderSize,int ColumnType,DWORD Flags,int Width) +{ + FormatString strResult; + + bool Physical=(ColumnType==PHYSICAL_COLUMN); + + if (ShowFolderSize==2) + { + Width--; + strResult<<L"~"; + } + + if (!Physical && (FileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT)) && !ShowFolderSize) + { + const wchar_t *PtrName=MSG(MListFolder); + + if (TestParentFolderName(strName)) + { + PtrName=MSG(MListUp); + } + else if (FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) + { + PtrName=MSG(MListSymLink); + } + + strResult<<fmt::Width(Width)<<fmt::Precision(Width); + if (StrLength(PtrName) <= Width-2) { + // precombine into tmp string to avoid miseffect of fmt::Width etc (#1137) + strResult<<FARString(L"<").Append(PtrName).Append(L">"); + } else { + strResult<<PtrName; + } + + } + else + { + FARString strOutStr; + strResult<<FileSizeToStr(strOutStr,Physical?PhysicalSize:FileSize,Width,Flags).CPtr(); + } + + return std::move(strResult.strValue()); +} diff --git a/far2l/src/mix/panelmix.hpp b/far2l/src/mix/panelmix.hpp new file mode 100644 index 00000000..4ff22f72 --- /dev/null +++ b/far2l/src/mix/panelmix.hpp @@ -0,0 +1,50 @@ +#pragma once + +/* +panelmix.hpp + +Misc functions for processing of path names +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class Panel; + +void ShellUpdatePanels(Panel *SrcPanel,BOOL NeedSetUpADir=FALSE); +int CheckUpdateAnotherPanel(Panel *SrcPanel,const wchar_t *SelName); + +int _MakePath1(DWORD Key,FARString &strPathName, const wchar_t *Param2); + +const FARString FormatStr_Attribute(DWORD FileAttributes, DWORD UnixMode, int Width=-1); +const FARString FormatStr_DateTime(const FILETIME *FileTime,int ColumnType,DWORD Flags,int Width); +const FARString FormatStr_Size(int64_t FileSize, int64_t PhysicalSize, const FARString &strName, DWORD FileAttributes,uint8_t ShowFolderSize,int ColumnType,DWORD Flags,int Width); +void TextToViewSettings(const wchar_t *ColumnTitles,const wchar_t *ColumnWidths, + unsigned int *ViewColumnTypes,int *ViewColumnWidths,int *ViewColumnWidthsTypes,int &ColumnCount); +void ViewSettingsToText(unsigned int *ViewColumnTypes, + int *ViewColumnWidths,int *ViewColumnWidthsTypes,int ColumnCount,FARString &strColumnTitles, + FARString &strColumnWidths); diff --git a/far2l/src/mix/pathmix.cpp b/far2l/src/mix/pathmix.cpp new file mode 100644 index 00000000..16970796 --- /dev/null +++ b/far2l/src/mix/pathmix.cpp @@ -0,0 +1,530 @@ +/* +pathmix.cpp + +Misc functions for processing of path names +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "pathmix.hpp" +#include "strmix.hpp" +#include "panel.hpp" +#include <time.h> +#include <sys/time.h> + +NTPath::NTPath(LPCWSTR Src) +{ + if (Src&&*Src) + { + Str=Src; + if (!HasPathPrefix(Src) && *Src!='/') + { + ConvertNameToFull(Str,Str); + } + } +} + +bool IsAbsolutePath(const wchar_t *Path) +{ + return Path && (HasPathPrefix(Path) || IsLocalPath(Path) || IsNetworkPath(Path)); +} + +bool IsNetworkPath(const wchar_t *Path) +{ + return false; +} + +bool IsNetworkServerPath(const wchar_t *Path) +{ + return false; +} + +bool IsLocalPath(const wchar_t *Path) +{ + return (Path && Path[0] == L'/'); // && Path[1] != L'/' +} + +bool IsLocalRootPath(const wchar_t *Path) +{ + return (Path && Path[0] == L'/' && !Path[1]); +} + +bool HasPathPrefix(const wchar_t *Path) +{ + return false; +} + +bool IsLocalPrefixPath(const wchar_t *Path) +{ + return HasPathPrefix(Path); +} + +bool IsLocalPrefixRootPath(const wchar_t *Path) +{ + return IsLocalPrefixPath(Path) && !Path[7]; +} + +bool IsLocalVolumePath(const wchar_t *Path) +{ + return HasPathPrefix(Path);//todo && !_wcsnicmp(&Path[4],L"Volume{",7) && Path[47] == L'}'; +} + +bool IsLocalVolumeRootPath(const wchar_t *Path) +{ + return IsLocalVolumePath(Path) && (!Path[48] || (IsSlash(Path[48]) && !Path[49])); +} + +bool PathCanHoldRegularFile(const wchar_t *Path) +{ + if (!IsNetworkPath(Path)) + return true; + + /* \\ */ + unsigned offset = 0; + + const wchar_t *p = FirstSlash(Path + offset); + + /* server || server\ */ + if (!p || !*(p+1)) + return false; + + return true; +} + +bool IsPluginPrefixPath(const wchar_t *Path) //Max: +{ + if (Path[0] == GOOD_SLASH) + return false; + + const wchar_t* pC = wcschr(Path, L':'); + + if (!pC) + return false; + + const wchar_t* pS = FirstSlash(Path); + + if (pS && pS < pC) + return false; + + return true; +} + +bool TestParentFolderName(const wchar_t *Name) +{ + return Name[0] == L'.' && Name[1] == L'.' && (!Name[2] || (IsSlash(Name[2]) && !Name[3])); +} + +bool TestCurrentFolderName(const wchar_t *Name) +{ + return Name[0] == L'.' && (!Name[1] || (IsSlash(Name[1]) && !Name[2])); +} + +bool TestCurrentDirectory(const wchar_t *TestDir) +{ + FARString strCurDir; + + if (apiGetCurrentDirectory(strCurDir) && !StrCmp(strCurDir,TestDir)) + return true; + + return false; +} + +const wchar_t* WINAPI PointToName(const wchar_t *lpwszPath) +{ + return PointToName(lpwszPath,nullptr); +} + +const wchar_t* PointToName(FARString& strPath) +{ + const wchar_t *lpwszPath=strPath.CPtr(); + const wchar_t *lpwszEndPtr=lpwszPath+strPath.GetLength(); + return PointToName(lpwszPath,lpwszEndPtr); +} + +const wchar_t* PointToName(const wchar_t *lpwszPath,const wchar_t *lpwszEndPtr) +{ + if (!lpwszPath) + return nullptr; + + if (lpwszEndPtr) + { + for (const wchar_t *lpwszScanPtr = lpwszEndPtr; lpwszScanPtr != lpwszPath;) + { + --lpwszScanPtr; + if (IsSlash(*lpwszScanPtr)) + return lpwszScanPtr + 1; + } + } + else + { + const wchar_t *lpwszLastSlashPtr = nullptr; + for (const wchar_t *lpwszScanPtr = lpwszPath; *lpwszScanPtr; ++lpwszScanPtr) + { + if (IsSlash(*lpwszScanPtr)) + lpwszLastSlashPtr = lpwszScanPtr; + } + if (lpwszLastSlashPtr) + return lpwszLastSlashPtr + 1; + } + + return lpwszPath; +} + +// Аналог PointToName, только для строк типа +// "name\" (оканчивается на слеш) возвращает указатель на name, а не на пустую +// строку +const wchar_t* WINAPI PointToFolderNameIfFolder(const wchar_t *Path) +{ + if (!Path) + return nullptr; + + const wchar_t *NamePtr=Path, *prevNamePtr=Path; + + while (*Path) + { + if (IsSlash(*Path)) + { + prevNamePtr=NamePtr; + NamePtr=Path+1; + } + + ++Path; + } + + return ((*NamePtr)?NamePtr:prevNamePtr); +} + +const wchar_t* PointToExt(const wchar_t *lpwszPath) +{ + if (!lpwszPath) + return nullptr; + + const wchar_t *lpwszEndPtr = lpwszPath; + + while (*lpwszEndPtr) lpwszEndPtr++; + + return PointToExt(lpwszPath,lpwszEndPtr); +} + +const wchar_t* PointToExt(FARString& strPath) +{ + return PointToExt(strPath.CPtr(), strPath.CEnd()); +} + +const wchar_t* PointToExt(const wchar_t *lpwszPath,const wchar_t *lpwszEndPtr) +{ + if (!lpwszPath || !lpwszEndPtr) + return nullptr; + + const wchar_t *lpwszExtPtr = lpwszEndPtr; + + while (lpwszExtPtr != lpwszPath) + { + if (*lpwszExtPtr==L'.') + { + return lpwszExtPtr; + } + + if (IsSlash(*lpwszExtPtr)) + return lpwszEndPtr; + + lpwszExtPtr--; + } + + return lpwszEndPtr; +} + +void AddEndSlash(FARString &strPath) +{ + if (strPath.IsEmpty() || strPath[strPath.GetLength() - 1] != GOOD_SLASH) + strPath+= GOOD_SLASH; +} + +void AddEndSlash(std::wstring &strPath) +{ + if (strPath.empty() || strPath.back() != GOOD_SLASH) + strPath+= GOOD_SLASH; +} + +BOOL WINAPI AddEndSlash(wchar_t *Path) +{ + const size_t len = wcslen(Path); + if (len && Path[len - 1] == GOOD_SLASH) + return FALSE; + + Path[len] = GOOD_SLASH; + Path[len + 1] = 0; + return TRUE; +} + +bool DeleteEndSlash(wchar_t *Path, bool AllEndSlash) +{ + bool Ret = false; + size_t len = StrLength(Path); + + while (len && IsSlash(Path[--len])) + { + Ret = true; + Path[len] = L'\0'; + + if (!AllEndSlash) + break; + } + + return Ret; +} + +BOOL DeleteEndSlash(std::wstring &strPath, bool AllEndSlash) +{ + BOOL out = FALSE; + while (!strPath.empty() && IsSlash(strPath.back())) { + out = TRUE; + strPath.pop_back(); + if (!AllEndSlash) + break; + } + return out; +} + +BOOL DeleteEndSlash(FARString &strPath, bool AllEndSlash) +{ + size_t LenToSlash = strPath.GetLength(); + + while (LenToSlash != 0 && IsSlash(strPath.At(LenToSlash - 1))) + { + --LenToSlash; + if (!AllEndSlash) break; + } + + if (LenToSlash == strPath.GetLength()) + return FALSE; + + strPath.Truncate(LenToSlash); + return TRUE; +} + +bool CutToSlash(FARString &strStr, bool bInclude) +{ + size_t pos; + + if (FindLastSlash(pos,strStr)) + { + if (bInclude) + strStr.Truncate(pos); + else + strStr.Truncate(pos+1); + + return true; + } + + return false; +} + +bool CutToSlash(std::wstring &strStr, bool bInclude) +{ + size_t pos = strStr.rfind(GOOD_SLASH); + if (pos == std::string::npos) + return false; + + strStr.resize(bInclude ? pos + 1 : pos); + return true; +} + +FARString &CutToFolderNameIfFolder(FARString &strPath) +{ + wchar_t *lpwszPath = strPath.GetBuffer(); + wchar_t *lpwszNamePtr=lpwszPath, *lpwszprevNamePtr=lpwszPath; + + while (*lpwszPath) + { + if (IsSlash(*lpwszPath)) + { + lpwszprevNamePtr=lpwszNamePtr; + lpwszNamePtr=lpwszPath+1; + } + + ++lpwszPath; + } + + if (*lpwszNamePtr) + *lpwszNamePtr=0; + else + *lpwszprevNamePtr=0; + + strPath.ReleaseBuffer(); + return strPath; +} + +const wchar_t *FirstSlash(const wchar_t *String) +{ + do + { + if (IsSlash(*String)) + return String; + } + while (*String++); + + return nullptr; +} + +const wchar_t *LastSlash(const wchar_t *String) +{ + const wchar_t *Start = String; + + while (*String++) + ; + + while (--String!=Start && !IsSlash(*String)) + ; + + return IsSlash(*String)?String:nullptr; +} + +bool FindSlash(size_t &Pos, const FARString &Str, size_t StartPos) +{ + for (size_t p = StartPos; p < Str.GetLength(); p++) + { + if (IsSlash(Str[p])) + { + Pos = p; + return true; + } + } + + return false; +} + +bool FindLastSlash(size_t &Pos, const FARString &Str) +{ + for (size_t p = Str.GetLength(); p > 0; p--) + { + if (IsSlash(Str[p - 1])) + { + Pos = p - 1; + return true; + } + } + + return false; +} + +FARString ExtractFileName(const FARString &Path) +{ + size_t p; + + if (FindLastSlash(p, Path)) + p++; + else + p = 0; + + return FARString(Path.CPtr() + p, Path.GetLength() - p); +} + +FARString ExtractFilePath(const FARString &Path) +{ + size_t p; + + if (!FindLastSlash(p, Path)) + p = 0; + + return FARString(Path.CPtr(), p); +} + +bool IsRootPath(const FARString &Path) +{ + return Path == L"/"; +} + +static std::string LookupExecutableInEnvPath(const char *file) +{ + std::string out; + + FARString str_path; + apiGetEnvironmentVariable("PATH", str_path); + const std::string &mb_path = str_path.GetMB(); + + for (const char *s = mb_path.c_str(); *s; ){ + const char *p = strchr(s, ':'); + + if (p != NULL) { + out.assign(s, p - s); + } else { + out.assign(s); + } + + if (out.empty() || out[out.size() - 1] != '/') { + out+= '/'; + } + out+= file; + struct stat st{}; + if (stat(out.c_str(), &st) == 0) { + break; + } + out.clear(); + if (p == NULL) break; + s = p + 1; + } + + return out; +} + +FARString LookupExecutable(const char *file) +{ + FARString out; + if (file[0] == GOOD_SLASH) { + out = file; + + } else if (file[0] == '.' && file[1] == GOOD_SLASH) { + apiGetCurrentDirectory(out); + out+= file + 1; + + } else { + out = LookupExecutableInEnvPath(file); + if (out.IsEmpty()) { + apiGetCurrentDirectory(out); + out+= GOOD_SLASH; + out+= file; + } + } + return out; +} + +bool PathHasParentPrefix(const FARString &Path) +{ + return (Path.Begins(GOOD_SLASH) || Path.Begins(L"./") || Path == L"."); +} + +void EnsurePathHasParentPrefix(FARString &Path) +{ + if (!PathHasParentPrefix(Path)) { + Path.Insert(0, L"./"); + } +} diff --git a/far2l/src/mix/pathmix.hpp b/far2l/src/mix/pathmix.hpp new file mode 100644 index 00000000..9f576499 --- /dev/null +++ b/far2l/src/mix/pathmix.hpp @@ -0,0 +1,121 @@ +#pragma once + +/* +pathmix.hpp + +Misc functions for processing of path names +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "FARString.hpp" + +const size_t cVolumeGuidLen = 48; + +class NTPath +{ + FARString Str; + public: + NTPath(LPCWSTR Src); + + operator LPCWSTR() const + { + return Str; + } + + const FARString Get() const + { + return Str; + } +}; + +inline int IsSlash(wchar_t x) { return x==GOOD_SLASH; } +inline int IsSlashA(char x) { return x==GOOD_SLASH; } + +bool IsNetworkPath(const wchar_t *Path); +bool IsNetworkServerPath(const wchar_t *Path); +bool IsLocalPath(const wchar_t *Path); +bool IsLocalRootPath(const wchar_t *Path); +bool IsLocalPrefixPath(const wchar_t *Path); +bool IsLocalPrefixRootPath(const wchar_t *Path); +bool IsLocalVolumePath(const wchar_t *Path); +bool IsLocalVolumeRootPath(const wchar_t *Path); +bool IsAbsolutePath(const wchar_t *Path); +bool IsRootPath(const FARString &Path); +bool HasPathPrefix(const wchar_t *Path); +bool PathCanHoldRegularFile(const wchar_t *Path); +bool IsPluginPrefixPath(const wchar_t *Path); + +bool CutToSlash(FARString &strStr, bool bInclude = false); +bool CutToSlash(std::wstring &strStr, bool bInclude = false); + +FARString &CutToFolderNameIfFolder(FARString &strPath); + +const wchar_t* WINAPI PointToName(const wchar_t *lpwszPath); +const wchar_t* PointToName(FARString &strPath); +const wchar_t* PointToName(const wchar_t *lpwszPath,const wchar_t *lpwszEndPtr); +const wchar_t* WINAPI PointToFolderNameIfFolder(const wchar_t *lpwszPath); +const wchar_t* PointToExt(const wchar_t *lpwszPath); +const wchar_t* PointToExt(FARString& strPath); +const wchar_t* PointToExt(const wchar_t *lpwszPath,const wchar_t *lpwszEndPtr); + +void AddEndSlash(FARString &strPath); +void AddEndSlash(std::wstring &strPath); +BOOL WINAPI AddEndSlash(wchar_t *Path); + +bool DeleteEndSlash(wchar_t* Path, bool AllEndSlash = false); +BOOL DeleteEndSlash(std::wstring &strPath,bool AllEndSlash = false); +BOOL DeleteEndSlash(FARString &strPath,bool AllEndSlash = false); + +const wchar_t *FirstSlash(const wchar_t *String); +const wchar_t *LastSlash(const wchar_t *String); +bool FindSlash(size_t &Pos, const FARString &Str, size_t StartPos = 0); +bool FindLastSlash(size_t &Pos, const FARString &Str); + +bool TestParentFolderName(const wchar_t *Name); +bool TestCurrentFolderName(const wchar_t *Name); +bool TestCurrentDirectory(const wchar_t *TestDir); + +FARString ExtractFileName(const FARString &Path); +FARString ExtractFilePath(const FARString &Path); + +template < bool (*PTranslateFN)(std::wstring &s) > + static bool TranslateFarString(FARString &str) +{ + std::wstring tmp(str.CPtr(), str.GetLength()); + if ( !PTranslateFN(tmp)) + return false; + + str.Copy(tmp.c_str(), tmp.size()); + return true; +} + +FARString LookupExecutable(const char *file); + +bool PathHasParentPrefix(const FARString &Path); +void EnsurePathHasParentPrefix(FARString &Path); diff --git a/far2l/src/mix/processname.cpp b/far2l/src/mix/processname.cpp new file mode 100644 index 00000000..64c28f9f --- /dev/null +++ b/far2l/src/mix/processname.cpp @@ -0,0 +1,307 @@ +/* +processname.cpp + +Обработать имя файла: сравнить с маской, масками, сгенерировать по маске +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "processname.hpp" +#include "strmix.hpp" +#include "pathmix.hpp" + +// обработать имя файла: сравнить с маской, масками, сгенерировать по маске +int WINAPI ProcessName(const wchar_t *param1, wchar_t *param2, DWORD size, DWORD flags) +{ + bool skippath = (flags&PN_SKIPPATH)!=0; + + flags &= ~PN_SKIPPATH; + + if (flags == PN_CMPNAME) + return CmpName(param1, param2, skippath); + + if (flags == PN_CMPNAMELIST) + { + int Found=FALSE; + FARString strFileMask; + const wchar_t *MaskPtr; + MaskPtr=param1; + + while ((MaskPtr=GetCommaWord(MaskPtr,strFileMask))) + { + if (CmpName(strFileMask,param2,skippath)) + { + Found=TRUE; + break; + } + } + + return Found; + } + + if (flags&PN_GENERATENAME) + { + FARString strResult = param2; + int nResult = ConvertWildcards(param1, strResult, (flags&0xFFFF)|(skippath?PN_SKIPPATH:0)); + far_wcsncpy(param2, strResult, size); + return nResult; + } + + return FALSE; +} + +/* $ 09.10.2000 IS + Генерация нового имени по маске + (взял из ShellCopy::ShellCopyConvertWildcards) +*/ +// На основе имени файла (Src) и маски (Dest) генерируем новое имя +// SelectedFolderNameLength - длина каталога. Например, есть +// каталог dir1, а в нем файл file1. Нужно сгенерировать имя по маске для dir1. +// Параметры могут быть следующими: Src="dir1", SelectedFolderNameLength=0 +// или Src="dir1/file1", а SelectedFolderNameLength=4 (длина "dir1") +int ConvertWildcards(const wchar_t *SrcName, FARString &strDest, int SelectedFolderNameLength) +{ + FARString strPartAfterFolderName; + FARString strSrc = SrcName; + wchar_t *DestName = strDest.GetBuffer(strDest.GetLength()+strSrc.GetLength()+1); //??? + wchar_t *DestNamePtr = (wchar_t*)PointToName(DestName); + FARString strWildName = DestNamePtr; + + if (!wcschr(strWildName, L'*') && !wcschr(strWildName, L'?')) + { + //strDest.ReleaseBuffer (); не надо так как строка не поменялась + return FALSE; + } + + if (SelectedFolderNameLength) + { + strPartAfterFolderName = ((const wchar_t *)strSrc+SelectedFolderNameLength); + strSrc.Truncate(SelectedFolderNameLength); + } + + const wchar_t *Src = strSrc; + + const wchar_t *SrcNamePtr = PointToName(Src); + + int BeforeNameLength = DestNamePtr==DestName ? (int)(SrcNamePtr-Src) : 0; + + wchar_t *PartBeforeName = (wchar_t*)malloc((BeforeNameLength+1)*sizeof(wchar_t)); + + far_wcsncpy(PartBeforeName, Src, BeforeNameLength+1); + + const wchar_t *SrcNameDot = wcsrchr(SrcNamePtr, L'.'); + + const wchar_t *CurWildPtr = strWildName; + + while (*CurWildPtr) + { + switch (*CurWildPtr) + { + case L'?': + CurWildPtr++; + + if (*SrcNamePtr) + *(DestNamePtr++)=*(SrcNamePtr++); + + break; + case L'*': + CurWildPtr++; + + while (*SrcNamePtr) + { + if (*CurWildPtr==L'.' && SrcNameDot && !wcschr(CurWildPtr+1,L'.')) + { + if (SrcNamePtr==SrcNameDot) + break; + } + else if (*SrcNamePtr==*CurWildPtr) + { + break; + } + + *(DestNamePtr++)=*(SrcNamePtr++); + } + + break; + case L'.': + CurWildPtr++; + *(DestNamePtr++)=L'.'; + + if (FindAnyOfChars(CurWildPtr, "*?")) + while (*SrcNamePtr) + if (*(SrcNamePtr++)==L'.') + break; + + break; + default: + *(DestNamePtr++)=*(CurWildPtr++); + + if (*SrcNamePtr && *SrcNamePtr!=L'.') + SrcNamePtr++; + + break; + } + } + + *DestNamePtr=0; + + if (DestNamePtr!=DestName && *(DestNamePtr-1)==L'.') + *(DestNamePtr-1)=0; + + strDest.ReleaseBuffer(); + + if (*PartBeforeName) + strDest = PartBeforeName+strDest; + + if (SelectedFolderNameLength) + strDest += strPartAfterFolderName; //BUGBUG???, was src in 1.7x + + free(PartBeforeName); + return TRUE; +} + +// IS: это реальное тело функции сравнения с маской, но использовать +// IS: "снаружи" нужно не эту функцию, а CmpName (ее тело расположено +// IS: после CmpName_Body) +static bool CmpName_Body(const wchar_t *pattern, const wchar_t *str) +{ + for (;;++str) + { + /* $ 01.05.2001 DJ + используем инлайновые версии + */ + wchar_t rangec; + int match; + wchar_t stringc = *str; + wchar_t patternc = *pattern++; + + switch (patternc) + { + case 0: + return !stringc; + + case L'?': + + if (!stringc) + return false; + + break; + + case L'*': + + if (!*pattern) + return true; + + if (!FindAnyOfChars(pattern, "*?[")) + { + const size_t pattern_len = wcslen(pattern); + const size_t str_len = wcslen(str); + return (str_len >= pattern_len + && wmemcmp(pattern, str + str_len - pattern_len, pattern_len) == 0); + } + + do + { + if (CmpName_Body(pattern, str)) + return true; + } + while (*str++); + + return false; + + case L'[': + + if (!wcschr(pattern, L']')) + { + if (patternc != stringc) + return false; + + break; + } + + if (*pattern && *(pattern+1) == L']') + { + if (*pattern!=*str) + return false; + + pattern+=2; + break; + } + + match = 0; + stringc = Upper(stringc); + while ((rangec = *pattern++) != 0) + { + if (rangec == L']') + { + if (match) + break; + else + return false; + } + + if (match) + continue; + + if (rangec == L'-' && *(pattern - 2) != L'[' && *pattern != L']') + { + match = (stringc <= Upper(*pattern) && Upper(*(pattern - 2)) <= stringc); + pattern++; + } + else + match = (stringc == Upper(rangec)); + } + + if (!rangec) + return false; + + break; + + default: + if (Upper(patternc) != stringc && Lower(patternc) != stringc) + return false; + + break; + } + } +} + +// IS: функция для внешнего мира, использовать ее +bool CmpName(const wchar_t *pattern, const wchar_t *str, bool skippath) +{ + if (!pattern || !str) + return false; + + if (skippath) + str = PointToName(str); + + return CmpName_Body(pattern, str); +} diff --git a/far2l/src/mix/processname.hpp b/far2l/src/mix/processname.hpp new file mode 100644 index 00000000..c5f1c02a --- /dev/null +++ b/far2l/src/mix/processname.hpp @@ -0,0 +1,41 @@ +#pragma once + +/* +processname.hpp + +Обработать имя файла: сравнить с маской, масками, сгенерировать по маске +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "FARString.hpp" + +// обработать имя файла: сравнить с маской, масками, сгенерировать по маске +int WINAPI ProcessName(const wchar_t *param1, wchar_t *param2, DWORD size, DWORD flags); +int ConvertWildcards(const wchar_t *SrcName,FARString &strDest, int SelectedFolderNameLength); +bool CmpName(const wchar_t *pattern, const wchar_t *str, bool skippath = true); diff --git a/far2l/src/mix/strmix.cpp b/far2l/src/mix/strmix.cpp new file mode 100644 index 00000000..b2af2383 --- /dev/null +++ b/far2l/src/mix/strmix.cpp @@ -0,0 +1,1379 @@ +/* +strmix.cpp + +Куча разных вспомогательных функций по работе со строками +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "strmix.hpp" +#include "lang.hpp" +#include "language.hpp" +#include "config.hpp" +#include "pathmix.hpp" + +FARString &FormatNumber(const wchar_t *Src, FARString &strDest, int NumDigits) +{ + FARString result;//can't use strDest cuz Src may point to its internal buffer + + const wchar_t *dot = wcschr(Src, L'.'); + const wchar_t *part = dot ? dot : Src + wcslen(Src); + if (part == Src) { + result = L"0"; + } else { + size_t i = 0; + for (;;) { + --part; + result.Insert(0, *part); + if (part == Src) break; + ++i; + if ((i % 3)==0) + result.Insert(0, L' '); + } + } + if (dot) { + result.Append(dot, std::min(wcslen(dot), (size_t)NumDigits + 1) ); + } + strDest = std::move(result); + return strDest; + + /* + static bool first = true; + static NUMBERFMT fmt; + static wchar_t DecimalSep[4]; + static wchar_t ThousandSep[4]; + + if (first) + { + GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,ThousandSep,ARRAYSIZE(ThousandSep)); + GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,DecimalSep,ARRAYSIZE(DecimalSep)); + DecimalSep[1]=0; //В винде сепараторы цифр могут быть больше одного символа + ThousandSep[1]=0; //но для нас это будет не очень хорошо + + if (LOWORD(Opt.FormatNumberSeparators)) + *DecimalSep=LOWORD(Opt.FormatNumberSeparators); + + if (HIWORD(Opt.FormatNumberSeparators)) + *ThousandSep=HIWORD(Opt.FormatNumberSeparators); + + fmt.LeadingZero = 1; + fmt.Grouping = 3; + fmt.lpDecimalSep = DecimalSep; + fmt.lpThousandSep = ThousandSep; + fmt.NegativeOrder = 1; + first = false; + } + + fmt.NumDigits = NumDigits; + FARString strSrc=Src; + int Size=GetNumberFormat(LOCALE_USER_DEFAULT,0,strSrc,&fmt,nullptr,0); + wchar_t* lpwszDest=strDest.GetBuffer(Size); + GetNumberFormat(LOCALE_USER_DEFAULT,0,strSrc,&fmt,lpwszDest,Size); + strDest.ReleaseBuffer(); + return strDest;*/ +} + +FARString &InsertCommas(uint64_t li,FARString &strDest) +{ + strDest.Format(L"%llu", li); + return FormatNumber(strDest,strDest); +} + +static wchar_t * WINAPI InsertCustomQuote(wchar_t *Str,wchar_t QuoteChar) +{ + size_t l = StrLength(Str); + + if (*Str != QuoteChar) + { + wmemmove(Str+1,Str,++l); + *Str=QuoteChar; + } + + if (l==1 || Str[l-1] != QuoteChar) + { + Str[l++] = QuoteChar; + Str[l] = 0; + } + + return Str; +} + +static FARString& InsertCustomQuote(FARString &strStr,wchar_t QuoteChar) +{ + size_t l = strStr.GetLength(); + + if (strStr.At(0) != QuoteChar) + { + strStr.Insert(0,QuoteChar); + l++; + } + + if (l==1 || strStr.At(l-1) != QuoteChar) + { + strStr += QuoteChar; + } + + return strStr; +} + +wchar_t * WINAPI InsertQuote(wchar_t *Str) +{ + return InsertCustomQuote(Str,L'\"'); +} + +wchar_t * WINAPI InsertRegexpQuote(wchar_t *Str) +{ + if (Str && *Str != L'/') + return InsertCustomQuote(Str,L'/'); + else //выражение вида /regexp/i не дополняем слэшем + return Str; +} + +FARString& InsertQuote(FARString &strStr) +{ + return InsertCustomQuote(strStr,L'\"'); +} + +FARString& InsertRegexpQuote(FARString &strStr) +{ + if (strStr.IsEmpty() || strStr[0] != L'/') + return InsertCustomQuote(strStr,L'/'); + else //выражение вида /regexp/i не дополняем слэшем + return strStr; +} + + +static FARString escapeSpace(const wchar_t* str) { + if (*str == L'\0') + return "''"; + FARString result; + for (const wchar_t *cur = str; *cur; ++cur) { + if (wcschr(Opt.strQuotedSymbols, *cur) != nullptr) + result.Append('\\'); + result.Append(*cur); + } + return result; +} + + +FARString &EscapeSpace(FARString &strStr) +{ + if (strStr.IsEmpty() || strStr.ContainsAnyOf(Opt.strQuotedSymbols.CPtr())) { + strStr.Copy(escapeSpace(strStr.CPtr())); + } + + return strStr; +} + + +wchar_t* WINAPI QuoteSpaceOnly(wchar_t *Str) +{ + if (wcschr(Str,L' ')) + InsertQuote(Str); + + return Str; +} + + +FARString& WINAPI QuoteSpaceOnly(FARString &strStr) +{ + if (strStr.Contains(L' ')) + InsertQuote(strStr); + + return(strStr); +} + + +FARString& WINAPI TruncStrFromEnd(FARString &strStr, int MaxLength) +{ + wchar_t *lpwszBuffer = strStr.GetBuffer(); + TruncStrFromEnd(lpwszBuffer, MaxLength); + strStr.ReleaseBuffer(); + return strStr; +} + +wchar_t* WINAPI TruncStrFromEnd(wchar_t *Str,int MaxLength) +{ + assert(MaxLength >= 0); + + MaxLength=Max(0, MaxLength); + + if (Str) + { + int Length = StrLength(Str); + + if (Length > MaxLength) + { + if (MaxLength>3) + wmemcpy(Str+MaxLength-3, L"...", 3); + + Str[MaxLength]=0; + } + } + + return Str; +} + + +wchar_t* WINAPI TruncStr(wchar_t *Str,int MaxLength) +{ + assert(MaxLength >= 0); + + MaxLength=Max(0, MaxLength); + + if (Str) + { + int Length=StrLength(Str); + + if (MaxLength<0) + MaxLength=0; + + if (Length > MaxLength) + { + if (MaxLength>3) + { + wchar_t *MovePos = Str+Length-MaxLength+3; + wmemmove(Str+3, MovePos, StrLength(MovePos)+1); + wmemcpy(Str,L"...",3); + } + + Str[MaxLength]=0; + } + } + + return Str; +} + + +FARString& WINAPI TruncStr(FARString &strStr, int MaxLength) +{ + wchar_t *lpwszBuffer = strStr.GetBuffer(); + TruncStr(lpwszBuffer, MaxLength); + strStr.ReleaseBuffer(); + return strStr; +} + +wchar_t* TruncStrFromCenter(wchar_t *Str, int MaxLength) +{ + assert(MaxLength >= 0); + + MaxLength=Max(0, MaxLength); + + if (Str) + { + int Length = StrLength(Str); + + if (MaxLength < 0) + MaxLength=0; + + if (Length > MaxLength) + { + const int DotsLen = 3; + + if (MaxLength > DotsLen) + { + int Len1 = (MaxLength - DotsLen) / 2; + int Len2 = MaxLength - DotsLen - Len1; + wmemcpy(Str + Len1, L"...", DotsLen); + wmemmove(Str + Len1 + DotsLen, Str + Length - Len2, Len2); + } + + Str[MaxLength] = 0; + } + } + + return Str; +} + +FARString& TruncStrFromCenter(FARString &strStr, int MaxLength) +{ + wchar_t *lpwszBuffer = strStr.GetBuffer(); + TruncStrFromCenter(lpwszBuffer, MaxLength); + strStr.ReleaseBuffer(); + return strStr; +} + +wchar_t* WINAPI TruncPathStr(wchar_t *Str, int MaxLength) +{ + assert(MaxLength >= 0); + + MaxLength=Max(0, MaxLength); + + if (Str) + { + int nLength = (int)wcslen(Str); + + if ((MaxLength > 0) && (nLength > MaxLength) && (nLength >= 2)) + { + wchar_t *lpStart = nullptr; + +/* if (*Str && (Str[1] == L':') && IsSlash(Str[2])) + lpStart = Str+3; + else*/ + { + if ((Str[0] == GOOD_SLASH) && (Str[1] == GOOD_SLASH)) + { + if ((lpStart = const_cast<wchar_t*>(FirstSlash(Str+2))) ) + { + wchar_t *lpStart2=lpStart; + + if ((lpStart-Str < nLength) && ((lpStart=const_cast<wchar_t*>(FirstSlash(lpStart2+1))))) + lpStart++; + } + } + } + + if (!lpStart || (lpStart-Str > MaxLength-5)) + return TruncStr(Str, MaxLength); + + wchar_t *lpInPos = lpStart+3+(nLength-MaxLength); + wmemmove(lpStart+3, lpInPos, (wcslen(lpInPos)+1)); + wmemcpy(lpStart, L"...", 3); + } + } + + return Str; +} + + +FARString& WINAPI TruncPathStr(FARString &strStr, int MaxLength) +{ + wchar_t *lpwszStr = strStr.GetBuffer(); + TruncPathStr(lpwszStr, MaxLength); + strStr.ReleaseBuffer(); + return strStr; +} + + +wchar_t* WINAPI RemoveLeadingSpaces(wchar_t *Str) +{ + wchar_t *ChPtr = Str; + + if (!ChPtr) + return nullptr; + + for (; IsSpace(*ChPtr) || IsEol(*ChPtr); ChPtr++) + ; + + if (ChPtr!=Str) + wmemmove(Str,ChPtr,StrLength(ChPtr)+1); + + return Str; +} + + +FARString& WINAPI RemoveLeadingSpaces(FARString &strStr) +{ + const wchar_t *ChPtr = strStr; + + for (; IsSpace(*ChPtr) || IsEol(*ChPtr); ChPtr++) + ; + + strStr.Remove(0,ChPtr-strStr.CPtr()); + return strStr; +} + + +// удалить конечные пробелы +wchar_t* WINAPI RemoveTrailingSpaces(wchar_t *Str) +{ + if (!Str) + return nullptr; + + if (!*Str) + return Str; + + for (wchar_t *ChPtr=Str+StrLength(Str)-1; ChPtr >= Str; ChPtr--) + { + if (IsSpace(*ChPtr) || IsEol(*ChPtr)) + *ChPtr=0; + else + break; + } + + return Str; +} + + +FARString& WINAPI RemoveTrailingSpaces(FARString &strStr) +{ + if (strStr.IsEmpty()) + return strStr; + + const wchar_t *Str = strStr; + const wchar_t *ChPtr = Str + strStr.GetLength() - 1; + + for (; ChPtr >= Str && (IsSpace(*ChPtr) || IsEol(*ChPtr)); ChPtr--) + ; + + strStr.Truncate(ChPtr < Str ? 0 : ChPtr-Str+1); + return strStr; +} + + +wchar_t* WINAPI RemoveExternalSpaces(wchar_t *Str) +{ + return RemoveTrailingSpaces(RemoveLeadingSpaces(Str)); +} + +FARString& WINAPI RemoveExternalSpaces(FARString &strStr) +{ + return RemoveTrailingSpaces(RemoveLeadingSpaces(strStr)); +} + + +/* $ 02.02.2001 IS + Заменяет пробелами непечатные символы в строке. В настоящий момент + обрабатываются только cr и lf. +*/ +FARString& WINAPI RemoveUnprintableCharacters(FARString &strStr) +{ + wchar_t *p = strStr.GetBuffer(); + + while (*p) + { + if (IsEol(*p)) + *p=L' '; + + p++; + } + + strStr.ReleaseBuffer(strStr.GetLength()); + return RemoveExternalSpaces(strStr); +} + + +// Удалить символ Target из строки Str (везде!) +FARString &RemoveChar(FARString &strStr,wchar_t Target,BOOL Dup) +{ + wchar_t *Ptr = strStr.GetBuffer(); + wchar_t *Str = Ptr, Chr; + + while ((Chr=*Str++) ) + { + if (Chr == Target) + { + if (Dup && *Str == Target) + { + *Ptr++ = Chr; + ++Str; + } + + continue; + } + + *Ptr++ = Chr; + } + + *Ptr = L'\0'; + strStr.ReleaseBuffer(); + return strStr; +} + +FARString& CenterStr(const wchar_t *Src, FARString &strDest, int Length) +{ + FARString strTempStr = Src; //если Src == strDest, то надо копировать Src! + int SrcLength = strTempStr.GetLength(); + + if (SrcLength >= Length) + { + /* Здесь не надо отнимать 1 от длины, т.к. strlen не учитывает \0 + и мы получали обрезанные строки */ + strDest = std::move(strTempStr); + strDest.Truncate(Length); + } + else + { + int Space = (Length - SrcLength) / 2; + FormatString FString; + FString << fmt::Width(Space) << L"" << strTempStr << fmt::Width(Length - Space - SrcLength) << L""; + strDest = std::move(FString.strValue()); + } + + return strDest; +} + +FARString FixedSizeStr(FARString str, size_t Length, bool RAlign) +{ + if (str.GetLength() > Length) + { + if (str.GetLength() > 2) + { + size_t RmLen = (str.GetLength() - Length) + 1; + size_t RmPos = (str.GetLength() - RmLen) / 2; + str.Replace(RmPos, RmLen, L"…", 1); + } + else + str = L"…"; + } + else while (str.GetLength() < Length) + { + if (RAlign) + str.Insert(0, L" ", 1); + else + str.Append(L" ", 1); + } + return str; +} + + +const wchar_t *GetCommaWord(const wchar_t *Src, FARString &strWord,wchar_t Separator) +{ + if (!*Src) + return nullptr; + + const wchar_t *StartPtr = Src; + size_t WordLen; + bool SkipBrackets=false; + + for (WordLen=0; *Src; Src++,WordLen++) + { + if (*Src==L'[' && wcschr(Src+1,L']')) + SkipBrackets=true; + + if (*Src==L']') + SkipBrackets=false; + + if (*Src==Separator && !SkipBrackets) + { + Src++; + + while (IsSpace(*Src)) + Src++; + + strWord.Copy(StartPtr,WordLen); + return Src; + } + } + + strWord.Copy(StartPtr,WordLen); + return Src; +} + + +BOOL IsCaseMixed(const FARString &strSrc) +{ + const wchar_t *lpwszSrc = strSrc; + + while (*lpwszSrc && !IsAlpha(*lpwszSrc)) + lpwszSrc++; + + int Case = IsLower(*lpwszSrc); + + while (*(lpwszSrc++)) + if (IsAlpha(*lpwszSrc) && (IsLower(*lpwszSrc) != Case)) + return TRUE; + + return FALSE; +} + +BOOL IsCaseLower(const FARString &strSrc) +{ + const wchar_t *lpwszSrc = strSrc; + + while (*lpwszSrc) + { + if (!IsLower(*lpwszSrc)) + return FALSE; + + lpwszSrc++; + } + + return TRUE; +} + + + +void WINAPI Unquote(wchar_t *Str) +{ + if (!Str) + return; + + wchar_t *Dst=Str; + + while (*Str) + { + if (*Str!=L'\"') + *Dst++=*Str; + + Str++; + } + + *Dst=0; +} + + +void WINAPI Unquote(FARString &strStr) +{ + wchar_t *Dst = strStr.GetBuffer(); + const wchar_t *Str = Dst; + const wchar_t *StartPtr = Dst; + + while (*Str) + { + if (*Str!=L'\"') + *Dst++=*Str; + + Str++; + } + + strStr.ReleaseBuffer(Dst-StartPtr); +} + + +void UnquoteExternal(FARString &strStr) +{ + size_t len = strStr.GetLength(); + + if (len > 1 && strStr.At(0) == L'\"' && strStr.At(len-1) == L'\"') + { + strStr.Truncate(len-1); + strStr.LShift(1); + } +} + + +/* FileSizeToStr() + Форматирование размера файла в удобочитаемый вид. +*/ +#define MAX_UNITSTR_SIZE 16 + +#define UNIT_COUNT 7 // byte, kilobyte, megabyte, gigabyte, terabyte, petabyte, exabyte. + +static wchar_t UnitStr[UNIT_COUNT][2][MAX_UNITSTR_SIZE]={}; + +void PrepareUnitStr() +{ + for (int i=0; i<UNIT_COUNT; i++) + { + far_wcsncpy(UnitStr[i][0],MSG(MListBytes+i),MAX_UNITSTR_SIZE); + wcscpy(UnitStr[i][1],UnitStr[i][0]); + WINPORT(CharLower)(UnitStr[i][0]); + WINPORT(CharUpper)(UnitStr[i][1]); + } +} + +FARString & WINAPI FileSizeToStr(FARString &strDestStr, uint64_t Size, int Width, int ViewFlags) +{ + FARString strStr; + uint64_t Divider; + int IndexDiv, IndexB; + + // подготовительные мероприятия + if (!UnitStr[0][0][0]) + { + PrepareUnitStr(); + } + + int Commas=(ViewFlags & COLUMN_COMMAS); + int FloatSize=(ViewFlags & COLUMN_FLOATSIZE); + int Economic=(ViewFlags & COLUMN_ECONOMIC); + int UseMinSizeIndex=(ViewFlags & COLUMN_MINSIZEINDEX); + int MinSizeIndex=(ViewFlags & COLUMN_MINSIZEINDEX_MASK)+1; + int ShowBytesIndex=(ViewFlags & COLUMN_SHOWBYTESINDEX); + + if (ViewFlags & COLUMN_THOUSAND) + { + Divider=1000; + IndexDiv=0; + } + else + { + Divider=1024; + IndexDiv=1; + } + + uint64_t Sz = Size, Divider2 = Divider/2, Divider64 = Divider, OldSize; + + if (FloatSize) + { + uint64_t Divider64F = 1, Divider64F_mul = 1000, Divider64F2 = 1, Divider64F2_mul = Divider; + + //выравнивание идёт по 1000 но само деление происходит на Divider + //например 999 bytes покажутся как 999 а вот 1000 bytes уже покажутся как 0.97 K + for (IndexB=0; IndexB<UNIT_COUNT-1; IndexB++) + { + if (Sz < Divider64F*Divider64F_mul) + break; + + Divider64F = Divider64F*Divider64F_mul; + Divider64F2 = Divider64F2*Divider64F2_mul; + } + + if (!IndexB) + strStr.Format(L"%d", (DWORD)Sz); + else + { + Sz = (OldSize=Sz) / Divider64F2; + OldSize = (OldSize % Divider64F2) / (Divider64F2 / Divider64F2_mul); + DWORD Decimal = (DWORD)(0.5+(double)(DWORD)OldSize/(double)Divider*100.0); + + if (Decimal >= 100) + { + Decimal -= 100; + Sz++; + } + + strStr.Format(L"%d.%02d", (DWORD)Sz,Decimal); + FormatNumber(strStr,strStr,2); + } + + if (IndexB>0 || ShowBytesIndex) + { + Width-=(Economic?1:2); + + if (Width<0) + Width=0; + + if (Economic) + strDestStr.Format(L"%*.*ls%1.1ls",Width,Width,strStr.CPtr(),UnitStr[IndexB][IndexDiv]); + else + strDestStr.Format(L"%*.*ls %1.1ls",Width,Width,strStr.CPtr(),UnitStr[IndexB][IndexDiv]); + } + else + strDestStr.Format(L"%*.*ls",Width,Width,strStr.CPtr()); + + return strDestStr; + } + + if (Commas) + InsertCommas(Sz,strStr); + else + strStr.Format(L"%llu", Sz); + + if ((!UseMinSizeIndex && strStr.GetLength()<=static_cast<size_t>(Width)) || Width<5) + { + if (ShowBytesIndex) + { + Width-=(Economic?1:2); + + if (Width<0) + Width=0; + + if (Economic) + strDestStr.Format(L"%*.*ls%1.1ls",Width,Width,strStr.CPtr(),UnitStr[0][IndexDiv]); + else + strDestStr.Format(L"%*.*ls %1.1ls",Width,Width,strStr.CPtr(),UnitStr[0][IndexDiv]); + } + else + strDestStr.Format(L"%*.*ls",Width,Width,strStr.CPtr()); + } + else + { + Width-=(Economic?1:2); + IndexB=0; + + do + { + //Sz=(Sz+Divider2)/Divider64; + Sz = (OldSize=Sz) / Divider64; + + if ((OldSize % Divider64) > Divider2) + ++Sz; + + IndexB++; + + if (Commas) + InsertCommas(Sz,strStr); + else + strStr.Format(L"%llu",Sz); + } + while ((UseMinSizeIndex && IndexB<MinSizeIndex) || strStr.GetLength() > static_cast<size_t>(Width)); + + if (Economic) + strDestStr.Format(L"%*.*ls%1.1ls",Width,Width,strStr.CPtr(),UnitStr[IndexB][IndexDiv]); + else + strDestStr.Format(L"%*.*ls %1.1ls",Width,Width,strStr.CPtr(),UnitStr[IndexB][IndexDiv]); + } + + return strDestStr; +} + + + +// вставить с позиции Pos в Str строку InsStr (размером InsSize байт) +// если InsSize = 0, то... вставлять все строку InsStr +// возвращает указатель на Str + +wchar_t *InsertString(wchar_t *Str,int Pos,const wchar_t *InsStr,int InsSize) +{ + int InsLen=StrLength(InsStr); + + if (InsSize && InsSize < InsLen) + InsLen=InsSize; + + wmemmove(Str+Pos+InsLen, Str+Pos, (StrLength(Str+Pos)+1)); + wmemcpy(Str+Pos, InsStr, InsLen); + return Str; +} + + +// Заменить в строке Str Count вхождений подстроки FindStr на подстроку ReplStr +// Если Count < 0 - заменять "до полной победы" +// Return - количество замен +int ReplaceStrings(FARString &strStr,const wchar_t *FindStr,const wchar_t *ReplStr,int Count,BOOL IgnoreCase) +{ + if (!Count) + return 0; + const int LenFindStr = StrLength(FindStr); + if ( !LenFindStr) + return 0; + const int LenReplStr = StrLength(ReplStr); + + int ReplacedCount = 0; + FARString strResult; + size_t StartPos = 0, FoundPos; + while ( (IgnoreCase ? strStr.PosI(FoundPos, FindStr, StartPos) : strStr.Pos(FoundPos, FindStr, StartPos)) && (Count == -1 || ReplacedCount < Count)) + { + strResult.Append(strStr.CPtr() + StartPos, FoundPos - StartPos); + strResult.Append(ReplStr, LenReplStr); + StartPos = FoundPos + LenFindStr; + ++ReplacedCount; + } + if (ReplacedCount) + { + if (StartPos < strStr.GetLength()) + strResult.Append(strStr.CPtr() + StartPos, strStr.GetLength() - StartPos); + strStr = std::move(strResult); + } + return ReplacedCount; +} + +/* +From PHP 4.x.x +Форматирует исходный текст по заданной ширине, используя +разделительную строку. Возвращает строку SrcText свёрнутую +в колонке, заданной параметром Width. Строка рубится при +помощи строки Break. + +Разбивает на строки с выравниваением влево. + +Если параметр Flahs & FFTM_BREAKLONGWORD, то строка всегда +сворачивается по заданной ширине. Так если у вас есть слово, +которое больше заданной ширины, то оно будет разрезано на части. + +Example 1. +FarFormatText("Пример строки, которая будет разбита на несколько строк по ширине в 20 символов.", 20 ,Dest, "\n", 0); +Этот пример вернет: +--- +Пример строки, +которая будет +разбита на +несколько строк по +ширине в 20 +символов. +--- + +Example 2. +FarFormatText( "Эта строка содержит оооооооооооооччччччччеееень длиное слово", 9, Dest, nullptr, FFTM_BREAKLONGWORD); +Этот пример вернет: + +--- +Эта +строка +содержит +ооооооооо +ооооччччч +чччеееень +длиное +слово +--- + +*/ + +enum FFTMODE +{ + FFTM_BREAKLONGWORD = 0x00000001, +}; + +FARString& WINAPI FarFormatText(const wchar_t *SrcText, // источник + int Width, // заданная ширина + FARString &strDestText, // приемник + const wchar_t* Break, // брик, если = nullptr, то принимается '\n' + DWORD Flags) // один из FFTM_* +{ + const wchar_t *breakchar; + breakchar = Break?Break:L"\n"; + + if (!SrcText || !*SrcText) + { + strDestText.Clear(); + return strDestText; + } + + FARString strSrc = SrcText; //copy FARString in case of SrcText == strDestText + + if (!strSrc.ContainsAnyOf(breakchar) && strSrc.GetLength() <= static_cast<size_t>(Width)) + { + strDestText = strSrc; + return strDestText; + } + + long i=0, l=0, pgr=0, last=0; + wchar_t *newtext; + const wchar_t *text= strSrc; + long linelength = Width; + int breakcharlen = StrLength(breakchar); + int docut = Flags&FFTM_BREAKLONGWORD?1:0; + /* Special case for a single-character break as it needs no + additional storage space */ + + if (breakcharlen == 1 && !docut) + { + newtext = wcsdup(text); + + if (!newtext) + { + strDestText.Clear(); + return strDestText; + } + + while (newtext[i] != L'\0') + { + /* prescan line to see if it is greater than linelength */ + l = 0; + + while (newtext[i+l] != breakchar[0]) + { + if (newtext[i+l] == L'\0') + { + l--; + break; + } + + l++; + } + + if (l >= linelength) + { + pgr = l; + l = linelength; + + /* needs breaking; work backwards to find previous word */ + while (l >= 0) + { + if (newtext[i+l] == L' ') + { + newtext[i+l] = breakchar[0]; + break; + } + + l--; + } + + if (l == -1) + { + /* couldn't break is backwards, try looking forwards */ + l = linelength; + + while (l <= pgr) + { + if (newtext[i+l] == L' ') + { + newtext[i+l] = breakchar[0]; + break; + } + + l++; + } + } + } + + i += l+1; + } + } + else + { + /* Multiple character line break */ + newtext = (wchar_t*)malloc((strSrc.GetLength() * (breakcharlen+1)+1)*sizeof(wchar_t)); + + if (!newtext) + { + strDestText.Clear(); + return strDestText; + } + + newtext[0] = L'\0'; + i = 0; + + while (text[i] != L'\0') + { + /* prescan line to see if it is greater than linelength */ + l = 0; + + while (text[i+l] != L'\0') + { + if (text[i+l] == breakchar[0]) + { + if (breakcharlen == 1 || !StrCmpN(text+i+l, breakchar, breakcharlen)) + break; + } + + l++; + } + + if (l >= linelength) + { + pgr = l; + l = linelength; + + /* needs breaking; work backwards to find previous word */ + while (l >= 0) + { + if (text[i+l] == L' ') + { + wcsncat(newtext, text+last, i+l-last); + wcscat(newtext, breakchar); + last = i + l + 1; + break; + } + + l--; + } + + if (l == -1) + { + /* couldn't break it backwards, try looking forwards */ + l = linelength - 1; + + while (l <= pgr) + { + if (!docut) + { + if (text[i+l] == L' ') + { + wcsncat(newtext, text+last, i+l-last); + wcscat(newtext, breakchar); + last = i + l + 1; + break; + } + } + + if (docut == 1) + { + if (text[i+l] == L' ' || l > i-last) + { + wcsncat(newtext, text+last, i+l-last+1); + wcscat(newtext, breakchar); + last = i + l + 1; + break; + } + } + + l++; + } + } + + i += l+1; + } + else + { + i += (l ? l : 1); + } + } + + if (i+l > last) + { + wcscat(newtext, text+last); + } + } + + strDestText = newtext; + free(newtext); + return strDestText; +} + +/* + Ptr=CalcWordFromString(Str,I,&Start,&End); + far_strncpy(Dest,Ptr,End-Start+1); + Dest[End-Start+1]=0; + +// Параметры: +// WordDiv - набор разделителей слова в кодировке OEM + возвращает указатель на начало слова +*/ + +const wchar_t * CalcWordFromString(const wchar_t *Str,int CurPos,int *Start,int *End, const wchar_t *WordDiv0) +{ + int StartWPos, EndWPos; + + const int StrSize = StrLength(Str); + + if (CurPos < 0 || CurPos >= StrSize) + return nullptr; + + if (IsWordDivSTNR(WordDiv0, Str[CurPos])) + { + // вычисляем дистанцию - куда копать, где ближе слово - слева или справа + int L, R; + + // копаем влево + for (L = CurPos - 1; (L >= 0 && IsWordDivSTNR(WordDiv0, Str[L])); --L); + + // копаем вправо + for (R = CurPos + 1; (R < StrSize && IsWordDivSTNR(WordDiv0, Str[R])); ++R); + + if ( L < 0) { + if (R >= StrSize) + return nullptr; + + StartWPos = EndWPos = R; + + } else if (R >= StrSize) { + if ( L < 0) + return nullptr; + + StartWPos = EndWPos = L; + + } else if (CurPos - L > R - CurPos) { // ?? >= + EndWPos = StartWPos = R; + + } else { + StartWPos = EndWPos = L; + } + + } else {// здесь все оби, т.е. стоим на буковке + EndWPos = StartWPos = CurPos; + } + + for ( ; (StartWPos > 0 && !IsWordDivSTNR(WordDiv0, Str[StartWPos - 1])); --StartWPos); + + for ( ; (EndWPos + 1 < StrSize && !IsWordDivSTNR(WordDiv0, Str[EndWPos + 1])); ++EndWPos); + + + *Start = StartWPos; + *End = EndWPos; + + return Str + StartWPos; +} + + +bool CheckFileSizeStringFormat(const wchar_t *FileSizeStr) +{ +//проверяет если формат строки такой: [0-9]+[BbKkMmGgTtPpEe]? + const wchar_t *p = FileSizeStr; + + while (iswdigit(*p)) + p++; + + if (p == FileSizeStr) + return false; + + if (*p) + { + if (*(p+1)) + return false; + + if (!StrStrI(L"BKMGTPE", p)) + return false; + } + + return true; +} + +uint64_t ConvertFileSizeString(const wchar_t *FileSizeStr) +{ + if (!CheckFileSizeStringFormat(FileSizeStr)) + return 0; + + uint64_t n = _wtoi64(FileSizeStr); + wchar_t c = Upper(FileSizeStr[StrLength(FileSizeStr)-1]); + + // http://en.wikipedia.org/wiki/SI_prefix + switch (c) + { + case L'K': // kilo 10x3 + n <<= 10; + break; + case L'M': // mega 10x6 + n <<= 20; + break; + case L'G': // giga 10x9 + n <<= 30; + break; + case L'T': // tera 10x12 + n <<= 40; + break; + case L'P': // peta 10x15 + n <<= 50; + break; + case L'E': // exa 10x18 + n <<= 60; + break; + // Z - zetta 10x21 + // Y - yotta 10x24 + } + + return n; +} + +/* $ 21.09.2003 KM + Трансформация строки по заданному типу. +*/ +void Transform(FARString &strBuffer,const wchar_t *ConvStr,wchar_t TransformType) +{ + FARString strTemp; + + switch (TransformType) + { + case L'X': // Convert common FARString to hexadecimal FARString representation + { + FARString strHex; + + while (*ConvStr) + { + strHex.Format(L"%02X",*ConvStr); + strTemp += strHex; + ConvStr++; + } + + break; + } + case L'S': // Convert hexadecimal FARString representation to common string + { + const wchar_t *ptrConvStr=ConvStr; + + while (*ptrConvStr) + { + if (*ptrConvStr != L' ') + { + WCHAR Hex[]={ptrConvStr[0],ptrConvStr[1],0}; + size_t l=strTemp.GetLength(); + wchar_t *Temp=strTemp.GetBuffer(l+2); + Temp[l]=(wchar_t)wcstoul(Hex,nullptr,16) & ((wchar_t)-1); + strTemp.ReleaseBuffer(l+1); + ptrConvStr++; + } + + ptrConvStr++; + } + + break; + } + default: + break; + } + + strBuffer=strTemp; +} + +wchar_t GetDecimalSeparator() +{ + //wchar_t Separator[4]; + //GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,Separator,ARRAYSIZE(Separator)); + //return *Separator; + return L'.'; +} + +FARString ReplaceBrackets(const FARString& SearchStr,const FARString& ReplaceStr,RegExpMatch* Match,int Count) +{ + FARString result; + size_t pos=0,length=ReplaceStr.GetLength(); + + while (pos<length) + { + bool common=true; + + if (ReplaceStr[pos]=='$') + { + ++pos; + + if (pos>length) break; + + wchar_t symbol=Upper(ReplaceStr[pos]); + int index=-1; + + if (symbol>='0'&&symbol<='9') + { + index=symbol-'0'; + } + else if (symbol>='A'&&symbol<='Z') + { + index=symbol-'A'+10; + } + + if (index>=0) + { + if (index<Count) + { + FARString bracket(SearchStr.CPtr()+Match[index].start,Match[index].end-Match[index].start); + result+=bracket; + } + + common=false; + } + } + + if (common) + { + result+=ReplaceStr[pos]; + } + + ++pos; + } + + return result; +} + + +std::string EscapeUnprintable(const std::string &str) +{ + std::string out; + out.reserve(str.size()); + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) { + unsigned char c = (unsigned char)*i; + if (c <= 0x20 || c > 0x7e || c=='\\') { + char buf[32]; + sprintf(buf, "\\x%02x", c); + out+= buf; + } else + out+= c; + } + return out; +} + +std::string UnescapeUnprintable(const std::string &str) +{ + std::string out; + out.reserve(str.size()); + for (size_t i = 0; i < str.size(); ++i) { + char c = str[i]; + if (c == '\\' && (i + 3) < str.size() && str[i+1] == 'x') { + char tmp[4] = {str[i+2], str[i+3]}; + unsigned int x = 0; + sscanf(tmp, "%x", &x); + c = (unsigned char)x; + i+= 3; + } + out+= c; + } + return out; +} diff --git a/far2l/src/mix/strmix.hpp b/far2l/src/mix/strmix.hpp new file mode 100644 index 00000000..e9ec6e5a --- /dev/null +++ b/far2l/src/mix/strmix.hpp @@ -0,0 +1,124 @@ +#pragma once + +/* +strmix.hpp + +Куча разных вспомогательных функций по работе со строками +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include <string> +#include <WinCompat.h> +#include "FARString.hpp" + +enum +{ + COLUMN_MARK = 0x80000000, + COLUMN_NAMEONLY = 0x40000000, + COLUMN_RIGHTALIGN = 0x20000000, + COLUMN_FORMATTED = 0x10000000, + COLUMN_COMMAS = 0x08000000, + COLUMN_THOUSAND = 0x04000000, + COLUMN_BRIEF = 0x02000000, + COLUMN_MONTH = 0x01000000, + COLUMN_FLOATSIZE = 0x00800000, + COLUMN_ECONOMIC = 0x00400000, + COLUMN_MINSIZEINDEX = 0x00200000, + COLUMN_SHOWBYTESINDEX = 0x00100000, + COLUMN_FULLOWNER = 0x00080000, + + //MINSIZEINDEX может быть только 0, 1, 2 или 3 (K,M,G,T) + COLUMN_MINSIZEINDEX_MASK = 0x00000003, +}; + +std::string EscapeUnprintable(const std::string &str); +std::string UnescapeUnprintable(const std::string &str); + +FARString &EscapeSpace(FARString &strStr); +wchar_t* WINAPI InsertQuote(wchar_t *Str); +FARString& InsertQuote(FARString& strStr); +void WINAPI Unquote(FARString &strStr); +void WINAPI Unquote(wchar_t *Str); +wchar_t * WINAPI InsertRegexpQuote(wchar_t *Str); +FARString& InsertRegexpQuote(FARString& strStr); +void UnquoteExternal(FARString &strStr); +wchar_t* WINAPI RemoveLeadingSpaces(wchar_t *Str); +FARString& WINAPI RemoveLeadingSpaces(FARString &strStr); +wchar_t *WINAPI RemoveTrailingSpaces(wchar_t *Str); +FARString& WINAPI RemoveTrailingSpaces(FARString &strStr); +wchar_t* WINAPI RemoveExternalSpaces(wchar_t *Str); +FARString & WINAPI RemoveExternalSpaces(FARString &strStr); +FARString & WINAPI RemoveUnprintableCharacters(FARString &strStr); +wchar_t* WINAPI QuoteSpaceOnly(wchar_t *Str); +FARString& WINAPI QuoteSpaceOnly(FARString &strStr); + +FARString &RemoveChar(FARString &strStr,wchar_t Target,BOOL Dup=TRUE); +wchar_t *InsertString(wchar_t *Str,int Pos,const wchar_t *InsStr,int InsSize=0); +int ReplaceStrings(FARString &strStr,const wchar_t *FindStr,const wchar_t *ReplStr,int Count=-1,BOOL IgnoreCase=FALSE); + +const wchar_t *GetCommaWord(const wchar_t *Src,FARString &strWord,wchar_t Separator=L','); + +FARString& WINAPI FarFormatText(const wchar_t *SrcText, int Width, FARString &strDestText, const wchar_t* Break, DWORD Flags); + +void PrepareUnitStr(); +FARString& WINAPI FileSizeToStr(FARString &strDestStr, uint64_t Size, int Width=-1, int ViewFlags=COLUMN_COMMAS); +bool CheckFileSizeStringFormat(const wchar_t *FileSizeStr); +uint64_t ConvertFileSizeString(const wchar_t *FileSizeStr); +FARString &FormatNumber(const wchar_t *Src, FARString &strDest, int NumDigits=0); +FARString &InsertCommas(uint64_t li, FARString &strDest); + +inline bool IsWordDiv(const wchar_t *WordDiv, wchar_t Chr) + { return wcschr(WordDiv, Chr) != nullptr; } + +inline bool IsWordDivSTNR(const wchar_t *WordDiv, wchar_t Chr) + { return wcschr(WordDiv, Chr) != nullptr || wcschr(L" \t\n\r", Chr) != nullptr; } + +// WordDiv - набор разделителей слова в кодировке OEM +// возвращает указатель на начало слова +const wchar_t * CalcWordFromString(const wchar_t *Str,int CurPos,int *Start,int *End,const wchar_t *WordDiv); + +wchar_t* WINAPI TruncStr(wchar_t *Str,int MaxLength); +FARString& WINAPI TruncStr(FARString &strStr,int MaxLength); +wchar_t* WINAPI TruncStrFromEnd(wchar_t *Str,int MaxLength); +FARString& WINAPI TruncStrFromEnd(FARString &strStr, int MaxLength); +wchar_t* TruncStrFromCenter(wchar_t *Str, int MaxLength); +FARString& TruncStrFromCenter(FARString &strStr, int MaxLength); +wchar_t* WINAPI TruncPathStr(wchar_t *Str, int MaxLength); +FARString& WINAPI TruncPathStr(FARString &strStr, int MaxLength); + +BOOL IsCaseMixed(const FARString &strStr); +BOOL IsCaseLower(const FARString &strStr); + +FARString& CenterStr(const wchar_t *Src, FARString &strDest,int Length); +FARString FixedSizeStr(FARString str, size_t Length, bool RAlign); + +void Transform(FARString &strBuffer,const wchar_t *ConvStr,wchar_t TransformType); + +wchar_t GetDecimalSeparator(); + +FARString ReplaceBrackets(const FARString& SearchStr,const FARString& ReplaceStr,RegExpMatch* Match,int Count); diff --git a/far2l/src/mix/udlist.cpp b/far2l/src/mix/udlist.cpp new file mode 100644 index 00000000..183bac0b --- /dev/null +++ b/far2l/src/mix/udlist.cpp @@ -0,0 +1,422 @@ +/* +udlist.cpp + +Список чего-либо, перечисленного через символ-разделитель. Если нужно, чтобы +элемент списка содержал разделитель, то этот элемент следует заключить в +кавычки. Если кроме разделителя ничего больше в строке нет, то считается, что +это не разделитель, а простой символ. +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "udlist.hpp" + +UserDefinedListItem::~UserDefinedListItem() +{ + if (Str) + free(Str); +} + +bool UserDefinedListItem::operator==(const UserDefinedListItem &rhs) const +{ + return (Str && rhs.Str)?(!StrCmpI(Str, rhs.Str)):false; +} + +int UserDefinedListItem::operator<(const UserDefinedListItem &rhs) const +{ + if (!Str) + return 1; + else if (!rhs.Str) + return -1; + else + return StrCmpI(Str, rhs.Str)<0; +} + +const UserDefinedListItem& UserDefinedListItem::operator=(const + UserDefinedListItem &rhs) +{ + if (this!=&rhs) + { + if (Str) + { + free(Str); + Str=nullptr; + } + + if (rhs.Str) + Str=wcsdup(rhs.Str); + + index=rhs.index; + } + + return *this; +} + +const UserDefinedListItem& UserDefinedListItem::operator=(const wchar_t *rhs) +{ + if (Str!=rhs) + { + if (Str) + { + free(Str); + Str=nullptr; + } + + if (rhs) + Str=wcsdup(rhs); + } + + return *this; +} + +wchar_t *UserDefinedListItem::set(const wchar_t *Src, size_t size) +{ + if (Str!=Src) + { + if (Str) + { + free(Str); + Str=nullptr; + } + + Str=static_cast<wchar_t*>(malloc((size+1)*sizeof(wchar_t))); + + if (Str) + { + wmemcpy(Str,Src,size); + Str[size]=0; + } + } + + return Str; +} + +UserDefinedList::UserDefinedList() +{ + SetParameters(0,0,0); +} + +UserDefinedList::UserDefinedList(WORD separator1, WORD separator2, + DWORD Flags) +{ + SetParameters(separator1, separator2, Flags); +} + +void UserDefinedList::SetDefaultSeparators() +{ + Separator1=L';'; + Separator2=L','; +} + +bool UserDefinedList::CheckSeparators() const +{ + return !((IsUnQuotes && (Separator1==L'\"' || Separator2==L'\"')) || + (ProcessBrackets && (Separator1==L'[' || Separator2==L'[' || + Separator1==L']' || Separator2==L']')) + ); +} + +bool UserDefinedList::SetParameters(WORD separator1, WORD separator2, + DWORD Flags) +{ + Free(); + Separator1=separator1; + Separator2=separator2; + ProcessBrackets=(Flags & ULF_PROCESSBRACKETS)?true:false; + AddAsterisk=(Flags & ULF_ADDASTERISK)?true:false; + PackAsterisks=(Flags & ULF_PACKASTERISKS)?true:false; + Unique=(Flags & ULF_UNIQUE)?true:false; + Sort=(Flags & ULF_SORT)?true:false; + IsTrim=(Flags & ULF_NOTTRIM)?false:true; + IsUnQuotes=(Flags & ULF_NOTUNQUOTES)?false:true; + AccountEmptyLine=(Flags & ULF_ACCOUNTEMPTYLINE)?true:false; + + if (!Separator1 && Separator2) + { + Separator1=Separator2; + Separator2=0; + } + + if (!Separator1 && !Separator2) SetDefaultSeparators(); + + return CheckSeparators(); +} + +void UserDefinedList::Free() +{ + Array.Free(); +} + +bool UserDefinedList::Set(const wchar_t *List, bool AddToList) +{ + if (AddToList) + { + if (List && !*List) // пусто, нечего добавлять + return true; + } + else + Free(); + + bool rc=false; + + if (CheckSeparators() && List && *List) + { + int Length, RealLength; + UserDefinedListItem item; + item.index=Array.getSize(); + + if (*List!=Separator1 && *List!=Separator2) + { + Length=StrLength(List); + bool Error=false; + const wchar_t *CurList=List; + + while (!Error && + nullptr!=(CurList=Skip(CurList, Length, RealLength, Error))) + { + if (Length > 0) + { + item.set(CurList, Length); + + if (item.Str) + { + if (PackAsterisks) + { + int i=0; + bool lastAsterisk=false; + + while (i<Length) + { + if (item.Str[i]==L'*') + { + if (!lastAsterisk) + lastAsterisk=true; + else + { + wmemcpy(item.Str+i, item.Str+i+1, StrLength(item.Str+i+1)+1); + --i; + } + } + else + lastAsterisk=false; + + ++i; + } + } + + if (AddAsterisk && !FindAnyOfChars(item.Str, "?*.")) + { + Length=StrLength(item.Str); + /* $ 18.09.2002 DJ + выделялось на 1 байт меньше, чем надо + */ + item.Str=static_cast<wchar_t*>(realloc(item.Str, (Length+2)*sizeof(wchar_t))); + + /* DJ $ */ + if (item.Str) + { + item.Str[Length]=L'*'; + item.Str[Length+1]=0; + } + else + Error=true; + } + + if (!Error && !Array.addItem(item)) + Error=true; + } + else + Error=true; + + CurList+=RealLength; + } + else + { + if (!AccountEmptyLine || (AccountEmptyLine && CurList==List && !item.index)) + Error=true; + } + + ++item.index; + } + + rc=!Error; + } + else + { + const wchar_t *End=List+1; + + if ( IsTrim ) + while (IsSpace(*End)) ++End; // пропустим мусор + + if (!*End) // Если кроме разделителя ничего больше в строке нет, + { // то считается, что это не разделитель, а простой символ + item=L" "; + + if (item.Str) + { + *item.Str=*List; + + if (Array.addItem(item)) + rc=true; + } + } + } + } + + if (rc) + { + if (Unique) + { + Array.Sort(); + Array.Pack(); + } + + if (!Sort) + Array.Sort(reinterpret_cast<TARRAYCMPFUNC>(CmpItems)); + else if (!Unique) // чтобы не сортировать уже отсортированное + Array.Sort(); + + size_t i=0, maxI=Array.getSize(); + + for (; i<maxI; ++i) + Array.getItem(i)->index=i; + } + else + Free(); + + return rc; +} + +int __cdecl UserDefinedList::CmpItems(const UserDefinedListItem **el1, + const UserDefinedListItem **el2) +{ + if (el1==el2) + return 0; + else if ((**el1).index==(**el2).index) + return 0; + else if ((**el1).index<(**el2).index) + return -1; + else + return 1; +} + +const wchar_t *UserDefinedList::Skip(const wchar_t *Str, int &Length, int &RealLength, bool &Error) +{ + Length=RealLength=0; + Error=false; + + if ( IsTrim ) + while (IsSpace(*Str)) ++Str; + + if (*Str==Separator1 || *Str==Separator2) ++Str; + + if ( IsTrim ) + while (IsSpace(*Str)) ++Str; + + if (!*Str) return nullptr; + + const wchar_t *cur=Str; + bool InBrackets=false, InQoutes = (*cur==L'\"'); + + if (!InQoutes) // если мы в кавычках, то обработка будет позже и чуть сложнее + while (*cur) // важно! проверка *cur должна стоять первой + { + if (ProcessBrackets) + { + if (*cur==L']') + InBrackets=false; + + if (*cur==L'[' && nullptr!=wcschr(cur+1, L']')) + InBrackets=true; + } + + if (!InBrackets && (*cur==Separator1 || *cur==Separator2)) + break; + + ++cur; + } + + if (!InQoutes || !*cur) + { + RealLength=Length=(int)(cur-Str); + --cur; + + if ( IsTrim ) + while (IsSpace(*cur)) + { + --Length; + --cur; + } + + return Str; + } + + if ( IsUnQuotes ) + { + // мы в кавычках - захватим все отсюда и до следующих кавычек + ++cur; + const wchar_t *QuoteEnd=wcschr(cur, L'\"'); + + if (!QuoteEnd) + { + Error=true; + return nullptr; + } + + const wchar_t *End=QuoteEnd+1; + + if ( IsTrim ) + while (IsSpace(*End)) ++End; + + if (!*End || *End==Separator1 || *End==Separator2) + { + Length=(int)(QuoteEnd-cur); + RealLength=(int)(End-cur); + return cur; + } + + Error=true; + return nullptr; + } + + return Str; +} + +bool UserDefinedList::IsEmpty() const +{ + return Array.getSize() == 0; +} + +const wchar_t *UserDefinedList::Get(size_t Index) const +{ + const UserDefinedListItem *item=Array.getConstItem(Index); + return item ? item->Str : nullptr; +} diff --git a/far2l/src/mix/udlist.hpp b/far2l/src/mix/udlist.hpp new file mode 100644 index 00000000..9dbfae59 --- /dev/null +++ b/far2l/src/mix/udlist.hpp @@ -0,0 +1,135 @@ +#pragma once + +/* +udlist.hpp + +Список чего-либо, перечисленного через символ-разделитель. Если нужно, чтобы +элемент списка содержал разделитель, то этот элемент следует заключить в +кавычки. Если кроме разделителя ничего больше в строке нет, то считается, что +это не разделитель, а простой символ. +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "array.hpp" +#include "noncopyable.hpp" + +enum UDL_FLAGS +{ + ULF_ADDASTERISK =0x00000001, // добавлять '*' к концу элемента списка, если он не содержит '?', '*' и '.' + ULF_PACKASTERISKS =0x00000002, // вместо "***" в список помещать просто "*" + ULF_PROCESSBRACKETS=0x00000004, // учитывать квадратные скобки при анализе строки инициализации + ULF_UNIQUE =0x00000010, // убирать дублирующиеся элементы + ULF_SORT =0x00000020, // отсортировать (с учетом регистра) + ULF_NOTTRIM =0x00000040, // не удалять пробелы + ULF_NOTUNQUOTES =0x00000080, // не раскавычивать + ULF_ACCOUNTEMPTYLINE=0x00000100, // учитывать пустые "строки" +}; + + +class UserDefinedListItem +{ + public: + size_t index; + wchar_t *Str; + UserDefinedListItem():index(0), Str(nullptr) {} + bool operator==(const UserDefinedListItem &rhs) const; + int operator<(const UserDefinedListItem &rhs) const; + const UserDefinedListItem& operator=(const UserDefinedListItem &rhs); + const UserDefinedListItem& operator=(const wchar_t *rhs); + wchar_t *set(const wchar_t *Src, size_t size); + ~UserDefinedListItem(); +}; + +class UserDefinedList : private NonCopyable +{ + private: + TArray<UserDefinedListItem> Array; + WORD Separator1, Separator2; + bool ProcessBrackets, AddAsterisk, PackAsterisks, Unique, Sort, IsTrim, IsUnQuotes; + bool AccountEmptyLine; + + private: + bool CheckSeparators() const; // проверка разделителей на корректность + void SetDefaultSeparators(); + const wchar_t *Skip(const wchar_t *Str, int &Length, int &RealLength, bool &Error); + static int __cdecl CmpItems(const UserDefinedListItem **el1, + const UserDefinedListItem **el2); + + public: + // по умолчанию разделителем считается ';' и ',', а + // ProcessBrackets=AddAsterisk=PackAsterisks=false + // Unique=Sort=false + UserDefinedList(); + + // Явно указываются разделители. См. описание SetParameters + UserDefinedList(WORD separator1, WORD separator2, DWORD Flags); + ~UserDefinedList() { Free(); } + + public: + // Сменить символы-разделитель и разрешить или запретить обработку + // квадратных скобок. + // Если один из Separator* равен 0x00, то он игнорируется при компиляции + // (т.е. в Set) + // Если оба разделителя равны 0x00, то восстанавливаются разделители по + // умолчанию (';' & ','). + // Если AddAsterisk равно true, то к концу элемента списка будет + // добавляться '*', если этот элемент не содержит '?', '*' и '.' + // Возвращает false, если один из разделителей является кавычкой или + // включена обработка скобок и один из разделителей является квадратной + // скобкой. + bool SetParameters(WORD Separator1, WORD Separator2, DWORD Flags); + + // Инициализирует список. Принимает список, разделенный разделителями. + // Возвращает false при неудаче. + // Фича: если List==nullptr, то происходит освобождение занятой ранее памяти + bool Set(const wchar_t *List, bool AddToList=false); + + // Добавление к уже существующему списку + // Фича: если NewItem==nullptr, то происходит освобождение занятой ранее + // памяти + bool AddItem(const wchar_t *NewItem) + { + return Set(NewItem,true); + } + + // Выдает указатель на очередной элемент списка или nullptr + const wchar_t *Get(size_t Index) const; + + // Освободить память + void Free(); + + // true, если элементов в списке нет + bool IsEmpty() const; + + inline bool IsLastElement(size_t Index) const { return Index + 1 == Array.getSize(); } + + // Вернуть количество элементов в списке + inline size_t GetTotal() const { return Array.getSize(); } +}; diff --git a/far2l/src/mix/wakeful.hpp b/far2l/src/mix/wakeful.hpp new file mode 100644 index 00000000..5dac12c6 --- /dev/null +++ b/far2l/src/mix/wakeful.hpp @@ -0,0 +1,47 @@ +#pragma once + +/* +wakeful.hpp + +Preventing the system from entering sleep +*/ +/* +Copyright (c) 2010 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class wakeful +{ +public: + wakeful() + { + //SetThreadExecutionState(ES_SYSTEM_REQUIRED|ES_CONTINUOUS); + } + + ~wakeful() + { + //SetThreadExecutionState(ES_CONTINUOUS); + } +}; |