Commit 76b1659177327a64037bf36d7f3e15a73d86bbed

Authored by Jay Berkenbilt
1 parent 14fe2e6d

enhance PointerHolder so that it can explicitly be told to use delete [] instead…

… of delete, thus making it useful to run valgrind over qpdf during its test suite
README.maintainer
1 Release Reminders 1 Release Reminders
2 ================= 2 =================
3 3
  4 + * Consider running tests with latest gcc and/or valgrind. To do
  5 + this, replace, build with debugging and without shared libraries.
  6 + In build, create z and move each executable into z. Then create a
  7 + script called exec-z that contains:
  8 +
  9 + #!/bin/sh
  10 + exec valgrind --suppressions=/tmp/a.supp -q \
  11 + `dirname $0`/z/`basename $0` ${1+"$@"}
  12 +
  13 + Symlink exec-v to each executable. /tmp/a.supp can be populated
  14 + with suppressions for libraries, for example:
  15 +
  16 + {
  17 + zlib1
  18 + Memcheck:Cond
  19 + fun:inflateReset2
  20 + fun:inflateInit2_
  21 + }
  22 +
  23 + You can generate these by running valgrind with --gen-suppressions=yes.
  24 +
4 * Check all open issues in the sourceforge trackers. 25 * Check all open issues in the sourceforge trackers.
5 26
6 * If any interfaces were added or changed, check C API to see whether 27 * If any interfaces were added or changed, check C API to see whether
@@ -3,8 +3,6 @@ @@ -3,8 +3,6 @@
3 3
4 * Provide an example of using replace and swap. Maybe. 4 * Provide an example of using replace and swap. Maybe.
5 5
6 - * Add C API for writing to memory if possible  
7 -  
8 General 6 General
9 ======= 7 =======
10 8
include/qpdf/PointerHolder.hh
@@ -8,8 +8,6 @@ @@ -8,8 +8,6 @@
8 #ifndef __POINTERHOLDER_HH__ 8 #ifndef __POINTERHOLDER_HH__
9 #define __POINTERHOLDER_HH__ 9 #define __POINTERHOLDER_HH__
10 10
11 -#include <iostream>  
12 -  
13 // This class is basically boost::shared_pointer but predates that by 11 // This class is basically boost::shared_pointer but predates that by
14 // several years. 12 // several years.
15 13
@@ -45,43 +43,42 @@ class PointerHolder @@ -45,43 +43,42 @@ class PointerHolder
45 class Data 43 class Data
46 { 44 {
47 public: 45 public:
48 - Data(T* pointer, bool tracing) : 46 + Data(T* pointer, bool array) :
49 pointer(pointer), 47 pointer(pointer),
50 - tracing(tracing), 48 + array(array),
51 refcount(0) 49 refcount(0)
52 { 50 {
53 - static int next_id = 0;  
54 - this->unique_id = ++next_id;  
55 } 51 }
56 ~Data() 52 ~Data()
57 { 53 {
58 - if (this->tracing) 54 + if (array)
59 { 55 {
60 - std::cerr << "PointerHolder deleting pointer "  
61 - << (void*)pointer  
62 - << std::endl; 56 + delete [] this->pointer;
63 } 57 }
64 - delete this->pointer;  
65 - if (this->tracing) 58 + else
66 { 59 {
67 - std::cerr << "PointerHolder done deleting pointer "  
68 - << (void*)pointer  
69 - << std::endl; 60 + delete this->pointer;
70 } 61 }
71 } 62 }
72 T* pointer; 63 T* pointer;
73 - bool tracing; 64 + bool array;
74 int refcount; 65 int refcount;
75 - int unique_id;  
76 private: 66 private:
77 Data(Data const&); 67 Data(Data const&);
78 Data& operator=(Data const&); 68 Data& operator=(Data const&);
79 }; 69 };
80 70
81 public: 71 public:
  72 + // "tracing" is not used but is kept for interface backward compatbility
82 PointerHolder(T* pointer = 0, bool tracing = false) 73 PointerHolder(T* pointer = 0, bool tracing = false)
83 { 74 {
84 - this->init(new Data(pointer, tracing)); 75 + this->init(new Data(pointer, false));
  76 + }
  77 + // Special constructor indicating to free memory with delete []
  78 + // instead of delete
  79 + PointerHolder(bool, T* pointer)
  80 + {
  81 + this->init(new Data(pointer, true));
85 } 82 }
86 PointerHolder(PointerHolder const& rhs) 83 PointerHolder(PointerHolder const& rhs)
87 { 84 {
@@ -148,12 +145,6 @@ class PointerHolder @@ -148,12 +145,6 @@ class PointerHolder
148 this->data = data; 145 this->data = data;
149 { 146 {
150 ++this->data->refcount; 147 ++this->data->refcount;
151 - if (this->data->tracing)  
152 - {  
153 - std::cerr << "PointerHolder " << this->data->unique_id  
154 - << " refcount increased to " << this->data->refcount  
155 - << std::endl;  
156 - }  
157 } 148 }
158 } 149 }
159 void copy(PointerHolder const& rhs) 150 void copy(PointerHolder const& rhs)
@@ -168,13 +159,6 @@ class PointerHolder @@ -168,13 +159,6 @@ class PointerHolder
168 { 159 {
169 gone = true; 160 gone = true;
170 } 161 }
171 - if (this->data->tracing)  
172 - {  
173 - std::cerr << "PointerHolder " << this->data->unique_id  
174 - << " refcount decreased to "  
175 - << this->data->refcount  
176 - << std::endl;  
177 - }  
178 } 162 }
179 if (gone) 163 if (gone)
180 { 164 {
include/qpdf/qpdf-c.h
@@ -285,6 +285,25 @@ extern &quot;C&quot; { @@ -285,6 +285,25 @@ extern &quot;C&quot; {
285 QPDF_DLL 285 QPDF_DLL
286 QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename); 286 QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename);
287 287
  288 + /* Initialize for writing but indicate that the PDF file should be
  289 + * written to memory. Call qpdf_get_buffer_length and
  290 + * qpdf_get_buffer to retrieve the resulting buffer. The memory
  291 + * containing the PDF file will be destroyed when qpdf_cleanup is
  292 + * called.
  293 + */
  294 + QPDF_DLL
  295 + QPDF_ERROR_CODE qpdf_init_write_memory(qpdf_data qpdf);
  296 +
  297 + /* Retrieve the buffer used if the file was written to memory.
  298 + * qpdf_get_buffer returns a null pointer if data was not written
  299 + * to memory. The memory is freed when qpdf_cleanup is called or
  300 + * if a subsequent call to qpdf_init_write or
  301 + * qpdf_init_write_memory is called. */
  302 + QPDF_DLL
  303 + unsigned long qpdf_get_buffer_length(qpdf_data qpdf);
  304 + QPDF_DLL
  305 + unsigned char const* qpdf_get_buffer(qpdf_data qpdf);
  306 +
288 QPDF_DLL 307 QPDF_DLL
289 void qpdf_set_object_stream_mode(qpdf_data qpdf, 308 void qpdf_set_object_stream_mode(qpdf_data qpdf,
290 enum qpdf_object_stream_e mode); 309 enum qpdf_object_stream_e mode);
libqpdf/QPDF.cc
@@ -403,10 +403,9 @@ QPDF::parse(char const* password) @@ -403,10 +403,9 @@ QPDF::parse(char const* password)
403 this->file->rewind(); 403 this->file->rewind();
404 } 404 }
405 char* buf = new char[tbuf_size + 1]; 405 char* buf = new char[tbuf_size + 1];
406 - // Put buf in a PointerHolder to guarantee deletion of buf. This  
407 - // calls delete rather than delete [], but it's okay since buf is  
408 - // an array of fundamental types.  
409 - PointerHolder<char> b(buf); 406 + // Put buf in an array-style PointerHolder to guarantee deletion
  407 + // of buf.
  408 + PointerHolder<char> b(true, buf);
410 memset(buf, '\0', tbuf_size + 1); 409 memset(buf, '\0', tbuf_size + 1);
411 this->file->read(buf, tbuf_size); 410 this->file->read(buf, tbuf_size);
412 411
libqpdf/QPDF_encryption.cc
@@ -589,12 +589,9 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -589,12 +589,9 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
589 { 589 {
590 QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); 590 QTC::TC("qpdf", "QPDF_encryption rc4 decode string");
591 unsigned int vlen = str.length(); 591 unsigned int vlen = str.length();
592 - // Using PointerHolder will cause a new char[] to be deleted  
593 - // with delete instead of delete [], but it's okay since the  
594 - // array is of a fundamental type, so there is no destructor  
595 - // to be called. Using PointerHolder guarantees that tmp will 592 + // Using PointerHolder guarantees that tmp will
596 // be freed even if rc4.process throws an exception. 593 // be freed even if rc4.process throws an exception.
597 - PointerHolder<char> tmp = QUtil::copy_string(str); 594 + PointerHolder<char> tmp(true, QUtil::copy_string(str));
598 RC4 rc4((unsigned char const*)key.c_str(), key.length()); 595 RC4 rc4((unsigned char const*)key.c_str(), key.length());
599 rc4.process((unsigned char*)tmp.getPointer(), vlen); 596 rc4.process((unsigned char*)tmp.getPointer(), vlen);
600 str = std::string(tmp.getPointer(), vlen); 597 str = std::string(tmp.getPointer(), vlen);
libqpdf/QPDF_linearization.cc
@@ -88,7 +88,7 @@ QPDF::isLinearized() @@ -88,7 +88,7 @@ QPDF::isLinearized()
88 88
89 char* buf = new char[tbuf_size]; 89 char* buf = new char[tbuf_size];
90 this->file->seek(0, SEEK_SET); 90 this->file->seek(0, SEEK_SET);
91 - PointerHolder<char> b(buf); // guarantee deletion 91 + PointerHolder<char> b(true, buf);
92 memset(buf, '\0', tbuf_size); 92 memset(buf, '\0', tbuf_size);
93 this->file->read(buf, tbuf_size - 1); 93 this->file->read(buf, tbuf_size - 1);
94 94
libqpdf/qpdf-c.cc
@@ -33,11 +33,15 @@ struct _qpdf_data @@ -33,11 +33,15 @@ struct _qpdf_data
33 char const* buffer; 33 char const* buffer;
34 unsigned long size; 34 unsigned long size;
35 char const* password; 35 char const* password;
  36 + bool write_memory;
  37 + Buffer* output_buffer;
36 }; 38 };
37 39
38 _qpdf_data::_qpdf_data() : 40 _qpdf_data::_qpdf_data() :
39 qpdf(0), 41 qpdf(0),
40 - qpdf_writer(0) 42 + qpdf_writer(0),
  43 + write_memory(false),
  44 + output_buffer(0)
41 { 45 {
42 } 46 }
43 47
@@ -45,6 +49,7 @@ _qpdf_data::~_qpdf_data() @@ -45,6 +49,7 @@ _qpdf_data::~_qpdf_data()
45 { 49 {
46 delete qpdf_writer; 50 delete qpdf_writer;
47 delete qpdf; 51 delete qpdf;
  52 + delete output_buffer;
48 } 53 }
49 54
50 // must set qpdf->filename and qpdf->password 55 // must set qpdf->filename and qpdf->password
@@ -66,6 +71,12 @@ static void call_init_write(qpdf_data qpdf) @@ -66,6 +71,12 @@ static void call_init_write(qpdf_data qpdf)
66 qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf), qpdf->filename); 71 qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf), qpdf->filename);
67 } 72 }
68 73
  74 +static void call_init_write_memory(qpdf_data qpdf)
  75 +{
  76 + qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf));
  77 + qpdf->qpdf_writer->setOutputMemory();
  78 +}
  79 +
69 static void call_write(qpdf_data qpdf) 80 static void call_write(qpdf_data qpdf)
70 { 81 {
71 qpdf->qpdf_writer->write(); 82 qpdf->qpdf_writer->write();
@@ -408,21 +419,71 @@ QPDF_BOOL qpdf_allow_modify_all(qpdf_data qpdf) @@ -408,21 +419,71 @@ QPDF_BOOL qpdf_allow_modify_all(qpdf_data qpdf)
408 return qpdf->qpdf->allowModifyAll(); 419 return qpdf->qpdf->allowModifyAll();
409 } 420 }
410 421
411 -QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename) 422 +static void qpdf_init_write_internal(qpdf_data qpdf)
412 { 423 {
413 - QPDF_ERROR_CODE status = QPDF_SUCCESS;  
414 if (qpdf->qpdf_writer) 424 if (qpdf->qpdf_writer)
415 { 425 {
416 QTC::TC("qpdf", "qpdf-c called qpdf_init_write multiple times"); 426 QTC::TC("qpdf", "qpdf-c called qpdf_init_write multiple times");
417 delete qpdf->qpdf_writer; 427 delete qpdf->qpdf_writer;
418 qpdf->qpdf_writer = 0; 428 qpdf->qpdf_writer = 0;
  429 + if (qpdf->output_buffer)
  430 + {
  431 + delete qpdf->output_buffer;
  432 + qpdf->output_buffer = 0;
  433 + qpdf->write_memory = false;
  434 + qpdf->filename = 0;
  435 + }
419 } 436 }
  437 +}
  438 +
  439 +QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename)
  440 +{
  441 + qpdf_init_write_internal(qpdf);
420 qpdf->filename = filename; 442 qpdf->filename = filename;
421 - status = trap_errors(qpdf, &call_init_write); 443 + QPDF_ERROR_CODE status = trap_errors(qpdf, &call_init_write);
422 QTC::TC("qpdf", "qpdf-c called qpdf_init_write", status); 444 QTC::TC("qpdf", "qpdf-c called qpdf_init_write", status);
423 return status; 445 return status;
424 } 446 }
425 447
  448 +QPDF_ERROR_CODE qpdf_init_write_memory(qpdf_data qpdf)
  449 +{
  450 + qpdf_init_write_internal(qpdf);
  451 + QPDF_ERROR_CODE status = trap_errors(qpdf, &call_init_write_memory);
  452 + QTC::TC("qpdf", "qpdf-c called qpdf_init_write_memory");
  453 + qpdf->write_memory = true;
  454 + return status;
  455 +}
  456 +
  457 +static void qpdf_get_buffer_internal(qpdf_data qpdf)
  458 +{
  459 + if (qpdf->write_memory && (qpdf->output_buffer == 0))
  460 + {
  461 + qpdf->output_buffer = qpdf->qpdf_writer->getBuffer();
  462 + }
  463 +}
  464 +
  465 +unsigned long qpdf_get_buffer_length(qpdf_data qpdf)
  466 +{
  467 + qpdf_get_buffer_internal(qpdf);
  468 + unsigned long result = 0L;
  469 + if (qpdf->output_buffer)
  470 + {
  471 + result = qpdf->output_buffer->getSize();
  472 + }
  473 + return result;
  474 +}
  475 +
  476 +unsigned char const* qpdf_get_buffer(qpdf_data qpdf)
  477 +{
  478 + unsigned char const* result = 0;
  479 + qpdf_get_buffer_internal(qpdf);
  480 + if (qpdf->output_buffer)
  481 + {
  482 + result = qpdf->output_buffer->getBuffer();
  483 + }
  484 + return result;
  485 +}
  486 +
426 void qpdf_set_object_stream_mode(qpdf_data qpdf, qpdf_object_stream_e mode) 487 void qpdf_set_object_stream_mode(qpdf_data qpdf, qpdf_object_stream_e mode)
427 { 488 {
428 QTC::TC("qpdf", "qpdf-c called qpdf_set_object_stream_mode"); 489 QTC::TC("qpdf", "qpdf-c called qpdf_set_object_stream_mode");
libtests/buffer.cc
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 #include <qpdf/Pl_Discard.hh> 3 #include <qpdf/Pl_Discard.hh>
4 #include <stdlib.h> 4 #include <stdlib.h>
5 #include <stdexcept> 5 #include <stdexcept>
  6 +#include <iostream>
6 7
7 typedef unsigned char* uc; 8 typedef unsigned char* uc;
8 9
manual/qpdf-manual.xml
@@ -2131,6 +2131,14 @@ print &quot;\n&quot;; @@ -2131,6 +2131,14 @@ print &quot;\n&quot;;
2131 <literal>/Info</literal> dictionary. 2131 <literal>/Info</literal> dictionary.
2132 </para> 2132 </para>
2133 </listitem> 2133 </listitem>
  2134 + <listitem>
  2135 + <para>
  2136 + Add functions <function>qpdf_init_write_memory</function>,
  2137 + <function>qpdf_get_buffer_length</function>, and
  2138 + <function>qpdf_get_buffer</function> to the C API for writing
  2139 + PDF files to a memory buffer instead of a file.
  2140 + </para>
  2141 + </listitem>
2134 </itemizedlist> 2142 </itemizedlist>
2135 </listitem> 2143 </listitem>
2136 </varlistentry> 2144 </varlistentry>
qpdf/qpdf-ctest.c
@@ -337,6 +337,10 @@ static void test16(char const* infile, @@ -337,6 +337,10 @@ static void test16(char const* infile,
337 char const* outfile, 337 char const* outfile,
338 char const* outfile2) 338 char const* outfile2)
339 { 339 {
  340 + unsigned long buflen = 0L;
  341 + unsigned char const* buf = 0;
  342 + FILE* f = 0;
  343 +
340 qpdf_read(qpdf, infile, password); 344 qpdf_read(qpdf, infile, password);
341 print_info("/Author"); 345 print_info("/Author");
342 print_info("/Producer"); 346 print_info("/Producer");
@@ -347,11 +351,22 @@ static void test16(char const* infile, @@ -347,11 +351,22 @@ static void test16(char const* infile,
347 print_info("/Author"); 351 print_info("/Author");
348 print_info("/Producer"); 352 print_info("/Producer");
349 print_info("/Creator"); 353 print_info("/Creator");
350 - qpdf_init_write(qpdf, outfile); 354 + qpdf_init_write_memory(qpdf);
351 qpdf_set_static_ID(qpdf, QPDF_TRUE); 355 qpdf_set_static_ID(qpdf, QPDF_TRUE);
352 qpdf_set_static_aes_IV(qpdf, QPDF_TRUE); 356 qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
353 qpdf_set_stream_data_mode(qpdf, qpdf_s_uncompress); 357 qpdf_set_stream_data_mode(qpdf, qpdf_s_uncompress);
354 qpdf_write(qpdf); 358 qpdf_write(qpdf);
  359 + f = fopen(outfile, "wb");
  360 + if (f == NULL)
  361 + {
  362 + fprintf(stderr, "%s: unable to open %s: %s\n",
  363 + whoami, outfile, strerror(errno));
  364 + exit(2);
  365 + }
  366 + buflen = qpdf_get_buffer_length(qpdf);
  367 + buf = qpdf_get_buffer(qpdf);
  368 + fwrite(buf, 1, buflen, f);
  369 + fclose(f);
355 report_errors(); 370 report_errors();
356 } 371 }
357 372
qpdf/qpdf.testcov
@@ -199,3 +199,4 @@ qpdf-c set_info_key to value 0 @@ -199,3 +199,4 @@ qpdf-c set_info_key to value 0
199 qpdf-c set_info_key to null 0 199 qpdf-c set_info_key to null 0
200 qpdf-c set-info-key use existing info 0 200 qpdf-c set-info-key use existing info 0
201 qpdf-c add info to trailer 0 201 qpdf-c add info to trailer 0
  202 +qpdf-c called qpdf_init_write_memory 0
qpdf/test_driver.cc
@@ -76,7 +76,7 @@ void runtest(int n, char const* filename) @@ -76,7 +76,7 @@ void runtest(int n, char const* filename)
76 fseek(f, 0, SEEK_END); 76 fseek(f, 0, SEEK_END);
77 size_t size = (size_t) ftell(f); 77 size_t size = (size_t) ftell(f);
78 fseek(f, 0, SEEK_SET); 78 fseek(f, 0, SEEK_SET);
79 - file_buf = new char[size]; 79 + file_buf = PointerHolder<char>(true, new char[size]);
80 char* buf_p = file_buf.getPointer(); 80 char* buf_p = file_buf.getPointer();
81 size_t bytes_read = 0; 81 size_t bytes_read = 0;
82 size_t len = 0; 82 size_t len = 0;