#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "common.h"
#include "code1.h"
#include "reedsol.h"
#include "large.h"
#define C1_MAX_CWS 1480
#define C1_MAX_CWS_S "1480"
#define C1_MAX_ECCS 560
#define C1_ASCII 1
#define C1_C40 2
#define C1_DECIMAL 3
#define C1_TEXT 4
#define C1_EDI 5
#define C1_BYTE 6
static void c1_horiz(struct zint_symbol *symbol, const int row_no, const int full) {
int i;
if (full) {
for (i = 0; i < symbol->width; i++) {
z_set_module(symbol, row_no, i);
}
} else {
for (i = 1; i < symbol->width - 1; i++) {
z_set_module(symbol, row_no, i);
}
}
}
static void c1_central_finder(struct zint_symbol *symbol, const int start_row, const int row_count,
const int full_rows) {
int i;
for (i = 0; i < row_count; i++) {
if (i < full_rows) {
c1_horiz(symbol, start_row + (i * 2), 1);
} else {
c1_horiz(symbol, start_row + (i * 2), 0);
if (i != row_count - 1) {
z_set_module(symbol, start_row + (i * 2) + 1, 1);
z_set_module(symbol, start_row + (i * 2) + 1, symbol->width - 2);
}
}
}
}
static void c1_vert(struct zint_symbol *symbol, const int top_col, const int top_height, const int bot_col,
const int bot_height) {
int i;
for (i = 0; i < top_height; i++) {
z_set_module(symbol, i, top_col);
}
for (i = 0; i < bot_height; i++) {
z_set_module(symbol, symbol->rows - i - 1, bot_col);
}
}
static void c1_spigot(struct zint_symbol *symbol, const int row1, const int row2) {
int i;
for (i = symbol->width - 1; i > 0; i--) {
if (z_module_is_set(symbol, row1, i - 1)) {
z_set_module(symbol, row1, i);
}
if (z_module_is_set(symbol, row2, i - 1)) {
z_set_module(symbol, row2, i);
}
}
}
static int c1_isc40(const unsigned char input) {
if (z_isdigit(input) || z_isupper(input) || input == ' ') {
return 1;
}
return 0;
}
static int c1_istext(const unsigned char input) {
if (z_isdigit(input) || z_islower(input) || input == ' ') {
return 1;
}
return 0;
}
static int c1_isc40text(const int current_mode, const unsigned char input) {
return current_mode == C1_C40 ? c1_isc40(input) : c1_istext(input);
}
static int c1_isedi(const unsigned char input) {
if (c1_isc40(input)) {
return 1;
}
if (input == 13 || input == '*' || input == '>') {
return 1;
}
return 0;
}
static int c1_is_step_Q4bi_applicable(const unsigned char source[], const int length, const int position) {
int i;
for (i = position; i < length && c1_isedi(source[i]); i++) {
if (source[i] == 13 || source[i] == '*' || source[i] == '>') {
return 1;
}
}
return 0;
}
#define C1_MULT 6
#define C1_MULT_1_DIV_2 3
#define C1_MULT_2_DIV_3 4
#define C1_MULT_1 6
#define C1_MULT_4_DIV_3 8
#define C1_MULT_2 12
#define C1_MULT_8_DIV_3 16
#define C1_MULT_3 18
#define C1_MULT_10_DIV_3 20
#define C1_MULT_13_DIV_3 26
#define C1_MULT_MINUS_1 5
#define C1_MULT_CEIL(n) ((((n) + C1_MULT_MINUS_1) / C1_MULT) * C1_MULT)
static int c1_look_ahead_test(const unsigned char source[], const int length, const int position,
const int current_mode, const int gs1) {
int ascii_count, c40_count, text_count, edi_count, byte_count;
int ascii_rnded, c40_rnded, text_rnded, edi_rnded, byte_rnded;
int cnt_1;
int sp;
if (current_mode == C1_ASCII) {
ascii_count = 0;
c40_count = C1_MULT_1;
text_count = C1_MULT_1;
edi_count = C1_MULT_1;
byte_count = C1_MULT_2;
} else {
ascii_count = C1_MULT_1;
c40_count = C1_MULT_2;
text_count = C1_MULT_2;
edi_count = C1_MULT_2;
byte_count = C1_MULT_3;
}
switch (current_mode) {
case C1_C40: c40_count = 0; break;
case C1_TEXT: text_count = 0; break;
case C1_EDI: edi_count = 0; break;
case C1_BYTE: byte_count = 0; break;
}
for (sp = position; sp < length; sp++) {
const unsigned char c = source[sp];
const int is_extended = !z_isascii(c);
if (z_isdigit(c)) {
ascii_count += C1_MULT_1_DIV_2;
} else {
if (is_extended) {
ascii_count = ceilf(ascii_count) + C1_MULT_2;
} else {
ascii_count = ceilf(ascii_count) + C1_MULT_1;
}
}
if (c1_isc40(c)) {
c40_count += C1_MULT_2_DIV_3;
} else if (is_extended) {
c40_count += C1_MULT_8_DIV_3;
} else {
c40_count += C1_MULT_4_DIV_3;
}
if (c1_istext(c)) {
text_count += C1_MULT_2_DIV_3;
} else if (is_extended) {
text_count += C1_MULT_8_DIV_3;
} else {
text_count += C1_MULT_4_DIV_3;
}
if (c1_isedi(c)) {
edi_count += C1_MULT_2_DIV_3;
} else if (is_extended) {
edi_count += C1_MULT_13_DIV_3;
} else {
edi_count += C1_MULT_10_DIV_3;
}
if (gs1 && c == '\x1D') {
byte_count += C1_MULT_3;
} else {
byte_count += C1_MULT_1;
}
if (sp >= position + 3) {
ascii_rnded = C1_MULT_CEIL(ascii_count);
c40_rnded = C1_MULT_CEIL(c40_count);
text_rnded = C1_MULT_CEIL(text_count);
edi_rnded = C1_MULT_CEIL(edi_count);
byte_rnded = C1_MULT_CEIL(byte_count);
cnt_1 = byte_count + C1_MULT_1;
if (cnt_1 <= ascii_rnded && cnt_1 <= c40_rnded && cnt_1 <= text_rnded && cnt_1 <= edi_rnded) {
return C1_BYTE;
}
cnt_1 = ascii_count + C1_MULT_1;
if (cnt_1 <= c40_rnded && cnt_1 <= text_rnded && cnt_1 <= edi_rnded && cnt_1 <= byte_rnded) {
return C1_ASCII;
}
cnt_1 = text_rnded + C1_MULT_1;
if (cnt_1 <= ascii_rnded && cnt_1 <= c40_rnded && cnt_1 <= edi_rnded && cnt_1 <= byte_rnded) {
return C1_TEXT;
}
cnt_1 = c40_rnded + C1_MULT_1;
if (cnt_1 <= ascii_rnded && cnt_1 <= text_rnded) {
if (c40_rnded < edi_rnded) {
return C1_C40;
}
if (c40_rnded == edi_rnded) {
if (c1_is_step_Q4bi_applicable(source, length, sp + 1)) {
return C1_EDI;
}
return C1_C40;
}
}
cnt_1 = edi_rnded + C1_MULT_1;
if (cnt_1 <= ascii_rnded && cnt_1 <= c40_rnded && cnt_1 <= text_rnded && cnt_1 <= byte_rnded) {
return C1_EDI;
}
}
}
ascii_rnded = C1_MULT_CEIL(ascii_count);
c40_rnded = C1_MULT_CEIL(c40_count);
text_rnded = C1_MULT_CEIL(text_count);
edi_rnded = C1_MULT_CEIL(edi_count);
byte_rnded = C1_MULT_CEIL(byte_count);
if (byte_count <= ascii_rnded && byte_count <= c40_rnded && byte_count <= text_rnded && byte_count <= edi_rnded) {
return C1_BYTE;
}
if (ascii_count <= c40_rnded && ascii_count <= text_rnded && ascii_count <= edi_rnded
&& ascii_count <= byte_rnded) {
return C1_ASCII;
}
if (c40_rnded <= text_rnded && c40_rnded <= edi_rnded) {
return C1_C40;
}
if (text_rnded <= edi_rnded) {
return C1_TEXT;
}
return C1_EDI;
}
static int c1_is_last_single_ascii(const unsigned char source[], const int length, const int sp) {
if (length - sp == 1 && source[sp] <= 127) {
return 1;
}
if (length - sp == 2 && z_is_twodigits(source, length, sp)) {
return 1;
}
return 0;
}
static void c1_set_num_digits(const unsigned char source[], const int length, int num_digits[]) {
int i;
for (i = length - 1; i >= 0; i--) {
if (z_isdigit(source[i])) {
num_digits[i] = num_digits[i + 1] + 1;
}
}
}
static int c1_cte_buffer_transfer(int cte_buffer[6], int cte_p, unsigned target[], int *p_tp) {
int cte_i, cte_e;
int tp = *p_tp;
cte_e = (cte_p / 3) * 3;
for (cte_i = 0; cte_i < cte_e; cte_i += 3) {
int iv = (1600 * cte_buffer[cte_i]) + (40 * cte_buffer[cte_i + 1]) + (cte_buffer[cte_i + 2]) + 1;
target[tp++] = iv >> 8;
target[tp++] = iv & 0xFF;
}
cte_p -= cte_e;
if (cte_p) {
memmove(cte_buffer, cte_buffer + cte_e, sizeof(int) * cte_p);
}
*p_tp = tp;
return cte_p;
}
static int c1_decimal_binary_transfer(char decimal_binary[24], int db_p, unsigned int target[], int *p_tp) {
int b_i, b_e, p;
int tp = *p_tp;
b_e = db_p & 0xF8;
for (b_i = 0; b_i < b_e; b_i += 8) {
int value = 0;
for (p = 0; p < 8; p++) {
value <<= 1;
value += decimal_binary[b_i + p] == '1';
}
target[tp++] = value;
}
db_p &= 0x07;
if (db_p) {
memmove(decimal_binary, decimal_binary + b_e, db_p);
}
*p_tp = tp;
return db_p;
}
static int c1_decimal_unlatch(char decimal_binary[24], int db_p, unsigned int target[], int *p_tp,
const int decimal_count, const unsigned char source[], int *p_sp) {
int sp = *p_sp;
int bits_left;
db_p = z_bin_append_posn(63, 6, decimal_binary, db_p);
if (db_p >= 8) {
db_p = c1_decimal_binary_transfer(decimal_binary, db_p, target, p_tp);
}
bits_left = (8 - db_p) & 0x07;
if (decimal_count >= 1 && bits_left >= 4) {
db_p = z_bin_append_posn(z_ctoi(source[sp]) + 1, 4, decimal_binary, db_p);
sp++;
if (bits_left == 6) {
db_p = z_bin_append_posn(1, 2, decimal_binary, db_p);
}
(void) c1_decimal_binary_transfer(decimal_binary, db_p, target, p_tp);
} else if (bits_left) {
if (bits_left >= 4) {
db_p = z_bin_append_posn(15, 4, decimal_binary, db_p);
}
if (bits_left == 2 || bits_left == 6) {
db_p = z_bin_append_posn(1, 2, decimal_binary, db_p);
}
(void) c1_decimal_binary_transfer(decimal_binary, db_p, target, p_tp);
}
*p_sp = sp;
return 0;
}
static int c1_codewords_remaining(struct zint_symbol *symbol, const int tp) {
int i;
if (symbol->option_2 == 10) {
if (tp > 24) {
return 38 - tp;
}
if (tp > 10) {
return 24 - tp;
}
return 10 - tp;
}
for (i = 6; i >= 0; i--) {
if (tp > c1_data_length[i]) {
return c1_data_length[i + 1] - tp;
}
}
return c1_data_length[0] - tp;
}
static int c1_c40text_cnt(const int current_mode, const int gs1, unsigned char input) {
int cnt;
if (gs1 && input == '\x1D') {
return 2;
}
cnt = 1;
if (!z_isascii(input)) {
cnt += 2;
input -= 128;
}
if ((current_mode == C1_C40 && c1_c40_shift[input]) || (current_mode == C1_TEXT && c1_text_shift[input])) {
cnt += 1;
}
return cnt;
}
static void c1_eci_escape(const int eci, unsigned char source[], const int length, unsigned char eci_buf[],
const int eci_length) {
int i, j;
j = sprintf((char *) eci_buf, "\\%06d", eci);
for (i = 0; i < length && j < eci_length; i++) {
if (source[i] == '\\') {
eci_buf[j++] = '\\';
}
eci_buf[j++] = source[i];
}
eci_buf[j] = '\0';
}
static int c1_encode(struct zint_symbol *symbol, unsigned char source[], int length, const int eci,
const int seg_count, const int gs1, unsigned int target[], int *p_tp, int *p_last_mode) {
int current_mode, next_mode, last_mode;
int sp = 0;
int tp = *p_tp;
int i;
int cte_buffer[6], cte_p = 0;
char decimal_binary[24];
int db_p = 0;
int byte_start = 0;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
const int eci_length = length + 7 + z_chr_cnt(source, length, '\\');
unsigned char *eci_buf = (unsigned char *) z_alloca(eci_length + 1);
int *num_digits = (int *) z_alloca(sizeof(int) * (eci_length + 1));
memset(num_digits, 0, sizeof(int) * (eci_length + 1));
current_mode = C1_ASCII;
next_mode = C1_ASCII;
if (gs1 && tp == 0) {
c1_set_num_digits(source, length, num_digits);
if (length >= 15 && num_digits[0] >= 15) {
target[tp++] = 236;
next_mode = C1_DECIMAL;
if (debug_print) fputs("FNC1Dec ", stdout);
} else if (length >= 7 && num_digits[0] == length) {
target[tp++] = 236;
next_mode = C1_DECIMAL;
if (debug_print) fputs("FNC1Dec ", stdout);
} else {
target[tp++] = 232;
if (debug_print) fputs("FNC1 ", stdout);
}
} else {
if (symbol->structapp.count && tp == 0) {
if (symbol->structapp.count < 16) {
if ((eci || seg_count > 1) && symbol->structapp.index == 1) {
target[tp++] = 129;
target[tp++] = 233;
target[tp++] = (symbol->structapp.index - 1) * 15 + (symbol->structapp.count - 1);
target[tp++] = '\\' + 1;
if (debug_print) fputs("SAGrp ", stdout);
} else {
target[tp++] = (symbol->structapp.index - 1) * 15 + (symbol->structapp.count - 1);
target[tp++] = 233;
if (debug_print) fputs("FNC2 ", stdout);
}
} else {
if ((eci || seg_count > 1) && symbol->structapp.index == 1) {
target[tp++] = 129;
target[tp++] = '\\' + 1;
target[tp++] = 233;
target[tp++] = symbol->structapp.index;
target[tp++] = symbol->structapp.count;
if (debug_print) fputs("SAExGrp ", stdout);
} else {
target[tp++] = symbol->structapp.index;
target[tp++] = symbol->structapp.count;
target[tp++] = 233;
if (debug_print) fputs("FNC2 ", stdout);
}
}
if (eci) {
c1_eci_escape(eci, source, length, eci_buf, eci_length);
source = eci_buf;
length = eci_length;
}
} else if (eci || seg_count > 1) {
if (tp == 0) {
target[tp++] = 129;
target[tp++] = '\\' + 1;
if (debug_print) fputs("PADEsc ", stdout);
}
if (eci) {
c1_eci_escape(eci, source, length, eci_buf, eci_length);
source = eci_buf;
length = eci_length;
}
}
c1_set_num_digits(source, length, num_digits);
}
do {
last_mode = current_mode;
if (current_mode != next_mode) {
switch (next_mode) {
case C1_C40:
target[tp++] = 230;
if (debug_print) fputs("->C40 ", stdout);
break;
case C1_TEXT:
target[tp++] = 239;
if (debug_print) fputs("->Text ", stdout);
break;
case C1_EDI:
target[tp++] = 238;
if (debug_print) fputs("->EDI ", stdout);
break;
case C1_BYTE:
target[tp++] = 231;
if (debug_print) fputs("->Byte ", stdout);
byte_start = tp;
target[tp++] = 0;
break;
}
current_mode = next_mode;
}
if (current_mode == C1_ASCII) {
next_mode = C1_ASCII;
if (length - sp >= 21 && num_digits[sp] >= 21) {
next_mode = C1_DECIMAL;
db_p = z_bin_append_posn(15, 4, decimal_binary, db_p);
} else if (length - sp >= 13 && num_digits[sp] == length - sp) {
next_mode = C1_DECIMAL;
db_p = z_bin_append_posn(15, 4, decimal_binary, db_p);
}
if (next_mode == C1_ASCII) {
if (z_is_twodigits(source, length, sp)) {
target[tp++] = z_to_int(source + sp, 2) + 130;
if (debug_print) printf("ASCDD(%.2s) ", source + sp);
sp += 2;
} else {
if (gs1 && source[sp] == '\x1D') {
if (length - (sp + 1) >= 15 && num_digits[sp + 1] >= 15) {
target[tp++] = 236;
if (debug_print) fputs("FNC1 ", stdout);
sp++;
next_mode = C1_DECIMAL;
} else if (length - (sp + 1) >= 7 && num_digits[sp + 1] == length - (sp + 1)) {
target[tp++] = 236;
if (debug_print) fputs("FNC1 ", stdout);
sp++;
next_mode = C1_DECIMAL;
}
}
if (next_mode == C1_ASCII) {
next_mode = c1_look_ahead_test(source, length, sp, current_mode, gs1);
if (next_mode == last_mode) {
next_mode = C1_ASCII;
}
if (next_mode == C1_ASCII) {
if (debug_print) printf("ASC(%d) ", source[sp]);
if (!z_isascii(source[sp])) {
target[tp++] = 235;
target[tp++] = (source[sp] - 128) + 1;
if (debug_print) printf("UpSh(%d) ", source[sp]);
} else if (gs1 && source[sp] == '\x1D') {
target[tp++] = 232;
if (debug_print) fputs("FNC1 ", stdout);
} else {
target[tp++] = source[sp] + 1;
if (debug_print) printf("ASC(%d) ", source[sp]);
}
sp++;
}
}
}
}
} else if (current_mode == C1_C40 || current_mode == C1_TEXT) {
next_mode = current_mode;
if (cte_p == 0) {
if (length - sp >= 12 && num_digits[sp] >= 12) {
next_mode = C1_ASCII;
} else if (length - sp >= 8 && num_digits[sp] == length - sp) {
next_mode = C1_ASCII;
} else {
next_mode = c1_look_ahead_test(source, length, sp, current_mode, gs1);
}
}
if (next_mode != current_mode) {
target[tp++] = 255;
if (debug_print) fputs("Unlatch ", stdout);
} else {
const char *ct_shift, *ct_value;
if (current_mode == C1_C40) {
ct_shift = c1_c40_shift;
ct_value = c1_c40_value;
} else {
ct_shift = c1_text_shift;
ct_value = c1_text_value;
}
if (debug_print) fputs(current_mode == C1_C40 ? "C40 " : "TEXT ", stdout);
if (!z_isascii(source[sp])) {
cte_buffer[cte_p++] = 1;
cte_buffer[cte_p++] = 30;
assert(source[sp] >= 128);
if (ct_shift[source[sp] - 128]) {
cte_buffer[cte_p++] = ct_shift[source[sp] - 128] - 1;
}
cte_buffer[cte_p++] = ct_value[source[sp] - 128];
} else if (gs1 && source[sp] == '\x1D') {
cte_buffer[cte_p++] = 1;
cte_buffer[cte_p++] = 27;
} else {
if (ct_shift[source[sp]]) {
cte_buffer[cte_p++] = ct_shift[source[sp]] - 1;
}
cte_buffer[cte_p++] = ct_value[source[sp]];
}
if (cte_p >= 3) {
cte_p = c1_cte_buffer_transfer(cte_buffer, cte_p, target, &tp);
}
sp++;
}
} else if (current_mode == C1_EDI) {
next_mode = C1_EDI;
if (cte_p == 0) {
if (length - sp >= 12 && num_digits[sp] >= 12) {
next_mode = C1_ASCII;
} else if (length - sp >= 8 && num_digits[sp] == length - sp) {
next_mode = C1_ASCII;
} else if (length - sp < 3 || !c1_isedi(source[sp]) || !c1_isedi(source[sp + 1])
|| !c1_isedi(source[sp + 2])) {
next_mode = C1_ASCII;
}
}
if (next_mode != C1_EDI) {
if (c1_is_last_single_ascii(source, length, sp) && c1_codewords_remaining(symbol, tp) == 1) {
} else {
target[tp++] = 255;
if (debug_print) fputs("Unlatch ", stdout);
}
} else {
static const char edi_nonalphanum_chars[] = "\015*> ";
if (debug_print) fputs("EDI ", stdout);
if (z_isdigit(source[sp])) {
cte_buffer[cte_p++] = source[sp] - '0' + 4;
} else if (z_isupper(source[sp])) {
cte_buffer[cte_p++] = source[sp] - 'A' + 14;
} else {
cte_buffer[cte_p++] = z_posn(edi_nonalphanum_chars, source[sp]);
}
if (cte_p >= 3) {
cte_p = c1_cte_buffer_transfer(cte_buffer, cte_p, target, &tp);
}
sp++;
}
} else if (current_mode == C1_DECIMAL) {
if (debug_print) fputs("DEC ", stdout);
next_mode = C1_DECIMAL;
if (length - sp < 3) {
const int bits_left = 8 - db_p;
const int can_ascii = bits_left == 8 && c1_is_last_single_ascii(source, length, sp);
if (c1_codewords_remaining(symbol, tp) == 1
&& (can_ascii || (num_digits[sp] == 1 && bits_left >= 4))) {
if (can_ascii) {
if (z_is_twodigits(source, length, sp)) {
target[tp++] = z_to_int(source + sp, 2) + 130;
if (debug_print) printf("ASCDD(%.2s) ", source + sp);
sp += 2;
} else {
target[tp++] = source[sp] + 1;
if (debug_print) printf("ASC(%d) ", source[sp]);
sp++;
}
} else {
db_p = z_bin_append_posn(z_ctoi(source[sp]) + 1, 4, decimal_binary, db_p);
sp++;
if (bits_left == 6) {
db_p = z_bin_append_posn(1, 2, decimal_binary, db_p);
}
db_p = c1_decimal_binary_transfer(decimal_binary, db_p, target, &tp);
}
} else {
db_p = c1_decimal_unlatch(decimal_binary, db_p, target, &tp, num_digits[sp], source, &sp);
current_mode = C1_ASCII;
}
next_mode = C1_ASCII;
} else {
if (num_digits[sp] < 3) {
db_p = c1_decimal_unlatch(decimal_binary, db_p, target, &tp, num_digits[sp], source, &sp);
current_mode = next_mode = C1_ASCII;
} else {
db_p = z_bin_append_posn(z_to_int(source + sp, 3) + 1, 10, decimal_binary, db_p);
if (db_p >= 8) {
db_p = c1_decimal_binary_transfer(decimal_binary, db_p, target, &tp);
}
sp += 3;
}
}
} else if (current_mode == C1_BYTE) {
next_mode = C1_BYTE;
if (gs1 && source[sp] == '\x1D') {
next_mode = C1_ASCII;
} else {
if (source[sp] <= 127) {
next_mode = c1_look_ahead_test(source, length, sp, current_mode, gs1);
}
}
if (next_mode != C1_BYTE) {
int byte_count = tp - (byte_start + 1);
if (byte_count <= 249) {
target[byte_start] = byte_count;
} else {
memmove(target + byte_start + 2, target + byte_start + 1, sizeof(unsigned int) * byte_count);
target[byte_start] = 249 + (byte_count / 250);
target[byte_start + 1] = (byte_count % 250);
tp++;
}
} else {
if (debug_print) printf("BYTE(%d) ", source[sp]);
target[tp++] = source[sp];
sp++;
}
}
if (tp > C1_MAX_CWS) {
if (debug_print) fputc('\n', stdout);
return 0;
}
} while (sp < length);
if (debug_print) {
printf("\nEnd Current Mode: %d, tp %d, cte_p %d, db_p %d\n", current_mode, tp, cte_p, db_p);
}
if (current_mode == C1_C40 || current_mode == C1_TEXT) {
if (cte_p >= 1) {
const int cws_remaining = c1_codewords_remaining(symbol, tp);
if (cws_remaining == 1 && cte_p == 1 && c1_isc40text(current_mode, source[sp - 1])) {
target[tp++] = source[sp - 1] + 1;
cte_p = 0;
} else if (cws_remaining == 2 && cte_p == 2) {
cte_buffer[cte_p++] = 0;
cte_p = c1_cte_buffer_transfer(cte_buffer, cte_p, target, &tp);
}
if (cte_p >= 1) {
int cnt, total_cnt = 0;
while (sp > 0 && cte_p % 3) {
sp--;
cnt = c1_c40text_cnt(current_mode, gs1, source[sp]);
total_cnt += cnt;
cte_p -= cnt;
}
tp -= (total_cnt / 3) * 2;
target[tp++] = 255;
for (; sp < length; sp++) {
if (z_is_twodigits(source, length, sp)) {
target[tp++] = z_to_int(source + sp, 2) + 130;
sp++;
} else if (!z_isascii(source[sp])) {
target[tp++] = 235;
target[tp++] = (source[sp] - 128) + 1;
} else if (gs1 && source[sp] == '\x1D') {
target[tp++] = 232;
} else {
target[tp++] = source[sp] + 1;
}
}
current_mode = C1_ASCII;
}
}
} else if (current_mode == C1_DECIMAL) {
int bits_left;
if (c1_codewords_remaining(symbol, tp) > 1) {
db_p = z_bin_append_posn(63, 6, decimal_binary, db_p);
}
if (db_p >= 8) {
db_p = c1_decimal_binary_transfer(decimal_binary, db_p, target, &tp);
}
bits_left = (8 - db_p) & 0x07;
if (bits_left) {
if (bits_left == 4 || bits_left == 6) {
db_p = z_bin_append_posn(15, 4, decimal_binary, db_p);
}
if (bits_left == 2 || bits_left == 6) {
db_p = z_bin_append_posn(1, 2, decimal_binary, db_p);
}
(void) c1_decimal_binary_transfer(decimal_binary, db_p, target, &tp);
}
current_mode = C1_ASCII;
} else if (current_mode == C1_BYTE) {
if (c1_codewords_remaining(symbol, tp) > 0) {
int byte_count = tp - (byte_start + 1);
if (byte_count <= 249) {
target[byte_start] = byte_count;
} else {
memmove(target + byte_start + 2, target + byte_start + 1, sizeof(unsigned int) * byte_count);
target[byte_start] = 249 + (byte_count / 250);
target[byte_start + 1] = (byte_count % 250);
tp++;
}
}
}
if (tp > C1_MAX_CWS) {
return 0;
}
*p_last_mode = current_mode;
if (debug_print) {
printf("Target (%d):", tp);
for (i = 0; i < tp; i++) {
printf(" [%d]", (int) target[i]);
}
printf("\nLast Mode: %d\n", *p_last_mode);
}
return tp;
}
static int c1_encode_segs(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count, const int gs1,
unsigned int target[], int *p_data_length, int *p_last_mode) {
int i;
int tp = 0;
const int content_segs = !gs1 && (symbol->output_options & BARCODE_CONTENT_SEGS);
for (i = 0; i < seg_count; i++) {
tp = c1_encode(symbol, segs[i].source, segs[i].length, segs[i].eci, seg_count, gs1, target, &tp, p_last_mode);
if (content_segs && segs[i].eci) {
z_ct_set_seg_eci(symbol, i, segs[i].eci);
}
}
*p_data_length = tp;
return 0;
}
static void c1_block_copy(struct zint_symbol *symbol, char datagrid[136][120], const int start_row,
const int start_col, const int height, const int width, const int row_offset, const int col_offset) {
int i, j;
for (i = start_row; i < (start_row + height); i++) {
for (j = start_col; j < (start_col + width); j++) {
if (datagrid[i][j]) {
z_set_module(symbol, i + row_offset, j + col_offset);
}
}
}
}
static int c1_total_length_segs(struct zint_seg segs[], const int seg_count) {
int total_len = 0;
int i;
if (segs[0].eci || seg_count > 1) {
for (i = 0; i < seg_count; i++) {
total_len += segs[i].length + 7 + z_chr_cnt(segs[i].source, segs[i].length, '\\');
}
} else {
total_len = segs[0].length;
}
return total_len;
}
INTERNAL int zint_codeone(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
int size = 1, i, j;
char datagrid[136][120];
int row, col;
int sub_version = 0;
rs_t rs;
const int gs1 = (symbol->input_mode & 0x07) == GS1_MODE;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
if (symbol->option_2 < 0 || symbol->option_2 > 10) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 513, "Version '%d' out of range (1 to 10)",
symbol->option_2);
}
if (symbol->structapp.count) {
if (symbol->option_2 == 9) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 714, "Structured Append not available for Version S");
}
if (gs1) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 710,
"Cannot have Structured Append and GS1 mode at the same time");
}
if (symbol->structapp.count < 2 || symbol->structapp.count > 128) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 711,
"Structured Append count '%d' out of range (2 to 128)", symbol->structapp.count);
}
if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
return ZEXT z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 712,
"Structured Append index '%1$d' out of range (1 to count %2$d)",
symbol->structapp.index, symbol->structapp.count);
}
if (symbol->structapp.id[0]) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 713,
"Structured Append ID not available for Code One");
}
}
if (symbol->option_2 == 9) {
int codewords;
large_uint elreg;
unsigned int target[30], ecc[15];
int block_width;
if (seg_count > 1) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 715, "Multiple segments not supported for Version S");
}
if (segs[0].length > 18) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 514, "Input length %d too long for Version S (maximum 18)",
segs[0].length);
}
if ((i = z_not_sane(NEON_F, segs[0].source, segs[0].length))) {
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 515,
"Invalid character at position %d in input (Version S encodes digits only)", i);
}
size = 9;
if (segs[0].length <= 6) {
sub_version = 1;
codewords = 4;
block_width = 2;
} else if (segs[0].length <= 12) {
sub_version = 2;
codewords = 8;
block_width = 4;
} else {
sub_version = 3;
codewords = 12;
block_width = 6;
}
if (debug_print) {
printf("Subversion: %d\n", sub_version);
}
zint_large_load_str_u64(&elreg, segs[0].source, segs[0].length);
zint_large_add_u64(&elreg, 1);
zint_large_uint_array(&elreg, target, codewords, 5 );
zint_rs_init_gf(&rs, 0x25);
zint_rs_init_code(&rs, codewords, 0);
zint_rs_encode_uint(&rs, codewords, target, ecc);
for (i = 0; i < codewords; i++) {
target[i + codewords] = ecc[i];
}
if (debug_print) {
printf("Codewords (%d): ", codewords);
for (i = 0; i < codewords * 2; i++) printf(" %d", (int) target[i]);
fputc('\n', stdout);
}
i = 0;
for (row = 0; row < 2; row++) {
for (col = 0; col < block_width; col++) {
datagrid[row * 2][col * 5] = target[i] & 0x10;
datagrid[row * 2][(col * 5) + 1] = target[i] & 0x08;
datagrid[row * 2][(col * 5) + 2] = target[i] & 0x04;
datagrid[(row * 2) + 1][col * 5] = target[i] & 0x02;
datagrid[(row * 2) + 1][(col * 5) + 1] = target[i] & 0x01;
datagrid[row * 2][(col * 5) + 3] = target[i + 1] & 0x10;
datagrid[row * 2][(col * 5) + 4] = target[i + 1] & 0x08;
datagrid[(row * 2) + 1][(col * 5) + 2] = target[i + 1] & 0x04;
datagrid[(row * 2) + 1][(col * 5) + 3] = target[i + 1] & 0x02;
datagrid[(row * 2) + 1][(col * 5) + 4] = target[i + 1] & 0x01;
i += 2;
}
}
symbol->rows = 8;
symbol->width = 10 * sub_version + 1;
} else if (symbol->option_2 == 10) {
unsigned int target[C1_MAX_CWS + C1_MAX_ECCS];
unsigned int ecc[22];
int data_length;
int data_cw, ecc_cw, block_width;
int last_mode = 0;
if ((i = c1_total_length_segs(segs, seg_count)) > 90) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 519, "Input length %d too long for Version T (maximum 90)",
i);
}
if (c1_encode_segs(symbol, segs, seg_count, gs1, target, &data_length, &last_mode)) {
return ZINT_ERROR_MEMORY;
}
assert(data_length);
if (data_length > 38) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 516,
"Input too long for Version T, requires %d codewords (maximum 38)", data_length);
}
size = 10;
if (data_length <= 10) {
sub_version = 1;
data_cw = 10;
ecc_cw = 10;
block_width = 4;
} else if (data_length <= 24) {
sub_version = 2;
data_cw = 24;
ecc_cw = 16;
block_width = 8;
} else {
sub_version = 3;
data_cw = 38;
ecc_cw = 22;
block_width = 12;
}
if (debug_print) {
printf("Padding: %d, Subversion: %d\n", data_cw - data_length, sub_version);
}
if (data_cw > data_length) {
if (last_mode != C1_ASCII && last_mode != C1_BYTE) {
target[data_length++] = 255;
}
for (i = data_length; i < data_cw; i++) {
target[i] = 129;
}
}
zint_rs_init_gf(&rs, 0x12d);
zint_rs_init_code(&rs, ecc_cw, 0);
zint_rs_encode_uint(&rs, data_cw, target, ecc);
for (i = 0; i < ecc_cw; i++) {
target[data_cw + i] = ecc[i];
}
if (debug_print) {
printf("Codewords (%d):", data_cw + ecc_cw);
for (i = 0; i < data_cw + ecc_cw; i++) printf(" %d", (int) target[i]);
fputc('\n', stdout);
}
i = 0;
for (row = 0; row < 5; row++) {
for (col = 0; col < block_width; col++) {
datagrid[row * 2][col * 4] = target[i] & 0x80;
datagrid[row * 2][(col * 4) + 1] = target[i] & 0x40;
datagrid[row * 2][(col * 4) + 2] = target[i] & 0x20;
datagrid[row * 2][(col * 4) + 3] = target[i] & 0x10;
datagrid[(row * 2) + 1][col * 4] = target[i] & 0x08;
datagrid[(row * 2) + 1][(col * 4) + 1] = target[i] & 0x04;
datagrid[(row * 2) + 1][(col * 4) + 2] = target[i] & 0x02;
datagrid[(row * 2) + 1][(col * 4) + 3] = target[i] & 0x01;
i++;
}
}
symbol->rows = 16;
symbol->width = (sub_version * 16) + 1;
} else {
unsigned int target[C1_MAX_CWS + C1_MAX_ECCS];
unsigned int sub_data[185], sub_ecc[70];
int data_length;
int data_cw;
int blocks, data_blocks, ecc_blocks, ecc_length;
int last_mode;
if (c1_encode_segs(symbol, segs, seg_count, gs1, target, &data_length, &last_mode)) {
return ZINT_ERROR_MEMORY;
}
if (data_length == 0) {
return z_errtxt(ZINT_ERROR_TOO_LONG, symbol, 517,
"Input too long, requires too many codewords (maximum " C1_MAX_CWS_S ")");
}
for (i = 7; i >= 0; i--) {
if (c1_data_length[i] >= data_length) {
size = i + 1;
}
}
if (symbol->option_2 > size) {
size = symbol->option_2;
}
if (symbol->option_2 && symbol->option_2 < size) {
return ZEXT z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 518,
"Input too long for Version %1$c, requires %2$d codewords (maximum %3$d)",
'A' + symbol->option_2 - 1, data_length, c1_data_length[symbol->option_2 - 1]);
}
symbol->option_2 = size;
data_cw = c1_data_length[size - 1];
if (data_cw > data_length) {
if (last_mode != C1_ASCII && last_mode != C1_BYTE) {
target[data_length++] = 255;
}
if (debug_print) {
printf("Padding: %d\n", data_cw - data_length);
}
for (i = data_length; i < data_cw; i++) {
target[i] = 129;
}
} else if (debug_print) {
fputs("No padding\n", stdout);
}
blocks = c1_blocks[size - 1];
data_blocks = c1_data_blocks[size - 1];
ecc_blocks = c1_ecc_blocks[size - 1];
ecc_length = c1_ecc_length[size - 1];
zint_rs_init_gf(&rs, 0x12d);
zint_rs_init_code(&rs, ecc_blocks, 0);
for (i = 0; i < blocks; i++) {
for (j = 0; j < data_blocks; j++) {
sub_data[j] = target[j * blocks + i];
}
zint_rs_encode_uint(&rs, data_blocks, sub_data, sub_ecc);
for (j = 0; j < ecc_blocks; j++) {
target[data_cw + j * blocks + i] = sub_ecc[j];
}
}
if (debug_print) {
printf("Codewords (%d):", data_cw + ecc_length);
for (i = 0; i < data_cw + ecc_length; i++) printf(" %d", (int) target[i]);
fputc('\n', stdout);
}
i = 0;
for (row = 0; row < c1_grid_height[size - 1]; row++) {
for (col = 0; col < c1_grid_width[size - 1]; col++) {
datagrid[row * 2][col * 4] = target[i] & 0x80;
datagrid[row * 2][(col * 4) + 1] = target[i] & 0x40;
datagrid[row * 2][(col * 4) + 2] = target[i] & 0x20;
datagrid[row * 2][(col * 4) + 3] = target[i] & 0x10;
datagrid[(row * 2) + 1][col * 4] = target[i] & 0x08;
datagrid[(row * 2) + 1][(col * 4) + 1] = target[i] & 0x04;
datagrid[(row * 2) + 1][(col * 4) + 2] = target[i] & 0x02;
datagrid[(row * 2) + 1][(col * 4) + 3] = target[i] & 0x01;
i++;
}
}
symbol->rows = c1_height[size - 1];
symbol->width = c1_width[size - 1];
}
if (debug_print) {
printf("Version: %d\n", size);
}
switch (size) {
case 1:
c1_central_finder(symbol, 6, 3, 1);
c1_vert(symbol, 4, 6, 12, 5);
z_set_module(symbol, 5, 12);
c1_spigot(symbol, 0, 15);
c1_block_copy(symbol, datagrid, 0, 0, 5, 4, 0, 0);
c1_block_copy(symbol, datagrid, 0, 4, 5, 12, 0, 2);
c1_block_copy(symbol, datagrid, 5, 0, 5, 12, 6, 0);
c1_block_copy(symbol, datagrid, 5, 12, 5, 4, 6, 2);
break;
case 2:
c1_central_finder(symbol, 8, 4, 1);
c1_vert(symbol, 4, 8, 16, 7);
z_set_module(symbol, 7, 16);
c1_spigot(symbol, 0, 21);
c1_block_copy(symbol, datagrid, 0, 0, 7, 4, 0, 0);
c1_block_copy(symbol, datagrid, 0, 4, 7, 16, 0, 2);
c1_block_copy(symbol, datagrid, 7, 0, 7, 16, 8, 0);
c1_block_copy(symbol, datagrid, 7, 16, 7, 4, 8, 2);
break;
case 3:
c1_central_finder(symbol, 11, 4, 2);
c1_vert(symbol, 4, 11, 4, 10);
c1_vert(symbol, 26, 13, 26, 10);
c1_spigot(symbol, 0, 27);
c1_block_copy(symbol, datagrid, 0, 0, 10, 4, 0, 0);
c1_block_copy(symbol, datagrid, 0, 4, 10, 20, 0, 2);
c1_block_copy(symbol, datagrid, 0, 24, 10, 4, 0, 4);
c1_block_copy(symbol, datagrid, 10, 0, 10, 4, 8, 0);
c1_block_copy(symbol, datagrid, 10, 4, 10, 20, 8, 2);
c1_block_copy(symbol, datagrid, 10, 24, 10, 4, 8, 4);
break;
case 4:
c1_central_finder(symbol, 16, 5, 1);
c1_vert(symbol, 4, 16, 4, 15);
c1_vert(symbol, 20, 16, 20, 15);
c1_vert(symbol, 36, 16, 36, 15);
c1_spigot(symbol, 0, 27);
c1_spigot(symbol, 12, 39);
c1_block_copy(symbol, datagrid, 0, 0, 15, 4, 0, 0);
c1_block_copy(symbol, datagrid, 0, 4, 15, 14, 0, 2);
c1_block_copy(symbol, datagrid, 0, 18, 15, 14, 0, 4);
c1_block_copy(symbol, datagrid, 0, 32, 15, 4, 0, 6);
c1_block_copy(symbol, datagrid, 15, 0, 15, 4, 10, 0);
c1_block_copy(symbol, datagrid, 15, 4, 15, 14, 10, 2);
c1_block_copy(symbol, datagrid, 15, 18, 15, 14, 10, 4);
c1_block_copy(symbol, datagrid, 15, 32, 15, 4, 10, 6);
break;
case 5:
c1_central_finder(symbol, 22, 5, 2);
c1_vert(symbol, 4, 22, 4, 21);
c1_vert(symbol, 26, 24, 26, 21);
c1_vert(symbol, 48, 22, 48, 21);
c1_spigot(symbol, 0, 39);
c1_spigot(symbol, 12, 51);
c1_block_copy(symbol, datagrid, 0, 0, 21, 4, 0, 0);
c1_block_copy(symbol, datagrid, 0, 4, 21, 20, 0, 2);
c1_block_copy(symbol, datagrid, 0, 24, 21, 20, 0, 4);
c1_block_copy(symbol, datagrid, 0, 44, 21, 4, 0, 6);
c1_block_copy(symbol, datagrid, 21, 0, 21, 4, 10, 0);
c1_block_copy(symbol, datagrid, 21, 4, 21, 20, 10, 2);
c1_block_copy(symbol, datagrid, 21, 24, 21, 20, 10, 4);
c1_block_copy(symbol, datagrid, 21, 44, 21, 4, 10, 6);
break;
case 6:
c1_central_finder(symbol, 31, 5, 3);
c1_vert(symbol, 4, 31, 4, 30);
c1_vert(symbol, 26, 35, 26, 30);
c1_vert(symbol, 48, 31, 48, 30);
c1_vert(symbol, 70, 35, 70, 30);
for (i = 0; i <= 24; i += 12) {
c1_spigot(symbol, i, i + 45);
}
c1_block_copy(symbol, datagrid, 0, 0, 30, 4, 0, 0);
c1_block_copy(symbol, datagrid, 0, 64, 30, 4, 0, 8);
c1_block_copy(symbol, datagrid, 30, 0, 30, 4, 10, 0);
c1_block_copy(symbol, datagrid, 30, 64, 30, 4, 10, 8);
for (i = 4, j = 2; i <= 44; i += 20, j += 2) {
c1_block_copy(symbol, datagrid, 0, i, 30, 20, 0, j);
c1_block_copy(symbol, datagrid, 30, i, 30, 20, 10, j);
}
break;
case 7:
c1_central_finder(symbol, 47, 6, 2);
c1_vert(symbol, 6, 47, 6, 46);
c1_vert(symbol, 27, 49, 27, 46);
c1_vert(symbol, 48, 47, 48, 46);
c1_vert(symbol, 69, 49, 69, 46);
c1_vert(symbol, 90, 47, 90, 46);
for (i = 0; i <= 36; i += 12) {
c1_spigot(symbol, i, i + 67);
}
c1_block_copy(symbol, datagrid, 0, 0, 46, 6, 0, 0);
c1_block_copy(symbol, datagrid, 0, 82, 46, 6, 0, 10);
c1_block_copy(symbol, datagrid, 46, 0, 46, 6, 12, 0);
c1_block_copy(symbol, datagrid, 46, 82, 46, 6, 12, 10);
for (i = 6, j = 2; i <= 63; i += 19, j += 2) {
c1_block_copy(symbol, datagrid, 0, i, 46, 19, 0, j);
c1_block_copy(symbol, datagrid, 46, i, 46, 19, 12, j);
}
break;
case 8:
c1_central_finder(symbol, 69, 6, 3);
c1_vert(symbol, 6, 69, 6, 68);
c1_vert(symbol, 26, 73, 26, 68);
c1_vert(symbol, 46, 69, 46, 68);
c1_vert(symbol, 66, 73, 66, 68);
c1_vert(symbol, 86, 69, 86, 68);
c1_vert(symbol, 106, 73, 106, 68);
c1_vert(symbol, 126, 69, 126, 68);
for (i = 0; i <= 60; i += 12) {
c1_spigot(symbol, i, i + 87);
}
c1_block_copy(symbol, datagrid, 0, 0, 68, 6, 0, 0);
c1_block_copy(symbol, datagrid, 0, 114, 68, 6, 0, 14);
c1_block_copy(symbol, datagrid, 68, 0, 68, 6, 12, 0);
c1_block_copy(symbol, datagrid, 68, 114, 68, 6, 12, 14);
for (i = 6, j = 2; i <= 96; i += 18, j += 2) {
c1_block_copy(symbol, datagrid, 0, i, 68, 18, 0, j);
c1_block_copy(symbol, datagrid, 68, i, 68, 18, 12, j);
}
break;
case 9:
c1_horiz(symbol, 5, 1);
c1_horiz(symbol, 7, 1);
z_set_module(symbol, 6, 0);
z_set_module(symbol, 6, symbol->width - 1);
z_unset_module(symbol, 7, 1);
z_unset_module(symbol, 7, symbol->width - 2);
switch (sub_version) {
case 1:
z_set_module(symbol, 0, 5);
c1_block_copy(symbol, datagrid, 0, 0, 4, 5, 0, 0);
c1_block_copy(symbol, datagrid, 0, 5, 4, 5, 0, 1);
break;
case 2:
z_set_module(symbol, 0, 10);
z_set_module(symbol, 4, 10);
c1_block_copy(symbol, datagrid, 0, 0, 4, 10, 0, 0);
c1_block_copy(symbol, datagrid, 0, 10, 4, 10, 0, 1);
break;
case 3:
z_set_module(symbol, 0, 15);
z_set_module(symbol, 4, 15);
z_set_module(symbol, 6, 15);
c1_block_copy(symbol, datagrid, 0, 0, 4, 15, 0, 0);
c1_block_copy(symbol, datagrid, 0, 15, 4, 15, 0, 1);
break;
}
break;
case 10:
c1_horiz(symbol, 11, 1);
c1_horiz(symbol, 13, 1);
c1_horiz(symbol, 15, 1);
z_set_module(symbol, 12, 0);
z_set_module(symbol, 12, symbol->width - 1);
z_set_module(symbol, 14, 0);
z_set_module(symbol, 14, symbol->width - 1);
z_unset_module(symbol, 13, 1);
z_unset_module(symbol, 13, symbol->width - 2);
z_unset_module(symbol, 15, 1);
z_unset_module(symbol, 15, symbol->width - 2);
switch (sub_version) {
case 1:
z_set_module(symbol, 0, 8);
z_set_module(symbol, 10, 8);
c1_block_copy(symbol, datagrid, 0, 0, 10, 8, 0, 0);
c1_block_copy(symbol, datagrid, 0, 8, 10, 8, 0, 1);
break;
case 2:
z_set_module(symbol, 0, 16);
z_set_module(symbol, 10, 16);
z_set_module(symbol, 12, 16);
c1_block_copy(symbol, datagrid, 0, 0, 10, 16, 0, 0);
c1_block_copy(symbol, datagrid, 0, 16, 10, 16, 0, 1);
break;
case 3:
z_set_module(symbol, 0, 24);
z_set_module(symbol, 10, 24);
z_set_module(symbol, 12, 24);
z_set_module(symbol, 14, 24);
c1_block_copy(symbol, datagrid, 0, 0, 10, 24, 0, 0);
c1_block_copy(symbol, datagrid, 0, 24, 10, 24, 0, 1);
break;
}
break;
}
for (i = 0; i < symbol->rows; i++) {
symbol->row_height[i] = 1;
}
symbol->height = symbol->rows;
if (symbol->option_2 == 9) {
if (symbol->eci || gs1) {
return z_errtxtf(ZINT_WARN_INVALID_OPTION, symbol, 511, "%s ignored for Version S",
symbol->eci && gs1 ? "ECI and GS1 mode" : symbol->eci ? "ECI" : "GS1 mode");
}
} else if (symbol->eci && gs1) {
return z_errtxt(ZINT_WARN_INVALID_OPTION, symbol, 512, "ECI ignored for GS1 mode");
}
return 0;
}