metalink_from_http (const struct response *resp, const struct http_stat *hs,
                    const struct url *u)
{
  metalink_t *metalink = NULL;
  metalink_file_t *mfile = xnew0 (metalink_file_t);
  const char *val_beg, *val_end;
  int res_count = 0, hash_count = 0, sig_count = 0, i;

  DEBUGP (("Checking for Metalink in HTTP response\n"));

  /* Initialize metalink file for our simple use case.  */
  if (hs->local_file)
    mfile->name = xstrdup (hs->local_file);
  else
    mfile->name = url_file_name (u, NULL);

  /* Begin with 1-element array (for 0-termination). */
  mfile->checksums = xnew0 (metalink_checksum_t *);
  mfile->resources = xnew0 (metalink_resource_t *);

  /* Find all Link headers.  */
  for (i = 0;
       (i = resp_header_locate (resp, "Link", i, &val_beg, &val_end)) != -1;
       i++)
    {
      char *rel = NULL, *reltype = NULL;
      char *urlstr = NULL;
      const char *url_beg, *url_end, *attrs_beg;
      size_t url_len;

      /* Sample Metalink Link headers:

           Link: <http://www2.example.com/dir1/dir2/dir3/dir4/dir5/example.ext>;
           rel=duplicate; pri=1; pref; geo=gb; depth=4

           Link: <http://example.com/example.ext.asc>; rel=describedby;
           type="application/pgp-signature"
       */

      /* Find beginning of URL.  */
      url_beg = val_beg;
      while (url_beg < val_end - 1 && c_isspace (*url_beg))
        url_beg++;

      /* Find end of URL.  */
      /* The convention here is that end ptr points to one element after
         end of string. In this case, it should be pointing to the '>', which
         is one element after end of actual URL. Therefore, it should never point
         to val_end, which is one element after entire header value string.  */
      url_end = url_beg + 1;
      while (url_end < val_end - 1 && *url_end != '>')
        url_end++;

      if (url_beg >= val_end || url_end >= val_end ||
          *url_beg != '<' || *url_end != '>')
        {
          DEBUGP (("This is not a valid Link header. Ignoring.\n"));
          continue;
        }

      /* Skip <.  */
      url_beg++;
      url_len = url_end - url_beg;

      /* URL found. Now handle the attributes.  */
      attrs_beg = url_end + 1;

      /* First we need to find out what type of link it is. Currently, we
         support rel=duplicate and rel=describedby.  */
      if (!find_key_value (attrs_beg, val_end, "rel", &rel))
        {
          DEBUGP (("No rel value in Link header, skipping.\n"));
          continue;
        }

      urlstr = xstrndup (url_beg, url_len);
      DEBUGP (("URL=%s\n", urlstr));
      DEBUGP (("rel=%s\n", rel));

      /* Handle signatures.
         Libmetalink only supports one signature per file. Therefore we stop
         as soon as we successfully get first supported signature.  */
      if (sig_count == 0 &&
          !strcmp (rel, "describedby") &&
          find_key_value (attrs_beg, val_end, "type", &reltype) &&
          !strcmp (reltype, "application/pgp-signature")
          )
        {
          /* Download the signature to a temporary file.  */
          FILE *_output_stream = output_stream;
          bool _output_stream_regular = output_stream_regular;

          output_stream = tmpfile ();
          if (output_stream)
            {
              struct iri *iri = iri_new ();
              struct url *url;
              int url_err;

              set_uri_encoding (iri, opt.locale, true);
              url = url_parse (urlstr, &url_err, iri, false);

              if (!url)
                {
                  char *error = url_error (urlstr, url_err);
                  logprintf (LOG_NOTQUIET, _("When downloading signature:\n"
                                             "%s: %s.\n"), urlstr, error);
                  xfree (error);
                  iri_free (iri);
                }
              else
                {
                  /* Avoid recursive Metalink from HTTP headers.  */
                  bool _metalink_http = opt.metalink_over_http;
                  uerr_t retr_err;

                  opt.metalink_over_http = false;
                  retr_err = retrieve_url (url, urlstr, NULL, NULL,
                                           NULL, NULL, false, iri, false);
                  opt.metalink_over_http = _metalink_http;

                  url_free (url);
                  iri_free (iri);

                  if (retr_err == RETROK)
                    {
                      /* Signature is in the temporary file. Read it into
                         metalink resource structure.  */
                      metalink_signature_t msig;
                      size_t siglen;

                      fseek (output_stream, 0, SEEK_END);
                      siglen = ftell (output_stream);
                      fseek (output_stream, 0, SEEK_SET);

                      DEBUGP (("siglen=%lu\n", siglen));

                      msig.signature = xmalloc (siglen + 1);
                      if (fread (msig.signature, siglen, 1, output_stream) != 1)
                        {
                          logputs (LOG_NOTQUIET,
                                   _("Unable to read signature content from "
                                     "temporary file. Skipping.\n"));
                          xfree (msig.signature);
                        }
                      else
                        {
                          msig.signature[siglen] = '\0'; /* Just in case.  */
                          msig.mediatype = xstrdup ("application/pgp-signature");

                          DEBUGP (("Signature (%s):\n%s\n",
                                   msig.mediatype, msig.signature));

                          mfile->signature = xnew (metalink_signature_t);
                          *mfile->signature = msig;

                          sig_count++;
                        }
                    }
                }
              fclose (output_stream);
            }
          else
            {
              logputs (LOG_NOTQUIET, _("Could not create temporary file. "
                                       "Skipping signature download.\n"));
            }
          output_stream_regular = _output_stream_regular;
          output_stream = _output_stream;
        } /* Iterate over signatures.  */

        /* Handle Metalink resources.  */
      else if (!strcmp (rel, "duplicate"))
        {
          metalink_resource_t mres = {0};
          char *pristr;

          /*
             Valid ranges for the "pri" attribute are from
             1 to 999999.  Mirror servers with a lower value of the "pri"
             attribute have a higher priority, while mirrors with an undefined
             "pri" attribute are considered to have a value of 999999, which is
             the lowest priority.

             rfc6249 section 3.1
           */
          mres.priority = DEFAULT_PRI;
          if (find_key_value (url_end, val_end, "pri", &pristr))
            {
              long pri;
              char *end_pristr;
              /* Do not care for errno since 0 is error in this case.  */
              pri = strtol (pristr, &end_pristr, 10);
              if (end_pristr != pristr + strlen (pristr) ||
                  !VALID_PRI_RANGE (pri))
                {
                  /* This is against the specification, so let's inform the user.  */
                  logprintf (LOG_NOTQUIET,
                             _("Invalid pri value. Assuming %d.\n"),
                             DEFAULT_PRI);
                }
              else
                mres.priority = pri;
              xfree (pristr);
            }

          switch (url_scheme (urlstr))
            {
            case SCHEME_HTTP:
              mres.type = xstrdup ("http");
              break;
#ifdef HAVE_SSL
            case SCHEME_HTTPS:
              mres.type = xstrdup ("https");
              break;
            case SCHEME_FTPS:
              mres.type = xstrdup ("ftps");
              break;
#endif
            case SCHEME_FTP:
              mres.type = xstrdup ("ftp");
              break;
            default:
              DEBUGP (("Unsupported url scheme in %s. Skipping resource.\n", urlstr));
            }

          if (mres.type)
            {
              DEBUGP (("TYPE=%s\n", mres.type));

              /* At this point we have validated the new resource.  */

              find_key_value (url_end, val_end, "geo", &mres.location);

              mres.url = urlstr;
              urlstr = NULL;

              mres.preference = 0;
              if (has_key (url_end, val_end, "pref"))
                {
                  DEBUGP (("This resource has preference\n"));
                  mres.preference = 1;
                }

              /* 1 slot from new resource, 1 slot for null-termination.  */
              mfile->resources = xrealloc (mfile->resources,
                                           sizeof (metalink_resource_t *) * (res_count + 2));
              mfile->resources[res_count] = xnew0 (metalink_resource_t);
              *mfile->resources[res_count] = mres;
              res_count++;
            }
        } /* Handle resource link (rel=duplicate).  */
      else
        DEBUGP (("This link header was not used for Metalink\n"));

      xfree (urlstr);
      xfree (reltype);
      xfree (rel);
    } /* Iterate over link headers.  */

  /* Null-terminate resources array.  */
  mfile->resources[res_count] = 0;

  if (res_count == 0)
    {
      DEBUGP (("No valid metalink references found.\n"));
      goto fail;
    }

  /* Find all Digest headers.  */
  for (i = 0;
       (i = resp_header_locate (resp, "Digest", i, &val_beg, &val_end)) != -1;
       i++)
    {
      const char *dig_pos;
      char *dig_type, *dig_hash;

      /* Each Digest header can include multiple hashes. Example:
           Digest: SHA=thvDyvhfIqlvFe+A9MYgxAfm1q5=,unixsum=30637
           Digest: md5=HUXZLQLMuI/KZ5KDcJPcOA==
       */
      for (dig_pos = val_beg;
           (dig_pos = find_key_values (dig_pos, val_end, &dig_type, &dig_hash));
           dig_pos++)
        {
          /* The hash here is assumed to be base64. We need the hash in hex.
             Therefore we convert: base64 -> binary -> hex.  */
          const size_t dig_hash_str_len = strlen (dig_hash);
          char *bin_hash = alloca (dig_hash_str_len * 3 / 4 + 1);
          size_t hash_bin_len;

          hash_bin_len = base64_decode (dig_hash, bin_hash);

          /* One slot for me, one for zero-termination.  */
          mfile->checksums =
                  xrealloc (mfile->checksums,
                            sizeof (metalink_checksum_t *) * (hash_count + 2));
          mfile->checksums[hash_count] = xnew (metalink_checksum_t);
          mfile->checksums[hash_count]->type = dig_type;

          mfile->checksums[hash_count]->hash = xmalloc (hash_bin_len * 2 + 1);
          wg_hex_to_string (mfile->checksums[hash_count]->hash, bin_hash, hash_bin_len);

          xfree (dig_hash);

          hash_count++;
        }
    }

  /* Zero-terminate checksums array.  */
  mfile->checksums[hash_count] = 0;

  /*
    If Instance Digests are not provided by the Metalink servers, the
    Link header fields pertaining to this specification MUST be ignored.

    rfc6249 section 6
   */
  if (hash_count == 0)
    {
      logputs (LOG_VERBOSE,
               _("Could not find acceptable digest for Metalink resources.\n"
                 "Ignoring them.\n"));
      goto fail;
    }

  /* Metalink data is OK. Now we just need to sort the resources based
     on their priorities, preference, and perhaps location.  */
  stable_sort (mfile->resources, res_count, sizeof (metalink_resource_t *), metalink_res_cmp);

  /* Restore sensible preference values (in case someone cares to look).  */
  for (i = 0; i < res_count; ++i)
    mfile->resources[i]->preference = 1000000 - mfile->resources[i]->priority;

  metalink = xnew0 (metalink_t);
  metalink->files = xmalloc (sizeof (metalink_file_t *) * 2);
  metalink->files[0] = mfile;
  metalink->files[1] = 0;
  metalink->origin = xstrdup (u->url);
  metalink->version = METALINK_VERSION_4;
  /* Leave other fields set to 0.  */

  return metalink;

fail:
  /* Free all allocated memory.  */
  if (metalink)
    metalink_delete (metalink);
  else
    metalink_file_delete (mfile);
  return NULL;
}


init_switches (void)
{
  char *p = short_options;
  size_t i, o = 0;
  for (i = 0; i < countof (option_data); i++)
    {
      struct cmdline_option *cmdopt = &option_data[i];
      struct option *longopt;

      if (!cmdopt->long_name)
        /* The option is disabled. */
        continue;

      longopt = &long_options[o++];
      longopt->name = cmdopt->long_name;
      longopt->val = i;
      if (cmdopt->short_name)
        {
          *p++ = cmdopt->short_name;
          optmap[cmdopt->short_name - 32] = longopt - long_options;
        }
      switch (cmdopt->type)
        {
        case OPT_VALUE:
          longopt->has_arg = required_argument;
          if (cmdopt->short_name)
            *p++ = ':';
          break;
        case OPT_BOOLEAN:
          /* Specify an optional argument for long options, so that
             --option=off works the same as --no-option, for
             compatibility with pre-1.10 Wget.  However, don't specify
             optional arguments short-option booleans because they
             prevent combining of short options.  */
          longopt->has_arg = optional_argument;
          /* For Boolean options, add the "--no-FOO" variant, which is
             identical to "--foo", except it has opposite meaning and
             it doesn't allow an argument.  */
          longopt = &long_options[o++];
          longopt->name = no_prefix (cmdopt->long_name);
          longopt->has_arg = no_argument;
          /* Mask the value so we'll be able to recognize that we're
             dealing with the false value.  */
          longopt->val = i | BOOLEAN_NEG_MARKER;
          break;
        default:
          assert (cmdopt->argtype != -1);
          longopt->has_arg = cmdopt->argtype;
          if (cmdopt->short_name)
            {
              if (longopt->has_arg == required_argument)
                *p++ = ':';
              /* Don't handle optional_argument */
            }
        }
    }
  /* Terminate short_options. */
  *p = '\0';
  /* No need for xzero(long_options[o]) because its storage is static
     and it will be zeroed by default.  */
  assert (o <= countof (long_options));
}


static inline __maybe_unused uint64_t h2_get_n64(const struct buffer *b, int o)
{
	return readv_n64(b_peek(b, o), b_wrap(b) - b_peek(b, o), b_orig(b));
}


static int h2c_handle_settings(struct h2c *h2c)
{
	unsigned int offset;
	int error;

	if (h2c->dff & H2_F_SETTINGS_ACK) {
		if (h2c->dfl) {
			error = H2_ERR_FRAME_SIZE_ERROR;
			goto fail;
		}
		return 1;
	}

	if (h2c->dsi != 0) {
		error = H2_ERR_PROTOCOL_ERROR;
		goto fail;
	}

	if (h2c->dfl % 6) {
		error = H2_ERR_FRAME_SIZE_ERROR;
		goto fail;
	}

	/* that's the limit we can process */
	if (h2c->dfl > global.tune.bufsize) {
		error = H2_ERR_FRAME_SIZE_ERROR;
		goto fail;
	}

	/* process full frame only */
	if (b_data(&h2c->dbuf) < h2c->dfl)
		return 0;

	/* parse the frame */
	for (offset = 0; offset < h2c->dfl; offset += 6) {
		uint16_t type = h2_get_n16(&h2c->dbuf, offset);
		int32_t  arg  = h2_get_n32(&h2c->dbuf, offset + 2);

		switch (type) {
		case H2_SETTINGS_INITIAL_WINDOW_SIZE:
			/* we need to update all existing streams with the
			 * difference from the previous iws.
			 */
			if (arg < 0) { // RFC7540#6.5.2
				error = H2_ERR_FLOW_CONTROL_ERROR;
				goto fail;
			}
			h2c_update_all_ws(h2c, arg - h2c->miw);
			h2c->miw = arg;
			break;
		case H2_SETTINGS_MAX_FRAME_SIZE:
			if (arg < 16384 || arg > 16777215) { // RFC7540#6.5.2
				error = H2_ERR_PROTOCOL_ERROR;
				goto fail;
			}
			h2c->mfs = arg;
			break;
		case H2_SETTINGS_ENABLE_PUSH:
			if (arg < 0 || arg > 1) { // RFC7540#6.5.2
				error = H2_ERR_PROTOCOL_ERROR;
				goto fail;
			}
			break;
		}
	}

	/* need to ACK this frame now */
	h2c->st0 = H2_CS_FRAME_A;
	return 1;
 fail:
	sess_log(h2c->conn->owner);
	h2c_error(h2c, error);
	return 0;
}


static int h2c_send_rst_stream(struct h2c *h2c, struct h2s *h2s)
{
	struct buffer *res;
	char str[13];
	int ret;

	/* RFC7540#5.4.2: To avoid looping, an endpoint MUST NOT send a
	 * RST_STREAM in response to a RST_STREAM frame.
	 */
	if (h2c->dft == H2_FT_RST_STREAM) {
		ret = 1;
		goto ignore;
	}

	if (h2c_mux_busy(h2c, h2s)) {
		h2c->flags |= H2_CF_DEM_MBUSY;
		return 0;
	}

	res = h2_get_buf(h2c, &h2c->mbuf);
	if (!res) {
		h2c->flags |= H2_CF_MUX_MALLOC;
		h2c->flags |= H2_CF_DEM_MROOM;
		return 0;
	}

	/* len: 4, type: 3, flags: none */
	memcpy(str, "\x00\x00\x04\x03\x00", 5);

	write_n32(str + 5, h2c->dsi);
	write_n32(str + 9, h2s->errcode);
	ret = b_istput(res, ist2(str, 13));

	if (unlikely(ret <= 0)) {
		if (!ret) {
			h2c->flags |= H2_CF_MUX_MFULL;
			h2c->flags |= H2_CF_DEM_MROOM;
			return 0;
		}
		else {
			h2c_error(h2c, H2_ERR_INTERNAL_ERROR);
			return 0;
		}
	}

 ignore:
	if (h2s->id) {
		h2s->flags |= H2_SF_RST_SENT;
		h2s_close(h2s);
	}

	return ret;
}


static int h2c_frt_recv_preface(struct h2c *h2c)
{
	int ret1;
	int ret2;

	ret1 = b_isteq(&h2c->dbuf, 0, b_data(&h2c->dbuf), ist(H2_CONN_PREFACE));

	if (unlikely(ret1 <= 0)) {
		if (ret1 < 0)
			sess_log(h2c->conn->owner);

		if (ret1 < 0 || conn_xprt_read0_pending(h2c->conn))
			h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
		return 0;
	}

	ret2 = h2c_send_settings(h2c);
	if (ret2 > 0)
		b_del(&h2c->dbuf, ret1);

	return ret2;
}


static inline struct h2s *h2c_st_by_id(struct h2c *h2c, int id)
{
	struct eb32_node *node;

	if (id == 0)
		return (struct h2s *)h2_closed_stream;

	if (id > h2c->max_id)
		return (struct h2s *)h2_idle_stream;

	node = eb32_lookup(&h2c->streams_by_id, id);
	if (!node)
		return (struct h2s *)h2_closed_stream;

	return container_of(node, struct h2s, by_id);
}


static size_t h2s_frt_make_resp_headers(struct h2s *h2s, const struct buffer *buf, size_t ofs, size_t max)
{
	struct http_hdr list[MAX_HTTP_HDR];
	struct h2c *h2c = h2s->h2c;
	struct h1m *h1m = &h2s->h1m;
	struct buffer outbuf;
	union h1_sl sl;
	int es_now = 0;
	int ret = 0;
	int hdr;

	if (h2c_mux_busy(h2c, h2s)) {
		h2s->flags |= H2_SF_BLK_MBUSY;
		return 0;
	}

	if (!h2_get_buf(h2c, &h2c->mbuf)) {
		h2c->flags |= H2_CF_MUX_MALLOC;
		h2s->flags |= H2_SF_BLK_MROOM;
		return 0;
	}

	/* First, try to parse the H1 response and index it into <list>.
	 * NOTE! Since it comes from haproxy, we *know* that a response header
	 * block does not wrap and we can safely read it this way without
	 * having to realign the buffer.
	 */
	ret = h1_headers_to_hdr_list(b_peek(buf, ofs), b_peek(buf, ofs) + max,
	                             list, sizeof(list)/sizeof(list[0]), h1m, &sl);
	if (ret <= 0) {
		/* incomplete or invalid response, this is abnormal coming from
		 * haproxy and may only result in a bad errorfile or bad Lua code
		 * so that won't be fixed, raise an error now.
		 *
		 * FIXME: we should instead add the ability to only return a
		 * 502 bad gateway. But in theory this is not supposed to
		 * happen.
		 */
		h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
		ret = 0;
		goto end;
	}

	h2s->status = sl.st.status;

	/* certain statuses have no body or an empty one, regardless of
	 * what the headers say.
	 */
	if (sl.st.status >= 100 && sl.st.status < 200) {
		h1m->flags &= ~(H1_MF_CLEN | H1_MF_CHNK);
		h1m->curr_len = h1m->body_len = 0;
	}
	else if (sl.st.status == 204 || sl.st.status == 304) {
		/* no contents, claim c-len is present and set to zero */
		h1m->flags &= ~H1_MF_CHNK;
		h1m->flags |=  H1_MF_CLEN;
		h1m->curr_len = h1m->body_len = 0;
	}

	chunk_reset(&outbuf);

	while (1) {
		outbuf.area  = b_tail(&h2c->mbuf);
		outbuf.size = b_contig_space(&h2c->mbuf);
		outbuf.data = 0;

		if (outbuf.size >= 9 || !b_space_wraps(&h2c->mbuf))
			break;
	realign_again:
		b_slow_realign(&h2c->mbuf, trash.area, b_data(&h2c->mbuf));
	}

	if (outbuf.size < 9)
		goto full;

	/* len: 0x000000 (fill later), type: 1(HEADERS), flags: ENDH=4 */
	memcpy(outbuf.area, "\x00\x00\x00\x01\x04", 5);
	write_n32(outbuf.area + 5, h2s->id); // 4 bytes
	outbuf.data = 9;

	/* encode status, which necessarily is the first one */
	if (unlikely(list[0].v.len != 3)) {
		/* this is an unparsable response */
		h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
		ret = 0;
		goto end;
	}

	if (!hpack_encode_str_status(&outbuf, h2s->status, list[0].v)) {
		if (b_space_wraps(&h2c->mbuf))
			goto realign_again;
		goto full;
	}

	/* encode all headers, stop at empty name */
	for (hdr = 1; hdr < sizeof(list)/sizeof(list[0]); hdr++) {
		/* these ones do not exist in H2 and must be dropped. */
		if (isteq(list[hdr].n, ist("connection")) ||
		    isteq(list[hdr].n, ist("proxy-connection")) ||
		    isteq(list[hdr].n, ist("keep-alive")) ||
		    isteq(list[hdr].n, ist("upgrade")) ||
		    isteq(list[hdr].n, ist("transfer-encoding")))
			continue;

		if (isteq(list[hdr].n, ist("")))
			break; // end

		if (!hpack_encode_header(&outbuf, list[hdr].n, list[hdr].v)) {
			/* output full */
			if (b_space_wraps(&h2c->mbuf))
				goto realign_again;
			goto full;
		}
	}

	/* we may need to add END_STREAM */
	if (((h1m->flags & H1_MF_CLEN) && !h1m->body_len) || h2s->cs->flags & CS_FL_SHW)
		es_now = 1;

	/* update the frame's size */
	h2_set_frame_size(outbuf.area, outbuf.data - 9);

	if (es_now)
		outbuf.area[4] |= H2_F_HEADERS_END_STREAM;

	/* consume incoming H1 response */
	max -= ret;

	/* commit the H2 response */
	b_add(&h2c->mbuf, outbuf.data);
	h2s->flags |= H2_SF_HEADERS_SENT;

	if (es_now) {
		// trim any possibly pending data (eg: inconsistent content-length)
		ret += max;

		h1m->state = H1_MSG_DONE;
		h2s->flags |= H2_SF_ES_SENT;
		if (h2s->st == H2_SS_OPEN)
			h2s->st = H2_SS_HLOC;
		else
			h2s_close(h2s);
	}
	else if (h2s->status >= 100 && h2s->status < 200) {
		/* we'll let the caller check if it has more headers to send */
		h1m_init_res(h1m);
		h1m->err_pos = -1; // don't care about errors on the response path
		h2s->h1m.flags |= H1_MF_TOLOWER;
		goto end;
	}

	/* now the h1m state is either H1_MSG_CHUNK_SIZE or H1_MSG_DATA */

 end:
	//fprintf(stderr, "[%d] sent simple H2 response (sid=%d) = %d bytes (%d in, ep=%u, es=%s)\n", h2c->st0, h2s->id, outbuf.len, ret, h1m->err_pos, h1m_state_str(h1m->err_state));
	return ret;
 full:
	h1m_init_res(h1m);
	h1m->err_pos = -1; // don't care about errors on the response path
	h2c->flags |= H2_CF_MUX_MFULL;
	h2s->flags |= H2_SF_BLK_MROOM;
	ret = 0;
	goto end;
}


static int ipddp_delete(struct ipddp_route *rt)
{
        struct ipddp_route **r = &ipddp_route_list;
        struct ipddp_route *tmp;

	spin_lock_bh(&ipddp_route_lock);
        while((tmp = *r) != NULL)
        {
                if(tmp->ip == rt->ip &&
		   tmp->at.s_net == rt->at.s_net &&
		   tmp->at.s_node == rt->at.s_node)
                {
                        *r = tmp->next;
			spin_unlock_bh(&ipddp_route_lock);
                        kfree(tmp);
                        return 0;
                }
                r = &tmp->next;
        }

	spin_unlock_bh(&ipddp_route_lock);
        return -ENOENT;
}


static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
{
	size_t alloc_size, i;

	/* Allocated size is a power of 2 to map sequence numbers
	 * (which may be up to 14 bits) in to a smaller array that is
	 * sized for the negotiated ERTM transmit windows.
	 */
	alloc_size = roundup_pow_of_two(size);

	seq_list->list = kmalloc_array(alloc_size, sizeof(u16), GFP_KERNEL);
	if (!seq_list->list)
		return -ENOMEM;

	seq_list->mask = alloc_size - 1;
	seq_list->head = L2CAP_SEQ_LIST_CLEAR;
	seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
	for (i = 0; i < alloc_size; i++)
		seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;

	return 0;
}


static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
			  struct netlink_ext_ack *extack)
{
	struct net *net = sock_net(skb->sk);
	struct rtcanmsg *r;
	struct cgw_job *gwj;
	struct cf_mod mod;
	struct can_can_gw ccgw;
	u8 limhops = 0;
	int err = 0;

	if (!netlink_capable(skb, CAP_NET_ADMIN))
		return -EPERM;

	if (nlmsg_len(nlh) < sizeof(*r))
		return -EINVAL;

	r = nlmsg_data(nlh);
	if (r->can_family != AF_CAN)
		return -EPFNOSUPPORT;

	/* so far we only support CAN -> CAN routings */
	if (r->gwtype != CGW_TYPE_CAN_CAN)
		return -EINVAL;

	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
	if (err < 0)
		return err;

	if (mod.uid) {

		ASSERT_RTNL();

		/* check for updating an existing job with identical uid */
		hlist_for_each_entry(gwj, &net->can.cgw_list, list) {

			if (gwj->mod.uid != mod.uid)
				continue;

			/* interfaces & filters must be identical */
			if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
				return -EINVAL;

			/* update modifications with disabled softirq & quit */
			local_bh_disable();
			memcpy(&gwj->mod, &mod, sizeof(mod));
			local_bh_enable();
			return 0;
		}
	}

	/* ifindex == 0 is not allowed for job creation */
	if (!ccgw.src_idx || !ccgw.dst_idx)
		return -ENODEV;

	gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
	if (!gwj)
		return -ENOMEM;

	gwj->handled_frames = 0;
	gwj->dropped_frames = 0;
	gwj->deleted_frames = 0;
	gwj->flags = r->flags;
	gwj->gwtype = r->gwtype;
	gwj->limit_hops = limhops;

	/* insert already parsed information */
	memcpy(&gwj->mod, &mod, sizeof(mod));
	memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));

	err = -ENODEV;

	gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);

	if (!gwj->src.dev)
		goto out;

	if (gwj->src.dev->type != ARPHRD_CAN)
		goto out;

	gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);

	if (!gwj->dst.dev)
		goto out;

	if (gwj->dst.dev->type != ARPHRD_CAN)
		goto out;

	ASSERT_RTNL();

	err = cgw_register_filter(net, gwj);
	if (!err)
		hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
out:
	if (err)
		kmem_cache_free(cgw_cache, gwj);

	return err;
}


static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
			  u8 gwtype, void *gwtypeattr, u8 *limhops)
{
	struct nlattr *tb[CGW_MAX+1];
	struct cgw_frame_mod mb;
	int modidx = 0;
	int err = 0;

	/* initialize modification & checksum data space */
	memset(mod, 0, sizeof(*mod));

	err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX,
			  cgw_policy, NULL);
	if (err < 0)
		return err;

	if (tb[CGW_LIM_HOPS]) {
		*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);

		if (*limhops < 1 || *limhops > max_hops)
			return -EINVAL;
	}

	/* check for AND/OR/XOR/SET modifications */

	if (tb[CGW_MOD_AND]) {
		nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);

		canframecpy(&mod->modframe.and, &mb.cf);
		mod->modtype.and = mb.modtype;

		if (mb.modtype & CGW_MOD_ID)
			mod->modfunc[modidx++] = mod_and_id;

		if (mb.modtype & CGW_MOD_DLC)
			mod->modfunc[modidx++] = mod_and_dlc;

		if (mb.modtype & CGW_MOD_DATA)
			mod->modfunc[modidx++] = mod_and_data;
	}

	if (tb[CGW_MOD_OR]) {
		nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);

		canframecpy(&mod->modframe.or, &mb.cf);
		mod->modtype.or = mb.modtype;

		if (mb.modtype & CGW_MOD_ID)
			mod->modfunc[modidx++] = mod_or_id;

		if (mb.modtype & CGW_MOD_DLC)
			mod->modfunc[modidx++] = mod_or_dlc;

		if (mb.modtype & CGW_MOD_DATA)
			mod->modfunc[modidx++] = mod_or_data;
	}

	if (tb[CGW_MOD_XOR]) {
		nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);

		canframecpy(&mod->modframe.xor, &mb.cf);
		mod->modtype.xor = mb.modtype;

		if (mb.modtype & CGW_MOD_ID)
			mod->modfunc[modidx++] = mod_xor_id;

		if (mb.modtype & CGW_MOD_DLC)
			mod->modfunc[modidx++] = mod_xor_dlc;

		if (mb.modtype & CGW_MOD_DATA)
			mod->modfunc[modidx++] = mod_xor_data;
	}

	if (tb[CGW_MOD_SET]) {
		nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);

		canframecpy(&mod->modframe.set, &mb.cf);
		mod->modtype.set = mb.modtype;

		if (mb.modtype & CGW_MOD_ID)
			mod->modfunc[modidx++] = mod_set_id;

		if (mb.modtype & CGW_MOD_DLC)
			mod->modfunc[modidx++] = mod_set_dlc;

		if (mb.modtype & CGW_MOD_DATA)
			mod->modfunc[modidx++] = mod_set_data;
	}

	/* check for checksum operations after CAN frame modifications */
	if (modidx) {

		if (tb[CGW_CS_CRC8]) {
			struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);

			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
						 c->result_idx);
			if (err)
				return err;

			nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
				   CGW_CS_CRC8_LEN);

			/*
			 * select dedicated processing function to reduce
			 * runtime operations in receive hot path.
			 */
			if (c->from_idx < 0 || c->to_idx < 0 ||
			    c->result_idx < 0)
				mod->csumfunc.crc8 = cgw_csum_crc8_rel;
			else if (c->from_idx <= c->to_idx)
				mod->csumfunc.crc8 = cgw_csum_crc8_pos;
			else
				mod->csumfunc.crc8 = cgw_csum_crc8_neg;
		}

		if (tb[CGW_CS_XOR]) {
			struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);

			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
						 c->result_idx);
			if (err)
				return err;

			nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
				   CGW_CS_XOR_LEN);

			/*
			 * select dedicated processing function to reduce
			 * runtime operations in receive hot path.
			 */
			if (c->from_idx < 0 || c->to_idx < 0 ||
			    c->result_idx < 0)
				mod->csumfunc.xor = cgw_csum_xor_rel;
			else if (c->from_idx <= c->to_idx)
				mod->csumfunc.xor = cgw_csum_xor_pos;
			else
				mod->csumfunc.xor = cgw_csum_xor_neg;
		}

		if (tb[CGW_MOD_UID]) {
			nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
		}
	}

	if (gwtype == CGW_TYPE_CAN_CAN) {

		/* check CGW_TYPE_CAN_CAN specific attributes */

		struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
		memset(ccgw, 0, sizeof(*ccgw));

		/* check for can_filter in attributes */
		if (tb[CGW_FILTER])
			nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
				   sizeof(struct can_filter));

		err = -ENODEV;

		/* specifying two interfaces is mandatory */
		if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
			return err;

		ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
		ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);

		/* both indices set to 0 for flushing all routing entries */
		if (!ccgw->src_idx && !ccgw->dst_idx)
			return 0;

		/* only one index set to 0 is an error */
		if (!ccgw->src_idx || !ccgw->dst_idx)
			return err;
	}

	/* add the checks for other gwtypes here */

	return 0;
}


static void cgw_remove_all_jobs(struct net *net)
{
	struct cgw_job *gwj = NULL;
	struct hlist_node *nx;

	ASSERT_RTNL();

	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
		hlist_del(&gwj->list);
		cgw_unregister_filter(net, gwj);
		kmem_cache_free(cgw_cache, gwj);
	}
}


cmp_func_intel (const void *elem1, const void *elem2)
{
	return cmp_func ((const unsigned char *) elem1,
			 (const unsigned char *) elem2, EXIF_BYTE_ORDER_INTEL);
}


exif_data_option_get_name (ExifDataOption o)
{
	unsigned int i;

	for (i = 0; exif_data_option[i].name; i++)
		if (exif_data_option[i].option == o)
			break;
	return _(exif_data_option[i].name);
}


cookie_write (void *cookie, const void *buffer_arg, size_t size)
{
  const char *buffer = buffer_arg;
  cookie_t c = cookie;
  int nwritten = 0;

#if HTTP_USE_NTBTLS
  if (c->use_tls && c->session && c->session->tls_session)
    {
      estream_t in, out;

      ntbtls_get_stream (c->session->tls_session, &in, &out);
      if (size == 0)
        es_fflush (out);
      else
        nwritten = es_fwrite (buffer, 1, size, out);
      if (opt_debug)
        log_debug ("TLS network write: %d/%zu\n", nwritten, size);
    }
  else
#elif HTTP_USE_GNUTLS
  if (c->use_tls && c->session && c->session->tls_session)
    {
      int nleft = size;
      while (nleft > 0)
        {
          nwritten = gnutls_record_send (c->session->tls_session,
                                         buffer, nleft);
          if (nwritten <= 0)
            {
              if (nwritten == GNUTLS_E_INTERRUPTED)
                continue;
              if (nwritten == GNUTLS_E_AGAIN)
                {
                  struct timeval tv;

                  tv.tv_sec = 0;
                  tv.tv_usec = 50000;
                  my_select (0, NULL, NULL, NULL, &tv);
                  continue;
                }
              log_info ("TLS network write failed: %s\n",
                        gnutls_strerror (nwritten));
              gpg_err_set_errno (EIO);
              return -1;
            }
          nleft -= nwritten;
          buffer += nwritten;
        }
    }
  else
#endif /*HTTP_USE_GNUTLS*/
    {
      if ( write_server (c->sock->fd, buffer, size) )
        {
          gpg_err_set_errno (EIO);
          nwritten = -1;
        }
      else
        nwritten = size;
    }

  return (gpgrt_ssize_t)nwritten;
}


create_new_hostinfo (const char *name)
{
  hostinfo_t hi, *newtable;
  int newsize;
  int idx, rc;

  hi = xtrymalloc (sizeof *hi + strlen (name));
  if (!hi)
    return -1;
  strcpy (hi->name, name);
  hi->pool = NULL;
  hi->pool_len = 0;
  hi->pool_size = 0;
  hi->poolidx = -1;
  hi->lastused = (time_t)(-1);
  hi->lastfail = (time_t)(-1);
  hi->v4 = 0;
  hi->v6 = 0;
  hi->onion = 0;
  hi->dead = 0;
  hi->did_a_lookup = 0;
  hi->did_srv_lookup = 0;
  hi->iporname_valid = 0;
  hi->died_at = 0;
  hi->cname = NULL;
  hi->iporname = NULL;
  hi->port[KS_PROTOCOL_HKP] = 0;
  hi->port[KS_PROTOCOL_HKPS] = 0;

  /* Add it to the hosttable. */
  for (idx=0; idx < hosttable_size; idx++)
    if (!hosttable[idx])
      {
        hosttable[idx] = hi;
        return idx;
      }
  /* Need to extend the hosttable.  */
  newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE;
  newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable);
  if (!newtable)
    {
      xfree (hi);
      return -1;
    }
  hosttable = newtable;
  idx = hosttable_size;
  hosttable_size = newsize;
  rc = idx;
  hosttable[idx++] = hi;
  while (idx < hosttable_size)
    hosttable[idx++] = NULL;

  return rc;
}


select_random_host (hostinfo_t hi)
{
  int *tbl;
  size_t tblsize;
  int pidx, idx;

  /* We create a new table so that we randomly select only from
     currently alive hosts.  */
  for (idx = 0, tblsize = 0;
       idx < hi->pool_len && (pidx = hi->pool[idx]) != -1;
       idx++)
    if (hosttable[pidx] && !hosttable[pidx]->dead)
      tblsize++;
  if (!tblsize)
    return -1; /* No hosts.  */

  tbl = xtrymalloc (tblsize * sizeof *tbl);
  if (!tbl)
    return -1;
  for (idx = 0, tblsize = 0;
       idx < hi->pool_len && (pidx = hi->pool[idx]) != -1;
       idx++)
    if (hosttable[pidx] && !hosttable[pidx]->dead)
      tbl[tblsize++] = pidx;

  if (tblsize == 1)  /* Save a get_uint_nonce.  */
    pidx = tbl[0];
  else
    pidx = tbl[get_uint_nonce () % tblsize];

  xfree (tbl);
  return pidx;
}


my_http_tls_verify_cb (void *opaque,
                       http_t http,
                       http_session_t session,
                       unsigned int http_flags,
                       void *tls_context)
{
  gpg_error_t err;
  int idx;
  ksba_cert_t cert;
  ksba_cert_t hostcert = NULL;

  (void)opaque;
  (void)http;
  (void)session;
  (void)http_flags;

  /* Get the peer's certs fron ntbtls.  */
  for (idx = 0;
       (cert = ntbtls_x509_get_peer_cert (tls_context, idx)); idx++)
    {
      if (!idx)
        {
          log_info ("Received host certificate\n");
          hostcert = cert;
        }
      else
        {

          log_info ("Received additional certificate\n");
          ksba_cert_release (cert);
        }
    }
  if (!idx)
    {
      err  = gpg_error (GPG_ERR_MISSING_CERT);
      goto leave;
    }

  err = 0;

 leave:
  ksba_cert_release (hostcert);
  log_info ("my_http_tls_verify_cb returns: %s\n", gpg_strerror (err));
  return err;
}


fp_onclose_notification (estream_t stream, void *opaque)
{
  http_t hd = opaque;

  log_assert (hd->magic == HTTP_CONTEXT_MAGIC);
  if (hd->fp_read && hd->fp_read == stream)
    hd->fp_read = NULL;
  else if (hd->fp_write && hd->fp_write == stream)
    hd->fp_write = NULL;
}


http_close (http_t hd, int keep_read_stream)
{
  if (!hd)
    return;

  log_assert (hd->magic == HTTP_CONTEXT_MAGIC);

  /* First remove the close notifications for the streams.  */
  if (hd->fp_read)
    es_onclose (hd->fp_read, 0, fp_onclose_notification, hd);
  if (hd->fp_write)
    es_onclose (hd->fp_write, 0, fp_onclose_notification, hd);

  /* Now we can close the streams.  */
  my_socket_unref (hd->sock, NULL, NULL);
  if (hd->fp_read && !keep_read_stream)
    es_fclose (hd->fp_read);
  if (hd->fp_write)
    es_fclose (hd->fp_write);
  http_session_unref (hd->session);
  hd->magic = 0xdeadbeef;
  http_release_parsed_uri (hd->uri);
  while (hd->headers)
    {
      header_t tmp = hd->headers->next;
      xfree (hd->headers->value);
      xfree (hd->headers);
      hd->headers = tmp;
    }
  xfree (hd->buffer);
  xfree (hd);
}


my_gnutls_read (gnutls_transport_ptr_t ptr, void *buffer, size_t size)
{
  my_socket_t sock = ptr;
#if USE_NPTH
  return npth_read (sock->fd, buffer, size);
#else
  return read (sock->fd, buffer, size);
#endif
}


HttpRequestEntry::getHttpHeaderProcessor() const
{
  return proc_;
}


static int oplgdt(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if ( op->operands[0].type & OT_MEMORY ) {
			data[l++] = 0x0f;
			data[l++] = 0x01;
			data[l++] = 0x10 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opsbb(RAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
		    op->operands[0].type & OT_WORD) {
			return process_16bit_group_1 (a, data, op, 0x18);
		}
		if (!is_al_reg (&op->operands[0])) {
			return process_group_1 (a, data, op);
		}
	}
	return process_1byte_op (a, data, op, 0x18);
}


static int opfdivp(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xf9;
		break;
	case 2:
		if ( op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
		     op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0 ) {
			data[l++] = 0xde;
			data[l++] = 0xf8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opfstcw(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if ( op->operands[0].type & OT_MEMORY &&
		     op->operands[0].type & OT_WORD ) {
			data[l++] = 0x9b;
			data[l++] = 0xd9;
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opidiv(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;

	if ( op->operands[0].type & OT_QWORD ) {
		data[l++] = 0x48;
	}
	switch (op->operands_count) {
	case 1:
		if ( op->operands[0].type & OT_WORD ) {
			data[l++] = 0x66;
		}
		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			data[l++] = 0xf7;
		}
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xf8 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opfimul(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if ( op->operands[0].type & OT_MEMORY ) {
			if ( op->operands[0].type & OT_DWORD ) {
				data[l++] = 0xda;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if ( op->operands[0].type & OT_WORD ) {
				data[l++] = 0xde;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opfisttp(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if ( op->operands[0].type & OT_MEMORY ) {
			if ( op->operands[0].type & OT_WORD ) {
				data[l++] = 0xdf;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if ( op->operands[0].type & OT_DWORD ) {
				data[l++] = 0xdb;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if ( op->operands[0].type & OT_QWORD ) {
				data[l++] = 0xdd;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opfucom(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if ( op->operands[0].type & OT_FPUREG & ~OT_REGALL ) {
			data[l++] = 0xdd;
			data[l++] = 0xe0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	case 0:
		data[l++] = 0xdd;
		data[l++] = 0xe1;
		break;
	default:
		return -1;
	}
	return l;
}


static int opadc(RAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
		    op->operands[0].type & OT_WORD) {
			return process_16bit_group_1 (a, data, op, 0x10);
		}
		if (!is_al_reg (&op->operands[0])) {
			return process_group_1 (a, data, op);
		}
	}
	return process_1byte_op (a, data, op, 0x10);
}


static int oples(RAsm *a, ut8* data, const Opcode *op) {
	int l = 0;
	int offset = 0;
	int mod = 0;

	if (op->operands[1].type & OT_MEMORY) {
		data[l++] = 0xc4;
		if (op->operands[1].type & OT_GPREG) {
			offset = op->operands[1].offset * op->operands[1].offset_sign;
			if (offset) {
				mod = 1;
				if (offset > 128 || offset < -128) {
					mod = 2;
				}
			}
			data[l++] = mod << 6 | op->operands[0].reg << 3 | op->operands[1].regs[0];
			if (mod) {
				data[l++] = offset;
				if (mod > 1) {
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			}
		} else {
			offset = op->operands[1].offset * op->operands[1].offset_sign;
			data[l++] = 0x05;
			data[l++] = offset;
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
		}
	}
	return l;
}


static int opsldt(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if ( a->bits == 64 ) {
			data[l++] = 0x48;
		}
		data[l++] = 0x0f;
		data[l++] = 0x00;
		if ( op->operands[0].type & OT_MEMORY ) {
			data[l++] = 0x00 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xc0 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}


static int opor(RAsm *a, ut8 * data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
		    op->operands[0].type & OT_WORD) {
			return process_16bit_group_1 (a, data, op, 0x08);
		}
		if (!is_al_reg (&op->operands[0])) {
			return process_group_1 (a, data, op);
		}
	}
	return process_1byte_op (a, data, op, 0x08);
}


static ut32 stp(ArmOp *op, int k) {
	ut32 data = UT32_MAX;

	if (op->operands[3].immediate & 0x7) {
		return data;
	}
	if (k == 0x000040a9 && (op->operands[0].reg == op->operands[1].reg)) {
		return data;
	}

	data = k;
	data += op->operands[0].reg << 24;
	data += op->operands[1].reg << 18;
	data += (op->operands[2].reg & 0x7) << 29;
	data += (op->operands[2].reg >> 3) << 16;
	data += (op->operands[3].immediate & 0x8) << 20;
	data += (op->operands[3].immediate >> 4) << 8;
	return data;
}


static ut32 adrp(ArmOp *op, ut64 addr, ut32 k) { //, int reg, ut64 dst) {
	ut64 at = 0LL;
	ut32 data = k;
	if (op->operands[0].type == ARM_GPR) {
		data += ((op->operands[0].reg & 0xff) << 24);
	} else {
		eprintf ("Usage: adrp x0, addr\n");
		return UT32_MAX;
	}
	if (op->operands[1].type == ARM_CONSTANT) {
		// XXX what about negative values?
		at = op->operands[1].immediate - addr;
		at /= 4;
	} else {
		eprintf ("Usage: adrp, x0, addr\n");
		return UT32_MAX;
	}
	ut8 b0 = at;
	ut8 b1 = (at >> 3) & 0xff;

#if 0
	ut8 b2 = (at >> (8 + 7)) & 0xff;
	data += b0 << 29;
	data += b1 << 16;
	data += b2 << 24;
#endif
	data += b0 << 16;
	data += b1 << 8;
	return data;
}


static void esil_init (RCore *core) {
	const char *pc = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
	int noNULL = r_config_get_i (core->config, "esil.noNULL");
	opc = r_reg_getv (core->anal->reg, pc);
	if (!opc || opc==UT64_MAX) {
		opc = core->offset;
	}
	if (!core->anal->esil) {
		int iotrap = r_config_get_i (core->config, "esil.iotrap");
		ut64 stackSize = r_config_get_i (core->config, "esil.stack.size");
		unsigned int addrsize = r_config_get_i (core->config, "esil.addr.size");
		if (!(core->anal->esil = r_anal_esil_new (stackSize, iotrap, addrsize))) {
			R_FREE (regstate);
			return;
		}
		r_anal_esil_setup (core->anal->esil, core->anal, 0, 0, noNULL);
	}
	free (regstate);
	regstate = r_reg_arena_peek (core->anal->reg);
}


static int cmd_anal(void *data, const char *input) {
	const char *r;
	RCore *core = (RCore *)data;
	ut32 tbs = core->blocksize;

	switch (input[0]) {
	case 'p': // "ap"
		{
			const ut8 *prelude = (const ut8*)"\xe9\x2d"; //:fffff000";
			const int prelude_sz = 2;
			const int bufsz = 4096;
			ut8 *buf = calloc (1, bufsz);
			ut64 off = core->offset;
			if (input[1] == ' ') {
				off = r_num_math (core->num, input+1);
				r_io_read_at (core->io, off - bufsz + prelude_sz, buf, bufsz);
			} else {
				r_io_read_at (core->io, off - bufsz + prelude_sz, buf, bufsz);
			}
			//const char *prelude = "\x2d\xe9\xf0\x47"; //:fffff000";
			r_mem_reverse (buf, bufsz);
			//r_print_hexdump (NULL, off, buf, bufsz, 16, -16);
			const ut8 *pos = r_mem_mem (buf, bufsz, prelude, prelude_sz);
			if (pos) {
				int delta = (size_t)(pos - buf);
				eprintf ("POS = %d\n", delta);
				eprintf ("HIT = 0x%"PFMT64x"\n", off - delta);
				r_cons_printf ("0x%08"PFMT64x"\n", off - delta);
			} else {
				eprintf ("Cannot find prelude\n");
			}
			free (buf);
		}
		break;
	case '8':
		{
			ut8 *buf = malloc (strlen (input) + 1);
			if (buf) {
				int len = r_hex_str2bin (input + 1, buf);
				if (len > 0) {
					core_anal_bytes (core, buf, len, 0, input[1]);
				}
				free (buf);
			}
		}
		break;
	case 'b':
		if (input[1] == 'b') { // "abb"
			core_anal_bbs (core, input + 2);
		} else if (input[1] == 'r') { // "abr"
			core_anal_bbs_range (core, input + 2);
		} else if (input[1] == 't') {
			cmd_anal_abt (core, input+2);
		} else if (input[1] == 'j') { // "abj"
			anal_fcn_list_bb (core, input + 1, false);
		} else if (input[1] == ' ' || !input[1]) {
			// find block
			ut64 addr = core->offset;
			if (input[1]) {
				addr = r_num_math (core->num, input + 1);
			}
			r_core_cmdf (core, "afbi @ 0x%"PFMT64x, addr);
		} else {
			r_core_cmd_help (core, help_msg_ab);
		}
		break;
	case 'L': return r_core_cmd0 (core, "e asm.arch=??"); break;
	case 'i': cmd_anal_info (core, input + 1); break; // "ai"
	case 'r': cmd_anal_reg (core, input + 1); break;  // "ar"
	case 'e': cmd_anal_esil (core, input + 1); break; // "ae"
	case 'o': cmd_anal_opcode (core, input + 1); break; // "ao"
	case 'O': cmd_anal_bytes (core, input + 1); break; // "aO"
	case 'F': // "aF"
		r_core_anal_fcn (core, core->offset, UT64_MAX, R_ANAL_REF_TYPE_NULL, 1);
		break;
	case 'f': // "af"
		{
		int res = cmd_anal_fcn (core, input);
		run_pending_anal (core);
		if (!res) {
			return false;
		}
		}
		break;
	case 'n': // 'an'
		{
		const char *name = NULL;
		bool use_json = false;

		if (input[1] == 'j') {
			use_json = true;
			input++;
		}

		if (input[1] == ' ') {
			name = input + 1;
			while (name[0] == ' ') {
				name++;
			}
			char *end = strchr (name, ' ');
			if (end) {
				*end = '\0';
			}
			if (*name == '\0') {
				name = NULL;
			}
		}

		cmd_an (core, use_json, name);
		}
		break;
	case 'g': // "ag"
		cmd_anal_graph (core, input + 1);
		break;
	case 's': // "as"
		cmd_anal_syscall (core, input + 1);
		break;
	case 'v': // "av"
		cmd_anal_virtual_functions (core, input + 1);
		break;
	case 'x': // "ax"
		if (!cmd_anal_refs (core, input + 1)) {
			return false;
		}
		break;
	case 'a': // "aa"
		if (!cmd_anal_all (core, input + 1)) {
			return false;
		}
		break;
	case 'c': // "ac"
		{
		RList *hooks;
		RListIter *iter;
		RAnalCycleHook *hook;
		char *instr_tmp = NULL;
		int ccl = input[1]? r_num_math (core->num, &input[2]): 0; //get cycles to look for
		int cr = r_config_get_i (core->config, "asm.cmt.right");
		int fun = r_config_get_i (core->config, "asm.functions");
		int li = r_config_get_i (core->config, "asm.lines");
		int xr = r_config_get_i (core->config, "asm.xrefs");

		r_config_set_i (core->config, "asm.cmt.right", true);
		r_config_set_i (core->config, "asm.functions", false);
		r_config_set_i (core->config, "asm.lines", false);
		r_config_set_i (core->config, "asm.xrefs", false);

		hooks = r_core_anal_cycles (core, ccl); //analyse
		r_cons_clear_line (1);
		r_list_foreach (hooks, iter, hook) {
			instr_tmp = r_core_disassemble_instr (core, hook->addr, 1);
			r_cons_printf ("After %4i cycles:\t%s", (ccl - hook->cycles), instr_tmp);
			r_cons_flush ();
			free (instr_tmp);
		}
		r_list_free (hooks);

		r_config_set_i (core->config, "asm.cmt.right", cr); //reset settings
		r_config_set_i (core->config, "asm.functions", fun);
		r_config_set_i (core->config, "asm.lines", li);
		r_config_set_i (core->config, "asm.xrefs", xr);
		}
		break;
	case 'd': // "ad"
		switch (input[1]) {
		case 'f': // "adf"
			if (input[2] == 'g') {
				anal_fcn_data_gaps (core, input + 1);
			} else {
				anal_fcn_data (core, input + 1);
			}
			break;
		case 't': // "adt"
			cmd_anal_trampoline (core, input + 2);
			break;
		case ' ': { // "ad"
			const int default_depth = 1;
			const char *p;
			int a, b;
			a = r_num_math (core->num, input + 2);
			p = strchr (input + 2, ' ');
			b = p? r_num_math (core->num, p + 1): default_depth;
			if (a < 1) {
				a = 1;
			}
			if (b < 1) {
				b = 1;
			}
			r_core_anal_data (core, core->offset, a, b, 0);
		} break;
		case 'k': // "adk"
			r = r_anal_data_kind (core->anal,
					core->offset, core->block, core->blocksize);
			r_cons_println (r);
			break;
		case '\0': // "ad"
			r_core_anal_data (core, core->offset, 2 + (core->blocksize / 4), 1, 0);
			break;
		case '4': // "ad4"
			r_core_anal_data (core, core->offset, 2 + (core->blocksize / 4), 1, 4);
			break;
		case '8': // "ad8"
			r_core_anal_data (core, core->offset, 2 + (core->blocksize / 4), 1, 8);
			break;
		default:
			r_core_cmd_help (core, help_msg_ad);
			break;
		}
		break;
	case 'h': // "ah"
		cmd_anal_hint (core, input + 1);
		break;
	case '!': // "a!"
		if (core->anal && core->anal->cur && core->anal->cur->cmd_ext) {
			return core->anal->cur->cmd_ext (core->anal, input + 1);
		} else {
			r_cons_printf ("No plugins for this analysis plugin\n");
		}
		break;
	default:
		r_core_cmd_help (core, help_msg_a);
#if 0
		r_cons_printf ("Examples:\n"
			" f ts @ `S*~text:0[3]`; f t @ section..text\n"
			" f ds @ `S*~data:0[3]`; f d @ section..data\n"
			" .ad t t+ts @ d:ds\n",
			NULL);
#endif
		break;
	}
	if (tbs != core->blocksize) {
		r_core_block_size (core, tbs);
	}
	if (r_cons_is_breaked ()) {
		r_cons_clear_line (1);
	}
	return 0;
}


static void agraph_print_node_gml(RANode *n, void *user) {
	r_cons_printf ("  node [\n"
		"    id  %d\n"
		"    label  \"%s\"\n"
		"  ]\n", n->gnode->idx, n->title);
}


static int var_cmd(RCore *core, const char *str) {
	char *p, *ostr;
	int delta, type = *str, res = true;
	RAnalVar *v1;
	RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
	ostr = p = NULL;
	if (!str[0]) {
		// "afv"
		if (fcn) {
			r_core_cmd0 (core, "afvs");
			r_core_cmd0 (core, "afvb");
			r_core_cmd0 (core, "afvr");
			return true;
		}
		eprintf ("Cannot find function\n");
		return false;
	}
	if (str[0] == 'j') {
		// "afvj"
		if (fcn) {
			r_cons_printf ("{\"sp\":");
			r_core_cmd0 (core, "afvsj");
			r_cons_printf (",\"bp\":");
			r_core_cmd0 (core, "afvbj");
			r_cons_printf (",\"reg\":");
			r_core_cmd0 (core, "afvrj");
			r_cons_printf ("}\n");
			return true;
		}
		eprintf ("Cannot find function\n");
		return false;
	}
	if (!str[0] || str[1] == '?'|| str[0] == '?') {
		var_help (core, *str);
		return res;
	}
	if (!fcn) {
		eprintf ("Cannot find function in 0x%08"PFMT64x"\n", core->offset);
		return false;
	}
	ostr = p = strdup (str);
	/* Variable access CFvs = set fun var */
	switch (str[0]) {
	case '-':
		// "afv"
		if (fcn) {
			r_core_cmdf (core, "afvs-%s", str + 1);
			r_core_cmdf (core, "afvb-%s", str + 1);
			r_core_cmdf (core, "afvr-%s", str + 1);
			return true;
		}
		eprintf ("Cannot find function\n");
		return false;
	case 'R': // "afvR"
	case 'W': // "afvW"
	case '*': // "afv*"
		{
			char *name = r_str_trim_head (strchr (ostr, ' '));
			list_vars (core, fcn, str[0], name);
			return true;
		}
	case 'a': // "afva"
		r_anal_var_delete_all (core->anal, fcn->addr, R_ANAL_VAR_KIND_REG);
		r_anal_var_delete_all (core->anal, fcn->addr, R_ANAL_VAR_KIND_BPV);
		r_anal_var_delete_all (core->anal, fcn->addr, R_ANAL_VAR_KIND_SPV);
		r_core_recover_vars (core, fcn, false);
		free (p);
		return true;
	case 'n':
		if (str[1]) { // "afvn"
			RAnalOp *op = r_core_anal_op (core, core->offset, R_ANAL_OP_MASK_BASIC);
			char *new_name = r_str_trim_head (strchr (ostr, ' '));
			if (!new_name) {
				r_anal_op_free (op);
				free (ostr);
				return false;
			}
			char *old_name = strchr (new_name, ' ');
			if (!old_name) {
				if (op && op->var) {
					old_name = op->var->name;
				} else {
					eprintf ("Cannot find var @ 0x%08"PFMT64x"\n", core->offset);
					r_anal_op_free (op);
					free (ostr);
					return false;
				}
			} else {
				*old_name++ = 0;
				r_str_trim (old_name);
			}
			v1 = r_anal_var_get_byname (core->anal, fcn->addr, old_name);
			if (v1) {
				r_anal_var_rename (core->anal, fcn->addr, R_ANAL_VAR_SCOPE_LOCAL,
						v1->kind, old_name, new_name, true);
				r_anal_var_free (v1);
			} else {
				eprintf ("Cant find var by name\n");
			}
			r_anal_op_free (op);
			free (ostr);
		} else {
			RListIter *iter;
			RAnalVar *v;
			RList *list = r_anal_var_all_list (core->anal, fcn);
			r_list_foreach (list, iter, v) {
				r_cons_printf ("%s\n", v->name);
			}
		}
		return true;
	case 'd': // "afvd"
		if (str[1]) {
			p = r_str_trim (strchr (ostr, ' '));
			if (!p) {
				free (ostr);
				return false;
			}
			v1 = r_anal_var_get_byname (core->anal, fcn->addr, p);
			if (!v1) {
				free (ostr);
				return false;
			}
			r_anal_var_display (core->anal, v1->delta, v1->kind, v1->type);
			r_anal_var_free (v1);
			free (ostr);
		} else {
			RListIter *iter;
			RAnalVar *p;
			RList *list = r_anal_var_all_list (core->anal, fcn);
			r_list_foreach (list, iter, p) {
				char *a = r_core_cmd_strf (core, ".afvd %s", p->name);
				if ((a && !*a) || !a) {
					free (a);
					a = strdup ("\n");
				}
				r_cons_printf ("%s %s = %s", p->isarg ? "arg": "var", p->name, a);
				free (a);
			}
			r_list_free (list);
		}
		return true;
	case 't':{ // "afvt"
		p = strchr (ostr, ' ');
		if (!p++) {
			free (ostr);
			return false;
		}

		char *type = strchr (p, ' ');
		if (!type) {
			free (ostr);
			return false;
		}
		*type++ = 0;
		v1 = r_anal_var_get_byname (core->anal, fcn->addr, p);
		if (!v1) {
			eprintf ("Cant find get by name %s\n", p);
			free (ostr);
			return false;
		}
		r_anal_var_retype (core->anal, fcn->addr,
			R_ANAL_VAR_SCOPE_LOCAL, -1, v1->kind, type, -1, v1->isarg, p);
		r_anal_var_free (v1);
		free (ostr);
		return true;

	}
	}
	switch (str[1]) { // afv[bsr]
	case '\0':
	case '*':
	case 'j':
		r_anal_var_list_show (core->anal, fcn, type, str[1]);
		if (str[1] == 'j') {
			r_cons_print ("\n");
		}
		break;
	case '.':
		r_anal_var_list_show (core->anal, fcn, core->offset, 0);
		break;
	case '-': // "afv[bsr]-"
		if (str[2] == '*') {
			r_anal_var_delete_all (core->anal, fcn->addr, type);
		} else {
			if (IS_DIGIT (str[2])) {
				r_anal_var_delete (core->anal, fcn->addr,
						type, 1, (int)r_num_math (core->num, str + 1));
			} else {
				char *name = r_str_trim ( strdup (str + 2));
				r_anal_var_delete_byname (core->anal, fcn, type, name);
				free (name);
			}
		}
		break;
	case 'd':
		eprintf ("This command is deprecated, use afvd instead\n");
		break;
	case 't':
		eprintf ("This command is deprecated use afvt instead\n");
		break;
	case 's':
	case 'g':
		if (str[2] != '\0') {
			int rw = 0; // 0 = read, 1 = write
			int idx = r_num_math (core->num, str + 2);
			char *vaddr;
			char *p = r_str_trim (strchr (ostr, ' '));
			if (!p) {
				var_help (core, type);
				break;
			}
			ut64 addr = core->offset;
			if ((vaddr = strchr (p , ' '))) {
				addr = r_num_math (core->num, vaddr);
			}
			RAnalVar *var = r_anal_var_get (core->anal, fcn->addr,
							str[0], R_ANAL_VAR_SCOPE_LOCAL, idx);
			if (!var) {
				eprintf ("Cannot find variable with delta %d\n", idx);
				res = false;
				break;
			}
			rw = (str[1] == 'g')? 0: 1;
			r_anal_var_access (core->anal, fcn->addr, str[0],
					R_ANAL_VAR_SCOPE_LOCAL, idx, rw, addr);
			r_anal_var_free (var);
		} else {
			eprintf ("Missing argument\n");
		}
		break;
	case ' ': {
		const char *name;
		char *vartype;
		bool isarg = false;
		int size = 4;
		int scope = 1;
		for (str++; *str == ' ';) str++;
		p = strchr (str, ' ');
		if (!p) {
			var_help (core, type);
			break;
		}
		*p++ = 0;
		if (type == 'r') { //registers
			RRegItem *i = r_reg_get (core->anal->reg, str, -1);
			if (!i) {
				eprintf ("Register not found");
				break;
			}
			delta = i->index;
			isarg = true;
		} else {
			delta = r_num_math (core->num, str);
		}
		name = p;
		if (!name) {
			eprintf ("Missing name\n");
			break;
		}
		vartype = strchr (name, ' ');
		if (!vartype) {
			vartype = "int";
		} else {
			*vartype++ = 0;
		}
		if ((type == 'b') && delta > 0) {
			isarg = true;
		} else if ((type == 's') && delta > fcn->maxstack) {
			isarg = true;
		}
		r_anal_var_add (core->anal, fcn->addr,scope,
				delta, type, vartype,
				size, isarg, name);
		}
		break;
	};

	free (ostr);
	return res;
}


static void __anal_reg_list(RCore *core, int type, int bits, char mode) {
	RReg *hack = core->dbg->reg;
	const char *use_color;
	int use_colors = r_config_get_i (core->config, "scr.color");
	if (use_colors) {
#undef ConsP
#define ConsP(x) (core->cons && core->cons->pal.x)? core->cons->pal.x
		use_color = ConsP (creg) : Color_BWHITE;
	} else {
		use_color = NULL;
	}
	if (bits < 0) {
		// TODO Change the `size` argument of r_debug_reg_list to use -1 for any and 0 for anal->bits
		bits = 0;
	} else if (!bits) {
		bits = core->anal->bits;
	}
	int mode2 = mode;
	if (core->anal) {
		core->dbg->reg = core->anal->reg;
		if (core->anal->cur && core->anal->cur->arch) {
			/* workaround for thumb */
			if (!strcmp (core->anal->cur->arch, "arm") && bits == 16) {
				bits = 32;
			}
			/* workaround for 6502 */
			if (!strcmp (core->anal->cur->arch, "6502") && bits == 8) {
				mode2 = mode == 'j' ? 'J' : mode;
				if (mode == 'j') {
					r_cons_printf ("{");
				}
				r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, 16, mode2, use_color); // XXX detect which one is current usage
				if (mode == 'j') {
					r_cons_printf (",");
				}
			}
			if (!strcmp (core->anal->cur->arch, "avr") && bits == 8) {
				mode2 = mode == 'j' ? 'J' : mode;
				if (mode == 'j') {
					r_cons_printf ("{");
				}
				r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, 16, mode2, use_color); // XXX detect which one is current usage
				if (mode == 'j') {
					r_cons_printf (",");
				}
			}
		}
	}

	if (mode == '=') {
		int pcbits = 0;
		const char *pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
		RRegItem *reg = r_reg_get (core->anal->reg, pcname, 0);
		if (bits != reg->size) {
			pcbits = reg->size;
		}
		if (pcbits) {
			r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, pcbits, 2, use_color); // XXX detect which one is current usage
		}
	}
	r_debug_reg_list (core->dbg, type, bits, mode2, use_color);
	if (mode2 == 'J') {
		r_cons_print ("}\n");
	}
	core->dbg->reg = hack;
}


static const char *syscallNumber(int n) {
	return sdb_fmt (n > 1000 ? "0x%x" : "%d", n);
}


static int myregread(RAnalEsil *esil, const char *name, ut64 *val, int *len) {
	AeaStats *stats = esil->user;
	if (!IS_DIGIT (*name)) {
		if (!contains (stats->inputregs, name)) {
			if (!contains (stats->regwrite, name)) {
				r_list_push (stats->inputregs, strdup (name));
			}
		}
		if (!contains (stats->regs, name)) {
			r_list_push (stats->regs, strdup (name));
		}
		if (!contains (stats->regread, name)) {
			r_list_push (stats->regread, strdup (name));
		}
	}
	return 0;
}


static int _aeli_iter(dictkv* kv, void* ud) {
	RAnalEsilInterrupt* interrupt = kv->u;
	r_cons_printf ("%3x: %s\n", kv->k, interrupt->handler->name ? interrupt->handler->name : _handler_no_name);
	return 0;
}


R_API char *cmd_syscall_dostr(RCore *core, int n, ut64 addr) {
	char *res = NULL;
	int i;
	char str[64];
	int defVector = r_syscall_get_swi (core->anal->syscall);
	if (defVector > 0) {
		n = -1;
	}
	if (n == -1) {
		n = (int)r_debug_reg_get (core->dbg, "oeax");
		if (!n || n == -1) {
			const char *a0 = r_reg_get_name (core->anal->reg, R_REG_NAME_SN);
			n = (a0 == NULL)? -1: (int)r_debug_reg_get (core->dbg, a0);
		}
	}
	RSyscallItem *item = (defVector > 0)
		? r_syscall_get (core->anal->syscall, n, -1)
		: r_syscall_get (core->anal->syscall, 0, n);
	if (!item) {
		res = r_str_appendf (res, "%s = unknown ()", syscallNumber (n));
		return res;
	}

	res = r_str_appendf (res, "%s = %s (", syscallNumber (item->num), item->name);
	// TODO: move this to r_syscall
	//TODO replace the hardcoded CC with the sdb ones
	for (i = 0; i < item->args; i++) {
		// XXX this is a hack to make syscall args work on x86-32 and x86-64
		// we need to shift sn first.. which is bad, but needs to be redesigned
		int regidx = i;
		if (core->assembler->bits == 32) {
			regidx++;
		}
		ut64 arg = r_debug_arg_get (core->dbg, R_ANAL_CC_TYPE_FASTCALL, regidx);
		//r_cons_printf ("(%d:0x%"PFMT64x")\n", i, arg);
		if (item->sargs) {
			switch (item->sargs[i]) {
			case 'p': // pointer
				res = r_str_appendf (res, "0x%08" PFMT64x "", arg);
				break;
			case 'i':
				res = r_str_appendf (res, "%" PFMT64u "", arg);
				break;
			case 'z':
				memset (str, 0, sizeof (str));
				r_io_read_at (core->io, arg, (ut8 *)str, sizeof (str) - 1);
				r_str_filter (str, strlen (str));
				res = r_str_appendf (res, "\"%s\"", str);
				break;
			case 'Z': {
				//TODO replace the hardcoded CC with the sdb ones
				ut64 len = r_debug_arg_get (core->dbg, R_ANAL_CC_TYPE_FASTCALL, i + 2);
				len = R_MIN (len + 1, sizeof (str) - 1);
				if (len == 0) {
					len = 16; // override default
				}
				(void)r_io_read_at (core->io, arg, (ut8 *)str, len);
				str[len] = 0;
				r_str_filter (str, -1);
				res = r_str_appendf (res, "\"%s\"", str);
			} break;
			default:
				res = r_str_appendf (res, "0x%08" PFMT64x "", arg);
				break;
			}
		} else {
			res = r_str_appendf (res, "0x%08" PFMT64x "", arg);
		}
		if (i + 1 < item->args) {
			res = r_str_appendf (res, ", ");
		}
	}
	r_syscall_item_free (item);
	res = r_str_appendf (res, ")");
	return res;
}


static void var_help(RCore *core, char ch) {
	switch (ch) {
	case 'b':
		r_core_cmd_help (core, help_msg_afvb);
		break;
	case 's':
		r_core_cmd_help (core, help_msg_afvs);
		break;
	case 'r':
		r_core_cmd_help (core, help_msg_afvr);
		break;
	case '?':
		r_core_cmd_help (core, help_msg_afv);
		break;
	default:
		eprintf ("See afv?, afvb?, afvr? and afvs?\n");
	}
}


static int listOpDescriptions(void *_core, const char *k, const char *v) {
        r_cons_printf ("%s=%s\n", k, v);
        return 1;
}


static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
{
	struct signal_struct *sig;

	if (clone_flags & CLONE_THREAD)
		return 0;

	sig = kmem_cache_zalloc(signal_cachep, GFP_KERNEL);
	tsk->signal = sig;
	if (!sig)
		return -ENOMEM;

	sig->nr_threads = 1;
	atomic_set(&sig->live, 1);
	atomic_set(&sig->sigcnt, 1);

	/* list_add(thread_node, thread_head) without INIT_LIST_HEAD() */
	sig->thread_head = (struct list_head)LIST_HEAD_INIT(tsk->thread_node);
	tsk->thread_node = (struct list_head)LIST_HEAD_INIT(sig->thread_head);

	init_waitqueue_head(&sig->wait_chldexit);
	sig->curr_target = tsk;
	init_sigpending(&sig->shared_pending);
	INIT_HLIST_HEAD(&sig->multiprocess);
	seqlock_init(&sig->stats_lock);
	prev_cputime_init(&sig->prev_cputime);

#ifdef CONFIG_POSIX_TIMERS
	INIT_LIST_HEAD(&sig->posix_timers);
	hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	sig->real_timer.function = it_real_fn;
#endif

	task_lock(current->group_leader);
	memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);
	task_unlock(current->group_leader);

	posix_cpu_timers_init_group(sig);

	tty_audit_fork(sig);
	sched_autogroup_fork(sig);

	sig->oom_score_adj = current->signal->oom_score_adj;
	sig->oom_score_adj_min = current->signal->oom_score_adj_min;

	mutex_init(&sig->cred_guard_mutex);

	return 0;
}


long _do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr,
	      unsigned long tls)
{
	struct completion vfork;
	struct pid *pid;
	struct task_struct *p;
	int trace = 0;
	long nr;

	/*
	 * Determine whether and which event to report to ptracer.  When
	 * called from kernel_thread or CLONE_UNTRACED is explicitly
	 * requested, no event is reported; otherwise, report if the event
	 * for the type of forking is enabled.
	 */
	if (!(clone_flags & CLONE_UNTRACED)) {
		if (clone_flags & CLONE_VFORK)
			trace = PTRACE_EVENT_VFORK;
		else if ((clone_flags & CSIGNAL) != SIGCHLD)
			trace = PTRACE_EVENT_CLONE;
		else
			trace = PTRACE_EVENT_FORK;

		if (likely(!ptrace_event_enabled(current, trace)))
			trace = 0;
	}

	p = copy_process(clone_flags, stack_start, stack_size,
			 child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
	add_latent_entropy();

	if (IS_ERR(p))
		return PTR_ERR(p);

	/*
	 * Do this prior waking up the new thread - the thread pointer
	 * might get invalid after that point, if the thread exits quickly.
	 */
	trace_sched_process_fork(current, p);

	pid = get_task_pid(p, PIDTYPE_PID);
	nr = pid_vnr(pid);

	if (clone_flags & CLONE_PARENT_SETTID)
		put_user(nr, parent_tidptr);

	if (clone_flags & CLONE_VFORK) {
		p->vfork_done = &vfork;
		init_completion(&vfork);
		get_task_struct(p);
	}

	wake_up_new_task(p);

	/* forking complete and child started to run, tell ptracer */
	if (unlikely(trace))
		ptrace_event_pid(trace, pid);

	if (clone_flags & CLONE_VFORK) {
		if (!wait_for_vfork_done(p, &vfork))
			ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
	}

	put_pid(pid);
	return nr;
}


static int memcg_charge_kernel_stack(struct task_struct *tsk)
{
#ifdef CONFIG_VMAP_STACK
	struct vm_struct *vm = task_stack_vm_area(tsk);
	int ret;

	if (vm) {
		int i;

		for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) {
			/*
			 * If memcg_kmem_charge() fails, page->mem_cgroup
			 * pointer is NULL, and both memcg_kmem_uncharge()
			 * and mod_memcg_page_state() in free_thread_stack()
			 * will ignore this page. So it's safe.
			 */
			ret = memcg_kmem_charge(vm->pages[i], GFP_KERNEL, 0);
			if (ret)
				return ret;

			mod_memcg_page_state(vm->pages[i],
					     MEMCG_KERNEL_STACK_KB,
					     PAGE_SIZE / 1024);
		}
	}
#endif
	return 0;
}


static void check_mm(struct mm_struct *mm)
{
	int i;

	for (i = 0; i < NR_MM_COUNTERS; i++) {
		long x = atomic_long_read(&mm->rss_stat.count[i]);

		if (unlikely(x))
			printk(KERN_ALERT "BUG: Bad rss-counter state "
					  "mm:%p idx:%d val:%ld\n", mm, i, x);
	}

	if (mm_pgtables_bytes(mm))
		pr_alert("BUG: non-zero pgtables_bytes on freeing mm: %ld\n",
				mm_pgtables_bytes(mm));

#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
	VM_BUG_ON_MM(mm->pmd_huge_pte, mm);
#endif
}


load_reduce(UnpicklerObject *self)
{
    PyObject *callable = NULL;
    PyObject *argtup = NULL;
    PyObject *obj = NULL;

    PDATA_POP(self->stack, argtup);
    if (argtup == NULL)
        return -1;
    PDATA_POP(self->stack, callable);
    if (callable) {
        obj = PyObject_CallObject(callable, argtup);
        Py_DECREF(callable);
    }
    Py_DECREF(argtup);

    if (obj == NULL)
        return -1;

    PDATA_PUSH(self->stack, obj, -1);
    return 0;
}


Pdata_clear(Pdata *self, Py_ssize_t clearto)
{
    Py_ssize_t i = Py_SIZE(self);

    assert(clearto >= self->fence);
    if (clearto >= i)
        return 0;

    while (--i >= clearto) {
        Py_CLEAR(self->data[i]);
    }
    Py_SIZE(self) = clearto;
    return 0;
}


load_tuple(UnpicklerObject *self)
{
    Py_ssize_t i;

    if ((i = marker(self)) < 0)
        return -1;

    return load_counted_tuple(self, Py_SIZE(self->stack) - i);
}


save_unicode(PicklerObject *self, PyObject *obj)
{
    if (self->bin) {
        if (write_unicode_binary(self, obj) < 0)
            return -1;
    }
    else {
        PyObject *encoded;
        Py_ssize_t size;
        const char unicode_op = UNICODE;

        encoded = raw_unicode_escape(obj);
        if (encoded == NULL)
            return -1;

        if (_Pickler_Write(self, &unicode_op, 1) < 0) {
            Py_DECREF(encoded);
            return -1;
        }

        size = PyBytes_GET_SIZE(encoded);
        if (_Pickler_Write(self, PyBytes_AS_STRING(encoded), size) < 0) {
            Py_DECREF(encoded);
            return -1;
        }
        Py_DECREF(encoded);

        if (_Pickler_Write(self, "\n", 1) < 0)
            return -1;
    }
    if (memo_put(self, obj) < 0)
        return -1;

    return 0;
}


PicklerMemoProxy_clear(PicklerMemoProxyObject *self)
{
    Py_CLEAR(self->pickler);
    return 0;
}


save_set(PicklerObject *self, PyObject *obj)
{
    PyObject *item;
    int i;
    Py_ssize_t set_size, ppos = 0;
    Py_hash_t hash;

    const char empty_set_op = EMPTY_SET;
    const char mark_op = MARK;
    const char additems_op = ADDITEMS;

    if (self->proto < 4) {
        PyObject *items;
        PyObject *reduce_value;
        int status;

        items = PySequence_List(obj);
        if (items == NULL) {
            return -1;
        }
        reduce_value = Py_BuildValue("(O(O))", (PyObject*)&PySet_Type, items);
        Py_DECREF(items);
        if (reduce_value == NULL) {
            return -1;
        }
        /* save_reduce() will memoize the object automatically. */
        status = save_reduce(self, reduce_value, obj);
        Py_DECREF(reduce_value);
        return status;
    }

    if (_Pickler_Write(self, &empty_set_op, 1) < 0)
        return -1;

    if (memo_put(self, obj) < 0)
        return -1;

    set_size = PySet_GET_SIZE(obj);
    if (set_size == 0)
        return 0;  /* nothing to do */

    /* Write in batches of BATCHSIZE. */
    do {
        i = 0;
        if (_Pickler_Write(self, &mark_op, 1) < 0)
            return -1;
        while (_PySet_NextEntry(obj, &ppos, &item, &hash)) {
            if (save(self, item, 0) < 0)
                return -1;
            if (++i == BATCHSIZE)
                break;
        }
        if (_Pickler_Write(self, &additems_op, 1) < 0)
            return -1;
        if (PySet_GET_SIZE(obj) != set_size) {
            PyErr_Format(
                PyExc_RuntimeError,
                "set changed size during iteration");
            return -1;
        }
    } while (i == BATCHSIZE);

    return 0;
}


load_newobj_ex(UnpicklerObject *self)
{
    PyObject *cls, *args, *kwargs;
    PyObject *obj;
    PickleState *st = _Pickle_GetGlobalState();

    PDATA_POP(self->stack, kwargs);
    if (kwargs == NULL) {
        return -1;
    }
    PDATA_POP(self->stack, args);
    if (args == NULL) {
        Py_DECREF(kwargs);
        return -1;
    }
    PDATA_POP(self->stack, cls);
    if (cls == NULL) {
        Py_DECREF(kwargs);
        Py_DECREF(args);
        return -1;
    }

    if (!PyType_Check(cls)) {
        Py_DECREF(kwargs);
        Py_DECREF(args);
        PyErr_Format(st->UnpicklingError,
                     "NEWOBJ_EX class argument must be a type, not %.200s",
                     Py_TYPE(cls)->tp_name);
        Py_DECREF(cls);
        return -1;
    }

    if (((PyTypeObject *)cls)->tp_new == NULL) {
        Py_DECREF(kwargs);
        Py_DECREF(args);
        Py_DECREF(cls);
        PyErr_SetString(st->UnpicklingError,
                        "NEWOBJ_EX class argument doesn't have __new__");
        return -1;
    }
    obj = ((PyTypeObject *)cls)->tp_new((PyTypeObject *)cls, args, kwargs);
    Py_DECREF(kwargs);
    Py_DECREF(args);
    Py_DECREF(cls);
    if (obj == NULL) {
        return -1;
    }
    PDATA_PUSH(self->stack, obj, -1);
    return 0;
}


load_put(UnpicklerObject *self)
{
    PyObject *key, *value;
    Py_ssize_t idx;
    Py_ssize_t len;
    char *s = NULL;

    if ((len = _Unpickler_Readline(self, &s)) < 0)
        return -1;
    if (len < 2)
        return bad_readline();
    if (Py_SIZE(self->stack) <= self->stack->fence)
        return Pdata_stack_underflow(self->stack);
    value = self->stack->data[Py_SIZE(self->stack) - 1];

    key = PyLong_FromString(s, NULL, 10);
    if (key == NULL)
        return -1;
    idx = PyLong_AsSsize_t(key);
    Py_DECREF(key);
    if (idx < 0) {
        if (!PyErr_Occurred())
            PyErr_SetString(PyExc_ValueError,
                            "negative PUT argument");
        return -1;
    }

    return _Unpickler_MemoPut(self, idx, value);
}


_Pickler_SetProtocol(PicklerObject *self, PyObject *protocol, int fix_imports)
{
    long proto;

    if (protocol == NULL || protocol == Py_None) {
        proto = DEFAULT_PROTOCOL;
    }
    else {
        proto = PyLong_AsLong(protocol);
        if (proto < 0) {
            if (proto == -1 && PyErr_Occurred())
                return -1;
            proto = HIGHEST_PROTOCOL;
        }
        else if (proto > HIGHEST_PROTOCOL) {
            PyErr_Format(PyExc_ValueError, "pickle protocol must be <= %d",
                         HIGHEST_PROTOCOL);
            return -1;
        }
    }
    self->proto = (int)proto;
    self->bin = proto > 0;
    self->fix_imports = fix_imports && proto < 3;
    return 0;
}


calc_binsize(char *bytes, int nbytes)
{
    unsigned char *s = (unsigned char *)bytes;
    int i;
    size_t x = 0;

    if (nbytes > (int)sizeof(size_t)) {
        /* Check for integer overflow.  BINBYTES8 and BINUNICODE8 opcodes
         * have 64-bit size that can't be represented on 32-bit platform.
         */
        for (i = (int)sizeof(size_t); i < nbytes; i++) {
            if (s[i])
                return -1;
        }
        nbytes = (int)sizeof(size_t);
    }
    for (i = 0; i < nbytes; i++) {
        x |= (size_t) s[i] << (8 * i);
    }

    if (x > PY_SSIZE_T_MAX)
        return -1;
    else
        return (Py_ssize_t) x;
}


_checkmodule(PyObject *module_name, PyObject *module,
             PyObject *global, PyObject *dotted_path)
{
    if (module == Py_None) {
        return -1;
    }
    if (PyUnicode_Check(module_name) &&
            _PyUnicode_EqualToASCIIString(module_name, "__main__")) {
        return -1;
    }

    PyObject *candidate = get_deep_attribute(module, dotted_path, NULL);
    if (candidate == NULL) {
        return -1;
    }
    if (candidate != global) {
        Py_DECREF(candidate);
        return -1;
    }
    Py_DECREF(candidate);
    return 0;
}


do_append(UnpicklerObject *self, Py_ssize_t x)
{
    PyObject *value;
    PyObject *slice;
    PyObject *list;
    PyObject *result;
    Py_ssize_t len, i;

    len = Py_SIZE(self->stack);
    if (x > len || x <= self->stack->fence)
        return Pdata_stack_underflow(self->stack);
    if (len == x)  /* nothing to do */
        return 0;

    list = self->stack->data[x - 1];

    if (PyList_CheckExact(list)) {
        Py_ssize_t list_len;
        int ret;

        slice = Pdata_poplist(self->stack, x);
        if (!slice)
            return -1;
        list_len = PyList_GET_SIZE(list);
        ret = PyList_SetSlice(list, list_len, list_len, slice);
        Py_DECREF(slice);
        return ret;
    }
    else {
        PyObject *extend_func;
        _Py_IDENTIFIER(extend);

        extend_func = _PyObject_GetAttrId(list, &PyId_extend);
        if (extend_func != NULL) {
            slice = Pdata_poplist(self->stack, x);
            if (!slice) {
                Py_DECREF(extend_func);
                return -1;
            }
            result = _Pickle_FastCall(extend_func, slice);
            Py_DECREF(extend_func);
            if (result == NULL)
                return -1;
            Py_DECREF(result);
        }
        else {
            PyObject *append_func;
            _Py_IDENTIFIER(append);

            /* Even if the PEP 307 requires extend() and append() methods,
               fall back on append() if the object has no extend() method
               for backward compatibility. */
            PyErr_Clear();
            append_func = _PyObject_GetAttrId(list, &PyId_append);
            if (append_func == NULL)
                return -1;
            for (i = x; i < len; i++) {
                value = self->stack->data[i];
                result = _Pickle_FastCall(append_func, value);
                if (result == NULL) {
                    Pdata_clear(self->stack, i + 1);
                    Py_SIZE(self->stack) = x;
                    Py_DECREF(append_func);
                    return -1;
                }
                Py_DECREF(result);
            }
            Py_SIZE(self->stack) = x;
            Py_DECREF(append_func);
        }
    }

    return 0;
}


load_stack_global(UnpicklerObject *self)
{
    PyObject *global;
    PyObject *module_name;
    PyObject *global_name;

    PDATA_POP(self->stack, global_name);
    PDATA_POP(self->stack, module_name);
    if (module_name == NULL || !PyUnicode_CheckExact(module_name) ||
        global_name == NULL || !PyUnicode_CheckExact(global_name)) {
        PickleState *st = _Pickle_GetGlobalState();
        PyErr_SetString(st->UnpicklingError, "STACK_GLOBAL requires str");
        Py_XDECREF(global_name);
        Py_XDECREF(module_name);
        return -1;
    }
    global = find_class(self, module_name, global_name);
    Py_DECREF(global_name);
    Py_DECREF(module_name);
    if (global == NULL)
        return -1;
    PDATA_PUSH(self->stack, global, -1);
    return 0;
}


_pickle_load_impl(PyObject *module, PyObject *file, int fix_imports,
                  const char *encoding, const char *errors)
/*[clinic end generated code: output=69e298160285199e input=01b44dd3fc07afa7]*/
{
    PyObject *result;
    UnpicklerObject *unpickler = _Unpickler_New();

    if (unpickler == NULL)
        return NULL;

    if (_Unpickler_SetInputStream(unpickler, file) < 0)
        goto error;

    if (_Unpickler_SetInputEncoding(unpickler, encoding, errors) < 0)
        goto error;

    unpickler->fix_imports = fix_imports;

    result = load(unpickler);
    Py_DECREF(unpickler);
    return result;

  error:
    Py_XDECREF(unpickler);
    return NULL;
}


load_empty_set(UnpicklerObject *self)
{
    PyObject *set;

    if ((set = PySet_New(NULL)) == NULL)
        return -1;
    PDATA_PUSH(self->stack, set, -1);
    return 0;
}


load_dup(UnpicklerObject *self)
{
    PyObject *last;
    Py_ssize_t len = Py_SIZE(self->stack);

    if (len <= self->stack->fence)
        return Pdata_stack_underflow(self->stack);
    last = self->stack->data[len - 1];
    PDATA_APPEND(self->stack, last, -1);
    return 0;
}


save_pers(PicklerObject *self, PyObject *obj)
{
    PyObject *pid = NULL;
    int status = 0;

    const char persid_op = PERSID;
    const char binpersid_op = BINPERSID;

    pid = call_method(self->pers_func, self->pers_func_self, obj);
    if (pid == NULL)
        return -1;

    if (pid != Py_None) {
        if (self->bin) {
            if (save(self, pid, 1) < 0 ||
                _Pickler_Write(self, &binpersid_op, 1) < 0)
                goto error;
        }
        else {
            PyObject *pid_str;

            pid_str = PyObject_Str(pid);
            if (pid_str == NULL)
                goto error;

            /* XXX: Should it check whether the pid contains embedded
               newlines? */
            if (!PyUnicode_IS_ASCII(pid_str)) {
                PyErr_SetString(_Pickle_GetGlobalState()->PicklingError,
                                "persistent IDs in protocol 0 must be "
                                "ASCII strings");
                Py_DECREF(pid_str);
                goto error;
            }

            if (_Pickler_Write(self, &persid_op, 1) < 0 ||
                _Pickler_Write(self, PyUnicode_DATA(pid_str),
                               PyUnicode_GET_LENGTH(pid_str)) < 0 ||
                _Pickler_Write(self, "\n", 1) < 0) {
                Py_DECREF(pid_str);
                goto error;
            }
            Py_DECREF(pid_str);
        }
        status = 1;
    }

    if (0) {
  error:
        status = -1;
    }
    Py_XDECREF(pid);

    return status;
}


Unpickler_set_persload(UnpicklerObject *self, PyObject *value)
{
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError,
                        "attribute deletion is not supported");
        return -1;
    }
    if (!PyCallable_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
                        "persistent_load must be a callable taking "
                        "one argument");
        return -1;
    }

    self->pers_func_self = NULL;
    Py_INCREF(value);
    Py_XSETREF(self->pers_func, value);

    return 0;
}


bad_readline(void)
{
    PickleState *st = _Pickle_GetGlobalState();
    PyErr_SetString(st->UnpicklingError, "pickle data was truncated");
    return -1;
}