1 /*
2     libzint - the open source barcode library
3     Copyright (C) 2019 - 2021 Robin Stuart <rstuart114@gmail.com>
4 
5     Redistribution and use in source and binary forms, with or without
6     modification, are permitted provided that the following conditions
7     are met:
8 
9     1. Redistributions of source code must retain the above copyright
10        notice, this list of conditions and the following disclaimer.
11     2. Redistributions in binary form must reproduce the above copyright
12        notice, this list of conditions and the following disclaimer in the
13        documentation and/or other materials provided with the distribution.
14     3. Neither the name of the project nor the names of its contributors
15        may be used to endorse or promote products derived from this software
16        without specific prior written permission.
17 
18     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28     SUCH DAMAGE.
29  */
30 /* vim: set ts=4 sw=4 et : */
31 /*
32  * Adapted from qrencode/tests/common.c
33  * Copyright (C) 2006-2017 Kentaro Fukuchi <kentaro@fukuchi.org>
34  */
35 
36 #include "testcommon.h"
37 
38 #ifdef _MSC_VER
39 #include <malloc.h>
40 #define testutil_alloca(nmemb) _alloca(nmemb)
41 #else
42 #define testutil_alloca(nmemb) alloca(nmemb)
43 #endif
44 
45 #ifdef _WIN32
46 #include <windows.h>
47 #include <direct.h>
48 #endif
49 
50 #include "../eci.h"
51 #ifndef NO_PNG
52 #include <png.h>
53 #include <zlib.h>
54 #include <setjmp.h>
55 #endif
56 #include <assert.h>
57 #include <errno.h>
58 #include <getopt.h>
59 #include <limits.h>
60 #include <sys/stat.h>
61 
62 static int tests = 0;
63 static int failed = 0;
64 static int skipped = 0;
65 int assertionFailed = 0;
66 int assertionNum = 0;
67 const char *assertionFilename = "";
68 static const char *testName = NULL;
69 static const char *testFunc = NULL;
70 
71 /* Visual C++ 6 doesn't support variadic args to macros, so make do with functions, which have inferior behaviour,
72    e.g. don't exit on failure, `assert_equal()` type-specific */
73 #if _MSC_VER == 1200 /* VC6 */
74 #include <stdarg.h>
assert_zero(int exp,const char * fmt,...)75 void assert_zero(int exp, const char *fmt, ...) {
76     assertionNum++;
77     if (exp != 0) {
78         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
79     }
80 }
assert_nonzero(int exp,const char * fmt,...)81 void assert_nonzero(int exp, const char *fmt, ...) {
82     assertionNum++;
83     if (exp == 0) {
84         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
85     }
86 }
assert_null(void * exp,const char * fmt,...)87 void assert_null(void *exp, const char *fmt, ...) {
88     assertionNum++;
89     if (exp != NULL) {
90         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
91     }
92 }
assert_nonnull(void * exp,const char * fmt,...)93 void assert_nonnull(void *exp, const char *fmt, ...) {
94     assertionNum++;
95     if (exp == NULL) {
96         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
97     }
98 }
assert_equal(int e1,int e2,const char * fmt,...)99 void assert_equal(int e1, int e2, const char *fmt, ...) {
100     assertionNum++;
101     if (e1 != e2) {
102         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
103     }
104 }
assert_equalu64(uint64_t e1,uint64_t e2,const char * fmt,...)105 void assert_equalu64(uint64_t e1, uint64_t e2, const char *fmt, ...) {
106     assertionNum++;
107     if (e1 != e2) {
108         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
109     }
110 }
assert_notequal(int e1,int e2,const char * fmt,...)111 void assert_notequal(int e1, int e2, const char *fmt, ...) {
112     assertionNum++;
113     if (e1 == e2) {
114         va_list args; assertionFailed++; va_start(args, fmt); vprintf(fmt, args); va_end(args); testFinish();
115     }
116 }
117 #endif
118 
119 /* Begin individual test function */
testStartReal(const char * func,const char * name)120 void testStartReal(const char *func, const char *name) {
121     tests++;
122     if (func && *func && name && *name && strcmp(func, name) == 0) {
123         testName = "";
124     } else {
125         testName = name;
126     }
127     testFunc = func ? func : "";
128     assertionFailed = 0;
129     assertionNum = 0;
130     printf("_____%d: %s: %s...\n", tests, testFunc, testName ? testName : "");
131 }
132 
133 /* End individual test function */
testFinish(void)134 void testFinish(void) {
135     if (testName && *testName) {
136         printf(".....%d: %s: %s ", tests, testFunc, testName);
137     } else {
138         printf(".....%d: %s: ", tests, testFunc);
139     }
140     if (assertionFailed) {
141         printf("FAILED. (%d assertions failed.)\n", assertionFailed);
142         failed++;
143     } else {
144         printf("PASSED. (%d assertions passed.)\n", assertionNum);
145     }
146 }
147 
148 /* Skip (and end) individual test function */
testSkip(const char * msg)149 void testSkip(const char *msg) {
150     skipped++;
151     if (testName && *testName) {
152         printf(".....%d: %s: %s ", tests, testFunc, testName);
153     } else {
154         printf(".....%d: %s: ", tests, testFunc);
155     }
156     if (assertionFailed) {
157         printf("FAILED. (%d assertions failed.)\n", assertionFailed);
158         failed++;
159     } else {
160         printf("SKIPPED. %s. (%d assertions passed.)\n", msg, assertionNum);
161     }
162 }
163 
164 /* End test program */
testReport()165 void testReport() {
166     if (failed && skipped) {
167         printf("Total %d tests, %d skipped, %d fails.\n", tests, skipped, failed);
168         exit(-1);
169     }
170     if (failed) {
171         printf("Total %d tests, %d fails.\n", tests, failed);
172         exit(-1);
173     }
174     if (skipped) {
175         printf("Total %d tests, %d skipped.\n", tests, skipped);
176     } else if (tests) {
177         printf("Total %d tests, all passed.\n", tests);
178     } else {
179         printf("Total %d tests.\n", tests);
180     }
181 }
182 
183 /* Begin test program, parse args */
testRun(int argc,char * argv[],testFunction funcs[],int funcs_size)184 void testRun(int argc, char *argv[], testFunction funcs[], int funcs_size) {
185     int i, opt, ran;
186     long long_opt;
187     char *optarg_endptr = NULL;
188     int debug = 0;
189     char *func = NULL;
190     char func_buf[256 + 5];
191     int index = -1;
192     int generate = 0;
193 
194     typedef void (*func_void)(void);
195     typedef void (*func_debug)(int debug);
196     typedef void (*func_index)(int index);
197     typedef void (*func_index_debug)(int index, int debug);
198     typedef void (*func_generate)(int generate);
199     typedef void (*func_generate_debug)(int generate, int debug);
200     typedef void (*func_index_generate)(int index, int generate);
201     typedef void (*func_index_generate_debug)(int index, int generate, int debug);
202 
203     if (argc) {
204         char *filename = strrchr(argv[0], '/');
205 #ifdef _WIN32
206         if (filename == NULL) {
207             filename = strrchr(argv[0], '\\');
208         }
209 #endif
210         if (filename) {
211             assertionFilename = filename + 1;
212         } else {
213             assertionFilename = argv[0];
214         }
215     }
216 
217     while ((opt = getopt(argc, argv, "d:f:gi:")) != -1) {
218         switch (opt) {
219             case 'd':
220                 errno = 0;
221                 long_opt = strtol(optarg, &optarg_endptr, 10);
222                 if (errno || optarg_endptr == optarg || long_opt < 0 || long_opt > INT_MAX) {
223                     fprintf(stderr, "testRun: -d debug value invalid\n");
224                     debug = 0;
225                 } else {
226                     debug = long_opt;
227                 }
228                 break;
229             case 'f':
230                 if (strlen(optarg) < 256) {
231                     if (strncmp(optarg, "test_", 5) == 0) {
232                         strcpy(func_buf, optarg);
233                     } else {
234                         strcpy(func_buf, "test_");
235                         strcat(func_buf, optarg);
236                     }
237                     func = func_buf;
238                 } else {
239                     fprintf(stderr, "testRun: -f func value too long\n");
240                     func = NULL;
241                 }
242                 break;
243             case 'g':
244                 generate = 1;
245                 break;
246             case 'i':
247                 errno = 0;
248                 long_opt = strtol(optarg, &optarg_endptr, 10);
249                 if (errno || optarg_endptr == optarg || long_opt < 0 || long_opt > INT_MAX) {
250                     fprintf(stderr, "testRun: -i index value invalid\n");
251                     index = -1;
252                 } else {
253                     index = long_opt;
254                 }
255                 break;
256         }
257     }
258 
259     ran = 0;
260     for (i = 0; i < funcs_size; i++) {
261         if (func && strcmp(func, funcs[i].name) != 0) {
262             continue;
263         }
264         if (funcs[i].has_index && funcs[i].has_generate && funcs[i].has_debug) {
265             (*(func_index_generate_debug)funcs[i].func)(index, generate, debug);
266         } else if (funcs[i].has_index && funcs[i].has_generate) {
267             if (debug) fprintf(stderr, "testRun %s: -d ignored\n", funcs[i].name);
268             (*(func_index_generate)funcs[i].func)(index, generate);
269         } else if (funcs[i].has_index && funcs[i].has_debug) {
270             if (generate) fprintf(stderr, "testRun %s: -g ignored\n", funcs[i].name);
271             (*(func_index_debug)funcs[i].func)(index, debug);
272         } else if (funcs[i].has_index) {
273             if (generate) fprintf(stderr, "testRun %s: -g ignored\n", funcs[i].name);
274             if (debug) fprintf(stderr, "testRun %s: -d ignored\n", funcs[i].name);
275             (*(func_index)funcs[i].func)(index);
276         } else if (funcs[i].has_generate && funcs[i].has_debug) {
277             if (index != -1) fprintf(stderr, "testRun %s: -i index ignored\n", funcs[i].name);
278             (*(func_generate_debug)funcs[i].func)(generate, debug);
279         } else if (funcs[i].has_generate) {
280             if (index != -1) fprintf(stderr, "testRun %s: -i index ignored\n", funcs[i].name);
281             if (debug) fprintf(stderr, "testRun %s: -d ignored\n", funcs[i].name);
282             (*(func_generate)funcs[i].func)(generate);
283         } else if (funcs[i].has_debug) {
284             if (index != -1) fprintf(stderr, "testRun %s: -i index ignored\n", funcs[i].name);
285             if (generate) fprintf(stderr, "testRun %s -g ignored\n", funcs[i].name);
286             (*(func_debug)funcs[i].func)(debug);
287         } else {
288             if (index != -1) fprintf(stderr, "testRun %s: -i index ignored\n", funcs[i].name);
289             if (generate) fprintf(stderr, "testRun %s -g ignored\n", funcs[i].name);
290             if (debug) fprintf(stderr, "testRun %s: -d ignored\n", funcs[i].name);
291             (*(func_void)funcs[i].func)();
292         }
293         ran++;
294     }
295 
296     if (func && !ran) {
297         fprintf(stderr, "testRun: unknown -f func arg '%s'\n", func);
298     }
299 }
300 
301 /* Helper to set common symbol fields */
testUtilSetSymbol(struct zint_symbol * symbol,int symbology,int input_mode,int eci,int option_1,int option_2,int option_3,int output_options,char * data,int length,int debug)302 int testUtilSetSymbol(struct zint_symbol *symbol, int symbology, int input_mode, int eci, int option_1, int option_2,
303             int option_3, int output_options, char *data, int length, int debug) {
304     symbol->symbology = symbology;
305     if (input_mode != -1) {
306         symbol->input_mode = input_mode;
307     }
308     if (eci != -1) {
309         symbol->eci = eci;
310     }
311     if (option_1 != -1) {
312         symbol->option_1 = option_1;
313     }
314     if (option_2 != -1) {
315         symbol->option_2 = option_2;
316     }
317     if (option_3 != -1) {
318         symbol->option_3 = option_3;
319     }
320     if (output_options != -1) {
321         symbol->output_options = output_options;
322     }
323     symbol->debug |= debug;
324     if (length == -1) {
325         length = (int) strlen(data);
326     }
327 
328     return length;
329 }
330 
331 /* Pretty name for symbology */
testUtilBarcodeName(int symbology)332 const char *testUtilBarcodeName(int symbology) {
333     struct item {
334         const char *name;
335         int define;
336         int val;
337     };
338     static const struct item data[] = {
339         { "", -1, 0 },
340         { "BARCODE_CODE11", BARCODE_CODE11, 1 },
341         { "BARCODE_C25STANDARD", BARCODE_C25STANDARD, 2 },
342         { "BARCODE_C25INTER", BARCODE_C25INTER, 3 },
343         { "BARCODE_C25IATA", BARCODE_C25IATA, 4 },
344         { "", -1, 5 },
345         { "BARCODE_C25LOGIC", BARCODE_C25LOGIC, 6 },
346         { "BARCODE_C25IND", BARCODE_C25IND, 7 },
347         { "BARCODE_CODE39", BARCODE_CODE39, 8 },
348         { "BARCODE_EXCODE39", BARCODE_EXCODE39, 9 },
349         { "", -1, 10 },
350         { "", -1, 11 },
351         { "", -1, 12 },
352         { "BARCODE_EANX", BARCODE_EANX, 13 },
353         { "BARCODE_EANX_CHK", BARCODE_EANX_CHK, 14 },
354         { "", -1, 15 },
355         { "BARCODE_GS1_128", BARCODE_GS1_128, 16 },
356         { "", -1, 17 },
357         { "BARCODE_CODABAR", BARCODE_CODABAR, 18 },
358         { "", -1, 19 },
359         { "BARCODE_CODE128", BARCODE_CODE128, 20 },
360         { "BARCODE_DPLEIT", BARCODE_DPLEIT, 21 },
361         { "BARCODE_DPIDENT", BARCODE_DPIDENT, 22 },
362         { "BARCODE_CODE16K", BARCODE_CODE16K, 23 },
363         { "BARCODE_CODE49", BARCODE_CODE49, 24 },
364         { "BARCODE_CODE93", BARCODE_CODE93, 25 },
365         { "", -1, 26 },
366         { "", -1, 27 },
367         { "BARCODE_FLAT", BARCODE_FLAT, 28 },
368         { "BARCODE_DBAR_OMN", BARCODE_DBAR_OMN, 29 },
369         { "BARCODE_DBAR_LTD", BARCODE_DBAR_LTD, 30 },
370         { "BARCODE_DBAR_EXP", BARCODE_DBAR_EXP, 31 },
371         { "BARCODE_TELEPEN", BARCODE_TELEPEN, 32 },
372         { "", -1, 33 },
373         { "BARCODE_UPCA", BARCODE_UPCA, 34 },
374         { "BARCODE_UPCA_CHK", BARCODE_UPCA_CHK, 35 },
375         { "", -1, 36 },
376         { "BARCODE_UPCE", BARCODE_UPCE, 37 },
377         { "BARCODE_UPCE_CHK", BARCODE_UPCE_CHK, 38 },
378         { "", -1, 39 },
379         { "BARCODE_POSTNET", BARCODE_POSTNET, 40 },
380         { "", -1, 41 },
381         { "", -1, 42 },
382         { "", -1, 43 },
383         { "", -1, 44 },
384         { "", -1, 45 },
385         { "", -1, 46 },
386         { "BARCODE_MSI_PLESSEY", BARCODE_MSI_PLESSEY, 47 },
387         { "", -1, 48 },
388         { "BARCODE_FIM", BARCODE_FIM, 49 },
389         { "BARCODE_LOGMARS", BARCODE_LOGMARS, 50 },
390         { "BARCODE_PHARMA", BARCODE_PHARMA, 51 },
391         { "BARCODE_PZN", BARCODE_PZN, 52 },
392         { "BARCODE_PHARMA_TWO", BARCODE_PHARMA_TWO, 53 },
393         { "", -1, 54 },
394         { "BARCODE_PDF417", BARCODE_PDF417, 55 },
395         { "BARCODE_PDF417COMP", BARCODE_PDF417COMP, 56 },
396         { "BARCODE_MAXICODE", BARCODE_MAXICODE, 57 },
397         { "BARCODE_QRCODE", BARCODE_QRCODE, 58 },
398         { "", -1, 59 },
399         { "BARCODE_CODE128B", BARCODE_CODE128B, 60 },
400         { "", -1, 61 },
401         { "", -1, 62 },
402         { "BARCODE_AUSPOST", BARCODE_AUSPOST, 63 },
403         { "", -1, 64 },
404         { "", -1, 65 },
405         { "BARCODE_AUSREPLY", BARCODE_AUSREPLY, 66 },
406         { "BARCODE_AUSROUTE", BARCODE_AUSROUTE, 67 },
407         { "BARCODE_AUSREDIRECT", BARCODE_AUSREDIRECT, 68 },
408         { "BARCODE_ISBNX", BARCODE_ISBNX, 69 },
409         { "BARCODE_RM4SCC", BARCODE_RM4SCC, 70 },
410         { "BARCODE_DATAMATRIX", BARCODE_DATAMATRIX, 71 },
411         { "BARCODE_EAN14", BARCODE_EAN14, 72 },
412         { "BARCODE_VIN", BARCODE_VIN, 73 },
413         { "BARCODE_CODABLOCKF", BARCODE_CODABLOCKF, 74 },
414         { "BARCODE_NVE18", BARCODE_NVE18, 75 },
415         { "BARCODE_JAPANPOST", BARCODE_JAPANPOST, 76 },
416         { "BARCODE_KOREAPOST", BARCODE_KOREAPOST, 77 },
417         { "", -1, 78 },
418         { "BARCODE_DBAR_STK", BARCODE_DBAR_STK, 79 },
419         { "BARCODE_DBAR_OMNSTK", BARCODE_DBAR_OMNSTK, 80 },
420         { "BARCODE_DBAR_EXPSTK", BARCODE_DBAR_EXPSTK, 81 },
421         { "BARCODE_PLANET", BARCODE_PLANET, 82 },
422         { "", -1, 83 },
423         { "BARCODE_MICROPDF417", BARCODE_MICROPDF417, 84 },
424         { "BARCODE_USPS_IMAIL", BARCODE_USPS_IMAIL, 85 },
425         { "BARCODE_PLESSEY", BARCODE_PLESSEY, 86 },
426         { "BARCODE_TELEPEN_NUM", BARCODE_TELEPEN_NUM, 87 },
427         { "", -1, 88 },
428         { "BARCODE_ITF14", BARCODE_ITF14, 89 },
429         { "BARCODE_KIX", BARCODE_KIX, 90 },
430         { "", -1, 91 },
431         { "BARCODE_AZTEC", BARCODE_AZTEC, 92 },
432         { "BARCODE_DAFT", BARCODE_DAFT, 93 },
433         { "", -1, 94 },
434         { "", -1, 95 },
435         { "BARCODE_DPD", BARCODE_DPD, 96 },
436         { "BARCODE_MICROQR", BARCODE_MICROQR, 97 },
437         { "BARCODE_HIBC_128", BARCODE_HIBC_128, 98 },
438         { "BARCODE_HIBC_39", BARCODE_HIBC_39, 99 },
439         { "", -1, 100 },
440         { "", -1, 101 },
441         { "BARCODE_HIBC_DM", BARCODE_HIBC_DM, 102 },
442         { "", -1, 103 },
443         { "BARCODE_HIBC_QR", BARCODE_HIBC_QR, 104 },
444         { "", -1, 105 },
445         { "BARCODE_HIBC_PDF", BARCODE_HIBC_PDF, 106 },
446         { "", -1, 107 },
447         { "BARCODE_HIBC_MICPDF", BARCODE_HIBC_MICPDF, 108 },
448         { "", -1, 109 },
449         { "BARCODE_HIBC_BLOCKF", BARCODE_HIBC_BLOCKF, 110 },
450         { "", -1, 111 },
451         { "BARCODE_HIBC_AZTEC", BARCODE_HIBC_AZTEC, 112 },
452         { "", -1, 113 },
453         { "", -1, 114 },
454         { "BARCODE_DOTCODE", BARCODE_DOTCODE, 115 },
455         { "BARCODE_HANXIN", BARCODE_HANXIN, 116 },
456         { "", -1, 117 },
457         { "", -1, 118 },
458         { "", -1, 119 },
459         { "", -1, 120 },
460         { "BARCODE_MAILMARK", BARCODE_MAILMARK, 121 },
461         { "", -1, 122 },
462         { "", -1, 123 },
463         { "", -1, 124 },
464         { "", -1, 125 },
465         { "", -1, 126 },
466         { "", -1, 127 },
467         { "BARCODE_AZRUNE", BARCODE_AZRUNE, 128 },
468         { "BARCODE_CODE32", BARCODE_CODE32, 129 },
469         { "BARCODE_EANX_CC", BARCODE_EANX_CC, 130 },
470         { "BARCODE_GS1_128_CC", BARCODE_GS1_128_CC, 131 },
471         { "BARCODE_DBAR_OMN_CC", BARCODE_DBAR_OMN_CC, 132 },
472         { "BARCODE_DBAR_LTD_CC", BARCODE_DBAR_LTD_CC, 133 },
473         { "BARCODE_DBAR_EXP_CC", BARCODE_DBAR_EXP_CC, 134 },
474         { "BARCODE_UPCA_CC", BARCODE_UPCA_CC, 135 },
475         { "BARCODE_UPCE_CC", BARCODE_UPCE_CC, 136 },
476         { "BARCODE_DBAR_STK_CC", BARCODE_DBAR_STK_CC, 137 },
477         { "BARCODE_DBAR_OMNSTK_CC", BARCODE_DBAR_OMNSTK_CC, 138 },
478         { "BARCODE_DBAR_EXPSTK_CC", BARCODE_DBAR_EXPSTK_CC, 139 },
479         { "BARCODE_CHANNEL", BARCODE_CHANNEL, 140 },
480         { "BARCODE_CODEONE", BARCODE_CODEONE, 141 },
481         { "BARCODE_GRIDMATRIX", BARCODE_GRIDMATRIX, 142 },
482         { "BARCODE_UPNQR", BARCODE_UPNQR, 143 },
483         { "BARCODE_ULTRA", BARCODE_ULTRA, 144 },
484         { "BARCODE_RMQR", BARCODE_RMQR, 145 },
485     };
486     static const int data_size = ARRAY_SIZE(data);
487 
488     if (symbology < 0 || symbology >= data_size) {
489         return "";
490     }
491     // Self-check
492     if (data[symbology].val != symbology || (data[symbology].define != -1 && data[symbology].define != symbology)) {
493         fprintf(stderr, "testUtilBarcodeName: data table out of sync (%d)\n", symbology);
494         abort();
495     }
496     return data[symbology].name;
497 }
498 
499 /* Pretty name for error/warning */
testUtilErrorName(int error_number)500 const char *testUtilErrorName(int error_number) {
501     struct item {
502         const char *name;
503         int define;
504         int val;
505     };
506     static const struct item data[] = {
507         { "0", 0, 0 },
508         { "", -1, 1 },
509         { "ZINT_WARN_INVALID_OPTION", ZINT_WARN_INVALID_OPTION, 2 },
510         { "ZINT_WARN_USES_ECI", ZINT_WARN_USES_ECI, 3 },
511         { "ZINT_WARN_NONCOMPLIANT", ZINT_WARN_NONCOMPLIANT, 4 },
512         { "ZINT_ERROR_TOO_LONG", ZINT_ERROR_TOO_LONG, 5 },
513         { "ZINT_ERROR_INVALID_DATA", ZINT_ERROR_INVALID_DATA, 6 },
514         { "ZINT_ERROR_INVALID_CHECK", ZINT_ERROR_INVALID_CHECK, 7 },
515         { "ZINT_ERROR_INVALID_OPTION", ZINT_ERROR_INVALID_OPTION, 8 },
516         { "ZINT_ERROR_ENCODING_PROBLEM", ZINT_ERROR_ENCODING_PROBLEM, 9 },
517         { "ZINT_ERROR_FILE_ACCESS", ZINT_ERROR_FILE_ACCESS, 10 },
518         { "ZINT_ERROR_MEMORY", ZINT_ERROR_MEMORY, 11 },
519     };
520     static const int data_size = ARRAY_SIZE(data);
521 
522     if (error_number < 0 || error_number >= data_size) {
523         return "";
524     }
525     // Self-check
526     if (data[error_number].val != error_number
527             || (data[error_number].define != -1 && data[error_number].define != error_number)) {
528         fprintf(stderr, "testUtilErrorName: data table out of sync (%d)\n", error_number);
529         abort();
530     }
531     return data[error_number].name;
532 }
533 
534 /* Pretty name for input mode */
testUtilInputModeName(int input_mode)535 const char *testUtilInputModeName(int input_mode) {
536     static char buf[512];
537 
538     struct item {
539         const char *name;
540         int define;
541         int val;
542     };
543     static const struct item data[] = {
544         { "ESCAPE_MODE", ESCAPE_MODE, 8 },
545         { "GS1PARENS_MODE", GS1PARENS_MODE, 16 },
546         { "GS1NOCHECK_MODE", GS1NOCHECK_MODE, 32 },
547     };
548     static const int data_size = ARRAY_SIZE(data);
549     int set, i;
550 
551     if (input_mode < 0) {
552         return "-1";
553     }
554     *buf = '\0';
555     if ((input_mode & 0x7) & UNICODE_MODE) {
556         strcpy(buf, "UNICODE_MODE");
557         set = UNICODE_MODE;
558     } else if ((input_mode & 0x7) & GS1_MODE) {
559         strcpy(buf, "GS1_MODE");
560         set = GS1_MODE;
561     } else {
562         set = DATA_MODE;
563     }
564     for (i = 0; i < data_size; i++) {
565         if (data[i].define != data[i].val) { // Self-check
566             fprintf(stderr, "testUtilInputModeName: data table out of sync (%d)\n", i);
567             abort();
568         }
569         if (input_mode & data[i].define) {
570             if (*buf) {
571                 strcat(buf, " | ");
572             }
573             strcat(buf, data[i].name);
574             set |= data[i].define;
575         }
576     }
577     if (set != input_mode) {
578         fprintf(stderr, "testUtilInputModeName: unknown input mode %d (%d)\n", input_mode & set, input_mode);
579         abort();
580     }
581     if (set == DATA_MODE && *buf == '\0') {
582         strcpy(buf, "DATA_MODE");
583     }
584     return buf;
585 }
586 
587 /* Pretty name for option 3 */
testUtilOption3Name(int option_3)588 const char *testUtilOption3Name(int option_3) {
589     static char buffer[64];
590 
591     const char *name = NULL;
592     unsigned int high_byte = option_3 == -1 ? 0 : (option_3 >> 8) & 0xFF;
593 
594     switch (option_3 & 0xFF) {
595         case DM_SQUARE:
596             name = "DM_SQUARE";
597             break;
598         case DM_DMRE:
599             name = "DM_DMRE";
600             break;
601         case ZINT_FULL_MULTIBYTE:
602             name = "ZINT_FULL_MULTIBYTE";
603             break;
604         case ULTRA_COMPRESSION:
605             name = "ULTRA_COMPRESSION";
606             break;
607         default:
608             if (option_3 != -1 && (option_3 & 0xFF) != 0) {
609                 fprintf(stderr, "testUtilOption3Name: unknown value (%d)\n", option_3);
610                 abort();
611             }
612             name = (option_3 & 0xFF) ? "-1" : "0";
613             break;
614     }
615 
616     if (high_byte) {
617         if (option_3 & 0xFF) {
618             sprintf(buffer, "%s | (%d << 8)", name, (int) high_byte);
619         } else {
620             sprintf(buffer, "%d << 8", (int) high_byte);
621         }
622         return buffer;
623     }
624 
625     return name;
626 }
627 
628 /* Pretty name for output options */
testUtilOutputOptionsName(int output_options)629 const char *testUtilOutputOptionsName(int output_options) {
630     static char buf[512];
631 
632     struct item {
633         const char *name;
634         int define;
635         int val;
636     };
637     static const struct item data[] = {
638         { "BARCODE_NO_ASCII", BARCODE_NO_ASCII, 1 },
639         { "BARCODE_BIND", BARCODE_BIND, 2 },
640         { "BARCODE_BOX", BARCODE_BOX, 4 },
641         { "BARCODE_STDOUT", BARCODE_STDOUT, 8 },
642         { "READER_INIT", READER_INIT, 16 },
643         { "SMALL_TEXT", SMALL_TEXT, 32 },
644         { "BOLD_TEXT", BOLD_TEXT, 64 },
645         { "CMYK_COLOUR", CMYK_COLOUR, 128 },
646         { "BARCODE_DOTTY_MODE", BARCODE_DOTTY_MODE, 256 },
647         { "GS1_GS_SEPARATOR", GS1_GS_SEPARATOR, 512 },
648         { "OUT_BUFFER_INTERMEDIATE", OUT_BUFFER_INTERMEDIATE, 1024 },
649     };
650     static int const data_size = ARRAY_SIZE(data);
651     int set = 0;
652     int i;
653 
654     if (output_options == -1) {
655         return "-1";
656     }
657     if (output_options == 0) {
658         return "0";
659     }
660     buf[0] = '\0';
661     for (i = 0; i < data_size; i++) {
662         if (data[i].define != data[i].val) { // Self-check
663             fprintf(stderr, "testUtilOutputOptionsName: data table out of sync (%d)\n", i);
664             abort();
665         }
666         if (output_options & data[i].define) {
667             if (set) {
668                 strcat(buf, " | ");
669             }
670             strcat(buf, data[i].name);
671             set |= data[i].define;
672         }
673     }
674     if (set != output_options) {
675         fprintf(stderr, "testUtilOutputOptionsName: unknown output option(s) %d (%d)\n",
676                 output_options & set, output_options);
677         abort();
678     }
679     return buf;
680 }
681 
682 /* Convert modules spanning 3 rows to DAFT equivalents */
testUtilDAFTConvert(const struct zint_symbol * symbol,char * buffer,int buffer_size)683 int testUtilDAFTConvert(const struct zint_symbol *symbol, char *buffer, int buffer_size) {
684     int i;
685     char *b = buffer;
686     *b = '\0';
687     for (i = 0; i < symbol->width && b < buffer + buffer_size; i += 2) {
688         if (module_is_set(symbol, 0, i) && module_is_set(symbol, 2, i)) {
689             *b++ = 'F';
690         } else if (module_is_set(symbol, 0, i)) {
691             *b++ = 'A';
692         } else if (module_is_set(symbol, 2, i)) {
693             *b++ = 'D';
694         } else {
695             *b++ = 'T';
696         }
697     }
698     if (b == buffer + buffer_size) {
699         return FALSE;
700     }
701     *b = '\0';
702     return TRUE;
703 }
704 
705 /* Is string valid UTF-8? */
testUtilIsValidUTF8(const unsigned char str[],const int length)706 int testUtilIsValidUTF8(const unsigned char str[], const int length) {
707     int i;
708     unsigned int codepoint, state = 0;
709 
710     for (i = 0; i < length; i++) {
711         if (decode_utf8(&state, &codepoint, str[i]) == 12) {
712             return 0;
713         }
714     }
715 
716     return state == 0;
717 }
718 
719 /* Escape data for printing on generate test. Has a number of issues, e.g. need to use octal escapes */
testUtilEscape(char * buffer,int length,char * escaped,int escaped_size)720 char *testUtilEscape(char *buffer, int length, char *escaped, int escaped_size) {
721     int i;
722     unsigned char *b = (unsigned char *) buffer;
723     unsigned char *be = b + length;
724     int non_utf8 = !testUtilIsValidUTF8(b, length);
725     int chunk = -1;
726 
727     for (i = 0; b < be && i < escaped_size; b++) {
728          // For VC6-compatibility need to split literal strings into <= 2K chunks
729          if (i > 2040 && i / 2040 != chunk) {
730             chunk = i / 2040;
731             if (i + 3 < escaped_size) {
732                 escaped[i] = '"';
733                 escaped[i + 1] = ' ';
734                 escaped[i + 2] = '"';
735             }
736             i += 3;
737          }
738          if (non_utf8 || *b < ' ' || *b == '\177') {
739             if (i + 4 < escaped_size) {
740                 sprintf(escaped + i, "\\%.3o", *b);
741             }
742             i += 4;
743         } else if (*b == '\\' || *b == '"') {
744             if (i + 2 < escaped_size) {
745                 escaped[i] = '\\';
746                 escaped[i + 1] = *b;
747             }
748             i += 2;
749         } else if (b + 1 < be && *b == 0xC2 && *(b + 1) < 0xA0) {
750             if (i + 8 < escaped_size) {
751                 sprintf(escaped + i, "\\%.3o\\%.3o", *b, *(b + 1));
752             }
753             i += 8;
754             b++;
755         } else {
756             escaped[i++] = *b;
757         }
758     }
759     if (i >= escaped_size) {
760         return NULL;
761     }
762     escaped[i] = '\0';
763     return escaped;
764 }
765 
766 /* Helper to read a CSV field */
testUtilReadCSVField(char * buffer,char * field,int field_size)767 char *testUtilReadCSVField(char *buffer, char *field, int field_size) {
768     int i;
769     char *b = buffer;
770     for (i = 0; i < field_size && *b && *b != ',' && *b != '\n' && *b != '\r'; i++) {
771         field[i] = *b++;
772     }
773     if (i == field_size) {
774         return NULL;
775     }
776     field[i] = '\0';
777     return b;
778 }
779 
780 /* Helper to fill a buffer (for "large" tests) - single-byte filler only */
testUtilStrCpyRepeat(char * buffer,char * repeat,int size)781 void testUtilStrCpyRepeat(char *buffer, char *repeat, int size) {
782     int i;
783     int len = (int) strlen(repeat);
784     int max = size - len;
785     if (len == 0) {
786         fprintf(stderr, "testUtilStrCpyRepeat: only use non-empty, non-NUL single-byte data for repeat pattern\n");
787         abort();
788     }
789     for (i = 0; i < max; i += len) {
790         memcpy(buffer + i, repeat, len);
791     }
792     memcpy(buffer + i, repeat, size - i);
793     buffer[size] = '\0';
794 }
795 
796 /* Compare some "important" symbol fields for equality */
testUtilSymbolCmp(const struct zint_symbol * a,const struct zint_symbol * b)797 int testUtilSymbolCmp(const struct zint_symbol *a, const struct zint_symbol *b) {
798     int i, j;
799     if (a->symbology != b->symbology) {
800         return 1;
801     }
802     if (a->rows != b->rows) {
803         return 2;
804     }
805     if (a->width != b->width) {
806         return 3;
807     }
808     if (a->symbology == BARCODE_ULTRA) {
809         for (i = 0; i < a->rows; i++) {
810             for (j = 0; j < a->width; j++) {
811                 if (module_colour_is_set(a, i, j) != module_colour_is_set(b, i, j)) {
812                     return 4;
813                 }
814             }
815         }
816     } else {
817         for (i = 0; i < a->rows; i++) {
818             for (j = 0; j < a->width; j++) {
819                 if (module_is_set(a, i, j) != module_is_set(b, i, j)) {
820                     return 4;
821                 }
822             }
823         }
824     }
825     if (a->height != b->height) {
826         return 5;
827     }
828     if (a->whitespace_width != b->whitespace_width) {
829         return 6;
830     }
831     if (a->whitespace_height != b->whitespace_height) {
832         return 7;
833     }
834     if (a->border_width != b->border_width) {
835         return 8;
836     }
837     if (a->output_options != b->output_options) {
838         return 9;
839     }
840     if (a->scale != b->scale) {
841         return 10;
842     }
843 
844     return 0;
845 }
846 
847 /* Copy a full vector structure (for later comparison) */
testUtilVectorCpy(const struct zint_vector * in)848 struct zint_vector *testUtilVectorCpy(const struct zint_vector *in) {
849     struct zint_vector_rect *rect;
850     struct zint_vector_string *string;
851     struct zint_vector_circle *circle;
852     struct zint_vector_hexagon *hexagon;
853 
854     struct zint_vector_rect **outrect;
855     struct zint_vector_string **outstring;
856     struct zint_vector_circle **outcircle;
857     struct zint_vector_hexagon **outhexagon;
858 
859     struct zint_vector *out = malloc(sizeof(struct zint_vector));
860     assert(out != NULL);
861     out->width = in->width;
862     out->height = in->height;
863     out->rectangles = NULL;
864     out->strings = NULL;
865     out->circles = NULL;
866     out->hexagons = NULL;
867 
868     // Copy rectangles
869     rect = in->rectangles;
870     outrect = &(out->rectangles);
871     while (rect) {
872         *outrect = malloc(sizeof(struct zint_vector_rect));
873         assert(*outrect != NULL);
874         memcpy(*outrect, rect, sizeof(struct zint_vector_rect));
875         outrect = &((*outrect)->next);
876         rect = rect->next;
877     }
878     *outrect = NULL;
879 
880     // Copy Strings
881     string = in->strings;
882     outstring = &(out->strings);
883     while (string) {
884         *outstring = malloc(sizeof(struct zint_vector_string));
885         assert(*outstring != NULL);
886         memcpy(*outstring, string, sizeof(struct zint_vector_string));
887         (*outstring)->text = malloc(ustrlen(string->text) + 1);
888         assert((*outstring)->text != NULL);
889         ustrcpy((*outstring)->text, string->text);
890         outstring = &((*outstring)->next);
891         string = string->next;
892     }
893     *outstring = NULL;
894 
895     // Copy Circles
896     circle = in->circles;
897     outcircle = &(out->circles);
898     while (circle) {
899         *outcircle = malloc(sizeof(struct zint_vector_circle));
900         assert(*outcircle != NULL);
901         memcpy(*outcircle, circle, sizeof(struct zint_vector_circle));
902         outcircle = &((*outcircle)->next);
903         circle = circle->next;
904     }
905     *outcircle = NULL;
906 
907     // Copy Hexagons
908     hexagon = in->hexagons;
909     outhexagon = &(out->hexagons);
910     while (hexagon) {
911         *outhexagon = malloc(sizeof(struct zint_vector_hexagon));
912         assert(*outhexagon != NULL);
913         memcpy(*outhexagon, hexagon, sizeof(struct zint_vector_hexagon));
914         outhexagon = &((*outhexagon)->next);
915         hexagon = hexagon->next;
916     }
917     *outhexagon = NULL;
918 
919     return out;
920 }
921 
922 /* Compare 2 full vector structures */
testUtilVectorCmp(const struct zint_vector * a,const struct zint_vector * b)923 int testUtilVectorCmp(const struct zint_vector *a, const struct zint_vector *b) {
924     struct zint_vector_rect *arect;
925     struct zint_vector_string *astring;
926     struct zint_vector_circle *acircle;
927     struct zint_vector_hexagon *ahexagon;
928 
929     struct zint_vector_rect *brect;
930     struct zint_vector_string *bstring;
931     struct zint_vector_circle *bcircle;
932     struct zint_vector_hexagon *bhexagon;
933 
934     if (a->width != b->width) {
935         return 1;
936     }
937     if (a->height != b->height) {
938         return 2;
939     }
940 
941     // Compare rectangles
942     arect = a->rectangles;
943     brect = b->rectangles;
944     while (arect) {
945         if (!brect) {
946             return 11;
947         }
948         if (arect->x != brect->x) {
949             return 12;
950         }
951         if (arect->y != brect->y) {
952             return 13;
953         }
954         if (arect->height != brect->height) {
955             return 14;
956         }
957         if (arect->width != brect->width) {
958             return 15;
959         }
960         if (arect->colour != brect->colour) {
961             return 16;
962         }
963         arect = arect->next;
964         brect = brect->next;
965     }
966     if (brect) {
967         return 10;
968     }
969 
970     // Compare strings
971     astring = a->strings;
972     bstring = b->strings;
973     while (astring) {
974         if (!bstring) {
975             return 21;
976         }
977         if (astring->x != bstring->x) {
978             return 22;
979         }
980         if (astring->y != bstring->y) {
981             return 23;
982         }
983         if (astring->fsize != bstring->fsize) {
984             return 24;
985         }
986         if (astring->width != bstring->width) {
987             return 25;
988         }
989         if (astring->length != bstring->length) {
990             return 26;
991         }
992         if (ustrlen(astring->text) != ustrlen(bstring->text)) {
993             return 27;
994         }
995         if (strcmp((const char *) astring->text, (const char *) bstring->text) != 0) {
996             return 28;
997         }
998         astring = astring->next;
999         bstring = bstring->next;
1000     }
1001     if (bstring) {
1002         return 20;
1003     }
1004 
1005     // Compare circles
1006     acircle = a->circles;
1007     bcircle = b->circles;
1008     while (acircle) {
1009         if (!bcircle) {
1010             return 31;
1011         }
1012         if (acircle->x != bcircle->x) {
1013             return 32;
1014         }
1015         if (acircle->y != bcircle->y) {
1016             return 33;
1017         }
1018         if (acircle->diameter != bcircle->diameter) {
1019             return 34;
1020         }
1021         if (acircle->colour != bcircle->colour) {
1022             return 35;
1023         }
1024         acircle = acircle->next;
1025         bcircle = bcircle->next;
1026     }
1027     if (bcircle) {
1028         return 30;
1029     }
1030 
1031     // Compare hexagons
1032     ahexagon = a->hexagons;
1033     bhexagon = b->hexagons;
1034     while (ahexagon) {
1035         if (!bhexagon) {
1036             return 41;
1037         }
1038         if (ahexagon->x != bhexagon->x) {
1039             return 42;
1040         }
1041         if (ahexagon->y != bhexagon->y) {
1042             return 43;
1043         }
1044         if (ahexagon->diameter != bhexagon->diameter) {
1045             return 44;
1046         }
1047         ahexagon = ahexagon->next;
1048         bhexagon = bhexagon->next;
1049     }
1050     if (bhexagon) {
1051         return 40;
1052     }
1053 
1054     return 0;
1055 }
1056 
1057 /* Dump modules into buffer as '0'/'1' (or colours '0', '1', '2' etc if Ultra) */
testUtilModulesDump(const struct zint_symbol * symbol,char dump[],int dump_size)1058 int testUtilModulesDump(const struct zint_symbol *symbol, char dump[], int dump_size) {
1059     int r, w;
1060     char *d = dump;
1061     char *de = dump + dump_size;
1062 
1063     for (r = 0; r < symbol->rows && d < de; r++) {
1064         if (symbol->symbology == BARCODE_ULTRA) {
1065             for (w = 0; w < symbol->width && d < de; w++) {
1066                 *d++ = module_colour_is_set(symbol, r, w) + '0';
1067             }
1068         } else {
1069             for (w = 0; w < symbol->width && d < de; w++) {
1070                 *d++ = module_is_set(symbol, r, w) + '0';
1071             }
1072         }
1073     }
1074     if (d == de) {
1075         return -1;
1076     }
1077     *d = '\0';
1078     return d - dump;
1079 }
1080 
1081 /* Print out module dump (for generate tests) */
testUtilModulesPrint(const struct zint_symbol * symbol,const char * prefix,const char * postfix)1082 void testUtilModulesPrint(const struct zint_symbol *symbol, const char *prefix, const char *postfix) {
1083     int r;
1084     for (r = 0; r < symbol->rows; r++) {
1085         testUtilModulesPrintRow(symbol, r, prefix, postfix);
1086     }
1087 }
1088 
1089 /* Print out a single row of a module dump (for generate tests where rows all the same, to avoid large dumps of
1090    duplicate data) */
testUtilModulesPrintRow(const struct zint_symbol * symbol,int row,const char * prefix,const char * postfix)1091 void testUtilModulesPrintRow(const struct zint_symbol *symbol, int row, const char *prefix, const char *postfix) {
1092     int w;
1093     if (*prefix) {
1094         fputs(prefix, stdout);
1095     }
1096     putchar('"');
1097     if (symbol->symbology == BARCODE_ULTRA) {
1098         for (w = 0; w < symbol->width; w++) {
1099             putchar(module_colour_is_set(symbol, row, w) + '0');
1100         }
1101     } else {
1102         for (w = 0; w < symbol->width; w++) {
1103             putchar(module_is_set(symbol, row, w) + '0');
1104         }
1105     }
1106     putchar('"');
1107     if (*postfix) {
1108         fputs(postfix, stdout);
1109     }
1110 }
1111 
1112 /* Whether 2 module dumps are the same */
testUtilModulesCmp(const struct zint_symbol * symbol,const char * expected,int * width,int * row)1113 int testUtilModulesCmp(const struct zint_symbol *symbol, const char *expected, int *width, int *row) {
1114     const char *e = expected;
1115     const char *ep = expected + strlen(expected);
1116     int r, w = 0;
1117     if (symbol->symbology == BARCODE_ULTRA) {
1118         for (r = 0; r < symbol->rows && e < ep; r++) {
1119             for (w = 0; w < symbol->width && e < ep; w++) {
1120                 if (module_colour_is_set(symbol, r, w) + '0' != *e) {
1121                     *row = r;
1122                     *width = w;
1123                     return 1 /*fail*/;
1124                 }
1125                 e++;
1126             }
1127         }
1128     } else {
1129         for (r = 0; r < symbol->rows && e < ep; r++) {
1130             for (w = 0; w < symbol->width && e < ep; w++) {
1131                 if (module_is_set(symbol, r, w) + '0' != *e) {
1132                     *row = r;
1133                     *width = w;
1134                     return 1 /*fail*/;
1135                 }
1136                 e++;
1137             }
1138         }
1139     }
1140     *row = r;
1141     *width = w;
1142     return e != ep || r != symbol->rows || w != symbol->width ? 1 /*fail*/ : 0 /*success*/;
1143 }
1144 
1145 /* Whether 2 module row dumps are the same */
testUtilModulesCmpRow(const struct zint_symbol * symbol,int row,const char * expected,int * width)1146 int testUtilModulesCmpRow(const struct zint_symbol *symbol, int row, const char *expected, int *width) {
1147     const char *e = expected;
1148     const char *ep = expected + strlen(expected);
1149     int w;
1150     if (symbol->symbology == BARCODE_ULTRA) {
1151         for (w = 0; w < symbol->width && e < ep; w++) {
1152             if (module_colour_is_set(symbol, row, w) + '0' != *e) {
1153                 *width = w;
1154                 return 1 /*fail*/;
1155             }
1156             e++;
1157         }
1158     } else {
1159         for (w = 0; w < symbol->width && e < ep; w++) {
1160             if (module_is_set(symbol, row, w) + '0' != *e) {
1161                 *width = w;
1162                 return 1 /*fail*/;
1163             }
1164             e++;
1165         }
1166     }
1167     *width = w;
1168     return e != ep || w != symbol->width ? 1 /*fail*/ : 0 /*success*/;
1169 }
1170 
1171 /* Dump an unsigned int array as hex */
testUtilUIntArrayDump(unsigned int * array,int size,char * dump,int dump_size)1172 char *testUtilUIntArrayDump(unsigned int *array, int size, char *dump, int dump_size) {
1173     int i, cnt_len = 0;
1174 
1175     for (i = 0; i < size; i++) {
1176         cnt_len += sprintf(dump + cnt_len, "%X ", array[i]);
1177         if (cnt_len + 17 >= dump_size) {
1178             break;
1179         }
1180     }
1181     dump[cnt_len ? cnt_len - 1 : 0] = '\0';
1182     return dump;
1183 }
1184 
1185 /* Dump an unsigned char array as hex */
testUtilUCharArrayDump(unsigned char * array,int size,char * dump,int dump_size)1186 char *testUtilUCharArrayDump(unsigned char *array, int size, char *dump, int dump_size) {
1187     int i, cnt_len = 0;
1188 
1189     for (i = 0; i < size; i++) {
1190         cnt_len += sprintf(dump + cnt_len, "%X ", array[i]);
1191         if (cnt_len + 3 >= dump_size) {
1192             break;
1193         }
1194     }
1195     dump[cnt_len ? cnt_len - 1 : 0] = '\0';
1196     return dump;
1197 }
1198 
1199 /* Dump a bitmap to stdout, for generate tests. Also useful for debugging */
testUtilBitmapPrint(const struct zint_symbol * symbol,const char * prefix,const char * postfix)1200 void testUtilBitmapPrint(const struct zint_symbol *symbol, const char *prefix, const char *postfix) {
1201     static const char colour[] = { '0', 'C', 'M', 'B', 'Y', 'G', 'R', '1' };
1202     int row, column, i, j;
1203 
1204     if (!prefix) {
1205         fputs("     ", stdout);
1206         for (column = 0; column < symbol->bitmap_width; column += 10) printf("%-3d       ", column);
1207         fputs("\n     ", stdout);
1208         for (column = 0; column < symbol->bitmap_width; column++) printf("%d", column % 10);
1209         putchar('\n');
1210     }
1211 
1212     for (row = 0; row < symbol->bitmap_height; row++) {
1213         if (!prefix) {
1214             printf("%3d: ", row);
1215         } else {
1216             if (*prefix) {
1217                 fputs(prefix, stdout);
1218             }
1219             putchar('"');
1220         }
1221         for (column = 0; column < symbol->bitmap_width; column++) {
1222             if (symbol->output_options & OUT_BUFFER_INTERMEDIATE) {
1223                 putchar(symbol->bitmap[(row * symbol->bitmap_width) + column]);
1224             } else {
1225                 i = ((row * symbol->bitmap_width) + column) * 3;
1226                 if ((symbol->bitmap[i] == 0 || symbol->bitmap[i] == 0xff)
1227                         && (symbol->bitmap[i + 1] == 0 || symbol->bitmap[i + 1] == 0xff)
1228                         && (symbol->bitmap[i + 2] == 0 || symbol->bitmap[i + 2] == 0xff)) {
1229                     j = !symbol->bitmap[i] + !symbol->bitmap[i + 1] * 2 + !symbol->bitmap[i + 2] * 4;
1230                     putchar(colour[j]);
1231                 } else {
1232                     printf("%02X%02X%02X", symbol->bitmap[i], symbol->bitmap[i + 1], symbol->bitmap[i + 2]);
1233                 }
1234             }
1235         }
1236         if (!postfix) {
1237             putchar('\n');
1238         } else {
1239             putchar('"');
1240             if (*postfix) {
1241                 fputs(postfix, stdout);
1242             }
1243         }
1244     }
1245 
1246     if (!postfix) {
1247         fputs("     ", stdout);
1248         for (column = 0; column < symbol->bitmap_width; column++) printf("%d", column % 10);
1249         fputs("\n     ", stdout);
1250         for (column = 0; column < symbol->bitmap_width; column += 10) printf("%-3d       ", column);
1251         putchar('\n');
1252     }
1253 }
1254 
1255 /* Compare a bitmap to a dump */
testUtilBitmapCmp(const struct zint_symbol * symbol,const char * expected,int * row,int * column)1256 int testUtilBitmapCmp(const struct zint_symbol *symbol, const char *expected, int *row, int *column) {
1257     static const char colour[] = { '0', 'C', 'M', 'B', 'Y', 'G', 'R', '1' };
1258     int r, c = -1, i, j;
1259     const char *e = expected;
1260     const char *ep = expected + strlen(expected);
1261     char buf[7];
1262 
1263     for (r = 0; r < symbol->bitmap_height; r++) {
1264         for (c = 0; c < symbol->bitmap_width; c++) {
1265             if (symbol->output_options & OUT_BUFFER_INTERMEDIATE) {
1266                 if (*e != symbol->bitmap[(r * symbol->bitmap_width) + c]) {
1267                     *row = r;
1268                     *column = c;
1269                     return 1 /*fail*/;
1270                 }
1271                 e++;
1272             } else {
1273                 i = ((r * symbol->bitmap_width) + c) * 3;
1274                 if ((symbol->bitmap[i] == 0 || symbol->bitmap[i] == 0xff)
1275                         && (symbol->bitmap[i + 1] == 0 || symbol->bitmap[i + 1] == 0xff)
1276                         && (symbol->bitmap[i + 2] == 0 || symbol->bitmap[i + 2] == 0xff)) {
1277                     j = !symbol->bitmap[i] + !symbol->bitmap[i + 1] * 2 + !symbol->bitmap[i + 2] * 4;
1278                     if (*e != colour[j]) {
1279                         *row = r;
1280                         *column = c;
1281                         return 1 /*fail*/;
1282                     }
1283                     e++;
1284                 } else {
1285                     sprintf(buf, "%02X%02X%02X", symbol->bitmap[i], symbol->bitmap[i + 1], symbol->bitmap[i + 2]);
1286                     if (strncmp(buf, e, 6) != 0) {
1287                         *row = r;
1288                         *column = c;
1289                         return 1 /*fail*/;
1290                     }
1291                     e += 6;
1292                 }
1293             }
1294         }
1295     }
1296 
1297     *row = r;
1298     *column = c;
1299     return e != ep || r != symbol->bitmap_height || c != symbol->bitmap_width ? 1 /*fail*/ : 0 /*success*/;
1300 }
1301 
1302 /* Determine the location of test data relative to where the test is being run */
testUtilDataPath(char * buffer,int buffer_size,const char * subdir,const char * filename)1303 int testUtilDataPath(char *buffer, int buffer_size, const char *subdir, const char *filename) {
1304     int subdir_len = subdir ? (int) strlen(subdir) : 0;
1305     int filename_len = filename ? (int) strlen(filename) : 0;
1306     char *s, *s2;
1307     int len;
1308     char *cmake_src_dir;
1309 #ifdef _WIN32
1310     int i;
1311 #endif
1312 
1313     if ((cmake_src_dir = getenv("CMAKE_CURRENT_SOURCE_DIR")) != NULL) {
1314         len = (int) strlen(cmake_src_dir);
1315         if (len <= 0 || len >= buffer_size) {
1316             fprintf(stderr, "testUtilDataPath: warning CMAKE_CURRENT_SOURCE_DIR len %d, ignoring\n", len);
1317             cmake_src_dir = NULL;
1318         } else {
1319             strcpy(buffer, cmake_src_dir);
1320         }
1321     }
1322 
1323     if (cmake_src_dir == NULL) {
1324         if (getcwd(buffer, buffer_size) == NULL) {
1325             fprintf(stderr, "testUtilDataPath: getcwd NULL buffer_size %d\n", buffer_size);
1326             return 0;
1327         }
1328         len = (int) strlen(buffer);
1329 
1330         if (len <= 0) {
1331             fprintf(stderr, "testUtilDataPath: strlen <= 0\n");
1332             return 0;
1333         }
1334     }
1335 #ifdef _WIN32
1336     for (i = 0; i < len; i++) {
1337         if (buffer[i] == '\\') {
1338             buffer[i] = '/';
1339         }
1340     }
1341 #endif
1342     if (buffer[len - 1] == '/') {
1343         buffer[len--] = '\0';
1344     }
1345     if (len == 0) {
1346         fprintf(stderr, "testUtilDataPath: len == 0\n");
1347         return 0;
1348     }
1349 
1350     if ((s = strstr(buffer, "/tests")) != NULL) {
1351         while ((s2 = strstr(s + 1, "/tests")) != NULL) { // Find rightmost
1352             s = s2;
1353         }
1354         *s = '\0';
1355         len = s - buffer;
1356     }
1357     if ((s = strstr(buffer, "/backend")) != NULL) {
1358         while ((s2 = strstr(s + 1, "/backend")) != NULL) { // Find rightmost
1359             s = s2;
1360         }
1361         *s = '\0';
1362         len = s - buffer;
1363     } else if ((s = strstr(buffer, "/frontend")) != NULL) {
1364         while ((s2 = strstr(s + 1, "/frontend")) != NULL) { // Find rightmost
1365             s = s2;
1366         }
1367         *s = '\0';
1368         len = s - buffer;
1369     }
1370     if (cmake_src_dir == NULL && (s = strrchr(buffer, '/')) != NULL) { // Remove "build" dir
1371         *s = '\0';
1372         len = s - buffer;
1373     }
1374 
1375     if (subdir_len) {
1376         if (*subdir != '/' && buffer[len - 1] != '/') {
1377             if (len + 1 >= buffer_size) {
1378                 fprintf(stderr, "testUtilDataPath: subdir len (%d) + 1 >= buffer_size (%d)\n", len, buffer_size);
1379                 return 0;
1380             }
1381             buffer[len++] = '/';
1382             buffer[len] = '\0';
1383         }
1384         if (len + subdir_len >= buffer_size) {
1385             fprintf(stderr, "testUtilDataPath: len (%d) + subdir_len (%d) >= buffer_size (%d)\n",
1386                     len, subdir_len, buffer_size);
1387             return 0;
1388         }
1389         strcpy(buffer + len, subdir);
1390         len += subdir_len;
1391     }
1392 
1393     if (filename_len) {
1394         if (*filename != '/' && buffer[len - 1] != '/') {
1395             if (len + 1 >= buffer_size) {
1396                 fprintf(stderr, "testUtilDataPath: filename len (%d) + 1 >= buffer_size (%d)\n", len, buffer_size);
1397                 return 0;
1398             }
1399             buffer[len++] = '/';
1400             buffer[len] = '\0';
1401         }
1402         if (len + filename_len >= buffer_size) {
1403             fprintf(stderr, "testUtilDataPath: len (%d) + filename_len (%d) >= buffer_size (%d)\n",
1404                     len, filename_len, buffer_size);
1405             return 0;
1406         }
1407         strcpy(buffer + len, filename);
1408     }
1409 
1410     return 1;
1411 }
1412 
1413 /* Does file exist? */
testUtilExists(const char * filename)1414 int testUtilExists(const char *filename) {
1415     FILE *fp = fopen(filename, "r");
1416     if (fp == NULL) {
1417         return 0;
1418     }
1419     fclose(fp);
1420     return 1;
1421 }
1422 
1423 /* Does directory exist? (Windows compatibility) */
testUtilDirExists(const char * dirname)1424 int testUtilDirExists(const char *dirname) {
1425 #ifdef _WIN32
1426     DWORD dwAttrib = GetFileAttributes(dirname);
1427     return dwAttrib != (DWORD) -1 && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
1428 #else
1429     return testUtilExists(dirname);
1430 #endif
1431 }
1432 
1433 /* Make a directory (Windows compatibility). Returns 0 if successful, non-zero if not */
testUtilMkDir(const char * dirname)1434 int testUtilMkDir(const char *dirname) {
1435 #ifdef _WIN32
1436     return CreateDirectory(dirname, NULL) == 0;
1437 #else
1438     return mkdir(dirname, S_IRWXU);
1439 #endif
1440 }
1441 
1442 /* Remove a directory (Windows compatibility). Returns 0 if successful, non-zero if not */
testUtilRmDir(const char * dirname)1443 int testUtilRmDir(const char *dirname) {
1444 #ifdef _WIN32
1445     return RemoveDirectory(dirname) == 0;
1446 #else
1447     return rmdir(dirname);
1448 #endif
1449 }
1450 
1451 /* Rename a file (Windows compatibility). */
testUtilRename(const char * oldpath,const char * newpath)1452 int testUtilRename(const char *oldpath, const char *newpath) {
1453 #ifdef _MSVC
1454     int ret = remove(newpath);
1455     if (ret != 0) return ret;
1456 #endif
1457     return rename(oldpath, newpath);
1458 }
1459 
1460 /* Compare 2 PNG files */
testUtilCmpPngs(const char * png1,const char * png2)1461 int testUtilCmpPngs(const char *png1, const char *png2) {
1462     int ret = -1;
1463 #ifdef NO_PNG
1464     (void)png1; (void)png2;
1465 #else
1466     FILE *fp1;
1467     FILE *fp2;
1468     png_structp png_ptr1, png_ptr2;
1469     png_infop info_ptr1, info_ptr2;
1470     int width1, height1, width2, height2;
1471     png_byte color_type1, color_type2;
1472     png_byte bit_depth1, bit_depth2;
1473     png_bytep row1 = NULL, row2 = NULL;
1474     size_t rowbytes1, rowbytes2;
1475     int r;
1476 
1477     fp1 = fopen(png1, "rb");
1478     if (!fp1) {
1479         return 2;
1480     }
1481     fp2 = fopen(png2, "rb");
1482     if (!fp2) {
1483         fclose(fp1);
1484         return 3;
1485     }
1486 
1487     png_ptr1 = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, NULL, NULL);
1488     if (!png_ptr1) {
1489         fclose(fp1);
1490         fclose(fp2);
1491         return 4;
1492     }
1493     info_ptr1 = png_create_info_struct(png_ptr1);
1494     if (!info_ptr1) {
1495         png_destroy_read_struct(&png_ptr1, (png_infopp) NULL, (png_infopp) NULL);
1496         fclose(fp1);
1497         fclose(fp2);
1498         return 5;
1499     }
1500 
1501     png_ptr2 = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, NULL, NULL);
1502     if (!png_ptr2) {
1503         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp) NULL);
1504         fclose(fp1);
1505         fclose(fp2);
1506         return 6;
1507     }
1508     info_ptr2 = png_create_info_struct(png_ptr2);
1509     if (!info_ptr2) {
1510         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp) NULL);
1511         png_destroy_read_struct(&png_ptr2, (png_infopp) NULL, (png_infopp) NULL);
1512         fclose(fp1);
1513         fclose(fp2);
1514         return 7;
1515     }
1516 
1517     if (setjmp(png_jmpbuf(png_ptr1))) {
1518         if (row1) {
1519             free(row1);
1520         }
1521         if (row2) {
1522             free(row2);
1523         }
1524         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp) NULL);
1525         png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp) NULL);
1526         fclose(fp1);
1527         fclose(fp2);
1528         return 8;
1529     }
1530     if (setjmp(png_jmpbuf(png_ptr2))) {
1531         if (row1) {
1532             free(row1);
1533         }
1534         if (row2) {
1535             free(row2);
1536         }
1537         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp) NULL);
1538         png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp) NULL);
1539         fclose(fp1);
1540         fclose(fp2);
1541         return 9;
1542     }
1543 
1544     png_init_io(png_ptr1, fp1);
1545     png_init_io(png_ptr2, fp2);
1546 
1547     png_read_info(png_ptr1, info_ptr1);
1548     png_read_info(png_ptr2, info_ptr2);
1549 
1550     width1 = png_get_image_width(png_ptr1, info_ptr1);
1551     height1 = png_get_image_height(png_ptr1, info_ptr1);
1552     width2 = png_get_image_width(png_ptr2, info_ptr2);
1553     height2 = png_get_image_height(png_ptr2, info_ptr2);
1554 
1555     if (width1 != width2 || height1 != height2) {
1556         printf("width1 %d, width2 %d, height1 %d, height2 %d\n", width1, width2, height1, height2);
1557         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp) NULL);
1558         png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp) NULL);
1559         fclose(fp1);
1560         fclose(fp2);
1561         return 10;
1562     }
1563 
1564     color_type1 = png_get_color_type(png_ptr1, info_ptr1);
1565     bit_depth1 = png_get_bit_depth(png_ptr1, info_ptr1);
1566     if (bit_depth1 == 16) {
1567         png_set_scale_16(png_ptr1);
1568     }
1569     if (color_type1 == PNG_COLOR_TYPE_PALETTE) {
1570         png_set_palette_to_rgb(png_ptr1);
1571     }
1572     if (color_type1 == PNG_COLOR_TYPE_GRAY && bit_depth1 < 8) {
1573         png_set_expand_gray_1_2_4_to_8(png_ptr1);
1574     }
1575     if (png_get_valid(png_ptr1, info_ptr1, PNG_INFO_tRNS)) {
1576         png_set_tRNS_to_alpha(png_ptr1);
1577     }
1578     if (color_type1 == PNG_COLOR_TYPE_RGB || color_type1 == PNG_COLOR_TYPE_GRAY
1579             || color_type1 == PNG_COLOR_TYPE_PALETTE) {
1580         png_set_filler(png_ptr1, 0xFF, PNG_FILLER_AFTER);
1581     }
1582     if (color_type1 == PNG_COLOR_TYPE_GRAY || color_type1 == PNG_COLOR_TYPE_GRAY_ALPHA) {
1583         png_set_gray_to_rgb(png_ptr1);
1584     }
1585 
1586     color_type2 = png_get_color_type(png_ptr2, info_ptr2);
1587     bit_depth2 = png_get_bit_depth(png_ptr2, info_ptr2);
1588     if (bit_depth2 == 16) {
1589         png_set_scale_16(png_ptr2);
1590     }
1591     if (color_type2 == PNG_COLOR_TYPE_PALETTE) {
1592         png_set_palette_to_rgb(png_ptr2);
1593     }
1594     if (color_type2 == PNG_COLOR_TYPE_GRAY && bit_depth2 < 8) {
1595         png_set_expand_gray_1_2_4_to_8(png_ptr2);
1596     }
1597     if (png_get_valid(png_ptr2, info_ptr2, PNG_INFO_tRNS)) {
1598         png_set_tRNS_to_alpha(png_ptr2);
1599     }
1600     if (color_type2 == PNG_COLOR_TYPE_RGB || color_type2 == PNG_COLOR_TYPE_GRAY
1601             || color_type2 == PNG_COLOR_TYPE_PALETTE) {
1602         png_set_filler(png_ptr2, 0xFF, PNG_FILLER_AFTER);
1603     }
1604     if (color_type2 == PNG_COLOR_TYPE_GRAY || color_type2 == PNG_COLOR_TYPE_GRAY_ALPHA) {
1605         png_set_gray_to_rgb(png_ptr2);
1606     }
1607 
1608     png_read_update_info(png_ptr1, info_ptr1);
1609     png_read_update_info(png_ptr2, info_ptr2);
1610 
1611     rowbytes1 = png_get_rowbytes(png_ptr1, info_ptr1);
1612     rowbytes2 = png_get_rowbytes(png_ptr2, info_ptr2);
1613     if (rowbytes1 != rowbytes2) {
1614         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp)NULL);
1615         png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp)NULL);
1616         fclose(fp1);
1617         fclose(fp2);
1618         return 11;
1619     }
1620 
1621     row1 = malloc(rowbytes1);
1622     if (!row1) {
1623         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp)NULL);
1624         png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp)NULL);
1625         fclose(fp1);
1626         fclose(fp2);
1627         return 12;
1628     }
1629     row2 = malloc(rowbytes2);
1630     if (!row2) {
1631         free(row1);
1632         png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp)NULL);
1633         png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp)NULL);
1634         fclose(fp1);
1635         fclose(fp2);
1636         return 13;
1637     }
1638 
1639     for (r = 0; r < height1; r++) {
1640         png_read_row(png_ptr1, row1, NULL);
1641         png_read_row(png_ptr2, row2, NULL);
1642         if (memcmp(row1, row2, rowbytes1) != 0) {
1643             break;
1644         }
1645     }
1646     ret = r == height1 ? 0 : 20;
1647 
1648     free(row1);
1649     free(row2);
1650     png_destroy_read_struct(&png_ptr1, &info_ptr1, (png_infopp)NULL);
1651     png_destroy_read_struct(&png_ptr2, &info_ptr2, (png_infopp)NULL);
1652     fclose(fp1);
1653     fclose(fp2);
1654 #endif
1655     return ret;
1656 }
1657 
1658 /* Compare 2 TXT files */
testUtilCmpTxts(const char * txt1,const char * txt2)1659 int testUtilCmpTxts(const char *txt1, const char *txt2) {
1660     int ret = -1;
1661     FILE *fp1;
1662     FILE *fp2;
1663     char buf1[1024];
1664     char buf2[1024];
1665     size_t len1 = 0, len2 = 0;
1666 
1667     fp1 = fopen(txt1, "r");
1668     if (!fp1) {
1669         return 2;
1670     }
1671     fp2 = fopen(txt2, "r");
1672     if (!fp2) {
1673         fclose(fp1);
1674         return 3;
1675     }
1676 
1677     while (1) {
1678         if (fgets(buf1, sizeof(buf1), fp1) == NULL) {
1679             if (fgets(buf2, sizeof(buf2), fp2) != NULL) {
1680                 ret = 4;
1681                 break;
1682             }
1683             break;
1684         }
1685         if (fgets(buf2, sizeof(buf2), fp2) == NULL) {
1686             ret = 5;
1687             break;
1688         }
1689         len1 = strlen(buf1);
1690         len2 = strlen(buf2);
1691         if (len1 != len2) {
1692             ret = 6;
1693             break;
1694         }
1695         if (strcmp(buf1, buf2) != 0) {
1696             ret = 7;
1697             break;
1698         }
1699     }
1700     if (ret == -1) {
1701         ret = feof(fp1) && feof(fp2) ? 0 : 20;
1702     }
1703     fclose(fp1);
1704     fclose(fp2);
1705 
1706     return ret;
1707 }
1708 
1709 /* Compare 2 binary files */
testUtilCmpBins(const char * bin1,const char * bin2)1710 int testUtilCmpBins(const char *bin1, const char *bin2) {
1711     int ret = -1;
1712     FILE *fp1;
1713     FILE *fp2;
1714     char buf1[1024];
1715     char buf2[1024];
1716     size_t len1 = 0, len2 = 0;
1717 
1718     fp1 = fopen(bin1, "rb");
1719     if (!fp1) {
1720         return 2;
1721     }
1722     fp2 = fopen(bin2, "rb");
1723     if (!fp2) {
1724         fclose(fp1);
1725         return 3;
1726     }
1727 
1728     do {
1729         len1 = fread(buf1, 1, sizeof(buf1), fp1);
1730         len2 = fread(buf2, 1, sizeof(buf2), fp2);
1731         if (len1 != len2) {
1732             ret = 6;
1733             break;
1734         }
1735         if (memcmp(buf1, buf2, len1) != 0) {
1736             ret = 7;
1737             break;
1738         }
1739     } while (!feof(fp1) && !feof(fp2));
1740 
1741     if (ret == -1) {
1742         ret = feof(fp1) && feof(fp2) ? 0 : 20;
1743     }
1744     fclose(fp1);
1745     fclose(fp2);
1746 
1747     return ret;
1748 }
1749 
1750 /* Compare 2 SVG files */
testUtilCmpSvgs(const char * svg1,const char * svg2)1751 int testUtilCmpSvgs(const char *svg1, const char *svg2) {
1752     return testUtilCmpTxts(svg1, svg2);
1753 }
1754 
1755 /* Compare 2 EPS files */
testUtilCmpEpss(const char * eps1,const char * eps2)1756 int testUtilCmpEpss(const char *eps1, const char *eps2) {
1757     int ret = -1;
1758     FILE *fp1;
1759     FILE *fp2;
1760     char buf1[1024];
1761     char buf2[1024];
1762     size_t len1 = 0, len2 = 0;
1763     char first_line[] = "%!PS-Adobe-3.0 EPSF-3.0\n";
1764     char second_line_start[] = "%%Creator: Zint ";
1765 
1766     fp1 = fopen(eps1, "r");
1767     if (!fp1) {
1768         return 2;
1769     }
1770     fp2 = fopen(eps2, "r");
1771     if (!fp2) {
1772         fclose(fp1);
1773         return 3;
1774     }
1775 
1776     // Preprocess the 1st 2 lines to avoid comparing changeable Zint version in 2nd line
1777     if (fgets(buf1, sizeof(buf1), fp1) == NULL || strcmp(buf1, first_line) != 0
1778             || fgets(buf2, sizeof(buf2), fp2) == NULL || strcmp(buf2, first_line) != 0) {
1779         ret = 10;
1780     } else if (fgets(buf1, sizeof(buf1), fp1) == NULL
1781             || strncmp(buf1, second_line_start, sizeof(second_line_start) - 1) != 0
1782             || fgets(buf2, sizeof(buf2), fp2) == NULL
1783             || strncmp(buf2, second_line_start, sizeof(second_line_start) - 1) != 0) {
1784         ret = 11;
1785     }
1786 
1787     if (ret == -1) {
1788         while (1) {
1789             if (fgets(buf1, sizeof(buf1), fp1) == NULL) {
1790                 if (fgets(buf2, sizeof(buf2), fp2) != NULL) {
1791                     ret = 4;
1792                     break;
1793                 }
1794                 break;
1795             }
1796             if (fgets(buf2, sizeof(buf2), fp2) == NULL) {
1797                 ret = 5;
1798                 break;
1799             }
1800             len1 = strlen(buf1);
1801             len2 = strlen(buf2);
1802             if (len1 != len2) {
1803                 ret = 6;
1804                 break;
1805             }
1806             if (strcmp(buf1, buf2) != 0) {
1807                 ret = 7;
1808                 break;
1809             }
1810         }
1811         if (ret == -1) {
1812             ret = feof(fp1) && feof(fp2) ? 0 : 20;
1813         }
1814     }
1815     fclose(fp1);
1816     fclose(fp2);
1817 
1818     return ret;
1819 }
1820 
1821 #ifdef _WIN32
1822 #define DEV_NULL "> NUL"
1823 #define DEV_NULL_STDERR "> NUL 2>&1"
1824 #else
1825 #define DEV_NULL "> /dev/null"
1826 #define DEV_NULL_STDERR "> /dev/null 2>&1"
1827 #endif
1828 
1829 /* Whether ImageMagick's identify utility available on system */
testUtilHaveIdentify()1830 int testUtilHaveIdentify() {
1831     return system("magick -version " DEV_NULL) == 0;
1832 }
1833 
1834 /* Check raster files */
testUtilVerifyIdentify(const char * filename,int debug)1835 int testUtilVerifyIdentify(const char *filename, int debug) {
1836     char cmd[512 + 128];
1837 
1838     if (strlen(filename) > 512) {
1839         return -1;
1840     }
1841     // Verbose option does a more thorough check
1842     if (debug & ZINT_DEBUG_TEST_PRINT) {
1843         // Verbose very noisy though so for quick check just return default output
1844         if (debug & ZINT_DEBUG_TEST_LESS_NOISY) {
1845             sprintf(cmd, "magick identify %s", filename);
1846         } else {
1847             sprintf(cmd, "magick identify -verbose %s", filename);
1848         }
1849     } else {
1850         sprintf(cmd, "magick identify -verbose %s " DEV_NULL, filename);
1851     }
1852 
1853     return system(cmd);
1854 }
1855 
1856 /* Whether Libre Office available on system */
testUtilHaveLibreOffice()1857 int testUtilHaveLibreOffice() {
1858     return system("libreoffice --version " DEV_NULL) == 0;
1859 }
1860 
1861 /* Check SVG files, very hacky to evoke. Will fail if Libre package that is not LibreOffice Draw is running */
testUtilVerifyLibreOffice(const char * filename,int debug)1862 int testUtilVerifyLibreOffice(const char *filename, int debug) {
1863     char cmd[512 + 128];
1864     char svg[512];
1865     char *slash, *dot;
1866     char buf[16384];
1867     char *b = buf, *be = buf + sizeof(buf) - 1;
1868     FILE *fp;
1869     int len;
1870 
1871     /* Hack to read SVG produced by LibreOffice and search for 'x="-32767"' which indicates it didn't load barcode
1872        file */
1873     if (strlen(filename) > 512) {
1874         return -1;
1875     }
1876     slash = strrchr(filename, '/');
1877     if (slash) {
1878         strcpy(svg, slash + 1);
1879     } else {
1880         strcpy(svg, filename);
1881     }
1882     dot = strrchr(svg, '.');
1883     if (dot) {
1884         strcpy(dot, ".svg");
1885     } else {
1886         strcat(svg, ".svg");
1887     }
1888     if (strcmp(svg, filename) == 0) {
1889         fprintf(stderr, "testUtilVerifyLibreOffice: input '%s' same as svg '%s'\n", filename, svg);
1890         return -1;
1891     }
1892 
1893     sprintf(cmd, "libreoffice --convert-to svg %s " DEV_NULL_STDERR, filename);
1894     if (debug & ZINT_DEBUG_TEST_PRINT) {
1895         printf("%s\n", cmd);
1896     }
1897     if (system(cmd) != 0) {
1898         fprintf(stderr, "testUtilVerifyLibreOffice: failed to run '%s'\n", cmd);
1899         return -1;
1900     }
1901 
1902     fp = fopen(svg, "r");
1903     if (!fp) {
1904         fprintf(stderr, "testUtilVerifyLibreOffice: failed to open '%s' (%s)\n", svg, cmd);
1905         return -1;
1906     }
1907     while (!feof(fp) && b < be) {
1908         if (fgets(b, be - b, fp) == NULL) {
1909             fprintf(stderr, "testUtilVerifyLibreOffice: failed to get line from '%s' (%s)\n", svg, cmd);
1910             fclose(fp);
1911             return -1;
1912         }
1913         len = (int) strlen(b);
1914         if (len == 0) {
1915             break;
1916         }
1917         b += len;
1918     }
1919     *b = '\0';
1920     fclose(fp);
1921 
1922     if (strlen(buf) < 1024) {
1923         fprintf(stderr, "testUtilVerifyLibreOffice: failed to get much input from '%s' (%s)\n", svg, cmd);
1924         return -1;
1925     }
1926     if (strstr(buf, "x=\"-32767\"") != NULL) {
1927         return -1;
1928     }
1929     remove(svg);
1930 
1931     return 0;
1932 }
1933 
1934 #ifdef _WIN32
1935 #define GS_FILENAME "gswin64c"
1936 #else
1937 #define GS_FILENAME "gs"
1938 #endif
1939 
1940 /* Whether Ghostscript available on system */
testUtilHaveGhostscript()1941 int testUtilHaveGhostscript() {
1942     return system(GS_FILENAME " -v " DEV_NULL) == 0;
1943 }
1944 
1945 /* Check EPS files */
testUtilVerifyGhostscript(const char * filename,int debug)1946 int testUtilVerifyGhostscript(const char *filename, int debug) {
1947     char cmd[512 + 128];
1948 
1949     if (strlen(filename) > 512) {
1950         return -1;
1951     }
1952     if (debug & ZINT_DEBUG_TEST_PRINT) {
1953         // Prints nothing of interest with or without -q unless bad
1954         sprintf(cmd, GS_FILENAME " -dNOPAUSE -dBATCH -dNODISPLAY -q %s", filename);
1955         printf("%s\n", cmd);
1956     } else {
1957         sprintf(cmd, GS_FILENAME " -dNOPAUSE -dBATCH -dNODISPLAY -q %s", filename);
1958     }
1959 
1960     return system(cmd);
1961 }
1962 
1963 /* Whether vnu validator available on system. v.Nu https://github.com/validator/validator
1964    Needs "$INSTALL_DIR/vnu-runtime-image/bin" in PATH */
testUtilHaveVnu()1965 int testUtilHaveVnu() {
1966     return system("vnu --version " DEV_NULL_STDERR) == 0;
1967 }
1968 
1969 /* Check SVG files, very full but very slow */
testUtilVerifyVnu(const char * filename,int debug)1970 int testUtilVerifyVnu(const char *filename, int debug) {
1971     char buf[512 + 128];
1972 
1973     if (strlen(filename) > 512) {
1974         return -1;
1975     }
1976     if (debug & ZINT_DEBUG_TEST_PRINT) {
1977         sprintf(buf, "vnu --svg --verbose %s", filename);
1978         printf("%s\n", buf);
1979     } else {
1980         sprintf(buf, "vnu --svg %s", filename);
1981     }
1982 
1983     return system(buf);
1984 }
1985 
1986 /* Whether tiffinfo available on system. Requires libtiff 4.2.0 http://www.libtiff.org to be installed */
testUtilHaveTiffInfo()1987 int testUtilHaveTiffInfo() {
1988     return system("tiffinfo -h " DEV_NULL) == 0;
1989 }
1990 
1991 /* Check TIF files */
testUtilVerifyTiffInfo(const char * filename,int debug)1992 int testUtilVerifyTiffInfo(const char *filename, int debug) {
1993     char cmd[512 + 128];
1994 
1995     if (strlen(filename) > 512) {
1996         return -1;
1997     }
1998     if (debug & ZINT_DEBUG_TEST_PRINT) {
1999         sprintf(cmd, "tiffinfo -D %s", filename);
2000     } else {
2001         sprintf(cmd, "tiffinfo -D %s " DEV_NULL_STDERR, filename);
2002     }
2003 
2004     return system(cmd);
2005 }
2006 
2007 /* Map Zint symbology to BWIPP routine */
testUtilBwippName(int index,const struct zint_symbol * symbol,int option_1,int option_2,int option_3,int debug,int * linear_row_height,int * gs1_cvt)2008 static const char *testUtilBwippName(int index, const struct zint_symbol *symbol, int option_1, int option_2,
2009                     int option_3, int debug, int *linear_row_height, int *gs1_cvt) {
2010     struct item {
2011         const char *name;
2012         int define;
2013         int val;
2014         int can_option_1;
2015         int can_option_2;
2016         int can_option_3;
2017         int linear_row_height;
2018         int gs1_cvt;
2019     };
2020     static const struct item data[] = {
2021         { "", -1, 0, 0, 0, 0, 0, 0, },
2022         { "code11", BARCODE_CODE11, 1, 0, 1, 0, 0, 0, },
2023         { "matrix2of5", BARCODE_C25STANDARD, 2, 0, 1, 0, 0, 0, },
2024         { "interleaved2of5", BARCODE_C25INTER, 3, 0, 1, 0, 0, 0, },
2025         { "iata2of5", BARCODE_C25IATA, 4, 0, 1, 0, 0, 0, },
2026         { "", -1, 5, 0, 0, 0, 0, 0, },
2027         { "datalogic2of5", BARCODE_C25LOGIC, 6, 0, 1, 0, 0, 0, },
2028         { "industrial2of5", BARCODE_C25IND, 7, 0, 1, 0, 0, 0, },
2029         { "code39", BARCODE_CODE39, 8, 0, 1, 0, 0, 0, },
2030         { "code39ext", BARCODE_EXCODE39, 9, 0, 1, 0, 0, 0, },
2031         { "", -1, 10, 0, 0, 0, 0, 0, },
2032         { "", -1, 11, 0, 0, 0, 0, 0, },
2033         { "", -1, 12, 0, 0, 0, 0, 0, },
2034         { "ean13", BARCODE_EANX, 13, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
2035         { "ean13", BARCODE_EANX_CHK, 14, 0, 1, 0, 0, 1, },
2036         { "", -1, 15, 0, 0, 0, 0, 0, },
2037         { "gs1-128", BARCODE_GS1_128, 16, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
2038         { "", -1, 17, 0, 0, 0, 0, 0, },
2039         { "rationalizedCodabar", BARCODE_CODABAR, 18, 0, 1, 0, 0, 0, },
2040         { "", -1, 19, 0, 0, 0, 0, 0, },
2041         { "code128", BARCODE_CODE128, 20, 0, 0, 0, 0, 0, },
2042         { "leitcode", BARCODE_DPLEIT, 21, 0, 0, 0, 0, 0, },
2043         { "identcode", BARCODE_DPIDENT, 22, 0, 0, 0, 0, 0, },
2044         { "code16k", BARCODE_CODE16K, 23, 0, 0, 0, 8 /*linear_row_height*/, 0, },
2045         { "code49", BARCODE_CODE49, 24, 0, 0, 0, 8 /*linear_row_height*/, 0, },
2046         { "code93", BARCODE_CODE93, 25, 0, 0, 0, 0, 0, },
2047         { "", -1, 26, 0, 0, 0, 0, 0, },
2048         { "", -1, 27, 0, 0, 0, 0, 0, },
2049         { "flattermarken", BARCODE_FLAT, 28, 0, 0, 0, 0, 0, },
2050         { "databaromni", BARCODE_DBAR_OMN, 29, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
2051         { "databarlimited", BARCODE_DBAR_LTD, 30, 0, 0, 0, 0, 1, },
2052         { "databarexpanded", BARCODE_DBAR_EXP, 31, 0, 1, 0, 1 /*linear_row_height*/, 1, },
2053         { "telepen", BARCODE_TELEPEN, 32, 0, 0, 0, 0, 0, },
2054         { "", -1, 33, 0, 0, 0, 0, 0, },
2055         { "upca", BARCODE_UPCA, 34, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
2056         { "upca", BARCODE_UPCA_CHK, 35, 0, 1, 0, 0, 1, },
2057         { "", -1, 36, 0, 0, 0, 0, 0, },
2058         { "upce", BARCODE_UPCE, 37, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
2059         { "upce", BARCODE_UPCE_CHK, 38, 0, 1, 0, 0, 1, },
2060         { "", -1, 39, 0, 0, 0, 0, 0, },
2061         { "postnet", BARCODE_POSTNET, 40, 0, 0, 0, 0, 0, },
2062         { "", -1, 41, 0, 0, 0, 0, 0, },
2063         { "", -1, 42, 0, 0, 0, 0, 0, },
2064         { "", -1, 43, 0, 0, 0, 0, 0, },
2065         { "", -1, 44, 0, 0, 0, 0, 0, },
2066         { "", -1, 45, 0, 0, 0, 0, 0, },
2067         { "", -1, 46, 0, 0, 0, 0, 0, },
2068         { "msi", BARCODE_MSI_PLESSEY, 47, 0, 1, 0, 0, 0, },
2069         { "", -1, 48, 0, 0, 0, 0, 0, },
2070         { "symbol", BARCODE_FIM, 49, 0, 0, 0, 0, 0, },
2071         { "code39", BARCODE_LOGMARS, 50, 0, 1, 0, 0, 0, },
2072         { "pharmacode", BARCODE_PHARMA, 51, 0, 0, 0, 1 /*linear_row_height*/, 0, },
2073         { "pzn", BARCODE_PZN, 52, 0, 0, 0, 0, 0, },
2074         { "pharmacode2", BARCODE_PHARMA_TWO, 53, 0, 0, 0, 0, 0, },
2075         { "", -1, 54, 0, 0, 0, 0, 0, },
2076         { "pdf417", BARCODE_PDF417, 55, 1, 1, 0, 0, 0, },
2077         { "pdf417compact", BARCODE_PDF417COMP, 56, 1, 1, 0, 0, 0, },
2078         { "maxicode", BARCODE_MAXICODE, 57, 1, 1, 0, 0, 0, },
2079         { "qrcode", BARCODE_QRCODE, 58, 0, 0, 0, 0, 0, },
2080         { "", -1, 59, 0, 0, 0, 0, 0, },
2081         { "", BARCODE_CODE128B, 60, 0, 0, 0, 0, 0, },
2082         { "", -1, 61, 0, 0, 0, 0, 0, },
2083         { "", -1, 62, 0, 0, 0, 0, 0, },
2084         { "auspost", BARCODE_AUSPOST, 63, 0, 0, 0, 0, 0, },
2085         { "", -1, 64, 0, 0, 0, 0, 0, },
2086         { "", -1, 65, 0, 0, 0, 0, 0, },
2087         { "", BARCODE_AUSREPLY, 66, 0, 0, 0, 0, 0, },
2088         { "", BARCODE_AUSROUTE, 67, 0, 0, 0, 0, 0, },
2089         { "", BARCODE_AUSREDIRECT, 68, 0, 0, 0, 0, 0, },
2090         { "isbn", BARCODE_ISBNX, 69, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
2091         { "royalmail", BARCODE_RM4SCC, 70, 0, 0, 0, 0, 0, },
2092         { "datamatrix", BARCODE_DATAMATRIX, 71, 0, 1, 1, 1, 0, },
2093         { "ean14", BARCODE_EAN14, 72, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
2094         { "code39", BARCODE_VIN, 73, 0, 0, 0, 0, 0, },
2095         { "codablockf", BARCODE_CODABLOCKF, 74, 1, 1, 0, 10 /*linear_row_height*/, 0, },
2096         { "sscc18", BARCODE_NVE18, 75, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
2097         { "japanpost", BARCODE_JAPANPOST, 76, 0, 0, 0, 0, 0, },
2098         { "", BARCODE_KOREAPOST, 77, 0, 0, 0, 0, 0, },
2099         { "", -1, 78, 0, 0, 0, 0, 0, },
2100         { "databarstacked", BARCODE_DBAR_STK, 79, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
2101         { "databarstackedomni", BARCODE_DBAR_OMNSTK, 80, 0, 0, 0, 33 /*linear_row_height*/, 1, },
2102         { "databarexpandedstacked", BARCODE_DBAR_EXPSTK, 81, 0, 1, 0, 34 /*linear_row_height*/, 1, },
2103         { "planet", BARCODE_PLANET, 82, 0, 0, 0, 0, 0, },
2104         { "", -1, 83, 0, 0, 0, 0, 0, },
2105         { "micropdf417", BARCODE_MICROPDF417, 84, 0, 1, 0, 0, 0, },
2106         { "onecode", BARCODE_USPS_IMAIL, 85, 0, 0, 0, 0, 0, },
2107         { "plessey", BARCODE_PLESSEY, 86, 0, 0, 0, 0, 0, },
2108         { "telepennumeric", BARCODE_TELEPEN_NUM, 87, 0, 0, 0, 0, 0, },
2109         { "", -1, 88, 0, 0, 0, 0, 0, },
2110         { "itf14", BARCODE_ITF14, 89, 0, 0, 0, 0, 0, },
2111         { "kix", BARCODE_KIX, 90, 0, 0, 0, 0, 0, },
2112         { "", -1, 91, 0, 0, 0, 0, 0, },
2113         { "azteccode", BARCODE_AZTEC, 92, 1, 1, 0, 0, 0, },
2114         { "daft", BARCODE_DAFT, 93, 0, 0, 0, 0, 0, },
2115         { "", -1, 94, 0, 0, 0, 0, 0, },
2116         { "", -1, 95, 0, 0, 0, 0, 0, },
2117         { "", BARCODE_DPD, 96, 0, 0, 0, 0, 0, },
2118         { "microqrcode", BARCODE_MICROQR, 97, 1, 1, 1, 0, 0, },
2119         { "hibccode128", BARCODE_HIBC_128, 98, 0, 0, 0, 0, 0, },
2120         { "hibccode39", BARCODE_HIBC_39, 99, 0, 0, 0, 0, 0, },
2121         { "", -1, 100, 0, 0, 0, 0, 0, },
2122         { "", -1, 101, 0, 0, 0, 0, 0, },
2123         { "hibcdatamatrix", BARCODE_HIBC_DM, 102, 0, 1, 1, 0, 0, },
2124         { "", -1, 103, 0, 0, 0, 0, 0, },
2125         { "hibcqrcode", BARCODE_HIBC_QR, 104, 0, 0, 0, 0, 0, },
2126         { "", -1, 105, 0, 0, 0, 0, 0, },
2127         { "hibcpdf417", BARCODE_HIBC_PDF, 106, 1, 1, 0, 0, 0, },
2128         { "", -1, 107, 0, 0, 0, 0, 0, },
2129         { "hibcmicropdf417", BARCODE_HIBC_MICPDF, 108, 0, 1, 0, 0, 0, },
2130         { "", -1, 109, 0, 0, 0, 0, 0, },
2131         { "hibccodablockf", BARCODE_HIBC_BLOCKF, 110, 1, 1, 0, 10 /*linear_row_height*/, 0, },
2132         { "", -1, 111, 0, 0, 0, 0, 0, },
2133         { "hibcazteccode", BARCODE_HIBC_AZTEC, 112, 1, 0, 1, 0, 0, },
2134         { "", -1, 113, 0, 0, 0, 0, 0, },
2135         { "", -1, 114, 0, 0, 0, 0, 0, },
2136         { "dotcode", BARCODE_DOTCODE, 115, 0, 1, 1, 0, 0, },
2137         { "hanxin", BARCODE_HANXIN, 116, 0, 0, 0, 0, 0, },
2138         { "", -1, 117, 0, 0, 0, 0, 0, },
2139         { "", -1, 118, 0, 0, 0, 0, 0, },
2140         { "", -1, 119, 0, 0, 0, 0, 0, },
2141         { "", -1, 120, 0, 0, 0, 0, 0, },
2142         { "", BARCODE_MAILMARK, 121, 0, 0, 0, 0, 0, }, /* Note BWIPP mailmark is Data Matrix variant */
2143         { "", -1, 122, 0, 0, 0, 0, 0, },
2144         { "", -1, 123, 0, 0, 0, 0, 0, },
2145         { "", -1, 124, 0, 0, 0, 0, 0, },
2146         { "", -1, 125, 0, 0, 0, 0, 0, },
2147         { "", -1, 126, 0, 0, 0, 0, 0, },
2148         { "", -1, 127, 0, 0, 0, 0, 0, },
2149         { "aztecrune", BARCODE_AZRUNE, 128, 0, 0, 0, 0, 0, },
2150         { "code32", BARCODE_CODE32, 129, 0, 0, 0, 0, 0, },
2151         { "ean13composite", BARCODE_EANX_CC, 130, 1, 1, 0, 72 /*linear_row_height*/, 1 /*gs1_cvt*/, },
2152         { "gs1-128composite", BARCODE_GS1_128_CC, 131, 1, 0, 0, 36, 1, },
2153         { "databaromnicomposite", BARCODE_DBAR_OMN_CC, 132, 1, 0, 0, 33, 1, },
2154         { "databarlimitedcomposite", BARCODE_DBAR_LTD_CC, 133, 1, 0, 0, 10 /*linear_row_height*/, 1, },
2155         { "databarexpandedcomposite", BARCODE_DBAR_EXP_CC, 134, 1, 1, 0, 34 /*linear_row_height*/, 1, },
2156         { "upcacomposite", BARCODE_UPCA_CC, 135, 1, 1, 0, 72, 1, },
2157         { "upcecomposite", BARCODE_UPCE_CC, 136, 1, 1, 0, 72, 1, },
2158         { "databarstackedcomposite", BARCODE_DBAR_STK_CC, 137, 1, 0, 0, 0, 1, },
2159         { "databarstackedomnicomposite", BARCODE_DBAR_OMNSTK_CC, 138, 1, 0, 0, 33 /*linear_row_height*/, 1, },
2160         { "databarexpandedstackedcomposite", BARCODE_DBAR_EXPSTK_CC, 139, 1, 1, 0, 34 /*linear_row_height*/, 1, },
2161         { "channelcode", BARCODE_CHANNEL, 140, 0, 0, 0, 0, 0, },
2162         { "codeone", BARCODE_CODEONE, 141, 0, 1, 0, 0, 0, },
2163         { "", BARCODE_GRIDMATRIX, 142, 0, 0, 0, 0, 0, },
2164         { "", BARCODE_UPNQR, 143, 0, 0, 0, 0, 0, },
2165         { "ultracode", BARCODE_ULTRA, 144, 1, 0, 0, 0, 0, },
2166         { "rectangularmicroqrcode", BARCODE_RMQR, 145, 1, 1, 0, 0, 0, },
2167     };
2168     static const int data_size = ARRAY_SIZE(data);
2169 
2170     int symbology = symbol->symbology;
2171     int gs1 = (symbol->input_mode & 0x07) == GS1_MODE;
2172 
2173     if (symbology < 0 || symbology >= data_size) {
2174         fprintf(stderr, "testUtilBwippName: unknown symbology (%d)\n", symbology);
2175         abort();
2176     }
2177     // Self-check
2178     if (data[symbology].val != symbology || (data[symbology].define != -1 && data[symbology].define != symbology)) {
2179         fprintf(stderr, "testUtilBwippName: data table out of sync (%d)\n", symbology);
2180         abort();
2181     }
2182     if (data[symbology].name[0] == '\0') {
2183         if (debug & ZINT_DEBUG_TEST_PRINT) {
2184             printf("i:%d %s no BWIPP mapping\n", index, testUtilBarcodeName(symbology));
2185         }
2186         return NULL;
2187     }
2188     if ((option_1 != -1 && !data[symbology].can_option_1) || (option_2 != -1 && !data[symbology].can_option_2)
2189              || (option_3 != -1 && !data[symbology].can_option_3)) {
2190         if (debug & ZINT_DEBUG_TEST_PRINT) {
2191             printf("i:%d %s not BWIPP compatible, options not supported, option_1 %d, option_2 %d, option_3 %d\n",
2192                     index, testUtilBarcodeName(symbology), option_1, option_2, option_3);
2193         }
2194         return NULL;
2195     }
2196 
2197     if (symbology == BARCODE_CODE11) {
2198         if (option_2 != 1 && option_2 != 2) { /* 2 check digits (Zint default) not supported */
2199             if (debug & ZINT_DEBUG_TEST_PRINT) {
2200                 printf("i:%d %s not BWIPP compatible, 2 check digits not supported, option_1 %d, option_2 %d\n",
2201                         index, testUtilBarcodeName(symbology), option_1, option_2);
2202             }
2203             return NULL;
2204         }
2205     } else if (symbology == BARCODE_CODABLOCKF || symbology == BARCODE_HIBC_BLOCKF) {
2206         if (option_1 == 1) { /* Single row i.e. CODE128 not supported */
2207             if (debug & ZINT_DEBUG_TEST_PRINT) {
2208                 printf("i:%d %s not BWIPP compatible, single row not supported, option_1 %d\n",
2209                         index, testUtilBarcodeName(symbology), option_1);
2210             }
2211             return NULL;
2212         }
2213     } else if (symbology == BARCODE_AZTEC) {
2214         if (option_1 > 0 && option_2 > 0) {
2215             if (debug & ZINT_DEBUG_TEST_PRINT) {
2216                 printf("i:%d %s not BWIPP compatible, cannot specify both option_1 %d and option_2 %d\n",
2217                         index, testUtilBarcodeName(symbology), option_1, option_2);
2218             }
2219             return NULL;
2220         }
2221     } else if (symbology == BARCODE_RMQR) {
2222         if (option_2 < 1) {
2223             if (debug & ZINT_DEBUG_TEST_PRINT) {
2224                 printf("i:%d %s not BWIPP compatible, version (option_2) must be specified\n",
2225                         index, testUtilBarcodeName(symbology));
2226             }
2227             return NULL;
2228         }
2229         if (option_2 > 32) {
2230             if (debug & ZINT_DEBUG_TEST_PRINT) {
2231                 printf("i:%d %s not BWIPP compatible, auto width (option_2 > 32) not supported\n",
2232                         index, testUtilBarcodeName(symbology));
2233             }
2234             return NULL;
2235         }
2236     }
2237 
2238     if (linear_row_height) {
2239         *linear_row_height = data[symbology].linear_row_height;
2240     }
2241     if (gs1_cvt) {
2242         *gs1_cvt = data[symbology].gs1_cvt;
2243     }
2244     if (gs1) {
2245         if (symbology == BARCODE_DATAMATRIX) {
2246             if (gs1_cvt) {
2247                 *gs1_cvt = 1;
2248             }
2249             return "gs1datamatrix";
2250         } else if (symbology == BARCODE_AZTEC || symbology == BARCODE_ULTRA) {
2251             if (debug & ZINT_DEBUG_TEST_PRINT) {
2252                 printf("i:%d %s not BWIPP compatible, GS1_MODE not supported\n",
2253                         index, testUtilBarcodeName(symbology));
2254             }
2255             return NULL;
2256         } else if (symbology == BARCODE_DOTCODE) {
2257             if (gs1_cvt) {
2258                 *gs1_cvt = 1;
2259             }
2260             return "gs1dotcode";
2261         }
2262     }
2263 
2264     return data[symbology].name;
2265 }
2266 
2267 /* Whether can use BWIPP to check a symbology with given options */
testUtilCanBwipp(int index,const struct zint_symbol * symbol,int option_1,int option_2,int option_3,int debug)2268 int testUtilCanBwipp(int index, const struct zint_symbol *symbol, int option_1, int option_2, int option_3,
2269             int debug) {
2270     return testUtilBwippName(index, symbol, option_1, option_2, option_3, debug, NULL, NULL) != NULL;
2271 }
2272 
2273 /* Convert Zint GS1 and add-on format to BWIPP's */
testUtilBwippCvtGS1Data(char * bwipp_data,int upcean,int * addon_posn)2274 static void testUtilBwippCvtGS1Data(char *bwipp_data, int upcean, int *addon_posn) {
2275     char *b;
2276     int pipe = 0;
2277 
2278     *addon_posn = 0;
2279     for (b = bwipp_data; *b; b++) {
2280         if (upcean && *b == '|') {
2281             pipe = 1;
2282         }
2283         if (*b == '[') {
2284             *b = '(';
2285         } else if (*b == ']') {
2286             *b = ')';
2287         } else if (*b == '+' && upcean && !pipe) {
2288             *b = ' ';
2289             *addon_posn = b - bwipp_data;
2290         }
2291     }
2292 }
2293 
2294 /* Convert data to Ghostscript format for passing to bwipp_dump.ps */
testUtilBwippEscape(char * bwipp_data,int bwipp_data_size,const char * data,int length,int zint_escape_mode,int eci,int * parse,int * parsefnc)2295 static char *testUtilBwippEscape(char *bwipp_data, int bwipp_data_size, const char *data, int length,
2296                 int zint_escape_mode, int eci, int *parse, int *parsefnc) {
2297     char *b = bwipp_data;
2298     char *be = b + bwipp_data_size;
2299     unsigned char *d = (unsigned char *) data;
2300     unsigned char *de = (unsigned char *) data + length;
2301 
2302     *parse = *parsefnc = 0;
2303 
2304     if (eci) {
2305         sprintf(bwipp_data, "^ECI%06d", eci);
2306         *parsefnc = 1;
2307         b = bwipp_data + 10;
2308     }
2309 
2310     while (b < be && d < de) {
2311         /* Have to escape double quote otherwise Ghostscript gives "Unterminated quote in @-file" for some reason */
2312         /* Escape single quote also to avoid having to do proper shell escaping TODO: proper shell escaping */
2313         if (*d < 0x20 || *d >= 0x7F || *d == '^' || *d == '"' || *d == '\'') {
2314             if (b + 4 >= be) {
2315                 fprintf(stderr, "testUtilBwippEscape: double quote bwipp_data buffer full (%d)\n", bwipp_data_size);
2316                 return NULL;
2317             }
2318             sprintf(b, "^%03u", *d++);
2319             b += 4;
2320             *parse = 1;
2321         } else if (zint_escape_mode && *d == '\\' && d + 1 < de) {
2322             int val;
2323             switch (*++d) {
2324                 case '0': val = 0x00; /* Null */ break;
2325                 case 'E': val = 0x04; /* End of Transmission */ break;
2326                 case 'a': val = 0x07; /* Bell */ break;
2327                 case 'b': val = 0x08; /* Backspace */ break;
2328                 case 't': val = 0x09; /* Horizontal tab */ break;
2329                 case 'n': val = 0x0a; /* Line feed */ break;
2330                 case 'v': val = 0x0b; /* Vertical tab */ break;
2331                 case 'f': val = 0x0c; /* Form feed */ break;
2332                 case 'r': val = 0x0d; /* Carriage return */ break;
2333                 case 'e': val = 0x1b; /* Escape */ break;
2334                 case 'G': val = 0x1d; /* Group Separator */ break;
2335                 case 'R': val = 0x1e; /* Record Separator */ break;
2336                 //case 'x': val = 0; /* TODO: implement */ break;
2337                 case '\\': val = '\\'; break;
2338                 //case 'u': val = 0; /* TODO: implement */ break;
2339                 default: fprintf(stderr, "testUtilBwippEscape: unknown escape %c\n", *d); return NULL; break;
2340             }
2341             if (b + 4 >= be) {
2342                 fprintf(stderr, "testUtilBwippEscape: loop bwipp_data buffer full (%d)\n", bwipp_data_size);
2343                 return NULL;
2344             }
2345             sprintf(b, "^%03d", val);
2346             b += 4;
2347             d++;
2348             *parse = 1;
2349         } else {
2350             *b++ = *d++;
2351         }
2352     }
2353 
2354     if (b == be && d < de) {
2355         fprintf(stderr, "testUtilBwippEscape: end bwipp_data buffer full (%d)\n", bwipp_data_size);
2356         return NULL;
2357     }
2358     *b = '\0';
2359 
2360     return bwipp_data;
2361 }
2362 
2363 /* Convert ISBN to BWIPP format */
testUtilISBNHyphenate(char * bwipp_data,int addon_posn)2364 static void testUtilISBNHyphenate(char *bwipp_data, int addon_posn) {
2365     /* Hack in 4 hyphens in fixed format, wrong for many ISBNs */
2366     char temp[13 + 4 + 1 + 5 + 1];
2367     int len = (int) strlen(bwipp_data);
2368     int i, j;
2369 
2370     if (len < 13 || (addon_posn && addon_posn < 13 ) || len >= (int) sizeof(temp)) {
2371         return;
2372     }
2373     for (i = 0, j = 0; i <= len; i++, j++) {
2374         if (i == 3 || i == 5 || i == 10 || i == 12) {
2375             temp[j++] = '-';
2376         }
2377         temp[j] = bwipp_data[i];
2378     }
2379     strcpy(bwipp_data, temp);
2380 }
2381 
2382 #define GS_INITIAL_LEN  35 /* Length of cmd up to -q */
2383 
2384 /* Create bwipp_dump.ps command and run */
testUtilBwipp(int index,const struct zint_symbol * symbol,int option_1,int option_2,int option_3,const char * data,int length,const char * primary,char * buffer,int buffer_size)2385 int testUtilBwipp(int index, const struct zint_symbol *symbol, int option_1, int option_2, int option_3,
2386             const char *data, int length, const char *primary, char *buffer, int buffer_size) {
2387     const char *cmd_fmt = "gs -dNOPAUSE -dBATCH -dNODISPLAY -q -sb=%s -sd='%s' backend/tests/tools/bwipp_dump.ps";
2388     const char *cmd_opts_fmt = "gs -dNOPAUSE -dBATCH -dNODISPLAY -q -sb=%s -sd='%s' -so='%s'"
2389                                 " backend/tests/tools/bwipp_dump.ps";
2390     // If data > 2K
2391     const char *cmd_fmt2 = "gs -dNOPAUSE -dBATCH -dNODISPLAY -q -sb=%s -sd='%.2043s' -sd2='%s'"
2392                                 " backend/tests/tools/bwipp_dump.ps";
2393     const char *cmd_opts_fmt2 = "gs -dNOPAUSE -dBATCH -dNODISPLAY -q -sb=%s -sd='%.2043s' -sd2='%s' -so='%s'"
2394                                 " backend/tests/tools/bwipp_dump.ps";
2395 
2396     int symbology = symbol->symbology;
2397     int data_len = length == -1 ? (int) strlen(data) : length;
2398     int primary_len = primary ? (int) strlen(primary) : 0;
2399     /* 4 AI prefix + primary + '|' + leading zero + escaped data + fudge */
2400     int max_data_len = 4 + primary_len + 1 + 1 + data_len * 4 + 64;
2401 
2402     int eci_length = get_eci_length(symbol->eci, (const unsigned char *) data, data_len);
2403     char *converted = (char *) testutil_alloca(eci_length + 1);
2404     char *cmd = (char *) testutil_alloca(max_data_len + 1024);
2405     const char *bwipp_barcode = NULL;
2406     char *bwipp_opts = NULL;
2407     int bwipp_data_size = max_data_len + 1;
2408     char *bwipp_data = (char *) testutil_alloca(bwipp_data_size);
2409     char bwipp_opts_buf[512];
2410     int *bwipp_row_height = (int *) testutil_alloca(sizeof(int) * symbol->rows);
2411     int linear_row_height;
2412     int gs1_cvt;
2413     int user_mask;
2414 
2415     FILE *fp = NULL;
2416     int cnt;
2417 
2418     char *b = buffer;
2419     char *be = buffer + buffer_size;
2420     int r, h;
2421     int parse, parsefnc;
2422 
2423     int upcean = is_extendable(symbology);
2424     int upca = symbology == BARCODE_UPCA || symbology == BARCODE_UPCA_CHK || symbology == BARCODE_UPCA_CC;
2425     char obracket = symbol->input_mode & GS1PARENS_MODE ? '(' : '[';
2426     char cbracket = symbol->input_mode & GS1PARENS_MODE ? ')' : ']';
2427     int addon_posn;
2428     int eci;
2429     int i, j, len;
2430 
2431     bwipp_data[0] = bwipp_opts_buf[0] = '\0';
2432 
2433     bwipp_barcode = testUtilBwippName(index, symbol, option_1, option_2, option_3, 0, &linear_row_height, &gs1_cvt);
2434     if (!bwipp_barcode) {
2435         fprintf(stderr, "i:%d testUtilBwipp: no mapping for %s, option_1 %d, option_2 %d, option_3 %d\n",
2436                 index, testUtilBarcodeName(symbology), option_1, option_2, option_3);
2437         return -1;
2438     }
2439 
2440     for (r = 0; r < symbol->rows; r++) {
2441         if (symbology == BARCODE_MAXICODE) {
2442             bwipp_row_height[r] = 1;
2443         } else {
2444             bwipp_row_height[r] = symbol->row_height[r] ? symbol->row_height[r] : linear_row_height;
2445         }
2446         if ((symbol->debug & ZINT_DEBUG_TEST_PRINT) && !(symbol->debug & ZINT_DEBUG_TEST_LESS_NOISY)) {
2447             fprintf(stderr, "bwipp_row_height[%d] %d, symbol->row_height[%d] %g\n",
2448                         r, bwipp_row_height[r], r, symbol->row_height[r]);
2449         }
2450     }
2451 
2452     if ((symbol->input_mode & 0x07) == UNICODE_MODE && ZBarcode_Cap(symbology, ZINT_CAP_ECI)
2453             && is_eci_convertible(symbol->eci)) {
2454         if (utf8_to_eci(symbol->eci, (const unsigned char *) data, (unsigned char *) converted, &data_len) == 0) {
2455             eci = symbol->eci;
2456         } else {
2457             if (symbol->eci != 0) {
2458                 eci = get_best_eci((const unsigned char *) data, data_len);
2459                 if (utf8_to_eci(eci, (const unsigned char *) data, (unsigned char *) converted, &data_len) != 0) {
2460                     fprintf(stderr, "i:%d testUtilBwipp: failed to convert Unicode data for %s\n",
2461                             index, testUtilBarcodeName(symbology));
2462                     return -1;
2463                 }
2464             } else {
2465                 fprintf(stderr, "i:%d testUtilBwipp: failed to convert Unicode data for %s\n",
2466                         index, testUtilBarcodeName(symbology));
2467                 return -1;
2468             }
2469         }
2470         data = converted;
2471     } else {
2472         eci = symbol->eci >= 3 && ZBarcode_Cap(symbology, ZINT_CAP_ECI) ? symbol->eci : 0;
2473     }
2474 
2475     if (is_composite(symbology)) {
2476         if (!primary) {
2477             fprintf(stderr, "i:%d testUtilBwipp: no primary data given %s\n", index, testUtilBarcodeName(symbology));
2478             return -1;
2479         }
2480         if (*primary != obracket && !upcean) {
2481             strcat(bwipp_data, "(01)");
2482         }
2483         strcat(bwipp_data, primary);
2484         strcat(bwipp_data, "|");
2485         strcat(bwipp_data, data);
2486         testUtilBwippCvtGS1Data(bwipp_data, upcean, &addon_posn);
2487 
2488         if (upcean) {
2489             if (symbology == BARCODE_EANX_CC && (primary_len <= 8 || (addon_posn && addon_posn <= 8))) {
2490                 bwipp_barcode = "ean8composite";
2491             }
2492             if (addon_posn) {
2493                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%saddongap=%d",
2494                         strlen(bwipp_opts_buf) ? " " : "", option_2 > 0 ? option_2 : upca ? 9 : 7);
2495                 bwipp_opts = bwipp_opts_buf;
2496             }
2497             bwipp_row_height[symbol->rows - 1] = 72;
2498         }
2499 
2500         if (option_1 > 0) {
2501             sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sccversion=%c",
2502                     strlen(bwipp_opts_buf) ? " " : "", option_1 == 1 ? 'a' : option_1 == 2 ? 'b' : 'c');
2503             bwipp_opts = bwipp_opts_buf;
2504         }
2505         if (option_2 > 0) {
2506             sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%ssegments=%d",
2507                     strlen(bwipp_opts_buf) ? " " : "", option_2 * 2);
2508             bwipp_opts = bwipp_opts_buf;
2509         }
2510 
2511         if (symbol->input_mode & GS1NOCHECK_MODE) {
2512             sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sdontlint", strlen(bwipp_opts_buf) ? " " : "");
2513             bwipp_opts = bwipp_opts_buf;
2514         }
2515     } else {
2516         if (gs1_cvt) {
2517             if (*data != obracket && !upcean) {
2518                 strcat(bwipp_data, symbology == BARCODE_NVE18 ? "(00)" : "(01)");
2519             }
2520             strcat(bwipp_data, data);
2521             testUtilBwippCvtGS1Data(bwipp_data, upcean, &addon_posn);
2522 
2523             if (upcean) {
2524                 if ((symbology == BARCODE_EANX || symbology == BARCODE_EANX_CHK)
2525                         && (data_len <= 8 || (addon_posn && addon_posn <= 8))) {
2526                     bwipp_barcode = data_len <= 3 ? "ean2" : data_len <= 5 ? "ean5" : "ean8";
2527                 }
2528                 if (symbology == BARCODE_ISBNX) {
2529                     testUtilISBNHyphenate(bwipp_data, addon_posn);
2530                 }
2531                 if (addon_posn) {
2532                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%saddongap=%d",
2533                             strlen(bwipp_opts_buf) ? " " : "", option_2 > 0 ? option_2 : upca ? 9 : 7);
2534                     bwipp_opts = bwipp_opts_buf;
2535                 }
2536             }
2537 
2538             if (option_2 > 0) {
2539                 if (symbology == BARCODE_DBAR_EXP || symbology == BARCODE_DBAR_EXPSTK) {
2540                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%ssegments=%d",
2541                             strlen(bwipp_opts_buf) ? " " : "", option_2 * 2);
2542                     bwipp_opts = bwipp_opts_buf;
2543                 }
2544             }
2545 
2546             if (symbol->input_mode & GS1NOCHECK_MODE) {
2547                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sdontlint", strlen(bwipp_opts_buf) ? " " : "");
2548                 bwipp_opts = bwipp_opts_buf;
2549             }
2550         } else {
2551             if (testUtilBwippEscape(bwipp_data, bwipp_data_size, data, data_len, symbol->input_mode & ESCAPE_MODE,
2552                     eci, &parse, &parsefnc) == NULL) {
2553                 return -1;
2554             }
2555             if (parse) {
2556                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sparse", strlen(bwipp_opts_buf) ? " " : "");
2557                 bwipp_opts = bwipp_opts_buf;
2558             }
2559             if (parsefnc) {
2560                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sparsefnc",
2561                         strlen(bwipp_opts_buf) ? " " : "");
2562                 bwipp_opts = bwipp_opts_buf;
2563             }
2564 
2565             if (symbology == BARCODE_C25STANDARD || symbology == BARCODE_C25INTER || symbology == BARCODE_C25IATA
2566                     || symbology == BARCODE_C25LOGIC || symbology == BARCODE_C25IND) {
2567                 if (option_2 == 1 || option_2 == 2) { // Add check digit without or with HRT suppression
2568                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sincludecheck",
2569                             strlen(bwipp_opts_buf) ? " " : "");
2570                     bwipp_opts = bwipp_opts_buf;
2571                 }
2572             } else if (symbology == BARCODE_CODE93) {
2573                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sincludecheck",
2574                         strlen(bwipp_opts_buf) ? " " : "");
2575                 if (parse) {
2576                     bwipp_barcode = "code93ext";
2577                 }
2578                 bwipp_opts = bwipp_opts_buf;
2579             } else if (symbology == BARCODE_PZN) {
2580                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%spzn8", strlen(bwipp_opts_buf) ? " " : "");
2581                 bwipp_opts = bwipp_opts_buf;
2582             } else if (symbology == BARCODE_TELEPEN_NUM) {
2583                 if (data_len & 1) { // Add leading zero
2584                     memmove(bwipp_data + 1, bwipp_data, strlen(bwipp_data) + 1);
2585                     *bwipp_data = '0';
2586                 }
2587             } else if (symbology == BARCODE_CODABLOCKF || symbology == BARCODE_HIBC_BLOCKF) {
2588                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%ssepheight=0", strlen(bwipp_opts_buf) ? " " : "");
2589                 if (option_1 > 0) {
2590                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%srows=%d",
2591                             strlen(bwipp_opts_buf) ? " " : "", option_1);
2592                 }
2593                 if (option_2 > 0) {
2594                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%scolumns=%d",
2595                             strlen(bwipp_opts_buf) ? " " : "", option_2 - 5);
2596                 } else {
2597                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%scolumns=%d",
2598                             strlen(bwipp_opts_buf) ? " " : "", (symbol->width - 57) / 11);
2599                 }
2600                 bwipp_opts = bwipp_opts_buf;
2601             } else if (symbology == BARCODE_CODE11 || symbology == BARCODE_CODE39 || symbology == BARCODE_EXCODE39
2602                         || symbology == BARCODE_LOGMARS || symbology == BARCODE_CODABAR) {
2603                 if (option_2 > 0) {
2604                     if (option_2 == 1) {
2605                         sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sincludecheck",
2606                                 strlen(bwipp_opts_buf) ? " " : "");
2607                     }
2608                     bwipp_opts = bwipp_opts_buf; /* Set always as option_2 == 2 is bwipp default */
2609                 }
2610             } else if (symbology == BARCODE_PLESSEY) {
2611                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sincludecheck", strlen(bwipp_opts_buf) ? " " : "");
2612                 bwipp_opts = bwipp_opts_buf;
2613             } else if (symbology == BARCODE_MSI_PLESSEY) {
2614                 if (option_2 > 0) {
2615                     const char *checktype = NULL;
2616                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sincludecheck",
2617                             strlen(bwipp_opts_buf) ? " " : "");
2618 
2619                     if (option_2 >= 11 && option_2 <= 16) {
2620                         option_2 -= 10; /* Remove no-check indicator */
2621                     }
2622                     if (option_2 == 2) {
2623                         checktype = "mod1010";
2624                     } else if (option_2 == 3) {
2625                         checktype = "mod11 badmod11";
2626                     } else if (option_2 == 4) {
2627                         checktype = "mod1110 badmod11";
2628                     } else if (option_2 == 5) {
2629                         checktype = "ncrmod11 badmod11";
2630                     } else if (option_2 == 6) {
2631                         checktype = "ncrmod1110 badmod11";
2632                     }
2633                     if (checktype) {
2634                         sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%schecktype=%s",
2635                                 strlen(bwipp_opts_buf) ? " " : "", checktype);
2636                     }
2637                     bwipp_opts = bwipp_opts_buf;
2638                 }
2639             } else if (symbology == BARCODE_PDF417 || symbology == BARCODE_PDF417COMP || symbology == BARCODE_HIBC_PDF
2640                         || symbology == BARCODE_MICROPDF417 || symbology == BARCODE_HIBC_MICPDF) {
2641                 for (r = 0; r < symbol->rows; r++) bwipp_row_height[r] = 1; /* Change from 3 */
2642                 if (option_1 >= 0) {
2643                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%seclevel=%d",
2644                             strlen(bwipp_opts_buf) ? " " : "", option_1);
2645                     bwipp_opts = bwipp_opts_buf;
2646                 }
2647                 if (option_2 > 0) {
2648                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%scolumns=%d",
2649                             strlen(bwipp_opts_buf) ? " " : "", option_2);
2650                     bwipp_opts = bwipp_opts_buf;
2651                 }
2652             } else if (symbology == BARCODE_POSTNET || symbology == BARCODE_PLANET || symbology == BARCODE_RM4SCC
2653                         || symbology == BARCODE_JAPANPOST || symbology == BARCODE_KIX || symbology == BARCODE_DAFT
2654                         || symbology == BARCODE_USPS_IMAIL || symbology == BARCODE_AUSPOST
2655                         || symbology == BARCODE_PHARMA_TWO) {
2656                 for (r = 0; r < symbol->rows; r++) bwipp_row_height[r] = 1; /* Zap */
2657                 if (symbology == BARCODE_KIX) {
2658                     to_upper((unsigned char *) bwipp_data);
2659                 } else if (symbology == BARCODE_USPS_IMAIL) {
2660                     char *dash = strchr(bwipp_data, '-');
2661                     if (dash) {
2662                         memmove(dash, dash + 1, strlen(dash));
2663                     }
2664                 } else if (symbology == BARCODE_AUSPOST) {
2665                     const char *prefix;
2666                     if (data_len == 8) {
2667                         prefix = "11";
2668                     } else if (data_len == 13 || data_len == 16) {
2669                         prefix = "59";
2670                         if (data_len == 16) {
2671                             sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%scustinfoenc=numeric",
2672                                     strlen(bwipp_opts_buf) ? " " : "");
2673                             bwipp_opts = bwipp_opts_buf;
2674                         }
2675                     } else {
2676                         prefix = "62";
2677                         if (data_len == 23) {
2678                             sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%scustinfoenc=numeric",
2679                                     strlen(bwipp_opts_buf) ? " " : "");
2680                             bwipp_opts = bwipp_opts_buf;
2681                         }
2682                     }
2683                     memmove(bwipp_data + 2, bwipp_data, data_len + 1);
2684                     memmove(bwipp_data, prefix, 2);
2685                 }
2686             } else if (symbology == BARCODE_FIM) {
2687                 strcpy(bwipp_data, "fima");
2688                 bwipp_data[3] = data[0] - 'A' + 'a';
2689             } else if (symbology == BARCODE_CODE16K || symbology == BARCODE_CODE49) {
2690                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%ssepheight=0", strlen(bwipp_opts_buf) ? " " : "");
2691                 bwipp_opts = bwipp_opts_buf;
2692             } else if (symbology == BARCODE_AZTEC || symbology == BARCODE_HIBC_AZTEC) {
2693                 int compact = 0, full = 0;
2694                 if (option_1 >= 1 && option_1 <= 4) {
2695                     int eclevel;
2696                     if (option_1 == 1) {
2697                         eclevel = 10;
2698                     } else if (option_1 == 2) {
2699                         eclevel = 23;
2700                     } else if (option_1 == 3) {
2701                         eclevel = 36;
2702                     } else {
2703                         eclevel = 50;
2704                     }
2705                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%seclevel=%d",
2706                             strlen(bwipp_opts_buf) ? " " : "", eclevel);
2707                     bwipp_opts = bwipp_opts_buf;
2708                 }
2709                 if (option_2 >= 1) {
2710                     int layers;
2711                     if (option_2 <= 4) {
2712                         compact = 1;
2713                         layers = option_2;
2714                     } else {
2715                         layers = option_2 - 4;
2716                         full = layers <= 4;
2717                     }
2718                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%slayers=%d",
2719                             strlen(bwipp_opts_buf) ? " " : "", layers);
2720                     bwipp_opts = bwipp_opts_buf;
2721                 }
2722                 if (symbol->output_options & READER_INIT) {
2723                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sreaderinit",
2724                             strlen(bwipp_opts_buf) ? " " : "");
2725                     bwipp_opts = bwipp_opts_buf;
2726                 }
2727                 if (symbology == BARCODE_HIBC_AZTEC) {
2728                     compact = 1;
2729                 }
2730                 if (compact || full) {
2731                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sformat=%s",
2732                             strlen(bwipp_opts_buf) ? " " : "", compact ? "compact" : "full");
2733                     bwipp_opts = bwipp_opts_buf;
2734                 }
2735             } else if (symbology == BARCODE_CODEONE) {
2736                 if ((symbol->input_mode & 0x07) == GS1_MODE) { /* Hack pseudo-GS1 support */
2737                     int last_ai, ai_latch = 0;
2738                     /* Reduce square brackets (include NUL) */
2739                     for (i = 0, j = 0, len = (int) strlen(bwipp_data); i <= len; i++) {
2740                         if (bwipp_data[i] == obracket) {
2741                             if (ai_latch == 0) {
2742                                 bwipp_data[j++] = '[';
2743                             }
2744                             last_ai = atoi(bwipp_data + i + 1);
2745                             if ((last_ai >= 0 && last_ai <= 4) || (last_ai >= 11 && last_ai <= 20) || last_ai == 23
2746                                     || (last_ai >= 31 && last_ai <= 36) || last_ai == 41) {
2747                                 ai_latch = 1;
2748                             }
2749                         } else if (bwipp_data[i] != cbracket) {
2750                             bwipp_data[j++] = bwipp_data[i];
2751                         }
2752                     }
2753                     /* Replace square brackets with ^FNC1 */
2754                     for (len = (int) strlen(bwipp_data), i = len - 1; i >= 0; i--) {
2755                         if (bwipp_data[i] == '[') {
2756                             memmove(bwipp_data + i + 5, bwipp_data + i + 1, len - i);
2757                             memcpy(bwipp_data + i, "^FNC1", 5);
2758                             len += 4;
2759                         }
2760                     }
2761                     if (symbol->eci == 0) { /* If not already done for ECI */
2762                         sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sparsefnc",
2763                                 strlen(bwipp_opts_buf) ? " " : "");
2764                         bwipp_opts = bwipp_opts_buf;
2765                     }
2766                 }
2767                 if (option_2 >= 1 && option_2 <= 10) {
2768                     static const char *codeone_versions[] = { "A", "B", "C", "D", "E", "F", "G", "H" };
2769                     const char *codeone_version;
2770                     if (option_2 == 9) {
2771                         codeone_version = length <= 6 ? "S-10" : length <= 12 ? "S-20" : "S-30";
2772                     } else if (option_2 == 10) {
2773                         // TODO: Properly allow for different T sizes
2774                         codeone_version = length <= 22 ? "T-16" : length <= 34 ? "T-32" : "T-48";
2775                     } else {
2776                         codeone_version = codeone_versions[option_2 - 1];
2777                     }
2778                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sversion=%s",
2779                             strlen(bwipp_opts_buf) ? " " : "", codeone_version);
2780                     bwipp_opts = bwipp_opts_buf;
2781                 }
2782             } else if (symbology == BARCODE_MAXICODE) {
2783                 int have_scm = memcmp(bwipp_data, "[)>^03001^02996", 15) == 0;
2784                 int mode = option_1;
2785                 if (mode <= 0) {
2786                     if (primary_len == 0) {
2787                         mode = 4;
2788                     } else {
2789                         for (i = 0; i < primary_len - 6; i++) {
2790                             if (((symbol->primary[i] < '0') || (symbol->primary[i] > '9'))
2791                                     && (symbol->primary[i] != ' ')) {
2792                                 mode = 3;
2793                                 break;
2794                             }
2795                         }
2796                     }
2797                 }
2798                 if (mode > 0) {
2799                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%smode=%d",
2800                             strlen(bwipp_opts_buf) ? " " : "", mode);
2801                     bwipp_opts = bwipp_opts_buf;
2802                 }
2803                 if (primary_len) {
2804                     char prefix_buf[30];
2805                     int prefix_len;
2806                     int postcode_len = primary_len - 6;
2807                     char postcode[10];
2808                     if (postcode_len >= 10) postcode_len = 9;
2809                     memcpy(postcode, primary, postcode_len);
2810                     postcode[postcode_len] = '\0';
2811                     if (mode == 2) {
2812                         for (i = 0; i < postcode_len; i++) {
2813                             if (postcode[i] == ' ') {
2814                                 postcode[i] = '\0';
2815                             }
2816                         }
2817                     } else {
2818                         postcode[6] = '\0';
2819                         for (i = postcode_len; i < 6; i++) {
2820                             postcode[i] = ' ';
2821                         }
2822                     }
2823                     sprintf(prefix_buf, "%s^029%.3s^029%.3s^029",
2824                             postcode, primary + primary_len - 6, primary + primary_len - 3);
2825                     prefix_len = (int) strlen(prefix_buf);
2826                     if (have_scm) {
2827                         memmove(bwipp_data + 15 + prefix_len, bwipp_data, strlen(bwipp_data) - 15 + 1);
2828                         memcpy(bwipp_data + 15, prefix_buf, prefix_len);
2829                     } else {
2830                         memmove(bwipp_data + prefix_len, bwipp_data, strlen(bwipp_data) + 1);
2831                         memcpy(bwipp_data, prefix_buf, prefix_len);
2832                     }
2833                     if (!parse) {
2834                         sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sparse",
2835                                 strlen(bwipp_opts_buf) ? " " : "");
2836                         bwipp_opts = bwipp_opts_buf;
2837                         parse = 1;
2838                     }
2839                 }
2840                 if (option_2 > 0) {
2841                     char scm_vv_buf[32];
2842                     sprintf(scm_vv_buf, "[)>^03001^029%02d", option_2); /* [)>\R01\Gvv */
2843                     memmove(bwipp_data + 15, bwipp_data, strlen(bwipp_data) + 1);
2844                     memcpy(bwipp_data, scm_vv_buf, 15);
2845                     if (!parse) {
2846                         sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sparse",
2847                                 strlen(bwipp_opts_buf) ? " " : "");
2848                         bwipp_opts = bwipp_opts_buf;
2849                         parse = 1;
2850                     }
2851                 }
2852             }
2853         }
2854 
2855         if (symbology == BARCODE_DATAMATRIX || symbology == BARCODE_HIBC_DM) {
2856             int added_dmre = 0;
2857             #include "../dmatrix.h"
2858             (void)matrixrsblock; (void)matrixdatablock; (void)matrixbytes; (void)matrixFW; (void)matrixFH;
2859             (void)isDMRE; (void)text_value; (void)text_shift; (void)c40_value; (void)c40_shift;
2860 
2861             if (symbol->output_options & GS1_GS_SEPARATOR) {
2862                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sgssep", strlen(bwipp_opts_buf) ? " " : "");
2863                 bwipp_opts = bwipp_opts_buf;
2864             }
2865             if (option_2 >= 1 && option_2 <= ARRAY_SIZE(intsymbol)) {
2866                 int idx = intsymbol[option_2 - 1];
2867                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%srows=%d columns=%d",
2868                         strlen(bwipp_opts_buf) ? " " : "", matrixH[idx], matrixW[idx]);
2869                 bwipp_opts = bwipp_opts_buf;
2870                 if (option_2 >= 31) {
2871                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sdmre", strlen(bwipp_opts_buf) ? " " : "");
2872                     added_dmre = 1;
2873                 }
2874             }
2875             if (option_3 != DM_SQUARE && symbol->width != symbol->height) {
2876                 if (option_3 == DM_DMRE && !added_dmre) {
2877                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sdmre", strlen(bwipp_opts_buf) ? " " : "");
2878                     //added_dmre = 1;
2879                 }
2880                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sformat=rectangle",
2881                         strlen(bwipp_opts_buf) ? " " : "");
2882                 bwipp_opts = bwipp_opts_buf;
2883             }
2884             if (option_3 != -1) {
2885                 bwipp_opts = bwipp_opts_buf;
2886             }
2887         } else if (symbology == BARCODE_DOTCODE) {
2888             if (option_2 > 0) {
2889                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%scolumns=%d",
2890                         strlen(bwipp_opts_buf) ? " " : "", symbol->option_2);
2891                 bwipp_opts = bwipp_opts_buf;
2892             }
2893             if (option_3 != -1) {
2894                 user_mask = (option_3 >> 8) & 0x0F; /* User mask is pattern + 1, so >= 1 and <= 8 */
2895                 if (user_mask >= 1 && user_mask <= 8) {
2896                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%smask=%d",
2897                             strlen(bwipp_opts_buf) ? " " : "", (user_mask - 1) % 4);
2898                     bwipp_opts = bwipp_opts_buf;
2899                 }
2900             }
2901         } else if (symbology == BARCODE_MICROQR || symbology == BARCODE_RMQR) {
2902             if (option_1 >= 1 && option_1 <= 4) {
2903                 static const char eccs[4] = { 'L', 'M', 'Q', 'H' };
2904                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%seclevel=%c",
2905                         strlen(bwipp_opts_buf) ? " " : "", eccs[option_1 - 1]);
2906                 bwipp_opts = bwipp_opts_buf;
2907             }
2908             if (symbology == BARCODE_MICROQR) {
2909                 if (option_2 >= 1 && option_2 <= 4) {
2910                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sversion=M%d",
2911                             strlen(bwipp_opts_buf) ? " " : "", option_2);
2912                     bwipp_opts = bwipp_opts_buf;
2913                 }
2914                 if (option_3 != -1) {
2915                     int mask = (symbol->option_3 >> 8) & 0x0F;
2916                     if (mask >= 1 && mask <= 4) {
2917                         sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%smask=%d",
2918                                 strlen(bwipp_opts_buf) ? " " : "", ((symbol->option_3 >> 8) & 0x0F));
2919                         bwipp_opts = bwipp_opts_buf;
2920                     }
2921                 }
2922             } else if (symbology == BARCODE_RMQR) {
2923                 if (option_2 >= 1 && option_2 <= 32) {
2924                     static const char *vers[] = {
2925                         "R7x43", "R7x59", "R7x77", "R7x99", "R7x139", "R9x43", "R9x59", "R9x77", "R9x99", "R9x139",
2926                         "R11x27", "R11x43", "R11x59", "R11x77", "R11x99", "R11x139", "R13x27", "R13x43", "R13x59", "R13x77",
2927                         "R13x99", "R13x139", "R15x43", "R15x59", "R15x77", "R15x99", "R15x139", "R17x43", "R17x59", "R17x77",
2928                         "R17x99", "R17x139",
2929                     };
2930                     sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%sversion=%s",
2931                             strlen(bwipp_opts_buf) ? " " : "", vers[option_2 - 1]);
2932                     bwipp_opts = bwipp_opts_buf;
2933                 }
2934             }
2935         } else if (symbology == BARCODE_ULTRA) {
2936             if (option_1 >= 1 && option_1 <= 6) {
2937                 sprintf(bwipp_opts_buf + strlen(bwipp_opts_buf), "%seclevel=EC%d",
2938                         strlen(bwipp_opts_buf) ? " " : "", option_1 - 1);
2939                 bwipp_opts = bwipp_opts_buf;
2940             }
2941         }
2942     }
2943 
2944     if ((option_1 != -1 || option_2 != -1 || option_3 != -1) && !bwipp_opts) {
2945         fprintf(stderr,
2946                 "i:%d testUtilBwipp: no BWIPP options set option_1 %d, option_2 %d, option_3 %d for symbology %s\n",
2947                 index, option_1, option_2, option_3, testUtilBarcodeName(symbology));
2948         return -1;
2949     }
2950 
2951     if (bwipp_opts && strlen(bwipp_opts)) {
2952         if (strlen(bwipp_data) >= 2043) { /* Ghostscript's `arg_str_max` 2048 less "-sd=" */
2953             sprintf(cmd, cmd_opts_fmt2, bwipp_barcode, bwipp_data, bwipp_data + 2043, bwipp_opts);
2954         } else {
2955             sprintf(cmd, cmd_opts_fmt, bwipp_barcode, bwipp_data, bwipp_opts);
2956         }
2957     } else {
2958         if (strlen(bwipp_data) >= 2043) {
2959             sprintf(cmd, cmd_fmt2, bwipp_barcode, bwipp_data, bwipp_data + 2043);
2960         } else {
2961             sprintf(cmd, cmd_fmt, bwipp_barcode, bwipp_data);
2962         }
2963     }
2964 
2965     /* Hack in various adjustments */
2966     if (symbology == BARCODE_DBAR_OMN || symbology == BARCODE_DBAR_LTD || symbology == BARCODE_DBAR_EXP) {
2967         /* Begin with space */
2968         char adj[5] = " -sbs";
2969         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
2970         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
2971     }
2972     if (symbology == BARCODE_CODE11 || symbology == BARCODE_CODE39 || symbology == BARCODE_EXCODE39
2973             || symbology == BARCODE_CODABAR || symbology == BARCODE_PHARMA || symbology == BARCODE_PZN
2974             || symbology == BARCODE_CODE32 || symbology == BARCODE_VIN) {
2975         /* Ratio 3 width bar/space -> 2 width */
2976         char adj[8] = " -sr=0.6";
2977         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
2978         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
2979     }
2980     if (symbology == BARCODE_C25INTER || symbology == BARCODE_DPLEIT || symbology == BARCODE_DPIDENT
2981             || symbology == BARCODE_ITF14) {
2982         /* Ratio 2 width bar/space -> 3 width */
2983         char adj[8] = " -sr=1.3";
2984         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
2985         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
2986     }
2987     if (symbology == BARCODE_FIM) {
2988         /* Ratio 2 width bar/space -> 1 width */
2989         char adj[8] = " -sr=0.5";
2990         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
2991         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
2992     }
2993     if (symbology == BARCODE_PLESSEY) {
2994         /* Ceiling ratio 3/4/5 width bar/space -> 2 width then round ratio 2 width bar/space -> 3 width */
2995         char adj[16] = " -sc=0.4 -sr=1.3";
2996         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
2997         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
2998     }
2999     if (symbology == BARCODE_CODE11 || symbology == BARCODE_CODE39 || symbology == BARCODE_EXCODE39
3000             || symbology == BARCODE_HIBC_39 || symbology == BARCODE_LOGMARS || symbology == BARCODE_PHARMA
3001             || symbology == BARCODE_PZN || symbology == BARCODE_CODE32 || symbology == BARCODE_VIN
3002             || symbology == BARCODE_C25INTER || symbology == BARCODE_DPLEIT || symbology == BARCODE_DPIDENT
3003             || symbology == BARCODE_ITF14 || symbology == BARCODE_PHARMA_TWO) {
3004         /* End sbs loop on bar */
3005         char adj[6] = " -selb";
3006         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
3007         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
3008     }
3009     if (symbology == BARCODE_C25STANDARD) {
3010         /* Zint uses 4X start/stop wides while BWIPP uses 3X - convert */
3011         char adj[91] = " -sp='i 0 eq i limit 4 sub eq or sbs i get 3 eq and { (1111) print true } { false } ifelse'";
3012         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
3013         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
3014     }
3015     if (symbology == BARCODE_POSTNET || symbology == BARCODE_PLANET || symbology == BARCODE_RM4SCC
3016             || symbology == BARCODE_JAPANPOST || symbology == BARCODE_KIX || symbology == BARCODE_DAFT
3017             || symbology == BARCODE_USPS_IMAIL || symbology == BARCODE_AUSPOST || symbology == BARCODE_PHARMA_TWO) {
3018         /* Emulate rows with BWIPP heights. */
3019         char adj[5] = " -shs";
3020         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
3021         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
3022     }
3023     if (symbology == BARCODE_CODE16K || symbology == BARCODE_CODE49) {
3024         char adj[15] = " -sxs=10 -sxe=1"; /* Strip first 10 and last zero */
3025         memmove(cmd + GS_INITIAL_LEN + sizeof(adj), cmd + GS_INITIAL_LEN, strlen(cmd) + 1 - GS_INITIAL_LEN);
3026         memcpy(cmd + GS_INITIAL_LEN, adj, sizeof(adj));
3027     }
3028 
3029     if (symbol->debug & ZINT_DEBUG_TEST_PRINT) {
3030         printf("i:%d testUtilBwipp: cmd %s\n", index, cmd);
3031     }
3032 
3033     fp = testutil_popen(cmd, "r");
3034     if (!fp) {
3035         fprintf(stderr, "i:%d testUtilBwipp: failed to run '%s'\n", index, cmd);
3036         return -1;
3037     }
3038 
3039     for (r = 0; r < symbol->rows; r++) {
3040         if (b + symbol->width > be) {
3041             fprintf(stderr, "i:%d testUtilBwipp: row %d, width %d, row width iteration overrun (%s)\n",
3042                     index, r, symbol->width, cmd);
3043             testutil_pclose(fp);
3044             return -1;
3045         }
3046         cnt = (int) fread(b, 1, symbol->width, fp);
3047         if (cnt != symbol->width) {
3048             fprintf(stderr, "i:%d testUtilBwipp: failed to read symbol->width %d bytes, cnt %d (%s)\n",
3049                     index, symbol->width, cnt, cmd);
3050             testutil_pclose(fp);
3051             return -1;
3052         }
3053         b += cnt;
3054         for (h = bwipp_row_height[r]; h > 1; h--) { /* Ignore row copies if any */
3055             cnt = (int) fread(b, 1, symbol->width, fp);
3056             if (cnt != symbol->width) {
3057                 fprintf(stderr,
3058                         "i:%d testUtilBwipp: failed to read/ignore symbol->width %d bytes, cnt %d, h %d"
3059                         ", bwipp_row_height[%d] %d, symbol->row_height[%d] %g (%s)\n",
3060                         index, symbol->width, cnt, h, r, bwipp_row_height[r], r, symbol->row_height[r], cmd);
3061                 testutil_pclose(fp);
3062                 return -1;
3063             }
3064         }
3065     }
3066     *b = '\0';
3067 
3068     if (fgetc(fp) != EOF) {
3069         fprintf(stderr, "i:%d testUtilBwipp: failed to read full stream (%s)\n", index, cmd);
3070         testutil_pclose(fp);
3071         return -1;
3072     }
3073 
3074     testutil_pclose(fp);
3075 
3076     return 0;
3077 }
3078 
3079 /* Compare bwipp_dump.ps output to test suite module dump */
testUtilBwippCmp(const struct zint_symbol * symbol,char * msg,char * bwipp_buf,const char * expected)3080 int testUtilBwippCmp(const struct zint_symbol *symbol, char *msg, char *bwipp_buf, const char *expected) {
3081     int bwipp_len = (int) strlen(bwipp_buf);
3082     int expected_len = (int) strlen(expected);
3083     int ret_memcmp;
3084     int i;
3085 
3086     (void)symbol;
3087 
3088     if (bwipp_len != expected_len) {
3089         sprintf(msg, "bwipp_len %d != expected_len %d", bwipp_len, expected_len);
3090         return 2;
3091     }
3092 
3093     if (symbol->symbology == BARCODE_ULTRA) {
3094         static const char map[] = { '8', '1', '2', '3', '4', '5', '6', '7', '8', '7' };
3095         for (i = 0; i < bwipp_len; i++) {
3096             if (bwipp_buf[i] >= '0' && bwipp_buf[i] <= '9') {
3097                 bwipp_buf[i] = map[bwipp_buf[i] - '0'];
3098             }
3099         }
3100     }
3101     ret_memcmp = memcmp(bwipp_buf, expected, expected_len);
3102     if (ret_memcmp != 0) {
3103         for (i = 0; i < expected_len; i++) {
3104             if (bwipp_buf[i] != expected[i]) {
3105                 break;
3106             }
3107         }
3108         sprintf(msg, "bwipp memcmp %d != 0, at %d, len %d", ret_memcmp, i, expected_len);
3109         return ret_memcmp;
3110     }
3111 
3112     return 0;
3113 }
3114 
3115 /* Compare bwipp_dump.ps output to single row module dump (see testUtilModulesPrintRow) */
testUtilBwippCmpRow(const struct zint_symbol * symbol,int row,char * msg,const char * bwipp_buf,const char * expected)3116 int testUtilBwippCmpRow(const struct zint_symbol *symbol, int row, char *msg, const char *bwipp_buf,
3117             const char *expected) {
3118     int bwipp_len = (int) strlen(bwipp_buf);
3119     int expected_len = (int) strlen(expected);
3120     int ret_memcmp;
3121     int i, j;
3122 
3123     (void)symbol;
3124 
3125     if (bwipp_len != expected_len * symbol->rows) {
3126         sprintf(msg, "bwipp_len %d != expected_len %d * symbol->rows %d", bwipp_len, expected_len, symbol->rows);
3127         return 2;
3128     }
3129 
3130     ret_memcmp = memcmp(bwipp_buf + expected_len * row, expected, expected_len);
3131     if (ret_memcmp != 0) {
3132         for (i = 0, j = expected_len * row; i < expected_len; i++, j++) {
3133             if (bwipp_buf[j] != expected[i]) {
3134                 break;
3135             }
3136         }
3137         sprintf(msg, "bwipp memcmp %d != 0, at %d (%d), len %d", ret_memcmp, i, j, expected_len);
3138         return ret_memcmp;
3139     }
3140 
3141     return 0;
3142 }
3143