Commit ddbe59179e64c45a375d6886f892059b49fd81b2
1 parent
59a64115
C API: simplify new error handling and improve documentation
Showing
10 changed files
with
161 additions
and
131 deletions
ChangeLog
| 1 | 2021-12-17 Jay Berkenbilt <ejb@ql.org> | 1 | 2021-12-17 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * C API: simplify error handling for uncaught errors (never in a | ||
| 4 | + released version) and clarify documentation in qpdf-c.h around | ||
| 5 | + error handling. See qpdf-c.h for details, including how to check | ||
| 6 | + for errors and the new function qpdf_silence_errors. | ||
| 7 | + | ||
| 3 | * C API: expose getTypeCode and getTypeName from QPDFObjectHandle. | 8 | * C API: expose getTypeCode and getTypeName from QPDFObjectHandle. |
| 4 | Fixes #597. | 9 | Fixes #597. |
| 5 | 10 | ||
| @@ -40,10 +45,7 @@ | @@ -40,10 +45,7 @@ | ||
| 40 | interfaces. Clarify documentation regarding object accessors and | 45 | interfaces. Clarify documentation regarding object accessors and |
| 41 | how type errors and warnings are handled. Many cases that used to | 46 | how type errors and warnings are handled. Many cases that used to |
| 42 | crash code that used the C API can now be trapped and will be | 47 | crash code that used the C API can now be trapped and will be |
| 43 | - written stderr if not trapped. The new method | ||
| 44 | - qpdf_register_oh_error_handler can be used to specifically handle | ||
| 45 | - errors that occur when accessing object handles. See qpdf-c.h for | ||
| 46 | - details. | 48 | + written stderr if not trapped. See qpdf-c.h for details. |
| 47 | 49 | ||
| 48 | * C API: Add qpdf_oh_new_uninitialized to explicitly create | 50 | * C API: Add qpdf_oh_new_uninitialized to explicitly create |
| 49 | uninitialized object handles. | 51 | uninitialized object handles. |
include/qpdf/qpdf-c.h
| @@ -33,6 +33,10 @@ | @@ -33,6 +33,10 @@ | ||
| 33 | * | 33 | * |
| 34 | * There are several things to keep in mind when using the C API. | 34 | * There are several things to keep in mind when using the C API. |
| 35 | * | 35 | * |
| 36 | + * Error handling is tricky because the underlying C++ API uses | ||
| 37 | + * exception handling. See "ERROR HANDLING" below for a detailed | ||
| 38 | + * explanation. | ||
| 39 | + * | ||
| 36 | * The C API is not as rich as the C++ API. For any operations | 40 | * The C API is not as rich as the C++ API. For any operations |
| 37 | * that involve actually manipulating PDF objects, you must use | 41 | * that involve actually manipulating PDF objects, you must use |
| 38 | * the C++ API. The C API is primarily useful for doing basic | 42 | * the C++ API. The C API is primarily useful for doing basic |
| @@ -46,8 +50,9 @@ | @@ -46,8 +50,9 @@ | ||
| 46 | * multiple threads. | 50 | * multiple threads. |
| 47 | * | 51 | * |
| 48 | * All dynamic memory, except for that of the qpdf_data object | 52 | * All dynamic memory, except for that of the qpdf_data object |
| 49 | - * itself, is managed by the library. You must create a qpdf_data | ||
| 50 | - * object using qpdf_init and free it using qpdf_cleanup. | 53 | + * itself, is managed by the library unless otherwise noted. You |
| 54 | + * must create a qpdf_data object using qpdf_init and free it | ||
| 55 | + * using qpdf_cleanup. | ||
| 51 | * | 56 | * |
| 52 | * Many functions return char*. In all cases, the char* values | 57 | * Many functions return char*. In all cases, the char* values |
| 53 | * returned are pointers to data inside the qpdf_data object. As | 58 | * returned are pointers to data inside the qpdf_data object. As |
| @@ -61,29 +66,6 @@ | @@ -61,29 +66,6 @@ | ||
| 61 | * qpdf_get_last_string_length() to get the length of whatever | 66 | * qpdf_get_last_string_length() to get the length of whatever |
| 62 | * string was just returned. | 67 | * string was just returned. |
| 63 | * | 68 | * |
| 64 | - * Many functions defined here merely set parameters and therefore | ||
| 65 | - * never return error conditions. Functions that access or return | ||
| 66 | - * qpdf_oh object handles may generate warnings but have no way to | ||
| 67 | - * return errors, but the errors may be checked afterwards or | ||
| 68 | - * handled using a registered handler. This is discussed in more | ||
| 69 | - * detail in the section on object handling. Functions that may | ||
| 70 | - * cause PDF files to be read or written may return error | ||
| 71 | - * conditions. Such functions return an error code. If there were | ||
| 72 | - * no errors or warnings, they return QPDF_SUCCESS. If there were | ||
| 73 | - * warnings, the return value has the QPDF_WARNINGS bit set. If | ||
| 74 | - * there were errors, the QPDF_ERRORS bit is set. In other words, | ||
| 75 | - * if there are both warnings and errors, then the return status | ||
| 76 | - * will be QPDF_WARNINGS | QPDF_ERRORS. You may also call the | ||
| 77 | - * qpdf_more_warnings and qpdf_more_errors functions to test | ||
| 78 | - * whether there are unseen warning or error conditions. By | ||
| 79 | - * default, warnings are written to stderr when detected, but this | ||
| 80 | - * behavior can be suppressed. In all cases, errors and warnings | ||
| 81 | - * may be retrieved by calling qpdf_next_warning and | ||
| 82 | - * qpdf_get_error. All exceptions thrown by the C++ interface are | ||
| 83 | - * caught and converted into error messages by the C interface. | ||
| 84 | - * Any exceptions to this are qpdf bugs and should be reported at | ||
| 85 | - * https://github.com/qpdf/qpdf/issues/new. | ||
| 86 | - * | ||
| 87 | * Most functions defined here have obvious counterparts that are | 69 | * Most functions defined here have obvious counterparts that are |
| 88 | * methods to either QPDF or QPDFWriter. Please see comments in | 70 | * methods to either QPDF or QPDFWriter. Please see comments in |
| 89 | * QPDF.hh and QPDFWriter.hh for details on their use. In order | 71 | * QPDF.hh and QPDFWriter.hh for details on their use. In order |
| @@ -91,6 +73,72 @@ | @@ -91,6 +73,72 @@ | ||
| 91 | * primarily on differences between the C and C++ API. | 73 | * primarily on differences between the C and C++ API. |
| 92 | */ | 74 | */ |
| 93 | 75 | ||
| 76 | +/* ERROR HANDLING -- changed in qpdf 10.5 */ | ||
| 77 | + | ||
| 78 | +/* SUMMARY: The only way to know whether a function that does not | ||
| 79 | + * return an error code has encountered an error is to call | ||
| 80 | + * qpdf_has_error after each function. You can do this even for | ||
| 81 | + * functions that do return error codes. You can also call | ||
| 82 | + * qpdf_silence_errors to prevent qpdf from writing these errors to | ||
| 83 | + * stderr. | ||
| 84 | + * | ||
| 85 | + * DETAILS: | ||
| 86 | + * | ||
| 87 | + * There is some complexity in this API's error handling as it tries | ||
| 88 | + * to adopt C++-style exception handling to work with languages that | ||
| 89 | + * don't support exceptions, such as C. | ||
| 90 | + * | ||
| 91 | + * The data type underlying qpdf_data maintains a list of warnings and | ||
| 92 | + * a single error. To retrieve warnings, call qpdf_next_warning while | ||
| 93 | + * qpdf_more_warnings is true. To retrieve the error, call | ||
| 94 | + * qpdf_get_error when qpdf_has_error is true. | ||
| 95 | + * | ||
| 96 | + * There are several things that are important to understand. | ||
| 97 | + * | ||
| 98 | + * Some functions return an error code. The value of the error code is | ||
| 99 | + * made up of a bitwise-OR of QPDF_WARNINGS and QPDF_ERRORS. The | ||
| 100 | + * QPDF_ERRORS bit is set if there was an error during the *most | ||
| 101 | + * recent call* to the API. The QPDF_WARNINGS bit is set if there are | ||
| 102 | + * any warnings that have not yet been retrieved by calling | ||
| 103 | + * qpdf_more_warnings. It is possible for both its or neither bit to | ||
| 104 | + * be set. | ||
| 105 | + * | ||
| 106 | + * The expected mode of operation is to go through a series of | ||
| 107 | + * operations, checking for errors after each call, but only checking | ||
| 108 | + * for warnings at the end. This is similar to how it works in the C++ | ||
| 109 | + * API where warnings are handled in exactly this way but errors | ||
| 110 | + * result in exceptions being thrown. However, in both the C and C++ | ||
| 111 | + * API, it is possible to check for and handle warnings as they arise. | ||
| 112 | + * | ||
| 113 | + * Some functions return values (or void) rather than an error code. | ||
| 114 | + * This is especially true with the object handling functions. Those | ||
| 115 | + * functions can still generate errors. To handle errors in those | ||
| 116 | + * cases, you should explicitly call qpdf_has_error(). Note that, if | ||
| 117 | + * you want to avoid the inconsistencies in the interface, you can | ||
| 118 | + * always check for error conditions in this way rather than looking | ||
| 119 | + * at status return codes. | ||
| 120 | + * | ||
| 121 | + * Prior to qpdf 10.5, if one of the functions that does not return an | ||
| 122 | + * error code encountered an exception, it would cause the entire | ||
| 123 | + * program to crash. Starting in qpdf 10.5, the default response to an | ||
| 124 | + * error condition in these situations is to print the error to | ||
| 125 | + * standard error, issue exactly one warning indicating that such an | ||
| 126 | + * error occurred, and return a sensible fallback value (0 for | ||
| 127 | + * numbers, QPDF_FALSE for booleans, "" for strings, or a null or | ||
| 128 | + * uninitialized object handle). This is better than the old behavior | ||
| 129 | + * but still undesirable as the best option is to explicitly check for | ||
| 130 | + * error conditions. | ||
| 131 | + * | ||
| 132 | + * To prevent qpdf from writing error messages to stderr in this way, | ||
| 133 | + * you can call qpdf_silence_errors(). This signals to the qpdf | ||
| 134 | + * library that you intend to check the error codes yourself. | ||
| 135 | + * | ||
| 136 | + * If you encounter a situation where an exception from the C++ code | ||
| 137 | + * is not properly converted to an error as described above, it is a | ||
| 138 | + * bug in qpdf, which should be reported at | ||
| 139 | + * https://github.com/qpdf/qpdf/issues/new. | ||
| 140 | + */ | ||
| 141 | + | ||
| 94 | #include <qpdf/DLL.h> | 142 | #include <qpdf/DLL.h> |
| 95 | #include <qpdf/Types.h> | 143 | #include <qpdf/Types.h> |
| 96 | #include <qpdf/Constants.h> | 144 | #include <qpdf/Constants.h> |
| @@ -116,6 +164,15 @@ extern "C" { | @@ -116,6 +164,15 @@ extern "C" { | ||
| 116 | # define QPDF_TRUE 1 | 164 | # define QPDF_TRUE 1 |
| 117 | # define QPDF_FALSE 0 | 165 | # define QPDF_FALSE 0 |
| 118 | 166 | ||
| 167 | + /* From qpdf 10.5: call this method to signal to the library that | ||
| 168 | + * you are explicitly handling errors from functions that don't | ||
| 169 | + * return error codes. Otherwise, the library will print these | ||
| 170 | + * error conditions to stderr and issue a warning. Prior to 10.5, | ||
| 171 | + * the program would have crashed from an unhandled exception. | ||
| 172 | + */ | ||
| 173 | + QPDF_DLL | ||
| 174 | + void qpdf_silence_errors(qpdf_data qpdf); | ||
| 175 | + | ||
| 119 | /* Returns the version of the qpdf software */ | 176 | /* Returns the version of the qpdf software */ |
| 120 | QPDF_DLL | 177 | QPDF_DLL |
| 121 | char const* qpdf_get_qpdf_version(); | 178 | char const* qpdf_get_qpdf_version(); |
| @@ -560,48 +617,14 @@ extern "C" { | @@ -560,48 +617,14 @@ extern "C" { | ||
| 560 | * same as letting a QPDFObjectHandle go out of scope in the C++ | 617 | * same as letting a QPDFObjectHandle go out of scope in the C++ |
| 561 | * API. | 618 | * API. |
| 562 | * | 619 | * |
| 563 | - * Important note about error handling: | ||
| 564 | - * | ||
| 565 | - * While many of the functions that operate on the QPDF object | ||
| 566 | - * return error codes, the qpdf_oh functions return values such as | ||
| 567 | - * object handles or data. They have no way to return error codes. | ||
| 568 | - * If they generate warnings, the warnings are handled using the | ||
| 569 | - * error/warning handling functions described above. If the | ||
| 570 | - * underlying C++ call throws an exception, the error handler | ||
| 571 | - * registered with qpdf_register_oh_error_handler() will be | ||
| 572 | - * called. If no handler is registered, the exception is written | ||
| 573 | - * to STDERR. In either case, a sensible fallback value is | ||
| 574 | - * returned (0 for numbers, QPDF_FALSE for booleans, "" for | ||
| 575 | - * strings, or a null object). It is sensible for a C program to | ||
| 576 | - * use setjmp and longjmp with this error handler since the C++ | ||
| 577 | - * code has raised an exception, but you can also just set a flag | ||
| 578 | - * and check it after each call. | ||
| 579 | - * | ||
| 580 | - * All conditions under which exceptions would be thrown by object | ||
| 581 | - * accessors are caused by programmer error or major problems such | ||
| 582 | - * as running out of memory or not being able to read the input | ||
| 583 | - * file. If they are ever caused by invalid data in the PDF file, | ||
| 584 | - * it is a bug in qpdf, which should be reported at | ||
| 585 | - * https://github.com/qpdf/qpdf/issues/new. | 620 | + * Please see "ERROR HANDLING" above for details on how error |
| 621 | + * conditions are handled. | ||
| 586 | */ | 622 | */ |
| 587 | 623 | ||
| 588 | /* For examples of using this API, see examples/pdf-c-objects.c */ | 624 | /* For examples of using this API, see examples/pdf-c-objects.c */ |
| 589 | 625 | ||
| 590 | typedef unsigned int qpdf_oh; | 626 | typedef unsigned int qpdf_oh; |
| 591 | 627 | ||
| 592 | - /* If an exception is thrown by the C++ code when any of the | ||
| 593 | - * qpdf_oh functions are called, the registered handle_error | ||
| 594 | - * function will be called. The value passed to data will be | ||
| 595 | - * passed along to the error handler function. If any errors occur | ||
| 596 | - * and no error handler is accessed, a single warning will be | ||
| 597 | - * issued, and the error will be written to stderr. | ||
| 598 | - */ | ||
| 599 | - QPDF_DLL | ||
| 600 | - void qpdf_register_oh_error_handler( | ||
| 601 | - qpdf_data qpdf, | ||
| 602 | - void (*handle_error)(qpdf_data qpdf, qpdf_error error, void* data), | ||
| 603 | - void* data); | ||
| 604 | - | ||
| 605 | /* Releasing objects -- see comments above. These functions have no | 628 | /* Releasing objects -- see comments above. These functions have no |
| 606 | * equivalent in the C++ API. | 629 | * equivalent in the C++ API. |
| 607 | */ | 630 | */ |
libqpdf/qpdf-c.cc
| @@ -42,9 +42,8 @@ struct _qpdf_data | @@ -42,9 +42,8 @@ struct _qpdf_data | ||
| 42 | PointerHolder<Buffer> output_buffer; | 42 | PointerHolder<Buffer> output_buffer; |
| 43 | 43 | ||
| 44 | // QPDFObjectHandle support | 44 | // QPDFObjectHandle support |
| 45 | - void (*oh_error_handler)(qpdf_data, qpdf_error, void*); | ||
| 46 | - void* oh_error_handler_data; | ||
| 47 | - bool default_oh_error_handler_called; | 45 | + bool silence_errors; |
| 46 | + bool oh_error_occurred; | ||
| 48 | std::map<qpdf_oh, PointerHolder<QPDFObjectHandle>> oh_cache; | 47 | std::map<qpdf_oh, PointerHolder<QPDFObjectHandle>> oh_cache; |
| 49 | qpdf_oh next_oh; | 48 | qpdf_oh next_oh; |
| 50 | std::set<std::string> cur_iter_dict_keys; | 49 | std::set<std::string> cur_iter_dict_keys; |
| @@ -52,32 +51,10 @@ struct _qpdf_data | @@ -52,32 +51,10 @@ struct _qpdf_data | ||
| 52 | std::string cur_dict_key; | 51 | std::string cur_dict_key; |
| 53 | }; | 52 | }; |
| 54 | 53 | ||
| 55 | -static void default_oh_error_handler(qpdf_data qpdf, qpdf_error e, void* data) | ||
| 56 | -{ | ||
| 57 | - bool* called = reinterpret_cast<bool*>(data); | ||
| 58 | - if (called != nullptr) | ||
| 59 | - { | ||
| 60 | - QTC::TC("qpdf", "qpdf-c warn about oh error", *called ? 0 : 1); | ||
| 61 | - if (! *called) | ||
| 62 | - { | ||
| 63 | - qpdf->warnings.push_back( | ||
| 64 | - QPDFExc( | ||
| 65 | - qpdf_e_internal, | ||
| 66 | - qpdf->qpdf->getFilename(), | ||
| 67 | - "", 0, | ||
| 68 | - "C API object handle accessor errors occurred," | ||
| 69 | - " and the application did not define an error handler")); | ||
| 70 | - *called = true; | ||
| 71 | - } | ||
| 72 | - } | ||
| 73 | - std::cerr << e->exc->what() << std::endl; | ||
| 74 | -} | ||
| 75 | - | ||
| 76 | _qpdf_data::_qpdf_data() : | 54 | _qpdf_data::_qpdf_data() : |
| 77 | write_memory(false), | 55 | write_memory(false), |
| 78 | - oh_error_handler(default_oh_error_handler), | ||
| 79 | - oh_error_handler_data(&this->default_oh_error_handler_called), | ||
| 80 | - default_oh_error_handler_called(false), | 56 | + silence_errors(false), |
| 57 | + oh_error_occurred(false), | ||
| 81 | next_oh(0) | 58 | next_oh(0) |
| 82 | { | 59 | { |
| 83 | } | 60 | } |
| @@ -876,14 +853,10 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) | @@ -876,14 +853,10 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) | ||
| 876 | return status; | 853 | return status; |
| 877 | } | 854 | } |
| 878 | 855 | ||
| 879 | -void qpdf_register_oh_error_handler( | ||
| 880 | - qpdf_data qpdf, | ||
| 881 | - void (*handle_error)(qpdf_data qpdf, qpdf_error error, void* data), | ||
| 882 | - void* data) | 856 | +void qpdf_silence_errors(qpdf_data qpdf) |
| 883 | { | 857 | { |
| 884 | - QTC::TC("qpdf", "qpdf-c registered oh error handler"); | ||
| 885 | - qpdf->oh_error_handler = handle_error; | ||
| 886 | - qpdf->oh_error_handler_data = data; | 858 | + QTC::TC("qpdf", "qpdf-c silence oh errors"); |
| 859 | + qpdf->silence_errors = true; | ||
| 887 | } | 860 | } |
| 888 | 861 | ||
| 889 | template<class RET> | 862 | template<class RET> |
| @@ -901,8 +874,24 @@ static RET trap_oh_errors( | @@ -901,8 +874,24 @@ static RET trap_oh_errors( | ||
| 901 | }); | 874 | }); |
| 902 | if (status & QPDF_ERRORS) | 875 | if (status & QPDF_ERRORS) |
| 903 | { | 876 | { |
| 904 | - (*qpdf->oh_error_handler)( | ||
| 905 | - qpdf, qpdf_get_error(qpdf), qpdf->oh_error_handler_data); | 877 | + if (! qpdf->silence_errors) |
| 878 | + { | ||
| 879 | + QTC::TC("qpdf", "qpdf-c warn about oh error", | ||
| 880 | + qpdf->oh_error_occurred ? 0 : 1); | ||
| 881 | + if (! qpdf->oh_error_occurred) | ||
| 882 | + { | ||
| 883 | + qpdf->warnings.push_back( | ||
| 884 | + QPDFExc( | ||
| 885 | + qpdf_e_internal, | ||
| 886 | + qpdf->qpdf->getFilename(), | ||
| 887 | + "", 0, | ||
| 888 | + "C API function caught an exception that it isn't" | ||
| 889 | + " returning; please point the application developer" | ||
| 890 | + " to ERROR HANDLING in qpdf-c.h")); | ||
| 891 | + qpdf->oh_error_occurred = true; | ||
| 892 | + } | ||
| 893 | + std::cerr << qpdf->error->what() << std::endl; | ||
| 894 | + } | ||
| 906 | return fallback(); | 895 | return fallback(); |
| 907 | } | 896 | } |
| 908 | return ret; | 897 | return ret; |
manual/index.rst
| @@ -3632,14 +3632,16 @@ For a detailed list of changes, please see the file | @@ -3632,14 +3632,16 @@ For a detailed list of changes, please see the file | ||
| 3632 | 3632 | ||
| 3633 | - C API Enhancements | 3633 | - C API Enhancements |
| 3634 | 3634 | ||
| 3635 | - - Overhaul error handling for the object handle functions | ||
| 3636 | - C API. See comments in the "Object handling" section of | ||
| 3637 | - :file:`include/qpdf/qpdf-c.h` for details. | ||
| 3638 | - In particular, exceptions thrown by the underlying C++ code | ||
| 3639 | - when calling object accessors are caught and converted into | ||
| 3640 | - errors. The errors can be trapped by registering an error | ||
| 3641 | - handler with ``qpdf_register_oh_error_handler`` or will be | ||
| 3642 | - written to stderr if no handler is registered. | 3635 | + - Overhaul error handling for the object handle functions C API. |
| 3636 | + Some rare error conditions that would previously have caused a | ||
| 3637 | + crash are now trapped and reported, and the functions that | ||
| 3638 | + generate them return fallback values. See comments in the | ||
| 3639 | + ``ERROR HANDLING`` section of :file:`include/qpdf/qpdf-c.h` for | ||
| 3640 | + details. In particular, exceptions thrown by the underlying C++ | ||
| 3641 | + code when calling object accessors are caught and converted into | ||
| 3642 | + errors. The errors can be checked by call ``qpdf_has_error``. | ||
| 3643 | + Use ``qpdf_silence_errors`` to prevent the error from being | ||
| 3644 | + written to stderr. | ||
| 3643 | 3645 | ||
| 3644 | - Add ``qpdf_get_last_string_length`` to the C API to get the | 3646 | - Add ``qpdf_get_last_string_length`` to the C API to get the |
| 3645 | length of the last string that was returned. This is needed to | 3647 | length of the last string that was returned. This is needed to |
qpdf/qpdf-ctest.c
| @@ -73,14 +73,12 @@ static void report_errors() | @@ -73,14 +73,12 @@ static void report_errors() | ||
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | -static void handle_oh_error(qpdf_data qpdf, qpdf_error error, void* data) | 76 | +static void handle_oh_error(qpdf_data qpdf, char const* label) |
| 77 | { | 77 | { |
| 78 | - char const* label = "oh error"; | ||
| 79 | - if (data) | 78 | + if (qpdf_has_error(qpdf)) |
| 80 | { | 79 | { |
| 81 | - label = *((char const**)data); | 80 | + print_error(label, qpdf, qpdf_get_error(qpdf)); |
| 82 | } | 81 | } |
| 83 | - print_error(label, qpdf, error); | ||
| 84 | } | 82 | } |
| 85 | 83 | ||
| 86 | static void read_file_into_memory(char const* filename, | 84 | static void read_file_into_memory(char const* filename, |
| @@ -837,42 +835,44 @@ static void test29(char const* infile, | @@ -837,42 +835,44 @@ static void test29(char const* infile, | ||
| 837 | * errors rather than warnings when they don't have an owning QPDF | 835 | * errors rather than warnings when they don't have an owning QPDF |
| 838 | * object. | 836 | * object. |
| 839 | */ | 837 | */ |
| 840 | - char const* label = "oh error"; | ||
| 841 | - qpdf_register_oh_error_handler(qpdf, handle_oh_error, (void*)&label); | 838 | + qpdf_silence_errors(qpdf); |
| 842 | 839 | ||
| 843 | /* get_root fails when we have no trailer */ | 840 | /* get_root fails when we have no trailer */ |
| 844 | - label = "get root"; | ||
| 845 | qpdf_oh root = qpdf_get_root(qpdf); | 841 | qpdf_oh root = qpdf_get_root(qpdf); |
| 842 | + handle_oh_error(qpdf, "get root"); | ||
| 846 | assert(root != 0); | 843 | assert(root != 0); |
| 847 | assert(! qpdf_oh_is_initialized(qpdf, root)); | 844 | assert(! qpdf_oh_is_initialized(qpdf, root)); |
| 848 | 845 | ||
| 849 | - label = "bad parse"; | ||
| 850 | assert(! qpdf_oh_is_initialized(qpdf, qpdf_oh_parse(qpdf, "[oops"))); | 846 | assert(! qpdf_oh_is_initialized(qpdf, qpdf_oh_parse(qpdf, "[oops"))); |
| 847 | + handle_oh_error(qpdf, "bad parse"); | ||
| 851 | report_errors(); | 848 | report_errors(); |
| 852 | 849 | ||
| 853 | - label = "type mismatch"; | ||
| 854 | assert(qpdf_oh_get_int_value_as_int( | 850 | assert(qpdf_oh_get_int_value_as_int( |
| 855 | qpdf, qpdf_oh_new_string(qpdf, "x")) == 0); | 851 | qpdf, qpdf_oh_new_string(qpdf, "x")) == 0); |
| 852 | + handle_oh_error(qpdf, "type mismatch (int operation on string)"); | ||
| 856 | qpdf_oh int_oh = qpdf_oh_new_integer(qpdf, 12); | 853 | qpdf_oh int_oh = qpdf_oh_new_integer(qpdf, 12); |
| 857 | assert(strlen(qpdf_oh_get_string_value(qpdf, int_oh)) == 0); | 854 | assert(strlen(qpdf_oh_get_string_value(qpdf, int_oh)) == 0); |
| 855 | + handle_oh_error(qpdf, "type mismatch (string operation on int)"); | ||
| 858 | 856 | ||
| 859 | // This doesn't test every possible error flow, but it tests each | 857 | // This doesn't test every possible error flow, but it tests each |
| 860 | // way of handling errors in the library code. | 858 | // way of handling errors in the library code. |
| 861 | - label = "array type mismatch"; | ||
| 862 | assert(qpdf_oh_get_array_n_items(qpdf, int_oh) == 0); | 859 | assert(qpdf_oh_get_array_n_items(qpdf, int_oh) == 0); |
| 860 | + handle_oh_error(qpdf, "array type mismatch - n_items"); | ||
| 863 | assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, int_oh, 3))); | 861 | assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, int_oh, 3))); |
| 864 | - label = "append to non-array"; | 862 | + handle_oh_error(qpdf, "array type mismatch - item"); |
| 865 | qpdf_oh_append_item(qpdf, int_oh, qpdf_oh_new_null(qpdf)); | 863 | qpdf_oh_append_item(qpdf, int_oh, qpdf_oh_new_null(qpdf)); |
| 864 | + handle_oh_error(qpdf, "append to non-array"); | ||
| 866 | qpdf_oh array = qpdf_oh_new_array(qpdf); | 865 | qpdf_oh array = qpdf_oh_new_array(qpdf); |
| 867 | - label = "array bounds"; | ||
| 868 | assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, array, 3))); | 866 | assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, array, 3))); |
| 867 | + handle_oh_error(qpdf, "array bounds"); | ||
| 869 | 868 | ||
| 870 | - label = "dictionary iter type mismatch"; | ||
| 871 | qpdf_oh_begin_dict_key_iter(qpdf, int_oh); | 869 | qpdf_oh_begin_dict_key_iter(qpdf, int_oh); |
| 872 | assert(qpdf_oh_dict_more_keys(qpdf) == QPDF_FALSE); | 870 | assert(qpdf_oh_dict_more_keys(qpdf) == QPDF_FALSE); |
| 873 | - label = "dictionary type mismatch"; | 871 | + handle_oh_error(qpdf, "dictionary iter type mismatch"); |
| 874 | assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_key(qpdf, int_oh, "potato"))); | 872 | assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_key(qpdf, int_oh, "potato"))); |
| 873 | + handle_oh_error(qpdf, "dictionary type mismatch"); | ||
| 875 | assert(qpdf_oh_has_key(qpdf, int_oh, "potato") == QPDF_FALSE); | 874 | assert(qpdf_oh_has_key(qpdf, int_oh, "potato") == QPDF_FALSE); |
| 875 | + handle_oh_error(qpdf, "dictionary type mismatch"); | ||
| 876 | 876 | ||
| 877 | report_errors(); | 877 | report_errors(); |
| 878 | } | 878 | } |
| @@ -1030,6 +1030,9 @@ static void test35(char const* infile, | @@ -1030,6 +1030,9 @@ static void test35(char const* infile, | ||
| 1030 | assert(qpdf_more_warnings(qpdf)); | 1030 | assert(qpdf_more_warnings(qpdf)); |
| 1031 | e = qpdf_next_warning(qpdf); | 1031 | e = qpdf_next_warning(qpdf); |
| 1032 | assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS); | 1032 | assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS); |
| 1033 | + assert(qpdf_has_error(qpdf)); | ||
| 1034 | + e = qpdf_get_error(qpdf); | ||
| 1035 | + assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS); | ||
| 1033 | assert(! qpdf_has_error(qpdf)); | 1036 | assert(! qpdf_has_error(qpdf)); |
| 1034 | 1037 | ||
| 1035 | assert(qpdf_find_page_by_id(qpdf, 100, 0) == -1); | 1038 | assert(qpdf_find_page_by_id(qpdf, 100, 0) == -1); |
qpdf/qpdf.testcov
| @@ -606,7 +606,6 @@ qpdf-c called qpdf_oh_is_initialized 0 | @@ -606,7 +606,6 @@ qpdf-c called qpdf_oh_is_initialized 0 | ||
| 606 | qpdf-c registered progress reporter 0 | 606 | qpdf-c registered progress reporter 0 |
| 607 | qpdf-c called qpdf_oh_new_uninitialized 0 | 607 | qpdf-c called qpdf_oh_new_uninitialized 0 |
| 608 | qpdf-c warn about oh error 1 | 608 | qpdf-c warn about oh error 1 |
| 609 | -qpdf-c registered oh error handler 0 | ||
| 610 | qpdf-c cleanup warned about unhandled error 0 | 609 | qpdf-c cleanup warned about unhandled error 0 |
| 611 | qpdf-c called qpdf_get_object_by_id 0 | 610 | qpdf-c called qpdf_get_object_by_id 0 |
| 612 | qpdf-c called qpdf_replace_object 0 | 611 | qpdf-c called qpdf_replace_object 0 |
| @@ -625,3 +624,4 @@ qpdf-c stream data filtered set 1 | @@ -625,3 +624,4 @@ qpdf-c stream data filtered set 1 | ||
| 625 | qpdf-c stream data buf set 1 | 624 | qpdf-c stream data buf set 1 |
| 626 | qpdf-c called qpdf_oh_get_page_content_data 0 | 625 | qpdf-c called qpdf_oh_get_page_content_data 0 |
| 627 | qpdf-c called qpdf_oh_replace_stream_data 0 | 626 | qpdf-c called qpdf_oh_replace_stream_data 0 |
| 627 | +qpdf-c silence oh errors 0 |
qpdf/qtest/qpdf/c-object-handles.out
| @@ -10,9 +10,14 @@ item 3: 792 792.00 | @@ -10,9 +10,14 @@ item 3: 792 792.00 | ||
| 10 | minimal.pdf (C API object handle 6): attempted access to unknown object handle | 10 | minimal.pdf (C API object handle 6): attempted access to unknown object handle |
| 11 | minimal.pdf (C API object handle 9): attempted access to unknown object handle | 11 | minimal.pdf (C API object handle 9): attempted access to unknown object handle |
| 12 | minimal.pdf (C API object handle 9): attempted access to unknown object handle | 12 | minimal.pdf (C API object handle 9): attempted access to unknown object handle |
| 13 | -warning: minimal.pdf: C API object handle accessor errors occurred, and the application did not define an error handler | 13 | +warning: minimal.pdf: C API function caught an exception that it isn't returning; please point the application developer to ERROR HANDLING in qpdf-c.h |
| 14 | code: 1 | 14 | code: 1 |
| 15 | file: minimal.pdf | 15 | file: minimal.pdf |
| 16 | pos : 0 | 16 | pos : 0 |
| 17 | - text: C API object handle accessor errors occurred, and the application did not define an error handler | 17 | + text: C API function caught an exception that it isn't returning; please point the application developer to ERROR HANDLING in qpdf-c.h |
| 18 | +error: minimal.pdf (C API object handle 9): attempted access to unknown object handle | ||
| 19 | + code: 1 | ||
| 20 | + file: minimal.pdf | ||
| 21 | + pos : 0 | ||
| 22 | + text: attempted access to unknown object handle | ||
| 18 | C test 24 done | 23 | C test 24 done |
qpdf/qtest/qpdf/c-oh-errors.out
| @@ -8,22 +8,22 @@ bad parse: parsed object (offset 1): unknown token while reading object; treatin | @@ -8,22 +8,22 @@ bad parse: parsed object (offset 1): unknown token while reading object; treatin | ||
| 8 | file: parsed object | 8 | file: parsed object |
| 9 | pos : 1 | 9 | pos : 1 |
| 10 | text: unknown token while reading object; treating as string | 10 | text: unknown token while reading object; treating as string |
| 11 | -type mismatch: operation for integer attempted on object of type string: returning 0 | 11 | +type mismatch (int operation on string): operation for integer attempted on object of type string: returning 0 |
| 12 | code: 7 | 12 | code: 7 |
| 13 | file: | 13 | file: |
| 14 | pos : 0 | 14 | pos : 0 |
| 15 | text: operation for integer attempted on object of type string: returning 0 | 15 | text: operation for integer attempted on object of type string: returning 0 |
| 16 | -type mismatch: operation for string attempted on object of type integer: returning empty string | 16 | +type mismatch (string operation on int): operation for string attempted on object of type integer: returning empty string |
| 17 | code: 7 | 17 | code: 7 |
| 18 | file: | 18 | file: |
| 19 | pos : 0 | 19 | pos : 0 |
| 20 | text: operation for string attempted on object of type integer: returning empty string | 20 | text: operation for string attempted on object of type integer: returning empty string |
| 21 | -array type mismatch: operation for array attempted on object of type integer: treating as empty | 21 | +array type mismatch - n_items: operation for array attempted on object of type integer: treating as empty |
| 22 | code: 7 | 22 | code: 7 |
| 23 | file: | 23 | file: |
| 24 | pos : 0 | 24 | pos : 0 |
| 25 | text: operation for array attempted on object of type integer: treating as empty | 25 | text: operation for array attempted on object of type integer: treating as empty |
| 26 | -array type mismatch: operation for array attempted on object of type integer: returning null | 26 | +array type mismatch - item: operation for array attempted on object of type integer: returning null |
| 27 | code: 7 | 27 | code: 7 |
| 28 | file: | 28 | file: |
| 29 | pos : 0 | 29 | pos : 0 |
qpdf/qtest/qpdf/c-oh-uninitialized-objects.out
| 1 | closed input source (C API object handle 1): attempted access to unknown object handle | 1 | closed input source (C API object handle 1): attempted access to unknown object handle |
| 2 | +WARNING: application did not handle error: closed input source (C API object handle 1): attempted access to unknown object handle | ||
| 2 | C test 26 done | 3 | C test 26 done |
qpdf/qtest/qpdf/c-page-errors.out
| @@ -6,4 +6,9 @@ warning: object 27 0: operation for dictionary attempted on object of type null: | @@ -6,4 +6,9 @@ warning: object 27 0: operation for dictionary attempted on object of type null: | ||
| 6 | file: | 6 | file: |
| 7 | pos : 0 | 7 | pos : 0 |
| 8 | text: operation for dictionary attempted on object of type null: ignoring key replacement request | 8 | text: operation for dictionary attempted on object of type null: ignoring key replacement request |
| 9 | +error: 11-pages.pdf (C API object handle 1000): attempted access to unknown object handle | ||
| 10 | + code: 1 | ||
| 11 | + file: 11-pages.pdf | ||
| 12 | + pos : 0 | ||
| 13 | + text: attempted access to unknown object handle | ||
| 9 | C test 35 done | 14 | C test 35 done |