Add a helper to copy a ByteString to a buffer

We have a few repetitive patterns similar to:
  if (buffer && len <= buflen)
    memcpy(buffer, byteString.c_str(), len);
  return len;

Replace them with a new helper, NulTerminateMaybeCopyAndReturnLength().
Add tests for the new helper.

Bug: pdfium:1514
Change-Id: I0d35cc775f6963833524c2eab1847aafa8e29a10
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/68695
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Daniel Hosseinian <dhoss@chromium.org>
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index 9eb9562..e0a9983 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -108,6 +108,7 @@
 
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "cpdfsdk_helpers_unittest.cpp",
     "fpdf_annot_unittest.cpp",
     "fpdf_catalog_unittest.cpp",
     "fpdf_doc_unittest.cpp",
diff --git a/fpdfsdk/cpdfsdk_helpers.cpp b/fpdfsdk/cpdfsdk_helpers.cpp
index db11927..2d3a95b 100644
--- a/fpdfsdk/cpdfsdk_helpers.cpp
+++ b/fpdfsdk/cpdfsdk_helpers.cpp
@@ -277,6 +277,15 @@
   return {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f};
 }
 
+unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text,
+                                                   void* buffer,
+                                                   unsigned long buflen) {
+  unsigned long len = text.GetLength() + 1;
+  if (buffer && len <= buflen)
+    memcpy(buffer, text.c_str(), len);
+  return len;
+}
+
 unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                   void* buffer,
                                                   unsigned long buflen) {
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index e32afc2..74ae582 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -238,6 +238,10 @@
 CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix);
 FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix);
 
+unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text,
+                                                   void* buffer,
+                                                   unsigned long buflen);
+
 unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                   void* buffer,
                                                   unsigned long buflen);
diff --git a/fpdfsdk/cpdfsdk_helpers_unittest.cpp b/fpdfsdk/cpdfsdk_helpers_unittest.cpp
new file mode 100644
index 0000000..6b65fa8
--- /dev/null
+++ b/fpdfsdk/cpdfsdk_helpers_unittest.cpp
@@ -0,0 +1,40 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CPDFSDK_HelpersTest, NulTerminateMaybeCopyAndReturnLength) {
+  {
+    const ByteString to_be_copied("toBeCopied");
+    constexpr size_t kExpectedToBeCopiedLen = 10;
+    ASSERT_EQ(kExpectedToBeCopiedLen, to_be_copied.GetLength());
+
+    EXPECT_EQ(kExpectedToBeCopiedLen + 1,
+              NulTerminateMaybeCopyAndReturnLength(to_be_copied, nullptr, 0));
+
+    // Buffer should not change if declared length is too short.
+    char buf[kExpectedToBeCopiedLen + 1];
+    memset(buf, 0x42, kExpectedToBeCopiedLen + 1);
+    ASSERT_EQ(kExpectedToBeCopiedLen + 1,
+              NulTerminateMaybeCopyAndReturnLength(to_be_copied, buf,
+                                                   kExpectedToBeCopiedLen));
+    for (char c : buf)
+      EXPECT_EQ(0x42, c);
+
+    // Buffer should copy over if long enough.
+    ASSERT_EQ(kExpectedToBeCopiedLen + 1,
+              NulTerminateMaybeCopyAndReturnLength(to_be_copied, buf,
+                                                   kExpectedToBeCopiedLen + 1));
+    EXPECT_EQ(to_be_copied, ByteString(buf));
+  }
+  {
+    // Empty ByteString should still copy NUL terminator.
+    const ByteString empty;
+    char buf[1];
+    ASSERT_EQ(1u, NulTerminateMaybeCopyAndReturnLength(empty, buf, 1));
+    EXPECT_EQ(empty, ByteString(buf));
+  }
+}
diff --git a/fpdfsdk/fpdf_doc.cpp b/fpdfsdk/fpdf_doc.cpp
index b0a2324..0a5f593 100644
--- a/fpdfsdk/fpdf_doc.cpp
+++ b/fpdfsdk/fpdf_doc.cpp
@@ -192,10 +192,7 @@
 
   CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action));
   ByteString path = cAction.GetFilePath().ToUTF8();
-  unsigned long len = path.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, path.c_str(), len);
-  return len;
+  return NulTerminateMaybeCopyAndReturnLength(path, buffer, buflen);
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
diff --git a/fpdfsdk/fpdf_editimg.cpp b/fpdfsdk/fpdf_editimg.cpp
index ecd11d1..1e0e560 100644
--- a/fpdfsdk/fpdf_editimg.cpp
+++ b/fpdfsdk/fpdf_editimg.cpp
@@ -292,10 +292,7 @@
   else
     bsFilter = pFilter->AsArray()->GetStringAt(index);
 
-  unsigned long len = bsFilter.GetLength() + 1;
-  if (buffer && len <= buflen)
-    memcpy(buffer, bsFilter.c_str(), len);
-  return len;
+  return NulTerminateMaybeCopyAndReturnLength(bsFilter, buffer, buflen);
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index b405a00..7266301 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -989,10 +989,7 @@
   if (!bsVal)
     return 0;
 
-  unsigned long dwStringLen = bsVal->GetLength() + 1;
-  if (buffer && length >= dwStringLen)
-    memcpy(buffer, bsVal->c_str(), dwStringLen);
-  return dwStringLen;
+  return NulTerminateMaybeCopyAndReturnLength(*bsVal, buffer, length);
 }
 
 FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV