Compare commits

...

2 Commits

Author SHA1 Message Date
Justin Marshall 93ac3c1685 Added unicode support. 2026-08-11 01:45:43 -07:00
Justin Marshall 6d95e7d78d First pass of security fixes 2026-08-10 22:31:19 -07:00
26 changed files with 2338 additions and 250 deletions
+10 -5
View File
@@ -689,7 +689,7 @@ target_include_directories(opus_original_engine PRIVATE ${OPUS_ORIGINAL_INCLUDE_
if(MSVC)
# /J preserves the original compiler's unsigned-char default. Do not use
# the C++ compiler for K&R-era original C translation units.
target_compile_options(opus_original_engine PRIVATE /J /W3 /wd4005 /Gy)
target_compile_options(opus_original_engine PRIVATE /J /W3 /wd4005 /Gy /guard:cf)
endif()
set_target_properties(opus_original_engine PROPERTIES
FOLDER "Original Opus x64"
@@ -731,13 +731,14 @@ target_compile_definitions(opus_x64_runtime PRIVATE
NONATIVE
OPUS_X64
NOMINMAX
$<$<CONFIG:Debug>:OPUS_TEST_HOOKS>
)
target_include_directories(opus_x64_runtime PRIVATE ${OPUS_ORIGINAL_INCLUDE_DIRS})
target_link_libraries(opus_x64_runtime PUBLIC ole32 comdlg32 gdi32)
if(MSVC)
# Opus was built with unsigned plain characters; the byte-oriented string
# and international-character assembly routines depend on that behavior.
target_compile_options(opus_x64_runtime PRIVATE /J /W4 /permissive-)
target_compile_options(opus_x64_runtime PRIVATE /J /W4 /permissive- /guard:cf /sdl)
endif()
set_target_properties(opus_x64_runtime PROPERTIES
FOLDER "Original Opus x64/runtime"
@@ -864,10 +865,10 @@ target_compile_features(WORD1 PRIVATE cxx_std_20)
target_compile_definitions(WORD1 PRIVATE
WIN WIN23 CRLF NONATIVE OPUS_X64 NOMINMAX UNICODE _UNICODE
)
target_link_libraries(WORD1 PRIVATE opus_original_engine opus_x64_runtime user32 dbghelp)
target_link_libraries(WORD1 PRIVATE opus_original_engine opus_x64_runtime user32 imm32 dbghelp)
if(MSVC)
target_compile_options(WORD1 PRIVATE /J /W3 /wd4005 /utf-8)
target_link_options(WORD1 PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /MANIFEST:NO)
target_compile_options(WORD1 PRIVATE /J /W3 /wd4005 /utf-8 /guard:cf /sdl)
target_link_options(WORD1 PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /guard:cf /CETCOMPAT /MANIFEST:NO)
endif()
set_target_properties(WORD1 PROPERTIES
FOLDER "Original Opus x64/product"
@@ -1005,6 +1006,10 @@ add_test(NAME opus_word1_clipboard_shortcut_test
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --clipboard
)
set_tests_properties(opus_word1_clipboard_shortcut_test PROPERTIES TIMEOUT 20)
add_test(NAME opus_word1_unicode_test
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --unicode
)
set_tests_properties(opus_word1_unicode_test PROPERTIES TIMEOUT 20)
add_test(NAME opus_word1_typing_test
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing
)
+1
View File
@@ -479,6 +479,7 @@ ChangeClipboard()
if (vsab.fOwnClipboard)
{ /* only set handles if we really have something in docScrap */
SetClipboardData( CF_OWNERDISPLAY, NULL );
SetClipboardData( CF_UNICODETEXT, NULL );
/* don't put out cf_tiff or other rejected pic types, or
rtf or link for any of those types
*/
+49 -3
View File
@@ -70,6 +70,11 @@ extern char szEmpty[];
extern struct SCI vsci;
extern int vwWinVersion;
extern int wwCur;
#ifdef OPUS_X64
extern HANDLE OpusUnicodeCreateClipboardHandle();
extern int OpusUnicodeClipboardToLegacy();
extern int OpusUnicodeBindPendingClipboard();
#endif
extern LPCH LpchIncr();
@@ -279,6 +284,21 @@ int *pfBlankPic; /* set true if an empty picture; else ignored */
AssureLegalSel (PcaSet(&ca, doc, cpFirst, cpLim));
switch (cf)
{
case CF_UNICODETEXT:
#ifdef OPUS_X64
{
HANDLE hAnsi;
HANDLE hUnicode;
hAnsi = HWriteText(ca.doc, ca.cpFirst, ca.cpLim, cbInitial);
if (hAnsi == NULL)
return NULL;
hUnicode = OpusUnicodeCreateClipboardHandle(ca.doc, ca.cpFirst, hAnsi);
GlobalFree(hAnsi);
return hUnicode;
}
#else
return NULL;
#endif
case CF_TEXT:
return HWriteText (ca.doc, ca.cpFirst, ca.cpLim, cbInitial);
@@ -845,7 +865,7 @@ int FReadExtScrap()
{
vsab.fMayHavePic = vsab.fPict = fTrue;
}
if (cf != CF_TEXT)
if (cf != CF_TEXT && cf != CF_UNICODETEXT)
vsab.fFormatted = fTrue;
break;
}
@@ -884,6 +904,8 @@ int cbInitial;
{
int docDest;
BOOL fOk;
BOOL fUnicode = fFalse;
HANDLE hConverted = NULL;
struct CA ca;
#ifdef BZ
@@ -892,6 +914,18 @@ int cbInitial;
switch (cf)
{
case CF_UNICODETEXT:
#ifdef OPUS_X64
if (!OpusUnicodeClipboardToLegacy(hData, &hConverted) ||
hConverted == NULL)
return fFalse;
hData = hConverted;
cf = CF_TEXT;
fUnicode = fTrue;
break;
#else
return fFalse;
#endif
case CF_TEXT:
case CF_DIB:
case CF_BITMAP:
@@ -917,7 +951,11 @@ int cbInitial;
docDest = docScrap;
/* otherwise use vdocScratch */
else if ((docDest = DocCreateScratch (pca->doc)) == docNil)
{
if (hConverted != NULL)
GlobalFree(hConverted);
return fFalse;
}
/* empty the destination doc and set styles, fonts to standard */
SetWholeDoc (docDest, PcaSetNil(&ca));
@@ -939,7 +977,11 @@ int cbInitial;
break;
case CF_TEXT:
fOk = FReadText (docDest, cf, hData, cbInitial);
if (fOk && fUnicode)
fOk = OpusUnicodeBindPendingClipboard(docDest);
}
if (hConverted != NULL)
GlobalFree(hConverted);
fOk &= (!vmerr.fMemFail && !vmerr.fDiskFail);
@@ -1843,7 +1885,8 @@ int cf;
BOOL fPrPic;
{
/* Desired order (win3 word: Notice text before picture formats):
/* Desired order (Unicode text precedes legacy formats):
CF_UNICODETEXT
cfRTF
CF_TEXT
cfPRPic
@@ -1876,7 +1919,9 @@ BOOL fPrPic;
goto LMetafile; /* 4th or 5th choice */
case cfNil:
return cfRTF; /* 1st choice */
return CF_UNICODETEXT; /* 1st choice */
case CF_UNICODETEXT:
return cfRTF;
case CF_TEXT:
if (fPrPic && cfPrPic != cfNil)
return cfPrPic; /* 4th choice (Excel specific) */
@@ -1915,6 +1960,7 @@ BOOL fRestrictRTF;
switch (cf)
{
case CF_UNICODETEXT:
case CF_TEXT:
/* reject text for either a normal pic or an import field pic,
since it has no text result and would show nothing */
+6
View File
@@ -813,6 +813,9 @@ int doc;
int isels;
struct DOD *pdod, dod;
extern struct PL **vhplbmc;
#ifdef OPUS_X64
extern void OpusUnicodeForgetDocument();
#endif
Assert(doc >= 0 && doc < docMac);
Assert(doc == docNil || mpdochdod[doc] != hNil);
@@ -848,6 +851,9 @@ int doc;
vdocTemp = docNil;
else if (doc == vdocScratch)
vdocScratch = docNil;
#ifdef OPUS_X64
OpusUnicodeForgetDocument(doc);
#endif
DisposeDocPdod(doc, &dod );
Assert (vdocScratch == docNil || PdodDoc(vdocScratch)->doc
+8
View File
@@ -81,6 +81,10 @@ int wLow, wHigh;
int das;
BOOL f;
#ifdef OPUS_X64
/* DDE execute is unauthenticated cross-process macro execution. */
goto LExeNACK;
#endif
if (fTerminating || PdcldDcl(dcl)->fExecuting || fElActive)
goto LExeNACK;
@@ -117,6 +121,10 @@ LExeNACK:
int das = dasNACK;
int ibkmk;
#ifdef OPUS_X64
/* Do not let another process overwrite an open document through DDE. */
goto LNackPoke;
#endif
if (fTerminating)
/* not much point in receiving while terminating */
{
+15
View File
@@ -73,6 +73,8 @@ BOOL vfNoInval=0; /* can be > 1 */
#ifdef OPUS_X64
struct PLC **HplcCreateEdc();
extern void OpusUnicodeOnReplace();
extern void OpusUnicodeOnReplaceCps();
#else
struct PLC *HplcCreateEdc();
#endif
@@ -463,6 +465,9 @@ struct CA *pca;
int fn;
FC fc, dfc;
{
int docUnicode = pca->doc;
CP cpFirstUnicode = pca->cpFirst;
CP cpLimUnicode = pca->cpLim;
struct XBC xbc;
struct XSR *pxsr;
/* this should be the maximum number of calls to XReplace or XReplaceCps that
@@ -489,6 +494,7 @@ should go off) */
XReplace(fFalse, &xbc, pxsr);
EndCommit();
CloseTns(&xbc);
OpusUnicodeOnReplace(docUnicode, cpFirstUnicode, cpLimUnicode, dfc);
return fTrue;
}
@@ -821,6 +827,12 @@ on failure (in which case document is unchanged) */
HANDNATIVE BOOL C_FReplaceCps(pcaDel, pcaIns)
struct CA *pcaDel, *pcaIns;
{
int docDelUnicode = pcaDel->doc;
int docInsUnicode = pcaIns->doc;
CP cpDelFirstUnicode = pcaDel->cpFirst;
CP cpDelLimUnicode = pcaDel->cpLim;
CP cpInsFirstUnicode = pcaIns->cpFirst;
CP cpInsLimUnicode = pcaIns->cpLim;
struct XBC xbc;
struct XSR *pxsr;
/* this should be the maximum number of calls to XReplace or XReplaceCps that
@@ -848,6 +860,9 @@ should go off). Win is greater than Mac because of annotations. */
XReplaceCps(fFalse, &xbc, pxsr);
EndCommit();
CloseTns(&xbc);
OpusUnicodeOnReplaceCps(docDelUnicode, cpDelFirstUnicode,
cpDelLimUnicode, docInsUnicode, cpInsFirstUnicode,
cpInsLimUnicode);
return fTrue;
}
+6
View File
@@ -1808,6 +1808,7 @@ BOOL fPasteLink;
{
if ((fPasteLink && fmt == cfLink)
|| (!fPasteLink && (fmt == cfRTF ||
fmt == CF_UNICODETEXT ||
fmt == CF_TEXT ||
fmt == CF_BITMAP ||
fmt == CF_METAFILEPICT)))
@@ -2479,6 +2480,11 @@ uns atm;
Assert(atm < atmMax);
#ifdef OPUS_X64
/* Never execute document/template macros merely because a file opened,
closed, or Word started. See the secure default in elmisc.c. */
return cmdOK;
#endif
/* Skip auto macro if shift key is down */
if (vfDisableAutoMacros || (GetAsyncKeyState(VK_SHIFT) & 0x8000))
+24 -7
View File
@@ -128,6 +128,10 @@ extern struct DBS vdbs;
extern struct PREF vpref;
extern struct PRI vpri;
#ifdef OPUS_X64
extern int OpusUnicodeExtTextOut();
extern int OpusUnicodeHasRange();
#endif
extern int vlm;
extern int vfPrvwDisp;
@@ -1256,13 +1260,26 @@ LDacNop:
else
{
LRealExtTextOut:
ExtTextOut(hdc, xp, yp,
eto, /* action bits */
fRcAdjusted ? (LPRECT)&rcAdjusted :
(LPRECT)&rcOpaque, /* opaque rect */
(LPCH)&vfli.rgch [ich], /* string */
cch, /* length */
lpdxp ); /* lpdx */
#ifdef OPUS_X64
ExtTextOut(hdc, xp, yp, eto,
fRcAdjusted ? (LPRECT)&rcAdjusted :
(LPRECT)&rcOpaque,
(LPCH)&vfli.rgch [ich], cch, lpdxp);
if (OpusUnicodeHasRange(vfli.doc,
vfli.cpMin + dcpVanish + ich, cch))
OpusUnicodeExtTextOut(hdc, xp, yp, eto,
fRcAdjusted ? (LPRECT)&rcAdjusted :
(LPRECT)&rcOpaque,
vfli.doc,
vfli.cpMin + dcpVanish + ich,
(LPCH)&vfli.rgch [ich], cch, lpdxp);
#else
ExtTextOut(hdc, xp, yp,
eto,
fRcAdjusted ? (LPRECT)&rcAdjusted :
(LPRECT)&rcOpaque,
(LPCH)&vfli.rgch [ich], cch, lpdxp);
#endif
}
vrf.fInExternalCall = fFalse;
(char *) pchr = (char *)*vhgrpchr + bchrCur;
+11
View File
@@ -80,7 +80,18 @@ extern BOOL vfAwfulNoise;
extern int vfSeeSel;
#ifdef OPUS_X64
/*
* Legacy Opus documents and templates can contain AutoOpen/AutoExec macros.
* The original application ran them without a trust boundary. That is not
* an acceptable default for documents received from modern file systems, so
* the x64 port keeps automatic macros disabled. Explicitly invoked macros
* continue to use the normal command path.
*/
BOOL vfDisableAutoMacros = fTrue;
#else
BOOL vfDisableAutoMacros = fFalse;
#endif
/* Simple Cursor Movement / Selection Commands */
+9
View File
@@ -1265,6 +1265,15 @@ LHaveEvent:
return fFalse;
}
#ifdef OPUS_X64
/* End the legacy one-byte insertion batch before a UTF-16 WM_CHAR is
consumed. The outer message loop will pass it to the Unicode bridge
with the full code unit intact. */
if (vmsgLast.message == WM_CHAR &&
(unsigned int)vmsgLast.wParam >= 0x100)
return fFalse;
#endif
if (FIsKeyMessage((LPMSG) &vmsgLast))
{
if (vmsgLast.wParam & 0x8000) /* indicates we did xlation */
+2
View File
@@ -620,6 +620,7 @@ BOOL fTutorial;
if (!fTutorial)
{
/* BLOCK: now run macros specified on command line */
#ifndef OPUS_X64
{
int isz;
CHAR * szArg;
@@ -639,6 +640,7 @@ BOOL fTutorial;
}
}
}
#endif /* !OPUS_X64 */
}
+7 -3
View File
@@ -91,6 +91,7 @@ extern int OpusModernPendingDocxParagraphCount();
extern int OpusModernGetPendingDocxRun();
extern int OpusModernGetPendingDocxParagraph();
extern int OpusModernGetPendingDocxPage();
extern int OpusModernBindPendingDocxUnicode();
extern void OpusModernClearPendingDocxFormatting();
extern int OpusX64FtcFromFontNameForDoc();
#endif
@@ -128,8 +129,9 @@ int doc;
struct CA ca;
char grpprl[96];
char szFont[LF_FACESIZE];
char szLanguage[32];
int bold, italic, underline, strike, smallCaps, allCaps, hidden;
int hps, ico;
int hps, ico, charset;
int jc, dxaLeft, dxaRight, dxaLeft1, dyaBefore, dyaAfter, dyaLine;
int fKeep, fKeepFollow, fPageBreakBefore, fBottomBorder;
int xaPage, yaPage, dxaLeftPage, dxaRightPage, dyaTopPage, dyaBottomPage;
@@ -195,7 +197,8 @@ int doc;
{
if (!OpusModernGetPendingDocxRun(i, &cpFirst, &cpLim, &bold,
&italic, &underline, &strike, &smallCaps, &allCaps,
&hidden, &hps, &ico, szFont, sizeof(szFont)))
&hidden, &hps, &ico, szFont, sizeof(szFont), &charset,
szLanguage, sizeof(szLanguage)))
continue;
if (cpFirst < cp0 || cpFirst >= cpMac)
continue;
@@ -214,7 +217,7 @@ int doc;
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFVanish, hidden);
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCHps, hps);
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCIco, ico);
ftc = OpusX64FtcFromFontNameForDoc(doc, szFont);
ftc = OpusX64FtcFromFontNameForDoc(doc, szFont, charset);
if (ftc >= 0)
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFtc, ftc);
ca.doc = doc;
@@ -224,6 +227,7 @@ int doc;
}
PdodDoc(doc)->fFormatted = fTrue;
OpusModernBindPendingDocxUnicode(doc);
OpusModernClearPendingDocxFormatting();
}
#endif
+76 -12
View File
@@ -104,6 +104,25 @@ extern struct REB * vpreb;
static int vWin95ZoomPercent = 100;
static int vWin95BaseDxsInch = 0;
static int vWin95BaseDysInch = 0;
int vOpusPdfExportStage = 0;
static int vOpusDocxSnapshotDoc = docNil;
static const char *vszOpusDocxSnapshotPath = NULL;
int OpusGetUnicodeSelection(pdoc, pcpFirst, pcpLim)
int *pdoc;
long *pcpFirst;
long *pcpLim;
{
if (selCur.doc == docNil)
return fFalse;
if (pdoc != NULL)
*pdoc = selCur.doc;
if (pcpFirst != NULL)
*pcpFirst = selCur.cpFirst;
if (pcpLim != NULL)
*pcpLim = selCur.cpLim;
return fTrue;
}
int OpusExportCurrentDocumentPdf()
{
@@ -116,17 +135,24 @@ int OpusExportCurrentDocumentPdf()
extern CHAR HUGE *vhpchFetch;
extern int OpusPdfSnapshotBegin();
extern int OpusPdfSnapshotAddParagraph();
extern int OpusPdfSnapshotAddRun();
extern int OpusPdfSnapshotAddRunUtf8();
extern int OpusPdfSnapshotExportDialog();
extern int OpusDocxSnapshotExportPath();
extern void OpusX64FontNameFromFtc();
extern int OpusX64CharsetFromFtc();
extern int OpusUnicodeTextToUtf8();
extern int OpusUnicodeLanguageTagAt();
if (selCur.doc == docNil)
vOpusPdfExportStage = 1;
if (vOpusDocxSnapshotDoc == docNil && selCur.doc == docNil)
return fFalse;
doc = DocMother(selCur.doc);
doc = vOpusDocxSnapshotDoc != docNil ? vOpusDocxSnapshotDoc :
DocMother(selCur.doc);
cpMac = CpMacDocEdit(doc);
if (cpMac < cp0 || cpMac > 0x3fffffffL)
return fFalse;
pdod = PdodMother(doc);
vOpusPdfExportStage = 2;
if (!OpusPdfSnapshotBegin(pdod->dop.xaPage, pdod->dop.yaPage,
pdod->dop.dxaLeft, pdod->dop.dxaRight,
pdod->dop.dyaTop < 0 ? -pdod->dop.dyaTop : pdod->dop.dyaTop,
@@ -141,6 +167,7 @@ int OpusExportCurrentDocumentPdf()
cpParaLim = CpMin(caPara.cpLim, cpMac);
if (cpParaLim <= cpPara)
cpParaLim = cpPara + 1;
vOpusPdfExportStage = 1000 + (int)cpPara;
if (!OpusPdfSnapshotAddParagraph(vpapFetch.jc,
vpapFetch.dxaLeft, vpapFetch.dxaRight,
vpapFetch.dxaLeft1, vpapFetch.dyaBefore,
@@ -155,49 +182,86 @@ int OpusExportCurrentDocumentPdf()
int ich;
int cch;
int cchOutput;
char rgch[1024];
char rgch[4096];
char szFont[LF_FACESIZE];
char szLanguage[32];
int charset;
FetchCp(doc, cpRun, fcmChars + fcmProps);
if (vccpFetch <= 0 || vhpchFetch == NULL)
return fFalse;
cch = (int)CpMin((CP)vccpFetch, cpParaLim - cpRun);
cch = min(cch, sizeof(rgch) - 1);
cch = min(cch, (sizeof(rgch) - 1) / 4);
charset = OpusX64CharsetFromFtc(doc, vchpFetch.ftc);
cchOutput = 0;
for (ich = 0; ich < cch; ++ich)
{
int ch = (unsigned char)vhpchFetch[ich];
char chOutput;
char rgchUtf8[8];
int cchUtf8;
if (ch == chReturn || ch == chEop)
continue;
if (ch == chTable || ch == chTab)
rgch[cchOutput++] = '\t';
chOutput = '\t';
else if (ch == chSect || ch == chColumnBreak)
rgch[cchOutput++] = '\f';
chOutput = '\f';
else if (ch == chNonReqHyphen)
rgch[cchOutput++] = '-';
chOutput = '-';
/* fSpec can cover imported field/list runs whose bytes are still
ordinary visible text. Filter actual control codes above, not
the whole run, or leading words disappear from PDF output. */
else if (ch >= chSpace)
rgch[cchOutput++] = (char)ch;
chOutput = (char)ch;
else
continue;
cchUtf8 = OpusUnicodeTextToUtf8(doc, cpRun + ich,
&chOutput, 1, charset, rgchUtf8, sizeof(rgchUtf8));
if (cchUtf8 < 0 || cchUtf8 > sizeof(rgch) - 1 - cchOutput)
return fFalse;
bltbyte(rgchUtf8, rgch + cchOutput, cchUtf8);
cchOutput += cchUtf8;
}
if (cchOutput > 0)
{
rgch[cchOutput] = '\0';
OpusX64FontNameFromFtc(vchpFetch.ftc,
szFont, sizeof(szFont));
if (!OpusPdfSnapshotAddRun(rgch, cchOutput, szFont,
OpusUnicodeLanguageTagAt(doc, cpRun,
szLanguage, sizeof(szLanguage));
vOpusPdfExportStage = 100000 + (int)cpRun;
if (!OpusPdfSnapshotAddRunUtf8(rgch, cchOutput, szFont,
vchpFetch.hps, vchpFetch.fBold,
vchpFetch.fItalic, vchpFetch.kul != kulNone,
vchpFetch.fStrike, vchpFetch.fSmallCaps,
vchpFetch.fCaps, vchpFetch.fVanish,
vchpFetch.ico))
vchpFetch.ico, szLanguage, charset))
return fFalse;
}
cpRun += cch;
}
cpPara = cpParaLim;
}
result = OpusPdfSnapshotExportDialog(vhwndApp);
vOpusPdfExportStage = 3;
result = vszOpusDocxSnapshotPath != NULL ?
OpusDocxSnapshotExportPath(vszOpusDocxSnapshotPath) :
OpusPdfSnapshotExportDialog(vhwndApp);
vOpusPdfExportStage = result ? 4 : -4;
return result;
}
int OpusSaveDocumentAsDocx(doc, szPath)
int doc;
const char *szPath;
{
int result;
if (doc == docNil || szPath == NULL || *szPath == '\0' ||
vOpusDocxSnapshotDoc != docNil)
return fFalse;
vOpusDocxSnapshotDoc = DocMother(doc);
vszOpusDocxSnapshotPath = szPath;
result = OpusExportCurrentDocumentPdf();
vOpusDocxSnapshotDoc = docNil;
vszOpusDocxSnapshotPath = NULL;
return result;
}
+1 -1
View File
@@ -708,7 +708,7 @@ LCleanup:
FWin95SaveAliasMatches(stFile) && PdodDoc(doc)->fn != fnNil)
FCloseFn(PdodDoc(doc)->fn);
if (!OpusFinishWin95SaveAlias(stFile,
cmd == cmdOK && pcmb->fAction) && cmd == cmdOK)
cmd == cmdOK && pcmb->fAction, doc) && cmd == cmdOK)
cmd = cmdError;
#endif
if (cmd == cmdCancelled)
+53 -14
View File
@@ -1002,27 +1002,50 @@ const char *sz;
/* Variant used while a DOCX is being opened. The new document has not yet
become selCur.doc, so the normal ribbon adapter would add the font to the
document that was active before File Open. */
EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz)
EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz, charset)
int doc;
const char *sz;
int charset;
{
int cch;
struct FFN *pffn;
char rgch[cbFfnLast] = {0};
if (sz == NULL || *sz == '\0')
return wNinch;
/* Word 1.x keeps only three guaranteed GDI font slots in a new plain-text
document: Times, Symbol, and Helvetica. Adding Office-era fonts before
the imported document becomes current can alias the new ftc to Symbol.
Preserve the requested family safely; Helvetica is the closest available
substitute for Aptos/Calibri/Arial and retains ANSI character mapping. */
/* A new Word 1.x document exposes only the three guaranteed screen-font
slots while DOCX formatting is applied. Adding a normal ANSI font at
this point can alias it to slot 1 (Symbol), corrupting every Latin
character in the document. Keep the historical safe mappings for ANSI
faces; charset-specific runs still get real FFN records below. */
if (!FNeNcSz(sz, "Symbol") || !FNeNcSz(sz, "Wingdings"))
return 1;
if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") ||
!FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") ||
!FNeNcSz(sz, "Cambria"))
return ftcDefault;
if (!FNeNcSz(sz, "Courier") || !FNeNcSz(sz, "Courier New") ||
!FNeNcSz(sz, "Consolas"))
return FtcFromDocIbst(doc, ibstCourier);
return 2;
if (charset == ANSI_CHARSET || charset == DEFAULT_CHARSET)
{
if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") ||
!FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") ||
!FNeNcSz(sz, "Cambria"))
return ftcDefault;
if (!FNeNcSz(sz, "Courier") || !FNeNcSz(sz, "Courier New") ||
!FNeNcSz(sz, "Consolas"))
return FtcFromDocIbst(doc, ibstCourier);
return 2;
}
pffn = (struct FFN *)rgch;
if (CchSz(sz) > LF_FACESIZE - 1)
{
bltbyte(sz, pffn->szFfn, LF_FACESIZE - 1);
pffn->szFfn[LF_FACESIZE - 1] = '\0';
}
else
CchCopySz(sz, pffn->szFfn);
cch = CchStripString(pffn->szFfn, CchSz(pffn->szFfn) - 1) + 1;
if (cch <= 1)
return wNinch;
pffn->ffid = FF_DONTCARE;
pffn->cbFfnM1 = CbFfnFromCchSzFfn(cch) - 1;
ChsPffn(pffn) = (charset >= 0 && charset <= 255) ? charset : ANSI_CHARSET;
return FtcChkDocFfn(doc, pffn);
}
EXPORT int OpusX64HpsFromFontSize(sz)
@@ -1067,6 +1090,22 @@ int cchMax;
bltbyte(szFont, sz, min(CchSz(szFont), cchMax));
sz[cchMax - 1] = '\0';
}
EXPORT int OpusX64CharsetFromFtc(doc, ftc)
int doc;
int ftc;
{
int ibst;
struct FFN *pffn;
if (doc == docNil || ftc == wNinch)
return ANSI_CHARSET;
ibst = IbstFontFromFtcDoc(ftc, doc);
if (ibst == iNil)
return ANSI_CHARSET;
pffn = (struct FFN *)PstFromSttb(vhsttbFont, ibst);
return (unsigned char)ChsPffn(pffn);
}
#endif
+24
View File
@@ -96,6 +96,8 @@ extern struct FTI vfti;
extern char (**vhgrpchr)[];
extern int vbchrMac;
extern struct STTB **vhsttbFont;
extern unsigned int OpusUnicodeScalarAt();
extern int OpusQueueUnicodeWmChar();
#endif
extern struct FCB **mpfnhfcb[];
extern int vfnPreload;
@@ -1829,6 +1831,17 @@ LONG lParam;
pffn = (struct FFN *)PstFromSttb(vhsttbFont, ibst);
return (LRESULT)(unsigned char)ChsPffn(pffn);
}
case 105:
{
extern int vOpusPdfExportStage;
return (LRESULT)vOpusPdfExportStage;
}
case 106:
return (LRESULT)OpusUnicodeScalarAt(
selCur.doc, (CP)lParam);
case 107:
return (LRESULT)OpusQueueUnicodeWmChar(
hwnd, (unsigned int)lParam);
}
return (LRESULT) -1;
#endif
@@ -2496,6 +2509,17 @@ LPMSG lpmsg;
wm = lpmsg->message;
kc = lpmsg->wParam; /* key code or char */
#ifdef OPUS_X64
/* Unicode window classes deliver non-ANSI keyboard layouts as UTF-16
WM_CHAR units. Queue those through the compatibility layer before the
legacy insertion loop truncates the character to one byte. */
if (wm == WM_CHAR && !vfInsertMode && (unsigned int)kc >= 0x100)
{
OpusQueueUnicodeWmChar(lpmsg->hwnd, (unsigned int)kc);
return fTrue;
}
#endif
#ifdef COMING_SOON_TO_A_WORD_PROCESSOR_NEAR_YOU
+9 -2
View File
@@ -143,14 +143,21 @@ int CchCurSzPathNat(char* path, const int drive) {
if (path == nullptr) {
return 0;
}
/* The original ABI requires callers to provide exactly 67 bytes. Do not
copy a modern MAX_PATH-sized current directory into that legacy buffer. */
constexpr int kLegacyPathCapacity = 67;
char current[MAX_PATH]{};
if (_getdcwd(drive, current, static_cast<int>(sizeof(current))) ==
nullptr) {
path[0] = '\0';
return 0;
}
std::strcpy(path, current);
std::size_t length = std::strlen(path);
std::size_t length = std::strlen(current);
if (length + 2 > kLegacyPathCapacity) {
path[0] = '\0';
return 0;
}
std::memcpy(path, current, length + 1);
if (length == 2 && path[1] == ':') {
path[length++] = '\\';
path[length] = '\0';
+6
View File
@@ -137,6 +137,12 @@ constexpr UINT kPromptMessages[] = {
extern "C" {
LRESULT CALLBACK NatAppWndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
if (m == kWmDdeInitiate) {
// The legacy DDE server exposes document contents and macro commands
// to any process in the desktop session. The modern port does not
// accept inbound DDE conversations.
return 0;
}
return Dispatch(h, m, w, l, AppWndProc, kAppMessages);
}
LRESULT CALLBACK NatMwdWndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
File diff suppressed because it is too large Load Diff
+95 -4
View File
@@ -1,15 +1,37 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <climits>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
extern "C" int OpusModernDocxToRtfFile(const char*, const char*);
extern "C" int OpusModernDocxToTextFile(const char*, const char*);
extern "C" int OpusModernRtfFileToDocx(const char*, const char*);
extern "C" int OpusModernRtfFileToPdf(const char*, const char*);
extern "C" int OpusModernBindPendingDocxUnicode(int);
extern "C" unsigned int OpusUnicodeScalarAt(int, long);
extern "C" int OpusPdfSnapshotBegin(int, int, int, int, int, int);
extern "C" int OpusPdfSnapshotAddParagraph(int, int, int, int, int, int,
int, int, int, int, int);
extern "C" int OpusPdfSnapshotAddRun(const char*, int, const char*, int,
int, int, int, int, int, int, int, int);
std::string read_file(const std::string& path) {
std::ifstream input(path, std::ios::binary);
return {std::istreambuf_iterator<char>(input), {}};
}
int main(const int argument_count, char** arguments) {
if (argument_count == 4 && std::strcmp(arguments[1], "--text") == 0) {
const bool converted =
OpusModernDocxToTextFile(arguments[2], arguments[3]) != 0;
std::cout << "DOCX text import "
<< (converted ? "passed" : "failed") << '\n';
return converted ? 0 : 4;
}
if (argument_count == 3) {
const bool converted =
OpusModernDocxToRtfFile(arguments[1], arguments[2]) != 0;
@@ -32,6 +54,9 @@ int main(const int argument_count, char** arguments) {
const std::string rtf = base + ".rtf";
const std::string docx = base + ".docx";
const std::string roundtrip = base + ".roundtrip.rtf";
const std::string imported_text = base + ".unicode.txt";
const std::string oversized = base + ".oversized.rtf";
const std::string preserved = base + ".preserved.pdf";
char requested_pdf[32768]{};
const DWORD requested_pdf_length = GetEnvironmentVariableA(
"WORD1_TEST_KEEP_PDF", requested_pdf,
@@ -45,12 +70,63 @@ int main(const int argument_count, char** arguments) {
output << "{\\rtf1\\ansi{\\fonttbl{\\f0 Arial;}}"
"{\\colortbl;\\red255\\green0\\blue0;}"
"\\f0\\fs24 Plain {\\b Bold} {\\i Italic} "
"{\\ul Underline} {\\cf1 Red}\\par Second paragraph}";
"{\\ul Underline} {\\cf1 Red} "
"{\\lang1049 \\u1055?\\u1088?\\u1080?\\u1074?\\u1077?\\u1090?}"
" {\\lang1032 \\u915?\\u949?\\u953?\\u940?}"
" {\\lang1025 \\u1605?\\u1585?\\u1581?\\u1576?\\u1575?}"
" {\\lang1041 \\u12371?\\u12435?\\u12395?\\u12385?\\u12399?}"
"\\par Second paragraph}";
}
const bool written = OpusModernRtfFileToDocx(rtf.c_str(), docx.c_str()) != 0;
const bool read = written &&
OpusModernDocxToRtfFile(docx.c_str(), roundtrip.c_str()) != 0;
const bool unicode_imported = written &&
OpusModernDocxToTextFile(docx.c_str(), imported_text.c_str()) != 0 &&
OpusModernBindPendingDocxUnicode(42) != 0;
bool cyrillic_sidecar = false;
bool greek_sidecar = false;
bool arabic_sidecar = false;
bool japanese_sidecar = false;
if (unicode_imported) {
const std::string imported_bytes = read_file(imported_text);
for (long cp = 0; cp < static_cast<long>(imported_bytes.size()); ++cp) {
const unsigned int scalar = OpusUnicodeScalarAt(42, cp);
if (scalar == 1055) cyrillic_sidecar = true;
if (scalar == 915) greek_sidecar = true;
if (scalar == 1605) arabic_sidecar = true;
if (scalar == 12371) japanese_sidecar = true;
}
}
const bool pdf_written = OpusModernRtfFileToPdf(rtf.c_str(), pdf.c_str()) != 0;
{
std::ofstream output(preserved, std::ios::binary);
output << "ORIGINAL";
}
HANDLE oversized_file = CreateFileA(
oversized.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY, nullptr);
bool oversized_created = oversized_file != INVALID_HANDLE_VALUE;
if (oversized_created) {
LARGE_INTEGER hostile_size{};
hostile_size.QuadPart = 64ll * 1024ll * 1024ll + 1;
oversized_created = SetFilePointerEx(oversized_file, hostile_size,
nullptr, FILE_BEGIN) &&
SetEndOfFile(oversized_file);
CloseHandle(oversized_file);
}
const bool oversized_rejected = oversized_created &&
OpusModernRtfFileToPdf(oversized.c_str(), preserved.c_str()) == 0 &&
read_file(preserved) == "ORIGINAL";
const bool invalid_snapshot_rejected =
OpusPdfSnapshotBegin(INT_MAX, INT_MAX, INT_MAX, INT_MAX,
INT_MAX, INT_MAX) == 0 &&
OpusPdfSnapshotBegin(12240, 15840, 1440, 1440, 1440, 1440) != 0 &&
OpusPdfSnapshotAddParagraph(0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0) != 0 &&
OpusPdfSnapshotAddParagraph(0, 0, 0, 0, 0, 0, -240,
0, 0, 0, 0) != 0 &&
OpusPdfSnapshotAddRun("x", INT_MAX, "Arial", 20,
0, 0, 0, 0, 0, 0, 0, 0) == 0;
std::string result;
if (read) {
std::ifstream input(roundtrip, std::ios::binary);
@@ -64,21 +140,36 @@ int main(const int argument_count, char** arguments) {
DeleteFileA(rtf.c_str());
DeleteFileA(docx.c_str());
DeleteFileA(roundtrip.c_str());
DeleteFileA(imported_text.c_str());
DeleteFileA(oversized.c_str());
DeleteFileA(preserved.c_str());
if (!keep_pdf) DeleteFileA(pdf.c_str());
if (!written || !read || result.find("Bold") == std::string::npos ||
if (!written || !read || !unicode_imported || !cyrillic_sidecar ||
!greek_sidecar || !arabic_sidecar || !japanese_sidecar ||
result.find("Bold") == std::string::npos ||
result.find("Second paragraph") == std::string::npos ||
result.find("\\u1055") == std::string::npos ||
result.find("\\u915") == std::string::npos ||
result.find("\\u1605") == std::string::npos ||
result.find("\\u12371") == std::string::npos ||
result.find("\\b") == std::string::npos ||
result.find("\\cf1") == std::string::npos || !pdf_written ||
!pdf_data.starts_with("%PDF-") ||
pdf_data.find("Plain") == std::string::npos ||
pdf_data.find("Bold") == std::string::npos ||
pdf_data.find("Second paragraph") == std::string::npos ||
pdf_data.find("/Encoding /Identity-H") == std::string::npos ||
pdf_data.find("/ToUnicode") == std::string::npos ||
pdf_data.find("/Helvetica-Bold") == std::string::npos ||
pdf_data.find("xref") == std::string::npos ||
pdf_data.find("%%EOF") == std::string::npos) {
pdf_data.find("%%EOF") == std::string::npos ||
!oversized_rejected || !invalid_snapshot_rejected) {
std::cerr << "DOCX round trip failed: write=" << written
<< " read=" << read << " pdf=" << pdf_written
<< " bytes=" << result.size() << '\n';
<< " bytes=" << result.size()
<< " oversizedRejected=" << oversized_rejected
<< " invalidSnapshotRejected="
<< invalid_snapshot_rejected << '\n';
return 2;
}
std::cout << "DOCX round trip passed (" << result.size() << " RTF bytes)\n";
@@ -374,28 +374,6 @@ LONG WINAPI ObserveVectoredException(EXCEPTION_POINTERS* exception) {
} // namespace
extern "C" void OpusX64TraceRibbon(const char* stage, int message, int tmc,
int first_value, int second_value,
long cp_first, long cp_limit,
int insertion) {
char trace_path[MAX_PATH] = {};
BuildDiagnosticPath("WORD1-ribbon.txt", trace_path, sizeof(trace_path));
HANDLE file = CreateFileA(trace_path, FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE) {
return;
}
char line[384] = {};
std::snprintf(line, sizeof(line),
"%llu %s msg=%d tmc=%d a=%d b=%d sel=%ld,%ld ins=%d\r\n",
static_cast<unsigned long long>(GetTickCount64()),
stage != nullptr ? stage : "", message, tmc, first_value,
second_value, cp_first, cp_limit, insertion);
WriteCrashText(file, line);
CloseHandle(file);
}
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous,
PWSTR command_line, int show_command) {
if ((command_line != nullptr &&
@@ -404,6 +382,15 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous,
return 0;
}
/* Exclude the current directory and PATH from DLL resolution. The app
directory remains available for intentionally deployed components and
system DLLs are resolved only from System32. */
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_APPLICATION_DIR |
LOAD_LIBRARY_SEARCH_SYSTEM32 |
LOAD_LIBRARY_SEARCH_USER_DIRS);
SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE |
BASE_SEARCH_PATH_PERMANENT);
SetUnhandledExceptionFilter(WriteCrashStack);
AddVectoredExceptionHandler(1, ObserveVectoredException);
_RTC_SetErrorFuncW(WriteRtcFailure);
+152 -36
View File
@@ -1,11 +1,13 @@
#include "opus_x64_compat.h"
#include "opus_x64_heap.h"
#include <commdlg.h>
#include <objbase.h>
extern "C" {
#include "dac.h"
}
#include <algorithm>
#include <atomic>
#include <cctype>
#include <cstdio>
#include <cstdint>
@@ -21,10 +23,17 @@ extern std::uintptr_t wRefDlgCur;
extern HWND vhWndMsgBoxParent;
void GetCabSz(void**, char*, std::uint16_t, std::uint16_t);
int FSetCabSz(void**, const char*, std::uint16_t);
void OpusX64TraceRibbon(const char*, int, int, int, int, long, long, int);
int OpusModernPathIsDocx(const char*);
int OpusModernDocxToTextFile(const char*, const char*);
int OpusModernRtfFileToDocx(const char*, const char*);
int OpusSaveDocumentAsDocx(int, const char*);
}
extern "C" void OpusX64TraceRibbon(const char*, int, int, int, int,
long, long, int) {
/* Ribbon tracing used during the bring-up of the x64 port. Keep the ABI
for original call sites without writing diagnostic files in product or
test processes. */
}
/*
@@ -131,6 +140,7 @@ struct Win95SaveAlias {
Win95SaveAlias g_win95_save_alias;
std::unordered_map<std::string, std::string> g_win95_saved_aliases;
std::string g_win95_staging_directory;
struct Win95AliasCleanup {
~Win95AliasCleanup() {
@@ -141,6 +151,9 @@ struct Win95AliasCleanup {
for (const auto& saved : g_win95_saved_aliases) {
DeleteFileA(saved.first.c_str());
}
if (!g_win95_staging_directory.empty()) {
RemoveDirectoryA(g_win95_staging_directory.c_str());
}
}
};
@@ -154,43 +167,131 @@ std::string win95_alias_key(std::string path) {
return path;
}
bool safe_dialog_file_path(const std::string& path, const bool must_exist) {
if (path.empty() || path.size() >= 32760 ||
path.starts_with(R"(\\.\)") ||
path.starts_with(R"(\\?\GLOBALROOT\)")) return false;
for (std::size_t index = 0; index < path.size(); ++index) {
if (path[index] == ':' && index != 1) return false;
}
const DWORD attributes = GetFileAttributesA(path.c_str());
if (attributes != INVALID_FILE_ATTRIBUTES)
return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
return !must_exist && (GetLastError() == ERROR_FILE_NOT_FOUND ||
GetLastError() == ERROR_PATH_NOT_FOUND);
}
bool import_file_within_limit(const std::string& path) {
if (!safe_dialog_file_path(path, true)) return false;
WIN32_FILE_ATTRIBUTE_DATA attributes{};
if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard,
&attributes)) return false;
constexpr ULONGLONG kMaximumImportBytes = 256ull * 1024ull * 1024ull;
const ULONGLONG size =
(static_cast<ULONGLONG>(attributes.nFileSizeHigh) << 32) |
attributes.nFileSizeLow;
return size <= kMaximumImportBytes;
}
bool make_win95_staging_path(std::string& path,
const char* desired_extension = ".DOC") {
char module_path[32768]{};
const DWORD module_length = GetModuleFileNameA(
nullptr, module_path, static_cast<DWORD>(std::size(module_path)));
if (module_length == 0 || module_length >= std::size(module_path)) {
return false;
}
char* separator = std::strrchr(module_path, '\\');
if (separator == nullptr) {
return false;
}
*separator = '\0';
std::string directory = std::string(module_path) + "\\W95TEMP";
if (!CreateDirectoryA(directory.c_str(), nullptr) &&
GetLastError() != ERROR_ALREADY_EXISTS) {
return false;
}
for (int attempt = 0; attempt < 32; ++attempt) {
char temporary[MAX_PATH]{};
if (GetTempFileNameA(directory.c_str(), "W95", 0, temporary) == 0) {
if (_stricmp(desired_extension, ".DOC") != 0 &&
_stricmp(desired_extension, ".TXT") != 0) return false;
if (g_win95_staging_directory.empty()) {
char temporary_root[32768]{};
const DWORD root_length = GetTempPathA(
static_cast<DWORD>(std::size(temporary_root)), temporary_root);
if (root_length == 0 || root_length >= std::size(temporary_root))
return false;
for (int attempt = 0; attempt < 32; ++attempt) {
GUID identifier{};
if (FAILED(CoCreateGuid(&identifier))) return false;
char leaf[9]{};
_snprintf_s(leaf, std::size(leaf), _TRUNCATE, "W%07lX",
identifier.Data1 & 0x0ffffffful);
const std::string candidate = std::string(temporary_root) + leaf;
/* The original normalizer requires DOS 8.3-safe path components
even though the native file APIs support long names. */
if (candidate.size() >= 52) return false;
if (CreateDirectoryA(candidate.c_str(), nullptr)) {
const DWORD attributes = GetFileAttributesA(candidate.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES ||
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
RemoveDirectoryA(candidate.c_str());
return false;
}
g_win95_staging_directory = candidate;
break;
}
if (GetLastError() != ERROR_ALREADY_EXISTS) return false;
}
DeleteFileA(temporary);
char* existing_extension = std::strrchr(temporary, '.');
if (existing_extension != nullptr) {
lstrcpyA(existing_extension, desired_extension);
}
if (GetFileAttributesA(temporary) == INVALID_FILE_ATTRIBUTES &&
std::strlen(temporary) < 120) {
path = temporary;
if (g_win95_staging_directory.empty()) return false;
}
static std::atomic<unsigned long> sequence{0};
for (int attempt = 0; attempt < 32; ++attempt) {
char leaf[18]{};
_snprintf_s(leaf, std::size(leaf), _TRUNCATE, "\\D%07lX%s",
++sequence & 0x0ffffffful, desired_extension);
const std::string candidate = g_win95_staging_directory + leaf;
if (candidate.size() >= 120) return false;
const DWORD attributes = GetFileAttributesA(candidate.c_str());
const DWORD attribute_error = GetLastError();
if (attributes == INVALID_FILE_ATTRIBUTES &&
(attribute_error == ERROR_FILE_NOT_FOUND ||
attribute_error == ERROR_PATH_NOT_FOUND)) {
path = candidate;
return true;
}
}
return false;
}
bool atomic_copy_file(const std::string& source, const std::string& target) {
if (source.empty() || target.empty()) return false;
static std::atomic<unsigned long> sequence{0};
std::string temporary;
bool copied = false;
for (int attempt = 0; attempt < 64; ++attempt) {
temporary = target + ".word1tmp-" +
std::to_string(GetCurrentProcessId()) + "-" +
std::to_string(++sequence);
if (CopyFileA(source.c_str(), temporary.c_str(), TRUE)) {
copied = true;
break;
}
if (GetLastError() != ERROR_FILE_EXISTS &&
GetLastError() != ERROR_ALREADY_EXISTS) return false;
}
if (!copied) return false;
HANDLE file = CreateFileA(temporary.c_str(), GENERIC_WRITE,
FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, nullptr);
const bool flushed = file != INVALID_HANDLE_VALUE &&
FlushFileBuffers(file) != FALSE;
if (file != INVALID_HANDLE_VALUE) CloseHandle(file);
if (!flushed) {
DeleteFileA(temporary.c_str());
return false;
}
const DWORD attributes = GetFileAttributesA(target.c_str());
bool committed = false;
if (attributes != INVALID_FILE_ATTRIBUTES &&
(attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
committed = ReplaceFileA(target.c_str(), temporary.c_str(), nullptr,
REPLACEFILE_WRITE_THROUGH, nullptr, nullptr) != FALSE;
if (!committed) {
committed = MoveFileExA(temporary.c_str(), target.c_str(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != FALSE;
}
} else if (attributes == INVALID_FILE_ATTRIBUTES &&
GetLastError() == ERROR_FILE_NOT_FOUND) {
committed = MoveFileExA(temporary.c_str(), target.c_str(),
MOVEFILE_WRITE_THROUGH) != FALSE;
}
if (!committed) DeleteFileA(temporary.c_str());
return committed;
}
std::string counted_path(const unsigned char* st_file) {
if (st_file == nullptr || st_file[0] == 0 || st_file[0] >= 120) {
return {};
@@ -2187,9 +2288,13 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
}
char test_path[32768]{};
#ifdef OPUS_TEST_HOOKS
const DWORD test_path_length = GetEnvironmentVariableA(
"WORD1_TEST_FILE_DIALOG_PATH", test_path,
static_cast<DWORD>(std::size(test_path)));
#else
const DWORD test_path_length = 0;
#endif
BOOL accepted = FALSE;
if (test_path_length > 0 && test_path_length < std::size(test_path)) {
lstrcpynA(file_buffer.data(), test_path,
@@ -2197,7 +2302,9 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
if (saving && OpusModernPathIsDocx(test_path)) {
file_dialog.nFilterIndex = 2;
}
#ifdef OPUS_TEST_HOOKS
SetEnvironmentVariableA("WORD1_TEST_FILE_DIALOG_PATH", nullptr);
#endif
accepted = TRUE;
} else {
accepted = opening ? GetOpenFileNameA(&file_dialog) :
@@ -2232,6 +2339,16 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
".docx");
}
}
if ((opening && !import_file_within_limit(selected_path)) ||
(saving && !safe_dialog_file_path(selected_path, false))) {
MessageBoxA(owner,
opening ?
"This document is not a regular file or is too large to open safely." :
"This is not a safe document file name.",
opening ? "Open" : "Save As",
MB_OK | MB_ICONEXCLAMATION);
continue;
}
std::string legacy_path;
const bool docx = OpusModernPathIsDocx(selected_path.c_str()) != 0;
if (!make_win95_staging_path(legacy_path,
@@ -2411,7 +2528,7 @@ int OpusWin95DisplayAlias(unsigned char* const st_file) {
}
int OpusFinishWin95SaveAlias(const unsigned char* st_file,
const int success) {
const int success, const int doc) {
const std::string path = counted_path(st_file);
if (path.empty()) {
return !g_win95_save_alias.active;
@@ -2425,11 +2542,10 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
if (copied) {
copied = OpusModernPathIsDocx(
g_win95_save_alias.selected_path.c_str()) ?
OpusModernRtfFileToDocx(
g_win95_save_alias.legacy_path.c_str(),
g_win95_save_alias.selected_path.c_str()) != 0 :
CopyFileA(g_win95_save_alias.legacy_path.c_str(),
g_win95_save_alias.selected_path.c_str(), FALSE) != 0;
OpusSaveDocumentAsDocx(
doc, g_win95_save_alias.selected_path.c_str()) != 0 :
atomic_copy_file(g_win95_save_alias.legacy_path,
g_win95_save_alias.selected_path);
}
if (!success) {
DeleteFileA(g_win95_save_alias.legacy_path.c_str());
@@ -2449,8 +2565,8 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
return false;
}
return OpusModernPathIsDocx(saved->second.c_str()) ?
OpusModernRtfFileToDocx(path.c_str(), saved->second.c_str()) != 0 :
CopyFileA(path.c_str(), saved->second.c_str(), FALSE) != 0;
OpusSaveDocumentAsDocx(doc, saved->second.c_str()) != 0 :
atomic_copy_file(path, saved->second);
}
int OpusWin95SaveAliasRequiresRtf(const unsigned char* st_file) {
+261 -2
View File
@@ -1,10 +1,12 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <imm.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <deque>
#include <string>
#include <vector>
@@ -36,6 +38,14 @@ extern "C" int OpusAdjustWin95HorizontalMargin(
HWND ruler, int left_margin, int delta_pixels);
extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler);
extern "C" int OpusExportCurrentDocumentPdf(void);
extern "C" int OpusGetUnicodeSelection(
int* doc, long* cp_first, long* cp_lim);
extern "C" int OpusUnicodeLegacyByteForScalar(unsigned int scalar);
extern "C" int OpusUnicodeSetScalar(
int doc, long cp, unsigned int scalar);
extern "C" int OpusUnicodeSetInputLanguage(const char* language);
extern "C" int OpusUnicodeGetInputLanguage(char* language, int capacity);
extern "C" int OpusQueueUnicodeWmChar(HWND pane, unsigned int code_unit);
namespace {
@@ -57,7 +67,10 @@ constexpr UINT kCmdToggleStandardToolbar = 0x7101;
constexpr UINT kCmdToggleFormattingToolbar = 0x7102;
constexpr UINT kCmdExportPdf = 0x7103;
constexpr UINT kCmdTextColorBase = 0x7200;
constexpr UINT kCmdLanguageBase = 0x7300;
constexpr UINT_PTR kSyncTimer = 0x951;
constexpr UINT kWmCommitUnicodeScalar = WM_APP + 0x452;
constexpr std::size_t kMaxPendingUnicodeInput = 0xffff;
constexpr wchar_t kOriginalPaneProcProperty[] = L"OpusWord95OriginalPaneProc";
enum class FormatGlyph {
@@ -168,8 +181,47 @@ struct ToolbarState {
HBRUSH g_menu_brush = nullptr;
HMENU g_table_menu = nullptr;
HMENU g_toolbars_menu = nullptr;
HMENU g_language_menu = nullptr;
WNDPROC g_original_app_proc = nullptr;
bool g_word95_page_view_active = false;
wchar_t g_pending_high_surrogate = 0;
struct LanguageChoice {
UINT command;
const wchar_t* label;
const char* tag;
};
struct PendingUnicodeInput {
int doc = -1;
long cp = 0;
std::uint32_t scalar = 0;
int retries = 0;
};
std::deque<PendingUnicodeInput> g_pending_unicode_inputs;
PendingUnicodeInput g_active_unicode_input;
bool g_unicode_input_active = false;
constexpr std::array<LanguageChoice, 17> kLanguageChoices{{
{kCmdLanguageBase, L"&Automatic (Keyboard)", "auto"},
{kCmdLanguageBase + 1, L"&English (United States)", "en-US"},
{kCmdLanguageBase + 2, L"&Spanish", "es-ES"},
{kCmdLanguageBase + 3, L"&French", "fr-FR"},
{kCmdLanguageBase + 4, L"&German", "de-DE"},
{kCmdLanguageBase + 5, L"&Polish / Central European", "pl-PL"},
{kCmdLanguageBase + 6, L"&Greek", "el-GR"},
{kCmdLanguageBase + 7, L"&Russian / Cyrillic", "ru-RU"},
{kCmdLanguageBase + 8, L"&Turkish", "tr-TR"},
{kCmdLanguageBase + 9, L"&Hebrew", "he-IL"},
{kCmdLanguageBase + 10, L"&Arabic", "ar-SA"},
{kCmdLanguageBase + 11, L"&Thai", "th-TH"},
{kCmdLanguageBase + 12, L"&Vietnamese", "vi-VN"},
{kCmdLanguageBase + 13, L"&Japanese", "ja-JP"},
{kCmdLanguageBase + 14, L"Chinese (&Simplified)", "zh-CN"},
{kCmdLanguageBase + 15, L"Chinese (&Traditional)", "zh-TW"},
{kCmdLanguageBase + 16, L"&Korean", "ko-KR"},
}};
struct VerticalRulerDragState {
HWND pane = nullptr;
@@ -227,7 +279,8 @@ int scale(HWND window, int value) {
void set_window_classic(HWND window) {
using SetWindowThemeProc = HRESULT(WINAPI*)(HWND, LPCWSTR, LPCWSTR);
static HMODULE theme_module = LoadLibraryW(L"uxtheme.dll");
static HMODULE theme_module = LoadLibraryExW(
L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
static const auto set_theme = theme_module != nullptr ?
reinterpret_cast<SetWindowThemeProc>(
GetProcAddress(theme_module, "SetWindowTheme")) : nullptr;
@@ -346,6 +399,33 @@ void configure_word95_menus(HWND window) {
MF_BYPOSITION | MF_POPUP | MF_STRING,
reinterpret_cast<UINT_PTR>(tools), L"&Tools");
}
const int tools_index = find_named(L"Tools");
HMENU tools_menu = tools_index >= 0 ? GetSubMenu(root, tools_index) :
(utilities_index >= 0 ? GetSubMenu(root, utilities_index) : nullptr);
if (g_language_menu == nullptr || !IsMenu(g_language_menu)) {
g_language_menu = CreatePopupMenu();
if (g_language_menu != nullptr) {
for (const LanguageChoice& choice : kLanguageChoices) {
AppendMenuW(g_language_menu, MF_STRING, choice.command,
choice.label);
}
}
}
if (tools_menu != nullptr && g_language_menu != nullptr) {
bool present = false;
for (int index = 0; index < GetMenuItemCount(tools_menu); ++index) {
if (GetSubMenu(tools_menu, index) == g_language_menu) {
present = true;
break;
}
}
if (!present) {
AppendMenuW(tools_menu, MF_SEPARATOR, 0, nullptr);
AppendMenuW(tools_menu, MF_POPUP | MF_STRING,
reinterpret_cast<UINT_PTR>(g_language_menu),
L"&Language");
}
}
if (g_table_menu == nullptr || !IsMenu(g_table_menu)) {
g_table_menu = CreatePopupMenu();
@@ -454,7 +534,8 @@ void configure_word95_menus(HWND window) {
void apply_caption_colors(HWND window) {
using DwmSetWindowAttributeProc = HRESULT(WINAPI*)(HWND, DWORD,
LPCVOID, DWORD);
HMODULE module = LoadLibraryW(L"dwmapi.dll");
HMODULE module = LoadLibraryExW(
L"dwmapi.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (module == nullptr) {
return;
}
@@ -2311,9 +2392,153 @@ void show_document_context_menu(HWND pane, POINT screen_point) {
}
}
void update_language_menu_check() {
if (g_language_menu == nullptr) return;
char selected[32]{};
OpusUnicodeGetInputLanguage(selected, static_cast<int>(std::size(selected)));
UINT command = kCmdLanguageBase;
for (const LanguageChoice& choice : kLanguageChoices) {
if (_stricmp(selected, choice.tag) == 0) {
command = choice.command;
break;
}
}
CheckMenuRadioItem(g_language_menu, kCmdLanguageBase,
kCmdLanguageBase +
static_cast<UINT>(kLanguageChoices.size() - 1),
command, MF_BYCOMMAND);
}
bool start_next_unicode_input(HWND pane) {
if (g_unicode_input_active || g_pending_unicode_inputs.empty())
return true;
int doc_before = -1;
long cp_before = 0;
long cp_lim_before = 0;
if (!OpusGetUnicodeSelection(&doc_before, &cp_before, &cp_lim_before)) {
g_pending_unicode_inputs.pop_front();
return false;
}
g_active_unicode_input = g_pending_unicode_inputs.front();
g_pending_unicode_inputs.pop_front();
g_active_unicode_input.doc = doc_before;
g_active_unicode_input.cp = cp_before;
g_active_unicode_input.retries = 0;
g_unicode_input_active = true;
const WPARAM legacy = g_active_unicode_input.scalar < 0x20 ?
static_cast<WPARAM>(g_active_unicode_input.scalar) :
static_cast<WPARAM>(OpusUnicodeLegacyByteForScalar(
g_active_unicode_input.scalar) & 0xff);
if (!PostMessageW(pane, WM_CHAR, legacy, 1) ||
!PostMessageW(pane, WM_LBUTTONUP, 0, 0) ||
!PostMessageW(pane, kWmCommitUnicodeScalar, 0, 0)) {
g_unicode_input_active = false;
return false;
}
return true;
}
bool queue_unicode_input(HWND pane, const std::uint32_t scalar) {
if (pane == nullptr || scalar == 0 || scalar > 0x10ffff ||
(scalar >= 0xd800 && scalar <= 0xdfff)) return false;
if (g_pending_unicode_inputs.size() >= kMaxPendingUnicodeInput)
return false;
try {
g_pending_unicode_inputs.push_back({-1, 0, scalar, 0});
} catch (...) {
return false;
}
return start_next_unicode_input(pane);
}
bool insert_unicode_scalar(HWND pane, const std::uint32_t scalar) {
return scalar >= 0x20 && queue_unicode_input(pane, scalar);
}
void insert_unicode_text(HWND pane, const std::wstring& text) {
for (std::size_t index = 0; index < text.size();) {
std::uint32_t scalar = static_cast<std::uint16_t>(text[index++]);
if (scalar >= 0xd800 && scalar <= 0xdbff && index < text.size()) {
const std::uint32_t low = static_cast<std::uint16_t>(text[index]);
if (low >= 0xdc00 && low <= 0xdfff) {
++index;
scalar = 0x10000 + ((scalar - 0xd800) << 10) +
(low - 0xdc00);
}
}
if (scalar == L'\r' || scalar == L'\n' || scalar == L'\t')
queue_unicode_input(pane, scalar);
else
insert_unicode_scalar(pane, scalar);
}
}
LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
WPARAM w_param, LPARAM l_param) {
WNDPROC original = original_pane_proc(pane);
if (message == kWmCommitUnicodeScalar) {
if (g_unicode_input_active) {
int doc_after = -1;
long cp_after = 0;
long cp_lim_after = 0;
if (OpusGetUnicodeSelection(
&doc_after, &cp_after, &cp_lim_after) &&
doc_after == g_active_unicode_input.doc &&
cp_after > g_active_unicode_input.cp) {
if (g_active_unicode_input.scalar >= 0x20)
OpusUnicodeSetScalar(
g_active_unicode_input.doc,
g_active_unicode_input.cp,
g_active_unicode_input.scalar);
g_unicode_input_active = false;
InvalidateRect(pane, nullptr, FALSE);
start_next_unicode_input(pane);
} else if (doc_after == g_active_unicode_input.doc &&
g_active_unicode_input.retries++ < 8) {
PostMessageW(pane, WM_LBUTTONUP, 0, 0);
PostMessageW(pane, kWmCommitUnicodeScalar, 0, 0);
} else {
g_unicode_input_active = false;
start_next_unicode_input(pane);
}
}
return 0;
}
if (message == WM_UNICHAR) {
if (w_param == UNICODE_NOCHAR) return TRUE;
insert_unicode_scalar(pane, static_cast<std::uint32_t>(w_param));
return 0;
}
/* The original message loop marks already-translated byte input by
setting bit 15 (0x80xx). That is an internal Word flag, not a Unicode
code point, and must reach the legacy pane procedure unchanged. */
if (message == WM_CHAR && (w_param & 0xff00) != 0x8000 &&
w_param >= 0x80) {
OpusQueueUnicodeWmChar(
pane, static_cast<unsigned int>(w_param));
return 0;
}
if (message == WM_IME_COMPOSITION &&
(l_param & GCS_RESULTSTR) != 0) {
HIMC context = ImmGetContext(pane);
if (context != nullptr) {
const LONG byte_count = ImmGetCompositionStringW(
context, GCS_RESULTSTR, nullptr, 0);
if (byte_count > 0 && byte_count <= 1024 * 1024 &&
(byte_count % sizeof(wchar_t)) == 0) {
std::wstring result(
static_cast<std::size_t>(byte_count) / sizeof(wchar_t),
L'\0');
if (ImmGetCompositionStringW(context, GCS_RESULTSTR,
&result[0], static_cast<DWORD>(byte_count)) ==
byte_count) {
insert_unicode_text(pane, result);
}
}
ImmReleaseContext(pane, context);
return 0;
}
}
if (message == WM_MOUSEWHEEL) {
if (g_document_wheel.pane != pane) {
g_document_wheel = {};
@@ -2507,6 +2732,9 @@ LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
if (g_document_wheel.pane == pane) {
g_document_wheel = {};
}
g_pending_unicode_inputs.clear();
g_unicode_input_active = false;
g_pending_high_surrogate = 0;
clear_page_snapshots(pane);
RemovePropW(pane, kOriginalPaneProcProperty);
if (original != nullptr) {
@@ -2588,6 +2816,15 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
OpusExportCurrentDocumentPdf();
return 0;
}
if (command >= kCmdLanguageBase &&
command < kCmdLanguageBase + kLanguageChoices.size()) {
const LanguageChoice& choice =
kLanguageChoices[command - kCmdLanguageBase];
OpusUnicodeSetInputLanguage(choice.tag);
update_language_menu_check();
DrawMenuBar(app);
return 0;
}
if (command == kCmdToggleStandardToolbar) {
toggle_toolbar_row(app, toolbar, true);
return 0;
@@ -2597,6 +2834,7 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
return 0;
}
} else if (message == WM_INITMENUPOPUP) {
update_language_menu_check();
HWND toolbar = FindWindowExW(app, nullptr, kToolbarClass, nullptr);
ToolbarState* state = toolbar_state(toolbar);
if (state != nullptr) {
@@ -2864,6 +3102,27 @@ bool register_ruler_overlay_class() {
} // namespace
extern "C" int OpusQueueUnicodeWmChar(
HWND pane, const unsigned int code_unit) {
if (code_unit >= 0xd800 && code_unit <= 0xdbff) {
g_pending_high_surrogate = static_cast<wchar_t>(code_unit);
return TRUE;
}
std::uint32_t scalar = code_unit;
if (code_unit >= 0xdc00 && code_unit <= 0xdfff) {
if (g_pending_high_surrogate < 0xd800 ||
g_pending_high_surrogate > 0xdbff) {
g_pending_high_surrogate = 0;
return FALSE;
}
scalar = 0x10000 +
((static_cast<std::uint32_t>(g_pending_high_surrogate) -
0xd800) << 10) + (code_unit - 0xdc00);
}
g_pending_high_surrogate = 0;
return insert_unicode_scalar(pane, scalar);
}
extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler) {
HDC dc = GetDC(ruler);
if (dc != nullptr) {
+75 -5
View File
@@ -653,7 +653,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
std::cerr <<
"usage: opus_word1_ui_test WORD1.exe "
"[--typing|--interaction|--selection|--caret|--formatting|--color|"
"--font-typing|--clipboard|--about|--save-as|--pdf-export|"
"--font-typing|--unicode|--clipboard|--about|--save-as|--pdf-export|"
"--docx-open FILE|--docx-format-open FILE|"
"--docx-pdf-export FILE]\n";
return 1;
@@ -678,6 +678,9 @@ int wmain(const int argument_count, wchar_t** arguments) {
const bool font_typing_mode =
argument_count == 3 &&
std::wcscmp(arguments[2], L"--font-typing") == 0;
const bool unicode_mode =
argument_count == 3 &&
std::wcscmp(arguments[2], L"--unicode") == 0;
const bool about_mode =
argument_count == 3 &&
std::wcscmp(arguments[2], L"--about") == 0;
@@ -703,7 +706,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
formatted_docx_open_mode);
if (argument_count == 3 && !typing_mode && !interaction_mode &&
!selection_mode && !caret_mode && !formatting_mode && !color_mode &&
!font_typing_mode && !clipboard_mode && !about_mode &&
!font_typing_mode && !unicode_mode && !clipboard_mode && !about_mode &&
!save_as_mode && !pdf_export_mode) {
std::cerr << "unknown test mode\n";
return 1;
@@ -959,8 +962,10 @@ int wmain(const int argument_count, wchar_t** arguments) {
}
}
bool rich_pdf_valid = !docx_pdf_export_mode;
if (docx_pdf_export_mode &&
PostMessageW(main_window, kWmCommand, kExportPdf, 0)) {
DWORD_PTR export_result = 0;
if (docx_pdf_export_mode && SendMessageTimeoutW(
main_window, kWmCommand, kExportPdf, 0,
SMTO_ABORTIFHUNG | SMTO_BLOCK, 30000, &export_result)) {
const ULONGLONG pdf_deadline = GetTickCount64() + 15000;
do {
std::ifstream input(pdf_path, std::ios::binary);
@@ -1001,7 +1006,11 @@ int wmain(const int argument_count, wchar_t** arguments) {
<< "} bodyFont={index:" << body_font_index
<< ",charset:" << body_font_charset << "}\n";
if (docx_pdf_export_mode)
std::cerr << "richPdf=" << rich_pdf_valid << '\n';
std::cerr << "richPdf=" << rich_pdf_valid
<< " exportStage="
<< (pane != nullptr ? SendMessageW(
pane, kWmOpusX64QuerySelection, 105, 0) : -1)
<< '\n';
if (!keep_pdf && !pdf_path.empty()) DeleteFileA(pdf_path.c_str());
return 84;
}
@@ -1091,6 +1100,67 @@ int wmain(const int argument_count, wchar_t** arguments) {
CloseHandle(process.hProcess);
return 0;
}
if (unicode_mode) {
const HWND pane = find_descendant_by_class(main_window, L"OpusWwd");
DWORD ignored_process_id = 0;
const DWORD thread_id =
GetWindowThreadProcessId(main_window, &ignored_process_id);
if (pane == nullptr ||
!make_foreground_and_focus(main_window, pane, thread_id)) {
return fail(process, 87,
"Unicode test could not focus the document pane");
}
const LRESULT cp_first = SendMessageW(
pane, kWmOpusX64QuerySelection, 0, 0);
const std::array<std::uint32_t, 5> scalars{
0x041f, 0x03a9, 0x0645, 0x3053, 0x1f642};
for (std::size_t index = 0; index < scalars.size(); ++index) {
const std::uint32_t scalar = scalars[index];
bool accepted = false;
if (index == 0) {
DWORD_PTR result = 0;
accepted = SendMessageTimeoutW(
pane, WM_UNICHAR, static_cast<WPARAM>(scalar), 1,
SMTO_ABORTIFHUNG | SMTO_BLOCK, 3000, &result) != FALSE;
} else {
accepted = SendMessageW(
pane, kWmOpusX64QuerySelection, 107,
static_cast<LPARAM>(scalar)) != FALSE;
}
if (!accepted) {
return fail(process, 88,
"Unicode input message was not accepted");
}
}
Sleep(500);
const LRESULT cp_after = SendMessageW(
pane, kWmOpusX64QuerySelection, 0, 0);
bool scalars_preserved = cp_after ==
cp_first + static_cast<LRESULT>(scalars.size());
std::array<LRESULT, 5> actual_scalars{};
for (std::size_t index = 0;
index < scalars.size(); ++index) {
actual_scalars[index] = SendMessageW(
pane, kWmOpusX64QuerySelection, 106,
cp_first + static_cast<LRESULT>(index));
scalars_preserved = scalars_preserved &&
actual_scalars[index] == scalars[index];
}
if (!scalars_preserved) {
std::cerr << "Unicode input range=" << cp_first << "->"
<< cp_after << " scalars=";
for (const LRESULT actual : actual_scalars)
std::cerr << actual << ',';
std::cerr << '\n';
return fail(process, 89,
"Unicode code points were not preserved in the document");
}
TerminateProcess(process.hProcess, 0);
WaitForSingleObject(process.hProcess, 2000);
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
return 0;
}
if (font_typing_mode) {
const HWND pane = find_descendant_by_class(main_window, L"OpusWwd");
DWORD ignored_process_id = 0;
+99 -9
View File
@@ -29,14 +29,18 @@ static_assert(offsetof(NativeHandle, data) == 0);
constexpr int kPrcTokenNil = 0x3fff;
constexpr int kPrcTokenMac = kPrcTokenNil - 1;
constexpr int kCompactHandleMac = 0x3fff;
constexpr std::size_t kNativeHandleSlots = 131071;
constexpr std::size_t kSegmentSlots = 65536;
constexpr std::size_t kGuardBytes = 64;
constexpr std::size_t kMaxAllocationBytes = 256u * 1024u * 1024u;
constexpr unsigned char kGuardValue = 0xa5;
SRWLOCK prc_registry_lock = SRWLOCK_INIT;
SRWLOCK compact_handle_lock = SRWLOCK_INIT;
SRWLOCK native_handle_lock = SRWLOCK_INIT;
SRWLOCK segment_registry_lock = SRWLOCK_INIT;
void** prc_handles[kPrcTokenNil]{};
void** compact_handles[kCompactHandleMac + 1]{};
void** native_handles[kNativeHandleSlots]{};
std::array<NativeSegment, kSegmentSlots> native_segments{};
std::atomic_size_t heap_bytes_used{0};
@@ -47,6 +51,7 @@ std::atomic_size_t heap_bytes_used{0};
[[nodiscard]] constexpr bool guarded_size(
const std::size_t logical_size, std::size_t* result) noexcept {
if (logical_size > kMaxAllocationBytes) return false;
const std::size_t payload = payload_size(logical_size);
if (payload > (std::numeric_limits<std::size_t>::max)() - kGuardBytes) {
return false;
@@ -55,6 +60,75 @@ std::atomic_size_t heap_bytes_used{0};
return true;
}
[[nodiscard]] bool valid_native_handle(void** opaque_handle) noexcept {
if (opaque_handle == nullptr) return false;
void** const tombstone = reinterpret_cast<void**>(static_cast<uintptr_t>(1));
const std::size_t start =
(reinterpret_cast<uintptr_t>(opaque_handle) >> 4) % kNativeHandleSlots;
AcquireSRWLockShared(&native_handle_lock);
bool valid = false;
for (std::size_t probe = 0; probe < kNativeHandleSlots; ++probe) {
void** const candidate =
native_handles[(start + probe) % kNativeHandleSlots];
if (candidate == opaque_handle) {
valid = true;
break;
}
if (candidate == nullptr) break;
if (candidate == tombstone) continue;
}
ReleaseSRWLockShared(&native_handle_lock);
return valid;
}
[[nodiscard]] bool register_native_handle(void** opaque_handle) noexcept {
void** const tombstone = reinterpret_cast<void**>(static_cast<uintptr_t>(1));
const std::size_t start =
(reinterpret_cast<uintptr_t>(opaque_handle) >> 4) % kNativeHandleSlots;
AcquireSRWLockExclusive(&native_handle_lock);
std::size_t first_tombstone = kNativeHandleSlots;
for (std::size_t probe = 0; probe < kNativeHandleSlots; ++probe) {
const std::size_t index = (start + probe) % kNativeHandleSlots;
if (native_handles[index] == opaque_handle) {
ReleaseSRWLockExclusive(&native_handle_lock);
return true;
}
if (native_handles[index] == tombstone &&
first_tombstone == kNativeHandleSlots) first_tombstone = index;
if (native_handles[index] == nullptr) {
native_handles[first_tombstone == kNativeHandleSlots ?
index : first_tombstone] = opaque_handle;
ReleaseSRWLockExclusive(&native_handle_lock);
return true;
}
}
if (first_tombstone != kNativeHandleSlots) {
native_handles[first_tombstone] = opaque_handle;
ReleaseSRWLockExclusive(&native_handle_lock);
return true;
}
ReleaseSRWLockExclusive(&native_handle_lock);
return false;
}
[[nodiscard]] bool unregister_native_handle(void** opaque_handle) noexcept {
void** const tombstone = reinterpret_cast<void**>(static_cast<uintptr_t>(1));
const std::size_t start =
(reinterpret_cast<uintptr_t>(opaque_handle) >> 4) % kNativeHandleSlots;
AcquireSRWLockExclusive(&native_handle_lock);
for (std::size_t probe = 0; probe < kNativeHandleSlots; ++probe) {
const std::size_t index = (start + probe) % kNativeHandleSlots;
if (native_handles[index] == opaque_handle) {
native_handles[index] = tombstone;
ReleaseSRWLockExclusive(&native_handle_lock);
return true;
}
if (native_handles[index] == nullptr) break;
}
ReleaseSRWLockExclusive(&native_handle_lock);
return false;
}
void set_guard(void* const allocation,
const std::size_t logical_size) noexcept {
std::memset(static_cast<unsigned char*>(allocation) +
@@ -126,9 +200,11 @@ extern "C" HP OpusHpOfSbIbImpl(const SB segment, const uintptr_t offset) {
return reinterpret_cast<HP>(static_cast<uintptr_t>(segment) + offset);
}
AcquireSRWLockShared(&segment_registry_lock);
void* base = native_segments[static_cast<std::size_t>(segment)].data;
const auto& slot = native_segments[static_cast<std::size_t>(segment)];
void* base = slot.data;
const std::size_t size = slot.size;
ReleaseSRWLockShared(&segment_registry_lock);
return base == nullptr
return base == nullptr || offset > size
? nullptr
: reinterpret_cast<HP>(reinterpret_cast<uintptr_t>(base) +
offset);
@@ -323,7 +399,7 @@ extern "C" int FAssureHcb(void*** handle_address,
const int byte_count_needed, int* byte_count,
int* byte_capacity) {
if (handle_address == nullptr || byte_count_needed < 0 ||
byte_capacity == nullptr) {
byte_capacity == nullptr || *byte_capacity < 0) {
return 0;
}
if (byte_count != nullptr) {
@@ -332,7 +408,10 @@ extern "C" int FAssureHcb(void*** handle_address,
if (*handle_address != nullptr && byte_count_needed <= *byte_capacity) {
return 1;
}
const int doubled = *byte_capacity > 0 ? *byte_capacity * 2 : 16;
const int doubled = *byte_capacity >
(std::numeric_limits<int>::max)() / 2 ?
(std::numeric_limits<int>::max)() :
(*byte_capacity > 0 ? *byte_capacity * 2 : 16);
const int new_capacity = (std::max)(byte_count_needed, doubled);
if (*handle_address == nullptr) {
*handle_address = OpusHAllocateCb(static_cast<std::size_t>(new_capacity));
@@ -397,6 +476,11 @@ extern "C" void** OpusHAllocateCb(const std::size_t byte_count) {
}
set_guard(handle->data, byte_count);
handle->size = byte_count;
if (!register_native_handle(reinterpret_cast<void**>(handle))) {
HeapFree(process_heap(), 0, handle->data);
HeapFree(process_heap(), 0, handle);
return nullptr;
}
heap_bytes_used.fetch_add(byte_count, std::memory_order_relaxed);
return reinterpret_cast<void**>(handle);
}
@@ -405,6 +489,9 @@ extern "C" void OpusFreeH(void** opaque_handle) {
if (opaque_handle == nullptr) {
return;
}
if (!unregister_native_handle(opaque_handle)) {
report_guard_failure();
}
unregister_prc_handle(opaque_handle);
unregister_compact_handle(opaque_handle);
auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle);
@@ -433,6 +520,9 @@ extern "C" int OpusFChngSizeHCb(void** opaque_handle,
if (opaque_handle == nullptr) {
return 0;
}
if (!valid_native_handle(opaque_handle)) {
report_guard_failure();
}
auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle);
if (!allow_shrink && byte_count <= handle->size) {
return 1;
@@ -470,7 +560,7 @@ extern "C" int OpusFChngSizePhqLcb(void*** handle_address,
}
extern "C" size_t OpusCbOfH(void** opaque_handle) {
return opaque_handle == nullptr
return !valid_native_handle(opaque_handle)
? 0
: reinterpret_cast<NativeHandle*>(opaque_handle)->size;
}
@@ -480,12 +570,13 @@ extern "C" size_t OpusHeapBytesUsed() {
}
extern "C" void* OpusDerefH(void** opaque_handle) {
return opaque_handle == nullptr
return !valid_native_handle(opaque_handle)
? nullptr
: reinterpret_cast<NativeHandle*>(opaque_handle)->data;
}
extern "C" void* OpusHpAlloc(const std::size_t byte_count) {
if (byte_count > kMaxAllocationBytes) return nullptr;
void* allocation =
HeapAlloc(process_heap(), 0, byte_count == 0 ? 1 : byte_count);
if (allocation != nullptr) {
@@ -500,9 +591,8 @@ extern "C" void* OpusHpAlloc(const std::size_t byte_count) {
extern "C" void OpusFreeHp(void* pointer) {
if (pointer != nullptr) {
const SIZE_T size = HeapSize(process_heap(), 0, pointer);
if (size != static_cast<SIZE_T>(-1)) {
heap_bytes_used.fetch_sub(size, std::memory_order_relaxed);
}
if (size == static_cast<SIZE_T>(-1)) report_guard_failure();
heap_bytes_used.fetch_sub(size, std::memory_order_relaxed);
HeapFree(process_heap(), 0, pointer);
}
}
+2 -1
View File
@@ -325,7 +325,8 @@ int main() {
EndSdm();
return 23;
}
TestDltHeader modal_template{{8, 24, 206, 104}, 3, 0x8400,
/* Character is native-modal but is not routed to a common file dialog. */
TestDltHeader modal_template{{8, 24, 206, 104}, 16, 0x8400,
reinterpret_cast<void*>(ModalRuntimeProbe),
11, 4};
auto* modal_template_pointer = &modal_template;