From a3cca33f32baa6743221778c30ef3118832e6fcf Mon Sep 17 00:00:00 2001 From: gitlost Date: Wed, 8 Oct 2025 12:21:29 +0100 Subject: [PATCH] general: suppress clang-tidy-21/22 warnings; some code fiddling on affected files manual: use new lua filter "lua-crossrefs.lua" hacked from https://github.com/rnwst/pandoc-lua-crossrefs as replacement for tablenos which broke with pandoc 2.8.2 (get nicer output anyway); fix "excode39.svg" data $ -> # to avoid shell processing; document `ZBarcode_HaveGS1SyntaxEngine()` --- README.clang-tidy | 11 +- backend/auspost.c | 46 +- backend/code1.c | 11 +- backend/composite.c | 2 + backend/dmatrix.c | 8 + backend/eci.c | 10 +- backend/gridmtx.c | 56 +- backend/gs1.c | 2 + backend/pdf417.c | 2 + backend/plessey.c | 7 +- backend/ps.c | 5 +- backend/qr.c | 4 + backend/raster.c | 2 + backend/rss.c | 2 + backend/svg.c | 3 + backend/tests/testcommon.c | 7 +- backend/tif.c | 1 + backend/ultra.c | 4 + docs/Makefile | 28 +- docs/README | 43 +- docs/images/excode39.svg | 10 +- docs/inc_header_pdf.tex | 42 +- docs/lua-crossrefs/COPYING | 339 ++++++ docs/lua-crossrefs/lua-crossrefs.lua | 271 +++++ docs/manual.html | 1456 ++++++++++++-------------- docs/manual.pmd | 176 ++-- docs/manual.txt | 169 +-- docs/zint.1 | 4 +- docs/zint.1.pmd | 2 +- docs/zint_images.sh | 2 +- 30 files changed, 1645 insertions(+), 1080 deletions(-) create mode 100644 docs/lua-crossrefs/COPYING create mode 100644 docs/lua-crossrefs/lua-crossrefs.lua diff --git a/README.clang-tidy b/README.clang-tidy index 96c6779a..e1186ea3 100644 --- a/README.clang-tidy +++ b/README.clang-tidy @@ -1,22 +1,23 @@ -% README.clang-tidy 2025-02-15 -% Current as of latest clang-tidy-20 from Ubuntu 24.04 apt package +% README.clang-tidy 2025-10-08 +% Current as of latest clang-tidy-22 via +% wget https://apt.llvm.org/llvm.sh; chmod +x llvm.sh; sudo ./llvm.sh 22 all Requires cmake in "build" sub-directory with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON (for "build/compile_commands.json") and -DCMAKE_BUILD_TYPE=Debug (so `assert()`s defined), and then make (for Qt generated includes). In project root directory (warning, slow): -clang-tidy-20 backend/*.c frontend/*.c backend_qt/*.cpp frontend_qt/*.cpp -p build/compile_commands.json +clang-tidy-22 backend/*.c frontend/*.c backend_qt/*.cpp frontend_qt/*.cpp -p build/compile_commands.json For "backend_tcl", which has no "compile_commands.json", specify the tcl include directory and package define, e.g. -clang-tidy-20 backend_tcl/*.c -- -I/usr/include/tcl8.6 -DPACKAGE_VERSION='"2.14.0"' +clang-tidy-22 backend_tcl/*.c -- -I/usr/include/tcl8.6 -DPACKAGE_VERSION='"2.15.0"' Options are in ".clang-tidy" (in the project root directory). The excluded check is `clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling` (for `sprintf()`). The test suite (cmake given -DZINT_TEST=ON) can also be analysed with additional checks disabled: -clang-tidy-20 backend/tests/*.c frontend/tests/*.c backend_qt/tests/*.cpp \ +clang-tidy-22 backend/tests/*.c frontend/tests/*.c backend_qt/tests/*.cpp \ -checks='-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding' \ -p build/compile_commands.json diff --git a/backend/auspost.c b/backend/auspost.c index c5afba9d..91c1c6f4 100644 --- a/backend/auspost.c +++ b/backend/auspost.c @@ -30,8 +30,8 @@ */ /* SPDX-License-Identifier: BSD-3-Clause */ -static const char GDSET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz #"; -#define GDSET_F (IS_NUM_F | IS_UPR_F | IS_LWR_F | IS_SPC_F | IS_HSH_F) +static const char AusGDSET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz #"; +#define AUS_GDSET_F (IS_NUM_F | IS_UPR_F | IS_LWR_F | IS_SPC_F | IS_HSH_F) static const char AusNTable[10][2] = { {'0','0'}, {'0','1'}, {'0','2'}, {'1','0'}, {'1','1'}, {'1','2'}, {'2','0'}, {'2','1'}, {'2','2'}, {'3','0'} @@ -65,25 +65,27 @@ static const char AusBarTable[64][3] = { {'3','3','0'}, {'3','3','1'}, {'3','3','2'}, {'3','3','3'} }; +#include #include #include "common.h" #include "reedsol.h" -static char aus_convert_pattern(char data, int shift) { +static unsigned char aus_convert_pattern(const char data, const int shift) { return (data - '0') << shift; } /* Adds Reed-Solomon error correction to auspost */ -static char *aus_rs_error(char data_pattern[], char *d) { - int reader, length, triple_writer = 0; +static char *aus_rs_error(const char data_pattern[], char *d) { + const int length = d - data_pattern; + int reader, triple_writer; unsigned char triple[31]; unsigned char result[5]; rs_t rs; - for (reader = 2, length = d - data_pattern; reader < length; reader += 3, triple_writer++) { + for (reader = 2, triple_writer = 0; reader < length; reader += 3, triple_writer++) { triple[triple_writer] = aus_convert_pattern(data_pattern[reader], 4) - + aus_convert_pattern(data_pattern[reader + 1], 2) - + aus_convert_pattern(data_pattern[reader + 2], 0); + | aus_convert_pattern(data_pattern[reader + 1], 2) + | aus_convert_pattern(data_pattern[reader + 2], 0); } zint_rs_init_gf(&rs, 0x43); @@ -97,6 +99,7 @@ static char *aus_rs_error(char data_pattern[], char *d) { return d; } +/* In "postal.c" */ INTERNAL int zint_daft_set_height(struct zint_symbol *symbol, const float min_height, const float max_height); /* Handles Australia Posts's 4 State Codes */ @@ -119,11 +122,14 @@ INTERNAL int zint_auspost(struct zint_symbol *symbol, unsigned char source[], in char data_pattern[200]; char *d = data_pattern; unsigned char fcc[2] = {0}; /* Suppress clang-tidy warning clang-analyzer-core.UndefinedBinaryOperatorResult */ - unsigned char dpid[9]; unsigned char local_source[30]; int zeroes = 0; const int raw_text = symbol->output_options & BARCODE_RAW_TEXT; + /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + assert(symbol->symbology == BARCODE_AUSPOST || symbol->symbology == BARCODE_AUSREPLY + || symbol->symbology == BARCODE_AUSROUTE || symbol->symbology == BARCODE_AUSREDIRECT); + /* Do all of the length checking first to avoid stack smashing */ if (symbol->symbology == BARCODE_AUSPOST) { if (length != 8 && length != 13 && length != 16 && length != 18 && length != 23) { @@ -134,8 +140,8 @@ INTERNAL int zint_auspost(struct zint_symbol *symbol, unsigned char source[], in return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 403, "Input length %d too long (maximum 8)", length); } - /* Check input immediately to catch nuls */ - if ((i = z_not_sane(GDSET_F, source, length))) { + /* Check input immediately to catch invalid chars */ + if ((i = z_not_sane(AUS_GDSET_F, source, length))) { return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 404, "Invalid character at position %d in input (alphanumerics, space and \"#\" only)", i); } @@ -187,9 +193,9 @@ INTERNAL int zint_auspost(struct zint_symbol *symbol, unsigned char source[], in memcpy(local_source + zeroes, source, length); length += zeroes; + /* Verify that the first 8 characters are numbers */ - memcpy(dpid, local_source, 8); - if ((i = z_not_sane(NEON_F, dpid, 8))) { + if ((i = z_not_sane(NEON_F, local_source, 8))) { return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 405, "Invalid character at position %d in DPID (first 8 characters) (digits only)", i); } @@ -205,16 +211,16 @@ INTERNAL int zint_auspost(struct zint_symbol *symbol, unsigned char source[], in /* Delivery Point Identifier (DPID) */ for (reader = 0; reader < 8; reader++, d += 2) { - memcpy(d, AusNTable[dpid[reader] - '0'], 2); + memcpy(d, AusNTable[local_source[reader] - '0'], 2); } /* Customer Information */ if (length > 8) { - if ((length == 13) || (length == 18)) { + if (length == 13 || length == 18) { for (reader = 8; reader < length; reader++, d += 3) { - memcpy(d, AusCTable[z_posn(GDSET, local_source[reader])], 3); + memcpy(d, AusCTable[z_posn(AusGDSET, local_source[reader])], 3); } - } else if ((length == 16) || (length == 23)) { + } else if (length == 16 || length == 23) { for (reader = 8; reader < length; reader++, d += 2) { memcpy(d, AusNTable[local_source[reader] - '0'], 2); } @@ -244,11 +250,11 @@ INTERNAL int zint_auspost(struct zint_symbol *symbol, unsigned char source[], in writer = 0; h = d - data_pattern; for (loopey = 0; loopey < h; loopey++) { - if ((data_pattern[loopey] == '1') || (data_pattern[loopey] == '0')) { + if (data_pattern[loopey] == '1' || data_pattern[loopey] == '0') { z_set_module(symbol, 0, writer); } z_set_module(symbol, 1, writer); - if ((data_pattern[loopey] == '2') || (data_pattern[loopey] == '0')) { + if (data_pattern[loopey] == '2' || data_pattern[loopey] == '0') { z_set_module(symbol, 2, writer); } writer += 2; @@ -272,7 +278,7 @@ INTERNAL int zint_auspost(struct zint_symbol *symbol, unsigned char source[], in symbol->row_height[1] = 2.0f; error_number = zint_daft_set_height(symbol, 0.0f, 0.0f); } - symbol->rows = 3; + symbol->rows = 3; /* Not stackable */ symbol->width = writer - 1; if (raw_text && z_rt_cpy_cat(symbol, fcc, 2, '\xFF' /*separator (none)*/, local_source, length)) { diff --git a/backend/code1.c b/backend/code1.c index c38fd177..e4e9c378 100644 --- a/backend/code1.c +++ b/backend/code1.c @@ -204,7 +204,7 @@ static int c1_look_ahead_test(const unsigned char source[], const int length, co for (sp = position; sp < length; sp++) { const unsigned char c = source[sp]; - const int is_extended = c & 0x80; + const int is_extended = !z_isascii(c); /* Step L */ if (z_isdigit(c)) { @@ -456,7 +456,7 @@ static int c1_c40text_cnt(const int current_mode, const int gs1, unsigned char i return 2; } cnt = 1; - if (input & 0x80) { + if (!z_isascii(input)) { cnt += 2; input -= 128; } @@ -646,7 +646,7 @@ static int c1_encode(struct zint_symbol *symbol, unsigned char source[], int len if (next_mode == C1_ASCII) { if (debug_print) printf("ASC(%d) ", source[sp]); - if (source[sp] & 0x80) { + if (!z_isascii(source[sp])) { /* Step B7 */ target[tp++] = 235; /* FNC4 (Upper Shift) */ target[tp++] = (source[sp] - 128) + 1; @@ -700,9 +700,10 @@ static int c1_encode(struct zint_symbol *symbol, unsigned char source[], int len } if (debug_print) fputs(current_mode == C1_C40 ? "C40 " : "TEXT ", stdout); - if (source[sp] & 0x80) { + if (!z_isascii(source[sp])) { cte_buffer[cte_p++] = 1; /* Shift 2 */ cte_buffer[cte_p++] = 30; /* FNC4 (Upper Shift) */ + assert(source[sp] >= 128); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ if (ct_shift[source[sp] - 128]) { cte_buffer[cte_p++] = ct_shift[source[sp] - 128] - 1; } @@ -900,7 +901,7 @@ static int c1_encode(struct zint_symbol *symbol, unsigned char source[], int len if (z_is_twodigits(source, length, sp)) { target[tp++] = z_to_int(source + sp, 2) + 130; sp++; - } else if (source[sp] & 0x80) { + } else if (!z_isascii(source[sp])) { target[tp++] = 235; /* FNC4 (Upper Shift) */ target[tp++] = (source[sp] - 128) + 1; } else if (gs1 && source[sp] == '\x1D') { diff --git a/backend/composite.c b/backend/composite.c index 2fc2f76a..c082b60f 100644 --- a/backend/composite.c +++ b/backend/composite.c @@ -572,6 +572,8 @@ static void cc_c(struct zint_symbol *symbol, const char source[], const int cc_w chainemc[mclength++] = mccorrection[i]; } + assert(cc_width > 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + /* 818 - The CW string is finished */ symbol->rows = mclength / cc_width; c1 = (symbol->rows - 1) / 3; diff --git a/backend/dmatrix.c b/backend/dmatrix.c index a760fe1b..0c88b0ef 100644 --- a/backend/dmatrix.c +++ b/backend/dmatrix.c @@ -549,6 +549,7 @@ static int dm_edi_buffer_xfer(int process_buffer[8], int process_p, unsigned cha memmove(process_buffer, process_buffer + process_e, sizeof(int) * process_p); if (empty) { if (process_p == 3) { + assert(i < 6); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ target[tp++] = (unsigned char) (process_buffer[i] << 2 | (process_buffer[i + 1] & 0x30) >> 4); target[tp++] = (unsigned char) ((process_buffer[i + 1] & 0x0F) << 4 | (process_buffer[i + 2] & 0x3C) >> 2); @@ -558,6 +559,7 @@ static int dm_edi_buffer_xfer(int process_buffer[8], int process_p, unsigned cha target[tp - 3], target[tp - 2], target[tp - 1]); } } else if (process_p == 2) { + assert(i < 7); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ target[tp++] = (unsigned char) (process_buffer[i] << 2 | (process_buffer[i + 1] & 0x30) >> 4); target[tp++] = (unsigned char) ((process_buffer[i + 1] & 0x0F) << 4); if (debug_print) { @@ -565,6 +567,7 @@ static int dm_edi_buffer_xfer(int process_buffer[8], int process_p, unsigned cha target[tp - 1]); } } else { + assert(i < 8); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ target[tp++] = (unsigned char) (process_buffer[i] << 2); if (debug_print) printf("[%d (%d)] ", process_buffer[i], target[tp - 1]); } @@ -932,6 +935,8 @@ static void dm_addEdges(struct zint_symbol *symbol, const unsigned char source[] const int last_seg, struct dm_edge *edges, const int from, struct dm_edge *previous, const int gs1) { int i, pos; + assert(from < length); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + /* Not possible to unlatch a full EDF edge to something else */ if (previous == NULL || previous->endMode != DM_EDIFACT) { @@ -1880,6 +1885,8 @@ static int dm_ecc200(struct zint_symbol *symbol, struct zint_seg segs[], const i datablock = dm_matrixdatablock[symbolsize]; rsblock = dm_matrixrsblock[symbolsize]; + assert(H > 1 && W > 1); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + taillength = bytes - binlen; if (taillength != 0) { @@ -1898,6 +1905,7 @@ static int dm_ecc200(struct zint_symbol *symbol, struct zint_seg segs[], const i dm_ecc(binary, bytes, datablock, rsblock, skew); if (debug_print) { printf("ECC (%d): ", rsblock * (bytes / datablock)); + assert(bytes > 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ for (i = bytes; i < bytes + rsblock * (bytes / datablock); i++) printf("%d ", binary[i]); fputc('\n', stdout); } diff --git a/backend/eci.c b/backend/eci.c index 12372d11..65d7fd9e 100644 --- a/backend/eci.c +++ b/backend/eci.c @@ -151,6 +151,10 @@ static int u_ascii_inv(const unsigned int u, unsigned char *dest) { return 0; } +/* `NOLINT`s required due to disconnect between `z_decode_utf8()` decoding lengths and `zint_get_eci_length()` */ + +/* NOLINTBEGIN(clang-analyzer-security.ArrayBound) clang-tidy-21 false positive */ + /* ECI 25 UTF-16 Big Endian (ISO/IEC 10646) - assumes valid Unicode */ static int u_utf16be(const unsigned int u, unsigned char *dest) { unsigned int u2, v; @@ -205,6 +209,8 @@ static int u_utf32le(const unsigned int u, unsigned char *dest) { return 4; } +/* NOLINTEND(clang-analyzer-security.ArrayBound) */ + /* ECI 899 Binary, included for libzueci compatibility - assumes valid Unicode */ static int u_binary(const unsigned int u, unsigned char *dest) { if (u <= 0xFF) { @@ -634,6 +640,7 @@ static int u_gb18030(const unsigned int u, unsigned char *dest) { unsigned int d1, d2; int ret = u_gb18030_int(u, &d1, &d2); if (ret) { + /* NOLINTBEGIN(clang-analyzer-security.ArrayBound) clang-tidy-21 false positive */ if (ret == 1) { dest[0] = (unsigned char) d1; } else { @@ -644,6 +651,7 @@ static int u_gb18030(const unsigned int u, unsigned char *dest) { dest[3] = (unsigned char) d2; } } + /* NOLINTEND(clang-analyzer-security.ArrayBound) */ } return ret; } @@ -794,7 +802,7 @@ INTERNAL int zint_utf8_to_eci(const int eci, const unsigned char source[], unsig } out_posn += incr; } - dest[out_posn] = '\0'; + dest[out_posn] = '\0'; /* NOLINT(clang-analyzer-security.ArrayBound) clang-tidy-21 false positive */ *p_length = out_posn; return 0; diff --git a/backend/gridmtx.c b/backend/gridmtx.c index 5304168e..1836de30 100644 --- a/backend/gridmtx.c +++ b/backend/gridmtx.c @@ -33,6 +33,7 @@ /* This file implements Grid Matrix as specified in AIM Global Document Number AIMD014 Rev. 1.63 Revised 9 Dec 2008 */ +#include #include #include "common.h" #include "reedsol.h" @@ -297,7 +298,7 @@ static void gm_add_byte_count(char binary[], const int byte_count_posn, const in } /* Add a control character to the data stream */ -static int gm_add_shift_char(char binary[], int bp, int shifty, const int debug_print) { +static int gm_add_shift_char(char binary[], int bp, const int shifty, const int debug_print) { int i; int glyph = 0; @@ -321,7 +322,7 @@ static int gm_add_shift_char(char binary[], int bp, int shifty, const int debug_ return bp; } -static int gm_encode(unsigned int ddata[], const int length, char binary[], const int eci, int *p_bp, +static int gm_encode(const unsigned int ddata[], const int length, char binary[], const int eci, int *p_bp, const int debug_print) { /* Create a binary stream representation of the input data. 7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters, @@ -733,10 +734,11 @@ static int gm_encode(unsigned int ddata[], const int length, char binary[], cons return 0; } -static int gm_encode_segs(unsigned int ddata[], const struct zint_seg segs[], const int seg_count, char binary[], - const int reader, const struct zint_structapp *p_structapp, int *p_bin_len, const int debug_print) { +static int gm_encode_segs(const unsigned int ddata[], const struct zint_seg segs[], const int seg_count, + char binary[], const int reader, const struct zint_structapp *p_structapp, int *p_bin_len, + const int debug_print) { int i; - unsigned int *dd = ddata; + const unsigned int *dd = ddata; int bp = 0; int p; @@ -863,11 +865,10 @@ static void gm_add_ecc(const char binary[], const int data_posn, const int layer } } -static void gm_place_macromodule(char grid[], int x, int y, int word1, int word2, int size) { - int i, j; - - i = (x * 6) + 1; - j = (y * 6) + 1; +static void gm_place_macromodule(char grid[], const int x, const int y, const int word1, const int word2, + const int size) { + const int i = (x * 6) + 1; + const int j = (y * 6) + 1; if (word2 & 0x40) { grid[(j * size) + i + 2] = '1'; @@ -913,10 +914,10 @@ static void gm_place_macromodule(char grid[], int x, int y, int word1, int word2 } } -static void gm_place_data_in_grid(unsigned char word[], char grid[], int modules, int size) { - int x, y, macromodule, offset; +static void gm_place_data_in_grid(const unsigned char word[], char grid[], const int modules, const int size) { + int x, y, macromodule; + const int offset = 13 - ((modules - 1) / 2); - offset = 13 - ((modules - 1) / 2); for (y = 0; y < modules; y++) { for (x = 0; x < modules; x++) { macromodule = gm_macro_matrix[((y + offset) * 27) + (x + offset)]; @@ -926,8 +927,9 @@ static void gm_place_data_in_grid(unsigned char word[], char grid[], int modules } /* Place the layer ID into each macromodule */ -static void gm_place_layer_id(char *grid, int size, int layers, int modules, int ecc_level) { +static void gm_place_layer_id(char *grid, const int size, const int layers, const int ecc_level) { int i, j, layer, start, stop; + const int modules = 1 + (layers << 1); int *layerid = (int *) z_alloca(sizeof(int) * (layers + 1)); int *id = (int *) z_alloca(sizeof(int) * (modules * modules)); @@ -947,8 +949,8 @@ static void gm_place_layer_id(char *grid, int size, int layers, int modules, int } /* Calculate which value goes in each macromodule */ - start = modules / 2; - stop = modules / 2; + start = modules >> 1; + stop = modules >> 1; for (layer = 0; layer <= layers; layer++) { for (i = start; i <= stop; i++) { id[(start * modules) + i] = layerid[layer]; @@ -1174,24 +1176,24 @@ INTERNAL int zint_gridmatrix(struct zint_symbol *symbol, struct zint_seg segs[], memset(grid, '0', size_squared); gm_place_data_in_grid(word, grid, modules, size); - gm_place_layer_id(grid, size, layers, modules, ecc_level); + gm_place_layer_id(grid, size, layers, ecc_level); /* Add macromodule frames */ for (x = 0; x < modules; x++) { - int dark = 1 - (x & 1); + const int x_offset = x * 6; + int dark = !(x & 1); for (y = 0; y < modules; y++) { - if (dark == 1) { + if (dark) { + const int y_offset = y * 6 * size; for (i = 0; i < 5; i++) { - grid[((y * 6) * size) + (x * 6) + i] = '1'; - grid[(((y * 6) + 5) * size) + (x * 6) + i] = '1'; - grid[(((y * 6) + i) * size) + (x * 6)] = '1'; - grid[(((y * 6) + i) * size) + (x * 6) + 5] = '1'; + grid[y_offset + x_offset + i] = '1'; + grid[y_offset + 5 * size + x_offset + i] = '1'; + grid[y_offset + i * size + x_offset] = '1'; + grid[y_offset + i * size + x_offset + 5] = '1'; } - grid[(((y * 6) + 5) * size) + (x * 6) + 5] = '1'; - dark = 0; - } else { - dark = 1; + grid[y_offset + 5 * size + x_offset + 5] = '1'; } + dark = !dark; } } diff --git a/backend/gs1.c b/backend/gs1.c index f2aab7ff..4ff12513 100644 --- a/backend/gs1.c +++ b/backend/gs1.c @@ -1640,6 +1640,7 @@ static int gs1se_verify(struct zint_symbol *symbol, const unsigned char source[] char msgBuf[120]; gs1_encoder_init_status_t status = GS1_ENCODERS_INIT_SUCCESS; gs1_encoder_init_opts_t opts = { + /* NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) suppress clang-tidy-21 warning OR-ing enums */ sizeof(gs1_encoder_init_opts_t), gs1_encoder_iNO_SYNDICT | gs1_encoder_iQUIET, &status, msgBuf, sizeof(msgBuf) }; gs1_encoder *ctx; @@ -1847,6 +1848,7 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou } } if (i != length) { + assert(length > 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ local_source = local_source_buf; /* Replace with control-char placeholders */ for (i = 0, j = 0; i < length; i++) { diff --git a/backend/pdf417.c b/backend/pdf417.c index 33e17d4f..06666d7a 100644 --- a/backend/pdf417.c +++ b/backend/pdf417.c @@ -1395,6 +1395,8 @@ static int pdf_enc(struct zint_symbol *symbol, struct zint_seg segs[], const int if (debug_print) printf("\nSymbol size:\n%d columns x %d rows\n", cols, rows); + assert(cols > 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + /* 818 - The CW string is finished */ c1 = (rows - 1) / 3; c2 = ecc * 3 + (rows - 1) % 3; diff --git a/backend/plessey.c b/backend/plessey.c index 0d4023be..7fb51d3b 100644 --- a/backend/plessey.c +++ b/backend/plessey.c @@ -30,10 +30,11 @@ */ /* SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include "common.h" -#define SSET_F (IS_NUM_F | IS_UHX_F) /* SSET "0123456789ABCDEF" */ +#define PLESS_SSET_F (IS_NUM_F | IS_UHX_F) /* SSET "0123456789ABCDEF" */ static const char PlessTable[16][8] = { {'1','3','1','3','1','3','1','3'}, {'3','1','1','3','1','3','1','3'}, {'1','3','3','1','1','3','1','3'}, @@ -67,7 +68,7 @@ INTERNAL int zint_plessey(struct zint_symbol *symbol, unsigned char source[], in if (length > 67) { /* 16 + 67 * 16 + 4 * 8 + 19 = 1139 */ return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 370, "Input length %d too long (maximum 67)", length); } - if ((i = z_not_sane(SSET_F, source, length))) { + if ((i = z_not_sane(PLESS_SSET_F, source, length))) { return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 371, "Invalid character at position %d in input (digits and \"ABCDEF\" only)", i); } @@ -334,6 +335,8 @@ INTERNAL int zint_msi_plessey(struct zint_symbol *symbol, unsigned char source[] int no_checktext = 0; const int raw_text = symbol->output_options & BARCODE_RAW_TEXT; + assert(length > 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + if (length > 92) { /* 3 (Start) + 92 * 12 + 3 * 12 + 4 (Stop) = 1147 */ return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 372, "Input length %d too long (maximum 92)", length); } diff --git a/backend/ps.c b/backend/ps.c index bf264006..b597f8bb 100644 --- a/backend/ps.c +++ b/backend/ps.c @@ -76,6 +76,8 @@ static void ps_convert(const unsigned char *string, unsigned char *ps_string) { const unsigned char *s; unsigned char *p = ps_string; + /* `NOLINT` required due to disconnect between `symbol->text` and vector `string`s which are always <= */ + /* NOLINTBEGIN(clang-analyzer-security.ArrayBound) clang-tidy-21 false positive */ for (s = string; *s; s++) { switch (*s) { case '(': @@ -95,10 +97,10 @@ static void ps_convert(const unsigned char *string, unsigned char *ps_string) { *p++ = *s; } break; - } } *p = '\0'; + /* NOLINTEND(clang-analyzer-security.ArrayBound) */ } #ifdef ZINT_TEST /* Wrapper for direct testing */ @@ -344,6 +346,7 @@ INTERNAL int zint_ps_plot(struct zint_symbol *symbol) { for (i = 0; i <= 8; i++) { for (rect = symbol->vector->rectangles; rect; rect = rect->next) { if ((i == 0 && rect->colour == -1) || rect->colour == i) { + assert(u_i < rect_cnt); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ ultra_rects[u_i++] = rect; } } diff --git a/backend/qr.c b/backend/qr.c index 15f0148a..80a51de0 100644 --- a/backend/qr.c +++ b/backend/qr.c @@ -1829,6 +1829,8 @@ INTERNAL int zint_qrcode(struct zint_symbol *symbol, struct zint_seg segs[], con size = qr_sizes[version - 1]; size_squared = size * size; + assert(size >= 21); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + grid = (unsigned char *) z_alloca(size_squared); memset(grid, 0, size_squared); @@ -2736,6 +2738,8 @@ INTERNAL int zint_rmqr(struct zint_symbol *symbol, struct zint_seg segs[], const h_size = rmqr_width[version]; v_size = rmqr_height[version]; + assert(h_size >= 27 && v_size >= 7); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + grid = (unsigned char *) z_alloca(h_size * v_size); memset(grid, 0, h_size * v_size); diff --git a/backend/raster.c b/backend/raster.c index edbd3bd0..20df2b7e 100644 --- a/backend/raster.c +++ b/backend/raster.c @@ -998,6 +998,8 @@ static int plot_raster_default(struct zint_symbol *symbol, const int rotate_angl main_width = symbol->width; + assert(symbol->rows >= 1); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + if (z_is_composite(symbol->symbology)) { while (!z_module_is_set(symbol, symbol->rows - 1, comp_xoffset)) { comp_xoffset++; diff --git a/backend/rss.c b/backend/rss.c index bf4e1b87..fac6f1f8 100644 --- a/backend/rss.c +++ b/backend/rss.c @@ -288,6 +288,8 @@ INTERNAL int zint_dbar_omnstk_set_height(struct zint_symbol *symbol, const int f const int second_row = first_row + 2; /* 2 row separator */ int i; + assert(first_row >= 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + for (i = 0; i < symbol->rows; i++) { if (i != first_row && i != second_row) { fixed_height += symbol->row_height[i]; diff --git a/backend/svg.c b/backend/svg.c index e20a8502..4a36228f 100644 --- a/backend/svg.c +++ b/backend/svg.c @@ -59,6 +59,8 @@ static void svg_pick_colour(const int colour, char colour_code[7]) { /* Convert text to use HTML entity codes */ static void svg_make_html_friendly(const unsigned char *string, char *html_version) { + /* `NOLINT` required due to disconnect between `symbol->text` and vector `string`s which are always <= */ + /* NOLINTBEGIN(clang-analyzer-security.ArrayBound) clang-tidy-21 false positive */ for (; *string; string++) { switch (*string) { case '>': @@ -93,6 +95,7 @@ static void svg_make_html_friendly(const unsigned char *string, char *html_versi } *html_version = '\0'; + /* NOLINTEND(clang-analyzer-security.ArrayBound) */ } /* Helper to output floating point attribute */ diff --git a/backend/tests/testcommon.c b/backend/tests/testcommon.c index df58382a..ae44b4df 100644 --- a/backend/tests/testcommon.c +++ b/backend/tests/testcommon.c @@ -1437,6 +1437,9 @@ int testUtilDataPath(char *buffer, int buffer_size, const char *subdir, const ch int i; #endif + assert(buffer); /* Suppress clang-tidy-21 clang-analyzer-core.NonNullParamChecker */ + + /* Apparently `getenv()` & `getcwd()` "taint" stuff (external attack vectors) hence later NOLINTs */ if ((cmake_src_dir = getenv("CMAKE_CURRENT_SOURCE_DIR")) != NULL) { len = (int) strlen(cmake_src_dir); if (len <= 0 || len >= buffer_size) { @@ -1500,7 +1503,7 @@ int testUtilDataPath(char *buffer, int buffer_size, const char *subdir, const ch } if (subdir_len) { - if (*subdir != '/' && buffer[len - 1] != '/') { + if (*subdir != '/' && buffer[len - 1] != '/') { /* NOLINT(clang-analyzer-security.ArrayBound) - see above */ if (len + 1 >= buffer_size) { fprintf(stderr, "testUtilDataPath: subdir len (%d) + 1 >= buffer_size (%d)\n", len, buffer_size); return 0; @@ -1518,7 +1521,7 @@ int testUtilDataPath(char *buffer, int buffer_size, const char *subdir, const ch } if (filename_len) { - if (*filename != '/' && buffer[len - 1] != '/') { + if (*filename != '/' && buffer[len - 1] != '/') { /* NOLINT(clang-analyzer-security.ArrayBound) - see above */ if (len + 1 >= buffer_size) { fprintf(stderr, "testUtilDataPath: filename len (%d) + 1 >= buffer_size (%d)\n", len, buffer_size); return 0; diff --git a/backend/tif.c b/backend/tif.c index ef1fd998..f71cfb1a 100644 --- a/backend/tif.c +++ b/backend/tif.c @@ -278,6 +278,7 @@ INTERNAL int zint_tif_pixel_plot(struct zint_symbol *symbol, const unsigned char bytes_per_strip = rows_per_strip * ((symbol->bitmap_width + pixels_per_sample - 1) / pixels_per_sample) * samples_per_pixel; + assert(bytes_per_strip >= 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ strip_offset = (uint32_t *) z_alloca(sizeof(uint32_t) * strip_count); strip_bytes = (uint32_t *) z_alloca(sizeof(uint32_t) * strip_count); diff --git a/backend/ultra.c b/backend/ultra.c index 3d876f0d..3a460fcf 100644 --- a/backend/ultra.c +++ b/backend/ultra.c @@ -32,6 +32,7 @@ /* This version was developed using AIMD/TSC15032-43 v0.99c Edit 60, dated 4th Nov 2015 */ +#include #include #include "common.h" @@ -637,6 +638,8 @@ static int ult_generate_codewords(struct zint_symbol *symbol, const unsigned cha char *mode = (char *) z_alloca(length + 1); int *cw_fragment = (int *) z_alloca(sizeof(int) * (length * 2 + 1)); + assert(length > 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ + /* Check for 06 Macro Sequence and crop accordingly */ if (length >= 9 && source[0] == '[' && source[1] == ')' && source[2] == '>' && source[3] == '\x1e' @@ -694,6 +697,7 @@ static int ult_generate_codewords(struct zint_symbol *symbol, const unsigned cha } input_locn += ascii_encoded; } else if (mode[input_locn] == 'c') { + assert(c43_encoded >= 0); /* Suppress clang-tidy-21 clang-analyzer-security.ArrayBound */ for (i = 0; i < c43_encoded; i++) { mode[input_locn + i] = 'c'; } diff --git a/docs/Makefile b/docs/Makefile index 7ee8dc50..304a5099 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ # Copyright (C) 2022-2025 # vim: set ts=4 sw=4 noet : # -# Requires a recent version of pandoc, plus pandoc-tablenos, xelatex and various other packages - see "README" +# Requires pandoc 3.8.2, plus xelatex and various other packages - see "README" # .svg images generated by "zint_images.sh" SOURCE = manual.pmd @@ -16,6 +16,8 @@ INCLUDES_TXT = inc_header_txt.tex INC_TXT = --include-in-header $(INCLUDES_TXT) SOURCE_MAN_PAGE = zint.1.pmd OUT_MAN_PAGE = zint.1 +LUA_FILTER = lua-crossrefs/lua-crossrefs.lua +INFRASTRUCTURE = Makefile $(LUA_FILTER) IMAGES = \ images/zint.png \ images/zint-qt.png \ @@ -148,15 +150,18 @@ IMAGES = \ MAIN_FONT = mainfont="TeX Gyre Pagella" MONO_FONT = monofont="Liberation Mono" CJK_FONT = CJKmainfont="WenQuanYi Micro Hei Mono" -PDF_OPTS = --pdf-engine=xelatex --filter pandoc-tablenos -M tablenos-warning-level=0 \ - --highlight-style=haddock -V colorlinks -V geometry:margin=20mm -V papersize=a4 -V csquotes=true --dpi=300 +PDF_OPTS = --pdf-engine=xelatex \ + --lua-filter=$(LUA_FILTER) \ + --syntax-highlighting=haddock -V colorlinks -V geometry:margin=20mm -V papersize=a4 \ + -V csquotes=true --dpi=300 TEX_MAN_PAGE = zint.1.tex -TXT_OPTS = --filter pandoc-tablenos -M tablenos-warning-level=0 --columns 80 --eol=lf -t plain +TXT_OPTS = --lua-filter=$(LUA_FILTER) \ + --columns 80 --eol=lf -t plain MAN_PAGE_OPTS = -s -t man all : $(OUT_PDF) $(OUT_TXT) $(OUT_MAN_PAGE) $(OUT_HTML) -$(OUT_PDF) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_HEADER_PDF) $(INC_BEFORE_BODY_PDF) $(IMAGES) Makefile +$(OUT_PDF) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_HEADER_PDF) $(INC_BEFORE_BODY_PDF) $(IMAGES) $(INFRASTRUCTURE) pandoc $(SOURCE_MAN_PAGE) -f markdown \ $(PDF_OPTS) \ -o $(TEX_MAN_PAGE) @@ -167,7 +172,7 @@ $(OUT_PDF) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_HEADER_PDF) $(INC_BEFORE_BODY_PD --include-after-body $(TEX_MAN_PAGE) \ -o $(OUT_PDF) -$(OUT_TXT) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INCLUDES_TXT) Makefile +$(OUT_TXT) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INCLUDES_TXT) $(INFRASTRUCTURE) pandoc $(SOURCE) $(SOURCE_MAN_PAGE) -f markdown $(INC_TXT) --toc --toc-depth=4 \ -V $(MAIN_FONT) -V $(MONO_FONT) -V $(CJK_FONT) \ $(TXT_OPTS) \ @@ -179,19 +184,19 @@ $(OUT_TXT) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INCLUDES_TXT) Makefile -e 's/ *$$//' \ -e '/^\[.*\]$$/{N;N;s/\[\(.*\)\]\n\n\1/[\1]/;p;d}' \ -e 's/ *{#tbl:[^}]*}//' \ - -e 's/: Table\xC2\xA0: \([^:]*\):/Table : \1/' \ + -e 's/^ : Table/ Table/' \ $(OUT_TXT) # Wrap sed -i '/.\{81\}/{s/.\{80\}/&\n/}' $(OUT_TXT) -$(OUT_MAN_PAGE) : $(SOURCE_MAN_PAGE) Makefile +$(OUT_MAN_PAGE) : $(SOURCE_MAN_PAGE) $(INFRASTRUCTURE) pandoc $(SOURCE_MAN_PAGE) -f markdown \ $(MAN_PAGE_OPTS) \ -o $(OUT_MAN_PAGE) # For debugging -manual.tex : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_HEADER_PDF) $(INC_BEFORE_BODY_PDF) $(IMAGES) Makefile +manual.tex : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_HEADER_PDF) $(INC_BEFORE_BODY_PDF) $(IMAGES) $(INFRASTRUCTURE) pandoc $(SOURCE_MAN_PAGE) -f markdown \ $(PDF_OPTS) \ -o $(TEX_MAN_PAGE) @@ -204,13 +209,14 @@ manual.tex : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_HEADER_PDF) $(INC_BEFORE_BODY_PD # HTML one-page (uses modified "templates/styles.html", unchanged "templates/default.html") -HTML_OPTS = --filter pandoc-tablenos -M tablenos-warning-level=0 --highlight-style=haddock \ +HTML_OPTS = --lua-filter=$(LUA_FILTER) \ + --syntax-highlighting=haddock \ --template=templates/default.html --eol=lf -s -t html INC_BEFORE_BODY_HTML = inc_before_body_html.html INC_HTML = --include-before-body $(INC_BEFORE_BODY_HTML) TEMPLATES_HTML = templates/default.html templates/styles.html -$(OUT_HTML) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_BEFORE_BODY_HTML) $(TEMPLATES_HTML) Makefile +$(OUT_HTML) : $(SOURCE) $(SOURCE_MAN_PAGE) $(INC_BEFORE_BODY_HTML) $(TEMPLATES_HTML) $(INFRASTRUCTURE) pandoc $(SOURCE) $(SOURCE_MAN_PAGE) -f markdown+link_attributes $(INC_HTML) --toc --toc-depth=4 \ -V $(MAIN_FONT) -V $(MONO_FONT) -V $(CJK_FONT) \ $(HTML_OPTS) \ diff --git a/docs/README b/docs/README index bfa9f267..142f4ff3 100644 --- a/docs/README +++ b/docs/README @@ -1,41 +1,22 @@ -% docs/README 2025-09-16 +% docs/README 2025-10-08 -For generation of "docs/manual.pdf" and "docs/manual.txt" from "manual.pmd" using a recent version of pandoc +For generation of "docs/manual.pdf" and "docs/manual.txt" from "manual.pmd" using pandoc 3.8.2. On Ubuntu/Debian (tested on Ubuntu 22.04 and Ubuntu 24.04) - wget https://github.com/jgm/pandoc/releases/download/3.8/pandoc-3.8-1-amd64.deb - sudo dpkg -i pandoc-3.8-1-amd64.deb -For Ubuntu 22.04 (python < 3.12) - sudo apt install python3-pip - pip install pandoc-tablenos --user -Else for Ubuntu 24.04 (one way around "externally-managed-environment" error, PEP 668) - sudo apt install python3-full - python3 -m venv ~/py_envs - source ~/py_envs/bin/activate - pip install pandoc-tablenos -Then - export PATH=~/.local/bin:"$PATH" - # Temporary fix for version regex - see https://github.com/tomduck/pandoc-xnos/pull/29 - python -m pip install --force-reinstall \ - git+https://github.com/tomduck/pandoc-xnos@284474574f51888be75603e7d1df667a0890504d#egg=pandoc-xnos + wget https://github.com/jgm/pandoc/releases/download/3.8.2/pandoc-3.8.2-1-amd64.deb + sudo dpkg -i pandoc-3.8.2-1-amd64.deb sudo apt install librsvg2-bin sudo apt install texlive-xetex sudo apt install texlive-lang-cjk sudo apt install fonts-wqy-microhei make -On Fedora (tested on Fedora Linux 38 (Workstation Edition) and Fedora Linux 40 (Workstation Edition)) +On Fedora (tested on Fedora Linux 38 (Workstation Edition) and Fedora Linux 42 (Workstation Edition)) - wget https://github.com/jgm/pandoc/releases/download/3.8/pandoc-3.8-linux-amd64.tar.gz - tar xf pandoc-3.8-linux-amd64.tar.gz - sudo mv -i pandoc-3.8/bin/pandoc /usr/local/bin - sudo dnf install python3-pip - pip install pandoc-tablenos --user - export PATH=~/.local/bin:"$PATH" - # Temporary fix for version regex - see https://github.com/tomduck/pandoc-xnos/pull/29 - python -m pip install --force-reinstall \ - git+https://github.com/tomduck/pandoc-xnos@284474574f51888be75603e7d1df667a0890504d#egg=pandoc-xnos + wget https://github.com/jgm/pandoc/releases/download/3.8.2/pandoc-3.8.2-linux-amd64.tar.gz + tar xf pandoc-3.8.2-linux-amd64.tar.gz + sudo mv -i pandoc-3.8.2/bin/pandoc /usr/local/bin sudo dnf install librsvg2-tools.x86_64 sudo dnf install texlive-xetex sudo dnf install texlive-ctex.noarch @@ -51,3 +32,11 @@ On Fedora (tested on Fedora Linux 38 (Workstation Edition) and Fedora Linux 40 ( On Windows Not compatible. + +On BSD + + TODO + +On macOS + + TODO diff --git a/docs/images/excode39.svg b/docs/images/excode39.svg index ee3fe941..25987689 100644 --- a/docs/images/excode39.svg +++ b/docs/images/excode39.svg @@ -1,12 +1,12 @@ - + Zint Generated Symbol - - - - 123.45fd + + + + 123.45#@fd diff --git a/docs/inc_header_pdf.tex b/docs/inc_header_pdf.tex index 92755703..da24ecaa 100644 --- a/docs/inc_header_pdf.tex +++ b/docs/inc_header_pdf.tex @@ -36,46 +36,8 @@ \let\oldparagraph\paragraph \renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}} -%% https://github.com/tomduck/pandoc-tablenos -%% As using --include-in-header (this file), need to manually include the following (displayed by setting -%% "-M tablenos-warning-level=2"): - -%% PDF metadata - the values are set in "docs/inc_before_body.tex" (otherwise may get overridden) +%% PDF metadata - the values are set in "docs/inc_before_body_pdf.tex" (otherwise may get overridden) \usepackage{hyperref} -%% pandoc-tablenos: required package +%% pandoc-lua-crossrefs: required package \usepackage{caption} - -%% pandoc-tablenos: environment to disable table caption prefixes -\makeatletter -\newcounter{tableno} -\newenvironment{tablenos:no-prefix-table-caption}{ - \caption@ifcompatibility{}{ - \let\oldthetable\thetable - \let\oldtheHtable\theHtable - \renewcommand{\thetable}{tableno:\thetableno} - \renewcommand{\theHtable}{tableno:\thetableno} - \stepcounter{tableno} - \captionsetup{labelformat=empty} - } -}{ - \caption@ifcompatibility{}{ - \captionsetup{labelformat=default} - \let\thetable\oldthetable - \let\theHtable\oldtheHtable - \addtocounter{table}{-1} - } -} -\makeatother - -%% pandoc-tablenos: environment for tagged tables -\newenvironment{tablenos:tagged-table}[1][]{ - \let\oldthetable\thetable - \let\oldtheHtable\theHtable - \renewcommand{\thetable}{#1} - \renewcommand{\theHtable}{#1} -}{ - \let\thetable\oldthetable - \let\theHtable\oldtheHtable - \addtocounter{table}{-1} -} diff --git a/docs/lua-crossrefs/COPYING b/docs/lua-crossrefs/COPYING new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/docs/lua-crossrefs/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/docs/lua-crossrefs/lua-crossrefs.lua b/docs/lua-crossrefs/lua-crossrefs.lua new file mode 100644 index 00000000..c5a23e0a --- /dev/null +++ b/docs/lua-crossrefs/lua-crossrefs.lua @@ -0,0 +1,271 @@ +-- libzint - the open source barcode library +-- Copyright (C) 2025 Robin Stuart + +-- Hacked from https://github.com/rnwst/pandoc-lua-crossrefs +-- © 2025 R. N. West. Released under the GPL version 2 or greater. +-- SPDX-License-Identifier: GPL-2.0-or-later + +-- Hacked from "pandoc-lua-crossrefs/init.lua" + +-- Table of Ids and corresponding cross-referenceable elements. To be populated +-- by various element numbering functions. +---@type table +IDs = {} + +-- Hacked from "pandoc-lua-crossrefs/lib/crossrefs.lua" + +---Check if AST element is a cross-reference (a cross-reference is a Span with class 'cross-ref'). +---@param inline Inline +---@return boolean +local _is_crossref = function(inline) + local is_crossref_link = inline and inline.tag == 'Link' and inline.attributes['reference-type'] ~= nil + return is_crossref_link +end + +---Parse a cross-reference in Pandoc's Markdown. +---@param str Str +---@return Inline[] | nil +local _parse_crossref = function(str) + local opening_bracket, prefix_suppressor, id, closing_bracket1, punctuation, closing_bracket2 = + str.text:match('^(%[?)(%-?)#([%a%d-_:%.]-)(%]?)([\\%.!:?,;)]-)(%]?)$') + if not id or id == '' then return end + local only_internal_punctuation = id:find('^[%a%d]+[%a%d%-_:%.]*[%a%d]+$') or id:find('^[%a%d]+$') + if not only_internal_punctuation then return end + if #closing_bracket1 ~= 0 and #closing_bracket2 ~= 0 then return end + + local crossref = pandoc.Link({}, '#' .. id, '', pandoc.Attr('', {}, { ['reference-type'] = 'ref+label' })) + if prefix_suppressor == '-' then crossref.attributes['reference-type'] = 'ref' end + local elts = pandoc.List { crossref } + if opening_bracket == '[' then elts:insert(1, pandoc.Str('[')) end + if closing_bracket1 == ']' then elts:insert(pandoc.Str(']')) end + if punctuation ~= '' then elts:insert(pandoc.Str(punctuation)) end + if closing_bracket2 == ']' then elts:insert(pandoc.Str(']')) end + + return elts +end + +---Parse cross-references in Inlines. +---@param inlines Inlines +---@return (Inline[] | Inlines | nil), boolean? +local parse_crossrefs = function(inlines) + -- Parse cross-references into Links. + local new_inlines = inlines:walk { Str = _parse_crossref } + + -- Early return if no cross-references were found! + if new_inlines == inlines then return end + + inlines = new_inlines + + -- Now separate out any opening or closing brackets in Strs into separate + -- Strs, in case crossref groups don't begin and end with cross-references. + inlines = inlines:walk { + Str = function(str) + if str.text:find('^%[.') then + return { pandoc.Str('['), pandoc.Str(str.text:sub(2)) } + elseif str.text:find('.%]$') then + return { pandoc.Str(str.text:sub(1, -2)), pandoc.Str(']') } + end + end, + } + + -- Now create crossref groups. Crossref Groups are represented by Spans of + -- class 'cross-ref-group'. + ---@type List + new_inlines = pandoc.List {} + local i = 1 + while inlines[i] do + if i < #inlines and inlines[i].tag == 'Str' and inlines[i].text == '[' then + ---@type boolean + local at_least_one_crossref = false + ---@type List + local group_content = pandoc.List {} + ---@type boolean + local group_valid = false + local j = i + 1 + while inlines[j] do + local elt = inlines[j] + if elt.tag == 'Str' and elt.text == ']' then + if at_least_one_crossref then + group_valid = true + break + else + group_valid = false + break + end + -- Another opening bracket invalidates the group if no cross-reference has yet been + -- found. This ensures that the smallest possible Crossref Groups are created. + elseif elt.tag == 'Str' and elt.text == '[' and not at_least_one_crossref then + group_valid = false + break + else + if _is_crossref(elt) then at_least_one_crossref = true end + group_content:insert(inlines[j]) + end + j = j + 1 + if _is_crossref(elt) and inlines[j] and inlines[j].tag == 'Str' then + if inlines[j].text == ';' and inlines[j + 1] and inlines[j + 1].tag == 'Space' then + -- Skip punctuation following crossref if it is ';'. + j = j + 2 + elseif inlines[j].text == '\\;' then + -- To still allow a semicolon to be used to separate cross-references, an + -- escaped semicolon is converted to a semicolon. + group_content:insert(pandoc.Str(';')) + j = j + 1 + end + end + end + if group_valid then + -- Insert Crossref Group into inlines. + local crossref_group = pandoc.Span(group_content, pandoc.Attr('', { 'cross-ref-group' })) + new_inlines:insert(crossref_group) + i = j + else + new_inlines:insert(inlines[i]) + end + else + new_inlines:insert(inlines[i]) + end + i = i + 1 + end + + return new_inlines, false -- Nested cross-references are not allowed! +end + +---Get cross-reference target +---@param crossref Link +---@return { type: 'fig'|'tbl', number: string, caption: string } +local _get_target = function(crossref) return IDs[crossref.target:sub(2)] end + +---Resolve cross-reference. +---@param crossref Link cross-reference +---@param suppress_prefix? boolean whether to suppress prefixing the referenced object's type (e.g. 'Fig.' or 'Tbl.') +---@return Link +local _resolve_crossref = function(crossref, suppress_prefix) + local target = _get_target(crossref) + local crossref_text = '' + if target ~= nil then + if crossref.attributes['reference-type'] == 'ref+label' and not suppress_prefix then + if target.type == 'fig' then + crossref_text = 'Figure ' + elseif target.type == 'tbl' then + crossref_text = 'Table ' + end + end + crossref_text = crossref_text .. target.number .. ': ' .. target.caption + else + crossref_text = '??' + pandoc.log.warn('Cross-referenced element with id ' .. tostring(crossref.target) .. ' could not be resolved.') + end + local link = pandoc.Link(crossref_text, crossref.target) + link.attr = pandoc.Attr('', { 'cross-ref' }) + return link +end + +---Resolve single cross-references. +---@param link Link +---@return Link? +local write_crossref = function(link) + if _is_crossref(link) then return _resolve_crossref(link) end +end + +-- Hacked from "pandoc-lua-crossrefs/lib/numbering.lua" + +local figure_number = 0 +local table_number = 0 +---Number figure or table. +---@param fig_or_tbl (Figure | Table) +---@return (Figure | Table), false Numbered Figure or Table, or `nil` if unnumbered +---@overload fun(fig_or_tbl: Figure | Table): nil +local number_fig_or_tbl = function(fig_or_tbl) + if not fig_or_tbl.classes:includes('unnumbered') then + ---@type string + local _type + ---@type integer + local number + ---@type string + local label_class + ---@type fun(num: integer): string + local number_formatter = function(num) return tostring(num) end + ---@type fun(num: integer): string + local label_formatter + ---@type boolean + local colon_after_label = true + + if fig_or_tbl.tag == 'Figure' then + _type = 'fig' + figure_number = figure_number + 1 + number = figure_number + label_class = 'figure-label' + label_formatter = function(num) return string.format('Figure %s', num) end + end + + if fig_or_tbl.tag == 'Table' then + _type = 'tbl' + table_number = table_number + 1 + number = table_number + label_class = 'table-label' + label_formatter = function(num) return string.format('Table %s', num) end + end + + ---Add Fig or Tbl to table of Ids, prepend label to caption. + ---@param elt (Figure | Table) + local function process_fig_or_tbl(elt) + if elt.identifier ~= '' then + local caption = pandoc.utils.stringify(elt.caption.long) + IDs[elt.identifier] = { type = _type, number = number_formatter(number), caption = caption } + end + local caption_prefix = pandoc.Span({ pandoc.Str(label_formatter(number)) }, pandoc.Attr('', { label_class })) + if FORMAT ~= 'latex' then + -- If figure or table caption is not empty, append colon to number. + if #elt.caption.long ~= 0 then + if colon_after_label then + caption_prefix.content[1].text = caption_prefix.content[1].text .. ':' + elt.caption.long[1].content:insert(1, pandoc.Space()) + end + elt.caption.long[1].content:insert(1, caption_prefix) + else + elt.caption.long:insert(pandoc.Plain(caption_prefix)) + end + end + end + + process_fig_or_tbl(fig_or_tbl) + + -- Number subfigs. + if _type == 'fig' then + number = 0 + number_formatter = function(num) return figure_number .. label_formatter(num) end + label_formatter = function(num) return string.format('(%s)', string.char(96 + num)) end + colon_after_label = false + fig_or_tbl = fig_or_tbl:walk { + Figure = function(subfig) + number = number + 1 + process_fig_or_tbl(subfig) + return subfig + end, + } + end + + return fig_or_tbl, false -- Return `false` as second value to avoid processing subfigures again. + end +end + +-- Hacked from "pandoc-lua-crossrefs/init.lua" + +---@param doc Pandoc +function Pandoc(doc) + return doc + :walk({ + Inlines = parse_crossrefs, + }) + :walk({ + -- Number cross-referenceable elements and construct table with Ids and numbers. + traverse = 'topdown', -- needed for subfigs + Figure = number_fig_or_tbl, + Table = number_fig_or_tbl, + }) + :walk { + -- Resolve single cross-references. + Link = write_crossref, + } +end diff --git a/docs/manual.html b/docs/manual.html index b37a8d01..90f54af0 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -333,7 +333,7 @@

Zint Barcode Generator and Zint Barcode Studio User Manual

Version 2.15.0.9

-

September 2025

+

October 2025