int acpi_table_add(const QemuOpts *opts)

{

    AcpiTableOptions *hdrs = NULL;

    Error *err = NULL;

    char **pathnames = NULL;

    char **cur;



    size_t len, start, allen;

    bool has_header;

    int changed;

    int r;

    struct acpi_table_header hdr;

    char unsigned *table_start;



    {

        OptsVisitor *ov;



        ov = opts_visitor_new(opts);

        visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err);

        opts_visitor_cleanup(ov);

    }



    if (err) {

        goto out;

    }

    if (hdrs->has_file == hdrs->has_data) {

        error_setg(&err, "'-acpitable' requires one of 'data' or 'file'");

        goto out;

    }

    has_header = hdrs->has_file;



    pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0);

    if (pathnames == NULL || pathnames[0] == NULL) {

        error_setg(&err, "'-acpitable' requires at least one pathname");

        goto out;

    }



    if (!acpi_tables) {

        allen = sizeof(uint16_t);

        acpi_tables = g_malloc0(allen);

    } else {

        allen = acpi_tables_len;

    }



    start = allen;

    acpi_tables = g_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE);

    allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE;



    /* now read in the data files, reallocating buffer as needed */



    for (cur = pathnames; *cur; ++cur) {

        int fd = open(*cur, O_RDONLY | O_BINARY);



        if (fd < 0) {

            error_setg(&err, "can't open file %s: %s", *cur, strerror(errno));

            goto out;

        }



        for (;;) {

            char unsigned data[8192];

            r = read(fd, data, sizeof(data));

            if (r == 0) {

                break;

            } else if (r > 0) {

                acpi_tables = g_realloc(acpi_tables, allen + r);

                memcpy(acpi_tables + allen, data, r);

                allen += r;

            } else if (errno != EINTR) {

                error_setg(&err, "can't read file %s: %s",

                           *cur, strerror(errno));

                close(fd);

                goto out;

            }

        }



        close(fd);

    }



    /* now fill in the header fields */



    table_start = acpi_tables + start;   /* start of the table */

    changed = 0;



    /* copy the header to temp place to align the fields */

    memcpy(&hdr, has_header ? table_start : dfl_hdr, ACPI_TABLE_HDR_SIZE);



    /* length of the table minus our prefix */

    len = allen - start - ACPI_TABLE_PFX_SIZE;



    hdr._length = cpu_to_le16(len);



    if (hdrs->has_sig) {

        /* strncpy is justified: the field need not be NUL-terminated. */

        strncpy(hdr.sig, hdrs->sig, sizeof(hdr.sig));

        ++changed;

    }



    /* length of the table including header, in bytes */

    if (has_header) {

        unsigned long val;



        /* check if actual length is correct */

        val = le32_to_cpu(hdr.length);

        if (val != len) {

            fprintf(stderr,

                "warning: acpitable has wrong length,"

                " header says %lu, actual size %zu bytes\n",

                val, len);

            ++changed;

        }

    }

    /* we may avoid putting length here if has_header is true */

    hdr.length = cpu_to_le32(len);



    if (hdrs->has_rev) {

        hdr.revision = hdrs->rev;

        ++changed;

    }



    if (hdrs->has_oem_id) {

        /* strncpy is justified: the field need not be NUL-terminated. */

        strncpy(hdr.oem_id, hdrs->oem_id, sizeof(hdr.oem_id));

        ++changed;

    }



    if (hdrs->has_oem_table_id) {

        /* strncpy is justified: the field need not be NUL-terminated. */

        strncpy(hdr.oem_table_id, hdrs->oem_table_id,

                sizeof(hdr.oem_table_id));

        ++changed;

    }



    if (hdrs->has_oem_rev) {

        hdr.oem_revision = cpu_to_le32(hdrs->oem_rev);

        ++changed;

    }



    if (hdrs->has_asl_compiler_id) {

        /* strncpy is justified: the field need not be NUL-terminated. */

        strncpy(hdr.asl_compiler_id, hdrs->asl_compiler_id,

                sizeof(hdr.asl_compiler_id));

        ++changed;

    }



    if (hdrs->has_asl_compiler_rev) {

        hdr.asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev);

        ++changed;

    }



    if (!has_header && !changed) {

        fprintf(stderr, "warning: acpitable: no table headers are specified\n");

    }





    /* now calculate checksum of the table, complete with the header */

    /* we may as well leave checksum intact if has_header is true */

    /* alternatively there may be a way to set cksum to a given value */

    hdr.checksum = 0;    /* for checksum calculation */



    /* put header back */

    memcpy(table_start, &hdr, sizeof(hdr));



    if (changed || !has_header || 1) {

        ((struct acpi_table_header *)table_start)->checksum =

            acpi_checksum((uint8_t *)table_start + ACPI_TABLE_PFX_SIZE, len);

    }



    /* increase number of tables */

    (*(uint16_t *)acpi_tables) =

        cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1);



    acpi_tables_len = allen;



out:

    g_strfreev(pathnames);



    if (hdrs != NULL) {

        QapiDeallocVisitor *dv;



        dv = qapi_dealloc_visitor_new();

        visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL,

                                    NULL);

        qapi_dealloc_visitor_cleanup(dv);

    }



    if (err) {

        fprintf(stderr, "%s\n", error_get_pretty(err));

        error_free(err);

        return -1;

    }

    return 0;

}
