static void v9fs_xattrcreate(void *opaque)
{
    int flags;
    int32_t fid;
    int64_t size;
    ssize_t err = 0;
    V9fsString name;
    size_t offset = 7;
    V9fsFidState *file_fidp;
    V9fsFidState *xattr_fidp;
    V9fsPDU *pdu = opaque;

    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags);
    if (err < 0) {
        goto out_nofid;
    }
    trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags);

    file_fidp = get_fid(pdu, fid);
    if (file_fidp == NULL) {
        err = -EINVAL;
        goto out_nofid;
    }
    /* Make the file fid point to xattr */
    xattr_fidp = file_fidp;
    xattr_fidp->fid_type = P9_FID_XATTR;
    xattr_fidp->fs.xattr.copied_len = 0;
    xattr_fidp->fs.xattr.len = size;
    xattr_fidp->fs.xattr.flags = flags;
    v9fs_string_init(&xattr_fidp->fs.xattr.name);
    v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name);
    xattr_fidp->fs.xattr.value = g_malloc(size);
    err = offset;
    put_fid(pdu, file_fidp);
out_nofid:
    pdu_complete(pdu, err);
    v9fs_string_free(&name);
}


static void v9fs_wstat(void *opaque)
{
    int32_t fid;
    int err = 0;
    int16_t unused;
    V9fsStat v9stat;
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;

    v9fs_stat_init(&v9stat);
    err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
    if (err < 0) {
        goto out_nofid;
    }
    trace_v9fs_wstat(pdu->tag, pdu->id, fid,
                     v9stat.mode, v9stat.atime, v9stat.mtime);

    fidp = get_fid(pdu, fid);
    if (fidp == NULL) {
        err = -EINVAL;
        goto out_nofid;
    }
    /* do we need to sync the file? */
    if (donttouch_stat(&v9stat)) {
        err = v9fs_co_fsync(pdu, fidp, 0);
        goto out;
    }
    if (v9stat.mode != -1) {
        uint32_t v9_mode;
        err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
        if (err < 0) {
            goto out;
        }
        v9_mode = stat_to_v9mode(&stbuf);
        if ((v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
            (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
            /* Attempting to change the type */
            err = -EIO;
            goto out;
        }
        err = v9fs_co_chmod(pdu, &fidp->path,
                            v9mode_to_mode(v9stat.mode,
                                           &v9stat.extension));
        if (err < 0) {
            goto out;
        }
    }
    if (v9stat.mtime != -1 || v9stat.atime != -1) {
        struct timespec times[2];
        if (v9stat.atime != -1) {
            times[0].tv_sec = v9stat.atime;
            times[0].tv_nsec = 0;
        } else {
            times[0].tv_nsec = UTIME_OMIT;
        }
        if (v9stat.mtime != -1) {
            times[1].tv_sec = v9stat.mtime;
            times[1].tv_nsec = 0;
        } else {
            times[1].tv_nsec = UTIME_OMIT;
        }
        err = v9fs_co_utimensat(pdu, &fidp->path, times);
        if (err < 0) {
            goto out;
        }
    }
    if (v9stat.n_gid != -1 || v9stat.n_uid != -1) {
        err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid);
        if (err < 0) {
            goto out;
        }
    }
    if (v9stat.name.size != 0) {
        err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name);
        if (err < 0) {
            goto out;
        }
    }
    if (v9stat.length != -1) {
        err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length);
        if (err < 0) {
            goto out;
        }
    }
    err = offset;
out:
    put_fid(pdu, fidp);
out_nofid:
    v9fs_stat_free(&v9stat);
    pdu_complete(pdu, err);
}


static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
                                V9fsStatDotl *v9lstat)
{
    memset(v9lstat, 0, sizeof(*v9lstat));

    v9lstat->st_mode = stbuf->st_mode;
    v9lstat->st_nlink = stbuf->st_nlink;
    v9lstat->st_uid = stbuf->st_uid;
    v9lstat->st_gid = stbuf->st_gid;
    v9lstat->st_rdev = stbuf->st_rdev;
    v9lstat->st_size = stbuf->st_size;
    v9lstat->st_blksize = stbuf->st_blksize;
    v9lstat->st_blocks = stbuf->st_blocks;
    v9lstat->st_atime_sec = stbuf->st_atime;
    v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
    v9lstat->st_mtime_sec = stbuf->st_mtime;
    v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
    v9lstat->st_ctime_sec = stbuf->st_ctime;
    v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
    /* Currently we only support BASIC fields in stat */
    v9lstat->st_result_mask = P9_STATS_BASIC;

    stat_to_qid(stbuf, &v9lstat->qid);
}


static void v9fs_op_not_supp(void *opaque)
{
    V9fsPDU *pdu = opaque;
    pdu_complete(pdu, -EOPNOTSUPP);
}


static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp)
{
    int retval = 0;

    if (fidp->fs.xattr.copied_len == -1) {
        /* getxattr/listxattr fid */
        goto free_value;
    }
    /*
     * if this is fid for setxattr. clunk should
     * result in setxattr localcall
     */
    if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
        /* clunk after partial write */
        retval = -EINVAL;
        goto free_out;
    }
    if (fidp->fs.xattr.len) {
        retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name,
                                   fidp->fs.xattr.value,
                                   fidp->fs.xattr.len,
                                   fidp->fs.xattr.flags);
    } else {
        retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name);
    }
free_out:
    v9fs_string_free(&fidp->fs.xattr.name);
free_value:
    g_free(fidp->fs.xattr.value);
    return retval;
}


CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
{
  char *s;
  va_list ap;
  va_start(ap, fmt);
  s = vaprintf(fmt, ap); /* this allocs a new string to append */
  va_end(ap);

  if(s) {
    CURLcode result = Curl_add_buffer(in, s, strlen(s));
    free(s);
    return result;
  }
  /* If we failed, we cleanup the whole buffer and return error */
  if(in->buffer)
    free(in->buffer);
  free(in);
  return CURLE_OUT_OF_MEMORY;
}


static bool pickoneauth(struct auth *pick)
{
  bool picked;
  /* only deal with authentication we want */
  unsigned long avail = pick->avail & pick->want;
  picked = TRUE;

  /* The order of these checks is highly relevant, as this will be the order
     of preference in case of the existence of multiple accepted types. */
  if(avail & CURLAUTH_GSSNEGOTIATE)
    pick->picked = CURLAUTH_GSSNEGOTIATE;
  else if(avail & CURLAUTH_DIGEST)
    pick->picked = CURLAUTH_DIGEST;
  else if(avail & CURLAUTH_NTLM)
    pick->picked = CURLAUTH_NTLM;
  else if(avail & CURLAUTH_NTLM_WB)
    pick->picked = CURLAUTH_NTLM_WB;
  else if(avail & CURLAUTH_BASIC)
    pick->picked = CURLAUTH_BASIC;
  else {
    pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
    picked = FALSE;
  }
  pick->avail = CURLAUTH_NONE; /* clear it here */

  return picked;
}


void Curl_cleanup_negotiate(struct SessionHandle *data)
{
  cleanup(&data->state.negotiate);
  cleanup(&data->state.proxyneg);
}


int Curl_input_negotiate(struct connectdata *conn, bool proxy,
                         const char *header)
{
  struct SessionHandle *data = conn->data;
  struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg:
    &data->state.negotiate;
  OM_uint32 major_status, minor_status, discard_st;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  int ret;
  size_t len;
  size_t rawlen = 0;
  bool gss;
  const char* protocol;
  CURLcode error;

  if(checkprefix("GSS-Negotiate", header)) {
    protocol = "GSS-Negotiate";
    gss = TRUE;
  }
  else if(checkprefix("Negotiate", header)) {
    protocol = "Negotiate";
    gss = FALSE;
  }
  else
    return -1;

  if(neg_ctx->context) {
    if(neg_ctx->gss != gss) {
      return -1;
    }
  }
  else {
    neg_ctx->protocol = protocol;
    neg_ctx->gss = gss;
  }

  if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
    /* We finished successfully our part of authentication, but server
     * rejected it (since we're again here). Exit with an error since we
     * can't invent anything better */
    Curl_cleanup_negotiate(data);
    return -1;
  }

  if(neg_ctx->server_name == NULL &&
      (ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
    return ret;

  header += strlen(neg_ctx->protocol);
  while(*header && ISSPACE(*header))
    header++;

  len = strlen(header);
  if(len > 0) {
    error = Curl_base64_decode(header,
                               (unsigned char **)&input_token.value, &rawlen);
    if(error || rawlen == 0)
      return -1;
    input_token.length = rawlen;

    DEBUGASSERT(input_token.value != NULL);
  }

  major_status = Curl_gss_init_sec_context(data,
                                           &minor_status,
                                           &neg_ctx->context,
                                           neg_ctx->server_name,
                                           TRUE,
                                           GSS_C_NO_CHANNEL_BINDINGS,
                                           &input_token,
                                           &output_token,
                                           NULL);
  Curl_safefree(input_token.value);

  neg_ctx->status = major_status;
  if(GSS_ERROR(major_status)) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    log_gss_error(conn, minor_status, "gss_init_sec_context() failed: ");
    return -1;
  }

  if(!output_token.value || !output_token.length) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    return -1;
  }

  neg_ctx->output_token = output_token;
  return 0;
}


get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server)
{
  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
    &conn->data->state.negotiate;
  OM_uint32 major_status, minor_status;
  gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
  char name[2048];
  const char* service;

  /* GSSAPI implementation by Globus (known as GSI) requires the name to be
     of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
     of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
     Change following lines if you want to use GSI */

  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */

  if(neg_ctx->gss)
    service = "KHTTP";
  else
    service = "HTTP";

  token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
                                              conn->host.name) + 1;
  if(token.length + 1 > sizeof(name))
    return EMSGSIZE;

  snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name :
           conn->host.name);

  token.value = (void *) name;
  major_status = gss_import_name(&minor_status,
                                 &token,
                                 GSS_C_NT_HOSTBASED_SERVICE,
                                 server);

  return GSS_ERROR(major_status) ? -1 : 0;
}


static int local_symlink(FsContext *fs_ctx, const char *oldpath,
                         V9fsPath *dir_path, const char *name, FsCred *credp)
{
    int err = -1;
    int dirfd;

    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
        local_is_mapped_file_metadata(fs_ctx, name)) {
        errno = EINVAL;
        return -1;
    }

    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
    if (dirfd == -1) {
        return -1;
    }

    /* Determine the security model */
    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int fd;
        ssize_t oldpath_size, write_size;

        fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
                         SM_LOCAL_MODE_BITS);
        if (fd == -1) {
            goto out;
        }
        /* Write the oldpath (target) to the file. */
        oldpath_size = strlen(oldpath);
        do {
            write_size = write(fd, (void *)oldpath, oldpath_size);
        } while (write_size == -1 && errno == EINTR);
        close_preserve_errno(fd);

        if (write_size != oldpath_size) {
            goto err_end;
        }
        /* Set cleint credentials in symlink's xattr */
        credp->fc_mode = credp->fc_mode | S_IFLNK;

        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
            err = local_set_xattrat(dirfd, name, credp);
        } else {
            err = local_set_mapped_file_attrat(dirfd, name, credp);
        }
        if (err == -1) {
            goto err_end;
        }
    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
               fs_ctx->export_flags & V9FS_SM_NONE) {
        err = symlinkat(oldpath, dirfd, name);
        if (err) {
            goto out;
        }
        err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
                       AT_SYMLINK_NOFOLLOW);
        if (err == -1) {
            /*
             * If we fail to change ownership and if we are
             * using security model none. Ignore the error
             */
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
                goto err_end;
            } else {
                err = 0;
            }
        }
    }
    goto out;

err_end:
    unlinkat_preserve_errno(dirfd, name, 0);
out:
    close_preserve_errno(dirfd);
    return err;
}


InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
{
    InputEvent *evt = g_new0(InputEvent, 1);
    evt->u.btn.data = g_new0(InputBtnEvent, 1);
    evt->type = INPUT_EVENT_KIND_BTN;
    evt->u.btn.data->button = btn;
    evt->u.btn.data->down = down;
    return evt;
}


void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
{
    KeyValue *key = g_new0(KeyValue, 1);
    key->type = KEY_VALUE_KIND_QCODE;
    key->u.qcode.data = q;
    qemu_input_event_send_key(src, key, down);
}


void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
{
    notifier_remove(notify);
}


void qemu_input_event_sync(void)
{
    if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
        return;
    }

    replay_input_sync_event();
}


static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue,
                                   QemuConsole *src, InputEvent *evt)
{
    QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1);

    item->type = QEMU_INPUT_QUEUE_EVENT;
    item->src = src;
    item->evt = evt;
    QTAILQ_INSERT_TAIL(queue, item, node);
    queue_count++;
}


int qemu_input_scale_axis(int value,
                          int min_in, int max_in,
                          int min_out, int max_out)
{
    int64_t range_in = (int64_t)max_in - min_in;
    int64_t range_out = (int64_t)max_out - min_out;

    if (range_in < 1) {
        return min_out + range_out / 2;
    }
    return ((int64_t)value - min_in) * range_out / range_in + min_out;
}


static int qemu_input_transform_invert_abs_value(int value)
{
  return (int64_t)INPUT_EVENT_ABS_MAX - value + INPUT_EVENT_ABS_MIN;
}


void hid_set_next_idle(HIDState *hs)
{
    if (hs->idle) {
        uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
                               NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000;
        if (!hs->idle_timer) {
            hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
        }
        timer_mod_ns(hs->idle_timer, expire_time);
    } else {
        hid_del_idle_timer(hs);
    }
}


void hid_free(HIDState *hs)
{
    qemu_input_handler_unregister(hs->s);
    hid_del_idle_timer(hs);
}


static void hid_del_idle_timer(HIDState *hs)
{
    if (hs->idle_timer) {
        timer_del(hs->idle_timer);
        timer_free(hs->idle_timer);
        hs->idle_timer = NULL;
    }
}


static inline int int_clamp(int val, int vmin, int vmax)
{
    if (val < vmin) {
        return vmin;
    } else if (val > vmax) {
        return vmax;
    } else {
        return val;
    }
}


bool hid_has_events(HIDState *hs)
{
    return hs->n > 0 || hs->idle_pending;
}


void vnc_flush(VncState *vs)
{
    vnc_lock_output(vs);
    if (vs->ioc != NULL && vs->output.offset) {
        vnc_client_write_locked(vs);
    }
    vnc_unlock_output(vs);
}


static int vnc_client_read(VncState *vs)
{
    ssize_t ret;

#ifdef CONFIG_VNC_SASL
    if (vs->sasl.conn && vs->sasl.runSSF)
        ret = vnc_client_read_sasl(vs);
    else
#endif /* CONFIG_VNC_SASL */
        ret = vnc_client_read_plain(vs);
    if (!ret) {
        if (vs->disconnecting) {
            vnc_disconnect_finish(vs);
            return -1;
        }
        return 0;
    }

    while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
        size_t len = vs->read_handler_expect;
        int ret;

        ret = vs->read_handler(vs, vs->input.buffer, len);
        if (vs->disconnecting) {
            vnc_disconnect_finish(vs);
            return -1;
        }

        if (!ret) {
            buffer_advance(&vs->input, len);
        } else {
            vs->read_handler_expect = ret;
        }
    }
    return 0;
}


static int vnc_display_listen_addr(VncDisplay *vd,
                                   SocketAddress *addr,
                                   const char *name,
                                   QIOChannelSocket ***lsock,
                                   guint **lsock_tag,
                                   size_t *nlsock,
                                   Error **errp)
{
    QIODNSResolver *resolver = qio_dns_resolver_get_instance();
    SocketAddress **rawaddrs = NULL;
    size_t nrawaddrs = 0;
    Error *listenerr = NULL;
    bool listening = false;
    size_t i;

    if (qio_dns_resolver_lookup_sync(resolver, addr, &nrawaddrs,
                                     &rawaddrs, errp) < 0) {
        return -1;
    }

    for (i = 0; i < nrawaddrs; i++) {
        QIOChannelSocket *sioc = qio_channel_socket_new();

        qio_channel_set_name(QIO_CHANNEL(sioc), name);
        if (qio_channel_socket_listen_sync(
                sioc, rawaddrs[i], listenerr == NULL ? &listenerr : NULL) < 0) {
            object_unref(OBJECT(sioc));
            continue;
        }
        listening = true;
        (*nlsock)++;
        *lsock = g_renew(QIOChannelSocket *, *lsock, *nlsock);
        *lsock_tag = g_renew(guint, *lsock_tag, *nlsock);

        (*lsock)[*nlsock - 1] = sioc;
        (*lsock_tag)[*nlsock - 1] = 0;
    }

    for (i = 0; i < nrawaddrs; i++) {
        qapi_free_SocketAddress(rawaddrs[i]);
    }
    g_free(rawaddrs);

    if (listenerr) {
        if (!listening) {
            error_propagate(errp, listenerr);
            return -1;
        } else {
            error_free(listenerr);
        }
    }

    for (i = 0; i < *nlsock; i++) {
        (*lsock_tag)[i] = qio_channel_add_watch(
            QIO_CHANNEL((*lsock)[i]),
            G_IO_IN, vnc_listen_io, vd, NULL);
    }

    return 0;
}


static ssize_t vnc_client_write_plain(VncState *vs)
{
    ssize_t ret;

#ifdef CONFIG_VNC_SASL
    VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
              vs->output.buffer, vs->output.capacity, vs->output.offset,
              vs->sasl.waitWriteSSF);

    if (vs->sasl.conn &&
        vs->sasl.runSSF &&
        vs->sasl.waitWriteSSF) {
        ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
        if (ret)
            vs->sasl.waitWriteSSF -= ret;
    } else
#endif /* CONFIG_VNC_SASL */
        ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
    if (!ret)
        return 0;

    buffer_advance(&vs->output, ret);

    if (vs->output.offset == 0) {
        if (vs->ioc_tag) {
            g_source_remove(vs->ioc_tag);
        }
        vs->ioc_tag = qio_channel_add_watch(
            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
    }

    return ret;
}


static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
{
    int i;
    uint16_t limit;
    VncDisplay *vd = vs->vd;

    if (data[0] > 3) {
        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
    }

    switch (data[0]) {
    case VNC_MSG_CLIENT_SET_PIXEL_FORMAT:
        if (len == 1)
            return 20;

        set_pixel_format(vs, read_u8(data, 4),
                         read_u8(data, 6), read_u8(data, 7),
                         read_u16(data, 8), read_u16(data, 10),
                         read_u16(data, 12), read_u8(data, 14),
                         read_u8(data, 15), read_u8(data, 16));
        break;
    case VNC_MSG_CLIENT_SET_ENCODINGS:
        if (len == 1)
            return 4;

        if (len == 4) {
            limit = read_u16(data, 2);
            if (limit > 0)
                return 4 + (limit * 4);
        } else
            limit = read_u16(data, 2);

        for (i = 0; i < limit; i++) {
            int32_t val = read_s32(data, 4 + (i * 4));
            memcpy(data + 4 + (i * 4), &val, sizeof(val));
        }

        set_encodings(vs, (int32_t *)(data + 4), limit);
        break;
    case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST:
        if (len == 1)
            return 10;

        framebuffer_update_request(vs,
                                   read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
                                   read_u16(data, 6), read_u16(data, 8));
        break;
    case VNC_MSG_CLIENT_KEY_EVENT:
        if (len == 1)
            return 8;

        key_event(vs, read_u8(data, 1), read_u32(data, 4));
        break;
    case VNC_MSG_CLIENT_POINTER_EVENT:
        if (len == 1)
            return 6;

        pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
        break;
    case VNC_MSG_CLIENT_CUT_TEXT:
        if (len == 1) {
            return 8;
        }
        if (len == 8) {
            uint32_t dlen = read_u32(data, 4);
            if (dlen > (1 << 20)) {
                error_report("vnc: client_cut_text msg payload has %u bytes"
                             " which exceeds our limit of 1MB.", dlen);
                vnc_client_error(vs);
                break;
            }
            if (dlen > 0) {
                return 8 + dlen;
            }
        }

        client_cut_text(vs, read_u32(data, 4), data + 8);
        break;
    case VNC_MSG_CLIENT_QEMU:
        if (len == 1)
            return 2;

        switch (read_u8(data, 1)) {
        case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT:
            if (len == 2)
                return 12;

            ext_key_event(vs, read_u16(data, 2),
                          read_u32(data, 4), read_u32(data, 8));
            break;
        case VNC_MSG_CLIENT_QEMU_AUDIO:
            if (len == 2)
                return 4;

            switch (read_u16 (data, 2)) {
            case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
                audio_add(vs);
                break;
            case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
                audio_del(vs);
                break;
            case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
                if (len == 4)
                    return 10;
                switch (read_u8(data, 4)) {
                case 0: vs->as.fmt = AUD_FMT_U8; break;
                case 1: vs->as.fmt = AUD_FMT_S8; break;
                case 2: vs->as.fmt = AUD_FMT_U16; break;
                case 3: vs->as.fmt = AUD_FMT_S16; break;
                case 4: vs->as.fmt = AUD_FMT_U32; break;
                case 5: vs->as.fmt = AUD_FMT_S32; break;
                default:
                    VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
                    vnc_client_error(vs);
                    break;
                }
                vs->as.nchannels = read_u8(data, 5);
                if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
                    VNC_DEBUG("Invalid audio channel coount %d\n",
                              read_u8(data, 5));
                    vnc_client_error(vs);
                    break;
                }
                vs->as.freq = read_u32(data, 6);
                break;
            default:
                VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
                vnc_client_error(vs);
                break;
            }
            break;

        default:
            VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
            vnc_client_error(vs);
            break;
        }
        break;
    default:
        VNC_DEBUG("Msg: %d\n", data[0]);
        vnc_client_error(vs);
        break;
    }

    vnc_read_when(vs, protocol_client_msg, 1);
    return 0;
}


void CalendarRegressionTest::TestDeprecates(void)
{
    UErrorCode status = U_ZERO_ERROR;
    Calendar *c1 = Calendar::createInstance("ja_JP@calendar=japanese",status);
    Calendar *c2 = Calendar::createInstance("ja_JP_TRADITIONAL",status);

    if(!c1 || !c2 || U_FAILURE(status)) {
        dataerrln("Couldn't create calendars for roll of HOUR: %s", u_errorName(status));
        return;
    }

    c2->set(UCAL_HOUR,2);
    c1->setTime(c2->getTime(status),status);
    // *c1 = *c2;

    c1->roll(Calendar::HOUR,(int32_t)3,status);
    c2->roll(UCAL_HOUR,(int32_t)3,status);

    if(U_FAILURE(status)) {
        errln("Error code when trying to roll");
    } else if(*c1 != *c2) {
        errln("roll(EDateField, int32_t) had different effect than roll(UCalendarField, int32_t)");
    }

    c1->setTime(c2->getTime(status),status);
    c1->roll(Calendar::HOUR,(UBool)FALSE,status);
    c2->roll(UCAL_HOUR,(UBool)FALSE,status);

    if(U_FAILURE(status)) {
        errln("Error code when trying to roll(UBool)");
    } else if(*c1 != *c2) {
        errln("roll(EDateField, UBool) had different effect than roll(UCalendarField, UBool)");
    }

    delete c1;
    delete c2;

    status = U_ZERO_ERROR;

    c1 = Calendar::createInstance("th_TH_TRADITIONAL",status);
    c2 = Calendar::createInstance("th_TH@calendar=buddhist",status);

    if(!c1 || !c2 || U_FAILURE(status)) {
        errln("Couldn't create calendars for add of HOUR");
        return;
    }

    c2->set(UCAL_HOUR,2);
    c1->setTime(c2->getTime(status),status);
    //*c1 = *c2;

    c1->add(Calendar::HOUR,(int32_t)1,status);

    if(U_FAILURE(status)) {
        errln("Error code when trying to add Calendar::HOUR - %s", u_errorName(status));
    }

    c2->add(UCAL_HOUR,(int32_t)1,status);

    if(U_FAILURE(status)) {
        errln("Error code when trying to add - UCAL_HOUR %s", u_errorName(status));
    } else if(*c1 != *c2) {
        errln("add(EDateField) had different effect than add(UCalendarField)");
    }

    delete c1;
    delete c2;

    status = U_ZERO_ERROR;

    c1 = Calendar::createInstance("es_ES",status);
    c2 = Calendar::createInstance("es_ES",status);

    if(!c1 || !c2 || U_FAILURE(status)) {
        errln("Couldn't create calendars for add of YEAR");
        return;
    }

    c2->set(UCAL_YEAR,1900);
    c1->setTime(c2->getTime(status),status);
    //*c1 = *c2;

    c1->add(Calendar::YEAR,(int32_t)9,status);
    c2->add(UCAL_YEAR,(int32_t)9,status);

    if(U_FAILURE(status)) {
        errln("Error code when trying to add YEARs");
    } else if(*c1 != *c2) {
        errln("add(EDateField YEAR) had different effect than add(UCalendarField YEAR)");
    }

    delete c1;
    delete c2;

}


void CalendarRegressionTest::test4108764()
{
    UErrorCode status = U_ZERO_ERROR;
    Calendar *cal = Calendar::createInstance(status);
    if(U_FAILURE(status)) {
      dataerrln("Error creating calendar %s", u_errorName(status));
      delete cal;
      return;
    }
    UDate d00 = makeDate(1997, UCAL_MARCH, 15, 12, 00, 00);
    UDate d01 = makeDate(1997, UCAL_MARCH, 15, 12, 00, 56);
    UDate d10 = makeDate(1997, UCAL_MARCH, 15, 12, 34, 00);
    UDate d11 = makeDate(1997, UCAL_MARCH, 15, 12, 34, 56);
    UDate epoch = makeDate(1970, UCAL_JANUARY, 1);

    cal->setTime(d11,status);

    cal->clear( UCAL_MINUTE );
    logln(UnicodeString("") + cal->getTime(status));
    if (cal->getTime(status)  != d01)
        errln("Fail: clear(MINUTE) broken");

    cal->set( UCAL_SECOND, 0 );
    logln(UnicodeString("") + cal->getTime(status));
    if (cal->getTime(status)  != d00)
        errln("Fail: set(SECOND, 0) broken");

    cal->setTime(d11,status);
    cal->set( UCAL_SECOND, 0 );
    logln(UnicodeString("") + cal->getTime(status));
    if (cal->getTime(status)  != d10)
        errln("Fail: set(SECOND, 0) broken #2");

    cal->clear( UCAL_MINUTE );
    logln(UnicodeString("") + cal->getTime(status));
    if (cal->getTime(status)  != d00)
        errln("Fail: clear(MINUTE) broken #2");

    cal->clear();
    logln(UnicodeString("") + cal->getTime(status));
    if (cal->getTime(status)  != epoch)
        errln(UnicodeString("Fail: clear() broken Want ") + epoch);

    delete cal;
}


int32_t PersianCalendar::yearStart(int32_t year) {
    return handleComputeMonthStart(year,0,FALSE);
}


static void U_CALLCONV initializeSystemDefaultCentury() {
    // initialize systemDefaultCentury and systemDefaultCenturyYear based
    // on the current time.  They'll be set to 80 years before
    // the current time.
    UErrorCode status = U_ZERO_ERROR;
    PersianCalendar calendar(Locale("@calendar=persian"),status);
    if (U_SUCCESS(status))
    {
        calendar.setTime(Calendar::getNow(), status);
        calendar.add(UCAL_YEAR, -80, status);

        gSystemDefaultCenturyStart = calendar.getTime(status);
        gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
    }
    // We have no recourse upon failure unless we want to propagate the failure
    // out.
}


void CalendarRegressionTest::test4096231()
{
    UErrorCode status = U_ZERO_ERROR;
    TimeZone *GMT = TimeZone::createTimeZone("GMT");
    TimeZone *PST = TimeZone::createTimeZone("PST");
    int32_t sec = 0, min = 0, hr = 0, day = 1, month = 10, year = 1997;

    Calendar *cal1 = new GregorianCalendar(*PST,status);
    if (U_FAILURE(status)) {
        dataerrln("Failure new GregorianCalendar: %s", u_errorName(status));
        delete GMT;
        delete PST;
        delete cal1;
        return;
    }
    cal1->setTime(880698639000.0,status);
    // Issue 1: Changing the timezone doesn't change the
    //          represented time.  The old API, pre 1.2.2a requires
    // setTime to be called in order to update the time fields after the time
    // zone has been set.
    int32_t h1,h2;
    logln(UnicodeString("PST 1 is: ") + (h1=cal1->get(UCAL_HOUR_OF_DAY, status)));
    cal1->setTimeZone(*GMT);
    logln(UnicodeString("GMT 2 is: ") + (h2=cal1->get(UCAL_HOUR_OF_DAY, status)));
    if ((*GMT != *PST) && (h1 == h2))
        errln("Fail: Hour same in different zones");

    Calendar *cal2 = new GregorianCalendar(*GMT,status);
    Calendar *cal3 = new GregorianCalendar(*PST,status);
    cal2->set(UCAL_MILLISECOND, 0);
    cal3->set(UCAL_MILLISECOND, 0);

    cal2->set(cal1->get(UCAL_YEAR,status),
             cal1->get(UCAL_MONTH,status),
             cal1->get(UCAL_DATE,status),
             cal1->get(UCAL_HOUR_OF_DAY,status),
             cal1->get(UCAL_MINUTE,status),
             cal1->get(UCAL_SECOND,status));

    double t1,t2,t3,t4;
    logln(UnicodeString("RGMT 1 is: ") + (t1=cal2->getTime(status)));
    cal3->set(year, month, day, hr, min, sec);
    logln(UnicodeString("RPST 1 is: ") + (t2=cal3->getTime(status)));
    cal3->setTimeZone(*GMT);
    logln(UnicodeString("RGMT 2 is: ") + (t3=cal3->getTime(status)));
    cal3->set(cal1->get(UCAL_YEAR,status),
             cal1->get(UCAL_MONTH,status),
             cal1->get(UCAL_DATE,status),
             cal1->get(UCAL_HOUR_OF_DAY,status),
             cal1->get(UCAL_MINUTE,status),
             cal1->get(UCAL_SECOND,status));
    // Issue 2: Calendar continues to use the timezone in its
    //          constructor for set() conversions, regardless
    //          of calls to setTimeZone()
    logln(UnicodeString("RGMT 3 is: ") + (t4=cal3->getTime(status)));
    if (t1 == t2 ||
        t1 != t4 ||
        t2 != t3)
        errln("Fail: Calendar zone behavior faulty");

    delete cal1;
    delete cal2;
    delete cal3;
    delete GMT;
    delete PST;
}


static void sudo_dp_reconnect_init(struct sbus_connection *conn,
                                   int status,
                                   void *pvt)
{
    struct be_conn *be_conn = talloc_get_type(pvt, struct be_conn);
    int ret;

    /* Did we reconnect successfully? */
    if (status == SBUS_RECONNECT_SUCCESS) {
        DEBUG(SSSDBG_TRACE_FUNC, "Reconnected to the Data Provider.\n");

        /* Identify ourselves to the data provider */
        ret = rdp_register_client(be_conn, "SUDO");
        /* all fine */
        if (ret == EOK) {
            handle_requests_after_reconnect(be_conn->rctx);
            return;
        }
    }

    /* Failed to reconnect */
    DEBUG(SSSDBG_FATAL_FAILURE, "Could not reconnect to %s provider.\n",
                                 be_conn->domain->name);
}


void Krecipes::readProperties( const KConfigGroup& group )
{
	// the 'config' object points to the session managed
	// config file.  this function is automatically called whenever
	// the app is being restored.  read in here whatever you wrote
	// in 'saveProperties'

	//QString url = group.readEntry("lastURL");

	//if (!url.isNull())
	//m_view->openURL(KUrl(url));
}


void Krecipes::changeStatusbar( const QString& text )
{
	// display the text on the statusbar
	statusLabel->setText( text );
}


void KrecipesView::setRemoveFromCategoryAction( KAction * action )
{
	selectPanel->setRemoveFromCategoryAction( action );
}


void Krecipes::fileExport()
{
	// this slot is called whenever the File->Export menu is selected,
	m_view->exportRecipe();
}


bool Krecipes::queryClose()
{
	if ( !m_view->inputPanel->everythingSaved() ) {
		switch ( KMessageBox::questionYesNoCancel( this,
		         i18n( "A recipe contains unsaved changes.\n"
		               "Do you want to save the changes before exiting?" ),
		         i18n( "Unsaved Changes" ) ) ) {
		case KMessageBox::Yes:
			return m_view->save();
		case KMessageBox::No:
			return true;
		case KMessageBox::Cancel:
			return false;
		default:
			return true;
		}
	}
	else
		return true;
}


void KrecipesView::moveTipButton( int, int )
{
	contextButton->setGeometry( leftPanel->width() - 42, leftPanel->height() - 42, 32, 32 );
}


void KrecipesView::setupUserPermissions( const QString &host, const QString &client, const QString &dbName, const QString &newUser, const QString &newPass, const QString &adminUser, const QString &adminPass, int port )
{
	QString user = adminUser;
	QString pass = adminPass;
	if ( user.isEmpty() ) {
		pass.clear();

		if ( dbType == "PostgreSQL" )
			user = "postgres";
		else if ( dbType == "MySQL" )
			user = "root";

		kDebug() << "Open db as " << user << ", with no password\n";
	}
	else
		kDebug() << "Open db as:" << user << ",*** with password ****\n";

	RecipeDB *db = RecipeDB::createDatabase( dbType, host, user, pass, dbName, port, dbName );
	if ( db ) {
		db->connect(true,false);//create the database, but no tables (do that when connected as the user)
		if ( db->ok() )
			db->givePermissions( dbName, newUser, newPass, client ); // give permissions to the user
		else
			questionRerunWizard( db->err(), i18n( "Unable to setup database" ) );
	}

	delete db; //it closes the db automatically
}


KrecipesView::KrecipesView( QWidget *parent )
	: QWidget( parent )
{
	new KrecipesAdaptor(this );
	QDBusConnection::sessionBus().registerObject("/Krecipes", this);

#ifndef NDEBUG
	QTime dbg_total_timer; dbg_total_timer.start();
#endif
	// Init the setup wizard if necessary
	kDebug() << "Beginning wizard" ;
	wizard();
	kDebug() << "Wizard finished correctly" ;

	// Show Splash Screen

	KStartupLogo* start_logo = 0L;
	start_logo = new KStartupLogo();
	start_logo -> setHideEnabled( true );
	start_logo->show();
	start_logo->raise();

	// Initialize Database

	// Check if the database type is among those supported
	// and initialize the database in each case
	START_TIMER("Initializing database")
	initDatabase();
	END_TIMER()

	// Design the GUI
	QHBoxLayout *layout = new QHBoxLayout;
	setLayout( layout );
	splitter = new KHBox( this );
	layout->addWidget( splitter );
	// Create Left and Right Panels (splitter)

	leftPanelFrame = new QFrame( splitter );
	leftPanel = new KreMenu;
	QHBoxLayout *leftPanelFrameLayout = new QHBoxLayout;
	leftPanelFrame->setLayout( leftPanelFrameLayout );
	leftPanelFrameLayout->addWidget( leftPanel );
	leftPanelFrameLayout->setMargin( 0 );
	leftPanelFrame->setFrameStyle( QFrame::StyledPanel | QFrame::Raised );
	leftPanelFrame->setFrameRect( QRect( 0, 0, 0, 0 ) );
	rightPanel = new PanelDeco( splitter, "rightPanel", i18n( "Find/Edit Recipes" ), "system-search" );

	// Design Left Panel

	START_TIMER("Setting up buttons")
	// Buttons

	button0 = new KreMenuButton( leftPanel, SelectP );
	button0->setIconSet( KIcon( "system-search" ) );
	buttonsList.append( button0 );

	button1 = new KreMenuButton( leftPanel, ShoppingP );
	button1->setIconSet( KIcon( "view-pim-tasks" ) );
	buttonsList.append( button1 );

	button7 = new KreMenuButton( leftPanel, DietP );
	button7->setIconSet( KIcon( "diet" ) );
	buttonsList.append( button7 );

	button8 = new KreMenuButton( leftPanel, MatcherP );
	button8->setIconSet( KIcon( "view-filter" ) );
	buttonsList.append( button8 );

	// Submenus
	dataMenu = leftPanel->createSubMenu( i18n( "Data..." ), "server-database" );

	recipeButton = new KreMenuButton( leftPanel, RecipeEdit );
	recipeButton->setIconSet( KIcon( "document-save" ) );
	buttonsList.append( recipeButton );
	recipeButton->setEnabled( false );
	recipeButton->hide();

	button2 = new KreMenuButton( leftPanel, IngredientsP, dataMenu );
	button2->setIconSet( KIcon( "ingredients" ) );
	buttonsList.append(button2);

	button3 = new KreMenuButton( leftPanel, PropertiesP, dataMenu );
	button3->setIconSet( KIcon( "properties" ) );
	buttonsList.append( button3 );

	button4 = new KreMenuButton( leftPanel, UnitsP, dataMenu );
	button4->setIconSet( KIcon( "units" ) );
	buttonsList.append( button4 );

	button9 = new KreMenuButton( leftPanel, PrepMethodsP, dataMenu );
	button9->setIconSet( KIcon( "methods" ) );
	buttonsList.append( button9 );

	button5 = new KreMenuButton( leftPanel, CategoriesP, dataMenu );
	button5->setIconSet( KIcon( "folder-yellow" ) );
	buttonsList.append( button5 );

	button6 = new KreMenuButton( leftPanel, AuthorsP, dataMenu );
	button6->setIconSet( KIcon( "authors" ) );
	buttonsList.append( button6 );

	contextButton = new QPushButton( leftPanel );
	contextButton->setObjectName( "contextButton" );
	contextButton->setIcon( KIcon( "system-help" ) );
	contextButton->setGeometry( leftPanel->width() - 42, leftPanel->height() - 42, 32, 32 );

	QPalette p = palette();
	p.setColor(backgroundRole(), contextButton->palette().color(backgroundRole()).light( 140 ) );
	contextButton->setPalette(p);
	contextButton->setFlat( true );
	END_TIMER()

	KConfigGroup config(KGlobal::config(), "Performance" );
	int limit = config.readEntry( "CategoryLimit", -1 );
	database->updateCategoryCache(limit);

	// Right Panel Widgets
	START_TIMER("Creating input dialog")
	inputPanel = new RecipeInputDialog( rightPanel, database );
	rightPanel->addStackWidget( inputPanel );
	END_TIMER()

	START_TIMER("Creating recipe view")
	viewPanel = new RecipeViewDialog( rightPanel, database );
	rightPanel->addStackWidget( viewPanel );
	END_TIMER()

	START_TIMER("Creating recipe selection dialog")
	selectPanel = new SelectRecipeDialog( rightPanel, database );
	rightPanel->addStackWidget( selectPanel );

	END_TIMER()

	START_TIMER("Creating ingredients component")
	ingredientsPanel = new IngredientsDialog( rightPanel, database );
	rightPanel->addStackWidget( ingredientsPanel );
	END_TIMER()

	START_TIMER("Creating properties component")
	propertiesPanel = new PropertiesDialog( rightPanel, database );
	rightPanel->addStackWidget( propertiesPanel );

	END_TIMER()

	START_TIMER("Creating units component")
	unitsPanel = new UnitsDialog( rightPanel, database );
	rightPanel->addStackWidget( unitsPanel );

	END_TIMER()

	START_TIMER("Creating shopping list dialog")
	shoppingListPanel = new ShoppingListDialog( rightPanel, database );
	rightPanel->addStackWidget( shoppingListPanel );

	END_TIMER()

	START_TIMER("Creating diet wizard dialog")
	dietPanel = new DietWizardDialog( rightPanel, database );
	rightPanel->addStackWidget( dietPanel );

	END_TIMER()

	START_TIMER("Creating categories component")
	categoriesPanel = new CategoriesEditorDialog( rightPanel, database );
	rightPanel->addStackWidget( categoriesPanel );

	END_TIMER()

	START_TIMER("Creating authors component")
	authorsPanel = new AuthorsDialog( rightPanel, database );
	rightPanel->addStackWidget( authorsPanel );

	END_TIMER()

	START_TIMER("Creating prep methods component")
	prepMethodsPanel = new PrepMethodsDialog( rightPanel, database );
	rightPanel->addStackWidget( prepMethodsPanel );

	END_TIMER()

	START_TIMER("Creating ingredients matcher dialog")
	ingredientMatcherPanel = new IngredientMatcherDialog( rightPanel, database );
	rightPanel->addStackWidget( ingredientMatcherPanel );

	END_TIMER()

	database->clearCategoryCache();

	// Use to keep track of the panels
	panelMap.insert( inputPanel, RecipeEdit );
	panelMap.insert( viewPanel, RecipeView );
	panelMap.insert( selectPanel, SelectP );
	panelMap.insert( ingredientsPanel, IngredientsP );
	panelMap.insert( propertiesPanel, PropertiesP );
	panelMap.insert( unitsPanel, UnitsP );
	panelMap.insert( shoppingListPanel, ShoppingP );
	panelMap.insert( dietPanel, DietP );
	panelMap.insert( categoriesPanel, CategoriesP );
	panelMap.insert( authorsPanel, AuthorsP );
	panelMap.insert( prepMethodsPanel, PrepMethodsP );
	panelMap.insert( ingredientMatcherPanel, MatcherP );

	m_activePanel = SelectP;
	m_previousActivePanel = SelectP;
	slotSetPanel( SelectP );

	// i18n
	translate();

	// Connect Signals from Left Panel to slotSetPanel()
	connect( leftPanel, SIGNAL( clicked( KrePanel ) ), this, SLOT( slotSetPanel( KrePanel ) ) );

	connect( contextButton, SIGNAL( clicked() ), SLOT( activateContextHelp() ) );

	connect( leftPanel, SIGNAL( resized( int, int ) ), this, SLOT( resizeRightPane( int, int ) ) );

	// Retransmit signal to parent to Enable/Disable the Save Button
	connect ( inputPanel, SIGNAL( enableSaveOption( bool ) ), this, SLOT( enableSaveOptionSlot( bool ) ) );

	// Create a new button when a recipe is unsaved
	connect ( inputPanel, SIGNAL( createButton( QWidget*, const QString & ) ), this, SLOT( addRecipeButton( QWidget*, const QString & ) ) );

	// Connect Signals from selectPanel (SelectRecipeDialog)

	connect ( selectPanel, SIGNAL( recipeSelected( int, int ) ), this, SLOT( actionRecipe( int, int ) ) );
	connect ( selectPanel, SIGNAL( recipesSelected( const QList<int>&, int ) ), this, SLOT( actionRecipes( const QList<int>&, int ) ) );

	// Connect Signals from ingredientMatcherPanel (IngredientMatcherDialog)

	connect ( ingredientMatcherPanel, SIGNAL( recipeSelected( int, int ) ), SLOT( actionRecipe( int, int ) ) );

	// Close a recipe when requested (just switch panels)
	connect( inputPanel, SIGNAL( closeRecipe() ), this, SLOT( closeRecipe() ) );

	// Show a recipe when requested (just switch panels)
	connect( inputPanel, SIGNAL( showRecipe( int ) ), this, SLOT( showRecipe( int ) ) );

	// Close the recipe view when requested (just switch panels)
	connect( viewPanel, SIGNAL( closeRecipeView() ), this, SLOT( closeRecipe() ) );

	// Create a new shopping list when a new diet is generated and accepted
	connect( dietPanel, SIGNAL( dietReady() ), this, SLOT( createShoppingListFromDiet() ) );

	// Place the Tip Button in correct position when the left pane is resized
	connect( leftPanel, SIGNAL( resized( int, int ) ), this, SLOT( moveTipButton( int, int ) ) );

	connect( rightPanel, SIGNAL( panelRaised( QWidget*, QWidget* ) ), SLOT( panelRaised( QWidget*, QWidget* ) ) );

	connect( selectPanel, SIGNAL( recipeSelected(bool) ), SIGNAL( recipeSelected(bool) ) );

	connect( ingredientMatcherPanel, SIGNAL( recipeSelected(bool) ), SIGNAL( recipeSelected(bool) ) );

	// Close Splash Screen
	delete start_logo;

#ifndef NDEBUG
	kDebug()<<"Total time elapsed: "<<dbg_total_timer.elapsed()/1000<<" sec";
#endif
}


bool KrecipesView::save( void )
{
	return inputPanel->save();
}


void Krecipes::setupActions()
{

	printAction =  KStandardAction::print( this, SLOT( filePrint() ), actionCollection() );

	reloadAction = new KAction( this );
	reloadAction->setText( i18nc("@action:inmenu Reload Recipe", "Reloa&d" ) );
	reloadAction->setIcon( KIcon( "view-refresh" ) );
	reloadAction->setShortcut( Qt::Key_F5 );
	actionCollection()->addAction( "reload_action" , reloadAction );
	connect( reloadAction, SIGNAL(triggered(bool)), m_view, SLOT( reloadDisplay() ) );

	editAction = new KAction( this );
	editAction->setText( i18n( "&Edit Recipe" ) );
	editAction->setIcon( KIcon( "document-edit" ) );
	editAction->setShortcut( Qt::CTRL + Qt::Key_E );
	actionCollection()->addAction( "edit_action" , editAction );
	connect( editAction, SIGNAL(triggered(bool)), m_view, SLOT( editRecipe() ) );

	converterAction = new KAction( this );
	converterAction->setText( i18n( "&Measurement Converter" ) );
	converterAction->setShortcut( Qt::CTRL + Qt::Key_M );
	actionCollection()->addAction( "converter_action" , converterAction );
	connect( converterAction, SIGNAL(triggered(bool)), this , SLOT( conversionToolSlot() ) );

	KConfigGroup grp(KGlobal::config(),"Advanced");

	if ( grp.readEntry("UnhideMergeTools",false) ) { //FIXME: Please review it, not used yet

		mergeCategoriesAction = new KAction( this );
		mergeCategoriesAction->setIcon( KIcon( "folder-yellow" ) );
		mergeCategoriesAction->setText( i18n( "&Merge Similar Categories..." ) );
		//mergeCategoriesAction->setShortcut( Qt::CTRL + Qt::Key_M );
		actionCollection()->addAction( "merge_categories_action", mergeCategoriesAction );
		connect( mergeCategoriesAction, SIGNAL(triggered(bool)), this, SLOT( mergeSimilarCategories() ) );

		mergeIngredientsAction = new KAction( this );
		mergeIngredientsAction->setIcon( KIcon( "ingredients" ) );
		mergeIngredientsAction->setText( i18n( "&Merge Similar Ingredients..." ) );
		//ºmergeIngredientsAction->setShortcut( Qt::CTRL + Qt::Key_M );
		actionCollection()->addAction( "merge_ingredients_action", mergeIngredientsAction );
		connect( mergeIngredientsAction, SIGNAL(triggered(bool)), this, SLOT( mergeSimilarIngredients() ) );

	}

	KAction *action = KStandardAction::openNew( this, SLOT( fileNew() ), actionCollection() );
	action->setText( i18n( "&New Recipe" ) );

	saveAction = KStandardAction::save( this, SLOT( fileSave() ), actionCollection() );

	KStandardAction::quit( kapp, SLOT( quit() ), actionCollection() );

	m_statusbarAction = KStandardAction::showStatusbar( this, SLOT( optionsShowStatusbar() ), actionCollection() );

	KStandardAction::keyBindings( this, SLOT( optionsConfigureKeys() ), actionCollection() );
	KStandardAction::configureToolbars( this, SLOT( optionsConfigureToolbars() ), actionCollection() );
	KStandardAction::preferences( this, SLOT( optionsPreferences() ), actionCollection() );

	importAction = new KAction( this );
	importAction->setText( i18n( "Import from File..." ) );
	importAction->setIcon( KIcon( "document-import" ) );
	importAction->setShortcut( Qt::CTRL + Qt::Key_I );
	actionCollection()->addAction( "import_action" , importAction );
	connect( importAction, SIGNAL(triggered(bool)), this, SLOT( import() ) );

	importDBAction= new KAction( this );
	importDBAction->setText( i18n( "Import from Database..." ) );
	importDBAction->setIcon( KIcon( "document-import" ) );
	actionCollection()->addAction( "import_db_action" , importDBAction );
	connect( importDBAction, SIGNAL(triggered(bool)), this, SLOT( kreDBImport() ) );

	exportAction = new KAction( this );
	exportAction->setText( i18n( "Export..." ) );
	exportAction->setShortcut( Qt::CTRL + Qt::Key_P );
	exportAction->setIcon( KIcon( "document-export" ) );
	actionCollection()->addAction( "export_action" , exportAction );
	connect( exportAction, SIGNAL(triggered(bool)), this, SLOT( fileExport() ) );

	copyToClipboardAction = new KAction( this );
	copyToClipboardAction->setText( i18n( "&Copy to Clipboard" ) );
	copyToClipboardAction->setIcon( KIcon( "edit-copy" ) );
	copyToClipboardAction->setShortcut( Qt::CTRL + Qt::Key_C );
	actionCollection()->addAction( "copy_to_clipboard_action" , copyToClipboardAction );
	connect( copyToClipboardAction, SIGNAL(triggered(bool)), this, SLOT( fileToClipboard()  ) );

        pageSetupAction = new KAction( this );
        pageSetupAction->setText( i18n( "Page Setup..." ) );
	pageSetupAction->setIcon( KIcon( "configure" ) );
        actionCollection()->addAction( "page_setup_action" , pageSetupAction );
        connect( pageSetupAction, SIGNAL(triggered(bool)), this, SLOT( pageSetupSlot() ) );

        printSetupAction = new KAction( this );
        printSetupAction->setText( i18n( "Print Setup..." ) );
	printSetupAction->setIcon( KIcon( "configure" ) );
        actionCollection()->addAction( "print_setup_action" , printSetupAction );
        connect( printSetupAction, SIGNAL(triggered(bool)), this, SLOT( printSetupSlot() ) );

        backupAction = new KAction( this );
        backupAction->setText( i18n( "Backup..." ) );
	backupAction->setIcon( KIcon( "document-save-all" ) );
        actionCollection()->addAction( "backup_action" , backupAction );
        connect( backupAction, SIGNAL(triggered(bool)), this, SLOT( backupSlot() ) );

        restoreAction = new KAction( this );
        restoreAction->setText( i18n( "Restore..." ) );
	restoreAction->setIcon( KIcon( "document-revert" ) );
        actionCollection()->addAction( "restore_action" , restoreAction );
        connect( restoreAction, SIGNAL(triggered(bool)), this, SLOT( restoreSlot() ) );

	addToShoppingListAction = new KAction( this );
	addToShoppingListAction->setText( i18n( "Add to Shopping List" ) );
	addToShoppingListAction->setIcon( KIcon( "view-pim-tasks" ) );
	addToShoppingListAction->setShortcut( Qt::CTRL + Qt::Key_S );
	actionCollection()->addAction( "add_to_shopping_list_action", addToShoppingListAction );
	connect( addToShoppingListAction, SIGNAL(triggered(bool)), m_view, SLOT( addToShoppingList() ) );

	removeFromCategoryAction = new KAction( this );
	removeFromCategoryAction->setText( i18n( "Remove From Category" ) );
	removeFromCategoryAction->setIcon( KIcon( "edit-delete-shred" ) );
	removeFromCategoryAction->setShortcut( Qt::ALT + Qt::Key_Delete );
	actionCollection()->addAction( "remove_from_category_action", removeFromCategoryAction );
	connect( removeFromCategoryAction, SIGNAL(triggered(bool)), m_view, SLOT( removeFromCategory() ) );

	deleteAction = new KAction( this );
	deleteAction->setText( i18n( "Delete" ) );
	deleteAction->setIcon( KIcon( "edit-delete" ) );
	deleteAction->setShortcut( Qt::Key_Delete );
	actionCollection()->addAction( "delete_action", deleteAction );
	connect( deleteAction, SIGNAL(triggered(bool)), m_view, SLOT( deleteCurrentElements() ) );

	showRecipeAction = new KAction( this );
	showRecipeAction->setText( i18n( "Show Recipe(s)" ) );
	showRecipeAction->setIcon( KIcon( "system-search" ) );
	showRecipeAction->setShortcut( Qt::CTRL + Qt::Key_L );
	actionCollection()->addAction( "show_recipe_action", showRecipeAction );
	connect( showRecipeAction, SIGNAL(triggered(bool)), m_view, SLOT( showCurrentRecipes() ) );

	categorizeAction = new KAction( this );
	categorizeAction->setText( i18n( "Categorize" ) );
	categorizeAction->setIcon( KIcon( "folder-yellow" ) );
	categorizeAction->setShortcut( Qt::CTRL + Qt::Key_T );
	actionCollection()->addAction( "categorize_action", categorizeAction );
	connect( categorizeAction, SIGNAL(triggered(bool)), m_view, SLOT( categorizeCurrentRecipe() ) );

	expandAllAction = new KAction( this );
	expandAllAction->setText( i18n( "Expand All" ) );
	expandAllAction->setShortcut( Qt::CTRL + Qt::Key_Plus );
	actionCollection()->addAction( "expand_all_action", expandAllAction );
	connect( expandAllAction, SIGNAL(triggered(bool)), m_view, SLOT( expandAll() ) );

	collapseAllAction = new KAction( this );
	collapseAllAction->setText( i18n ( "Collapse All") );
	collapseAllAction->setShortcut( Qt::CTRL + Qt::Key_Minus );
	actionCollection()->addAction( "collapse_all_action", collapseAllAction );
	connect( collapseAllAction, SIGNAL(triggered(bool)), m_view, SLOT( collapseAll() ) );

	//Note that createAction is intended to create anything except a new recipe.
	createAction = new KAction( this );
	createAction->setText( i18nc("@action:button Create new data item", "Create New" ) );
	createAction->setIcon( KIcon( "document-new" ) );
	createAction->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_N );
	actionCollection()->addAction( "create_new_action", createAction );
	connect( createAction, SIGNAL(triggered(bool)), m_view, SLOT( createNewElement() ) );

	renameAction = new KAction( this );
	renameAction->setText( i18n( "Rename" ) );
	renameAction->setIcon( KIcon( "edit-rename" ) );
	renameAction->setShortcut( Qt::CTRL + Qt::Key_R );
	actionCollection()->addAction( "rename_action", renameAction );
	connect( renameAction, SIGNAL(triggered(bool)), m_view, SLOT( renameCurrentElement() ) );

	//Not using an standard action because I don't want it in toolbar nor in menus.
	cutAction = new KAction( this );
	cutAction->setText( i18n( "Cut" ) );
	cutAction->setIcon( KIcon( "edit-cut" ) );
	cutAction->setShortcut( Qt::CTRL + Qt::Key_X );
	actionCollection()->addAction( "cut_action", cutAction );
	connect( cutAction, SIGNAL(triggered(bool)), m_view, SLOT( cut() ) );

	//Not using an standard action because I don't want it in toolbar nor in menus.
	pasteAction = new KAction( this );
	pasteAction->setText( i18n( "Paste" ) );
	pasteAction->setIcon( KIcon( "edit-paste" ) );
	pasteAction->setShortcut( Qt::CTRL + Qt::Key_V );
	actionCollection()->addAction( "paste_action", pasteAction );
	connect( pasteAction, SIGNAL(triggered(bool)), m_view, SLOT( paste() ) );

	pasteAsSubcategoryAction = new KAction( this );
	pasteAsSubcategoryAction->setText( i18n( "Paste as Subcategory" ) );
	pasteAsSubcategoryAction->setIcon( KIcon( "edit-paste" ) );
	pasteAsSubcategoryAction->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_V );
	actionCollection()->addAction( "paste_as_subcategory_action", pasteAsSubcategoryAction );
	connect( pasteAsSubcategoryAction, SIGNAL(triggered(bool)), m_view, SLOT( pasteAsSubcategory() ) );

	// Add actions to the context menus in the "select recipe" dialog
	m_view->addSelectRecipeAction( showRecipeAction );
	m_view->addSelectRecipeAction( editAction );
	m_view->addSelectRecipeAction( exportAction );
	m_view->addSelectRecipeAction( addToShoppingListAction );
	m_view->addSelectRecipeAction( copyToClipboardAction );
	m_view->setCategorizeAction( categorizeAction );
	m_view->setRemoveFromCategoryAction( removeFromCategoryAction );
	m_view->addSelectRecipeAction( deleteAction );
	m_view->addCategoryAction( expandAllAction );
	m_view->addCategoryAction( collapseAllAction );
	m_view->addCategoryAction( exportAction );
	// Add actions to the context menus in the "advanced search" dialog
	m_view->addFindRecipeAction( showRecipeAction );
	m_view->addFindRecipeAction( editAction );
	m_view->addFindRecipeAction( exportAction );
	m_view->addFindRecipeAction( copyToClipboardAction );
	m_view->addFindRecipeAction( deleteAction );
	// Add actions to the context menus in the "ingredient matcher" dialog
	m_view->addIngredientMatcherAction( showRecipeAction );
	m_view->addIngredientMatcherAction( editAction );
	m_view->addIngredientMatcherAction( exportAction );
	// Add actions to the dialogs in the "data" submenu
	m_view->addElementAction( createAction );
	m_view->addElementAction( renameAction );
	m_view->addCategoriesPanelAction( cutAction );
	m_view->setCategoryPasteAction( pasteAction );
	m_view->setPasteAsSubcategoryAction( pasteAsSubcategoryAction );
	m_view->addElementAction( deleteAction );

	updateActions( SelectP, true );
	updateActions( RecipeView, false );
	exportAction->setEnabled( true );

	createGUI();
}


ffi_closure_alloc (size_t size, void **code)
{
  if (!code)
    return NULL;

  return *code = malloc (size);
}


hash_compare_symbol (const symbol *m1, const symbol *m2)
{
  /* Since tags are unique, we can compare the pointers themselves.  */
  return UNIQSTR_EQ (m1->tag, m2->tag);
}


symbol_translation (const symbol *sym)
{
  if (sym->content->class == token_sym && !sym->is_alias)
    {
      /* A token whose translation has already been set? */
      if (token_translations[sym->content->code]
          != undeftoken->content->number)
        complain_code_redeclared
          (sym->content->code,
           symbols[token_translations[sym->content->code]], sym);
      else
        token_translations[sym->content->code]
          = sym->content->number;
    }
}


hash_semantic_type (const semantic_type *m, size_t tablesize)
{
  /* Since names are unique, we can hash the pointer itself.  */
  return ((uintptr_t) m->tag) % tablesize;
}


grammar_current_rule_predicate_append (const char *pred, location loc)
{
  if (current_rule->action_props.code)
    grammar_midrule_action ();
  code_props_rule_action_init (&current_rule->action_props, pred, loc,
                               current_rule,
                               NULL, NULL,
                               /* is_predicate */ true);
}


complain_semantic_type_redeclared (semantic_type *s, const char *what, location first,
                                   location second)
{
  locations_sort (&first, &second);
  complain (&second, complaint, _("%s redeclaration for <%s>"), what, s->tag);
  subcomplain (&first, complaint, _("previous declaration"));
}


grammar_start_symbol_set (symbol *sym, location loc)
{
  if (start_flag)
    complain (&loc, complaint, _("multiple %s declarations"), "%start");
  else
    {
      start_flag = true;
      startsymbol = sym;
      startsymbol_loc = loc;
    }
}


symbol_from_uniqstr (const uniqstr key, location loc)
{
  symbol probe;

  probe.tag = key;
  symbol *res = hash_lookup (symbol_table, &probe);

  if (!res)
    {
      /* First insertion in the hash. */
      aver (!symbols_sorted);
      res = symbol_new (key, loc);
      hash_xinsert (symbol_table, res);
    }
  return res;
}


semantic_type_new (uniqstr tag, const location *loc)
{
  semantic_type *res = xmalloc (sizeof *res);

  uniqstr_assert (tag);
  res->tag = tag;
  res->location = loc ? *loc : empty_loc;
  res->status = undeclared;
  for (int i = 0; i < CODE_PROPS_SIZE; ++i)
    code_props_none_init (&res->props[i]);

  return res;
}


nbd_unlocked_flush (struct nbd_handle *h, uint32_t flags)
{
  int64_t cookie;
  nbd_completion_callback c = NBD_NULL_COMPLETION;

  cookie = nbd_unlocked_aio_flush (h, &c, flags);
  if (cookie == -1)
    return -1;

  return wait_for_command (h, cookie);
}


nbd_unlocked_trim (struct nbd_handle *h,
                   uint64_t count, uint64_t offset, uint32_t flags)
{
  int64_t cookie;
  nbd_completion_callback c = NBD_NULL_COMPLETION;

  cookie = nbd_unlocked_aio_trim (h, count, offset, &c, flags);
  if (cookie == -1)
    return -1;

  return wait_for_command (h, cookie);
}


nbd_unlocked_aio_zero (struct nbd_handle *h,
                       uint64_t count, uint64_t offset,
                       nbd_completion_callback *completion,
                       uint32_t flags)
{
  struct command_cb cb = { .completion = *completion };

  if (h->strict & LIBNBD_STRICT_COMMANDS) {
    if (nbd_unlocked_can_zero (h) != 1) {
      set_error (EINVAL, "server does not support zero operations");
      return -1;
    }
    if (nbd_unlocked_is_read_only (h) == 1) {
      set_error (EPERM, "server does not support write operations");
      return -1;
    }

    if ((flags & LIBNBD_CMD_FLAG_FUA) != 0 &&
        nbd_unlocked_can_fua (h) != 1) {
      set_error (EINVAL, "server does not support the FUA flag");
      return -1;
    }

    if ((flags & LIBNBD_CMD_FLAG_FAST_ZERO) != 0 &&
        nbd_unlocked_can_fast_zero (h) != 1) {
      set_error (EINVAL, "server does not support the fast zero flag");
      return -1;
    }
  }

  SET_CALLBACK_TO_NULL (*completion);
  return nbd_internal_command_common (h, flags, NBD_CMD_WRITE_ZEROES, offset,
                                      count, ENOSPC, NULL, &cb);
}


check (int experr, const char *prefix)
{
  const char *msg = nbd_get_error ();
  int errnum = nbd_get_errno ();

  fprintf (stderr, "error: \"%s\"\n", msg);
  fprintf (stderr, "errno: %d (%s)\n", errnum, strerror (errnum));
  if (strncmp (msg, prefix, strlen (prefix)) != 0) {
    fprintf (stderr, "%s: test failed: missing context prefix: %s\n",
             progname, msg);
    exit (EXIT_FAILURE);
  }
  if (errnum != experr) {
    fprintf (stderr, "%s: test failed: "
             "expected errno = %d (%s), but got %d\n",
             progname, experr, strerror (experr), errnum);
    exit (EXIT_FAILURE);
  }
}


nbd_internal_command_common (struct nbd_handle *h,
                             uint16_t flags, uint16_t type,
                             uint64_t offset, uint64_t count, int count_err,
                             void *data, struct command_cb *cb)
{
  struct command *cmd;

  if (h->disconnect_request) {
      set_error (EINVAL, "cannot request more commands after NBD_CMD_DISC");
      goto err;
  }
  if (h->in_flight == INT_MAX) {
      set_error (ENOMEM, "too many commands already in flight");
      goto err;
  }

  if (count_err) {
    if ((h->strict & LIBNBD_STRICT_ZERO_SIZE) && count == 0) {
      set_error (EINVAL, "count cannot be 0");
      goto err;
    }

    if ((h->strict & LIBNBD_STRICT_BOUNDS) &&
        (offset > h->exportsize || count > h->exportsize - offset)) {
      set_error (count_err, "request out of bounds");
      goto err;
    }

    if (h->block_minimum && (h->strict & LIBNBD_STRICT_ALIGN) &&
        (offset | count) & (h->block_minimum - 1)) {
      set_error (EINVAL, "request is unaligned");
      goto err;
    }
  }

  switch (type) {
    /* Commands which send or receive data are limited to MAX_REQUEST_SIZE. */
  case NBD_CMD_READ:
  case NBD_CMD_WRITE:
    if (count > MAX_REQUEST_SIZE) {
      set_error (ERANGE, "request too large: maximum request size is %d",
                 MAX_REQUEST_SIZE);
      goto err;
    }
    break;

    /* Other commands are currently limited by the 32 bit field in the
     * command structure on the wire, but in future we hope to support
     * 64 bit values here with a change to the NBD protocol which is
     * being discussed upstream.
     */
  default:
    if (count > UINT32_MAX) {
      set_error (ERANGE, "request too large: maximum request size is %" PRIu32,
                 UINT32_MAX);
      goto err;
    }
    break;
  }

  cmd = calloc (1, sizeof *cmd);
  if (cmd == NULL) {
    set_error (errno, "calloc");
    goto err;
  }
  cmd->flags = flags;
  cmd->type = type;
  cmd->cookie = h->unique++;
  cmd->offset = offset;
  cmd->count = count;
  cmd->data = data;
  if (cb)
    cmd->cb = *cb;

  /* For NBD_CMD_READ, cmd->data was pre-zeroed in the prologue
   * created by the generator.  Thus, if a (non-compliant) server with
   * structured replies fails to send back sufficient data to cover
   * the whole buffer, we still behave as if it had sent zeroes for
   * those portions, rather than leaking any uninitialized data, and
   * without having to complicate our state machine to track which
   * portions of the read buffer were actually populated.
   */

  /* Add the command to the end of the queue. Kick the state machine
   * if there is no other command being processed, otherwise, it will
   * be handled automatically on a future cycle around to READY.
   * Beyond this point, we have to return a cookie to the user, since
   * we are queuing the command, even if kicking the state machine
   * detects a failure.  Not reporting a state machine failure here is
   * okay - any caller of an async command will be calling more API to
   * await results, and will eventually learn that the machine has
   * moved on to DEAD at that time.
   */
  h->in_flight++;
  if (h->cmds_to_issue != NULL) {
    assert (nbd_internal_is_state_processing (get_next_state (h)));
    h->cmds_to_issue_tail = h->cmds_to_issue_tail->next = cmd;
  }
  else {
    assert (h->cmds_to_issue_tail == NULL);
    h->cmds_to_issue = h->cmds_to_issue_tail = cmd;
    if (nbd_internal_is_state_ready (get_next_state (h)) &&
        nbd_internal_run (h, cmd_issue) == -1)
      debug (h, "command queued, ignoring state machine failure");
  }

  return cmd->cookie;

 err:
  /* Since we did not queue the command, we must free the callbacks. */
  if (cb) {
    if (type == NBD_CMD_BLOCK_STATUS)
      FREE_CALLBACK (cb->fn.extent);
    if (type == NBD_CMD_READ)
      FREE_CALLBACK (cb->fn.chunk);
    FREE_CALLBACK (cb->completion);
  }
  return -1;
}


nbd_unlocked_set_private_data (struct nbd_handle *h, uintptr_t data)
{
  uintptr_t old_data;

  /* atomic_exchange? XXX */
  old_data = h->private_data;
  h->private_data = data;
  return old_data;
}


nbd_unlocked_set_strict_mode (struct nbd_handle *h, uint32_t flags)
{
  h->strict = flags;
  return 0;
}


main (int argc, char *argv[])
{
  struct nbd_handle *nbd;
  /* We will connect to a custom nbdkit sh plugin which always fails
   * on reads (with a precise spelling required for older nbdkit), and
   * which delays responding to writes until a witness file no longer
   * exists.
   */
  const char *cmd[] = { "nbdkit", "-s", "-v", "--exit-with-parent", "sh",
                        script, NULL };
  uint32_t strict;

  progname = argv[0];

  if (atexit (cleanup) != 0) {
    perror ("atexit");
    exit (EXIT_FAILURE);
  }
  if ((script_fd = mkstemp (script)) == -1) {
    perror ("mkstemp");
    exit (EXIT_FAILURE);
  }
  if ((witness_fd = mkstemp (witness)) == -1) {
    perror ("mkstemp");
    exit (EXIT_FAILURE);
  }

  if (dprintf (script_fd, "case $1 in\n"
               "  get_size) echo 128m || exit 1 ;;\n"
               "  thread_model) echo serialize_all_requests; exit 0 ;;\n"
               "  # XXX require 512-alignment, with new enough nbdkit\n"
               "  pread) printf 'ENOMEM ' >&2; exit 1 ;;\n"
               "  can_write) exit 0 ;;\n"
               "  pwrite)\n"
               "    while test -e %s; do sleep 1; done\n"
               "    exit 0;;\n"
               "  *) exit 2 ;;\n"
               "esac\n", witness) < 0) {
    perror ("dprintf");
    exit (EXIT_FAILURE);
  }
  if (fchmod (script_fd, 0700) == -1) {
    perror ("fchmod");
    exit (EXIT_FAILURE);
  }
  if (close (script_fd) == -1) {  /* Unlinked later during atexit */
    perror ("close");
    exit (EXIT_FAILURE);
  }
  script_fd = -2;

  nbd = nbd_create ();
  if (nbd == NULL) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }

  /* Attempt to set an enum to garbage. */
  if (nbd_set_tls (nbd, LIBNBD_TLS_REQUIRE + 1) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_set_tls did not reject invalid enum\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  if (nbd_get_tls (nbd) != LIBNBD_TLS_DISABLE) {
    fprintf (stderr, "%s: test failed: "
             "nbd_get_tls not left at default value\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }

  /* Attempt to set a bitmask with an unknown bit. */
  if (nbd_set_handshake_flags (nbd, LIBNBD_HANDSHAKE_FLAG_MASK + 1) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_set_handshake_flags did not reject invalid bitmask\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  if (nbd_get_handshake_flags (nbd) != LIBNBD_HANDSHAKE_FLAG_MASK) {
    fprintf (stderr, "%s: test failed: "
             "nbd_get_handshake_flags not left at default value\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }

  /* Issue a connected command when not connected. pread_initialize defaults
   * to set.
   */
  if (nbd_get_pread_initialize (nbd) != 1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_get_pread_initialize gave unexpected result\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  buf[0] = '1';
  if (nbd_pread (nbd, buf, 512, 0, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_pread did not fail on non-connected handle\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (ENOTCONN, "nbd_pread: ");
  if (buf[0] != '\0') {
    fprintf (stderr, "%s: test failed: "
             "nbd_pread did not sanitize buffer on error\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }

  /* Request a name that is too long. */
  memset (buf, 'a', 4999);
  buf[4999] = '\0';
  if (nbd_set_export_name (nbd, buf) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_set_export_name did not reject large name\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (ENAMETOOLONG, "nbd_set_export_name: ");

  /* Poll while there is no fd. */
  if (nbd_aio_get_fd (nbd) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_get_fd did not fail prior to connection\n",
             argv[0]);
  }
  check (EINVAL, "nbd_aio_get_fd: ");
  if (nbd_poll (nbd, 1000) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_poll did not fail prior to connection\n",
             argv[0]);
  }
  check (EINVAL, "nbd_poll: ");

  /* Connect to a working server, then try to connect again. */
  if (nbd_connect_command (nbd, (char **) cmd) == -1) {
    fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_connect_command (nbd, (char **) cmd) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_connect_command did not reject repeat attempt\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_connect_command: ");

  /* nbd_opt_abort can only be called during negotiation. */
  if (nbd_opt_abort (nbd) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_opt_abort did not reject attempt in wrong state\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_opt_abort: ");

  /* Try to notify that writes are ready when we aren't blocked on POLLOUT */
  if (nbd_aio_notify_write (nbd) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_notify_write in wrong state did not fail\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_notify_write: ");

  /* Check for status of a bogus cookie */
  if (nbd_aio_command_completed (nbd, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_command_completed on bogus cookie did not fail\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_command_completed: ");

  /* Read from an invalid offset, client-side. When pread_initialize is off,
   * libnbd should not have touched our buffer.
   */
  if (nbd_set_pread_initialize (nbd, false) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  buf[0] = '1';
  strict = nbd_get_strict_mode (nbd) | LIBNBD_STRICT_BOUNDS;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_pread (nbd, buf, 512, -1, NBD_NULL_COMPLETION, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_pread did not fail with bogus offset\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_pread: ");
  if (buf[0] != '1') {
    fprintf (stderr, "%s: test failed: "
             "nbd_pread incorrectly sanitized buffer on client-side error\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }

  /* We guarantee callbacks will be freed even on all error paths. */
  if (nbd_aio_pread_structured (nbd, buf, 512, -1,
                                (nbd_chunk_callback) { .free = check_chunk, },
                                (nbd_completion_callback) {
                                  .free = check_completion, },
                                0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_pread_structured did not fail with bogus offset\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_pread_structured: ");
  if (!chunk_clean || !completion_clean) {
    fprintf (stderr, "%s: test failed: "
             "callbacks not freed on nbd_aio_pread_structured failure\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }

  /* Read from an invalid offset, server-side */
  strict &= ~LIBNBD_STRICT_BOUNDS;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  check_server_fail (nbd,
                     nbd_aio_pread (nbd, buf, 512, -1, NBD_NULL_COMPLETION, 0),
                     "nbd_aio_pread with bogus offset", EINVAL);

  /* Use in-range unknown command flags, client-side */
  strict = nbd_get_strict_mode (nbd) | LIBNBD_STRICT_FLAGS;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_pread (nbd, buf, 512, 0, NBD_NULL_COMPLETION,
                     LIBNBD_CMD_FLAG_MASK + 1) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_pread did not fail with bogus flags\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_pread: ");
  /* Use in-range unknown command flags, server-side */
  strict &= ~LIBNBD_STRICT_FLAGS;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  check_server_fail (nbd, nbd_aio_pread (nbd, buf, 512, 0, NBD_NULL_COMPLETION,
                                         LIBNBD_CMD_FLAG_MASK + 1),
                     "nbd_aio_pread with bogus flag", EINVAL);
  /* Use out-of-range unknown command flags, client-side */
  if (nbd_aio_pread (nbd, buf, 512, 0, NBD_NULL_COMPLETION, 0x10000) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_pread did not fail with bogus flags\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_pread: ");

  /* Check that oversized requests are rejected */
  if (nbd_pread (nbd, buf, MAXSIZE, 0, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_pread did not fail with oversize request\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (ERANGE, "nbd_pread: ");
  if (nbd_aio_pwrite (nbd, buf, MAXSIZE, 0,
                      NBD_NULL_COMPLETION, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_aio_pwrite did not fail with oversize request\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (ERANGE, "nbd_aio_pwrite: ");

  /* Use unadvertised command, client-side */
  strict = nbd_get_strict_mode (nbd) | LIBNBD_STRICT_COMMANDS;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_trim (nbd, 512, 0, NBD_NULL_COMPLETION, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "unpermitted nbd_aio_trim did not fail\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_trim: ");
  /* Use unadvertised command, server-side */
  strict &= ~LIBNBD_STRICT_COMMANDS;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  check_server_fail (nbd, nbd_aio_trim (nbd, 512, 0, NBD_NULL_COMPLETION, 0),
                     "unadvertised nbd_aio_trim", EINVAL);

  /* Send a read that the nbdkit sh plugin will fail. */
  if (nbd_pread (nbd, buf, 512, 0, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_pread did not report server failure\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (ENOMEM, "nbd_pread: ");

  /* Send a zero-sized read, client-side */
  strict = nbd_get_strict_mode (nbd) | LIBNBD_STRICT_ZERO_SIZE;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_pread (nbd, buf, 0, 0, NBD_NULL_COMPLETION, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "zero-size nbd_aio_pread did not fail\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_pread: ");
  /* Send a zero-sized read, server-side */
  strict &= ~LIBNBD_STRICT_ZERO_SIZE;
  if (nbd_set_strict_mode (nbd, strict) == -1) {
    fprintf (stderr, "%s\n", nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  check_server_fail (nbd,
                     nbd_aio_pread (nbd, buf, 0, 0, NBD_NULL_COMPLETION, 0),
                     "zero-size nbd_aio_pread", EINVAL);

  /* XXX nbdkit does not yet have a way to test STRICT_ALIGN */
  if (nbd_get_block_size (nbd, LIBNBD_SIZE_MINIMUM) > 1) {
    /* Send an unaligned read, client-side */
    strict = nbd_get_strict_mode (nbd) | LIBNBD_STRICT_ALIGN;
    if (nbd_set_strict_mode (nbd, strict) == -1) {
      fprintf (stderr, "%s\n", nbd_get_error ());
      exit (EXIT_FAILURE);
    }
    if (nbd_aio_pread (nbd, buf, 1, 1, NBD_NULL_COMPLETION, 0) != -1) {
      fprintf (stderr, "%s: test failed: "
               "unaligned nbd_aio_pread did not fail\n",
               argv[0]);
      exit (EXIT_FAILURE);
    }
    check (EINVAL, "nbd_aio_pread: ");
    /* Send an unaligned read, server-side */
    strict &= ~LIBNBD_STRICT_ALIGN;
    if (nbd_set_strict_mode (nbd, strict) == -1) {
      fprintf (stderr, "%s\n", nbd_get_error ());
      exit (EXIT_FAILURE);
    }
    check_server_fail (nbd,
                       nbd_aio_pread (nbd, buf, 1, 1, NBD_NULL_COMPLETION, 0),
                       "unaligned nbd_aio_pread", EINVAL);
  }
  else
    fprintf (stderr, "skipping alignment tests, nbdkit too old\n");

  /* Queue up two write commands so large that we block on POLLIN (the
   * first might not block when under load, such as valgrind, but the
   * second definitely will, since the nbdkit sh plugin reads only one
   * command at a time and stalls on the first), then queue multiple
   * disconnects.
   */
  if (nbd_aio_pwrite (nbd, buf, 2 * 1024 * 1024, 0,
                      NBD_NULL_COMPLETION, 0) == -1) {
    fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_pwrite (nbd, buf, 2 * 1024 * 1024, 0,
                      NBD_NULL_COMPLETION, 0) == -1) {
    fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if ((nbd_aio_get_direction (nbd) & LIBNBD_AIO_DIRECTION_WRITE) == 0) {
    fprintf (stderr, "%s: test failed: "
             "expect to be blocked on write\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_disconnect (nbd, 0) == -1) {
    fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
    exit (EXIT_FAILURE);
  }
  if (nbd_aio_disconnect (nbd, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "no diagnosis that nbd_aio_disconnect prevents new commands\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_aio_disconnect: ");

  /* Unblock the nbdkit sh plugin */
  if (close (witness_fd) == -1) {
    perror ("close");
    exit (EXIT_FAILURE);
  }
  witness_fd = -1;
  if (unlink (witness) == -1) {
    perror ("unlink");
    exit (EXIT_FAILURE);
  }

  /* Flush the queue (whether this one fails is a race with how fast
   * the server shuts down, so don't enforce status), then try to send
   * another command while CLOSED/DEAD
   */
  if (nbd_shutdown (nbd, 0) == -1) {
    fprintf (stderr, "%s: ignoring %s\n", argv[0], nbd_get_error ());
  }
  else
    fprintf (stderr, "%s: shutdown completed successfully\n", argv[0]);
  if (nbd_pread (nbd, buf, 512, 0, 0) != -1) {
    fprintf (stderr, "%s: test failed: "
             "nbd_pread did not fail on non-connected handle\n",
             argv[0]);
    exit (EXIT_FAILURE);
  }
  check (EINVAL, "nbd_pread: ");

  nbd_close (nbd);
  exit (EXIT_SUCCESS);
}


duint32 dwgReader18::checksum(duint32 seed, duint8* data, duint64 sz){
    duint64 size = sz;
    duint32 sum1 = seed & 0xffff;
    duint32 sum2 = seed >> 0x10;
    while (size != 0) {
//        duint32 chunkSize = min(0x15b0, size);
        duint64 chunkSize = 0x15b0 < size? 0x15b0:size;
        size -= chunkSize;
        for (duint64 i = 0; i < chunkSize; i++) {
            sum1 += *data++;
            sum2 += sum1;
        }
        sum1 %= 0xFFF1;
        sum2 %= 0xFFF1;
    }
    return (sum2 << 0x10) | (sum1 & 0xffff);
}


bool dwgCompressor::buffersGood(void)
{
    return compressedGood && decompGood;
}


bool dwgReader21::readFileHeader() {

    DRW_DBG("\n\ndwgReader21::parsing file header\n");
    if (! fileBuf->setPosition(0x80))
        return false;
    duint8 fileHdrRaw[0x2FD];//0x3D8
    fileBuf->getBytes(fileHdrRaw, 0x2FD);
    duint8 fileHdrdRS[0x2CD];
    dwgRSCodec::decode239I(fileHdrRaw, fileHdrdRS, 3);

#ifdef DRW_DBG_DUMP
    DRW_DBG("\ndwgReader21::parsed Reed Solomon decode:\n");
    int j = 0;
    for (int i=0, j=0; i<0x2CD; i++){
        DRW_DBGH( (unsigned char)fileHdrdRS[i]);
        if (j== 15){ j=0; DRW_DBG("\n");
        } else{ j++; DRW_DBG(", "); }
    } DRW_DBG("\n");
#endif

    dwgBuffer fileHdrBuf(fileHdrdRS, 0x2CD, &decoder);
    DRW_DBG("\nCRC 64b= "); DRW_DBGH(fileHdrBuf.getRawLong64());
    DRW_DBG("\nunknown key 64b= "); DRW_DBGH(fileHdrBuf.getRawLong64());
    DRW_DBG("\ncomp data CRC 64b= "); DRW_DBGH(fileHdrBuf.getRawLong64());
    dint32 fileHdrCompLength = fileHdrBuf.getRawLong32();
    DRW_DBG("\ncompr len 4bytes= "); DRW_DBG(fileHdrCompLength);
    dint32 fileHdrCompLength2 = fileHdrBuf.getRawLong32();
    DRW_DBG("\nlength2 4bytes= "); DRW_DBG(fileHdrCompLength2);

    int fileHdrDataLength = 0x110;
    std::vector<duint8> fileHdrData;
    if (fileHdrCompLength < 0) {
        fileHdrDataLength = fileHdrCompLength * -1;
        fileHdrData.resize(fileHdrDataLength);
        fileHdrBuf.getBytes(&fileHdrData.front(), fileHdrDataLength);
    }
    else {
        DRW_DBG("\ndwgReader21:: file header are compressed:\n");
        std::vector<duint8> compByteStr(fileHdrCompLength);
        fileHdrBuf.getBytes(compByteStr.data(), fileHdrCompLength);
        fileHdrData.resize(fileHdrDataLength);
        if (!dwgCompressor::decompress21(compByteStr.data(), &fileHdrData.front(),
                                         fileHdrCompLength, fileHdrDataLength)) {
            return false;
        }
    }

#ifdef DRW_DBG_DUMP
    DRW_DBG("\ndwgReader21::parsed file header:\n");
    for (int i=0, j=0; i<fileHdrDataLength; i++){
        DRW_DBGH( (unsigned char)fileHdrData[i]);
        if (j== 15){ j=0; DRW_DBG("\n");
        } else{ j++; DRW_DBG(", "); }
    } DRW_DBG("\n");
#endif

    dwgBuffer fileHdrDataBuf(&fileHdrData.front(), fileHdrDataLength, &decoder);
    DRW_DBG("\nHeader size = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nFile size = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nPagesMapCrcCompressed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    duint64 PagesMapCorrectionFactor = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nPagesMapCorrectionFactor = "); DRW_DBG(PagesMapCorrectionFactor);
    DRW_DBG("\nPagesMapCrcSeed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nPages map2offset = "); DRW_DBGH(fileHdrDataBuf.getRawLong64()); //relative to data page map 1, add 0x480 to get stream position
    DRW_DBG("\nPages map2Id = "); DRW_DBG(fileHdrDataBuf.getRawLong64());
    duint64 PagesMapOffset = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nPagesMapOffset = "); DRW_DBGH(PagesMapOffset); //relative to data page map 1, add 0x480 to get stream position
    DRW_DBG("\nPagesMapId = "); DRW_DBG(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nHeader2offset = "); DRW_DBGH(fileHdrDataBuf.getRawLong64()); //relative to data page map 1, add 0x480 to get stream position
    duint64 PagesMapSizeCompressed = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nPagesMapSizeCompressed = "); DRW_DBG(PagesMapSizeCompressed);
    duint64 PagesMapSizeUncompressed = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nPagesMapSizeUncompressed = "); DRW_DBG(PagesMapSizeUncompressed);
    DRW_DBG("\nPagesAmount = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    duint64 PagesMaxId = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nPagesMaxId = "); DRW_DBG(PagesMaxId);
    DRW_DBG("\nUnknown (normally 0x20) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nUnknown (normally 0x40) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nPagesMapCrcUncompressed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nUnknown (normally 0xf800) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nUnknown (normally 4) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nUnknown (normally 1) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nSectionsAmount (number of sections + 1) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nSectionsMapCrcUncompressed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    duint64 SectionsMapSizeCompressed = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nSectionsMapSizeCompressed = "); DRW_DBGH(SectionsMapSizeCompressed);
    DRW_DBG("\nSectionsMap2Id = "); DRW_DBG(fileHdrDataBuf.getRawLong64());
    duint64 SectionsMapId = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nSectionsMapId = "); DRW_DBG(SectionsMapId);
    duint64 SectionsMapSizeUncompressed = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nSectionsMapSizeUncompressed = "); DRW_DBGH(SectionsMapSizeUncompressed);
    DRW_DBG("\nSectionsMapCrcCompressed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    duint64 SectionsMapCorrectionFactor = fileHdrDataBuf.getRawLong64();
    DRW_DBG("\nSectionsMapCorrectionFactor = "); DRW_DBG(SectionsMapCorrectionFactor);
    DRW_DBG("\nSectionsMapCrcSeed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nStreamVersion (normally 0x60100) = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nCrcSeed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nCrcSeedEncoded = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nRandomSeed = "); DRW_DBGH(fileHdrDataBuf.getRawLong64());
    DRW_DBG("\nHeader CRC64 = "); DRW_DBGH(fileHdrDataBuf.getRawLong64()); DRW_DBG("\n");

    DRW_DBG("\ndwgReader21::parse page map:\n");
    std::vector<duint8> PagesMapData(PagesMapSizeUncompressed);

    if (!parseSysPage(PagesMapSizeCompressed, PagesMapSizeUncompressed,
                      PagesMapCorrectionFactor, 0x480+PagesMapOffset,
                      &PagesMapData.front())) {
        return false;
    }

    duint64 address = 0x480;
    duint64 i = 0;
    dwgBuffer PagesMapBuf(&PagesMapData.front(), PagesMapSizeUncompressed, &decoder);
    //stores temporarily info of all pages:
    std::unordered_map<duint64, dwgPageInfo >sectionPageMapTmp;

//    dwgPageInfo *m_pages= new dwgPageInfo[PagesMaxId+1];
    while (PagesMapSizeUncompressed > i ) {
        duint64 size = PagesMapBuf.getRawLong64();
        dint64 id = PagesMapBuf.getRawLong64();
        duint64 ind = id > 0 ? id : -id;
        i += 16;

        DRW_DBG("Page gap= "); DRW_DBG(id); DRW_DBG(" Page num= "); DRW_DBG(ind); DRW_DBG(" size= "); DRW_DBGH(size);
        DRW_DBG(" address= "); DRW_DBGH(address);  DRW_DBG("\n");
        sectionPageMapTmp[ind] = dwgPageInfo(ind, address,size);
        address += size;
        //TODO num can be negative indicating gap
//        seek += offset;
    }

    DRW_DBG("\n*** dwgReader21: Processing Section Map ***\n");
    std::vector<duint8> SectionsMapData( SectionsMapSizeUncompressed);
    dwgPageInfo sectionMap = sectionPageMapTmp[SectionsMapId];
    if (!parseSysPage( SectionsMapSizeCompressed, SectionsMapSizeUncompressed,
                       SectionsMapCorrectionFactor, sectionMap.address, &SectionsMapData.front()) ) {
        return false;
    }

//reads sections:
    //Note: compressed value are not stored in file then, commpresed field are use to store
    // encoding value
    dwgBuffer SectionsMapBuf( &SectionsMapData.front(), SectionsMapSizeUncompressed, &decoder);
    duint8 nextId = 1;
    while(SectionsMapBuf.getPosition() < SectionsMapBuf.size()){
        dwgSectionInfo secInfo;
        secInfo.size = SectionsMapBuf.getRawLong64();
        DRW_DBG("\nSize of section (data size)= "); DRW_DBGH(secInfo.size);
        secInfo.maxSize = SectionsMapBuf.getRawLong64();
        DRW_DBG("\nMax Decompressed Size= "); DRW_DBGH(secInfo.maxSize);
        secInfo.encrypted = SectionsMapBuf.getRawLong64();
        //encrypted (doc: 0 no, 1 yes, 2 unkn) on read: objects 0 and encrypted yes
        DRW_DBG("\nencription= "); DRW_DBGH(secInfo.encrypted);
        DRW_DBG("\nHashCode = "); DRW_DBGH(SectionsMapBuf.getRawLong64());
        duint64 SectionNameLength = SectionsMapBuf.getRawLong64();
        DRW_DBG("\nSectionNameLength = "); DRW_DBG(SectionNameLength);
        DRW_DBG("\nUnknown = "); DRW_DBGH(SectionsMapBuf.getRawLong64());
        secInfo.compressed = SectionsMapBuf.getRawLong64();
        DRW_DBG("\nEncoding (compressed) = "); DRW_DBGH(secInfo.compressed);
        secInfo.pageCount = SectionsMapBuf.getRawLong64();
        DRW_DBG("\nPage count= "); DRW_DBGH(secInfo.pageCount);
        secInfo.name = SectionsMapBuf.getUCSStr(SectionNameLength);
        DRW_DBG("\nSection name = "); DRW_DBG(secInfo.name); DRW_DBG("\n");

        for (unsigned int i=0; i< secInfo.pageCount; i++){
            duint64 po = SectionsMapBuf.getRawLong64();
            duint32 ds = SectionsMapBuf.getRawLong64();
            duint32 pn = SectionsMapBuf.getRawLong64();
            DRW_DBG("  pag Id = "); DRW_DBGH(pn); DRW_DBG(" data size = "); DRW_DBGH(ds);
            dwgPageInfo pi = sectionPageMapTmp[pn]; //get a copy
            pi.dataSize = ds;
            pi.startOffset = po;
            pi.uSize = SectionsMapBuf.getRawLong64();
            pi.cSize = SectionsMapBuf.getRawLong64();
            secInfo.pages[pn]= pi;//complete copy in secInfo
            DRW_DBG("\n    Page number= "); DRW_DBGH(secInfo.pages[pn].Id);
            DRW_DBG("\n    address in file= "); DRW_DBGH(secInfo.pages[pn].address);
            DRW_DBG("\n    size in file= "); DRW_DBGH(secInfo.pages[pn].size);
            DRW_DBG("\n    Data size= "); DRW_DBGH(secInfo.pages[pn].dataSize);
            DRW_DBG("\n    Start offset= "); DRW_DBGH(secInfo.pages[pn].startOffset);
            DRW_DBG("\n    Page uncompressed size = "); DRW_DBGH(secInfo.pages[pn].uSize);
            DRW_DBG("\n    Page compressed size = "); DRW_DBGH(secInfo.pages[pn].cSize);

            DRW_DBG("\n    Page checksum = "); DRW_DBGH(SectionsMapBuf.getRawLong64());
            DRW_DBG("\n    Page CRC = "); DRW_DBGH(SectionsMapBuf.getRawLong64()); DRW_DBG("\n");
        }

        if (!secInfo.name.empty()) {
            secInfo.Id = nextId++;
            DRW_DBG("Saved section Name= "); DRW_DBG( secInfo.name.c_str() ); DRW_DBG("\n");
            sections[secEnum::getEnum(secInfo.name)] = secInfo;
        }
    }

    if (! fileBuf->isGood())
        return false;

    DRW_DBG("\ndwgReader21::readFileHeader END\n");
    return true;
}


bool dwgReader21::readDwgClasses(){
    DRW_DBG("\ndwgReader21::readDwgClasses");
    dwgSectionInfo si = sections[secEnum::CLASSES];
    if (si.Id<0)//not found, ends
        return false;

    DRW_DBG("\nprepare section of size "); DRW_DBG(si.size);DRW_DBG("\n");
    std::vector<duint8> tmpClassesData( si.size);
    bool ret = dwgReader21::parseDataPage(si, tmpClassesData.data());
    if (!ret) {
        return ret;
    }

    dwgBuffer buff(tmpClassesData.data(), si.size, &decoder);
    DRW_DBG("classes section sentinel= ");
    checkSentinel(&buff, secEnum::CLASSES, true);

    duint32 size = buff.getRawLong32();
    DRW_DBG("\ndata size in bytes "); DRW_DBG(size);

    duint32 bitSize = buff.getRawLong32();
    DRW_DBG("\ntotal size in bits "); DRW_DBG(bitSize);

    duint32 maxClassNum = buff.getBitShort();
    DRW_DBG("\nMaximum class number "); DRW_DBG(maxClassNum);
    DRW_DBG("\nRc 1 "); DRW_DBG(buff.getRawChar8());
    DRW_DBG("\nRc 2 "); DRW_DBG(buff.getRawChar8());
    DRW_DBG("\nBit "); DRW_DBG(buff.getBit());

    /*******************************/
    //prepare string stream
    dwgBuffer strBuff(tmpClassesData.data(), si.size, &decoder);
    duint32 strStartPos = bitSize + 159;//size in bits + 20 bytes (sn+size) - 1 bit (endbit)
    DRW_DBG("\nstrStartPos: "); DRW_DBG(strStartPos);
    strBuff.setPosition(strStartPos >> 3);
    strBuff.setBitPos(strStartPos & 7);
    DRW_DBG("\nclasses strings buff.getPosition: "); DRW_DBG(strBuff.getPosition());
    DRW_DBG("\nclasses strings buff.getBitPos: "); DRW_DBG(strBuff.getBitPos());
    DRW_DBG("\nendBit "); DRW_DBG(strBuff.getBit());
    strStartPos -= 16;//decrement 16 bits
    DRW_DBG("\nstrStartPos: "); DRW_DBG(strStartPos);
    strBuff.setPosition(strStartPos >> 3);
    strBuff.setBitPos(strStartPos & 7);
    DRW_DBG("\nclasses strings buff.getPosition: "); DRW_DBG(strBuff.getPosition());
    DRW_DBG("\nclasses strings buff.getBitPos: "); DRW_DBG(strBuff.getBitPos());
    duint32 strDataSize = strBuff.getRawShort16();
    DRW_DBG("\nstrDataSize: "); DRW_DBG(strDataSize);
    if (strDataSize & 0x8000) {
        strStartPos -= 16;//decrement 16 bits
        strDataSize &= 0x7FFF; //strip 0x8000;
        strBuff.setPosition(strStartPos >> 3);
        strBuff.setBitPos(strStartPos & 7);
        duint32 hiSize = strBuff.getRawShort16();
        strDataSize |= (hiSize << 15);
    }
    strStartPos -= strDataSize;
    DRW_DBG("\nstrStartPos: "); DRW_DBG(strStartPos);
    strBuff.setPosition(strStartPos >> 3);
    strBuff.setBitPos(strStartPos & 7);
    DRW_DBG("\nclasses strings buff.getPosition: "); DRW_DBG(strBuff.getPosition());
    DRW_DBG("\nclasses strings buff.getBitPos: "); DRW_DBG(strBuff.getBitPos());

    /*******************************/

    duint32 endDataPos = maxClassNum-499;
    DRW_DBG("\nbuff.getPosition: "); DRW_DBG(buff.getPosition());
    for (duint32 i= 0; i<endDataPos;i++) {
        DRW_Class *cl = new DRW_Class();
        cl->parseDwg(version, &buff, &strBuff);
        classesmap[cl->classNum] = cl;
        DRW_DBG("\nbuff.getPosition: "); DRW_DBG(buff.getPosition());
    }
    DRW_DBG("\nend classes data buff.getPosition: "); DRW_DBG(buff.getPosition());
    DRW_DBG("\nend classes data buff.getBitPos: "); DRW_DBG(buff.getBitPos());

    buff.setPosition(size+20);//sizeVal+sn+32bSize
    DRW_DBG("\nCRC: "); DRW_DBGH(buff.getRawShort16());
    DRW_DBG("\nclasses section end sentinel= ");
    checkSentinel(&buff, secEnum::CLASSES, true);
    return buff.isGood();
}