virDomainDefFormatFeatures(virBufferPtr buf,
                           virDomainDefPtr def)
{
    g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
    size_t i;

    for (i = 0; i < VIR_DOMAIN_FEATURE_LAST; i++) {
        g_auto(virBuffer) tmpAttrBuf = VIR_BUFFER_INITIALIZER;
        g_auto(virBuffer) tmpChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf);
        const char *name = virDomainFeatureTypeToString(i);
        size_t j;

        switch ((virDomainFeature) i) {
        case VIR_DOMAIN_FEATURE_ACPI:
        case VIR_DOMAIN_FEATURE_PAE:
        case VIR_DOMAIN_FEATURE_VIRIDIAN:
        case VIR_DOMAIN_FEATURE_PRIVNET:
            /* NOTE: This is for old style <opt/> booleans. New XML
             * should use the explicit state=on|off output below */
            switch ((virTristateSwitch) def->features[i]) {
            case VIR_TRISTATE_SWITCH_ABSENT:
                break;

            case VIR_TRISTATE_SWITCH_ON:
               virBufferAsprintf(&childBuf, "<%s/>\n", name);
               break;

            case VIR_TRISTATE_SWITCH_LAST:
            case VIR_TRISTATE_SWITCH_OFF:
               virReportError(VIR_ERR_INTERNAL_ERROR,
                             _("Unexpected state of feature '%s'"), name);
               return -1;
               break;
            }

            break;

        case VIR_DOMAIN_FEATURE_VMCOREINFO:
        case VIR_DOMAIN_FEATURE_HAP:
        case VIR_DOMAIN_FEATURE_PMU:
        case VIR_DOMAIN_FEATURE_PVSPINLOCK:
        case VIR_DOMAIN_FEATURE_VMPORT:
        case VIR_DOMAIN_FEATURE_HTM:
        case VIR_DOMAIN_FEATURE_NESTED_HV:
        case VIR_DOMAIN_FEATURE_CCF_ASSIST:
            switch ((virTristateSwitch) def->features[i]) {
            case VIR_TRISTATE_SWITCH_LAST:
            case VIR_TRISTATE_SWITCH_ABSENT:
                break;

            case VIR_TRISTATE_SWITCH_ON:
               virBufferAsprintf(&childBuf, "<%s state='on'/>\n", name);
               break;

            case VIR_TRISTATE_SWITCH_OFF:
               virBufferAsprintf(&childBuf, "<%s state='off'/>\n", name);
               break;
            }

            break;

        case VIR_DOMAIN_FEATURE_SMM:
            if (def->features[i] == VIR_TRISTATE_SWITCH_ABSENT)
                break;

            virBufferAsprintf(&tmpAttrBuf, " state='%s'",
                              virTristateSwitchTypeToString(def->features[i]));

            if (def->features[i] == VIR_TRISTATE_SWITCH_ON &&
                def->tseg_specified) {
                const char *unit;
                unsigned long long short_size = virFormatIntPretty(def->tseg_size,
                                                                   &unit);

                virBufferAsprintf(&tmpChildBuf, "<tseg unit='%s'>%llu</tseg>\n",
                                  unit, short_size);
            }

            virXMLFormatElement(&childBuf, "smm", &tmpAttrBuf, &tmpChildBuf);

            break;

        case VIR_DOMAIN_FEATURE_APIC:
            if (def->features[i] == VIR_TRISTATE_SWITCH_ON) {
                virBufferAddLit(&childBuf, "<apic");
                if (def->apic_eoi) {
                    virBufferAsprintf(&childBuf, " eoi='%s'",
                                      virTristateSwitchTypeToString(def->apic_eoi));
                }
                virBufferAddLit(&childBuf, "/>\n");
            }
            break;

        case VIR_DOMAIN_FEATURE_HYPERV:
            if (def->features[i] != VIR_TRISTATE_SWITCH_ON)
                break;

            virBufferAddLit(&childBuf, "<hyperv>\n");
            virBufferAdjustIndent(&childBuf, 2);
            for (j = 0; j < VIR_DOMAIN_HYPERV_LAST; j++) {
                if (def->hyperv_features[j] == VIR_TRISTATE_SWITCH_ABSENT)
                    continue;

                virBufferAsprintf(&childBuf, "<%s state='%s'",
                                  virDomainHypervTypeToString(j),
                                  virTristateSwitchTypeToString(
                                      def->hyperv_features[j]));

                switch ((virDomainHyperv) j) {
                case VIR_DOMAIN_HYPERV_RELAXED:
                case VIR_DOMAIN_HYPERV_VAPIC:
                case VIR_DOMAIN_HYPERV_VPINDEX:
                case VIR_DOMAIN_HYPERV_RUNTIME:
                case VIR_DOMAIN_HYPERV_SYNIC:
                case VIR_DOMAIN_HYPERV_RESET:
                case VIR_DOMAIN_HYPERV_FREQUENCIES:
                case VIR_DOMAIN_HYPERV_REENLIGHTENMENT:
                case VIR_DOMAIN_HYPERV_TLBFLUSH:
                case VIR_DOMAIN_HYPERV_IPI:
                case VIR_DOMAIN_HYPERV_EVMCS:
                    virBufferAddLit(&childBuf, "/>\n");
                    break;

                case VIR_DOMAIN_HYPERV_SPINLOCKS:
                    if (def->hyperv_features[j] != VIR_TRISTATE_SWITCH_ON) {
                        virBufferAddLit(&childBuf, "/>\n");
                        break;
                    }
                    virBufferAsprintf(&childBuf, " retries='%d'/>\n",
                                      def->hyperv_spinlocks);
                    break;

                case VIR_DOMAIN_HYPERV_STIMER:
                    if (def->hyperv_features[j] != VIR_TRISTATE_SWITCH_ON) {
                        virBufferAddLit(&childBuf, "/>\n");
                        break;
                    }
                    if (def->hyperv_stimer_direct == VIR_TRISTATE_SWITCH_ON) {
                        virBufferAddLit(&childBuf, ">\n");
                        virBufferAdjustIndent(&childBuf, 2);
                        virBufferAddLit(&childBuf, "<direct state='on'/>\n");
                        virBufferAdjustIndent(&childBuf, -2);
                        virBufferAddLit(&childBuf, "</stimer>\n");
                    } else {
                        virBufferAddLit(&childBuf, "/>\n");
                    }

                    break;

                case VIR_DOMAIN_HYPERV_VENDOR_ID:
                    if (def->hyperv_features[j] != VIR_TRISTATE_SWITCH_ON) {
                        virBufferAddLit(&childBuf, "/>\n");
                        break;
                    }
                    virBufferEscapeString(&childBuf, " value='%s'/>\n",
                                          def->hyperv_vendor_id);
                    break;

                /* coverity[dead_error_begin] */
                case VIR_DOMAIN_HYPERV_LAST:
                    break;
                }
            }
            virBufferAdjustIndent(&childBuf, -2);
            virBufferAddLit(&childBuf, "</hyperv>\n");
            break;

        case VIR_DOMAIN_FEATURE_KVM:
            if (def->features[i] != VIR_TRISTATE_SWITCH_ON)
                break;

            virBufferAddLit(&childBuf, "<kvm>\n");
            virBufferAdjustIndent(&childBuf, 2);
            for (j = 0; j < VIR_DOMAIN_KVM_LAST; j++) {
                switch ((virDomainKVM) j) {
                case VIR_DOMAIN_KVM_HIDDEN:
                case VIR_DOMAIN_KVM_DEDICATED:
                    if (def->kvm_features[j])
                        virBufferAsprintf(&childBuf, "<%s state='%s'/>\n",
                                          virDomainKVMTypeToString(j),
                                          virTristateSwitchTypeToString(
                                              def->kvm_features[j]));
                    break;

                /* coverity[dead_error_begin] */
                case VIR_DOMAIN_KVM_LAST:
                    break;
                }
            }
            virBufferAdjustIndent(&childBuf, -2);
            virBufferAddLit(&childBuf, "</kvm>\n");
            break;

        case VIR_DOMAIN_FEATURE_CAPABILITIES:
            for (j = 0; j < VIR_DOMAIN_PROCES_CAPS_FEATURE_LAST; j++) {
                if (def->caps_features[j] != VIR_TRISTATE_SWITCH_ABSENT)
                    virBufferAsprintf(&tmpChildBuf, "<%s state='%s'/>\n",
                                      virDomainProcessCapsFeatureTypeToString(j),
                                      virTristateSwitchTypeToString(def->caps_features[j]));
            }

            /* the 'default' policy should be printed if any capability is present */
            if (def->features[i] != VIR_DOMAIN_CAPABILITIES_POLICY_DEFAULT ||
                virBufferUse(&tmpChildBuf))
                virBufferAsprintf(&tmpAttrBuf, " policy='%s'",
                                  virDomainCapabilitiesPolicyTypeToString(def->features[i]));

            virXMLFormatElement(&childBuf, "capabilities", &tmpAttrBuf, &tmpChildBuf);
            break;

        case VIR_DOMAIN_FEATURE_GIC:
            if (def->features[i] == VIR_TRISTATE_SWITCH_ON) {
                virBufferAddLit(&childBuf, "<gic");
                if (def->gic_version != VIR_GIC_VERSION_NONE)
                    virBufferAsprintf(&childBuf, " version='%s'",
                                      virGICVersionTypeToString(def->gic_version));
                virBufferAddLit(&childBuf, "/>\n");
            }
            break;

        case VIR_DOMAIN_FEATURE_IOAPIC:
            if (def->features[i] == VIR_DOMAIN_IOAPIC_NONE)
                break;

            virBufferAsprintf(&childBuf, "<ioapic driver='%s'/>\n",
                              virDomainIOAPICTypeToString(def->features[i]));
            break;

        case VIR_DOMAIN_FEATURE_HPT:
            if (def->features[i] != VIR_TRISTATE_SWITCH_ON)
                break;

            if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE) {
                virBufferAsprintf(&tmpAttrBuf,
                                  " resizing='%s'",
                                  virDomainHPTResizingTypeToString(def->hpt_resizing));
            }
            if (def->hpt_maxpagesize > 0) {
                virBufferAsprintf(&tmpChildBuf,
                                  "<maxpagesize unit='KiB'>%llu</maxpagesize>\n",
                                  def->hpt_maxpagesize);
            }

            virXMLFormatElement(&childBuf, "hpt", &tmpAttrBuf, &tmpChildBuf);
            break;

        case VIR_DOMAIN_FEATURE_MSRS:
            if (def->features[i] != VIR_TRISTATE_SWITCH_ON)
                break;

            virBufferAsprintf(&childBuf, "<msrs unknown='%s'/>\n",
                              virDomainMsrsUnknownTypeToString(def->msrs_features[VIR_DOMAIN_MSRS_UNKNOWN]));
            break;

        /* coverity[dead_error_begin] */
        case VIR_DOMAIN_FEATURE_LAST:
            break;
        }
    }

    virXMLFormatElement(buf, "features", NULL, &childBuf);
    return 0;
}