int imap_open_connection(struct ImapAccountData *adata)
{
  if (mutt_socket_open(adata->conn) < 0)
    return -1;

  adata->state = IMAP_CONNECTED;

  if (imap_cmd_step(adata) != IMAP_RES_OK)
  {
    imap_close_connection(adata);
    return -1;
  }

  if (mutt_istr_startswith(adata->buf, "* OK"))
  {
    if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
    {
      goto bail;
    }
#ifdef USE_SSL
    /* Attempt STARTTLS if available and desired. */
    if ((adata->conn->ssf == 0) && (C_SslForceTls || (adata->capabilities & IMAP_CAP_STARTTLS)))
    {
      enum QuadOption ans;

      if (C_SslForceTls)
        ans = MUTT_YES;
      else if ((ans = query_quadoption(C_SslStarttls,
                                       _("Secure connection with TLS?"))) == MUTT_ABORT)
      {
        goto bail;
      }
      if (ans == MUTT_YES)
      {
        enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
        // Clear any data after the STARTTLS acknowledgement
        mutt_socket_empty(adata->conn);

        if (rc == IMAP_EXEC_FATAL)
          goto bail;
        if (rc != IMAP_EXEC_ERROR)
        {
          if (mutt_ssl_starttls(adata->conn))
          {
            mutt_error(_("Could not negotiate TLS connection"));
            goto bail;
          }
          else
          {
            /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
            if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS))
              goto bail;
          }
        }
      }
    }

    if (C_SslForceTls && (adata->conn->ssf == 0))
    {
      mutt_error(_("Encrypted connection unavailable"));
      goto bail;
    }
#endif
  }
  else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
  {
#ifdef USE_SSL
    /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
     * MITM attack.  The only way to stop "STARTTLS" MITM attacks is via
     * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
     * STARTTLS capability.  So consult $ssl_force_tls, not $ssl_starttls, to
     * decide whether to abort. Note that if using $tunnel and
     * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
    if ((adata->conn->ssf == 0) && C_SslForceTls)
    {
      mutt_error(_("Encrypted connection unavailable"));
      goto bail;
    }
#endif

    adata->state = IMAP_AUTHENTICATED;
    if (check_capabilities(adata) != 0)
      goto bail;
    FREE(&adata->capstr);
  }
  else
  {
    imap_error("imap_open_connection()", adata->buf);
    goto bail;
  }

  return 0;

bail:
  imap_close_connection(adata);
  FREE(&adata->capstr);
  return -1;
}


int imap_access(const char *path)
{
  if (imap_path_status(path, false) >= 0)
    return 0;
  return -1;
}

imap: close connection on all failures

Thanks to Gabriel Salles-Loustau for spotting the problem.

Co-authored-by: Kevin McCarthy <kevin@8t8.us>

static const struct Command imap_commands[] = {
  // clang-format off
  { "subscribe-to",     parse_subscribe_to,     0 },
  { "unsubscribe-from", parse_unsubscribe_from, 0 },
  // clang-format on
};

struct MxOps MxImapOps = {
  .type            = MUTT_IMAP,
  .name             = "imap",
  .is_local         = false,
  .ac_find          = imap_ac_find,
  .ac_add           = imap_ac_add,
  .mbox_open        = imap_mbox_open,
  .mbox_open_append = imap_mbox_open_append,
  .mbox_check       = imap_mbox_check,
  .mbox_check_stats = imap_mbox_check_stats,
  .mbox_sync        = NULL, /* imap syncing is handled by imap_sync_mailbox */
  .mbox_close       = imap_mbox_close,
  .msg_open         = imap_msg_open,
  .msg_open_new     = imap_msg_open_new,
  .msg_commit       = imap_msg_commit,
  .msg_close        = imap_msg_close,
  .msg_padding_size = NULL,
  .msg_save_hcache  = imap_msg_save_hcache,
  .tags_edit        = imap_tags_edit,
  .tags_commit      = imap_tags_commit,
  .path_probe       = imap_path_probe,
  .path_canon       = imap_path_canon,
  .path_pretty      = imap_path_pretty,
  .path_parent      = imap_path_parent,
  .path_is_empty    = imap_path_is_empty,
};


static int complete_hosts(char *buf, size_t buflen)
{
  // struct Connection *conn = NULL;
  int rc = -1;
  size_t matchlen;

  matchlen = mutt_str_len(buf);
  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
  neomutt_mailboxlist_get_all(&ml, NeoMutt, MUTT_MAILBOX_ANY);
  struct MailboxNode *np = NULL;
  STAILQ_FOREACH(np, &ml, entries)
  {
    if (!mutt_str_startswith(mailbox_path(np->mailbox), buf))
      continue;

    if (rc)
    {
      mutt_str_copy(buf, mailbox_path(np->mailbox), buflen);
      rc = 0;
    }
    else
      longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen, buflen);
  }
  neomutt_mailboxlist_clear(&ml);

#if 0
  TAILQ_FOREACH(conn, mutt_socket_head(), entries)
  {
    struct Url url = { 0 };
    char urlstr[1024];

    if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
      continue;

    mutt_account_tourl(&conn->account, &url);
    /* FIXME: how to handle multiple users on the same host? */
    url.user = NULL;
    url.path = NULL;
    url_tostring(&url, urlstr, sizeof(urlstr), 0);
    if (mutt_strn_equal(buf, urlstr, matchlen))
    {
      if (rc)
      {
        mutt_str_copy(buf, urlstr, buflen);
        rc = 0;
      }
      else
        longest_common_prefix(buf, urlstr, matchlen, buflen);
    }
  }
#endif

  return rc;
}

struct stat

static const struct Command imap_commands[] = {
  // clang-format off
  { "subscribe-to",     parse_subscribe_to,     0 },
  { "unsubscribe-from", parse_unsubscribe_from, 0 },
  // clang-format on
};

struct MxOps MxImapOps = {
  .type            = MUTT_IMAP,
  .name             = "imap",
  .is_local         = false,
  .ac_find          = imap_ac_find,
  .ac_add           = imap_ac_add,
  .mbox_open        = imap_mbox_open,
  .mbox_open_append = imap_mbox_open_append,
  .mbox_check       = imap_mbox_check,
  .mbox_check_stats = imap_mbox_check_stats,
  .mbox_sync        = NULL, /* imap syncing is handled by imap_sync_mailbox */
  .mbox_close       = imap_mbox_close,
  .msg_open         = imap_msg_open,
  .msg_open_new     = imap_msg_open_new,
  .msg_commit       = imap_msg_commit,
  .msg_close        = imap_msg_close,
  .msg_padding_size = NULL,
  .msg_save_hcache  = imap_msg_save_hcache,
  .tags_edit        = imap_tags_edit,
  .tags_commit      = imap_tags_commit,
  .path_probe       = imap_path_probe,
  .path_canon       = imap_path_canon,
  .path_pretty      = imap_path_pretty,
  .path_parent      = imap_path_parent,
  .path_is_empty    = imap_path_is_empty,
};


static void imap_mbox_select(struct Mailbox *m)
{
  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);
  if (!adata || !mdata)
    return;

  const char *condstore = NULL;
#ifdef USE_HCACHE
  if ((adata->capabilities & IMAP_CAP_CONDSTORE) && C_ImapCondstore)
    condstore = " (CONDSTORE)";
  else
#endif
    condstore = "";

  char buf[PATH_MAX];
  snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
           mdata->munge_name, condstore);

  adata->state = IMAP_SELECTED;

  imap_cmd_start(adata, buf);
}


int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
{
  char buf[2048], mbox[1024];

  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
  snprintf(buf, sizeof(buf), "CREATE %s", mbox);

  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
  {
    mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
    return -1;
  }

  return 0;
}


int imap_delete_mailbox(struct Mailbox *m, char *path)
{
  char buf[PATH_MAX + 7];
  char mbox[PATH_MAX];
  struct Url *url = url_parse(path);

  struct ImapAccountData *adata = imap_adata_get(m);
  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
  url_free(&url);
  snprintf(buf, sizeof(buf), "DELETE %s", mbox);
  if (imap_exec(m->account->adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
    return -1;

  return 0;
}


static int imap_path_pretty(char *buf, size_t buflen, const char *folder)
{
  if (!folder)
    return -1;

  imap_pretty_mailbox(buf, buflen, folder);
  return 0;
}


bool imap_has_flag(struct ListHead *flag_list, const char *flag)
{
  if (STAILQ_EMPTY(flag_list))
    return false;

  const size_t flaglen = mutt_str_len(flag);
  struct ListNode *np = NULL;
  STAILQ_FOREACH(np, flag_list, entries)
  {
    const size_t nplen = strlen(np->data);
    if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
        mutt_istrn_equal(np->data, flag, nplen))
    {
      return true;
    }

    if (mutt_str_equal(np->data, "\\*"))
      return true;
  }

  return false;
}


static void imap_logout(struct ImapAccountData *adata)
{
  /* we set status here to let imap_handle_untagged know we _expect_ to
   * receive a bye response (so it doesn't freak out and close the conn) */
  if (adata->state == IMAP_DISCONNECTED)
  {
    return;
  }

  adata->status = IMAP_BYE;
  imap_cmd_start(adata, "LOGOUT");
  if ((C_ImapPollTimeout <= 0) || (mutt_socket_poll(adata->conn, C_ImapPollTimeout) != 0))
  {
    while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
      ; // do nothing
  }
  mutt_socket_close(adata->conn);
  adata->state = IMAP_DISCONNECTED;
}


void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
{
  struct ImapMboxData *mdata = imap_mdata_get(m);
  struct ImapEmailData *edata = imap_edata_get(e);

  if (!mdata || !edata)
    return;

  imap_msn_remove(&mdata->msn, edata->msn - 1);
  edata->msn = 0;
}


static int imap_mbox_close(struct Mailbox *m)
{
  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);

  /* Check to see if the mailbox is actually open */
  if (!adata || !mdata)
    return 0;

  /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
   * just for the connection.
   *
   * So when these are equal, it means we are actually closing the
   * mailbox and should clean up adata.  Otherwise, we don't want to
   * touch adata - it's still being used.  */
  if (m == adata->mailbox)
  {
    if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
    {
      /* mx_mbox_close won't sync if there are no deleted messages
       * and the mailbox is unchanged, so we may have to close here */
      if (m->msg_deleted == 0)
      {
        adata->closing = true;
        imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
      }
      adata->state = IMAP_AUTHENTICATED;
    }

    mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
               (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
    adata->mailbox = adata->prev_mailbox;
    imap_mbox_select(adata->prev_mailbox);
    imap_mdata_cache_reset(m->mdata);
  }

  return 0;
}


static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
{
  size_t pos = start;

  while ((pos < dlen) && dest[pos] && (dest[pos] == src[pos]))
    pos++;
  dest[pos] = '\0';

  return pos;
}

static const struct Command imap_commands[] = {
  // clang-format off
  { "subscribe-to",     parse_subscribe_to,     0 },
  { "unsubscribe-from", parse_unsubscribe_from, 0 },
  // clang-format on
};


void imap_expunge_mailbox(struct Mailbox *m)
{
  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);
  if (!adata || !mdata)
    return;

  struct Email *e = NULL;

#ifdef USE_HCACHE
  imap_hcache_open(adata, mdata);
#endif

  for (int i = 0; i < m->msg_count; i++)
  {
    e = m->emails[i];
    if (!e)
      break;

    if (e->index == INT_MAX)
    {
      mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);

      e->deleted = true;

      imap_cache_del(m, e);
#ifdef USE_HCACHE
      imap_hcache_del(mdata, imap_edata_get(e)->uid);
#endif

      mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);

      imap_edata_free((void **) &e->edata);
    }
    else
    {
      /* NeoMutt has several places where it turns off e->active as a
       * hack.  For example to avoid FLAG updates, or to exclude from
       * imap_exec_msgset.
       *
       * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
       * flag becomes set (e.g. a flag update to a modified header),
       * this function will be called by imap_cmd_finish().
       *
       * The ctx_update_tables() will free and remove these "inactive" headers,
       * despite that an EXPUNGE was not received for them.
       * This would result in memory leaks and segfaults due to dangling
       * pointers in the msn_index and uid_hash.
       *
       * So this is another hack to work around the hacks.  We don't want to
       * remove the messages, so make sure active is on.  */
      e->active = true;
    }
  }

#ifdef USE_HCACHE
  imap_hcache_close(mdata);
#endif

  mailbox_changed(m, NT_MAILBOX_UPDATE);
  mailbox_changed(m, NT_MAILBOX_RESORT);
}


int imap_fast_trash(struct Mailbox *m, char *dest)
{
  char prompt[1024];
  int rc = -1;
  bool triedcreate = false;
  enum QuadOption err_continue = MUTT_NO;

  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapAccountData *dest_adata = NULL;
  struct ImapMboxData *dest_mdata = NULL;

  if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
    return -1;

  struct Buffer sync_cmd = mutt_buffer_make(0);

  /* check that the save-to folder is in the same account */
  if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
  {
    mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
    goto out;
  }

  for (int i = 0; i < m->msg_count; i++)
  {
    struct Email *e = m->emails[i];
    if (!e)
      break;
    if (e->active && e->changed && e->deleted && !e->purge)
    {
      rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
      if (rc < 0)
      {
        mutt_debug(LL_DEBUG1, "could not sync\n");
        goto out;
      }
    }
  }

  /* loop in case of TRYCREATE */
  do
  {
    rc = imap_exec_msgset(m, "UID COPY", dest_mdata->munge_name, MUTT_TRASH, false, false);
    if (rc == 0)
    {
      mutt_debug(LL_DEBUG1, "No messages to trash\n");
      rc = -1;
      goto out;
    }
    else if (rc < 0)
    {
      mutt_debug(LL_DEBUG1, "could not queue copy\n");
      goto out;
    }
    else if (m->verbose)
    {
      mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
                   rc, dest_mdata->name);
    }

    /* let's get it on */
    rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
    if (rc == IMAP_EXEC_ERROR)
    {
      if (triedcreate)
      {
        mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
        break;
      }
      /* bail out if command failed for reasons other than nonexistent target */
      if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
        break;
      mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
      snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
      if (C_Confirmcreate && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
      {
        mutt_clear_error();
        goto out;
      }
      if (imap_create_mailbox(adata, dest_mdata->name) < 0)
        break;
      triedcreate = true;
    }
  } while (rc == IMAP_EXEC_ERROR);

  if (rc != IMAP_EXEC_SUCCESS)
  {
    imap_error("imap_fast_trash", adata->buf);
    goto out;
  }

  rc = IMAP_EXEC_SUCCESS;

out:
  mutt_buffer_dealloc(&sync_cmd);
  imap_mdata_free((void *) &dest_mdata);

  return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
}


static int sync_helper(struct Mailbox *m, AclFlags right, int flag, const char *name)
{
  int count = 0;
  int rc;
  char buf[1024];

  if (!m)
    return -1;

  if ((m->rights & right) == 0)
    return 0;

  if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
    return 0;

  snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, false);
  if (rc < 0)
    return rc;
  count += rc;

  buf[0] = '-';
  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, true);
  if (rc < 0)
    return rc;
  count += rc;

  return count;
}


int imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
{
  if (!m)
    return -1;

  struct Email **emails = NULL;
  int oldsort;
  int rc;
  int check;

  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);

  if (adata->state < IMAP_SELECTED)
  {
    mutt_debug(LL_DEBUG2, "no mailbox selected\n");
    return -1;
  }

  /* This function is only called when the calling code expects the context
   * to be changed. */
  imap_allow_reopen(m);

  check = imap_check_mailbox(m, false);
  if (check < 0)
    return check;

  /* if we are expunging anyway, we can do deleted messages very quickly... */
  if (expunge && (m->rights & MUTT_ACL_DELETE))
  {
    rc = imap_exec_msgset(m, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
                          MUTT_DELETED, true, false);
    if (rc < 0)
    {
      mutt_error(_("Expunge failed"));
      return rc;
    }

    if (rc > 0)
    {
      /* mark these messages as unchanged so second pass ignores them. Done
       * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
      for (int i = 0; i < m->msg_count; i++)
      {
        struct Email *e = m->emails[i];
        if (!e)
          break;
        if (e->deleted && e->changed)
          e->active = false;
      }
      if (m->verbose)
      {
        mutt_message(ngettext("Marking %d message deleted...",
                              "Marking %d messages deleted...", rc),
                     rc);
      }
    }
  }

#ifdef USE_HCACHE
  imap_hcache_open(adata, mdata);
#endif

  /* save messages with real (non-flag) changes */
  for (int i = 0; i < m->msg_count; i++)
  {
    struct Email *e = m->emails[i];
    if (!e)
      break;

    if (e->deleted)
    {
      imap_cache_del(m, e);
#ifdef USE_HCACHE
      imap_hcache_del(mdata, imap_edata_get(e)->uid);
#endif
    }

    if (e->active && e->changed)
    {
#ifdef USE_HCACHE
      imap_hcache_put(mdata, e);
#endif
      /* if the message has been rethreaded or attachments have been deleted
       * we delete the message and reupload it.
       * This works better if we're expunging, of course. */
      /* TODO: why the e->env check? */
      if ((e->env && e->env->changed) || e->attach_del)
      {
        /* L10N: The plural is chosen by the last %d, i.e. the total number */
        if (m->verbose)
        {
          mutt_message(ngettext("Saving changed message... [%d/%d]",
                                "Saving changed messages... [%d/%d]", m->msg_count),
                       i + 1, m->msg_count);
        }
        bool save_append = m->append;
        m->append = true;
        mutt_save_message_ctx(e, true, false, false, m);
        m->append = save_append;
        /* TODO: why the check for e->env?  Is this possible? */
        if (e->env)
          e->env->changed = 0;
      }
    }
  }

#ifdef USE_HCACHE
  imap_hcache_close(mdata);
#endif

  /* presort here to avoid doing 10 resorts in imap_exec_msgset */
  oldsort = C_Sort;
  if (C_Sort != SORT_ORDER)
  {
    emails = m->emails;
    m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
    memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));

    C_Sort = SORT_ORDER;
    qsort(m->emails, m->msg_count, sizeof(struct Email *), mutt_get_sort_func(SORT_ORDER));
  }

  rc = sync_helper(m, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
  if (rc >= 0)
    rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
  if (rc >= 0)
    rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_OLD, "Old");
  if (rc >= 0)
    rc |= sync_helper(m, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
  if (rc >= 0)
    rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");

  if (oldsort != C_Sort)
  {
    C_Sort = oldsort;
    FREE(&m->emails);
    m->emails = emails;
  }

  /* Flush the queued flags if any were changed in sync_helper. */
  if (rc > 0)
    if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
      rc = -1;

  if (rc < 0)
  {
    if (close)
    {
      if (mutt_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
      {
        adata->state = IMAP_AUTHENTICATED;
        return 0;
      }
    }
    else
      mutt_error(_("Error saving flags"));
    return -1;
  }

  /* Update local record of server state to reflect the synchronization just
   * completed.  imap_read_headers always overwrites hcache-origin flags, so
   * there is no need to mutate the hcache after flag-only changes. */
  for (int i = 0; i < m->msg_count; i++)
  {
    struct Email *e = m->emails[i];
    if (!e)
      break;
    struct ImapEmailData *edata = imap_edata_get(e);
    edata->deleted = e->deleted;
    edata->flagged = e->flagged;
    edata->old = e->old;
    edata->read = e->read;
    edata->replied = e->replied;
    e->changed = false;
  }
  m->changed = false;

  /* We must send an EXPUNGE command if we're not closing. */
  if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
  {
    if (m->verbose)
      mutt_message(_("Expunging messages from server..."));
    /* Set expunge bit so we don't get spurious reopened messages */
    mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
    if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
    {
      mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
      imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
      return -1;
    }
    mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
  }

  if (expunge && close)
  {
    adata->closing = true;
    imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
    adata->state = IMAP_AUTHENTICATED;
  }

  if (C_MessageCacheClean)
    imap_cache_clean(m);

  return check;
}


int imap_path_status(const char *path, bool queue)
{
  struct Mailbox *m = mx_mbox_find2(path);

  const bool is_temp = !m;
  if (is_temp)
  {
    m = mx_path_resolve(path);
    if (!mx_mbox_ac_link(m))
    {
      mailbox_free(&m);
      return 0;
    }
  }

  int rc = imap_mailbox_status(m, queue);

  if (is_temp)
  {
    mx_ac_remove(m);
  }

  return rc;
}


static int imap_mbox_check_stats(struct Mailbox *m, int flags)
{
  return imap_mailbox_status(m, true);
}


static int imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
{
  if (!m->account)
    return -1;

  /* in APPEND mode, we appear to hijack an existing IMAP connection -
   * ctx is brand new and mostly empty */
  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);

  int rc = imap_mailbox_status(m, false);
  if (rc >= 0)
    return 0;
  if (rc == -1)
    return -1;

  char buf[PATH_MAX + 64];
  snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
  if (C_Confirmcreate && (mutt_yesorno(buf, MUTT_YES) != MUTT_YES))
    return -1;

  if (imap_create_mailbox(adata, mdata->name) < 0)
    return -1;

  return 0;
}


static int make_msg_set(struct Mailbox *m, struct Buffer *buf, int flag,
                        bool changed, bool invert, int *pos)
{
  int count = 0;             /* number of messages in message set */
  unsigned int setstart = 0; /* start of current message range */
  int n;
  bool started = false;

  struct ImapAccountData *adata = imap_adata_get(m);
  if (!adata || (adata->mailbox != m))
    return -1;

  for (n = *pos; (n < m->msg_count) && (mutt_buffer_len(buf) < IMAP_MAX_CMDLEN); n++)
  {
    struct Email *e = m->emails[n];
    if (!e)
      break;
    bool match = false; /* whether current message matches flag condition */
    /* don't include pending expunged messages.
     *
     * TODO: can we unset active in cmd_parse_expunge() and
     * cmd_parse_vanished() instead of checking for index != INT_MAX. */
    if (e->active && (e->index != INT_MAX))
    {
      switch (flag)
      {
        case MUTT_DELETED:
          if (e->deleted != imap_edata_get(e)->deleted)
            match = invert ^ e->deleted;
          break;
        case MUTT_FLAG:
          if (e->flagged != imap_edata_get(e)->flagged)
            match = invert ^ e->flagged;
          break;
        case MUTT_OLD:
          if (e->old != imap_edata_get(e)->old)
            match = invert ^ e->old;
          break;
        case MUTT_READ:
          if (e->read != imap_edata_get(e)->read)
            match = invert ^ e->read;
          break;
        case MUTT_REPLIED:
          if (e->replied != imap_edata_get(e)->replied)
            match = invert ^ e->replied;
          break;
        case MUTT_TAG:
          if (e->tagged)
            match = true;
          break;
        case MUTT_TRASH:
          if (e->deleted && !e->purge)
            match = true;
          break;
      }
    }

    if (match && (!changed || e->changed))
    {
      count++;
      if (setstart == 0)
      {
        setstart = imap_edata_get(e)->uid;
        if (started)
        {
          mutt_buffer_add_printf(buf, ",%u", imap_edata_get(e)->uid);
        }
        else
        {
          mutt_buffer_add_printf(buf, "%u", imap_edata_get(e)->uid);
          started = true;
        }
      }
      /* tie up if the last message also matches */
      else if (n == (m->msg_count - 1))
        mutt_buffer_add_printf(buf, ":%u", imap_edata_get(e)->uid);
    }
    /* End current set if message doesn't match or we've reached the end
     * of the mailbox via inactive messages following the last match. */
    else if (setstart && (e->active || (n == adata->mailbox->msg_count - 1)))
    {
      if (imap_edata_get(m->emails[n - 1])->uid > setstart)
        mutt_buffer_add_printf(buf, ":%u", imap_edata_get(m->emails[n - 1])->uid);
      setstart = 0;
    }
  }

  *pos = n;

  return count;
}


static OPJ_BOOL opj_j2k_calculate_tp(opj_j2k_t *p_j2k,
                                     opj_cp_t *cp,
                                     OPJ_UINT32 * p_nb_tiles,
                                     opj_image_t *image,
                                     opj_event_mgr_t * p_manager
                                    )
{
    OPJ_UINT32 pino, tileno;
    OPJ_UINT32 l_nb_tiles;
    opj_tcp_t *tcp;

    /* preconditions */
    assert(p_nb_tiles != 00);
    assert(cp != 00);
    assert(image != 00);
    assert(p_j2k != 00);
    assert(p_manager != 00);

    OPJ_UNUSED(p_j2k);
    OPJ_UNUSED(p_manager);

    l_nb_tiles = cp->tw * cp->th;
    * p_nb_tiles = 0;
    tcp = cp->tcps;

    /* INDEX >> */
    /* TODO mergeV2: check this part which use cstr_info */
    /*if (p_j2k->cstr_info) {
            opj_tile_info_t * l_info_tile_ptr = p_j2k->cstr_info->tile;

            for (tileno = 0; tileno < l_nb_tiles; ++tileno) {
                    OPJ_UINT32 cur_totnum_tp = 0;

                    opj_pi_update_encoding_parameters(image,cp,tileno);

                    for (pino = 0; pino <= tcp->numpocs; ++pino)
                    {
                            OPJ_UINT32 tp_num = opj_j2k_get_num_tp(cp,pino,tileno);

                            *p_nb_tiles = *p_nb_tiles + tp_num;

                            cur_totnum_tp += tp_num;
                    }

                    tcp->m_nb_tile_parts = cur_totnum_tp;

                    l_info_tile_ptr->tp = (opj_tp_info_t *) opj_malloc(cur_totnum_tp * sizeof(opj_tp_info_t));
                    if (l_info_tile_ptr->tp == 00) {
                            return OPJ_FALSE;
                    }

                    memset(l_info_tile_ptr->tp,0,cur_totnum_tp * sizeof(opj_tp_info_t));

                    l_info_tile_ptr->num_tps = cur_totnum_tp;

                    ++l_info_tile_ptr;
                    ++tcp;
            }
    }
    else */{
        for (tileno = 0; tileno < l_nb_tiles; ++tileno) {
            OPJ_UINT32 cur_totnum_tp = 0;

            opj_pi_update_encoding_parameters(image, cp, tileno);

            for (pino = 0; pino <= tcp->numpocs; ++pino) {
                OPJ_UINT32 tp_num = opj_j2k_get_num_tp(cp, pino, tileno);

                *p_nb_tiles = *p_nb_tiles + tp_num;

                cur_totnum_tp += tp_num;
            }
            tcp->m_nb_tile_parts = cur_totnum_tp;

            ++tcp;
        }
    }

    return OPJ_TRUE;
}

opj_j2k_write_sod(): avoid potential heap buffer overflow (fixes #1299) (probably master only)

static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
                                       opj_stream_private_t *p_stream,
                                       opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
                                   opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
                             opj_procedure_list_t * p_procedure_list,
                             opj_stream_private_t *p_stream,
                             opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
                                     opj_stream_private_t *p_stream,
                                     opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static const struct opj_dec_memory_marker_handler * opj_j2k_get_marker_handler(
    OPJ_UINT32 p_id);

static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp);

static void opj_j2k_tcp_data_destroy(opj_tcp_t *p_tcp);

static void opj_j2k_cp_destroy(opj_cp_t *p_cp);

static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);


void j2k_dump_image_comp_header(opj_image_comp_t* comp_header,
                                OPJ_BOOL dev_dump_flag, FILE* out_stream)
{
    char tab[3];

    if (dev_dump_flag) {
        fprintf(stdout, "[DEV] Dump an image_comp_header struct {\n");
        tab[0] = '\0';
    }       else {
        tab[0] = '\t';
        tab[1] = '\t';
        tab[2] = '\0';
    }

    fprintf(out_stream, "%s dx=%d, dy=%d\n", tab, comp_header->dx, comp_header->dy);
    fprintf(out_stream, "%s prec=%d\n", tab, comp_header->prec);
    fprintf(out_stream, "%s sgnd=%d\n", tab, comp_header->sgnd);

    if (dev_dump_flag) {
        fprintf(out_stream, "}\n");
    }
}


static OPJ_BOOL opj_j2k_write_regions(opj_j2k_t *p_j2k,
                                      struct opj_stream_private *p_stream,
                                      struct opj_event_mgr * p_manager)
{
    OPJ_UINT32 compno;
    const opj_tccp_t *l_tccp = 00;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);
    assert(p_stream != 00);

    l_tccp = p_j2k->m_cp.tcps->tccps;

    for (compno = 0; compno < p_j2k->m_private_image->numcomps; ++compno)  {
        if (l_tccp->roishift) {

            if (! opj_j2k_write_rgn(p_j2k, 0, compno, p_j2k->m_private_image->numcomps,
                                    p_stream, p_manager)) {
                return OPJ_FALSE;
            }
        }

        ++l_tccp;
    }

    return OPJ_TRUE;
}


OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
                        opj_stream_private_t * p_stream,
                        opj_image_t * p_image,
                        opj_event_mgr_t * p_manager)
{
    if (!p_image) {
        return OPJ_FALSE;
    }

    /* Heuristics to detect sequence opj_read_header(), opj_set_decoded_resolution_factor() */
    /* and finally opj_decode_image() without manual setting of comps[].factor */
    /* We could potentially always execute it, if we don't allow people to do */
    /* opj_read_header(), modify x0,y0,x1,y1 of returned image an call opj_decode_image() */
    if (p_j2k->m_cp.m_specific_param.m_dec.m_reduce > 0 &&
            p_j2k->m_private_image != NULL &&
            p_j2k->m_private_image->numcomps > 0 &&
            p_j2k->m_private_image->comps[0].factor ==
            p_j2k->m_cp.m_specific_param.m_dec.m_reduce &&
            p_image->numcomps > 0 &&
            p_image->comps[0].factor == 0 &&
            /* Don't mess with image dimension if the user has allocated it */
            p_image->comps[0].data == NULL) {
        OPJ_UINT32 it_comp;

        /* Update the comps[].factor member of the output image with the one */
        /* of m_reduce */
        for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
            p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
        }
        if (!opj_j2k_update_image_dimensions(p_image, p_manager)) {
            return OPJ_FALSE;
        }
    }

    if (p_j2k->m_output_image == NULL) {
        p_j2k->m_output_image = opj_image_create0();
        if (!(p_j2k->m_output_image)) {
            return OPJ_FALSE;
        }
    }
    opj_copy_image_header(p_image, p_j2k->m_output_image);

    /* customization of the decoding */
    if (!opj_j2k_setup_decoding(p_j2k, p_manager)) {
        return OPJ_FALSE;
    }

    /* Decode the codestream */
    if (! opj_j2k_exec(p_j2k, p_j2k->m_procedure_list, p_stream, p_manager)) {
        opj_image_destroy(p_j2k->m_private_image);
        p_j2k->m_private_image = NULL;
        return OPJ_FALSE;
    }

    /* Move data and copy one information from codec to output image*/
    return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
}

static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
                                       opj_stream_private_t *p_stream,
                                       opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
                                   opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
                             opj_procedure_list_t * p_procedure_list,
                             opj_stream_private_t *p_stream,
                             opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
                                     opj_stream_private_t *p_stream,
                                     opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static const struct opj_dec_memory_marker_handler * opj_j2k_get_marker_handler(
    OPJ_UINT32 p_id);

static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);

static OPJ_BOOL opj_j2k_write_SPCod_SPCoc(opj_j2k_t *p_j2k,
        OPJ_UINT32 p_tile_no,
        OPJ_UINT32 p_comp_no,
        OPJ_BYTE * p_data,
        OPJ_UINT32 * p_header_size,
        opj_event_mgr_t * p_manager);


static OPJ_BOOL opj_j2k_allocate_tile_element_cstr_index(opj_j2k_t *p_j2k)
{
    OPJ_UINT32 it_tile = 0;

    p_j2k->cstr_index->nb_of_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
    p_j2k->cstr_index->tile_index = (opj_tile_index_t*)opj_calloc(
                                        p_j2k->cstr_index->nb_of_tiles, sizeof(opj_tile_index_t));
    if (!p_j2k->cstr_index->tile_index) {
        return OPJ_FALSE;
    }

    for (it_tile = 0; it_tile < p_j2k->cstr_index->nb_of_tiles; it_tile++) {
        p_j2k->cstr_index->tile_index[it_tile].maxmarknum = 100;
        p_j2k->cstr_index->tile_index[it_tile].marknum = 0;
        p_j2k->cstr_index->tile_index[it_tile].marker = (opj_marker_info_t*)
                opj_calloc(p_j2k->cstr_index->tile_index[it_tile].maxmarknum,
                           sizeof(opj_marker_info_t));
        if (!p_j2k->cstr_index->tile_index[it_tile].marker) {
            return OPJ_FALSE;
        }
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_read_qcc(opj_j2k_t *p_j2k,
                                 OPJ_BYTE * p_header_data,
                                 OPJ_UINT32 p_header_size,
                                 opj_event_mgr_t * p_manager
                                )
{
    OPJ_UINT32 l_num_comp, l_comp_no;

    /* preconditions */
    assert(p_header_data != 00);
    assert(p_j2k != 00);
    assert(p_manager != 00);

    l_num_comp = p_j2k->m_private_image->numcomps;

    if (l_num_comp <= 256) {
        if (p_header_size < 1) {
            opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
            return OPJ_FALSE;
        }
        opj_read_bytes(p_header_data, &l_comp_no, 1);
        ++p_header_data;
        --p_header_size;
    } else {
        if (p_header_size < 2) {
            opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
            return OPJ_FALSE;
        }
        opj_read_bytes(p_header_data, &l_comp_no, 2);
        p_header_data += 2;
        p_header_size -= 2;
    }

#ifdef USE_JPWL
    if (p_j2k->m_cp.correct) {

        static OPJ_UINT32 backup_compno = 0;

        /* compno is negative or larger than the number of components!!! */
        if (/*(l_comp_no < 0) ||*/ (l_comp_no >= l_num_comp)) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "JPWL: bad component number in QCC (%d out of a maximum of %d)\n",
                          l_comp_no, l_num_comp);
            if (!JPWL_ASSUME) {
                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
                return OPJ_FALSE;
            }
            /* we try to correct */
            l_comp_no = backup_compno % l_num_comp;
            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
                          "- setting component number to %d\n",
                          l_comp_no);
        }

        /* keep your private count of tiles */
        backup_compno++;
    };
#endif /* USE_JPWL */

    if (l_comp_no >= p_j2k->m_private_image->numcomps) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid component number: %d, regarding the number of components %d\n",
                      l_comp_no, p_j2k->m_private_image->numcomps);
        return OPJ_FALSE;
    }

    if (! opj_j2k_read_SQcd_SQcc(p_j2k, l_comp_no, p_header_data, &p_header_size,
                                 p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
        return OPJ_FALSE;
    }

    if (p_header_size != 0) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading QCC marker\n");
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


static int opj_j2k_initialise_4K_poc(opj_poc_t *POC, int numres)
{
    POC[0].tile  = 1;
    POC[0].resno0  = 0;
    POC[0].compno0 = 0;
    POC[0].layno1  = 1;
    POC[0].resno1  = (OPJ_UINT32)(numres - 1);
    POC[0].compno1 = 3;
    POC[0].prg1 = OPJ_CPRL;
    POC[1].tile  = 1;
    POC[1].resno0  = (OPJ_UINT32)(numres - 1);
    POC[1].compno0 = 0;
    POC[1].layno1  = 1;
    POC[1].resno1  = (OPJ_UINT32)numres;
    POC[1].compno1 = 3;
    POC[1].prg1 = OPJ_CPRL;
    return 2;
}


static void  opj_j2k_read_float64_to_float(const void * p_src_data,
        void * p_dest_data, OPJ_UINT32 p_nb_elem)
{
    OPJ_BYTE * l_src_data = (OPJ_BYTE *) p_src_data;
    OPJ_FLOAT32 * l_dest_data = (OPJ_FLOAT32 *) p_dest_data;
    OPJ_UINT32 i;
    OPJ_FLOAT64 l_temp;

    for (i = 0; i < p_nb_elem; ++i) {
        opj_read_double(l_src_data, &l_temp);

        l_src_data += sizeof(OPJ_FLOAT64);

        *(l_dest_data++) = (OPJ_FLOAT32) l_temp;
    }
}


static OPJ_BOOL opj_j2k_compare_qcc(opj_j2k_t *p_j2k,
                                    OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no)
{
    return opj_j2k_compare_SQcd_SQcc(p_j2k, p_j2k->m_current_tile_number,
                                     p_first_comp_no, p_second_comp_no);
}


static OPJ_BOOL opj_j2k_merge_ppm(opj_cp_t *p_cp, opj_event_mgr_t * p_manager)
{
    OPJ_UINT32 i, l_ppm_data_size, l_N_ppm_remaining;

    /* preconditions */
    assert(p_cp != 00);
    assert(p_manager != 00);
    assert(p_cp->ppm_buffer == NULL);

    if (p_cp->ppm == 0U) {
        return OPJ_TRUE;
    }

    l_ppm_data_size = 0U;
    l_N_ppm_remaining = 0U;
    for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
        if (p_cp->ppm_markers[i].m_data !=
                NULL) { /* standard doesn't seem to require contiguous Zppm */
            OPJ_UINT32 l_N_ppm;
            OPJ_UINT32 l_data_size = p_cp->ppm_markers[i].m_data_size;
            const OPJ_BYTE* l_data = p_cp->ppm_markers[i].m_data;

            if (l_N_ppm_remaining >= l_data_size) {
                l_N_ppm_remaining -= l_data_size;
                l_data_size = 0U;
            } else {
                l_data += l_N_ppm_remaining;
                l_data_size -= l_N_ppm_remaining;
                l_N_ppm_remaining = 0U;
            }

            if (l_data_size > 0U) {
                do {
                    /* read Nppm */
                    if (l_data_size < 4U) {
                        /* clean up to be done on l_cp destruction */
                        opj_event_msg(p_manager, EVT_ERROR, "Not enough bytes to read Nppm\n");
                        return OPJ_FALSE;
                    }
                    opj_read_bytes(l_data, &l_N_ppm, 4);
                    l_data += 4;
                    l_data_size -= 4;
                    l_ppm_data_size +=
                        l_N_ppm; /* can't overflow, max 256 markers of max 65536 bytes, that is when PPM markers are not corrupted which is checked elsewhere */

                    if (l_data_size >= l_N_ppm) {
                        l_data_size -= l_N_ppm;
                        l_data += l_N_ppm;
                    } else {
                        l_N_ppm_remaining = l_N_ppm - l_data_size;
                        l_data_size = 0U;
                    }
                } while (l_data_size > 0U);
            }
        }
    }

    if (l_N_ppm_remaining != 0U) {
        /* clean up to be done on l_cp destruction */
        opj_event_msg(p_manager, EVT_ERROR, "Corrupted PPM markers\n");
        return OPJ_FALSE;
    }

    p_cp->ppm_buffer = (OPJ_BYTE *) opj_malloc(l_ppm_data_size);
    if (p_cp->ppm_buffer == 00) {
        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPM marker\n");
        return OPJ_FALSE;
    }
    p_cp->ppm_len = l_ppm_data_size;
    l_ppm_data_size = 0U;
    l_N_ppm_remaining = 0U;
    for (i = 0U; i < p_cp->ppm_markers_count; ++i) {
        if (p_cp->ppm_markers[i].m_data !=
                NULL) { /* standard doesn't seem to require contiguous Zppm */
            OPJ_UINT32 l_N_ppm;
            OPJ_UINT32 l_data_size = p_cp->ppm_markers[i].m_data_size;
            const OPJ_BYTE* l_data = p_cp->ppm_markers[i].m_data;

            if (l_N_ppm_remaining >= l_data_size) {
                memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_data_size);
                l_ppm_data_size += l_data_size;
                l_N_ppm_remaining -= l_data_size;
                l_data_size = 0U;
            } else {
                memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_N_ppm_remaining);
                l_ppm_data_size += l_N_ppm_remaining;
                l_data += l_N_ppm_remaining;
                l_data_size -= l_N_ppm_remaining;
                l_N_ppm_remaining = 0U;
            }

            if (l_data_size > 0U) {
                do {
                    /* read Nppm */
                    if (l_data_size < 4U) {
                        /* clean up to be done on l_cp destruction */
                        opj_event_msg(p_manager, EVT_ERROR, "Not enough bytes to read Nppm\n");
                        return OPJ_FALSE;
                    }
                    opj_read_bytes(l_data, &l_N_ppm, 4);
                    l_data += 4;
                    l_data_size -= 4;

                    if (l_data_size >= l_N_ppm) {
                        memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_N_ppm);
                        l_ppm_data_size += l_N_ppm;
                        l_data_size -= l_N_ppm;
                        l_data += l_N_ppm;
                    } else {
                        memcpy(p_cp->ppm_buffer + l_ppm_data_size, l_data, l_data_size);
                        l_ppm_data_size += l_data_size;
                        l_N_ppm_remaining = l_N_ppm - l_data_size;
                        l_data_size = 0U;
                    }
                } while (l_data_size > 0U);
            }
            opj_free(p_cp->ppm_markers[i].m_data);
            p_cp->ppm_markers[i].m_data = NULL;
            p_cp->ppm_markers[i].m_data_size = 0U;
        }
    }

    p_cp->ppm_data = p_cp->ppm_buffer;
    p_cp->ppm_data_size = p_cp->ppm_len;

    p_cp->ppm_markers_count = 0U;
    opj_free(p_cp->ppm_markers);
    p_cp->ppm_markers = NULL;

    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
                                  opj_stream_private_t *p_stream,
                                  opj_event_mgr_t * p_manager
                                 )
{
    OPJ_BYTE * l_current_data = 00;
    OPJ_UINT32 l_tlm_size;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);
    assert(p_stream != 00);

    l_tlm_size = 6 + (5 * p_j2k->m_specific_param.m_encoder.m_total_tile_parts);

    if (l_tlm_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size);
        if (! new_header_tile_data) {
            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write TLM marker\n");
            return OPJ_FALSE;
        }
        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_tlm_size;
    }

    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;

    /* change the way data is written to avoid seeking if possible */
    /* TODO */
    p_j2k->m_specific_param.m_encoder.m_tlm_start = opj_stream_tell(p_stream);

    opj_write_bytes(l_current_data, J2K_MS_TLM,
                    2);                                   /* TLM */
    l_current_data += 2;

    opj_write_bytes(l_current_data, l_tlm_size - 2,
                    2);                                 /* Lpoc */
    l_current_data += 2;

    opj_write_bytes(l_current_data, 0,
                    1);                                                    /* Ztlm=0*/
    ++l_current_data;

    opj_write_bytes(l_current_data, 0x50,
                    1);                                                 /* Stlm ST=1(8bits-255 tiles max),SP=1(Ptlm=32bits) */
    ++l_current_data;

    /* do nothing on the 5 * l_j2k->m_specific_param.m_encoder.m_total_tile_parts remaining data */
    if (opj_stream_write_data(p_stream,
                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size,
                              p_manager) != l_tlm_size) {
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


static void opj_j2k_write_poc_in_memory(opj_j2k_t *p_j2k,
                                        OPJ_BYTE * p_data,
                                        OPJ_UINT32 * p_data_written,
                                        opj_event_mgr_t * p_manager
                                       )
{
    OPJ_UINT32 i;
    OPJ_BYTE * l_current_data = 00;
    OPJ_UINT32 l_nb_comp;
    OPJ_UINT32 l_nb_poc;
    OPJ_UINT32 l_poc_size;
    opj_image_t *l_image = 00;
    opj_tcp_t *l_tcp = 00;
    opj_tccp_t *l_tccp = 00;
    opj_poc_t *l_current_poc = 00;
    OPJ_UINT32 l_poc_room;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);

    OPJ_UNUSED(p_manager);

    l_tcp = &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number];
    l_tccp = &l_tcp->tccps[0];
    l_image = p_j2k->m_private_image;
    l_nb_comp = l_image->numcomps;
    l_nb_poc = 1 + l_tcp->numpocs;

    if (l_nb_comp <= 256) {
        l_poc_room = 1;
    } else {
        l_poc_room = 2;
    }

    l_poc_size = 4 + (5 + 2 * l_poc_room) * l_nb_poc;

    l_current_data = p_data;

    opj_write_bytes(l_current_data, J2K_MS_POC,
                    2);                                   /* POC  */
    l_current_data += 2;

    opj_write_bytes(l_current_data, l_poc_size - 2,
                    2);                                 /* Lpoc */
    l_current_data += 2;

    l_current_poc =  l_tcp->pocs;
    for (i = 0; i < l_nb_poc; ++i) {
        opj_write_bytes(l_current_data, l_current_poc->resno0,
                        1);                                /* RSpoc_i */
        ++l_current_data;

        opj_write_bytes(l_current_data, l_current_poc->compno0,
                        l_poc_room);              /* CSpoc_i */
        l_current_data += l_poc_room;

        opj_write_bytes(l_current_data, l_current_poc->layno1,
                        2);                                /* LYEpoc_i */
        l_current_data += 2;

        opj_write_bytes(l_current_data, l_current_poc->resno1,
                        1);                                /* REpoc_i */
        ++l_current_data;

        opj_write_bytes(l_current_data, l_current_poc->compno1,
                        l_poc_room);              /* CEpoc_i */
        l_current_data += l_poc_room;

        opj_write_bytes(l_current_data, (OPJ_UINT32)l_current_poc->prg,
                        1);   /* Ppoc_i */
        ++l_current_data;

        /* change the value of the max layer according to the actual number of layers in the file, components and resolutions*/
        l_current_poc->layno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
                                l_current_poc->layno1, (OPJ_INT32)l_tcp->numlayers);
        l_current_poc->resno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
                                l_current_poc->resno1, (OPJ_INT32)l_tccp->numresolutions);
        l_current_poc->compno1 = (OPJ_UINT32)opj_int_min((OPJ_INT32)
                                 l_current_poc->compno1, (OPJ_INT32)l_nb_comp);

        ++l_current_poc;
    }

    *p_data_written = l_poc_size;
}


static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager)
{
    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);

    /* DEVELOPER CORNER, insert your custom procedures */
    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                           (opj_procedure)opj_j2k_write_eoc, p_manager)) {
        return OPJ_FALSE;
    }

    if (OPJ_IS_CINEMA(p_j2k->m_cp.rsiz) || OPJ_IS_IMF(p_j2k->m_cp.rsiz)) {
        if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                               (opj_procedure)opj_j2k_write_updated_tlm, p_manager)) {
            return OPJ_FALSE;
        }
    }

    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                           (opj_procedure)opj_j2k_write_epc, p_manager)) {
        return OPJ_FALSE;
    }
    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                           (opj_procedure)opj_j2k_end_encoding, p_manager)) {
        return OPJ_FALSE;
    }
    if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                           (opj_procedure)opj_j2k_destroy_header_memory, p_manager)) {
        return OPJ_FALSE;
    }
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
                                  struct opj_stream_private *p_stream,
                                  struct opj_event_mgr * p_manager)
{
    opj_codestream_index_t * l_cstr_index = 00;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);
    assert(p_stream != 00);

    OPJ_UNUSED(p_manager);

    l_cstr_index = p_j2k->cstr_index;
    if (l_cstr_index) {
        l_cstr_index->codestream_size = (OPJ_UINT64)opj_stream_tell(p_stream);
        /* UniPG>> */
        /* The following adjustment is done to adjust the codestream size */
        /* if SOD is not at 0 in the buffer. Useful in case of JP2, where */
        /* the first bunch of bytes is not in the codestream              */
        l_cstr_index->codestream_size -= (OPJ_UINT64)l_cstr_index->main_head_start;
        /* <<UniPG */
    }

#ifdef USE_JPWL
    /* preparation of JPWL marker segments */
#if 0
    if (cp->epc_on) {

        /* encode according to JPWL */
        jpwl_encode(p_j2k, p_stream, image);

    }
#endif
    assert(0 && "TODO");
#endif /* USE_JPWL */

    return OPJ_TRUE;
}


OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
                               opj_cparameters_t *parameters,
                               opj_image_t *image,
                               opj_event_mgr_t * p_manager)
{
    OPJ_UINT32 i, j, tileno, numpocs_tile;
    opj_cp_t *cp = 00;
    OPJ_UINT32 cblkw, cblkh;

    if (!p_j2k || !parameters || ! image) {
        return OPJ_FALSE;
    }

    if ((parameters->numresolution <= 0) ||
            (parameters->numresolution > OPJ_J2K_MAXRLVLS)) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid number of resolutions : %d not in range [1,%d]\n",
                      parameters->numresolution, OPJ_J2K_MAXRLVLS);
        return OPJ_FALSE;
    }

    if (parameters->cblockw_init < 4 || parameters->cblockw_init > 1024) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
                      parameters->cblockw_init);
        return OPJ_FALSE;
    }
    if (parameters->cblockh_init < 4 || parameters->cblockh_init > 1024) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid value for cblockh_init: %d not a power of 2 not in range [4,1024]\n",
                      parameters->cblockh_init);
        return OPJ_FALSE;
    }
    if (parameters->cblockw_init * parameters->cblockh_init > 4096) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid value for cblockw_init * cblockh_init: should be <= 4096\n");
        return OPJ_FALSE;
    }
    cblkw = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockw_init);
    cblkh = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockh_init);
    if (parameters->cblockw_init != (1 << cblkw)) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
                      parameters->cblockw_init);
        return OPJ_FALSE;
    }
    if (parameters->cblockh_init != (1 << cblkh)) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid value for cblockw_init: %d not a power of 2 in range [4,1024]\n",
                      parameters->cblockh_init);
        return OPJ_FALSE;
    }

    /* keep a link to cp so that we can destroy it later in j2k_destroy_compress */
    cp = &(p_j2k->m_cp);

    /* set default values for cp */
    cp->tw = 1;
    cp->th = 1;

    /* FIXME ADE: to be removed once deprecated cp_cinema and cp_rsiz have been removed */
    if (parameters->rsiz ==
            OPJ_PROFILE_NONE) { /* consider deprecated fields only if RSIZ has not been set */
        OPJ_BOOL deprecated_used = OPJ_FALSE;
        switch (parameters->cp_cinema) {
        case OPJ_CINEMA2K_24:
            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
            parameters->max_cs_size = OPJ_CINEMA_24_CS;
            parameters->max_comp_size = OPJ_CINEMA_24_COMP;
            deprecated_used = OPJ_TRUE;
            break;
        case OPJ_CINEMA2K_48:
            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
            parameters->max_cs_size = OPJ_CINEMA_48_CS;
            parameters->max_comp_size = OPJ_CINEMA_48_COMP;
            deprecated_used = OPJ_TRUE;
            break;
        case OPJ_CINEMA4K_24:
            parameters->rsiz = OPJ_PROFILE_CINEMA_4K;
            parameters->max_cs_size = OPJ_CINEMA_24_CS;
            parameters->max_comp_size = OPJ_CINEMA_24_COMP;
            deprecated_used = OPJ_TRUE;
            break;
        case OPJ_OFF:
        default:
            break;
        }
        switch (parameters->cp_rsiz) {
        case OPJ_CINEMA2K:
            parameters->rsiz = OPJ_PROFILE_CINEMA_2K;
            deprecated_used = OPJ_TRUE;
            break;
        case OPJ_CINEMA4K:
            parameters->rsiz = OPJ_PROFILE_CINEMA_4K;
            deprecated_used = OPJ_TRUE;
            break;
        case OPJ_MCT:
            parameters->rsiz = OPJ_PROFILE_PART2 | OPJ_EXTENSION_MCT;
            deprecated_used = OPJ_TRUE;
        case OPJ_STD_RSIZ:
        default:
            break;
        }
        if (deprecated_used) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "Deprecated fields cp_cinema or cp_rsiz are used\n"
                          "Please consider using only the rsiz field\n"
                          "See openjpeg.h documentation for more details\n");
        }
    }

    /* If no explicit layers are provided, use lossless settings */
    if (parameters->tcp_numlayers == 0) {
        parameters->tcp_numlayers = 1;
        parameters->cp_disto_alloc = 1;
        parameters->tcp_rates[0] = 0;
    }

    if (parameters->cp_disto_alloc) {
        /* Emit warnings if tcp_rates are not decreasing */
        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
            OPJ_FLOAT32 rate_i_corr = parameters->tcp_rates[i];
            OPJ_FLOAT32 rate_i_m_1_corr = parameters->tcp_rates[i - 1];
            if (rate_i_corr <= 1.0) {
                rate_i_corr = 1.0;
            }
            if (rate_i_m_1_corr <= 1.0) {
                rate_i_m_1_corr = 1.0;
            }
            if (rate_i_corr >= rate_i_m_1_corr) {
                if (rate_i_corr != parameters->tcp_rates[i] &&
                        rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
                    opj_event_msg(p_manager, EVT_WARNING,
                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
                                  i, parameters->tcp_rates[i], rate_i_corr,
                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
                } else if (rate_i_corr != parameters->tcp_rates[i]) {
                    opj_event_msg(p_manager, EVT_WARNING,
                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
                                  "than tcp_rates[%d]=%f\n",
                                  i, parameters->tcp_rates[i], rate_i_corr,
                                  i - 1, parameters->tcp_rates[i - 1]);
                } else if (rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
                    opj_event_msg(p_manager, EVT_WARNING,
                                  "tcp_rates[%d]=%f should be strictly lesser "
                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
                                  i, parameters->tcp_rates[i],
                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
                } else {
                    opj_event_msg(p_manager, EVT_WARNING,
                                  "tcp_rates[%d]=%f should be strictly lesser "
                                  "than tcp_rates[%d]=%f\n",
                                  i, parameters->tcp_rates[i],
                                  i - 1, parameters->tcp_rates[i - 1]);
                }
            }
        }
    } else if (parameters->cp_fixed_quality) {
        /* Emit warnings if tcp_distoratio are not increasing */
        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
            if (parameters->tcp_distoratio[i] < parameters->tcp_distoratio[i - 1] &&
                    !(i == (OPJ_UINT32)parameters->tcp_numlayers - 1 &&
                      parameters->tcp_distoratio[i] == 0)) {
                opj_event_msg(p_manager, EVT_WARNING,
                              "tcp_distoratio[%d]=%f should be strictly greater "
                              "than tcp_distoratio[%d]=%f\n",
                              i, parameters->tcp_distoratio[i], i - 1,
                              parameters->tcp_distoratio[i - 1]);
            }
        }
    }

    /* see if max_codestream_size does limit input rate */
    if (parameters->max_cs_size <= 0) {
        if (parameters->tcp_rates[parameters->tcp_numlayers - 1] > 0) {
            OPJ_FLOAT32 temp_size;
            temp_size = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
                                       image->comps[0].h * image->comps[0].prec) /
                                      ((double)parameters->tcp_rates[parameters->tcp_numlayers - 1] * 8 *
                                       image->comps[0].dx * image->comps[0].dy));
            if (temp_size > INT_MAX) {
                parameters->max_cs_size = INT_MAX;
            } else {
                parameters->max_cs_size = (int) floor(temp_size);
            }
        } else {
            parameters->max_cs_size = 0;
        }
    } else {
        OPJ_FLOAT32 temp_rate;
        OPJ_BOOL cap = OPJ_FALSE;

        if (OPJ_IS_IMF(parameters->rsiz) && parameters->max_cs_size > 0 &&
                parameters->tcp_numlayers == 1 && parameters->tcp_rates[0] == 0) {
            parameters->tcp_rates[0] = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
                                       image->comps[0].h * image->comps[0].prec) /
                                       (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
                                               image->comps[0].dy);
        }

        temp_rate = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
                                   image->comps[0].h * image->comps[0].prec) /
                                  (((double)parameters->max_cs_size) * 8 * image->comps[0].dx *
                                   image->comps[0].dy));
        for (i = 0; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
            if (parameters->tcp_rates[i] < temp_rate) {
                parameters->tcp_rates[i] = temp_rate;
                cap = OPJ_TRUE;
            }
        }
        if (cap) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "The desired maximum codestream size has limited\n"
                          "at least one of the desired quality layers\n");
        }
    }

    /* Manage profiles and applications and set RSIZ */
    /* set cinema parameters if required */
    if (OPJ_IS_CINEMA(parameters->rsiz)) {
        if ((parameters->rsiz == OPJ_PROFILE_CINEMA_S2K)
                || (parameters->rsiz == OPJ_PROFILE_CINEMA_S4K)) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Scalable Digital Cinema profiles not yet supported\n");
            parameters->rsiz = OPJ_PROFILE_NONE;
        } else {
            opj_j2k_set_cinema_parameters(parameters, image, p_manager);
            if (!opj_j2k_is_cinema_compliant(image, parameters->rsiz, p_manager)) {
                parameters->rsiz = OPJ_PROFILE_NONE;
            }
        }
    } else if (OPJ_IS_STORAGE(parameters->rsiz)) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Long Term Storage profile not yet supported\n");
        parameters->rsiz = OPJ_PROFILE_NONE;
    } else if (OPJ_IS_BROADCAST(parameters->rsiz)) {
        opj_event_msg(p_manager, EVT_WARNING,
                      "JPEG 2000 Broadcast profiles not yet supported\n");
        parameters->rsiz = OPJ_PROFILE_NONE;
    } else if (OPJ_IS_IMF(parameters->rsiz)) {
        opj_j2k_set_imf_parameters(parameters, image, p_manager);
        if (!opj_j2k_is_imf_compliant(parameters, image, p_manager)) {
            parameters->rsiz = OPJ_PROFILE_NONE;
        }
    } else if (OPJ_IS_PART2(parameters->rsiz)) {
        if (parameters->rsiz == ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_NONE))) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "JPEG 2000 Part-2 profile defined\n"
                          "but no Part-2 extension enabled.\n"
                          "Profile set to NONE.\n");
            parameters->rsiz = OPJ_PROFILE_NONE;
        } else if (parameters->rsiz != ((OPJ_PROFILE_PART2) | (OPJ_EXTENSION_MCT))) {
            opj_event_msg(p_manager, EVT_WARNING,
                          "Unsupported Part-2 extension enabled\n"
                          "Profile set to NONE.\n");
            parameters->rsiz = OPJ_PROFILE_NONE;
        }
    }

    /*
    copy user encoding parameters
    */
    cp->m_specific_param.m_enc.m_max_comp_size = (OPJ_UINT32)
            parameters->max_comp_size;
    cp->rsiz = parameters->rsiz;
    cp->m_specific_param.m_enc.m_disto_alloc = (OPJ_UINT32)
            parameters->cp_disto_alloc & 1u;
    cp->m_specific_param.m_enc.m_fixed_alloc = (OPJ_UINT32)
            parameters->cp_fixed_alloc & 1u;
    cp->m_specific_param.m_enc.m_fixed_quality = (OPJ_UINT32)
            parameters->cp_fixed_quality & 1u;

    /* mod fixed_quality */
    if (parameters->cp_fixed_alloc && parameters->cp_matrice) {
        size_t array_size = (size_t)parameters->tcp_numlayers *
                            (size_t)parameters->numresolution * 3 * sizeof(OPJ_INT32);
        cp->m_specific_param.m_enc.m_matrice = (OPJ_INT32 *) opj_malloc(array_size);
        if (!cp->m_specific_param.m_enc.m_matrice) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Not enough memory to allocate copy of user encoding parameters matrix \n");
            return OPJ_FALSE;
        }
        memcpy(cp->m_specific_param.m_enc.m_matrice, parameters->cp_matrice,
               array_size);
    }

    /* tiles */
    cp->tdx = (OPJ_UINT32)parameters->cp_tdx;
    cp->tdy = (OPJ_UINT32)parameters->cp_tdy;

    /* tile offset */
    cp->tx0 = (OPJ_UINT32)parameters->cp_tx0;
    cp->ty0 = (OPJ_UINT32)parameters->cp_ty0;

    /* comment string */
    if (parameters->cp_comment) {
        cp->comment = (char*)opj_malloc(strlen(parameters->cp_comment) + 1U);
        if (!cp->comment) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Not enough memory to allocate copy of comment string\n");
            return OPJ_FALSE;
        }
        strcpy(cp->comment, parameters->cp_comment);
    } else {
        /* Create default comment for codestream */
        const char comment[] = "Created by OpenJPEG version ";
        const size_t clen = strlen(comment);
        const char *version = opj_version();

        /* UniPG>> */
#ifdef USE_JPWL
        cp->comment = (char*)opj_malloc(clen + strlen(version) + 11);
        if (!cp->comment) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Not enough memory to allocate comment string\n");
            return OPJ_FALSE;
        }
        sprintf(cp->comment, "%s%s with JPWL", comment, version);
#else
        cp->comment = (char*)opj_malloc(clen + strlen(version) + 1);
        if (!cp->comment) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Not enough memory to allocate comment string\n");
            return OPJ_FALSE;
        }
        sprintf(cp->comment, "%s%s", comment, version);
#endif
        /* <<UniPG */
    }

    /*
    calculate other encoding parameters
    */

    if (parameters->tile_size_on) {
        if (cp->tdx == 0) {
            opj_event_msg(p_manager, EVT_ERROR, "Invalid tile width\n");
            return OPJ_FALSE;
        }
        if (cp->tdy == 0) {
            opj_event_msg(p_manager, EVT_ERROR, "Invalid tile height\n");
            return OPJ_FALSE;
        }
        cp->tw = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(image->x1 - cp->tx0),
                                             (OPJ_INT32)cp->tdx);
        cp->th = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)(image->y1 - cp->ty0),
                                             (OPJ_INT32)cp->tdy);
    } else {
        cp->tdx = image->x1 - cp->tx0;
        cp->tdy = image->y1 - cp->ty0;
    }

    if (parameters->tp_on) {
        cp->m_specific_param.m_enc.m_tp_flag = (OPJ_BYTE)parameters->tp_flag;
        cp->m_specific_param.m_enc.m_tp_on = 1;
    }

#ifdef USE_JPWL
    /*
    calculate JPWL encoding parameters
    */

    if (parameters->jpwl_epc_on) {
        OPJ_INT32 i;

        /* set JPWL on */
        cp->epc_on = OPJ_TRUE;
        cp->info_on = OPJ_FALSE; /* no informative technique */

        /* set EPB on */
        if ((parameters->jpwl_hprot_MH > 0) || (parameters->jpwl_hprot_TPH[0] > 0)) {
            cp->epb_on = OPJ_TRUE;

            cp->hprot_MH = parameters->jpwl_hprot_MH;
            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
                cp->hprot_TPH_tileno[i] = parameters->jpwl_hprot_TPH_tileno[i];
                cp->hprot_TPH[i] = parameters->jpwl_hprot_TPH[i];
            }
            /* if tile specs are not specified, copy MH specs */
            if (cp->hprot_TPH[0] == -1) {
                cp->hprot_TPH_tileno[0] = 0;
                cp->hprot_TPH[0] = parameters->jpwl_hprot_MH;
            }
            for (i = 0; i < JPWL_MAX_NO_PACKSPECS; i++) {
                cp->pprot_tileno[i] = parameters->jpwl_pprot_tileno[i];
                cp->pprot_packno[i] = parameters->jpwl_pprot_packno[i];
                cp->pprot[i] = parameters->jpwl_pprot[i];
            }
        }

        /* set ESD writing */
        if ((parameters->jpwl_sens_size == 1) || (parameters->jpwl_sens_size == 2)) {
            cp->esd_on = OPJ_TRUE;

            cp->sens_size = parameters->jpwl_sens_size;
            cp->sens_addr = parameters->jpwl_sens_addr;
            cp->sens_range = parameters->jpwl_sens_range;

            cp->sens_MH = parameters->jpwl_sens_MH;
            for (i = 0; i < JPWL_MAX_NO_TILESPECS; i++) {
                cp->sens_TPH_tileno[i] = parameters->jpwl_sens_TPH_tileno[i];
                cp->sens_TPH[i] = parameters->jpwl_sens_TPH[i];
            }
        }

        /* always set RED writing to false: we are at the encoder */
        cp->red_on = OPJ_FALSE;

    } else {
        cp->epc_on = OPJ_FALSE;
    }
#endif /* USE_JPWL */

    /* initialize the mutiple tiles */
    /* ---------------------------- */
    cp->tcps = (opj_tcp_t*) opj_calloc(cp->tw * cp->th, sizeof(opj_tcp_t));
    if (!cp->tcps) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Not enough memory to allocate tile coding parameters\n");
        return OPJ_FALSE;
    }

    for (tileno = 0; tileno < cp->tw * cp->th; tileno++) {
        opj_tcp_t *tcp = &cp->tcps[tileno];
        tcp->numlayers = (OPJ_UINT32)parameters->tcp_numlayers;

        for (j = 0; j < tcp->numlayers; j++) {
            if (OPJ_IS_CINEMA(cp->rsiz) || OPJ_IS_IMF(cp->rsiz)) {
                if (cp->m_specific_param.m_enc.m_fixed_quality) {
                    tcp->distoratio[j] = parameters->tcp_distoratio[j];
                }
                tcp->rates[j] = parameters->tcp_rates[j];
            } else {
                if (cp->m_specific_param.m_enc.m_fixed_quality) {       /* add fixed_quality */
                    tcp->distoratio[j] = parameters->tcp_distoratio[j];
                } else {
                    tcp->rates[j] = parameters->tcp_rates[j];
                }
            }
            if (!cp->m_specific_param.m_enc.m_fixed_quality &&
                    tcp->rates[j] <= 1.0) {
                tcp->rates[j] = 0.0;    /* force lossless */
            }
        }

        tcp->csty = (OPJ_UINT32)parameters->csty;
        tcp->prg = parameters->prog_order;
        tcp->mct = (OPJ_UINT32)parameters->tcp_mct;

        numpocs_tile = 0;
        tcp->POC = 0;

        if (parameters->numpocs) {
            /* initialisation of POC */
            for (i = 0; i < parameters->numpocs; i++) {
                if (tileno + 1 == parameters->POC[i].tile)  {
                    opj_poc_t *tcp_poc = &tcp->pocs[numpocs_tile];

                    if (parameters->POC[numpocs_tile].compno0 >= image->numcomps) {
                        opj_event_msg(p_manager, EVT_ERROR,
                                      "Invalid compno0 for POC %d\n", i);
                        return OPJ_FALSE;
                    }

                    tcp_poc->resno0         = parameters->POC[numpocs_tile].resno0;
                    tcp_poc->compno0        = parameters->POC[numpocs_tile].compno0;
                    tcp_poc->layno1         = parameters->POC[numpocs_tile].layno1;
                    tcp_poc->resno1         = parameters->POC[numpocs_tile].resno1;
                    tcp_poc->compno1        = opj_uint_min(parameters->POC[numpocs_tile].compno1,
                                                           image->numcomps);
                    tcp_poc->prg1           = parameters->POC[numpocs_tile].prg1;
                    tcp_poc->tile           = parameters->POC[numpocs_tile].tile;

                    numpocs_tile++;
                }
            }

            if (numpocs_tile) {

                /* TODO MSD use the return value*/
                opj_j2k_check_poc_val(parameters->POC, tileno, parameters->numpocs,
                                      (OPJ_UINT32)parameters->numresolution, image->numcomps,
                                      (OPJ_UINT32)parameters->tcp_numlayers, p_manager);

                tcp->POC = 1;
                tcp->numpocs = numpocs_tile - 1 ;
            }
        } else {
            tcp->numpocs = 0;
        }

        tcp->tccps = (opj_tccp_t*) opj_calloc(image->numcomps, sizeof(opj_tccp_t));
        if (!tcp->tccps) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Not enough memory to allocate tile component coding parameters\n");
            return OPJ_FALSE;
        }
        if (parameters->mct_data) {

            OPJ_UINT32 lMctSize = image->numcomps * image->numcomps * (OPJ_UINT32)sizeof(
                                      OPJ_FLOAT32);
            OPJ_FLOAT32 * lTmpBuf = (OPJ_FLOAT32*)opj_malloc(lMctSize);
            OPJ_INT32 * l_dc_shift = (OPJ_INT32 *)((OPJ_BYTE *) parameters->mct_data +
                                                   lMctSize);

            if (!lTmpBuf) {
                opj_event_msg(p_manager, EVT_ERROR,
                              "Not enough memory to allocate temp buffer\n");
                return OPJ_FALSE;
            }

            tcp->mct = 2;
            tcp->m_mct_coding_matrix = (OPJ_FLOAT32*)opj_malloc(lMctSize);
            if (! tcp->m_mct_coding_matrix) {
                opj_free(lTmpBuf);
                lTmpBuf = NULL;
                opj_event_msg(p_manager, EVT_ERROR,
                              "Not enough memory to allocate encoder MCT coding matrix \n");
                return OPJ_FALSE;
            }
            memcpy(tcp->m_mct_coding_matrix, parameters->mct_data, lMctSize);
            memcpy(lTmpBuf, parameters->mct_data, lMctSize);

            tcp->m_mct_decoding_matrix = (OPJ_FLOAT32*)opj_malloc(lMctSize);
            if (! tcp->m_mct_decoding_matrix) {
                opj_free(lTmpBuf);
                lTmpBuf = NULL;
                opj_event_msg(p_manager, EVT_ERROR,
                              "Not enough memory to allocate encoder MCT decoding matrix \n");
                return OPJ_FALSE;
            }
            if (opj_matrix_inversion_f(lTmpBuf, (tcp->m_mct_decoding_matrix),
                                       image->numcomps) == OPJ_FALSE) {
                opj_free(lTmpBuf);
                lTmpBuf = NULL;
                opj_event_msg(p_manager, EVT_ERROR,
                              "Failed to inverse encoder MCT decoding matrix \n");
                return OPJ_FALSE;
            }

            tcp->mct_norms = (OPJ_FLOAT64*)
                             opj_malloc(image->numcomps * sizeof(OPJ_FLOAT64));
            if (! tcp->mct_norms) {
                opj_free(lTmpBuf);
                lTmpBuf = NULL;
                opj_event_msg(p_manager, EVT_ERROR,
                              "Not enough memory to allocate encoder MCT norms \n");
                return OPJ_FALSE;
            }
            opj_calculate_norms(tcp->mct_norms, image->numcomps,
                                tcp->m_mct_decoding_matrix);
            opj_free(lTmpBuf);

            for (i = 0; i < image->numcomps; i++) {
                opj_tccp_t *tccp = &tcp->tccps[i];
                tccp->m_dc_level_shift = l_dc_shift[i];
            }

            if (opj_j2k_setup_mct_encoding(tcp, image) == OPJ_FALSE) {
                /* free will be handled by opj_j2k_destroy */
                opj_event_msg(p_manager, EVT_ERROR, "Failed to setup j2k mct encoding\n");
                return OPJ_FALSE;
            }
        } else {
            if (tcp->mct == 1 && image->numcomps >= 3) { /* RGB->YCC MCT is enabled */
                if ((image->comps[0].dx != image->comps[1].dx) ||
                        (image->comps[0].dx != image->comps[2].dx) ||
                        (image->comps[0].dy != image->comps[1].dy) ||
                        (image->comps[0].dy != image->comps[2].dy)) {
                    opj_event_msg(p_manager, EVT_WARNING,
                                  "Cannot perform MCT on components with different sizes. Disabling MCT.\n");
                    tcp->mct = 0;
                }
            }
            for (i = 0; i < image->numcomps; i++) {
                opj_tccp_t *tccp = &tcp->tccps[i];
                opj_image_comp_t * l_comp = &(image->comps[i]);

                if (! l_comp->sgnd) {
                    tccp->m_dc_level_shift = 1 << (l_comp->prec - 1);
                }
            }
        }

        for (i = 0; i < image->numcomps; i++) {
            opj_tccp_t *tccp = &tcp->tccps[i];

            tccp->csty = parameters->csty &
                         0x01;   /* 0 => one precinct || 1 => custom precinct  */
            tccp->numresolutions = (OPJ_UINT32)parameters->numresolution;
            tccp->cblkw = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockw_init);
            tccp->cblkh = (OPJ_UINT32)opj_int_floorlog2(parameters->cblockh_init);
            tccp->cblksty = (OPJ_UINT32)parameters->mode;
            tccp->qmfbid = parameters->irreversible ? 0 : 1;
            tccp->qntsty = parameters->irreversible ? J2K_CCP_QNTSTY_SEQNT :
                           J2K_CCP_QNTSTY_NOQNT;
            tccp->numgbits = 2;

            if ((OPJ_INT32)i == parameters->roi_compno) {
                tccp->roishift = parameters->roi_shift;
            } else {
                tccp->roishift = 0;
            }

            if (parameters->csty & J2K_CCP_CSTY_PRT) {
                OPJ_INT32 p = 0, it_res;
                assert(tccp->numresolutions > 0);
                for (it_res = (OPJ_INT32)tccp->numresolutions - 1; it_res >= 0; it_res--) {
                    if (p < parameters->res_spec) {

                        if (parameters->prcw_init[p] < 1) {
                            tccp->prcw[it_res] = 1;
                        } else {
                            tccp->prcw[it_res] = (OPJ_UINT32)opj_int_floorlog2(parameters->prcw_init[p]);
                        }

                        if (parameters->prch_init[p] < 1) {
                            tccp->prch[it_res] = 1;
                        } else {
                            tccp->prch[it_res] = (OPJ_UINT32)opj_int_floorlog2(parameters->prch_init[p]);
                        }

                    } else {
                        OPJ_INT32 res_spec = parameters->res_spec;
                        OPJ_INT32 size_prcw = 0;
                        OPJ_INT32 size_prch = 0;

                        assert(res_spec > 0); /* issue 189 */
                        size_prcw = parameters->prcw_init[res_spec - 1] >> (p - (res_spec - 1));
                        size_prch = parameters->prch_init[res_spec - 1] >> (p - (res_spec - 1));

                        if (size_prcw < 1) {
                            tccp->prcw[it_res] = 1;
                        } else {
                            tccp->prcw[it_res] = (OPJ_UINT32)opj_int_floorlog2(size_prcw);
                        }

                        if (size_prch < 1) {
                            tccp->prch[it_res] = 1;
                        } else {
                            tccp->prch[it_res] = (OPJ_UINT32)opj_int_floorlog2(size_prch);
                        }
                    }
                    p++;
                    /*printf("\nsize precinct for level %d : %d,%d\n", it_res,tccp->prcw[it_res], tccp->prch[it_res]); */
                }       /*end for*/
            } else {
                for (j = 0; j < tccp->numresolutions; j++) {
                    tccp->prcw[j] = 15;
                    tccp->prch[j] = 15;
                }
            }

            opj_dwt_calc_explicit_stepsizes(tccp, image->comps[i].prec);
        }
    }

    if (parameters->mct_data) {
        opj_free(parameters->mct_data);
        parameters->mct_data = 00;
    }
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_read_plm(opj_j2k_t *p_j2k,
                                 OPJ_BYTE * p_header_data,
                                 OPJ_UINT32 p_header_size,
                                 opj_event_mgr_t * p_manager
                                )
{
    /* preconditions */
    assert(p_header_data != 00);
    assert(p_j2k != 00);
    assert(p_manager != 00);

    OPJ_UNUSED(p_j2k);
    OPJ_UNUSED(p_header_data);

    if (p_header_size < 1) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
        return OPJ_FALSE;
    }
    /* Do not care of this at the moment since only local variables are set here */
    /*
    opj_read_bytes(p_header_data,&l_Zplm,1);                                        // Zplm
    ++p_header_data;
    --p_header_size;

    while
            (p_header_size > 0)
    {
            opj_read_bytes(p_header_data,&l_Nplm,1);                                // Nplm
            ++p_header_data;
            p_header_size -= (1+l_Nplm);
            if
                    (p_header_size < 0)
            {
                    opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
                    return false;
            }
            for
                    (i = 0; i < l_Nplm; ++i)
            {
                    opj_read_bytes(p_header_data,&l_tmp,1);                         // Iplm_ij
                    ++p_header_data;
                    // take only the last seven bytes
                    l_packet_len |= (l_tmp & 0x7f);
                    if
                            (l_tmp & 0x80)
                    {
                            l_packet_len <<= 7;
                    }
                    else
                    {
            // store packet length and proceed to next packet
                            l_packet_len = 0;
                    }
            }
            if
                    (l_packet_len != 0)
            {
                    opj_event_msg(p_manager, EVT_ERROR, "Error reading PLM marker\n");
                    return false;
            }
    }
    */
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_write_cbd(opj_j2k_t *p_j2k,
                                  struct opj_stream_private *p_stream,
                                  struct opj_event_mgr * p_manager)
{
    OPJ_UINT32 i;
    OPJ_UINT32 l_cbd_size;
    OPJ_BYTE * l_current_data = 00;
    opj_image_t *l_image = 00;
    opj_image_comp_t * l_comp = 00;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);
    assert(p_stream != 00);

    l_image = p_j2k->m_private_image;
    l_cbd_size = 6 + p_j2k->m_private_image->numcomps;

    if (l_cbd_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size);
        if (! new_header_tile_data) {
            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write CBD marker\n");
            return OPJ_FALSE;
        }
        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_cbd_size;
    }

    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;

    opj_write_bytes(l_current_data, J2K_MS_CBD, 2);                 /* CBD */
    l_current_data += 2;

    opj_write_bytes(l_current_data, l_cbd_size - 2, 2);             /* L_CBD */
    l_current_data += 2;

    opj_write_bytes(l_current_data, l_image->numcomps, 2);          /* Ncbd */
    l_current_data += 2;

    l_comp = l_image->comps;

    for (i = 0; i < l_image->numcomps; ++i) {
        opj_write_bytes(l_current_data, (l_comp->sgnd << 7) | (l_comp->prec - 1),
                        1);           /* Component bit depth */
        ++l_current_data;

        ++l_comp;
    }

    if (opj_stream_write_data(p_stream,
                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size,
                              p_manager) != l_cbd_size) {
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
                                      OPJ_UINT32 tileno,
                                      OPJ_UINT32 p_nb_pocs,
                                      OPJ_UINT32 p_nb_resolutions,
                                      OPJ_UINT32 p_num_comps,
                                      OPJ_UINT32 p_num_layers,
                                      opj_event_mgr_t * p_manager)
{
    OPJ_UINT32* packet_array;
    OPJ_UINT32 index, resno, compno, layno;
    OPJ_UINT32 i;
    OPJ_UINT32 step_c = 1;
    OPJ_UINT32 step_r = p_num_comps * step_c;
    OPJ_UINT32 step_l = p_nb_resolutions * step_r;
    OPJ_BOOL loss = OPJ_FALSE;

    assert(p_nb_pocs > 0);

    packet_array = (OPJ_UINT32*) opj_calloc(step_l * p_num_layers,
                                            sizeof(OPJ_UINT32));
    if (packet_array == 00) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Not enough memory for checking the poc values.\n");
        return OPJ_FALSE;
    }

    /* iterate through all the pocs that match our tile of interest. */
    for (i = 0; i < p_nb_pocs; ++i) {
        const opj_poc_t *poc = &p_pocs[i];
        if (tileno + 1 == poc->tile) {
            index = step_r * poc->resno0;

            /* take each resolution for each poc */
            for (resno = poc->resno0 ;
                    resno < opj_uint_min(poc->resno1, p_nb_resolutions); ++resno) {
                OPJ_UINT32 res_index = index + poc->compno0 * step_c;

                /* take each comp of each resolution for each poc */
                for (compno = poc->compno0 ;
                        compno < opj_uint_min(poc->compno1, p_num_comps); ++compno) {
                    /* The layer index always starts at zero for every progression. */
                    const OPJ_UINT32 layno0 = 0;
                    OPJ_UINT32 comp_index = res_index + layno0 * step_l;

                    /* and finally take each layer of each res of ... */
                    for (layno = layno0; layno < opj_uint_min(poc->layno1, p_num_layers);
                            ++layno) {
                        packet_array[comp_index] = 1;
                        comp_index += step_l;
                    }

                    res_index += step_c;
                }

                index += step_r;
            }
        }
    }

    index = 0;
    for (layno = 0; layno < p_num_layers ; ++layno) {
        for (resno = 0; resno < p_nb_resolutions; ++resno) {
            for (compno = 0; compno < p_num_comps; ++compno) {
                loss |= (packet_array[index] != 1);
#ifdef DEBUG_VERBOSE
                if (packet_array[index] != 1) {
                    fprintf(stderr,
                            "Missing packet in POC: layno=%d resno=%d compno=%d\n",
                            layno, resno, compno);
                }
#endif
                index += step_c;
            }
        }
    }

    if (loss) {
        opj_event_msg(p_manager, EVT_ERROR, "Missing packets possible loss of data\n");
    }

    opj_free(packet_array);

    return !loss;
}


static OPJ_BOOL opj_j2k_write_SQcd_SQcc(opj_j2k_t *p_j2k,
                                        OPJ_UINT32 p_tile_no,
                                        OPJ_UINT32 p_comp_no,
                                        OPJ_BYTE * p_data,
                                        OPJ_UINT32 * p_header_size,
                                        struct opj_event_mgr * p_manager)
{
    OPJ_UINT32 l_header_size;
    OPJ_UINT32 l_band_no, l_num_bands;
    OPJ_UINT32 l_expn, l_mant;

    opj_cp_t *l_cp = 00;
    opj_tcp_t *l_tcp = 00;
    opj_tccp_t *l_tccp = 00;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_header_size != 00);
    assert(p_manager != 00);
    assert(p_data != 00);

    l_cp = &(p_j2k->m_cp);
    l_tcp = &l_cp->tcps[p_tile_no];
    l_tccp = &l_tcp->tccps[p_comp_no];

    /* preconditions again */
    assert(p_tile_no < l_cp->tw * l_cp->th);
    assert(p_comp_no < p_j2k->m_private_image->numcomps);

    l_num_bands = (l_tccp->qntsty == J2K_CCP_QNTSTY_SIQNT) ? 1 :
                  (l_tccp->numresolutions * 3 - 2);

    if (l_tccp->qntsty == J2K_CCP_QNTSTY_NOQNT)  {
        l_header_size = 1 + l_num_bands;

        if (*p_header_size < l_header_size) {
            opj_event_msg(p_manager, EVT_ERROR, "Error writing SQcd SQcc element\n");
            return OPJ_FALSE;
        }

        opj_write_bytes(p_data, l_tccp->qntsty + (l_tccp->numgbits << 5),
                        1);   /* Sqcx */
        ++p_data;

        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
            l_expn = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].expn;
            opj_write_bytes(p_data, l_expn << 3, 1);        /* SPqcx_i */
            ++p_data;
        }
    } else {
        l_header_size = 1 + 2 * l_num_bands;

        if (*p_header_size < l_header_size) {
            opj_event_msg(p_manager, EVT_ERROR, "Error writing SQcd SQcc element\n");
            return OPJ_FALSE;
        }

        opj_write_bytes(p_data, l_tccp->qntsty + (l_tccp->numgbits << 5),
                        1);   /* Sqcx */
        ++p_data;

        for (l_band_no = 0; l_band_no < l_num_bands; ++l_band_no) {
            l_expn = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].expn;
            l_mant = (OPJ_UINT32)l_tccp->stepsizes[l_band_no].mant;

            opj_write_bytes(p_data, (l_expn << 11) + l_mant, 2);    /* SPqcx_i */
            p_data += 2;
        }
    }

    *p_header_size = *p_header_size - l_header_size;

    return OPJ_TRUE;
}


opj_j2k_t* opj_j2k_create_decompress(void)
{
    opj_j2k_t *l_j2k = (opj_j2k_t*) opj_calloc(1, sizeof(opj_j2k_t));
    if (!l_j2k) {
        return 00;
    }

    l_j2k->m_is_decoder = 1;
    l_j2k->m_cp.m_is_decoder = 1;
    /* in the absence of JP2 boxes, consider different bit depth / sign */
    /* per component is allowed */
    l_j2k->m_cp.allow_different_bit_depth_sign = 1;

#ifdef OPJ_DISABLE_TPSOT_FIX
    l_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
#endif

    l_j2k->m_specific_param.m_decoder.m_default_tcp = (opj_tcp_t*) opj_calloc(1,
            sizeof(opj_tcp_t));
    if (!l_j2k->m_specific_param.m_decoder.m_default_tcp) {
        opj_j2k_destroy(l_j2k);
        return 00;
    }

    l_j2k->m_specific_param.m_decoder.m_header_data = (OPJ_BYTE *) opj_calloc(1,
            OPJ_J2K_DEFAULT_HEADER_SIZE);
    if (! l_j2k->m_specific_param.m_decoder.m_header_data) {
        opj_j2k_destroy(l_j2k);
        return 00;
    }

    l_j2k->m_specific_param.m_decoder.m_header_data_size =
        OPJ_J2K_DEFAULT_HEADER_SIZE;

    l_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec = -1 ;

    l_j2k->m_specific_param.m_decoder.m_last_sot_read_pos = 0 ;

    /* codestream index creation */
    l_j2k->cstr_index = opj_j2k_create_cstr_index();
    if (!l_j2k->cstr_index) {
        opj_j2k_destroy(l_j2k);
        return 00;
    }

    /* validation list creation */
    l_j2k->m_validation_list = opj_procedure_list_create();
    if (! l_j2k->m_validation_list) {
        opj_j2k_destroy(l_j2k);
        return 00;
    }

    /* execution list creation */
    l_j2k->m_procedure_list = opj_procedure_list_create();
    if (! l_j2k->m_procedure_list) {
        opj_j2k_destroy(l_j2k);
        return 00;
    }

    l_j2k->m_tp = opj_thread_pool_create(opj_j2k_get_default_thread_count());
    if (!l_j2k->m_tp) {
        l_j2k->m_tp = opj_thread_pool_create(0);
    }
    if (!l_j2k->m_tp) {
        opj_j2k_destroy(l_j2k);
        return NULL;
    }

    return l_j2k;
}

static OPJ_BOOL opj_j2k_setup_header_reading(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_encoding_validation(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_encoding_validation(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_decoding_validation(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_mct_validation(opj_j2k_t * p_j2k,
                                       opj_stream_private_t *p_stream,
                                       opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_build_decoder(opj_j2k_t * p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_build_encoder(opj_j2k_t * p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
                                   opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_exec(opj_j2k_t * p_j2k,
                             opj_procedure_list_t * p_procedure_list,
                             opj_stream_private_t *p_stream,
                             opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
                                     opj_stream_private_t *p_stream,
                                     opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static OPJ_BOOL opj_j2k_destroy_header_memory(opj_j2k_t * p_j2k,
        opj_stream_private_t *p_stream,
        opj_event_mgr_t * p_manager);

static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp);

static void opj_j2k_tcp_data_destroy(opj_tcp_t *p_tcp);

static void opj_j2k_cp_destroy(opj_cp_t *p_cp);

static OPJ_BOOL opj_j2k_compare_SPCod_SPCoc(opj_j2k_t *p_j2k,
        OPJ_UINT32 p_tile_no, OPJ_UINT32 p_first_comp_no, OPJ_UINT32 p_second_comp_no);