typedef struct {
    char *type;
    char *text;
    int x;
    int y;
    int width;
    int height;
} B2026_BarReadItem;

#define B2026_BARREAD_RESULTS_MAX 64

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);
int stbi_info(char const *filename, int *x, int *y, int *comp);
static char *b2026_barread_quote_shell_arg(const char *text);

static char *b2026_barread_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 void b2026_barread_item_init(B2026_BarReadItem *item) {
    if (item == NULL) return;
    item->type = NULL;
    item->text = NULL;
    item->x = 0;
    item->y = 0;
    item->width = 0;
    item->height = 0;
}

static void b2026_barread_item_free(B2026_BarReadItem *item) {
    if (item == NULL) return;
    free(item->type);
    item->type = NULL;
    free(item->text);
    item->text = NULL;
}

static int b2026_barread_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_barread_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_barread_require_zbarimg(void) {
    if (!b2026_barread_probe_zbarimg_available()) {
        const char *message = "READBARCODE failed: zbarimg command is required / 【READBARCODE関数エラー】zbarimg コマンドが必要です。__BASIC2026解説/46_必要な外部コマンド を確認してください。";
        if (!(g_last_error.code == 7002 && g_last_error.line == current_line)) {
            basic_set_error_logged(7002, message, "READBARCODE", current_source_path, current_line, "BARCODE");
        }
        return 0;
    }
    return 1;
}

static char *b2026_barread_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_barread_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_barread_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_barread_strdup("");
    fp = b2026_fopen(path, "rb");
    if (fp == NULL) return b2026_barread_strdup("");
    if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); return b2026_barread_strdup(""); }
    size = ftell(fp);
    if (size < 0) { fclose(fp); return b2026_barread_strdup(""); }
    if (fseek(fp, 0, SEEK_SET) != 0) { fclose(fp); return b2026_barread_strdup(""); }
    buffer = (char *)malloc((size_t)size + 1U);
    if (buffer == NULL) { fclose(fp); return b2026_barread_strdup(""); }
    read_size = fread(buffer, 1U, (size_t)size, fp);
    buffer[read_size] = '\0';
    fclose(fp);
    return buffer;
}

static unsigned char b2026_barread_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_barread_save_array_png(const ArrayVariable *array_var, 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;
    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) {
            long long src_base = option_base + (((long long)y * (long long)array_var->screen_width) + (long long)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_barread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 0]), source_value_max);
                rgba[dst_base + 1] = b2026_barread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 1]), source_value_max);
                rgba[dst_base + 2] = b2026_barread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 2]), source_value_max);
                rgba[dst_base + 3] = b2026_barread_value_to_u8(b2026_var_as_number(&array_var->data[src_base + 3]), source_value_max);
            } else {
                unsigned char gray = b2026_barread_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(".barcode.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_barread_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 char *b2026_barread_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_barread_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_barread_trim_text_inplace(copy);
}

static int b2026_barread_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_barread_xml_attr_value(pos, "x", xbuf, sizeof(xbuf)) == NULL || b2026_barread_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_barread_validate_ean13_text(const char *text) {
    int sum = 0;
    int i;
    int check;
    if (text == NULL || strlen(text) != 13U) return 0;
    for (i = 0; i < 13; ++i) {
        if (text[i] < '0' || text[i] > '9') return 0;
    }
    for (i = 0; i < 12; ++i) {
        int d = text[i] - '0';
        if ((i % 2) == 0) sum += d;
        else sum += d * 3;
    }
    check = (10 - (sum % 10)) % 10;
    return text[12] == (char)('0' + check);
}

static const char *b2026_barread_detect_subtype(const char *type_name, const char *text) {
    if (type_name == NULL || text == NULL) return NULL;
    if ((strcmp(type_name, "EAN-13") == 0 || strcmp(type_name, "EAN13") == 0) &&
        (strncmp(text, "978", 3U) == 0 || strncmp(text, "979", 3U) == 0) &&
        b2026_barread_validate_ean13_text(text)) {
        return "ISBN";
    }
    if ((strcmp(type_name, "I2/5") == 0 || strcmp(type_name, "ITF") == 0 || strstr(type_name, "INTERLEAVED") != NULL) &&
        strlen(text) == 14U) {
        int sum = 0;
        int i;
        int expected;
        for (i = 0; i < 14; ++i) {
            if (text[i] < '0' || text[i] > '9') return NULL;
        }
        for (i = 12; i >= 0; --i) {
            int d = text[i] - '0';
            if (((12 - i) % 2) == 0) sum += d * 3;
            else sum += d;
        }
        expected = (10 - (sum % 10)) % 10;
        if (text[13] == (char)('0' + expected)) return "ITF-14";
    }
    return NULL;
}

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

static int b2026_barread_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_barread_detect_results_from_path(const char *path, int fallback_width, int fallback_height, B2026_BarReadItem *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_barread_require_zbarimg()) return 0;
    stdout_path = image_temp_path_with_suffix(".zbar_barcode.xml");
    stderr_path = image_temp_path_with_suffix(".zbar_barcode.err");
    if (stdout_path == NULL || stderr_path == NULL) goto cleanup;
    quoted_cmd = b2026_barread_quote_shell_arg(zbarimg_path);
    quoted_input = b2026_barread_quote_shell_arg(path);
    quoted_stdout = b2026_barread_quote_shell_arg(stdout_path);
    quoted_stderr = b2026_barread_quote_shell_arg(stderr_path);
    if (quoted_cmd == NULL || quoted_input == NULL || quoted_stdout == NULL || quoted_stderr == NULL) goto cleanup;
    snprintf(command, sizeof(command), "%s --xml --quiet %s >%s 2>%s", quoted_cmd, quoted_input, quoted_stdout, quoted_stderr);
    command_status = system(command);
    if (command_status != 0) {
        error_text = b2026_barread_read_text_file_path(stderr_path);
        if (error_text != NULL && error_text[0] != '\0') {
            basic_set_error_logged(7002, error_text, "READBARCODE", current_source_path, current_line, "BARCODE");
            goto cleanup;
        }
    }
    xml = b2026_barread_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 *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';
        (void)b2026_barread_xml_attr_value(symbol_start, "type", typebuf, sizeof(typebuf));
        if (!b2026_barread_symbol_is_barcode(typebuf)) {
            scan = symbol_end + 9;
            continue;
        }
        text = b2026_barread_xml_extract_data_text(symbol_start, symbol_end);
        if (text == NULL) {
            scan = symbol_end + 9;
            continue;
        }
        if (!b2026_barread_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_barread_item_init(&results[count]);
        results[count].type = b2026_barread_strdup(typebuf[0] != '\0' ? typebuf : "UNKNOWN");
        results[count].text = text;
        results[count].x = x;
        results[count].y = y;
        results[count].width = w;
        results[count].height = h;
        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_barread_first_text_from_results(B2026_BarReadItem *results, int count) {
    if (results == NULL || count <= 0) return NULL;
    if (results[0].text == NULL || results[0].text[0] == '\0') return NULL;
    return b2026_barread_strdup(results[0].text);
}

static void b2026_barread_fill_string_array_from_results(const char *target_name, B2026_BarReadItem *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_barcode_from_array_text(const char *array_name) {
    ArrayVariable *array_var = require_array_variable(array_name);
    B2026_BarReadItem results[16];
    char *temp_path = NULL;
    char *result = NULL;
    int count = 0;
    int i;
    if (array_var == NULL || array_var->screen_width <= 0 || array_var->screen_height <= 0) return NULL;
    b2026_barread_require_zbarimg();
    for (i = 0; i < 16; ++i) b2026_barread_item_init(&results[i]);
    if (b2026_barread_save_array_png(array_var, &temp_path) && temp_path != NULL) {
        (void)b2026_barread_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &count, 16);
        result = b2026_barread_first_text_from_results(results, count);
        b2026_unlink(temp_path);
        free(temp_path);
    }
    for (i = 0; i < 16; ++i) b2026_barread_item_free(&results[i]);
    return result;
}

char *read_barcode_from_path_text(const char *path) {
    B2026_BarReadItem results[16];
    char *result = NULL;
    int count = 0;
    int width = 0;
    int height = 0;
    int i;
    if (path == NULL || *path == '\0') return NULL;
    b2026_barread_require_zbarimg();
    (void)b2026_barread_path_dimensions(path, &width, &height);
    for (i = 0; i < 16; ++i) b2026_barread_item_init(&results[i]);
    (void)b2026_barread_detect_results_from_path(path, width, height, results, &count, 16);
    result = b2026_barread_first_text_from_results(results, count);
    for (i = 0; i < 16; ++i) b2026_barread_item_free(&results[i]);
    return result;
}

char *read_barcode_from_index_text(int target_index) {
    return read_barcode_from_path_text(g_runtime_argv[resolve_cli_target_index(target_index)]);
}

void read_barcode_to_string_array_from_array(const char *target_name, const char *array_name) {
    ArrayVariable *array_var = require_array_variable(array_name);
    B2026_BarReadItem results[B2026_BARREAD_RESULTS_MAX];
    char *temp_path = NULL;
    int count = 0;
    int i;
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_init(&results[i]);
    if (array_var != NULL && array_var->screen_width > 0 && array_var->screen_height > 0) {
        b2026_barread_require_zbarimg();
        if (b2026_barread_save_array_png(array_var, &temp_path) && temp_path != NULL) {
            (void)b2026_barread_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &count, B2026_BARREAD_RESULTS_MAX);
            b2026_unlink(temp_path);
            free(temp_path);
        }
    }
    b2026_barread_fill_string_array_from_results(target_name, results, count);
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_free(&results[i]);
}

void read_barcode_to_string_array_from_path(const char *target_name, const char *path) {
    B2026_BarReadItem results[B2026_BARREAD_RESULTS_MAX];
    int count = 0;
    int width = 0;
    int height = 0;
    int i;
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_init(&results[i]);
    if (path != NULL && *path != '\0') {
        b2026_barread_require_zbarimg();
        (void)b2026_barread_path_dimensions(path, &width, &height);
        (void)b2026_barread_detect_results_from_path(path, width, height, results, &count, B2026_BARREAD_RESULTS_MAX);
    }
    b2026_barread_fill_string_array_from_results(target_name, results, count);
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_free(&results[i]);
}

void read_barcode_to_string_array_from_index(const char *target_name, int target_index) {
    read_barcode_to_string_array_from_path(target_name, g_runtime_argv[resolve_cli_target_index(target_index)]);
}

static void b2026_barread_store_results_to_hash(const char *target_name, B2026_BarReadItem *results, int count) {
    HashVariable *hash_var = ensure_local_hash(get_hash_frame(target_name), target_name);
    int i;
    char key[64];
    if (hash_var == NULL) return;
    clear_hash_entries(hash_var);
    hash_set_string(target_name, "kind", "BARCODELIST");
    hash_set_number(target_name, "flag", count > 0 ? -1.0 : 0.0);
    hash_set_number(target_name, "count", (double)count);
    if (count <= 0) {
        hash_set_number(target_name, "text", null_value());
        hash_set_number(target_name, "type", null_value());
        hash_set_number(target_name, "subtype", null_value());
        return;
    }
    hash_set_string(target_name, "text", results[0].text != NULL ? results[0].text : "");
    hash_set_string(target_name, "type", results[0].type != NULL ? results[0].type : "UNKNOWN");
    {
        const char *subtype0 = b2026_barread_detect_subtype(results[0].type, results[0].text);
        if (subtype0 != NULL) hash_set_string(target_name, "subtype", subtype0);
        else hash_set_number(target_name, "subtype", null_value());
    }
    hash_set_number(target_name, "x", (double)results[0].x);
    hash_set_number(target_name, "y", (double)results[0].y);
    hash_set_number(target_name, "width", (double)results[0].width);
    hash_set_number(target_name, "height", (double)results[0].height);
    for (i = 0; i < count; ++i) {
        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), "type%d", i + 1);
        hash_set_string(target_name, key, results[i].type != NULL ? results[i].type : "UNKNOWN");
        {
            const char *subtype_i = b2026_barread_detect_subtype(results[i].type, results[i].text);
            snprintf(key, sizeof(key), "subtype%d", i + 1);
            if (subtype_i != NULL) hash_set_string(target_name, key, subtype_i);
            else hash_set_number(target_name, key, null_value());
            snprintf(key, sizeof(key), "%d.subtype", i + 1);
            if (subtype_i != NULL) hash_set_string(target_name, key, subtype_i);
            else hash_set_number(target_name, key, null_value());
        }
        snprintf(key, sizeof(key), "x%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].x);
        snprintf(key, sizeof(key), "y%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].y);
        snprintf(key, sizeof(key), "width%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].width);
        snprintf(key, sizeof(key), "height%d", i + 1);
        hash_set_number(target_name, key, (double)results[i].height);
        snprintf(key, sizeof(key), "%d.text", i + 1);
        hash_set_string(target_name, key, results[i].text != NULL ? results[i].text : "");
        snprintf(key, sizeof(key), "%d.type", i + 1);
        hash_set_string(target_name, key, results[i].type != NULL ? results[i].type : "UNKNOWN");
    }
}

void read_barcode_into_hash_from_array(const char *target_name, const char *array_name) {
    ArrayVariable *array_var = require_array_variable(array_name);
    B2026_BarReadItem results[B2026_BARREAD_RESULTS_MAX];
    char *temp_path = NULL;
    int count = 0;
    int i;
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_init(&results[i]);
    if (array_var != NULL && array_var->screen_width > 0 && array_var->screen_height > 0) {
        b2026_barread_require_zbarimg();
        if (b2026_barread_save_array_png(array_var, &temp_path) && temp_path != NULL) {
            (void)b2026_barread_detect_results_from_path(temp_path, array_var->screen_width, array_var->screen_height, results, &count, B2026_BARREAD_RESULTS_MAX);
            b2026_unlink(temp_path);
            free(temp_path);
        }
    }
    b2026_barread_store_results_to_hash(target_name, results, count);
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_free(&results[i]);
}

void read_barcode_into_hash_from_path(const char *target_name, const char *path) {
    B2026_BarReadItem results[B2026_BARREAD_RESULTS_MAX];
    int count = 0;
    int width = 0;
    int height = 0;
    int i;
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_init(&results[i]);
    if (path != NULL && *path != '\0') {
        b2026_barread_require_zbarimg();
        (void)b2026_barread_path_dimensions(path, &width, &height);
        (void)b2026_barread_detect_results_from_path(path, width, height, results, &count, B2026_BARREAD_RESULTS_MAX);
    }
    b2026_barread_store_results_to_hash(target_name, results, count);
    for (i = 0; i < B2026_BARREAD_RESULTS_MAX; ++i) b2026_barread_item_free(&results[i]);
}

void read_barcode_into_hash_from_index(const char *target_name, int target_index) {
    read_barcode_into_hash_from_path(target_name, g_runtime_argv[resolve_cli_target_index(target_index)]);
}
