Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/elfmz/far2l.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelfmz <fenix1905@tut.by>2021-12-31 12:15:36 +0300
committerelfmz <fenix1905@tut.by>2021-12-31 12:21:48 +0300
commit6b7b5f7fe480107373a6ef3cbd6ee41378c93642 (patch)
tree1af3f15ecc76c47749f74f38302cb33e0a3796e1 /far2l/src/mix
parenta14dc1a81c797928d4f1b7d6a6b46ecc63f98308 (diff)
split /aux to /mix and /base
Diffstat (limited to 'far2l/src/mix')
-rw-r--r--far2l/src/mix/MountInfo.cpp209
-rw-r--r--far2l/src/mix/MountInfo.h26
-rw-r--r--far2l/src/mix/RegExp.cpp3766
-rw-r--r--far2l/src/mix/RegExp.hpp308
-rw-r--r--far2l/src/mix/cddrv.cpp52
-rw-r--r--far2l/src/mix/cddrv.hpp142
-rw-r--r--far2l/src/mix/chgprior.cpp45
-rw-r--r--far2l/src/mix/chgprior.hpp45
-rw-r--r--far2l/src/mix/cvtname.cpp457
-rw-r--r--far2l/src/mix/cvtname.hpp45
-rw-r--r--far2l/src/mix/dirmix.cpp327
-rw-r--r--far2l/src/mix/dirmix.hpp65
-rw-r--r--far2l/src/mix/drivemix.cpp95
-rw-r--r--far2l/src/mix/drivemix.hpp68
-rw-r--r--far2l/src/mix/format.cpp147
-rw-r--r--far2l/src/mix/format.hpp138
-rw-r--r--far2l/src/mix/mix.cpp201
-rw-r--r--far2l/src/mix/mix.hpp59
-rw-r--r--far2l/src/mix/panelmix.cpp611
-rw-r--r--far2l/src/mix/panelmix.hpp50
-rw-r--r--far2l/src/mix/pathmix.cpp530
-rw-r--r--far2l/src/mix/pathmix.hpp121
-rw-r--r--far2l/src/mix/processname.cpp307
-rw-r--r--far2l/src/mix/processname.hpp41
-rw-r--r--far2l/src/mix/strmix.cpp1379
-rw-r--r--far2l/src/mix/strmix.hpp124
-rw-r--r--far2l/src/mix/udlist.cpp422
-rw-r--r--far2l/src/mix/udlist.hpp135
-rw-r--r--far2l/src/mix/wakeful.hpp47
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&QUOTEDNAME_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);
+ }
+};