blob: a2cb0e0e3f414b4f1d3e82eb5c9e49bc59b50635 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * qrencode - QR Code encoder
3 *
4 * Input data splitter.
5 * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@fukuchi.org>
6 *
7 * The following data / specifications are taken from
8 * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
9 * or
10 * "Automatic identification and data capture techniques --
11 * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 */
27
28#if HAVE_CONFIG_H
29# include "config.h"
30#endif
31#include <stdlib.h>
32#include <string.h>
33#include <errno.h>
34#include "qrencode.h"
35#include "qrinput.h"
36#include "qrspec.h"
37#include "split.h"
38
39#define isdigit(__c__) ((unsigned char)((signed char)(__c__) - '0') < 10)
40#define isalnum(__c__) (QRinput_lookAnTable(__c__) >= 0)
41
42#if !HAVE_STRDUP
43#undef strdup
44char *strdup(const char *s)
45{
46 size_t len = strlen(s) + 1;
47 void *new = malloc(len);
48 if(new == NULL) return NULL;
49 return (char *)memcpy(new, s, len);
50}
51#endif
52
53static QRencodeMode Split_identifyMode(const char *string, QRencodeMode hint)
54{
55 unsigned char c, d;
56 unsigned int word;
57
58 c = string[0];
59
60 if(c == '\0') return QR_MODE_NUL;
61 if(isdigit(c)) {
62 return QR_MODE_NUM;
63 } else if(isalnum(c)) {
64 return QR_MODE_AN;
65 } else if(hint == QR_MODE_KANJI) {
66 d = string[1];
67 if(d != '\0') {
68 word = ((unsigned int)c << 8) | d;
69 if((word >= 0x8140 && word <= 0x9ffc) || (word >= 0xe040 && word <= 0xebbf)) {
70 return QR_MODE_KANJI;
71 }
72 }
73 }
74
75 return QR_MODE_8;
76}
77
78static int Split_eatNum(const char *string, QRinput *input, QRencodeMode hint);
79static int Split_eatAn(const char *string, QRinput *input, QRencodeMode hint);
80static int Split_eat8(const char *string, QRinput *input, QRencodeMode hint);
81static int Split_eatKanji(const char *string, QRinput *input, QRencodeMode hint);
82
83static int Split_eatNum(const char *string, QRinput *input,QRencodeMode hint)
84{
85 const char *p;
86 int ret;
87 int run;
88 int dif;
89 int ln;
90 QRencodeMode mode;
91
92 ln = QRspec_lengthIndicator(QR_MODE_NUM, input->version);
93
94 p = string;
95 while(isdigit(*p)) {
96 p++;
97 }
98 run = p - string;
99 mode = Split_identifyMode(p, hint);
100 if(mode == QR_MODE_8) {
101 dif = QRinput_estimateBitsModeNum(run) + 4 + ln
102 + QRinput_estimateBitsMode8(1) /* + 4 + l8 */
103 - QRinput_estimateBitsMode8(run + 1) /* - 4 - l8 */;
104 if(dif > 0) {
105 return Split_eat8(string, input, hint);
106 }
107 }
108 if(mode == QR_MODE_AN) {
109 dif = QRinput_estimateBitsModeNum(run) + 4 + ln
110 + QRinput_estimateBitsModeAn(1) /* + 4 + la */
111 - QRinput_estimateBitsModeAn(run + 1) /* - 4 - la */;
112 if(dif > 0) {
113 return Split_eatAn(string, input, hint);
114 }
115 }
116
117 ret = QRinput_append(input, QR_MODE_NUM, run, (unsigned char *)string);
118 if(ret < 0) return -1;
119
120 return run;
121}
122
123static int Split_eatAn(const char *string, QRinput *input, QRencodeMode hint)
124{
125 const char *p, *q;
126 int ret;
127 int run;
128 int dif;
129 int la, ln;
130
131 la = QRspec_lengthIndicator(QR_MODE_AN, input->version);
132 ln = QRspec_lengthIndicator(QR_MODE_NUM, input->version);
133
134 p = string;
135 while(isalnum(*p)) {
136 if(isdigit(*p)) {
137 q = p;
138 while(isdigit(*q)) {
139 q++;
140 }
141 dif = QRinput_estimateBitsModeAn(p - string) /* + 4 + la */
142 + QRinput_estimateBitsModeNum(q - p) + 4 + ln
143 + (isalnum(*q)?(4 + ln):0)
144 - QRinput_estimateBitsModeAn(q - string) /* - 4 - la */;
145 if(dif < 0) {
146 break;
147 } else {
148 p = q;
149 }
150 } else {
151 p++;
152 }
153 }
154
155 run = p - string;
156
157 if(*p && !isalnum(*p)) {
158 dif = QRinput_estimateBitsModeAn(run) + 4 + la
159 + QRinput_estimateBitsMode8(1) /* + 4 + l8 */
160 - QRinput_estimateBitsMode8(run + 1) /* - 4 - l8 */;
161 if(dif > 0) {
162 return Split_eat8(string, input, hint);
163 }
164 }
165
166 ret = QRinput_append(input, QR_MODE_AN, run, (unsigned char *)string);
167 if(ret < 0) return -1;
168
169 return run;
170}
171
172static int Split_eatKanji(const char *string, QRinput *input, QRencodeMode hint)
173{
174 const char *p;
175 int ret;
176 int run;
177
178 p = string;
179 while(Split_identifyMode(p, hint) == QR_MODE_KANJI) {
180 p += 2;
181 }
182 run = p - string;
183 ret = QRinput_append(input, QR_MODE_KANJI, run, (unsigned char *)string);
184 if(ret < 0) return -1;
185
186 return run;
187}
188
189static int Split_eat8(const char *string, QRinput *input, QRencodeMode hint)
190{
191 const char *p, *q;
192 QRencodeMode mode;
193 int ret;
194 int run;
195 int dif;
196 int la, ln, l8;
197 int swcost;
198
199 la = QRspec_lengthIndicator(QR_MODE_AN, input->version);
200 ln = QRspec_lengthIndicator(QR_MODE_NUM, input->version);
201 l8 = QRspec_lengthIndicator(QR_MODE_8, input->version);
202
203 p = string + 1;
204 while(*p != '\0') {
205 mode = Split_identifyMode(p, hint);
206 if(mode == QR_MODE_KANJI) {
207 break;
208 }
209 if(mode == QR_MODE_NUM) {
210 q = p;
211 while(isdigit(*q)) {
212 q++;
213 }
214 if(Split_identifyMode(q, hint) == QR_MODE_8) {
215 swcost = 4 + l8;
216 } else {
217 swcost = 0;
218 }
219 dif = QRinput_estimateBitsMode8(p - string) /* + 4 + l8 */
220 + QRinput_estimateBitsModeNum(q - p) + 4 + ln
221 + swcost
222 - QRinput_estimateBitsMode8(q - string) /* - 4 - l8 */;
223 if(dif < 0) {
224 break;
225 } else {
226 p = q;
227 }
228 } else if(mode == QR_MODE_AN) {
229 q = p;
230 while(isalnum(*q)) {
231 q++;
232 }
233 if(Split_identifyMode(q, hint) == QR_MODE_8) {
234 swcost = 4 + l8;
235 } else {
236 swcost = 0;
237 }
238 dif = QRinput_estimateBitsMode8(p - string) /* + 4 + l8 */
239 + QRinput_estimateBitsModeAn(q - p) + 4 + la
240 + swcost
241 - QRinput_estimateBitsMode8(q - string) /* - 4 - l8 */;
242 if(dif < 0) {
243 break;
244 } else {
245 p = q;
246 }
247 } else {
248 p++;
249 }
250 }
251
252 run = p - string;
253 ret = QRinput_append(input, QR_MODE_8, run, (unsigned char *)string);
254 if(ret < 0) return -1;
255
256 return run;
257}
258
259static int Split_splitString(const char *string, QRinput *input,
260 QRencodeMode hint)
261{
262 int length;
263 QRencodeMode mode;
264
265 if(*string == '\0') return 0;
266
267 mode = Split_identifyMode(string, hint);
268 if(mode == QR_MODE_NUM) {
269 length = Split_eatNum(string, input, hint);
270 } else if(mode == QR_MODE_AN) {
271 length = Split_eatAn(string, input, hint);
272 } else if(mode == QR_MODE_KANJI && hint == QR_MODE_KANJI) {
273 length = Split_eatKanji(string, input, hint);
274 } else {
275 length = Split_eat8(string, input, hint);
276 }
277 if(length == 0) return 0;
278 if(length < 0) return -1;
279 return Split_splitString(&string[length], input, hint);
280}
281
282static char *dupAndToUpper(const char *str, QRencodeMode hint)
283{
284 char *newstr, *p;
285 QRencodeMode mode;
286
287 newstr = strdup(str);
288 if(newstr == NULL) return NULL;
289
290 p = newstr;
291 while(*p != '\0') {
292 mode = Split_identifyMode(p, hint);
293 if(mode == QR_MODE_KANJI) {
294 p += 2;
295 } else {
296 if (*p >= 'a' && *p <= 'z') {
297 *p = (char)((int)*p - 32);
298 }
299 p++;
300 }
301 }
302
303 return newstr;
304}
305
306int Split_splitStringToQRinput(const char *string, QRinput *input,
307 QRencodeMode hint, int casesensitive)
308{
309 char *newstr;
310 int ret;
311
312 if(string == NULL || *string == '\0') {
313 errno = EINVAL;
314 return -1;
315 }
316 if(!casesensitive) {
317 newstr = dupAndToUpper(string, hint);
318 if(newstr == NULL) return -1;
319 ret = Split_splitString(newstr, input, hint);
320 free(newstr);
321 } else {
322 ret = Split_splitString(string, input, hint);
323 }
324
325 return ret;
326}