typedef struct {
    int success;
    int count;
    int is_micro;
    int version;
    int ecl;
    int mask;
    int mode_kind;
    int rotation;
    int inverted;
    int bbox_x;
    int bbox_y;
    int bbox_width;
    int bbox_height;
    int encoding_kind;
    int used_eci;
    int fnc1_mode;
    int fnc1_app_id;
    int segment_count;
    int module_scale;
    int symbol_size;
    int detect_method;
    int finder_count;
    int finder_tl_x;
    int finder_tl_y;
    int finder_tr_x;
    int finder_tr_y;
    int finder_bl_x;
    int finder_bl_y;
    double quad_score;
    double quiet_zone_score;
    char *text;
    char *raw_text;
    char *data_format;
} B2026_QRReadInfo;

#define B2026_QRREAD_TEXT_RESULTS_MAX 32
#define B2026_QRREAD_MODE_AUTO 0
#define B2026_QRREAD_MODE_TEXT 1
#define B2026_QRREAD_MODE_BASE64 2
#define B2026_QRREAD_MODE_RAW 3

static int saveimage_write_png(const char *path, const unsigned char *image_data, int width, int height, int bytes_per_sample, int phys_x, int phys_y, int phys_unit);
const char *base64_encode_text_to_text(const char *text);
static unsigned char *base64_decode_text_malloc(const char *text, size_t *out_len, int *ok);
static char *b2026_qrread_quote_shell_arg(const char *text);

static void b2026_qrread_info_init(B2026_QRReadInfo *info) {
    if (info == NULL) return;
    memset(info, 0, sizeof(*info));
    info->ecl = -1;
    info->mask = -1;
    info->mode_kind = -1;
    info->encoding_kind = -1;
    info->used_eci = -1;
    info->fnc1_app_id = -1;
    info->finder_tl_x = -1;
    info->finder_tl_y = -1;
    info->finder_tr_x = -1;
    info->finder_tr_y = -1;
    info->finder_bl_x = -1;
    info->finder_bl_y = -1;
    info->quad_score = null_value();
    info->quiet_zone_score = null_value();
}

static void b2026_qrread_info_free(B2026_QRReadInfo *info) {
    if (info == NULL) return;
    free(info->text);
    info->text = NULL;
    free(info->raw_text);
    info->raw_text = NULL;
    free(info->data_format);
    info->data_format = NULL;
}

static char *b2026_qrread_strdup(const char *text) {
    size_t len;
    char *copy;
    if (text == NULL) text = "";
    len = strlen(text);
    copy = (char *)malloc(len + 1U);
    if (copy == NULL) return NULL;
    memcpy(copy, text, len + 1U);
    return copy;
}

static int b2026_qrread_probe_zbarimg_available(void) {
    const char *zbarimg_path = b2026_envset_zbarimg_path();
    FILE *fp;
    int status;
    char *quoted_path;
    char command[8192];
    if (b2026_envset_zbarimg_disabled()) return 0;
    if (zbarimg_path == NULL || zbarimg_path[0] == '\0') return 0;
    quoted_path = b2026_qrread_quote_shell_arg(zbarimg_path);
    if (quoted_path == NULL) return 0;
#ifdef _WIN32
    snprintf(command, sizeof(command), "%s --version >NUL 2>NUL", quoted_path);
#else
    snprintf(command, sizeof(command), "%s --version >/dev/null 2>/dev/null", quoted_path);
#endif
    free(quoted_path);
    fp = b2026_popen(command, "r");
    if (fp == NULL) return 0;
    status = b2026_pclose(fp);
    return status == 0;
}

static int b2026_qrread_require_zbarimg(void) {
    if (!b2026_qrread_probe_zbarimg_available()) {
        const char *message = "READQRCODE failed: zbarimg command is required / 【READQRCODE関数エラー】zbarimg コマンドが必要です。__BASIC2026解説/46_必要な外部コマンド を確認してください。";
        if (!(g_last_error.code == 7001 && g_last_error.line == current_line)) {
            basic_set_error_logged(7001, message, "READQRCODE", current_source_path, current_line, "BARCODE");
        }
        return 0;
    }
    return 1;
}

static char *b2026_qrread_quote_shell_arg(const char *text) {
    size_t i;
    size_t extra = 2U;
    size_t len;
    char *result;
    char *out;
    if (text == NULL) text = "";
    len = strlen(text);
#ifdef _WIN32
    for (i = 0; i < len; ++i) {
        if (text[i] == '"') extra += 2U;
        else if (text[i] == '%') extra += 1U;
    }
    result = (char *)malloc(len + extra + 1U);
    if (result == NULL) return NULL;
    out = result;
    *out++ = '"';
    for (i = 0; i < len; ++i) {
        if (text[i] == '"') { *out++ = '\\'; *out++ = '"'; }
        else if (text[i] == '%') { *out++ = '%'; *out++ = '%'; }
        else *out++ = text[i];
    }
    *out++ = '"';
    *out = '\0';
#else
    for (i = 0; i < len; ++i) {
        if (text[i] == '\'') extra += 4U;
    }
    result = (char *)malloc(len + extra + 1U);
    if (result == NULL) return NULL;
    out = result;
    *out++ = '\'';
    for (i = 0; i < len; ++i) {
        if (text[i] == '\'') {
            *out++ = '\'';
            *out++ = '\\';
            *out++ = '\'';
            *out++ = '\'';
        } else {
            *out++ = text[i];
        }
    }
    *out++ = '\'';
    *out = '\0';
#endif
    return result;
}

static char *b2026_qrread_trim_text_inplace(char *text) {
    char *start;
    size_t len;
    if (text == NULL) return NULL;
    start = text;
    while (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n') start++;
    if (start != text) memmove(text, start, strlen(start) + 1U);
    len = strlen(text);
    while (len > 0U && (text[len - 1U] == ' ' || text[len - 1U] == '\t' || text[len - 1U] == '\r' || text[len - 1U] == '\n')) {
        text[len - 1U] = '\0';
        len--;
    }
    return text;
}

static char *b2026_qrread_read_text_file_path(const char *path) {
    FILE *fp;
    long size;
    size_t read_size;
    char *buffer;
    if (path == NULL || path[0] == '\0') return b2026_qrread_strdup("");
    fp = b2026_fopen(path, "rb");
    if (fp == NULL) return b2026_qrread_strdup("");
    if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); return b2026_qrread_strdup(""); }
    size = ftell(fp);
    if (size < 0) { fclose(fp); return b2026_qrread_strdup(""); }
    if (fseek(fp, 0, SEEK_SET) != 0) { fclose(fp); return b2026_qrread_strdup(""); }
    buffer = (char *)malloc((size_t)size + 1U);
    if (buffer == NULL) { fclose(fp); return b2026_qrread_strdup(""); }
    read_size = fread(buffer, 1U, (size_t)size, fp);
    buffer[read_size] = '\0';
    fclose(fp);
    return buffer;
}

static int b2026_qrread_ascii_equals_ignore_case(const char *left, const char *right) {
    unsigned char lch;
    unsigned char rch;
    if (left == NULL || right == NULL) return 0;
    while (*left != '\0' && *right != '\0') {
        lch = (unsigned char)*left++;
        rch = (unsigned char)*right++;
        if (lch >= 'A' && lch <= 'Z') lch = (unsigned char)(lch - 'A' + 'a');
        if (rch >= 'A' && rch <= 'Z') rch = (unsigned char)(rch - 'A' + 'a');
        if (lch != rch) return 0;
    }
    return *left == '\0' && *right == '\0';
}

static int b2026_qrread_result_mode_kind(const char *mode_name) {
    if (mode_name == NULL || *mode_name == '\0') return B2026_QRREAD_MODE_AUTO;
    if (b2026_qrread_ascii_equals_ignore_case(mode_name, "AUTO")) return B2026_QRREAD_MODE_AUTO;
    if (b2026_qrread_ascii_equals_ignore_case(mode_name, "TEXT")) return B2026_QRREAD_MODE_TEXT;
    if (b2026_qrread_ascii_equals_ignore_case(mode_name, "UTF8")) return B2026_QRREAD_MODE_TEXT;
    if (b2026_qrread_ascii_equals_ignore_case(mode_name, "SHIFTJIS")) return B2026_QRREAD_MODE_TEXT;
    if (b2026_qrread_ascii_equals_ignore_case(mode_name, "BASE64")) return B2026_QRREAD_MODE_BASE64;
    if (b2026_qrread_ascii_equals_ignore_case(mode_name, "RAW")) return B2026_QRREAD_MODE_RAW;
    runtime_error("READQRCODE mode must be AUTO / TEXT / BASE64 / RAW / UTF8 / SHIFTJIS / 【READQRCODE関数エラー】mode$ には AUTO / TEXT / BASE64 / RAW / UTF8 / SHIFTJIS を指定してください。");
    return B2026_QRREAD_MODE_AUTO;
}

static int b2026_qrread_is_base64_format(const char *format_name) {
    return b2026_qrread_ascii_equals_ignore_case(format_name, "base64");
}

static char *b2026_qrread_bytes_to_c_string(const unsigned char *bytes, size_t len) {
    char *copy = (char *)malloc(len + 1U);
    if (copy == NULL) runtime_error("Out of memory while converting QR text");
    if (bytes != NULL && len > 0U) memcpy(copy, bytes, len);
    copy[len] = '\0';
    return copy;
}

static char *b2026_qrread_decode_base64_text_copy(const char *text) {
    unsigned char *decoded = NULL;
    size_t out_len = 0U;
    int ok = 0;
    char *copy;
    decoded = base64_decode_text_malloc(text != NULL ? text : "", &out_len, &ok);
    if (!ok || decoded == NULL) {
        free(decoded);
        runtime_error("READQRCODE failed: BASE64 text could not be decoded / 【READQRCODE関数エラー】BASE64 データをデコードできませんでした。");
    }
    copy = b2026_qrread_bytes_to_c_string(decoded, out_len);
    free(decoded);
    return copy;
}

static char *b2026_qrread_render_text_for_mode(const char *raw_text, const char *data_format, const char *mode_name) {
    int mode_kind = b2026_qrread_result_mode_kind(mode_name);
    int is_base64 = b2026_qrread_is_base64_format(data_format);
    const char *encoded;
    if (raw_text == NULL) raw_text = "";
    if (mode_kind == B2026_QRREAD_MODE_RAW) {
        return b2026_qrread_strdup(raw_text);
    }
    if (mode_kind == B2026_QRREAD_MODE_BASE64) {
        if (is_base64) return b2026_qrread_strdup(raw_text);
        encoded = base64_encode_text_to_text(raw_text);
        return b2026_qrread_strdup(encoded != NULL ? encoded : "");
    }
    if (is_base64) return b2026_qrread_decode_base64_text_copy(raw_text);
    return b2026_qrread_strdup(raw_text);
}

static void b2026_qrread_apply_result_mode(B2026_QRReadInfo *results, int count, const char *mode_name) {
    int i;
    if (results == NULL || count <= 0) return;
    for (i = 0; i < count; ++i) {
        free(results[i].text);
        results[i].text = b2026_qrread_render_text_for_mode(results[i].raw_text, results[i].data_format, mode_name);
    }
}

static unsigned char b2026_qrread_value_to_u8(double value, double value_max) {
    double scaled;
    if (value_max <= 0.0) value_max = 255.0;
    scaled = (value / value_max) * 255.0;
    if (scaled <= 0.0) return 0U;
    if (scaled >= 255.0) return 255U;
    return (unsigned char)floor(scaled + 0.5);
}

static int b2026_qrread_save_array_bbox_png(const ArrayVariable *array_var, int bbox_x, int bbox_y, int bbox_width, int bbox_height, char **path_out) {
    char *temp_path = NULL;
    unsigned char *rgba = NULL;
    int source_channels;
    double source_value_max;
    int width;
    int height;
    int x;
    int y;
    if (path_out != NULL) *path_out = NULL;
    if (array_var == NULL || array_var->screen_width <= 0 || array_var->screen_height <= 0) return 0;
    width = array_var->screen_width;
    height = array_var->screen_height;
    if (bbox_width > 0 && bbox_height > 0) {
        if (bbox_x < 0) { bbox_width += bbox_x; bbox_x = 0; }
        if (bbox_y < 0) { bbox_height += bbox_y; bbox_y = 0; }
        if (bbox_x + bbox_width > width) bbox_width = width - bbox_x;
        if (bbox_y + bbox_height > height) bbox_height = height - bbox_y;
        if (bbox_width > 0 && bbox_height > 0) {
            width = bbox_width;
            height = bbox_height;
        } else {
            bbox_x = 0;
            bbox_y = 0;
            width = array_var->screen_width;
            height = array_var->screen_height;
        }
    } else {
        bbox_x = 0;
        bbox_y = 0;
    }
    rgba = (unsigned char *)malloc((size_t)width * (size_t)height * 4U);
    if (rgba == NULL) return 0;
    source_channels = (array_var->image_pixel_mode == 0) ? 1 : 4;
    source_value_max = (array_var->image_value_max > 0.0) ? array_var->image_value_max : 255.0;
    for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
            int src_x = bbox_x + x;
            int src_y = bbox_y + y;
            long long src_base = option_base + (((long long)src_y * (long long)array_var->screen_width) + (long long)src_x) * (long long)source_channels;
            size_t dst_base = ((size_t)y * (size_t)width + (size_t)x) * 4U;
            if (source_channels == 4) {
                rgba[dst_base + 0] = b2026_qrread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 0]), source_value_max);
                rgba[dst_base + 1] = b2026_qrread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 1]), source_value_max);
                rgba[dst_base + 2] = b2026_qrread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 2]), source_value_max);
                rgba[dst_base + 3] = b2026_qrread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 3]), source_value_max);
            } else {
                unsigned char gray = b2026_qrread_value_to_u8(b2026_var_as_number(&array_var->data[src_base]), source_value_max);
                rgba[dst_base + 0] = gray;
                rgba[dst_base + 1] = gray;
                rgba[dst_base + 2] = gray;
                rgba[dst_base + 3] = 255U;
            }
        }
    }
    temp_path = image_temp_path_with_suffix(".qrcode.png");
    if (temp_path == NULL) {
        free(rgba);
        return 0;
    }
    if (!saveimage_write_png(temp_path, rgba, width, height, 1, 0, 0, 0)) {
        free(rgba);
        b2026_unlink(temp_path);
        free(temp_path);
        return 0;
    }
    free(rgba);
    if (path_out != NULL) {
        *path_out = temp_path;
        return 1;
    }
    b2026_unlink(temp_path);
    free(temp_path);
    return 1;
}

static const char *b2026_qrread_xml_attr_value(const char *text, const char *name, char *buffer, size_t buffer_size) {
    const char *pos;
    char quote;
    size_t len;
    if (buffer != NULL && buffer_size > 0U) buffer[0] = '\0';
    if (text == NULL || name == NULL || buffer == NULL || buffer_size == 0U) return NULL;
    pos = strstr(text, name);
    if (pos == NULL) return NULL;
    pos += strlen(name);
    while (*pos == ' ' || *pos == '\t') pos++;
    if (*pos != '=') return NULL;
    pos++;
    while (*pos == ' ' || *pos == '\t') pos++;
    quote = *pos;
    if (quote != '"' && quote != '\'') return NULL;
    pos++;
    len = 0U;
    while (pos[len] != '\0' && pos[len] != quote) len++;
    if (len >= buffer_size) len = buffer_size - 1U;
    memcpy(buffer, pos, len);
    buffer[len] = '\0';
    return buffer;
}

static const char *b2026_qrread_xml_attr_value_in_tag(const char *tag_start, const char *tag_end, const char *name, char *buffer, size_t buffer_size) {
    const char *gt;
    size_t len;
    char *copy;
    const char *result;
    if (buffer != NULL && buffer_size > 0U) buffer[0] = '\0';
    if (tag_start == NULL || name == NULL || buffer == NULL || buffer_size == 0U) return NULL;
    gt = strchr(tag_start, '>');
    if (gt == NULL) return NULL;
    if (tag_end != NULL && gt > tag_end) return NULL;
    len = (size_t)(gt - tag_start);
    copy = (char *)malloc(len + 1U);
    if (copy == NULL) return NULL;
    memcpy(copy, tag_start, len);
    copy[len] = '\0';
    result = b2026_qrread_xml_attr_value(copy, name, buffer, buffer_size);
    free(copy);
    return result;
}

static char *b2026_qrread_xml_extract_data_text(const char *symbol_start, const char *symbol_end) {
    const char *data_tag;
    const char *gt;
    const char *data_end;
    const char *cdata_start;
    const char *cdata_end;
    size_t len;
    char *copy;
    if (symbol_start == NULL || symbol_end == NULL || symbol_end <= symbol_start) return NULL;
    data_tag = strstr(symbol_start, "<data");
    if (data_tag == NULL || data_tag >= symbol_end) return NULL;
    gt = strchr(data_tag, '>');
    if (gt == NULL || gt >= symbol_end) return NULL;
    gt++;
    if (strncmp(gt, "<![CDATA[", 9) == 0) {
        cdata_start = gt + 9;
        cdata_end = strstr(cdata_start, "]]>");
        if (cdata_end == NULL || cdata_end > symbol_end) return NULL;
        len = (size_t)(cdata_end - cdata_start);
        copy = (char *)malloc(len + 1U);
        if (copy == NULL) return NULL;
        memcpy(copy, cdata_start, len);
        copy[len] = '\0';
        return b2026_qrread_trim_text_inplace(copy);
    }
    data_end = strstr(gt, "</data>");
    if (data_end == NULL || data_end > symbol_end) return NULL;
    len = (size_t)(data_end - gt);
    copy = (char *)malloc(len + 1U);
    if (copy == NULL) return NULL;
    memcpy(copy, gt, len);
    copy[len] = '\0';
    return b2026_qrread_trim_text_inplace(copy);
}

static const char *b2026_qrread_xml_extract_data_format(const char *symbol_start, const char *symbol_end, char *buffer, size_t buffer_size) {
    const char *data_tag;
    if (buffer != NULL && buffer_size > 0U) buffer[0] = '\0';
    if (symbol_start == NULL || symbol_end == NULL || symbol_end <= symbol_start || buffer == NULL || buffer_size == 0U) return NULL;
    data_tag = strstr(symbol_start, "<data");
    if (data_tag == NULL || data_tag >= symbol_end) return NULL;
    if (b2026_qrread_xml_attr_value_in_tag(data_tag, symbol_end, "format", buffer, buffer_size) == NULL || buffer[0] == '\0') {
        snprintf(buffer, buffer_size, "%s", "text");
    }
    return buffer;
}

static int b2026_qrread_parse_zbar_points(const char *symbol_start, const char *symbol_end, int *x_out, int *y_out, int *w_out, int *h_out) {
    const char *pos = symbol_start;
    int found = 0;
    int min_x = 0, min_y = 0, max_x = 0, max_y = 0;
    char xbuf[64];
    char ybuf[64];
    if (x_out != NULL) *x_out = 0;
    if (y_out != NULL) *y_out = 0;
    if (w_out != NULL) *w_out = 0;
    if (h_out != NULL) *h_out = 0;
    while (pos != NULL && pos < symbol_end) {
        int x;
        int y;
        pos = strstr(pos, "<point");
        if (pos == NULL || pos >= symbol_end) break;
        if (b2026_qrread_xml_attr_value(pos, "x", xbuf, sizeof(xbuf)) == NULL || b2026_qrread_xml_attr_value(pos, "y", ybuf, sizeof(ybuf)) == NULL) {
            pos += 6;
            continue;
        }
        x = atoi(xbuf);
        y = atoi(ybuf);
        if (!found) {
            min_x = max_x = x;
            min_y = max_y = y;
            found = 1;
        } else {
            if (x < min_x) min_x = x;
            if (x > max_x) max_x = x;
            if (y < min_y) min_y = y;
            if (y > max_y) max_y = y;
        }
        pos += 6;
    }
    if (!found) return 0;
    if (x_out != NULL) *x_out = min_x;
    if (y_out != NULL) *y_out = min_y;
    if (w_out != NULL) *w_out = max_x - min_x + 1;
    if (h_out != NULL) *h_out = max_y - min_y + 1;
    return 1;
}

static int b2026_qrread_type_is_micro(const char *type_name) {
    if (type_name == NULL) return 0;
    if (strstr(type_name, "Micro") != NULL) return 1;
    if (strstr(type_name, "MICRO") != NULL) return 1;
    if (strcmp(type_name, "SQ-Code") == 0) return 1;
    return 0;
}

static int b2026_qrread_symbol_is_qr_family(const char *type_name) {
    if (type_name == NULL || *type_name == '\0') return 1;
    if (strstr(type_name, "QR") != NULL) return 1;
    if (strstr(type_name, "qr") != NULL) return 1;
    if (strstr(type_name, "SQ") != NULL) return 1;
    return 0;
}

static int b2026_qrread_path_dimensions(const char *path, int *width_out, int *height_out) {
    int width = 0;
    int height = 0;
    int components = 0;
    if (width_out != NULL) *width_out = 0;
    if (height_out != NULL) *height_out = 0;
    if (path == NULL || *path == '\0') return 0;
    if (!stbi_info(path, &width, &height, &components)) return 0;
    if (width_out != NULL) *width_out = width;
    if (height_out != NULL) *height_out = height;
    return 1;
}

static int b2026_qrread_zbar_detect_results_from_path(const char *path, int fallback_width, int fallback_height, B2026_QRReadInfo *results, int *count_io, int max_results) {
    const char *zbarimg_path = b2026_envset_zbarimg_path();
    char *quoted_cmd = NULL;
    char *quoted_input = NULL;
    char *quoted_stdout = NULL;
    char *quoted_stderr = NULL;
    char *stdout_path = NULL;
    char *stderr_path = NULL;
    char *xml = NULL;
    char *error_text = NULL;
    char command[32768];
    const char *scan;
    int count = 0;
    int command_status = 0;
    if (count_io != NULL) *count_io = 0;
    if (path == NULL || *path == '\0' || results == NULL || count_io == NULL || max_results <= 0) return 0;
    if (!b2026_qrread_require_zbarimg()) return 0;
    stdout_path = image_temp_path_with_suffix(".zbar.xml");
    stderr_path = image_temp_path_with_suffix(".zbar.err");
    if (stdout_path == NULL || stderr_path == NULL) goto cleanup;
    quoted_cmd = b2026_qrread_quote_shell_arg(zbarimg_path);
    quoted_input = b2026_qrread_quote_shell_arg(path);
    quoted_stdout = b2026_qrread_quote_shell_arg(stdout_path);
    quoted_stderr = b2026_qrread_quote_shell_arg(stderr_path);
    if (quoted_cmd == NULL || quoted_input == NULL || quoted_stdout == NULL || quoted_stderr == NULL) goto cleanup;
#ifdef _WIN32
    snprintf(command, sizeof(command), "%s --xml --quiet %s >%s 2>%s", quoted_cmd, quoted_input, quoted_stdout, quoted_stderr);
#else
    snprintf(command, sizeof(command), "%s --xml --quiet %s >%s 2>%s", quoted_cmd, quoted_input, quoted_stdout, quoted_stderr);
#endif
    command_status = system(command);
    if (command_status != 0) {
        error_text = b2026_qrread_read_text_file_path(stderr_path);
        if (error_text != NULL && error_text[0] != '\0') {
            basic_set_error_logged(7001, error_text, "READQRCODE", current_source_path, current_line, "BARCODE");
            goto cleanup;
        }
    }
    xml = b2026_qrread_read_text_file_path(stdout_path);
    if (xml == NULL) goto cleanup;
    scan = xml;
    while (scan != NULL && *scan != '\0' && count < max_results) {
        const char *symbol_start = strstr(scan, "<symbol");
        const char *symbol_end;
        char typebuf[128];
        char formatbuf[64];
        char *text = NULL;
        int x = 0, y = 0, w = 0, h = 0;
        if (symbol_start == NULL) break;
        symbol_end = strstr(symbol_start, "</symbol>");
        if (symbol_end == NULL) break;
        typebuf[0] = '\0';
        formatbuf[0] = '\0';
        (void)b2026_qrread_xml_attr_value(symbol_start, "type", typebuf, sizeof(typebuf));
        (void)b2026_qrread_xml_extract_data_format(symbol_start, symbol_end, formatbuf, sizeof(formatbuf));
        if (!b2026_qrread_symbol_is_qr_family(typebuf)) {
            scan = symbol_end + 9;
            continue;
        }
        text = b2026_qrread_xml_extract_data_text(symbol_start, symbol_end);
        if (text == NULL || text[0] == '\0') {
            free(text);
            scan = symbol_end + 9;
            continue;
        }
        if (!b2026_qrread_parse_zbar_points(symbol_start, symbol_end, &x, &y, &w, &h)) {
            x = 0;
            y = 0;
            w = fallback_width > 0 ? fallback_width : 0;
            h = fallback_height > 0 ? fallback_height : 0;
        }
        b2026_qrread_info_init(&results[count]);
        results[count].success = 1;
        results[count].count = 1;
        results[count].is_micro = b2026_qrread_type_is_micro(typebuf);
        results[count].bbox_x = x;
        results[count].bbox_y = y;
        results[count].bbox_width = w;
        results[count].bbox_height = h;
        results[count].detect_method = 3;
        results[count].raw_text = text;
        results[count].data_format = b2026_qrread_strdup(formatbuf[0] != '\0' ? formatbuf : "text");
        results[count].text = NULL;
        count += 1;
        scan = symbol_end + 9;
    }
    *count_io = count;
cleanup:
    if (stdout_path != NULL) b2026_unlink(stdout_path);
    if (stderr_path != NULL) b2026_unlink(stderr_path);
    free(quoted_cmd);
    free(quoted_input);
    free(quoted_stdout);
    free(quoted_stderr);
    free(stdout_path);
    free(stderr_path);
    free(xml);
    free(error_text);
    return count > 0;
}

static char *b2026_qrread_first_text_from_results(B2026_QRReadInfo *results, int count) {
    if (results == NULL || count <= 0) return NULL;
    if (results[0].text == NULL || results[0].text[0] == '\0') return NULL;
    return b2026_qrread_strdup(results[0].text);
}

static void b2026_qrread_copy_first_info(B2026_QRReadInfo *dst, B2026_QRReadInfo *results, int count) {
    if (dst == NULL || results == NULL || count <= 0) return;
    b2026_qrread_info_free(dst);
    *dst = results[0];
    results[0].text = NULL;
    results[0].raw_text = NULL;
    results[0].data_format = NULL;
}

static void b2026_qrread_fill_string_array_from_results(const char *target_name, B2026_QRReadInfo *results, int count) {
    ArrayVariable *target_var;
    int i;
    if (count <= 0) {
        dim_array(target_name, (double)(option_base - 1));
        target_var = require_array_variable(target_name);
        target_var->read_count = 0;
        clear_array_runtime_metadata(target_var);
        return;
    }
    dim_array(target_name, (double)(option_base + count - 1));
    target_var = require_array_variable(target_name);
    for (i = 0; i < count; ++i) {
        set_array_string_value_at(target_name, (double)(option_base + i), results[i].text != NULL ? results[i].text : "");
    }
    target_var->read_count = count;
    clear_array_runtime_metadata(target_var);
}

char *read_qrcode_from_array_text(const char *array_name, const char *encoding_name) {
    ArrayVariable *array_var = require_array_variable(array_name);
    B2026_QRReadInfo results[16];
    char *temp_path = NULL;
    char *result = NULL;
    int count = 0;
    int i;
    (void)encoding_name;
    if (array_var == NULL || array_var->screen_width <= 0 || array_var->screen_height <= 0) return NULL;
    b2026_qrread_require_zbarimg();
    for (i = 0; i < 16; ++i) b2026_qrread_info_init(&results[i]);
    if (b2026_qrread_save_array_bbox_png(array_var, 0, 0, 0, 0, &temp_path) && temp_path != NULL) {
        (void)b2026_qrread_zbar_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &count, 16);
        b2026_qrread_apply_result_mode(results, count, encoding_name);
        result = b2026_qrread_first_text_from_results(results, count);
        b2026_unlink(temp_path);
        free(temp_path);
    }
    for (i = 0; i < 16; ++i) b2026_qrread_info_free(&results[i]);
    return result;
}

char *read_qrcode_from_path_text(const char *path, const char *encoding_name) {
    B2026_QRReadInfo results[16];
    char *result = NULL;
    int count = 0;
    int width = 0;
    int height = 0;
    int i;
    (void)encoding_name;
    if (path == NULL || *path == '\0') return NULL;
    b2026_qrread_require_zbarimg();
    (void)b2026_qrread_path_dimensions(path, &width, &height);
    for (i = 0; i < 16; ++i) b2026_qrread_info_init(&results[i]);
    (void)b2026_qrread_zbar_detect_results_from_path(path, width, height, results, &count, 16);
    b2026_qrread_apply_result_mode(results, count, encoding_name);
    result = b2026_qrread_first_text_from_results(results, count);
    for (i = 0; i < 16; ++i) b2026_qrread_info_free(&results[i]);
    return result;
}

char *read_qrcode_from_index_text(int target_index, const char *encoding_name) {
    return read_qrcode_from_path_text(g_runtime_argv[resolve_cli_target_index(target_index)], encoding_name);
}

void read_qrcode_to_string_array_from_array(const char *target_name, const char *array_name, const char *encoding_name) {
    ArrayVariable *array_var = require_array_variable(array_name);
    B2026_QRReadInfo results[B2026_QRREAD_TEXT_RESULTS_MAX];
    char *temp_path = NULL;
    int count = 0;
    int i;
    (void)encoding_name;
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_init(&results[i]);
    if (array_var != NULL && array_var->screen_width > 0 && array_var->screen_height > 0) {
        b2026_qrread_require_zbarimg();
        if (b2026_qrread_save_array_bbox_png(array_var, 0, 0, 0, 0, &temp_path) && temp_path != NULL) {
            (void)b2026_qrread_zbar_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &count, B2026_QRREAD_TEXT_RESULTS_MAX);
            b2026_unlink(temp_path);
            free(temp_path);
        }
    }
    b2026_qrread_apply_result_mode(results, count, encoding_name);
    b2026_qrread_fill_string_array_from_results(target_name, results, count);
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_free(&results[i]);
}

void read_qrcode_to_string_array_from_path(const char *target_name, const char *path, const char *encoding_name) {
    B2026_QRReadInfo results[B2026_QRREAD_TEXT_RESULTS_MAX];
    int count = 0;
    int width = 0;
    int height = 0;
    int i;
    (void)encoding_name;
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_init(&results[i]);
    if (path != NULL && *path != '\0') {
        b2026_qrread_require_zbarimg();
        (void)b2026_qrread_path_dimensions(path, &width, &height);
        (void)b2026_qrread_zbar_detect_results_from_path(path, width, height, results, &count, B2026_QRREAD_TEXT_RESULTS_MAX);
    }
    b2026_qrread_apply_result_mode(results, count, encoding_name);
    b2026_qrread_fill_string_array_from_results(target_name, results, count);
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_free(&results[i]);
}

void read_qrcode_to_string_array_from_index(const char *target_name, int target_index, const char *encoding_name) {
    read_qrcode_to_string_array_from_path(target_name, g_runtime_argv[resolve_cli_target_index(target_index)], encoding_name);
}

static void read_qrcode_info_to_hash(const char *target_name, const B2026_QRReadInfo *info) {
    if (info == NULL || !info->success) {
        hash_set_number(target_name, "flag", 0.0);
        hash_set_number(target_name, "count", 0.0);
        hash_set_string(target_name, "kind", "null");
        hash_set_number(target_name, "text", null_value());
        hash_set_number(target_name, "rawText", null_value());
        hash_set_number(target_name, "dataFormat", null_value());
        hash_set_number(target_name, "version", null_value());
        hash_set_number(target_name, "rotation", null_value());
        hash_set_number(target_name, "inverted", null_value());
        hash_set_number(target_name, "x", null_value());
        hash_set_number(target_name, "y", null_value());
        hash_set_number(target_name, "width", null_value());
        hash_set_number(target_name, "height", null_value());
        hash_set_number(target_name, "mask", null_value());
        hash_set_number(target_name, "ecc", null_value());
        hash_set_number(target_name, "mode", null_value());
        hash_set_number(target_name, "encoding", null_value());
        hash_set_number(target_name, "eci", null_value());
        hash_set_number(target_name, "fnc1", null_value());
        hash_set_number(target_name, "fnc1AppId", null_value());
        hash_set_number(target_name, "gs1", 0.0);
        hash_set_number(target_name, "segmentCount", null_value());
        hash_set_number(target_name, "moduleScale", null_value());
        hash_set_number(target_name, "symbolSize", null_value());
        hash_set_number(target_name, "isMicro", 0.0);
        hash_set_number(target_name, "textLength", null_value());
        hash_set_number(target_name, "textByteLength", null_value());
        hash_set_number(target_name, "detectMethod", null_value());
        return;
    }
    hash_set_number(target_name, "flag", -1.0);
    hash_set_number(target_name, "count", (double)(info->count > 0 ? info->count : 1));
    hash_set_string(target_name, "kind", info->is_micro ? "MICROQRCODE" : "QRCODE");
    hash_set_string(target_name, "text", info->text != NULL ? info->text : "");
    hash_set_string(target_name, "rawText", info->raw_text != NULL ? info->raw_text : "");
    hash_set_string(target_name, "dataFormat", info->data_format != NULL ? info->data_format : "text");
    hash_set_number(target_name, "version", info->version >= 0 ? (double)info->version : null_value());
    hash_set_number(target_name, "rotation", info->rotation != 0 ? (double)info->rotation : 0.0);
    hash_set_number(target_name, "inverted", (double)info->inverted);
    hash_set_number(target_name, "x", (double)info->bbox_x);
    hash_set_number(target_name, "y", (double)info->bbox_y);
    hash_set_number(target_name, "width", (double)info->bbox_width);
    hash_set_number(target_name, "height", (double)info->bbox_height);
    hash_set_number(target_name, "mask", info->mask >= 0 ? (double)info->mask : null_value());
    hash_set_number(target_name, "ecc", null_value());
    hash_set_number(target_name, "mode", null_value());
    hash_set_number(target_name, "encoding", null_value());
    hash_set_number(target_name, "eci", null_value());
    hash_set_number(target_name, "fnc1", null_value());
    hash_set_number(target_name, "fnc1AppId", null_value());
    hash_set_number(target_name, "gs1", 0.0);
    hash_set_number(target_name, "segmentCount", null_value());
    hash_set_number(target_name, "moduleScale", null_value());
    hash_set_number(target_name, "symbolSize", null_value());
    hash_set_number(target_name, "isMicro", info->is_micro ? -1.0 : 0.0);
    hash_set_string(target_name, "detectMethod", "ZBAR");
    if (info->text != NULL) {
        hash_set_number(target_name, "textLength", (double)utf8_codepoint_count(info->text));
        hash_set_number(target_name, "textByteLength", (double)strlen(info->text));
    } else {
        hash_set_number(target_name, "textLength", null_value());
        hash_set_number(target_name, "textByteLength", null_value());
    }
}

static void b2026_qrread_store_result_series_to_hash(const char *target_name, B2026_QRReadInfo *results, int count) {
    int i;
    char key[64];
    for (i = 0; i < count; ++i) {
        snprintf(key, sizeof(key), "x%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].bbox_x);
        snprintf(key, sizeof(key), "y%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].bbox_y);
        snprintf(key, sizeof(key), "width%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].bbox_width);
        snprintf(key, sizeof(key), "height%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].bbox_height);
        snprintf(key, sizeof(key), "cx%d", i + 1);
        hash_set_number(target_name, key, (double)(results[i].bbox_x + results[i].bbox_width / 2));
        snprintf(key, sizeof(key), "cy%d", i + 1);
        hash_set_number(target_name, key, (double)(results[i].bbox_y + results[i].bbox_height / 2));
        snprintf(key, sizeof(key), "rotation%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].rotation);
        snprintf(key, sizeof(key), "version%d", i + 1);
        hash_set_number(target_name, key, results[i].version >= 0 ? (double)results[i].version : null_value());
        snprintf(key, sizeof(key), "mask%d", i + 1);
        hash_set_number(target_name, key, results[i].mask >= 0 ? (double)results[i].mask : null_value());
        snprintf(key, sizeof(key), "moduleScale%d", i + 1);
        hash_set_number(target_name, key, results[i].module_scale > 0 ? (double)results[i].module_scale : null_value());
        snprintf(key, sizeof(key), "symbolSize%d", i + 1);
        hash_set_number(target_name, key, results[i].symbol_size > 0 ? (double)results[i].symbol_size : null_value());
        snprintf(key, sizeof(key), "detectMethod%d", i + 1);
        hash_set_string(target_name, key, "ZBAR");
        snprintf(key, sizeof(key), "finderCount%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].finder_count);
        snprintf(key, sizeof(key), "quadScore%d", i + 1);
        hash_set_number(target_name, key, results[i].quad_score);
        snprintf(key, sizeof(key), "quietZoneScore%d", i + 1);
        hash_set_number(target_name, key, results[i].quiet_zone_score);
        snprintf(key, sizeof(key), "inverted%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].inverted);
        snprintf(key, sizeof(key), "score%d", i + 1);
        hash_set_number(target_name, key, (double)(results[i].bbox_width * results[i].bbox_height));
        snprintf(key, sizeof(key), "type%d", i + 1);
        hash_set_string(target_name, key, results[i].is_micro ? "MICROQRCODE" : "QRCODE");
        snprintf(key, sizeof(key), "kind%d", i + 1);
        hash_set_string(target_name, key, results[i].is_micro ? "MICROQRCODE" : "QRCODE");
        snprintf(key, sizeof(key), "text%d", i + 1);
        hash_set_string(target_name, key, results[i].text != NULL ? results[i].text : "");
        snprintf(key, sizeof(key), "rawText%d", i + 1);
        hash_set_string(target_name, key, results[i].raw_text != NULL ? results[i].raw_text : "");
        snprintf(key, sizeof(key), "dataFormat%d", i + 1);
        hash_set_string(target_name, key, results[i].data_format != NULL ? results[i].data_format : "text");
        snprintf(key, sizeof(key), "textLength%d", i + 1);
        hash_set_number(target_name, key, results[i].text != NULL ? (double)utf8_codepoint_count(results[i].text) : null_value());
        snprintf(key, sizeof(key), "textByteLength%d", i + 1);
        hash_set_number(target_name, key, results[i].text != NULL ? (double)strlen(results[i].text) : null_value());
        snprintf(key, sizeof(key), "isMicro%d", i + 1);
        hash_set_number(target_name, key, results[i].is_micro ? -1.0 : 0.0);
    }
}

static void b2026_qrread_store_results_to_hash_info(const char *target_name, B2026_QRReadInfo *results, int count) {
    HashVariable *hash_var = ensure_local_hash(get_hash_frame(target_name), target_name);
    if (hash_var == NULL) return;
    clear_hash_entries(hash_var);
    if (count > 0 && results != NULL) {
        read_qrcode_info_to_hash(target_name, &results[0]);
        hash_set_number(target_name, "count", (double)count);
        b2026_qrread_store_result_series_to_hash(target_name, results, count);
    } else {
        B2026_QRReadInfo info;
        b2026_qrread_info_init(&info);
        read_qrcode_info_to_hash(target_name, &info);
        b2026_qrread_info_free(&info);
    }
}

void read_qrcode_into_hash_from_array(const char *target_name, const char *array_name, const char *encoding_name) {
    ArrayVariable *array_var = require_array_variable(array_name);
    B2026_QRReadInfo results[B2026_QRREAD_TEXT_RESULTS_MAX];
    char *temp_path = NULL;
    int count = 0;
    int i;
    (void)encoding_name;
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_init(&results[i]);
    if (array_var != NULL && array_var->screen_width > 0 && array_var->screen_height > 0) {
        b2026_qrread_require_zbarimg();
        if (b2026_qrread_save_array_bbox_png(array_var, 0, 0, 0, 0, &temp_path) && temp_path != NULL) {
            (void)b2026_qrread_zbar_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &count, B2026_QRREAD_TEXT_RESULTS_MAX);
            b2026_unlink(temp_path);
            free(temp_path);
        }
    }
    b2026_qrread_apply_result_mode(results, count, encoding_name);
    b2026_qrread_store_results_to_hash_info(target_name, results, count);
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_free(&results[i]);
}

void read_qrcode_into_hash_from_path(const char *target_name, const char *path, const char *encoding_name) {
    B2026_QRReadInfo results[B2026_QRREAD_TEXT_RESULTS_MAX];
    int count = 0;
    int width = 0;
    int height = 0;
    int i;
    (void)encoding_name;
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_init(&results[i]);
    if (path != NULL && *path != '\0') {
        b2026_qrread_require_zbarimg();
        (void)b2026_qrread_path_dimensions(path, &width, &height);
        (void)b2026_qrread_zbar_detect_results_from_path(path, width, height, results, &count, B2026_QRREAD_TEXT_RESULTS_MAX);
    }
    b2026_qrread_apply_result_mode(results, count, encoding_name);
    b2026_qrread_store_results_to_hash_info(target_name, results, count);
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_free(&results[i]);
}

void read_qrcode_into_hash_from_index(const char *target_name, int target_index, const char *encoding_name) {
    read_qrcode_into_hash_from_path(target_name, g_runtime_argv[resolve_cli_target_index(target_index)], encoding_name);
}

void image_detect_qrcode_to_hash(const char *target_name, const char *source_name) {
    ArrayVariable *array_var = require_array_variable(source_name);
    Frame *frame = get_hash_frame(target_name);
    HashVariable *hash_var = ensure_local_hash(frame, target_name);
    B2026_QRReadInfo results[B2026_QRREAD_TEXT_RESULTS_MAX];
    char *temp_path = NULL;
    int result_count = 0;
    int i;
    if (hash_var == NULL) return;
    clear_hash_entries(hash_var);
    hash_set_string(target_name, "kind", "QRCODELIST");
    hash_set_number(target_name, "flag", 0.0);
    hash_set_number(target_name, "count", 0.0);
    if (array_var == NULL || array_var->screen_width <= 0 || array_var->screen_height <= 0) return;
    b2026_qrread_require_zbarimg();
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_init(&results[i]);
    if (b2026_qrread_save_array_bbox_png(array_var, 0, 0, 0, 0, &temp_path) && temp_path != NULL) {
        (void)b2026_qrread_zbar_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &result_count, B2026_QRREAD_TEXT_RESULTS_MAX);
        b2026_unlink(temp_path);
        free(temp_path);
    }
    if (result_count <= 0) {
        for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_free(&results[i]);
        return;
    }
    hash_set_number(target_name, "flag", -1.0);
    hash_set_number(target_name, "count", (double)result_count);
    b2026_qrread_store_result_series_to_hash(target_name, results, result_count);
    for (i = 0; i < B2026_QRREAD_TEXT_RESULTS_MAX; ++i) b2026_qrread_info_free(&results[i]);
}
