comparison mupdf-source/thirdparty/zint/frontend/main.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /* main.c - Command line handling routines for Zint */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2008-2024 Robin Stuart <rstuart114@gmail.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 /* SPDX-License-Identifier: GPL-3.0-or-later */
21
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(_AIX)
29 # include <getopt.h>
30 # include <zint.h>
31 #else
32 # include "../getopt/getopt.h"
33 # ifdef _MSC_VER
34 # include "zint.h"
35 # if _MSC_VER > 1200 /* VC6 */
36 # pragma warning(disable: 4996) /* function or variable may be unsafe */
37 # endif
38 # else
39 # include <zint.h>
40 # endif
41 #endif
42
43 /* Following copied from "backend/library.c" */
44
45 /* It's assumed that int is at least 32 bits, the following will compile-time fail if not
46 * https://stackoverflow.com/a/1980056 */
47 typedef char static_assert_int_at_least_32bits[sizeof(int) * CHAR_BIT < 32 ? -1 : 1];
48
49 /* Following copied from "backend/common.h" */
50
51 #define ARRAY_SIZE(x) ((int) (sizeof(x) / sizeof((x)[0])))
52
53 #ifdef _MSC_VER
54 # include <malloc.h>
55 # define z_alloca(nmemb) _alloca(nmemb)
56 #elif defined(__COMPCERT__)
57 # define z_alloca(nmemb) malloc(nmemb) /* So links - leads to loads of leaks obs */
58 #else
59 # if (defined(__GNUC__) && !defined(alloca) && !defined(__NetBSD__)) || defined(__NuttX__) || defined(_AIX) \
60 || (defined(__sun) && defined(__SVR4) /*Solaris*/)
61 # include <alloca.h>
62 # endif
63 # define z_alloca(nmemb) alloca(nmemb)
64 #endif
65
66 /* Print list of supported symbologies */
67 static void types(void) {
68 /* Breaking up strings so don't get too long (i.e. 500 or so) */
69 fputs(" # Name Description # Name Description\n"
70 " 1 CODE11 Code 11 75 NVE18 NVE-18\n"
71 " 2 C25STANDARD Standard 2 of 5 76 JAPANPOST Japanese Post\n"
72 " 3 C25INTER Interleaved 2 of 5 77 KOREAPOST Korea Post\n"
73 " 4 C25IATA IATA 2 of 5 79 DBAR_STK GS1 DataBar Stacked\n", stdout);
74 fputs(" 6 C25LOGIC Data Logic 2 of 5 80 DBAR_OMNSTK GS1 DataBar Stack Omni\n"
75 " 7 C25IND Industrial 2 of 5 81 DBAR_EXPSTK GS1 DataBar Exp Stack\n"
76 " 8 CODE39 Code 39 82 PLANET USPS PLANET\n"
77 " 9 EXCODE39 Extended Code 39 84 MICROPDF417 MicroPDF417\n"
78 "13 EANX EAN-2 to EAN-13 85 USPS_IMAIL USPS Intelligent Mail\n", stdout);
79 fputs("14 EANX_CHK EAN + Check Digit 86 PLESSEY UK Plessey\n"
80 "16 GS1_128 GS1-128 87 TELEPEN_NUM Telepen Numeric\n"
81 "18 CODABAR Codabar 89 ITF14 ITF-14\n"
82 "20 CODE128 Code 128 90 KIX Dutch Post KIX Code\n"
83 "21 DPLEIT Deutsche Post Leitcode 92 AZTEC Aztec Code\n", stdout);
84 fputs("22 DPIDENT Deutsche Post Identcode 93 DAFT DAFT Code\n"
85 "23 CODE16K Code 16K 96 DPD DPD Parcel Code 128\n"
86 "24 CODE49 Code 49 97 MICROQR Micro QR Code\n"
87 "25 CODE93 Code 93 98 HIBC_128 HIBC Code 128\n"
88 "28 FLAT Flattermarken 99 HIBC_39 HIBC Code 39\n", stdout);
89 fputs("29 DBAR_OMN GS1 DataBar Omni 102 HIBC_DM HIBC Data Matrix\n"
90 "30 DBAR_LTD GS1 DataBar Limited 104 HIBC_QR HIBC QR Code\n"
91 "31 DBAR_EXP GS1 DataBar Expanded 106 HIBC_PDF HIBC PDF417\n"
92 "32 TELEPEN Telepen Alpha 108 HIBC_MICPDF HIBC MicroPDF417\n"
93 "34 UPCA UPC-A 110 HIBC_BLOCKF HIBC Codablock-F\n", stdout);
94 fputs("35 UPCA_CHK UPC-A + Check Digit 112 HIBC_AZTEC HIBC Aztec Code\n"
95 "37 UPCE UPC-E 115 DOTCODE DotCode\n"
96 "38 UPCE_CHK UPC-E + Check Digit 116 HANXIN Han Xin Code\n"
97 "40 POSTNET USPS POSTNET 119 MAILMARK_2D Royal Mail 2D Mailmark\n"
98 "47 MSI_PLESSEY MSI Plessey 120 UPU_S10 UPU S10\n", stdout);
99 fputs("49 FIM Facing Ident Mark 121 MAILMARK_4S RM 4-State Mailmark\n"
100 "50 LOGMARS LOGMARS Code 39 128 AZRUNE Aztec Runes\n"
101 "51 PHARMA Pharmacode One-Track 129 CODE32 Code 32\n"
102 "52 PZN Pharmazentralnummer 130 EANX_CC Composite EAN\n"
103 "53 PHARMA_TWO Pharmacode Two-Track 131 GS1_128_CC Composite GS1-128\n", stdout);
104 fputs("54 CEPNET Brazilian CEPNet 132 DBAR_OMN_CC Comp DataBar Omni\n"
105 "55 PDF417 PDF417 133 DBAR_LTD_CC Comp DataBar Limited\n"
106 "56 PDF417COMP Compact PDF417 134 DBAR_EXP_CC Comp DataBar Expanded\n"
107 "57 MAXICODE MaxiCode 135 UPCA_CC Composite UPC-A\n"
108 "58 QRCODE QR Code 136 UPCE_CC Composite UPC-E\n", stdout);
109 fputs("60 CODE128AB Code 128 (Suppress C) 137 DBAR_STK_CC Comp DataBar Stacked\n"
110 "63 AUSPOST AP Standard Customer 138 DBAR_OMNSTK_CC Comp DataBar Stack Omn\n"
111 "66 AUSREPLY AP Reply Paid 139 DBAR_EXPSTK_CC Comp DataBar Exp Stack\n"
112 "67 AUSROUTE AP Routing 140 CHANNEL Channel Code\n"
113 "68 AUSREDIRECT AP Redirection 141 CODEONE Code One\n", stdout);
114 fputs("69 ISBNX ISBN 142 GRIDMATRIX Grid Matrix\n"
115 "70 RM4SCC Royal Mail 4SCC 143 UPNQR UPN QR Code\n"
116 "71 DATAMATRIX Data Matrix 144 ULTRA Ultracode\n"
117 "72 EAN14 EAN-14 145 RMQR Rectangular Micro QR\n"
118 "73 VIN Vehicle Information No. 146 BC412 BC412\n", stdout);
119 fputs("74 CODABLOCKF Codablock-F 147 DXFILMEDGE DX Film Edge Barcode\n", stdout);
120 }
121
122 /* Output version information */
123 static void version(const int no_png) {
124 const char *no_png_lib = no_png ? " (no libpng)" : "";
125 const int zint_version = ZBarcode_Version();
126 const int version_major = zint_version / 10000;
127 const int version_minor = (zint_version % 10000) / 100;
128 int version_release = zint_version % 100;
129 int version_build;
130
131 if (version_release >= 9) {
132 /* This is a test release */
133 version_release = version_release / 10;
134 version_build = zint_version % 10;
135 printf("Zint version %d.%d.%d.%d (dev)%s\n", version_major, version_minor, version_release, version_build,
136 no_png_lib);
137 } else {
138 /* This is a stable release */
139 printf("Zint version %d.%d.%d%s\n", version_major, version_minor, version_release, no_png_lib);
140 }
141 }
142
143 /* Output usage information */
144 static void usage(const int no_png) {
145 const char *no_png_type = no_png ? "" : "/PNG";
146 const char *no_png_ext = no_png ? "gif" : "png";
147
148 version(no_png);
149
150 /* Breaking up strings so don't get too long (i.e. 500 or so) */
151 printf("Encode input data in a barcode and save as BMP/EMF/EPS/GIF/PCX%s/SVG/TIF/TXT\n\n", no_png_type);
152 fputs( " -b, --barcode=TYPE Number or name of barcode type. Default is 20 (CODE128)\n"
153 " --addongap=INTEGER Set add-on gap in multiples of X-dimension for EAN/UPC\n"
154 " --batch Treat each line of input file as a separate data set\n"
155 " --bg=COLOUR Specify a background colour (as RGB(A) or \"C,M,Y,K\")\n"
156 " --binary Treat input as raw binary data\n", stdout);
157 fputs( " --bind Add boundary bars\n"
158 " --bindtop Add top boundary bar only\n"
159 " --bold Use bold text (HRT)\n"
160 " --border=INTEGER Set width of border in multiples of X-dimension\n"
161 " --box Add a box around the symbol\n", stdout);
162 fputs( " --cmyk Use CMYK colour space in EPS/TIF symbols\n"
163 " --cols=INTEGER Set the number of data columns in symbol\n"
164 " --compliantheight Warn if height not compliant, and use standard default\n"
165 " -d, --data=DATA Set the symbol data content (segment 0)\n"
166 " --direct Send output to stdout\n", stdout);
167 fputs( " --dmiso144 Use ISO format for 144x144 Data Matrix symbols\n"
168 " --dmre Allow Data Matrix Rectangular Extended\n"
169 " --dotsize=NUMBER Set radius of dots in dotty mode\n"
170 " --dotty Use dots instead of squares for matrix symbols\n"
171 " --dump Dump hexadecimal representation to stdout\n", stdout);
172 fputs( " -e, --ecinos Display ECI (Extended Channel Interpretation) table\n"
173 " --eci=INTEGER Set the ECI code for the data (segment 0)\n"
174 " --embedfont Embed font in vector output (SVG only)\n"
175 " --esc Process escape sequences in input data\n"
176 " --extraesc Process symbology-specific escape sequences (Code 128)\n", stdout);
177 fputs( " --fast Use faster encodation or other shortcuts if available\n"
178 " --fg=COLOUR Specify a foreground colour (as RGB(A) or \"C,M,Y,K\")\n", stdout);
179 printf(" --filetype=TYPE Set output file type BMP/EMF/EPS/GIF/PCX%s/SVG/TIF/TXT\n", no_png_type);
180 fputs( " --fullmultibyte Use multibyte for binary/Latin (QR/Han Xin/Grid Matrix)\n"
181 " --gs1 Treat input as GS1 compatible data\n"
182 " --gs1nocheck Do not check validity of GS1 data\n"
183 " --gs1parens Process parentheses \"()\" as GS1 AI delimiters, not \"[]\"\n"
184 " --gssep Use separator GS for GS1 (Data Matrix)\n", stdout);
185 fputs( " --guarddescent=NUMBER Set height of guard bar descent in X-dims (EAN/UPC)\n"
186 " --guardwhitespace Add quiet zone indicators (\"<\"/\">\") to HRT (EAN/UPC)\n"
187 " -h, --help Display help message\n"
188 " --height=NUMBER Set height of symbol in multiples of X-dimension\n"
189 " --heightperrow Treat height as per-row\n", stdout);
190 fputs( " -i, --input=FILE Read input data from FILE\n"
191 " --init Create Reader Initialisation (Programming) symbol\n"
192 " --mask=INTEGER Set masking pattern to use (QR/Han Xin/DotCode)\n"
193 " --mirror Use batch data to determine filename\n"
194 " --mode=INTEGER Set encoding mode (MaxiCode/Composite)\n", stdout);
195 printf(" --nobackground Remove background (EMF/EPS/GIF%s/SVG/TIF only)\n", no_png_type);
196 fputs( " --noquietzones Disable default quiet zones\n"
197 " --notext Remove human readable text (HRT)\n", stdout);
198 printf(" -o, --output=FILE Send output to FILE. Default is out.%s\n", no_png_ext);
199 fputs( " --primary=STRING Set primary message (MaxiCode/Composite)\n"
200 " --quietzones Add compliant quiet zones\n"
201 " -r, --reverse Reverse colours (white on black)\n"
202 " --rotate=INTEGER Rotate symbol by INTEGER (0, 90, 180, 270) degrees\n"
203 " --rows=INTEGER Set number of rows (Codablock-F/PDF417)\n", stdout);
204 fputs( " --scale=NUMBER Adjust size of X-dimension\n"
205 " --scalexdimdp=X[,R] Adjust size to X-dimension X at resolution R\n"
206 " --scmvv=INTEGER Prefix SCM with \"[)>\\R01\\Gvv\" (vv is INTEGER) (MaxiCode)\n"
207 " --secure=INTEGER Set error correction level (ECC)\n"
208 " --segN=ECI,DATA Set the ECI & data content for segment N, where N 1 to 9\n", stdout);
209 fputs( " --separator=INTEGER Set height of row separator bars (stacked symbologies)\n"
210 " --small Use small text (HRT)\n"
211 " --square Force Data Matrix symbols to be square\n"
212 " --structapp=I,C[,ID] Set Structured Append info (I index, C count)\n"
213 " -t, --types Display table of barcode types\n", stdout);
214 fputs( " --textgap=NUMBER Adjust gap between barcode and HRT in multiples of X-dim\n"
215 " --vers=INTEGER Set symbol version (size, check digits, other options)\n"
216 " -v, --version Display Zint version\n"
217 " --vwhitesp=INTEGER Set height of vertical whitespace in multiples of X-dim\n"
218 " -w, --whitesp=INTEGER Set width of horizontal whitespace in multiples of X-dim\n", stdout);
219 fputs( " --werror Convert all warnings into errors\n", stdout);
220 }
221
222 /* Display supported ECI codes */
223 static void show_eci(void) {
224 /* Breaking up strings so don't get too long (i.e. 500 or so) */
225 fputs(" 3: ISO/IEC 8859-1 - Latin alphabet No. 1 (default)\n"
226 " 4: ISO/IEC 8859-2 - Latin alphabet No. 2\n"
227 " 5: ISO/IEC 8859-3 - Latin alphabet No. 3\n"
228 " 6: ISO/IEC 8859-4 - Latin alphabet No. 4\n"
229 " 7: ISO/IEC 8859-5 - Latin/Cyrillic alphabet\n", stdout);
230 fputs(" 8: ISO/IEC 8859-6 - Latin/Arabic alphabet\n"
231 " 9: ISO/IEC 8859-7 - Latin/Greek alphabet\n"
232 " 10: ISO/IEC 8859-8 - Latin/Hebrew alphabet\n"
233 " 11: ISO/IEC 8859-9 - Latin alphabet No. 5 (Turkish)\n"
234 " 12: ISO/IEC 8859-10 - Latin alphabet No. 6 (Nordic)\n", stdout);
235 fputs(" 13: ISO/IEC 8859-11 - Latin/Thai alphabet\n"
236 " 15: ISO/IEC 8859-13 - Latin alphabet No. 7 (Baltic)\n"
237 " 16: ISO/IEC 8859-14 - Latin alphabet No. 8 (Celtic)\n"
238 " 17: ISO/IEC 8859-15 - Latin alphabet No. 9\n"
239 " 18: ISO/IEC 8859-16 - Latin alphabet No. 10\n", stdout);
240 fputs(" 20: Shift JIS (JIS X 0208 and JIS X 0201)\n"
241 " 21: Windows 1250 - Latin 2 (Central Europe)\n"
242 " 22: Windows 1251 - Cyrillic\n"
243 " 23: Windows 1252 - Latin 1\n"
244 " 24: Windows 1256 - Arabic\n", stdout);
245 fputs(" 25: UTF-16BE (High order byte first)\n"
246 " 26: UTF-8\n"
247 " 27: ASCII (ISO/IEC 646 IRV)\n"
248 " 28: Big5 (Taiwan) Chinese Character Set\n"
249 " 29: GB 2312 (PRC) Chinese Character Set\n", stdout);
250 fputs(" 30: Korean Character Set EUC-KR (KS X 1001:2002)\n"
251 " 31: GBK Chinese Character Set\n"
252 " 32: GB 18030 Chinese Character Set\n"
253 " 33: UTF-16LE (Low order byte first)\n"
254 " 34: UTF-32BE (High order bytes first)\n", stdout);
255 fputs(" 35: UTF-32LE (Low order bytes first)\n"
256 "170: ISO/IEC 646 Invariant (ASCII subset)\n"
257 "899: 8-bit binary data\n", stdout);
258 }
259
260 /* Verifies that a string (length <= 9) only uses digits. On success returns value in arg */
261 static int validate_int(const char source[], int len, int *p_val) {
262 int val = 0;
263 int i;
264 const int length = len == -1 ? (int) strlen(source) : len;
265
266 if (length > 9) { /* Prevent overflow */
267 return 0;
268 }
269 for (i = 0; i < length; i++) {
270 if (source[i] < '0' || source[i] > '9') {
271 return 0;
272 }
273 val *= 10;
274 val += source[i] - '0';
275 }
276 *p_val = val;
277
278 return 1;
279 }
280
281 /* Verifies that a string is a simplified form of floating point, max 7 significant decimal digits with
282 optional decimal point. On success returns val in arg. On failure sets `errbuf` */
283 static int validate_float(const char source[], const int allow_neg, float *p_val, char errbuf[64]) {
284 static const float fract_muls[7] = { 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f };
285 int val = 0;
286 int neg = 0;
287 const char *dot = strchr(source, '.');
288 int int_len;
289
290 if (*source == '+' || *source == '-') {
291 if (*source == '-') {
292 if (!allow_neg) {
293 strcpy(errbuf, "negative value not permitted");
294 return 0;
295 }
296 neg = 1;
297 }
298 source++;
299 }
300
301 int_len = dot ? (int) (dot - source) : (int) strlen(source);
302 if (int_len > 9) {
303 strcpy(errbuf, "integer part must be 7 digits maximum"); /* Say 7 not 9 to "manage expections" */
304 return 0;
305 }
306 if (int_len) {
307 int tmp_val;
308 if (!validate_int(source, int_len, &val)) {
309 strcpy(errbuf, "integer part must be digits only");
310 return 0;
311 }
312 for (int_len = 0, tmp_val = val; tmp_val; tmp_val /= 10, int_len++); /* log10(val) */
313 if (int_len > 7) {
314 strcpy(errbuf, "integer part must be 7 digits maximum");
315 return 0;
316 }
317 }
318 if (dot && *++dot) {
319 int val2, fract_len;
320 const char *e;
321 for (e = dot + strlen(dot) - 1; e > dot && *e == '0'; e--); /* Ignore trailing zeroes */
322 fract_len = (int) (e + 1 - dot);
323 if (fract_len) {
324 if (fract_len > 7) {
325 strcpy(errbuf, "fractional part must be 7 digits maximum");
326 return 0;
327 }
328 if (!validate_int(dot, fract_len, &val2)) {
329 strcpy(errbuf, "fractional part must be digits only");
330 return 0;
331 }
332 if (val2 && int_len + fract_len > 7) {
333 if (val) {
334 strcpy(errbuf, "7 significant digits maximum");
335 } else {
336 strcpy(errbuf, "fractional part must be 7 digits maximum");
337 }
338 return 0;
339 }
340 *p_val = val + val2 * fract_muls[fract_len - 1];
341 } else {
342 *p_val = (float) val;
343 }
344 } else {
345 *p_val = (float) val;
346 }
347 if (neg) {
348 *p_val = -*p_val;
349 }
350 return 1;
351 }
352
353 /* Converts upper case characters to lower case in a string source[] */
354 static void to_lower(char source[]) {
355 int i;
356 const int src_len = (int) strlen(source);
357
358 for (i = 0; i < src_len; i++) {
359 if ((source[i] >= 'A') && (source[i] <= 'Z')) {
360 source[i] |= 0x20;
361 }
362 }
363 }
364
365 /* Return symbology id if `barcode_name` a barcode name */
366 static int get_barcode_name(const char *barcode_name) {
367 /* Must be sorted for binary search to work */
368 static const struct { int symbology; const char *n; } names[] = {
369 { BARCODE_C25LOGIC, "2of5datalogic" }, /* Synonym */
370 { BARCODE_C25IATA, "2of5iata" }, /* Synonym */
371 { BARCODE_C25IND, "2of5ind" }, /* Synonym */
372 { BARCODE_C25IND, "2of5industrial" }, /* Synonym */
373 { BARCODE_C25INTER, "2of5inter" }, /* Synonym */
374 { BARCODE_C25INTER, "2of5interleaved" }, /* Synonym */
375 { BARCODE_C25LOGIC, "2of5logic" }, /* Synonym */
376 { BARCODE_C25STANDARD, "2of5matrix" }, /* Synonym */
377 { BARCODE_C25STANDARD, "2of5standard" }, /* Synonym */
378 { BARCODE_AUSPOST, "auspost" },
379 { BARCODE_AUSREDIRECT, "ausredirect" },
380 { BARCODE_AUSREPLY, "ausreply" },
381 { BARCODE_AUSROUTE, "ausroute" },
382 { BARCODE_AZRUNE, "azrune" },
383 { BARCODE_AZTEC, "aztec" },
384 { BARCODE_AZTEC, "azteccode" }, /* Synonym */
385 { BARCODE_AZRUNE, "aztecrune" }, /* Synonym */
386 { BARCODE_AZRUNE, "aztecrunes" }, /* Synonym */
387 { BARCODE_BC412, "bc412" },
388 { BARCODE_C25LOGIC, "c25datalogic" }, /* Synonym */
389 { BARCODE_C25IATA, "c25iata" },
390 { BARCODE_C25IND, "c25ind" },
391 { BARCODE_C25IND, "c25industrial" }, /* Synonym */
392 { BARCODE_C25INTER, "c25inter" },
393 { BARCODE_C25INTER, "c25interleaved" }, /* Synonym */
394 { BARCODE_C25LOGIC, "c25logic" },
395 { BARCODE_C25STANDARD, "c25matrix" },
396 { BARCODE_C25STANDARD, "c25standard" },
397 { BARCODE_CEPNET, "cepnet" },
398 { BARCODE_CHANNEL, "channel" },
399 { BARCODE_CHANNEL, "channelcode" }, /* Synonym */
400 { BARCODE_CODABAR, "codabar" },
401 { BARCODE_CODABLOCKF, "codablockf" },
402 { BARCODE_CODE11, "code11" },
403 { BARCODE_CODE128, "code128" },
404 { BARCODE_CODE128AB, "code128ab" },
405 { BARCODE_CODE128AB, "code128b" }, /* Synonym */
406 { BARCODE_CODE16K, "code16k" },
407 { BARCODE_C25LOGIC, "code2of5datalogic" }, /* Synonym */
408 { BARCODE_C25IATA, "code2of5iata" }, /* Synonym */
409 { BARCODE_C25IND, "code2of5ind" }, /* Synonym */
410 { BARCODE_C25IND, "code2of5industrial" }, /* Synonym */
411 { BARCODE_C25INTER, "code2of5inter" }, /* Synonym */
412 { BARCODE_C25INTER, "code2of5interleaved" }, /* Synonym */
413 { BARCODE_C25LOGIC, "code2of5logic" }, /* Synonym */
414 { BARCODE_C25STANDARD, "code2of5matrix" }, /* Synonym */
415 { BARCODE_C25STANDARD, "code2of5standard" }, /* Synonym */
416 { BARCODE_CODE32, "code32" },
417 { BARCODE_CODE39, "code39" },
418 { BARCODE_CODE49, "code49" },
419 { BARCODE_CODE93, "code93" },
420 { BARCODE_CODEONE, "codeone" },
421 { BARCODE_DAFT, "daft" },
422 { BARCODE_DBAR_EXP, "databarexp" }, /* Synonym */
423 { BARCODE_DBAR_EXP, "databarexpanded" }, /* Synonym */
424 { BARCODE_DBAR_EXP_CC, "databarexpandedcc" }, /* Synonym */
425 { BARCODE_DBAR_EXPSTK, "databarexpandedstacked" }, /* Synonym */
426 { BARCODE_DBAR_EXPSTK_CC, "databarexpandedstackedcc" }, /* Synonym */
427 { BARCODE_DBAR_EXPSTK, "databarexpandedstk" }, /* Synonym */
428 { BARCODE_DBAR_EXPSTK_CC, "databarexpandedstkcc" }, /* Synonym */
429 { BARCODE_DBAR_EXP_CC, "databarexpcc" }, /* Synonym */
430 { BARCODE_DBAR_EXPSTK, "databarexpstk" }, /* Synonym */
431 { BARCODE_DBAR_EXPSTK_CC, "databarexpstkcc" }, /* Synonym */
432 { BARCODE_DBAR_LTD, "databarlimited" }, /* Synonym */
433 { BARCODE_DBAR_LTD_CC, "databarlimitedcc" }, /* Synonym */
434 { BARCODE_DBAR_LTD, "databarltd" }, /* Synonym */
435 { BARCODE_DBAR_LTD_CC, "databarltdcc" }, /* Synonym */
436 { BARCODE_DBAR_OMN, "databaromn" }, /* Synonym */
437 { BARCODE_DBAR_OMN_CC, "databaromncc" }, /* Synonym */
438 { BARCODE_DBAR_OMN, "databaromni" }, /* Synonym */
439 { BARCODE_DBAR_OMN_CC, "databaromnicc" }, /* Synonym */
440 { BARCODE_DBAR_OMNSTK, "databaromnstk" }, /* Synonym */
441 { BARCODE_DBAR_OMNSTK_CC, "databaromnstkcc" }, /* Synonym */
442 { BARCODE_DBAR_STK, "databarstacked" }, /* Synonym */
443 { BARCODE_DBAR_STK_CC, "databarstackedcc" }, /* Synonym */
444 { BARCODE_DBAR_OMNSTK, "databarstackedomn" }, /* Synonym */
445 { BARCODE_DBAR_OMNSTK_CC, "databarstackedomncc" }, /* Synonym */
446 { BARCODE_DBAR_OMNSTK, "databarstackedomni" }, /* Synonym */
447 { BARCODE_DBAR_OMNSTK_CC, "databarstackedomnicc" }, /* Synonym */
448 { BARCODE_DBAR_STK, "databarstk" }, /* Synonym */
449 { BARCODE_DBAR_STK_CC, "databarstkcc" }, /* Synonym */
450 { BARCODE_DATAMATRIX, "datamatrix" },
451 { BARCODE_DBAR_EXP, "dbarexp" },
452 { BARCODE_DBAR_EXP, "dbarexpanded" }, /* Synonym */
453 { BARCODE_DBAR_EXP_CC, "dbarexpandedcc" }, /* Synonym */
454 { BARCODE_DBAR_EXPSTK, "dbarexpandedstacked" }, /* Synonym */
455 { BARCODE_DBAR_EXPSTK_CC, "dbarexpandedstackedcc" }, /* Synonym */
456 { BARCODE_DBAR_EXPSTK, "dbarexpandedstk" }, /* Synonym */
457 { BARCODE_DBAR_EXPSTK_CC, "dbarexpandedstkcc" }, /* Synonym */
458 { BARCODE_DBAR_EXP_CC, "dbarexpcc" },
459 { BARCODE_DBAR_EXPSTK, "dbarexpstk" },
460 { BARCODE_DBAR_EXPSTK_CC, "dbarexpstkcc" },
461 { BARCODE_DBAR_LTD, "dbarlimited" }, /* Synonym */
462 { BARCODE_DBAR_LTD_CC, "dbarlimitedcc" }, /* Synonym */
463 { BARCODE_DBAR_LTD, "dbarltd" },
464 { BARCODE_DBAR_LTD_CC, "dbarltdcc" },
465 { BARCODE_DBAR_OMN, "dbaromn" },
466 { BARCODE_DBAR_OMN_CC, "dbaromncc" },
467 { BARCODE_DBAR_OMN, "dbaromni" }, /* Synonym */
468 { BARCODE_DBAR_OMN_CC, "dbaromnicc" }, /* Synonym */
469 { BARCODE_DBAR_OMNSTK, "dbaromnstk" },
470 { BARCODE_DBAR_OMNSTK_CC, "dbaromnstkcc" },
471 { BARCODE_DBAR_STK, "dbarstacked" }, /* Synonym */
472 { BARCODE_DBAR_STK_CC, "dbarstackedcc" }, /* Synonym */
473 { BARCODE_DBAR_OMNSTK, "dbarstackedomn" }, /* Synonym */
474 { BARCODE_DBAR_OMNSTK_CC, "dbarstackedomncc" }, /* Synonym */
475 { BARCODE_DBAR_OMNSTK, "dbarstackedomni" }, /* Synonym */
476 { BARCODE_DBAR_OMNSTK_CC, "dbarstackedomnicc" }, /* Synonym */
477 { BARCODE_DBAR_STK, "dbarstk" },
478 { BARCODE_DBAR_STK_CC, "dbarstkcc" },
479 { BARCODE_DOTCODE, "dotcode" },
480 { BARCODE_DPD, "dpd" },
481 { BARCODE_DPIDENT, "dpident" },
482 { BARCODE_DPLEIT, "dpleit" },
483 { BARCODE_DXFILMEDGE, "dxfilmedge" },
484 { BARCODE_EANX, "ean" }, /* Synonym */
485 { BARCODE_GS1_128, "ean128" }, /* Synonym */
486 { BARCODE_GS1_128_CC, "ean128cc" }, /* Synonym */
487 { BARCODE_EAN14, "ean14" },
488 { BARCODE_EANX_CC, "eancc" }, /* Synonym */
489 { BARCODE_EANX_CHK, "eanchk" }, /* Synonym */
490 { BARCODE_EANX, "eanx" },
491 { BARCODE_EANX_CC, "eanxcc" },
492 { BARCODE_EANX_CHK, "eanxchk" },
493 { BARCODE_EXCODE39, "excode39" },
494 { BARCODE_EXCODE39, "extendedcode39" }, /* Synonym */
495 { BARCODE_FIM, "fim" },
496 { BARCODE_FLAT, "flat" },
497 { BARCODE_GRIDMATRIX, "gridmatrix" },
498 { BARCODE_GS1_128, "gs1128" },
499 { BARCODE_GS1_128_CC, "gs1128cc" },
500 { BARCODE_HANXIN, "hanxin" },
501 { BARCODE_HIBC_128, "hibc128" },
502 { BARCODE_HIBC_39, "hibc39" },
503 { BARCODE_HIBC_AZTEC, "hibcaztec" },
504 { BARCODE_HIBC_BLOCKF, "hibcblockf" },
505 { BARCODE_HIBC_BLOCKF, "hibccodablockf" }, /* Synonym */
506 { BARCODE_HIBC_128, "hibccode128" }, /* Synonym */
507 { BARCODE_HIBC_39, "hibccode39" }, /* Synonym */
508 { BARCODE_HIBC_DM, "hibcdatamatrix" }, /* Synonym */
509 { BARCODE_HIBC_DM, "hibcdm" },
510 { BARCODE_HIBC_MICPDF, "hibcmicpdf" },
511 { BARCODE_HIBC_MICPDF, "hibcmicropdf" }, /* Synonym */
512 { BARCODE_HIBC_MICPDF, "hibcmicropdf417" }, /* Synonym */
513 { BARCODE_HIBC_PDF, "hibcpdf" },
514 { BARCODE_HIBC_PDF, "hibcpdf417" }, /* Synonym */
515 { BARCODE_HIBC_QR, "hibcqr" },
516 { BARCODE_HIBC_QR, "hibcqrcode" }, /* Synonym */
517 { BARCODE_C25IATA, "iata2of5" }, /* Synonym */
518 { BARCODE_C25IATA, "iatacode2of5" }, /* Synonym */
519 { BARCODE_C25IND, "industrial2of5" }, /* Synonym */
520 { BARCODE_C25IND, "industrialcode2of5" }, /* Synonym */
521 { BARCODE_C25INTER, "interleaved2of5" }, /* Synonym */
522 { BARCODE_C25INTER, "interleavedcode2of5" }, /* Synonym */
523 { BARCODE_ISBNX, "isbn" }, /* Synonym */
524 { BARCODE_ISBNX, "isbnx" },
525 { BARCODE_ITF14, "itf14" },
526 { BARCODE_JAPANPOST, "japanpost" },
527 { BARCODE_KIX, "kix" },
528 { BARCODE_KOREAPOST, "koreapost" },
529 { BARCODE_LOGMARS, "logmars" },
530 { BARCODE_MAILMARK_4S, "mailmark" }, /* Synonym */
531 { BARCODE_MAILMARK_2D, "mailmark2d" },
532 { BARCODE_MAILMARK_4S, "mailmark4s" },
533 { BARCODE_MAILMARK_4S, "mailmark4state" }, /* Synonym */
534 { BARCODE_MAXICODE, "maxicode" },
535 { BARCODE_MICROPDF417, "micropdf417" },
536 { BARCODE_MICROQR, "microqr" },
537 { BARCODE_MICROQR, "microqrcode" }, /* Synonym */
538 { BARCODE_MSI_PLESSEY, "msi" }, /* Synonym */
539 { BARCODE_MSI_PLESSEY, "msiplessey" },
540 { BARCODE_NVE18, "nve18" },
541 { BARCODE_USPS_IMAIL, "onecode" }, /* Synonym */
542 { BARCODE_PDF417, "pdf417" },
543 { BARCODE_PDF417COMP, "pdf417comp" },
544 { BARCODE_PDF417COMP, "pdf417trunc" }, /* Synonym */
545 { BARCODE_PHARMA, "pharma" },
546 { BARCODE_PHARMA_TWO, "pharmatwo" },
547 { BARCODE_PLANET, "planet" },
548 { BARCODE_PLESSEY, "plessey" },
549 { BARCODE_POSTNET, "postnet" },
550 { BARCODE_PZN, "pzn" },
551 { BARCODE_QRCODE, "qr" }, /* Synonym */
552 { BARCODE_QRCODE, "qrcode" },
553 { BARCODE_RM4SCC, "rm4scc" },
554 { BARCODE_RMQR, "rmqr" },
555 { BARCODE_DBAR_OMN, "rss14" }, /* Synonym */
556 { BARCODE_DBAR_OMN_CC, "rss14cc" }, /* Synonym */
557 { BARCODE_DBAR_OMNSTK_CC, "rss14omnicc" }, /* Synonym */
558 { BARCODE_DBAR_STK, "rss14stack" }, /* Synonym */
559 { BARCODE_DBAR_STK_CC, "rss14stackcc" }, /* Synonym */
560 { BARCODE_DBAR_OMNSTK, "rss14stackomni" }, /* Synonym */
561 { BARCODE_DBAR_EXP, "rssexp" }, /* Synonym */
562 { BARCODE_DBAR_EXP_CC, "rssexpcc" }, /* Synonym */
563 { BARCODE_DBAR_EXPSTK, "rssexpstack" }, /* Synonym */
564 { BARCODE_DBAR_EXPSTK_CC, "rssexpstackcc" }, /* Synonym */
565 { BARCODE_DBAR_LTD, "rssltd" }, /* Synonym */
566 { BARCODE_DBAR_LTD_CC, "rssltdcc" }, /* Synonym */
567 { BARCODE_C25STANDARD, "standardcode2of5" }, /* Synonym */
568 { BARCODE_TELEPEN, "telepen" },
569 { BARCODE_TELEPEN_NUM, "telepennum" },
570 { BARCODE_ULTRA, "ultra" },
571 { BARCODE_ULTRA, "ultracode" }, /* Synonym */
572 { BARCODE_UPCA, "upca" },
573 { BARCODE_UPCA_CC, "upcacc" },
574 { BARCODE_UPCA_CHK, "upcachk" },
575 { BARCODE_UPCE, "upce" },
576 { BARCODE_UPCE_CC, "upcecc" },
577 { BARCODE_UPCE_CHK, "upcechk" },
578 { BARCODE_UPNQR, "upnqr" },
579 { BARCODE_UPNQR, "upnqrcode" }, /* Synonym */
580 { BARCODE_UPU_S10, "upus10" },
581 { BARCODE_USPS_IMAIL, "uspsimail" },
582 { BARCODE_VIN, "vin" },
583 };
584 int s = 0, e = ARRAY_SIZE(names) - 1;
585
586 char n[30] = {0};
587 int i, j, length;
588
589 /* Ignore case and any "BARCODE" prefix */
590 strncpy(n, barcode_name, 29);
591 to_lower(n);
592 length = (int) strlen(n);
593 if (strncmp(n, "barcode", 7) == 0) {
594 memmove(n, n + 7, length - 7 + 1); /* Include NUL char */
595 length = (int) strlen(n);
596 }
597
598 /* Ignore any non-alphanumeric characters */
599 for (i = 0, j = 0; i < length; i++) {
600 if ((n[i] >= 'a' && n[i] <= 'z') || (n[i] >= '0' && n[i] <= '9')) {
601 n[j++] = n[i];
602 }
603 }
604 if (j == 0) {
605 return 0;
606 }
607 n[j] = '\0';
608
609 while (s <= e) {
610 const int m = (s + e) / 2;
611 const int cmp = strcmp(names[m].n, n);
612 if (cmp < 0) {
613 s = m + 1;
614 } else if (cmp > 0) {
615 e = m - 1;
616 } else {
617 return names[m].symbology;
618 }
619 }
620
621 return 0;
622 }
623
624 /* Whether `filetype` supported by Zint. Sets `png_refused` if `no_png` and PNG requested */
625 static int supported_filetype(const char *filetype, const int no_png, int *png_refused) {
626 static const char filetypes[][4] = {
627 "bmp", "emf", "eps", "gif", "pcx", "png", "svg", "tif", "txt",
628 };
629 char lc_filetype[4] = {0};
630 int i;
631
632 if (png_refused) {
633 *png_refused = 0;
634 }
635 strncpy(lc_filetype, filetype, 3);
636 to_lower(lc_filetype);
637
638 if (no_png && strcmp(lc_filetype, "png") == 0) {
639 if (png_refused) {
640 *png_refused = 1;
641 }
642 return 0;
643 }
644
645 for (i = 0; i < ARRAY_SIZE(filetypes); i++) {
646 if (strcmp(lc_filetype, filetypes[i]) == 0) {
647 return 1;
648 }
649 }
650 return 0;
651 }
652
653 /* Get file extension, excluding those of 4 or more letters */
654 static char *get_extension(const char *file) {
655 char *dot;
656
657 dot = strrchr(file, '.');
658 if (dot && strlen(file) - (dot - file) <= 4) { /* Only recognize up to 3 letter extensions */
659 return dot + 1;
660 }
661 return NULL;
662 }
663
664 /* Set extension of `file` to `filetype`, replacing existing extension if any.
665 * Does nothing if file already has `filetype` extension */
666 static void set_extension(char *file, const char *filetype) {
667 char lc_filetype[4] = {0};
668 char *extension;
669 char lc_extension[4];
670
671 strncpy(lc_filetype, filetype, 3);
672 to_lower(lc_filetype);
673
674 extension = get_extension(file);
675 if (extension) {
676 strcpy(lc_extension, extension);
677 to_lower(lc_extension);
678 if (strcmp(lc_filetype, lc_extension) == 0) {
679 return;
680 }
681 *(extension - 1) = '\0'; /* Cut off at dot */
682 }
683 if (strlen(file) > 251) {
684 file[251] = '\0';
685 }
686 strcat(file, ".");
687 strncat(file, filetype, 3);
688 }
689
690 /* Whether `filetype` is raster type */
691 static int is_raster(const char *filetype, const int no_png) {
692 static const char raster_filetypes[][4] = {
693 "bmp", "gif", "pcx", "png", "tif",
694 };
695 int i;
696 char lc_filetype[4] = {0};
697
698 if (filetype == NULL) {
699 return 0;
700 }
701 strcpy(lc_filetype, filetype);
702 to_lower(lc_filetype);
703
704 if (no_png && strcmp(lc_filetype, "png") == 0) {
705 return 0;
706 }
707
708 for (i = 0; i < ARRAY_SIZE(raster_filetypes); i++) {
709 if (strcmp(lc_filetype, raster_filetypes[i]) == 0) {
710 return 1;
711 }
712 }
713 return 0;
714 }
715
716 /* Helper for `validate_scalexdimdp()` to search for units, returning -2 on error, -1 if not found, else index */
717 static int validate_units(char *buf, const char units[][5], int units_size) {
718 int i;
719 char *unit;
720
721 to_lower(buf);
722 for (i = 0; i < units_size; i++) {
723 if ((unit = strstr(buf, units[i])) != NULL) {
724 if (strlen(units[i]) != strlen(unit)) {
725 return -2;
726 }
727 *unit = '\0';
728 break;
729 }
730 }
731 if (i == units_size) {
732 i = -1;
733 }
734 return i;
735 }
736
737 /* Parse and validate argument "xdim[,resolution]" to "--scalexdimdp" */
738 static int validate_scalexdimdp(const char *arg, float *p_x_dim_mm, float *p_dpmm) {
739 static const char x_units[][5] = { "mm", "in" };
740 static const char r_units[][5] = { "dpmm", "dpi" };
741 char x_buf[7 + 1 + 4 + 1] = {0}; /* Allow for 7 digits + dot + 4-char unit + NUL */
742 char r_buf[7 + 1 + 4 + 1] = {0}; /* As above */
743 int units_i; /* For `validate_units()` */
744 char errbuf[64]; /* For `validate_float()` */
745 const char *comma = strchr(arg, ',');
746 if (comma) {
747 if (comma == arg || comma - arg >= ARRAY_SIZE(x_buf)) {
748 fprintf(stderr, "Error 174: scalexdimdp X-dim too %s\n", comma == arg ? "short" : "long");
749 return 0;
750 }
751 strncpy(x_buf, arg, comma - arg);
752 comma++;
753 if (!*comma || strlen(comma) >= ARRAY_SIZE(r_buf)) {
754 fprintf(stderr, "Error 175: scalexdimdp resolution too %s\n", !*comma ? "short" : "long");
755 return 0;
756 }
757 strcpy(r_buf, comma);
758 } else {
759 if (!*arg || strlen(arg) >= ARRAY_SIZE(x_buf)) {
760 fprintf(stderr, "Error 176: scalexdimdp X-dim too %s\n", !*arg ? "short" : "long");
761 return 0;
762 }
763 strcpy(x_buf, arg);
764 }
765 if ((units_i = validate_units(x_buf, x_units, ARRAY_SIZE(x_units))) == -2) {
766 fprintf(stderr, "Error 177: scalexdimdp X-dim units must occur at end\n");
767 return 0;
768 }
769 if (!validate_float(x_buf, 0 /*allow_neg*/, p_x_dim_mm, errbuf)) {
770 fprintf(stderr, "Error 178: scalexdimdp X-dim invalid floating point (%s)\n", errbuf);
771 return 0;
772 }
773 if (units_i > 0) { /* Ignore mm */
774 *p_x_dim_mm /= 25.4f /*in*/;
775 }
776 *p_dpmm = 0.0f;
777 if (comma) {
778 if ((units_i = validate_units(r_buf, r_units, ARRAY_SIZE(r_units))) == -2) {
779 fprintf(stderr, "Error 179: scalexdimdp resolution units must occur at end\n");
780 return 0;
781 }
782 if (!validate_float(r_buf, 0 /*allow_neg*/, p_dpmm, errbuf)) {
783 fprintf(stderr, "Error 180: scalexdimdp resolution invalid floating point (%s)\n", errbuf);
784 return 0;
785 }
786 if (units_i > 0) { /* Ignore dpmm */
787 *p_dpmm /= 25.4f /*dpi*/;
788 }
789 }
790 if (*p_dpmm == 0.0f) {
791 *p_dpmm = 12.0f; /* 300 dpi */
792 }
793
794 return 1;
795 }
796
797 /* Parse and validate Structured Append argument "index,count[,ID]" to "--structapp" */
798 static int validate_structapp(const char *arg, struct zint_structapp *structapp) {
799 char index[10] = {0}, count[10] = {0};
800 const char *comma = strchr(arg, ',');
801 const char *comma2;
802 if (!comma) {
803 fprintf(stderr, "Error 155: Invalid Structured Append argument, expect \"index,count[,ID]\"\n");
804 return 0;
805 }
806 if (comma == arg || comma - arg > 9) {
807 fprintf(stderr, "Error 156: Structured Append index too %s\n", comma == arg ? "short" : "long");
808 return 0;
809 }
810 strncpy(index, arg, comma - arg);
811 comma++;
812 comma2 = strchr(comma, ',');
813 if (comma2) {
814 if (comma2 == comma || comma2 - comma > 9) {
815 fprintf(stderr, "Error 157: Structured Append count too %s\n", comma2 == comma ? "short" : "long");
816 return 0;
817 }
818 strncpy(count, comma, comma2 - comma);
819 comma2++;
820 if (!*comma2 || strlen(comma2) > 32) {
821 fprintf(stderr, "Error 158: Structured Append ID too %s\n", !*comma2 ? "short" : "long");
822 return 0;
823 }
824 strncpy(structapp->id, comma2, 32);
825 } else {
826 if (!*comma || strlen(comma) > 9) {
827 fprintf(stderr, "Error 159: Structured Append count too %s\n", !*comma ? "short" : "long");
828 return 0;
829 }
830 strcpy(count, comma);
831 }
832 if (!validate_int(index, -1 /*len*/, &structapp->index)) {
833 fprintf(stderr, "Error 160: Invalid Structured Append index (digits only)\n");
834 return 0;
835 }
836 if (!validate_int(count, -1 /*len*/, &structapp->count)) {
837 fprintf(stderr, "Error 161: Invalid Structured Append count (digits only)\n");
838 return 0;
839 }
840 if (structapp->count < 2) {
841 fprintf(stderr, "Error 162: Invalid Structured Append count '%d', must be greater than or equal to 2\n",
842 structapp->count);
843 return 0;
844 }
845 if (structapp->index < 1 || structapp->index > structapp->count) {
846 fprintf(stderr, "Error 163: Structured Append index '%d' out of range (1 to count '%d')\n", structapp->index,
847 structapp->count);
848 return 0;
849 }
850
851 return 1;
852 }
853
854 /* Parse and validate the segment argument "ECI,DATA" to "--segN" */
855 static int validate_seg(const char *arg, const int N, struct zint_seg segs[10]) {
856 char eci[10] = {0};
857 const char *comma = strchr(arg, ',');
858 if (!comma || comma == arg || comma - arg > 9 || *(comma + 1) == '\0') {
859 fprintf(stderr, "Error 166: Invalid segment argument, expect \"ECI,DATA\"\n");
860 return 0;
861 }
862 strncpy(eci, arg, comma - arg);
863 if (!validate_int(eci, -1 /*len*/, &segs[N].eci)) {
864 fprintf(stderr, "Error 167: Invalid segment ECI (digits only)\n");
865 return 0;
866 }
867 if (segs[N].eci > 999999) {
868 fprintf(stderr, "Error 168: Segment ECI code '%d' out of range (0 to 999999)\n", segs[N].eci);
869 return 0;
870 }
871 segs[N].length = (int) strlen(comma + 1);
872 segs[N].source = (unsigned char *) (comma + 1);
873 return 1;
874 }
875
876 #ifdef _WIN32
877 static FILE *win_fopen(const char *filename, const char *mode); /* Forward ref */
878 #endif
879
880 /* Batch mode - output symbol for each line of text in `filename` */
881 static int batch_process(struct zint_symbol *symbol, const char *filename, const int mirror_mode,
882 const char *filetype, const int output_given, const int rotate_angle) {
883 FILE *file;
884 unsigned char buffer[ZINT_MAX_DATA_LEN] = {0}; /* Maximum HanXin input */
885 unsigned char character = 0;
886 int buf_posn = 0, error_number = 0, warn_number = 0, line_count = 1;
887 char output_file[256];
888 char number[12], reverse_number[12];
889 int inpos, local_line_count;
890 char format_string[256], reversed_string[256], format_char;
891 int format_len, i, o, mirror_start_o = 0;
892 char adjusted[2] = {0};
893 const int from_stdin = strcmp(filename, "-") == 0; /* Suppress clang-19 warning clang-analyzer-unix.Stream */
894
895 if (mirror_mode) {
896 /* Use directory if any from outfile */
897 if (output_given && symbol->outfile[0]) {
898 #ifndef _WIN32
899 const char *dir = strrchr(symbol->outfile, '/');
900 #else
901 const char *dir = strrchr(symbol->outfile, '\\');
902 if (!dir) {
903 dir = strrchr(symbol->outfile, '/');
904 }
905 #endif
906 if (dir) {
907 mirror_start_o = (int) (dir + 1 - symbol->outfile);
908 if (mirror_start_o > 221) { /* Insist on leaving at least ~30 chars for filename */
909 fprintf(stderr, "Warning 188: directory for mirrored batch output too long (greater than 220),"
910 " ignoring\n");
911 fflush(stderr);
912 warn_number = ZINT_WARN_INVALID_OPTION; /* TODO: maybe new warning ZINT_WARN_INVALID_INPUT? */
913 mirror_start_o = 0;
914 } else {
915 memcpy(output_file, symbol->outfile, mirror_start_o);
916 }
917 }
918 }
919 } else {
920 if (symbol->outfile[0] == '\0' || !output_given) {
921 strcpy(format_string, "~~~~~.");
922 strncat(format_string, filetype, 3);
923 } else {
924 strcpy(format_string, symbol->outfile);
925 set_extension(format_string, filetype);
926 }
927 }
928
929 if (from_stdin) {
930 file = stdin;
931 } else {
932 #ifdef _WIN32
933 file = win_fopen(filename, "rb");
934 #else
935 file = fopen(filename, "rb");
936 #endif
937 if (!file) {
938 fprintf(stderr, "Error 102: Unable to read input file '%s' (%d: %s)\n", filename, errno, strerror(errno));
939 fflush(stderr);
940 return ZINT_ERROR_INVALID_DATA;
941 }
942 }
943
944 do {
945 int intChar;
946 intChar = fgetc(file);
947 if (intChar == EOF) {
948 break;
949 }
950 character = (unsigned char) intChar;
951 if (character == '\n') {
952 if (buf_posn > 0 && buffer[buf_posn - 1] == '\r') {
953 /* CR+LF - assume Windows formatting and remove CR */
954 buf_posn--;
955 buffer[buf_posn] = '\0';
956 }
957
958 if (mirror_mode == 0) {
959 inpos = 0;
960 local_line_count = line_count;
961 memset(number, 0, sizeof(number));
962 memset(reverse_number, 0, sizeof(reverse_number));
963 memset(reversed_string, 0, sizeof(reversed_string));
964 memset(output_file, 0, sizeof(output_file));
965 do {
966 number[inpos] = (local_line_count % 10) + '0';
967 local_line_count /= 10;
968 inpos++;
969 } while (local_line_count > 0);
970 number[inpos] = '\0';
971
972 for (i = 0; i < inpos; i++) {
973 reverse_number[i] = number[inpos - i - 1];
974 }
975
976 format_len = (int) strlen(format_string);
977 for (i = format_len; i > 0; i--) {
978 format_char = format_string[i - 1];
979
980 switch (format_char) {
981 case '#':
982 if (inpos > 0) {
983 adjusted[0] = reverse_number[inpos - 1];
984 inpos--;
985 } else {
986 adjusted[0] = ' ';
987 }
988 break;
989 case '~':
990 if (inpos > 0) {
991 adjusted[0] = reverse_number[inpos - 1];
992 inpos--;
993 } else {
994 adjusted[0] = '0';
995 }
996 break;
997 case '@':
998 if (inpos > 0) {
999 adjusted[0] = reverse_number[inpos - 1];
1000 inpos--;
1001 } else {
1002 #ifndef _WIN32
1003 adjusted[0] = '*';
1004 #else
1005 adjusted[0] = '+';
1006 #endif
1007 }
1008 break;
1009 default:
1010 adjusted[0] = format_string[i - 1];
1011 break;
1012 }
1013 strcat(reversed_string, adjusted);
1014 }
1015
1016 for (i = 0; i < format_len; i++) {
1017 output_file[i] = reversed_string[format_len - i - 1];
1018 }
1019 } else {
1020 /* Name the output file from the data being processed */
1021 i = 0;
1022 o = mirror_start_o;
1023 do {
1024 if (buffer[i] < 0x20) {
1025 output_file[o] = '_';
1026 } else {
1027 switch (buffer[i]) {
1028 case '!':
1029 case '"':
1030 case '*':
1031 case '/':
1032 case ':':
1033 case '<':
1034 case '>':
1035 case '?':
1036 case '\\':
1037 case '|':
1038 case 0x7f: /* DEL */
1039 output_file[o] = '_';
1040 break;
1041 default:
1042 output_file[o] = buffer[i];
1043 break;
1044 }
1045 }
1046
1047 /* Skip escape characters */
1048 if ((buffer[i] == '\\') && (symbol->input_mode & ESCAPE_MODE)) {
1049 i++;
1050 if (buffer[i] == 'x') {
1051 i += 2;
1052 } else if (buffer[i] == 'd' || buffer[i] == 'o') {
1053 i += 3;
1054 } else if (buffer[i] == 'u') {
1055 i += 4;
1056 } else if (buffer[i] == 'U') {
1057 i += 6;
1058 }
1059 }
1060 i++;
1061 o++;
1062 } while (i < buf_posn && o < 251);
1063
1064 /* Add file extension */
1065 output_file[o] = '.';
1066 output_file[o + 1] = '\0';
1067
1068 strncat(output_file, filetype, 3);
1069 }
1070
1071 strcpy(symbol->outfile, output_file);
1072 warn_number = ZBarcode_Encode_and_Print(symbol, buffer, buf_posn, rotate_angle);
1073 if (warn_number != 0) {
1074 fprintf(stderr, "On line %d: %s\n", line_count, symbol->errtxt);
1075 fflush(stderr);
1076 if (warn_number >= ZINT_ERROR) {
1077 error_number = warn_number;
1078 }
1079 }
1080 ZBarcode_Clear(symbol);
1081 memset(buffer, 0, sizeof(buffer));
1082 buf_posn = 0;
1083 line_count++;
1084 } else {
1085 buffer[buf_posn] = character;
1086 buf_posn++;
1087 }
1088 if (buf_posn >= (int) sizeof(buffer)) {
1089 fprintf(stderr, "On line %d: Error 103: Input data too long\n", line_count);
1090 fflush(stderr);
1091 do {
1092 if ((intChar = fgetc(file)) == EOF) {
1093 break;
1094 }
1095 character = (unsigned char) intChar;
1096 } while ((!feof(file)) && (character != '\n'));
1097 }
1098 } while ((!feof(file)) && (line_count < 2000000000));
1099
1100 if (character != '\n') {
1101 fprintf(stderr, "Warning 104: No newline at end of file\n");
1102 fflush(stderr);
1103 warn_number = ZINT_WARN_INVALID_OPTION; /* TODO: maybe new warning e.g. ZINT_WARN_INVALID_INPUT? */
1104 }
1105
1106 if (!from_stdin) {
1107 if (fclose(file) != 0) {
1108 fprintf(stderr, "Warning 196: Failure on closing input file '%s' (%d: %s)\n", filename, errno,
1109 strerror(errno));
1110 fflush(stderr);
1111 warn_number = ZINT_WARN_INVALID_OPTION; /* TODO: maybe new warning e.g. ZINT_WARN_INVALID_INPUT? */
1112 }
1113 }
1114 if (error_number == 0) {
1115 error_number = warn_number;
1116 }
1117 return error_number;
1118 }
1119
1120 /* Stuff to convert args on Windows command line to UTF-8 */
1121 #ifdef _WIN32
1122 #include <windows.h>
1123
1124 #ifndef WC_ERR_INVALID_CHARS
1125 #define WC_ERR_INVALID_CHARS 0x00000080
1126 #endif
1127
1128 static int win_argc = 0;
1129 static char **win_argv = NULL;
1130
1131 /* Free Windows args */
1132 static void win_free_args(void) {
1133 int i;
1134 if (!win_argv) {
1135 return;
1136 }
1137 for (i = 0; i < win_argc; i++) {
1138 if (!win_argv[i]) {
1139 break;
1140 }
1141 free(win_argv[i]);
1142 win_argv[i] = NULL;
1143 }
1144 free(win_argv);
1145 win_argv = NULL;
1146 }
1147
1148 /* Using Wine version of `CommandLineToArgvW()` (slightly adapted) to avoid loading shell32.dll - see
1149 https://source.winehq.org/git/wine.git/blob/5a66eab72:/dlls/shcore/main.c#l264
1150 and https://news.ycombinator.com/item?id=18596841 */
1151 /*
1152 * Copyright 2002 Jon Griffiths
1153 * Copyright 2016 Sebastian Lackner
1154 *
1155 * This library is free software; you can redistribute it and/or
1156 * modify it under the terms of the GNU Lesser General Public
1157 * License as published by the Free Software Foundation; either
1158 * version 2.1 of the License, or (at your option) any later version.
1159 *
1160 * This library is distributed in the hope that it will be useful,
1161 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1162 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1163 * Lesser General Public License for more details.
1164 *
1165 * You should have received a copy of the GNU Lesser General Public
1166 * License along with this library; if not, write to the Free Software
1167 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
1168 */
1169 static WCHAR **win_CommandLineToArgvW(const WCHAR *cmdline, int *numargs) {
1170 int qcount, bcount;
1171 const WCHAR *s;
1172 WCHAR **argv;
1173 DWORD argc;
1174 WCHAR *d;
1175
1176 /* Adapted to require non-empty command line */
1177 if (*cmdline == 0) {
1178 return NULL;
1179 }
1180
1181 /* --- First count the arguments */
1182 argc = 1;
1183 s = cmdline;
1184 /* The first argument, the executable path, follows special rules */
1185 if (*s == '"') {
1186 /* The executable path ends at the next quote, no matter what */
1187 s++;
1188 while (*s)
1189 if (*s++ == '"')
1190 break;
1191 } else {
1192 /* The executable path ends at the next space, no matter what */
1193 while (*s && *s != ' ' && *s != '\t')
1194 s++;
1195 }
1196 /* Skip to the first argument, if any */
1197 while (*s == ' ' || *s == '\t')
1198 s++;
1199 if (*s)
1200 argc++;
1201
1202 /* Analyze the remaining arguments */
1203 qcount = bcount = 0;
1204 while (*s) {
1205 if ((*s == ' ' || *s == '\t') && qcount == 0) {
1206 /* Skip to the next argument and count it if any */
1207 while (*s == ' ' || *s == '\t')
1208 s++;
1209 if (*s)
1210 argc++;
1211 bcount = 0;
1212 } else if (*s == '\\') {
1213 /* '\', count them */
1214 bcount++;
1215 s++;
1216 } else if (*s == '"') {
1217 /* '"' */
1218 if ((bcount & 1) == 0)
1219 qcount++; /* Unescaped '"' */
1220 s++;
1221 bcount = 0;
1222 /* Consecutive quotes, see comment in copying code below */
1223 while (*s == '"') {
1224 qcount++;
1225 s++;
1226 }
1227 qcount = qcount % 3;
1228 if (qcount == 2)
1229 qcount = 0;
1230 } else {
1231 /* A regular character */
1232 bcount = 0;
1233 s++;
1234 }
1235 }
1236
1237 /* Allocate in a single lump, the string array, and the strings that go
1238 * with it. This way the caller can make a single LocalFree() call to free
1239 * both, as per MSDN.
1240 */
1241 argv = LocalAlloc(LMEM_FIXED, (argc + 1) * sizeof(WCHAR *) + (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
1242 if (!argv)
1243 return NULL;
1244
1245 /* --- Then split and copy the arguments */
1246 argv[0] = d = lstrcpyW((WCHAR *)(argv + argc + 1), cmdline);
1247 argc = 1;
1248 /* The first argument, the executable path, follows special rules */
1249 if (*d == '"') {
1250 /* The executable path ends at the next quote, no matter what */
1251 s = d + 1;
1252 while (*s) {
1253 if (*s == '"') {
1254 s++;
1255 break;
1256 }
1257 *d++ = *s++;
1258 }
1259 } else {
1260 /* The executable path ends at the next space, no matter what */
1261 while (*d && *d != ' ' && *d != '\t')
1262 d++;
1263 s = d;
1264 if (*s)
1265 s++;
1266 }
1267 /* Close the executable path */
1268 *d++ = 0;
1269 /* Skip to the first argument and initialize it if any */
1270 while (*s == ' ' || *s == '\t')
1271 s++;
1272 if (!*s) {
1273 /* There are no parameters so we are all done */
1274 argv[argc] = NULL;
1275 *numargs = argc;
1276 return argv;
1277 }
1278
1279 /* Split and copy the remaining arguments */
1280 argv[argc++] = d;
1281 qcount = bcount = 0;
1282 while (*s) {
1283 if ((*s == ' ' || *s == '\t') && qcount == 0) {
1284 /* Close the argument */
1285 *d++ = 0;
1286 bcount = 0;
1287
1288 /* Skip to the next one and initialize it if any */
1289 do {
1290 s++;
1291 } while (*s == ' ' || *s == '\t');
1292 if (*s)
1293 argv[argc++] = d;
1294 } else if (*s == '\\') {
1295 *d++ = *s++;
1296 bcount++;
1297 } else if (*s == '"') {
1298 if ((bcount & 1) == 0) {
1299 /* Preceded by an even number of '\', this is half that
1300 * number of '\', plus a quote which we erase.
1301 */
1302 d -= bcount / 2;
1303 qcount++;
1304 } else {
1305 /* Preceded by an odd number of '\', this is half that
1306 * number of '\' followed by a '"'
1307 */
1308 d = d - bcount / 2 - 1;
1309 *d++ = '"';
1310 }
1311 s++;
1312 bcount = 0;
1313 /* Now count the number of consecutive quotes. Note that qcount
1314 * already takes into account the opening quote if any, as well as
1315 * the quote that lead us here.
1316 */
1317 while (*s == '"') {
1318 if (++qcount == 3) {
1319 *d++ = '"';
1320 qcount = 0;
1321 }
1322 s++;
1323 }
1324 if (qcount == 2)
1325 qcount = 0;
1326 } else {
1327 /* A regular character */
1328 *d++ = *s++;
1329 bcount = 0;
1330 }
1331 }
1332 *d = '\0';
1333 argv[argc] = NULL;
1334 *numargs = argc;
1335
1336 return argv;
1337 }
1338
1339 /* For Windows replace args with UTF-8 versions */
1340 static void win_args(int *p_argc, char ***p_argv) {
1341 int i;
1342 LPWSTR *szArgList = win_CommandLineToArgvW(GetCommandLineW(), &win_argc);
1343 if (szArgList) {
1344 if (!(win_argv = (char **) calloc(win_argc + 1, sizeof(char *)))) {
1345 LocalFree(szArgList);
1346 } else {
1347 for (i = 0; i < win_argc; i++) {
1348 const int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, szArgList[i], -1, NULL, 0,
1349 NULL /*lpDefaultChar*/, NULL /*lpUsedDefaultChar*/);
1350 if (len == 0 || !(win_argv[i] = malloc(len + 1))) {
1351 win_free_args();
1352 LocalFree(szArgList);
1353 return;
1354 }
1355 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, szArgList[i], -1, win_argv[i], len,
1356 NULL /*lpDefaultChar*/, NULL /*lpUsedDefaultChar*/) == 0) {
1357 win_free_args();
1358 LocalFree(szArgList);
1359 return;
1360 }
1361 }
1362 for (i = 0; i < win_argc; i++) {
1363 (*p_argv)[i] = win_argv[i];
1364 }
1365 *p_argc = win_argc;
1366 LocalFree(szArgList);
1367 }
1368 }
1369 }
1370
1371 /* Convert UTF-8 to Windows wide chars. Ticket #288, props Marcel */
1372 #define utf8_to_wide(u, w, r) \
1373 { \
1374 int lenW; /* Includes NUL terminator */ \
1375 if ((lenW = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u, -1, NULL, 0)) == 0) return r; \
1376 w = (wchar_t *) z_alloca(sizeof(wchar_t) * lenW); \
1377 if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u, -1, w, lenW) == 0) return r; \
1378 }
1379
1380 /* Do `fopen()` on Windows, assuming `filename` is UTF-8 encoded. Ticket #288, props Marcel */
1381 static FILE *win_fopen(const char *filename, const char *mode) {
1382 wchar_t *filenameW, *modeW;
1383
1384 utf8_to_wide(filename, filenameW, NULL);
1385 utf8_to_wide(mode, modeW, NULL);
1386
1387 return _wfopen(filenameW, modeW);
1388 }
1389 #endif /* _WIN32 */
1390
1391 /* Helper to free Windows args on exit */
1392 static int do_exit(int error_number) {
1393 #ifdef _WIN32
1394 win_free_args();
1395 #endif
1396 exit(error_number);
1397 return error_number; /* Not reached */
1398 }
1399
1400 typedef struct { const char *arg; int opt; } arg_opt;
1401
1402 int main(int argc, char **argv) {
1403 struct zint_symbol *my_symbol;
1404 struct zint_seg segs[10] = {{0}};
1405 int error_number = 0;
1406 int warn_number = 0;
1407 int rotate_angle = 0;
1408 int help = 0;
1409 int data_cnt = 0;
1410 int input_cnt = 0;
1411 int batch_mode = 0;
1412 int mirror_mode = 0;
1413 int fullmultibyte = 0;
1414 int mask = 0;
1415 int separator = 0;
1416 int addon_gap = 0;
1417 int rows = 0;
1418 char filetype[4] = {0};
1419 int output_given = 0;
1420 int png_refused;
1421 int val;
1422 int i;
1423 int ret;
1424 char *outfile_extension;
1425 int data_arg_num = 0;
1426 int seg_count = 0;
1427 float x_dim_mm = 0.0f, dpmm = 0.0f;
1428 float float_opt;
1429 char errbuf[64]; /* For `validate_float()` */
1430 arg_opt *arg_opts = (arg_opt *) z_alloca(sizeof(arg_opt) * argc);
1431
1432 const int no_png = ZBarcode_NoPng();
1433
1434 if (argc == 1) {
1435 usage(no_png);
1436 exit(ZINT_ERROR_INVALID_DATA);
1437 }
1438
1439 my_symbol = ZBarcode_Create();
1440 if (!my_symbol) {
1441 fprintf(stderr, "Error 151: Memory failure\n");
1442 exit(ZINT_ERROR_MEMORY);
1443 }
1444 my_symbol->input_mode = UNICODE_MODE;
1445
1446 #ifdef _WIN32
1447 win_args(&argc, &argv);
1448 #endif
1449
1450 opterr = 0; /* Disable `getopt_long_only()` printing errors */
1451 while (1) {
1452 enum options {
1453 OPT_ADDONGAP = 128, OPT_BATCH, OPT_BINARY, OPT_BG, OPT_BIND, OPT_BIND_TOP, OPT_BOLD, OPT_BORDER, OPT_BOX,
1454 OPT_CMYK, OPT_COLS, OPT_COMPLIANTHEIGHT,
1455 OPT_DIRECT, OPT_DMISO144, OPT_DMRE, OPT_DOTSIZE, OPT_DOTTY, OPT_DUMP,
1456 OPT_ECI, OPT_EMBEDFONT, OPT_ESC, OPT_EXTRAESC, OPT_FAST, OPT_FG, OPT_FILETYPE, OPT_FULLMULTIBYTE,
1457 OPT_GS1, OPT_GS1NOCHECK, OPT_GS1PARENS, OPT_GSSEP, OPT_GUARDDESCENT, OPT_GUARDWHITESPACE,
1458 OPT_HEIGHT, OPT_HEIGHTPERROW, OPT_INIT, OPT_MIRROR, OPT_MASK, OPT_MODE,
1459 OPT_NOBACKGROUND, OPT_NOQUIETZONES, OPT_NOTEXT, OPT_PRIMARY, OPT_QUIETZONES,
1460 OPT_ROTATE, OPT_ROWS, OPT_SCALE, OPT_SCALEXDIM, OPT_SCMVV, OPT_SECURE,
1461 OPT_SEG1, OPT_SEG2, OPT_SEG3, OPT_SEG4, OPT_SEG5, OPT_SEG6, OPT_SEG7, OPT_SEG8, OPT_SEG9,
1462 OPT_SEPARATOR, OPT_SMALL, OPT_SQUARE, OPT_STRUCTAPP, OPT_TEXTGAP,
1463 OPT_VERBOSE, OPT_VERS, OPT_VWHITESP, OPT_WERROR
1464 };
1465 static const struct option long_options[] = {
1466 {"addongap", 1, NULL, OPT_ADDONGAP},
1467 {"barcode", 1, NULL, 'b'},
1468 {"batch", 0, NULL, OPT_BATCH},
1469 {"binary", 0, NULL, OPT_BINARY},
1470 {"bg", 1, 0, OPT_BG},
1471 {"bgcolor", 1, 0, OPT_BG}, /* Synonym */
1472 {"bgcolour", 1, 0, OPT_BG}, /* Synonym */
1473 {"bind", 0, NULL, OPT_BIND},
1474 {"bindtop", 0, NULL, OPT_BIND_TOP},
1475 {"bold", 0, NULL, OPT_BOLD},
1476 {"border", 1, NULL, OPT_BORDER},
1477 {"box", 0, NULL, OPT_BOX},
1478 {"cmyk", 0, NULL, OPT_CMYK},
1479 {"cols", 1, NULL, OPT_COLS},
1480 {"compliantheight", 0, NULL, OPT_COMPLIANTHEIGHT},
1481 {"data", 1, NULL, 'd'},
1482 {"direct", 0, NULL, OPT_DIRECT},
1483 {"dmiso144", 0, NULL, OPT_DMISO144},
1484 {"dmre", 0, NULL, OPT_DMRE},
1485 {"dotsize", 1, NULL, OPT_DOTSIZE},
1486 {"dotty", 0, NULL, OPT_DOTTY},
1487 {"dump", 0, NULL, OPT_DUMP},
1488 {"eci", 1, NULL, OPT_ECI},
1489 {"ecinos", 0, NULL, 'e'},
1490 {"embedfont", 0, NULL, OPT_EMBEDFONT},
1491 {"esc", 0, NULL, OPT_ESC},
1492 {"extraesc", 0, NULL, OPT_EXTRAESC},
1493 {"fast", 0, NULL, OPT_FAST},
1494 {"fg", 1, 0, OPT_FG},
1495 {"fgcolor", 1, 0, OPT_FG}, /* Synonym */
1496 {"fgcolour", 1, 0, OPT_FG}, /* Synonym */
1497 {"filetype", 1, NULL, OPT_FILETYPE},
1498 {"fullmultibyte", 0, NULL, OPT_FULLMULTIBYTE},
1499 {"gs1", 0, 0, OPT_GS1},
1500 {"gs1nocheck", 0, NULL, OPT_GS1NOCHECK},
1501 {"gs1parens", 0, NULL, OPT_GS1PARENS},
1502 {"gssep", 0, NULL, OPT_GSSEP},
1503 {"guarddescent", 1, NULL, OPT_GUARDDESCENT},
1504 {"guardwhitespace", 0, NULL, OPT_GUARDWHITESPACE},
1505 {"height", 1, NULL, OPT_HEIGHT},
1506 {"heightperrow", 0, NULL, OPT_HEIGHTPERROW},
1507 {"help", 0, NULL, 'h'},
1508 {"init", 0, NULL, OPT_INIT},
1509 {"input", 1, NULL, 'i'},
1510 {"mirror", 0, NULL, OPT_MIRROR},
1511 {"mask", 1, NULL, OPT_MASK},
1512 {"mode", 1, NULL, OPT_MODE},
1513 {"nobackground", 0, NULL, OPT_NOBACKGROUND},
1514 {"noquietzones", 0, NULL, OPT_NOQUIETZONES},
1515 {"notext", 0, NULL, OPT_NOTEXT},
1516 {"output", 1, NULL, 'o'},
1517 {"primary", 1, NULL, OPT_PRIMARY},
1518 {"quietzones", 0, NULL, OPT_QUIETZONES},
1519 {"reverse", 0, NULL, 'r'},
1520 {"rotate", 1, NULL, OPT_ROTATE},
1521 {"rows", 1, NULL, OPT_ROWS},
1522 {"scale", 1, NULL, OPT_SCALE},
1523 {"scalexdimdp", 1, NULL, OPT_SCALEXDIM},
1524 {"scmvv", 1, NULL, OPT_SCMVV},
1525 {"secure", 1, NULL, OPT_SECURE},
1526 {"seg1", 1, NULL, OPT_SEG1},
1527 {"seg2", 1, NULL, OPT_SEG2},
1528 {"seg3", 1, NULL, OPT_SEG3},
1529 {"seg4", 1, NULL, OPT_SEG4},
1530 {"seg5", 1, NULL, OPT_SEG5},
1531 {"seg6", 1, NULL, OPT_SEG6},
1532 {"seg7", 1, NULL, OPT_SEG7},
1533 {"seg8", 1, NULL, OPT_SEG8},
1534 {"seg9", 1, NULL, OPT_SEG9},
1535 {"separator", 1, NULL, OPT_SEPARATOR},
1536 {"small", 0, NULL, OPT_SMALL},
1537 {"square", 0, NULL, OPT_SQUARE},
1538 {"structapp", 1, NULL, OPT_STRUCTAPP},
1539 {"textgap", 1, NULL, OPT_TEXTGAP},
1540 {"types", 0, NULL, 't'},
1541 {"verbose", 0, NULL, OPT_VERBOSE}, /* Currently undocumented, output some debug info */
1542 {"vers", 1, NULL, OPT_VERS},
1543 {"version", 0, NULL, 'v'},
1544 {"vwhitesp", 1, NULL, OPT_VWHITESP},
1545 {"werror", 0, NULL, OPT_WERROR},
1546 {"whitesp", 1, NULL, 'w'},
1547 {NULL, 0, NULL, 0}
1548 };
1549 const int c = getopt_long_only(argc, argv, "b:d:ehi:o:rtvw:", long_options, NULL);
1550 if (c == -1) break;
1551
1552 switch (c) {
1553 case OPT_ADDONGAP:
1554 if (!validate_int(optarg, -1 /*len*/, &val)) {
1555 fprintf(stderr, "Error 139: Invalid add-on gap value (digits only)\n");
1556 return do_exit(ZINT_ERROR_INVALID_OPTION);
1557 }
1558 if (val >= 7 && val <= 12) {
1559 addon_gap = val;
1560 } else {
1561 fprintf(stderr, "Warning 140: Add-on gap '%d' out of range (7 to 12), ignoring\n", val);
1562 fflush(stderr);
1563 warn_number = ZINT_WARN_INVALID_OPTION;
1564 }
1565 break;
1566 case OPT_BATCH:
1567 if (data_cnt == 0) {
1568 /* Switch to batch processing mode */
1569 batch_mode = 1;
1570 } else {
1571 fprintf(stderr, "Warning 141: Can't use batch mode if data given, ignoring\n");
1572 fflush(stderr);
1573 warn_number = ZINT_WARN_INVALID_OPTION;
1574 }
1575 break;
1576 case OPT_BG:
1577 strncpy(my_symbol->bgcolour, optarg, 15); /* Allow for "CCC,MMM,YYY,KKK" */
1578 break;
1579 case OPT_BINARY:
1580 my_symbol->input_mode = (my_symbol->input_mode & ~0x07) | DATA_MODE;
1581 break;
1582 case OPT_BIND:
1583 my_symbol->output_options |= BARCODE_BIND;
1584 break;
1585 case OPT_BIND_TOP:
1586 my_symbol->output_options |= BARCODE_BIND_TOP;
1587 break;
1588 case OPT_BOLD:
1589 my_symbol->output_options |= BOLD_TEXT;
1590 break;
1591 case OPT_BORDER:
1592 if (!validate_int(optarg, -1 /*len*/, &val)) {
1593 fprintf(stderr, "Error 107: Invalid border width value (digits only)\n");
1594 return do_exit(ZINT_ERROR_INVALID_OPTION);
1595 }
1596 if (val <= 1000) { /* `val` >= 0 always */
1597 my_symbol->border_width = val;
1598 } else {
1599 fprintf(stderr, "Warning 108: Border width '%d' out of range (0 to 1000), ignoring\n", val);
1600 fflush(stderr);
1601 warn_number = ZINT_WARN_INVALID_OPTION;
1602 }
1603 break;
1604 case OPT_BOX:
1605 my_symbol->output_options |= BARCODE_BOX;
1606 break;
1607 case OPT_CMYK:
1608 my_symbol->output_options |= CMYK_COLOUR;
1609 break;
1610 case OPT_COLS:
1611 if (!validate_int(optarg, -1 /*len*/, &val)) {
1612 fprintf(stderr, "Error 131: Invalid columns value (digits only)\n");
1613 return do_exit(ZINT_ERROR_INVALID_OPTION);
1614 }
1615 if ((val >= 1) && (val <= 200)) {
1616 my_symbol->option_2 = val;
1617 } else {
1618 fprintf(stderr, "Warning 111: Number of columns '%d' out of range (1 to 200), ignoring\n", val);
1619 fflush(stderr);
1620 warn_number = ZINT_WARN_INVALID_OPTION;
1621 }
1622 break;
1623 case OPT_COMPLIANTHEIGHT:
1624 my_symbol->output_options |= COMPLIANT_HEIGHT;
1625 break;
1626 case OPT_DIRECT:
1627 my_symbol->output_options |= BARCODE_STDOUT;
1628 break;
1629 case OPT_DMISO144:
1630 my_symbol->option_3 |= DM_ISO_144;
1631 break;
1632 case OPT_DMRE:
1633 /* Square overwrites DMRE */
1634 if ((my_symbol->option_3 & 0x7F) != DM_SQUARE) {
1635 my_symbol->option_3 = DM_DMRE | (my_symbol->option_3 & ~0x7F);
1636 }
1637 break;
1638 case OPT_DOTSIZE:
1639 if (!validate_float(optarg, 0 /*allow_neg*/, &float_opt, errbuf)) {
1640 fprintf(stderr, "Error 181: Invalid dot radius floating point (%s)\n", errbuf);
1641 return do_exit(ZINT_ERROR_INVALID_OPTION);
1642 }
1643 if (float_opt >= 0.01f) {
1644 my_symbol->dot_size = float_opt;
1645 } else {
1646 fprintf(stderr, "Warning 106: Invalid dot radius value (less than 0.01), ignoring\n");
1647 fflush(stderr);
1648 warn_number = ZINT_WARN_INVALID_OPTION;
1649 }
1650 break;
1651 case OPT_DOTTY:
1652 my_symbol->output_options |= BARCODE_DOTTY_MODE;
1653 break;
1654 case OPT_DUMP:
1655 my_symbol->output_options |= BARCODE_STDOUT;
1656 strcpy(my_symbol->outfile, "dummy.txt");
1657 break;
1658 case OPT_ECI:
1659 if (!validate_int(optarg, -1 /*len*/, &val)) {
1660 fprintf(stderr, "Error 138: Invalid ECI code (digits only)\n");
1661 return do_exit(ZINT_ERROR_INVALID_OPTION);
1662 }
1663 if (val <= 999999) { /* `val` >= 0 always */
1664 my_symbol->eci = val;
1665 } else {
1666 fprintf(stderr, "Warning 118: ECI code '%d' out of range (0 to 999999), ignoring\n", val);
1667 fflush(stderr);
1668 warn_number = ZINT_WARN_INVALID_OPTION;
1669 }
1670 break;
1671 case OPT_EMBEDFONT:
1672 my_symbol->output_options |= EMBED_VECTOR_FONT;
1673 break;
1674 case OPT_ESC:
1675 my_symbol->input_mode |= ESCAPE_MODE;
1676 break;
1677 case OPT_EXTRAESC:
1678 my_symbol->input_mode |= EXTRA_ESCAPE_MODE;
1679 break;
1680 case OPT_FAST:
1681 my_symbol->input_mode |= FAST_MODE;
1682 break;
1683 case OPT_FG:
1684 strncpy(my_symbol->fgcolour, optarg, 15); /* Allow for "CCC,MMM,YYY,KKK" */
1685 break;
1686 case OPT_FILETYPE:
1687 /* Select the type of output file */
1688 if (supported_filetype(optarg, no_png, &png_refused)) {
1689 strncpy(filetype, optarg, (size_t) 3);
1690 } else {
1691 if (png_refused) {
1692 fprintf(stderr, "Warning 152: PNG format disabled at compile time, ignoring\n");
1693 } else {
1694 fprintf(stderr, "Warning 142: File type '%s' not supported, ignoring\n", optarg);
1695 }
1696 fflush(stderr);
1697 warn_number = ZINT_WARN_INVALID_OPTION;
1698 }
1699 break;
1700 case OPT_FULLMULTIBYTE:
1701 fullmultibyte = 1;
1702 break;
1703 case OPT_GS1:
1704 my_symbol->input_mode = (my_symbol->input_mode & ~0x07) | GS1_MODE;
1705 break;
1706 case OPT_GS1NOCHECK:
1707 my_symbol->input_mode |= GS1NOCHECK_MODE;
1708 break;
1709 case OPT_GS1PARENS:
1710 my_symbol->input_mode |= GS1PARENS_MODE;
1711 break;
1712 case OPT_GSSEP:
1713 my_symbol->output_options |= GS1_GS_SEPARATOR;
1714 break;
1715 case OPT_GUARDDESCENT:
1716 if (!validate_float(optarg, 0 /*allow_neg*/, &float_opt, errbuf)) {
1717 fprintf(stderr, "Error 182: Invalid guard bar descent floating point (%s)\n", errbuf);
1718 return do_exit(ZINT_ERROR_INVALID_OPTION);
1719 }
1720 if (float_opt >= 0.0f && float_opt <= 50.0f) {
1721 my_symbol->guard_descent = float_opt;
1722 } else {
1723 fprintf(stderr, "Warning 135: Guard bar descent '%g' out of range (0 to 50), ignoring\n",
1724 float_opt);
1725 fflush(stderr);
1726 warn_number = ZINT_WARN_INVALID_OPTION;
1727 }
1728 break;
1729 case OPT_GUARDWHITESPACE:
1730 my_symbol->output_options |= EANUPC_GUARD_WHITESPACE;
1731 break;
1732 case OPT_HEIGHT:
1733 if (!validate_float(optarg, 0 /*allow_neg*/, &float_opt, errbuf)) {
1734 fprintf(stderr, "Error 183: Invalid symbol height floating point (%s)\n", errbuf);
1735 return do_exit(ZINT_ERROR_INVALID_OPTION);
1736 }
1737 if (float_opt >= 0.5f && float_opt <= 2000.0f) {
1738 my_symbol->height = float_opt;
1739 } else {
1740 fprintf(stderr, "Warning 110: Symbol height '%g' out of range (0.5 to 2000), ignoring\n",
1741 float_opt);
1742 fflush(stderr);
1743 warn_number = ZINT_WARN_INVALID_OPTION;
1744 }
1745 break;
1746 case OPT_HEIGHTPERROW:
1747 my_symbol->input_mode |= HEIGHTPERROW_MODE;
1748 break;
1749 case OPT_INIT:
1750 my_symbol->output_options |= READER_INIT;
1751 break;
1752 case OPT_MIRROR:
1753 /* Use filenames which reflect content */
1754 mirror_mode = 1;
1755 break;
1756 case OPT_MASK:
1757 if (!validate_int(optarg, -1 /*len*/, &val)) {
1758 fprintf(stderr, "Error 148: Invalid mask value (digits only)\n");
1759 return do_exit(ZINT_ERROR_INVALID_OPTION);
1760 }
1761 if (val <= 7) { /* `val` >= 0 always */
1762 mask = val + 1;
1763 } else {
1764 /* mask pattern >= 0 and <= 7 (i.e. values >= 1 and <= 8) only permitted */
1765 fprintf(stderr, "Warning 147: Mask value '%d' out of range (0 to 7), ignoring\n", val);
1766 fflush(stderr);
1767 warn_number = ZINT_WARN_INVALID_OPTION;
1768 }
1769 break;
1770 case OPT_MODE:
1771 if (!validate_int(optarg, -1 /*len*/, &val)) {
1772 fprintf(stderr, "Error 136: Invalid mode value (digits only)\n");
1773 return do_exit(ZINT_ERROR_INVALID_OPTION);
1774 }
1775 if (val <= 6) { /* `val` >= 0 always */
1776 my_symbol->option_1 = val;
1777 } else {
1778 fprintf(stderr, "Warning 116: Mode value '%d' out of range (0 to 6), ignoring\n", val);
1779 fflush(stderr);
1780 warn_number = ZINT_WARN_INVALID_OPTION;
1781 }
1782 break;
1783 case OPT_NOBACKGROUND:
1784 strcpy(my_symbol->bgcolour, "ffffff00");
1785 break;
1786 case OPT_NOQUIETZONES:
1787 my_symbol->output_options |= BARCODE_NO_QUIET_ZONES;
1788 break;
1789 case OPT_NOTEXT:
1790 my_symbol->show_hrt = 0;
1791 break;
1792 case OPT_PRIMARY:
1793 if (strlen(optarg) <= 127) {
1794 strcpy(my_symbol->primary, optarg);
1795 } else {
1796 strncpy(my_symbol->primary, optarg, 127);
1797 fprintf(stderr,
1798 "Warning 115: Primary data string too long (127 character maximum), truncating\n");
1799 fflush(stderr);
1800 warn_number = ZINT_WARN_INVALID_OPTION;
1801 }
1802 break;
1803 case OPT_QUIETZONES:
1804 my_symbol->output_options |= BARCODE_QUIET_ZONES;
1805 break;
1806 case OPT_ROTATE:
1807 /* Only certain inputs allowed */
1808 if (!validate_int(optarg, -1 /*len*/, &val)) {
1809 fprintf(stderr, "Error 117: Invalid rotation value (digits only)\n");
1810 return do_exit(ZINT_ERROR_INVALID_OPTION);
1811 }
1812 switch (val) {
1813 case 0:
1814 case 90:
1815 case 180:
1816 case 270:
1817 rotate_angle = val;
1818 break;
1819 default:
1820 fprintf(stderr, "Warning 137: Rotation value '%d' out of range (0, 90, 180 or 270 only),"
1821 " ignoring\n", val);
1822 fflush(stderr);
1823 warn_number = ZINT_WARN_INVALID_OPTION;
1824 break;
1825 }
1826 break;
1827 case OPT_ROWS:
1828 if (!validate_int(optarg, -1 /*len*/, &val)) {
1829 fprintf(stderr, "Error 132: Invalid rows value (digits only)\n");
1830 return do_exit(ZINT_ERROR_INVALID_OPTION);
1831 }
1832 if ((val >= 1) && (val <= 90)) {
1833 rows = val;
1834 } else {
1835 fprintf(stderr, "Warning 112: Number of rows '%d' out of range (1 to 90), ignoring\n", val);
1836 fflush(stderr);
1837 warn_number = ZINT_WARN_INVALID_OPTION;
1838 }
1839 break;
1840 case OPT_SCALE:
1841 if (!validate_float(optarg, 0 /*allow_neg*/, &float_opt, errbuf)) {
1842 fprintf(stderr, "Error 184: Invalid scale floating point (%s)\n", errbuf);
1843 return do_exit(ZINT_ERROR_INVALID_OPTION);
1844 }
1845 if (float_opt >= 0.01f) {
1846 my_symbol->scale = float_opt;
1847 } else {
1848 fprintf(stderr, "Warning 105: Invalid scale value '%g' (less than 0.01), ignoring\n", float_opt);
1849 fflush(stderr);
1850 warn_number = ZINT_WARN_INVALID_OPTION;
1851 }
1852 break;
1853 case OPT_SCALEXDIM:
1854 if (!validate_scalexdimdp(optarg, &x_dim_mm, &dpmm)) {
1855 return do_exit(ZINT_ERROR_INVALID_OPTION);
1856 }
1857 if (x_dim_mm > 10.0f || dpmm > 1000.0f) {
1858 if (x_dim_mm > 10.0f) {
1859 fprintf(stderr, "Warning 185: scalexdimdp X-dim '%g' out of range (greater than 10),"
1860 " ignoring\n", x_dim_mm);
1861 } else {
1862 fprintf(stderr, "Warning 186: scalexdimdp resolution '%g' out of range (greater than 1000),"
1863 " ignoring\n", dpmm);
1864 }
1865 fflush(stderr);
1866 warn_number = ZINT_WARN_INVALID_OPTION;
1867 x_dim_mm = dpmm = 0.0f;
1868 }
1869 break;
1870 case OPT_SCMVV:
1871 if (!validate_int(optarg, -1 /*len*/, &val)) {
1872 fprintf(stderr, "Error 149: Invalid Structured Carrier Message version value (digits only)\n");
1873 return do_exit(ZINT_ERROR_INVALID_OPTION);
1874 }
1875 if (val <= 99) { /* `val` >= 0 always */
1876 my_symbol->option_2 = val + 1;
1877 } else {
1878 /* Version 00-99 only */
1879 fprintf(stderr, "Warning 150: Structured Carrier Message version '%d' out of range (0 to 99),"
1880 " ignoring\n", val);
1881 fflush(stderr);
1882 warn_number = ZINT_WARN_INVALID_OPTION;
1883 }
1884 break;
1885 case OPT_SECURE:
1886 if (!validate_int(optarg, -1 /*len*/, &val)) {
1887 fprintf(stderr, "Error 134: Invalid ECC value (digits only)\n");
1888 return do_exit(ZINT_ERROR_INVALID_OPTION);
1889 }
1890 if (val <= 8) { /* `val` >= 0 always */
1891 my_symbol->option_1 = val;
1892 } else {
1893 fprintf(stderr, "Warning 114: ECC level '%d' out of range (0 to 8), ignoring\n", val);
1894 fflush(stderr);
1895 warn_number = ZINT_WARN_INVALID_OPTION;
1896 }
1897 break;
1898 case OPT_SEG1:
1899 case OPT_SEG2:
1900 case OPT_SEG3:
1901 case OPT_SEG4:
1902 case OPT_SEG5:
1903 case OPT_SEG6:
1904 case OPT_SEG7:
1905 case OPT_SEG8:
1906 case OPT_SEG9:
1907 if (batch_mode == 0) {
1908 val = c - OPT_SEG1 + 1; /* Segment number */
1909 if (segs[val].source) {
1910 fprintf(stderr, "Error 164: Duplicate segment %d\n", val);
1911 return do_exit(ZINT_ERROR_INVALID_OPTION);
1912 }
1913 if (!validate_seg(optarg, c - OPT_SEG1 + 1, segs)) {
1914 return do_exit(ZINT_ERROR_INVALID_OPTION);
1915 }
1916 if (val >= seg_count) {
1917 seg_count = val + 1;
1918 }
1919 } else {
1920 fprintf(stderr, "Warning 165: Can't define segments in batch mode, ignoring '%s'\n", optarg);
1921 fflush(stderr);
1922 warn_number = ZINT_WARN_INVALID_OPTION;
1923 }
1924 break;
1925 case OPT_SEPARATOR:
1926 if (!validate_int(optarg, -1 /*len*/, &val)) {
1927 fprintf(stderr, "Error 128: Invalid separator value (digits only)\n");
1928 return do_exit(ZINT_ERROR_INVALID_OPTION);
1929 }
1930 if (val <= 4) { /* `val` >= 0 always */
1931 separator = val;
1932 } else {
1933 /* Greater than 4 values are not permitted */
1934 fprintf(stderr, "Warning 127: Separator value '%d' out of range (0 to 4), ignoring\n", val);
1935 fflush(stderr);
1936 warn_number = ZINT_WARN_INVALID_OPTION;
1937 }
1938 break;
1939 case OPT_SMALL:
1940 my_symbol->output_options |= SMALL_TEXT;
1941 break;
1942 case OPT_SQUARE:
1943 my_symbol->option_3 = DM_SQUARE | (my_symbol->option_3 & ~0x7F);
1944 break;
1945 case OPT_STRUCTAPP:
1946 memset(&my_symbol->structapp, 0, sizeof(my_symbol->structapp));
1947 if (!validate_structapp(optarg, &my_symbol->structapp)) {
1948 return do_exit(ZINT_ERROR_INVALID_OPTION);
1949 }
1950 break;
1951 case OPT_TEXTGAP:
1952 if (!validate_float(optarg, 1 /*allow_neg*/, &float_opt, errbuf)) {
1953 fprintf(stderr, "Error 194: Invalid text gap floating point (%s)\n", errbuf);
1954 return do_exit(ZINT_ERROR_INVALID_OPTION);
1955 }
1956 if (float_opt >= -5.0f && float_opt <= 10.0f) {
1957 my_symbol->text_gap = float_opt;
1958 } else {
1959 fprintf(stderr, "Warning 195: Text gap '%g' out of range (-5 to 10), ignoring\n",
1960 float_opt);
1961 fflush(stderr);
1962 warn_number = ZINT_WARN_INVALID_OPTION;
1963 }
1964 break;
1965 case OPT_VERBOSE:
1966 my_symbol->debug = 1;
1967 break;
1968 case OPT_VERS:
1969 if (!validate_int(optarg, -1 /*len*/, &val)) {
1970 fprintf(stderr, "Error 133: Invalid version value (digits only)\n");
1971 return do_exit(ZINT_ERROR_INVALID_OPTION);
1972 }
1973 if ((val >= 1) && (val <= 999)) {
1974 my_symbol->option_2 = val;
1975 } else {
1976 fprintf(stderr, "Warning 113: Version value '%d' out of range (1 to 999), ignoring\n", val);
1977 fflush(stderr);
1978 warn_number = ZINT_WARN_INVALID_OPTION;
1979 }
1980 break;
1981 case OPT_VWHITESP:
1982 if (!validate_int(optarg, -1 /*len*/, &val)) {
1983 fprintf(stderr, "Error 153: Invalid vertical whitespace value '%s' (digits only)\n", optarg);
1984 return do_exit(ZINT_ERROR_INVALID_OPTION);
1985 }
1986 if (val <= 1000) { /* `val` >= 0 always */
1987 my_symbol->whitespace_height = val;
1988 } else {
1989 fprintf(stderr,
1990 "Warning 154: Vertical whitespace value '%d' out of range (0 to 1000), ignoring\n", val);
1991 fflush(stderr);
1992 warn_number = ZINT_WARN_INVALID_OPTION;
1993 }
1994 break;
1995 case OPT_WERROR:
1996 my_symbol->warn_level = WARN_FAIL_ALL;
1997 break;
1998
1999 case 'h':
2000 usage(no_png);
2001 help = 1;
2002 break;
2003 case 'v':
2004 version(no_png);
2005 help = 1;
2006 break;
2007 case 't':
2008 types();
2009 help = 1;
2010 break;
2011 case 'e':
2012 show_eci();
2013 help = 1;
2014 break;
2015
2016 case 'b':
2017 if (!validate_int(optarg, -1 /*len*/, &val) && !(val = get_barcode_name(optarg))) {
2018 fprintf(stderr, "Error 119: Invalid barcode type '%s'\n", optarg);
2019 return do_exit(ZINT_ERROR_INVALID_OPTION);
2020 }
2021 my_symbol->symbology = val;
2022 break;
2023
2024 case 'w':
2025 if (!validate_int(optarg, -1 /*len*/, &val)) {
2026 fprintf(stderr, "Error 120: Invalid horizontal whitespace value '%s' (digits only)\n", optarg);
2027 return do_exit(ZINT_ERROR_INVALID_OPTION);
2028 }
2029 if (val <= 1000) { /* `val` >= 0 always */
2030 my_symbol->whitespace_width = val;
2031 } else {
2032 fprintf(stderr,
2033 "Warning 121: Horizontal whitespace value '%d' out of range (0 to 1000), ignoring\n",
2034 val);
2035 fflush(stderr);
2036 warn_number = ZINT_WARN_INVALID_OPTION;
2037 }
2038 break;
2039
2040 case 'd': /* we have some data! */
2041 if (batch_mode == 0) {
2042 arg_opts[data_arg_num].arg = optarg;
2043 arg_opts[data_arg_num].opt = c;
2044 data_arg_num++;
2045 data_cnt++;
2046 } else {
2047 fprintf(stderr, "Warning 122: Can't define data in batch mode, ignoring '%s'\n", optarg);
2048 fflush(stderr);
2049 warn_number = ZINT_WARN_INVALID_OPTION;
2050 }
2051 break;
2052
2053 case 'i': /* Take data from file */
2054 if (batch_mode == 0 || input_cnt == 0) {
2055 arg_opts[data_arg_num].arg = optarg;
2056 arg_opts[data_arg_num].opt = c;
2057 data_arg_num++;
2058 input_cnt++;
2059 } else {
2060 fprintf(stderr, "Warning 143: Can only define one input file in batch mode, ignoring '%s'\n",
2061 optarg);
2062 fflush(stderr);
2063 warn_number = ZINT_WARN_INVALID_OPTION;
2064 }
2065 break;
2066
2067 case 'o':
2068 strncpy(my_symbol->outfile, optarg, 255);
2069 output_given = 1;
2070 break;
2071
2072 case 'r':
2073 strcpy(my_symbol->fgcolour, "ffffff");
2074 strcpy(my_symbol->bgcolour, "000000");
2075 break;
2076
2077 case '?':
2078 if (optopt) {
2079 for (i = 0; i < ARRAY_SIZE(long_options) && long_options[i].val != optopt; i++);
2080 if (i == ARRAY_SIZE(long_options)) { /* Shouldn't happen */
2081 fprintf(stderr, "Error 125: ?? unknown optopt '%d'\n", optopt); /* Not reached */
2082 return do_exit(ZINT_ERROR_ENCODING_PROBLEM);
2083 }
2084 if (long_options[i].has_arg) {
2085 fprintf(stderr, "Error 109: option '%s' requires an argument\n", argv[optind - 1]);
2086 } else {
2087 const char *eqs = strchr(argv[optind - 1], '=');
2088 const int optlen = eqs ? (int) (eqs - argv[optind - 1]) : (int) strlen(argv[optind - 1]);
2089 fprintf(stderr, "Error 126: option '%.*s' does not take an argument\n", optlen,
2090 argv[optind - 1]);
2091 }
2092 } else {
2093 fprintf(stderr, "Error 101: unknown option '%s'\n", argv[optind - 1]);
2094 }
2095 return do_exit(ZINT_ERROR_INVALID_OPTION);
2096 break;
2097
2098 default: /* Shouldn't happen */
2099 fprintf(stderr, "Error 123: ?? getopt error 0%o\n", c); /* Not reached */
2100 return do_exit(ZINT_ERROR_ENCODING_PROBLEM);
2101 break;
2102 }
2103 }
2104 if (optind != argc) {
2105 if (optind + 1 == argc) {
2106 fprintf(stderr, "Warning 191: extra argument '%s' ignoring\n", argv[optind]);
2107 } else {
2108 fprintf(stderr, "Warning 192: extra arguments beginning with '%s' ignoring\n", argv[optind]);
2109 }
2110 fflush(stderr);
2111 warn_number = ZINT_WARN_INVALID_OPTION;
2112 }
2113
2114 if (data_arg_num) {
2115 const int symbology = my_symbol->symbology;
2116 const unsigned int cap = ZBarcode_Cap(symbology, ZINT_CAP_STACKABLE | ZINT_CAP_EXTENDABLE |
2117 ZINT_CAP_FULL_MULTIBYTE | ZINT_CAP_MASK);
2118 if (fullmultibyte && (cap & ZINT_CAP_FULL_MULTIBYTE)) {
2119 my_symbol->option_3 = ZINT_FULL_MULTIBYTE;
2120 }
2121 if (mask && (cap & ZINT_CAP_MASK)) {
2122 my_symbol->option_3 |= mask << 8;
2123 }
2124 if (separator && (cap & ZINT_CAP_STACKABLE)) {
2125 my_symbol->option_3 = separator;
2126 }
2127 if (addon_gap && (cap & ZINT_CAP_EXTENDABLE)) {
2128 my_symbol->option_2 = addon_gap;
2129 }
2130 if (rows) {
2131 if (symbology == BARCODE_PDF417 || symbology == BARCODE_PDF417COMP || symbology == BARCODE_HIBC_PDF
2132 || symbology == BARCODE_DBAR_EXPSTK || symbology == BARCODE_DBAR_EXPSTK_CC) {
2133 my_symbol->option_3 = rows;
2134 } else if (symbology == BARCODE_CODABLOCKF || symbology == BARCODE_HIBC_BLOCKF
2135 || symbology == BARCODE_CODE16K || symbology == BARCODE_CODE49) {
2136 my_symbol->option_1 = rows;
2137 }
2138 }
2139
2140 if (output_given && (my_symbol->output_options & BARCODE_STDOUT)) {
2141 my_symbol->output_options &= ~BARCODE_STDOUT;
2142 fprintf(stderr, "Warning 193: Output file given, ignoring '--direct' option\n");
2143 fflush(stderr);
2144 warn_number = ZINT_WARN_INVALID_OPTION;
2145 }
2146 if (batch_mode) {
2147 /* Take each line of text as a separate data set */
2148 if (data_arg_num > 1) {
2149 fprintf(stderr, "Warning 144: Processing first input file '%s' only\n", arg_opts[0].arg);
2150 fflush(stderr);
2151 warn_number = ZINT_WARN_INVALID_OPTION;
2152 }
2153 if (seg_count) {
2154 fprintf(stderr, "Warning 169: Ignoring segment arguments\n");
2155 fflush(stderr);
2156 warn_number = ZINT_WARN_INVALID_OPTION;
2157 }
2158 if (filetype[0] == '\0') {
2159 outfile_extension = get_extension(my_symbol->outfile);
2160 if (outfile_extension && supported_filetype(outfile_extension, no_png, NULL)) {
2161 strcpy(filetype, outfile_extension);
2162 } else {
2163 strcpy(filetype, no_png ? "gif" : "png");
2164 }
2165 }
2166 if (dpmm) { /* Allow `x_dim_mm` to be zero */
2167 if (x_dim_mm == 0.0f) {
2168 x_dim_mm = ZBarcode_Default_Xdim(symbology);
2169 }
2170 float_opt = ZBarcode_Scale_From_XdimDp(symbology, x_dim_mm, dpmm, filetype);
2171 if (float_opt > 0.0f) {
2172 my_symbol->scale = float_opt;
2173 my_symbol->dpmm = dpmm;
2174 } else {
2175 fprintf(stderr, "Warning 187: Invalid scalexdimdp X-dim '%g', resolution '%g' combo, ignoring\n",
2176 x_dim_mm, dpmm);
2177 fflush(stderr);
2178 warn_number = ZINT_WARN_INVALID_OPTION;
2179 }
2180 }
2181 if (((symbology != BARCODE_MAXICODE && my_symbol->scale < 0.5f) || my_symbol->scale < 0.2f)
2182 && is_raster(filetype, no_png)) {
2183 const int min = symbology != BARCODE_MAXICODE ? 5 : 2;
2184 fprintf(stderr, "Warning 145: Scaling less than 0.%d will be set to 0.%d for '%s' output\n", min, min,
2185 filetype);
2186 fflush(stderr);
2187 warn_number = ZINT_WARN_INVALID_OPTION;
2188 }
2189 error_number = batch_process(my_symbol, arg_opts[0].arg, mirror_mode, filetype, output_given,
2190 rotate_angle);
2191 } else {
2192 if (seg_count) {
2193 if (data_arg_num > 1) {
2194 fprintf(stderr, "Error 170: Cannot specify segments and multiple data arguments together\n");
2195 return do_exit(ZINT_ERROR_INVALID_OPTION);
2196 }
2197 if (arg_opts[0].opt != 'd') { /* For simplicity disallow input args for now */
2198 fprintf(stderr, "Error 171: Cannot use input argument with segment arguments\n");
2199 return do_exit(ZINT_ERROR_INVALID_OPTION);
2200 }
2201 segs[0].eci = my_symbol->eci;
2202 segs[0].source = (unsigned char *) arg_opts[0].arg;
2203 segs[0].length = (int) strlen(arg_opts[0].arg);
2204 for (i = 0; i < seg_count; i++) {
2205 if (segs[i].source == NULL) {
2206 fprintf(stderr, "Error 172: Segments must be consecutive - segment %d missing\n", i);
2207 return do_exit(ZINT_ERROR_INVALID_OPTION);
2208 }
2209 }
2210 }
2211 if (filetype[0] != '\0') {
2212 set_extension(my_symbol->outfile, filetype);
2213 }
2214 if (dpmm) { /* Allow `x_dim_mm` to be zero */
2215 if (x_dim_mm == 0.0f) {
2216 x_dim_mm = ZBarcode_Default_Xdim(symbology);
2217 }
2218 float_opt = ZBarcode_Scale_From_XdimDp(symbology, x_dim_mm, dpmm, get_extension(my_symbol->outfile));
2219 if (float_opt > 0.0f) {
2220 my_symbol->scale = float_opt;
2221 my_symbol->dpmm = dpmm;
2222 } else {
2223 fprintf(stderr, "Warning 190: Invalid scalexdimdp X-dim '%g', resolution '%g' combo, ignoring\n",
2224 x_dim_mm, dpmm);
2225 fflush(stderr);
2226 warn_number = ZINT_WARN_INVALID_OPTION;
2227 }
2228 }
2229 if (((symbology != BARCODE_MAXICODE && my_symbol->scale < 0.5f) || my_symbol->scale < 0.2f)
2230 && is_raster(get_extension(my_symbol->outfile), no_png)) {
2231 const int min = symbology != BARCODE_MAXICODE ? 5 : 2;
2232 fprintf(stderr, "Warning 146: Scaling less than 0.%d will be set to 0.%d for '%s' output\n", min, min,
2233 get_extension(my_symbol->outfile));
2234 fflush(stderr);
2235 warn_number = ZINT_WARN_INVALID_OPTION;
2236 }
2237 for (i = 0; i < data_arg_num; i++) {
2238 if (arg_opts[i].opt == 'd') {
2239 if (seg_count) {
2240 ret = ZBarcode_Encode_Segs(my_symbol, segs, seg_count);
2241 } else {
2242 if (i == 1 && (ZBarcode_Cap(symbology, ZINT_CAP_STACKABLE) & ZINT_CAP_STACKABLE) == 0) {
2243 fprintf(stderr,
2244 "Error 173: Symbology must be stackable if multiple data arguments given\n");
2245 fflush(stderr);
2246 error_number = ZINT_ERROR_INVALID_DATA;
2247 break;
2248 }
2249 ret = ZBarcode_Encode(my_symbol, (unsigned char *) arg_opts[i].arg,
2250 (int) strlen(arg_opts[i].arg));
2251 }
2252 } else {
2253 ret = ZBarcode_Encode_File(my_symbol, arg_opts[i].arg);
2254 }
2255 if (ret != 0) {
2256 fprintf(stderr, "%s\n", my_symbol->errtxt);
2257 fflush(stderr);
2258 if (error_number < ZINT_ERROR) {
2259 error_number = ret;
2260 }
2261 }
2262 }
2263 if (error_number < ZINT_ERROR) {
2264 error_number = ZBarcode_Print(my_symbol, rotate_angle);
2265
2266 if (error_number != 0) {
2267 fprintf(stderr, "%s\n", my_symbol->errtxt);
2268 fflush(stderr);
2269 }
2270 }
2271 }
2272 } else if (help == 0) {
2273 fprintf(stderr, "Warning 124: No data received, no symbol generated\n");
2274 fflush(stderr);
2275 warn_number = ZINT_WARN_INVALID_OPTION;
2276 }
2277
2278 ZBarcode_Delete(my_symbol);
2279
2280 return do_exit(error_number ? error_number : warn_number);
2281 }
2282
2283 /* vim: set ts=4 sw=4 et : */