Repository: qpdf/qpdf (cpp)

Issue:
Some CRTs don't support 200 open files, resulting at least in failing tests.
I'm updating QPDF from 6.0 to 8.4 and one of the new features is that it automatically decides if to keep files open or not. The currently hard coded limit is 200, but my Embarcadero C++Builder [only supports 50 files open](https://stackoverflow.com/a/46928454/2055163) at the same time for legacy 32 Bit apps. I came across that issue in my own app merging PDFs already and simply fixed it by only opening 35 files at a time at most, so don't run into actual problems with using QPDF itself.

But you have added tests based on the knowledge of the 200 open files and the following fails in my case:

	$td->runtest("don't disable keep files open",
				 {$td->COMMAND =>
					  "qpdf --verbose --static-id --empty" .
					  " --pages 1*kfo.pdf -- a.pdf"},
				 {$td->FILE => "enable-kfo.out", $td->EXIT_STATUS => 0},
				 $td->NORMALIZE_NEWLINES);

The reason simply is that the used file name expands to more than 50 files and after file 47 they can't be opened anymore. This can easily be seen in the logs:

	--> BEGIN ACTUAL OUTPUT <--
	qpdf: selecting --keep-open-files=y
	qpdf: processing 100-kfo.pdf
	qpdf: processing 101-kfo.pdf
	qpdf: processing 102-kfo.pdf
	qpdf: processing 103-kfo.pdf
	qpdf: processing 104-kfo.pdf
	qpdf: processing 105-kfo.pdf
	qpdf: processing 106-kfo.pdf
	qpdf: processing 107-kfo.pdf
	qpdf: processing 108-kfo.pdf
	qpdf: processing 109-kfo.pdf
	qpdf: processing 110-kfo.pdf
	qpdf: processing 111-kfo.pdf
	qpdf: processing 112-kfo.pdf
	qpdf: processing 113-kfo.pdf
	qpdf: processing 114-kfo.pdf
	qpdf: processing 115-kfo.pdf
	qpdf: processing 116-kfo.pdf
	qpdf: processing 117-kfo.pdf
	qpdf: processing 118-kfo.pdf
	qpdf: processing 119-kfo.pdf
	qpdf: processing 120-kfo.pdf
	qpdf: processing 121-kfo.pdf
	qpdf: processing 122-kfo.pdf
	qpdf: processing 123-kfo.pdf
	qpdf: processing 124-kfo.pdf
	qpdf: processing 125-kfo.pdf
	qpdf: processing 126-kfo.pdf
	qpdf: processing 127-kfo.pdf
	qpdf: processing 128-kfo.pdf
	qpdf: processing 129-kfo.pdf
	qpdf: processing 130-kfo.pdf
	qpdf: processing 131-kfo.pdf
	qpdf: processing 132-kfo.pdf
	qpdf: processing 133-kfo.pdf
	qpdf: processing 134-kfo.pdf
	qpdf: processing 135-kfo.pdf
	qpdf: processing 136-kfo.pdf
	qpdf: processing 137-kfo.pdf
	qpdf: processing 138-kfo.pdf
	qpdf: processing 139-kfo.pdf
	qpdf: processing 140-kfo.pdf
	qpdf: processing 141-kfo.pdf
	qpdf: processing 142-kfo.pdf
	qpdf: processing 143-kfo.pdf
	qpdf: processing 144-kfo.pdf
	qpdf: processing 145-kfo.pdf
	qpdf: processing 146-kfo.pdf
	qpdf: processing 147-kfo.pdf
	open 147-kfo.pdf: Error 0

	--> END ACTUAL OUTPUT <--

It's 50 files minus STD* and stuff, so breaking on file `147-kfo.pdf` makes sense to me. If I simply change the files to `11*kfo.pdf` instead, reducing the number to 10, and after adopting the expected output as well, the test passes. I'll additionally provide a PR implementing this later as well.

I would be very thankful if you could address this issue somehow. I currently preferred changing the test simply because in your two other tests you are only using a subset of those 200 possible files as well and I need to get things going.

But because your chosen limit of 200 is some arbitrary value anyway, how about reducing it to a safer value like 25 or 35 like I use? That would need adoption of manuals etc. as well.

Checking things for the current runtime wouldn't help me solve this problem necessarily as well, because like can be read in my link to SO, my compiler lacks lots of functions suggested for this purpose. And I'm not sure what of the things my compiler provides is usable for others.

So I guess it's either changing the tests only or simply reducing the hard coded value. Thanks!

Pull request description:
Don't open more than 50 files.
Embarcadero C++Builder doesn't support more than 50 files open at the same time for legacy 32 Bit apps, which makes a test fail trying to open more than that many files. This changes the number of open files for that test to far less to make the test succeed. Alternatively one could reduce the hard coded number of 200 in QPDF itself, which I didn't do currently because it needs adoption of manuals etc. and is something which needs to be discussed with the author of QPDF. I guess chances are better to get the test changed upstream.

This fixes #288: https://github.com/qpdf/qpdf/issues/288

Patch:
diff --git a/ChangeLog b/ChangeLog
index 62c20948..9f33d4cf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2019-03-11  Jay Berkenbilt  <ejb@ql.org>
+
+	* JSON serialization: add missing leading 0 to decimal values
+	between -1 and 1. Fixes #308.
+
 2019-02-01  Jay Berkenbilt  <ejb@ql.org>
 
 	* 8.4.0: release
diff --git a/README-maintainer b/README-maintainer
index ec257178..4b47a809 100644
--- a/README-maintainer
+++ b/README-maintainer
@@ -129,7 +129,7 @@ gpg --detach-sign --armor qpdf-$version.tar.gz
 \rm -f *.{md5,sha1,sha512}
 files=(*)
 for i in md5 sha1 sha512; do
-  ${i}sum $files >| qpdf-$version.$i
+  ${i}sum ${files[*]} >| qpdf-$version.$i
   gpg --clearsign --armor qpdf-$version.$i
   mv qpdf-$version.$i.asc qpdf-$version.$i
 done
diff --git a/TODO b/TODO
index 0bd28ad7..fe6f1569 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,9 @@
+Before Next Release
+===================
+
+ * Make sure it is clear to downloaders where they can find my public
+   key for verifying downloads.
+
 Soon
 ====
 
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index 634c9bd1..6de51ef3 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -30,8 +30,10 @@
 #include <map>
 #include <list>
 #include <iostream>
+#include <vector>
 
 #include <qpdf/QPDFExc.hh>
+#include <qpdf/QPDFObjectHandle.hh>
 #include <qpdf/QPDFObjGen.hh>
 #include <qpdf/QPDFXRefEntry.hh>
 #include <qpdf/QPDFObjectHandle.hh>
@@ -1298,6 +1300,7 @@ class QPDF
     class Members
     {
         friend class QPDF;
+        friend class ResolveRecorder;
 
       public:
         QPDF_DLL
diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh
index b181bd79..978cddff 100644
--- a/include/qpdf/QPDFObjectHandle.hh
+++ b/include/qpdf/QPDFObjectHandle.hh
@@ -1028,6 +1028,7 @@ class QPDFObjectHandle
 
     class Members
     {
+        friend class ObjAccessor;
         friend class QPDFObjectHandle;
 
       public:
diff --git a/libqpdf/QPDF_Real.cc b/libqpdf/QPDF_Real.cc
index b28c2f70..b4c8283f 100644
--- a/libqpdf/QPDF_Real.cc
+++ b/libqpdf/QPDF_Real.cc
@@ -25,7 +25,30 @@ QPDF_Real::unparse()
 JSON
 QPDF_Real::getJSON()
 {
-    return JSON::makeNumber(this->val);
+    // While PDF allows .x or -.x, JSON does not. Rather than
+    // convering from string to double and back, just handle this as a
+    // special case for JSON.
+    std::string result;
+    if (this->val.length() == 0)
+    {
+        // Can't really happen...
+        result = "0";
+    }
+    else if (this->val.at(0) == '.')
+    {
+        result = "0" + this->val;
+    }
+    else if ((this->val.length() >= 2) &&
+             (this->val.at(0) == '-') &&
+             (this->val.at(1) == '.'))
+    {
+        result = "-0." + this->val.substr(2);
+    }
+    else
+    {
+        result = this->val;
+    }
+    return JSON::makeNumber(result);
 }
 
 QPDFObject::object_type_e
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index 58646ade..7ea3f5e7 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -529,7 +529,9 @@ QUtil::hex_decode(std::string const& input)
 void
 QUtil::binary_stdout()
 {
-#ifdef _WIN32
+#if defined(_WIN32) && defined(__BORLANDC__)
+     setmode(_fileno(stdout), _O_BINARY);
+#elif defined(_WIN32)
     _setmode(_fileno(stdout), _O_BINARY);
 #endif
 }
@@ -537,7 +539,9 @@ QUtil::binary_stdout()
 void
 QUtil::binary_stdin()
 {
-#ifdef _WIN32
+#if defined(_WIN32) && defined(__BORLANDC__)
+     setmode(_fileno(stdin), _O_BINARY);
+#elif defined(_WIN32)
     _setmode(_fileno(stdin), _O_BINARY);
 #endif
 }
@@ -918,7 +922,9 @@ QUtil::read_lines_from_file(std::istream& in)
 int
 QUtil::strcasecmp(char const *s1, char const *s2)
 {
-#ifdef _WIN32
+#if defined(_WIN32) && defined(__BORLANDC__)
+    return stricmp(s1, s2);
+#elif defined(_WIN32)
     return _stricmp(s1, s2);
 #else
     return ::strcasecmp(s1, s2);
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index 17484b3f..4ec0a48c 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -3836,7 +3836,7 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next)
         h_obj.isInteger() ? h_obj.getIntValue() : h_obj.getNumericValue());
     std::string colorspace = (colorspace_obj.isName() ?
                               colorspace_obj.getName() :
-                              "");
+                              std::string());
     int components = 0;
     J_COLOR_SPACE cs = JCS_UNKNOWN;
     if (colorspace == "/DeviceRGB")


Test patch:
diff --git a/libtests/json.cc b/libtests/json.cc
index 1a9123ad..e3086c18 100644
--- a/libtests/json.cc
+++ b/libtests/json.cc
@@ -3,7 +3,7 @@
 #include <iostream>
 #include <assert.h>
 
-static void check(JSON& j, std::string const& exp)
+static void check(JSON const& j, std::string const& exp)
 {
     if (exp != j.unparse())
     {
@@ -69,6 +69,10 @@ static void test_main()
           "  ],\n"
           "  \"yes\": false\n"
           "}");
+    check(QPDFObjectHandle::newReal("0.12").getJSON(), "0.12");
+    check(QPDFObjectHandle::newReal(".34").getJSON(), "0.34");
+    check(QPDFObjectHandle::newReal("-0.56").getJSON(), "-0.56");
+    check(QPDFObjectHandle::newReal("-.78").getJSON(), "-0.78");
 }
 
 static void check_schema(JSON& obj, JSON& schema, bool exp,
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index b6b33977..a968404c 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -1675,6 +1675,7 @@ $n_tests += 4;
     for (my $i = 1; $i <= 201; ++$i)
     {
         open(F, sprintf(">%03d-kfo.pdf", $i)) or die;
+        binmode F;
         print F $content;
         close(F);
     }
@@ -1688,7 +1689,7 @@ $td->runtest("disable keep files open",
 $td->runtest("don't disable keep files open",
              {$td->COMMAND =>
                   "qpdf --verbose --static-id --empty" .
-                  " --pages 1*kfo.pdf -- a.pdf"},
+                  " --pages 11*kfo.pdf -- a.pdf"},
              {$td->FILE => "enable-kfo.out", $td->EXIT_STATUS => 0},
              $td->NORMALIZE_NEWLINES);
 $td->runtest("explict keep files open",
diff --git a/qpdf/qtest/qpdf/enable-kfo.out b/qpdf/qtest/qpdf/enable-kfo.out
index b889638c..82e1b7c4 100644
--- a/qpdf/qtest/qpdf/enable-kfo.out
+++ b/qpdf/qtest/qpdf/enable-kfo.out
@@ -1,14 +1,4 @@
 qpdf: selecting --keep-open-files=y
-qpdf: processing 100-kfo.pdf
-qpdf: processing 101-kfo.pdf
-qpdf: processing 102-kfo.pdf
-qpdf: processing 103-kfo.pdf
-qpdf: processing 104-kfo.pdf
-qpdf: processing 105-kfo.pdf
-qpdf: processing 106-kfo.pdf
-qpdf: processing 107-kfo.pdf
-qpdf: processing 108-kfo.pdf
-qpdf: processing 109-kfo.pdf
 qpdf: processing 110-kfo.pdf
 qpdf: processing 111-kfo.pdf
 qpdf: processing 112-kfo.pdf
@@ -19,97 +9,7 @@ qpdf: processing 116-kfo.pdf
 qpdf: processing 117-kfo.pdf
 qpdf: processing 118-kfo.pdf
 qpdf: processing 119-kfo.pdf
-qpdf: processing 120-kfo.pdf
-qpdf: processing 121-kfo.pdf
-qpdf: processing 122-kfo.pdf
-qpdf: processing 123-kfo.pdf
-qpdf: processing 124-kfo.pdf
-qpdf: processing 125-kfo.pdf
-qpdf: processing 126-kfo.pdf
-qpdf: processing 127-kfo.pdf
-qpdf: processing 128-kfo.pdf
-qpdf: processing 129-kfo.pdf
-qpdf: processing 130-kfo.pdf
-qpdf: processing 131-kfo.pdf
-qpdf: processing 132-kfo.pdf
-qpdf: processing 133-kfo.pdf
-qpdf: processing 134-kfo.pdf
-qpdf: processing 135-kfo.pdf
-qpdf: processing 136-kfo.pdf
-qpdf: processing 137-kfo.pdf
-qpdf: processing 138-kfo.pdf
-qpdf: processing 139-kfo.pdf
-qpdf: processing 140-kfo.pdf
-qpdf: processing 141-kfo.pdf
-qpdf: processing 142-kfo.pdf
-qpdf: processing 143-kfo.pdf
-qpdf: processing 144-kfo.pdf
-qpdf: processing 145-kfo.pdf
-qpdf: processing 146-kfo.pdf
-qpdf: processing 147-kfo.pdf
-qpdf: processing 148-kfo.pdf
-qpdf: processing 149-kfo.pdf
-qpdf: processing 150-kfo.pdf
-qpdf: processing 151-kfo.pdf
-qpdf: processing 152-kfo.pdf
-qpdf: processing 153-kfo.pdf
-qpdf: processing 154-kfo.pdf
-qpdf: processing 155-kfo.pdf
-qpdf: processing 156-kfo.pdf
-qpdf: processing 157-kfo.pdf
-qpdf: processing 158-kfo.pdf
-qpdf: processing 159-kfo.pdf
-qpdf: processing 160-kfo.pdf
-qpdf: processing 161-kfo.pdf
-qpdf: processing 162-kfo.pdf
-qpdf: processing 163-kfo.pdf
-qpdf: processing 164-kfo.pdf
-qpdf: processing 165-kfo.pdf
-qpdf: processing 166-kfo.pdf
-qpdf: processing 167-kfo.pdf
-qpdf: processing 168-kfo.pdf
-qpdf: processing 169-kfo.pdf
-qpdf: processing 170-kfo.pdf
-qpdf: processing 171-kfo.pdf
-qpdf: processing 172-kfo.pdf
-qpdf: processing 173-kfo.pdf
-qpdf: processing 174-kfo.pdf
-qpdf: processing 175-kfo.pdf
-qpdf: processing 176-kfo.pdf
-qpdf: processing 177-kfo.pdf
-qpdf: processing 178-kfo.pdf
-qpdf: processing 179-kfo.pdf
-qpdf: processing 180-kfo.pdf
-qpdf: processing 181-kfo.pdf
-qpdf: processing 182-kfo.pdf
-qpdf: processing 183-kfo.pdf
-qpdf: processing 184-kfo.pdf
-qpdf: processing 185-kfo.pdf
-qpdf: processing 186-kfo.pdf
-qpdf: processing 187-kfo.pdf
-qpdf: processing 188-kfo.pdf
-qpdf: processing 189-kfo.pdf
-qpdf: processing 190-kfo.pdf
-qpdf: processing 191-kfo.pdf
-qpdf: processing 192-kfo.pdf
-qpdf: processing 193-kfo.pdf
-qpdf: processing 194-kfo.pdf
-qpdf: processing 195-kfo.pdf
-qpdf: processing 196-kfo.pdf
-qpdf: processing 197-kfo.pdf
-qpdf: processing 198-kfo.pdf
-qpdf: processing 199-kfo.pdf
 qpdf: removing unreferenced pages from primary input
-qpdf: adding pages from 100-kfo.pdf
-qpdf: adding pages from 101-kfo.pdf
-qpdf: adding pages from 102-kfo.pdf
-qpdf: adding pages from 103-kfo.pdf
-qpdf: adding pages from 104-kfo.pdf
-qpdf: adding pages from 105-kfo.pdf
-qpdf: adding pages from 106-kfo.pdf
-qpdf: adding pages from 107-kfo.pdf
-qpdf: adding pages from 108-kfo.pdf
-qpdf: adding pages from 109-kfo.pdf
 qpdf: adding pages from 110-kfo.pdf
 qpdf: adding pages from 111-kfo.pdf
 qpdf: adding pages from 112-kfo.pdf
@@ -120,84 +20,4 @@ qpdf: adding pages from 116-kfo.pdf
 qpdf: adding pages from 117-kfo.pdf
 qpdf: adding pages from 118-kfo.pdf
 qpdf: adding pages from 119-kfo.pdf
-qpdf: adding pages from 120-kfo.pdf
-qpdf: adding pages from 121-kfo.pdf
-qpdf: adding pages from 122-kfo.pdf
-qpdf: adding pages from 123-kfo.pdf
-qpdf: adding pages from 124-kfo.pdf
-qpdf: adding pages from 125-kfo.pdf
-qpdf: adding pages from 126-kfo.pdf
-qpdf: adding pages from 127-kfo.pdf
-qpdf: adding pages from 128-kfo.pdf
-qpdf: adding pages from 129-kfo.pdf
-qpdf: adding pages from 130-kfo.pdf
-qpdf: adding pages from 131-kfo.pdf
-qpdf: adding pages from 132-kfo.pdf
-qpdf: adding pages from 133-kfo.pdf
-qpdf: adding pages from 134-kfo.pdf
-qpdf: adding pages from 135-kfo.pdf
-qpdf: adding pages from 136-kfo.pdf
-qpdf: adding pages from 137-kfo.pdf
-qpdf: adding pages from 138-kfo.pdf
-qpdf: adding pages from 139-kfo.pdf
-qpdf: adding pages from 140-kfo.pdf
-qpdf: adding pages from 141-kfo.pdf
-qpdf: adding pages from 142-kfo.pdf
-qpdf: adding pages from 143-kfo.pdf
-qpdf: adding pages from 144-kfo.pdf
-qpdf: adding pages from 145-kfo.pdf
-qpdf: adding pages from 146-kfo.pdf
-qpdf: adding pages from 147-kfo.pdf
-qpdf: adding pages from 148-kfo.pdf
-qpdf: adding pages from 149-kfo.pdf
-qpdf: adding pages from 150-kfo.pdf
-qpdf: adding pages from 151-kfo.pdf
-qpdf: adding pages from 152-kfo.pdf
-qpdf: adding pages from 153-kfo.pdf
-qpdf: adding pages from 154-kfo.pdf
-qpdf: adding pages from 155-kfo.pdf
-qpdf: adding pages from 156-kfo.pdf
-qpdf: adding pages from 157-kfo.pdf
-qpdf: adding pages from 158-kfo.pdf
-qpdf: adding pages from 159-kfo.pdf
-qpdf: adding pages from 160-kfo.pdf
-qpdf: adding pages from 161-kfo.pdf
-qpdf: adding pages from 162-kfo.pdf
-qpdf: adding pages from 163-kfo.pdf
-qpdf: adding pages from 164-kfo.pdf
-qpdf: adding pages from 165-kfo.pdf
-qpdf: adding pages from 166-kfo.pdf
-qpdf: adding pages from 167-kfo.pdf
-qpdf: adding pages from 168-kfo.pdf
-qpdf: adding pages from 169-kfo.pdf
-qpdf: adding pages from 170-kfo.pdf
-qpdf: adding pages from 171-kfo.pdf
-qpdf: adding pages from 172-kfo.pdf
-qpdf: adding pages from 173-kfo.pdf
-qpdf: adding pages from 174-kfo.pdf
-qpdf: adding pages from 175-kfo.pdf
-qpdf: adding pages from 176-kfo.pdf
-qpdf: adding pages from 177-kfo.pdf
-qpdf: adding pages from 178-kfo.pdf
-qpdf: adding pages from 179-kfo.pdf
-qpdf: adding pages from 180-kfo.pdf
-qpdf: adding pages from 181-kfo.pdf
-qpdf: adding pages from 182-kfo.pdf
-qpdf: adding pages from 183-kfo.pdf
-qpdf: adding pages from 184-kfo.pdf
-qpdf: adding pages from 185-kfo.pdf
-qpdf: adding pages from 186-kfo.pdf
-qpdf: adding pages from 187-kfo.pdf
-qpdf: adding pages from 188-kfo.pdf
-qpdf: adding pages from 189-kfo.pdf
-qpdf: adding pages from 190-kfo.pdf
-qpdf: adding pages from 191-kfo.pdf
-qpdf: adding pages from 192-kfo.pdf
-qpdf: adding pages from 193-kfo.pdf
-qpdf: adding pages from 194-kfo.pdf
-qpdf: adding pages from 195-kfo.pdf
-qpdf: adding pages from 196-kfo.pdf
-qpdf: adding pages from 197-kfo.pdf
-qpdf: adding pages from 198-kfo.pdf
-qpdf: adding pages from 199-kfo.pdf
 qpdf: wrote file a.pdf
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 462a8a23..d3abbe46 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -1631,7 +1631,7 @@ void runtest(int n, char const* filename1, char const* arg2)
                 QPDFFormFieldObjectHelper parent(node.getParent());
                 std::cout << "  Parent: "
                           << (parent.isNull()
-                              ? "none"
+                              ? std::string("none")
                               : parent.getObjectHandle().unparse())
                           << std::endl;
                 node = parent;