/* code1.c - USS Code One */
/*
    libzint - the open source barcode library
    Copyright (C) 2009-2022 Robin Stuart <rstuart114@gmail.com>

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.
    3. Neither the name of the project nor the names of its contributors
       may be used to endorse or promote products derived from this software
       without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.
 */
/* SPDX-License-Identifier: BSD-3-Clause */

#include <math.h>
#include <stdio.h>
#include "common.h"
#include "code1.h"
#include "reedsol.h"
#include "large.h"

/* Add solid bar */
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++) {
            set_module(symbol, row_no, i);
        }
    } else {
        for (i = 1; i < symbol->width - 1; i++) {
            set_module(symbol, row_no, i);
        }
    }
}

/* Central recognition pattern for Versions A-H */
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) {
                set_module(symbol, start_row + (i * 2) + 1, 1);
                set_module(symbol, start_row + (i * 2) + 1, symbol->width - 2);
            }
        }
    }
}

/* Add solid column */
static void c1_vert(struct zint_symbol *symbol, const int column, const int height, const int top) {
    int i;

    if (top) {
        for (i = 0; i < height; i++) {
            set_module(symbol, i, column);
        }
    } else {
        for (i = 0; i < height; i++) {
            set_module(symbol, symbol->rows - i - 1, column);
        }
    }
}

/* Add bump to the right of the vertical recognition pattern column (Versions A-H) */
static void c1_spigot(struct zint_symbol *symbol, const int row_no) {
    int i;

    for (i = symbol->width - 1; i > 0; i--) {
        if (module_is_set(symbol, row_no, i - 1)) {
            set_module(symbol, row_no, i);
        }
    }
}

/* Is basic (non-shifted) C40? */
static int c1_isc40(const unsigned char input) {
    if (z_isdigit(input) || z_isupper(input) || input == ' ') {
        return 1;
    }
    return 0;
}

/* Is basic (non-shifted) TEXT? */
static int c1_istext(const unsigned char input) {
    if (z_isdigit(input) || z_islower(input) || input == ' ') {
        return 1;
    }
    return 0;
}

/* Is basic (non-shifted) C40/TEXT? */
static int c1_isc40text(const int current_mode, const unsigned char input) {
    return current_mode == C1_C40 ? c1_isc40(input) : c1_istext(input);
}

/* EDI characters are uppercase alphanumerics plus space plus EDI terminator (CR) plus 2 EDI separator chars */
static int c1_isedi(const unsigned char input) {

    if (c1_isc40(input)) {
        return 1;
    }
    if (input == 13 || input == '*' || input == '>') {
        return 1;
    }

    return 0;
}

/* Whether Step Q4bi applies, i.e. if one of the 3 EDI terminator/separator chars appears before a non-EDI char */
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;
}

/* Character counts are multiplied by this, so as to be whole integer divisible by 2 and 3 */
#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)

/* AIM USS Code One Annex D Steps J-R */
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;

    /* Step J1 */
    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; /* Step J2 */
            break;
        case C1_TEXT: text_count = 0; /* Step J3 */
            break;
        case C1_EDI: edi_count = 0; /* Missing in spec */
            break;
        case C1_BYTE: byte_count = 0; /* Step J4 */
            break;
    }

    for (sp = position; sp < length; sp++) {
        const unsigned char c = source[sp];
        const int is_extended = c & 0x80;

        /* Step L */
        if (z_isdigit(c)) {
            ascii_count += C1_MULT_1_DIV_2; /* Step L1 */
        } else {
            if (is_extended) {
                ascii_count = ceilf(ascii_count) + C1_MULT_2; /* Step L2 */
            } else {
                ascii_count = ceilf(ascii_count) + C1_MULT_1; /* Step L3 */
            }
        }

        /* Step M */
        if (c1_isc40(c)) {
            c40_count += C1_MULT_2_DIV_3; /* Step M1 */
        } else if (is_extended) {
            c40_count += C1_MULT_8_DIV_3; /* Step M2 */
        } else {
            c40_count += C1_MULT_4_DIV_3; /* Step M3 */
        }

        /* Step N */
        if (c1_istext(c)) {
            text_count += C1_MULT_2_DIV_3; /* Step N1 */
        } else if (is_extended) {
            text_count += C1_MULT_8_DIV_3; /* Step N2 */
        } else {
            text_count += C1_MULT_4_DIV_3; /* Step N3 */
        }

        /* Step O */
        if (c1_isedi(c)) {
            edi_count += C1_MULT_2_DIV_3; /* Step O1 */
        } else if (is_extended) {
            edi_count += C1_MULT_13_DIV_3; /* Step O2 */
        } else {
            edi_count += C1_MULT_10_DIV_3; /* Step O3 */
        }

        /* Step P */
        if (gs1 && (c == '[')) {
            byte_count += C1_MULT_3; /* Step P1 */
        } else {
            byte_count += C1_MULT_1; /* Step P2 */
        }

        /* If at least 4 characters processed */
        /* NOTE: different than spec, where it's at least 3, but that ends up suppressing C40/TEXT/EDI.
           BWIPP also uses 4 (cf very similar Data Matrix ISO/IEC 16022:2006 Annex P algorithm) */
        if (sp >= position + 3) {
            /* Step Q */
            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; /* Step Q1 */
            }
            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; /* Step Q2 */
            }
            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; /* Step Q3 */
            }
            cnt_1 = c40_rnded + C1_MULT_1;
            if (cnt_1 <= ascii_rnded && cnt_1 <= text_rnded) {
                /* Step Q4 */
                if (c40_rnded < edi_rnded) {
                    return C1_C40; /* Step Q4a */
                }
                if (c40_rnded == edi_rnded) {
                    /* Step Q4b */
                    if (c1_is_step_Q4bi_applicable(source, length, sp + 1)) {
                        return C1_EDI; /* Step Q4bi */
                    }
                    return C1_C40; /* Step Q4bii */
                }
            }
            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; /* Step Q5 */
            }
        }
    }

    /* Step K */
    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; /* Step K1 */
    }
    if (ascii_count <= c40_rnded && ascii_count <= text_rnded && ascii_count <= edi_rnded
            && ascii_count <= byte_rnded) {
        return C1_ASCII; /* Step K2 */
    }
    if (c40_rnded <= text_rnded && c40_rnded <= edi_rnded) {
        return C1_C40; /* Step K3 */
    }
    if (text_rnded <= edi_rnded) {
        return C1_TEXT; /* Step K4 */
    }

    return C1_EDI; /* Step K5 */
}

/* Whether can fit last character or characters in a single ASCII codeword */
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 && is_twodigits(source, length, sp)) {
        return 1;
    }
    return 0;
}

/* Initialize number of digits array (taken from BWIPP) */
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;
        }
    }
}

/* Copy C40/TEXT/EDI triplets from buffer to `target`. Returns elements left in buffer (< 3) */
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;
}

/* Copy DECIMAL bytes to `target`. Returns bits left in buffer (< 8) */
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;

    /* Transfer full bytes to target */
    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; /* Bits remaining */

    if (db_p) {
        memmove(decimal_binary, decimal_binary + b_e, db_p);
    }

    *p_tp = tp;

    return db_p;
}

/* Unlatch to ASCII from DECIMAL mode using 6 ones flag. DECIMAL binary buffer will be empty */
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 = bin_append_posn(63, 6, decimal_binary, db_p); /* Unlatch */
    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 = bin_append_posn(ctoi(source[sp]) + 1, 4, decimal_binary, db_p);
        sp++;
        if (bits_left == 6) {
            db_p = 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 = bin_append_posn(15, 4, decimal_binary, db_p);
        }
        if (bits_left == 2 || bits_left == 6) {
            db_p = 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;
}

/* Number of codewords remaining in a particular version (may be negative) */
static int c1_codewords_remaining(struct zint_symbol *symbol, const int tp) {
    int i;

    if (symbol->option_2 == 10) { /* Version T */
        if (tp > 24) {
            return 38 - tp;
        }
        if (tp > 10) {
            return 24 - tp;
        }
        return 10 - tp;
    }
    /* Versions A to H */
    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;
}

/* Number of C40/TEXT elements needed to encode `input` */
static int c1_c40text_cnt(const int current_mode, const int gs1, unsigned char input) {
    int cnt;

    if (gs1 && input == '[') {
        return 2;
    }
    cnt = 1;
    if ((current_mode == C1_C40 && c40_shift[input]) || (current_mode == C1_TEXT && text_shift[input])) {
        cnt += 1;
    }

    return cnt;
}

/* Copy `source` to `eci_buf` with "\NNNNNN" ECI indicator at start and backslashes escaped */
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';
}

/* Convert to codewords */
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;
    int sp = 0;
    int tp = *p_tp;
    int i;
    int cte_buffer[6], cte_p = 0; /* C1_C40/TEXT/EDI buffer and index */
    char decimal_binary[24]; /* C1_DECIMAL buffer */
    int db_p = 0;
    int byte_start = 0;
    const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
    const int eci_length = length + 7 + 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));

    /* Step A */
    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; /* FNC1 and change to Decimal */
            next_mode = C1_DECIMAL;
            if (debug_print) printf("FNC1Dec ");
        } else if (length >= 7 && num_digits[0] == length) {
            target[tp++] = 236; /* FNC1 and change to Decimal */
            next_mode = C1_DECIMAL;
            if (debug_print) printf("FNC1Dec ");
        } else {
            target[tp++] = 232; /* FNC1 */
            if (debug_print) printf("FNC1 ");
        }
        /* Note ignoring Structured Append and ECI if GS1 mode (up to caller to warn/error) */
    } else {
        if (symbol->structapp.count && tp == 0) {
            if (symbol->structapp.count < 16) { /* Group mode */
                if ((eci || seg_count > 1) && symbol->structapp.index == 1) {
                    /* Initial pad indicator for 1st symbol only */
                    target[tp++] = 129; /* Pad */
                    target[tp++] = 233; /* FNC2 */
                    target[tp++] = (symbol->structapp.index - 1) * 15 + (symbol->structapp.count - 1);
                    target[tp++] = '\\' + 1; /* Escape char */
                    if (debug_print) printf("SAGrp ");
                } else {
                    target[tp++] = (symbol->structapp.index - 1) * 15 + (symbol->structapp.count - 1);
                    target[tp++] = 233; /* FNC2 */
                    if (debug_print) printf("FNC2 ");
                }
            } else { /* Extended Group mode */
                if ((eci || seg_count > 1) && symbol->structapp.index == 1) {
                    /* Initial pad indicator for 1st symbol only */
                    target[tp++] = 129; /* Pad */
                    target[tp++] = '\\' + 1; /* Escape char */
                    target[tp++] = 233; /* FNC2 */
                    target[tp++] = symbol->structapp.index;
                    target[tp++] = symbol->structapp.count;
                    if (debug_print) printf("SAExGrp ");
                } else {
                    target[tp++] = symbol->structapp.index;
                    target[tp++] = symbol->structapp.count;
                    target[tp++] = 233; /* FNC2 */
                    if (debug_print) printf("FNC2 ");
                }
            }
            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; /* Pad */
                target[tp++] = '\\' + 1; /* Escape char */
                if (debug_print) printf("PADEsc ");
            }
            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 {
        if (current_mode != next_mode) {
            /* Change mode */
            switch (next_mode) {
                case C1_C40: target[tp++] = 230;
                    if (debug_print) printf("->C40 ");
                    break;
                case C1_TEXT: target[tp++] = 239;
                    if (debug_print) printf("->Text ");
                    break;
                case C1_EDI: target[tp++] = 238;
                    if (debug_print) printf("->EDI ");
                    break;
                case C1_BYTE: target[tp++] = 231;
                    if (debug_print) printf("->Byte ");
                    byte_start = tp;
                    target[tp++] = 0; /* Byte count holder (may be expanded to 2 codewords) */
                    break;
            }
            current_mode = next_mode;
        }

        if (current_mode == C1_ASCII) {
            /* Step B - ASCII encodation */
            next_mode = C1_ASCII;

            if ((length - sp) >= 21 && num_digits[sp] >= 21) {
                /* Step B1 */
                next_mode = C1_DECIMAL;
                db_p = bin_append_posn(15, 4, decimal_binary, db_p);
            } else if ((length - sp) >= 13 && num_digits[sp] == (length - sp)) {
                /* Step B2 */
                next_mode = C1_DECIMAL;
                db_p = bin_append_posn(15, 4, decimal_binary, db_p);
            }

            if (next_mode == C1_ASCII) {
                if (is_twodigits(source, length, sp)) {
                    /* Step B3 */
                    target[tp++] = (10 * ctoi(source[sp])) + ctoi(source[sp + 1]) + 130;
                    if (debug_print) printf("ASCDD(%.2s) ", source + sp);
                    sp += 2;
                } else {
                    if ((gs1) && (source[sp] == '[')) {
                        if (length - (sp + 1) >= 15 && num_digits[sp + 1] >= 15) {
                            /* Step B4 */
                            target[tp++] = 236; /* FNC1 and change to Decimal */
                            if (debug_print) printf("FNC1 ");
                            sp++;
                            next_mode = C1_DECIMAL;
                        } else if (length - (sp + 1) >= 7 && num_digits[sp + 1] == length - (sp + 1)) {
                            /* Step B5 */
                            target[tp++] = 236; /* FNC1 and change to Decimal */
                            if (debug_print) printf("FNC1 ");
                            sp++;
                            next_mode = C1_DECIMAL;
                        }
                    }

                    if (next_mode == C1_ASCII) {

                        /* Step B6 */
                        next_mode = c1_look_ahead_test(source, length, sp, current_mode, gs1);

                        if (next_mode == C1_ASCII) {
                            if (debug_print) printf("ASC(%d) ", source[sp]);

                            if (source[sp] & 0x80) {
                                /* Step B7 */
                                target[tp++] = 235; /* FNC4 (Upper Shift) */
                                target[tp++] = (source[sp] - 128) + 1;
                                if (debug_print) printf("UpSh(%d) ", source[sp]);
                            } else if ((gs1) && (source[sp] == '[')) {
                                /* Step B8 */
                                target[tp++] = 232; /* FNC1 */
                                if (debug_print) printf("FNC1 ");
                            } else {
                                /* Step B8 */
                                target[tp++] = source[sp] + 1;
                                if (debug_print) printf("ASC(%d) ", source[sp]);
                            }
                            sp++;
                        }
                    }
                }
            }

        } else if (current_mode == C1_C40 || current_mode == C1_TEXT) {
            /* Step C/D - C40/TEXT encodation */

            next_mode = current_mode;
            if (cte_p == 0) {
                /* Step C/D1 */
                if ((length - sp) >= 12 && num_digits[sp] >= 12) {
                    /* Step C/D1a */
                    next_mode = C1_ASCII;
                } else if ((length - sp) >= 8 && num_digits[sp] == (length - sp)) {
                    /* Step C/D1b */
                    next_mode = C1_ASCII;
                } else {
                    next_mode = c1_look_ahead_test(source, length, sp, current_mode, gs1);
                }
            }

            if (next_mode != current_mode) {
                /* Step C/D1c */
                target[tp++] = 255; /* Unlatch */
                if (debug_print) printf("Unlatch ");
            } else {
                /* Step C/D2 */
                const char *ct_shift, *ct_value;

                if (current_mode == C1_C40) {
                    ct_shift = c40_shift;
                    ct_value = c40_value;
                } else {
                    ct_shift = text_shift;
                    ct_value = text_value;
                }
                if (debug_print) printf(current_mode == C1_C40 ? "C40 " : "TEXT ");

                if (source[sp] & 0x80) {
                    cte_buffer[cte_p++] = 1; /* Shift 2 */
                    cte_buffer[cte_p++] = 30; /* FNC4 (Upper Shift) */
                    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] == '[')) {
                    cte_buffer[cte_p++] = 1; /* Shift 2 */
                    cte_buffer[cte_p++] = 27; /* FNC1 */
                } 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) {
            /* Step E - EDI Encodation */

            next_mode = C1_EDI;
            if (cte_p == 0) {
                /* Step E1 */
                if ((length - sp) >= 12 && num_digits[sp] >= 12) {
                    /* Step E1a */
                    next_mode = C1_ASCII;
                } else if ((length - sp) >= 8 && num_digits[sp] == (length - sp)) {
                    /* Step E1b */
                    next_mode = C1_ASCII;
                } else if ((length - sp) < 3 || !c1_isedi(source[sp]) || !c1_isedi(source[sp + 1])
                        || !c1_isedi(source[sp + 2])) {
                    /* Step E1c */
                    /* This ensures ASCII switch if don't have EDI triplet, so cte_p will be zero on loop exit */
                    next_mode = C1_ASCII;
                }
            }

            if (next_mode != C1_EDI) {
                if (c1_is_last_single_ascii(source, length, sp) && c1_codewords_remaining(symbol, tp) == 1) {
                    /* No unlatch needed if data fits as ASCII in last data codeword */
                } else {
                    target[tp++] = 255; /* Unlatch */
                    if (debug_print) printf("Unlatch ");
                }
            } else {
                /* Step E2 */
                static const char edi_nonalphanum_chars[] = "\015*> ";

                if (debug_print) printf("EDI ");

                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++] = 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) {
            /* Step F - Decimal encodation */

            if (debug_print) printf("DEC ");

            next_mode = C1_DECIMAL;

            if (length - sp < 3) {
                /* Step F1 */
                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) {
                        /* Encode last character or last 2 digits as ASCII */
                        if (is_twodigits(source, length, sp)) {
                            target[tp++] = (10 * ctoi(source[sp])) + ctoi(source[sp + 1]) + 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 {
                        /* Encode last digit in 4 bits */
                        db_p = bin_append_posn(ctoi(source[sp]) + 1, 4, decimal_binary, db_p);
                        sp++;
                        if (bits_left == 6) {
                            db_p = 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; /* Note need to set current_mode also in case exit loop */
                }
                next_mode = C1_ASCII;

            } else {
                if (num_digits[sp] < 3) {
                    /* Step F2 */
                    db_p = c1_decimal_unlatch(decimal_binary, db_p, target, &tp, num_digits[sp], source, &sp);
                    current_mode = next_mode = C1_ASCII; /* Note need to set current_mode also in case exit loop */
                } else {
                    /* Step F3 */
                    /* There are three digits - convert the value to binary */
                    int value = (100 * ctoi(source[sp])) + (10 * ctoi(source[sp + 1])) + ctoi(source[sp + 2]) + 1;
                    db_p = bin_append_posn(value, 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] == '[')) {
                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) {
                /* Update byte field length */
                int byte_count = tp - (byte_start + 1);
                if (byte_count <= 249) {
                    target[byte_start] = byte_count;
                } else {
                    /* Insert extra codeword */
                    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 > 1480) {
            if (debug_print) printf("\n");
            /* Data is too large for symbol */
            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);
    }

    /* Empty buffers (note cte_buffer will be empty if current_mode C1_EDI) */
    if (current_mode == C1_C40 || current_mode == C1_TEXT) {
        if (cte_p >= 1) {
            const int cws_remaining = c1_codewords_remaining(symbol, tp);

            /* Note doing strict interpretation of spec here (same as BWIPP), as now also done in Data Matrix case */
            if (cws_remaining == 1 && cte_p == 1 && c1_isc40text(current_mode, source[sp - 1])) {
                /* 2.2.2.2 "...except when a single symbol character is left at the end before the first
                   error correction character. This single character is encoded in the ASCII code set." */
                target[tp++] = source[sp - 1] + 1; /* As ASCII */
                cte_p = 0;
            } else if (cws_remaining == 2 && cte_p == 2) {
                /* 2.2.2.2 "Two characters may be encoded in C40 mode in the last two data symbol characters of the
                   symbol as two C40 values followed by one of the C40 shift characters." */
                cte_buffer[cte_p++] = 0; /* Shift 0 */
                cte_p = c1_cte_buffer_transfer(cte_buffer, cte_p, target, &tp);
            }
            if (cte_p >= 1) {
                int cnt, total_cnt = 0;
                /* Backtrack to last complete triplet (same technique as BWIPP) */
                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; /* Unlatch */
                for (; sp < length; sp++) {
                    if (is_twodigits(source, length, sp)) {
                        target[tp++] = (10 * ctoi(source[sp])) + ctoi(source[sp + 1]) + 130;
                        sp++;
                    } else if (source[sp] & 0x80) {
                        target[tp++] = 235; /* FNC4 (Upper Shift) */
                        target[tp++] = (source[sp] - 128) + 1;
                    } else if ((gs1) && (source[sp] == '[')) {
                        target[tp++] = 232; /* FNC1 */
                    } else {
                        target[tp++] = source[sp] + 1;
                    }
                }
                current_mode = C1_ASCII;
            }
        }

    } else if (current_mode == C1_DECIMAL) {
        int bits_left;

        /* Finish Decimal mode and go back to ASCII unless only one codeword remaining */
        if (c1_codewords_remaining(symbol, tp) > 1) {
            db_p = bin_append_posn(63, 6, decimal_binary, db_p); /* Unlatch */
        }

        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 = bin_append_posn(15, 4, decimal_binary, db_p);
            }

            if (bits_left == 2 || bits_left == 6) {
                db_p = 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) {
        /* Update byte field length unless no codewords remaining */
        if (c1_codewords_remaining(symbol, tp) > 0) {
            int byte_count = tp - (byte_start + 1);
            if (byte_count <= 249) {
                target[byte_start] = byte_count;
            } else {
                /* Insert extra byte field byte */
                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++;
            }
        }
    }

    /* Re-check length of data */
    if (tp > 1480) {
        /* Data is too large for symbol */
        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;
}

/* Call `c1_encode()` for each segment */
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_last_mode) {
    int i;
    int tp = 0;

    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);
    }

    return tp;
}

/* Set symbol from datagrid */
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]) {
                set_module(symbol, i + row_offset, j + col_offset);
            }
        }
    }
}

/* Get total length allowing for ECIs and escaping backslashes */
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 + chr_cnt(segs[i].source, segs[i].length, '\\');
        }
    } else {
        total_len = segs[0].length;
    }

    return total_len;
}

INTERNAL int 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;
    int error_number = 0;
    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)) {
        strcpy(symbol->errtxt, "513: Invalid symbol size");
        return ZINT_ERROR_INVALID_OPTION;
    }

    if (symbol->structapp.count) {
        if (symbol->option_2 == 9) { /* Version S */
            strcpy(symbol->errtxt, "714: Structured Append not available for Version S");
            return ZINT_ERROR_INVALID_OPTION;
        }
        if (gs1) {
            strcpy(symbol->errtxt, "710: Cannot have Structured Append and GS1 mode at the same time");
            return ZINT_ERROR_INVALID_OPTION;
        }
        if (symbol->structapp.count < 2 || symbol->structapp.count > 128) {
            strcpy(symbol->errtxt, "711: Structured Append count out of range (2-128)");
            return ZINT_ERROR_INVALID_OPTION;
        }
        if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
            sprintf(symbol->errtxt, "712: Structured Append index out of range (1-%d)", symbol->structapp.count);
            return ZINT_ERROR_INVALID_OPTION;
        }
        if (symbol->structapp.id[0]) {
            strcpy(symbol->errtxt, "713: Structured Append ID not available for Code One");
            return ZINT_ERROR_INVALID_OPTION;
        }
    }

    if (symbol->option_2 == 9) {
        /* Version S */
        int codewords;
        large_int elreg;
        unsigned int target[30], ecc[15];
        int block_width;

        if (seg_count > 1) {
            strcpy(symbol->errtxt, "715: Multiple segments not supported for Version S");
            return ZINT_ERROR_INVALID_OPTION;
        }
        if (segs[0].length > 18) {
            strcpy(symbol->errtxt, "514: Input data too long for Version S");
            return ZINT_ERROR_TOO_LONG;
        }
        if (!is_sane(NEON_F, segs[0].source, segs[0].length)) {
            strcpy(symbol->errtxt, "515: Invalid input data (Version S encodes numeric input only)");
            return ZINT_ERROR_INVALID_DATA;
        }

        size = 9;
        if (segs[0].length <= 6) {
            /* Version S-10 */
            sub_version = 1;
            codewords = 4;
            block_width = 2;
        } else if (segs[0].length <= 12) {
            /* Version S-20 */
            sub_version = 2;
            codewords = 8;
            block_width = 4;
        } else {
            /* Version S-30 */
            sub_version = 3;
            codewords = 12;
            block_width = 6;
        }

        if (debug_print) {
            printf("Subversion: %d\n", sub_version);
        }

        /* Convert value plus one to binary */
        large_load_str_u64(&elreg, segs[0].source, segs[0].length);
        large_add_u64(&elreg, 1);
        large_uint_array(&elreg, target, codewords, 5 /*bits*/);

        rs_init_gf(&rs, 0x25);
        rs_init_code(&rs, codewords, 0);
        rs_encode_uint(&rs, codewords, target, ecc);

        for (i = 0; i < codewords; i++) {
            target[i + codewords] = ecc[codewords - i - 1];
        }

        if (debug_print) {
            printf("Codewords (%d): ", codewords);
            for (i = 0; i < codewords * 2; i++) printf(" %d", (int) target[i]);
            printf("\n");
        }

        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) {
        /* Version T */
        unsigned int target[90 + 2]; /* Allow for 90 BYTE mode (+ latch and byte count) */
        unsigned int ecc[22];
        int data_length;
        int data_cw, ecc_cw, block_width;
        int last_mode;

        if (c1_total_length_segs(segs, seg_count) > 90) {
            strcpy(symbol->errtxt, "519: Input data too long for Version T");
            return ZINT_ERROR_TOO_LONG;
        }

        data_length = c1_encode_segs(symbol, segs, seg_count, gs1, target, &last_mode);

        if (data_length == 0 || data_length > 38) {
            strcpy(symbol->errtxt, "516: Input data too long for Version T");
            return ZINT_ERROR_TOO_LONG;
        }

        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 require padding */
        if (data_cw > data_length) {
            /* If did not finish in ASCII or BYTE mode, switch to ASCII */
            if (last_mode != C1_ASCII && last_mode != C1_BYTE) {
                target[data_length++] = 255; /* Unlatch */
            }
            for (i = data_length; i < data_cw; i++) {
                target[i] = 129; /* Pad */
            }
        }

        /* Calculate error correction data */
        rs_init_gf(&rs, 0x12d);
        rs_init_code(&rs, ecc_cw, 0);
        rs_encode_uint(&rs, data_cw, target, ecc);

        for (i = 0; i < ecc_cw; i++) {
            target[data_cw + i] = ecc[ecc_cw - i - 1];
        }

        if (debug_print) {
            printf("Codewords (%d):", data_cw + ecc_cw);
            for (i = 0; i < data_cw + ecc_cw; i++) printf(" %d", (int) target[i]);
            printf("\n");
        }

        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 {
        /* Versions A to H */
        unsigned int target[1480 + 560];
        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;

        data_length = c1_encode_segs(symbol, segs, seg_count, gs1, target, &last_mode);

        if (data_length == 0) {
            strcpy(symbol->errtxt, "517: Input data is too long");
            return ZINT_ERROR_TOO_LONG;
        }

        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 != 0) && (symbol->option_2 < size)) {
            strcpy(symbol->errtxt, "518: Input too long for selected symbol size");
            return ZINT_ERROR_TOO_LONG;
        }
        data_cw = c1_data_length[size - 1];

        /* If require padding */
        if (data_cw > data_length) {
            /* If did not finish in ASCII or BYTE mode, switch to ASCII */
            if (last_mode != C1_ASCII && last_mode != C1_BYTE) {
                target[data_length++] = 255; /* Unlatch */
            }
            if (debug_print) {
                printf("Padding: %d\n", data_cw - data_length);
            }
            for (i = data_length; i < data_cw; i++) {
                target[i] = 129; /* Pad */
            }
        } else if (debug_print) {
            printf("No padding\n");
        }

        /* Calculate error correction data */
        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];

        rs_init_gf(&rs, 0x12d);
        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];
            }
            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[ecc_blocks - 1 - 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]);
            printf("\n");
        }

        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: /* Version A */
            c1_central_finder(symbol, 6, 3, 1);
            c1_vert(symbol, 4, 6, 1);
            c1_vert(symbol, 12, 5, 0);
            set_module(symbol, 5, 12);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 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: /* Version B */
            c1_central_finder(symbol, 8, 4, 1);
            c1_vert(symbol, 4, 8, 1);
            c1_vert(symbol, 16, 7, 0);
            set_module(symbol, 7, 16);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 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: /* Version C */
            c1_central_finder(symbol, 11, 4, 2);
            c1_vert(symbol, 4, 11, 1);
            c1_vert(symbol, 26, 13, 1);
            c1_vert(symbol, 4, 10, 0);
            c1_vert(symbol, 26, 10, 0);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 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: /* Version D */
            c1_central_finder(symbol, 16, 5, 1);
            c1_vert(symbol, 4, 16, 1);
            c1_vert(symbol, 20, 16, 1);
            c1_vert(symbol, 36, 16, 1);
            c1_vert(symbol, 4, 15, 0);
            c1_vert(symbol, 20, 15, 0);
            c1_vert(symbol, 36, 15, 0);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 12);
            c1_spigot(symbol, 27);
            c1_spigot(symbol, 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: /* Version E */
            c1_central_finder(symbol, 22, 5, 2);
            c1_vert(symbol, 4, 22, 1);
            c1_vert(symbol, 26, 24, 1);
            c1_vert(symbol, 48, 22, 1);
            c1_vert(symbol, 4, 21, 0);
            c1_vert(symbol, 26, 21, 0);
            c1_vert(symbol, 48, 21, 0);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 12);
            c1_spigot(symbol, 39);
            c1_spigot(symbol, 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: /* Version F */
            c1_central_finder(symbol, 31, 5, 3);
            c1_vert(symbol, 4, 31, 1);
            c1_vert(symbol, 26, 35, 1);
            c1_vert(symbol, 48, 31, 1);
            c1_vert(symbol, 70, 35, 1);
            c1_vert(symbol, 4, 30, 0);
            c1_vert(symbol, 26, 30, 0);
            c1_vert(symbol, 48, 30, 0);
            c1_vert(symbol, 70, 30, 0);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 12);
            c1_spigot(symbol, 24);
            c1_spigot(symbol, 45);
            c1_spigot(symbol, 57);
            c1_spigot(symbol, 69);
            c1_block_copy(symbol, datagrid, 0, 0, 30, 4, 0, 0);
            c1_block_copy(symbol, datagrid, 0, 4, 30, 20, 0, 2);
            c1_block_copy(symbol, datagrid, 0, 24, 30, 20, 0, 4);
            c1_block_copy(symbol, datagrid, 0, 44, 30, 20, 0, 6);
            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, 4, 30, 20, 10, 2);
            c1_block_copy(symbol, datagrid, 30, 24, 30, 20, 10, 4);
            c1_block_copy(symbol, datagrid, 30, 44, 30, 20, 10, 6);
            c1_block_copy(symbol, datagrid, 30, 64, 30, 4, 10, 8);
            break;
        case 7: /* Version G */
            c1_central_finder(symbol, 47, 6, 2);
            c1_vert(symbol, 6, 47, 1);
            c1_vert(symbol, 27, 49, 1);
            c1_vert(symbol, 48, 47, 1);
            c1_vert(symbol, 69, 49, 1);
            c1_vert(symbol, 90, 47, 1);
            c1_vert(symbol, 6, 46, 0);
            c1_vert(symbol, 27, 46, 0);
            c1_vert(symbol, 48, 46, 0);
            c1_vert(symbol, 69, 46, 0);
            c1_vert(symbol, 90, 46, 0);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 12);
            c1_spigot(symbol, 24);
            c1_spigot(symbol, 36);
            c1_spigot(symbol, 67);
            c1_spigot(symbol, 79);
            c1_spigot(symbol, 91);
            c1_spigot(symbol, 103);
            c1_block_copy(symbol, datagrid, 0, 0, 46, 6, 0, 0);
            c1_block_copy(symbol, datagrid, 0, 6, 46, 19, 0, 2);
            c1_block_copy(symbol, datagrid, 0, 25, 46, 19, 0, 4);
            c1_block_copy(symbol, datagrid, 0, 44, 46, 19, 0, 6);
            c1_block_copy(symbol, datagrid, 0, 63, 46, 19, 0, 8);
            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, 6, 46, 19, 12, 2);
            c1_block_copy(symbol, datagrid, 46, 25, 46, 19, 12, 4);
            c1_block_copy(symbol, datagrid, 46, 44, 46, 19, 12, 6);
            c1_block_copy(symbol, datagrid, 46, 63, 46, 19, 12, 8);
            c1_block_copy(symbol, datagrid, 46, 82, 46, 6, 12, 10);
            break;
        case 8: /* Version H */
            c1_central_finder(symbol, 69, 6, 3);
            c1_vert(symbol, 6, 69, 1);
            c1_vert(symbol, 26, 73, 1);
            c1_vert(symbol, 46, 69, 1);
            c1_vert(symbol, 66, 73, 1);
            c1_vert(symbol, 86, 69, 1);
            c1_vert(symbol, 106, 73, 1);
            c1_vert(symbol, 126, 69, 1);
            c1_vert(symbol, 6, 68, 0);
            c1_vert(symbol, 26, 68, 0);
            c1_vert(symbol, 46, 68, 0);
            c1_vert(symbol, 66, 68, 0);
            c1_vert(symbol, 86, 68, 0);
            c1_vert(symbol, 106, 68, 0);
            c1_vert(symbol, 126, 68, 0);
            c1_spigot(symbol, 0);
            c1_spigot(symbol, 12);
            c1_spigot(symbol, 24);
            c1_spigot(symbol, 36);
            c1_spigot(symbol, 48);
            c1_spigot(symbol, 60);
            c1_spigot(symbol, 87);
            c1_spigot(symbol, 99);
            c1_spigot(symbol, 111);
            c1_spigot(symbol, 123);
            c1_spigot(symbol, 135);
            c1_spigot(symbol, 147);
            c1_block_copy(symbol, datagrid, 0, 0, 68, 6, 0, 0);
            c1_block_copy(symbol, datagrid, 0, 6, 68, 18, 0, 2);
            c1_block_copy(symbol, datagrid, 0, 24, 68, 18, 0, 4);
            c1_block_copy(symbol, datagrid, 0, 42, 68, 18, 0, 6);
            c1_block_copy(symbol, datagrid, 0, 60, 68, 18, 0, 8);
            c1_block_copy(symbol, datagrid, 0, 78, 68, 18, 0, 10);
            c1_block_copy(symbol, datagrid, 0, 96, 68, 18, 0, 12);
            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, 6, 68, 18, 12, 2);
            c1_block_copy(symbol, datagrid, 68, 24, 68, 18, 12, 4);
            c1_block_copy(symbol, datagrid, 68, 42, 68, 18, 12, 6);
            c1_block_copy(symbol, datagrid, 68, 60, 68, 18, 12, 8);
            c1_block_copy(symbol, datagrid, 68, 78, 68, 18, 12, 10);
            c1_block_copy(symbol, datagrid, 68, 96, 68, 18, 12, 12);
            c1_block_copy(symbol, datagrid, 68, 114, 68, 6, 12, 14);
            break;
        case 9: /* Version S */
            c1_horiz(symbol, 5, 1);
            c1_horiz(symbol, 7, 1);
            set_module(symbol, 6, 0);
            set_module(symbol, 6, symbol->width - 1);
            unset_module(symbol, 7, 1);
            unset_module(symbol, 7, symbol->width - 2);
            switch (sub_version) {
                case 1: /* Version S-10 */
                    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: /* Version S-20 */
                    set_module(symbol, 0, 10);
                    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: /* Version S-30 */
                    set_module(symbol, 0, 15);
                    set_module(symbol, 4, 15);
                    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: /* Version T */
            c1_horiz(symbol, 11, 1);
            c1_horiz(symbol, 13, 1);
            c1_horiz(symbol, 15, 1);
            set_module(symbol, 12, 0);
            set_module(symbol, 12, symbol->width - 1);
            set_module(symbol, 14, 0);
            set_module(symbol, 14, symbol->width - 1);
            unset_module(symbol, 13, 1);
            unset_module(symbol, 13, symbol->width - 2);
            unset_module(symbol, 15, 1);
            unset_module(symbol, 15, symbol->width - 2);
            switch (sub_version) {
                case 1: /* Version T-16 */
                    set_module(symbol, 0, 8);
                    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: /* Version T-32 */
                    set_module(symbol, 0, 16);
                    set_module(symbol, 10, 16);
                    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: /* Verion T-48 */
                    set_module(symbol, 0, 24);
                    set_module(symbol, 10, 24);
                    set_module(symbol, 12, 24);
                    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) { /* Version S */
        if (symbol->eci || gs1) {
            sprintf(symbol->errtxt, "511: %s ignored for Version S",
                    symbol->eci && gs1 ? "ECI and GS1 mode" : symbol->eci ? "ECI" : "GS1 mode");
            error_number = ZINT_WARN_INVALID_OPTION;
        }
    } else if (symbol->eci && gs1) {
        strcpy(symbol->errtxt, "512: ECI ignored for GS1 mode");
        error_number = ZINT_WARN_INVALID_OPTION;
    }

    return error_number;
}

/* vim: set ts=4 sw=4 et : */
