static int handle_exception_nmi(struct kvm_vcpu *vcpu)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);
	struct kvm_run *kvm_run = vcpu->run;
	u32 intr_info, ex_no, error_code;
	unsigned long cr2, dr6;
	u32 vect_info;

	vect_info = vmx->idt_vectoring_info;
	intr_info = vmx_get_intr_info(vcpu);

	if (is_machine_check(intr_info) || is_nmi(intr_info))
		return 1; /* handled by handle_exception_nmi_irqoff() */

	/*
	 * Queue the exception here instead of in handle_nm_fault_irqoff().
	 * This ensures the nested_vmx check is not skipped so vmexit can
	 * be reflected to L1 (when it intercepts #NM) before reaching this
	 * point.
	 */
	if (is_nm_fault(intr_info)) {
		kvm_queue_exception(vcpu, NM_VECTOR);
		return 1;
	}

	if (is_invalid_opcode(intr_info))
		return handle_ud(vcpu);

	error_code = 0;
	if (intr_info & INTR_INFO_DELIVER_CODE_MASK)
		error_code = vmcs_read32(VM_EXIT_INTR_ERROR_CODE);

	if (!vmx->rmode.vm86_active && is_gp_fault(intr_info)) {
		WARN_ON_ONCE(!enable_vmware_backdoor);

		/*
		 * VMware backdoor emulation on #GP interception only handles
		 * IN{S}, OUT{S}, and RDPMC, none of which generate a non-zero
		 * error code on #GP.
		 */
		if (error_code) {
			kvm_queue_exception_e(vcpu, GP_VECTOR, error_code);
			return 1;
		}
		return kvm_emulate_instruction(vcpu, EMULTYPE_VMWARE_GP);
	}

	/*
	 * The #PF with PFEC.RSVD = 1 indicates the guest is accessing
	 * MMIO, it is better to report an internal error.
	 * See the comments in vmx_handle_exit.
	 */
	if ((vect_info & VECTORING_INFO_VALID_MASK) &&
	    !(is_page_fault(intr_info) && !(error_code & PFERR_RSVD_MASK))) {
		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
		vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_SIMUL_EX;
		vcpu->run->internal.ndata = 4;
		vcpu->run->internal.data[0] = vect_info;
		vcpu->run->internal.data[1] = intr_info;
		vcpu->run->internal.data[2] = error_code;
		vcpu->run->internal.data[3] = vcpu->arch.last_vmentry_cpu;
		return 0;
	}

	if (is_page_fault(intr_info)) {
		cr2 = vmx_get_exit_qual(vcpu);
		if (enable_ept && !vcpu->arch.apf.host_apf_flags) {
			/*
			 * EPT will cause page fault only if we need to
			 * detect illegal GPAs.
			 */
			WARN_ON_ONCE(!allow_smaller_maxphyaddr);
			kvm_fixup_and_inject_pf_error(vcpu, cr2, error_code);
			return 1;
		} else
			return kvm_handle_page_fault(vcpu, error_code, cr2, NULL, 0);
	}

	ex_no = intr_info & INTR_INFO_VECTOR_MASK;

	if (vmx->rmode.vm86_active && rmode_exception(vcpu, ex_no))
		return handle_rmode_exception(vcpu, ex_no, error_code);

	switch (ex_no) {
	case DB_VECTOR:
		dr6 = vmx_get_exit_qual(vcpu);
		if (!(vcpu->guest_debug &
		      (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
			/*
			 * If the #DB was due to ICEBP, a.k.a. INT1, skip the
			 * instruction.  ICEBP generates a trap-like #DB, but
			 * despite its interception control being tied to #DB,
			 * is an instruction intercept, i.e. the VM-Exit occurs
			 * on the ICEBP itself.  Note, skipping ICEBP also
			 * clears STI and MOVSS blocking.
			 *
			 * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS
			 * if single-step is enabled in RFLAGS and STI or MOVSS
			 * blocking is active, as the CPU doesn't set the bit
			 * on VM-Exit due to #DB interception.  VM-Entry has a
			 * consistency check that a single-step #DB is pending
			 * in this scenario as the previous instruction cannot
			 * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV
			 * don't modify RFLAGS), therefore the one instruction
			 * delay when activating single-step breakpoints must
			 * have already expired.  Note, the CPU sets/clears BS
			 * as appropriate for all other VM-Exits types.
			 */
			if (is_icebp(intr_info))
				WARN_ON(!skip_emulated_instruction(vcpu));
			else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) &&
				 (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
				  (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS)))
				vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS,
					    vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS);

			kvm_queue_exception_p(vcpu, DB_VECTOR, dr6);
			return 1;
		}
		kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW;
		kvm_run->debug.arch.dr7 = vmcs_readl(GUEST_DR7);
		fallthrough;
	case BP_VECTOR:
		/*
		 * Update instruction length as we may reinject #BP from
		 * user space while in guest debugging mode. Reading it for
		 * #DB as well causes no harm, it is not used in that case.
		 */
		vmx->vcpu.arch.event_exit_inst_len =
			vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
		kvm_run->exit_reason = KVM_EXIT_DEBUG;
		kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu);
		kvm_run->debug.arch.exception = ex_no;
		break;
	case AC_VECTOR:
		if (vmx_guest_inject_ac(vcpu)) {
			kvm_queue_exception_e(vcpu, AC_VECTOR, error_code);
			return 1;
		}

		/*
		 * Handle split lock. Depending on detection mode this will
		 * either warn and disable split lock detection for this
		 * task or force SIGBUS on it.
		 */
		if (handle_guest_split_lock(kvm_rip_read(vcpu)))
			return 1;
		fallthrough;
	default:
		kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
		kvm_run->ex.exception = ex_no;
		kvm_run->ex.error_code = error_code;
		break;
	}
	return 0;
}


void svm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
{
	unsigned long host_cr4_mce = cr4_read_shadow() & X86_CR4_MCE;
	unsigned long old_cr4 = vcpu->arch.cr4;

	if (npt_enabled && ((old_cr4 ^ cr4) & X86_CR4_PGE))
		svm_flush_tlb_current(vcpu);

	vcpu->arch.cr4 = cr4;
	if (!npt_enabled) {
		cr4 |= X86_CR4_PAE;

		if (!is_paging(vcpu))
			cr4 &= ~(X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE);
	}
	cr4 |= host_cr4_mce;
	to_svm(vcpu)->vmcb->save.cr4 = cr4;
	vmcb_mark_dirty(to_svm(vcpu)->vmcb, VMCB_CR);

	if ((cr4 ^ old_cr4) & (X86_CR4_OSXSAVE | X86_CR4_PKE))
		kvm_update_cpuid_runtime(vcpu);
}


static int vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);
	int r;

	r = vmx_deliver_nested_posted_interrupt(vcpu, vector);
	if (!r)
		return 0;

	if (!vcpu->arch.apicv_active)
		return -1;

	if (pi_test_and_set_pir(vector, &vmx->pi_desc))
		return 0;

	/* If a previous notification has sent the IPI, nothing to do.  */
	if (pi_test_and_set_on(&vmx->pi_desc))
		return 0;

	/*
	 * The implied barrier in pi_test_and_set_on() pairs with the smp_mb_*()
	 * after setting vcpu->mode in vcpu_enter_guest(), thus the vCPU is
	 * guaranteed to see PID.ON=1 and sync the PIR to IRR if triggering a
	 * posted interrupt "fails" because vcpu->mode != IN_GUEST_MODE.
	 */
	kvm_vcpu_trigger_posted_interrupt(vcpu, POSTED_INTR_VECTOR);
	return 0;
}


RZ_API RZ_OWN RzList /*<RzBinString *>*/ *rz_bin_dex_strings(RZ_NONNULL RzBinDex *dex) {
	rz_return_val_if_fail(dex, NULL);

	DexString *string;
	void **it;
	RzList *strings = rz_list_newf(rz_bin_string_free);
	if (!strings) {
		return NULL;
	}

	ut32 ordinal = 0;
	rz_pvector_foreach (dex->strings, it) {
		string = (DexString *)*it;
		RzBinString *bstr = RZ_NEW0(RzBinString);
		if (!bstr) {
			continue;
		}
		bstr->paddr = string->offset;
		bstr->vaddr = RZ_DEX_VIRT_ADDRESS + string->offset;
		bstr->ordinal = ordinal;
		bstr->length = string->size;
		bstr->size = string->size;
		bstr->string = rz_str_ndup(string->data, string->size);
		bstr->type = RZ_STRING_ENC_UTF8;
		if (!rz_list_append(strings, bstr)) {
			free(bstr);
		}
		ordinal++;
	}
	return strings;
}


RZ_API ut64 rz_bin_dex_debug_info(RZ_NONNULL RzBinDex *dex) {
	rz_return_val_if_fail(dex, 0);
	// TODO
	return 0;
}


RZ_API RZ_OWN char *rz_bin_dex_access_flags_readable(ut32 access_flags) {
	RzStrBuf *sb = NULL;
	for (ut32 i = 0; i < CLASS_ACCESS_FLAGS_SIZE; ++i) {
		const DexAccessFlagsReadable *afr = &access_flags_list[i];
		if (afr->flag == ACCESS_FLAG_VARARGS) {
			continue;
		}
		if (access_flags & afr->flag) {
			if (!sb) {
				sb = rz_strbuf_new(afr->readable);
				if (!sb) {
					return NULL;
				}
			} else {
				rz_strbuf_appendf(sb, " %s", afr->readable);
			}
		}
	}

	return sb ? rz_strbuf_drain(sb) : NULL;
}


static RzBinSymbol *dex_method_to_symbol(RzBinDex *dex, DexEncodedMethod *encoded_method, DexMethodId *method_id, bool is_imported) {
	RzBinSymbol *symbol = RZ_NEW0(RzBinSymbol);
	if (!symbol) {
		return NULL;
	}

	bool varargs = dex_is_varargs(encoded_method->access_flags);
	symbol->name = dex_resolve_string_id(dex, method_id->name_idx);
	symbol->classname = dex_resolve_type_id(dex, method_id->class_idx);
	symbol->libname = dex_resolve_library(symbol->classname);
	symbol->dname = dex_resolve_proto_id(dex, symbol->name, method_id->proto_idx, varargs);
	symbol->bind = dex_is_static(encoded_method->access_flags) ? RZ_BIN_BIND_GLOBAL_STR : RZ_BIN_BIND_LOCAL_STR;
	symbol->is_imported = is_imported;
	symbol->visibility = encoded_method->access_flags & UT32_MAX;
	symbol->visibility_str = rz_bin_dex_access_flags_readable(symbol->visibility);
	symbol->size = encoded_method->code_size;
	if (encoded_method->code_offset < RZ_DEX_RELOC_ADDRESS) {
		symbol->vaddr = RZ_DEX_VIRT_ADDRESS + encoded_method->code_offset;
		symbol->paddr = encoded_method->code_offset;
	} else {
		symbol->vaddr = encoded_method->code_offset;
		symbol->paddr = 0;
	}
	symbol->ordinal = encoded_method->method_idx;
	symbol->method_flags = dex_access_flags_to_bin_flags(encoded_method->access_flags);
	symbol->type = RZ_BIN_TYPE_METH_STR;

	return symbol;
}


static DexMethodId *dex_method_id_new(RzBuffer *buf, ut64 offset) {
	DexMethodId *method_id = RZ_NEW0(DexMethodId);
	if (!method_id) {
		return NULL;
	}

	read_le16_or_fail(buf, method_id->class_idx, dex_method_id_new_fail);
	read_le16_or_fail(buf, method_id->proto_idx, dex_method_id_new_fail);
	read_le32_or_fail(buf, method_id->name_idx, dex_method_id_new_fail);
	method_id->offset = offset;
	return method_id;

dex_method_id_new_fail:
	free(method_id);
	return NULL;
}


RZ_API RZ_OWN RzList /*<RzBinAddr *>*/ *rz_bin_dex_entrypoints(RZ_NONNULL RzBinDex *dex) {
	rz_return_val_if_fail(dex, NULL);

	DexClassDef *class_def;
	RzList *list = NULL;
	RzList *entrypoints = NULL;
	void **it;

	entrypoints = rz_list_newf((RzListFree)free);
	if (!entrypoints) {
		return NULL;
	}

	rz_pvector_foreach (dex->class_defs, it) {
		class_def = (DexClassDef *)*it;
		list = dex_resolve_entrypoints_in_class(dex, class_def);
		if (list) {
			rz_list_join(entrypoints, list);
			rz_list_free(list);
		}
	}

	return entrypoints;
}


static int compare_strings(const void *a, const void *b) {
	return strcmp((const char *)a, (const char *)b);
}


void sdb(Voice * currentVoice, const char * currLine, int len)
{
  int bp=0;
  if ((len>1)
  && ((currentVoice->pi->trace_flag & 1)
    || (bp=sdb_checkline(currentVoice->pi->trace_flag)))
  )
  {
    loop
    {
      char gdb[80];
      char *p=(char *)currLine+len-1;
      while ((*p<=' ') && (p!=currLine))
      {
        p--; len--;
      }
      if (p==currLine) return;

      currentVoice->pi->trace_flag&= ~1; // delete flag for "all lines"
      Print("(%s,%d) >>",currentVoice->filename,yylineno);
      fwrite(currLine,1,len,stdout);
      Print("<<\nbreakpoint %d (press ? for list of commands)\n",bp);
      p=fe_fgets_stdin(">>",gdb,80);
      while (*p==' ') p++;
      if (*p >' ')
      {
        sdb_lastcmd=*p;
      }
      Print("command:%c\n",sdb_lastcmd);
      switch(sdb_lastcmd)
      {
        case '?':
        case 'h':
        {
          PrintS(
          "b - print backtrace of calling stack\n"
          "B <proc> [<line>] - define breakpoint\n"
          "c - continue\n"
          "d - delete current breakpoint\n"
          "D - show all breakpoints\n"
          "e - edit the current procedure (current call will be aborted)\n"
          "h,? - display this help screen\n"
          "n - execute current line, break at next line\n"
          "p <var> - display type and value of the variable <var>\n"
          "q <flags> - quit debugger, set debugger flags(0,1,2)\n"
	  "   0: stop debug, 1:continue, 2: throw an error, return to toplevel\n"
          "Q - quit Singular\n");
          int i;
          for(i=0;i<7;i++)
          {
            if (sdb_lines[i] != -1)
              Print("breakpoint %d at line %d in %s\n",
                i,sdb_lines[i],sdb_files[i]);
          }
          break;
        }
        case 'd':
        {
          Print("delete break point %d\n",bp);
          currentVoice->pi->trace_flag &= (~Sy_bit(bp));
          if (bp!=0)
          {
            sdb_lines[bp-1]=-1;
          }
          break;
        }
        case 'D':
          sdb_show_bp();
          break;
	#if 0
	case 'l':
	{
	  extern void listall(int showproc);
	  listall(FALSE);
	  break;
	}
	#endif
        case 'n':
          currentVoice->pi->trace_flag|= 1;
          return;
        case 'e':
        {
          sdb_edit(currentVoice->pi);
          sdb_flags=2;
          return;
        }
        case 'p':
        {
          p=sdb_find_arg(p);
	  EXTERN_VAR int myynest;
          Print("variable `%s`at level %d",p,myynest);
          idhdl h=ggetid(p);
          if (h==NULL)
            PrintS(" not found\n");
          else
          {
            sleftv tmp;
            memset(&tmp,0,sizeof(tmp));
            tmp.rtyp=IDHDL;
            tmp.data=h;
            Print("(type %s):\n",Tok2Cmdname(tmp.Typ()));
            tmp.Print();
          }
          break;
        }
        case 'b':
          VoiceBackTrack();
          break;
        case 'B':
        {
          p=sdb_find_arg(p);
          Print("procedure `%s` ",p);
          sdb_set_breakpoint(p);
          break;
        }
        case 'q':
        {
          p=sdb_find_arg(p);
          if (*p!='\0')
          {
            sdb_flags=atoi(p);
            Print("new sdb_flags:%d\n",sdb_flags);
          }
          return;
        }
        case 'Q':
          m2_end(999);
        case 'c':
        default:
          return;
      }
    }
  }
}


static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
			     unsigned int cmd, void __user *arg)
{
	struct snd_ctl_card_info *info;

	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (! info)
		return -ENOMEM;
	down_read(&snd_ioctl_rwsem);
	info->card = card->number;
	strscpy(info->id, card->id, sizeof(info->id));
	strscpy(info->driver, card->driver, sizeof(info->driver));
	strscpy(info->name, card->shortname, sizeof(info->name));
	strscpy(info->longname, card->longname, sizeof(info->longname));
	strscpy(info->mixername, card->mixername, sizeof(info->mixername));
	strscpy(info->components, card->components, sizeof(info->components));
	up_read(&snd_ioctl_rwsem);
	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
		kfree(info);
		return -EFAULT;
	}
	kfree(info);
	return 0;
}


void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
{
	struct snd_card *card;
	int card_number;

	down_write(&snd_ctl_layer_rwsem);
	lops->next = snd_ctl_layer;
	snd_ctl_layer = lops;
	up_write(&snd_ctl_layer_rwsem);
	for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
		card = snd_card_ref(card_number);
		if (card) {
			down_read(&card->controls_rwsem);
			lops->lregister(card);
			up_read(&card->controls_rwsem);
			snd_card_unref(card);
		}
	}
}


static int snd_ctl_dev_free(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	struct snd_kcontrol *control;

	down_write(&card->controls_rwsem);
	while (!list_empty(&card->controls)) {
		control = snd_kcontrol(card->controls.next);
		__snd_ctl_remove(card, control, false);
	}

#ifdef CONFIG_SND_CTL_FAST_LOOKUP
	xa_destroy(&card->ctl_numids);
	xa_destroy(&card->ctl_hash);
#endif
	up_write(&card->controls_rwsem);
	put_device(&card->ctl_dev);
	return 0;
}


static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
				 struct snd_ctl_elem_info __user *_info, int replace)
{
	struct snd_ctl_elem_info info;
	int err;

	if (copy_from_user(&info, _info, sizeof(info)))
		return -EFAULT;
	err = snd_ctl_elem_add(file, &info, replace);
	if (err < 0)
		return err;
	if (copy_to_user(_info, &info, sizeof(info))) {
		snd_ctl_remove_user_ctl(file, &info.id);
		return -EFAULT;
	}

	return 0;
}


int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
{
	struct snd_ctl_file *kctl;
	int subdevice = -1;
	unsigned long flags;

	read_lock_irqsave(&card->ctl_files_rwlock, flags);
	list_for_each_entry(kctl, &card->ctl_files, list) {
		if (kctl->pid == task_pid(current)) {
			subdevice = kctl->preferred_subdevice[type];
			if (subdevice != -1)
				break;
		}
	}
	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
	return subdevice;
}


static int snd_ctl_elem_remove(struct snd_ctl_file *file,
			       struct snd_ctl_elem_id __user *_id)
{
	struct snd_ctl_elem_id id;

	if (copy_from_user(&id, _id, sizeof(id)))
		return -EFAULT;
	return snd_ctl_remove_user_ctl(file, &id);
}


static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
{
	unsigned long flags;
	struct snd_kctl_event *cread;

	spin_lock_irqsave(&ctl->read_lock, flags);
	while (!list_empty(&ctl->events)) {
		cread = snd_kctl_event(ctl->events.next);
		list_del(&cread->list);
		kfree(cread);
	}
	spin_unlock_irqrestore(&ctl->read_lock, flags);
}


static int snd_ctl_dev_disconnect(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	struct snd_ctl_file *ctl;
	struct snd_ctl_layer_ops *lops;
	unsigned long flags;

	read_lock_irqsave(&card->ctl_files_rwlock, flags);
	list_for_each_entry(ctl, &card->ctl_files, list) {
		wake_up(&ctl->change_sleep);
		snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
	}
	read_unlock_irqrestore(&card->ctl_files_rwlock, flags);

	down_read(&card->controls_rwsem);
	down_read(&snd_ctl_layer_rwsem);
	for (lops = snd_ctl_layer; lops; lops = lops->next)
		lops->ldisconnect(card);
	up_read(&snd_ctl_layer_rwsem);
	up_read(&card->controls_rwsem);

	return snd_unregister_device(&card->ctl_dev);
}


const char *MACH0_(get_intrp)(struct MACH0_(obj_t) * bin) {
	return bin ? bin->intrp : NULL;
}


static int inSymtab(HtPP *hash, const char *name, ut64 addr) {
	bool found = false;
	char *key = rz_str_newf("%" PFMT64x ".%s", addr, name);
	ht_pp_find(hash, key, &found);
	if (found) {
		free(key);
		return true;
	}
	ht_pp_insert(hash, key, "1");
	free(key);
	return false;
}


static bool parse_signature(struct MACH0_(obj_t) * bin, ut64 off) {
	int i, len;
	ut32 data;
	bin->signature = NULL;
	struct linkedit_data_command link = { 0 };
	ut8 lit[sizeof(struct linkedit_data_command)] = { 0 };
	struct blob_index_t idx = { 0 };
	struct super_blob_t super = { { 0 } };

	if (off > bin->size || off + sizeof(struct linkedit_data_command) > bin->size) {
		return false;
	}
	len = rz_buf_read_at(bin->b, off, lit, sizeof(struct linkedit_data_command));
	if (len != sizeof(struct linkedit_data_command)) {
		bprintf("Failed to get data while parsing LC_CODE_SIGNATURE command\n");
		return false;
	}
	link.cmd = rz_read_ble32(&lit[0], bin->big_endian);
	link.cmdsize = rz_read_ble32(&lit[4], bin->big_endian);
	link.dataoff = rz_read_ble32(&lit[8], bin->big_endian);
	link.datasize = rz_read_ble32(&lit[12], bin->big_endian);

	data = link.dataoff;
	if (data > bin->size || data + sizeof(struct super_blob_t) > bin->size) {
		bin->signature = (ut8 *)strdup("Malformed entitlement");
		return true;
	}

	if (!rz_buf_read_ble32_at(bin->b, data, &super.blob.magic, mach0_endian) ||
		!rz_buf_read_ble32_at(bin->b, data + 4, &super.blob.length, mach0_endian) ||
		!rz_buf_read_ble32_at(bin->b, data + 8, &super.count, mach0_endian)) {
		return false;
	}

	char *verbose = rz_sys_getenv("RZ_BIN_CODESIGN_VERBOSE");
	bool isVerbose = false;
	if (verbose) {
		isVerbose = *verbose;
		free(verbose);
	}
	// to dump all certificates
	// [0x00053f75]> b 5K;/x 30800609;wtf @@ hit*
	// then do this:
	// $ openssl asn1parse -inform der -in a|less
	// $ openssl pkcs7 -inform DER -print_certs -text -in a
	for (i = 0; i < super.count; i++) {
		if (data + i > bin->size) {
			bin->signature = (ut8 *)strdup("Malformed entitlement");
			break;
		}
		struct blob_index_t bi;
		if (rz_buf_read_at(bin->b, data + 12 + (i * sizeof(struct blob_index_t)),
			    (ut8 *)&bi, sizeof(struct blob_index_t)) < sizeof(struct blob_index_t)) {
			break;
		}
		idx.type = rz_read_ble32(&bi.type, mach0_endian);
		idx.offset = rz_read_ble32(&bi.offset, mach0_endian);
		switch (idx.type) {
		case CSSLOT_ENTITLEMENTS:
			if (true || isVerbose) {
				ut64 off = data + idx.offset;
				if (off > bin->size || off + sizeof(struct blob_t) > bin->size) {
					bin->signature = (ut8 *)strdup("Malformed entitlement");
					break;
				}
				struct blob_t entitlements = { 0 };
				if (!rz_buf_read_ble32_at(bin->b, off, &entitlements.magic, mach0_endian) ||
					!rz_buf_read_ble32_at(bin->b, off + 4, &entitlements.length, mach0_endian)) {
					break;
				}
				len = entitlements.length - sizeof(struct blob_t);
				if (len <= bin->size && len > 1) {
					bin->signature = calloc(1, len + 1);
					if (!bin->signature) {
						break;
					}
					if (off + sizeof(struct blob_t) + len < rz_buf_size(bin->b)) {
						rz_buf_read_at(bin->b, off + sizeof(struct blob_t), (ut8 *)bin->signature, len);
						if (len >= 0) {
							bin->signature[len] = '\0';
						}
					} else {
						bin->signature = (ut8 *)strdup("Malformed entitlement");
					}
				} else {
					bin->signature = (ut8 *)strdup("Malformed entitlement");
				}
			}
			break;
		case CSSLOT_CODEDIRECTORY:
			if (isVerbose) {
				parseCodeDirectory(bin, bin->b, data + idx.offset, link.datasize);
			}
			break;
		case 0x1000:
			// unknown
			break;
		case CSSLOT_CMS_SIGNATURE: // ASN1/DER certificate
			if (isVerbose) {
				ut8 header[8] = { 0 };
				rz_buf_read_at(bin->b, data + idx.offset, header, sizeof(header));
				ut32 length = RZ_MIN(UT16_MAX, rz_read_ble32(header + 4, 1));
				ut8 *p = calloc(length, 1);
				if (p) {
					rz_buf_read_at(bin->b, data + idx.offset + 0, p, length);
					ut32 *words = (ut32 *)p;
					eprintf("Magic: %x\n", words[0]);
					eprintf("wtf DUMP @%d!%d\n",
						(int)data + idx.offset + 8, (int)length);
					eprintf("openssl pkcs7 -print_certs -text -inform der -in DUMP\n");
					eprintf("openssl asn1parse -offset %d -length %d -inform der -in /bin/ls\n",
						(int)data + idx.offset + 8, (int)length);
					eprintf("pFp@%d!%d\n",
						(int)data + idx.offset + 8, (int)length);
					free(p);
				}
			}
			break;
		case CSSLOT_REQUIREMENTS: // 2
		{
			ut8 p[256];
			rz_buf_read_at(bin->b, data + idx.offset + 16, p, sizeof(p));
			p[sizeof(p) - 1] = 0;
			ut32 slot_size = rz_read_ble32(p + 8, 1);
			if (slot_size < sizeof(p)) {
				ut32 ident_size = rz_read_ble32(p + 8, 1);
				char *ident = rz_str_ndup((const char *)p + 28, ident_size);
				if (ident) {
					sdb_set(bin->kv, "mach0.ident", ident, 0);
					free(ident);
				}
			} else {
				if (bin->options.verbose) {
					eprintf("Invalid code slot size\n");
				}
			}
		} break;
		case CSSLOT_INFOSLOT: // 1;
		case CSSLOT_RESOURCEDIR: // 3;
		case CSSLOT_APPLICATION: // 4;
			// TODO: parse those codesign slots
			if (bin->options.verbose) {
				eprintf("TODO: Some codesign slots are not yet supported\n");
			}
			break;
		default:
			if (bin->options.verbose) {
				eprintf("Unknown Code signature slot %d\n", idx.type);
			}
			break;
		}
	}
	if (!bin->signature) {
		bin->signature = (ut8 *)strdup("No entitlement found");
	}
	return true;
}


static int walk_exports(struct MACH0_(obj_t) * bin, RExportsIterator iterator, void *ctx) {
	rz_return_val_if_fail(bin, 0);
	if (!bin->dyld_info) {
		return 0;
	}

	size_t count = 0;
	ut8 *p = NULL;
	ut8 *trie = NULL;
	RzList *states = NULL;
	ut64 size = bin->dyld_info->export_size;
	if (!size || size >= SIZE_MAX) {
		return count;
	}
	trie = calloc(size + 1, 1);
	if (!trie) {
		return count;
	}
	ut8 *end = trie + size;

	if (rz_buf_read_at(bin->b, bin->dyld_info->export_off, trie, bin->dyld_info->export_size) != size) {
		goto beach;
	}

	states = rz_list_newf((RzListFree)free);
	if (!states) {
		goto beach;
	}

	RTrieState *root = RZ_NEW0(RTrieState);
	if (!root) {
		goto beach;
	}
	root->node = trie;
	root->i = 0;
	root->label = NULL;
	rz_list_push(states, root);

	do {
		RTrieState *state = rz_list_get_top(states);
		p = state->node;
		ut64 len = read_uleb128(&p, end);
		if (len == UT64_MAX) {
			break;
		}
		if (len) {
			ut64 flags = read_uleb128(&p, end);
			if (flags == UT64_MAX) {
				break;
			}
			ut64 offset = read_uleb128(&p, end);
			if (offset == UT64_MAX) {
				break;
			}
			ut64 resolver = 0;
			bool isReexport = flags & EXPORT_SYMBOL_FLAGS_REEXPORT;
			bool hasResolver = flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER;
			if (hasResolver) {
				ut64 res = read_uleb128(&p, end);
				if (res == UT64_MAX) {
					break;
				}
				resolver = res + bin->options.header_at;
			} else if (isReexport) {
				p += strlen((char *)p) + 1;
				// TODO: handle this
			}
			if (!isReexport) {
				offset += bin->options.header_at;
			}
			if (iterator && !isReexport) {
				char *name = NULL;
				RzListIter *iter;
				RTrieState *s;
				rz_list_foreach (states, iter, s) {
					if (!s->label) {
						continue;
					}
					name = rz_str_append(name, s->label);
				}
				if (!name) {
					RZ_LOG_ERROR("malformed export trie\n");
					goto beach;
				}
				if (hasResolver) {
					char *stub_name = rz_str_newf("stub.%s", name);
					iterator(bin, stub_name, flags, offset, ctx);
					iterator(bin, name, flags, resolver, ctx);
					RZ_FREE(stub_name);
				} else {
					iterator(bin, name, flags, offset, ctx);
				}
				RZ_FREE(name);
			}
			if (!isReexport) {
				if (hasResolver) {
					count++;
				}
				count++;
			}
		}
		ut64 child_count = read_uleb128(&p, end);
		if (child_count == UT64_MAX) {
			goto beach;
		}
		if (state->i == child_count) {
			rz_list_pop(states);
			continue;
		}
		if (!state->next_child) {
			state->next_child = p;
		} else {
			p = state->next_child;
		}
		RTrieState *next = RZ_NEW0(RTrieState);
		if (!next) {
			goto beach;
		}
		next->label = (char *)p;
		p += strlen(next->label) + 1;
		if (p >= end) {
			RZ_LOG_ERROR("malformed export trie\n");
			RZ_FREE(next);
			goto beach;
		}
		ut64 tr = read_uleb128(&p, end);
		if (tr == UT64_MAX) {
			RZ_FREE(next);
			goto beach;
		}
		if (tr >= size) {
			RZ_LOG_ERROR("malformed export trie\n");
			RZ_FREE(next);
			goto beach;
		}
		next->node = trie + (size_t)tr;
		{
			// avoid loops
			RzListIter *it;
			RTrieState *s;
			rz_list_foreach (states, it, s) {
				if (s->node == next->node) {
					RZ_LOG_ERROR("malformed export trie\n");
					RZ_FREE(next);
					goto beach;
				}
			}
		}
		next->i = 0;
		state->i++;
		state->next_child = p;
		rz_list_push(states, next);
	} while (rz_list_length(states));

beach:
	rz_list_free(states);
	RZ_FREE(trie);
	return count;
}


char *MACH0_(get_cpusubtype_from_hdr)(struct MACH0_(mach_header) * hdr) {
	rz_return_val_if_fail(hdr, NULL);
	return strdup(cpusubtype_tostring(hdr->cputype, hdr->cpusubtype));
}


static int init_items(struct MACH0_(obj_t) * bin) {
	struct load_command lc = { 0, 0 };
	ut8 loadc[sizeof(struct load_command)] = { 0 };
	bool is_first_thread = true;
	ut64 off = 0LL;
	int i, len;

	bin->uuidn = 0;
	bin->os = 0;
	bin->has_crypto = 0;
	if (bin->hdr.sizeofcmds > bin->size) {
		bprintf("Warning: chopping hdr.sizeofcmds\n");
		bin->hdr.sizeofcmds = bin->size - 128;
		// return false;
	}
	// bprintf ("Commands: %d\n", bin->hdr.ncmds);
	for (i = 0, off = sizeof(struct MACH0_(mach_header)) + bin->options.header_at;
		i < bin->hdr.ncmds; i++, off += lc.cmdsize) {
		if (off > bin->size || off + sizeof(struct load_command) > bin->size) {
			bprintf("mach0: out of bounds command\n");
			return false;
		}
		len = rz_buf_read_at(bin->b, off, loadc, sizeof(struct load_command));
		if (len < 1) {
			bprintf("Error: read (lc) at 0x%08" PFMT64x "\n", off);
			return false;
		}
		lc.cmd = rz_read_ble32(&loadc[0], bin->big_endian);
		lc.cmdsize = rz_read_ble32(&loadc[4], bin->big_endian);

		if (lc.cmdsize < 1 || off + lc.cmdsize > bin->size) {
			bprintf("Warning: mach0_header %d = cmdsize<1. (0x%llx vs 0x%llx)\n", i,
				(ut64)(off + lc.cmdsize), (ut64)(bin->size));
			break;
		}

		sdb_num_set(bin->kv, sdb_fmt("mach0_cmd_%d.offset", i), off, 0);
		const char *format_name = cmd_to_pf_definition(lc.cmd);
		if (format_name) {
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.format", i), format_name, 0);
		} else {
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.format", i), "[4]Ed (mach_load_command_type)cmd size", 0);
		}

		switch (lc.cmd) {
		case LC_DATA_IN_CODE:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "data_in_code", 0);
			break;
		case LC_RPATH:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "rpath", 0);
			// bprintf ("--->\n");
			break;
		case LC_SEGMENT_64:
		case LC_SEGMENT:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "segment", 0);
			bin->nsegs++;
			if (!parse_segments(bin, off)) {
				bprintf("error parsing segment\n");
				bin->nsegs--;
				return false;
			}
			break;
		case LC_SYMTAB:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "symtab", 0);
			if (!parse_symtab(bin, off)) {
				bprintf("error parsing symtab\n");
				return false;
			}
			break;
		case LC_DYSYMTAB:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "dysymtab", 0);
			if (!parse_dysymtab(bin, off)) {
				bprintf("error parsing dysymtab\n");
				return false;
			}
			break;
		case LC_DYLIB_CODE_SIGN_DRS:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "dylib_code_sign_drs", 0);
			// bprintf ("[mach0] code is signed\n");
			break;
		case LC_VERSION_MIN_MACOSX:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "version_min_macosx", 0);
			bin->os = 1;
			// set OS = osx
			// bprintf ("[mach0] Requires OSX >= x\n");
			break;
		case LC_VERSION_MIN_IPHONEOS:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "version_min_iphoneos", 0);
			bin->os = 2;
			// set OS = ios
			// bprintf ("[mach0] Requires iOS >= x\n");
			break;
		case LC_VERSION_MIN_TVOS:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "version_min_tvos", 0);
			bin->os = 4;
			break;
		case LC_VERSION_MIN_WATCHOS:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "version_min_watchos", 0);
			bin->os = 3;
			break;
		case LC_UUID:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "uuid", 0);
			{
				struct uuid_command uc = { 0 };
				if (off + sizeof(struct uuid_command) > bin->size) {
					bprintf("UUID out of bounds\n");
					return false;
				}
				if (rz_buf_fread_at(bin->b, off, (ut8 *)&uc, "24c", 1) != -1) {
					char key[128];
					char val[128];
					snprintf(key, sizeof(key) - 1, "uuid.%d", bin->uuidn++);
					rz_hex_bin2str((ut8 *)&uc.uuid, 16, val);
					sdb_set(bin->kv, key, val, 0);
					// for (i=0;i<16; i++) bprintf ("%02x%c", uc.uuid[i], (i==15)?'\n':'-');
				}
			}
			break;
		case LC_ENCRYPTION_INFO_64:
			/* TODO: the struct is probably different here */
		case LC_ENCRYPTION_INFO:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "encryption_info", 0);
			{
				struct MACH0_(encryption_info_command) eic = { 0 };
				ut8 seic[sizeof(struct MACH0_(encryption_info_command))] = { 0 };
				if (off + sizeof(struct MACH0_(encryption_info_command)) > bin->size) {
					bprintf("encryption info out of bounds\n");
					return false;
				}
				if (rz_buf_read_at(bin->b, off, seic, sizeof(struct MACH0_(encryption_info_command))) != -1) {
					eic.cmd = rz_read_ble32(&seic[0], bin->big_endian);
					eic.cmdsize = rz_read_ble32(&seic[4], bin->big_endian);
					eic.cryptoff = rz_read_ble32(&seic[8], bin->big_endian);
					eic.cryptsize = rz_read_ble32(&seic[12], bin->big_endian);
					eic.cryptid = rz_read_ble32(&seic[16], bin->big_endian);

					bin->has_crypto = eic.cryptid;
					sdb_set(bin->kv, "crypto", "true", 0);
					sdb_num_set(bin->kv, "cryptid", eic.cryptid, 0);
					sdb_num_set(bin->kv, "cryptoff", eic.cryptoff, 0);
					sdb_num_set(bin->kv, "cryptsize", eic.cryptsize, 0);
					sdb_num_set(bin->kv, "cryptheader", off, 0);
				}
			}
			break;
		case LC_LOAD_DYLINKER: {
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "dylinker", 0);
			RZ_FREE(bin->intrp);
			// bprintf ("[mach0] load dynamic linker\n");
			struct dylinker_command dy = { 0 };
			ut8 sdy[sizeof(struct dylinker_command)] = { 0 };
			if (off + sizeof(struct dylinker_command) > bin->size) {
				bprintf("Warning: Cannot parse dylinker command\n");
				return false;
			}
			if (rz_buf_read_at(bin->b, off, sdy, sizeof(struct dylinker_command)) == -1) {
				bprintf("Warning: read (LC_DYLD_INFO) at 0x%08" PFMT64x "\n", off);
			} else {
				dy.cmd = rz_read_ble32(&sdy[0], bin->big_endian);
				dy.cmdsize = rz_read_ble32(&sdy[4], bin->big_endian);
				dy.name = rz_read_ble32(&sdy[8], bin->big_endian);

				int len = dy.cmdsize;
				char *buf = malloc(len + 1);
				if (buf) {
					// wtf @ off + 0xc ?
					rz_buf_read_at(bin->b, off + 0xc, (ut8 *)buf, len);
					buf[len] = 0;
					free(bin->intrp);
					bin->intrp = buf;
				}
			}
		} break;
		case LC_MAIN: {
			struct {
				ut64 eo;
				ut64 ss;
			} ep = { 0 };
			ut8 sep[2 * sizeof(ut64)] = { 0 };
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "main", 0);

			if (!is_first_thread) {
				bprintf("Error: LC_MAIN with other threads\n");
				return false;
			}
			if (off + 8 > bin->size || off + sizeof(ep) > bin->size) {
				bprintf("invalid command size for main\n");
				return false;
			}
			rz_buf_read_at(bin->b, off + 8, sep, 2 * sizeof(ut64));
			ep.eo = rz_read_ble64(&sep[0], bin->big_endian);
			ep.ss = rz_read_ble64(&sep[8], bin->big_endian);

			bin->entry = ep.eo;
			bin->main_cmd = lc;

			sdb_num_set(bin->kv, "mach0.entry.offset", off + 8, 0);
			sdb_num_set(bin->kv, "stacksize", ep.ss, 0);

			is_first_thread = false;
		} break;
		case LC_UNIXTHREAD:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "unixthread", 0);
			if (!is_first_thread) {
				bprintf("Error: LC_UNIXTHREAD with other threads\n");
				return false;
			}
			// fallthrough
		case LC_THREAD:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "thread", 0);
			if (!parse_thread(bin, &lc, off, is_first_thread)) {
				bprintf("Cannot parse thread\n");
				return false;
			}
			is_first_thread = false;
			break;
		case LC_LOAD_DYLIB:
		case LC_LOAD_WEAK_DYLIB:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "load_dylib", 0);
			bin->nlibs++;
			if (!parse_dylib(bin, off)) {
				bprintf("Cannot parse dylib\n");
				bin->nlibs--;
				return false;
			}
			break;
		case LC_DYLD_INFO:
		case LC_DYLD_INFO_ONLY: {
			ut8 dyldi[sizeof(struct dyld_info_command)] = { 0 };
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "dyld_info", 0);
			bin->dyld_info = calloc(1, sizeof(struct dyld_info_command));
			if (bin->dyld_info) {
				if (off + sizeof(struct dyld_info_command) > bin->size) {
					bprintf("Cannot parse dyldinfo\n");
					RZ_FREE(bin->dyld_info);
					return false;
				}
				if (rz_buf_read_at(bin->b, off, dyldi, sizeof(struct dyld_info_command)) == -1) {
					RZ_FREE(bin->dyld_info);
					bprintf("Error: read (LC_DYLD_INFO) at 0x%08" PFMT64x "\n", off);
				} else {
					bin->dyld_info->cmd = rz_read_ble32(&dyldi[0], bin->big_endian);
					bin->dyld_info->cmdsize = rz_read_ble32(&dyldi[4], bin->big_endian);
					bin->dyld_info->rebase_off = rz_read_ble32(&dyldi[8], bin->big_endian);
					bin->dyld_info->rebase_size = rz_read_ble32(&dyldi[12], bin->big_endian);
					bin->dyld_info->bind_off = rz_read_ble32(&dyldi[16], bin->big_endian);
					bin->dyld_info->bind_size = rz_read_ble32(&dyldi[20], bin->big_endian);
					bin->dyld_info->weak_bind_off = rz_read_ble32(&dyldi[24], bin->big_endian);
					bin->dyld_info->weak_bind_size = rz_read_ble32(&dyldi[28], bin->big_endian);
					bin->dyld_info->lazy_bind_off = rz_read_ble32(&dyldi[32], bin->big_endian);
					bin->dyld_info->lazy_bind_size = rz_read_ble32(&dyldi[36], bin->big_endian);
					bin->dyld_info->export_off = rz_read_ble32(&dyldi[40], bin->big_endian) + bin->options.symbols_off;
					bin->dyld_info->export_size = rz_read_ble32(&dyldi[44], bin->big_endian);
				}
			}
		} break;
		case LC_CODE_SIGNATURE:
			parse_signature(bin, off);
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "signature", 0);
			/* ut32 dataoff
			// ut32 datasize */
			break;
		case LC_SOURCE_VERSION:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "version", 0);
			/* uint64_t  version;  */
			/* A.B.C.D.E packed as a24.b10.c10.d10.e10 */
			// bprintf ("mach0: TODO: Show source version\n");
			break;
		case LC_SEGMENT_SPLIT_INFO:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "split_info", 0);
			/* TODO */
			break;
		case LC_FUNCTION_STARTS:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "function_starts", 0);
			if (!parse_function_starts(bin, off)) {
				bprintf("Cannot parse LC_FUNCTION_STARTS\n");
			}
			break;
		case LC_REEXPORT_DYLIB:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "dylib", 0);
			/* TODO */
			break;
		default:
			// bprintf ("mach0: Unknown header command %x\n", lc.cmd);
			break;
		}
	}
	bool has_chained_fixups = false;
	for (i = 0, off = sizeof(struct MACH0_(mach_header)) + bin->options.header_at;
		i < bin->hdr.ncmds; i++, off += lc.cmdsize) {
		len = rz_buf_read_at(bin->b, off, loadc, sizeof(struct load_command));
		if (len < 1) {
			bprintf("Error: read (lc) at 0x%08" PFMT64x "\n", off);
			return false;
		}
		lc.cmd = rz_read_ble32(&loadc[0], bin->big_endian);
		lc.cmdsize = rz_read_ble32(&loadc[4], bin->big_endian);

		if (lc.cmdsize < 1 || off + lc.cmdsize > bin->size) {
			bprintf("Warning: mach0_header %d = cmdsize<1. (0x%llx vs 0x%llx)\n", i,
				(ut64)(off + lc.cmdsize), (ut64)(bin->size));
			break;
		}

		sdb_num_set(bin->kv, sdb_fmt("mach0_cmd_%d.offset", i), off, 0);
		const char *format_name = cmd_to_pf_definition(lc.cmd);
		if (format_name) {
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.format", i), format_name, 0);
		} else {
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.format", i), "[4]Ed (mach_load_command_type)cmd size", 0);
		}

		switch (lc.cmd) {
		case LC_DATA_IN_CODE:
			sdb_set(bin->kv, sdb_fmt("mach0_cmd_%d.cmd", i), "data_in_code", 0);
			if (bin->options.verbose) {
				ut8 buf[8];
				rz_buf_read_at(bin->b, off + 8, buf, sizeof(buf));
				ut32 dataoff = rz_read_ble32(buf, bin->big_endian);
				ut32 datasize = rz_read_ble32(buf + 4, bin->big_endian);
				eprintf("data-in-code at 0x%x size %d\n", dataoff, datasize);
				ut8 *db = (ut8 *)malloc(datasize);
				if (db) {
					rz_buf_read_at(bin->b, dataoff, db, datasize);
					// TODO table of non-instructions regions in __text
					int j;
					for (j = 0; j < datasize; j += 8) {
						ut32 dw = rz_read_ble32(db + j, bin->big_endian);
						// int kind = rz_read_ble16 (db + i + 4 + 2, bin->big_endian);
						int len = rz_read_ble16(db + j + 4, bin->big_endian);
						ut64 va = MACH0_(paddr_to_vaddr)(bin, dw);
						//	eprintf ("# 0x%d -> 0x%x\n", dw, va);
						//	eprintf ("0x%x kind %d len %d\n", dw, kind, len);
						eprintf("Cd 4 %d @ 0x%" PFMT64x "\n", len / 4, va);
					}
				}
			}
			break;
		case LC_DYLD_EXPORTS_TRIE:
			if (bin->options.verbose) {
				ut8 buf[8];
				rz_buf_read_at(bin->b, off + 8, buf, sizeof(buf));
				ut32 dataoff = rz_read_ble32(buf, bin->big_endian);
				ut32 datasize = rz_read_ble32(buf + 4, bin->big_endian);
				eprintf("exports trie at 0x%x size %d\n", dataoff, datasize);
			}
			break;
		case LC_DYLD_CHAINED_FIXUPS: {
			ut8 buf[8];
			if (rz_buf_read_at(bin->b, off + 8, buf, sizeof(buf)) == sizeof(buf)) {
				ut32 dataoff = rz_read_ble32(buf, bin->big_endian);
				ut32 datasize = rz_read_ble32(buf + 4, bin->big_endian);
				if (bin->options.verbose) {
					eprintf("chained fixups at 0x%x size %d\n", dataoff, datasize);
				}
				has_chained_fixups = parse_chained_fixups(bin, dataoff, datasize);
			}
		} break;
		}
	}

	if (!has_chained_fixups && bin->hdr.cputype == CPU_TYPE_ARM64 &&
		(bin->hdr.cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) {
		reconstruct_chained_fixup(bin);
	}
	return true;
}


static bool init(struct MACH0_(obj_t) * mo) {
	if (!init_hdr(mo)) {
		return false;
	}
	if (!init_items(mo)) {
		Eprintf("Warning: Cannot initialize items\n");
	}
	mo->baddr = MACH0_(get_baddr)(mo);
	return true;
}


static RzList *classes_from_symbols(RzBinFile *bf) {
	RzBinSymbol *sym;
	RzListIter *iter;
	rz_list_foreach (bf->o->symbols, iter, sym) {
		if (!sym->name || sym->name[0] != '_') {
			continue;
		}
		const char *cn = sym->classname;
		if (cn) {
			RzBinClass *c = rz_bin_file_add_class(bf, sym->classname, NULL, 0);
			if (!c) {
				continue;
			}
			// swift specific
			char *dn = sym->dname;
			char *fn = swiftField(dn, cn);
			if (fn) {
				RzBinField *f = rz_bin_field_new(sym->paddr, sym->vaddr, sym->size, fn, NULL, NULL, false);
				rz_list_append(c->fields, f);
				free(fn);
			} else {
				char *mn = strstr(dn, "..");
				if (!mn) {
					mn = strstr(dn, cn);
					if (mn && mn[strlen(cn)] == '.') {
						rz_list_append(c->methods, sym);
					}
				}
			}
		}
	}
	return bf->o->classes;
}


RZ_API RZ_OWN RzList /*<RzBinSection *>*/ *rz_bin_object_get_sections(RZ_NONNULL RzBinObject *obj) {
	rz_return_val_if_fail(obj, NULL);
	return get_sections_or_segment(obj, false);
}


RZ_API void rz_bin_string_decode_base64(RZ_NONNULL RzBinString *bstr) {
	rz_return_if_fail(bstr);

	char *decoded = bstr->string;
	do {
		// ensure to decode base64 strings encoded multiple times.
		char *tmp = (char *)rz_base64_decode_dyn(decoded, -1);
		if (!tmp || !rz_str_is_printable(tmp)) {
			free(tmp);
			break;
		}
		free(decoded);
		decoded = tmp;
	} while (1);

	if (decoded == bstr->string) {
		return;
	}
	free(bstr->string);
	bstr->string = decoded;
	bstr->length = strlen(decoded);
	bstr->type = RZ_STRING_ENC_BASE64;
}


static RzList *strings(RzBinFile *bf) {
	if (!bf) {
		return NULL;
	}
	LuacBinInfo *bin_info_obj = GET_INTERNAL_BIN_INFO_OBJ(bf);
	if (!bin_info_obj) {
		return NULL;
	}

	return rz_list_clone(bin_info_obj->string_list);
}


f_assert_fails(typval_T *argvars, typval_T *rettv)
{
    char_u	*cmd;
    garray_T	ga;
    int		save_trylevel = trylevel;
    int		called_emsg_before = called_emsg;
    char	*wrong_arg_msg = NULL;
    char_u	*tofree = NULL;

    if (check_for_string_or_number_arg(argvars, 0) == FAIL
	    || check_for_opt_string_or_list_arg(argvars, 1) == FAIL
	    || (argvars[1].v_type != VAR_UNKNOWN
		&& (argvars[2].v_type != VAR_UNKNOWN
		    && (check_for_opt_number_arg(argvars, 3) == FAIL
			|| (argvars[3].v_type != VAR_UNKNOWN
			    && check_for_opt_string_arg(argvars, 4) == FAIL)))))
	return;

    cmd = tv_get_string_chk(&argvars[0]);

    // trylevel must be zero for a ":throw" command to be considered failed
    trylevel = 0;
    suppress_errthrow = TRUE;
    in_assert_fails = TRUE;

    do_cmdline_cmd(cmd);

    // reset here for any errors reported below
    trylevel = save_trylevel;
    suppress_errthrow = FALSE;

    if (called_emsg == called_emsg_before)
    {
	prepare_assert_error(&ga);
	ga_concat(&ga, (char_u *)"command did not fail: ");
	assert_append_cmd_or_arg(&ga, argvars, cmd);
	assert_error(&ga);
	ga_clear(&ga);
	rettv->vval.v_number = 1;
    }
    else if (argvars[1].v_type != VAR_UNKNOWN)
    {
	char_u	buf[NUMBUFLEN];
	char_u	*expected;
	char_u	*expected_str = NULL;
	int	error_found = FALSE;
	int	error_found_index = 1;
	char_u	*actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]"
						       : emsg_assert_fails_msg;

	if (argvars[1].v_type == VAR_STRING)
	{
	    expected = tv_get_string_buf_chk(&argvars[1], buf);
	    error_found = expected == NULL
			   || strstr((char *)actual, (char *)expected) == NULL;
	}
	else if (argvars[1].v_type == VAR_LIST)
	{
	    list_T	*list = argvars[1].vval.v_list;
	    typval_T	*tv;

	    if (list == NULL || list->lv_len < 1 || list->lv_len > 2)
	    {
		wrong_arg_msg = e_assert_fails_second_arg;
		goto theend;
	    }
	    CHECK_LIST_MATERIALIZE(list);
	    tv = &list->lv_first->li_tv;
	    expected = tv_get_string_buf_chk(tv, buf);
	    if (expected == NULL)
		goto theend;
	    if (!pattern_match(expected, actual, FALSE))
	    {
		error_found = TRUE;
		expected_str = expected;
	    }
	    else if (list->lv_len == 2)
	    {
		// make a copy, an error in pattern_match() may free it
		tofree = actual = vim_strsave(get_vim_var_str(VV_ERRMSG));
		if (actual != NULL)
		{
		    tv = &list->lv_u.mat.lv_last->li_tv;
		    expected = tv_get_string_buf_chk(tv, buf);
		    if (expected == NULL)
			goto theend;
		    if (!pattern_match(expected, actual, FALSE))
		    {
			error_found = TRUE;
			expected_str = expected;
		    }
		}
	    }
	}
	else
	{
	    wrong_arg_msg = e_assert_fails_second_arg;
	    goto theend;
	}

	if (!error_found && argvars[2].v_type != VAR_UNKNOWN
		&& argvars[3].v_type != VAR_UNKNOWN)
	{
	    if (argvars[3].v_type != VAR_NUMBER)
	    {
		wrong_arg_msg = e_assert_fails_fourth_argument;
		goto theend;
	    }
	    else if (argvars[3].vval.v_number >= 0
			 && argvars[3].vval.v_number != emsg_assert_fails_lnum)
	    {
		error_found = TRUE;
		error_found_index = 3;
	    }
	    if (!error_found && argvars[4].v_type != VAR_UNKNOWN)
	    {
		if (argvars[4].v_type != VAR_STRING)
		{
		    wrong_arg_msg = e_assert_fails_fifth_argument;
		    goto theend;
		}
		else if (argvars[4].vval.v_string != NULL
		    && !pattern_match(argvars[4].vval.v_string,
					     emsg_assert_fails_context, FALSE))
		{
		    error_found = TRUE;
		    error_found_index = 4;
		}
	    }
	}

	if (error_found)
	{
	    typval_T actual_tv;

	    prepare_assert_error(&ga);
	    if (error_found_index == 3)
	    {
		actual_tv.v_type = VAR_NUMBER;
		actual_tv.vval.v_number = emsg_assert_fails_lnum;
	    }
	    else if (error_found_index == 4)
	    {
		actual_tv.v_type = VAR_STRING;
		actual_tv.vval.v_string = emsg_assert_fails_context;
	    }
	    else
	    {
		actual_tv.v_type = VAR_STRING;
		actual_tv.vval.v_string = actual;
	    }
	    fill_assert_error(&ga, &argvars[2], expected_str,
			&argvars[error_found_index], &actual_tv, ASSERT_OTHER);
	    ga_concat(&ga, (char_u *)": ");
	    assert_append_cmd_or_arg(&ga, argvars, cmd);
	    assert_error(&ga);
	    ga_clear(&ga);
	    rettv->vval.v_number = 1;
	}
    }

theend:
    trylevel = save_trylevel;
    suppress_errthrow = FALSE;
    in_assert_fails = FALSE;
    did_emsg = FALSE;
    got_int = FALSE;
    msg_col = 0;
    need_wait_return = FALSE;
    emsg_on_display = FALSE;
    msg_scrolled = 0;
    lines_left = Rows;
    VIM_CLEAR(emsg_assert_fails_msg);
    vim_free(tofree);
    set_vim_var_string(VV_ERRMSG, NULL, 0);
    if (wrong_arg_msg != NULL)
	emsg(_(wrong_arg_msg));
}


static ssize_t jfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
	struct file *file = iocb->ki_filp;
	struct address_space *mapping = file->f_mapping;
	struct inode *inode = file->f_mapping->host;
	size_t count = iov_iter_count(iter);
	ssize_t ret;

	ret = blockdev_direct_IO(iocb, inode, iter, jfs_get_block);

	/*
	 * In case of error extending write may have instantiated a few
	 * blocks outside i_size. Trim these off again.
	 */
	if (unlikely(iov_iter_rw(iter) == WRITE && ret < 0)) {
		loff_t isize = i_size_read(inode);
		loff_t end = iocb->ki_pos + count;

		if (end > isize)
			jfs_write_failed(mapping, end);
	}

	return ret;
}


void jfs_truncate_nolock(struct inode *ip, loff_t length)
{
	loff_t newsize;
	tid_t tid;

	ASSERT(length >= 0);

	if (test_cflag(COMMIT_Nolink, ip)) {
		xtTruncate(0, ip, length, COMMIT_WMAP);
		return;
	}

	do {
		tid = txBegin(ip->i_sb, 0);

		/*
		 * The commit_mutex cannot be taken before txBegin.
		 * txBegin may block and there is a chance the inode
		 * could be marked dirty and need to be committed
		 * before txBegin unblocks
		 */
		mutex_lock(&JFS_IP(ip)->commit_mutex);

		newsize = xtTruncate(tid, ip, length,
				     COMMIT_TRUNCATE | COMMIT_PWMAP);
		if (newsize < 0) {
			txEnd(tid);
			mutex_unlock(&JFS_IP(ip)->commit_mutex);
			break;
		}

		ip->i_mtime = ip->i_ctime = current_time(ip);
		mark_inode_dirty(ip);

		txCommit(tid, 1, &ip, 0);
		txEnd(tid);
		mutex_unlock(&JFS_IP(ip)->commit_mutex);
	} while (newsize > length);	/* Truncate isn't always atomic */
}


static Image *ReadGROUP4Image(const ImageInfo *image_info,
  ExceptionInfo *exception)
{
  char
    filename[MagickPathExtent];

  FILE
    *file;

  Image
    *image;

  ImageInfo
    *read_info;

  int
    c,
    unique_file;

  MagickBooleanType
    status;

  size_t
    length;

  ssize_t
    offset,
    strip_offset;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Write raw CCITT Group 4 wrapped as a TIFF image file.
  */
  file=(FILE *) NULL;
  unique_file=AcquireUniqueFileResource(filename);
  if (unique_file != -1)
    file=fdopen(unique_file,"wb");
  if ((unique_file == -1) || (file == (FILE *) NULL))
    ThrowImageException(FileOpenError,"UnableToCreateTemporaryFile");
  length=fwrite("\111\111\052\000\010\000\000\000\016\000",1,10,file);
  if (length != 10)
    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
  length=fwrite("\376\000\003\000\001\000\000\000\000\000\000\000",1,12,file);
  length=fwrite("\000\001\004\000\001\000\000\000",1,8,file);
  length=WriteLSBLong(file,(unsigned int) image->columns);
  length=fwrite("\001\001\004\000\001\000\000\000",1,8,file);
  length=WriteLSBLong(file,(unsigned int) image->rows);
  length=fwrite("\002\001\003\000\001\000\000\000\001\000\000\000",1,12,file);
  length=fwrite("\003\001\003\000\001\000\000\000\004\000\000\000",1,12,file);
  length=fwrite("\006\001\003\000\001\000\000\000\000\000\000\000",1,12,file);
  length=fwrite("\021\001\003\000\001\000\000\000",1,8,file);
  strip_offset=10+(12*14)+4+8;
  length=WriteLSBLong(file,(unsigned int) strip_offset);
  length=fwrite("\022\001\003\000\001\000\000\000",1,8,file);
  length=WriteLSBLong(file,(unsigned int) image_info->orientation);
  length=fwrite("\025\001\003\000\001\000\000\000\001\000\000\000",1,12,file);
  length=fwrite("\026\001\004\000\001\000\000\000",1,8,file);
  length=WriteLSBLong(file,(unsigned int) image->rows);
  length=fwrite("\027\001\004\000\001\000\000\000\000\000\000\000",1,12,file);
  offset=(ssize_t) ftell(file)-4;
  length=fwrite("\032\001\005\000\001\000\000\000",1,8,file);
  length=WriteLSBLong(file,(unsigned int) (strip_offset-8));
  length=fwrite("\033\001\005\000\001\000\000\000",1,8,file);
  length=WriteLSBLong(file,(unsigned int) (strip_offset-8));
  length=fwrite("\050\001\003\000\001\000\000\000\002\000\000\000",1,12,file);
  length=fwrite("\000\000\000\000",1,4,file);
  length=WriteLSBLong(file,(unsigned int) image->resolution.x);
  length=WriteLSBLong(file,1);
  status=MagickTrue;
  for (length=0; (c=ReadBlobByte(image)) != EOF; length++)
    if (fputc(c,file) != c)
      status=MagickFalse;
  offset=(ssize_t) fseek(file,(off_t) offset,SEEK_SET);
  length=WriteLSBLong(file,(unsigned int) length);
  if (ferror(file) != 0)
    {
      (void) fclose(file);
      ThrowImageException(FileOpenError,"UnableToCreateTemporaryFile");
    }
  (void) fclose(file);
  (void) CloseBlob(image);
  image=DestroyImage(image);
  /*
    Read TIFF image.
  */
  read_info=CloneImageInfo((ImageInfo *) NULL);
  (void) FormatLocaleString(read_info->filename,MagickPathExtent,"%s",filename);
  image=ReadTIFFImage(read_info,exception);
  read_info=DestroyImageInfo(read_info);
  if (image != (Image *) NULL)
    {
      (void) CopyMagickString(image->filename,image_info->filename,
        MagickPathExtent);
      (void) CopyMagickString(image->magick_filename,image_info->filename,
        MagickPathExtent);
      (void) CopyMagickString(image->magick,"GROUP4",MagickPathExtent);
    }
  (void) RelinquishUniqueFileResource(filename);
  if (status == MagickFalse)
    image=DestroyImage(image);
  return(image);
}


static int efi_capsule_open(struct inode *inode, struct file *file)
{
	struct capsule_info *cap_info;

	cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL);
	if (!cap_info)
		return -ENOMEM;

	cap_info->pages = kzalloc(sizeof(void *), GFP_KERNEL);
	if (!cap_info->pages) {
		kfree(cap_info);
		return -ENOMEM;
	}

	cap_info->phys = kzalloc(sizeof(void *), GFP_KERNEL);
	if (!cap_info->phys) {
		kfree(cap_info->pages);
		kfree(cap_info);
		return -ENOMEM;
	}

	file->private_data = cap_info;

	return 0;
}


static int em28xx_wait_until_ac97_features_equals(struct em28xx *dev,
						  int expected_feat)
{
	unsigned long timeout = jiffies + msecs_to_jiffies(2000);
	int feat, powerdown;

	while (time_is_after_jiffies(timeout)) {
		feat = em28xx_read_ac97(dev, AC97_RESET);
		if (feat < 0)
			return feat;

		powerdown = em28xx_read_ac97(dev, AC97_POWERDOWN);
		if (powerdown < 0)
			return powerdown;

		if (feat == expected_feat && feat != powerdown)
			return 0;

		msleep(50);
	}

	dev_warn(&dev->intf->dev, "AC97 registers access is not reliable !\n");
	return -ETIMEDOUT;
}


static int em28xx_usb_probe(struct usb_interface *intf,
			    const struct usb_device_id *id)
{
	struct usb_device *udev;
	struct em28xx *dev = NULL;
	int retval;
	bool has_vendor_audio = false, has_video = false, has_dvb = false;
	int i, nr, try_bulk;
	const int ifnum = intf->altsetting[0].desc.bInterfaceNumber;
	char *speed;

	udev = usb_get_dev(interface_to_usbdev(intf));

	/* Check to see next free device and mark as used */
	do {
		nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);
		if (nr >= EM28XX_MAXBOARDS) {
			/* No free device slots */
			dev_err(&intf->dev,
				"Driver supports up to %i em28xx boards.\n",
			       EM28XX_MAXBOARDS);
			retval = -ENOMEM;
			goto err_no_slot;
		}
	} while (test_and_set_bit(nr, em28xx_devused));

	/* Don't register audio interfaces */
	if (intf->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
		dev_info(&intf->dev,
			"audio device (%04x:%04x): interface %i, class %i\n",
			le16_to_cpu(udev->descriptor.idVendor),
			le16_to_cpu(udev->descriptor.idProduct),
			ifnum,
			intf->altsetting[0].desc.bInterfaceClass);

		retval = -ENODEV;
		goto err;
	}

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto err;
	}

	/* compute alternate max packet sizes */
	dev->alt_max_pkt_size_isoc = kcalloc(intf->num_altsetting,
					     sizeof(dev->alt_max_pkt_size_isoc[0]),
					     GFP_KERNEL);
	if (!dev->alt_max_pkt_size_isoc) {
		kfree(dev);
		retval = -ENOMEM;
		goto err;
	}

	/* Get endpoints */
	for (i = 0; i < intf->num_altsetting; i++) {
		int ep;

		for (ep = 0;
		     ep < intf->altsetting[i].desc.bNumEndpoints;
		     ep++)
			em28xx_check_usb_descriptor(dev, udev, intf,
						    i, ep,
						    &has_vendor_audio,
						    &has_video,
						    &has_dvb);
	}

	if (!(has_vendor_audio || has_video || has_dvb)) {
		retval = -ENODEV;
		goto err_free;
	}

	switch (udev->speed) {
	case USB_SPEED_LOW:
		speed = "1.5";
		break;
	case USB_SPEED_UNKNOWN:
	case USB_SPEED_FULL:
		speed = "12";
		break;
	case USB_SPEED_HIGH:
		speed = "480";
		break;
	default:
		speed = "unknown";
	}

	dev_info(&intf->dev,
		"New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n",
		udev->manufacturer ? udev->manufacturer : "",
		udev->product ? udev->product : "",
		speed,
		le16_to_cpu(udev->descriptor.idVendor),
		le16_to_cpu(udev->descriptor.idProduct),
		ifnum,
		intf->altsetting->desc.bInterfaceNumber);

	/*
	 * Make sure we have 480 Mbps of bandwidth, otherwise things like
	 * video stream wouldn't likely work, since 12 Mbps is generally
	 * not enough even for most Digital TV streams.
	 */
	if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
		dev_err(&intf->dev, "Device initialization failed.\n");
		dev_err(&intf->dev,
			"Device must be connected to a high-speed USB 2.0 port.\n");
		retval = -ENODEV;
		goto err_free;
	}

	kref_init(&dev->ref);

	dev->devno = nr;
	dev->model = id->driver_info;
	dev->alt   = -1;
	dev->is_audio_only = has_vendor_audio && !(has_video || has_dvb);
	dev->has_video = has_video;
	dev->ifnum = ifnum;

	dev->ts = PRIMARY_TS;
	snprintf(dev->name, 28, "em28xx");
	dev->dev_next = NULL;

	if (has_vendor_audio) {
		dev_info(&intf->dev,
			"Audio interface %i found (Vendor Class)\n", ifnum);
		dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR;
	}
	/* Checks if audio is provided by a USB Audio Class intf */
	for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
		struct usb_interface *uif = udev->config->interface[i];

		if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
			if (has_vendor_audio)
				dev_err(&intf->dev,
					"em28xx: device seems to have vendor AND usb audio class interfaces !\n"
					"\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n");
			dev->usb_audio_type = EM28XX_USB_AUDIO_CLASS;
			break;
		}
	}

	if (has_video)
		dev_info(&intf->dev, "Video interface %i found:%s%s\n",
			ifnum,
			dev->analog_ep_bulk ? " bulk" : "",
			dev->analog_ep_isoc ? " isoc" : "");
	if (has_dvb)
		dev_info(&intf->dev, "DVB interface %i found:%s%s\n",
			ifnum,
			dev->dvb_ep_bulk ? " bulk" : "",
			dev->dvb_ep_isoc ? " isoc" : "");

	dev->num_alt = intf->num_altsetting;

	if ((unsigned int)card[nr] < em28xx_bcount)
		dev->model = card[nr];

	/* save our data pointer in this intf device */
	usb_set_intfdata(intf, dev);

	/* allocate device struct and check if the device is a webcam */
	mutex_init(&dev->lock);
	retval = em28xx_init_dev(dev, udev, intf, nr);
	if (retval)
		goto err_free;

	if (usb_xfer_mode < 0) {
		if (dev->is_webcam)
			try_bulk = 1;
		else
			try_bulk = 0;
	} else {
		try_bulk = usb_xfer_mode > 0;
	}

	/* Disable V4L2 if the device doesn't have a decoder or image sensor */
	if (has_video &&
	    dev->board.decoder == EM28XX_NODECODER &&
	    dev->em28xx_sensor == EM28XX_NOSENSOR) {
		dev_err(&intf->dev,
			"Currently, V4L2 is not supported on this model\n");
		has_video = false;
		dev->has_video = false;
	}

	if (dev->board.has_dual_ts &&
	    (dev->tuner_type != TUNER_ABSENT || INPUT(0)->type)) {
		/*
		 * The logic with sets alternate is not ready for dual-tuners
		 * which analog modes.
		 */
		dev_err(&intf->dev,
			"We currently don't support analog TV or stream capture on dual tuners.\n");
		has_video = false;
	}

	/* Select USB transfer types to use */
	if (has_video) {
		if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
			dev->analog_xfer_bulk = 1;
		dev_info(&intf->dev, "analog set to %s mode.\n",
			dev->analog_xfer_bulk ? "bulk" : "isoc");
	}
	if (has_dvb) {
		if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk))
			dev->dvb_xfer_bulk = 1;
		dev_info(&intf->dev, "dvb set to %s mode.\n",
			dev->dvb_xfer_bulk ? "bulk" : "isoc");
	}

	if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) {
		kref_init(&dev->dev_next->ref);

		dev->dev_next->ts = SECONDARY_TS;
		dev->dev_next->alt   = -1;
		dev->dev_next->is_audio_only = has_vendor_audio &&
						!(has_video || has_dvb);
		dev->dev_next->has_video = false;
		dev->dev_next->ifnum = ifnum;
		dev->dev_next->model = id->driver_info;

		mutex_init(&dev->dev_next->lock);
		retval = em28xx_init_dev(dev->dev_next, udev, intf,
					 dev->dev_next->devno);
		if (retval)
			goto err_free;

		dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */
		dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */

		if (usb_xfer_mode < 0) {
			if (dev->dev_next->is_webcam)
				try_bulk = 1;
			else
				try_bulk = 0;
		} else {
			try_bulk = usb_xfer_mode > 0;
		}

		/* Select USB transfer types to use */
		if (has_dvb) {
			if (!dev->dvb_ep_isoc_ts2 ||
			    (try_bulk && dev->dvb_ep_bulk_ts2))
				dev->dev_next->dvb_xfer_bulk = 1;
			dev_info(&dev->intf->dev, "dvb ts2 set to %s mode.\n",
				 dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc");
		}

		dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2;
		dev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2;
		dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2;
		dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc;

		/* Configure hardware to support TS2*/
		if (dev->dvb_xfer_bulk) {
			/* The ep4 and ep5 are configured for BULK */
			em28xx_write_reg(dev, 0x0b, 0x96);
			mdelay(100);
			em28xx_write_reg(dev, 0x0b, 0x80);
			mdelay(100);
		} else {
			/* The ep4 and ep5 are configured for ISO */
			em28xx_write_reg(dev, 0x0b, 0x96);
			mdelay(100);
			em28xx_write_reg(dev, 0x0b, 0x82);
			mdelay(100);
		}
	}

	request_modules(dev);

	/*
	 * Do it at the end, to reduce dynamic configuration changes during
	 * the device init. Yet, as request_modules() can be async, the
	 * topology will likely change after the load of the em28xx subdrivers.
	 */
#ifdef CONFIG_MEDIA_CONTROLLER
	retval = media_device_register(dev->media_dev);
#endif

	return 0;

err_free:
	kfree(dev->alt_max_pkt_size_isoc);
	kfree(dev);

err:
	clear_bit(nr, em28xx_devused);

err_no_slot:
	usb_put_dev(udev);
	return retval;
}


static void request_modules(struct em28xx *dev)
{
	INIT_WORK(&dev->request_module_wk, request_module_async);
	schedule_work(&dev->request_module_wk);
}


DEFUN (no_ip_community_list_standard_all,
       no_ip_community_list_standard_all_cmd,
       "no ip community-list <1-99>",
       NO_STR
       IP_STR
       COMMUNITY_LIST_STR
       "Community list number (standard)\n")
{
  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD);
}


DEFUN (bgp_confederation_identifier,
       bgp_confederation_identifier_cmd,
       "bgp confederation identifier <1-65535>",
       "BGP specific commands\n"
       "AS confederation parameters\n"
       "AS number\n"
       "Set routing domain confederation AS\n")
{
  struct bgp *bgp;
  as_t as;

  bgp = vty->index;

  VTY_GET_INTEGER ("AS", as, argv[0]);

  bgp_confederation_id_set (bgp, as);

  return CMD_SUCCESS;
}


DEFUN (neighbor_route_reflector_client,
       neighbor_route_reflector_client_cmd,
       NEIGHBOR_CMD2 "route-reflector-client",
       NEIGHBOR_STR
       NEIGHBOR_ADDR_STR2
       "Configure a neighbor as Route Reflector client\n")
{
  struct peer *peer;

  peer = peer_and_group_lookup_vty (vty, argv[0]);
  if (! peer)
    return CMD_WARNING;

  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
			       bgp_node_safi (vty),
			       PEER_FLAG_REFLECTOR_CLIENT);
}


DEFUN (neighbor_disable_connected_check,
       neighbor_disable_connected_check_cmd,
       NEIGHBOR_CMD2 "disable-connected-check",
       NEIGHBOR_STR
       NEIGHBOR_ADDR_STR2
       "one-hop away EBGP peer using loopback address\n")
{
  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DISABLE_CONNECTED_CHECK);
}


DEFUN (neighbor_timers,
       neighbor_timers_cmd,
       NEIGHBOR_CMD2 "timers <0-65535> <0-65535>",
       NEIGHBOR_STR
       NEIGHBOR_ADDR_STR2
       "BGP per neighbor timers\n"
       "Keepalive interval\n"
       "Holdtime\n")
{
  return peer_timers_set_vty (vty, argv[0], argv[1], argv[2]);
}


open_crashlog(void)
{
#define CRASHLOG_PREFIX "/var/tmp/quagga."
#define CRASHLOG_SUFFIX "crashlog"
  if (zlog_default && zlog_default->ident)
    {
      /* Avoid strlen since it is not async-signal-safe. */
      const char *p;
      size_t ilen;

      for (p = zlog_default->ident, ilen = 0; *p; p++)
	ilen++;
      {
	char buf[sizeof(CRASHLOG_PREFIX)+ilen+sizeof(CRASHLOG_SUFFIX)+3];
	char *s = buf;
#define LOC s,buf+sizeof(buf)-s
	s = str_append(LOC, CRASHLOG_PREFIX);
	s = str_append(LOC, zlog_default->ident);
	s = str_append(LOC, ".");
	s = str_append(LOC, CRASHLOG_SUFFIX);
#undef LOC
	*s = '\0';
	return open(buf, O_WRONLY|O_CREAT|O_EXCL, LOGFILE_MASK);
      }
    }
  return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, O_WRONLY|O_CREAT|O_EXCL,
	      LOGFILE_MASK);
#undef CRASHLOG_SUFFIX
#undef CRASHLOG_PREFIX
}


DEFUN (bgp_client_to_client_reflection,
       bgp_client_to_client_reflection_cmd,
       "bgp client-to-client reflection",
       "BGP specific commands\n"
       "Configure client to client route reflection\n"
       "reflection of routes allowed\n")
{
  struct bgp *bgp;

  bgp = vty->index;
  bgp_flag_unset (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT);
  return CMD_SUCCESS;
}


DEFUN (show_ip_bgp_instance_rsclient_summary,
       show_ip_bgp_instance_rsclient_summary_cmd,
       "show ip bgp view WORD rsclient summary",
       SHOW_STR
       IP_STR
       BGP_STR
       "BGP view\n"
       "View name\n"
       "Information about Route Server Clients\n"
       "Summary of all Route Server Clients\n")
{
  return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
}


time_print(FILE *fp, struct timestamp_control *ctl)
{
  if (!ctl->already_rendered)
    {
      ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
      ctl->already_rendered = 1;
    }
  fprintf(fp, "%s ", ctl->buf);
}


DEFUN (show_ip_bgp_attr_info,
       show_ip_bgp_attr_info_cmd,
       "show ip bgp attribute-info",
       SHOW_STR
       IP_STR
       BGP_STR
       "List all bgp attribute information\n")
{
  attr_show_all (vty);
  return CMD_SUCCESS;
}


DEFUN (clear_bgp_all_rsclient,
       clear_bgp_all_rsclient_cmd,
       "clear bgp * rsclient",
       CLEAR_STR
       BGP_STR
       "Clear all peers\n"
       "Soft reconfig for rsclient RIB\n")
{
  if (argc == 1)
    return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all,
                          BGP_CLEAR_SOFT_RSCLIENT, NULL);

  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
                        BGP_CLEAR_SOFT_RSCLIENT, NULL);
}


DEFUN (bgp_default_ipv4_unicast,
       bgp_default_ipv4_unicast_cmd,
       "bgp default ipv4-unicast",
       "BGP specific commands\n"
       "Configure BGP defaults\n"
       "Activate ipv4-unicast for a peer by default\n")
{
  struct bgp *bgp;

  bgp = vty->index;
  bgp_flag_unset (bgp, BGP_FLAG_NO_DEFAULT_IPV4);
  return CMD_SUCCESS;
}


DEFUN (clear_ip_bgp_external_ipv4_soft_out,
       clear_ip_bgp_external_ipv4_soft_out_cmd,
       "clear ip bgp external ipv4 (unicast|multicast) soft out",
       CLEAR_STR
       IP_STR
       BGP_STR
       "Clear all external peers\n"
       "Address family\n"
       "Address Family modifier\n"
       "Address Family modifier\n"
       "Soft reconfig\n"
       "Soft reconfig outbound update\n")
{
  if (strncmp (argv[0], "m", 1) == 0)
    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external,
			  BGP_CLEAR_SOFT_OUT, NULL);

  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
			BGP_CLEAR_SOFT_OUT, NULL);
}


peer_distribute_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
			   safi_t safi, const char *direct_str)
{
  int ret;
  struct peer *peer;
  int direct = FILTER_IN;

  peer = peer_and_group_lookup_vty (vty, ip_str);
  if (! peer)
    return CMD_WARNING;

  /* Check filter direction. */
  if (strncmp (direct_str, "i", 1) == 0)
    direct = FILTER_IN;
  else if (strncmp (direct_str, "o", 1) == 0)
    direct = FILTER_OUT;

  ret = peer_distribute_unset (peer, afi, safi, direct);

  return bgp_vty_return (vty, ret);
}


DEFUN (neighbor_send_community,
       neighbor_send_community_cmd,
       NEIGHBOR_CMD2 "send-community",
       NEIGHBOR_STR
       NEIGHBOR_ADDR_STR2
       "Send Community attribute to this neighbor\n")
{
  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
			       bgp_node_safi (vty),
			       PEER_FLAG_SEND_COMMUNITY);
}


DEFUN (show_ip_bgp_vpnv4_all_summary,
       show_ip_bgp_vpnv4_all_summary_cmd,
       "show ip bgp vpnv4 all summary",
       SHOW_STR
       IP_STR
       BGP_STR
       "Display VPNv4 NLRI specific information\n"
       "Display information about all VPNv4 NLRIs\n"
       "Summary of BGP neighbor status\n")
{
  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
}


community_show_all_iterator (struct hash_backet *backet, struct vty *vty)
{
  struct community *com;

  com = (struct community *) backet->data;
  vty_out (vty, "[%p] (%ld) %s%s", backet, com->refcnt,
	   community_str (com), VTY_NEWLINE);
}


DEFUN (clear_ip_bgp_all_ipv4_soft,
       clear_ip_bgp_all_ipv4_soft_cmd,
       "clear ip bgp * ipv4 (unicast|multicast) soft",
       CLEAR_STR
       IP_STR
       BGP_STR
       "Clear all peers\n"
       "Address family\n"
       "Address Family Modifier\n"
       "Address Family Modifier\n"
       "Soft reconfig\n")
{
  if (strncmp (argv[0], "m", 1) == 0)
    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all,
			  BGP_CLEAR_SOFT_BOTH, NULL);

  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
			BGP_CLEAR_SOFT_BOTH, NULL);
}


DEFUN (no_bgp_default_local_preference,
       no_bgp_default_local_preference_cmd,
       "no bgp default local-preference",
       NO_STR
       "BGP specific commands\n"
       "Configure BGP defaults\n"
       "local preference (higher=more preferred)\n")
{
  struct bgp *bgp;

  bgp = vty->index;
  bgp_default_local_preference_unset (bgp);
  return CMD_SUCCESS;
}


DEFUN (show_bgp_instance_summary,
       show_bgp_instance_summary_cmd,
       "show bgp view WORD summary",
       SHOW_STR
       BGP_STR
       "BGP view\n"
       "View name\n"
       "Summary of BGP neighbor status\n")
{
  return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
}


DEFUN (clear_ip_bgp_as_vpnv4_soft_in,
       clear_ip_bgp_as_vpnv4_soft_in_cmd,
       "clear ip bgp <1-65535> vpnv4 unicast soft in",
       CLEAR_STR
       IP_STR
       BGP_STR
       "Clear peers with the AS number\n"
       "Address family\n"
       "Address Family modifier\n"
       "Soft reconfig\n"
       "Soft reconfig inbound update\n")
{
  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as,
			BGP_CLEAR_SOFT_IN, argv[0]);
}


bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
			u_char orf_type, u_char when_to_refresh, int remove)
{
  struct stream *s;
  struct stream *packet;
  int length;
  struct bgp_filter *filter;
  int orf_refresh = 0;

#ifdef DISABLE_BGP_ANNOUNCE
  return;
#endif /* DISABLE_BGP_ANNOUNCE */

  filter = &peer->filter[afi][safi];

  /* Adjust safi code. */
  if (safi == SAFI_MPLS_VPN)
    safi = BGP_SAFI_VPNV4;

  s = stream_new (BGP_MAX_PACKET_SIZE);

  /* Make BGP update packet. */
  if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
    bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_NEW);
  else
    bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_OLD);

  /* Encode Route Refresh message. */
  stream_putw (s, afi);
  stream_putc (s, 0);
  stream_putc (s, safi);

  if (orf_type == ORF_TYPE_PREFIX
      || orf_type == ORF_TYPE_PREFIX_OLD)
    if (remove || filter->plist[FILTER_IN].plist)
      {
	u_int16_t orf_len;
	unsigned long orfp;

	orf_refresh = 1;
	stream_putc (s, when_to_refresh);
	stream_putc (s, orf_type);
	orfp = stream_get_endp (s);
	stream_putw (s, 0);

	if (remove)
	  {
	    UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
	    stream_putc (s, ORF_COMMON_PART_REMOVE_ALL);
	    if (BGP_DEBUG (normal, NORMAL))
	      zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d",
			 peer->host, orf_type,
			 (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"),
			 afi, safi);
	  }
	else
	  {
	    SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
	    prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].plist,
				  ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT,
				  ORF_COMMON_PART_DENY);
	    if (BGP_DEBUG (normal, NORMAL))
	      zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d",
			 peer->host, orf_type,
			 (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"),
			 afi, safi);
	  }

	/* Total ORF Entry Len. */
	orf_len = stream_get_endp (s) - orfp - 2;
	stream_putw_at (s, orfp, orf_len);
      }

  /* Set packet size. */
  length = bgp_packet_set_size (s);

  if (BGP_DEBUG (normal, NORMAL))
    {
      if (! orf_refresh)
	zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d",
		   peer->host, afi, safi);
      zlog_debug ("%s send message type %d, length (incl. header) %d",
		 peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ?
		 BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length);
    }

  /* Make real packet. */
  packet = stream_dup (s);
  stream_free (s);

  /* Add packet to the peer. */
  bgp_packet_add (peer, packet);

  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
}


bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
{
  struct peer *peer;
  struct listnode *node, *nnode;
  unsigned int count = 0;
  char timebuf[BGP_UPTIME_LEN];
  int len;

  /* Header string for each address family. */
  static char header[] = "Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd";

  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
    {
      if (peer->afc[afi][safi])
	{
          if (!count)
            {
              unsigned long ents;
              char memstrbuf[MTYPE_MEMSTR_LEN];

              /* Usage summary and header */
              vty_out (vty,
                       "BGP router identifier %s, local AS number %d%s",
                       inet_ntoa (bgp->router_id), bgp->as, VTY_NEWLINE);

              ents = bgp_table_count (bgp->rib[afi][safi]);
              vty_out (vty, "RIB entries %ld, using %s of memory%s", ents,
                       mtype_memstr (memstrbuf, sizeof (memstrbuf),
                                     ents * sizeof (struct bgp_node)),
                       VTY_NEWLINE);

              /* Peer related usage */
              ents = listcount (bgp->peer);
              vty_out (vty, "Peers %ld, using %s of memory%s",
                       ents,
                       mtype_memstr (memstrbuf, sizeof (memstrbuf),
                                     ents * sizeof (struct peer)),
                       VTY_NEWLINE);

              if ((ents = listcount (bgp->rsclient)))
                vty_out (vty, "RS-Client peers %ld, using %s of memory%s",
                         ents,
                         mtype_memstr (memstrbuf, sizeof (memstrbuf),
                                       ents * sizeof (struct peer)),
                         VTY_NEWLINE);

              if ((ents = listcount (bgp->group)))
                vty_out (vty, "Peer groups %ld, using %s of memory%s", ents,
                         mtype_memstr (memstrbuf, sizeof (memstrbuf),
                                       ents * sizeof (struct peer_group)),
                         VTY_NEWLINE);

              if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
                vty_out (vty, "Dampening enabled.%s", VTY_NEWLINE);
              vty_out (vty, "%s", VTY_NEWLINE);
              vty_out (vty, "%s%s", header, VTY_NEWLINE);
            }

	  count++;

	  len = vty_out (vty, "%s", peer->host);
	  len = 16 - len;
	  if (len < 1)
	    vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " ");
	  else
	    vty_out (vty, "%*s", len, " ");

	  vty_out (vty, "4 ");

	  vty_out (vty, "%5d %7d %7d %8d %4d %4lu ",
		   peer->as,
		   peer->open_in + peer->update_in + peer->keepalive_in
		   + peer->notify_in + peer->refresh_in + peer->dynamic_cap_in,
		   peer->open_out + peer->update_out + peer->keepalive_out
		   + peer->notify_out + peer->refresh_out
		   + peer->dynamic_cap_out,
		   0, 0, (unsigned long)peer->obuf->count);

	  vty_out (vty, "%8s",
		   peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN));

	  if (peer->status == Established)
	    {
	      vty_out (vty, " %8ld", peer->pcount[afi][safi]);
	    }
	  else
	    {
	      if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
		vty_out (vty, " Idle (Admin)");
	      else if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
		vty_out (vty, " Idle (PfxCt)");
	      else
		vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, peer->status));
	    }

	  vty_out (vty, "%s", VTY_NEWLINE);
	}
    }

  if (count)
    vty_out (vty, "%sTotal number of neighbors %d%s", VTY_NEWLINE,
	     count, VTY_NEWLINE);
  else
    vty_out (vty, "No %s neighbor is configured%s",
	     afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE);
  return CMD_SUCCESS;
}


DEFUN (no_neighbor_attr_unchanged3,
       no_neighbor_attr_unchanged3_cmd,
       NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop (as-path|med)",
       NO_STR
       NEIGHBOR_STR
       NEIGHBOR_ADDR_STR2
       "BGP attribute is propagated unchanged to this neighbor\n"
       "Nexthop attribute\n"
       "As-path attribute\n"
       "Med attribute\n")
{
  u_int16_t flags = PEER_FLAG_NEXTHOP_UNCHANGED;

  if (strncmp (argv[1], "as-path", 1) == 0)
    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
  else if (strncmp (argv[1], "med", 1) == 0)
    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);

  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
				 bgp_node_safi (vty), flags);
}


extcommunity_list_unset_vty (struct vty *vty, int argc, const char **argv,
			     int style)
{
  int ret;
  int direct = 0;
  char *str = NULL;

  if (argc > 1)
    {
      /* Check the list direct. */
      if (strncmp (argv[1], "p", 1) == 0)
	direct = COMMUNITY_PERMIT;
      else if (strncmp (argv[1], "d", 1) == 0)
	direct = COMMUNITY_DENY;
      else
	{
	  vty_out (vty, "%% Matching condition must be permit or deny%s",
		   VTY_NEWLINE);
	  return CMD_WARNING;
	}

      /* Concat community string argument.  */
      str = argv_concat (argv, argc, 2);
    }

  /* Unset community list.  */
  ret = extcommunity_list_unset (bgp_clist, argv[0], str, direct, style);

  /* Free temporary community list string allocated by
     argv_concat().  */
  if (str)
    XFREE (MTYPE_TMP, str);

  if (ret < 0)
    {
      community_list_perror (vty, ret);
      return CMD_WARNING;
    }

  return CMD_SUCCESS;
}


DEFUN (clear_ip_bgp_peer_vpnv4_soft,
       clear_ip_bgp_peer_vpnv4_soft_cmd,
       "clear ip bgp A.B.C.D vpnv4 unicast soft",
       CLEAR_STR
       IP_STR
       BGP_STR
       "BGP neighbor address to clear\n"
       "Address family\n"
       "Address Family Modifier\n"
       "Soft reconfig\n")
{
  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer,
			BGP_CLEAR_SOFT_BOTH, argv[0]);
}


DEFUN (bgp_bestpath_aspath_confed,
       bgp_bestpath_aspath_confed_cmd,
       "bgp bestpath as-path confed",
       "BGP specific commands\n"
       "Change the default bestpath selection\n"
       "AS-path attribute\n"
       "Compare path lengths including confederation sets & sequences in selecting a route\n")
{
  struct bgp *bgp;

  bgp = vty->index;
  bgp_flag_set (bgp, BGP_FLAG_ASPATH_CONFED);
  return CMD_SUCCESS;
}


DEFUN (show_ip_bgp_ipv4_summary,
       show_ip_bgp_ipv4_summary_cmd,
       "show ip bgp ipv4 (unicast|multicast) summary",
       SHOW_STR
       IP_STR
       BGP_STR
       "Address family\n"
       "Address Family modifier\n"
       "Address Family modifier\n"
       "Summary of BGP neighbor status\n")
{
  if (strncmp (argv[0], "m", 1) == 0)
    return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);

  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
}


DEFUN (no_bgp_bestpath_aspath_confed,
       no_bgp_bestpath_aspath_confed_cmd,
       "no bgp bestpath as-path confed",
       NO_STR
       "BGP specific commands\n"
       "Change the default bestpath selection\n"
       "AS-path attribute\n"
       "Compare path lengths including confederation sets & sequences in selecting a route\n")
{
  struct bgp *bgp;

  bgp = vty->index;
  bgp_flag_unset (bgp, BGP_FLAG_ASPATH_CONFED);
  return CMD_SUCCESS;
}


peer_timers_unset_vty (struct vty *vty, const char *ip_str)
{
  int ret;
  struct peer *peer;

  peer = peer_lookup_vty (vty, ip_str);
  if (! peer)
    return CMD_WARNING;

  ret = peer_timers_unset (peer);

  return bgp_vty_return (vty, ret);
}


community_list_config_str (struct community_entry *entry)
{
  const char *str;

  if (entry->any)
    str = "";
  else
    {
      if (entry->style == COMMUNITY_LIST_STANDARD)
	str = community_str (entry->u.com);
      else
	str = entry->config;
    }
  return str;
}


DEFUN (ip_community_list_standard,
       ip_community_list_standard_cmd,
       "ip community-list <1-99> (deny|permit) .AA:NN",
       IP_STR
       COMMUNITY_LIST_STR
       "Community list number (standard)\n"
       "Specify community to reject\n"
       "Specify community to accept\n"
       COMMUNITY_VAL_STR)
{
  return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD, 0);
}


peer_remote_as_vty (struct vty *vty, const char *peer_str,
                    const char *as_str, afi_t afi, safi_t safi)
{
  int ret;
  struct bgp *bgp;
  as_t as;
  union sockunion su;

  bgp = vty->index;

  /* Get AS number.  */
  VTY_GET_INTEGER_RANGE ("AS", as, as_str, 1, 65535);

  /* If peer is peer group, call proper function.  */
  ret = str2sockunion (peer_str, &su);
  if (ret < 0)
    {
      ret = peer_group_remote_as (bgp, peer_str, &as);
      if (ret < 0)
	{
	  vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE);
	  return CMD_WARNING;
	}
      return CMD_SUCCESS;
    }

  if (peer_address_self_check (&su))
    {
      vty_out (vty, "%% Can not configure the local system as neighbor%s",
	       VTY_NEWLINE);
      return CMD_WARNING;
    }

  ret = peer_remote_as (bgp, &su, &as, afi, safi);

  /* This peer belongs to peer group.  */
  switch (ret)
    {
    case BGP_ERR_PEER_GROUP_MEMBER:
      vty_out (vty, "%% Peer-group AS %d. Cannot configure remote-as for member%s", as, VTY_NEWLINE);
      return CMD_WARNING;
    case BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT:
      vty_out (vty, "%% The AS# can not be changed from %d to %s, peer-group members must be all internal or all external%s", as, as_str, VTY_NEWLINE);
      return CMD_WARNING;
    }
  return bgp_vty_return (vty, ret);
}


syslog_connect(void)
{
#ifdef SUNOS_5
  return -1;
#else
  int fd;
  char *s;
  struct sockaddr_un addr;

  if ((fd = socket(AF_UNIX,SOCK_DGRAM,0)) < 0)
    return -1;
  addr.sun_family = AF_UNIX;
#ifdef _PATH_LOG
#define SYSLOG_SOCKET_PATH _PATH_LOG
#else
#define SYSLOG_SOCKET_PATH "/dev/log"
#endif
  s = str_append(addr.sun_path,sizeof(addr.sun_path),SYSLOG_SOCKET_PATH);
#undef SYSLOG_SOCKET_PATH
  *s = '\0';
  if (connect(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
    {
      close(fd);
      return -1;
    }
  return fd;
#endif
}


DEFUN (no_bgp_redistribute_ipv6,
       no_bgp_redistribute_ipv6_cmd,
       "no redistribute (connected|kernel|ospf6|ripng|static)",
       NO_STR
       "Redistribute information from another routing protocol\n"
       "Connected\n"
       "Kernel routes\n"
       "Open Shurtest Path First (OSPFv3)\n"
       "Routing Information Protocol (RIPng)\n"
       "Static routes\n")
{
  int type;

  type = bgp_str2route_type (AFI_IP6, argv[0]);
  if (! type)
    {
      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
      return CMD_WARNING;
    }

  return bgp_redistribute_unset (vty->index, AFI_IP6, type);
}


bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
{
  struct stream *s = BGP_INPUT (peer);
  u_int16_t restart_flag_time;
  int restart_bit = 0;
  size_t end = stream_get_getp (s) + caphdr->length;

  SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV);
  restart_flag_time = stream_getw(s);
  if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT))
    restart_bit = 1;
  UNSET_FLAG (restart_flag_time, 0xF000);
  peer->v_gr_restart = restart_flag_time;

  if (BGP_DEBUG (normal, NORMAL))
    {
      zlog_debug ("%s OPEN has Graceful Restart capability", peer->host);
      zlog_debug ("%s Peer has%srestarted. Restart Time : %d",
                  peer->host, restart_bit ? " " : " not ",
                  peer->v_gr_restart);
    }

  while (stream_get_getp (s) + 4 < end)
    {
      afi_t afi = stream_getw (s);
      safi_t safi = stream_getc (s);
      u_char flag = stream_getc (s);

      if (!bgp_afi_safi_valid_indices (afi, &safi))
        {
          if (BGP_DEBUG (normal, NORMAL))
            zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported."
                        " Ignore the Graceful Restart capability",
                        peer->host, afi, safi);
        }
      else if (!peer->afc[afi][safi])
        {
          if (BGP_DEBUG (normal, NORMAL))
            zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled."
                        " Ignore the Graceful Restart capability",
                        peer->host, afi, safi);
        }
      else
        {
          if (BGP_DEBUG (normal, NORMAL))
            zlog_debug ("%s Address family %s is%spreserved", peer->host,
                        afi_safi_print (afi, safi),
                        CHECK_FLAG (peer->af_cap[afi][safi],
                                    PEER_CAP_RESTART_AF_PRESERVE_RCV)
                        ? " " : " not ");

          SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV);
          if (CHECK_FLAG (flag, RESTART_F_BIT))
            SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV);

        }
    }
  return 0;
}


DEFUN (clear_ip_bgp_peer_vpnv4_soft_out,
       clear_ip_bgp_peer_vpnv4_soft_out_cmd,
       "clear ip bgp A.B.C.D vpnv4 unicast soft out",
       CLEAR_STR
       IP_STR
       BGP_STR
       "BGP neighbor address to clear\n"
       "Address family\n"
       "Address Family Modifier\n"
       "Soft reconfig\n"
       "Soft reconfig outbound update\n")
{
  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer,
			BGP_CLEAR_SOFT_OUT, argv[0]);
}


DEFUN (clear_ip_bgp_all_soft,
       clear_ip_bgp_all_soft_cmd,
       "clear ip bgp * soft",
       CLEAR_STR
       IP_STR
       BGP_STR
       "Clear all peers\n"
       "Soft reconfig\n")
{
  if (argc == 1)
    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
                        BGP_CLEAR_SOFT_BOTH, NULL);

  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
			BGP_CLEAR_SOFT_BOTH, NULL);
}


mount_args_check(int argc, char **argv, const char *action)
{
   Eina_Bool opts = EINA_FALSE;
   const char *node;
   char buf[PATH_MAX];
   struct stat st;

   if (!strcmp(action, "mount"))
     {
        /* will ALWAYS be:
           /path/to/mount -o nosuid,uid=XYZ,[utf8,] UUID=XXXX-XXXX[-XXXX-XXXX] /media/$devnode
         */
        if (argc != 6) return EINA_FALSE;
        if (argv[2][0] == '-')
          {
             /* disallow any -options other than -o */
             if (strcmp(argv[2], "-o")) return EINA_FALSE;
             opts = mountopts_check(argv[3]);
          }
        if (!opts) return EINA_FALSE;
        if (!strncmp(argv[4], "UUID=", sizeof("UUID=") - 1))
          {
             if (!check_uuid(argv[4] + 5)) return EINA_FALSE;
          }
        else
          {
             if (!check_sane_dev(argv[4])) return EINA_FALSE;
          }
        if (!check_sane_path(argv[5])) return EINA_FALSE;

        node = strrchr(argv[5], '/');
        if (!node) return EINA_FALSE;
        if (!node[1]) return EINA_FALSE;
        if (node - argv[5] != 6) return EINA_FALSE;
        snprintf(buf, sizeof(buf), "/dev%s", node);
        if (stat(buf, &st)) return EINA_FALSE;
     }
   else if (!strcmp(action, "umount"))
     {
        /* will ALWAYS be:
           /path/to/umount /dev/$devnode
         */
        if (argc != 3) return EINA_FALSE;
        if (!check_sane_dev(argv[2])) return EINA_FALSE;
        node = strrchr(argv[2], '/');
        if (!node) return EINA_FALSE;
        if (!node[1]) return EINA_FALSE;
        if (node - argv[2] != 4) return EINA_FALSE;
        /* this is good, but it prevents umounting user-mounted removable media;
         * need to figure out a better way...
         *
           snprintf(buf, sizeof(buf), "/media%s", node);
           if (stat(buf, &st)) return EINA_FALSE;
           if (!S_ISDIR(st.st_mode)) return EINA_FALSE;
         */
     }
   else if (!strcmp(action, "eject"))
     {
        /* will ALWAYS be:
           /path/to/eject /dev/$devnode
         */
        if (argc != 3) return EINA_FALSE;
        if (!check_sane_dev(argv[2])) return EINA_FALSE;
        node = strrchr(argv[2], '/');
        if (!node) return EINA_FALSE;
        if (!node[1]) return EINA_FALSE;
        if (node - argv[2] != 4) return EINA_FALSE;
     }
   else return EINA_FALSE;
   return EINA_TRUE;
}


static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
				   const struct nft_set_ext_tmpl *tmpl,
				   const struct nft_set_ext *ext,
				   struct nft_expr *expr_array[],
				   u32 num_exprs)
{
	struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
	u32 len = sizeof(struct nft_set_elem_expr);
	struct nft_expr *expr;
	int i, err;

	if (num_exprs == 0)
		return 0;

	for (i = 0; i < num_exprs; i++)
		len += expr_array[i]->ops->size;

	if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
		return -EINVAL;

	for (i = 0; i < num_exprs; i++) {
		expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
		err = nft_expr_clone(expr, expr_array[i]);
		if (err < 0)
			goto err_elem_expr_setup;

		elem_expr->size += expr_array[i]->ops->size;
		nft_expr_destroy(ctx, expr_array[i]);
		expr_array[i] = NULL;
	}

	return 0;

err_elem_expr_setup:
	for (; i < num_exprs; i++) {
		nft_expr_destroy(ctx, expr_array[i]);
		expr_array[i] = NULL;
	}

	return -ENOMEM;
}


static void nft_netdev_unregister_hooks(struct net *net,
					struct list_head *hook_list,
					bool release_netdev)
{
	struct nft_hook *hook, *next;

	list_for_each_entry_safe(hook, next, hook_list, list) {
		nf_unregister_net_hook(net, &hook->ops);
		if (release_netdev) {
			list_del(&hook->list);
			kfree_rcu(hook, rcu);
		}
	}
}


static int nf_tables_dump_flowtable(struct sk_buff *skb,
				    struct netlink_callback *cb)
{
	const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
	struct nft_flowtable_filter *filter = cb->data;
	unsigned int idx = 0, s_idx = cb->args[0];
	struct net *net = sock_net(skb->sk);
	int family = nfmsg->nfgen_family;
	struct nft_flowtable *flowtable;
	struct nftables_pernet *nft_net;
	const struct nft_table *table;

	rcu_read_lock();
	nft_net = nft_pernet(net);
	cb->seq = READ_ONCE(nft_net->base_seq);

	list_for_each_entry_rcu(table, &nft_net->tables, list) {
		if (family != NFPROTO_UNSPEC && family != table->family)
			continue;

		list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
			if (!nft_is_active(net, flowtable))
				goto cont;
			if (idx < s_idx)
				goto cont;
			if (idx > s_idx)
				memset(&cb->args[1], 0,
				       sizeof(cb->args) - sizeof(cb->args[0]));
			if (filter && filter->table &&
			    strcmp(filter->table, table->name))
				goto cont;

			if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid,
							  cb->nlh->nlmsg_seq,
							  NFT_MSG_NEWFLOWTABLE,
							  NLM_F_MULTI | NLM_F_APPEND,
							  table->family,
							  flowtable,
							  &flowtable->hook_list) < 0)
				goto done;

			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
cont:
			idx++;
		}
	}
done:
	rcu_read_unlock();

	cb->args[0] = idx;
	return skb->len;
}


static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
			      u8 policy, u32 flags,
			      struct netlink_ext_ack *extack)
{
	const struct nlattr * const *nla = ctx->nla;
	struct nft_stats __percpu *stats = NULL;
	struct nft_table *table = ctx->table;
	struct nft_base_chain *basechain;
	struct net *net = ctx->net;
	char name[NFT_NAME_MAXLEN];
	struct nft_rule_blob *blob;
	struct nft_trans *trans;
	struct nft_chain *chain;
	unsigned int data_size;
	int err;

	if (table->use == UINT_MAX)
		return -EOVERFLOW;

	if (nla[NFTA_CHAIN_HOOK]) {
		struct nft_chain_hook hook;

		if (flags & NFT_CHAIN_BINDING)
			return -EOPNOTSUPP;

		err = nft_chain_parse_hook(net, nla, &hook, family, extack,
					   true);
		if (err < 0)
			return err;

		basechain = kzalloc(sizeof(*basechain), GFP_KERNEL_ACCOUNT);
		if (basechain == NULL) {
			nft_chain_release_hook(&hook);
			return -ENOMEM;
		}
		chain = &basechain->chain;

		if (nla[NFTA_CHAIN_COUNTERS]) {
			stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
			if (IS_ERR(stats)) {
				nft_chain_release_hook(&hook);
				kfree(basechain);
				return PTR_ERR(stats);
			}
			rcu_assign_pointer(basechain->stats, stats);
		}

		err = nft_basechain_init(basechain, family, &hook, flags);
		if (err < 0) {
			nft_chain_release_hook(&hook);
			kfree(basechain);
			return err;
		}
	} else {
		if (flags & NFT_CHAIN_BASE)
			return -EINVAL;
		if (flags & NFT_CHAIN_HW_OFFLOAD)
			return -EOPNOTSUPP;

		chain = kzalloc(sizeof(*chain), GFP_KERNEL_ACCOUNT);
		if (chain == NULL)
			return -ENOMEM;

		chain->flags = flags;
	}
	ctx->chain = chain;

	INIT_LIST_HEAD(&chain->rules);
	chain->handle = nf_tables_alloc_handle(table);
	chain->table = table;

	if (nla[NFTA_CHAIN_NAME]) {
		chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL_ACCOUNT);
	} else {
		if (!(flags & NFT_CHAIN_BINDING)) {
			err = -EINVAL;
			goto err_destroy_chain;
		}

		snprintf(name, sizeof(name), "__chain%llu", ++chain_id);
		chain->name = kstrdup(name, GFP_KERNEL_ACCOUNT);
	}

	if (!chain->name) {
		err = -ENOMEM;
		goto err_destroy_chain;
	}

	if (nla[NFTA_CHAIN_USERDATA]) {
		chain->udata = nla_memdup(nla[NFTA_CHAIN_USERDATA], GFP_KERNEL_ACCOUNT);
		if (chain->udata == NULL) {
			err = -ENOMEM;
			goto err_destroy_chain;
		}
		chain->udlen = nla_len(nla[NFTA_CHAIN_USERDATA]);
	}

	data_size = offsetof(struct nft_rule_dp, data);	/* last rule */
	blob = nf_tables_chain_alloc_rules(data_size);
	if (!blob) {
		err = -ENOMEM;
		goto err_destroy_chain;
	}

	RCU_INIT_POINTER(chain->blob_gen_0, blob);
	RCU_INIT_POINTER(chain->blob_gen_1, blob);

	err = nf_tables_register_hook(net, table, chain);
	if (err < 0)
		goto err_destroy_chain;

	trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
	if (IS_ERR(trans)) {
		err = PTR_ERR(trans);
		goto err_unregister_hook;
	}

	nft_trans_chain_policy(trans) = NFT_CHAIN_POLICY_UNSET;
	if (nft_is_base_chain(chain))
		nft_trans_chain_policy(trans) = policy;

	err = nft_chain_add(table, chain);
	if (err < 0) {
		nft_trans_destroy(trans);
		goto err_unregister_hook;
	}

	if (stats)
		static_branch_inc(&nft_counters_enabled);

	table->use++;

	return 0;
err_unregister_hook:
	nf_tables_unregister_hook(net, table, chain);
err_destroy_chain:
	nf_tables_chain_destroy(ctx);

	return err;
}


static void __nf_tables_unregister_hook(struct net *net,
					const struct nft_table *table,
					struct nft_chain *chain,
					bool release_netdev)
{
	struct nft_base_chain *basechain;
	const struct nf_hook_ops *ops;

	if (table->flags & NFT_TABLE_F_DORMANT ||
	    !nft_is_base_chain(chain))
		return;
	basechain = nft_base_chain(chain);
	ops = &basechain->ops;

	if (basechain->type->ops_unregister)
		return basechain->type->ops_unregister(net, ops);

	if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
		nft_netdev_unregister_hooks(net, &basechain->hook_list,
					    release_netdev);
	else
		nf_unregister_net_hook(net, &basechain->ops);
}


static void nft_commit_release(struct nft_trans *trans)
{
	switch (trans->msg_type) {
	case NFT_MSG_DELTABLE:
		nf_tables_table_destroy(&trans->ctx);
		break;
	case NFT_MSG_NEWCHAIN:
		free_percpu(nft_trans_chain_stats(trans));
		kfree(nft_trans_chain_name(trans));
		break;
	case NFT_MSG_DELCHAIN:
		nf_tables_chain_destroy(&trans->ctx);
		break;
	case NFT_MSG_DELRULE:
		if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
			nft_flow_rule_destroy(nft_trans_flow_rule(trans));

		nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
		break;
	case NFT_MSG_DELSET:
		nft_set_destroy(&trans->ctx, nft_trans_set(trans));
		break;
	case NFT_MSG_DELSETELEM:
		nf_tables_set_elem_destroy(&trans->ctx,
					   nft_trans_elem_set(trans),
					   nft_trans_elem(trans).priv);
		break;
	case NFT_MSG_DELOBJ:
		nft_obj_destroy(&trans->ctx, nft_trans_obj(trans));
		break;
	case NFT_MSG_DELFLOWTABLE:
		if (nft_trans_flowtable_update(trans))
			nft_flowtable_hooks_destroy(&nft_trans_flowtable_hooks(trans));
		else
			nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
		break;
	}

	if (trans->put_net)
		put_net(trans->ctx.net);

	kfree(trans);
}