From 3d04b17524f7821d0ff5dee0632d5e74d01d2dd9 Mon Sep 17 00:00:00 2001 From: skipik Date: Tue, 26 Nov 2024 17:25:22 +0300 Subject: [PATCH 1/8] SQLite v3.47.1 --- far/changelog | 5 ++ far/thirdparty/sqlite/sqlite3.c | 118 +++++++++++++++++++++++--------- far/thirdparty/sqlite/sqlite3.h | 15 +++- far/vbuild.m4 | 2 +- 4 files changed, 102 insertions(+), 38 deletions(-) diff --git a/far/changelog b/far/changelog index 61138eeda8..8e59e5cc5d 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,8 @@ +-------------------------------------------------------------------------------- +skipik 2024-11-26 17:23:11+03:00 - build 6395 + +1. SQLite v3.47.1. + -------------------------------------------------------------------------------- drkns 2024-11-22 19:07:44+00:00 - build 6394 diff --git a/far/thirdparty/sqlite/sqlite3.c b/far/thirdparty/sqlite/sqlite3.c index 2886d04ae5..099c5482f6 100644 --- a/far/thirdparty/sqlite/sqlite3.c +++ b/far/thirdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.47.0. By combining all the individual C code files into this +** version 3.47.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 03a9703e27c44437c39363d0baf82db4ebc9. +** b95d11e958643b969c47a8e5857f3793b9e6. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -462,9 +462,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.47.0" -#define SQLITE_VERSION_NUMBER 3047000 -#define SQLITE_SOURCE_ID "2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e" +#define SQLITE_VERSION "3.47.1" +#define SQLITE_VERSION_NUMBER 3047001 +#define SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -968,6 +968,13 @@ SQLITE_API int sqlite3_exec( ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. +** +** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read +** from the database file in amounts that are not a multiple of the +** page size and that do not begin at a page boundary. Without this +** property, SQLite is careful to only do full-page reads and write +** on aligned pages, with the one exception that it will do a sub-page +** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 @@ -984,6 +991,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 +#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels @@ -1130,6 +1138,7 @@ struct sqlite3_file { **
  • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] **
  • [SQLITE_IOCAP_IMMUTABLE] **
  • [SQLITE_IOCAP_BATCH_ATOMIC] +**
  • [SQLITE_IOCAP_SUBPAGE_READ] ** ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of @@ -32298,6 +32307,7 @@ SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExp pExpr = pExpr->pLeft; } if( pExpr==0 ) return; + if( ExprHasProperty(pExpr, EP_FromDDL) ) return; db->errByteOffset = pExpr->w.iOfst; } @@ -42591,6 +42601,7 @@ static void setDeviceCharacteristics(unixFile *pFd){ if( pFd->ctrlFlags & UNIXFILE_PSOW ){ pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; } + pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ; pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; } @@ -50391,7 +50402,7 @@ static int winSectorSize(sqlite3_file *id){ */ static int winDeviceCharacteristics(sqlite3_file *id){ winFile *p = (winFile*)id; - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ | ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); } @@ -51779,7 +51790,7 @@ static int winOpen( int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE - int eType = flags&0xFFFFFF00; /* Type of file to open */ + int eType = flags&0x0FFF00; /* Type of file to open */ #endif int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); @@ -57999,18 +58010,26 @@ static const unsigned char aJournalMagic[] = { ** Return true if page pgno can be read directly from the database file ** by the b-tree layer. This is the case if: ** -** * the database file is open, -** * there are no dirty pages in the cache, and -** * the desired page is not currently in the wal file. +** (1) the database file is open +** (2) the VFS for the database is able to do unaligned sub-page reads +** (3) there are no dirty pages in the cache, and +** (4) the desired page is not currently in the wal file. */ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ - if( pPager->fd->pMethods==0 ) return 0; - if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; + assert( pPager!=0 ); + assert( pPager->fd!=0 ); + if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */ + assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); + if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) + & SQLITE_IOCAP_SUBPAGE_READ)==0 ){ + return 0; /* Case (2) */ + } + if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */ #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return iRead==0; + return iRead==0; /* Condition (4) */ } #endif return 1; @@ -158939,6 +158958,7 @@ static Expr *removeUnindexableInClauseTerms( pNew->pLeft->x.pList = pLhs; } pSelect->pEList = pRhs; + pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */ if( pLhs && pLhs->nExpr==1 ){ /* Take care here not to generate a TK_VECTOR containing only a ** single value. Since the parser never creates such a vector, some @@ -189798,10 +189818,15 @@ static int fts3PoslistPhraseMerge( if( *p1==POS_COLUMN ){ p1++; p1 += fts3GetVarint32(p1, &iCol1); + /* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN + ** entry, so this is actually end-of-doclist. */ + if( iCol1==0 ) return 0; } if( *p2==POS_COLUMN ){ p2++; p2 += fts3GetVarint32(p2, &iCol2); + /* As above, iCol2==0 indicates corruption. */ + if( iCol2==0 ) return 0; } while( 1 ){ @@ -192972,7 +192997,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ nTmp += p->pRight->pPhrase->doclist.nList; } nTmp += p->pPhrase->doclist.nList; - aTmp = sqlite3_malloc64(nTmp*2); + aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX); if( !aTmp ){ *pRc = SQLITE_NOMEM; res = 0; @@ -194525,10 +194550,11 @@ static int getNextString( Fts3PhraseToken *pToken; p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); - if( !p ) goto no_mem; - zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); - if( !zTemp ) goto no_mem; + if( !zTemp || !p ){ + rc = SQLITE_NOMEM; + goto getnextstring_out; + } assert( nToken==ii ); pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; @@ -194543,9 +194569,6 @@ static int getNextString( nToken = ii+1; } } - - pModule->xClose(pCursor); - pCursor = 0; } if( rc==SQLITE_DONE ){ @@ -194553,7 +194576,10 @@ static int getNextString( char *zBuf = 0; p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); - if( !p ) goto no_mem; + if( !p ){ + rc = SQLITE_NOMEM; + goto getnextstring_out; + } memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); p->eType = FTSQUERY_PHRASE; p->pPhrase = (Fts3Phrase *)&p[1]; @@ -194561,11 +194587,9 @@ static int getNextString( p->pPhrase->nToken = nToken; zBuf = (char *)&p->pPhrase->aToken[nToken]; + assert( nTemp==0 || zTemp ); if( zTemp ){ memcpy(zBuf, zTemp, nTemp); - sqlite3_free(zTemp); - }else{ - assert( nTemp==0 ); } for(jj=0; jjpPhrase->nToken; jj++){ @@ -194575,17 +194599,17 @@ static int getNextString( rc = SQLITE_OK; } - *ppExpr = p; - return rc; -no_mem: - + getnextstring_out: if( pCursor ){ pModule->xClose(pCursor); } sqlite3_free(zTemp); - sqlite3_free(p); - *ppExpr = 0; - return SQLITE_NOMEM; + if( rc!=SQLITE_OK ){ + sqlite3_free(p); + p = 0; + } + *ppExpr = p; + return rc; } /* @@ -232806,7 +232830,27 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ /************** End of sqlite3session.c **************************************/ /************** Begin file fts5.c ********************************************/ - +/* +** This, the "fts5.c" source file, is a composite file that is itself +** assembled from the following files: +** +** fts5.h +** fts5Int.h +** fts5parse.h <--- Generated from fts5parse.y by Lemon +** fts5parse.c <--- Generated from fts5parse.y by Lemon +** fts5_aux.c +** fts5_buffer.c +** fts5_config.c +** fts5_expr.c +** fts5_hash.c +** fts5_index.c +** fts5_main.c +** fts5_storage.c +** fts5_tokenize.c +** fts5_unicode2.c +** fts5_varint.c +** fts5_vocab.c +*/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) @@ -232816,6 +232860,12 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ # undef NDEBUG #endif +#ifdef HAVE_STDINT_H +/* #include */ +#endif +#ifdef HAVE_INTTYPES_H +/* #include */ +#endif /* ** 2014 May 31 ** @@ -254888,7 +254938,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e", -1, SQLITE_TRANSIENT); } /* @@ -260079,7 +260129,7 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ } - +/* Here ends the fts5.c composite file. */ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ /************** End of fts5.c ************************************************/ diff --git a/far/thirdparty/sqlite/sqlite3.h b/far/thirdparty/sqlite/sqlite3.h index eaffd1ec16..dbecc3fe89 100644 --- a/far/thirdparty/sqlite/sqlite3.h +++ b/far/thirdparty/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.47.0" -#define SQLITE_VERSION_NUMBER 3047000 -#define SQLITE_SOURCE_ID "2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e" +#define SQLITE_VERSION "3.47.1" +#define SQLITE_VERSION_NUMBER 3047001 +#define SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -652,6 +652,13 @@ SQLITE_API int sqlite3_exec( ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. +** +** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read +** from the database file in amounts that are not a multiple of the +** page size and that do not begin at a page boundary. Without this +** property, SQLite is careful to only do full-page reads and write +** on aligned pages, with the one exception that it will do a sub-page +** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 @@ -668,6 +675,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 +#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels @@ -814,6 +822,7 @@ struct sqlite3_file { **
  • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] **
  • [SQLITE_IOCAP_IMMUTABLE] **
  • [SQLITE_IOCAP_BATCH_ATOMIC] +**
  • [SQLITE_IOCAP_SUBPAGE_READ] ** ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of diff --git a/far/vbuild.m4 b/far/vbuild.m4 index f8c74000eb..9c18c9129d 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6394 +6395 From 80725d0e71f4be032e914dc3f34ec8f01489b629 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Wed, 27 Nov 2024 17:22:12 +0000 Subject: [PATCH 2/8] gh-886: ArcLite crash during an archive update --- plugins/arclite/archive.hpp | 2 +- plugins/arclite/attr.cpp | 10 ++++++++-- plugins/arclite/changelog | 5 +++++ plugins/arclite/plugin.cpp | 4 ++-- plugins/arclite/update.cpp | 2 +- plugins/arclite/version.hpp | 2 +- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/plugins/arclite/archive.hpp b/plugins/arclite/archive.hpp index e5f5f528cd..06e399bff8 100644 --- a/plugins/arclite/archive.hpp +++ b/plugins/arclite/archive.hpp @@ -315,7 +315,7 @@ class Archive: public std::enable_shared_from_this { int m_open_password; bool m_update_props_defined; bool m_has_crc; - void load_update_props(); + void load_update_props(const ArcType& arc_type); public: void create(const std::wstring& src_dir, const std::vector& file_names, const UpdateOptions& options, std::shared_ptr error_log); void update(const std::wstring& src_dir, const std::vector& file_names, const std::wstring& dst_dir, const UpdateOptions& options, std::shared_ptr error_log); diff --git a/plugins/arclite/attr.cpp b/plugins/arclite/attr.cpp index 4708b69d74..066363d6f8 100644 --- a/plugins/arclite/attr.cpp +++ b/plugins/arclite/attr.cpp @@ -447,7 +447,7 @@ void Archive::load_arc_attr() { } } -void Archive::load_update_props() { +void Archive::load_update_props(const ArcType& arc_type) { if (m_update_props_defined) return; m_encrypted = false; @@ -463,7 +463,10 @@ void Archive::load_update_props() { m_level = (unsigned)-1; m_method.clear(); - if ((in_arc->GetArchiveProperty(kpidMethod, prop.ref()) == S_OK && prop.is_str()) || (in_arc->GetProperty(0, kpidMethod, prop.ref()) == S_OK && prop.is_str())) { + if (UInt32 NumberOfItems; + (in_arc->GetArchiveProperty(kpidMethod, prop.ref()) == S_OK && prop.is_str()) || + (in_arc->GetNumberOfItems(&NumberOfItems) && NumberOfItems && in_arc->GetProperty(0, kpidMethod, prop.ref()) == S_OK && prop.is_str()) + ) { std::list m_list = split(prop.get_str(), L' '); static const wchar_t *known_methods[] = { c_method_lzma, c_method_lzma2, c_method_ppmd, c_method_deflate, c_method_deflate64 }; @@ -487,6 +490,9 @@ void Archive::load_update_props() { break; } } + else if (arc_type == c_zip) { + m_method = c_method_deflate; + } if (m_level == (unsigned)-1) m_level = 7; // maximum diff --git a/plugins/arclite/changelog b/plugins/arclite/changelog index 1faba2d5bb..9149e7172d 100644 --- a/plugins/arclite/changelog +++ b/plugins/arclite/changelog @@ -1,3 +1,8 @@ +drkns 2024-11-27 17:23:01+00:00 - build 346 + +1. gh-886: ArcLite crash during an archive update. + Improve compression method detection when updating empty archives. + drkns 2024-09-15 14:00:02+01:00 - build 345 1. Improve compression method detection when updating archives. diff --git a/plugins/arclite/plugin.cpp b/plugins/arclite/plugin.cpp index 11e5fafb6a..a47e26076e 100644 --- a/plugins/arclite/plugin.cpp +++ b/plugins/arclite/plugin.cpp @@ -881,7 +881,7 @@ class Plugin FAIL_MSG(Far::get_msg(MSG_ERROR_UPDATE_UNSUPPORTED_FOR_SINGLEFILEARCHIVE)); } } - archive->load_update_props(); + archive->load_update_props(options.arc_type); options.method = archive->m_method; options.solid = archive->m_solid; options.encrypt = archive->m_encrypted; @@ -1127,7 +1127,7 @@ class Plugin archive->make_index(); options.arc_type = archive->arc_chain.back().type; - archive->load_update_props(); + archive->load_update_props(options.arc_type); if (!cmd.level_defined) options.level = archive->m_level; if (!cmd.method_defined) diff --git a/plugins/arclite/update.cpp b/plugins/arclite/update.cpp index 22c360274b..26e3d5dc90 100644 --- a/plugins/arclite/update.cpp +++ b/plugins/arclite/update.cpp @@ -1206,7 +1206,7 @@ void Archive::create_dir(const std::wstring& dir_name, const std::wstring& dst_d UpdateOptions options; options.arc_type = arc_chain.back().type; - load_update_props(); + load_update_props(options.arc_type); options.level = m_level; options.method = m_method; options.solid = m_solid; diff --git a/plugins/arclite/version.hpp b/plugins/arclite/version.hpp index 97424e19ff..65c26ed108 100644 --- a/plugins/arclite/version.hpp +++ b/plugins/arclite/version.hpp @@ -1,6 +1,6 @@ #include -#define PLUGIN_BUILD 345 +#define PLUGIN_BUILD 346 #define PLUGIN_DESC L"Archive support for Far Manager (based on 7-Zip project)" #define PLUGIN_NAME L"ArcLite" #define PLUGIN_FILENAME L"arclite.dll" From 2fe2415a400e7a7888c3f8df633cbaed680937fc Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Sat, 30 Nov 2024 23:41:21 +0000 Subject: [PATCH 3/8] Warn the user when the terminal uses grapheme clusters --- far/changelog | 5 ++++ far/char_width.cpp | 6 +++++ far/char_width.hpp | 3 +++ far/config.cpp | 14 +++++++++++ far/console.cpp | 17 +++++++++----- far/console.hpp | 1 + far/farlang.templ.m4 | 56 ++++++++++++++++++++++++++++++++++++++++++++ far/vbuild.m4 | 2 +- 8 files changed, 97 insertions(+), 7 deletions(-) diff --git a/far/changelog b/far/changelog index 8e59e5cc5d..5f198b8e40 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,8 @@ +-------------------------------------------------------------------------------- +drkns 2024-11-30 23:34:14+00:00 - build 6396 + +1. Warn the user when the terminal uses grapheme clusters. + -------------------------------------------------------------------------------- skipik 2024-11-26 17:23:11+03:00 - build 6395 diff --git a/far/char_width.cpp b/far/char_width.cpp index 7a2ec29193..52011215da 100644 --- a/far/char_width.cpp +++ b/far/char_width.cpp @@ -552,4 +552,10 @@ namespace char_width static const auto Result = console.GetWidthPreciseExpensive(U'𝖆'); return Result > 1; } + + bool is_grapheme_clusters_on() + { + static const auto Result = console.GetWidthPreciseExpensive(L"à"sv); + return Result == 1; + } } diff --git a/far/char_width.hpp b/far/char_width.hpp index 04f1f9c989..8a8bcf7dde 100644 --- a/far/char_width.hpp +++ b/far/char_width.hpp @@ -63,6 +63,9 @@ namespace char_width [[nodiscard]] bool is_half_width_surrogate_broken(); + + [[nodiscard]] + bool is_grapheme_clusters_on(); } #endif // CHAR_WIDTH_HPP_D66C86AC_3415_4FD1_89DA_0AB843FFEEB8 diff --git a/far/config.cpp b/far/config.cpp index 1024b77ada..151b298d35 100644 --- a/far/config.cpp +++ b/far/config.cpp @@ -378,6 +378,20 @@ void Options::InterfaceSettings() Panels->SetScreenPosition(); // $ 10.07.2001 SKV ! надо это делать, иначе если кейбар спрятали, будет полный рамс. Panels->Redraw(); + + if (FullWidthAwareRendering && char_width::is_grapheme_clusters_on()) + { + Message(FMSG_WARNING, msg(lng::MWarning), + { + msg(lng::MConfigFullWidthAwareRenderingGraphemeClustersDected1), + msg(lng::MConfigFullWidthAwareRenderingGraphemeClustersDected2), + msg(lng::MConfigFullWidthAwareRenderingGraphemeClustersDected3), + msg(lng::MConfigFullWidthAwareRenderingGraphemeClustersDected4), + }, + { + lng::MOk + }); + } } } diff --git a/far/console.cpp b/far/console.cpp index fed5e98318..30c7aabbac 100644 --- a/far/console.cpp +++ b/far/console.cpp @@ -2906,7 +2906,7 @@ namespace console_detail return ExternalConsole.Imports.pWriteOutput.operator bool(); } - size_t console::GetWidthPreciseExpensive(char32_t const Codepoint) + size_t console::GetWidthPreciseExpensive(string_view const Str) { // It ain't stupid if it works @@ -2944,21 +2944,26 @@ namespace console_detail } DWORD Written; - const auto Pair = encoding::utf16::to_surrogate(Codepoint); - const std::array Chars{ Pair.first, Pair.second }; - if (!WriteConsole(m_WidthTestScreen.native_handle(), Chars.data(), Pair.second? 2 : 1, &Written, {})) + if (!WriteConsole(m_WidthTestScreen.native_handle(), Str.data(), static_cast(Str.size()), &Written, {})) { LOGWARNING(L"WriteConsole(): {}"sv, os::last_error()); - return 1; + return Str.size(); } CONSOLE_SCREEN_BUFFER_INFO Info; if (!get_console_screen_buffer_info(m_WidthTestScreen.native_handle(), &Info)) - return 1; + return Str.size(); return Info.dwCursorPosition.X; } + size_t console::GetWidthPreciseExpensive(char32_t const Codepoint) + { + const auto Pair = encoding::utf16::to_surrogate(Codepoint); + const std::array Chars{ Pair.first, Pair.second }; + return GetWidthPreciseExpensive({ Chars.data(), Pair.second? 2uz : 1uz }); + } + void console::ClearWideCache() { m_WidthTestScreen = {}; diff --git a/far/console.hpp b/far/console.hpp index 9e1784fa93..76efff8a76 100644 --- a/far/console.hpp +++ b/far/console.hpp @@ -190,6 +190,7 @@ namespace console_detail bool ExternalRendererLoaded() const; [[nodiscard]] + size_t GetWidthPreciseExpensive(string_view Str); size_t GetWidthPreciseExpensive(char32_t Codepoint); void ClearWideCache(); diff --git a/far/farlang.templ.m4 b/far/farlang.templ.m4 index 05b51bbd4e..21dec61d2d 100644 --- a/far/farlang.templ.m4 +++ b/far/farlang.templ.m4 @@ -2871,6 +2871,62 @@ upd:"Рендерінг з урахуванням повної ширини" "Візуалізацыя з улікам поўнай шырыні" upd:"Fullwidth-aware rendering" +MConfigFullWidthAwareRenderingGraphemeClustersDected1 +upd:"It appears that your terminal supports Unicode grapheme clusters." +"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." +upd:"It appears that your terminal supports Unicode grapheme clusters." + +MConfigFullWidthAwareRenderingGraphemeClustersDected2 +upd:"Unfortunately, Far does not support them yet." +"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." +upd:"Unfortunately, Far does not support them yet." + +MConfigFullWidthAwareRenderingGraphemeClustersDected3 +upd:"For best experience it is recommended to switch to codepoint-based rendering." +"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." +upd:"For best experience it is recommended to switch to codepoint-based rendering." + +MConfigFullWidthAwareRenderingGraphemeClustersDected4 +upd:"Consult the documentation of your terminal for advice on how to do this." +"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." +upd:"Consult the documentation of your terminal for advice on how to do this." + MConfigClearType "Перер&исовка с поддержкой ClearType (может быть медленной)" "Cl&earType-friendly redraw (can be slow)" diff --git a/far/vbuild.m4 b/far/vbuild.m4 index 9c18c9129d..dde853edca 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6395 +6396 From 21d38eccb3b85e79d3cb15296fa03af9445d28e2 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Sun, 1 Dec 2024 10:18:34 +0100 Subject: [PATCH 4/8] Update farlang.templ.m4 --- far/farlang.templ.m4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/far/farlang.templ.m4 b/far/farlang.templ.m4 index 21dec61d2d..d28d88b29c 100644 --- a/far/farlang.templ.m4 +++ b/far/farlang.templ.m4 @@ -2877,7 +2877,7 @@ upd:"It appears that your terminal supports Unicode grapheme clusters." upd:"It appears that your terminal supports Unicode grapheme clusters." upd:"It appears that your terminal supports Unicode grapheme clusters." upd:"It appears that your terminal supports Unicode grapheme clusters." -upd:"It appears that your terminal supports Unicode grapheme clusters." +"Wydaje się, że Twój terminal obsługuje klastry grapheme Unicode." upd:"It appears that your terminal supports Unicode grapheme clusters." upd:"It appears that your terminal supports Unicode grapheme clusters." upd:"It appears that your terminal supports Unicode grapheme clusters." @@ -2891,7 +2891,7 @@ upd:"Unfortunately, Far does not support them yet." upd:"Unfortunately, Far does not support them yet." upd:"Unfortunately, Far does not support them yet." upd:"Unfortunately, Far does not support them yet." -upd:"Unfortunately, Far does not support them yet." +"Niestety, Far jeszcze ich nie obsługuje." upd:"Unfortunately, Far does not support them yet." upd:"Unfortunately, Far does not support them yet." upd:"Unfortunately, Far does not support them yet." @@ -2905,7 +2905,7 @@ upd:"For best experience it is recommended to switch to codepoint-based renderin upd:"For best experience it is recommended to switch to codepoint-based rendering." upd:"For best experience it is recommended to switch to codepoint-based rendering." upd:"For best experience it is recommended to switch to codepoint-based rendering." -upd:"For best experience it is recommended to switch to codepoint-based rendering." +"Dla zachowania najlepszych wrażeń, zalecamy przełączenie na renderowanie oparte na punktach kodowych." upd:"For best experience it is recommended to switch to codepoint-based rendering." upd:"For best experience it is recommended to switch to codepoint-based rendering." upd:"For best experience it is recommended to switch to codepoint-based rendering." @@ -2919,7 +2919,7 @@ upd:"Consult the documentation of your terminal for advice on how to do this." upd:"Consult the documentation of your terminal for advice on how to do this." upd:"Consult the documentation of your terminal for advice on how to do this." upd:"Consult the documentation of your terminal for advice on how to do this." -upd:"Consult the documentation of your terminal for advice on how to do this." +"Sprawdź w dokumentacji terminala, aby dowiedzieć się jak to zrobić." upd:"Consult the documentation of your terminal for advice on how to do this." upd:"Consult the documentation of your terminal for advice on how to do this." upd:"Consult the documentation of your terminal for advice on how to do this." From 82ea6e06d5f442f7599640bbe5e1dc7189f112e4 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Sun, 1 Dec 2024 23:26:16 +0000 Subject: [PATCH 5/8] Experimental: FMSG_KEEPBACKGROUND is a no-op --- far/message.cpp | 9 +++------ far/message.hpp | 1 - far/plugapi.cpp | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/far/message.cpp b/far/message.cpp index b8e36425a9..d5ceab6adf 100644 --- a/far/message.cpp +++ b/far/message.cpp @@ -449,12 +449,9 @@ static message_result MessageImpl( // *** Без Диалога! *** HideCursor(); - if (!(Flags & MSG_KEEPBACKGROUND)) - { - SetScreen(Position, L' ', colors::PaletteColorToFarColor((Flags & MSG_WARNING)? COL_WARNDIALOGTEXT : COL_DIALOGTEXT)); - DropShadow(Position); - Box({ Position.left + 3, Position.top + 1, Position.right - 3, Position.bottom - 1 }, colors::PaletteColorToFarColor((Flags & MSG_WARNING)? COL_WARNDIALOGBOX : COL_DIALOGBOX), DOUBLE_BOX); - } + SetScreen(Position, L' ', colors::PaletteColorToFarColor((Flags & MSG_WARNING)? COL_WARNDIALOGTEXT : COL_DIALOGTEXT)); + DropShadow(Position); + Box({ Position.left + 3, Position.top + 1, Position.right - 3, Position.bottom - 1 }, colors::PaletteColorToFarColor((Flags & MSG_WARNING)? COL_WARNDIALOGBOX : COL_DIALOGBOX), DOUBLE_BOX); SetColor((Flags & MSG_WARNING)?COL_WARNDIALOGTEXT:COL_DIALOGTEXT); diff --git a/far/message.hpp b/far/message.hpp index a20d9feaec..9806174b4c 100644 --- a/far/message.hpp +++ b/far/message.hpp @@ -52,7 +52,6 @@ enum class lng; enum { MSG_WARNING = 0_bit, - MSG_KEEPBACKGROUND = 2_bit, MSG_LEFTALIGN = 3_bit, MSG_KILLSAVESCREEN = 28_bit, diff --git a/far/plugapi.cpp b/far/plugapi.cpp index c31370a437..0eba504c0b 100644 --- a/far/plugapi.cpp +++ b/far/plugapi.cpp @@ -1224,7 +1224,6 @@ intptr_t WINAPI apiMessageFn(const UUID* PluginId, const UUID* Id, unsigned long const DWORD InternalFlags = ((Flags & FMSG_WARNING)? MSG_WARNING : 0) | - ((Flags & FMSG_KEEPBACKGROUND)? MSG_KEEPBACKGROUND : 0) | ((Flags & FMSG_LEFTALIGN)? MSG_LEFTALIGN : 0); return static_cast(Message( From 9bfa9134a808c740ebf5af077aa8c1ec156db14c Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Sun, 1 Dec 2024 23:26:16 +0000 Subject: [PATCH 6/8] Correction of 6396 --- far/config.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/far/config.cpp b/far/config.cpp index 151b298d35..6b45d01584 100644 --- a/far/config.cpp +++ b/far/config.cpp @@ -364,6 +364,8 @@ void Options::InterfaceSettings() Builder.AddEditField(strTitleAddons, 47); Builder.AddOKCancel(); + const auto PreviousFullWidthAwareRendering = FullWidthAwareRendering.Get(); + if (Builder.ShowDialog()) { if (CMOpt.CopyTimeRule) @@ -379,7 +381,7 @@ void Options::InterfaceSettings() // $ 10.07.2001 SKV ! надо это делать, иначе если кейбар спрятали, будет полный рамс. Panels->Redraw(); - if (FullWidthAwareRendering && char_width::is_grapheme_clusters_on()) + if (!PreviousFullWidthAwareRendering && FullWidthAwareRendering && char_width::is_grapheme_clusters_on()) { Message(FMSG_WARNING, msg(lng::MWarning), { From ba306bd1c56a9d6061b425b0ca0ea2eaffdcf248 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Sun, 1 Dec 2024 23:26:16 +0000 Subject: [PATCH 7/8] Build & changelog --- far/changelog | 7 +++++++ far/vbuild.m4 | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/far/changelog b/far/changelog index 5f198b8e40..48e538628c 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,10 @@ +-------------------------------------------------------------------------------- +drkns 2024-12-01 23:24:48+00:00 - build 6397 + +1. Experimental: FMSG_KEEPBACKGROUND is a no-op. + +2. Correction of 6396. + -------------------------------------------------------------------------------- drkns 2024-11-30 23:34:14+00:00 - build 6396 diff --git a/far/vbuild.m4 b/far/vbuild.m4 index dde853edca..495c1df87c 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6396 +6397 From af775c5b2274a4cb0b465b4f6fae531a68db8890 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Mon, 2 Dec 2024 20:42:42 +0000 Subject: [PATCH 8/8] Use DECRQM 2027 to check grapheme clusters state (thanks to DHowett) --- far/changelog | 5 ++++ far/char_width.cpp | 17 +++++++++-- far/console.cpp | 71 ++++++++++++++++++++++++++++++++++++---------- far/console.hpp | 4 +++ far/vbuild.m4 | 2 +- 5 files changed, 81 insertions(+), 18 deletions(-) diff --git a/far/changelog b/far/changelog index 48e538628c..9d16993c5e 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,8 @@ +-------------------------------------------------------------------------------- +drkns 2024-12-02 20:39:53+00:00 - build 6398 + +1. Use DECRQM 2027 to check grapheme clusters state (thanks to DHowett). + -------------------------------------------------------------------------------- drkns 2024-12-01 23:24:48+00:00 - build 6397 diff --git a/far/char_width.cpp b/far/char_width.cpp index 52011215da..6b0e0006ed 100644 --- a/far/char_width.cpp +++ b/far/char_width.cpp @@ -39,6 +39,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Internal: #include "console.hpp" #include "locale.hpp" +#include "log.hpp" // Platform: @@ -555,7 +556,19 @@ namespace char_width bool is_grapheme_clusters_on() { - static const auto Result = console.GetWidthPreciseExpensive(L"à"sv); - return Result == 1; + static const auto Result = [] + { + if (const auto IsOn = console.is_grapheme_clusters_on(); IsOn) + { + LOGDEBUG(L"Grapheme clusters (VT): {}"sv, *IsOn); + return *IsOn; + } + + const auto IsOn = console.GetWidthPreciseExpensive(L"à"sv) == 1; + LOGDEBUG(L"Grapheme clusters (heuristics): {}"sv, IsOn); + return IsOn; + }(); + + return Result; } } diff --git a/far/console.cpp b/far/console.cpp index 30c7aabbac..e95d369788 100644 --- a/far/console.cpp +++ b/far/console.cpp @@ -631,15 +631,15 @@ namespace console_detail std::optional FirstTokenPrefixPos, - FirstTokenSuffixPos, - SecondTokenPrefixPos, - SecondTokenSuffixPos; + FirstTokenSuffixPos; const auto TokenPrefix = CSI "?"sv, TokenSuffix = L"c"sv; - while (!SecondTokenSuffixPos) + size_t DA_ResponseSize{}; + + for (;;) { wchar_t ResponseBuffer[8192]; size_t ResponseSize; @@ -655,21 +655,19 @@ namespace console_detail if (FirstTokenPrefixPos && !FirstTokenSuffixPos) if (const auto Pos = Response.find(TokenSuffix, *FirstTokenPrefixPos + TokenPrefix.size()); Pos != Response.npos) + { FirstTokenSuffixPos = Pos; + DA_ResponseSize = Pos + TokenSuffix.size(); + } - if (FirstTokenSuffixPos && !SecondTokenPrefixPos) - if (const auto Pos = Response.find(TokenPrefix, *FirstTokenSuffixPos + TokenSuffix.size()); Pos != Response.npos) - SecondTokenPrefixPos = Pos; - - if (SecondTokenPrefixPos && !SecondTokenSuffixPos) - if (const auto Pos = Response.find(TokenSuffix, *SecondTokenPrefixPos + TokenPrefix.size()); Pos != Response.npos) - SecondTokenSuffixPos = Pos; + if (DA_ResponseSize && Response.size() >= DA_ResponseSize * 2) + { + if (const auto DA_Response = string_view(Response).substr(*FirstTokenPrefixPos, DA_ResponseSize); Response.ends_with(DA_Response)) + break; + } } - Response.resize(*SecondTokenPrefixPos); - Response.erase(0, *FirstTokenSuffixPos + TokenSuffix.size()); - - return Response; + return Response.substr(DA_ResponseSize, Response.size() - DA_ResponseSize * 2); } console::console(): @@ -3100,6 +3098,49 @@ namespace console_detail send_vt_command(far::format(OSC("9001;CmdNotFound;{}"), Command)); } + std::optional console::is_grapheme_clusters_on() const + { + try + { +#define DECRQM_REQUEST "?2027" + + const auto ResponseData = query_vt(CSI DECRQM_REQUEST "$p"sv); + if (ResponseData.empty()) + { + LOGWARNING(L"DECRQM 2027 query is not supported"sv); + return {}; + } + + const auto + Prefix = CSI DECRQM_REQUEST ";"sv, + Suffix = L"$y"sv; + +#undef DECRQM_REQUEST + + const auto give_up = [&] + { + throw far_exception(far::format(L"Incorrect response: {}"sv, ResponseData), false); + }; + + if (ResponseData.size() != Prefix.size() + 1 + Suffix.size() || !ResponseData.starts_with(Prefix) || !ResponseData.ends_with(Suffix)) + give_up(); + + switch (ResponseData[Prefix.size()]) + { + case L'3': return true; + case L'4': return false; + default: + give_up(); + std::unreachable(); + } + } + catch (far_exception const& e) + { + LOGERROR(L"{}"sv, e); + return {}; + } + } + bool console::GetCursorRealPosition(point& Position) const { CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; diff --git a/far/console.hpp b/far/console.hpp index 76efff8a76..1482e04027 100644 --- a/far/console.hpp +++ b/far/console.hpp @@ -191,6 +191,7 @@ namespace console_detail [[nodiscard]] size_t GetWidthPreciseExpensive(string_view Str); + [[nodiscard]] size_t GetWidthPreciseExpensive(char32_t Codepoint); void ClearWideCache(); @@ -213,6 +214,9 @@ namespace console_detail void command_finished(int ExitCode) const; void command_not_found(string_view Command) const; + [[nodiscard]] + std::optional is_grapheme_clusters_on() const; + [[nodiscard]] short GetDelta() const; diff --git a/far/vbuild.m4 b/far/vbuild.m4 index 495c1df87c..602f247530 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6397 +6398