virDomainChrTargetDefFormat(virBufferPtr buf,
                            const virDomainChrDef *def,
                            unsigned int flags)
{
    const char *targetType = virDomainChrTargetTypeToString(def->deviceType,
                                                            def->targetType);

    switch ((virDomainChrDeviceType) def->deviceType) {
    case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: {
        if (!targetType) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Could not format channel target type"));
            return -1;
        }
        virBufferAsprintf(buf, "<target type='%s'", targetType);

        switch (def->targetType) {
        case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: {
            int port = virSocketAddrGetPort(def->target.addr);
            g_autofree char *addr = NULL;
            if (port < 0) {
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("Unable to format guestfwd port"));
                return -1;
            }

            addr = virSocketAddrFormat(def->target.addr);
            if (addr == NULL)
                return -1;

            virBufferAsprintf(buf, " address='%s' port='%d'",
                              addr, port);
            break;
        }

        case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN:
        case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO:
            if (def->target.name)
                virBufferEscapeString(buf, " name='%s'", def->target.name);

            if (def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO &&
                def->state != VIR_DOMAIN_CHR_DEVICE_STATE_DEFAULT &&
                !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) {
                virBufferAsprintf(buf, " state='%s'",
                                  virDomainChrDeviceStateTypeToString(def->state));
            }
            break;
        }

        virBufferAddLit(buf, "/>\n");
        break;
    }

    case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE:
        if (!targetType) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Could not format console target type"));
            return -1;
        }

        virBufferAsprintf(buf,
                          "<target type='%s' port='%d'/>\n",
                          targetType, def->target.port);
        break;

    case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL:
        if (!targetType) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Could not format serial target type"));
            return -1;
        }

        virBufferAddLit(buf, "<target ");

        if (def->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) {
            virBufferAsprintf(buf,
                              "type='%s' ",
                              targetType);
        }

        virBufferAsprintf(buf,
                          "port='%d'",
                          def->target.port);

        if (def->targetModel != VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) {
            virBufferAddLit(buf, ">\n");

            virBufferAdjustIndent(buf, 2);
            virBufferAsprintf(buf,
                              "<model name='%s'/>\n",
                              virDomainChrSerialTargetModelTypeToString(def->targetModel));
            virBufferAdjustIndent(buf, -2);

            virBufferAddLit(buf, "</target>\n");
        } else {
            virBufferAddLit(buf, "/>\n");
        }

        break;

    case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL:
        virBufferAsprintf(buf, "<target port='%d'/>\n",
                          def->target.port);
        break;

    case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST:
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unexpected char device type %d"),
                       def->deviceType);
        return -1;
    }

    return 0;
}