do_buffer_ext(
    int		action,
    int		start,
    int		dir,		// FORWARD or BACKWARD
    int		count,		// buffer number or number of buffers
    int		flags)		// DOBUF_FORCEIT etc.
{
    buf_T	*buf;
    buf_T	*bp;
    int		unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
			|| action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);

    switch (start)
    {
	case DOBUF_FIRST:   buf = firstbuf; break;
	case DOBUF_LAST:    buf = lastbuf;  break;
	default:	    buf = curbuf;   break;
    }
    if (start == DOBUF_MOD)	    // find next modified buffer
    {
	while (count-- > 0)
	{
	    do
	    {
		buf = buf->b_next;
		if (buf == NULL)
		    buf = firstbuf;
	    }
	    while (buf != curbuf && !bufIsChanged(buf));
	}
	if (!bufIsChanged(buf))
	{
	    emsg(_(e_no_modified_buffer_found));
	    return FAIL;
	}
    }
    else if (start == DOBUF_FIRST && count) // find specified buffer number
    {
	while (buf != NULL && buf->b_fnum != count)
	    buf = buf->b_next;
    }
    else
    {
	bp = NULL;
	while (count > 0 || (!unload && !buf->b_p_bl && bp != buf))
	{
	    // remember the buffer where we start, we come back there when all
	    // buffers are unlisted.
	    if (bp == NULL)
		bp = buf;
	    if (dir == FORWARD)
	    {
		buf = buf->b_next;
		if (buf == NULL)
		    buf = firstbuf;
	    }
	    else
	    {
		buf = buf->b_prev;
		if (buf == NULL)
		    buf = lastbuf;
	    }
	    // don't count unlisted buffers
	    if (unload || buf->b_p_bl)
	    {
		 --count;
		 bp = NULL;	// use this buffer as new starting point
	    }
	    if (bp == buf)
	    {
		// back where we started, didn't find anything.
		emsg(_(e_there_is_no_listed_buffer));
		return FAIL;
	    }
	}
    }

    if (buf == NULL)	    // could not find it
    {
	if (start == DOBUF_FIRST)
	{
	    // don't warn when deleting
	    if (!unload)
		semsg(_(e_buffer_nr_does_not_exist), count);
	}
	else if (dir == FORWARD)
	    emsg(_(e_cannot_go_beyond_last_buffer));
	else
	    emsg(_(e_cannot_go_before_first_buffer));
	return FAIL;
    }
#ifdef FEAT_PROP_POPUP
    if ((flags & DOBUF_NOPOPUP) && bt_popup(buf)
# ifdef FEAT_TERMINAL
				&& !bt_terminal(buf)
#endif
       )
	return OK;
#endif

#ifdef FEAT_GUI
    need_mouse_correct = TRUE;
#endif

    /*
     * delete buffer "buf" from memory and/or the list
     */
    if (unload)
    {
	int	forward;
	bufref_T bufref;

	if (!can_unload_buffer(buf))
	    return FAIL;

	set_bufref(&bufref, buf);

	// When unloading or deleting a buffer that's already unloaded and
	// unlisted: fail silently.
	if (action != DOBUF_WIPE && action != DOBUF_WIPE_REUSE
				   && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
	    return FAIL;

	if ((flags & DOBUF_FORCEIT) == 0 && bufIsChanged(buf))
	{
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
	    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
	    {
		dialog_changed(buf, FALSE);
		if (!bufref_valid(&bufref))
		    // Autocommand deleted buffer, oops!  It's not changed
		    // now.
		    return FAIL;
		// If it's still changed fail silently, the dialog already
		// mentioned why it fails.
		if (bufIsChanged(buf))
		    return FAIL;
	    }
	    else
#endif
	    {
		semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
								 buf->b_fnum);
		return FAIL;
	    }
	}

	// When closing the current buffer stop Visual mode.
	if (buf == curbuf && VIsual_active)
	    end_visual_mode();

	// If deleting the last (listed) buffer, make it empty.
	// The last (listed) buffer cannot be unloaded.
	FOR_ALL_BUFFERS(bp)
	    if (bp->b_p_bl && bp != buf)
		break;
	if (bp == NULL && buf == curbuf)
	    return empty_curbuf(TRUE, (flags & DOBUF_FORCEIT), action);

	// If the deleted buffer is the current one, close the current window
	// (unless it's the only window).  Repeat this so long as we end up in
	// a window with this buffer.
	while (buf == curbuf
		   && !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
		   && (!ONE_WINDOW || first_tabpage->tp_next != NULL))
	{
	    if (win_close(curwin, FALSE) == FAIL)
		break;
	}

	// If the buffer to be deleted is not the current one, delete it here.
	if (buf != curbuf)
	{
	    close_windows(buf, FALSE);
	    if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0)
		    close_buffer(NULL, buf, action, FALSE, FALSE);
	    return OK;
	}

	/*
	 * Deleting the current buffer: Need to find another buffer to go to.
	 * There should be another, otherwise it would have been handled
	 * above.  However, autocommands may have deleted all buffers.
	 * First use au_new_curbuf.br_buf, if it is valid.
	 * Then prefer the buffer we most recently visited.
	 * Else try to find one that is loaded, after the current buffer,
	 * then before the current buffer.
	 * Finally use any buffer.
	 */
	buf = NULL;	// selected buffer
	bp = NULL;	// used when no loaded buffer found
	if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf))
	    buf = au_new_curbuf.br_buf;
	else if (curwin->w_jumplistlen > 0)
	{
	    int     jumpidx;

	    jumpidx = curwin->w_jumplistidx - 1;
	    if (jumpidx < 0)
		jumpidx = curwin->w_jumplistlen - 1;

	    forward = jumpidx;
	    while (jumpidx != curwin->w_jumplistidx)
	    {
		buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
		if (buf != NULL)
		{
		    // Skip current and unlisted bufs.  Also skip a quickfix
		    // buffer, it might be deleted soon.
		    if (buf == curbuf || !buf->b_p_bl
#if defined(FEAT_QUICKFIX)
			    || bt_quickfix(buf)
#endif
			    )
			buf = NULL;
		    else if (buf->b_ml.ml_mfp == NULL)
		    {
			// skip unloaded buf, but may keep it for later
			if (bp == NULL)
			    bp = buf;
			buf = NULL;
		    }
		}
		if (buf != NULL)   // found a valid buffer: stop searching
		    break;
		// advance to older entry in jump list
		if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
		    break;
		if (--jumpidx < 0)
		    jumpidx = curwin->w_jumplistlen - 1;
		if (jumpidx == forward)		// List exhausted for sure
		    break;
	    }
	}

	if (buf == NULL)	// No previous buffer, Try 2'nd approach
	{
	    forward = TRUE;
	    buf = curbuf->b_next;
	    for (;;)
	    {
		if (buf == NULL)
		{
		    if (!forward)	// tried both directions
			break;
		    buf = curbuf->b_prev;
		    forward = FALSE;
		    continue;
		}
		// in non-help buffer, try to skip help buffers, and vv
		if (buf->b_help == curbuf->b_help && buf->b_p_bl
#if defined(FEAT_QUICKFIX)
			    && !bt_quickfix(buf)
#endif
			   )
		{
		    if (buf->b_ml.ml_mfp != NULL)   // found loaded buffer
			break;
		    if (bp == NULL)	// remember unloaded buf for later
			bp = buf;
		}
		if (forward)
		    buf = buf->b_next;
		else
		    buf = buf->b_prev;
	    }
	}
	if (buf == NULL)	// No loaded buffer, use unloaded one
	    buf = bp;
	if (buf == NULL)	// No loaded buffer, find listed one
	{
	    FOR_ALL_BUFFERS(buf)
		if (buf->b_p_bl && buf != curbuf
#if defined(FEAT_QUICKFIX)
			    && !bt_quickfix(buf)
#endif
		       )
		    break;
	}
	if (buf == NULL)	// Still no buffer, just take one
	{
	    if (curbuf->b_next != NULL)
		buf = curbuf->b_next;
	    else
		buf = curbuf->b_prev;
#if defined(FEAT_QUICKFIX)
	    if (bt_quickfix(buf))
		buf = NULL;
#endif
	}
    }

    if (buf == NULL)
    {
	// Autocommands must have wiped out all other buffers.  Only option
	// now is to make the current buffer empty.
	return empty_curbuf(FALSE, (flags & DOBUF_FORCEIT), action);
    }

    /*
     * make "buf" the current buffer
     */
    if (action == DOBUF_SPLIT)	    // split window first
    {
	// If 'switchbuf' contains "useopen": jump to first window containing
	// "buf" if one exists
	if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
	    return OK;
	// If 'switchbuf' contains "usetab": jump to first window in any tab
	// page containing "buf" if one exists
	if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
	    return OK;
	if (win_split(0, 0) == FAIL)
	    return FAIL;
    }

    // go to current buffer - nothing to do
    if (buf == curbuf)
	return OK;

    // Check if the current buffer may be abandoned.
    if (action == DOBUF_GOTO && !can_abandon(curbuf, (flags & DOBUF_FORCEIT)))
    {
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
	if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
	{
	    bufref_T bufref;

	    set_bufref(&bufref, buf);
	    dialog_changed(curbuf, FALSE);
	    if (!bufref_valid(&bufref))
		// Autocommand deleted buffer, oops!
		return FAIL;
	}
	if (bufIsChanged(curbuf))
#endif
	{
	    no_write_message();
	    return FAIL;
	}
    }

    // Go to the other buffer.
    set_curbuf(buf, action);

    if (action == DOBUF_SPLIT)
	RESET_BINDING(curwin);	// reset 'scrollbind' and 'cursorbind'

#if defined(FEAT_EVAL)
    if (aborting())	    // autocmds may abort script processing
	return FAIL;
#endif

    return OK;
}


struct r_bin_pe_lib_t* PE_(r_bin_pe_get_libs)(RBinPEObj* pe) {
	if (!pe) {
		return NULL;
	}
	struct r_bin_pe_lib_t* libs = NULL;
	struct r_bin_pe_lib_t* new_libs = NULL;
	PE_(image_import_directory) curr_import_dir;
	PE_(image_delay_import_directory) curr_delay_import_dir;
	PE_DWord name_off = 0;
	HtPP *lib_map = NULL;
	ut64 off; //cache value
	int index = 0;
	int len = 0;
	int max_libs = 20;
	libs = calloc (max_libs + 1, sizeof (struct r_bin_pe_lib_t));
	if (!libs) {
		r_sys_perror ("malloc (libs)");
		return NULL;
	}

	if (pe->import_directory_offset + pe->import_directory_size > pe->size) {
		pe_printf ("import directory offset bigger than file\n");
		goto out_error;
	}
	lib_map = sdb_ht_new ();
	off = pe->import_directory_offset;
	if (off < pe->size && off > 0) {
		ut64 last;
		int iidi = 0;
		// normal imports
		if (off + sizeof (PE_(image_import_directory)) > pe->size) {
			goto out_error;
		}
		int r = read_image_import_directory (pe->b, off + iidi * sizeof (curr_import_dir),
			&curr_import_dir);
		last = off + pe->import_directory_size;
		while (r == sizeof (curr_import_dir) && off + (iidi + 1) * sizeof (curr_import_dir) <= last && (
			curr_import_dir.FirstThunk || curr_import_dir.Name ||
			curr_import_dir.TimeDateStamp || curr_import_dir.Characteristics ||
			curr_import_dir.ForwarderChain)) {
			name_off = PE_(va2pa) (pe, curr_import_dir.Name);
			len = r_buf_read_at (pe->b, name_off, (ut8*) libs[index].name, PE_STRING_LENGTH);
			if (!libs[index].name[0]) { // minimum string length
				goto next;
			}
			if (len < 2 || libs[index].name[0] == 0) { // minimum string length
				pe_printf ("Warning: read (libs - import dirs) %d\n", len);
				break;
			}
			libs[index].name[len - 1] = '\0';
			r_str_case (libs[index].name, 0);
			if (!sdb_ht_find (lib_map, libs[index].name, NULL)) {
				sdb_ht_insert (lib_map, libs[index].name, "a");
				libs[index++].last = 0;
				if (index >= max_libs) {
					new_libs = realloc (libs, (max_libs * 2) * sizeof (struct r_bin_pe_lib_t));
					if (!new_libs) {
						r_sys_perror ("realloc (libs)");
						goto out_error;
					}
					libs = new_libs;
					new_libs = NULL;
					max_libs *= 2;
				}
			}
next:
			iidi++;
			r = read_image_import_directory (pe->b, off + iidi * sizeof (curr_import_dir),
				&curr_import_dir);
		}
	}
	off = pe->delay_import_directory_offset;
	if (off < pe->size && off > 0) {
		ut64 did = 0;
		if (off + sizeof (PE_(image_delay_import_directory)) > pe->size) {
			goto out_error;
		}
		int r = read_image_delay_import_directory (pe->b, off, &curr_delay_import_dir);
		if (r != sizeof (curr_delay_import_dir)) {
			goto out_error;
		}
		while (r == sizeof (curr_delay_import_dir) &&
			curr_delay_import_dir.Name != 0 && curr_delay_import_dir.DelayImportNameTable != 0) {
			name_off = PE_(va2pa) (pe, curr_delay_import_dir.Name);
			if (name_off > pe->size || name_off + PE_STRING_LENGTH > pe->size) {
				goto out_error;
			}
			len = r_buf_read_at (pe->b, name_off, (ut8*) libs[index].name, PE_STRING_LENGTH);
			if (len != PE_STRING_LENGTH) {
				pe_printf ("Warning: read (libs - delay import dirs)\n");
				break;
			}
			libs[index].name[len - 1] = '\0';
			r_str_case (libs[index].name, 0);
			if (!sdb_ht_find (lib_map, libs[index].name, NULL)) {
				sdb_ht_insert (lib_map, libs[index].name, "a");
				libs[index++].last = 0;
				if (index >= max_libs) {
					new_libs = realloc (libs, (max_libs * 2) * sizeof (struct r_bin_pe_lib_t));
					if (!new_libs) {
						r_sys_perror ("realloc (libs)");
						goto out_error;
					}
					libs = new_libs;
					new_libs = NULL;
					max_libs *= 2;
				}
			}
			did++;
			r = read_image_delay_import_directory (pe->b, off + did * sizeof (curr_delay_import_dir),
				&curr_delay_import_dir);
		}
	}
	sdb_ht_free (lib_map);
	libs[index].last = 1;
	return libs;
out_error:
	sdb_ht_free (lib_map);
	free (libs);
	return NULL;
}


static int read_image_resource_directory_entry(RBuffer *b, ut64 addr, Pe_image_resource_directory_entry *entry) {
	st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
	if (r_buf_seek (b, addr, R_BUF_SET) < 0) {
		return -1;
	}
	entry->u1.Name = r_buf_read_le32 (b);
	entry->u2.OffsetToData = r_buf_read_le32 (b);
	r_buf_seek (b, o_addr, R_BUF_SET);
	return sizeof (Pe_image_resource_directory_entry);
}


static int read_tls_directory(RBuffer *b, ut64 addr, PE_(image_tls_directory) *tls_directory) {
	st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
	if (r_buf_seek (b, addr, R_BUF_SET) < 0) {
		return -1;
	}
	tls_directory->StartAddressOfRawData = r_buf_read_le32 (b);
	tls_directory->EndAddressOfRawData = r_buf_read_le32 (b);
	tls_directory->AddressOfIndex = r_buf_read_le32 (b);
	tls_directory->AddressOfCallBacks = r_buf_read_le32 (b);
	tls_directory->SizeOfZeroFill = r_buf_read_le32 (b);
	tls_directory->Characteristics = r_buf_read_le32 (b);
	r_buf_seek (b, o_addr, R_BUF_SET);
	return sizeof (PE_(image_tls_directory));
}


static inline int is_arm(RBinPEObj* pe) {
	switch (pe->nt_headers->file_header.Machine) {
	case PE_IMAGE_FILE_MACHINE_RPI2: // 462
	case PE_IMAGE_FILE_MACHINE_ARM:
	case PE_IMAGE_FILE_MACHINE_THUMB:
		return 1;
	}
	return 0;
}


ut64 PE_(r_bin_pe_get_image_base)(RBinPEObj* pe) {
	ut64 imageBase = 0;
	if (!pe || !pe->nt_headers) {
		return 0LL;
	}
	imageBase = pe->nt_headers->optional_header.ImageBase;
	if (!imageBase) {
		//this should only happens with messed up binaries
		//XXX this value should be user defined by bin.baddr
		//but from here we can not access config API
		imageBase = 0x10000;
	}
	return imageBase;
}


R_API RBuffer *r_buf_new_file(const char *file, int perm, int mode) {
	struct buf_file_user u = { 0 };
	u.file = file;
	u.perm = perm;
	u.mode = mode;
	return new_buffer (R_BUFFER_FILE, &u);
}


int PE_(r_bin_pe_get_image_size)(RBinPEObj* pe) {
	return pe->nt_headers->optional_header.SizeOfImage;
}


R_API ut8 r_buf_read8(RBuffer *b) {
	ut8 res;
	st64 r = r_buf_read (b, &res, sizeof (res));
	return r == sizeof (res)? res: b->Oxff_priv;
}


static ut64 get_vaddr(RBinFile *bf, ut64 baddr, ut64 paddr, ut64 vaddr) {
	return vaddr;
}


static bool buf_fini(RBuffer *b) {
	r_return_val_if_fail (b && b->methods, false);
	return b->methods->fini? b->methods->fini (b): true;
}


R_API bool r_buf_prepend_bytes(RBuffer *b, const ut8 *buf, ut64 length) {
	r_return_val_if_fail (b && buf && !b->readonly, false);
	return r_buf_insert_bytes (b, 0, buf, length) >= 0;
}


static int get_debug_info(RBinPEObj* pe, PE_(image_debug_directory_entry)* dbg_dir_entry, ut8* dbg_data, int dbg_data_len, SDebugInfo* res) {
	#define SIZEOF_FILE_NAME 255
	int i = 0;
	const char* dbgname;
	if (!dbg_data) {
		return 0;
	}
	switch (dbg_dir_entry->Type) {
	case IMAGE_DEBUG_TYPE_CODEVIEW:
		if (!strncmp ((char*) dbg_data, "RSDS", 4)) {
			SCV_RSDS_HEADER rsds_hdr;
			init_rsdr_hdr (&rsds_hdr);
			if (!get_rsds (dbg_data, dbg_data_len, &rsds_hdr)) {
				pe_printf ("Warning: Cannot read PE debug info\n");
				return 0;
			}
			snprintf (res->guidstr, GUIDSTR_LEN,
				"%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x%x",
				rsds_hdr.guid.data1,
				rsds_hdr.guid.data2,
				rsds_hdr.guid.data3,
				rsds_hdr.guid.data4[0],
				rsds_hdr.guid.data4[1],
				rsds_hdr.guid.data4[2],
				rsds_hdr.guid.data4[3],
				rsds_hdr.guid.data4[4],
				rsds_hdr.guid.data4[5],
				rsds_hdr.guid.data4[6],
				rsds_hdr.guid.data4[7],
				rsds_hdr.age);
			dbgname = (char*) rsds_hdr.file_name;
			strncpy (res->file_name, (const char*)
				dbgname, sizeof (res->file_name));
			res->file_name[sizeof (res->file_name) - 1] = 0;
			rsds_hdr.free ((struct SCV_RSDS_HEADER*) &rsds_hdr);
		} else if (strncmp ((const char*) dbg_data, "NB10", 4) == 0) {
			if (dbg_data_len < 20) {
				eprintf ("Truncated NB10 entry, not enough data to parse\n");
				return 0;
			}
			SCV_NB10_HEADER nb10_hdr = {{0}};
			init_cv_nb10_header (&nb10_hdr);
			get_nb10 (dbg_data, dbg_data_len, &nb10_hdr);
			snprintf (res->guidstr, sizeof (res->guidstr),
				"%x%x", nb10_hdr.timestamp, nb10_hdr.age);
			res->file_name[0] = 0;
			if (nb10_hdr.file_name) {
				strncpy (res->file_name, (const char*)
						nb10_hdr.file_name, sizeof (res->file_name) - 1);
			}
			res->file_name[sizeof (res->file_name) - 1] = 0;
			nb10_hdr.free ((struct SCV_NB10_HEADER*) &nb10_hdr);
		} else {
			pe_printf ("CodeView section not NB10 or RSDS\n");
			return 0;
		}
		break;
	default:
		//pe_printf("get_debug_info(): not supported type\n");
		return 0;
	}

	while (i < 33) {
		res->guidstr[i] = toupper ((ut8) res->guidstr[i]);
		i++;
	}

	return 1;
}


R_API st64 r_buf_insert_bytes(RBuffer *b, ut64 addr, const ut8 *buf, ut64 length) {
	r_return_val_if_fail (b && !b->readonly, -1);
	st64 pos, r = r_buf_seek (b, 0, R_BUF_CUR);
	if (r < 0) {
		return r;
	}
	pos = r;
	r = r_buf_seek (b, addr, R_BUF_SET);
	if (r < 0) {
		goto restore_pos;
	}

	ut64 sz = r_buf_size (b);
	ut8 *tmp = R_NEWS (ut8, sz - addr);
	r = r_buf_read (b, tmp, sz - addr);
	if (r < 0) {
		goto free_tmp;
	}
	st64 tmp_length = r;
	if (!r_buf_resize (b, sz + length)) {
		goto free_tmp;
	}
	r = r_buf_seek (b, addr + length, R_BUF_SET);
	if (r < 0) {
		goto free_tmp;
	}
	r = r_buf_write (b, tmp, tmp_length);
	if (r < 0) {
		goto free_tmp;
	}
	r = r_buf_seek (b, addr, R_BUF_SET);
	if (r < 0) {
		goto free_tmp;
	}
	r = r_buf_write (b, buf, length);
free_tmp:
	free (tmp);
restore_pos:
	r_buf_seek (b, pos, R_BUF_SET);
	return r;
}


R_API st64 r_buf_read(RBuffer *b, ut8 *buf, ut64 len) {
	r_return_val_if_fail (b && buf, -1);
	st64 r = buf_read (b, buf, len);
	if (r >= 0 && r < len) {
		memset (buf + r, b->Oxff_priv, len - r);
	}
	return r;
}


static int bin_pe_init_exports(RBinPEObj* pe) {
	PE_(image_data_directory) * data_dir_export = &pe->data_directory[PE_IMAGE_DIRECTORY_ENTRY_EXPORT];
	PE_DWord export_dir_paddr = PE_(va2pa) (pe, data_dir_export->VirtualAddress);
	if (!export_dir_paddr) {
		// This export-dir-paddr should only appear in DLL files
		// pe_printf ("Warning: Cannot find the paddr of the export directory\n");
		return false;
	}
	// sdb_setn (DB, "hdr.exports_directory", export_dir_paddr);
	// pe_printf ("Pexports paddr at 0x%"PFMT64x"\n", export_dir_paddr);
	if (!(pe->export_directory = malloc (sizeof (PE_(image_export_directory))))) {
		r_sys_perror ("malloc (export directory)");
		return false;
	}
	if (read_image_export_directory (pe->b, export_dir_paddr, pe->export_directory) < 0) {
		pe_printf ("Warning: read (export directory)\n");
		R_FREE (pe->export_directory);
		return false;
	}
	return true;
}


R_API bool r_buf_append_nbytes(RBuffer *b, ut64 length) {
	r_return_val_if_fail (b && !b->readonly, false);
	ut8 *buf = R_NEWS0 (ut8, length);
	if (!buf) {
		return false;
	}
	bool res = r_buf_append_bytes (b, buf, length);
	free (buf);
	return res;
}


R_API bool r_buf_append_buf(RBuffer *b, RBuffer *a) {
	r_return_val_if_fail (b && a && !b->readonly, false);
	ut64 sz = 0;
	const ut8 *tmp = r_buf_data (a, &sz);
	return r_buf_append_bytes (b, tmp, sz);
}


static bool load_buffer(RBinFile *bf, void **bin_obj, RBuffer *b, ut64 loadaddr, Sdb *sdb) {
	return check_buffer (bf, b);
}


static bool check_buffer(RBinFile *bf, RBuffer *buf) {
	ut8 tmp[6];
	int r = r_buf_read_at (buf, 0, tmp, sizeof (tmp));
	return r == sizeof (tmp) && !memcmp (tmp, QNX_MAGIC, sizeof (tmp));
}


static PE_DWord bin_pe_rva_to_va(RBinPEObj* pe, PE_DWord rva) {
	return PE_(r_bin_pe_get_image_base) (pe) + rva;
}


void PE_(write_image_section_header)(RBuffer *b, ut64 addr, PE_(image_section_header) *section_header) {
	ut8 buf[sizeof (PE_(image_section_header))];
	memcpy (buf, section_header->Name, PE_IMAGE_SIZEOF_SHORT_NAME);
	r_write_at_le32 (buf, section_header->Misc.PhysicalAddress, PE_IMAGE_SIZEOF_SHORT_NAME);
	r_write_at_le32 (buf, section_header->VirtualAddress, PE_IMAGE_SIZEOF_SHORT_NAME + 4);
	r_write_at_le32 (buf, section_header->SizeOfRawData, PE_IMAGE_SIZEOF_SHORT_NAME + 8);
	r_write_at_le32 (buf, section_header->PointerToRawData, PE_IMAGE_SIZEOF_SHORT_NAME + 12);
	r_write_at_le32 (buf, section_header->PointerToRelocations, PE_IMAGE_SIZEOF_SHORT_NAME + 16);
	r_write_at_le32 (buf, section_header->PointerToLinenumbers, PE_IMAGE_SIZEOF_SHORT_NAME + 20);
	r_write_at_le16 (buf, section_header->NumberOfRelocations, PE_IMAGE_SIZEOF_SHORT_NAME + 24);
	r_write_at_le16 (buf, section_header->NumberOfLinenumbers, PE_IMAGE_SIZEOF_SHORT_NAME + 26);
	r_write_at_le32 (buf, section_header->Characteristics, PE_IMAGE_SIZEOF_SHORT_NAME + 28);
	r_buf_write_at (b, addr, buf, sizeof (PE_(image_section_header)));
}


static int read_image_resource_directory(RBuffer *b, ut64 addr, Pe_image_resource_directory *dir) {
	st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
	if (r_buf_seek (b, addr, R_BUF_SET) < 0) {
		return -1;
	}
	dir->Characteristics = r_buf_read_le32 (b);
	dir->TimeDateStamp = r_buf_read_le32 (b);
	dir->MajorVersion = r_buf_read_le16 (b);
	dir->MinorVersion = r_buf_read_le16 (b);
	dir->NumberOfNamedEntries = r_buf_read_le16 (b);
	dir->NumberOfIdEntries = r_buf_read_le16 (b);
	r_buf_seek (b, o_addr, R_BUF_SET);
	return sizeof (Pe_image_resource_directory);
}


double yang_str2dec64(const char *xpath, const char *value)
{
	double dbl = 0;

	if (sscanf(value, "%lf", &dbl) != 1) {
		flog_err(EC_LIB_YANG_DATA_CONVERT,
			 "%s: couldn't convert string to decimal64 [xpath %s]",
			 __func__, xpath);
		zlog_backtrace(LOG_ERR);
		abort();
	}

	return dbl;
}


char base64_encode_value(char value_in)
{
	if (value_in > 63)
		return '=';
	return ENCODING[(int)value_in];
}


struct yang_data *yang_data_new_mac(const char *xpath,
				    const struct ethaddr *mac)
{
	char value_str[ETHER_ADDR_STRLEN];

	prefix_mac2str(mac, value_str, sizeof(value_str));
	return yang_data_new(xpath, value_str);
}


void isis_notif_adj_state_change(const struct isis_adjacency *adj,
				 int new_state, const char *reason)
{
	const char *xpath = "/frr-isisd:adjacency-state-change";
	struct list *arguments = yang_data_list_new();
	char xpath_arg[XPATH_MAXLEN];
	struct yang_data *data;
	struct isis_circuit *circuit = adj->circuit;
	struct isis_area *area = circuit->area;
	struct isis_dynhn *dyn = dynhn_find_by_id(circuit->isis, adj->sysid);

	notif_prep_instance_hdr(xpath, area, "default", arguments);
	notif_prepr_iface_hdr(xpath, circuit, arguments);
	if (dyn) {
		snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor", xpath);
		data = yang_data_new_string(xpath_arg, dyn->hostname);
		listnode_add(arguments, data);
	}
	snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath);
	data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid));
	listnode_add(arguments, data);

	snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath);
	data = yang_data_new_string(xpath_arg, isis_adj_yang_state(new_state));
	listnode_add(arguments, data);
	if (new_state == ISIS_ADJ_DOWN) {
		snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath);
		data = yang_data_new_string(xpath_arg, reason);
		listnode_add(arguments, data);
	}

	hook_call(isis_hook_adj_state_change, adj);

	nb_notification_send(xpath, arguments);
}


void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt, ...)
{
	char xpath[XPATH_MAXLEN];
	const char *value;
	va_list ap;

	va_start(ap, xpath_fmt);
	vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
	va_end(ap);

	value = yang_get_default_value(xpath);
	yang_str2ipv4(value, var);
}


struct yang_data *yang_data_new_dec64(const char *xpath, double value)
{
	char value_str[BUFSIZ];

	snprintf(value_str, sizeof(value_str), "%lf", value);
	return yang_data_new(xpath, value_str);
}


uint32_t yang_get_default_uint32(const char *xpath_fmt, ...)
{
	char xpath[XPATH_MAXLEN];
	const char *value;
	va_list ap;

	va_start(ap, xpath_fmt);
	vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
	va_end(ap);

	value = yang_get_default_value(xpath);
	return yang_str2uint32(value);
}


void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
				       uint8_t max_area_addrs,
				       const char *raw_pdu, size_t raw_pdu_len)
{
	const char *xpath = "/frr-isisd:max-area-addresses-mismatch";
	struct list *arguments = yang_data_list_new();
	char xpath_arg[XPATH_MAXLEN];
	struct yang_data *data;
	struct isis_area *area = circuit->area;

	notif_prep_instance_hdr(xpath, area, "default", arguments);
	notif_prepr_iface_hdr(xpath, circuit, arguments);
	snprintf(xpath_arg, sizeof(xpath_arg), "%s/max-area-addresses", xpath);
	data = yang_data_new_uint8(xpath_arg, max_area_addrs);
	listnode_add(arguments, data);
	snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
	data = yang_data_new_binary(xpath_arg, raw_pdu, raw_pdu_len);
	listnode_add(arguments, data);

	hook_call(isis_hook_max_area_addr_mismatch, circuit, max_area_addrs,
		  raw_pdu, raw_pdu_len);

	nb_notification_send(xpath, arguments);
}


uint64_t yang_str2uint64(const char *value)
{
	return strtoull(value, NULL, 10);
}


const char *yang_afi_safi_value2identity(afi_t afi, safi_t safi)
{
	if (afi == AFI_IP && safi == SAFI_UNICAST)
		return "frr-routing:ipv4-unicast";
	if (afi == AFI_IP6 && safi == SAFI_UNICAST)
		return "frr-routing:ipv6-unicast";
	if (afi == AFI_IP && safi == SAFI_MULTICAST)
		return "frr-routing:ipv4-multicast";
	if (afi == AFI_IP6 && safi == SAFI_MULTICAST)
		return "frr-routing:ipv6-multicast";
	if (afi == AFI_IP && safi == SAFI_MPLS_VPN)
		return "frr-routing:l3vpn-ipv4-unicast";
	if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN)
		return "frr-routing:l3vpn-ipv6-unicast";
	if (afi == AFI_L2VPN && safi == SAFI_EVPN)
		return "frr-routing:l2vpn-evpn";
	if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST)
		return "frr-routing:ipv4-labeled-unicast";
	if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST)
		return "frr-routing:ipv6-labeled-unicast";
	if (afi == AFI_IP && safi == SAFI_FLOWSPEC)
		return "frr-routing:ipv4-flowspec";
	if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC)
		return "frr-routing:ipv6-flowspec";

	return NULL;
}


void base64_init_encodestate(struct base64_encodestate *state_in)
{
	state_in->step = step_A;
	state_in->result = 0;
	state_in->stepcount = 0;
}


void rndis_free_response(struct rndis_params *params, u8 *buf)
{
	rndis_resp_t *r, *n;

	list_for_each_entry_safe(r, n, &params->resp_queue, list) {
		if (r->buf == buf) {
			list_del(&r->list);
			kfree(r);
		}
	}
}


static int rndis_query_response(struct rndis_params *params,
				rndis_query_msg_type *buf)
{
	rndis_query_cmplt_type *resp;
	rndis_resp_t *r;

	/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
	if (!params->dev)
		return -ENOTSUPP;

	/*
	 * we need more memory:
	 * gen_ndis_query_resp expects enough space for
	 * rndis_query_cmplt_type followed by data.
	 * oid_supported_list is the largest data reply
	 */
	r = rndis_add_response(params,
		sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
	if (!r)
		return -ENOMEM;
	resp = (rndis_query_cmplt_type *)r->buf;

	resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */

	if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
			le32_to_cpu(buf->InformationBufferOffset)
					+ 8 + (u8 *)buf,
			le32_to_cpu(buf->InformationBufferLength),
			r)) {
		/* OID not supported */
		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
		resp->MessageLength = cpu_to_le32(sizeof *resp);
		resp->InformationBufferLength = cpu_to_le32(0);
		resp->InformationBufferOffset = cpu_to_le32(0);
	} else
		resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);

	params->resp_avail(params->v);
	return 0;
}


struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
{
	struct rndis_params *params;
	int i;

	if (!resp_avail)
		return ERR_PTR(-EINVAL);

	i = rndis_get_nr();
	if (i < 0) {
		pr_debug("failed\n");

		return ERR_PTR(-ENODEV);
	}

	params = kzalloc(sizeof(*params), GFP_KERNEL);
	if (!params) {
		rndis_put_nr(i);

		return ERR_PTR(-ENOMEM);
	}

#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
	{
		struct proc_dir_entry *proc_entry;
		char name[20];

		sprintf(name, NAME_TEMPLATE, i);
		proc_entry = proc_create_data(name, 0660, NULL,
					      &rndis_proc_ops, params);
		if (!proc_entry) {
			kfree(params);
			rndis_put_nr(i);

			return ERR_PTR(-EIO);
		}
	}
#endif

	params->confignr = i;
	params->used = 1;
	params->state = RNDIS_UNINITIALIZED;
	params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
	params->resp_avail = resp_avail;
	params->v = v;
	INIT_LIST_HEAD(&params->resp_queue);
	pr_debug("%s: configNr = %d\n", __func__, i);

	return params;
}


static size_t csum_and_copy_to_pipe_iter(const void *addr, size_t bytes,
					 struct iov_iter *i, __wsum *sump)
{
	struct pipe_inode_info *pipe = i->pipe;
	unsigned int p_mask = pipe->ring_size - 1;
	__wsum sum = *sump;
	size_t off = 0;
	unsigned int i_head;
	size_t r;

	if (!sanity(i))
		return 0;

	bytes = push_pipe(i, bytes, &i_head, &r);
	while (bytes) {
		size_t chunk = min_t(size_t, bytes, PAGE_SIZE - r);
		char *p = kmap_local_page(pipe->bufs[i_head & p_mask].page);
		sum = csum_and_memcpy(p + r, addr + off, chunk, sum, off);
		kunmap_local(p);
		i->head = i_head;
		i->iov_offset = r + chunk;
		bytes -= chunk;
		off += chunk;
		r = 0;
		i_head++;
	}
	*sump = sum;
	i->count -= off;
	return off;
}


size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
			 struct iov_iter *i)
{
	size_t res = 0;
	if (unlikely(!page_copy_sane(page, offset, bytes)))
		return 0;
	page += offset / PAGE_SIZE; // first subpage
	offset %= PAGE_SIZE;
	while (1) {
		size_t n = __copy_page_to_iter(page, offset,
				min(bytes, (size_t)PAGE_SIZE - offset), i);
		res += n;
		bytes -= n;
		if (!bytes || !n)
			break;
		offset += n;
		if (offset == PAGE_SIZE) {
			page++;
			offset = 0;
		}
	}
	return res;
}


size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp,
		struct iov_iter *i)
{
#ifdef CONFIG_CRYPTO_HASH
	struct ahash_request *hash = hashp;
	struct scatterlist sg;
	size_t copied;

	copied = copy_to_iter(addr, bytes, i);
	sg_init_one(&sg, addr, copied);
	ahash_request_set_crypt(hash, &sg, NULL, copied);
	crypto_ahash_update(hash);
	return copied;
#else
	return 0;
#endif
}


void iov_iter_xarray(struct iov_iter *i, unsigned int direction,
		     struct xarray *xarray, loff_t start, size_t count)
{
	BUG_ON(direction & ~1);
	*i = (struct iov_iter) {
		.iter_type = ITER_XARRAY,
		.data_source = direction,
		.xarray = xarray,
		.xarray_start = start,
		.count = count,
		.iov_offset = 0
	};
}


struct iovec *iovec_from_user(const struct iovec __user *uvec,
		unsigned long nr_segs, unsigned long fast_segs,
		struct iovec *fast_iov, bool compat)
{
	struct iovec *iov = fast_iov;
	int ret;

	/*
	 * SuS says "The readv() function *may* fail if the iovcnt argument was
	 * less than or equal to 0, or greater than {IOV_MAX}.  Linux has
	 * traditionally returned zero for zero segments, so...
	 */
	if (nr_segs == 0)
		return iov;
	if (nr_segs > UIO_MAXIOV)
		return ERR_PTR(-EINVAL);
	if (nr_segs > fast_segs) {
		iov = kmalloc_array(nr_segs, sizeof(struct iovec), GFP_KERNEL);
		if (!iov)
			return ERR_PTR(-ENOMEM);
	}

	if (compat)
		ret = copy_compat_iovec_from_user(iov, uvec, nr_segs);
	else
		ret = copy_iovec_from_user(iov, uvec, nr_segs);
	if (ret) {
		if (iov != fast_iov)
			kfree(iov);
		return ERR_PTR(ret);
	}

	return iov;
}


static ssize_t pipe_get_pages_alloc(struct iov_iter *i,
		   struct page ***pages, size_t maxsize,
		   size_t *start)
{
	struct page **p;
	unsigned int iter_head, npages;
	ssize_t n;

	if (!sanity(i))
		return -EFAULT;

	data_start(i, &iter_head, start);
	/* Amount of free space: some of this one + all after this one */
	npages = pipe_space_for_user(iter_head, i->pipe->tail, i->pipe);
	n = npages * PAGE_SIZE - *start;
	if (maxsize > n)
		maxsize = n;
	else
		npages = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE);
	p = get_pages_array(npages);
	if (!p)
		return -ENOMEM;
	n = __pipe_get_pages(i, maxsize, p, iter_head, start);
	if (n > 0)
		*pages = p;
	else
		kvfree(p);
	return n;
}


R_API void r_core_anal_autoname_all_fcns(RCore *core) {
	RListIter *it;
	RAnalFunction *fcn;

	r_list_foreach (core->anal->fcns, it, fcn) {
		if (!strncmp (fcn->name, "fcn.", 4) || !strncmp (fcn->name, "sym.func.", 9)) {
			RFlagItem *item = r_flag_get (core->flags, fcn->name);
			if (item) {
				char *name = anal_fcn_autoname (core, fcn, 0, 0);
				if (name) {
					r_flag_rename (core->flags, item, name);
					free (fcn->name);
					fcn->name = name;
				}
			} else {
				// there should always be a flag for a function
				r_warn_if_reached ();
			}
		}
	}
}


static ut64 RAnalRef_val(const void *_ref1) {
	const RAnalRef* ref1 = _ref1;
	return ref1->addr;
}


static bool anal_block_cb(RAnalBlock *bb, BlockRecurseCtx *ctx) {
	if (r_cons_is_breaked ()) {
		return false;
	}
	if (bb->size < 1) {
		return true;
	}
	if (bb->size > ctx->core->anal->opt.bb_max_size) {
		return true;
	}
	int *parent_reg_set = r_pvector_at (&ctx->reg_set, r_pvector_len (&ctx->reg_set) - 1);
	int *reg_set = R_NEWS (int, REG_SET_SIZE);
	memcpy (reg_set, parent_reg_set, REG_SET_SIZE * sizeof (int));
	r_pvector_push (&ctx->reg_set, reg_set);
	RCore *core = ctx->core;
	RAnalFunction *fcn = ctx->fcn;
	fcn->stack = bb->parent_stackptr;
	ut64 pos = bb->addr;
	while (pos < bb->addr + bb->size) {
		if (r_cons_is_breaked ()) {
			break;
		}
		RAnalOp *op = r_core_anal_op (core, pos, R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_VAL | R_ANAL_OP_MASK_HINT);
		if (!op) {
			//eprintf ("Cannot get op\n");
			break;
		}
		r_anal_extract_rarg (core->anal, op, fcn, reg_set, &ctx->count);
		if (!ctx->argonly) {
			if (op->stackop == R_ANAL_STACK_INC) {
				fcn->stack += op->stackptr;
			} else if (op->stackop == R_ANAL_STACK_RESET) {
				fcn->stack = 0;
			}
			r_anal_extract_vars (core->anal, fcn, op);
		}
		int opsize = op->size;
		int optype = op->type;
		r_anal_op_free (op);
		if (opsize < 1) {
			break;
		}
		if (optype == R_ANAL_OP_TYPE_CALL) {
			size_t i;
			int max_count = fcn->cc ? r_anal_cc_max_arg (core->anal, fcn->cc) : 0;
			for (i = 0; i < max_count; i++) {
				reg_set[i] = 2;
			}
		}
		pos += opsize;
	}
	return true;
}


R_API int r_core_anal_search_xrefs(RCore *core, ut64 from, ut64 to, PJ *pj, int rad) {
	const bool cfg_debug = r_config_get_b (core->config, "cfg.debug");
	bool cfg_anal_strings = r_config_get_i (core->config, "anal.strings");
	ut64 at;
	int count = 0;
	int bsz = 8096;
	RAnalOp op = { 0 };

	if (from == to) {
		return -1;
	}
	if (from > to) {
		eprintf ("Invalid range (0x%"PFMT64x
		" >= 0x%"PFMT64x")\n", from, to);
		return -1;
	}

	if (core->blocksize <= OPSZ) {
		eprintf ("Error: block size too small\n");
		return -1;
	}
	ut8 *buf = malloc (bsz);
	if (!buf) {
		eprintf ("Error: cannot allocate a block\n");
		return -1;
	}
	ut8 *block = malloc (bsz);
	if (!block) {
		eprintf ("Error: cannot allocate a temp block\n");
		free (buf);
		return -1;
	}
	r_cons_break_push (NULL, NULL);
	at = from;
	st64 asm_sub_varmin = r_config_get_i (core->config, "asm.sub.varmin");
	int maxopsz = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MAX_OP_SIZE);
	int minopsz = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MIN_OP_SIZE);
	if (maxopsz < 1) {
		maxopsz = 4;
	}
	if (minopsz < 1) {
		minopsz = 1;
	}
	if (bsz < maxopsz) {
		// wtf
		eprintf ("Error: Something is really wrong deep inside\n");
		free (block);
		return -1;
	}
	while (at < to && !r_cons_is_breaked ()) {
		int i = 0, ret = bsz;
		if (!r_io_is_valid_offset (core->io, at, R_PERM_X)) {
			break;
		}
		ut64 left = to - at;
		if (bsz > left) {
			bsz = left;
		}
		(void)r_io_read_at (core->io, at, buf, bsz);
		memset (block, -1, bsz);
		if (!memcmp (buf, block, bsz)) {
		//	eprintf ("Error: skipping uninitialized block \n");
			at += ret;
			continue;
		}
		memset (block, 0, bsz);
		if (!memcmp (buf, block, bsz)) {
		//	eprintf ("Error: skipping uninitialized block \n");
			at += ret;
			continue;
		}
		(void) r_anal_op (core->anal, &op, at, buf, bsz, R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT);
		while ((i + maxopsz) < bsz && !r_cons_is_breaked ()) {
			r_anal_op_fini (&op);
			ret = r_anal_op (core->anal, &op, at + i, buf + i, bsz - i, R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT);
			if (ret < 1) {
				i += minopsz;
				continue;
			}
			i += ret;
			if (i > bsz) {
				// at += minopsz;
				break;
			}
			// find references
			if ((st64)op.val > asm_sub_varmin && op.val != UT64_MAX && op.val != UT32_MAX) {
				if (found_xref (core, op.addr, op.val, R_ANAL_REF_TYPE_DATA, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
			}
			// find references
			if (op.ptr && op.ptr != UT64_MAX && op.ptr != UT32_MAX) {
				if (found_xref (core, op.addr, op.ptr, R_ANAL_REF_TYPE_DATA, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
			}
			// find references
			if (op.addr > 512 && op.disp > 512 && op.disp && op.disp != UT64_MAX) {
				if (found_xref (core, op.addr, op.disp, R_ANAL_REF_TYPE_DATA, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
			}
			switch (op.type) {
			case R_ANAL_OP_TYPE_JMP:
			case R_ANAL_OP_TYPE_CJMP:
				if (found_xref (core, op.addr, op.jump, R_ANAL_REF_TYPE_CODE, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
				break;
			case R_ANAL_OP_TYPE_CALL:
			case R_ANAL_OP_TYPE_CCALL:
				if (found_xref (core, op.addr, op.jump, R_ANAL_REF_TYPE_CALL, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
				break;
			case R_ANAL_OP_TYPE_UJMP:
			case R_ANAL_OP_TYPE_IJMP:
			case R_ANAL_OP_TYPE_RJMP:
			case R_ANAL_OP_TYPE_IRJMP:
			case R_ANAL_OP_TYPE_MJMP:
			case R_ANAL_OP_TYPE_UCJMP:
				count++;
				if (found_xref (core, op.addr, op.ptr, R_ANAL_REF_TYPE_CODE, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
				break;
			case R_ANAL_OP_TYPE_UCALL:
			case R_ANAL_OP_TYPE_ICALL:
			case R_ANAL_OP_TYPE_RCALL:
			case R_ANAL_OP_TYPE_IRCALL:
			case R_ANAL_OP_TYPE_UCCALL:
				if (found_xref (core, op.addr, op.ptr, R_ANAL_REF_TYPE_CALL, pj, rad, cfg_debug, cfg_anal_strings)) {
					count++;
				}
				break;
			default:
				break;
			}
		}
		r_anal_op_fini (&op);
		if (i < 1) {
			break;
		}
		at += i + 1;
	}
	r_cons_break_pop ();
	free (buf);
	free (block);
	return count;
}


static void hint_node_print(HintNode *node, int mode, PJ *pj) {
	switch (mode) {
	case '*':
#define HINTCMD_ADDR(hint,fmt,x) r_cons_printf (fmt" @ 0x%"PFMT64x"\n", x, (hint)->addr)
		switch (node->type) {
		case HINT_NODE_ADDR: {
			const RAnalAddrHintRecord *record;
			r_vector_foreach (node->addr_hints, record) {
				switch (record->type) {
				case R_ANAL_ADDR_HINT_TYPE_IMMBASE:
					HINTCMD_ADDR (node, "ahi %d", record->immbase);
					break;
				case R_ANAL_ADDR_HINT_TYPE_JUMP:
					HINTCMD_ADDR (node, "ahc 0x%"PFMT64x, record->jump);
					break;
				case R_ANAL_ADDR_HINT_TYPE_FAIL:
					HINTCMD_ADDR (node, "ahf 0x%"PFMT64x, record->fail);
					break;
				case R_ANAL_ADDR_HINT_TYPE_STACKFRAME:
					HINTCMD_ADDR (node, "ahF 0x%"PFMT64x, record->stackframe);
					break;
				case R_ANAL_ADDR_HINT_TYPE_PTR:
					HINTCMD_ADDR (node, "ahp 0x%"PFMT64x, record->ptr);
					break;
				case R_ANAL_ADDR_HINT_TYPE_NWORD:
					// no command for this
					break;
				case R_ANAL_ADDR_HINT_TYPE_RET:
					HINTCMD_ADDR (node, "ahr 0x%"PFMT64x, record->retval);
					break;
				case R_ANAL_ADDR_HINT_TYPE_NEW_BITS:
					// no command for this
					break;
				case R_ANAL_ADDR_HINT_TYPE_SIZE:
					HINTCMD_ADDR (node, "ahs 0x%"PFMT64x, record->size);
					break;
				case R_ANAL_ADDR_HINT_TYPE_SYNTAX:
					HINTCMD_ADDR (node, "ahS %s", record->syntax); // TODO: escape for newcmd
					break;
				case R_ANAL_ADDR_HINT_TYPE_OPTYPE: {
					const char *type = r_anal_optype_to_string (record->optype);
					if (type) {
						HINTCMD_ADDR (node, "aho %s", type); // TODO: escape for newcmd
					}
					break;
				}
				case R_ANAL_ADDR_HINT_TYPE_OPCODE:
					HINTCMD_ADDR (node, "ahd %s", record->opcode);
					break;
				case R_ANAL_ADDR_HINT_TYPE_TYPE_OFFSET:
					HINTCMD_ADDR (node, "aht %s", record->type_offset); // TODO: escape for newcmd
					break;
				case R_ANAL_ADDR_HINT_TYPE_ESIL:
					HINTCMD_ADDR (node, "ahe %s", record->esil); // TODO: escape for newcmd
					break;
				case R_ANAL_ADDR_HINT_TYPE_HIGH:
					r_cons_printf ("ahh @ 0x%"PFMT64x"\n", node->addr);
					break;
				case R_ANAL_ADDR_HINT_TYPE_VAL:
					// no command for this
					break;
				}
			}
			break;
		}
		case HINT_NODE_ARCH:
			HINTCMD_ADDR (node, "aha %s", r_str_get_fail (node->arch, "0"));
			break;
		case HINT_NODE_BITS:
			HINTCMD_ADDR (node, "ahb %d", node->bits);
			break;
		}
#undef HINTCMD_ADDR
		break;
	case 'j':
		switch (node->type) {
		case HINT_NODE_ADDR: {
			const RAnalAddrHintRecord *record;
			r_vector_foreach (node->addr_hints, record) {
				switch (record->type) {
				case R_ANAL_ADDR_HINT_TYPE_IMMBASE:
					pj_ki (pj, "immbase", record->immbase);
					break;
				case R_ANAL_ADDR_HINT_TYPE_JUMP:
					pj_kn (pj, "jump", record->jump);
					break;
				case R_ANAL_ADDR_HINT_TYPE_FAIL:
					pj_kn (pj, "fail", record->fail);
					break;
				case R_ANAL_ADDR_HINT_TYPE_STACKFRAME:
					pj_kn (pj, "stackframe", record->stackframe);
					break;
				case R_ANAL_ADDR_HINT_TYPE_PTR:
					pj_kn (pj, "ptr", record->ptr);
					break;
				case R_ANAL_ADDR_HINT_TYPE_NWORD:
					pj_ki (pj, "nword", record->nword);
					break;
				case R_ANAL_ADDR_HINT_TYPE_RET:
					pj_kn (pj, "ret", record->retval);
					break;
				case R_ANAL_ADDR_HINT_TYPE_NEW_BITS:
					pj_ki (pj, "newbits", record->newbits);
					break;
				case R_ANAL_ADDR_HINT_TYPE_SIZE:
					pj_kn (pj, "size", record->size);
					break;
				case R_ANAL_ADDR_HINT_TYPE_SYNTAX:
					pj_ks (pj, "syntax", record->syntax);
					break;
				case R_ANAL_ADDR_HINT_TYPE_OPTYPE: {
					const char *type = r_anal_optype_to_string (record->optype);
					if (type) {
						pj_ks (pj, "type", type);
					}
					break;
				}
				case R_ANAL_ADDR_HINT_TYPE_OPCODE:
					pj_ks (pj, "opcode", record->opcode);
					break;
				case R_ANAL_ADDR_HINT_TYPE_TYPE_OFFSET:
					pj_ks (pj, "offset", record->type_offset);
					break;
				case R_ANAL_ADDR_HINT_TYPE_ESIL:
					pj_ks (pj, "esil", record->esil);
					break;
				case R_ANAL_ADDR_HINT_TYPE_HIGH:
					pj_kb (pj, "high", true);
					break;
				case R_ANAL_ADDR_HINT_TYPE_VAL:
					pj_kn (pj, "val", record->val);
					break;
				}
			}
			break;
		}
		case HINT_NODE_ARCH:
			if (node->arch) {
				pj_ks (pj, "arch", node->arch);
			} else {
				pj_knull (pj, "arch");
			}
			break;
		case HINT_NODE_BITS:
			pj_ki (pj, "bits", node->bits);
			break;
		}
		break;
	default:
		print_hint_h_format (node);
		break;
	}
}


static void handle_var_stack_access(RAnalEsil *esil, ut64 addr, RAnalVarAccessType type, int len) {
	EsilBreakCtx *ctx = esil->user;
	const char *regname = reg_name_for_access (ctx->op, type);
	if (ctx->fcn && regname) {
		ut64 spaddr = r_reg_getv (esil->anal->reg, ctx->spname);
		if (addr >= spaddr && addr < ctx->initial_sp) {
			int stack_off = addr - ctx->initial_sp;
			RAnalVar *var = r_anal_function_get_var (ctx->fcn, R_ANAL_VAR_KIND_SPV, stack_off);
			if (!var) {
				var = r_anal_function_get_var (ctx->fcn, R_ANAL_VAR_KIND_BPV, stack_off);
			}
			if (!var && stack_off >= -ctx->fcn->maxstack) {
				char *varname;
				varname = ctx->fcn->anal->opt.varname_stack
					? r_str_newf ("var_%xh", R_ABS (stack_off))
					: r_anal_function_autoname_var (ctx->fcn, R_ANAL_VAR_KIND_SPV, "var", delta_for_access (ctx->op, type));
				var = r_anal_function_set_var (ctx->fcn, stack_off, R_ANAL_VAR_KIND_SPV, NULL, len, false, varname);
				free (varname);
			}
			if (var) {
				r_anal_var_set_access (var, regname, ctx->op->addr, type, delta_for_access (ctx->op, type));
			}
		}
	}
}


static int fcn_list_json(RCore *core, RList *fcns, bool quiet) {
	RListIter *iter;
	RAnalFunction *fcn;
	PJ *pj = r_core_pj_new (core);
	if (!pj) {
		r_cons_println ("[]");
		return -1;
	}
	pj_a (pj);
	r_list_foreach (fcns, iter, fcn) {
		if (quiet) {
			pj_n (pj, fcn->addr);
		} else {
			fcn_print_json (core, fcn, pj);
		}
	}
	pj_end (pj);
	r_cons_println (pj_string (pj));
	pj_free (pj);
	return 0;
}


R_API RCoreAnalStats* r_core_anal_get_stats(RCore *core, ut64 from, ut64 to, ut64 step) {
	RAnalFunction *F;
	RAnalBlock  *B;
	RBinSymbol *S;
	RListIter *iter, *iter2;
	RCoreAnalStats *as = NULL;
	int piece, as_size, blocks;
	ut64 at;

	if (from == to || from == UT64_MAX || to == UT64_MAX) {
		eprintf ("Cannot alloc for this range\n");
		return NULL;
	}
	as = R_NEW0 (RCoreAnalStats);
	if (!as) {
		return NULL;
	}
	if (step < 1) {
		step = 1;
	}
	blocks = (to - from) / step;
	as_size = (1 + blocks) * sizeof (RCoreAnalStatsItem);
	as->block = malloc (as_size);
	if (!as->block) {
		free (as);
		return NULL;
	}
	memset (as->block, 0, as_size);
	for (at = from; at < to; at += step) {
		RIOMap *map = r_io_map_get_at (core->io, at);
		piece = (at - from) / step;
		as->block[piece].perm = map ? map->perm: (core->io->desc ? core->io->desc->perm: 0);
	}
	// iter all flags
	struct block_flags_stat_t u = { .step = step, .from = from, .as = as };
	r_flag_foreach_range (core->flags, from, to + 1, block_flags_stat, &u);
	// iter all functions
	r_list_foreach (core->anal->fcns, iter, F) {
		if (F->addr < from || F->addr > to) {
			continue;
		}
		piece = (F->addr - from) / step;
		as->block[piece].functions++;
		ut64 last_piece = R_MIN ((F->addr + r_anal_function_linear_size (F) - 1) / step, blocks - 1);
		for (; piece <= last_piece; piece++) {
			as->block[piece].in_functions++;
		}
		// iter all basic blocks
		r_list_foreach (F->bbs, iter2, B) {
			if (B->addr < from || B->addr > to) {
				continue;
			}
			piece = (B->addr - from) / step;
			as->block[piece].blocks++;
		}
	}
	// iter all symbols
	r_list_foreach (r_bin_get_symbols (core->bin), iter, S) {
		if (S->vaddr < from || S->vaddr > to) {
			continue;
		}
		piece = (S->vaddr - from) / step;
		as->block[piece].symbols++;
	}
	RPVector *metas = to > from ? r_meta_get_all_intersect (core->anal, from, to - from, R_META_TYPE_ANY) : NULL;
	if (metas) {
		void **it;
		r_pvector_foreach (metas, it) {
			RIntervalNode *node = *it;
			RAnalMetaItem *mi = node->data;
			if (node->start < from || node->end > to) {
				continue;
			}
			piece = (node->start - from) / step;
			switch (mi->type) {
			case R_META_TYPE_STRING:
				as->block[piece].strings++;
				break;
			case R_META_TYPE_COMMENT:
				as->block[piece].comments++;
				break;
			default:
				break;
			}
		}
		r_pvector_free (metas);
	}
	return as;
}


R_API char *r_core_anal_fcn_name(RCore *core, RAnalFunction *fcn) {
	bool demangle = r_config_get_i (core->config, "bin.demangle");
	const char *lang = demangle ? r_config_get (core->config, "bin.lang") : NULL;
	bool keep_lib = r_config_get_i (core->config, "bin.demangle.libs");
	char *name = strdup (r_str_get (fcn->name));
	if (demangle) {
		char *tmp = r_bin_demangle (core->bin->cur, lang, name, fcn->addr, keep_lib);
		if (tmp) {
			free (name);
			name = tmp;
		}
	}
	return name;
}


static void print_hint_tree(RBTree tree, int mode) {
#define END_ADDR if (mode == 'j') { pj_end (pj); } else if (mode != '*') { r_cons_newline (); }
	PJ *pj = NULL;
	if (mode == 'j') {
		pj = pj_new ();
		pj_a (pj);
	}
	RBIter it;
	HintNode *node;
	ut64 last_addr = 0;
	bool in_addr = false;
	r_rbtree_foreach (tree, it, node, HintNode, rb) {
		if (!in_addr || last_addr != node->addr) {
			if (in_addr) {
				END_ADDR
			}
			in_addr = true;
			last_addr = node->addr;
			if (pj) {
				pj_o (pj);
				pj_kn (pj, "addr", node->addr);
			} else if (mode != '*') {
				r_cons_printf (" 0x%08"PFMT64x" =>", node->addr);
			}
		}
		hint_node_print (node, mode, pj);
	}
	if (in_addr) {
		END_ADDR
	}
	if (pj) {
		pj_end (pj);
		r_cons_printf ("%s\n", pj_string (pj));
		pj_free (pj);
	}
#undef END_ADDR
}


static bool esilbreak_reg_write(RAnalEsil *esil, const char *name, ut64 *val) {
	if (!esil) {
		return false;
	}
	RAnal *anal = esil->anal;
	EsilBreakCtx *ctx = esil->user;
	RAnalOp *op = ctx->op;
	RCore *core = anal->coreb.core;
	handle_var_stack_access (esil, *val, R_ANAL_VAR_ACCESS_TYPE_PTR, esil->anal->bits / 8);
	//specific case to handle blx/bx cases in arm through emulation
	// XXX this thing creates a lot of false positives
	ut64 at = *val;
	if (anal && anal->opt.armthumb) {
		if (anal->cur && anal->cur->arch && anal->bits < 33 &&
			strstr (anal->cur->arch, "arm") && !strcmp (name, "pc") && op) {
			switch (op->type) {
			case R_ANAL_OP_TYPE_UCALL: // BLX
			case R_ANAL_OP_TYPE_UJMP: // BX
				// maybe UJMP/UCALL is enough here
				if (!(*val & 1)) {
					r_anal_hint_set_bits (anal, *val, 32);
				} else {
					ut64 snv = r_reg_getv (anal->reg, "pc");
					if (snv != UT32_MAX && snv != UT64_MAX) {
						if (r_io_is_valid_offset (anal->iob.io, *val, 1)) {
							r_anal_hint_set_bits (anal, *val - 1, 16);
						}
					}
				}
				break;
			default:
				break;
			}
		}
	}
	if (core->rasm->bits == 32 && strstr (core->rasm->cur->name, "arm")) {
		if ((!(at & 1)) && r_io_is_valid_offset (anal->iob.io, at, 0)) { //  !core->anal->opt.noncode)) {
			add_string_ref (anal->coreb.core, esil->address, at);
		}
	}
	return 0;
}


static ut64 *next_append(ut64 *next, int *nexti, ut64 v) {
	ut64 *tmp_next = realloc (next, sizeof (ut64) * (1 + *nexti));
	if (!tmp_next) {
		return NULL;
	}
	next = tmp_next;
	next[*nexti] = v;
	(*nexti)++;
	return next;
}


static int esp4_err(struct sk_buff *skb, u32 info)
{
	struct net *net = dev_net(skb->dev);
	const struct iphdr *iph = (const struct iphdr *)skb->data;
	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
	struct xfrm_state *x;

	switch (icmp_hdr(skb)->type) {
	case ICMP_DEST_UNREACH:
		if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
			return 0;
		break;
	case ICMP_REDIRECT:
		break;
	default:
		return 0;
	}

	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
			      esph->spi, IPPROTO_ESP, AF_INET);
	if (!x)
		return 0;

	if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
		ipv4_update_pmtu(skb, net, info, 0, IPPROTO_ESP);
	else
		ipv4_redirect(skb, net, 0, IPPROTO_ESP);
	xfrm_state_put(x);

	return 0;
}


static int esp_init_aead(struct xfrm_state *x)
{
	char aead_name[CRYPTO_MAX_ALG_NAME];
	struct crypto_aead *aead;
	int err;

	err = -ENAMETOOLONG;
	if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
		     x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME)
		goto error;

	aead = crypto_alloc_aead(aead_name, 0, 0);
	err = PTR_ERR(aead);
	if (IS_ERR(aead))
		goto error;

	x->data = aead;

	err = crypto_aead_setkey(aead, x->aead->alg_key,
				 (x->aead->alg_key_len + 7) / 8);
	if (err)
		goto error;

	err = crypto_aead_setauthsize(aead, x->aead->alg_icv_len / 8);
	if (err)
		goto error;

error:
	return err;
}


static struct ip_esp_hdr *esp_output_tcp_encap(struct xfrm_state *x,
						    struct sk_buff *skb,
						    struct esp_info *esp)
{
	return ERR_PTR(-EOPNOTSUPP);
}


static void esp_ssg_unref(struct xfrm_state *x, void *tmp)
{
	struct crypto_aead *aead = x->data;
	int extralen = 0;
	u8 *iv;
	struct aead_request *req;
	struct scatterlist *sg;

	if (x->props.flags & XFRM_STATE_ESN)
		extralen += sizeof(struct esp_output_extra);

	iv = esp_tmp_iv(aead, tmp, extralen);
	req = esp_tmp_req(aead, iv);

	/* Unref skb_frag_pages in the src scatterlist if necessary.
	 * Skip the first sg which comes from skb->data.
	 */
	if (req->src != req->dst)
		for (sg = sg_next(req->src); sg; sg = sg_next(sg))
			put_page(sg_page(sg));
}


static int esp4_rcv_cb(struct sk_buff *skb, int err)
{
	return 0;
}


static int esp_output_tail_tcp(struct xfrm_state *x, struct sk_buff *skb)
{
	kfree_skb(skb);

	return -EOPNOTSUPP;
}


static int __init esp4_init(void)
{
	if (xfrm_register_type(&esp_type, AF_INET) < 0) {
		pr_info("%s: can't add xfrm type\n", __func__);
		return -EAGAIN;
	}
	if (xfrm4_protocol_register(&esp4_protocol, IPPROTO_ESP) < 0) {
		pr_info("%s: can't add protocol\n", __func__);
		xfrm_unregister_type(&esp_type, AF_INET);
		return -EAGAIN;
	}
	return 0;
}


static inline int esp_remove_trailer(struct sk_buff *skb)
{
	struct xfrm_state *x = xfrm_input_state(skb);
	struct xfrm_offload *xo = xfrm_offload(skb);
	struct crypto_aead *aead = x->data;
	int alen, hlen, elen;
	int padlen, trimlen;
	__wsum csumdiff;
	u8 nexthdr[2];
	int ret;

	alen = crypto_aead_authsize(aead);
	hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
	elen = skb->len - hlen;

	if (xo && (xo->flags & XFRM_ESP_NO_TRAILER)) {
		ret = xo->proto;
		goto out;
	}

	if (skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2))
		BUG();

	ret = -EINVAL;
	padlen = nexthdr[0];
	if (padlen + 2 + alen >= elen) {
		net_dbg_ratelimited("ipsec esp packet is garbage padlen=%d, elen=%d\n",
				    padlen + 2, elen - alen);
		goto out;
	}

	trimlen = alen + padlen + 2;
	if (skb->ip_summed == CHECKSUM_COMPLETE) {
		csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0);
		skb->csum = csum_block_sub(skb->csum, csumdiff,
					   skb->len - trimlen);
	}
	pskb_trim(skb, skb->len - trimlen);

	ret = nexthdr[1];

out:
	return ret;
}


static struct ip_esp_hdr *esp6_output_tcp_encap(struct xfrm_state *x,
						struct sk_buff *skb,
						struct esp_info *esp)
{
	__be16 *lenp = (void *)esp->esph;
	struct ip_esp_hdr *esph;
	unsigned int len;
	struct sock *sk;

	len = skb->len + esp->tailen - skb_transport_offset(skb);
	if (len > IP_MAX_MTU)
		return ERR_PTR(-EMSGSIZE);

	rcu_read_lock();
	sk = esp6_find_tcp_sk(x);
	rcu_read_unlock();

	if (IS_ERR(sk))
		return ERR_CAST(sk);

	*lenp = htons(len);
	esph = (struct ip_esp_hdr *)(lenp + 1);

	return esph;
}


static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc)
{
    VirtQueueElement *elem;
    VirtQueue *vq = vvc->event_vq;
    struct virtio_vsock_event event = {
        .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET),
    };

    elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
    if (!elem) {
        error_report("vhost-vsock missed transport reset event");
        return;
    }

    if (elem->out_num) {
        error_report("invalid vhost-vsock event virtqueue element with "
                     "out buffers");
        goto err;
    }

    if (iov_from_buf(elem->in_sg, elem->in_num, 0,
                     &event, sizeof(event)) != sizeof(event)) {
        error_report("vhost-vsock event virtqueue element is too short");
        goto err;
    }

    virtqueue_push(vq, elem, sizeof(event));
    virtio_notify(VIRTIO_DEVICE(vvc), vq);

    g_free(elem);
    return;

err:
    virtqueue_detach_element(vq, elem, 0);
    g_free(elem);
}


static void vover_new(void)
{
	int voice, mvoice;

	mvoice = curvoice->mvoice;
	for (voice = curvoice - voice_tb + 1; voice <= nvoice; voice++)
		if (voice_tb[voice].mvoice == mvoice)
			break;
	if (voice > nvoice) {
		if (nvoice >= MAXVOICE) {
			syntax("Too many voices", 0);
			return;
		}
		nvoice = voice;
		voice_tb[voice].id[0] = '&';
		voice_tb[voice].mvoice = mvoice;
	}
	voice_tb[voice].ulen = curvoice->ulen;
	voice_tb[voice].microscale = curvoice->microscale;
	curvoice = &voice_tb[voice];
}


static char *parse_gchord(char *p)
{
	char *q;
	int l, l2;

	q = p;
	while (*p != '"') {
		if (*p == '\\')
			p++;
		if (*p == '\0') {
			syntax("No end of guitar chord", p);
			break;
		}
		p++;
	}
	l = p - q;
	if (gchord) {
		char *gch;

		/* many guitar chords: concatenate with '\n' */
		l2 = strlen(gchord);
		gch = getarena(l2 + 1 + l + 1);
		strcpy(gch, gchord);
		gch[l2++] = '\n';
		strncpy(&gch[l2], q, l);
		gch[l2 + l] = '\0';
		gchord = gch;
	} else {
		gchord = getarena(l + 1);
		strncpy(gchord, q, l);
		gchord[l] = '\0';
	}
	if (*p != '\0')
		p++;
	return p;
}


static void gch_capo(struct SYMBOL *s)
{
	char *p = s->text, *q, *r;
	int i, l, li = 0;
	static const char *capo_txt = "  (capo: %d)";
	static signed char cap_trans[] =
		{0, 5, -2, 3, -4, 1, -6, -1, 4, -3, 2, -5};

	// search the chord symbols
	for (;;) {
		if (!strchr("^_<>@", *p))
			break;
		p = strchr(p, '\n');
		if (!p)
			return;
		p++;
	}

	// add a capo chord symbol
	i = p - s->text;
	q = strchr(p + 1, '\n');
	if (q)
		l = q - p;
	else
		l = strlen(p);
	if (!capo) {
		capo = 1;
		li = strlen(capo_txt);
	}
	r = (char *) getarena(strlen(s->text) + l + li + 1);
	i += l;
	strncpy(r, s->text, i);		// annotations + chord symbol
	r[i++] = '\n';
	strncpy(r + i, p, l);		// capo
	if (li) {
		sprintf(r + i + l, capo_txt, cfmt.capo);
		l += li;
	}
	if (q)
		strcpy(r + i + l, q);	// ending annotations
	s->text = r;
	gch_tr1(s, i, cap_trans[cfmt.capo % 12]);
}


static int sr9700_get_eeprom_len(struct net_device *netdev)
{
	return SR_EEPROM_LEN;
}


static int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data)
{
	int err;

	err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data,
			      length);
	if ((err != length) && (err >= 0))
		err = -EINVAL;
	return err;
}


static int sr_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
{
	return sr_share_read_word(dev, 0, offset, value);
}


static int sr_write_reg(struct usbnet *dev, u8 reg, u8 value)
{
	return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG,
				value, reg, NULL, 0);
}


static void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
{
	usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG,
			       value, reg, NULL, 0);
}


static int sr_mdio_read(struct net_device *netdev, int phy_id, int loc)
{
	struct usbnet *dev = netdev_priv(netdev);
	__le16 res;
	int rc = 0;

	if (phy_id) {
		netdev_dbg(netdev, "Only internal phy supported\n");
		return 0;
	}

	/* Access NSR_LINKST bit for link status instead of MII_BMSR */
	if (loc == MII_BMSR) {
		u8 value;

		sr_read_reg(dev, SR_NSR, &value);
		if (value & NSR_LINKST)
			rc = 1;
	}
	sr_share_read_word(dev, 1, loc, &res);
	if (rc == 1)
		res = le16_to_cpu(res) | BMSR_LSTATUS;
	else
		res = le16_to_cpu(res) & ~BMSR_LSTATUS;

	netdev_dbg(netdev, "sr_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
		   phy_id, loc, res);

	return res;
}


nwfilterConnectListAllNWFilterBindings(virConnectPtr conn,
                                       virNWFilterBindingPtr **bindings,
                                       unsigned int flags)
{
    virCheckFlags(0, -1);

    if (virConnectListAllNWFilterBindingsEnsureACL(conn) < 0)
        return -1;

    return virNWFilterBindingObjListExport(driver->bindings, conn, bindings,
                                           virConnectListAllNWFilterBindingsCheckACL);
}


static void nwfilterDriverUnlock(void)
{
    virMutexUnlock(&driver->lock);
}


static int virtbt_setup_intel(struct hci_dev *hdev)
{
	struct sk_buff *skb;

	/* Intel Read Version */
	skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
	if (IS_ERR(skb))
		return PTR_ERR(skb);

	kfree_skb(skb);
	return 0;
}


static void st21nfca_se_get_atr(struct nfc_hci_dev *hdev)
{
	int r;
	struct sk_buff *skb;
	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);

	r = nfc_hci_get_param(hdev, ST21NFCA_APDU_READER_GATE,
			ST21NFCA_PARAM_ATR, &skb);
	if (r < 0)
		return;

	if (skb->len <= ST21NFCA_ESE_MAX_LENGTH) {
		memcpy(info->se_info.atr, skb->data, skb->len);
		info->se_info.wt_timeout =
			ST21NFCA_BWI_TO_TIMEOUT(st21nfca_se_get_bwi(hdev));
	}
	kfree_skb(skb);
}


static int xudc_get_frame(struct usb_gadget *gadget)
{
	struct xusb_udc *udc;
	int frame;

	if (!gadget)
		return -ENODEV;

	udc = to_udc(gadget);
	frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET);
	return frame;
}


static void xudc_clear_stall_all_ep(struct xusb_udc *udc)
{
	struct xusb_ep *ep;
	u32 epcfgreg;
	int i;

	for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
		ep = &udc->ep[i];
		epcfgreg = udc->read_fn(udc->addr + ep->offset);
		epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
		udc->write_fn(udc->addr, ep->offset, epcfgreg);
		if (ep->epnumber) {
			/* Reset the toggle bit.*/
			epcfgreg = udc->read_fn(udc->addr + ep->offset);
			epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
			udc->write_fn(udc->addr, ep->offset, epcfgreg);
		}
	}
}


static void xudc_ep0_out(struct xusb_udc *udc)
{
	struct xusb_ep *ep0 = &udc->ep[0];
	struct xusb_req *req;
	u8 *ep0rambase;
	unsigned int bytes_to_rx;
	void *buffer;

	req = list_first_entry(&ep0->queue, struct xusb_req, queue);

	switch (udc->setupseqrx) {
	case STATUS_PHASE:
		/*
		 * This resets both state machines for the next
		 * Setup packet.
		 */
		udc->setupseqrx = SETUP_PHASE;
		udc->setupseqtx = SETUP_PHASE;
		req->usb_req.actual = req->usb_req.length;
		xudc_done(ep0, req, 0);
		break;
	case DATA_PHASE:
		bytes_to_rx = udc->read_fn(udc->addr +
					   XUSB_EP_BUF0COUNT_OFFSET);
		/* Copy the data to be received from the DPRAM. */
		ep0rambase = (u8 __force *) (udc->addr +
			     (ep0->rambase << 2));
		buffer = req->usb_req.buf + req->usb_req.actual;
		req->usb_req.actual = req->usb_req.actual + bytes_to_rx;
		memcpy(buffer, ep0rambase, bytes_to_rx);

		if (req->usb_req.length == req->usb_req.actual) {
			/* Data transfer completed get ready for Status stage */
			xudc_wrstatus(udc);
		} else {
			/* Enable EP0 buffer to receive data */
			udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
			udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
		}
		break;
	default:
		break;
	}
}


static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
	struct xusb_ep *ep	= to_xusb_ep(_ep);
	struct xusb_req *req	= to_xusb_req(_req);
	struct xusb_udc *udc	= ep->udc;
	unsigned long flags;

	spin_lock_irqsave(&udc->lock, flags);
	/* Make sure it's actually queued on this endpoint */
	list_for_each_entry(req, &ep->queue, queue) {
		if (&req->usb_req == _req)
			break;
	}
	if (&req->usb_req != _req) {
		spin_unlock_irqrestore(&udc->lock, flags);
		return -EINVAL;
	}
	xudc_done(ep, req, -ECONNRESET);
	spin_unlock_irqrestore(&udc->lock, flags);

	return 0;
}


static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
				 struct dquot *dquot, uint blk)
{
	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
	loff_t ret = 0;
	int i;
	char *ddquot;

	if (!buf)
		return -ENOMEM;
	ret = read_blk(info, blk, buf);
	if (ret < 0) {
		quota_error(dquot->dq_sb, "Can't read quota tree "
			    "block %u", blk);
		goto out_buf;
	}
	ddquot = buf + sizeof(struct qt_disk_dqdbheader);
	for (i = 0; i < qtree_dqstr_in_blk(info); i++) {
		if (info->dqi_ops->is_id(ddquot, dquot))
			break;
		ddquot += info->dqi_entry_size;
	}
	if (i == qtree_dqstr_in_blk(info)) {
		quota_error(dquot->dq_sb,
			    "Quota for id %u referenced but not present",
			    from_kqid(&init_user_ns, dquot->dq_id));
		ret = -EIO;
		goto out_buf;
	} else {
		ret = ((loff_t)blk << info->dqi_blocksize_bits) + sizeof(struct
		  qt_disk_dqdbheader) + i * info->dqi_entry_size;
	}
out_buf:
	kfree(buf);
	return ret;
}


static void ext_session_lock_v1_handle_locked(void *data, struct ext_session_lock_v1 *lock) {
	// Who cares
}


static void handle_global_remove(void *data, struct wl_registry *registry,
		uint32_t name) {
	struct swaylock_state *state = data;
	struct swaylock_surface *surface;
	wl_list_for_each(surface, &state->surfaces, link) {
		if (surface->output_global_name == name) {
			destroy_surface(surface);
			break;
		}
	}
}


static uint32_t parse_color(const char *color) {
	if (color[0] == '#') {
		++color;
	}

	int len = strlen(color);
	if (len != 6 && len != 8) {
		swaylock_log(LOG_DEBUG, "Invalid color %s, defaulting to 0xFFFFFFFF",
				color);
		return 0xFFFFFFFF;
	}
	uint32_t res = (uint32_t)strtoul(color, NULL, 16);
	if (strlen(color) == 6) {
		res = (res << 8) | 0xFF;
	}
	return res;
}


static void layer_surface_closed(void *data,
		struct zwlr_layer_surface_v1 *layer_surface) {
	struct swaylock_surface *surface = data;
	destroy_surface(surface);
}


R_API void r_anal_extract_vars(RAnal *anal, RAnalFunction *fcn, RAnalOp *op) {
	r_return_if_fail (anal && fcn && op);

	const char *BP = anal->reg->name[R_REG_NAME_BP];
	if (BP) {
		extract_arg (anal, fcn, op, BP, "+", R_ANAL_VAR_KIND_BPV);
		extract_arg (anal, fcn, op, BP, "-", R_ANAL_VAR_KIND_BPV);
	}
	const char *SP = anal->reg->name[R_REG_NAME_SP];
	if (SP) {
		extract_arg (anal, fcn, op, SP, "+", R_ANAL_VAR_KIND_SPV);
	}
}


R_API bool r_anal_var_check_name(const char *name) {
	return !isdigit ((unsigned char)*name) && strcspn (name, "., =/");
}


R_API ut64 r_anal_var_addr(RAnalVar *var) {
	r_return_val_if_fail (var, UT64_MAX);
	RAnal *anal = var->fcn->anal;
	const char *regname = NULL;
	if (var->kind == R_ANAL_VAR_KIND_BPV) {
		regname = r_reg_get_name (anal->reg, R_REG_NAME_BP);
		return r_reg_getv (anal->reg, regname) + var->delta + var->fcn->bp_off;
	} else if (var->kind == R_ANAL_VAR_KIND_SPV) {
		regname = r_reg_get_name (anal->reg, R_REG_NAME_SP);
		return r_reg_getv (anal->reg, regname) + var->delta;
	}
	return 0;
}


R_API int r_anal_var_count_args(RAnalFunction *fcn) {
	r_return_val_if_fail (fcn, 0); // No function implies no variables, but probably mistake
	int args = 0;
	void **it;
	r_pvector_foreach (&fcn->vars, it) {
		RAnalVar *var = *it;
		if (var->isarg) {
			args++;
		}
	}
	return args;
}


static const char *__int_type_from_size(int size) {
	switch (size) {
	case 1: return "int8_t";
	case 2: return "int16_t";
	case 4: return "int32_t";
	case 8: return "int64_t";
	default: return NULL;
	}
}


R_API void r_anal_function_vars_cache_fini(RAnalFcnVarsCache *cache) {
	if (!cache) {
		return;
	}
	r_list_free (cache->bvars);
	r_list_free (cache->rvars);
	r_list_free (cache->svars);
}


static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
				    volatile parse_context *ctx)
{
    pjmedia_sdp_attr *attr;

    ctx->last_error = PJMEDIA_SDP_EINATTR;

    attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);

    /* check equal sign */
    if (*(scanner->curptr+1) != '=') {
	on_scanner_error(scanner);
	return NULL;
    }

    /* skip a= */
    pj_scan_advance_n(scanner, 2, SKIP_WS);

    /* get attr name. */
    pj_scan_get(scanner, &cs_token, &attr->name);

    if (*scanner->curptr && *scanner->curptr != '\r' &&
	*scanner->curptr != '\n')
    {
	/* skip ':' if present. */
	if (*scanner->curptr == ':')
	    pj_scan_get_char(scanner);

	/* get value */
	if (*scanner->curptr != '\r' && *scanner->curptr != '\n') {
	    pj_scan_get_until_chr(scanner, "\r\n", &attr->value);
	} else {
	    attr->value.ptr = NULL;
	    attr->value.slen = 0;
	}

    } else {
	attr->value.ptr = NULL;
	attr->value.slen = 0;
    }

    /* We've got what we're looking for, skip anything until newline */
    pj_scan_skip_line(scanner);

    return attr;
}


static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
			       volatile parse_context *ctx)
{
    ctx->last_error = PJMEDIA_SDP_EINSDP;

    /* check equal sign */
    if (*(scanner->curptr+1) != '=') {
	on_scanner_error(scanner);
	return;
    }

    /* x= */
    pj_scan_advance_n(scanner, 2, SKIP_WS);

    /* get anything until newline (including whitespaces). */
    pj_scan_get_until_chr(scanner, "\r\n", str);

    /* newline. */
    pj_scan_get_newline(scanner);
}


static int print_media_desc(const pjmedia_sdp_media *m, char *buf, pj_size_t len)
{
    char *p = buf;
    char *end = buf+len;
    unsigned i;
    int printed;

    /* check length for the "m=" line. */
    if (len < (pj_size_t)m->desc.media.slen+m->desc.transport.slen+12+24) {
	return -1;
    }
    *p++ = 'm';	    /* m= */
    *p++ = '=';
    pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
    p += m->desc.media.slen;
    *p++ = ' ';
    printed = pj_utoa(m->desc.port, p);
    p += printed;
    if (m->desc.port_count > 1) {
	*p++ = '/';
	printed = pj_utoa(m->desc.port_count, p);
	p += printed;
    }
    *p++ = ' ';
    pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
    p += m->desc.transport.slen;
    for (i=0; i<m->desc.fmt_count; ++i) {
	if (end-p > m->desc.fmt[i].slen) {
	    *p++ = ' ';
	    pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
	    p += m->desc.fmt[i].slen;
	} else {
	    return -1;
	}
    }

    if (end-p >= 2) {
	*p++ = '\r';
	*p++ = '\n';
    } else {
	return -1;
    }

    /* print connection info, if present. */
    if (m->conn) {
	printed = print_connection_info(m->conn, p, (int)(end-p));
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

    /* print optional bandwidth info. */
    for (i=0; i<m->bandw_count; ++i) {
	printed = (int)print_bandw(m->bandw[i], p, end-p);
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

    /* print attributes. */
    for (i=0; i<m->attr_count; ++i) {
	printed = (int)print_attr(m->attr[i], p, end-p);
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

    return (int)(p-buf);
}


PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_rtcp(pj_pool_t *pool,
						       const pj_sockaddr *a)
{
    enum {
	ATTR_LEN = PJ_INET6_ADDRSTRLEN+16
    };
    char tmp_addr[PJ_INET6_ADDRSTRLEN];
    pjmedia_sdp_attr *attr;

    attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
    attr->name = pj_str("rtcp");
    attr->value.ptr = (char*) pj_pool_alloc(pool, ATTR_LEN);
    if (a->addr.sa_family == pj_AF_INET()) {
	attr->value.slen =
	    pj_ansi_snprintf(attr->value.ptr, ATTR_LEN,
			    "%u IN IP4 %s",
			    pj_sockaddr_get_port(a),
			    pj_sockaddr_print(a, tmp_addr,
					      sizeof(tmp_addr), 0));
    } else if (a->addr.sa_family == pj_AF_INET6()) {
	attr->value.slen =
	    pj_ansi_snprintf(attr->value.ptr, ATTR_LEN,
			    "%u IN IP6 %s",
			    pj_sockaddr_get_port(a),
			    pj_sockaddr_print(a, tmp_addr,
					      sizeof(tmp_addr), 0));

    } else {
	pj_assert(!"Unsupported address family");
	return NULL;
    }

    return attr;
}


PJ_DEF(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp,
					  pj_bool_t strict)
{
    unsigned i;
    const pj_str_t STR_RTPMAP = { "rtpmap", 6 };

    CHECK( sdp != NULL, PJ_EINVAL);

    /* Validate origin line. */
    CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
    CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0,
	   PJMEDIA_SDP_EINORIGIN);
    CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
	   pj_strcmp2(&sdp->origin.addr_type, "IP6")==0,
	   PJMEDIA_SDP_EINORIGIN);
    CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);

    /* Validate subject line. */
    CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);

    /* Ignore start and stop time. */

    /* If session level connection info is present, validate it. */
    if (sdp->conn) {
	pj_status_t status = validate_sdp_conn(sdp->conn);
	if (status != PJ_SUCCESS)
	    return status;
    }

    /* Validate each media. */
    for (i=0; i<sdp->media_count; ++i) {
	const pjmedia_sdp_media *m = sdp->media[i];
	unsigned j;

	/* Validate the m= line. */
	CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
	CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
	CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT);

	/* If media level connection info is present, validate it. */
	if (m->conn) {
	    pj_status_t status = validate_sdp_conn(m->conn);
	    if (status != PJ_SUCCESS)
		return status;
	}

	/* If media doesn't have connection info, then connection info
	 * must be present in the session.
	 */
	if (m->conn == NULL) {
	    if (sdp->conn == NULL)
		if (strict || m->desc.port != 0)
		    return PJMEDIA_SDP_EMISSINGCONN;
	}

	/* Verify payload type. */
	for (j=0; j<m->desc.fmt_count; ++j) {

	    /* Arrgh noo!! Payload type can be non-numeric!!
	     * RTC based programs sends "null" for instant messaging!
	     */
	    if (pj_isdigit(*m->desc.fmt[j].ptr)) {
		unsigned long pt;
		pj_status_t status = pj_strtoul3(&m->desc.fmt[j], &pt, 10);

		/* Payload type is between 0 and 127.
		 */
		CHECK( status == PJ_SUCCESS && pt <= 127, PJMEDIA_SDP_EINPT);

		/* If port is not zero, then for each dynamic payload type, an
		 * rtpmap attribute must be specified.
		 */
		if (m->desc.port != 0 && pt >= 96) {
		    const pjmedia_sdp_attr *a;

		    a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP,
						    &m->desc.fmt[j]);
		    CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
		}
	    }
	}
    }

    /* Looks good. */
    return PJ_SUCCESS;
}


PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_ssrc( pj_pool_t *pool,
							pj_uint32_t ssrc,
							const pj_str_t *cname)
{
    pjmedia_sdp_attr *attr;

    if (cname->slen == 0)
        return NULL;

    attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
    attr->name = pj_str("ssrc");
    attr->value.ptr = (char*) pj_pool_alloc(pool, cname->slen+7 /* " cname:"*/
    						  + 10 /* 32-bit integer */
    						  + 1 /* NULL */);
    attr->value.slen = pj_ansi_snprintf(attr->value.ptr, cname->slen+18,
    					"%u cname:%.*s", ssrc,
			   	   	(int)cname->slen, cname->ptr);

    return attr;
}


static int print_session(const pjmedia_sdp_session *ses,
			 char *buf, pj_ssize_t len)
{
    char *p = buf;
    char *end = buf+len;
    unsigned i;
    int printed;

    /* Check length for v= and o= lines. */
    if (len < 5+
	      2+ses->origin.user.slen+18+
	      ses->origin.net_type.slen+ses->origin.addr.slen + 2)
    {
	return -1;
    }

    /* SDP version (v= line) */
    pj_memcpy(p, "v=0\r\n", 5);
    p += 5;

    /* Owner (o=) line. */
    *p++ = 'o';
    *p++ = '=';
    pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
    p += ses->origin.user.slen;
    *p++ = ' ';
    printed = pj_utoa(ses->origin.id, p);
    p += printed;
    *p++ = ' ';
    printed = pj_utoa(ses->origin.version, p);
    p += printed;
    *p++ = ' ';
    pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
    p += ses->origin.net_type.slen;
    *p++ = ' ';
    pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
    p += ses->origin.addr_type.slen;
    *p++ = ' ';
    pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
    p += ses->origin.addr.slen;
    *p++ = '\r';
    *p++ = '\n';

    /* Session name (s=) line. */
    if ((end-p)  < 8+ses->name.slen) {
	return -1;
    }
    *p++ = 's';
    *p++ = '=';
    pj_memcpy(p, ses->name.ptr, ses->name.slen);
    p += ses->name.slen;
    *p++ = '\r';
    *p++ = '\n';

    /* Connection line (c=) if exist. */
    if (ses->conn) {
	printed = print_connection_info(ses->conn, p, (int)(end-p));
	if (printed < 1) {
	    return -1;
	}
	p += printed;
    }

    /* print optional bandwidth info. */
    for (i=0; i<ses->bandw_count; ++i) {
	printed = (int)print_bandw(ses->bandw[i], p, end-p);
	if (printed < 1) {
	    return -1;
	}
	p += printed;
    }

    /* Time */
    if ((end-p) < 24) {
	return -1;
    }
    *p++ = 't';
    *p++ = '=';
    printed = pj_utoa(ses->time.start, p);
    p += printed;
    *p++ = ' ';
    printed = pj_utoa(ses->time.stop, p);
    p += printed;
    *p++ = '\r';
    *p++ = '\n';

    /* Print all attribute (a=) lines. */
    for (i=0; i<ses->attr_count; ++i) {
	printed = (int)print_attr(ses->attr[i], p, end-p);
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

    /* Print media (m=) lines. */
    for (i=0; i<ses->media_count; ++i) {
	printed = print_media_desc(ses->media[i], p, (int)(end-p));
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

    return (int)(p-buf);
}


int parse_cmdline_decoder(int argc, char **argv,
                          opj_decompress_parameters *parameters, img_fol_t *img_fol)
{
    /* parse the command line */
    int totlen, c;
    opj_option_t long_option[] = {
        {"ImgDir",    REQ_ARG, NULL, 'y'},
        {"OutFor",    REQ_ARG, NULL, 'O'},
        {"force-rgb", NO_ARG,  NULL, 1},
        {"upsample",  NO_ARG,  NULL, 1},
        {"split-pnm", NO_ARG,  NULL, 1},
        {"threads",   REQ_ARG, NULL, 'T'},
        {"quiet", NO_ARG,  NULL, 1},
    };

    const char optlist[] = "i:o:r:l:x:d:t:p:c:"

                           /* UniPG>> */
#ifdef USE_JPWL
                           "W:"
#endif /* USE_JPWL */
                           /* <<UniPG */
                           "h"     ;

    long_option[2].flag = &(parameters->force_rgb);
    long_option[3].flag = &(parameters->upsample);
    long_option[4].flag = &(parameters->split_pnm);
    long_option[6].flag = &(parameters->quiet);
    totlen = sizeof(long_option);
    opj_reset_options_reading();
    img_fol->set_out_format = 0;
    do {
        c = opj_getopt_long(argc, argv, optlist, long_option, totlen);
        if (c == -1) {
            break;
        }
        switch (c) {
        case 0: /* long opt with flag */
            break;
        case 'i': {         /* input file */
            char *infile = opj_optarg;
            parameters->decod_format = infile_format(infile);
            switch (parameters->decod_format) {
            case J2K_CFMT:
                break;
            case JP2_CFMT:
                break;
            case JPT_CFMT:
                break;
            case -2:
                fprintf(stderr,
                        "!! infile cannot be read: %s !!\n\n",
                        infile);
                return 1;
            default:
                fprintf(stderr,
                        "[ERROR] Unknown input file format: %s \n"
                        "        Known file formats are *.j2k, *.jp2, *.jpc or *.jpt\n",
                        infile);
                return 1;
            }
            if (opj_strcpy_s(parameters->infile, sizeof(parameters->infile), infile) != 0) {
                fprintf(stderr, "[ERROR] Path is too long\n");
                return 1;
            }
        }
        break;

        /* ----------------------------------------------------- */

        case 'o': {         /* output file */
            char *outfile = opj_optarg;
            parameters->cod_format = get_file_format(outfile);
            switch (parameters->cod_format) {
            case PGX_DFMT:
            case PXM_DFMT:
            case BMP_DFMT:
            case TIF_DFMT:
            case RAW_DFMT:
            case RAWL_DFMT:
            case TGA_DFMT:
            case PNG_DFMT:
                break;
            default:
                fprintf(stderr,
                        "Unknown output format image %s [only *.png, *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif(f), *.raw, *.yuv or *.tga]!!\n",
                        outfile);
                return 1;
            }
            if (opj_strcpy_s(parameters->outfile, sizeof(parameters->outfile),
                             outfile) != 0) {
                fprintf(stderr, "[ERROR] Path is too long\n");
                return 1;
            }
        }
        break;

        /* ----------------------------------------------------- */

        case 'O': {         /* output format */
            char outformat[50];
            char *of = opj_optarg;
            sprintf(outformat, ".%s", of);
            img_fol->set_out_format = 1;
            parameters->cod_format = get_file_format(outformat);
            switch (parameters->cod_format) {
            case PGX_DFMT:
                img_fol->out_format = "pgx";
                break;
            case PXM_DFMT:
                img_fol->out_format = "ppm";
                break;
            case BMP_DFMT:
                img_fol->out_format = "bmp";
                break;
            case TIF_DFMT:
                img_fol->out_format = "tif";
                break;
            case RAW_DFMT:
                img_fol->out_format = "raw";
                break;
            case RAWL_DFMT:
                img_fol->out_format = "rawl";
                break;
            case TGA_DFMT:
                img_fol->out_format = "raw";
                break;
            case PNG_DFMT:
                img_fol->out_format = "png";
                break;
            default:
                fprintf(stderr,
                        "Unknown output format image %s [only *.png, *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif(f), *.raw, *.yuv or *.tga]!!\n",
                        outformat);
                return 1;
                break;
            }
        }
        break;

        /* ----------------------------------------------------- */

        case 'r': {     /* reduce option */
            sscanf(opj_optarg, "%u", &(parameters->core.cp_reduce));
        }
        break;

        /* ----------------------------------------------------- */

        case 'l': {     /* layering option */
            sscanf(opj_optarg, "%u", &(parameters->core.cp_layer));
        }
        break;

        /* ----------------------------------------------------- */

        case 'h':           /* display an help description */
            decode_help_display();
            return 1;

        /* ----------------------------------------------------- */

        case 'y': {         /* Image Directory path */
            img_fol->imgdirpath = (char*)malloc(strlen(opj_optarg) + 1);
            if (img_fol->imgdirpath == NULL) {
                return 1;
            }
            strcpy(img_fol->imgdirpath, opj_optarg);
            img_fol->set_imgdir = 1;
        }
        break;

        /* ----------------------------------------------------- */

        case 'd': {         /* Input decode ROI */
            size_t size_optarg = (size_t)strlen(opj_optarg) + 1U;
            char *ROI_values = (char*) malloc(size_optarg);
            if (ROI_values == NULL) {
                fprintf(stderr, "[ERROR] Couldn't allocate memory\n");
                return 1;
            }
            ROI_values[0] = '\0';
            memcpy(ROI_values, opj_optarg, size_optarg);
            /*printf("ROI_values = %s [%d / %d]\n", ROI_values, strlen(ROI_values), size_optarg ); */
            parse_DA_values(ROI_values, &parameters->DA_x0, &parameters->DA_y0,
                            &parameters->DA_x1, &parameters->DA_y1);

            free(ROI_values);
        }
        break;

        /* ----------------------------------------------------- */

        case 't': {         /* Input tile index */
            sscanf(opj_optarg, "%u", &parameters->tile_index);
            parameters->nb_tile_to_decode = 1;
        }
        break;

        /* ----------------------------------------------------- */

        case 'x': {         /* Creation of index file */
            if (opj_strcpy_s(parameters->indexfilename, sizeof(parameters->indexfilename),
                             opj_optarg) != 0) {
                fprintf(stderr, "[ERROR] Path is too long\n");
                return 1;
            }
        }
        break;

        /* ----------------------------------------------------- */
        case 'p': { /* Force precision */
            if (!parse_precision(opj_optarg, parameters)) {
                return 1;
            }
        }
        break;

        /* ----------------------------------------------------- */
        case 'c': { /* Componenets */
            const char* iter = opj_optarg;
            while (1) {
                parameters->numcomps ++;
                parameters->comps_indices = (OPJ_UINT32*) realloc(
                                                parameters->comps_indices,
                                                parameters->numcomps * sizeof(OPJ_UINT32));
                parameters->comps_indices[parameters->numcomps - 1] =
                    (OPJ_UINT32) atoi(iter);
                iter = strchr(iter, ',');
                if (iter == NULL) {
                    break;
                }
                iter ++;
            }
        }
        break;
            /* ----------------------------------------------------- */

            /* UniPG>> */
#ifdef USE_JPWL

        case 'W': {         /* activate JPWL correction */
            char *token = NULL;

            token = strtok(opj_optarg, ",");
            while (token != NULL) {

                /* search expected number of components */
                if (*token == 'c') {

                    static int compno;

                    compno = JPWL_EXPECTED_COMPONENTS; /* predefined no. of components */

                    if (sscanf(token, "c=%d", &compno) == 1) {
                        /* Specified */
                        if ((compno < 1) || (compno > 256)) {
                            fprintf(stderr, "ERROR -> invalid number of components c = %d\n", compno);
                            return 1;
                        }
                        parameters->jpwl_exp_comps = compno;

                    } else if (!strcmp(token, "c")) {
                        /* default */
                        parameters->jpwl_exp_comps = compno; /* auto for default size */

                    } else {
                        fprintf(stderr, "ERROR -> invalid components specified = %s\n", token);
                        return 1;
                    };
                }

                /* search maximum number of tiles */
                if (*token == 't') {

                    static int tileno;

                    tileno = JPWL_MAXIMUM_TILES; /* maximum no. of tiles */

                    if (sscanf(token, "t=%d", &tileno) == 1) {
                        /* Specified */
                        if ((tileno < 1) || (tileno > JPWL_MAXIMUM_TILES)) {
                            fprintf(stderr, "ERROR -> invalid number of tiles t = %d\n", tileno);
                            return 1;
                        }
                        parameters->jpwl_max_tiles = tileno;

                    } else if (!strcmp(token, "t")) {
                        /* default */
                        parameters->jpwl_max_tiles = tileno; /* auto for default size */

                    } else {
                        fprintf(stderr, "ERROR -> invalid tiles specified = %s\n", token);
                        return 1;
                    };
                }

                /* next token or bust */
                token = strtok(NULL, ",");
            };
            parameters->jpwl_correct = OPJ_TRUE;
            if (!(parameter->quiet)) {
                fprintf(stdout, "JPWL correction capability activated\n");
                fprintf(stdout, "- expecting %d components\n", parameters->jpwl_exp_comps);
            }
        }
        break;
#endif /* USE_JPWL */
        /* <<UniPG */

        /* ----------------------------------------------------- */
        case 'T': { /* Number of threads */
            if (strcmp(opj_optarg, "ALL_CPUS") == 0) {
                parameters->num_threads = opj_get_num_cpus();
                if (parameters->num_threads == 1) {
                    parameters->num_threads = 0;
                }
            } else {
                sscanf(opj_optarg, "%d", &parameters->num_threads);
            }
        }
        break;

        /* ----------------------------------------------------- */

        default:
            fprintf(stderr, "[WARNING] An invalid option has been ignored.\n");
            break;
        }
    } while (c != -1);

    /* check for possible errors */
    if (img_fol->set_imgdir == 1) {
        if (!(parameters->infile[0] == 0)) {
            fprintf(stderr, "[ERROR] options -ImgDir and -i cannot be used together.\n");
            return 1;
        }
        if (img_fol->set_out_format == 0) {
            fprintf(stderr,
                    "[ERROR] When -ImgDir is used, -OutFor <FORMAT> must be used.\n");
            fprintf(stderr, "Only one format allowed.\n"
                    "Valid format are PGM, PPM, PNM, PGX, BMP, TIF, TIFF, RAW, YUV, and TGA.\n");
            return 1;
        }
        if (!((parameters->outfile[0] == 0))) {
            fprintf(stderr, "[ERROR] options -ImgDir and -o cannot be used together.\n");
            return 1;
        }
    } else {
        if ((parameters->infile[0] == 0) || (parameters->outfile[0] == 0)) {
            fprintf(stderr, "[ERROR] Required parameters are missing\n"
                    "Example: %s -i image.j2k -o image.pgm\n", argv[0]);
            fprintf(stderr, "   Help: %s -h\n", argv[0]);
            return 1;
        }
    }

    return 0;
}


static void destroy_parameters(opj_decompress_parameters* parameters)
{
    if (parameters) {
        if (parameters->precision) {
            free(parameters->precision);
            parameters->precision = NULL;
        }

        free(parameters->comps_indices);
        parameters->comps_indices = NULL;
    }
}


static int ax88179_stop(struct usbnet *dev)
{
	u16 tmp16;

	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
			 2, 2, &tmp16);
	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
			  2, 2, &tmp16);

	return 0;
}


ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
		   u8 *data)
{
	struct usbnet *dev = netdev_priv(net);
	u16 *eeprom_buff;
	int first_word, last_word;
	int i, ret;

	if (eeprom->len == 0)
		return -EINVAL;

	eeprom->magic = AX88179_EEPROM_MAGIC;

	first_word = eeprom->offset >> 1;
	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
				    GFP_KERNEL);
	if (!eeprom_buff)
		return -ENOMEM;

	/* ax88179/178A returns 2 bytes from eeprom on read */
	for (i = first_word; i <= last_word; i++) {
		ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
					 &eeprom_buff[i - first_word],
					 0);
		if (ret < 0) {
			kfree(eeprom_buff);
			return -EIO;
		}
	}

	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
	kfree(eeprom_buff);
	return 0;
}


static void ax88179_set_multicast(struct net_device *net)
{
	struct usbnet *dev = netdev_priv(net);
	struct ax88179_data *data = (struct ax88179_data *)dev->data;
	u8 *m_filter = ((u8 *)dev->data) + 12;

	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);

	if (net->flags & IFF_PROMISC) {
		data->rxctl |= AX_RX_CTL_PRO;
	} else if (net->flags & IFF_ALLMULTI ||
		   netdev_mc_count(net) > AX_MAX_MCAST) {
		data->rxctl |= AX_RX_CTL_AMALL;
	} else if (netdev_mc_empty(net)) {
		/* just broadcast and directed */
	} else {
		/* We use the 20 byte dev->data for our 8 byte filter buffer
		 * to avoid allocating memory that is tricky to free later
		 */
		u32 crc_bits;
		struct netdev_hw_addr *ha;

		memset(m_filter, 0, AX_MCAST_FLTSIZE);

		netdev_for_each_mc_addr(ha, net) {
			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
		}

		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
					m_filter);

		data->rxctl |= AX_RX_CTL_AM;
	}

	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
				2, 2, &data->rxctl);
}


ax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad)
{
	int ret;
	u16 tmp16;

	ax88179_phy_mmd_indirect(dev, prtad, devad);

	ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
			       MII_MMD_DATA, 2, &tmp16);
	if (ret < 0)
		return ret;

	return tmp16;
}


ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad,
			       u16 data)
{
	int ret;

	ax88179_phy_mmd_indirect(dev, prtad, devad);

	ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
				MII_MMD_DATA, 2, &data);

	if (ret < 0)
		return ret;

	return 0;
}


static void ax88179_disable_eee(struct usbnet *dev)
{
	u16 tmp16;

	tmp16 = GMII_PHY_PGSEL_PAGE3;
	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
			  GMII_PHY_PAGE_SELECT, 2, &tmp16);

	tmp16 = 0x3246;
	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
			  MII_PHYADDR, 2, &tmp16);

	tmp16 = GMII_PHY_PGSEL_PAGE0;
	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
			  GMII_PHY_PAGE_SELECT, 2, &tmp16);
}


static int ax88179_change_mtu(struct net_device *net, int new_mtu)
{
	struct usbnet *dev = netdev_priv(net);
	u16 tmp16;

	net->mtu = new_mtu;
	dev->hard_mtu = net->mtu + net->hard_header_len;

	if (net->mtu > 1500) {
		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
				 2, 2, &tmp16);
		tmp16 |= AX_MEDIUM_JUMBO_EN;
		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
				  2, 2, &tmp16);
	} else {
		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
				 2, 2, &tmp16);
		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
				  2, 2, &tmp16);
	}

	/* max qlen depend on hard_mtu and rx_urb_size */
	usbnet_update_max_qlen(dev);

	return 0;
}


void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
    deflate_state *s;
    charf *buf;       /* input block */
    ulg stored_len;   /* length of input block */
    int last;         /* one if this is the last block for a file */
{
    send_bits(s, (STORED_BLOCK<<1)+last, 3);    /* send block type */
    bi_windup(s);        /* align on byte boundary */
    put_short(s, (ush)stored_len);
    put_short(s, (ush)~stored_len);
    if (stored_len)
        zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len);
    s->pending += stored_len;
#ifdef ZLIB_DEBUG
    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
    s->compressed_len += (stored_len + 4) << 3;
    s->bits_sent += 2*16;
    s->bits_sent += stored_len<<3;
#endif
}


local void putShortMSB (s, b)
    deflate_state *s;
    uInt b;
{
    put_byte(s, (Byte)(b >> 8));
    put_byte(s, (Byte)(b & 0xff));
}


uLong ZEXPORT deflateBound(strm, sourceLen)
    z_streamp strm;
    uLong sourceLen;
{
    deflate_state *s;
    uLong complen, wraplen;

    /* conservative upper bound for compressed data */
    complen = sourceLen +
              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;

    /* if can't get parameters, return conservative bound plus zlib wrapper */
    if (deflateStateCheck(strm))
        return complen + 6;

    /* compute wrapper length */
    s = strm->state;
    switch (s->wrap) {
    case 0:                                 /* raw deflate */
        wraplen = 0;
        break;
    case 1:                                 /* zlib wrapper */
        wraplen = 6 + (s->strstart ? 4 : 0);
        break;
#ifdef GZIP
    case 2:                                 /* gzip wrapper */
        wraplen = 18;
        if (s->gzhead != Z_NULL) {          /* user-supplied gzip header */
            Bytef *str;
            if (s->gzhead->extra != Z_NULL)
                wraplen += 2 + s->gzhead->extra_len;
            str = s->gzhead->name;
            if (str != Z_NULL)
                do {
                    wraplen++;
                } while (*str++);
            str = s->gzhead->comment;
            if (str != Z_NULL)
                do {
                    wraplen++;
                } while (*str++);
            if (s->gzhead->hcrc)
                wraplen += 2;
        }
        break;
#endif
    default:                                /* for compiler happiness */
        wraplen = 6;
    }

    /* if not default parameters, return conservative bound */
    if (s->w_bits != 15 || s->hash_bits != 8 + 7)
        return complen + wraplen;

    /* default settings: return tight bound for that case */
    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
           (sourceLen >> 25) + 13 - 6 + wraplen;
}


local void flush_pending(strm)
    z_streamp strm;
{
    unsigned len;
    deflate_state *s = strm->state;

    _tr_flush_bits(s);
    len = s->pending;
    if (len > strm->avail_out) len = strm->avail_out;
    if (len == 0) return;

    zmemcpy(strm->next_out, s->pending_out, len);
    strm->next_out  += len;
    s->pending_out  += len;
    strm->total_out += len;
    strm->avail_out -= len;
    s->pending      -= len;
    if (s->pending == 0) {
        s->pending_out = s->pending_buf;
    }
}


local int deflateStateCheck (strm)
    z_streamp strm;
{
    deflate_state *s;
    if (strm == Z_NULL ||
        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
        return 1;
    s = strm->state;
    if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE &&
#ifdef GZIP
                                           s->status != GZIP_STATE &&
#endif
                                           s->status != EXTRA_STATE &&
                                           s->status != NAME_STATE &&
                                           s->status != COMMENT_STATE &&
                                           s->status != HCRC_STATE &&
                                           s->status != BUSY_STATE &&
                                           s->status != FINISH_STATE))
        return 1;
    return 0;
}


static ut64 offset_to_vaddr(struct MACH0_(obj_t) *bin, ut64 offset) {
	if (bin->segs) {
		size_t i;
		for (i = 0; i < bin->nsegs; i++) {
			ut64 segment_base = (ut64)bin->segs[i].fileoff;
			ut64 segment_size = (ut64)bin->segs[i].filesize;
			if (offset >= segment_base && offset < segment_base + segment_size) {
				return bin->segs[i].vmaddr + (offset - segment_base);
			}
		}
	}
	return 0;
}


static void assign_export_symbol_t(struct MACH0_(obj_t) *bin, const char *name, ut64 flags, ut64 offset, void *ctx) {
	RSymCtx *sym_ctx = (RSymCtx*) ctx;
	int j = sym_ctx->j;
	if (j < sym_ctx->symbols_count) {
		sym_ctx->symbols[j].offset = offset;
		sym_ctx->symbols[j].addr = offset_to_vaddr (bin, offset);
		if (inSymtab (sym_ctx->hash, name, sym_ctx->symbols[j].addr)) {
			return;
		}
		sym_ctx->symbols[j].size = 0;
		sym_ctx->symbols[j].type = R_BIN_MACH0_SYMBOL_TYPE_EXT;
		sym_ctx->symbols[j].name = strdup (name);
		sym_ctx->j++;
	}
}


static ut64 entry_to_vaddr(struct MACH0_(obj_t) *bin) {
	switch (bin->main_cmd.cmd) {
	case LC_MAIN:
		return bin->entry + bin->baddr;
	case LC_UNIXTHREAD:
	case LC_THREAD:
		return bin->entry;
	default:
		return 0;
	}
}


R_API RList *r_cmd_alias_keys(RCmd *cmd) {
	RList *keylist = r_list_new ();
	if (!keylist) {
		return NULL;
	}

	ht_pp_foreach (cmd->aliases, get_keys, keylist);

	return keylist;
}


R_API RCmdStatus r_cmd_call_parsed_args(RCmd *cmd, RCmdParsedArgs *args) {
	RCmdStatus res = R_CMD_STATUS_INVALID;

	// As old RCorePlugin do not register new commands in RCmd, we have no
	// way of knowing if one of those is able to handle the input, so we
	// have to pass the input to all of them before looking into the
	// RCmdDesc tree
	RListIter *iter;
	RCorePlugin *cp;
	char *exec_string = r_cmd_parsed_args_execstr (args);
	r_list_foreach (cmd->plist, iter, cp) {
		if (cp->call && cp->call (cmd->data, exec_string)) {
			res = R_CMD_STATUS_OK;
			break;
		}
	}
	R_FREE (exec_string);
	if (res == R_CMD_STATUS_OK) {
		return res;
	}

	RCmdDesc *cd = r_cmd_get_desc (cmd, r_cmd_parsed_args_cmd (args));
	if (!cd) {
		return R_CMD_STATUS_INVALID;
	}

	res = R_CMD_STATUS_INVALID;
	switch (cd->type) {
	case R_CMD_DESC_TYPE_GROUP:
		if (!cd->d.group_data.exec_cd) {
			break;
		}
		cd = cd->d.group_data.exec_cd;
		// fallthrough
	case R_CMD_DESC_TYPE_ARGV:
		if (cd->d.argv_data.cb) {
			res = cd->d.argv_data.cb (cmd->data, args->argc, (const char **)args->argv);
		}
		break;
	case R_CMD_DESC_TYPE_OLDINPUT:
		exec_string = r_cmd_parsed_args_execstr (args);
		res = int2cmdstatus (cd->d.oldinput_data.cb (cmd->data, exec_string + strlen (cd->name)));
		R_FREE (exec_string);
		break;
	default:
		res = R_CMD_STATUS_INVALID;
		R_LOG_ERROR ("RCmdDesc type not handled\n");
		break;
	}
	return res;
}