aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2024-02-15 14:17:08 +1100
committerNeil <nyamatongwe@gmail.com>2024-02-15 14:17:08 +1100
commit38d2856d341358bf9c691157ae0ca2e8960e35df (patch)
treea1a187630421e954b65181bb2d2fb4a58d2549ff
parenta6a2fe933e0103464c1a357f987ad10e3bcf6622 (diff)
downloadscintilla-mirror-38d2856d341358bf9c691157ae0ca2e8960e35df.tar.gz
Perform validation of undo state when SCI_SETUNDOCURRENT called, setting status
when invalid.
-rw-r--r--doc/ScintillaDoc.html14
-rw-r--r--src/CellBuffer.cxx4
-rw-r--r--src/CellBuffer.h2
-rw-r--r--src/Document.cxx2
-rw-r--r--src/Document.h2
-rw-r--r--src/UndoHistory.cxx54
-rw-r--r--src/UndoHistory.h5
7 files changed, 68 insertions, 15 deletions
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html
index 12f2be0eb..4a10d280f 100644
--- a/doc/ScintillaDoc.html
+++ b/doc/ScintillaDoc.html
@@ -2063,9 +2063,9 @@ struct Sci_TextToFindFull {
</p>
<p>The history should first be set up with <code>SCI_PUSHUNDOACTIONTYPE</code>, and
- <code>SCI_CHANGELASTUNDOACTIONTEXT</code> then the save, current, and tentativ points set
- with <code>SCI_SETUNDOSAVEPOINT</code>, <code>SCI_SETUNDOCURRENT</code>, and
- <code>SCI_SETUNDOTENTATIVE</code>.
+ <code>SCI_CHANGELASTUNDOACTIONTEXT</code> then the save, current, and tentative points set
+ with <code>SCI_SETUNDOSAVEPOINT</code>, <code>SCI_SETUNDOTENTATIVE</code>, and
+ <code>SCI_SETUNDOCURRENT</code>.
</p>
<p><code>SCI_PUSHUNDOACTIONTYPE(int type, position pos)</code> appends an action to the undo stack
@@ -2073,6 +2073,14 @@ struct Sci_TextToFindFull {
<code>SCI_CHANGELASTUNDOACTIONTEXT(position length, const char *text)</code>.
</p>
+ <p>
+ The last restoration API called should be <code>SCI_SETUNDOCURRENT</code> as this validates
+ the restored history and values against the document. For example, an undo history that could cause a negative
+ document length or inserting / removing text outside the document is invalid.
+ If the restored undo state is invalid then a failure status is set and the undo history cleared.
+ Check for failure with <a class="seealso" href="#SCI_GETSTATUS">SCI_GETSTATUS</a>.
+ </p>
+
<p>The current implementation may only work when the current and save point are the same and there is no tentative point.
</p>
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx
index b3111914e..691a811dd 100644
--- a/src/CellBuffer.cxx
+++ b/src/CellBuffer.cxx
@@ -1160,8 +1160,8 @@ int CellBuffer::UndoSavePoint() const noexcept {
return uh->SavePoint();
}
-void CellBuffer::SetUndoCurrent(int action) noexcept {
- uh->SetCurrent(action);
+void CellBuffer::SetUndoCurrent(int action) {
+ uh->SetCurrent(action, Length());
}
int CellBuffer::UndoCurrent() const noexcept {
diff --git a/src/CellBuffer.h b/src/CellBuffer.h
index 5422e92aa..916937845 100644
--- a/src/CellBuffer.h
+++ b/src/CellBuffer.h
@@ -183,7 +183,7 @@ public:
int UndoActions() const noexcept;
void SetUndoSavePoint(int action) noexcept;
int UndoSavePoint() const noexcept;
- void SetUndoCurrent(int action) noexcept;
+ void SetUndoCurrent(int action);
int UndoCurrent() const noexcept;
void SetUndoTentative(int action) noexcept;
int UndoTentative() const noexcept;
diff --git a/src/Document.cxx b/src/Document.cxx
index 52081e1cd..8ab837daa 100644
--- a/src/Document.cxx
+++ b/src/Document.cxx
@@ -363,7 +363,7 @@ int Document::UndoSavePoint() const noexcept {
return cb.UndoSavePoint();
}
-void Document::SetUndoCurrent(int action) noexcept {
+void Document::SetUndoCurrent(int action) {
cb.SetUndoCurrent(action);
}
diff --git a/src/Document.h b/src/Document.h
index 185066a6c..81cb299c0 100644
--- a/src/Document.h
+++ b/src/Document.h
@@ -409,7 +409,7 @@ public:
int UndoActions() const noexcept;
void SetUndoSavePoint(int action) noexcept;
int UndoSavePoint() const noexcept;
- void SetUndoCurrent(int action) noexcept;
+ void SetUndoCurrent(int action);
int UndoCurrent() const noexcept;
void SetUndoTentative(int action) noexcept;
int UndoTentative() const noexcept;
diff --git a/src/UndoHistory.cxx b/src/UndoHistory.cxx
index 1a956b998..62f871a60 100644
--- a/src/UndoHistory.cxx
+++ b/src/UndoHistory.cxx
@@ -173,6 +173,14 @@ bool UndoActions::AtStart(size_t index) const noexcept {
return !types[index-1].mayCoalesce;
}
+size_t UndoActions::LengthTo(size_t index) const noexcept {
+ size_t sum = 0;
+ for (size_t act = 0; act < index; act++) {
+ sum += lengths.ValueAt(act);
+ }
+ return sum;
+}
+
void ScrapStack::Clear() noexcept {
stack.clear();
current = 0;
@@ -395,15 +403,49 @@ bool UndoHistory::AfterOrAtDetachPoint() const noexcept {
return detach && (*detach <= currentAction);
}
-void UndoHistory::SetCurrent(int action) noexcept {
- // Find position in scraps for action
- memory = {};
- size_t position = 0;
+intptr_t UndoHistory::Delta(int action) noexcept {
+ intptr_t sizeChange = 0;
for (int act = 0; act < action; act++) {
- position += actions.lengths.ValueAt(act);
+ const intptr_t lengthChange = actions.lengths.SignedValueAt(act);
+ sizeChange += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange;
+ }
+ return sizeChange;
+}
+
+bool UndoHistory::Validate(intptr_t lengthDocument) noexcept {
+ // Check history for validity
+ const intptr_t sizeChange = Delta(currentAction);
+ if (sizeChange > lengthDocument) {
+ // Current document size too small for changes made in undo history.
+ return false;
}
- scraps->SetCurrent(position);
+ const intptr_t lengthOriginal = lengthDocument - sizeChange;
+ intptr_t lengthCurrent = lengthOriginal;
+ for (int act = 0; act < actions.SSize(); act++) {
+ const intptr_t lengthChange = actions.lengths.SignedValueAt(act);
+ if (actions.positions.SignedValueAt(act) > lengthCurrent) {
+ // Change outside document.
+ return false;
+ }
+ lengthCurrent += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange;
+ if (lengthCurrent < 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void UndoHistory::SetCurrent(int action, intptr_t lengthDocument) {
+ // Find position in scraps for action
+ memory = {};
+ const size_t lengthSum = actions.LengthTo(action);
+ scraps->SetCurrent(lengthSum);
currentAction = action;
+ if (!Validate(lengthDocument)) {
+ currentAction = 0;
+ DeleteUndoHistory();
+ throw std::runtime_error("UndoHistory::SetCurrent: invalid undo history.");
+ }
}
int UndoHistory::Current() const noexcept {
diff --git a/src/UndoHistory.h b/src/UndoHistory.h
index 1186a1e0e..13aaca98c 100644
--- a/src/UndoHistory.h
+++ b/src/UndoHistory.h
@@ -53,6 +53,7 @@ struct UndoActions {
[[nodiscard]] intptr_t SSize() const noexcept;
void Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_);
[[nodiscard]] bool AtStart(size_t index) const noexcept;
+ [[nodiscard]] size_t LengthTo(size_t index) const noexcept;
};
class ScrapStack {
@@ -112,7 +113,9 @@ public:
bool AfterDetachPoint() const noexcept;
bool AfterOrAtDetachPoint() const noexcept;
- void SetCurrent(int action) noexcept;
+ [[nodiscard]] intptr_t Delta(int action) noexcept;
+ [[nodiscard]] bool Validate(intptr_t lengthDocument) noexcept;
+ void SetCurrent(int action, intptr_t lengthDocument);
[[nodiscard]] int Current() const noexcept;
[[nodiscard]] int Type(int action) const noexcept;
[[nodiscard]] Sci::Position Position(int action) const noexcept;