static int fix_paths(void)
{
  char buff[FN_REFLEN],*pos;
  bool secure_file_priv_nonempty= false;
  convert_dirname(mysql_home,mysql_home,NullS);
  /* Resolve symlinks to allow 'mysql_home' to be a relative symlink */
  my_realpath(mysql_home,mysql_home,MYF(0));
  /* Ensure that mysql_home ends in FN_LIBCHAR */
  pos=strend(mysql_home);
  if (pos[-1] != FN_LIBCHAR)
  {
    pos[0]= FN_LIBCHAR;
    pos[1]= 0;
  }
  convert_dirname(lc_messages_dir, lc_messages_dir, NullS);
  convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS);
  (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir
  (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home);
  (void) my_load_path(pidfile_name, pidfile_name_ptr, mysql_real_data_home);

  convert_dirname(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr :
                                  get_relative_path(PLUGINDIR), NullS);
  (void) my_load_path(opt_plugin_dir, opt_plugin_dir, mysql_home);
  opt_plugin_dir_ptr= opt_plugin_dir;

  my_realpath(mysql_unpacked_real_data_home, mysql_real_data_home, MYF(0));
  mysql_unpacked_real_data_home_len=
    (int) strlen(mysql_unpacked_real_data_home);
  if (mysql_unpacked_real_data_home[mysql_unpacked_real_data_home_len-1] == FN_LIBCHAR)
    --mysql_unpacked_real_data_home_len;

  char *sharedir=get_relative_path(SHAREDIR);
  if (test_if_hard_path(sharedir))
    strmake(buff,sharedir,sizeof(buff)-1);		/* purecov: tested */
  else
    strxnmov(buff,sizeof(buff)-1,mysql_home,sharedir,NullS);
  convert_dirname(buff,buff,NullS);
  (void) my_load_path(lc_messages_dir, lc_messages_dir, buff);

  /* If --character-sets-dir isn't given, use shared library dir */
  if (charsets_dir)
    strmake(mysql_charsets_dir, charsets_dir, sizeof(mysql_charsets_dir)-1);
  else
    strxnmov(mysql_charsets_dir, sizeof(mysql_charsets_dir)-1, buff,
	     CHARSET_DIR, NullS);
  (void) my_load_path(mysql_charsets_dir, mysql_charsets_dir, buff);
  convert_dirname(mysql_charsets_dir, mysql_charsets_dir, NullS);
  charsets_dir=mysql_charsets_dir;

  if (init_tmpdir(&mysql_tmpdir_list, opt_mysql_tmpdir))
    return 1;
  if (!opt_mysql_tmpdir)
    opt_mysql_tmpdir= mysql_tmpdir;
#ifdef HAVE_REPLICATION
  if (!slave_load_tmpdir)
    slave_load_tmpdir= mysql_tmpdir;
#endif /* HAVE_REPLICATION */
  /*
    Convert the secure-file-priv option to system format, allowing
    a quick strcmp to check if read or write is in an allowed dir
   */
  if (opt_bootstrap)
    opt_secure_file_priv= EMPTY_STR.str;
  secure_file_priv_nonempty= opt_secure_file_priv[0] ? true : false;

  if (secure_file_priv_nonempty && strlen(opt_secure_file_priv) > FN_REFLEN)
  {
    sql_print_warning("Value for --secure-file-priv is longer than maximum "
                      "limit of %d", FN_REFLEN-1);
    return 1;
  }

  memset(buff, 0, sizeof(buff));
  if (secure_file_priv_nonempty &&
      my_strcasecmp(system_charset_info, opt_secure_file_priv, "NULL"))
  {
    int retval= my_realpath(buff, opt_secure_file_priv, MYF(MY_WME));
    if (!retval)
    {
      convert_dirname(secure_file_real_path, buff, NullS);
#ifdef WIN32
      MY_DIR *dir= my_dir(secure_file_real_path, MYF(MY_DONT_SORT+MY_WME));
      if (!dir)
      {
        retval= 1;
      }
      else
      {
        my_dirend(dir);
      }
#endif
    }

    if (retval)
    {
      char err_buffer[FN_REFLEN];
      my_snprintf(err_buffer, FN_REFLEN-1,
                  "Failed to access directory for --secure-file-priv."
                  " Please make sure that directory exists and is "
                  "accessible by MySQL Server. Supplied value : %s",
                  opt_secure_file_priv);
      err_buffer[FN_REFLEN-1]='\0';
      sql_print_error("%s", err_buffer);
      return 1;
    }
    opt_secure_file_priv= secure_file_real_path;
  }

  if (!check_secure_file_priv_path())
    return 1;

  return 0;
}


static void arcmsr_iop2drv_data_wrote_handle(struct AdapterControlBlock *acb)
{
	unsigned long flags;
	struct QBUFFER __iomem  *prbuffer;
	int32_t buf_empty_len;

	spin_lock_irqsave(&acb->rqbuffer_lock, flags);
	prbuffer = arcmsr_get_iop_rqbuffer(acb);
	buf_empty_len = (acb->rqbuf_putIndex - acb->rqbuf_getIndex - 1) &
		(ARCMSR_MAX_QBUFFER - 1);
	if (buf_empty_len >= readl(&prbuffer->data_len)) {
		if (arcmsr_Read_iop_rqbuffer_data(acb, prbuffer) == 0)
			acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
	} else
		acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
	spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
}


static const char *arcmsr_info(struct Scsi_Host *host)
{
	struct AdapterControlBlock *acb =
		(struct AdapterControlBlock *) host->hostdata;
	static char buf[256];
	char *type;
	int raid6 = 1;
	switch (acb->pdev->device) {
	case PCI_DEVICE_ID_ARECA_1110:
	case PCI_DEVICE_ID_ARECA_1200:
	case PCI_DEVICE_ID_ARECA_1202:
	case PCI_DEVICE_ID_ARECA_1210:
		raid6 = 0;
		/*FALLTHRU*/
	case PCI_DEVICE_ID_ARECA_1120:
	case PCI_DEVICE_ID_ARECA_1130:
	case PCI_DEVICE_ID_ARECA_1160:
	case PCI_DEVICE_ID_ARECA_1170:
	case PCI_DEVICE_ID_ARECA_1201:
	case PCI_DEVICE_ID_ARECA_1203:
	case PCI_DEVICE_ID_ARECA_1220:
	case PCI_DEVICE_ID_ARECA_1230:
	case PCI_DEVICE_ID_ARECA_1260:
	case PCI_DEVICE_ID_ARECA_1270:
	case PCI_DEVICE_ID_ARECA_1280:
		type = "SATA";
		break;
	case PCI_DEVICE_ID_ARECA_1214:
	case PCI_DEVICE_ID_ARECA_1380:
	case PCI_DEVICE_ID_ARECA_1381:
	case PCI_DEVICE_ID_ARECA_1680:
	case PCI_DEVICE_ID_ARECA_1681:
	case PCI_DEVICE_ID_ARECA_1880:
		type = "SAS/SATA";
		break;
	default:
		type = "unknown";
		raid6 =	0;
		break;
	}
	sprintf(buf, "Areca %s RAID Controller %s\narcmsr version %s\n",
		type, raid6 ? "(RAID6 capable)" : "", ARCMSR_DRIVER_VERSION);
	return buf;
}


static void arcmsr_hbaA_start_bgrb(struct AdapterControlBlock *acb)
{
	struct MessageUnit_A __iomem *reg = acb->pmuA;
	acb->acb_flags |= ACB_F_MSG_START_BGRB;
	writel(ARCMSR_INBOUND_MESG0_START_BGRB, &reg->inbound_msgaddr0);
	if (!arcmsr_hbaA_wait_msgint_ready(acb)) {
		printk(KERN_NOTICE "arcmsr%d: wait 'start adapter background \
				rebulid' timeout \n", acb->host->host_no);
	}
}


static bool arcmsr_hbaB_get_config(struct AdapterControlBlock *acb)
{
	struct MessageUnit_B *reg = acb->pmuB;
	char *acb_firm_model = acb->firm_model;
	char *acb_firm_version = acb->firm_version;
	char *acb_device_map = acb->device_map;
	char __iomem *iop_firm_model;
	/*firm_model,15,60-67*/
	char __iomem *iop_firm_version;
	/*firm_version,17,68-83*/
	char __iomem *iop_device_map;
	/*firm_version,21,84-99*/
	int count;

	iop_firm_model = (char __iomem *)(&reg->message_rwbuffer[15]);	/*firm_model,15,60-67*/
	iop_firm_version = (char __iomem *)(&reg->message_rwbuffer[17]);	/*firm_version,17,68-83*/
	iop_device_map = (char __iomem *)(&reg->message_rwbuffer[21]);	/*firm_version,21,84-99*/

	arcmsr_wait_firmware_ready(acb);
	writel(ARCMSR_MESSAGE_START_DRIVER_MODE, reg->drv2iop_doorbell);
	if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
		printk(KERN_ERR "arcmsr%d: can't set driver mode.\n", acb->host->host_no);
		return false;
	}
	writel(ARCMSR_MESSAGE_GET_CONFIG, reg->drv2iop_doorbell);
	if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
		printk(KERN_NOTICE "arcmsr%d: wait 'get adapter firmware \
			miscellaneous data' timeout \n", acb->host->host_no);
		return false;
	}
	count = 8;
	while (count){
		*acb_firm_model = readb(iop_firm_model);
		acb_firm_model++;
		iop_firm_model++;
		count--;
	}
	count = 16;
	while (count){
		*acb_firm_version = readb(iop_firm_version);
		acb_firm_version++;
		iop_firm_version++;
		count--;
	}

	count = 16;
	while(count){
		*acb_device_map = readb(iop_device_map);
		acb_device_map++;
		iop_device_map++;
		count--;
	}

	pr_notice("Areca RAID Controller%d: Model %s, F/W %s\n",
		acb->host->host_no,
		acb->firm_model,
		acb->firm_version);

	acb->signature = readl(&reg->message_rwbuffer[0]);
	/*firm_signature,1,00-03*/
	acb->firm_request_len = readl(&reg->message_rwbuffer[1]);
	/*firm_request_len,1,04-07*/
	acb->firm_numbers_queue = readl(&reg->message_rwbuffer[2]);
	/*firm_numbers_queue,2,08-11*/
	acb->firm_sdram_size = readl(&reg->message_rwbuffer[3]);
	/*firm_sdram_size,3,12-15*/
	acb->firm_hd_channels = readl(&reg->message_rwbuffer[4]);
	/*firm_ide_channels,4,16-19*/
	acb->firm_cfg_version = readl(&reg->message_rwbuffer[25]);  /*firm_cfg_version,25,100-103*/
	/*firm_ide_channels,4,16-19*/
	return true;
}


static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb)
{
	struct MessageUnit_C __iomem *phbcmu;
	struct ARCMSR_CDB *arcmsr_cdb;
	struct CommandControlBlock *ccb;
	uint32_t flag_ccb, ccb_cdb_phy, throttling = 0;
	int error;

	phbcmu = acb->pmuC;
	/* areca cdb command done */
	/* Use correct offset and size for syncing */

	while ((flag_ccb = readl(&phbcmu->outbound_queueport_low)) !=
			0xFFFFFFFF) {
		ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
		arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset
			+ ccb_cdb_phy);
		ccb = container_of(arcmsr_cdb, struct CommandControlBlock,
			arcmsr_cdb);
		error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)
			? true : false;
		/* check if command done with no error */
		arcmsr_drain_donequeue(acb, ccb, error);
		throttling++;
		if (throttling == ARCMSR_HBC_ISR_THROTTLING_LEVEL) {
			writel(ARCMSR_HBCMU_DRV2IOP_POSTQUEUE_THROTTLING,
				&phbcmu->inbound_doorbell);
			throttling = 0;
		}
	}
}


static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb)
{
	switch (acb->adapter_type) {
	case ACB_ADAPTER_TYPE_A: {
		arcmsr_hbaA_stop_bgrb(acb);
		}
		break;

	case ACB_ADAPTER_TYPE_B: {
		arcmsr_hbaB_stop_bgrb(acb);
		}
		break;
	case ACB_ADAPTER_TYPE_C: {
		arcmsr_hbaC_stop_bgrb(acb);
		}
		break;
	case ACB_ADAPTER_TYPE_D:
		arcmsr_hbaD_stop_bgrb(acb);
		break;
	}
}


static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
{
	int i = 0;
	uint32_t flag_ccb, ccb_cdb_phy;
	struct ARCMSR_CDB *pARCMSR_CDB;
	bool error;
	struct CommandControlBlock *pCCB;
	switch (acb->adapter_type) {

	case ACB_ADAPTER_TYPE_A: {
		struct MessageUnit_A __iomem *reg = acb->pmuA;
		uint32_t outbound_intstatus;
		outbound_intstatus = readl(&reg->outbound_intstatus) &
					acb->outbound_int_enable;
		/*clear and abort all outbound posted Q*/
		writel(outbound_intstatus, &reg->outbound_intstatus);/*clear interrupt*/
		while(((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF)
				&& (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {
			pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/
			pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
			error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
			arcmsr_drain_donequeue(acb, pCCB, error);
		}
		}
		break;

	case ACB_ADAPTER_TYPE_B: {
		struct MessageUnit_B *reg = acb->pmuB;
		/*clear all outbound posted Q*/
		writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell); /* clear doorbell interrupt */
		for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) {
			flag_ccb = reg->done_qbuffer[i];
			if (flag_ccb != 0) {
				reg->done_qbuffer[i] = 0;
				pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/
				pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
				error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
				arcmsr_drain_donequeue(acb, pCCB, error);
			}
			reg->post_qbuffer[i] = 0;
		}
		reg->doneq_index = 0;
		reg->postq_index = 0;
		}
		break;
	case ACB_ADAPTER_TYPE_C: {
		struct MessageUnit_C __iomem *reg = acb->pmuC;
		while ((readl(&reg->host_int_status) & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) && (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {
			/*need to do*/
			flag_ccb = readl(&reg->outbound_queueport_low);
			ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
			pARCMSR_CDB = (struct  ARCMSR_CDB *)(acb->vir2phy_offset+ccb_cdb_phy);/*frame must be 32 bytes aligned*/
			pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
			error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
			arcmsr_drain_donequeue(acb, pCCB, error);
		}
		}
		break;
	case ACB_ADAPTER_TYPE_D: {
		struct MessageUnit_D  *pmu = acb->pmuD;
		uint32_t outbound_write_pointer;
		uint32_t doneq_index, index_stripped, addressLow, residual, toggle;
		unsigned long flags;

		residual = atomic_read(&acb->ccboutstandingcount);
		for (i = 0; i < residual; i++) {
			spin_lock_irqsave(&acb->doneq_lock, flags);
			outbound_write_pointer =
				pmu->done_qbuffer[0].addressLow + 1;
			doneq_index = pmu->doneq_index;
			if ((doneq_index & 0xFFF) !=
				(outbound_write_pointer & 0xFFF)) {
				toggle = doneq_index & 0x4000;
				index_stripped = (doneq_index & 0xFFF) + 1;
				index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
				pmu->doneq_index = index_stripped ? (index_stripped | toggle) :
					((toggle ^ 0x4000) + 1);
				doneq_index = pmu->doneq_index;
				spin_unlock_irqrestore(&acb->doneq_lock, flags);
				addressLow = pmu->done_qbuffer[doneq_index &
					0xFFF].addressLow;
				ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
				pARCMSR_CDB = (struct  ARCMSR_CDB *)
					(acb->vir2phy_offset + ccb_cdb_phy);
				pCCB = container_of(pARCMSR_CDB,
					struct CommandControlBlock, arcmsr_cdb);
				error = (addressLow &
					ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ?
					true : false;
				arcmsr_drain_donequeue(acb, pCCB, error);
				writel(doneq_index,
					pmu->outboundlist_read_pointer);
			} else {
				spin_unlock_irqrestore(&acb->doneq_lock, flags);
				mdelay(10);
			}
		}
		pmu->postq_index = 0;
		pmu->doneq_index = 0x40FF;
		}
		break;
	}
}


static void arcmsr_ccb_complete(struct CommandControlBlock *ccb)
{
	struct AdapterControlBlock *acb = ccb->acb;
	struct scsi_cmnd *pcmd = ccb->pcmd;
	unsigned long flags;
	atomic_dec(&acb->ccboutstandingcount);
	arcmsr_pci_unmap_dma(ccb);
	ccb->startdone = ARCMSR_CCB_DONE;
	spin_lock_irqsave(&acb->ccblist_lock, flags);
	list_add_tail(&ccb->list, &acb->ccb_free_list);
	spin_unlock_irqrestore(&acb->ccblist_lock, flags);
	pcmd->scsi_done(pcmd);
}


static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb)
{
	u32 orig_mask = 0;
	switch (acb->adapter_type) {
	case ACB_ADAPTER_TYPE_A : {
		struct MessageUnit_A __iomem *reg = acb->pmuA;
		orig_mask = readl(&reg->outbound_intmask);
		writel(orig_mask|ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE, \
						&reg->outbound_intmask);
		}
		break;
	case ACB_ADAPTER_TYPE_B : {
		struct MessageUnit_B *reg = acb->pmuB;
		orig_mask = readl(reg->iop2drv_doorbell_mask);
		writel(0, reg->iop2drv_doorbell_mask);
		}
		break;
	case ACB_ADAPTER_TYPE_C:{
		struct MessageUnit_C __iomem *reg = acb->pmuC;
		/* disable all outbound interrupt */
		orig_mask = readl(&reg->host_int_mask); /* disable outbound message0 int */
		writel(orig_mask|ARCMSR_HBCMU_ALL_INTMASKENABLE, &reg->host_int_mask);
		}
		break;
	case ACB_ADAPTER_TYPE_D: {
		struct MessageUnit_D *reg = acb->pmuD;
		/* disable all outbound interrupt */
		writel(ARCMSR_ARC1214_ALL_INT_DISABLE, reg->pcief0_int_enable);
		}
		break;
	}
	return orig_mask;
}


static void arcmsr_hbaB_postqueue_isr(struct AdapterControlBlock *acb)
{
	uint32_t index;
	uint32_t flag_ccb;
	struct MessageUnit_B *reg = acb->pmuB;
	struct ARCMSR_CDB *pARCMSR_CDB;
	struct CommandControlBlock *pCCB;
	bool error;
	index = reg->doneq_index;
	while ((flag_ccb = reg->done_qbuffer[index]) != 0) {
		reg->done_qbuffer[index] = 0;
		pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/
		pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
		error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
		arcmsr_drain_donequeue(acb, pCCB, error);
		index++;
		index %= ARCMSR_MAX_HBB_POSTQUEUE;
		reg->doneq_index = index;
	}
}


static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct Scsi_Host *host;
	struct AdapterControlBlock *acb;
	uint8_t bus,dev_fun;
	int error;
	error = pci_enable_device(pdev);
	if(error){
		return -ENODEV;
	}
	host = scsi_host_alloc(&arcmsr_scsi_host_template, sizeof(struct AdapterControlBlock));
	if(!host){
    		goto pci_disable_dev;
	}
	error = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
	if(error){
		error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
		if(error){
			printk(KERN_WARNING
			       "scsi%d: No suitable DMA mask available\n",
			       host->host_no);
			goto scsi_host_release;
		}
	}
	init_waitqueue_head(&wait_q);
	bus = pdev->bus->number;
	dev_fun = pdev->devfn;
	acb = (struct AdapterControlBlock *) host->hostdata;
	memset(acb,0,sizeof(struct AdapterControlBlock));
	acb->pdev = pdev;
	acb->host = host;
	host->max_lun = ARCMSR_MAX_TARGETLUN;
	host->max_id = ARCMSR_MAX_TARGETID;		/*16:8*/
	host->max_cmd_len = 16;	 			/*this is issue of 64bit LBA ,over 2T byte*/
	host->can_queue = ARCMSR_MAX_OUTSTANDING_CMD;
	host->cmd_per_lun = ARCMSR_MAX_CMD_PERLUN;
	host->this_id = ARCMSR_SCSI_INITIATOR_ID;
	host->unique_id = (bus << 8) | dev_fun;
	pci_set_drvdata(pdev, host);
	pci_set_master(pdev);
	error = pci_request_regions(pdev, "arcmsr");
	if(error){
		goto scsi_host_release;
	}
	spin_lock_init(&acb->eh_lock);
	spin_lock_init(&acb->ccblist_lock);
	spin_lock_init(&acb->postq_lock);
	spin_lock_init(&acb->doneq_lock);
	spin_lock_init(&acb->rqbuffer_lock);
	spin_lock_init(&acb->wqbuffer_lock);
	acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
			ACB_F_MESSAGE_RQBUFFER_CLEARED |
			ACB_F_MESSAGE_WQBUFFER_READED);
	acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER;
	INIT_LIST_HEAD(&acb->ccb_free_list);
	acb->adapter_type = id->driver_data;
	error = arcmsr_remap_pciregion(acb);
	if(!error){
		goto pci_release_regs;
	}
	error = arcmsr_alloc_io_queue(acb);
	if (!error)
		goto unmap_pci_region;
	error = arcmsr_get_firmware_spec(acb);
	if(!error){
		goto free_hbb_mu;
	}
	error = arcmsr_alloc_ccb_pool(acb);
	if(error){
		goto free_hbb_mu;
	}
	error = scsi_add_host(host, &pdev->dev);
	if(error){
		goto free_ccb_pool;
	}
	if (arcmsr_request_irq(pdev, acb) == FAILED)
		goto scsi_host_remove;
	arcmsr_iop_init(acb);
	INIT_WORK(&acb->arcmsr_do_message_isr_bh, arcmsr_message_isr_bh_fn);
	atomic_set(&acb->rq_map_token, 16);
	atomic_set(&acb->ante_token_value, 16);
	acb->fw_flag = FW_NORMAL;
	init_timer(&acb->eternal_timer);
	acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6 * HZ);
	acb->eternal_timer.data = (unsigned long) acb;
	acb->eternal_timer.function = &arcmsr_request_device_map;
	add_timer(&acb->eternal_timer);
	if(arcmsr_alloc_sysfs_attr(acb))
		goto out_free_sysfs;
	scsi_scan_host(host);
	return 0;
out_free_sysfs:
	del_timer_sync(&acb->eternal_timer);
	flush_work(&acb->arcmsr_do_message_isr_bh);
	arcmsr_stop_adapter_bgrb(acb);
	arcmsr_flush_adapter_cache(acb);
	arcmsr_free_irq(pdev, acb);
scsi_host_remove:
	scsi_remove_host(host);
free_ccb_pool:
	arcmsr_free_ccb_pool(acb);
free_hbb_mu:
	arcmsr_free_mu(acb);
unmap_pci_region:
	arcmsr_unmap_pciregion(acb);
pci_release_regs:
	pci_release_regions(pdev);
scsi_host_release:
	scsi_host_put(host);
pci_disable_dev:
	pci_disable_device(pdev);
	return -ENODEV;
}


static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb,
		struct scsi_cmnd *cmd)
{
	switch (cmd->cmnd[0]) {
	case INQUIRY: {
		unsigned char inqdata[36];
		char *buffer;
		struct scatterlist *sg;

		if (cmd->device->lun) {
			cmd->result = (DID_TIME_OUT << 16);
			cmd->scsi_done(cmd);
			return;
		}
		inqdata[0] = TYPE_PROCESSOR;
		/* Periph Qualifier & Periph Dev Type */
		inqdata[1] = 0;
		/* rem media bit & Dev Type Modifier */
		inqdata[2] = 0;
		/* ISO, ECMA, & ANSI versions */
		inqdata[4] = 31;
		/* length of additional data */
		strncpy(&inqdata[8], "Areca   ", 8);
		/* Vendor Identification */
		strncpy(&inqdata[16], "RAID controller ", 16);
		/* Product Identification */
		strncpy(&inqdata[32], "R001", 4); /* Product Revision */

		sg = scsi_sglist(cmd);
		buffer = kmap_atomic(sg_page(sg)) + sg->offset;

		memcpy(buffer, inqdata, sizeof(inqdata));
		sg = scsi_sglist(cmd);
		kunmap_atomic(buffer - sg->offset);

		cmd->scsi_done(cmd);
	}
	break;
	case WRITE_BUFFER:
	case READ_BUFFER: {
		if (arcmsr_iop_message_xfer(acb, cmd))
			cmd->result = (DID_ERROR << 16);
		cmd->scsi_done(cmd);
	}
	break;
	default:
		cmd->scsi_done(cmd);
	}
}


static uint8_t arcmsr_iop_reset(struct AdapterControlBlock *acb)
{
	struct CommandControlBlock *ccb;
	uint32_t intmask_org;
	uint8_t rtnval = 0x00;
	int i = 0;
	unsigned long flags;

	if (atomic_read(&acb->ccboutstandingcount) != 0) {
		/* disable all outbound interrupt */
		intmask_org = arcmsr_disable_outbound_ints(acb);
		/* talk to iop 331 outstanding command aborted */
		rtnval = arcmsr_abort_allcmd(acb);
		/* clear all outbound posted Q */
		arcmsr_done4abort_postqueue(acb);
		for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
			ccb = acb->pccb_pool[i];
			if (ccb->startdone == ARCMSR_CCB_START) {
				scsi_dma_unmap(ccb->pcmd);
				ccb->startdone = ARCMSR_CCB_DONE;
				ccb->ccb_flags = 0;
				spin_lock_irqsave(&acb->ccblist_lock, flags);
				list_add_tail(&ccb->list, &acb->ccb_free_list);
				spin_unlock_irqrestore(&acb->ccblist_lock, flags);
			}
		}
		atomic_set(&acb->ccboutstandingcount, 0);
		/* enable all outbound interrupt */
		arcmsr_enable_outbound_ints(acb, intmask_org);
		return rtnval;
	}
	return rtnval;
}


static void arcmsr_hbaA_message_isr(struct AdapterControlBlock *acb)
{
	struct MessageUnit_A __iomem *reg  = acb->pmuA;
	/*clear interrupt and message state*/
	writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT, &reg->outbound_intstatus);
	schedule_work(&acb->arcmsr_do_message_isr_bh);
}


static void arcmsr_unmap_pciregion(struct AdapterControlBlock *acb)
{
	switch (acb->adapter_type) {
	case ACB_ADAPTER_TYPE_A:{
		iounmap(acb->pmuA);
	}
	break;
	case ACB_ADAPTER_TYPE_B:{
		iounmap(acb->mem_base0);
		iounmap(acb->mem_base1);
	}

	break;
	case ACB_ADAPTER_TYPE_C:{
		iounmap(acb->pmuC);
	}
	break;
	case ACB_ADAPTER_TYPE_D:
		iounmap(acb->mem_base0);
		break;
	}
}


static inline void SetPixelGreen(const Image *restrict image,
  const Quantum green,Quantum *restrict pixel)
{
  pixel[image->channel_map[GreenPixelChannel].offset]=green;
}


static inline PixelTrait GetPixelIndexTraits(const Image *restrict image)
{
  return(image->channel_map[IndexPixelChannel].traits);
}


MagickExport MagickBooleanType PerceptibleImage(Image *image,
  const double epsilon,ExceptionInfo *exception)
{
#define PerceptibleImageTag  "Perceptible/Image"

  CacheView
    *image_view;

  MagickBooleanType
    status;

  MagickOffsetType
    progress;

  ssize_t
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  if (image->storage_class == PseudoClass)
    {
      register ssize_t
        i;

      register PixelInfo
        *restrict q;

      q=image->colormap;
      for (i=0; i < (ssize_t) image->colors; i++)
      {
        q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
          epsilon);
        q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
          epsilon);
        q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
          epsilon);
        q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
          epsilon);
        q++;
      }
      return(SyncImage(image,exception));
    }
  /*
    Perceptible image.
  */
  status=MagickTrue;
  progress=0;
  image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static,4) shared(progress,status) \
    magick_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register ssize_t
      x;

    register Quantum
      *restrict q;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      register ssize_t
        i;

      if (GetPixelReadMask(image,q) == 0)
        {
          q+=GetPixelChannels(image);
          continue;
        }
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
      {
        PixelChannel channel=GetPixelChannelChannel(image,i);
        PixelTrait traits=GetPixelChannelTraits(image,channel);
        if (traits == UndefinedPixelTrait)
          continue;
        q[i]=PerceptibleThreshold(q[i],epsilon);
      }
      q+=GetPixelChannels(image);
    }
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
      status=MagickFalse;
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
      {
        MagickBooleanType
          proceed;

#if defined(MAGICKCORE_OPENMP_SUPPORT)
        #pragma omp critical (MagickCore_PerceptibleImage)
#endif
        proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
        if (proceed == MagickFalse)
          status=MagickFalse;
      }
  }
  image_view=DestroyCacheView(image_view);
  return(status);
}


static inline PixelTrait GetPixelCyanTraits(const Image *restrict image)
{
  return(image->channel_map[CyanPixelChannel].traits);
}


static inline void GetPixelInfoPixel(const Image *restrict image,
  const Quantum *restrict pixel,PixelInfo *restrict pixel_info)
{
  pixel_info->colorspace=image->colorspace;
  pixel_info->fuzz=image->fuzz;
  pixel_info->red=(MagickRealType)
    pixel[image->channel_map[RedPixelChannel].offset];
  pixel_info->green=(MagickRealType)
    pixel[image->channel_map[GreenPixelChannel].offset];
  pixel_info->blue=(MagickRealType)
    pixel[image->channel_map[BluePixelChannel].offset];
  pixel_info->black=0.0f;
  if (image->channel_map[BlackPixelChannel].traits != UndefinedPixelTrait)
    pixel_info->black=(MagickRealType)
      pixel[image->channel_map[BlackPixelChannel].offset];
  pixel_info->alpha=(MagickRealType) OpaqueAlpha;
  pixel_info->alpha_trait=UndefinedPixelTrait;
  if (image->channel_map[AlphaPixelChannel].traits != UndefinedPixelTrait)
    {
      pixel_info->alpha=(MagickRealType)
        pixel[image->channel_map[AlphaPixelChannel].offset];
      pixel_info->alpha_trait=BlendPixelTrait;
    }
  pixel_info->index=0.0f;
  if (image->channel_map[IndexPixelChannel].traits != UndefinedPixelTrait)
    pixel_info->index=(MagickRealType)
      pixel[image->channel_map[IndexPixelChannel].offset];
}


MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
  const char *threshold_map,ExceptionInfo *exception)
{
#define DitherImageTag  "Dither/Image"

  CacheView
    *image_view;

  char
    token[MaxTextExtent];

  const char
    *p;

  double
    levels[CompositePixelChannel];

  MagickBooleanType
    status;

  MagickOffsetType
    progress;

  register ssize_t
    i;

  ssize_t
    y;

  ThresholdMap
    *map;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickSignature);
  if (threshold_map == (const char *) NULL)
    return(MagickTrue);
  p=(char *) threshold_map;
  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
         (*p != '\0'))
    p++;
  threshold_map=p;
  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
         (*p != '\0'))
  {
    if ((p-threshold_map) >= (MaxTextExtent-1))
      break;
    token[p-threshold_map]=(*p);
    p++;
  }
  token[p-threshold_map]='\0';
  map=GetThresholdMap(token,exception);
  if (map == (ThresholdMap *) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
        "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
      return(MagickFalse);
    }
  for (i=0; i < MaxPixelChannels; i++)
    levels[i]=2.0;
  p=strchr((char *) threshold_map,',');
  if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
    for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
    {
      GetMagickToken(p,&p,token);
      if (*token == ',')
        GetMagickToken(p,&p,token);
      levels[i]=StringToDouble(token,(char **) NULL);
    }
  for (i=0; i < MaxPixelChannels; i++)
    if (fabs(levels[i]) >= 1)
      levels[i]-=1.0;
  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
    return(MagickFalse);
  status=MagickTrue;
  progress=0;
  image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static,4) shared(progress,status) \
    magick_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register ssize_t
      x;

    register Quantum
      *restrict q;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      register ssize_t
        i;

      ssize_t
        n;

      n=0;
      if (GetPixelReadMask(image,q) == 0)
        {
          q+=GetPixelChannels(image);
          continue;
        }
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
      {
        ssize_t
          level,
          threshold;

        PixelChannel channel=GetPixelChannelChannel(image,i);
        PixelTrait traits=GetPixelChannelTraits(image,channel);
        if ((traits & UpdatePixelTrait) == 0)
          continue;
        if (fabs(levels[n++]) < MagickEpsilon)
          continue;
        threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
        level=threshold/(map->divisor-1);
        threshold-=level*(map->divisor-1);
        q[i]=ClampToQuantum((double) (level+(threshold >=
          map->levels[(x % map->width)+map->width*(y % map->height)]))*
          QuantumRange/levels[n]);
        n++;
      }
      q+=GetPixelChannels(image);
    }
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
      status=MagickFalse;
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
      {
        MagickBooleanType
          proceed;

#if defined(MAGICKCORE_OPENMP_SUPPORT)
        #pragma omp critical (MagickCore_OrderedPosterizeImage)
#endif
        proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
        if (proceed == MagickFalse)
          status=MagickFalse;
      }
  }
  image_view=DestroyCacheView(image_view);
  map=DestroyThresholdMap(map);
  return(MagickTrue);
}


static inline MagickBooleanType IsPixelInfoMonochrome(
  const PixelInfo *restrict pixel_info)
{
  if ((pixel_info->colorspace != GRAYColorspace) &&
      (pixel_info->colorspace != RGBColorspace))
    return(MagickFalse);
  if ((AbsolutePixelValue(pixel_info->red) >= MagickEpsilon) ||
      (AbsolutePixelValue(pixel_info->red-QuantumRange) >= MagickEpsilon))
    return(MagickFalse);
  if ((AbsolutePixelValue(pixel_info->red-pixel_info->green) < MagickEpsilon) &&
      (AbsolutePixelValue(pixel_info->green-pixel_info->blue) < MagickEpsilon))
    return(MagickTrue);
  return(MagickFalse);
}


static inline MagickRealType GetPixelInfoLuma(const PixelInfo *restrict pixel)
{
  MagickRealType
    blue,
    green,
    red;

  if (pixel->colorspace == GRAYColorspace)
    return(pixel->red);
  if (pixel->colorspace == sRGBColorspace)
    return(0.212656f*pixel->red+0.715158f*pixel->green+0.072186f*pixel->blue);
  red=EncodePixelGamma(pixel->red);
  green=EncodePixelGamma(pixel->green);
  blue=EncodePixelGamma(pixel->blue);
  return(0.212656f*red+0.715158f*green+0.072186f*blue);
}


static inline MagickRealType GetPixelInfoLuminance(
  const PixelInfo *restrict pixel)
{
  MagickRealType
    blue,
    green,
    red;

  if (pixel->colorspace == GRAYColorspace)
    return(pixel->red);
  if (pixel->colorspace != sRGBColorspace)
    return(0.212656f*pixel->red+0.715158f*pixel->green+0.072186f*pixel->blue);
  red=DecodePixelGamma(pixel->red);
  green=DecodePixelGamma(pixel->green);
  blue=DecodePixelGamma(pixel->blue);
  return(0.212656f*red+0.715158f*green+0.072186f*blue);
}


static inline MagickBooleanType IsPixelInfoGray(
  const PixelInfo *restrict pixel_info)
{
  if ((pixel_info->colorspace != GRAYColorspace) &&
      (pixel_info->colorspace != RGBColorspace))
    return(MagickFalse);
  if ((AbsolutePixelValue(pixel_info->red-pixel_info->green) < MagickEpsilon) &&
      (AbsolutePixelValue(pixel_info->green-pixel_info->blue) < MagickEpsilon))
    return(MagickTrue);
  return(MagickFalse);
}


static inline PixelTrait GetPixelGrayTraits(const Image *restrict image)
{
  return(image->channel_map[GrayPixelChannel].traits);
}


static MagickBooleanType ReadPSDChannelPixels(Image *image,
  const size_t channels,const size_t row,const ssize_t type,
  const unsigned char *pixels,ExceptionInfo *exception)
{
  Quantum
    pixel;

  register const unsigned char
    *p;

  register Quantum
    *q;

  register ssize_t
    x;

  size_t
    packet_size;

  unsigned short
    nibble;

  p=pixels;
  q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
  if (q == (Quantum *) NULL)
    return MagickFalse;
  packet_size=GetPSDPacketSize(image);
  for (x=0; x < (ssize_t) image->columns; x++)
  {
    if (packet_size == 1)
      pixel=ScaleCharToQuantum(*p++);
    else
      {
        p=PushShortPixel(MSBEndian,p,&nibble);
        pixel=ScaleShortToQuantum(nibble);
      }
    if (image->depth > 1)
      {
        SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
        q+=GetPixelChannels(image);
      }
    else
      {
        ssize_t
          bit,
          number_bits;

        number_bits=image->columns-x;
        if (number_bits > 8)
          number_bits=8;
        for (bit = 0; bit < number_bits; bit++)
        {
          SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
            & (0x01 << (7-bit))) != 0 ? 0 : 255,q,exception);
          q+=GetPixelChannels(image);
          x++;
        }
        if (x != image->columns)
          x--;
        continue;
      }
  }
  return(SyncAuthenticPixels(image,exception));
}


ModuleExport void UnregisterRLEImage(void)
{
  (void) UnregisterMagickInfo("RLE");
}


static Image *ReadPICTImage(const ImageInfo *image_info,
  ExceptionInfo *exception)
{
  char
    geometry[MagickPathExtent],
    header_ole[4];

  Image
    *image;

  int
    c,
    code;

  MagickBooleanType
    jpeg,
    status;

  PICTRectangle
    frame;

  PICTPixmap
    pixmap;

  Quantum
    index;

  register Quantum
    *q;

  register ssize_t
    i,
    x;

  size_t
    extent,
    length;

  ssize_t
    count,
    flags,
    j,
    version,
    y;

  StringInfo
    *profile;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Read PICT header.
  */
  pixmap.bits_per_pixel=0;
  pixmap.component_count=0;
  /*
    Skip header : 512 for standard PICT and 4, ie "PICT" for OLE2.
  */
  header_ole[0]=ReadBlobByte(image);
  header_ole[1]=ReadBlobByte(image);
  header_ole[2]=ReadBlobByte(image);
  header_ole[3]=ReadBlobByte(image);
  if (!((header_ole[0] == 0x50) && (header_ole[1] == 0x49) &&
      (header_ole[2] == 0x43) && (header_ole[3] == 0x54 )))
    for (i=0; i < 508; i++)
      if (ReadBlobByte(image) == EOF)
        break;
  (void) ReadBlobMSBShort(image);  /* skip picture size */
  if (ReadRectangle(image,&frame) == MagickFalse)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  while ((c=ReadBlobByte(image)) == 0) ;
  if (c != 0x11)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  version=ReadBlobByte(image);
  if (version == 2)
    {
      c=ReadBlobByte(image);
      if (c != 0xff)
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
  else
    if (version != 1)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  if ((frame.left < 0) || (frame.right < 0) || (frame.top < 0) ||
      (frame.bottom < 0) || (frame.left >= frame.right) ||
      (frame.top >= frame.bottom))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  /*
    Create black canvas.
  */
  flags=0;
  image->depth=8;
  image->columns=1UL*(frame.right-frame.left);
  image->rows=1UL*(frame.bottom-frame.top);
  image->resolution.x=DefaultResolution;
  image->resolution.y=DefaultResolution;
  image->units=UndefinedResolution;
  if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
    if (image->scene >= (image_info->scene+image_info->number_scenes-1))
      {
        (void) CloseBlob(image);
        return(GetFirstImageInList(image));
      }
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  /*
    Interpret PICT opcodes.
  */
  jpeg=MagickFalse;
  for (code=0; EOFBlob(image) == MagickFalse; )
  {
    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    if ((version == 1) || ((TellBlob(image) % 2) != 0))
      code=ReadBlobByte(image);
    if (version == 2)
      code=(int) ReadBlobMSBShort(image);
    if (code < 0)
      break;
    if (code > 0xa1)
      {
        if (image->debug != MagickFalse)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%04X:",code);
      }
    else
      {
        if (image->debug != MagickFalse)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "  %04X %s: %s",code,codes[code].name,codes[code].description);
        switch (code)
        {
          case 0x01:
          {
            /*
              Clipping rectangle.
            */
            length=ReadBlobMSBShort(image);
            if (length != 0x000a)
              {
                for (i=0; i < (ssize_t) (length-2); i++)
                  if (ReadBlobByte(image) == EOF)
                    break;
                break;
              }
            if (ReadRectangle(image,&frame) == MagickFalse)
              ThrowReaderException(CorruptImageError,"ImproperImageHeader");
            if (((frame.left & 0x8000) != 0) || ((frame.top & 0x8000) != 0))
              break;
            image->columns=1UL*(frame.right-frame.left);
            image->rows=1UL*(frame.bottom-frame.top);
            status=SetImageExtent(image,image->columns,image->rows,exception);
            if (status == MagickFalse)
              return(DestroyImageList(image));
            (void) SetImageBackgroundColor(image,exception);
            break;
          }
          case 0x12:
          case 0x13:
          case 0x14:
          {
            ssize_t
              pattern;

            size_t
              height,
              width;

            /*
              Skip pattern definition.
            */
            pattern=1L*ReadBlobMSBShort(image);
            for (i=0; i < 8; i++)
              if (ReadBlobByte(image) == EOF)
                break;
            if (pattern == 2)
              {
                for (i=0; i < 5; i++)
                  if (ReadBlobByte(image) == EOF)
                    break;
                break;
              }
            if (pattern != 1)
              ThrowReaderException(CorruptImageError,"UnknownPatternType");
            length=ReadBlobMSBShort(image);
            if (ReadRectangle(image,&frame) == MagickFalse)
              ThrowReaderException(CorruptImageError,"ImproperImageHeader");
            ReadPixmap(pixmap);
            image->depth=1UL*pixmap.component_size;
            image->resolution.x=1.0*pixmap.horizontal_resolution;
            image->resolution.y=1.0*pixmap.vertical_resolution;
            image->units=PixelsPerInchResolution;
            (void) ReadBlobMSBLong(image);
            flags=1L*ReadBlobMSBShort(image);
            length=ReadBlobMSBShort(image);
            for (i=0; i <= (ssize_t) length; i++)
              (void) ReadBlobMSBLong(image);
            width=1UL*(frame.bottom-frame.top);
            height=1UL*(frame.right-frame.left);
            if (pixmap.bits_per_pixel <= 8)
              length&=0x7fff;
            if (pixmap.bits_per_pixel == 16)
              width<<=1;
            if (length == 0)
              length=width;
            if (length < 8)
              {
                for (i=0; i < (ssize_t) (length*height); i++)
                  if (ReadBlobByte(image) == EOF)
                    break;
              }
            else
              for (j=0; j < (int) height; j++)
                if (length > 200)
                  {
                    for (j=0; j < (ssize_t) ReadBlobMSBShort(image); j++)
                      if (ReadBlobByte(image) == EOF)
                        break;
                  }
                else
                  for (j=0; j < (ssize_t) ReadBlobByte(image); j++)
                    if (ReadBlobByte(image) == EOF)
                      break;
            break;
          }
          case 0x1b:
          {
            /*
              Initialize image background color.
            */
            image->background_color.red=(Quantum)
              ScaleShortToQuantum(ReadBlobMSBShort(image));
            image->background_color.green=(Quantum)
              ScaleShortToQuantum(ReadBlobMSBShort(image));
            image->background_color.blue=(Quantum)
              ScaleShortToQuantum(ReadBlobMSBShort(image));
            break;
          }
          case 0x70:
          case 0x71:
          case 0x72:
          case 0x73:
          case 0x74:
          case 0x75:
          case 0x76:
          case 0x77:
          {
            /*
              Skip polygon or region.
            */
            length=ReadBlobMSBShort(image);
            for (i=0; i < (ssize_t) (length-2); i++)
              if (ReadBlobByte(image) == EOF)
                break;
            break;
          }
          case 0x90:
          case 0x91:
          case 0x98:
          case 0x99:
          case 0x9a:
          case 0x9b:
          {
            Image
              *tile_image;

            PICTRectangle
              source,
              destination;

            register unsigned char
              *p;

            size_t
              j;

            ssize_t
              bytes_per_line;

            unsigned char
              *pixels;

            /*
              Pixmap clipped by a rectangle.
            */
            bytes_per_line=0;
            if ((code != 0x9a) && (code != 0x9b))
              bytes_per_line=1L*ReadBlobMSBShort(image);
            else
              {
                (void) ReadBlobMSBShort(image);
                (void) ReadBlobMSBShort(image);
                (void) ReadBlobMSBShort(image);
              }
            if (ReadRectangle(image,&frame) == MagickFalse)
              ThrowReaderException(CorruptImageError,"ImproperImageHeader");
            /*
              Initialize tile image.
            */
            tile_image=CloneImage(image,1UL*(frame.right-frame.left),
              1UL*(frame.bottom-frame.top),MagickTrue,exception);
            if (tile_image == (Image *) NULL)
              return((Image *) NULL);
            if ((code == 0x9a) || (code == 0x9b) ||
                ((bytes_per_line & 0x8000) != 0))
              {
                ReadPixmap(pixmap);
                tile_image->depth=1UL*pixmap.component_size;
                tile_image->alpha_trait=pixmap.component_count == 4 ?
                  BlendPixelTrait : UndefinedPixelTrait;
                tile_image->resolution.x=(double) pixmap.horizontal_resolution;
                tile_image->resolution.y=(double) pixmap.vertical_resolution;
                tile_image->units=PixelsPerInchResolution;
                if (tile_image->alpha_trait != UndefinedPixelTrait)
                  image->alpha_trait=tile_image->alpha_trait;
              }
            if ((code != 0x9a) && (code != 0x9b))
              {
                /*
                  Initialize colormap.
                */
                tile_image->colors=2;
                if ((bytes_per_line & 0x8000) != 0)
                  {
                    (void) ReadBlobMSBLong(image);
                    flags=1L*ReadBlobMSBShort(image);
                    tile_image->colors=1UL*ReadBlobMSBShort(image)+1;
                  }
                status=AcquireImageColormap(tile_image,tile_image->colors,
                  exception);
                if (status == MagickFalse)
                  {
                    tile_image=DestroyImage(tile_image);
                    ThrowReaderException(ResourceLimitError,
                      "MemoryAllocationFailed");
                  }
                if ((bytes_per_line & 0x8000) != 0)
                  {
                    for (i=0; i < (ssize_t) tile_image->colors; i++)
                    {
                      j=ReadBlobMSBShort(image) % tile_image->colors;
                      if ((flags & 0x8000) != 0)
                        j=(size_t) i;
                      tile_image->colormap[j].red=(Quantum)
                        ScaleShortToQuantum(ReadBlobMSBShort(image));
                      tile_image->colormap[j].green=(Quantum)
                        ScaleShortToQuantum(ReadBlobMSBShort(image));
                      tile_image->colormap[j].blue=(Quantum)
                        ScaleShortToQuantum(ReadBlobMSBShort(image));
                    }
                  }
                else
                  {
                    for (i=0; i < (ssize_t) tile_image->colors; i++)
                    {
                      tile_image->colormap[i].red=(Quantum) (QuantumRange-
                        tile_image->colormap[i].red);
                      tile_image->colormap[i].green=(Quantum) (QuantumRange-
                        tile_image->colormap[i].green);
                      tile_image->colormap[i].blue=(Quantum) (QuantumRange-
                        tile_image->colormap[i].blue);
                    }
                  }
              }
            if (ReadRectangle(image,&source) == MagickFalse)
              ThrowReaderException(CorruptImageError,"ImproperImageHeader");
            if (ReadRectangle(image,&destination) == MagickFalse)
              ThrowReaderException(CorruptImageError,"ImproperImageHeader");
            (void) ReadBlobMSBShort(image);
            if ((code == 0x91) || (code == 0x99) || (code == 0x9b))
              {
                /*
                  Skip region.
                */
                length=ReadBlobMSBShort(image);
                for (i=0; i < (ssize_t) (length-2); i++)
                  if (ReadBlobByte(image) == EOF)
                    break;
              }
            if ((code != 0x9a) && (code != 0x9b) &&
                (bytes_per_line & 0x8000) == 0)
              pixels=DecodeImage(image,tile_image,1UL*bytes_per_line,1,&extent,
                exception);
            else
              pixels=DecodeImage(image,tile_image,1UL*bytes_per_line,1U*
                pixmap.bits_per_pixel,&extent,exception);
            if (pixels == (unsigned char *) NULL)
              {
                tile_image=DestroyImage(tile_image);
                ThrowReaderException(ResourceLimitError,
                  "MemoryAllocationFailed");
              }
            /*
              Convert PICT tile image to pixel packets.
            */
            p=pixels;
            for (y=0; y < (ssize_t) tile_image->rows; y++)
            {
              if (p > (pixels+extent+image->columns))
                ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
              q=QueueAuthenticPixels(tile_image,0,y,tile_image->columns,1,
                exception);
              if (q == (Quantum *) NULL)
                break;
              for (x=0; x < (ssize_t) tile_image->columns; x++)
              {
                if (tile_image->storage_class == PseudoClass)
                  {
                    index=ConstrainColormapIndex(tile_image,*p,exception);
                    SetPixelIndex(tile_image,index,q);
                    SetPixelRed(tile_image,
                      tile_image->colormap[(ssize_t) index].red,q);
                    SetPixelGreen(tile_image,
                      tile_image->colormap[(ssize_t) index].green,q);
                    SetPixelBlue(tile_image,
                      tile_image->colormap[(ssize_t) index].blue,q);
                  }
                else
                  {
                    if (pixmap.bits_per_pixel == 16)
                      {
                        i=(*p++);
                        j=(*p);
                        SetPixelRed(tile_image,ScaleCharToQuantum(
                          (unsigned char) ((i & 0x7c) << 1)),q);
                        SetPixelGreen(tile_image,ScaleCharToQuantum(
                          (unsigned char) (((i & 0x03) << 6) |
                          ((j & 0xe0) >> 2))),q);
                        SetPixelBlue(tile_image,ScaleCharToQuantum(
                          (unsigned char) ((j & 0x1f) << 3)),q);
                      }
                    else
                      if (tile_image->alpha_trait == UndefinedPixelTrait)
                        {
                          if (p > (pixels+extent+2*image->columns))
                            ThrowReaderException(CorruptImageError,
                              "NotEnoughPixelData");
                          SetPixelRed(tile_image,ScaleCharToQuantum(*p),q);
                          SetPixelGreen(tile_image,ScaleCharToQuantum(
                            *(p+tile_image->columns)),q);
                          SetPixelBlue(tile_image,ScaleCharToQuantum(
                            *(p+2*tile_image->columns)),q);
                        }
                      else
                        {
                          if (p > (pixels+extent+3*image->columns))
                            ThrowReaderException(CorruptImageError,
                              "NotEnoughPixelData");
                          SetPixelAlpha(tile_image,ScaleCharToQuantum(*p),q);
                          SetPixelRed(tile_image,ScaleCharToQuantum(
                            *(p+tile_image->columns)),q);
                          SetPixelGreen(tile_image,ScaleCharToQuantum(
                            *(p+2*tile_image->columns)),q);
                          SetPixelBlue(tile_image,ScaleCharToQuantum(
                            *(p+3*tile_image->columns)),q);
                        }
                  }
                p++;
                q+=GetPixelChannels(tile_image);
              }
              if (SyncAuthenticPixels(tile_image,exception) == MagickFalse)
                break;
              if ((tile_image->storage_class == DirectClass) &&
                  (pixmap.bits_per_pixel != 16))
                {
                  p+=(pixmap.component_count-1)*tile_image->columns;
                  if (p < pixels)
                    break;
                }
              status=SetImageProgress(image,LoadImageTag,y,tile_image->rows);
              if (status == MagickFalse)
                break;
            }
            pixels=(unsigned char *) RelinquishMagickMemory(pixels);
            if (jpeg == MagickFalse)
              if ((code == 0x9a) || (code == 0x9b) ||
                  ((bytes_per_line & 0x8000) != 0))
                (void) CompositeImage(image,tile_image,CopyCompositeOp,
                  MagickTrue,destination.left,destination.top,exception);
            tile_image=DestroyImage(tile_image);
            break;
          }
          case 0xa1:
          {
            unsigned char
              *info;

            size_t
              type;

            /*
              Comment.
            */
            type=ReadBlobMSBShort(image);
            length=ReadBlobMSBShort(image);
            if (length == 0)
              break;
            (void) ReadBlobMSBLong(image);
            length-=4;
            if (length == 0)
              break;
            info=(unsigned char *) AcquireQuantumMemory(length,sizeof(*info));
            if (info == (unsigned char *) NULL)
              break;
            count=ReadBlob(image,length,info);
            if (count != (ssize_t) length)
              ThrowReaderException(ResourceLimitError,"UnableToReadImageData");
            switch (type)
            {
              case 0xe0:
              {
                profile=BlobToStringInfo((const void *) NULL,length);
                SetStringInfoDatum(profile,info);
                status=SetImageProfile(image,"icc",profile,exception);
                profile=DestroyStringInfo(profile);
                if (status == MagickFalse)
                  ThrowReaderException(ResourceLimitError,
                    "MemoryAllocationFailed");
                break;
              }
              case 0x1f2:
              {
                profile=BlobToStringInfo((const void *) NULL,length);
                SetStringInfoDatum(profile,info);
                status=SetImageProfile(image,"iptc",profile,exception);
                if (status == MagickFalse)
                  ThrowReaderException(ResourceLimitError,
                    "MemoryAllocationFailed");
                profile=DestroyStringInfo(profile);
                break;
              }
              default:
                break;
            }
            info=(unsigned char *) RelinquishMagickMemory(info);
            break;
          }
          default:
          {
            /*
              Skip to next op code.
            */
            if (codes[code].length == -1)
              (void) ReadBlobMSBShort(image);
            else
              for (i=0; i < (ssize_t) codes[code].length; i++)
                if (ReadBlobByte(image) == EOF)
                  break;
          }
        }
      }
    if (code == 0xc00)
      {
        /*
          Skip header.
        */
        for (i=0; i < 24; i++)
          if (ReadBlobByte(image) == EOF)
            break;
        continue;
      }
    if (((code >= 0xb0) && (code <= 0xcf)) ||
        ((code >= 0x8000) && (code <= 0x80ff)))
      continue;
    if (code == 0x8200)
      {
        FILE
          *file;

        Image
          *tile_image;

        ImageInfo
          *read_info;

        int
          unique_file;

        /*
          Embedded JPEG.
        */
        jpeg=MagickTrue;
        read_info=CloneImageInfo(image_info);
        SetImageInfoBlob(read_info,(void *) NULL,0);
        file=(FILE *) NULL;
        unique_file=AcquireUniqueFileResource(read_info->filename);
        if (unique_file != -1)
          file=fdopen(unique_file,"wb");
        if ((unique_file == -1) || (file == (FILE *) NULL))
          {
            (void) RelinquishUniqueFileResource(read_info->filename);
            (void) CopyMagickString(image->filename,read_info->filename,
              MagickPathExtent);
            ThrowFileException(exception,FileOpenError,
              "UnableToCreateTemporaryFile",image->filename);
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
        length=ReadBlobMSBLong(image);
        if (length > 154)
          {
            for (i=0; i < 6; i++)
              (void) ReadBlobMSBLong(image);
            if (ReadRectangle(image,&frame) == MagickFalse)
              {
                (void) fclose(file);
                (void) RelinquishUniqueFileResource(read_info->filename);
                ThrowReaderException(CorruptImageError,"ImproperImageHeader");
              }
            for (i=0; i < 122; i++)
              if (ReadBlobByte(image) == EOF)
                break;
            for (i=0; i < (ssize_t) (length-154); i++)
            {
              c=ReadBlobByte(image);
              if (c == EOF)
                break;
              (void) fputc(c,file);
            }
          }
        (void) fclose(file);
        (void) close(unique_file);
        tile_image=ReadImage(read_info,exception);
        (void) RelinquishUniqueFileResource(read_info->filename);
        read_info=DestroyImageInfo(read_info);
        if (tile_image == (Image *) NULL)
          continue;
        (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g",
          (double) MagickMax(image->columns,tile_image->columns),
          (double) MagickMax(image->rows,tile_image->rows));
        (void) SetImageExtent(image,
          MagickMax(image->columns,tile_image->columns),
          MagickMax(image->rows,tile_image->rows),exception);
        (void) TransformImageColorspace(image,tile_image->colorspace,exception);
        (void) CompositeImage(image,tile_image,CopyCompositeOp,MagickTrue,
          frame.left,frame.right,exception);
        image->compression=tile_image->compression;
        tile_image=DestroyImage(tile_image);
        continue;
      }
    if ((code == 0xff) || (code == 0xffff))
      break;
    if (((code >= 0xd0) && (code <= 0xfe)) ||
        ((code >= 0x8100) && (code <= 0xffff)))
      {
        /*
          Skip reserved.
        */
        length=ReadBlobMSBShort(image);
        for (i=0; i < (ssize_t) length; i++)
          if (ReadBlobByte(image) == EOF)
            break;
        continue;
      }
    if ((code >= 0x100) && (code <= 0x7fff))
      {
        /*
          Skip reserved.
        */
        length=(size_t) ((code >> 7) & 0xff);
        for (i=0; i < (ssize_t) length; i++)
          if (ReadBlobByte(image) == EOF)
            break;
        continue;
      }
  }
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}


static inline size_t GetPSDRowSize(Image *image)
{
  if (image->depth == 1)
    return(((image->columns+7)/8)*GetPSDPacketSize(image));
  else
    return(image->columns*GetPSDPacketSize(image));
}


static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  char
    magick[14];

  Image
    *image;

  int
    foundPropEnd = 0;

  MagickBooleanType
    status;

  MagickOffsetType
    offset;

  register ssize_t
    i;

  size_t
    image_type,
    length;

  ssize_t
    count;

  XCFDocInfo
    doc_info;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  count=ReadBlob(image,14,(unsigned char *) magick);
  if ((count != 14) ||
      (LocaleNCompare((char *) magick,"gimp xcf",8) != 0))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  (void) ResetMagickMemory(&doc_info,0,sizeof(XCFDocInfo));
  doc_info.width=ReadBlobMSBLong(image);
  doc_info.height=ReadBlobMSBLong(image);
  if ((doc_info.width > 262144) || (doc_info.height > 262144))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  doc_info.image_type=ReadBlobMSBLong(image);
  /*
    Initialize image attributes.
  */
  image->columns=doc_info.width;
  image->rows=doc_info.height;
  image_type=doc_info.image_type;
  doc_info.file_size=GetBlobSize(image);
  image->compression=NoCompression;
  image->depth=8;
  if (image_type == GIMP_RGB)
    SetImageColorspace(image,sRGBColorspace,exception);
  else
    if (image_type == GIMP_GRAY)
      SetImageColorspace(image,GRAYColorspace,exception);
    else
      if (image_type == GIMP_INDEXED)
        ThrowReaderException(CoderError,"ColormapTypeNotSupported");
  (void) SetImageBackgroundColor(image,exception);
  (void) SetImageAlpha(image,OpaqueAlpha,exception);
  /*
    Read properties.
  */
  while ((foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse))
  {
    PropType prop_type = (PropType) ReadBlobMSBLong(image);
    size_t prop_size = ReadBlobMSBLong(image);

    switch (prop_type)
    {
      case PROP_END:
        foundPropEnd=1;
        break;
      case PROP_COLORMAP:
      {
        /* Cannot rely on prop_size here--the value is set incorrectly
           by some Gimp versions.
        */
        size_t num_colours = ReadBlobMSBLong(image);
        if (DiscardBlobBytes(image,3*num_colours) == MagickFalse)
          ThrowFileException(exception,CorruptImageError,
            "UnexpectedEndOfFile",image->filename);
    /*
      if (info->file_version == 0)
      {
        gint i;

        g_message (_("XCF warning: version 0 of XCF file format\n"
           "did not save indexed colormaps correctly.\n"
           "Substituting grayscale map."));
        info->cp +=
          xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
        gimage->cmap = g_new (guchar, gimage->num_cols*3);
        xcf_seek_pos (info, info->cp + gimage->num_cols);
        for (i = 0; i<gimage->num_cols; i++)
          {
            gimage->cmap[i*3+0] = i;
            gimage->cmap[i*3+1] = i;
            gimage->cmap[i*3+2] = i;
          }
      }
      else
      {
        info->cp +=
          xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
        gimage->cmap = g_new (guchar, gimage->num_cols*3);
        info->cp +=
          xcf_read_int8 (info->fp,
                   (guint8*) gimage->cmap, gimage->num_cols*3);
      }
     */
        break;
      }
      case PROP_COMPRESSION:
      {
        doc_info.compression = ReadBlobByte(image);
        if ((doc_info.compression != COMPRESS_NONE) &&
            (doc_info.compression != COMPRESS_RLE) &&
            (doc_info.compression != COMPRESS_ZLIB) &&
            (doc_info.compression != COMPRESS_FRACTAL))
          ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
      }
      break;

      case PROP_GUIDES:
      {
         /* just skip it - we don't care about guides */
        if (DiscardBlobBytes(image,prop_size) == MagickFalse)
          ThrowFileException(exception,CorruptImageError,
            "UnexpectedEndOfFile",image->filename);
      }
      break;

    case PROP_RESOLUTION:
      {
        /* float xres = (float) */ (void) ReadBlobMSBLong(image);
        /* float yres = (float) */ (void) ReadBlobMSBLong(image);

        /*
        if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
            yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
        {
        g_message ("Warning, resolution out of range in XCF file");
        xres = gimage->gimp->config->default_xresolution;
        yres = gimage->gimp->config->default_yresolution;
        }
        */

        /* BOGUS: we don't write these yet because we aren't
              reading them properly yet :(
              image->resolution.x = xres;
              image->resolution.y = yres;
        */
      }
      break;

    case PROP_TATTOO:
      {
        /* we need to read it, even if we ignore it */
        /*size_t  tattoo_state = */ (void) ReadBlobMSBLong(image);
      }
      break;

    case PROP_PARASITES:
      {
        /* BOGUS: we may need these for IPTC stuff */
        if (DiscardBlobBytes(image,prop_size) == MagickFalse)
          ThrowFileException(exception,CorruptImageError,
            "UnexpectedEndOfFile",image->filename);
        /*
      gssize_t         base = info->cp;
      GimpParasite *p;

      while (info->cp - base < prop_size)
        {
          p = xcf_load_parasite (info);
          gimp_image_parasite_attach (gimage, p);
          gimp_parasite_free (p);
        }
      if (info->cp - base != prop_size)
        g_message ("Error detected while loading an image's parasites");
      */
          }
      break;

    case PROP_UNIT:
      {
        /* BOGUS: ignore for now... */
      /*size_t unit =  */ (void) ReadBlobMSBLong(image);
      }
      break;

    case PROP_PATHS:
      {
      /* BOGUS: just skip it for now */
        if (DiscardBlobBytes(image,prop_size) == MagickFalse)
          ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
            image->filename);

        /*
      PathList *paths = xcf_load_bzpaths (gimage, info);
      gimp_image_set_paths (gimage, paths);
      */
      }
      break;

    case PROP_USER_UNIT:
      {
        char  unit_string[1000];
        /*BOGUS: ignored for now */
        /*float  factor = (float) */ (void) ReadBlobMSBLong(image);
        /* size_t digits =  */ (void) ReadBlobMSBLong(image);
        for (i=0; i<5; i++)
         (void) ReadBlobStringWithLongSize(image, unit_string,
           sizeof(unit_string),exception);
      }
     break;

      default:
      {
        int buf[16];
        ssize_t amount;

      /* read over it... */
      while ((prop_size > 0) && (EOFBlob(image) == MagickFalse))
      {
        amount=(ssize_t) MagickMin(16, prop_size);
        amount=(ssize_t) ReadBlob(image,(size_t) amount,(unsigned char *) &buf);
        if (!amount)
          ThrowReaderException(CorruptImageError,"CorruptImage");
        prop_size -= (size_t) MagickMin(16,(size_t) amount);
      }
    }
    break;
  }
  }
  if (foundPropEnd == MagickFalse)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");

  if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
    {
      ; /* do nothing, were just pinging! */
    }
  else
    {
      int
        current_layer = 0,
        foundAllLayers = MagickFalse,
        number_layers = 0;

      MagickOffsetType
        oldPos=TellBlob(image);

      XCFLayerInfo
        *layer_info;

      status=SetImageExtent(image,image->columns,image->rows,exception);
      if (status == MagickFalse)
        return(DestroyImageList(image));
      /*
        the read pointer
      */
      do
      {
        ssize_t offset = (int) ReadBlobMSBLong(image);
        if (offset == 0)
          foundAllLayers=MagickTrue;
        else
          number_layers++;
        if (EOFBlob(image) != MagickFalse)
          {
            ThrowFileException(exception,CorruptImageError,
              "UnexpectedEndOfFile",image->filename);
            break;
          }
    } while (foundAllLayers == MagickFalse);
    doc_info.number_layers=number_layers;
    offset=SeekBlob(image,oldPos,SEEK_SET); /* restore the position! */
    if (offset < 0)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    /* allocate our array of layer info blocks */
    length=(size_t) number_layers;
    layer_info=(XCFLayerInfo *) AcquireQuantumMemory(length,
      sizeof(*layer_info));
    if (layer_info == (XCFLayerInfo *) NULL)
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    (void) ResetMagickMemory(layer_info,0,number_layers*sizeof(XCFLayerInfo));
    for ( ; ; )
    {
      MagickBooleanType
        layer_ok;

      MagickOffsetType
        offset,
        saved_pos;

      /* read in the offset of the next layer */
      offset=(MagickOffsetType) ReadBlobMSBLong(image);
      /* if the offset is 0 then we are at the end
      *  of the layer list.
      */
      if (offset == 0)
        break;
      /* save the current position as it is where the
      *  next layer offset is stored.
      */
      saved_pos=TellBlob(image);
      /* seek to the layer offset */
      offset=SeekBlob(image,offset,SEEK_SET);
      /* read in the layer */
      layer_ok=ReadOneLayer(image_info,image,&doc_info,
        &layer_info[current_layer],current_layer,exception);
      if (layer_ok == MagickFalse)
        {
          int j;

          for (j=0; j < current_layer; j++)
            layer_info[j].image=DestroyImage(layer_info[j].image);
          layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      /* restore the saved position so we'll be ready to
      *  read the next offset.
      */
      offset=SeekBlob(image, saved_pos, SEEK_SET);
      current_layer++;
    }
#if 0
        {
        /* NOTE: XCF layers are REVERSED from composite order! */
        signed int  j;
        for (j=number_layers-1; j>=0; j--) {
          /* BOGUS: need to consider layer blending modes!! */

          if ( layer_info[j].visible ) { /* only visible ones, please! */
            CompositeImage(image, OverCompositeOp, layer_info[j].image,
                     layer_info[j].offset_x, layer_info[j].offset_y );
             layer_info[j].image =DestroyImage( layer_info[j].image );

            /* If we do this, we'll get REAL gray images! */
            if ( image_type == GIMP_GRAY ) {
              QuantizeInfo  qi;
              GetQuantizeInfo(&qi);
              qi.colorspace = GRAYColorspace;
              QuantizeImage( &qi, layer_info[j].image );
            }
          }
        }
      }
#else
      {
        /* NOTE: XCF layers are REVERSED from composite order! */
        ssize_t  j;

        /* now reverse the order of the layers as they are put
           into subimages
        */
        for (j=(ssize_t) number_layers-1; j >= 0; j--)
          AppendImageToList(&image,layer_info[j].image);
      }
#endif

    layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info);

#if 0  /* BOGUS: do we need the channels?? */
    while (MagickTrue)
    {
      /* read in the offset of the next channel */
      info->cp += xcf_read_int32 (info->fp, &offset, 1);

      /* if the offset is 0 then we are at the end
      *  of the channel list.
      */
      if (offset == 0)
        break;

      /* save the current position as it is where the
      *  next channel offset is stored.
      */
      saved_pos = info->cp;

      /* seek to the channel offset */
      xcf_seek_pos (info, offset);

      /* read in the layer */
      channel = xcf_load_channel (info, gimage);
      if (channel == 0)
        goto error;

      num_successful_elements++;

      /* add the channel to the image if its not the selection */
      if (channel != gimage->selection_mask)
        gimp_image_add_channel (gimage, channel, -1);

      /* restore the saved position so we'll be ready to
      *  read the next offset.
      */
      xcf_seek_pos (info, saved_pos);
    }
#endif
  }

  (void) CloseBlob(image);
  DestroyImage(RemoveFirstImageFromList(&image));
  if (image_type == GIMP_GRAY)
    image->type=GrayscaleType;
  return(GetFirstImageInList(image));
}


MagickExport QuantumFormatType GetQuantumFormat(const QuantumInfo *quantum_info)
{
  assert(quantum_info != (QuantumInfo *) NULL);
  assert(quantum_info->signature == MagickCoreSignature);
  return(quantum_info->format);
}


static void ImportBGRAQuantum(const Image *image,QuantumInfo *quantum_info,
  const MagickSizeType number_pixels,const unsigned char *magick_restrict p,
  Quantum *magick_restrict q,ExceptionInfo *exception)
{
  QuantumAny
    range;

  register ssize_t
    x;

  unsigned int
    pixel;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  switch (quantum_info->depth)
  {
    case 8:
    {
      unsigned char
        pixel;

      for (x=0; x < (ssize_t) number_pixels; x++)
      {
        p=PushCharPixel(p,&pixel);
        SetPixelBlue(image,ScaleCharToQuantum(pixel),q);
        p=PushCharPixel(p,&pixel);
        SetPixelGreen(image,ScaleCharToQuantum(pixel),q);
        p=PushCharPixel(p,&pixel);
        SetPixelRed(image,ScaleCharToQuantum(pixel),q);
        p=PushCharPixel(p,&pixel);
        SetPixelAlpha(image,ScaleCharToQuantum(pixel),q);
        p+=quantum_info->pad;
        q+=GetPixelChannels(image);
      }
      break;
    }
    case 10:
    {
      pixel=0;
      if (quantum_info->pack == MagickFalse)
        {
          register ssize_t
            i;

          size_t
            quantum;

          ssize_t
            n;

          n=0;
          quantum=0;
          for (x=0; x < (ssize_t) number_pixels; x++)
          {
            for (i=0; i < 4; i++)
            {
              switch (n % 3)
              {
                case 0:
                {
                  p=PushLongPixel(quantum_info->endian,p,&pixel);
                  quantum=(size_t) (ScaleShortToQuantum((unsigned short)
                    (((pixel >> 22) & 0x3ff) << 6)));
                  break;
                }
                case 1:
                {
                  quantum=(size_t) (ScaleShortToQuantum((unsigned short)
                    (((pixel >> 12) & 0x3ff) << 6)));
                  break;
                }
                case 2:
                {
                  quantum=(size_t) (ScaleShortToQuantum((unsigned short)
                    (((pixel >> 2) & 0x3ff) << 6)));
                  break;
                }
              }
              switch (i)
              {
                case 0: SetPixelRed(image,(Quantum) quantum,q); break;
                case 1: SetPixelGreen(image,(Quantum) quantum,q); break;
                case 2: SetPixelBlue(image,(Quantum) quantum,q); break;
                case 3: SetPixelAlpha(image,(Quantum) quantum,q); break;
              }
              n++;
            }
            p+=quantum_info->pad;
            q+=GetPixelChannels(image);
          }
          break;
        }
      for (x=0; x < (ssize_t) number_pixels; x++)
      {
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelRed(image,ScaleShortToQuantum((unsigned short) (pixel << 6)),q);
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelGreen(image,ScaleShortToQuantum((unsigned short) (pixel << 6)),
          q);
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelBlue(image,ScaleShortToQuantum((unsigned short) (pixel << 6)),
          q);
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelAlpha(image,ScaleShortToQuantum((unsigned short) (pixel << 6)),
          q);
        q+=GetPixelChannels(image);
      }
      break;
    }
    case 16:
    {
      unsigned short
        pixel;

      if (quantum_info->format == FloatingPointQuantumFormat)
        {
          for (x=0; x < (ssize_t) number_pixels; x++)
          {
            p=PushShortPixel(quantum_info->endian,p,&pixel);
            SetPixelRed(image,ClampToQuantum(QuantumRange*
              HalfToSinglePrecision(pixel)),q);
            p=PushShortPixel(quantum_info->endian,p,&pixel);
            SetPixelGreen(image,ClampToQuantum(QuantumRange*
              HalfToSinglePrecision(pixel)),q);
            p=PushShortPixel(quantum_info->endian,p,&pixel);
            SetPixelBlue(image,ClampToQuantum(QuantumRange*
              HalfToSinglePrecision(pixel)),q);
            p=PushShortPixel(quantum_info->endian,p,&pixel);
            SetPixelAlpha(image,ClampToQuantum(QuantumRange*
              HalfToSinglePrecision(pixel)),q);
            p+=quantum_info->pad;
            q+=GetPixelChannels(image);
          }
          break;
        }
      for (x=0; x < (ssize_t) number_pixels; x++)
      {
        p=PushShortPixel(quantum_info->endian,p,&pixel);
        SetPixelBlue(image,ScaleShortToQuantum(pixel),q);
        p=PushShortPixel(quantum_info->endian,p,&pixel);
        SetPixelGreen(image,ScaleShortToQuantum(pixel),q);
        p=PushShortPixel(quantum_info->endian,p,&pixel);
        SetPixelRed(image,ScaleShortToQuantum(pixel),q);
        p=PushShortPixel(quantum_info->endian,p,&pixel);
        SetPixelAlpha(image,ScaleShortToQuantum(pixel),q);
        p+=quantum_info->pad;
        q+=GetPixelChannels(image);
      }
      break;
    }
    case 32:
    {
      unsigned int
        pixel;

      if (quantum_info->format == FloatingPointQuantumFormat)
        {
          float
            pixel;

          for (x=0; x < (ssize_t) number_pixels; x++)
          {
            p=PushFloatPixel(quantum_info,p,&pixel);
            SetPixelRed(image,ClampToQuantum(pixel),q);
            p=PushFloatPixel(quantum_info,p,&pixel);
            SetPixelGreen(image,ClampToQuantum(pixel),q);
            p=PushFloatPixel(quantum_info,p,&pixel);
            SetPixelBlue(image,ClampToQuantum(pixel),q);
            p=PushFloatPixel(quantum_info,p,&pixel);
            SetPixelAlpha(image,ClampToQuantum(pixel),q);
            p+=quantum_info->pad;
            q+=GetPixelChannels(image);
          }
          break;
        }
      for (x=0; x < (ssize_t) number_pixels; x++)
      {
        p=PushLongPixel(quantum_info->endian,p,&pixel);
        SetPixelBlue(image,ScaleLongToQuantum(pixel),q);
        p=PushLongPixel(quantum_info->endian,p,&pixel);
        SetPixelGreen(image,ScaleLongToQuantum(pixel),q);
        p=PushLongPixel(quantum_info->endian,p,&pixel);
        SetPixelRed(image,ScaleLongToQuantum(pixel),q);
        p=PushLongPixel(quantum_info->endian,p,&pixel);
        SetPixelAlpha(image,ScaleLongToQuantum(pixel),q);
        p+=quantum_info->pad;
        q+=GetPixelChannels(image);
      }
      break;
    }
    case 64:
    {
      if (quantum_info->format == FloatingPointQuantumFormat)
        {
          double
            pixel;

          for (x=0; x < (ssize_t) number_pixels; x++)
          {
            p=PushDoublePixel(quantum_info,p,&pixel);
            SetPixelRed(image,ClampToQuantum(pixel),q);
            p=PushDoublePixel(quantum_info,p,&pixel);
            SetPixelGreen(image,ClampToQuantum(pixel),q);
            p=PushDoublePixel(quantum_info,p,&pixel);
            SetPixelBlue(image,ClampToQuantum(pixel),q);
            p=PushDoublePixel(quantum_info,p,&pixel);
            SetPixelAlpha(image,ClampToQuantum(pixel),q);
            p+=quantum_info->pad;
            q+=GetPixelChannels(image);
          }
          break;
        }
    }
    default:
    {
      range=GetQuantumRange(quantum_info->depth);
      for (x=0; x < (ssize_t) number_pixels; x++)
      {
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelBlue(image,ScaleAnyToQuantum(pixel,range),q);
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelGreen(image,ScaleAnyToQuantum(pixel,range),q);
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelRed(image,ScaleAnyToQuantum(pixel,range),q);
        p=PushQuantumPixel(quantum_info,p,&pixel);
        SetPixelAlpha(image,ScaleAnyToQuantum(pixel,range),q);
        q+=GetPixelChannels(image);
      }
      break;
    }
  }
}


get_one_option(int optid,
	       const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
  switch (optid) {
  case 'a':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_STATISTICS;
    else
      check_param.testflag|= T_STATISTICS;
    break;
  case 'A':
    if (argument)
      check_param.auto_increment_value= strtoull(argument, NULL, 0);
    else
      check_param.auto_increment_value= 0;	/* Set to max used value */
    check_param.testflag|= T_AUTO_INC;
    break;
  case 'b':
    check_param.search_after_block= strtoul(argument, NULL, 10);
    break;
  case 'B':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_BACKUP_DATA;
    else
      check_param.testflag|= T_BACKUP_DATA;
    break;
  case 'c':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_CHECK;
    else
      check_param.testflag|= T_CHECK;
    break;
  case 'C':
    if (argument == disabled_my_option)
      check_param.testflag&= ~(T_CHECK | T_CHECK_ONLY_CHANGED);
    else
      check_param.testflag|= T_CHECK | T_CHECK_ONLY_CHANGED;
    break;
  case 'D':
    check_param.max_data_file_length=strtoll(argument, NULL, 10);
    break;
  case 's':				/* silent */
    if (argument == disabled_my_option)
      check_param.testflag&= ~(T_SILENT | T_VERY_SILENT);
    else
    {
      if (check_param.testflag & T_SILENT)
	check_param.testflag|= T_VERY_SILENT;
      check_param.testflag|= T_SILENT;
      check_param.testflag&= ~T_WRITE_LOOP;
    }
    break;
  case 'w':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_WAIT_FOREVER;
    else
      check_param.testflag|= T_WAIT_FOREVER;
    break;
  case 'd':				/* description if isam-file */
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_DESCRIPT;
    else
      check_param.testflag|= T_DESCRIPT;
    break;
  case 'e':				/* extend check */
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_EXTEND;
    else
      check_param.testflag|= T_EXTEND;
    break;
  case 'i':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_INFO;
    else
      check_param.testflag|= T_INFO;
    break;
  case 'f':
    if (argument == disabled_my_option)
    {
      check_param.tmpfile_createflag= O_RDWR | O_TRUNC | O_EXCL;
      check_param.testflag&= ~(T_FORCE_CREATE | T_UPDATE_STATE);
    }
    else
    {
      check_param.tmpfile_createflag= O_RDWR | O_TRUNC;
      check_param.testflag|= T_FORCE_CREATE | T_UPDATE_STATE;
    }
    break;
  case 'F':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_FAST;
    else
      check_param.testflag|= T_FAST;
    break;
  case 'k':
    check_param.keys_in_use= (ulonglong) strtoll(argument, NULL, 10);
    break;
  case 'm':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_MEDIUM;
    else
      check_param.testflag|= T_MEDIUM;		/* Medium check */
    break;
  case 'r':				/* Repair table */
    check_param.testflag&= ~T_REP_ANY;
    if (argument != disabled_my_option)
      check_param.testflag|= T_REP_BY_SORT;
    break;
  case 'p':
    check_param.testflag&= ~T_REP_ANY;
    if (argument != disabled_my_option)
      check_param.testflag|= T_REP_PARALLEL;
    break;
  case 'o':
    check_param.testflag&= ~T_REP_ANY;
    check_param.force_sort= 0;
    if (argument != disabled_my_option)
    {
      check_param.testflag|= T_REP;
      my_disable_async_io= 1;		/* More safety */
    }
    break;
  case 'n':
    check_param.testflag&= ~T_REP_ANY;
    if (argument == disabled_my_option)
      check_param.force_sort= 0;
    else
    {
      check_param.testflag|= T_REP_BY_SORT;
      check_param.force_sort= 1;
    }
    break;
  case 'q':
    if (argument == disabled_my_option)
      check_param.testflag&= ~(T_QUICK | T_FORCE_UNIQUENESS);
    else
      check_param.testflag|=
        (check_param.testflag & T_QUICK) ? T_FORCE_UNIQUENESS : T_QUICK;
    break;
  case 'u':
    if (argument == disabled_my_option)
      check_param.testflag&= ~(T_UNPACK | T_REP_BY_SORT);
    else
      check_param.testflag|= T_UNPACK | T_REP_BY_SORT;
    break;
  case 'v':				/* Verbose */
    if (argument == disabled_my_option)
    {
      check_param.testflag&= ~T_VERBOSE;
      check_param.verbose=0;
    }
    else
    {
      check_param.testflag|= T_VERBOSE;
      check_param.verbose++;
    }
    break;
  case 'R':				/* Sort records */
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_SORT_RECORDS;
    else
    {
      check_param.testflag|= T_SORT_RECORDS;
      check_param.opt_sort_key= (uint) atoi(argument) - 1;
      if (check_param.opt_sort_key >= MI_MAX_KEY)
      {
	fprintf(stderr,
		"The value of the sort key is bigger than max key: %d.\n",
		MI_MAX_KEY);
	exit(1);
      }
    }
    break;
  case 'S':			      /* Sort index */
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_SORT_INDEX;
    else
      check_param.testflag|= T_SORT_INDEX;
    break;
  case 'T':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_READONLY;
    else
      check_param.testflag|= T_READONLY;
    break;
  case 'U':
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_UPDATE_STATE;
    else
      check_param.testflag|= T_UPDATE_STATE;
    break;
  case '#':
    if (argument == disabled_my_option)
    {
      DBUG_POP();
    }
    else
    {
      DBUG_PUSH(argument ? argument : "d:t:o,/tmp/myisamchk.trace");
    }
    break;
  case 'V':
    print_version();
    exit(0);
  case OPT_CORRECT_CHECKSUM:
    if (argument == disabled_my_option)
      check_param.testflag&= ~T_CALC_CHECKSUM;
    else
      check_param.testflag|= T_CALC_CHECKSUM;
    break;
  case OPT_STATS_METHOD:
  {
    int method;
    enum_mi_stats_method UNINIT_VAR(method_conv);
    myisam_stats_method_str= argument;
    if ((method= find_type(argument, &myisam_stats_method_typelib,
                           FIND_TYPE_BASIC)) <= 0)
    {
      fprintf(stderr, "Invalid value of stats_method: %s.\n", argument);
      exit(1);
    }
    switch (method-1) {
    case 0:
      method_conv= MI_STATS_METHOD_NULLS_EQUAL;
      break;
    case 1:
      method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
      break;
    case 2:
      method_conv= MI_STATS_METHOD_IGNORE_NULLS;
      break;
    default: assert(0);                         /* Impossible */
    }
    check_param.stats_method= method_conv;
    break;
  }
#ifdef DEBUG					/* Only useful if debugging */
  case OPT_START_CHECK_POS:
    check_param.start_check_pos= strtoull(argument, NULL, 0);
    break;
#endif
  case 'H':
    my_print_help(my_long_options);
    exit(0);
  case '?':
    usage();
    exit(0);
  }
  return 0;
}


int my_redel(const char *org_name, const char *tmp_name, myf MyFlags)
{
  int error=1;
  DBUG_ENTER("my_redel");
  DBUG_PRINT("my",("org_name: '%s' tmp_name: '%s'  MyFlags: %d",
		   org_name,tmp_name,MyFlags));

  if (!(MyFlags & MY_REDEL_NO_COPY_STAT))
  {
    if (my_copystat(org_name,tmp_name,MyFlags) < 0)
      goto end;
  }
  if (MyFlags & MY_REDEL_MAKE_BACKUP)
  {
    char name_buff[FN_REFLEN+20];
    char ext[20];
    ext[0]='-';
    get_date(ext+1,2+4,(time_t) 0);
    strmov(strend(ext),REDEL_EXT);
    if (my_rename(org_name, fn_format(name_buff, org_name, "", ext, 2),
		  MyFlags))
      goto end;
  }
  else if (my_delete_allow_opened(org_name, MyFlags))
      goto end;
  if (my_rename(tmp_name,org_name,MyFlags))
    goto end;

  error=0;
end:
  DBUG_RETURN(error);
} /* my_redel */


void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
{
  va_list args;
  DBUG_ENTER("mi_check_print_warning");

  fflush(stdout);
  if (!param->warning_printed && !param->error_printed)
  {
    if (param->testflag & T_SILENT)
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,
	      param->isam_file_name);
    param->out_flag|= O_DATA_LOST;
  }
  param->warning_printed=1;
  va_start(args,fmt);
  fprintf(stderr,"%s: warning: ",my_progname_short);
  (void) vfprintf(stderr, fmt, args);
  (void) fputc('\n',stderr);
  fflush(stderr);
  va_end(args);
  DBUG_VOID_RETURN;
}


int movepoint(register MI_INFO *info, uchar *record, my_off_t oldpos,
	      my_off_t newpos, uint prot_key)
{
  register uint i;
  uchar *key;
  uint key_length;
  DBUG_ENTER("movepoint");

  key=info->lastkey+info->s->base.max_key_length;
  for (i=0 ; i < info->s->base.keys; i++)
  {
    if (i != prot_key && mi_is_key_active(info->s->state.key_map, i))
    {
      key_length=_mi_make_key(info,i,key,record,oldpos);
      if (info->s->keyinfo[i].flag & HA_NOSAME)
      {					/* Change pointer direct */
	uint nod_flag;
	MI_KEYDEF *keyinfo;
	keyinfo=info->s->keyinfo+i;
	if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY,
		       (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
		       info->s->state.key_root[i]))
	  DBUG_RETURN(-1);
	nod_flag=mi_test_if_nod(info->buff);
	_mi_dpointer(info,info->int_keypos-nod_flag-
		     info->s->rec_reflength,newpos);
	if (_mi_write_keypage(info,keyinfo,info->last_keypage,
                              DFLT_INIT_HITS,info->buff))
	  DBUG_RETURN(-1);
      }
      else
      {					/* Change old key to new */
	if (_mi_ck_delete(info,i,key,key_length))
	  DBUG_RETURN(-1);
	key_length=_mi_make_key(info,i,key,record,newpos);
	if (_mi_ck_write(info,i,key,key_length))
	  DBUG_RETURN(-1);
      }
    }
  }
  DBUG_RETURN(0);
} /* movepoint */


int ha_myisam::delete_row(const uchar *buf)
{
  ha_statistic_increment(&SSV::ha_delete_count);
  return mi_delete(file,buf);
}


int chk_key(MI_CHECK *param, register MI_INFO *info)
{
  uint key,found_keys=0,full_text_keys=0,result=0;
  ha_rows keys;
  ha_checksum old_record_checksum,init_checksum;
  my_off_t all_keydata,all_totaldata,key_totlength,length;
  ulong   *rec_per_key_part;
  MYISAM_SHARE *share=info->s;
  MI_KEYDEF *keyinfo;
  char buff[22],buff2[22];
  DBUG_ENTER("chk_key");

  if (!(param->testflag & T_SILENT))
    puts("- check key delete-chain");

  param->key_file_blocks=info->s->base.keystart;
  for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
    if (check_k_link(param,info,key))
    {
      if (param->testflag & T_VERBOSE) puts("");
      mi_check_print_error(param,"key delete-link-chain corrupted");
      DBUG_RETURN(-1);
    }

  if (!(param->testflag & T_SILENT)) puts("- check index reference");

  all_keydata=all_totaldata=key_totlength=0;
  old_record_checksum=0;
  init_checksum=param->record_checksum;
  if (!(share->options &
	(HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
    old_record_checksum=calc_checksum(info->state->records+info->state->del-1)*
      share->base.pack_reclength;
  rec_per_key_part= param->rec_per_key_part;
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
       rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
  {
    param->key_crc[key]=0;
    if (! mi_is_key_active(share->state.key_map, key))
    {
      /* Remember old statistics for key */
      memcpy((char*) rec_per_key_part,
	     (char*) (share->state.rec_per_key_part +
		      (uint) (rec_per_key_part - param->rec_per_key_part)),
	     keyinfo->keysegs*sizeof(*rec_per_key_part));
      continue;
    }
    found_keys++;

    param->record_checksum=init_checksum;

    bzero((char*) &param->unique_count,sizeof(param->unique_count));
    bzero((char*) &param->notnull_count,sizeof(param->notnull_count));

    if ((!(param->testflag & T_SILENT)))
      printf ("- check data record references index: %d\n",key+1);
    if (keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))
      full_text_keys++;
    if (share->state.key_root[key] == HA_OFFSET_ERROR &&
	(info->state->records == 0 || keyinfo->flag & HA_FULLTEXT))
      goto do_stat;
    if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
                           DFLT_INIT_HITS,info->buff,0))
    {
      mi_check_print_error(param,"Can't read indexpage from filepos: %s",
		  llstr(share->state.key_root[key],buff));
      if (!(param->testflag & T_INFO))
	DBUG_RETURN(-1);
      result= -1;
      continue;
    }
    param->key_file_blocks+=keyinfo->block_length;
    keys=0;
    param->keydata=param->totaldata=0;
    param->key_blocks=0;
    param->max_level=0;
    if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
		  &keys, param->key_crc+key,1))
      DBUG_RETURN(-1);
    if(!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL)))
    {
      if (keys != info->state->records)
      {
	mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
		    llstr(info->state->records,buff2));
	if (!(param->testflag & T_INFO))
	DBUG_RETURN(-1);
	result= -1;
	continue;
      }
      if (found_keys - full_text_keys == 1 &&
	  ((share->options &
	    (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
	   (param->testflag & T_DONT_CHECK_CHECKSUM)))
	old_record_checksum=param->record_checksum;
      else if (old_record_checksum != param->record_checksum)
      {
	if (key)
	  mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
		      key+1);
	else
	  mi_check_print_error(param,"Key 1 doesn't point at all records");
	if (!(param->testflag & T_INFO))
	  DBUG_RETURN(-1);
	result= -1;
	continue;
      }
    }
    if ((uint) share->base.auto_key -1 == key)
    {
      /* Check that auto_increment key is bigger than max key value */
      ulonglong auto_increment;
      info->lastinx=key;
      _mi_read_key_record(info, 0L, info->rec_buff);
      auto_increment= retrieve_auto_increment(info, info->rec_buff);
      if (auto_increment > info->s->state.auto_increment)
      {
        mi_check_print_warning(param, "Auto-increment value: %s is smaller "
                               "than max used value: %s",
                               llstr(info->s->state.auto_increment,buff2),
                               llstr(auto_increment, buff));
      }
      if (param->testflag & T_AUTO_INC)
      {
        set_if_bigger(info->s->state.auto_increment,
                      auto_increment);
        set_if_bigger(info->s->state.auto_increment,
                      param->auto_increment_value);
      }

      /* Check that there isn't a row with auto_increment = 0 in the table */
      mi_extra(info,HA_EXTRA_KEYREAD,0);
      bzero(info->lastkey,keyinfo->seg->length);
      if (!mi_rkey(info, info->rec_buff, key, (const uchar*) info->lastkey,
		   (key_part_map)1, HA_READ_KEY_EXACT))
      {
	/* Don't count this as a real warning, as myisamchk can't correct it */
	uint save=param->warning_printed;
        mi_check_print_warning(param, "Found row where the auto_increment "
                               "column has the value 0");
	param->warning_printed=save;
      }
      mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
    }

    length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
    if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
      printf("Key: %2d:  Keyblocks used: %3d%%  Packed: %4d%%  Max levels: %2d\n",
	     key+1,
	     (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
	     (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
		    my_off_t2double(length)),
	     param->max_level);
    all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;

do_stat:
    if (param->testflag & T_STATISTICS)
      update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
                       param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
                       param->notnull_count: NULL,
                       (ulonglong)info->state->records);
  }
  if (param->testflag & T_INFO)
  {
    if (all_totaldata != 0L && found_keys > 0)
      printf("Total:    Keyblocks used: %3d%%  Packed: %4d%%\n\n",
	     (int) (my_off_t2double(all_keydata)*100.0/
		    my_off_t2double(all_totaldata)),
	     (int) ((my_off_t2double(key_totlength) -
		     my_off_t2double(all_keydata))*100.0/
		     my_off_t2double(key_totlength)));
    else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map))
      puts("");
  }
  if (param->key_file_blocks != info->state->key_file_length &&
      param->keys_in_use != ~(ulonglong) 0)
    mi_check_print_warning(param, "Some data are unreferenced in keyfile");
  if (found_keys != full_text_keys)
    param->record_checksum=old_record_checksum-init_checksum;	/* Remove delete links */
  else
    param->record_checksum=0;
  DBUG_RETURN(result);
} /* chk_key */


void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, ulonglong *notnull,
                                    uchar *key)
{
  uint first_null, kp;
  first_null= (uint) (ha_find_null(keyseg, key) - keyseg);
  /*
    All prefix tuples that don't include keypart_{first_null} are not-null
    tuples (and all others aren't), increment counters for them.
  */
  for (kp= 0; kp < first_null; kp++)
    notnull[kp]++;
}


int ha_myisam::reset(void)
{
  return mi_reset(file);
}