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) if(MSVC)
# /J preserves the original compiler's unsigned-char default. Do not use # /J preserves the original compiler's unsigned-char default. Do not use
# the C++ compiler for K&R-era original C translation units. # 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() endif()
set_target_properties(opus_original_engine PROPERTIES set_target_properties(opus_original_engine PROPERTIES
FOLDER "Original Opus x64" FOLDER "Original Opus x64"
@@ -731,13 +731,14 @@ target_compile_definitions(opus_x64_runtime PRIVATE
NONATIVE NONATIVE
OPUS_X64 OPUS_X64
NOMINMAX NOMINMAX
$<$<CONFIG:Debug>:OPUS_TEST_HOOKS>
) )
target_include_directories(opus_x64_runtime PRIVATE ${OPUS_ORIGINAL_INCLUDE_DIRS}) target_include_directories(opus_x64_runtime PRIVATE ${OPUS_ORIGINAL_INCLUDE_DIRS})
target_link_libraries(opus_x64_runtime PUBLIC ole32 comdlg32 gdi32) target_link_libraries(opus_x64_runtime PUBLIC ole32 comdlg32 gdi32)
if(MSVC) if(MSVC)
# Opus was built with unsigned plain characters; the byte-oriented string # Opus was built with unsigned plain characters; the byte-oriented string
# and international-character assembly routines depend on that behavior. # 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() endif()
set_target_properties(opus_x64_runtime PROPERTIES set_target_properties(opus_x64_runtime PROPERTIES
FOLDER "Original Opus x64/runtime" FOLDER "Original Opus x64/runtime"
@@ -864,10 +865,10 @@ target_compile_features(WORD1 PRIVATE cxx_std_20)
target_compile_definitions(WORD1 PRIVATE target_compile_definitions(WORD1 PRIVATE
WIN WIN23 CRLF NONATIVE OPUS_X64 NOMINMAX UNICODE _UNICODE 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) if(MSVC)
target_compile_options(WORD1 PRIVATE /J /W3 /wd4005 /utf-8) target_compile_options(WORD1 PRIVATE /J /W3 /wd4005 /utf-8 /guard:cf /sdl)
target_link_options(WORD1 PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /MANIFEST:NO) target_link_options(WORD1 PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /guard:cf /CETCOMPAT /MANIFEST:NO)
endif() endif()
set_target_properties(WORD1 PROPERTIES set_target_properties(WORD1 PROPERTIES
FOLDER "Original Opus x64/product" 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 COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --clipboard
) )
set_tests_properties(opus_word1_clipboard_shortcut_test PROPERTIES TIMEOUT 20) 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 add_test(NAME opus_word1_typing_test
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing
) )
+1
View File
@@ -479,6 +479,7 @@ ChangeClipboard()
if (vsab.fOwnClipboard) if (vsab.fOwnClipboard)
{ /* only set handles if we really have something in docScrap */ { /* only set handles if we really have something in docScrap */
SetClipboardData( CF_OWNERDISPLAY, NULL ); SetClipboardData( CF_OWNERDISPLAY, NULL );
SetClipboardData( CF_UNICODETEXT, NULL );
/* don't put out cf_tiff or other rejected pic types, or /* don't put out cf_tiff or other rejected pic types, or
rtf or link for any of those types rtf or link for any of those types
*/ */
+49 -3
View File
@@ -70,6 +70,11 @@ extern char szEmpty[];
extern struct SCI vsci; extern struct SCI vsci;
extern int vwWinVersion; extern int vwWinVersion;
extern int wwCur; extern int wwCur;
#ifdef OPUS_X64
extern HANDLE OpusUnicodeCreateClipboardHandle();
extern int OpusUnicodeClipboardToLegacy();
extern int OpusUnicodeBindPendingClipboard();
#endif
extern LPCH LpchIncr(); extern LPCH LpchIncr();
@@ -279,6 +284,21 @@ int *pfBlankPic; /* set true if an empty picture; else ignored */
AssureLegalSel (PcaSet(&ca, doc, cpFirst, cpLim)); AssureLegalSel (PcaSet(&ca, doc, cpFirst, cpLim));
switch (cf) 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: case CF_TEXT:
return HWriteText (ca.doc, ca.cpFirst, ca.cpLim, cbInitial); return HWriteText (ca.doc, ca.cpFirst, ca.cpLim, cbInitial);
@@ -845,7 +865,7 @@ int FReadExtScrap()
{ {
vsab.fMayHavePic = vsab.fPict = fTrue; vsab.fMayHavePic = vsab.fPict = fTrue;
} }
if (cf != CF_TEXT) if (cf != CF_TEXT && cf != CF_UNICODETEXT)
vsab.fFormatted = fTrue; vsab.fFormatted = fTrue;
break; break;
} }
@@ -884,6 +904,8 @@ int cbInitial;
{ {
int docDest; int docDest;
BOOL fOk; BOOL fOk;
BOOL fUnicode = fFalse;
HANDLE hConverted = NULL;
struct CA ca; struct CA ca;
#ifdef BZ #ifdef BZ
@@ -892,6 +914,18 @@ int cbInitial;
switch (cf) 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_TEXT:
case CF_DIB: case CF_DIB:
case CF_BITMAP: case CF_BITMAP:
@@ -917,7 +951,11 @@ int cbInitial;
docDest = docScrap; docDest = docScrap;
/* otherwise use vdocScratch */ /* otherwise use vdocScratch */
else if ((docDest = DocCreateScratch (pca->doc)) == docNil) else if ((docDest = DocCreateScratch (pca->doc)) == docNil)
{
if (hConverted != NULL)
GlobalFree(hConverted);
return fFalse; return fFalse;
}
/* empty the destination doc and set styles, fonts to standard */ /* empty the destination doc and set styles, fonts to standard */
SetWholeDoc (docDest, PcaSetNil(&ca)); SetWholeDoc (docDest, PcaSetNil(&ca));
@@ -939,7 +977,11 @@ int cbInitial;
break; break;
case CF_TEXT: case CF_TEXT:
fOk = FReadText (docDest, cf, hData, cbInitial); fOk = FReadText (docDest, cf, hData, cbInitial);
if (fOk && fUnicode)
fOk = OpusUnicodeBindPendingClipboard(docDest);
} }
if (hConverted != NULL)
GlobalFree(hConverted);
fOk &= (!vmerr.fMemFail && !vmerr.fDiskFail); fOk &= (!vmerr.fMemFail && !vmerr.fDiskFail);
@@ -1843,7 +1885,8 @@ int cf;
BOOL fPrPic; BOOL fPrPic;
{ {
/* Desired order (win3 word: Notice text before picture formats): /* Desired order (Unicode text precedes legacy formats):
CF_UNICODETEXT
cfRTF cfRTF
CF_TEXT CF_TEXT
cfPRPic cfPRPic
@@ -1876,7 +1919,9 @@ BOOL fPrPic;
goto LMetafile; /* 4th or 5th choice */ goto LMetafile; /* 4th or 5th choice */
case cfNil: case cfNil:
return cfRTF; /* 1st choice */ return CF_UNICODETEXT; /* 1st choice */
case CF_UNICODETEXT:
return cfRTF;
case CF_TEXT: case CF_TEXT:
if (fPrPic && cfPrPic != cfNil) if (fPrPic && cfPrPic != cfNil)
return cfPrPic; /* 4th choice (Excel specific) */ return cfPrPic; /* 4th choice (Excel specific) */
@@ -1915,6 +1960,7 @@ BOOL fRestrictRTF;
switch (cf) switch (cf)
{ {
case CF_UNICODETEXT:
case CF_TEXT: case CF_TEXT:
/* reject text for either a normal pic or an import field pic, /* reject text for either a normal pic or an import field pic,
since it has no text result and would show nothing */ since it has no text result and would show nothing */
+6
View File
@@ -813,6 +813,9 @@ int doc;
int isels; int isels;
struct DOD *pdod, dod; struct DOD *pdod, dod;
extern struct PL **vhplbmc; extern struct PL **vhplbmc;
#ifdef OPUS_X64
extern void OpusUnicodeForgetDocument();
#endif
Assert(doc >= 0 && doc < docMac); Assert(doc >= 0 && doc < docMac);
Assert(doc == docNil || mpdochdod[doc] != hNil); Assert(doc == docNil || mpdochdod[doc] != hNil);
@@ -848,6 +851,9 @@ int doc;
vdocTemp = docNil; vdocTemp = docNil;
else if (doc == vdocScratch) else if (doc == vdocScratch)
vdocScratch = docNil; vdocScratch = docNil;
#ifdef OPUS_X64
OpusUnicodeForgetDocument(doc);
#endif
DisposeDocPdod(doc, &dod ); DisposeDocPdod(doc, &dod );
Assert (vdocScratch == docNil || PdodDoc(vdocScratch)->doc Assert (vdocScratch == docNil || PdodDoc(vdocScratch)->doc
+8
View File
@@ -81,6 +81,10 @@ int wLow, wHigh;
int das; int das;
BOOL f; BOOL f;
#ifdef OPUS_X64
/* DDE execute is unauthenticated cross-process macro execution. */
goto LExeNACK;
#endif
if (fTerminating || PdcldDcl(dcl)->fExecuting || fElActive) if (fTerminating || PdcldDcl(dcl)->fExecuting || fElActive)
goto LExeNACK; goto LExeNACK;
@@ -117,6 +121,10 @@ LExeNACK:
int das = dasNACK; int das = dasNACK;
int ibkmk; int ibkmk;
#ifdef OPUS_X64
/* Do not let another process overwrite an open document through DDE. */
goto LNackPoke;
#endif
if (fTerminating) if (fTerminating)
/* not much point in receiving while terminating */ /* not much point in receiving while terminating */
{ {
+15
View File
@@ -73,6 +73,8 @@ BOOL vfNoInval=0; /* can be > 1 */
#ifdef OPUS_X64 #ifdef OPUS_X64
struct PLC **HplcCreateEdc(); struct PLC **HplcCreateEdc();
extern void OpusUnicodeOnReplace();
extern void OpusUnicodeOnReplaceCps();
#else #else
struct PLC *HplcCreateEdc(); struct PLC *HplcCreateEdc();
#endif #endif
@@ -463,6 +465,9 @@ struct CA *pca;
int fn; int fn;
FC fc, dfc; FC fc, dfc;
{ {
int docUnicode = pca->doc;
CP cpFirstUnicode = pca->cpFirst;
CP cpLimUnicode = pca->cpLim;
struct XBC xbc; struct XBC xbc;
struct XSR *pxsr; struct XSR *pxsr;
/* this should be the maximum number of calls to XReplace or XReplaceCps that /* this should be the maximum number of calls to XReplace or XReplaceCps that
@@ -489,6 +494,7 @@ should go off) */
XReplace(fFalse, &xbc, pxsr); XReplace(fFalse, &xbc, pxsr);
EndCommit(); EndCommit();
CloseTns(&xbc); CloseTns(&xbc);
OpusUnicodeOnReplace(docUnicode, cpFirstUnicode, cpLimUnicode, dfc);
return fTrue; return fTrue;
} }
@@ -821,6 +827,12 @@ on failure (in which case document is unchanged) */
HANDNATIVE BOOL C_FReplaceCps(pcaDel, pcaIns) HANDNATIVE BOOL C_FReplaceCps(pcaDel, pcaIns)
struct CA *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 XBC xbc;
struct XSR *pxsr; struct XSR *pxsr;
/* this should be the maximum number of calls to XReplace or XReplaceCps that /* 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); XReplaceCps(fFalse, &xbc, pxsr);
EndCommit(); EndCommit();
CloseTns(&xbc); CloseTns(&xbc);
OpusUnicodeOnReplaceCps(docDelUnicode, cpDelFirstUnicode,
cpDelLimUnicode, docInsUnicode, cpInsFirstUnicode,
cpInsLimUnicode);
return fTrue; return fTrue;
} }
+6
View File
@@ -1808,6 +1808,7 @@ BOOL fPasteLink;
{ {
if ((fPasteLink && fmt == cfLink) if ((fPasteLink && fmt == cfLink)
|| (!fPasteLink && (fmt == cfRTF || || (!fPasteLink && (fmt == cfRTF ||
fmt == CF_UNICODETEXT ||
fmt == CF_TEXT || fmt == CF_TEXT ||
fmt == CF_BITMAP || fmt == CF_BITMAP ||
fmt == CF_METAFILEPICT))) fmt == CF_METAFILEPICT)))
@@ -2479,6 +2480,11 @@ uns atm;
Assert(atm < atmMax); 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 */ /* Skip auto macro if shift key is down */
if (vfDisableAutoMacros || (GetAsyncKeyState(VK_SHIFT) & 0x8000)) if (vfDisableAutoMacros || (GetAsyncKeyState(VK_SHIFT) & 0x8000))
+23 -6
View File
@@ -128,6 +128,10 @@ extern struct DBS vdbs;
extern struct PREF vpref; extern struct PREF vpref;
extern struct PRI vpri; extern struct PRI vpri;
#ifdef OPUS_X64
extern int OpusUnicodeExtTextOut();
extern int OpusUnicodeHasRange();
#endif
extern int vlm; extern int vlm;
extern int vfPrvwDisp; extern int vfPrvwDisp;
@@ -1256,13 +1260,26 @@ LDacNop:
else else
{ {
LRealExtTextOut: LRealExtTextOut:
ExtTextOut(hdc, xp, yp, #ifdef OPUS_X64
eto, /* action bits */ ExtTextOut(hdc, xp, yp, eto,
fRcAdjusted ? (LPRECT)&rcAdjusted : fRcAdjusted ? (LPRECT)&rcAdjusted :
(LPRECT)&rcOpaque, /* opaque rect */ (LPRECT)&rcOpaque,
(LPCH)&vfli.rgch [ich], /* string */ (LPCH)&vfli.rgch [ich], cch, lpdxp);
cch, /* length */ if (OpusUnicodeHasRange(vfli.doc,
lpdxp ); /* lpdx */ 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; vrf.fInExternalCall = fFalse;
(char *) pchr = (char *)*vhgrpchr + bchrCur; (char *) pchr = (char *)*vhgrpchr + bchrCur;
+11
View File
@@ -80,7 +80,18 @@ extern BOOL vfAwfulNoise;
extern int vfSeeSel; 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; BOOL vfDisableAutoMacros = fFalse;
#endif
/* Simple Cursor Movement / Selection Commands */ /* Simple Cursor Movement / Selection Commands */
+9
View File
@@ -1265,6 +1265,15 @@ LHaveEvent:
return fFalse; 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 (FIsKeyMessage((LPMSG) &vmsgLast))
{ {
if (vmsgLast.wParam & 0x8000) /* indicates we did xlation */ if (vmsgLast.wParam & 0x8000) /* indicates we did xlation */
+2
View File
@@ -620,6 +620,7 @@ BOOL fTutorial;
if (!fTutorial) if (!fTutorial)
{ {
/* BLOCK: now run macros specified on command line */ /* BLOCK: now run macros specified on command line */
#ifndef OPUS_X64
{ {
int isz; int isz;
CHAR * szArg; 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 OpusModernGetPendingDocxRun();
extern int OpusModernGetPendingDocxParagraph(); extern int OpusModernGetPendingDocxParagraph();
extern int OpusModernGetPendingDocxPage(); extern int OpusModernGetPendingDocxPage();
extern int OpusModernBindPendingDocxUnicode();
extern void OpusModernClearPendingDocxFormatting(); extern void OpusModernClearPendingDocxFormatting();
extern int OpusX64FtcFromFontNameForDoc(); extern int OpusX64FtcFromFontNameForDoc();
#endif #endif
@@ -128,8 +129,9 @@ int doc;
struct CA ca; struct CA ca;
char grpprl[96]; char grpprl[96];
char szFont[LF_FACESIZE]; char szFont[LF_FACESIZE];
char szLanguage[32];
int bold, italic, underline, strike, smallCaps, allCaps, hidden; int bold, italic, underline, strike, smallCaps, allCaps, hidden;
int hps, ico; int hps, ico, charset;
int jc, dxaLeft, dxaRight, dxaLeft1, dyaBefore, dyaAfter, dyaLine; int jc, dxaLeft, dxaRight, dxaLeft1, dyaBefore, dyaAfter, dyaLine;
int fKeep, fKeepFollow, fPageBreakBefore, fBottomBorder; int fKeep, fKeepFollow, fPageBreakBefore, fBottomBorder;
int xaPage, yaPage, dxaLeftPage, dxaRightPage, dyaTopPage, dyaBottomPage; int xaPage, yaPage, dxaLeftPage, dxaRightPage, dyaTopPage, dyaBottomPage;
@@ -195,7 +197,8 @@ int doc;
{ {
if (!OpusModernGetPendingDocxRun(i, &cpFirst, &cpLim, &bold, if (!OpusModernGetPendingDocxRun(i, &cpFirst, &cpLim, &bold,
&italic, &underline, &strike, &smallCaps, &allCaps, &italic, &underline, &strike, &smallCaps, &allCaps,
&hidden, &hps, &ico, szFont, sizeof(szFont))) &hidden, &hps, &ico, szFont, sizeof(szFont), &charset,
szLanguage, sizeof(szLanguage)))
continue; continue;
if (cpFirst < cp0 || cpFirst >= cpMac) if (cpFirst < cp0 || cpFirst >= cpMac)
continue; continue;
@@ -214,7 +217,7 @@ int doc;
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFVanish, hidden); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFVanish, hidden);
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCHps, hps); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCHps, hps);
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCIco, ico); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCIco, ico);
ftc = OpusX64FtcFromFontNameForDoc(doc, szFont); ftc = OpusX64FtcFromFontNameForDoc(doc, szFont, charset);
if (ftc >= 0) if (ftc >= 0)
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFtc, ftc); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFtc, ftc);
ca.doc = doc; ca.doc = doc;
@@ -224,6 +227,7 @@ int doc;
} }
PdodDoc(doc)->fFormatted = fTrue; PdodDoc(doc)->fFormatted = fTrue;
OpusModernBindPendingDocxUnicode(doc);
OpusModernClearPendingDocxFormatting(); OpusModernClearPendingDocxFormatting();
} }
#endif #endif
+76 -12
View File
@@ -104,6 +104,25 @@ extern struct REB * vpreb;
static int vWin95ZoomPercent = 100; static int vWin95ZoomPercent = 100;
static int vWin95BaseDxsInch = 0; static int vWin95BaseDxsInch = 0;
static int vWin95BaseDysInch = 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() int OpusExportCurrentDocumentPdf()
{ {
@@ -116,17 +135,24 @@ int OpusExportCurrentDocumentPdf()
extern CHAR HUGE *vhpchFetch; extern CHAR HUGE *vhpchFetch;
extern int OpusPdfSnapshotBegin(); extern int OpusPdfSnapshotBegin();
extern int OpusPdfSnapshotAddParagraph(); extern int OpusPdfSnapshotAddParagraph();
extern int OpusPdfSnapshotAddRun(); extern int OpusPdfSnapshotAddRunUtf8();
extern int OpusPdfSnapshotExportDialog(); extern int OpusPdfSnapshotExportDialog();
extern int OpusDocxSnapshotExportPath();
extern void OpusX64FontNameFromFtc(); 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; return fFalse;
doc = DocMother(selCur.doc); doc = vOpusDocxSnapshotDoc != docNil ? vOpusDocxSnapshotDoc :
DocMother(selCur.doc);
cpMac = CpMacDocEdit(doc); cpMac = CpMacDocEdit(doc);
if (cpMac < cp0 || cpMac > 0x3fffffffL) if (cpMac < cp0 || cpMac > 0x3fffffffL)
return fFalse; return fFalse;
pdod = PdodMother(doc); pdod = PdodMother(doc);
vOpusPdfExportStage = 2;
if (!OpusPdfSnapshotBegin(pdod->dop.xaPage, pdod->dop.yaPage, if (!OpusPdfSnapshotBegin(pdod->dop.xaPage, pdod->dop.yaPage,
pdod->dop.dxaLeft, pdod->dop.dxaRight, pdod->dop.dxaLeft, pdod->dop.dxaRight,
pdod->dop.dyaTop < 0 ? -pdod->dop.dyaTop : pdod->dop.dyaTop, pdod->dop.dyaTop < 0 ? -pdod->dop.dyaTop : pdod->dop.dyaTop,
@@ -141,6 +167,7 @@ int OpusExportCurrentDocumentPdf()
cpParaLim = CpMin(caPara.cpLim, cpMac); cpParaLim = CpMin(caPara.cpLim, cpMac);
if (cpParaLim <= cpPara) if (cpParaLim <= cpPara)
cpParaLim = cpPara + 1; cpParaLim = cpPara + 1;
vOpusPdfExportStage = 1000 + (int)cpPara;
if (!OpusPdfSnapshotAddParagraph(vpapFetch.jc, if (!OpusPdfSnapshotAddParagraph(vpapFetch.jc,
vpapFetch.dxaLeft, vpapFetch.dxaRight, vpapFetch.dxaLeft, vpapFetch.dxaRight,
vpapFetch.dxaLeft1, vpapFetch.dyaBefore, vpapFetch.dxaLeft1, vpapFetch.dyaBefore,
@@ -155,49 +182,86 @@ int OpusExportCurrentDocumentPdf()
int ich; int ich;
int cch; int cch;
int cchOutput; int cchOutput;
char rgch[1024]; char rgch[4096];
char szFont[LF_FACESIZE]; char szFont[LF_FACESIZE];
char szLanguage[32];
int charset;
FetchCp(doc, cpRun, fcmChars + fcmProps); FetchCp(doc, cpRun, fcmChars + fcmProps);
if (vccpFetch <= 0 || vhpchFetch == NULL) if (vccpFetch <= 0 || vhpchFetch == NULL)
return fFalse; return fFalse;
cch = (int)CpMin((CP)vccpFetch, cpParaLim - cpRun); 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; cchOutput = 0;
for (ich = 0; ich < cch; ++ich) for (ich = 0; ich < cch; ++ich)
{ {
int ch = (unsigned char)vhpchFetch[ich]; int ch = (unsigned char)vhpchFetch[ich];
char chOutput;
char rgchUtf8[8];
int cchUtf8;
if (ch == chReturn || ch == chEop) if (ch == chReturn || ch == chEop)
continue; continue;
if (ch == chTable || ch == chTab) if (ch == chTable || ch == chTab)
rgch[cchOutput++] = '\t'; chOutput = '\t';
else if (ch == chSect || ch == chColumnBreak) else if (ch == chSect || ch == chColumnBreak)
rgch[cchOutput++] = '\f'; chOutput = '\f';
else if (ch == chNonReqHyphen) else if (ch == chNonReqHyphen)
rgch[cchOutput++] = '-'; chOutput = '-';
/* fSpec can cover imported field/list runs whose bytes are still /* fSpec can cover imported field/list runs whose bytes are still
ordinary visible text. Filter actual control codes above, not ordinary visible text. Filter actual control codes above, not
the whole run, or leading words disappear from PDF output. */ the whole run, or leading words disappear from PDF output. */
else if (ch >= chSpace) 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) if (cchOutput > 0)
{ {
rgch[cchOutput] = '\0'; rgch[cchOutput] = '\0';
OpusX64FontNameFromFtc(vchpFetch.ftc, OpusX64FontNameFromFtc(vchpFetch.ftc,
szFont, sizeof(szFont)); 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.hps, vchpFetch.fBold,
vchpFetch.fItalic, vchpFetch.kul != kulNone, vchpFetch.fItalic, vchpFetch.kul != kulNone,
vchpFetch.fStrike, vchpFetch.fSmallCaps, vchpFetch.fStrike, vchpFetch.fSmallCaps,
vchpFetch.fCaps, vchpFetch.fVanish, vchpFetch.fCaps, vchpFetch.fVanish,
vchpFetch.ico)) vchpFetch.ico, szLanguage, charset))
return fFalse; return fFalse;
} }
cpRun += cch; cpRun += cch;
} }
cpPara = cpParaLim; 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; return result;
} }
+1 -1
View File
@@ -708,7 +708,7 @@ LCleanup:
FWin95SaveAliasMatches(stFile) && PdodDoc(doc)->fn != fnNil) FWin95SaveAliasMatches(stFile) && PdodDoc(doc)->fn != fnNil)
FCloseFn(PdodDoc(doc)->fn); FCloseFn(PdodDoc(doc)->fn);
if (!OpusFinishWin95SaveAlias(stFile, if (!OpusFinishWin95SaveAlias(stFile,
cmd == cmdOK && pcmb->fAction) && cmd == cmdOK) cmd == cmdOK && pcmb->fAction, doc) && cmd == cmdOK)
cmd = cmdError; cmd = cmdError;
#endif #endif
if (cmd == cmdCancelled) if (cmd == cmdCancelled)
+45 -6
View File
@@ -1002,19 +1002,26 @@ const char *sz;
/* Variant used while a DOCX is being opened. The new document has not yet /* 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 become selCur.doc, so the normal ribbon adapter would add the font to the
document that was active before File Open. */ document that was active before File Open. */
EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz) EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz, charset)
int doc; int doc;
const char *sz; const char *sz;
int charset;
{ {
int cch;
struct FFN *pffn;
char rgch[cbFfnLast] = {0};
if (sz == NULL || *sz == '\0') if (sz == NULL || *sz == '\0')
return wNinch; return wNinch;
/* Word 1.x keeps only three guaranteed GDI font slots in a new plain-text /* A new Word 1.x document exposes only the three guaranteed screen-font
document: Times, Symbol, and Helvetica. Adding Office-era fonts before slots while DOCX formatting is applied. Adding a normal ANSI font at
the imported document becomes current can alias the new ftc to Symbol. this point can alias it to slot 1 (Symbol), corrupting every Latin
Preserve the requested family safely; Helvetica is the closest available character in the document. Keep the historical safe mappings for ANSI
substitute for Aptos/Calibri/Arial and retains ANSI character mapping. */ faces; charset-specific runs still get real FFN records below. */
if (!FNeNcSz(sz, "Symbol") || !FNeNcSz(sz, "Wingdings")) if (!FNeNcSz(sz, "Symbol") || !FNeNcSz(sz, "Wingdings"))
return 1; return 1;
if (charset == ANSI_CHARSET || charset == DEFAULT_CHARSET)
{
if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") || if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") ||
!FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") || !FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") ||
!FNeNcSz(sz, "Cambria")) !FNeNcSz(sz, "Cambria"))
@@ -1023,6 +1030,22 @@ const char *sz;
!FNeNcSz(sz, "Consolas")) !FNeNcSz(sz, "Consolas"))
return FtcFromDocIbst(doc, ibstCourier); return FtcFromDocIbst(doc, ibstCourier);
return 2; 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) EXPORT int OpusX64HpsFromFontSize(sz)
@@ -1067,6 +1090,22 @@ int cchMax;
bltbyte(szFont, sz, min(CchSz(szFont), cchMax)); bltbyte(szFont, sz, min(CchSz(szFont), cchMax));
sz[cchMax - 1] = '\0'; 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 #endif
+24
View File
@@ -96,6 +96,8 @@ extern struct FTI vfti;
extern char (**vhgrpchr)[]; extern char (**vhgrpchr)[];
extern int vbchrMac; extern int vbchrMac;
extern struct STTB **vhsttbFont; extern struct STTB **vhsttbFont;
extern unsigned int OpusUnicodeScalarAt();
extern int OpusQueueUnicodeWmChar();
#endif #endif
extern struct FCB **mpfnhfcb[]; extern struct FCB **mpfnhfcb[];
extern int vfnPreload; extern int vfnPreload;
@@ -1829,6 +1831,17 @@ LONG lParam;
pffn = (struct FFN *)PstFromSttb(vhsttbFont, ibst); pffn = (struct FFN *)PstFromSttb(vhsttbFont, ibst);
return (LRESULT)(unsigned char)ChsPffn(pffn); 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; return (LRESULT) -1;
#endif #endif
@@ -2496,6 +2509,17 @@ LPMSG lpmsg;
wm = lpmsg->message; wm = lpmsg->message;
kc = lpmsg->wParam; /* key code or char */ 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 #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) { if (path == nullptr) {
return 0; 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]{}; char current[MAX_PATH]{};
if (_getdcwd(drive, current, static_cast<int>(sizeof(current))) == if (_getdcwd(drive, current, static_cast<int>(sizeof(current))) ==
nullptr) { nullptr) {
path[0] = '\0'; path[0] = '\0';
return 0; return 0;
} }
std::strcpy(path, current); std::size_t length = std::strlen(current);
std::size_t length = std::strlen(path); if (length + 2 > kLegacyPathCapacity) {
path[0] = '\0';
return 0;
}
std::memcpy(path, current, length + 1);
if (length == 2 && path[1] == ':') { if (length == 2 && path[1] == ':') {
path[length++] = '\\'; path[length++] = '\\';
path[length] = '\0'; path[length] = '\0';
+6
View File
@@ -137,6 +137,12 @@ constexpr UINT kPromptMessages[] = {
extern "C" { extern "C" {
LRESULT CALLBACK NatAppWndProc(HWND h, UINT m, WPARAM w, LPARAM l) { 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); return Dispatch(h, m, w, l, AppWndProc, kAppMessages);
} }
LRESULT CALLBACK NatMwdWndProc(HWND h, UINT m, WPARAM w, LPARAM l) { 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 #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <climits>
#include <cstring>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
extern "C" int OpusModernDocxToRtfFile(const char*, const char*); 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 OpusModernRtfFileToDocx(const char*, const char*);
extern "C" int OpusModernRtfFileToPdf(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) { 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) { if (argument_count == 3) {
const bool converted = const bool converted =
OpusModernDocxToRtfFile(arguments[1], arguments[2]) != 0; 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 rtf = base + ".rtf";
const std::string docx = base + ".docx"; const std::string docx = base + ".docx";
const std::string roundtrip = base + ".roundtrip.rtf"; 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]{}; char requested_pdf[32768]{};
const DWORD requested_pdf_length = GetEnvironmentVariableA( const DWORD requested_pdf_length = GetEnvironmentVariableA(
"WORD1_TEST_KEEP_PDF", requested_pdf, "WORD1_TEST_KEEP_PDF", requested_pdf,
@@ -45,12 +70,63 @@ int main(const int argument_count, char** arguments) {
output << "{\\rtf1\\ansi{\\fonttbl{\\f0 Arial;}}" output << "{\\rtf1\\ansi{\\fonttbl{\\f0 Arial;}}"
"{\\colortbl;\\red255\\green0\\blue0;}" "{\\colortbl;\\red255\\green0\\blue0;}"
"\\f0\\fs24 Plain {\\b Bold} {\\i Italic} " "\\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 written = OpusModernRtfFileToDocx(rtf.c_str(), docx.c_str()) != 0;
const bool read = written && const bool read = written &&
OpusModernDocxToRtfFile(docx.c_str(), roundtrip.c_str()) != 0; 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; 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; std::string result;
if (read) { if (read) {
std::ifstream input(roundtrip, std::ios::binary); std::ifstream input(roundtrip, std::ios::binary);
@@ -64,21 +140,36 @@ int main(const int argument_count, char** arguments) {
DeleteFileA(rtf.c_str()); DeleteFileA(rtf.c_str());
DeleteFileA(docx.c_str()); DeleteFileA(docx.c_str());
DeleteFileA(roundtrip.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 (!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("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("\\b") == std::string::npos ||
result.find("\\cf1") == std::string::npos || !pdf_written || result.find("\\cf1") == std::string::npos || !pdf_written ||
!pdf_data.starts_with("%PDF-") || !pdf_data.starts_with("%PDF-") ||
pdf_data.find("Plain") == std::string::npos || pdf_data.find("Plain") == std::string::npos ||
pdf_data.find("Bold") == std::string::npos || pdf_data.find("Bold") == std::string::npos ||
pdf_data.find("Second paragraph") == 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("/Helvetica-Bold") == std::string::npos ||
pdf_data.find("xref") == 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 std::cerr << "DOCX round trip failed: write=" << written
<< " read=" << read << " pdf=" << pdf_written << " read=" << read << " pdf=" << pdf_written
<< " bytes=" << result.size() << '\n'; << " bytes=" << result.size()
<< " oversizedRejected=" << oversized_rejected
<< " invalidSnapshotRejected="
<< invalid_snapshot_rejected << '\n';
return 2; return 2;
} }
std::cout << "DOCX round trip passed (" << result.size() << " RTF bytes)\n"; std::cout << "DOCX round trip passed (" << result.size() << " RTF bytes)\n";
@@ -374,28 +374,6 @@ LONG WINAPI ObserveVectoredException(EXCEPTION_POINTERS* exception) {
} // namespace } // 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, int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous,
PWSTR command_line, int show_command) { PWSTR command_line, int show_command) {
if ((command_line != nullptr && if ((command_line != nullptr &&
@@ -404,6 +382,15 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous,
return 0; 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); SetUnhandledExceptionFilter(WriteCrashStack);
AddVectoredExceptionHandler(1, ObserveVectoredException); AddVectoredExceptionHandler(1, ObserveVectoredException);
_RTC_SetErrorFuncW(WriteRtcFailure); _RTC_SetErrorFuncW(WriteRtcFailure);
+149 -33
View File
@@ -1,11 +1,13 @@
#include "opus_x64_compat.h" #include "opus_x64_compat.h"
#include "opus_x64_heap.h" #include "opus_x64_heap.h"
#include <commdlg.h> #include <commdlg.h>
#include <objbase.h>
extern "C" { extern "C" {
#include "dac.h" #include "dac.h"
} }
#include <algorithm> #include <algorithm>
#include <atomic>
#include <cctype> #include <cctype>
#include <cstdio> #include <cstdio>
#include <cstdint> #include <cstdint>
@@ -21,10 +23,17 @@ extern std::uintptr_t wRefDlgCur;
extern HWND vhWndMsgBoxParent; extern HWND vhWndMsgBoxParent;
void GetCabSz(void**, char*, std::uint16_t, std::uint16_t); void GetCabSz(void**, char*, std::uint16_t, std::uint16_t);
int FSetCabSz(void**, const char*, 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 OpusModernPathIsDocx(const char*);
int OpusModernDocxToTextFile(const char*, const char*); int OpusModernDocxToTextFile(const char*, const char*);
int OpusModernRtfFileToDocx(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; Win95SaveAlias g_win95_save_alias;
std::unordered_map<std::string, std::string> g_win95_saved_aliases; std::unordered_map<std::string, std::string> g_win95_saved_aliases;
std::string g_win95_staging_directory;
struct Win95AliasCleanup { struct Win95AliasCleanup {
~Win95AliasCleanup() { ~Win95AliasCleanup() {
@@ -141,6 +151,9 @@ struct Win95AliasCleanup {
for (const auto& saved : g_win95_saved_aliases) { for (const auto& saved : g_win95_saved_aliases) {
DeleteFileA(saved.first.c_str()); 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; 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, bool make_win95_staging_path(std::string& path,
const char* desired_extension = ".DOC") { const char* desired_extension = ".DOC") {
char module_path[32768]{}; if (_stricmp(desired_extension, ".DOC") != 0 &&
const DWORD module_length = GetModuleFileNameA( _stricmp(desired_extension, ".TXT") != 0) return false;
nullptr, module_path, static_cast<DWORD>(std::size(module_path))); if (g_win95_staging_directory.empty()) {
if (module_length == 0 || module_length >= std::size(module_path)) { 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; 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) { for (int attempt = 0; attempt < 32; ++attempt) {
char temporary[MAX_PATH]{}; GUID identifier{};
if (GetTempFileNameA(directory.c_str(), "W95", 0, temporary) == 0) { 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; return false;
} }
DeleteFileA(temporary); g_win95_staging_directory = candidate;
char* existing_extension = std::strrchr(temporary, '.'); break;
if (existing_extension != nullptr) {
lstrcpyA(existing_extension, desired_extension);
} }
if (GetFileAttributesA(temporary) == INVALID_FILE_ATTRIBUTES && if (GetLastError() != ERROR_ALREADY_EXISTS) return false;
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 true;
} }
} }
return false; 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) { std::string counted_path(const unsigned char* st_file) {
if (st_file == nullptr || st_file[0] == 0 || st_file[0] >= 120) { if (st_file == nullptr || st_file[0] == 0 || st_file[0] >= 120) {
return {}; return {};
@@ -2187,9 +2288,13 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
} }
char test_path[32768]{}; char test_path[32768]{};
#ifdef OPUS_TEST_HOOKS
const DWORD test_path_length = GetEnvironmentVariableA( const DWORD test_path_length = GetEnvironmentVariableA(
"WORD1_TEST_FILE_DIALOG_PATH", test_path, "WORD1_TEST_FILE_DIALOG_PATH", test_path,
static_cast<DWORD>(std::size(test_path))); static_cast<DWORD>(std::size(test_path)));
#else
const DWORD test_path_length = 0;
#endif
BOOL accepted = FALSE; BOOL accepted = FALSE;
if (test_path_length > 0 && test_path_length < std::size(test_path)) { if (test_path_length > 0 && test_path_length < std::size(test_path)) {
lstrcpynA(file_buffer.data(), 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)) { if (saving && OpusModernPathIsDocx(test_path)) {
file_dialog.nFilterIndex = 2; file_dialog.nFilterIndex = 2;
} }
#ifdef OPUS_TEST_HOOKS
SetEnvironmentVariableA("WORD1_TEST_FILE_DIALOG_PATH", nullptr); SetEnvironmentVariableA("WORD1_TEST_FILE_DIALOG_PATH", nullptr);
#endif
accepted = TRUE; accepted = TRUE;
} else { } else {
accepted = opening ? GetOpenFileNameA(&file_dialog) : accepted = opening ? GetOpenFileNameA(&file_dialog) :
@@ -2232,6 +2339,16 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
".docx"); ".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; std::string legacy_path;
const bool docx = OpusModernPathIsDocx(selected_path.c_str()) != 0; const bool docx = OpusModernPathIsDocx(selected_path.c_str()) != 0;
if (!make_win95_staging_path(legacy_path, 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, int OpusFinishWin95SaveAlias(const unsigned char* st_file,
const int success) { const int success, const int doc) {
const std::string path = counted_path(st_file); const std::string path = counted_path(st_file);
if (path.empty()) { if (path.empty()) {
return !g_win95_save_alias.active; return !g_win95_save_alias.active;
@@ -2425,11 +2542,10 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
if (copied) { if (copied) {
copied = OpusModernPathIsDocx( copied = OpusModernPathIsDocx(
g_win95_save_alias.selected_path.c_str()) ? g_win95_save_alias.selected_path.c_str()) ?
OpusModernRtfFileToDocx( OpusSaveDocumentAsDocx(
g_win95_save_alias.legacy_path.c_str(), doc, g_win95_save_alias.selected_path.c_str()) != 0 :
g_win95_save_alias.selected_path.c_str()) != 0 : atomic_copy_file(g_win95_save_alias.legacy_path,
CopyFileA(g_win95_save_alias.legacy_path.c_str(), g_win95_save_alias.selected_path);
g_win95_save_alias.selected_path.c_str(), FALSE) != 0;
} }
if (!success) { if (!success) {
DeleteFileA(g_win95_save_alias.legacy_path.c_str()); DeleteFileA(g_win95_save_alias.legacy_path.c_str());
@@ -2449,8 +2565,8 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
return false; return false;
} }
return OpusModernPathIsDocx(saved->second.c_str()) ? return OpusModernPathIsDocx(saved->second.c_str()) ?
OpusModernRtfFileToDocx(path.c_str(), saved->second.c_str()) != 0 : OpusSaveDocumentAsDocx(doc, saved->second.c_str()) != 0 :
CopyFileA(path.c_str(), saved->second.c_str(), FALSE) != 0; atomic_copy_file(path, saved->second);
} }
int OpusWin95SaveAliasRequiresRtf(const unsigned char* st_file) { int OpusWin95SaveAliasRequiresRtf(const unsigned char* st_file) {
+261 -2
View File
@@ -1,10 +1,12 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <windowsx.h> #include <windowsx.h>
#include <imm.h>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <deque>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -36,6 +38,14 @@ extern "C" int OpusAdjustWin95HorizontalMargin(
HWND ruler, int left_margin, int delta_pixels); HWND ruler, int left_margin, int delta_pixels);
extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler); extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler);
extern "C" int OpusExportCurrentDocumentPdf(void); 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 { namespace {
@@ -57,7 +67,10 @@ constexpr UINT kCmdToggleStandardToolbar = 0x7101;
constexpr UINT kCmdToggleFormattingToolbar = 0x7102; constexpr UINT kCmdToggleFormattingToolbar = 0x7102;
constexpr UINT kCmdExportPdf = 0x7103; constexpr UINT kCmdExportPdf = 0x7103;
constexpr UINT kCmdTextColorBase = 0x7200; constexpr UINT kCmdTextColorBase = 0x7200;
constexpr UINT kCmdLanguageBase = 0x7300;
constexpr UINT_PTR kSyncTimer = 0x951; constexpr UINT_PTR kSyncTimer = 0x951;
constexpr UINT kWmCommitUnicodeScalar = WM_APP + 0x452;
constexpr std::size_t kMaxPendingUnicodeInput = 0xffff;
constexpr wchar_t kOriginalPaneProcProperty[] = L"OpusWord95OriginalPaneProc"; constexpr wchar_t kOriginalPaneProcProperty[] = L"OpusWord95OriginalPaneProc";
enum class FormatGlyph { enum class FormatGlyph {
@@ -168,8 +181,47 @@ struct ToolbarState {
HBRUSH g_menu_brush = nullptr; HBRUSH g_menu_brush = nullptr;
HMENU g_table_menu = nullptr; HMENU g_table_menu = nullptr;
HMENU g_toolbars_menu = nullptr; HMENU g_toolbars_menu = nullptr;
HMENU g_language_menu = nullptr;
WNDPROC g_original_app_proc = nullptr; WNDPROC g_original_app_proc = nullptr;
bool g_word95_page_view_active = false; 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 { struct VerticalRulerDragState {
HWND pane = nullptr; HWND pane = nullptr;
@@ -227,7 +279,8 @@ int scale(HWND window, int value) {
void set_window_classic(HWND window) { void set_window_classic(HWND window) {
using SetWindowThemeProc = HRESULT(WINAPI*)(HWND, LPCWSTR, LPCWSTR); 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 ? static const auto set_theme = theme_module != nullptr ?
reinterpret_cast<SetWindowThemeProc>( reinterpret_cast<SetWindowThemeProc>(
GetProcAddress(theme_module, "SetWindowTheme")) : nullptr; GetProcAddress(theme_module, "SetWindowTheme")) : nullptr;
@@ -346,6 +399,33 @@ void configure_word95_menus(HWND window) {
MF_BYPOSITION | MF_POPUP | MF_STRING, MF_BYPOSITION | MF_POPUP | MF_STRING,
reinterpret_cast<UINT_PTR>(tools), L"&Tools"); 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)) { if (g_table_menu == nullptr || !IsMenu(g_table_menu)) {
g_table_menu = CreatePopupMenu(); g_table_menu = CreatePopupMenu();
@@ -454,7 +534,8 @@ void configure_word95_menus(HWND window) {
void apply_caption_colors(HWND window) { void apply_caption_colors(HWND window) {
using DwmSetWindowAttributeProc = HRESULT(WINAPI*)(HWND, DWORD, using DwmSetWindowAttributeProc = HRESULT(WINAPI*)(HWND, DWORD,
LPCVOID, DWORD); LPCVOID, DWORD);
HMODULE module = LoadLibraryW(L"dwmapi.dll"); HMODULE module = LoadLibraryExW(
L"dwmapi.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (module == nullptr) { if (module == nullptr) {
return; 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, LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
WPARAM w_param, LPARAM l_param) { WPARAM w_param, LPARAM l_param) {
WNDPROC original = original_pane_proc(pane); 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 (message == WM_MOUSEWHEEL) {
if (g_document_wheel.pane != pane) { if (g_document_wheel.pane != pane) {
g_document_wheel = {}; g_document_wheel = {};
@@ -2507,6 +2732,9 @@ LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
if (g_document_wheel.pane == pane) { if (g_document_wheel.pane == pane) {
g_document_wheel = {}; g_document_wheel = {};
} }
g_pending_unicode_inputs.clear();
g_unicode_input_active = false;
g_pending_high_surrogate = 0;
clear_page_snapshots(pane); clear_page_snapshots(pane);
RemovePropW(pane, kOriginalPaneProcProperty); RemovePropW(pane, kOriginalPaneProcProperty);
if (original != nullptr) { if (original != nullptr) {
@@ -2588,6 +2816,15 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
OpusExportCurrentDocumentPdf(); OpusExportCurrentDocumentPdf();
return 0; 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) { if (command == kCmdToggleStandardToolbar) {
toggle_toolbar_row(app, toolbar, true); toggle_toolbar_row(app, toolbar, true);
return 0; return 0;
@@ -2597,6 +2834,7 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
return 0; return 0;
} }
} else if (message == WM_INITMENUPOPUP) { } else if (message == WM_INITMENUPOPUP) {
update_language_menu_check();
HWND toolbar = FindWindowExW(app, nullptr, kToolbarClass, nullptr); HWND toolbar = FindWindowExW(app, nullptr, kToolbarClass, nullptr);
ToolbarState* state = toolbar_state(toolbar); ToolbarState* state = toolbar_state(toolbar);
if (state != nullptr) { if (state != nullptr) {
@@ -2864,6 +3102,27 @@ bool register_ruler_overlay_class() {
} // namespace } // 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) { extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler) {
HDC dc = GetDC(ruler); HDC dc = GetDC(ruler);
if (dc != nullptr) { if (dc != nullptr) {
+75 -5
View File
@@ -653,7 +653,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
std::cerr << std::cerr <<
"usage: opus_word1_ui_test WORD1.exe " "usage: opus_word1_ui_test WORD1.exe "
"[--typing|--interaction|--selection|--caret|--formatting|--color|" "[--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-open FILE|--docx-format-open FILE|"
"--docx-pdf-export FILE]\n"; "--docx-pdf-export FILE]\n";
return 1; return 1;
@@ -678,6 +678,9 @@ int wmain(const int argument_count, wchar_t** arguments) {
const bool font_typing_mode = const bool font_typing_mode =
argument_count == 3 && argument_count == 3 &&
std::wcscmp(arguments[2], L"--font-typing") == 0; 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 = const bool about_mode =
argument_count == 3 && argument_count == 3 &&
std::wcscmp(arguments[2], L"--about") == 0; std::wcscmp(arguments[2], L"--about") == 0;
@@ -703,7 +706,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
formatted_docx_open_mode); formatted_docx_open_mode);
if (argument_count == 3 && !typing_mode && !interaction_mode && if (argument_count == 3 && !typing_mode && !interaction_mode &&
!selection_mode && !caret_mode && !formatting_mode && !color_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) { !save_as_mode && !pdf_export_mode) {
std::cerr << "unknown test mode\n"; std::cerr << "unknown test mode\n";
return 1; return 1;
@@ -959,8 +962,10 @@ int wmain(const int argument_count, wchar_t** arguments) {
} }
} }
bool rich_pdf_valid = !docx_pdf_export_mode; bool rich_pdf_valid = !docx_pdf_export_mode;
if (docx_pdf_export_mode && DWORD_PTR export_result = 0;
PostMessageW(main_window, kWmCommand, kExportPdf, 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; const ULONGLONG pdf_deadline = GetTickCount64() + 15000;
do { do {
std::ifstream input(pdf_path, std::ios::binary); 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 << "} bodyFont={index:" << body_font_index
<< ",charset:" << body_font_charset << "}\n"; << ",charset:" << body_font_charset << "}\n";
if (docx_pdf_export_mode) 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()); if (!keep_pdf && !pdf_path.empty()) DeleteFileA(pdf_path.c_str());
return 84; return 84;
} }
@@ -1091,6 +1100,67 @@ int wmain(const int argument_count, wchar_t** arguments) {
CloseHandle(process.hProcess); CloseHandle(process.hProcess);
return 0; 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) { if (font_typing_mode) {
const HWND pane = find_descendant_by_class(main_window, L"OpusWwd"); const HWND pane = find_descendant_by_class(main_window, L"OpusWwd");
DWORD ignored_process_id = 0; DWORD ignored_process_id = 0;
+98 -8
View File
@@ -29,14 +29,18 @@ static_assert(offsetof(NativeHandle, data) == 0);
constexpr int kPrcTokenNil = 0x3fff; constexpr int kPrcTokenNil = 0x3fff;
constexpr int kPrcTokenMac = kPrcTokenNil - 1; constexpr int kPrcTokenMac = kPrcTokenNil - 1;
constexpr int kCompactHandleMac = 0x3fff; constexpr int kCompactHandleMac = 0x3fff;
constexpr std::size_t kNativeHandleSlots = 131071;
constexpr std::size_t kSegmentSlots = 65536; constexpr std::size_t kSegmentSlots = 65536;
constexpr std::size_t kGuardBytes = 64; constexpr std::size_t kGuardBytes = 64;
constexpr std::size_t kMaxAllocationBytes = 256u * 1024u * 1024u;
constexpr unsigned char kGuardValue = 0xa5; constexpr unsigned char kGuardValue = 0xa5;
SRWLOCK prc_registry_lock = SRWLOCK_INIT; SRWLOCK prc_registry_lock = SRWLOCK_INIT;
SRWLOCK compact_handle_lock = SRWLOCK_INIT; SRWLOCK compact_handle_lock = SRWLOCK_INIT;
SRWLOCK native_handle_lock = SRWLOCK_INIT;
SRWLOCK segment_registry_lock = SRWLOCK_INIT; SRWLOCK segment_registry_lock = SRWLOCK_INIT;
void** prc_handles[kPrcTokenNil]{}; void** prc_handles[kPrcTokenNil]{};
void** compact_handles[kCompactHandleMac + 1]{}; void** compact_handles[kCompactHandleMac + 1]{};
void** native_handles[kNativeHandleSlots]{};
std::array<NativeSegment, kSegmentSlots> native_segments{}; std::array<NativeSegment, kSegmentSlots> native_segments{};
std::atomic_size_t heap_bytes_used{0}; 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( [[nodiscard]] constexpr bool guarded_size(
const std::size_t logical_size, std::size_t* result) noexcept { 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); const std::size_t payload = payload_size(logical_size);
if (payload > (std::numeric_limits<std::size_t>::max)() - kGuardBytes) { if (payload > (std::numeric_limits<std::size_t>::max)() - kGuardBytes) {
return false; return false;
@@ -55,6 +60,75 @@ std::atomic_size_t heap_bytes_used{0};
return true; 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, void set_guard(void* const allocation,
const std::size_t logical_size) noexcept { const std::size_t logical_size) noexcept {
std::memset(static_cast<unsigned char*>(allocation) + 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); return reinterpret_cast<HP>(static_cast<uintptr_t>(segment) + offset);
} }
AcquireSRWLockShared(&segment_registry_lock); 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); ReleaseSRWLockShared(&segment_registry_lock);
return base == nullptr return base == nullptr || offset > size
? nullptr ? nullptr
: reinterpret_cast<HP>(reinterpret_cast<uintptr_t>(base) + : reinterpret_cast<HP>(reinterpret_cast<uintptr_t>(base) +
offset); offset);
@@ -323,7 +399,7 @@ extern "C" int FAssureHcb(void*** handle_address,
const int byte_count_needed, int* byte_count, const int byte_count_needed, int* byte_count,
int* byte_capacity) { int* byte_capacity) {
if (handle_address == nullptr || byte_count_needed < 0 || if (handle_address == nullptr || byte_count_needed < 0 ||
byte_capacity == nullptr) { byte_capacity == nullptr || *byte_capacity < 0) {
return 0; return 0;
} }
if (byte_count != nullptr) { if (byte_count != nullptr) {
@@ -332,7 +408,10 @@ extern "C" int FAssureHcb(void*** handle_address,
if (*handle_address != nullptr && byte_count_needed <= *byte_capacity) { if (*handle_address != nullptr && byte_count_needed <= *byte_capacity) {
return 1; 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); const int new_capacity = (std::max)(byte_count_needed, doubled);
if (*handle_address == nullptr) { if (*handle_address == nullptr) {
*handle_address = OpusHAllocateCb(static_cast<std::size_t>(new_capacity)); *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); set_guard(handle->data, byte_count);
handle->size = 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); heap_bytes_used.fetch_add(byte_count, std::memory_order_relaxed);
return reinterpret_cast<void**>(handle); return reinterpret_cast<void**>(handle);
} }
@@ -405,6 +489,9 @@ extern "C" void OpusFreeH(void** opaque_handle) {
if (opaque_handle == nullptr) { if (opaque_handle == nullptr) {
return; return;
} }
if (!unregister_native_handle(opaque_handle)) {
report_guard_failure();
}
unregister_prc_handle(opaque_handle); unregister_prc_handle(opaque_handle);
unregister_compact_handle(opaque_handle); unregister_compact_handle(opaque_handle);
auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle); auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle);
@@ -433,6 +520,9 @@ extern "C" int OpusFChngSizeHCb(void** opaque_handle,
if (opaque_handle == nullptr) { if (opaque_handle == nullptr) {
return 0; return 0;
} }
if (!valid_native_handle(opaque_handle)) {
report_guard_failure();
}
auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle); auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle);
if (!allow_shrink && byte_count <= handle->size) { if (!allow_shrink && byte_count <= handle->size) {
return 1; return 1;
@@ -470,7 +560,7 @@ extern "C" int OpusFChngSizePhqLcb(void*** handle_address,
} }
extern "C" size_t OpusCbOfH(void** opaque_handle) { extern "C" size_t OpusCbOfH(void** opaque_handle) {
return opaque_handle == nullptr return !valid_native_handle(opaque_handle)
? 0 ? 0
: reinterpret_cast<NativeHandle*>(opaque_handle)->size; : reinterpret_cast<NativeHandle*>(opaque_handle)->size;
} }
@@ -480,12 +570,13 @@ extern "C" size_t OpusHeapBytesUsed() {
} }
extern "C" void* OpusDerefH(void** opaque_handle) { extern "C" void* OpusDerefH(void** opaque_handle) {
return opaque_handle == nullptr return !valid_native_handle(opaque_handle)
? nullptr ? nullptr
: reinterpret_cast<NativeHandle*>(opaque_handle)->data; : reinterpret_cast<NativeHandle*>(opaque_handle)->data;
} }
extern "C" void* OpusHpAlloc(const std::size_t byte_count) { extern "C" void* OpusHpAlloc(const std::size_t byte_count) {
if (byte_count > kMaxAllocationBytes) return nullptr;
void* allocation = void* allocation =
HeapAlloc(process_heap(), 0, byte_count == 0 ? 1 : byte_count); HeapAlloc(process_heap(), 0, byte_count == 0 ? 1 : byte_count);
if (allocation != nullptr) { if (allocation != nullptr) {
@@ -500,9 +591,8 @@ extern "C" void* OpusHpAlloc(const std::size_t byte_count) {
extern "C" void OpusFreeHp(void* pointer) { extern "C" void OpusFreeHp(void* pointer) {
if (pointer != nullptr) { if (pointer != nullptr) {
const SIZE_T size = HeapSize(process_heap(), 0, pointer); const SIZE_T size = HeapSize(process_heap(), 0, pointer);
if (size != static_cast<SIZE_T>(-1)) { if (size == static_cast<SIZE_T>(-1)) report_guard_failure();
heap_bytes_used.fetch_sub(size, std::memory_order_relaxed); heap_bytes_used.fetch_sub(size, std::memory_order_relaxed);
}
HeapFree(process_heap(), 0, pointer); HeapFree(process_heap(), 0, pointer);
} }
} }
+2 -1
View File
@@ -325,7 +325,8 @@ int main() {
EndSdm(); EndSdm();
return 23; 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), reinterpret_cast<void*>(ModalRuntimeProbe),
11, 4}; 11, 4};
auto* modal_template_pointer = &modal_template; auto* modal_template_pointer = &modal_template;